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