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