1 /* $OpenBSD: memprobe.c,v 1.13 2014/03/29 18:09:28 guenther 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 /* 41 * Check gateA20 42 * 43 * A sanity check. 44 */ 45 static __inline int 46 checkA20(void) 47 { 48 register char *p = (char *)0x100000; 49 register char *q = (char *)0x000000; 50 int st; 51 52 /* Simple check */ 53 if (*p != *q) 54 return 1; 55 56 /* Complex check */ 57 *p = ~(*p); 58 st = (*p != *q); 59 *p = ~(*p); 60 61 return st; 62 } 63 64 /* 65 * BIOS int 15, AX=E820 66 * 67 * This is the "preferred" method. 68 */ 69 static __inline bios_memmap_t * 70 bios_E820(bios_memmap_t *mp) 71 { 72 int rc, off = 0, sig, gotcha = 0; 73 74 do { 75 BIOS_regs.biosr_es = ((u_int)(mp) >> 4); 76 __asm volatile(DOINT(0x15) "; setc %b1" 77 : "=a" (sig), "=d" (rc), "=b" (off) 78 : "0" (0xE820), "1" (0x534d4150), "b" (off), 79 "c" (sizeof(*mp)), "D" (((u_int)mp) & 0xf) 80 : "cc", "memory"); 81 off = BIOS_regs.biosr_bx; 82 83 if (rc & 0xff || sig != 0x534d4150) 84 break; 85 gotcha++; 86 if (!mp->type) 87 mp->type = BIOS_MAP_RES; 88 mp++; 89 } while (off); 90 91 if (!gotcha) 92 return NULL; 93 #ifdef DEBUG 94 printf("0x15[E820] "); 95 #endif 96 return mp; 97 } 98 99 #if 0 100 /* 101 * BIOS int 15, AX=E801 102 * 103 * Only used if int 15, AX=E820 does not work. 104 * This should work for more than 64MB on most 105 * modern machines. However, there is always 106 * an exception, the older IBM machine do not 107 * like this call. 108 */ 109 static __inline bios_memmap_t * 110 bios_E801(bios_memmap_t *mp) 111 { 112 int rc, m1, m2, m3, m4; 113 u_int8_t *info; 114 115 /* Test for possibility of 0xE801 */ 116 info = getSYSCONFaddr(); 117 if (!info) 118 return NULL; 119 /* XXX - Should test model/submodel/rev here */ 120 printf("model(%d,%d,%d)", info[2], info[3], info[4]); 121 122 /* Check for 94 or later bios */ 123 info = (void *)0xFFFFB; 124 if (info[0] == '9' && info[1] <= '3') 125 return NULL; 126 127 /* We might have this call */ 128 __asm volatile(DOINT(0x15) "; mov %%ax, %%si; setc %b0" 129 : "=a" (rc), "=S" (m1), "=b" (m2), "=c" (m3), "=d" (m4) 130 : "0" (0xE801)); 131 132 /* Test for failure */ 133 if (rc & 0xff) 134 return NULL; 135 136 /* Fixup for screwed up machines */ 137 if (m1 == 0) { 138 m1 = m3; 139 m2 = m4; 140 } 141 #ifdef DEBUG 142 printf("0x15[E801] "); 143 #endif 144 /* Fill out BIOS map */ 145 mp->addr = (1024 * 1024); /* 1MB */ 146 mp->size = (m1 & 0xffff) * 1024; 147 mp->type = BIOS_MAP_FREE; 148 149 mp++; 150 mp->addr = (1024 * 1024) * 16; /* 16MB */ 151 mp->size = (m2 & 0xffff) * 64L * 1024; 152 mp->type = BIOS_MAP_FREE; 153 154 return ++mp; 155 } 156 #endif 157 158 /* 159 * BIOS int 15, AX=8800 160 * 161 * Only used if int 15, AX=E801 does not work. 162 * Machines with this are restricted to 64MB. 163 */ 164 static __inline bios_memmap_t * 165 bios_8800(bios_memmap_t *mp) 166 { 167 int rc, mem; 168 169 __asm volatile(DOINT(0x15) "; setc %b0" 170 : "=c" (rc), "=a" (mem) : "a" (0x8800)); 171 172 if (rc & 0xff) 173 return NULL; 174 #ifdef DEBUG 175 printf("0x15[8800] "); 176 #endif 177 /* Fill out a BIOS_MAP */ 178 mp->addr = 1024 * 1024; /* 1MB */ 179 mp->size = (mem & 0xffff) * 1024; 180 mp->type = BIOS_MAP_FREE; 181 182 return ++mp; 183 } 184 185 /* 186 * BIOS int 0x12 Get Conventional Memory 187 * 188 * Only used if int 15, AX=E820 does not work. 189 */ 190 static __inline bios_memmap_t * 191 bios_int12(bios_memmap_t *mp) 192 { 193 int mem; 194 #ifdef DEBUG 195 printf("0x12 "); 196 #endif 197 __asm volatile(DOINT(0x12) : "=a" (mem) :: "%ecx", "%edx", "cc"); 198 199 /* Fill out a bios_memmap_t */ 200 mp->addr = 0; 201 mp->size = (mem & 0xffff) * 1024; 202 mp->type = BIOS_MAP_FREE; 203 204 return ++mp; 205 } 206 207 208 /* 209 * addrprobe(kloc): Probe memory at address kloc * 1024. 210 * 211 * This is a hack, but it seems to work ok. Maybe this is 212 * the *real* way that you are supposed to do probing??? 213 * 214 * Modify the original a bit. We write everything first, and 215 * then test for the values. This should croak on machines that 216 * return values just written on non-existent memory... 217 * 218 * BTW: These machines are pretty broken IMHO. 219 * 220 * XXX - Does not detect aliased memory. 221 */ 222 const u_int addrprobe_pat[] = { 223 0x00000000, 0xFFFFFFFF, 224 0x01010101, 0x10101010, 225 0x55555555, 0xCCCCCCCC 226 }; 227 static int 228 addrprobe(u_int kloc) 229 { 230 volatile u_int *loc; 231 register u_int i, ret = 0; 232 u_int save[nitems(addrprobe_pat)]; 233 234 /* Get location */ 235 loc = (int *)(kloc * 1024); 236 237 save[0] = *loc; 238 /* Probe address */ 239 for (i = 0; i < nitems(addrprobe_pat); i++) { 240 *loc = addrprobe_pat[i]; 241 if (*loc != addrprobe_pat[i]) 242 ret++; 243 } 244 *loc = save[0]; 245 246 if (!ret) { 247 /* Write address */ 248 for (i = 0; i < nitems(addrprobe_pat); i++) { 249 save[i] = loc[i]; 250 loc[i] = addrprobe_pat[i]; 251 } 252 253 /* Read address */ 254 for (i = 0; i < nitems(addrprobe_pat); i++) { 255 if (loc[i] != addrprobe_pat[i]) 256 ret++; 257 loc[i] = save[i]; 258 } 259 } 260 261 return ret; 262 } 263 264 /* 265 * Probe for all extended memory. 266 * 267 * This is only used as a last resort. If we resort to this 268 * routine, we are getting pretty desperate. Hopefully nobody 269 * has to rely on this after all the work above. 270 * 271 * XXX - Does not detect aliased memory. 272 * XXX - Could be destructive, as it does write. 273 */ 274 static __inline bios_memmap_t * 275 badprobe(bios_memmap_t *mp) 276 { 277 u_int64_t ram; 278 #ifdef DEBUG 279 printf("scan "); 280 #endif 281 /* 282 * probe extended memory 283 * 284 * There is no need to do this in assembly language. This is 285 * much easier to debug in C anyways. 286 */ 287 for (ram = 1024; ram < 512 * 1024; ram += 4) 288 if (addrprobe(ram)) 289 break; 290 291 mp->addr = 1024 * 1024; 292 mp->size = (ram - 1024) * 1024; 293 mp->type = BIOS_MAP_FREE; 294 295 return ++mp; 296 } 297 298 bios_memmap_t bios_memmap[64]; /* This is easier */ 299 #ifndef _TEST 300 void 301 memprobe(void) 302 { 303 bios_memmap_t *pm = bios_memmap, *im; 304 305 #ifdef DEBUG 306 printf(" mem("); 307 #else 308 printf(" mem["); 309 #endif 310 311 if ((pm = bios_E820(bios_memmap)) == NULL) { 312 im = bios_int12(bios_memmap); 313 #if 0 314 pm = bios_E801(im); 315 if (pm == NULL) 316 #endif 317 pm = bios_8800(im); 318 if (pm == NULL) 319 pm = badprobe(im); 320 if (pm == NULL) { 321 printf(" No Extended memory detected."); 322 pm = im; 323 } 324 } 325 pm->type = BIOS_MAP_END; 326 327 /* XXX - gotta peephole optimize the list */ 328 329 #ifdef DEBUG 330 printf(")["); 331 #endif 332 333 /* XXX - Compatibility, remove later (smpprobe() relies on it) */ 334 extmem = cnvmem = 0; 335 for (im = bios_memmap; im->type != BIOS_MAP_END; im++) { 336 /* Count only "good" memory chunks 12K and up in size */ 337 if ((im->type == BIOS_MAP_FREE) && (im->size >= 12 * 1024)) { 338 if (im->size > 1024 * 1024) 339 printf("%uM ", (u_int)(im->size / 340 (1024 * 1024))); 341 else 342 printf("%uK ", (u_int)im->size / 1024); 343 344 /* 345 * Compute compatibility values: 346 * cnvmem -- is the upper boundary of conventional 347 * memory (below IOM_BEGIN (=640k)) 348 * extmem -- is the size of the contignous extended 349 * memory segment starting at 1M 350 * 351 * We ignore "good" memory in the 640K-1M hole. 352 * We drop "machine {cnvmem,extmem}" commands. 353 */ 354 if (im->addr < IOM_BEGIN) 355 cnvmem = max(cnvmem, 356 im->addr + im->size) / 1024; 357 if (im->addr >= IOM_END && 358 (im->addr / 1024) == (extmem + 1024)) 359 extmem += im->size / 1024; 360 } 361 } 362 363 /* 364 * Adjust extmem to be no more than 4G (which it usually is not 365 * anyways). In order for an x86 type machine (amd64/etc) to use 366 * more than 4GB of memory, it will need to grok and use the bios 367 * memory map we pass it. Note that above we only count CONTIGUOUS 368 * memory from the 1MB boundary on for extmem (think I/O holes). 369 * 370 * extmem is in KB, and we have 4GB - 1MB (base/io hole) worth of it. 371 */ 372 if (extmem > 4 * 1024 * 1024 - 1024) 373 extmem = 4 * 1024 * 1024 - 1024; 374 375 /* Check if gate A20 is on */ 376 printf("a20=o%s] ", checkA20()? "n" : "ff!"); 377 } 378 #endif 379 380 void 381 dump_biosmem(bios_memmap_t *tm) 382 { 383 register bios_memmap_t *p; 384 register u_int total = 0; 385 386 if (tm == NULL) 387 tm = bios_memmap; 388 389 for (p = tm; p->type != BIOS_MAP_END; p++) { 390 printf("Region %ld: type %u at 0x%llx for %uKB\n", 391 (long)(p - tm), p->type, p->addr, 392 (u_int)(p->size / 1024)); 393 394 if (p->type == BIOS_MAP_FREE) 395 total += p->size / 1024; 396 } 397 398 printf("Low ram: %dKB High ram: %dKB\n", cnvmem, extmem); 399 printf("Total free memory: %uKB\n", total); 400 } 401 402 int 403 mem_limit(long long ml) 404 { 405 register bios_memmap_t *p; 406 407 for (p = bios_memmap; p->type != BIOS_MAP_END; p++) { 408 register int64_t sp = p->addr, ep = p->addr + p->size; 409 410 if (p->type != BIOS_MAP_FREE) 411 continue; 412 413 /* Wholy above limit, nuke it */ 414 if ((sp >= ml) && (ep >= ml)) { 415 bcopy (p + 1, p, (char *)bios_memmap + 416 sizeof(bios_memmap) - (char *)p); 417 } else if ((sp < ml) && (ep >= ml)) { 418 p->size -= (ep - ml); 419 } 420 } 421 return 0; 422 } 423 424 int 425 mem_delete(long long sa, long long ea) 426 { 427 register bios_memmap_t *p; 428 429 for (p = bios_memmap; p->type != BIOS_MAP_END; p++) { 430 if (p->type == BIOS_MAP_FREE) { 431 register int64_t sp = p->addr, ep = p->addr + p->size; 432 433 /* can we eat it as a whole? */ 434 if ((sa - sp) <= PAGE_SIZE && (ep - ea) <= PAGE_SIZE) { 435 bcopy(p + 1, p, (char *)bios_memmap + 436 sizeof(bios_memmap) - (char *)p); 437 break; 438 /* eat head or legs */ 439 } else if (sa <= sp && sp < ea) { 440 p->addr = ea; 441 p->size = ep - ea; 442 break; 443 } else if (sa < ep && ep <= ea) { 444 p->size = sa - sp; 445 break; 446 } else if (sp < sa && ea < ep) { 447 /* bite in half */ 448 bcopy(p, p + 1, (char *)bios_memmap + 449 sizeof(bios_memmap) - (char *)p - 450 sizeof(bios_memmap[0])); 451 p[1].addr = ea; 452 p[1].size = ep - ea; 453 p->size = sa - sp; 454 break; 455 } 456 } 457 } 458 return 0; 459 } 460 461 int 462 mem_add(long long sa, long long ea) 463 { 464 register bios_memmap_t *p; 465 466 for (p = bios_memmap; p->type != BIOS_MAP_END; p++) { 467 if (p->type == BIOS_MAP_FREE) { 468 register int64_t sp = p->addr, ep = p->addr + p->size; 469 470 /* is it already there? */ 471 if (sp <= sa && ea <= ep) { 472 break; 473 /* join head or legs */ 474 } else if (sa < sp && sp <= ea) { 475 p->addr = sa; 476 p->size = ep - sa; 477 break; 478 } else if (sa <= ep && ep < ea) { 479 p->size = ea - sp; 480 break; 481 } else if (ea < sp) { 482 /* insert before */ 483 bcopy(p, p + 1, (char *)bios_memmap + 484 sizeof(bios_memmap) - (char *)(p - 1)); 485 p->addr = sa; 486 p->size = ea - sa; 487 break; 488 } 489 } 490 } 491 492 /* meaning add new item at the end of the list */ 493 if (p->type == BIOS_MAP_END) { 494 p[1] = p[0]; 495 p->type = BIOS_MAP_FREE; 496 p->addr = sa; 497 p->size = ea - sa; 498 } 499 500 return 0; 501 } 502 503 void 504 mem_pass(void) 505 { 506 bios_memmap_t *p; 507 508 for (p = bios_memmap; p->type != BIOS_MAP_END; p++) 509 ; 510 addbootarg(BOOTARG_MEMMAP, (p - bios_memmap + 1) * sizeof *bios_memmap, 511 bios_memmap); 512 } 513