These changes are the raw update to qemu-2.6.
[kvmfornfv.git] / qemu / roms / ipxe / src / arch / i386 / firmware / pcbios / memmap.c
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 */
46         uint64_t start;
47         /** Length of region */
48         uint64_t len;
49         /** Type of region */
50         uint32_t type;
51         /** Extended attributes (optional) */
52         uint32_t attrs;
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 */
71 uint8_t __bss16 ( memmap_post );
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         size_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 %zd 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
343 PROVIDE_IOAPI ( x86, get_memmap, x86_get_memmap );