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