xref: /illumos-gate/usr/src/cmd/bhyve/e820.c (revision 32640292339b07090f10ce34d455f98711077343)
1*32640292SAndy Fiddaman /*-
2*32640292SAndy Fiddaman  * SPDX-License-Identifier: BSD-2-Clause
3*32640292SAndy Fiddaman  *
4*32640292SAndy Fiddaman  * Copyright (c) 2021 Beckhoff Automation GmbH & Co. KG
5*32640292SAndy Fiddaman  * Author: Corvin Köhne <c.koehne@beckhoff.com>
6*32640292SAndy Fiddaman  */
7*32640292SAndy Fiddaman 
8*32640292SAndy Fiddaman #include <sys/types.h>
9*32640292SAndy Fiddaman #include <sys/queue.h>
10*32640292SAndy Fiddaman 
11*32640292SAndy Fiddaman #include <machine/vmm.h>
12*32640292SAndy Fiddaman 
13*32640292SAndy Fiddaman #include <assert.h>
14*32640292SAndy Fiddaman #include <err.h>
15*32640292SAndy Fiddaman #include <errno.h>
16*32640292SAndy Fiddaman #include <stdio.h>
17*32640292SAndy Fiddaman #include <stdlib.h>
18*32640292SAndy Fiddaman #include <string.h>
19*32640292SAndy Fiddaman 
20*32640292SAndy Fiddaman #include "e820.h"
21*32640292SAndy Fiddaman #include "qemu_fwcfg.h"
22*32640292SAndy Fiddaman 
23*32640292SAndy Fiddaman /*
24*32640292SAndy Fiddaman  * E820 always uses 64 bit entries. Emulation code will use vm_paddr_t since it
25*32640292SAndy Fiddaman  * works on physical addresses. If vm_paddr_t is larger than uint64_t E820 can't
26*32640292SAndy Fiddaman  * hold all possible physical addresses and we can get into trouble.
27*32640292SAndy Fiddaman  */
28*32640292SAndy Fiddaman static_assert(sizeof(vm_paddr_t) <= sizeof(uint64_t),
29*32640292SAndy Fiddaman     "Unable to represent physical memory by E820 table");
30*32640292SAndy Fiddaman 
31*32640292SAndy Fiddaman #define E820_FWCFG_FILE_NAME "etc/e820"
32*32640292SAndy Fiddaman 
33*32640292SAndy Fiddaman #define KB (1024UL)
34*32640292SAndy Fiddaman #define MB (1024 * KB)
35*32640292SAndy Fiddaman #define GB (1024 * MB)
36*32640292SAndy Fiddaman 
37*32640292SAndy Fiddaman /*
38*32640292SAndy Fiddaman  * Fix E820 memory holes:
39*32640292SAndy Fiddaman  * [    A0000,    C0000) VGA
40*32640292SAndy Fiddaman  * [    C0000,   100000) ROM
41*32640292SAndy Fiddaman  */
42*32640292SAndy Fiddaman #define E820_VGA_MEM_BASE 0xA0000
43*32640292SAndy Fiddaman #define E820_VGA_MEM_END 0xC0000
44*32640292SAndy Fiddaman #define E820_ROM_MEM_BASE 0xC0000
45*32640292SAndy Fiddaman #define E820_ROM_MEM_END 0x100000
46*32640292SAndy Fiddaman 
47*32640292SAndy Fiddaman struct e820_element {
48*32640292SAndy Fiddaman 	TAILQ_ENTRY(e820_element) chain;
49*32640292SAndy Fiddaman 	uint64_t base;
50*32640292SAndy Fiddaman 	uint64_t end;
51*32640292SAndy Fiddaman 	enum e820_memory_type type;
52*32640292SAndy Fiddaman };
53*32640292SAndy Fiddaman static TAILQ_HEAD(e820_table, e820_element) e820_table = TAILQ_HEAD_INITIALIZER(
54*32640292SAndy Fiddaman     e820_table);
55*32640292SAndy Fiddaman 
56*32640292SAndy Fiddaman static struct e820_element *
e820_element_alloc(uint64_t base,uint64_t end,enum e820_memory_type type)57*32640292SAndy Fiddaman e820_element_alloc(uint64_t base, uint64_t end, enum e820_memory_type type)
58*32640292SAndy Fiddaman {
59*32640292SAndy Fiddaman 	struct e820_element *element;
60*32640292SAndy Fiddaman 
61*32640292SAndy Fiddaman 	element = calloc(1, sizeof(*element));
62*32640292SAndy Fiddaman 	if (element == NULL) {
63*32640292SAndy Fiddaman 		return (NULL);
64*32640292SAndy Fiddaman 	}
65*32640292SAndy Fiddaman 
66*32640292SAndy Fiddaman 	element->base = base;
67*32640292SAndy Fiddaman 	element->end = end;
68*32640292SAndy Fiddaman 	element->type = type;
69*32640292SAndy Fiddaman 
70*32640292SAndy Fiddaman 	return (element);
71*32640292SAndy Fiddaman }
72*32640292SAndy Fiddaman 
73*32640292SAndy Fiddaman static const char *
e820_get_type_name(const enum e820_memory_type type)74*32640292SAndy Fiddaman e820_get_type_name(const enum e820_memory_type type)
75*32640292SAndy Fiddaman {
76*32640292SAndy Fiddaman 	switch (type) {
77*32640292SAndy Fiddaman 	case E820_TYPE_MEMORY:
78*32640292SAndy Fiddaman 		return ("RAM");
79*32640292SAndy Fiddaman 	case E820_TYPE_RESERVED:
80*32640292SAndy Fiddaman 		return ("Reserved");
81*32640292SAndy Fiddaman 	case E820_TYPE_ACPI:
82*32640292SAndy Fiddaman 		return ("ACPI");
83*32640292SAndy Fiddaman 	case E820_TYPE_NVS:
84*32640292SAndy Fiddaman 		return ("NVS");
85*32640292SAndy Fiddaman 	default:
86*32640292SAndy Fiddaman 		return ("Unknown");
87*32640292SAndy Fiddaman 	}
88*32640292SAndy Fiddaman }
89*32640292SAndy Fiddaman 
90*32640292SAndy Fiddaman void
e820_dump_table(void)91*32640292SAndy Fiddaman e820_dump_table(void)
92*32640292SAndy Fiddaman {
93*32640292SAndy Fiddaman 	struct e820_element *element;
94*32640292SAndy Fiddaman 	uint64_t i;
95*32640292SAndy Fiddaman 
96*32640292SAndy Fiddaman 	fprintf(stderr, "E820 map:\n");
97*32640292SAndy Fiddaman 
98*32640292SAndy Fiddaman 	i = 0;
99*32640292SAndy Fiddaman 	TAILQ_FOREACH(element, &e820_table, chain) {
100*32640292SAndy Fiddaman 		fprintf(stderr, "  (%4lu) [%16lx, %16lx] %s\n", i,
101*32640292SAndy Fiddaman 		    element->base, element->end,
102*32640292SAndy Fiddaman 		    e820_get_type_name(element->type));
103*32640292SAndy Fiddaman 
104*32640292SAndy Fiddaman 		++i;
105*32640292SAndy Fiddaman 	}
106*32640292SAndy Fiddaman }
107*32640292SAndy Fiddaman 
108*32640292SAndy Fiddaman struct qemu_fwcfg_item *
e820_get_fwcfg_item(void)109*32640292SAndy Fiddaman e820_get_fwcfg_item(void)
110*32640292SAndy Fiddaman {
111*32640292SAndy Fiddaman 	struct qemu_fwcfg_item *fwcfg_item;
112*32640292SAndy Fiddaman 	struct e820_element *element;
113*32640292SAndy Fiddaman 	struct e820_entry *entries;
114*32640292SAndy Fiddaman 	int count, i;
115*32640292SAndy Fiddaman 
116*32640292SAndy Fiddaman 	count = 0;
117*32640292SAndy Fiddaman 	TAILQ_FOREACH(element, &e820_table, chain) {
118*32640292SAndy Fiddaman 		++count;
119*32640292SAndy Fiddaman 	}
120*32640292SAndy Fiddaman 	if (count == 0) {
121*32640292SAndy Fiddaman 		warnx("%s: E820 table empty", __func__);
122*32640292SAndy Fiddaman 		return (NULL);
123*32640292SAndy Fiddaman 	}
124*32640292SAndy Fiddaman 
125*32640292SAndy Fiddaman 	fwcfg_item = calloc(1, sizeof(struct qemu_fwcfg_item));
126*32640292SAndy Fiddaman 	if (fwcfg_item == NULL) {
127*32640292SAndy Fiddaman 		return (NULL);
128*32640292SAndy Fiddaman 	}
129*32640292SAndy Fiddaman 
130*32640292SAndy Fiddaman 	fwcfg_item->size = count * sizeof(struct e820_entry);
131*32640292SAndy Fiddaman 	fwcfg_item->data = calloc(count, sizeof(struct e820_entry));
132*32640292SAndy Fiddaman 	if (fwcfg_item->data == NULL) {
133*32640292SAndy Fiddaman 		free(fwcfg_item);
134*32640292SAndy Fiddaman 		return (NULL);
135*32640292SAndy Fiddaman 	}
136*32640292SAndy Fiddaman 
137*32640292SAndy Fiddaman 	i = 0;
138*32640292SAndy Fiddaman 	entries = (struct e820_entry *)fwcfg_item->data;
139*32640292SAndy Fiddaman 	TAILQ_FOREACH(element, &e820_table, chain) {
140*32640292SAndy Fiddaman 		struct e820_entry *entry = &entries[i];
141*32640292SAndy Fiddaman 
142*32640292SAndy Fiddaman 		entry->base = element->base;
143*32640292SAndy Fiddaman 		entry->length = element->end - element->base;
144*32640292SAndy Fiddaman 		entry->type = element->type;
145*32640292SAndy Fiddaman 
146*32640292SAndy Fiddaman 		++i;
147*32640292SAndy Fiddaman 	}
148*32640292SAndy Fiddaman 
149*32640292SAndy Fiddaman 	return (fwcfg_item);
150*32640292SAndy Fiddaman }
151*32640292SAndy Fiddaman 
152*32640292SAndy Fiddaman static int
e820_add_entry(const uint64_t base,const uint64_t end,const enum e820_memory_type type)153*32640292SAndy Fiddaman e820_add_entry(const uint64_t base, const uint64_t end,
154*32640292SAndy Fiddaman     const enum e820_memory_type type)
155*32640292SAndy Fiddaman {
156*32640292SAndy Fiddaman 	struct e820_element *new_element;
157*32640292SAndy Fiddaman 	struct e820_element *element;
158*32640292SAndy Fiddaman 	struct e820_element *ram_element;
159*32640292SAndy Fiddaman 
160*32640292SAndy Fiddaman 	assert(end >= base);
161*32640292SAndy Fiddaman 
162*32640292SAndy Fiddaman 	new_element = e820_element_alloc(base, end, type);
163*32640292SAndy Fiddaman 	if (new_element == NULL) {
164*32640292SAndy Fiddaman 		return (ENOMEM);
165*32640292SAndy Fiddaman 	}
166*32640292SAndy Fiddaman 
167*32640292SAndy Fiddaman 	/*
168*32640292SAndy Fiddaman 	 * E820 table should always be sorted in ascending order. Therefore,
169*32640292SAndy Fiddaman 	 * search for a range whose end is larger than the base parameter.
170*32640292SAndy Fiddaman 	 */
171*32640292SAndy Fiddaman 	TAILQ_FOREACH(element, &e820_table, chain) {
172*32640292SAndy Fiddaman 		if (element->end > base) {
173*32640292SAndy Fiddaman 			break;
174*32640292SAndy Fiddaman 		}
175*32640292SAndy Fiddaman 	}
176*32640292SAndy Fiddaman 
177*32640292SAndy Fiddaman 	/*
178*32640292SAndy Fiddaman 	 * System memory requires special handling.
179*32640292SAndy Fiddaman 	 */
180*32640292SAndy Fiddaman 	if (type == E820_TYPE_MEMORY) {
181*32640292SAndy Fiddaman 		/*
182*32640292SAndy Fiddaman 		 * base is larger than of any existing element. Add new system
183*32640292SAndy Fiddaman 		 * memory at the end of the table.
184*32640292SAndy Fiddaman 		 */
185*32640292SAndy Fiddaman 		if (element == NULL) {
186*32640292SAndy Fiddaman 			TAILQ_INSERT_TAIL(&e820_table, new_element, chain);
187*32640292SAndy Fiddaman 			return (0);
188*32640292SAndy Fiddaman 		}
189*32640292SAndy Fiddaman 
190*32640292SAndy Fiddaman 		/*
191*32640292SAndy Fiddaman 		 * System memory shouldn't overlap with any existing element.
192*32640292SAndy Fiddaman 		 */
193*32640292SAndy Fiddaman 		assert(end >= element->base);
194*32640292SAndy Fiddaman 
195*32640292SAndy Fiddaman 		TAILQ_INSERT_BEFORE(element, new_element, chain);
196*32640292SAndy Fiddaman 
197*32640292SAndy Fiddaman 		return (0);
198*32640292SAndy Fiddaman 	}
199*32640292SAndy Fiddaman 
200*32640292SAndy Fiddaman 	assert(element != NULL);
201*32640292SAndy Fiddaman 	/* Non system memory should be allocated inside system memory. */
202*32640292SAndy Fiddaman 	assert(element->type == E820_TYPE_MEMORY);
203*32640292SAndy Fiddaman 	/* New element should fit into existing system memory element. */
204*32640292SAndy Fiddaman 	assert(base >= element->base && end <= element->end);
205*32640292SAndy Fiddaman 
206*32640292SAndy Fiddaman 	if (base == element->base) {
207*32640292SAndy Fiddaman 		/*
208*32640292SAndy Fiddaman 		 * New element at system memory base boundary. Add new
209*32640292SAndy Fiddaman 		 * element before current and adjust the base of the old
210*32640292SAndy Fiddaman 		 * element.
211*32640292SAndy Fiddaman 		 *
212*32640292SAndy Fiddaman 		 * Old table:
213*32640292SAndy Fiddaman 		 * 	[ 0x1000, 0x4000] RAM		<-- element
214*32640292SAndy Fiddaman 		 * New table:
215*32640292SAndy Fiddaman 		 * 	[ 0x1000, 0x2000] Reserved
216*32640292SAndy Fiddaman 		 * 	[ 0x2000, 0x4000] RAM		<-- element
217*32640292SAndy Fiddaman 		 */
218*32640292SAndy Fiddaman 		TAILQ_INSERT_BEFORE(element, new_element, chain);
219*32640292SAndy Fiddaman 		element->base = end;
220*32640292SAndy Fiddaman 	} else if (end == element->end) {
221*32640292SAndy Fiddaman 		/*
222*32640292SAndy Fiddaman 		 * New element at system memory end boundary. Add new
223*32640292SAndy Fiddaman 		 * element after current and adjust the end of the
224*32640292SAndy Fiddaman 		 * current element.
225*32640292SAndy Fiddaman 		 *
226*32640292SAndy Fiddaman 		 * Old table:
227*32640292SAndy Fiddaman 		 * 	[ 0x1000, 0x4000] RAM		<-- element
228*32640292SAndy Fiddaman 		 * New table:
229*32640292SAndy Fiddaman 		 * 	[ 0x1000, 0x3000] RAM		<-- element
230*32640292SAndy Fiddaman 		 * 	[ 0x3000, 0x4000] Reserved
231*32640292SAndy Fiddaman 		 */
232*32640292SAndy Fiddaman 		TAILQ_INSERT_AFTER(&e820_table, element, new_element, chain);
233*32640292SAndy Fiddaman 		element->end = base;
234*32640292SAndy Fiddaman 	} else {
235*32640292SAndy Fiddaman 		/*
236*32640292SAndy Fiddaman 		 * New element inside system memory entry. Split it by
237*32640292SAndy Fiddaman 		 * adding a system memory element and the new element
238*32640292SAndy Fiddaman 		 * before current.
239*32640292SAndy Fiddaman 		 *
240*32640292SAndy Fiddaman 		 * Old table:
241*32640292SAndy Fiddaman 		 * 	[ 0x1000, 0x4000] RAM		<-- element
242*32640292SAndy Fiddaman 		 * New table:
243*32640292SAndy Fiddaman 		 * 	[ 0x1000, 0x2000] RAM
244*32640292SAndy Fiddaman 		 * 	[ 0x2000, 0x3000] Reserved
245*32640292SAndy Fiddaman 		 * 	[ 0x3000, 0x4000] RAM		<-- element
246*32640292SAndy Fiddaman 		 */
247*32640292SAndy Fiddaman 		ram_element = e820_element_alloc(element->base, base,
248*32640292SAndy Fiddaman 		    E820_TYPE_MEMORY);
249*32640292SAndy Fiddaman 		if (ram_element == NULL) {
250*32640292SAndy Fiddaman 			return (ENOMEM);
251*32640292SAndy Fiddaman 		}
252*32640292SAndy Fiddaman 		TAILQ_INSERT_BEFORE(element, ram_element, chain);
253*32640292SAndy Fiddaman 		TAILQ_INSERT_BEFORE(element, new_element, chain);
254*32640292SAndy Fiddaman 		element->base = end;
255*32640292SAndy Fiddaman 	}
256*32640292SAndy Fiddaman 
257*32640292SAndy Fiddaman 	return (0);
258*32640292SAndy Fiddaman }
259*32640292SAndy Fiddaman 
260*32640292SAndy Fiddaman static int
e820_add_memory_hole(const uint64_t base,const uint64_t end)261*32640292SAndy Fiddaman e820_add_memory_hole(const uint64_t base, const uint64_t end)
262*32640292SAndy Fiddaman {
263*32640292SAndy Fiddaman 	struct e820_element *element;
264*32640292SAndy Fiddaman 	struct e820_element *ram_element;
265*32640292SAndy Fiddaman 
266*32640292SAndy Fiddaman 	assert(end >= base);
267*32640292SAndy Fiddaman 
268*32640292SAndy Fiddaman 	/*
269*32640292SAndy Fiddaman 	 * E820 table should be always sorted in ascending order. Therefore,
270*32640292SAndy Fiddaman 	 * search for an element which end is larger than the base parameter.
271*32640292SAndy Fiddaman 	 */
272*32640292SAndy Fiddaman 	TAILQ_FOREACH(element, &e820_table, chain) {
273*32640292SAndy Fiddaman 		if (element->end > base) {
274*32640292SAndy Fiddaman 			break;
275*32640292SAndy Fiddaman 		}
276*32640292SAndy Fiddaman 	}
277*32640292SAndy Fiddaman 
278*32640292SAndy Fiddaman 	if (element == NULL || end <= element->base) {
279*32640292SAndy Fiddaman 		/* Nothing to do. Hole already exists */
280*32640292SAndy Fiddaman 		return (0);
281*32640292SAndy Fiddaman 	}
282*32640292SAndy Fiddaman 
283*32640292SAndy Fiddaman 	/* Memory holes are only allowed in system memory */
284*32640292SAndy Fiddaman 	assert(element->type == E820_TYPE_MEMORY);
285*32640292SAndy Fiddaman 
286*32640292SAndy Fiddaman 	if (base == element->base) {
287*32640292SAndy Fiddaman 		/*
288*32640292SAndy Fiddaman 		 * New hole at system memory base boundary.
289*32640292SAndy Fiddaman 		 *
290*32640292SAndy Fiddaman 		 * Old table:
291*32640292SAndy Fiddaman 		 * 	[ 0x1000, 0x4000] RAM
292*32640292SAndy Fiddaman 		 * New table:
293*32640292SAndy Fiddaman 		 * 	[ 0x2000, 0x4000] RAM
294*32640292SAndy Fiddaman 		 */
295*32640292SAndy Fiddaman 		element->base = end;
296*32640292SAndy Fiddaman 	} else if (end == element->end) {
297*32640292SAndy Fiddaman 		/*
298*32640292SAndy Fiddaman 		 * New hole at system memory end boundary.
299*32640292SAndy Fiddaman 		 *
300*32640292SAndy Fiddaman 		 * Old table:
301*32640292SAndy Fiddaman 		 * 	[ 0x1000, 0x4000] RAM
302*32640292SAndy Fiddaman 		 * New table:
303*32640292SAndy Fiddaman 		 * 	[ 0x1000, 0x3000] RAM
304*32640292SAndy Fiddaman 		 */
305*32640292SAndy Fiddaman 		element->end = base;
306*32640292SAndy Fiddaman 	} else {
307*32640292SAndy Fiddaman 		/*
308*32640292SAndy Fiddaman 		 * New hole inside system memory entry. Split the system memory.
309*32640292SAndy Fiddaman 		 *
310*32640292SAndy Fiddaman 		 * Old table:
311*32640292SAndy Fiddaman 		 * 	[ 0x1000, 0x4000] RAM		<-- element
312*32640292SAndy Fiddaman 		 * New table:
313*32640292SAndy Fiddaman 		 * 	[ 0x1000, 0x2000] RAM
314*32640292SAndy Fiddaman 		 * 	[ 0x3000, 0x4000] RAM		<-- element
315*32640292SAndy Fiddaman 		 */
316*32640292SAndy Fiddaman 		ram_element = e820_element_alloc(element->base, base,
317*32640292SAndy Fiddaman 		    E820_TYPE_MEMORY);
318*32640292SAndy Fiddaman 		if (ram_element == NULL) {
319*32640292SAndy Fiddaman 			return (ENOMEM);
320*32640292SAndy Fiddaman 		}
321*32640292SAndy Fiddaman 		TAILQ_INSERT_BEFORE(element, ram_element, chain);
322*32640292SAndy Fiddaman 		element->base = end;
323*32640292SAndy Fiddaman 	}
324*32640292SAndy Fiddaman 
325*32640292SAndy Fiddaman 	return (0);
326*32640292SAndy Fiddaman }
327*32640292SAndy Fiddaman 
328*32640292SAndy Fiddaman static uint64_t
e820_alloc_highest(const uint64_t max_address,const uint64_t length,const uint64_t alignment,const enum e820_memory_type type)329*32640292SAndy Fiddaman e820_alloc_highest(const uint64_t max_address, const uint64_t length,
330*32640292SAndy Fiddaman     const uint64_t alignment, const enum e820_memory_type type)
331*32640292SAndy Fiddaman {
332*32640292SAndy Fiddaman 	struct e820_element *element;
333*32640292SAndy Fiddaman 
334*32640292SAndy Fiddaman 	TAILQ_FOREACH_REVERSE(element, &e820_table, e820_table, chain) {
335*32640292SAndy Fiddaman 		uint64_t address, base, end;
336*32640292SAndy Fiddaman 
337*32640292SAndy Fiddaman 		end = MIN(max_address, element->end);
338*32640292SAndy Fiddaman 		base = roundup2(element->base, alignment);
339*32640292SAndy Fiddaman 
340*32640292SAndy Fiddaman 		/*
341*32640292SAndy Fiddaman 		 * If end - length == 0, we would allocate memory at address 0. This
342*32640292SAndy Fiddaman 		 * address is mostly unusable and we should avoid allocating it.
343*32640292SAndy Fiddaman 		 * Therefore, search for another block in that case.
344*32640292SAndy Fiddaman 		 */
345*32640292SAndy Fiddaman 		if (element->type != E820_TYPE_MEMORY || end < base ||
346*32640292SAndy Fiddaman 		    end - base < length || end - length == 0) {
347*32640292SAndy Fiddaman 			continue;
348*32640292SAndy Fiddaman 		}
349*32640292SAndy Fiddaman 
350*32640292SAndy Fiddaman 		address = rounddown2(end - length, alignment);
351*32640292SAndy Fiddaman 
352*32640292SAndy Fiddaman 		if (e820_add_entry(address, address + length, type) != 0) {
353*32640292SAndy Fiddaman 			return (0);
354*32640292SAndy Fiddaman 		}
355*32640292SAndy Fiddaman 
356*32640292SAndy Fiddaman 		return (address);
357*32640292SAndy Fiddaman 	}
358*32640292SAndy Fiddaman 
359*32640292SAndy Fiddaman 	return (0);
360*32640292SAndy Fiddaman }
361*32640292SAndy Fiddaman 
362*32640292SAndy Fiddaman static uint64_t
e820_alloc_lowest(const uint64_t min_address,const uint64_t length,const uint64_t alignment,const enum e820_memory_type type)363*32640292SAndy Fiddaman e820_alloc_lowest(const uint64_t min_address, const uint64_t length,
364*32640292SAndy Fiddaman     const uint64_t alignment, const enum e820_memory_type type)
365*32640292SAndy Fiddaman {
366*32640292SAndy Fiddaman 	struct e820_element *element;
367*32640292SAndy Fiddaman 
368*32640292SAndy Fiddaman 	TAILQ_FOREACH(element, &e820_table, chain) {
369*32640292SAndy Fiddaman 		uint64_t base, end;
370*32640292SAndy Fiddaman 
371*32640292SAndy Fiddaman 		end = element->end;
372*32640292SAndy Fiddaman 		base = MAX(min_address, roundup2(element->base, alignment));
373*32640292SAndy Fiddaman 
374*32640292SAndy Fiddaman 		/*
375*32640292SAndy Fiddaman 		 * If base == 0, we would allocate memory at address 0. This
376*32640292SAndy Fiddaman 		 * address is mostly unusable and we should avoid allocating it.
377*32640292SAndy Fiddaman 		 * Therefore, search for another block in that case.
378*32640292SAndy Fiddaman 		 */
379*32640292SAndy Fiddaman 		if (element->type != E820_TYPE_MEMORY || end < base ||
380*32640292SAndy Fiddaman 		    end - base < length || base == 0) {
381*32640292SAndy Fiddaman 			continue;
382*32640292SAndy Fiddaman 		}
383*32640292SAndy Fiddaman 
384*32640292SAndy Fiddaman 		if (e820_add_entry(base, base + length, type) != 0) {
385*32640292SAndy Fiddaman 			return (0);
386*32640292SAndy Fiddaman 		}
387*32640292SAndy Fiddaman 
388*32640292SAndy Fiddaman 		return (base);
389*32640292SAndy Fiddaman 	}
390*32640292SAndy Fiddaman 
391*32640292SAndy Fiddaman 	return (0);
392*32640292SAndy Fiddaman }
393*32640292SAndy Fiddaman 
394*32640292SAndy Fiddaman uint64_t
e820_alloc(const uint64_t address,const uint64_t length,const uint64_t alignment,const enum e820_memory_type type,const enum e820_allocation_strategy strategy)395*32640292SAndy Fiddaman e820_alloc(const uint64_t address, const uint64_t length,
396*32640292SAndy Fiddaman     const uint64_t alignment, const enum e820_memory_type type,
397*32640292SAndy Fiddaman     const enum e820_allocation_strategy strategy)
398*32640292SAndy Fiddaman {
399*32640292SAndy Fiddaman 	assert(powerof2(alignment));
400*32640292SAndy Fiddaman 	assert((address & (alignment - 1)) == 0);
401*32640292SAndy Fiddaman 
402*32640292SAndy Fiddaman 	switch (strategy) {
403*32640292SAndy Fiddaman 	case E820_ALLOCATE_ANY:
404*32640292SAndy Fiddaman 		/*
405*32640292SAndy Fiddaman 		 * Allocate any address. Therefore, ignore the address parameter
406*32640292SAndy Fiddaman 		 * and reuse the code path for allocating the lowest address.
407*32640292SAndy Fiddaman 		 */
408*32640292SAndy Fiddaman 		return (e820_alloc_lowest(0, length, alignment, type));
409*32640292SAndy Fiddaman 	case E820_ALLOCATE_LOWEST:
410*32640292SAndy Fiddaman 		return (e820_alloc_lowest(address, length, alignment, type));
411*32640292SAndy Fiddaman 	case E820_ALLOCATE_HIGHEST:
412*32640292SAndy Fiddaman 		return (e820_alloc_highest(address, length, alignment, type));
413*32640292SAndy Fiddaman 	case E820_ALLOCATE_SPECIFIC:
414*32640292SAndy Fiddaman 		if (e820_add_entry(address, address + length, type) != 0) {
415*32640292SAndy Fiddaman 			return (0);
416*32640292SAndy Fiddaman 		}
417*32640292SAndy Fiddaman 
418*32640292SAndy Fiddaman 		return (address);
419*32640292SAndy Fiddaman 	}
420*32640292SAndy Fiddaman 
421*32640292SAndy Fiddaman 	return (0);
422*32640292SAndy Fiddaman }
423*32640292SAndy Fiddaman 
424*32640292SAndy Fiddaman int
e820_init(struct vmctx * const ctx)425*32640292SAndy Fiddaman e820_init(struct vmctx *const ctx)
426*32640292SAndy Fiddaman {
427*32640292SAndy Fiddaman 	uint64_t lowmem_size, highmem_size;
428*32640292SAndy Fiddaman 	int error;
429*32640292SAndy Fiddaman 
430*32640292SAndy Fiddaman 	TAILQ_INIT(&e820_table);
431*32640292SAndy Fiddaman 
432*32640292SAndy Fiddaman 	lowmem_size = vm_get_lowmem_size(ctx);
433*32640292SAndy Fiddaman 	error = e820_add_entry(0, lowmem_size, E820_TYPE_MEMORY);
434*32640292SAndy Fiddaman 	if (error) {
435*32640292SAndy Fiddaman 		warnx("%s: Could not add lowmem", __func__);
436*32640292SAndy Fiddaman 		return (error);
437*32640292SAndy Fiddaman 	}
438*32640292SAndy Fiddaman 
439*32640292SAndy Fiddaman 	highmem_size = vm_get_highmem_size(ctx);
440*32640292SAndy Fiddaman 	if (highmem_size != 0) {
441*32640292SAndy Fiddaman 		error = e820_add_entry(4 * GB, 4 * GB + highmem_size,
442*32640292SAndy Fiddaman 		    E820_TYPE_MEMORY);
443*32640292SAndy Fiddaman 		if (error) {
444*32640292SAndy Fiddaman 			warnx("%s: Could not add highmem", __func__);
445*32640292SAndy Fiddaman 			return (error);
446*32640292SAndy Fiddaman 		}
447*32640292SAndy Fiddaman 	}
448*32640292SAndy Fiddaman 
449*32640292SAndy Fiddaman 	error = e820_add_memory_hole(E820_VGA_MEM_BASE, E820_VGA_MEM_END);
450*32640292SAndy Fiddaman 	if (error) {
451*32640292SAndy Fiddaman 		warnx("%s: Could not add VGA memory", __func__);
452*32640292SAndy Fiddaman 		return (error);
453*32640292SAndy Fiddaman 	}
454*32640292SAndy Fiddaman 
455*32640292SAndy Fiddaman 	error = e820_add_memory_hole(E820_ROM_MEM_BASE, E820_ROM_MEM_END);
456*32640292SAndy Fiddaman 	if (error) {
457*32640292SAndy Fiddaman 		warnx("%s: Could not add ROM area", __func__);
458*32640292SAndy Fiddaman 		return (error);
459*32640292SAndy Fiddaman 	}
460*32640292SAndy Fiddaman 
461*32640292SAndy Fiddaman 	return (0);
462*32640292SAndy Fiddaman }
463