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 {NULL, NULL, NULL, 0} 78 }; 79 80 static int 81 bios_getquirks(void) 82 { 83 int i; 84 85 for (i = 0; quirks[i].quirk != 0; ++i) { 86 if (smbios_match(quirks[i].bios_vendor, quirks[i].maker, 87 quirks[i].product)) 88 return (quirks[i].quirk); 89 } 90 91 return (0); 92 } 93 94 void 95 bios_getmem(void) 96 { 97 uint64_t size; 98 99 /* Parse system memory map */ 100 v86.ebx = 0; 101 do { 102 v86.ctl = V86_FLAGS; 103 v86.addr = 0x15; /* int 0x15 function 0xe820 */ 104 v86.eax = 0xe820; 105 v86.ecx = sizeof(struct bios_smap_xattr); 106 v86.edx = SMAP_SIG; 107 v86.es = VTOPSEG(&smap); 108 v86.edi = VTOPOFF(&smap); 109 v86int(); 110 if ((V86_CY(v86.efl)) || (v86.eax != SMAP_SIG)) 111 break; 112 /* look for a low-memory segment that's large enough */ 113 if ((smap.type == SMAP_TYPE_MEMORY) && (smap.base == 0) && 114 (smap.length >= (512 * 1024))) { 115 bios_basemem = smap.length; 116 b_bios_probed |= B_BASEMEM_E820; 117 } 118 119 /* look for the first segment in 'extended' memory */ 120 if ((smap.type == SMAP_TYPE_MEMORY) && 121 (smap.base == 0x100000) && 122 !(bios_getquirks() & BQ_DISTRUST_E820_EXTMEM)) { 123 bios_extmem = smap.length; 124 b_bios_probed |= B_EXTMEM_E820; 125 } 126 127 /* 128 * Look for the highest segment in 'extended' memory beyond 129 * 1MB but below 4GB. 130 */ 131 if ((smap.type == SMAP_TYPE_MEMORY) && 132 (smap.base > 0x100000) && 133 (smap.base < 0x100000000ull)) { 134 size = smap.length; 135 136 /* 137 * If this segment crosses the 4GB boundary, 138 * truncate it. 139 */ 140 if (smap.base + size > 0x100000000ull) 141 size = 0x100000000ull - smap.base; 142 143 /* 144 * To make maximum space for the kernel and the modules, 145 * set heap to use highest HEAP_MIN bytes below 4GB. 146 */ 147 if (high_heap_base < smap.base && size >= HEAP_MIN) { 148 high_heap_base = smap.base + size - HEAP_MIN; 149 high_heap_size = HEAP_MIN; 150 } 151 } 152 } while (v86.ebx != 0); 153 154 /* Fall back to the old compatibility function for base memory */ 155 if (bios_basemem == 0) { 156 v86.ctl = 0; 157 v86.addr = 0x12; /* int 0x12 */ 158 v86int(); 159 160 bios_basemem = (v86.eax & 0xffff) * 1024; 161 b_bios_probed |= B_BASEMEM_12; 162 } 163 164 /* 165 * Fall back through several compatibility functions for extended 166 * memory. 167 */ 168 if (bios_extmem == 0) { 169 v86.ctl = V86_FLAGS; 170 v86.addr = 0x15; /* int 0x15 function 0xe801 */ 171 v86.eax = 0xe801; 172 v86int(); 173 if (!(V86_CY(v86.efl))) { 174 /* 175 * Clear high_heap; it may end up overlapping 176 * with the segment we're determining here. 177 * Let the default "steal stuff from top of 178 * bios_extmem" code below pick up on it. 179 */ 180 high_heap_size = 0; 181 high_heap_base = 0; 182 183 /* 184 * %cx is the number of 1KiB blocks between 1..16MiB. 185 * It can only be up to 0x3c00; if it's smaller then 186 * there's a PC AT memory hole so we can't treat 187 * it as contiguous. 188 */ 189 bios_extmem = (v86.ecx & 0xffff) * 1024; 190 if (bios_extmem == (1024 * 0x3c00)) 191 bios_extmem += (v86.edx & 0xffff) * 64 * 1024; 192 193 /* truncate bios_extmem */ 194 if (bios_extmem > 0x3ff00000) 195 bios_extmem = 0x3ff00000; 196 197 b_bios_probed |= B_EXTMEM_E801; 198 } 199 } 200 if (bios_extmem == 0) { 201 v86.ctl = 0; 202 v86.addr = 0x15; /* int 0x15 function 0x88 */ 203 v86.eax = 0x8800; 204 v86int(); 205 bios_extmem = (v86.eax & 0xffff) * 1024; 206 b_bios_probed |= B_EXTMEM_8800; 207 } 208 209 /* Set memtop to actual top of memory */ 210 if (high_heap_size != 0) { 211 memtop = memtop_copyin = high_heap_base; 212 } else { 213 memtop = memtop_copyin = 0x100000 + bios_extmem; 214 } 215 216 /* 217 * If we have extended memory and did not find a suitable heap 218 * region in the SMAP, use the last HEAP_MIN of 'extended' memory as a 219 * high heap candidate. 220 */ 221 if (bios_extmem >= HEAP_MIN && high_heap_size < HEAP_MIN) { 222 high_heap_size = HEAP_MIN; 223 high_heap_base = memtop - HEAP_MIN; 224 memtop = memtop_copyin = high_heap_base; 225 } 226 } 227 228 static int 229 command_biosmem(int argc, char *argv[]) 230 { 231 int bq = bios_getquirks(); 232 233 printf("bios_basemem: 0x%llx\n", (unsigned long long)bios_basemem); 234 printf("bios_extmem: 0x%llx\n", (unsigned long long)bios_extmem); 235 printf("memtop: 0x%llx\n", (unsigned long long)memtop); 236 printf("high_heap_base: 0x%llx\n", (unsigned long long)high_heap_base); 237 printf("high_heap_size: 0x%llx\n", (unsigned long long)high_heap_size); 238 printf("bios_quirks: 0x%02x", bq); 239 if (bq & BQ_DISTRUST_E820_EXTMEM) 240 printf(" BQ_DISTRUST_E820_EXTMEM"); 241 printf("\n"); 242 printf("b_bios_probed: 0x%02x", (int)b_bios_probed); 243 if (b_bios_probed & B_BASEMEM_E820) 244 printf(" B_BASEMEM_E820"); 245 if (b_bios_probed & B_BASEMEM_12) 246 printf(" B_BASEMEM_12"); 247 if (b_bios_probed & B_EXTMEM_E820) 248 printf(" B_EXTMEM_E820"); 249 if (b_bios_probed & B_EXTMEM_E801) 250 printf(" B_EXTMEM_E801"); 251 if (b_bios_probed & B_EXTMEM_8800) 252 printf(" B_EXTMEM_8800"); 253 printf("\n"); 254 255 return (CMD_OK); 256 } 257 258 COMMAND_SET(biosmem, "biosmem", "show BIOS memory setup", command_biosmem); 259