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