1 /* $OpenBSD: memprobe.c,v 1.46 2009/11/30 16:33:20 canacar 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[64]; /* 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 } 474