Annotation of sys/arch/i386/stand/libsa/memprobe.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: memprobe.c,v 1.45 2006/09/18 21:14:15 mpf Exp $ */
! 2:
! 3: /*
! 4: * Copyright (c) 1997-1999 Michael Shalayeff
! 5: * Copyright (c) 1997-1999 Tobias Weingartner
! 6: * All rights reserved.
! 7: *
! 8: * Redistribution and use in source and binary forms, with or without
! 9: * modification, are permitted provided that the following conditions
! 10: * are met:
! 11: * 1. Redistributions of source code must retain the above copyright
! 12: * notice, this list of conditions and the following disclaimer.
! 13: * 2. Redistributions in binary form must reproduce the above copyright
! 14: * notice, this list of conditions and the following disclaimer in the
! 15: * documentation and/or other materials provided with the distribution.
! 16: *
! 17: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
! 18: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
! 19: * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
! 20: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
! 21: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
! 22: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
! 23: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
! 24: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
! 25: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
! 26: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
! 27: * SUCH DAMAGE.
! 28: *
! 29: */
! 30:
! 31: #include <sys/param.h>
! 32: #include <machine/biosvar.h>
! 33: #include <dev/isa/isareg.h>
! 34: #include <stand/boot/bootarg.h>
! 35: #include "libsa.h"
! 36:
! 37: u_int cnvmem, extmem; /* XXX - compatibility */
! 38:
! 39:
! 40: /* Check gateA20
! 41: *
! 42: * A sanity check.
! 43: */
! 44: static __inline int
! 45: checkA20(void)
! 46: {
! 47: register char *p = (char *)0x100000;
! 48: register char *q = (char *)0x000000;
! 49: int st;
! 50:
! 51: /* Simple check */
! 52: if (*p != *q)
! 53: return 1;
! 54:
! 55: /* Complex check */
! 56: *p = ~(*p);
! 57: st = (*p != *q);
! 58: *p = ~(*p);
! 59:
! 60: return st;
! 61: }
! 62:
! 63: /* BIOS int 15, AX=E820
! 64: *
! 65: * This is the "preferred" method.
! 66: */
! 67: static __inline bios_memmap_t *
! 68: bios_E820(bios_memmap_t *mp)
! 69: {
! 70: int rc, off = 0, sig, gotcha = 0;
! 71:
! 72: do {
! 73: BIOS_regs.biosr_es = ((u_int)(mp) >> 4);
! 74:
! 75: __asm __volatile(DOINT(0x15) "; setc %b1"
! 76: : "=a" (sig), "=d" (rc), "=b" (off)
! 77: : "0" (0xE820), "1" (0x534d4150), "b" (off),
! 78: "c" (sizeof(*mp)), "D" (((u_int)mp) & 0xF)
! 79: : "cc", "memory");
! 80: off = BIOS_regs.biosr_bx;
! 81:
! 82: if (rc & 0xff || sig != 0x534d4150)
! 83: break;
! 84: gotcha++;
! 85: if (!mp->type)
! 86: mp->type = BIOS_MAP_RES;
! 87: mp++;
! 88: } while (off);
! 89:
! 90: if (!gotcha)
! 91: return NULL;
! 92: #ifdef DEBUG
! 93: printf("0x15[E820] ");
! 94: #endif
! 95: return mp;
! 96: }
! 97:
! 98: #if 0
! 99: /* BIOS int 15, AX=E801
! 100: *
! 101: * Only used if int 15, AX=E820 does not work.
! 102: * This should work for more than 64MB on most
! 103: * modern machines. However, there is always
! 104: * an exception, the older IBM machine do not
! 105: * like this call.
! 106: */
! 107: static __inline bios_memmap_t *
! 108: bios_E801(bios_memmap_t *mp)
! 109: {
! 110: int rc, m1, m2, m3, m4;
! 111: u_int8_t *info;
! 112:
! 113: /* Test for possibility of 0xE801 */
! 114: info = getSYSCONFaddr();
! 115: if (!info)
! 116: return NULL;
! 117: /* XXX - Should test model/submodel/rev here */
! 118: printf("model(%d,%d,%d)", info[2], info[3], info[4]);
! 119:
! 120: /* Check for 94 or later bios */
! 121: info = (void *)0xFFFFB;
! 122: if (info[0] == '9' && info[1] <= '3')
! 123: return NULL;
! 124:
! 125: /* We might have this call */
! 126: __asm __volatile(DOINT(0x15) "; mov %%ax, %%si; setc %b0"
! 127: : "=a" (rc), "=S" (m1), "=b" (m2), "=c" (m3), "=d" (m4)
! 128: : "0" (0xE801));
! 129:
! 130: /* Test for failure */
! 131: if (rc & 0xff)
! 132: return NULL;
! 133:
! 134: /* Fixup for screwed up machines */
! 135: if (m1 == 0) {
! 136: m1 = m3;
! 137: m2 = m4;
! 138: }
! 139: #ifdef DEBUG
! 140: printf("0x15[E801] ");
! 141: #endif
! 142: /* Fill out BIOS map */
! 143: mp->addr = (1024 * 1024); /* 1MB */
! 144: mp->size = (m1 & 0xffff) * 1024;
! 145: mp->type = BIOS_MAP_FREE;
! 146:
! 147: mp++;
! 148: mp->addr = (1024 * 1024) * 16; /* 16MB */
! 149: mp->size = (m2 & 0xffff) * 64L * 1024;
! 150: mp->type = BIOS_MAP_FREE;
! 151:
! 152: return ++mp;
! 153: }
! 154: #endif
! 155:
! 156: /* BIOS int 15, AX=8800
! 157: *
! 158: * Only used if int 15, AX=E801 does not work.
! 159: * Machines with this are restricted to 64MB.
! 160: */
! 161: static __inline bios_memmap_t *
! 162: bios_8800(bios_memmap_t *mp)
! 163: {
! 164: int rc, mem;
! 165:
! 166: __asm __volatile(DOINT(0x15) "; setc %b0"
! 167: : "=c" (rc), "=a" (mem) : "a" (0x8800));
! 168:
! 169: if (rc & 0xff)
! 170: return NULL;
! 171: #ifdef DEBUG
! 172: printf("0x15[8800] ");
! 173: #endif
! 174: /* Fill out a BIOS_MAP */
! 175: mp->addr = 1024 * 1024; /* 1MB */
! 176: mp->size = (mem & 0xffff) * 1024;
! 177: mp->type = BIOS_MAP_FREE;
! 178:
! 179: return ++mp;
! 180: }
! 181:
! 182: /* BIOS int 0x12 Get Conventional Memory
! 183: *
! 184: * Only used if int 15, AX=E820 does not work.
! 185: */
! 186: static __inline bios_memmap_t *
! 187: bios_int12(bios_memmap_t *mp)
! 188: {
! 189: int mem;
! 190: #ifdef DEBUG
! 191: printf("0x12 ");
! 192: #endif
! 193: __asm __volatile(DOINT(0x12) : "=a" (mem) :: "%ecx", "%edx", "cc");
! 194:
! 195: /* Fill out a bios_memmap_t */
! 196: mp->addr = 0;
! 197: mp->size = (mem & 0xffff) * 1024;
! 198: mp->type = BIOS_MAP_FREE;
! 199:
! 200: return ++mp;
! 201: }
! 202:
! 203:
! 204: /* addrprobe(kloc): Probe memory at address kloc * 1024.
! 205: *
! 206: * This is a hack, but it seems to work ok. Maybe this is
! 207: * the *real* way that you are supposed to do probing???
! 208: *
! 209: * Modify the original a bit. We write everything first, and
! 210: * then test for the values. This should croak on machines that
! 211: * return values just written on non-existent memory...
! 212: *
! 213: * BTW: These machines are pretty broken IMHO.
! 214: *
! 215: * XXX - Does not detect aliased memory.
! 216: */
! 217: const u_int addrprobe_pat[] = {
! 218: 0x00000000, 0xFFFFFFFF,
! 219: 0x01010101, 0x10101010,
! 220: 0x55555555, 0xCCCCCCCC
! 221: };
! 222: static int
! 223: addrprobe(u_int kloc)
! 224: {
! 225: __volatile u_int *loc;
! 226: register u_int i, ret = 0;
! 227: u_int save[NENTS(addrprobe_pat)];
! 228:
! 229: /* Get location */
! 230: loc = (int *)(kloc * 1024);
! 231:
! 232: save[0] = *loc;
! 233: /* Probe address */
! 234: for (i = 0; i < NENTS(addrprobe_pat); i++) {
! 235: *loc = addrprobe_pat[i];
! 236: if (*loc != addrprobe_pat[i])
! 237: ret++;
! 238: }
! 239: *loc = save[0];
! 240:
! 241: if (!ret) {
! 242: /* Write address */
! 243: for (i = 0; i < NENTS(addrprobe_pat); i++) {
! 244: save[i] = loc[i];
! 245: loc[i] = addrprobe_pat[i];
! 246: }
! 247:
! 248: /* Read address */
! 249: for (i = 0; i < NENTS(addrprobe_pat); i++) {
! 250: if (loc[i] != addrprobe_pat[i])
! 251: ret++;
! 252: loc[i] = save[i];
! 253: }
! 254: }
! 255:
! 256: return ret;
! 257: }
! 258:
! 259: /* Probe for all extended memory.
! 260: *
! 261: * This is only used as a last resort. If we resort to this
! 262: * routine, we are getting pretty desperate. Hopefully nobody
! 263: * has to rely on this after all the work above.
! 264: *
! 265: * XXX - Does not detect aliased memory.
! 266: * XXX - Could be destructive, as it does write.
! 267: */
! 268: static __inline bios_memmap_t *
! 269: badprobe(bios_memmap_t *mp)
! 270: {
! 271: u_int64_t ram;
! 272: #ifdef DEBUG
! 273: printf("scan ");
! 274: #endif
! 275: /* probe extended memory
! 276: *
! 277: * There is no need to do this in assembly language. This is
! 278: * much easier to debug in C anyways.
! 279: */
! 280: for (ram = 1024; ram < 512 * 1024; ram += 4)
! 281: if (addrprobe(ram))
! 282: break;
! 283:
! 284: mp->addr = 1024 * 1024;
! 285: mp->size = (ram - 1024) * 1024;
! 286: mp->type = BIOS_MAP_FREE;
! 287:
! 288: return ++mp;
! 289: }
! 290:
! 291: bios_memmap_t bios_memmap[32]; /* This is easier */
! 292: #ifndef _TEST
! 293: void
! 294: memprobe(void)
! 295: {
! 296: bios_memmap_t *pm = bios_memmap, *im;
! 297:
! 298: #ifdef DEBUG
! 299: printf(" mem(");
! 300: #else
! 301: printf(" mem[");
! 302: #endif
! 303:
! 304: if ((pm = bios_E820(bios_memmap)) == NULL) {
! 305: im = bios_int12(bios_memmap);
! 306: #if 0
! 307: pm = bios_E801(im);
! 308: if (pm == NULL)
! 309: #endif
! 310: pm = bios_8800(im);
! 311: if (pm == NULL)
! 312: pm = badprobe(im);
! 313: if (pm == NULL) {
! 314: printf(" No Extended memory detected.");
! 315: pm = im;
! 316: }
! 317: }
! 318: pm->type = BIOS_MAP_END;
! 319:
! 320: /* XXX - gotta peephole optimize the list */
! 321:
! 322: /* Remove APM needed RAM */
! 323: apmfixmem();
! 324:
! 325: #ifdef DEBUG
! 326: printf(")[");
! 327: #endif
! 328:
! 329: /* XXX - Compatibility, remove later (smpprobe() relies on it) */
! 330: extmem = cnvmem = 0;
! 331: for (im = bios_memmap; im->type != BIOS_MAP_END; im++) {
! 332: /* Count only "good" memory chunks 12K and up in size */
! 333: if ((im->type == BIOS_MAP_FREE) && (im->size >= 12*1024)) {
! 334: if (im->size > 1024 * 1024)
! 335: printf("%uM ", (u_int)(im->size /
! 336: (1024 * 1024)));
! 337: else
! 338: printf("%uK ", (u_int)im->size / 1024);
! 339:
! 340: /*
! 341: * Compute compatibility values:
! 342: * cnvmem -- is the upper boundary of conventional
! 343: * memory (below IOM_BEGIN (=640k))
! 344: * extmem -- is the size of the contignous extended
! 345: * memory segment starting at 1M
! 346: *
! 347: * We ignore "good" memory in the 640K-1M hole.
! 348: * We drop "machine {cnvmem,extmem}" commands.
! 349: */
! 350: if (im->addr < IOM_BEGIN)
! 351: cnvmem = max(cnvmem,
! 352: im->addr + im->size) / 1024;
! 353: if (im->addr >= IOM_END)
! 354: extmem += im->size / 1024;
! 355: }
! 356: }
! 357:
! 358: /* Check if gate A20 is on */
! 359: printf("a20=o%s] ", checkA20()? "n" : "ff!");
! 360: }
! 361: #endif
! 362:
! 363: void
! 364: dump_biosmem(bios_memmap_t *tm)
! 365: {
! 366: register bios_memmap_t *p;
! 367: register u_int total = 0;
! 368:
! 369: if (tm == NULL)
! 370: tm = bios_memmap;
! 371:
! 372: for (p = tm; p->type != BIOS_MAP_END; p++) {
! 373: printf("Region %ld: type %u at 0x%llx for %uKB\n",
! 374: (long)(p - tm), p->type, p->addr,
! 375: (u_int)(p->size / 1024));
! 376:
! 377: if (p->type == BIOS_MAP_FREE)
! 378: total += p->size / 1024;
! 379: }
! 380:
! 381: printf("Low ram: %dKB High ram: %dKB\n", cnvmem, extmem);
! 382: printf("Total free memory: %uKB\n", total);
! 383: }
! 384:
! 385: int
! 386: mem_delete(long long sa, long long ea)
! 387: {
! 388: register bios_memmap_t *p;
! 389:
! 390: for (p = bios_memmap; p->type != BIOS_MAP_END; p++) {
! 391: if (p->type == BIOS_MAP_FREE) {
! 392: register int64_t sp = p->addr, ep = p->addr + p->size;
! 393:
! 394: /* can we eat it as a whole? */
! 395: if ((sa - sp) <= NBPG && (ep - ea) <= NBPG) {
! 396: bcopy(p + 1, p, (char *)bios_memmap +
! 397: sizeof(bios_memmap) - (char *)p);
! 398: break;
! 399: /* eat head or legs */
! 400: } else if (sa <= sp && sp < ea) {
! 401: p->addr = ea;
! 402: p->size = ep - ea;
! 403: break;
! 404: } else if (sa < ep && ep <= ea) {
! 405: p->size = sa - sp;
! 406: break;
! 407: } else if (sp < sa && ea < ep) {
! 408: /* bite in half */
! 409: bcopy(p, p + 1, (char *)bios_memmap +
! 410: sizeof(bios_memmap) - (char *)p -
! 411: sizeof(bios_memmap[0]));
! 412: p[1].addr = ea;
! 413: p[1].size = ep - ea;
! 414: p->size = sa - sp;
! 415: break;
! 416: }
! 417: }
! 418: }
! 419: return 0;
! 420: }
! 421:
! 422: int
! 423: mem_add(long long sa, long long ea)
! 424: {
! 425: register bios_memmap_t *p;
! 426:
! 427: for (p = bios_memmap; p->type != BIOS_MAP_END; p++) {
! 428: if (p->type == BIOS_MAP_FREE) {
! 429: register int64_t sp = p->addr, ep = p->addr + p->size;
! 430:
! 431: /* is it already there? */
! 432: if (sp <= sa && ea <= ep) {
! 433: break;
! 434: /* join head or legs */
! 435: } else if (sa < sp && sp <= ea) {
! 436: p->addr = sa;
! 437: p->size = ep - sa;
! 438: break;
! 439: } else if (sa <= ep && ep < ea) {
! 440: p->size = ea - sp;
! 441: break;
! 442: } else if (ea < sp) {
! 443: /* insert before */
! 444: bcopy(p, p + 1, (char *)bios_memmap +
! 445: sizeof(bios_memmap) - (char *)(p - 1));
! 446: p->addr = sa;
! 447: p->size = ea - sa;
! 448: break;
! 449: }
! 450: }
! 451: }
! 452:
! 453: /* meaning add new item at the end of the list */
! 454: if (p->type == BIOS_MAP_END) {
! 455: p[1] = p[0];
! 456: p->type = BIOS_MAP_FREE;
! 457: p->addr = sa;
! 458: p->size = ea - sa;
! 459: }
! 460:
! 461: return 0;
! 462: }
! 463:
! 464: void
! 465: mem_pass(void)
! 466: {
! 467: bios_memmap_t *p;
! 468:
! 469: for (p = bios_memmap; p->type != BIOS_MAP_END; p++)
! 470: ;
! 471: addbootarg(BOOTARG_MEMMAP, (p - bios_memmap + 1) * sizeof *bios_memmap,
! 472: bios_memmap);
! 473: }
CVSweb