iPXE
memmap.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2006 Michael Brown <mbrown@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., 51 Franklin Street, Fifth Floor, Boston, MA
17  * 02110-1301, USA.
18  *
19  * You can also choose to distribute this program under the terms of
20  * the Unmodified Binary Distribution Licence (as given in the file
21  * COPYING.UBDL), provided that you have satisfied its requirements.
22  */
23 
24 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25 
26 #include <stdint.h>
27 #include <errno.h>
28 #include <realmode.h>
29 #include <bios.h>
30 #include <memsizes.h>
31 #include <ipxe/io.h>
32 
33 /**
34  * @file
35  *
36  * Memory mapping
37  *
38  */
39 
40 /** Magic value for INT 15,e820 calls */
41 #define SMAP ( 0x534d4150 )
42 
43 /** An INT 15,e820 memory map entry */
44 struct e820_entry {
45  /** Start of region */
47  /** Length of region */
48  uint64_t len;
49  /** Type of region */
50  uint32_t type;
51  /** Extended attributes (optional) */
53 } __attribute__ (( packed ));
54 
55 #define E820_TYPE_RAM 1 /**< Normal memory */
56 #define E820_TYPE_RESERVED 2 /**< Reserved and unavailable */
57 #define E820_TYPE_ACPI 3 /**< ACPI reclaim memory */
58 #define E820_TYPE_NVS 4 /**< ACPI NVS memory */
59 
60 #define E820_ATTR_ENABLED 0x00000001UL
61 #define E820_ATTR_NONVOLATILE 0x00000002UL
62 #define E820_ATTR_UNKNOWN 0xfffffffcUL
63 
64 #define E820_MIN_SIZE 20
65 
66 /** Buffer for INT 15,e820 calls */
67 static struct e820_entry __bss16 ( e820buf );
68 #define e820buf __use_data16 ( e820buf )
69 
70 /** We are running during POST; inhibit INT 15,e820 and INT 15,e801 */
72 #define memmap_post __use_data16 ( memmap_post )
73 
74 /**
75  * Get size of extended memory via INT 15,e801
76  *
77  * @ret extmem Extended memory size, in kB, or 0
78  */
79 static unsigned int extmemsize_e801 ( void ) {
80  uint16_t extmem_1m_to_16m_k, extmem_16m_plus_64k;
81  uint16_t confmem_1m_to_16m_k, confmem_16m_plus_64k;
82  unsigned int flags;
83  unsigned int extmem;
84 
85  /* Inhibit INT 15,e801 during POST */
86  if ( memmap_post ) {
87  DBG ( "INT 15,e801 not available during POST\n" );
88  return 0;
89  }
90 
91  __asm__ __volatile__ ( REAL_CODE ( "stc\n\t"
92  "int $0x15\n\t"
93  "pushfw\n\t"
94  "popw %w0\n\t" )
95  : "=R" ( flags ),
96  "=a" ( extmem_1m_to_16m_k ),
97  "=b" ( extmem_16m_plus_64k ),
98  "=c" ( confmem_1m_to_16m_k ),
99  "=d" ( confmem_16m_plus_64k )
100  : "a" ( 0xe801 ) );
101 
102  if ( flags & CF ) {
103  DBG ( "INT 15,e801 failed with CF set\n" );
104  return 0;
105  }
106 
107  if ( ! ( extmem_1m_to_16m_k | extmem_16m_plus_64k ) ) {
108  DBG ( "INT 15,e801 extmem=0, using confmem\n" );
109  extmem_1m_to_16m_k = confmem_1m_to_16m_k;
110  extmem_16m_plus_64k = confmem_16m_plus_64k;
111  }
112 
113  extmem = ( extmem_1m_to_16m_k + ( extmem_16m_plus_64k * 64 ) );
114  DBG ( "INT 15,e801 extended memory size %d+64*%d=%d kB "
115  "[100000,%llx)\n", extmem_1m_to_16m_k, extmem_16m_plus_64k,
116  extmem, ( 0x100000 + ( ( ( uint64_t ) extmem ) * 1024 ) ) );
117 
118  /* Sanity check. Some BIOSes report the entire 4GB address
119  * space as available, which cannot be correct (since that
120  * would leave no address space available for 32-bit PCI
121  * BARs).
122  */
123  if ( extmem == ( 0x400000 - 0x400 ) ) {
124  DBG ( "INT 15,e801 reported whole 4GB; assuming insane\n" );
125  return 0;
126  }
127 
128  return extmem;
129 }
130 
131 /**
132  * Get size of extended memory via INT 15,88
133  *
134  * @ret extmem Extended memory size, in kB
135  */
136 static unsigned int extmemsize_88 ( void ) {
137  uint16_t extmem;
138 
139  /* Ignore CF; it is not reliable for this call */
140  __asm__ __volatile__ ( REAL_CODE ( "int $0x15" )
141  : "=a" ( extmem ) : "a" ( 0x8800 ) );
142 
143  DBG ( "INT 15,88 extended memory size %d kB [100000, %x)\n",
144  extmem, ( 0x100000 + ( extmem * 1024 ) ) );
145  return extmem;
146 }
147 
148 /**
149  * Get size of extended memory
150  *
151  * @ret extmem Extended memory size, in kB
152  *
153  * Note that this is only an approximation; for an accurate picture,
154  * use the E820 memory map obtained via get_memmap();
155  */
156 unsigned int extmemsize ( void ) {
157  unsigned int extmem_e801;
158  unsigned int extmem_88;
159 
160  /* Try INT 15,e801 first, then fall back to INT 15,88 */
161  extmem_88 = extmemsize_88();
162  extmem_e801 = extmemsize_e801();
163  return ( extmem_e801 ? extmem_e801 : extmem_88 );
164 }
165 
166 /**
167  * Get e820 memory map
168  *
169  * @v memmap Memory map to fill in
170  * @ret rc Return status code
171  */
172 static int meme820 ( struct memory_map *memmap ) {
173  struct memory_region *region = memmap->regions;
174  struct memory_region *prev_region = NULL;
175  uint32_t next = 0;
176  uint32_t smap;
177  uint32_t size;
178  unsigned int flags;
179  unsigned int discard_D;
180 
181  /* Inhibit INT 15,e820 during POST */
182  if ( memmap_post ) {
183  DBG ( "INT 15,e820 not available during POST\n" );
184  return -ENOTTY;
185  }
186 
187  /* Clear the E820 buffer. Do this once before starting,
188  * rather than on each call; some BIOSes rely on the contents
189  * being preserved between calls.
190  */
191  memset ( &e820buf, 0, sizeof ( e820buf ) );
192 
193  do {
194  /* Some BIOSes corrupt %esi for fun. Guard against
195  * this by telling gcc that all non-output registers
196  * may be corrupted.
197  */
198  __asm__ __volatile__ ( REAL_CODE ( "pushl %%ebp\n\t"
199  "stc\n\t"
200  "int $0x15\n\t"
201  "pushfw\n\t"
202  "popw %%dx\n\t"
203  "popl %%ebp\n\t" )
204  : "=a" ( smap ), "=b" ( next ),
205  "=c" ( size ), "=d" ( flags ),
206  "=D" ( discard_D )
207  : "a" ( 0xe820 ), "b" ( next ),
208  "D" ( __from_data16 ( &e820buf ) ),
209  "c" ( sizeof ( e820buf ) ),
210  "d" ( SMAP )
211  : "esi", "memory" );
212 
213  if ( smap != SMAP ) {
214  DBG ( "INT 15,e820 failed SMAP signature check\n" );
215  return -ENOTSUP;
216  }
217 
218  if ( size < E820_MIN_SIZE ) {
219  DBG ( "INT 15,e820 returned only %d bytes\n", size );
220  return -EINVAL;
221  }
222 
223  if ( flags & CF ) {
224  DBG ( "INT 15,e820 terminated on CF set\n" );
225  break;
226  }
227 
228  /* If first region is not RAM, assume map is invalid */
229  if ( ( memmap->count == 0 ) &&
230  ( e820buf.type != E820_TYPE_RAM ) ) {
231  DBG ( "INT 15,e820 failed, first entry not RAM\n" );
232  return -EINVAL;
233  }
234 
235  DBG ( "INT 15,e820 region [%llx,%llx) type %d",
236  e820buf.start, ( e820buf.start + e820buf.len ),
237  ( int ) e820buf.type );
238  if ( size > offsetof ( typeof ( e820buf ), attrs ) ) {
239  DBG ( " (%s", ( ( e820buf.attrs & E820_ATTR_ENABLED )
240  ? "enabled" : "disabled" ) );
241  if ( e820buf.attrs & E820_ATTR_NONVOLATILE )
242  DBG ( ", non-volatile" );
243  if ( e820buf.attrs & E820_ATTR_UNKNOWN )
244  DBG ( ", other [%08x]", e820buf.attrs );
245  DBG ( ")" );
246  }
247  DBG ( "\n" );
248 
249  /* Discard non-RAM regions */
250  if ( e820buf.type != E820_TYPE_RAM )
251  continue;
252 
253  /* Check extended attributes, if present */
254  if ( size > offsetof ( typeof ( e820buf ), attrs ) ) {
255  if ( ! ( e820buf.attrs & E820_ATTR_ENABLED ) )
256  continue;
257  if ( e820buf.attrs & E820_ATTR_NONVOLATILE )
258  continue;
259  }
260 
261  region->start = e820buf.start;
262  region->end = e820buf.start + e820buf.len;
263 
264  /* Check for adjacent regions and merge them */
265  if ( prev_region && ( region->start == prev_region->end ) ) {
266  prev_region->end = region->end;
267  } else {
268  prev_region = region;
269  region++;
270  memmap->count++;
271  }
272 
273  if ( memmap->count >= ( sizeof ( memmap->regions ) /
274  sizeof ( memmap->regions[0] ) ) ) {
275  DBG ( "INT 15,e820 too many regions returned\n" );
276  /* Not a fatal error; what we've got so far at
277  * least represents valid regions of memory,
278  * even if we couldn't get them all.
279  */
280  break;
281  }
282  } while ( next != 0 );
283 
284  /* Sanity checks. Some BIOSes report complete garbage via INT
285  * 15,e820 (especially at POST time), despite passing the
286  * signature checks. We currently check for a base memory
287  * region (starting at 0) and at least one high memory region
288  * (starting at 0x100000).
289  */
290  if ( memmap->count < 2 ) {
291  DBG ( "INT 15,e820 returned only %d regions; assuming "
292  "insane\n", memmap->count );
293  return -EINVAL;
294  }
295  if ( memmap->regions[0].start != 0 ) {
296  DBG ( "INT 15,e820 region 0 starts at %llx (expected 0); "
297  "assuming insane\n", memmap->regions[0].start );
298  return -EINVAL;
299  }
300  if ( memmap->regions[1].start != 0x100000 ) {
301  DBG ( "INT 15,e820 region 1 starts at %llx (expected 100000); "
302  "assuming insane\n", memmap->regions[0].start );
303  return -EINVAL;
304  }
305 
306  return 0;
307 }
308 
309 /**
310  * Get memory map
311  *
312  * @v memmap Memory map to fill in
313  */
314 void x86_get_memmap ( struct memory_map *memmap ) {
315  unsigned int basemem, extmem;
316  int rc;
317 
318  DBG ( "Fetching system memory map\n" );
319 
320  /* Clear memory map */
321  memset ( memmap, 0, sizeof ( *memmap ) );
322 
323  /* Get base and extended memory sizes */
324  basemem = basememsize();
325  DBG ( "FBMS base memory size %d kB [0,%x)\n",
326  basemem, ( basemem * 1024 ) );
327  extmem = extmemsize();
328 
329  /* Try INT 15,e820 first */
330  if ( ( rc = meme820 ( memmap ) ) == 0 ) {
331  DBG ( "Obtained system memory map via INT 15,e820\n" );
332  return;
333  }
334 
335  /* Fall back to constructing a map from basemem and extmem sizes */
336  DBG ( "INT 15,e820 failed; constructing map\n" );
337  memmap->regions[0].end = ( basemem * 1024 );
338  memmap->regions[1].start = 0x100000;
339  memmap->regions[1].end = 0x100000 + ( extmem * 1024 );
340  memmap->count = 2;
341 }
342 
#define EINVAL
Invalid argument.
Definition: errno.h:428
iPXE I/O API
struct arbelprm_rc_send_wqe rc
Definition: arbel.h:14
unsigned short uint16_t
Definition: stdint.h:11
void get_memmap(struct memory_map *memmap)
Get memory map.
#define CF
Definition: registers.h:181
uint32_t attrs
Extended attributes (optional)
Definition: memmap.c:52
#define E820_ATTR_ENABLED
Definition: memmap.c:60
PROVIDE_IOAPI(x86, get_memmap, x86_get_memmap)
uint32_t next
Next descriptor address.
Definition: myson.h:18
unsigned int count
Number of used regions.
Definition: io.h:503
Error codes.
static unsigned int basememsize(void)
Get size of base memory from BIOS free base memory counter.
Definition: memsizes.h:13
#define E820_ATTR_NONVOLATILE
Definition: memmap.c:61
FILE_LICENCE(GPL2_OR_LATER_OR_UBDL)
unsigned long long uint64_t
Definition: stdint.h:13
#define offsetof(type, field)
Get offset of a field within a structure.
Definition: stddef.h:24
A memory map.
Definition: io.h:499
#define E820_ATTR_UNKNOWN
Definition: memmap.c:62
#define memmap_post
Definition: memmap.c:72
#define ENOTSUP
Operation not supported.
Definition: errno.h:589
struct memory_region regions[MAX_MEMORY_REGIONS]
Memory regions.
Definition: io.h:501
uint64_t start
Start of region.
Definition: fakee820.c:38
#define E820_TYPE_RAM
Normal memory.
Definition: memmap.c:55
void x86_get_memmap(struct memory_map *memmap)
Get memory map.
Definition: memmap.c:314
unsigned int extmemsize(void)
Get size of extended memory.
Definition: memmap.c:156
An INT 15,e820 memory map entry.
Definition: fakee820.c:36
struct e820_entry __attribute__((packed))
uint64_t len
Length of region.
Definition: fakee820.c:40
uint32_t attrs
Extended attributes (optional)
Definition: memmap.c:32
unsigned char uint8_t
Definition: stdint.h:10
A usable memory region.
Definition: io.h:488
void * discard_D
Definition: bigint.h:29
unsigned int uint32_t
Definition: stdint.h:12
static struct e820_entry __bss16(e820buf)
Buffer for INT 15,e820 calls.
__asm__ __volatile__("\n1:\n\t" "movb -1(%2,%1), %%al\n\t" "stosb\n\t" "loop 1b\n\t" "xorl %%eax, %%eax\n\t" "mov %3, %1\n\t" "rep stosb\n\t" :"=&D"(discard_D), "=&c"(discard_c) :"r"(data), "g"(pad_len), "0"(value0), "1"(len) :"eax")
uint32_t type
Type of region.
Definition: fakee820.c:42
static unsigned int extmemsize_88(void)
Get size of extended memory via INT 15,88.
Definition: memmap.c:136
__asm__(".section \".rodata\", \"a\", " PROGBITS "\n\t" "\nprivate_key_data:\n\t" ".size private_key_data, ( . - private_key_data )\n\t" ".equ private_key_len, ( . - private_key_data )\n\t" ".previous\n\t")
#define ENOTTY
Inappropriate I/O control operation.
Definition: errno.h:594
uint64_t start
Physical start address.
Definition: io.h:490
#define __from_data16(pointer)
Definition: libkir.h:22
uint8_t size
Entry size (in 32-bit words)
Definition: ena.h:16
#define DBG(...)
Print a debugging message.
Definition: compiler.h:498
#define e820buf
Definition: memmap.c:68
uint64_t end
Physical end address.
Definition: io.h:492
#define REAL_CODE(asm_code_str)
Definition: libkir.h:226
#define SMAP
Magic value for INT 15,e820 calls.
Definition: memmap.c:41
#define NULL
NULL pointer (VOID *)
Definition: Base.h:362
static int meme820(struct memory_map *memmap)
Get e820 memory map.
Definition: memmap.c:172
#define E820_MIN_SIZE
Definition: memmap.c:64
void * memset(void *dest, int character, size_t len) __nonnull
uint8_t flags
Flags.
Definition: ena.h:18
static unsigned int extmemsize_e801(void)
Get size of extended memory via INT 15,e801.
Definition: memmap.c:79