#include <sys/param.h>
#include <machine/biosvar.h>
#include <dev/isa/isareg.h>
#include <stand/boot/bootarg.h>
#include "libsa.h"
u_int cnvmem, extmem;
bios_memmap_t bios_memmap[64];
#ifndef EFIBOOT
static __inline int
checkA20(void)
{
register char *p = (char *)0x100000;
register char *q = (char *)0x000000;
int st;
if (*p != *q)
return 1;
*p = ~(*p);
st = (*p != *q);
*p = ~(*p);
return st;
}
static __inline bios_memmap_t *
bios_E820(bios_memmap_t *mp)
{
int rc, off = 0, sig, gotcha = 0;
do {
BIOS_regs.biosr_es = ((u_int)(mp) >> 4);
__asm volatile(DOINT(0x15) "; setc %b1"
: "=a" (sig), "=d" (rc), "=b" (off)
: "0" (0xE820), "1" (0x534d4150), "b" (off),
"c" (sizeof(*mp)), "D" (((u_int)mp) & 0xf)
: "cc", "memory");
off = BIOS_regs.biosr_bx;
if (rc & 0xff || sig != 0x534d4150)
break;
gotcha++;
if (!mp->type)
mp->type = BIOS_MAP_RES;
mp++;
} while (off);
if (!gotcha)
return NULL;
#ifdef DEBUG
printf("0x15[E820] ");
#endif
return mp;
}
static __inline bios_memmap_t *
bios_8800(bios_memmap_t *mp)
{
int rc, mem;
__asm volatile(DOINT(0x15) "; setc %b0"
: "=c" (rc), "=a" (mem) : "a" (0x8800));
if (rc & 0xff)
return NULL;
#ifdef DEBUG
printf("0x15[8800] ");
#endif
mp->addr = 1024 * 1024;
mp->size = (mem & 0xffff) * 1024;
mp->type = BIOS_MAP_FREE;
return ++mp;
}
static __inline bios_memmap_t *
bios_int12(bios_memmap_t *mp)
{
int mem;
#ifdef DEBUG
printf("0x12 ");
#endif
__asm volatile(DOINT(0x12) : "=a" (mem) :: "%ecx", "%edx", "cc");
mp->addr = 0;
mp->size = (mem & 0xffff) * 1024;
mp->type = BIOS_MAP_FREE;
return ++mp;
}
const u_int addrprobe_pat[] = {
0x00000000, 0xFFFFFFFF,
0x01010101, 0x10101010,
0x55555555, 0xCCCCCCCC
};
static int
addrprobe(u_int kloc)
{
volatile u_int *loc;
register u_int i, ret = 0;
u_int save[nitems(addrprobe_pat)];
loc = (int *)(intptr_t)(kloc * 1024);
save[0] = *loc;
for (i = 0; i < nitems(addrprobe_pat); i++) {
*loc = addrprobe_pat[i];
if (*loc != addrprobe_pat[i])
ret++;
}
*loc = save[0];
if (!ret) {
for (i = 0; i < nitems(addrprobe_pat); i++) {
save[i] = loc[i];
loc[i] = addrprobe_pat[i];
}
for (i = 0; i < nitems(addrprobe_pat); i++) {
if (loc[i] != addrprobe_pat[i])
ret++;
loc[i] = save[i];
}
}
return ret;
}
static __inline bios_memmap_t *
badprobe(bios_memmap_t *mp)
{
u_int64_t ram;
#ifdef DEBUG
printf("scan ");
#endif
for (ram = 1024; ram < 512 * 1024; ram += 4)
if (addrprobe(ram))
break;
mp->addr = 1024 * 1024;
mp->size = (ram - 1024) * 1024;
mp->type = BIOS_MAP_FREE;
return ++mp;
}
bios_memmap_t bios_memmap[64];
void
memprobe(void)
{
bios_memmap_t *pm = bios_memmap, *im;
#ifdef DEBUG
printf(" mem(");
#else
printf(" mem[");
#endif
if ((pm = bios_E820(bios_memmap)) == NULL) {
im = bios_int12(bios_memmap);
pm = bios_8800(im);
if (pm == NULL)
pm = badprobe(im);
if (pm == NULL) {
printf(" No Extended memory detected.");
pm = im;
}
}
pm->type = BIOS_MAP_END;
apmfixmem();
#ifdef DEBUG
printf(")[");
#endif
extmem = cnvmem = 0;
for (im = bios_memmap; im->type != BIOS_MAP_END; im++) {
if ((im->type == BIOS_MAP_FREE) && (im->size >= 12 * 1024)) {
if (im->size > 1024 * 1024)
printf("%uM ", (u_int)(im->size /
(1024 * 1024)));
else
printf("%uK ", (u_int)im->size / 1024);
if (im->addr < IOM_BEGIN)
cnvmem = max(cnvmem,
im->addr + im->size) / 1024;
if (im->addr >= IOM_END)
extmem += im->size / 1024;
}
}
printf("a20=o%s] ", checkA20()? "n" : "ff!");
}
#endif
void
dump_biosmem(bios_memmap_t *tm)
{
register bios_memmap_t *p;
register u_int total = 0;
if (tm == NULL)
tm = bios_memmap;
for (p = tm; p->type != BIOS_MAP_END; p++) {
printf("Region %ld: type %u at 0x%llx for %uKB\n",
(long)(p - tm), p->type, p->addr,
(u_int)(p->size / 1024));
if (p->type == BIOS_MAP_FREE)
total += p->size / 1024;
}
printf("Low ram: %dKB High ram: %dKB\n", cnvmem, extmem);
printf("Total free memory: %uKB\n", total);
}
int
mem_limit(long long ml)
{
register bios_memmap_t *p;
for (p = bios_memmap; p->type != BIOS_MAP_END; p++) {
register int64_t sp = p->addr, ep = p->addr + p->size;
if (p->type != BIOS_MAP_FREE)
continue;
if ((sp >= ml) && (ep >= ml)) {
bcopy (p + 1, p, (char *)bios_memmap +
sizeof(bios_memmap) - (char *)p);
} else if ((sp < ml) && (ep >= ml)) {
p->size -= (ep - ml);
}
}
return 0;
}
int
mem_delete(long long sa, long long ea)
{
register bios_memmap_t *p;
for (p = bios_memmap; p->type != BIOS_MAP_END; p++) {
if (p->type == BIOS_MAP_FREE) {
register int64_t sp = p->addr, ep = p->addr + p->size;
if ((sa - sp) <= PAGE_SIZE && (ep - ea) <= PAGE_SIZE) {
bcopy(p + 1, p, (char *)bios_memmap +
sizeof(bios_memmap) - (char *)p);
break;
} else if (sa <= sp && sp < ea) {
p->addr = ea;
p->size = ep - ea;
break;
} else if (sa < ep && ep <= ea) {
p->size = sa - sp;
break;
} else if (sp < sa && ea < ep) {
bcopy(p, p + 1, (char *)bios_memmap +
sizeof(bios_memmap) - (char *)p -
sizeof(bios_memmap[0]));
p[1].addr = ea;
p[1].size = ep - ea;
p->size = sa - sp;
break;
}
}
}
return 0;
}
int
mem_add(long long sa, long long ea)
{
register bios_memmap_t *p;
for (p = bios_memmap; p->type != BIOS_MAP_END; p++) {
if (p->type == BIOS_MAP_FREE) {
register int64_t sp = p->addr, ep = p->addr + p->size;
if (sp <= sa && ea <= ep) {
break;
} else if (sa < sp && sp <= ea) {
p->addr = sa;
p->size = ep - sa;
break;
} else if (sa <= ep && ep < ea) {
p->size = ea - sp;
break;
} else if (ea < sp) {
bcopy(p, p + 1, (char *)bios_memmap +
sizeof(bios_memmap) - (char *)(p - 1));
p->addr = sa;
p->size = ea - sa;
break;
}
}
}
if (p->type == BIOS_MAP_END) {
p[1] = p[0];
p->type = BIOS_MAP_FREE;
p->addr = sa;
p->size = ea - sa;
}
return 0;
}
void
mem_pass(void)
{
bios_memmap_t *p;
for (p = bios_memmap; p->type != BIOS_MAP_END; p++)
;
addbootarg(BOOTARG_MEMMAP, (p - bios_memmap + 1) * sizeof *bios_memmap,
bios_memmap);
}