1 /* 2 * Copyright (C) 2006 Michael Brown <mbrown (at) fensystems.co.uk>. 3 * 4 * This program is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU General Public License as 6 * published by the Free Software Foundation; either version 2 of the 7 * License, or any later version. 8 * 9 * This program is distributed in the hope that it will be useful, but 10 * WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 * General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program; if not, write to the Free Software 16 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 17 */ 18 19 FILE_LICENCE ( GPL2_OR_LATER ); 20 21 #include <stdint.h> 22 #include <errno.h> 23 #include <realmode.h> 24 #include <bios.h> 25 #include <memsizes.h> 26 #include <gpxe/memmap.h> 27 28 /** 29 * @file 30 * 31 * Memory mapping 32 * 33 */ 34 35 /** Magic value for INT 15,e820 calls */ 36 #define SMAP ( 0x534d4150 ) 37 38 /** An INT 15,e820 memory map entry */ 39 struct e820_entry { 40 /** Start of region */ 41 uint64_t start; 42 /** Length of region */ 43 uint64_t len; 44 /** Type of region */ 45 uint32_t type; 46 /** Extended attributes (optional) */ 47 uint32_t attrs; 48 } __attribute__ (( packed )); 49 50 #define E820_TYPE_RAM 1 /**< Normal memory */ 51 #define E820_TYPE_RESERVED 2 /**< Reserved and unavailable */ 52 #define E820_TYPE_ACPI 3 /**< ACPI reclaim memory */ 53 #define E820_TYPE_NVS 4 /**< ACPI NVS memory */ 54 55 #define E820_ATTR_ENABLED 0x00000001UL 56 #define E820_ATTR_NONVOLATILE 0x00000002UL 57 #define E820_ATTR_UNKNOWN 0xfffffffcUL 58 59 #define E820_MIN_SIZE 20 60 61 /** Buffer for INT 15,e820 calls */ 62 static struct e820_entry __bss16 ( e820buf ); 63 #define e820buf __use_data16 ( e820buf ) 64 65 /** 66 * Get size of extended memory via INT 15,e801 67 * 68 * @ret extmem Extended memory size, in kB, or 0 69 */ 70 static unsigned int extmemsize_e801 ( void ) { 71 uint16_t extmem_1m_to_16m_k, extmem_16m_plus_64k; 72 uint16_t confmem_1m_to_16m_k, confmem_16m_plus_64k; 73 unsigned int flags; 74 unsigned int extmem; 75 76 __asm__ __volatile__ ( REAL_CODE ( "stc\n\t" 77 "int $0x15\n\t" 78 "pushfw\n\t" 79 "popw %w0\n\t" ) 80 : "=r" ( flags ), 81 "=a" ( extmem_1m_to_16m_k ), 82 "=b" ( extmem_16m_plus_64k ), 83 "=c" ( confmem_1m_to_16m_k ), 84 "=d" ( confmem_16m_plus_64k ) 85 : "a" ( 0xe801 ) ); 86 87 if ( flags & CF ) { 88 DBG ( "INT 15,e801 failed with CF set\n" ); 89 return 0; 90 } 91 92 if ( ! ( extmem_1m_to_16m_k | extmem_16m_plus_64k ) ) { 93 DBG ( "INT 15,e801 extmem=0, using confmem\n" ); 94 extmem_1m_to_16m_k = confmem_1m_to_16m_k; 95 extmem_16m_plus_64k = confmem_16m_plus_64k; 96 } 97 98 extmem = ( extmem_1m_to_16m_k + ( extmem_16m_plus_64k * 64 ) ); 99 DBG ( "INT 15,e801 extended memory size %d+64*%d=%d kB " 100 "[100000,%llx)\n", extmem_1m_to_16m_k, extmem_16m_plus_64k, 101 extmem, ( 0x100000 + ( ( ( uint64_t ) extmem ) * 1024 ) ) ); 102 103 /* Sanity check. Some BIOSes report the entire 4GB address 104 * space as available, which cannot be correct (since that 105 * would leave no address space available for 32-bit PCI 106 * BARs). 107 */ 108 if ( extmem == ( 0x400000 - 0x400 ) ) { 109 DBG ( "INT 15,e801 reported whole 4GB; assuming insane\n" ); 110 return 0; 111 } 112 113 return extmem; 114 } 115 116 /** 117 * Get size of extended memory via INT 15,88 118 * 119 * @ret extmem Extended memory size, in kB 120 */ 121 static unsigned int extmemsize_88 ( void ) { 122 uint16_t extmem; 123 124 /* Ignore CF; it is not reliable for this call */ 125 __asm__ __volatile__ ( REAL_CODE ( "int $0x15" ) 126 : "=a" ( extmem ) : "a" ( 0x8800 ) ); 127 128 DBG ( "INT 15,88 extended memory size %d kB [100000, %x)\n", 129 extmem, ( 0x100000 + ( extmem * 1024 ) ) ); 130 return extmem; 131 } 132 133 /** 134 * Get size of extended memory 135 * 136 * @ret extmem Extended memory size, in kB 137 * 138 * Note that this is only an approximation; for an accurate picture, 139 * use the E820 memory map obtained via get_memmap(); 140 */ 141 unsigned int extmemsize ( void ) { 142 unsigned int extmem; 143 144 /* Try INT 15,e801 first, then fall back to INT 15,88 */ 145 extmem = extmemsize_e801(); 146 if ( ! extmem ) 147 extmem = extmemsize_88(); 148 return extmem; 149 } 150 151 /** 152 * Get e820 memory map 153 * 154 * @v memmap Memory map to fill in 155 * @ret rc Return status code 156 */ 157 static int meme820 ( struct memory_map *memmap ) { 158 struct memory_region *region = memmap->regions; 159 uint32_t next = 0; 160 uint32_t smap; 161 size_t size; 162 unsigned int flags; 163 unsigned int discard_D; 164 165 /* Clear the E820 buffer. Do this once before starting, 166 * rather than on each call; some BIOSes rely on the contents 167 * being preserved between calls. 168 */ 169 memset ( &e820buf, 0, sizeof ( e820buf ) ); 170 171 do { 172 /* Some BIOSes corrupt %esi for fun. Guard against 173 * this by telling gcc that all non-output registers 174 * may be corrupted. 175 */ 176 __asm__ __volatile__ ( REAL_CODE ( "pushl %%ebp\n\t" 177 "stc\n\t" 178 "int $0x15\n\t" 179 "pushfw\n\t" 180 "popw %%dx\n\t" 181 "popl %%ebp\n\t" ) 182 : "=a" ( smap ), "=b" ( next ), 183 "=c" ( size ), "=d" ( flags ), 184 "=D" ( discard_D ) 185 : "a" ( 0xe820 ), "b" ( next ), 186 "D" ( __from_data16 ( &e820buf ) ), 187 "c" ( sizeof ( e820buf ) ), 188 "d" ( SMAP ) 189 : "esi", "memory" ); 190 191 if ( smap != SMAP ) { 192 DBG ( "INT 15,e820 failed SMAP signature check\n" ); 193 return -ENOTSUP; 194 } 195 196 if ( size < E820_MIN_SIZE ) { 197 DBG ( "INT 15,e820 returned only %zd bytes\n", size ); 198 return -EINVAL; 199 } 200 201 if ( flags & CF ) { 202 DBG ( "INT 15,e820 terminated on CF set\n" ); 203 break; 204 } 205 206 /* If first region is not RAM, assume map is invalid */ 207 if ( ( memmap->count == 0 ) && 208 ( e820buf.type != E820_TYPE_RAM ) ) { 209 DBG ( "INT 15,e820 failed, first entry not RAM\n" ); 210 return -EINVAL; 211 } 212 213 DBG ( "INT 15,e820 region [%llx,%llx) type %d", 214 e820buf.start, ( e820buf.start + e820buf.len ), 215 ( int ) e820buf.type ); 216 if ( size > offsetof ( typeof ( e820buf ), attrs ) ) { 217 DBG ( " (%s", ( ( e820buf.attrs & E820_ATTR_ENABLED ) 218 ? "enabled" : "disabled" ) ); 219 if ( e820buf.attrs & E820_ATTR_NONVOLATILE ) 220 DBG ( ", non-volatile" ); 221 if ( e820buf.attrs & E820_ATTR_UNKNOWN ) 222 DBG ( ", other [%08x]", e820buf.attrs ); 223 DBG ( ")" ); 224 } 225 DBG ( "\n" ); 226 227 /* Discard non-RAM regions */ 228 if ( e820buf.type != E820_TYPE_RAM ) 229 continue; 230 231 /* Check extended attributes, if present */ 232 if ( size > offsetof ( typeof ( e820buf ), attrs ) ) { 233 if ( ! ( e820buf.attrs & E820_ATTR_ENABLED ) ) 234 continue; 235 if ( e820buf.attrs & E820_ATTR_NONVOLATILE ) 236 continue; 237 } 238 239 region->start = e820buf.start; 240 region->end = e820buf.start + e820buf.len; 241 region++; 242 memmap->count++; 243 244 if ( memmap->count >= ( sizeof ( memmap->regions ) / 245 sizeof ( memmap->regions[0] ) ) ) { 246 DBG ( "INT 15,e820 too many regions returned\n" ); 247 /* Not a fatal error; what we've got so far at 248 * least represents valid regions of memory, 249 * even if we couldn't get them all. 250 */ 251 break; 252 } 253 } while ( next != 0 ); 254 255 /* Sanity checks. Some BIOSes report complete garbage via INT 256 * 15,e820 (especially at POST time), despite passing the 257 * signature checks. We currently check for a base memory 258 * region (starting at 0) and at least one high memory region 259 * (starting at 0x100000). 260 */ 261 if ( memmap->count < 2 ) { 262 DBG ( "INT 15,e820 returned only %d regions; assuming " 263 "insane\n", memmap->count ); 264 return -EINVAL; 265 } 266 if ( memmap->regions[0].start != 0 ) { 267 DBG ( "INT 15,e820 region 0 starts at %llx (expected 0); " 268 "assuming insane\n", memmap->regions[0].start ); 269 return -EINVAL; 270 } 271 if ( memmap->regions[1].start != 0x100000 ) { 272 DBG ( "INT 15,e820 region 1 starts at %llx (expected 100000); " 273 "assuming insane\n", memmap->regions[0].start ); 274 return -EINVAL; 275 } 276 277 return 0; 278 } 279 280 /** 281 * Get memory map 282 * 283 * @v memmap Memory map to fill in 284 */ 285 void get_memmap ( struct memory_map *memmap ) { 286 unsigned int basemem, extmem; 287 int rc; 288 289 DBG ( "Fetching system memory map\n" ); 290 291 /* Clear memory map */ 292 memset ( memmap, 0, sizeof ( *memmap ) ); 293 294 /* Get base and extended memory sizes */ 295 basemem = basememsize(); 296 DBG ( "FBMS base memory size %d kB [0,%x)\n", 297 basemem, ( basemem * 1024 ) ); 298 extmem = extmemsize(); 299 300 /* Try INT 15,e820 first */ 301 if ( ( rc = meme820 ( memmap ) ) == 0 ) { 302 DBG ( "Obtained system memory map via INT 15,e820\n" ); 303 return; 304 } 305 306 /* Fall back to constructing a map from basemem and extmem sizes */ 307 DBG ( "INT 15,e820 failed; constructing map\n" ); 308 memmap->regions[0].end = ( basemem * 1024 ); 309 memmap->regions[1].start = 0x100000; 310 memmap->regions[1].end = 0x100000 + ( extmem * 1024 ); 311 memmap->count = 2; 312 } 313