1 /*
2  * HVM e820 support.
3  *
4  * Leendert van Doorn, leendert@watson.ibm.com
5  * Copyright (c) 2005, International Business Machines Corporation.
6  * Copyright (c) 2006, Keir Fraser, XenSource Inc.
7  * Copyright (c) 2011, Citrix Inc.
8  *
9  * This program is free software; you can redistribute it and/or modify it
10  * under the terms and conditions of the GNU General Public License,
11  * version 2, as published by the Free Software Foundation.
12  *
13  * This program is distributed in the hope it will be useful, but WITHOUT
14  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
16  * more details.
17  *
18  * You should have received a copy of the GNU General Public License along with
19  * this program; If not, see <http://www.gnu.org/licenses/>.
20  */
21 
22 #include "config.h"
23 #include "util.h"
24 
25 struct e820map memory_map;
26 
memory_map_setup(void)27 void memory_map_setup(void)
28 {
29     unsigned int nr_entries = E820MAX, i;
30     int rc;
31     uint64_t alloc_addr = RESERVED_MEMORY_DYNAMIC_START;
32     uint64_t alloc_size = RESERVED_MEMORY_DYNAMIC_END - alloc_addr;
33 
34     rc = get_mem_mapping_layout(memory_map.map, &nr_entries);
35 
36     if ( rc || !nr_entries )
37     {
38         printf("Get guest memory maps[%d] failed. (%d)\n", nr_entries, rc);
39         BUG();
40     }
41 
42     memory_map.nr_map = nr_entries;
43 
44     for ( i = 0; i < nr_entries; i++ )
45     {
46         if ( memory_map.map[i].type == E820_RESERVED &&
47              check_overlap(alloc_addr, alloc_size,
48                            memory_map.map[i].addr, memory_map.map[i].size) )
49         {
50             printf("Fail to setup memory map due to conflict");
51             printf(" on dynamic reserved memory range.\n");
52             BUG();
53         }
54     }
55 }
56 
57 /*
58  * Sometimes hvmloader may have relocated RAM so low_mem_pgend/high_mem_end
59  * would be changed over there. But memory_map[] just records the
60  * original low/high memory, so we need to sync these entries once
61  * hvmloader modifies low/high memory.
62  */
adjust_memory_map(void)63 void adjust_memory_map(void)
64 {
65     uint32_t low_mem_end = hvm_info->low_mem_pgend << PAGE_SHIFT;
66     uint64_t high_mem_end = (uint64_t)hvm_info->high_mem_pgend << PAGE_SHIFT;
67     unsigned int i;
68 
69     for ( i = 0; i < memory_map.nr_map; i++ )
70     {
71         uint64_t map_start = memory_map.map[i].addr;
72         uint64_t map_size = memory_map.map[i].size;
73         uint64_t map_end = map_start + map_size;
74 
75         /* If we need to adjust lowmem. */
76         if ( memory_map.map[i].type == E820_RAM &&
77              low_mem_end > map_start && low_mem_end < map_end )
78         {
79             memory_map.map[i].size = low_mem_end - map_start;
80             continue;
81         }
82 
83         /* Modify the existing highmem region if it exists. */
84         if ( memory_map.map[i].type == E820_RAM &&
85              high_mem_end && map_start == GB(4) )
86         {
87             if ( high_mem_end != map_end )
88                 memory_map.map[i].size = high_mem_end - map_start;
89             high_mem_end = 0;
90             continue;
91         }
92     }
93 
94     /* If there was no highmem region, just create one. */
95     if ( high_mem_end )
96     {
97         memory_map.map[i].addr = GB(4);
98         memory_map.map[i].size =
99                 ((uint64_t)hvm_info->high_mem_pgend << PAGE_SHIFT) -
100                     memory_map.map[i].addr;
101         memory_map.map[i].type = E820_RAM;
102         memory_map.nr_map++;
103     }
104 }
105 
dump_e820_table(struct e820entry * e820,unsigned int nr)106 void dump_e820_table(struct e820entry *e820, unsigned int nr)
107 {
108     uint64_t last_end = 0, start, end;
109     int i;
110 
111     printf("E820 table:\n");
112 
113     for ( i = 0; i < nr; i++ )
114     {
115         start = e820[i].addr;
116         end = e820[i].addr + e820[i].size;
117 
118         if ( start < last_end )
119             printf(" OVERLAP!!\n");
120         else if ( start > last_end )
121             printf(" HOLE: %08x:%08x - %08x:%08x\n",
122                    (uint32_t)(last_end >> 32), (uint32_t)last_end,
123                    (uint32_t)(start >> 32), (uint32_t)start);
124 
125         printf(" [%02d]: %08x:%08x - %08x:%08x: ", i,
126                (uint32_t)(start >> 32), (uint32_t)start,
127                (uint32_t)(end >> 32), (uint32_t)end);
128         switch ( e820[i].type )
129         {
130         case E820_RAM:
131             printf("RAM\n");
132             break;
133         case E820_RESERVED:
134             printf("RESERVED\n");
135             break;
136         case E820_ACPI:
137             printf("ACPI\n");
138             break;
139         case E820_NVS:
140             printf("NVS\n");
141             break;
142         default:
143             printf("UNKNOWN (%08x)\n", e820[i].type);
144             break;
145         }
146 
147         last_end = end;
148     }
149 }
150 
151 /* Create an E820 table based on memory parameters provided in hvm_info. */
build_e820_table(struct e820entry * e820,unsigned int lowmem_reserved_base,unsigned int bios_image_base)152 int build_e820_table(struct e820entry *e820,
153                      unsigned int lowmem_reserved_base,
154                      unsigned int bios_image_base)
155 {
156     unsigned int nr = 0, i, j;
157     uint32_t low_mem_end = hvm_info->low_mem_pgend << PAGE_SHIFT;
158     unsigned long acpi_mem_end = acpi_enabled ?
159         ACPI_MEMORY_DYNAMIC_START + (acpi_pages_allocated() << PAGE_SHIFT) :
160         RESERVED_MEMBASE;
161 
162     if ( !lowmem_reserved_base )
163             lowmem_reserved_base = 0xA0000;
164 
165     /* Lowmem must be at least 512K to keep Windows happy) */
166     ASSERT ( lowmem_reserved_base > 512<<10 );
167 
168     ASSERT ( bios_image_base < 0x100000 );
169 
170     /*
171      * 0x0-lowmem_reserved_base: Ordinary RAM.
172      */
173     e820[nr].addr = 0x00000;
174     e820[nr].size = lowmem_reserved_base;
175     e820[nr].type = E820_RAM;
176     nr++;
177 
178     /* lowmem_reserved_base-0xA0000: reserved by BIOS implementation. */
179     if ( lowmem_reserved_base < 0xA0000 )
180     {
181         /* Reserved for internal use. */
182         e820[nr].addr = lowmem_reserved_base;
183         e820[nr].size = 0xA0000-lowmem_reserved_base;
184         e820[nr].type = E820_RESERVED;
185         nr++;
186     }
187 
188     /*
189      * Following regions are standard regions of the PC memory map.
190      * They are not covered by e820 regions. OSes will not use as RAM.
191      * 0xA0000-0xC0000: VGA memory-mapped I/O. Not covered by E820.
192      * 0xC0000-0xE0000: 16-bit devices, expansion ROMs (inc. vgabios).
193      * TODO: free pages which turn out to be unused.
194      */
195 
196     /*
197      * BIOS region.
198      */
199     e820[nr].addr = bios_image_base;
200     e820[nr].size = 0x100000-bios_image_base;
201     e820[nr].type = E820_RESERVED;
202     nr++;
203 
204     /*
205      * Mark populated reserved memory that contains ACPI tables as ACPI NVS.
206      * That should help the guest to treat it correctly later: e.g. pass to
207      * the next kernel on kexec.
208      *
209      * Using NVS type instead of a regular one helps to prevent potential
210      * space reuse by an ACPI unaware / buggy bootloader, option ROM, etc.
211      * before an ACPI OS takes control. This is possible due to the fact that
212      * ACPI NVS memory is explicitly described as non-reclaimable in ACPI spec.
213      *
214      * Furthermore, Xen relies on accessing ACPI tables from within the AML
215      * code exposed to guests. So Xen's ACPI tables are not, in general,
216      * reclaimable.
217      */
218 
219     if ( acpi_enabled )
220     {
221         e820[nr].addr = RESERVED_MEMBASE;
222         e820[nr].size = acpi_mem_end - RESERVED_MEMBASE;
223         e820[nr].type = E820_NVS;
224         nr++;
225     }
226 
227     /*
228      * Explicitly reserve space for special pages.
229      * This space starts right after ACPI region (to avoid creating a hole that
230      * might be accidentally occupied by MMIO) and extends to cover various
231      * fixed hardware mappings (e.g., LAPIC, IOAPIC, default SVGA framebuffer).
232      *
233      * If igd_opregion_pgbase we need to split the RESERVED region in two.
234      */
235 
236     if ( igd_opregion_pgbase )
237     {
238         uint32_t igd_opregion_base = igd_opregion_pgbase << PAGE_SHIFT;
239 
240         e820[nr].addr = acpi_mem_end;
241         e820[nr].size = igd_opregion_base - acpi_mem_end;
242         e820[nr].type = E820_RESERVED;
243         nr++;
244 
245         e820[nr].addr = igd_opregion_base;
246         e820[nr].size = IGD_OPREGION_PAGES * PAGE_SIZE;
247         e820[nr].type = E820_NVS;
248         nr++;
249 
250         e820[nr].addr = igd_opregion_base + IGD_OPREGION_PAGES * PAGE_SIZE;
251         e820[nr].size = (uint32_t)-e820[nr].addr;
252         e820[nr].type = E820_RESERVED;
253         nr++;
254     }
255     else
256     {
257         e820[nr].addr = acpi_mem_end;
258         e820[nr].size = (uint32_t)-e820[nr].addr;
259         e820[nr].type = E820_RESERVED;
260         nr++;
261     }
262 
263     /* Low RAM goes here. Reserve space for special pages. */
264     BUG_ON(low_mem_end < MB(2));
265 
266     /*
267      * Construct E820 table according to recorded memory map.
268      *
269      * The memory map created by toolstack may include,
270      *
271      * #1. Low memory region
272      *
273      * Low RAM starts at least from 1M to make sure all standard regions
274      * of the PC memory map, like BIOS, VGA memory-mapped I/O and vgabios,
275      * have enough space.
276      *
277      * #2. Reserved regions if they exist
278      *
279      * #3. High memory region if it exists
280      *
281      * Note we just have one low memory entry and one high mmeory entry if
282      * exists.
283      */
284     for ( i = 0; i < memory_map.nr_map; i++ )
285     {
286         e820[nr] = memory_map.map[i];
287         nr++;
288     }
289 
290     /* Finally we need to sort all e820 entries. */
291     for ( j = 0; j < nr - 1; j++ )
292     {
293         for ( i = j + 1; i < nr; i++ )
294         {
295             if ( e820[j].addr > e820[i].addr )
296             {
297                 struct e820entry tmp = e820[j];
298 
299                 e820[j] = e820[i];
300                 e820[i] = tmp;
301             }
302         }
303     }
304 
305     return nr;
306 }
307 
308 /*
309  * Local variables:
310  * mode: C
311  * c-file-style: "BSD"
312  * c-basic-offset: 4
313  * tab-width: 4
314  * indent-tabs-mode: nil
315  * End:
316  */
317