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 /* 28 * Obtain memory configuration information from the BIOS 29 */ 30 #include <stand.h> 31 #include <machine/pc/bios.h> 32 #include "bootstrap.h" 33 #include "libi386.h" 34 #include "btxv86.h" 35 #include "smbios.h" 36 37 vm_offset_t memtop, memtop_copyin, high_heap_base; 38 uint32_t bios_basemem, bios_extmem, high_heap_size; 39 40 static struct bios_smap_xattr smap; 41 42 /* 43 * Used to track which method was used to set BIOS memory 44 * regions. 45 */ 46 static uint8_t b_bios_probed; 47 #define B_BASEMEM_E820 0x1 48 #define B_BASEMEM_12 0x2 49 #define B_EXTMEM_E820 0x4 50 #define B_EXTMEM_E801 0x8 51 #define B_EXTMEM_8800 0x10 52 53 /* 54 * The minimum amount of memory to reserve in bios_extmem for the heap. 55 */ 56 #define HEAP_MIN (64 * 1024 * 1024) 57 58 /* 59 * Products in this list need quirks to detect 60 * memory correctly. You need both maker and product as 61 * reported by smbios. 62 */ 63 /* e820 might not return useful extended memory */ 64 #define BQ_DISTRUST_E820_EXTMEM 0x1 65 struct bios_getmem_quirks { 66 const char *bios_vendor; 67 const char *maker; 68 const char *product; 69 int quirk; 70 }; 71 72 static struct bios_getmem_quirks quirks[] = { 73 {"coreboot", "Acer", "Peppy", BQ_DISTRUST_E820_EXTMEM}, 74 {"coreboot", "Dell", "Wolf", BQ_DISTRUST_E820_EXTMEM}, 75 {NULL, NULL, NULL, 0} 76 }; 77 78 static int 79 bios_getquirks(void) 80 { 81 int i; 82 83 for (i = 0; quirks[i].quirk != 0; ++i) { 84 if (smbios_match(quirks[i].bios_vendor, quirks[i].maker, 85 quirks[i].product)) 86 return (quirks[i].quirk); 87 } 88 89 return (0); 90 } 91 92 void 93 bios_getmem(void) 94 { 95 uint64_t size; 96 97 /* Parse system memory map */ 98 v86.ebx = 0; 99 do { 100 v86.ctl = V86_FLAGS; 101 v86.addr = 0x15; /* int 0x15 function 0xe820 */ 102 v86.eax = 0xe820; 103 v86.ecx = sizeof(struct bios_smap_xattr); 104 v86.edx = SMAP_SIG; 105 v86.es = VTOPSEG(&smap); 106 v86.edi = VTOPOFF(&smap); 107 v86int(); 108 if ((V86_CY(v86.efl)) || (v86.eax != SMAP_SIG)) 109 break; 110 /* look for a low-memory segment that's large enough */ 111 if ((smap.type == SMAP_TYPE_MEMORY) && (smap.base == 0) && 112 (smap.length >= (512 * 1024))) { 113 bios_basemem = smap.length; 114 b_bios_probed |= B_BASEMEM_E820; 115 } 116 117 /* look for the first segment in 'extended' memory */ 118 if ((smap.type == SMAP_TYPE_MEMORY) && 119 (smap.base == 0x100000) && 120 !(bios_getquirks() & BQ_DISTRUST_E820_EXTMEM)) { 121 bios_extmem = smap.length; 122 b_bios_probed |= B_EXTMEM_E820; 123 } 124 125 /* 126 * Look for the highest segment in 'extended' memory beyond 127 * 1MB but below 4GB. 128 */ 129 if ((smap.type == SMAP_TYPE_MEMORY) && 130 (smap.base > 0x100000) && 131 (smap.base < 0x100000000ull)) { 132 size = smap.length; 133 134 /* 135 * If this segment crosses the 4GB boundary, 136 * truncate it. 137 */ 138 if (smap.base + size > 0x100000000ull) 139 size = 0x100000000ull - smap.base; 140 141 /* 142 * To make maximum space for the kernel and the modules, 143 * set heap to use highest HEAP_MIN bytes below 4GB. 144 */ 145 if (high_heap_base < smap.base && size >= HEAP_MIN) { 146 high_heap_base = smap.base + size - HEAP_MIN; 147 high_heap_size = HEAP_MIN; 148 } 149 } 150 } while (v86.ebx != 0); 151 152 /* Fall back to the old compatibility function for base memory */ 153 if (bios_basemem == 0) { 154 v86.ctl = 0; 155 v86.addr = 0x12; /* int 0x12 */ 156 v86int(); 157 158 bios_basemem = (v86.eax & 0xffff) * 1024; 159 b_bios_probed |= B_BASEMEM_12; 160 } 161 162 /* 163 * Fall back through several compatibility functions for extended 164 * memory. 165 */ 166 if (bios_extmem == 0) { 167 v86.ctl = V86_FLAGS; 168 v86.addr = 0x15; /* int 0x15 function 0xe801 */ 169 v86.eax = 0xe801; 170 v86int(); 171 if (!(V86_CY(v86.efl))) { 172 /* 173 * Clear high_heap; it may end up overlapping 174 * with the segment we're determining here. 175 * Let the default "steal stuff from top of 176 * bios_extmem" code below pick up on it. 177 */ 178 high_heap_size = 0; 179 high_heap_base = 0; 180 181 /* 182 * %cx is the number of 1KiB blocks between 1..16MiB. 183 * It can only be up to 0x3c00; if it's smaller then 184 * there's a PC AT memory hole so we can't treat 185 * it as contiguous. 186 */ 187 bios_extmem = (v86.ecx & 0xffff) * 1024; 188 if (bios_extmem == (1024 * 0x3c00)) 189 bios_extmem += (v86.edx & 0xffff) * 64 * 1024; 190 191 /* truncate bios_extmem */ 192 if (bios_extmem > 0x3ff00000) 193 bios_extmem = 0x3ff00000; 194 195 b_bios_probed |= B_EXTMEM_E801; 196 } 197 } 198 if (bios_extmem == 0) { 199 v86.ctl = 0; 200 v86.addr = 0x15; /* int 0x15 function 0x88 */ 201 v86.eax = 0x8800; 202 v86int(); 203 bios_extmem = (v86.eax & 0xffff) * 1024; 204 b_bios_probed |= B_EXTMEM_8800; 205 } 206 207 /* Set memtop to actual top of memory */ 208 if (high_heap_size != 0) { 209 memtop = memtop_copyin = high_heap_base; 210 } else { 211 memtop = memtop_copyin = 0x100000 + bios_extmem; 212 } 213 214 /* 215 * If we have extended memory and did not find a suitable heap 216 * region in the SMAP, use the last HEAP_MIN of 'extended' memory as a 217 * high heap candidate. 218 */ 219 if (bios_extmem >= HEAP_MIN && high_heap_size < HEAP_MIN) { 220 high_heap_size = HEAP_MIN; 221 high_heap_base = memtop - HEAP_MIN; 222 memtop = memtop_copyin = high_heap_base; 223 } 224 } 225 226 static int 227 command_biosmem(int argc, char *argv[]) 228 { 229 int bq = bios_getquirks(); 230 231 printf("bios_basemem: 0x%llx\n", (unsigned long long)bios_basemem); 232 printf("bios_extmem: 0x%llx\n", (unsigned long long)bios_extmem); 233 printf("memtop: 0x%llx\n", (unsigned long long)memtop); 234 printf("high_heap_base: 0x%llx\n", (unsigned long long)high_heap_base); 235 printf("high_heap_size: 0x%llx\n", (unsigned long long)high_heap_size); 236 printf("bios_quirks: 0x%02x", bq); 237 if (bq & BQ_DISTRUST_E820_EXTMEM) 238 printf(" BQ_DISTRUST_E820_EXTMEM"); 239 printf("\n"); 240 printf("b_bios_probed: 0x%02x", (int)b_bios_probed); 241 if (b_bios_probed & B_BASEMEM_E820) 242 printf(" B_BASEMEM_E820"); 243 if (b_bios_probed & B_BASEMEM_12) 244 printf(" B_BASEMEM_12"); 245 if (b_bios_probed & B_EXTMEM_E820) 246 printf(" B_EXTMEM_E820"); 247 if (b_bios_probed & B_EXTMEM_E801) 248 printf(" B_EXTMEM_E801"); 249 if (b_bios_probed & B_EXTMEM_8800) 250 printf(" B_EXTMEM_8800"); 251 printf("\n"); 252 253 return (CMD_OK); 254 } 255 256 COMMAND_SET(biosmem, "biosmem", "show BIOS memory setup", command_biosmem); 257