1 /*- 2 * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27 #include <sys/cdefs.h> 28 __FBSDID("$FreeBSD$"); 29 30 /* 31 * Obtain memory configuration information from the BIOS 32 */ 33 #include <stand.h> 34 #include <machine/pc/bios.h> 35 #include "bootstrap.h" 36 #include "libi386.h" 37 #include "btxv86.h" 38 #include "smbios.h" 39 40 vm_offset_t memtop, memtop_copyin, high_heap_base; 41 uint32_t bios_basemem, bios_extmem, high_heap_size; 42 43 static struct bios_smap_xattr smap; 44 45 /* 46 * Used to track which method was used to set BIOS memory 47 * regions. 48 */ 49 static uint8_t b_bios_probed; 50 #define B_BASEMEM_E820 0x1 51 #define B_BASEMEM_12 0x2 52 #define B_EXTMEM_E820 0x4 53 #define B_EXTMEM_E801 0x8 54 #define B_EXTMEM_8800 0x10 55 56 /* 57 * The minimum amount of memory to reserve in bios_extmem for the heap. 58 */ 59 #define HEAP_MIN (64 * 1024 * 1024) 60 61 /* 62 * Products in this list need quirks to detect 63 * memory correctly. You need both maker and product as 64 * reported by smbios. 65 */ 66 /* e820 might not return useful extended memory */ 67 #define BQ_DISTRUST_E820_EXTMEM 0x1 68 struct bios_getmem_quirks { 69 const char *bios_vendor; 70 const char *maker; 71 const char *product; 72 int quirk; 73 }; 74 75 static struct bios_getmem_quirks quirks[] = { 76 {"coreboot", "Acer", "Peppy", BQ_DISTRUST_E820_EXTMEM}, 77 {"coreboot", "Dell", "Wolf", BQ_DISTRUST_E820_EXTMEM}, 78 {NULL, NULL, NULL, 0} 79 }; 80 81 static int 82 bios_getquirks(void) 83 { 84 int i; 85 86 for (i = 0; quirks[i].quirk != 0; ++i) { 87 if (smbios_match(quirks[i].bios_vendor, quirks[i].maker, 88 quirks[i].product)) 89 return (quirks[i].quirk); 90 } 91 92 return (0); 93 } 94 95 void 96 bios_getmem(void) 97 { 98 uint64_t size; 99 100 /* Parse system memory map */ 101 v86.ebx = 0; 102 do { 103 v86.ctl = V86_FLAGS; 104 v86.addr = 0x15; /* int 0x15 function 0xe820 */ 105 v86.eax = 0xe820; 106 v86.ecx = sizeof(struct bios_smap_xattr); 107 v86.edx = SMAP_SIG; 108 v86.es = VTOPSEG(&smap); 109 v86.edi = VTOPOFF(&smap); 110 v86int(); 111 if ((V86_CY(v86.efl)) || (v86.eax != SMAP_SIG)) 112 break; 113 /* look for a low-memory segment that's large enough */ 114 if ((smap.type == SMAP_TYPE_MEMORY) && (smap.base == 0) && 115 (smap.length >= (512 * 1024))) { 116 bios_basemem = smap.length; 117 b_bios_probed |= B_BASEMEM_E820; 118 } 119 120 /* look for the first segment in 'extended' memory */ 121 if ((smap.type == SMAP_TYPE_MEMORY) && 122 (smap.base == 0x100000) && 123 !(bios_getquirks() & BQ_DISTRUST_E820_EXTMEM)) { 124 bios_extmem = smap.length; 125 b_bios_probed |= B_EXTMEM_E820; 126 } 127 128 /* 129 * Look for the highest segment in 'extended' memory beyond 130 * 1MB but below 4GB. 131 */ 132 if ((smap.type == SMAP_TYPE_MEMORY) && 133 (smap.base > 0x100000) && 134 (smap.base < 0x100000000ull)) { 135 size = smap.length; 136 137 /* 138 * If this segment crosses the 4GB boundary, 139 * truncate it. 140 */ 141 if (smap.base + size > 0x100000000ull) 142 size = 0x100000000ull - smap.base; 143 144 /* 145 * To make maximum space for the kernel and the modules, 146 * set heap to use highest HEAP_MIN bytes below 4GB. 147 */ 148 if (high_heap_base < smap.base && size >= HEAP_MIN) { 149 high_heap_base = smap.base + size - HEAP_MIN; 150 high_heap_size = HEAP_MIN; 151 } 152 } 153 } while (v86.ebx != 0); 154 155 /* Fall back to the old compatibility function for base memory */ 156 if (bios_basemem == 0) { 157 v86.ctl = 0; 158 v86.addr = 0x12; /* int 0x12 */ 159 v86int(); 160 161 bios_basemem = (v86.eax & 0xffff) * 1024; 162 b_bios_probed |= B_BASEMEM_12; 163 } 164 165 /* 166 * Fall back through several compatibility functions for extended 167 * memory. 168 */ 169 if (bios_extmem == 0) { 170 v86.ctl = V86_FLAGS; 171 v86.addr = 0x15; /* int 0x15 function 0xe801 */ 172 v86.eax = 0xe801; 173 v86int(); 174 if (!(V86_CY(v86.efl))) { 175 /* 176 * Clear high_heap; it may end up overlapping 177 * with the segment we're determining here. 178 * Let the default "steal stuff from top of 179 * bios_extmem" code below pick up on it. 180 */ 181 high_heap_size = 0; 182 high_heap_base = 0; 183 184 /* 185 * %cx is the number of 1KiB blocks between 1..16MiB. 186 * It can only be up to 0x3c00; if it's smaller then 187 * there's a PC AT memory hole so we can't treat 188 * it as contiguous. 189 */ 190 bios_extmem = (v86.ecx & 0xffff) * 1024; 191 if (bios_extmem == (1024 * 0x3c00)) 192 bios_extmem += (v86.edx & 0xffff) * 64 * 1024; 193 194 /* truncate bios_extmem */ 195 if (bios_extmem > 0x3ff00000) 196 bios_extmem = 0x3ff00000; 197 198 b_bios_probed |= B_EXTMEM_E801; 199 } 200 } 201 if (bios_extmem == 0) { 202 v86.ctl = 0; 203 v86.addr = 0x15; /* int 0x15 function 0x88 */ 204 v86.eax = 0x8800; 205 v86int(); 206 bios_extmem = (v86.eax & 0xffff) * 1024; 207 b_bios_probed |= B_EXTMEM_8800; 208 } 209 210 /* Set memtop to actual top of memory */ 211 if (high_heap_size != 0) { 212 memtop = memtop_copyin = high_heap_base; 213 } else { 214 memtop = memtop_copyin = 0x100000 + bios_extmem; 215 } 216 217 /* 218 * If we have extended memory and did not find a suitable heap 219 * region in the SMAP, use the last HEAP_MIN of 'extended' memory as a 220 * high heap candidate. 221 */ 222 if (bios_extmem >= HEAP_MIN && high_heap_size < HEAP_MIN) { 223 high_heap_size = HEAP_MIN; 224 high_heap_base = memtop - HEAP_MIN; 225 memtop = memtop_copyin = high_heap_base; 226 } 227 } 228 229 static int 230 command_biosmem(int argc, char *argv[]) 231 { 232 int bq = bios_getquirks(); 233 234 printf("bios_basemem: 0x%llx\n", (unsigned long long)bios_basemem); 235 printf("bios_extmem: 0x%llx\n", (unsigned long long)bios_extmem); 236 printf("memtop: 0x%llx\n", (unsigned long long)memtop); 237 printf("high_heap_base: 0x%llx\n", (unsigned long long)high_heap_base); 238 printf("high_heap_size: 0x%llx\n", (unsigned long long)high_heap_size); 239 printf("bios_quirks: 0x%02x", bq); 240 if (bq & BQ_DISTRUST_E820_EXTMEM) 241 printf(" BQ_DISTRUST_E820_EXTMEM"); 242 printf("\n"); 243 printf("b_bios_probed: 0x%02x", (int)b_bios_probed); 244 if (b_bios_probed & B_BASEMEM_E820) 245 printf(" B_BASEMEM_E820"); 246 if (b_bios_probed & B_BASEMEM_12) 247 printf(" B_BASEMEM_12"); 248 if (b_bios_probed & B_EXTMEM_E820) 249 printf(" B_EXTMEM_E820"); 250 if (b_bios_probed & B_EXTMEM_E801) 251 printf(" B_EXTMEM_E801"); 252 if (b_bios_probed & B_EXTMEM_8800) 253 printf(" B_EXTMEM_8800"); 254 printf("\n"); 255 256 return (CMD_OK); 257 } 258 259 COMMAND_SET(biosmem, "biosmem", "show BIOS memory setup", command_biosmem); 260