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