xref: /freebsd/usr.sbin/bhyve/amd64/e820.c (revision 08139140c5f96fd9deb7a8de7a534bccf9a1d0c8)
14fe5b70cSMark Johnston /*-
24fe5b70cSMark Johnston  * SPDX-License-Identifier: BSD-2-Clause
34fe5b70cSMark Johnston  *
44fe5b70cSMark Johnston  * Copyright (c) 2021 Beckhoff Automation GmbH & Co. KG
54fe5b70cSMark Johnston  * Author: Corvin Köhne <c.koehne@beckhoff.com>
64fe5b70cSMark Johnston  */
74fe5b70cSMark Johnston 
84fe5b70cSMark Johnston #include <sys/types.h>
94fe5b70cSMark Johnston #include <sys/queue.h>
104fe5b70cSMark Johnston 
114fe5b70cSMark Johnston #include <machine/vmm.h>
124fe5b70cSMark Johnston 
134fe5b70cSMark Johnston #include <assert.h>
144fe5b70cSMark Johnston #include <err.h>
154fe5b70cSMark Johnston #include <errno.h>
164fe5b70cSMark Johnston #include <stdio.h>
174fe5b70cSMark Johnston #include <stdlib.h>
184fe5b70cSMark Johnston #include <string.h>
194fe5b70cSMark Johnston 
20b0936440SJohn Baldwin #include "debug.h"
214fe5b70cSMark Johnston #include "e820.h"
224fe5b70cSMark Johnston #include "qemu_fwcfg.h"
234fe5b70cSMark Johnston 
244fe5b70cSMark Johnston /*
254fe5b70cSMark Johnston  * E820 always uses 64 bit entries. Emulation code will use vm_paddr_t since it
264fe5b70cSMark Johnston  * works on physical addresses. If vm_paddr_t is larger than uint64_t E820 can't
274fe5b70cSMark Johnston  * hold all possible physical addresses and we can get into trouble.
284fe5b70cSMark Johnston  */
294fe5b70cSMark Johnston static_assert(sizeof(vm_paddr_t) <= sizeof(uint64_t),
304fe5b70cSMark Johnston     "Unable to represent physical memory by E820 table");
314fe5b70cSMark Johnston 
324fe5b70cSMark Johnston #define E820_FWCFG_FILE_NAME "etc/e820"
334fe5b70cSMark Johnston 
344fe5b70cSMark Johnston #define KB (1024UL)
354fe5b70cSMark Johnston #define MB (1024 * KB)
364fe5b70cSMark Johnston #define GB (1024 * MB)
374fe5b70cSMark Johnston 
384fe5b70cSMark Johnston /*
394fe5b70cSMark Johnston  * Fix E820 memory holes:
404fe5b70cSMark Johnston  * [    A0000,    C0000) VGA
414fe5b70cSMark Johnston  * [    C0000,   100000) ROM
424fe5b70cSMark Johnston  */
434fe5b70cSMark Johnston #define E820_VGA_MEM_BASE 0xA0000
444fe5b70cSMark Johnston #define E820_VGA_MEM_END 0xC0000
454fe5b70cSMark Johnston #define E820_ROM_MEM_BASE 0xC0000
464fe5b70cSMark Johnston #define E820_ROM_MEM_END 0x100000
474fe5b70cSMark Johnston 
484fe5b70cSMark Johnston struct e820_element {
494fe5b70cSMark Johnston 	TAILQ_ENTRY(e820_element) chain;
504fe5b70cSMark Johnston 	uint64_t base;
514fe5b70cSMark Johnston 	uint64_t end;
524fe5b70cSMark Johnston 	enum e820_memory_type type;
534fe5b70cSMark Johnston };
544fe5b70cSMark Johnston static TAILQ_HEAD(e820_table, e820_element) e820_table = TAILQ_HEAD_INITIALIZER(
554fe5b70cSMark Johnston     e820_table);
564fe5b70cSMark Johnston 
574fe5b70cSMark Johnston static struct e820_element *
e820_element_alloc(uint64_t base,uint64_t end,enum e820_memory_type type)584fe5b70cSMark Johnston e820_element_alloc(uint64_t base, uint64_t end, enum e820_memory_type type)
594fe5b70cSMark Johnston {
604fe5b70cSMark Johnston 	struct e820_element *element;
614fe5b70cSMark Johnston 
624fe5b70cSMark Johnston 	element = calloc(1, sizeof(*element));
634fe5b70cSMark Johnston 	if (element == NULL) {
644fe5b70cSMark Johnston 		return (NULL);
654fe5b70cSMark Johnston 	}
664fe5b70cSMark Johnston 
674fe5b70cSMark Johnston 	element->base = base;
684fe5b70cSMark Johnston 	element->end = end;
694fe5b70cSMark Johnston 	element->type = type;
704fe5b70cSMark Johnston 
714fe5b70cSMark Johnston 	return (element);
724fe5b70cSMark Johnston }
734fe5b70cSMark Johnston 
744fe5b70cSMark Johnston static const char *
e820_get_type_name(const enum e820_memory_type type)754fe5b70cSMark Johnston e820_get_type_name(const enum e820_memory_type type)
764fe5b70cSMark Johnston {
774fe5b70cSMark Johnston 	switch (type) {
784fe5b70cSMark Johnston 	case E820_TYPE_MEMORY:
794fe5b70cSMark Johnston 		return ("RAM");
804fe5b70cSMark Johnston 	case E820_TYPE_RESERVED:
814fe5b70cSMark Johnston 		return ("Reserved");
824fe5b70cSMark Johnston 	case E820_TYPE_ACPI:
834fe5b70cSMark Johnston 		return ("ACPI");
844fe5b70cSMark Johnston 	case E820_TYPE_NVS:
854fe5b70cSMark Johnston 		return ("NVS");
864fe5b70cSMark Johnston 	default:
874fe5b70cSMark Johnston 		return ("Unknown");
884fe5b70cSMark Johnston 	}
894fe5b70cSMark Johnston }
904fe5b70cSMark Johnston 
914fe5b70cSMark Johnston void
e820_dump_table(void)924fe5b70cSMark Johnston e820_dump_table(void)
934fe5b70cSMark Johnston {
944fe5b70cSMark Johnston 	struct e820_element *element;
954fe5b70cSMark Johnston 	uint64_t i;
964fe5b70cSMark Johnston 
97b0936440SJohn Baldwin 	EPRINTLN("E820 map:");
984fe5b70cSMark Johnston 
994fe5b70cSMark Johnston 	i = 0;
1004fe5b70cSMark Johnston 	TAILQ_FOREACH(element, &e820_table, chain) {
101b0936440SJohn Baldwin 		EPRINTLN("  (%4lu) [%16lx, %16lx] %s", i,
1024fe5b70cSMark Johnston 		    element->base, element->end,
1034fe5b70cSMark Johnston 		    e820_get_type_name(element->type));
1044fe5b70cSMark Johnston 
1054fe5b70cSMark Johnston 		++i;
1064fe5b70cSMark Johnston 	}
1074fe5b70cSMark Johnston }
1084fe5b70cSMark Johnston 
1094fe5b70cSMark Johnston static struct qemu_fwcfg_item *
e820_get_fwcfg_item(void)1104fe5b70cSMark Johnston e820_get_fwcfg_item(void)
1114fe5b70cSMark Johnston {
1124fe5b70cSMark Johnston 	struct qemu_fwcfg_item *fwcfg_item;
1134fe5b70cSMark Johnston 	struct e820_element *element;
1144fe5b70cSMark Johnston 	struct e820_entry *entries;
1154fe5b70cSMark Johnston 	int count, i;
1164fe5b70cSMark Johnston 
1174fe5b70cSMark Johnston 	count = 0;
1184fe5b70cSMark Johnston 	TAILQ_FOREACH(element, &e820_table, chain) {
1194fe5b70cSMark Johnston 		++count;
1204fe5b70cSMark Johnston 	}
1214fe5b70cSMark Johnston 	if (count == 0) {
1224fe5b70cSMark Johnston 		warnx("%s: E820 table empty", __func__);
1234fe5b70cSMark Johnston 		return (NULL);
1244fe5b70cSMark Johnston 	}
1254fe5b70cSMark Johnston 
1264fe5b70cSMark Johnston 	fwcfg_item = calloc(1, sizeof(struct qemu_fwcfg_item));
1274fe5b70cSMark Johnston 	if (fwcfg_item == NULL) {
1284fe5b70cSMark Johnston 		return (NULL);
1294fe5b70cSMark Johnston 	}
1304fe5b70cSMark Johnston 
1314fe5b70cSMark Johnston 	fwcfg_item->size = count * sizeof(struct e820_entry);
1324fe5b70cSMark Johnston 	fwcfg_item->data = calloc(count, sizeof(struct e820_entry));
1334fe5b70cSMark Johnston 	if (fwcfg_item->data == NULL) {
1344fe5b70cSMark Johnston 		free(fwcfg_item);
1354fe5b70cSMark Johnston 		return (NULL);
1364fe5b70cSMark Johnston 	}
1374fe5b70cSMark Johnston 
1384fe5b70cSMark Johnston 	i = 0;
1394fe5b70cSMark Johnston 	entries = (struct e820_entry *)fwcfg_item->data;
1404fe5b70cSMark Johnston 	TAILQ_FOREACH(element, &e820_table, chain) {
1414fe5b70cSMark Johnston 		struct e820_entry *entry = &entries[i];
1424fe5b70cSMark Johnston 
1434fe5b70cSMark Johnston 		entry->base = element->base;
1444fe5b70cSMark Johnston 		entry->length = element->end - element->base;
1454fe5b70cSMark Johnston 		entry->type = element->type;
1464fe5b70cSMark Johnston 
1474fe5b70cSMark Johnston 		++i;
1484fe5b70cSMark Johnston 	}
1494fe5b70cSMark Johnston 
1504fe5b70cSMark Johnston 	return (fwcfg_item);
1514fe5b70cSMark Johnston }
1524fe5b70cSMark Johnston 
1534fe5b70cSMark Johnston static int
e820_add_entry(const uint64_t base,const uint64_t end,const enum e820_memory_type type)1544fe5b70cSMark Johnston e820_add_entry(const uint64_t base, const uint64_t end,
1554fe5b70cSMark Johnston     const enum e820_memory_type type)
1564fe5b70cSMark Johnston {
1574fe5b70cSMark Johnston 	struct e820_element *new_element;
1584fe5b70cSMark Johnston 	struct e820_element *element;
159*08139140SCorvin Köhne 	struct e820_element *sib_element;
1604fe5b70cSMark Johnston 	struct e820_element *ram_element;
1614fe5b70cSMark Johnston 
1624fe5b70cSMark Johnston 	assert(end >= base);
1634fe5b70cSMark Johnston 
1644fe5b70cSMark Johnston 	new_element = e820_element_alloc(base, end, type);
1654fe5b70cSMark Johnston 	if (new_element == NULL) {
1664fe5b70cSMark Johnston 		return (ENOMEM);
1674fe5b70cSMark Johnston 	}
1684fe5b70cSMark Johnston 
1694fe5b70cSMark Johnston 	/*
1704fe5b70cSMark Johnston 	 * E820 table should always be sorted in ascending order. Therefore,
1714fe5b70cSMark Johnston 	 * search for a range whose end is larger than the base parameter.
1724fe5b70cSMark Johnston 	 */
1734fe5b70cSMark Johnston 	TAILQ_FOREACH(element, &e820_table, chain) {
1744fe5b70cSMark Johnston 		if (element->end > base) {
1754fe5b70cSMark Johnston 			break;
1764fe5b70cSMark Johnston 		}
1774fe5b70cSMark Johnston 	}
1784fe5b70cSMark Johnston 
1794fe5b70cSMark Johnston 	/*
1804fe5b70cSMark Johnston 	 * System memory requires special handling.
1814fe5b70cSMark Johnston 	 */
1824fe5b70cSMark Johnston 	if (type == E820_TYPE_MEMORY) {
1834fe5b70cSMark Johnston 		/*
1844fe5b70cSMark Johnston 		 * base is larger than of any existing element. Add new system
1854fe5b70cSMark Johnston 		 * memory at the end of the table.
1864fe5b70cSMark Johnston 		 */
1874fe5b70cSMark Johnston 		if (element == NULL) {
1884fe5b70cSMark Johnston 			TAILQ_INSERT_TAIL(&e820_table, new_element, chain);
1894fe5b70cSMark Johnston 			return (0);
1904fe5b70cSMark Johnston 		}
1914fe5b70cSMark Johnston 
1924fe5b70cSMark Johnston 		/*
1934fe5b70cSMark Johnston 		 * System memory shouldn't overlap with any existing element.
1944fe5b70cSMark Johnston 		 */
1954fe5b70cSMark Johnston 		assert(end >= element->base);
1964fe5b70cSMark Johnston 
1974fe5b70cSMark Johnston 		TAILQ_INSERT_BEFORE(element, new_element, chain);
1984fe5b70cSMark Johnston 
1994fe5b70cSMark Johnston 		return (0);
2004fe5b70cSMark Johnston 	}
2014fe5b70cSMark Johnston 
2024fe5b70cSMark Johnston 	/*
2034fe5b70cSMark Johnston 	 * If some one tries to allocate a specific address, it could happen, that
2044fe5b70cSMark Johnston 	 * this address is not allocatable. Therefore, do some checks. If the
2054fe5b70cSMark Johnston 	 * address is not allocatable, don't panic. The user may have a fallback and
2064fe5b70cSMark Johnston 	 * tries to allocate another address. This is true for the GVT-d emulation
2074fe5b70cSMark Johnston 	 * which tries to reuse the host address of the graphics stolen memory and
2084fe5b70cSMark Johnston 	 * falls back to allocating the highest address below 4 GB.
2094fe5b70cSMark Johnston 	 */
2104fe5b70cSMark Johnston 	if (element == NULL || element->type != E820_TYPE_MEMORY ||
2114fe5b70cSMark Johnston 	    (base < element->base || end > element->end))
2124fe5b70cSMark Johnston 		return (ENOMEM);
2134fe5b70cSMark Johnston 
214f325f81fSCorvin Köhne 	if (base == element->base && end == element->end) {
215f325f81fSCorvin Köhne 		/*
216f325f81fSCorvin Köhne 		 * The new entry replaces an existing one.
217f325f81fSCorvin Köhne 		 *
218f325f81fSCorvin Köhne 		 * Old table:
219f325f81fSCorvin Köhne 		 * 	[ 0x1000, 0x4000] RAM		<-- element
220f325f81fSCorvin Köhne 		 * New table:
221f325f81fSCorvin Köhne 		 *	[ 0x1000, 0x4000] Reserved
222f325f81fSCorvin Köhne 		 */
223f325f81fSCorvin Köhne 		TAILQ_INSERT_BEFORE(element, new_element, chain);
224f325f81fSCorvin Köhne 		TAILQ_REMOVE(&e820_table, element, chain);
225f325f81fSCorvin Köhne 		free(element);
226f325f81fSCorvin Köhne 	} else if (base == element->base) {
2274fe5b70cSMark Johnston 		/*
2284fe5b70cSMark Johnston 		 * New element at system memory base boundary. Add new
2294fe5b70cSMark Johnston 		 * element before current and adjust the base of the old
2304fe5b70cSMark Johnston 		 * element.
2314fe5b70cSMark Johnston 		 *
2324fe5b70cSMark Johnston 		 * Old table:
2334fe5b70cSMark Johnston 		 * 	[ 0x1000, 0x4000] RAM		<-- element
2344fe5b70cSMark Johnston 		 * New table:
2354fe5b70cSMark Johnston 		 * 	[ 0x1000, 0x2000] Reserved
2364fe5b70cSMark Johnston 		 * 	[ 0x2000, 0x4000] RAM		<-- element
2374fe5b70cSMark Johnston 		 */
2384fe5b70cSMark Johnston 		TAILQ_INSERT_BEFORE(element, new_element, chain);
2394fe5b70cSMark Johnston 		element->base = end;
2404fe5b70cSMark Johnston 	} else if (end == element->end) {
2414fe5b70cSMark Johnston 		/*
2424fe5b70cSMark Johnston 		 * New element at system memory end boundary. Add new
2434fe5b70cSMark Johnston 		 * element after current and adjust the end of the
2444fe5b70cSMark Johnston 		 * current element.
2454fe5b70cSMark Johnston 		 *
2464fe5b70cSMark Johnston 		 * Old table:
2474fe5b70cSMark Johnston 		 * 	[ 0x1000, 0x4000] RAM		<-- element
2484fe5b70cSMark Johnston 		 * New table:
2494fe5b70cSMark Johnston 		 * 	[ 0x1000, 0x3000] RAM		<-- element
2504fe5b70cSMark Johnston 		 * 	[ 0x3000, 0x4000] Reserved
2514fe5b70cSMark Johnston 		 */
2524fe5b70cSMark Johnston 		TAILQ_INSERT_AFTER(&e820_table, element, new_element, chain);
2534fe5b70cSMark Johnston 		element->end = base;
2544fe5b70cSMark Johnston 	} else {
2554fe5b70cSMark Johnston 		/*
2564fe5b70cSMark Johnston 		 * New element inside system memory entry. Split it by
2574fe5b70cSMark Johnston 		 * adding a system memory element and the new element
2584fe5b70cSMark Johnston 		 * before current.
2594fe5b70cSMark Johnston 		 *
2604fe5b70cSMark Johnston 		 * Old table:
2614fe5b70cSMark Johnston 		 * 	[ 0x1000, 0x4000] RAM		<-- element
2624fe5b70cSMark Johnston 		 * New table:
2634fe5b70cSMark Johnston 		 * 	[ 0x1000, 0x2000] RAM
2644fe5b70cSMark Johnston 		 * 	[ 0x2000, 0x3000] Reserved
2654fe5b70cSMark Johnston 		 * 	[ 0x3000, 0x4000] RAM		<-- element
2664fe5b70cSMark Johnston 		 */
2674fe5b70cSMark Johnston 		ram_element = e820_element_alloc(element->base, base,
2684fe5b70cSMark Johnston 		    E820_TYPE_MEMORY);
2694fe5b70cSMark Johnston 		if (ram_element == NULL) {
2704fe5b70cSMark Johnston 			return (ENOMEM);
2714fe5b70cSMark Johnston 		}
2724fe5b70cSMark Johnston 		TAILQ_INSERT_BEFORE(element, ram_element, chain);
2734fe5b70cSMark Johnston 		TAILQ_INSERT_BEFORE(element, new_element, chain);
2744fe5b70cSMark Johnston 		element->base = end;
2754fe5b70cSMark Johnston 	}
2764fe5b70cSMark Johnston 
277*08139140SCorvin Köhne 	/*
278*08139140SCorvin Köhne 	 * If the previous element has the same type and ends at our base
279*08139140SCorvin Köhne 	 * boundary, we can merge both entries.
280*08139140SCorvin Köhne 	 */
281*08139140SCorvin Köhne 	sib_element = TAILQ_PREV(new_element, e820_table, chain);
282*08139140SCorvin Köhne 	if (sib_element != NULL &&
283*08139140SCorvin Köhne 	    sib_element->type == new_element->type &&
284*08139140SCorvin Köhne 	    sib_element->end == new_element->base) {
285*08139140SCorvin Köhne 		new_element->base = sib_element->base;
286*08139140SCorvin Köhne 		TAILQ_REMOVE(&e820_table, sib_element, chain);
287*08139140SCorvin Köhne 		free(sib_element);
288*08139140SCorvin Köhne 	}
289*08139140SCorvin Köhne 
290*08139140SCorvin Köhne 	/*
291*08139140SCorvin Köhne 	 * If the next element has the same type and starts at our end
292*08139140SCorvin Köhne 	 * boundary, we can merge both entries.
293*08139140SCorvin Köhne 	 */
294*08139140SCorvin Köhne 	sib_element = TAILQ_NEXT(new_element, chain);
295*08139140SCorvin Köhne 	if (sib_element != NULL &&
296*08139140SCorvin Köhne 	    sib_element->type == new_element->type &&
297*08139140SCorvin Köhne 	    sib_element->base == new_element->end) {
298*08139140SCorvin Köhne 		/* Merge new element into subsequent one. */
299*08139140SCorvin Köhne 		new_element->end = sib_element->end;
300*08139140SCorvin Köhne 		TAILQ_REMOVE(&e820_table, sib_element, chain);
301*08139140SCorvin Köhne 		free(sib_element);
302*08139140SCorvin Köhne 	}
303*08139140SCorvin Köhne 
3044fe5b70cSMark Johnston 	return (0);
3054fe5b70cSMark Johnston }
3064fe5b70cSMark Johnston 
3074fe5b70cSMark Johnston static int
e820_add_memory_hole(const uint64_t base,const uint64_t end)3084fe5b70cSMark Johnston e820_add_memory_hole(const uint64_t base, const uint64_t end)
3094fe5b70cSMark Johnston {
3104fe5b70cSMark Johnston 	struct e820_element *element;
3114fe5b70cSMark Johnston 	struct e820_element *ram_element;
3124fe5b70cSMark Johnston 
3134fe5b70cSMark Johnston 	assert(end >= base);
3144fe5b70cSMark Johnston 
3154fe5b70cSMark Johnston 	/*
3164fe5b70cSMark Johnston 	 * E820 table should be always sorted in ascending order. Therefore,
3174fe5b70cSMark Johnston 	 * search for an element which end is larger than the base parameter.
3184fe5b70cSMark Johnston 	 */
3194fe5b70cSMark Johnston 	TAILQ_FOREACH(element, &e820_table, chain) {
3204fe5b70cSMark Johnston 		if (element->end > base) {
3214fe5b70cSMark Johnston 			break;
3224fe5b70cSMark Johnston 		}
3234fe5b70cSMark Johnston 	}
3244fe5b70cSMark Johnston 
3254fe5b70cSMark Johnston 	if (element == NULL || end <= element->base) {
3264fe5b70cSMark Johnston 		/* Nothing to do. Hole already exists */
3274fe5b70cSMark Johnston 		return (0);
3284fe5b70cSMark Johnston 	}
3294fe5b70cSMark Johnston 
3304fe5b70cSMark Johnston 	/* Memory holes are only allowed in system memory */
3314fe5b70cSMark Johnston 	assert(element->type == E820_TYPE_MEMORY);
3324fe5b70cSMark Johnston 
3334fe5b70cSMark Johnston 	if (base == element->base) {
3344fe5b70cSMark Johnston 		/*
3354fe5b70cSMark Johnston 		 * New hole at system memory base boundary.
3364fe5b70cSMark Johnston 		 *
3374fe5b70cSMark Johnston 		 * Old table:
3384fe5b70cSMark Johnston 		 * 	[ 0x1000, 0x4000] RAM
3394fe5b70cSMark Johnston 		 * New table:
3404fe5b70cSMark Johnston 		 * 	[ 0x2000, 0x4000] RAM
3414fe5b70cSMark Johnston 		 */
3424fe5b70cSMark Johnston 		element->base = end;
3434fe5b70cSMark Johnston 	} else if (end == element->end) {
3444fe5b70cSMark Johnston 		/*
3454fe5b70cSMark Johnston 		 * New hole at system memory end boundary.
3464fe5b70cSMark Johnston 		 *
3474fe5b70cSMark Johnston 		 * Old table:
3484fe5b70cSMark Johnston 		 * 	[ 0x1000, 0x4000] RAM
3494fe5b70cSMark Johnston 		 * New table:
3504fe5b70cSMark Johnston 		 * 	[ 0x1000, 0x3000] RAM
3514fe5b70cSMark Johnston 		 */
3524fe5b70cSMark Johnston 		element->end = base;
3534fe5b70cSMark Johnston 	} else {
3544fe5b70cSMark Johnston 		/*
3554fe5b70cSMark Johnston 		 * New hole inside system memory entry. Split the system memory.
3564fe5b70cSMark Johnston 		 *
3574fe5b70cSMark Johnston 		 * Old table:
3584fe5b70cSMark Johnston 		 * 	[ 0x1000, 0x4000] RAM		<-- element
3594fe5b70cSMark Johnston 		 * New table:
3604fe5b70cSMark Johnston 		 * 	[ 0x1000, 0x2000] RAM
3614fe5b70cSMark Johnston 		 * 	[ 0x3000, 0x4000] RAM		<-- element
3624fe5b70cSMark Johnston 		 */
3634fe5b70cSMark Johnston 		ram_element = e820_element_alloc(element->base, base,
3644fe5b70cSMark Johnston 		    E820_TYPE_MEMORY);
3654fe5b70cSMark Johnston 		if (ram_element == NULL) {
3664fe5b70cSMark Johnston 			return (ENOMEM);
3674fe5b70cSMark Johnston 		}
3684fe5b70cSMark Johnston 		TAILQ_INSERT_BEFORE(element, ram_element, chain);
3694fe5b70cSMark Johnston 		element->base = end;
3704fe5b70cSMark Johnston 	}
3714fe5b70cSMark Johnston 
3724fe5b70cSMark Johnston 	return (0);
3734fe5b70cSMark Johnston }
3744fe5b70cSMark Johnston 
3754fe5b70cSMark Johnston 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)3764fe5b70cSMark Johnston e820_alloc_highest(const uint64_t max_address, const uint64_t length,
3774fe5b70cSMark Johnston     const uint64_t alignment, const enum e820_memory_type type)
3784fe5b70cSMark Johnston {
3794fe5b70cSMark Johnston 	struct e820_element *element;
3804fe5b70cSMark Johnston 
3814fe5b70cSMark Johnston 	TAILQ_FOREACH_REVERSE(element, &e820_table, e820_table, chain) {
3824fe5b70cSMark Johnston 		uint64_t address, base, end;
3834fe5b70cSMark Johnston 
3844fe5b70cSMark Johnston 		end = MIN(max_address, element->end);
3854fe5b70cSMark Johnston 		base = roundup2(element->base, alignment);
3864fe5b70cSMark Johnston 
3874fe5b70cSMark Johnston 		/*
3884fe5b70cSMark Johnston 		 * If end - length == 0, we would allocate memory at address 0. This
3894fe5b70cSMark Johnston 		 * address is mostly unusable and we should avoid allocating it.
3904fe5b70cSMark Johnston 		 * Therefore, search for another block in that case.
3914fe5b70cSMark Johnston 		 */
3924fe5b70cSMark Johnston 		if (element->type != E820_TYPE_MEMORY || end < base ||
3934fe5b70cSMark Johnston 		    end - base < length || end - length == 0) {
3944fe5b70cSMark Johnston 			continue;
3954fe5b70cSMark Johnston 		}
3964fe5b70cSMark Johnston 
3974fe5b70cSMark Johnston 		address = rounddown2(end - length, alignment);
3984fe5b70cSMark Johnston 
3994fe5b70cSMark Johnston 		if (e820_add_entry(address, address + length, type) != 0) {
4004fe5b70cSMark Johnston 			return (0);
4014fe5b70cSMark Johnston 		}
4024fe5b70cSMark Johnston 
4034fe5b70cSMark Johnston 		return (address);
4044fe5b70cSMark Johnston 	}
4054fe5b70cSMark Johnston 
4064fe5b70cSMark Johnston 	return (0);
4074fe5b70cSMark Johnston }
4084fe5b70cSMark Johnston 
4094fe5b70cSMark Johnston 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)4104fe5b70cSMark Johnston e820_alloc_lowest(const uint64_t min_address, const uint64_t length,
4114fe5b70cSMark Johnston     const uint64_t alignment, const enum e820_memory_type type)
4124fe5b70cSMark Johnston {
4134fe5b70cSMark Johnston 	struct e820_element *element;
4144fe5b70cSMark Johnston 
4154fe5b70cSMark Johnston 	TAILQ_FOREACH(element, &e820_table, chain) {
4164fe5b70cSMark Johnston 		uint64_t base, end;
4174fe5b70cSMark Johnston 
4184fe5b70cSMark Johnston 		end = element->end;
4194fe5b70cSMark Johnston 		base = MAX(min_address, roundup2(element->base, alignment));
4204fe5b70cSMark Johnston 
4214fe5b70cSMark Johnston 		/*
4224fe5b70cSMark Johnston 		 * If base == 0, we would allocate memory at address 0. This
4234fe5b70cSMark Johnston 		 * address is mostly unusable and we should avoid allocating it.
4244fe5b70cSMark Johnston 		 * Therefore, search for another block in that case.
4254fe5b70cSMark Johnston 		 */
4264fe5b70cSMark Johnston 		if (element->type != E820_TYPE_MEMORY || end < base ||
4274fe5b70cSMark Johnston 		    end - base < length || base == 0) {
4284fe5b70cSMark Johnston 			continue;
4294fe5b70cSMark Johnston 		}
4304fe5b70cSMark Johnston 
4314fe5b70cSMark Johnston 		if (e820_add_entry(base, base + length, type) != 0) {
4324fe5b70cSMark Johnston 			return (0);
4334fe5b70cSMark Johnston 		}
4344fe5b70cSMark Johnston 
4354fe5b70cSMark Johnston 		return (base);
4364fe5b70cSMark Johnston 	}
4374fe5b70cSMark Johnston 
4384fe5b70cSMark Johnston 	return (0);
4394fe5b70cSMark Johnston }
4404fe5b70cSMark Johnston 
4414fe5b70cSMark Johnston 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)4424fe5b70cSMark Johnston e820_alloc(const uint64_t address, const uint64_t length,
4434fe5b70cSMark Johnston     const uint64_t alignment, const enum e820_memory_type type,
4444fe5b70cSMark Johnston     const enum e820_allocation_strategy strategy)
4454fe5b70cSMark Johnston {
4464fe5b70cSMark Johnston 	assert(powerof2(alignment));
4474fe5b70cSMark Johnston 	assert((address & (alignment - 1)) == 0);
4484fe5b70cSMark Johnston 
4494fe5b70cSMark Johnston 	switch (strategy) {
4504fe5b70cSMark Johnston 	case E820_ALLOCATE_ANY:
4514fe5b70cSMark Johnston 		/*
4524fe5b70cSMark Johnston 		 * Allocate any address. Therefore, ignore the address parameter
4534fe5b70cSMark Johnston 		 * and reuse the code path for allocating the lowest address.
4544fe5b70cSMark Johnston 		 */
4554fe5b70cSMark Johnston 		return (e820_alloc_lowest(0, length, alignment, type));
4564fe5b70cSMark Johnston 	case E820_ALLOCATE_LOWEST:
4574fe5b70cSMark Johnston 		return (e820_alloc_lowest(address, length, alignment, type));
4584fe5b70cSMark Johnston 	case E820_ALLOCATE_HIGHEST:
4594fe5b70cSMark Johnston 		return (e820_alloc_highest(address, length, alignment, type));
4604fe5b70cSMark Johnston 	case E820_ALLOCATE_SPECIFIC:
4614fe5b70cSMark Johnston 		if (e820_add_entry(address, address + length, type) != 0) {
4624fe5b70cSMark Johnston 			return (0);
4634fe5b70cSMark Johnston 		}
4644fe5b70cSMark Johnston 
4654fe5b70cSMark Johnston 		return (address);
4664fe5b70cSMark Johnston 	}
4674fe5b70cSMark Johnston 
4684fe5b70cSMark Johnston 	return (0);
4694fe5b70cSMark Johnston }
4704fe5b70cSMark Johnston 
4714fe5b70cSMark Johnston int
e820_init(struct vmctx * const ctx)4724fe5b70cSMark Johnston e820_init(struct vmctx *const ctx)
4734fe5b70cSMark Johnston {
4744fe5b70cSMark Johnston 	uint64_t lowmem_size, highmem_size;
4754fe5b70cSMark Johnston 	int error;
4764fe5b70cSMark Johnston 
4774fe5b70cSMark Johnston 	TAILQ_INIT(&e820_table);
4784fe5b70cSMark Johnston 
4794fe5b70cSMark Johnston 	lowmem_size = vm_get_lowmem_size(ctx);
4804fe5b70cSMark Johnston 	error = e820_add_entry(0, lowmem_size, E820_TYPE_MEMORY);
4814fe5b70cSMark Johnston 	if (error) {
4824fe5b70cSMark Johnston 		warnx("%s: Could not add lowmem", __func__);
4834fe5b70cSMark Johnston 		return (error);
4844fe5b70cSMark Johnston 	}
4854fe5b70cSMark Johnston 
4864fe5b70cSMark Johnston 	highmem_size = vm_get_highmem_size(ctx);
4874fe5b70cSMark Johnston 	if (highmem_size != 0) {
4884fe5b70cSMark Johnston 		error = e820_add_entry(4 * GB, 4 * GB + highmem_size,
4894fe5b70cSMark Johnston 		    E820_TYPE_MEMORY);
4904fe5b70cSMark Johnston 		if (error) {
4914fe5b70cSMark Johnston 			warnx("%s: Could not add highmem", __func__);
4924fe5b70cSMark Johnston 			return (error);
4934fe5b70cSMark Johnston 		}
4944fe5b70cSMark Johnston 	}
4954fe5b70cSMark Johnston 
4964fe5b70cSMark Johnston 	error = e820_add_memory_hole(E820_VGA_MEM_BASE, E820_VGA_MEM_END);
4974fe5b70cSMark Johnston 	if (error) {
4984fe5b70cSMark Johnston 		warnx("%s: Could not add VGA memory", __func__);
4994fe5b70cSMark Johnston 		return (error);
5004fe5b70cSMark Johnston 	}
5014fe5b70cSMark Johnston 
5024fe5b70cSMark Johnston 	error = e820_add_memory_hole(E820_ROM_MEM_BASE, E820_ROM_MEM_END);
5034fe5b70cSMark Johnston 	if (error) {
5044fe5b70cSMark Johnston 		warnx("%s: Could not add ROM area", __func__);
5054fe5b70cSMark Johnston 		return (error);
5064fe5b70cSMark Johnston 	}
5074fe5b70cSMark Johnston 
5084fe5b70cSMark Johnston 	return (0);
5094fe5b70cSMark Johnston }
5104fe5b70cSMark Johnston 
5114fe5b70cSMark Johnston int
e820_finalize(void)5124fe5b70cSMark Johnston e820_finalize(void)
5134fe5b70cSMark Johnston {
5144fe5b70cSMark Johnston 	struct qemu_fwcfg_item *e820_fwcfg_item;
5154fe5b70cSMark Johnston 	int error;
5164fe5b70cSMark Johnston 
5174fe5b70cSMark Johnston 	e820_fwcfg_item = e820_get_fwcfg_item();
5184fe5b70cSMark Johnston 	if (e820_fwcfg_item == NULL) {
5194fe5b70cSMark Johnston 		warnx("invalid e820 table");
5204fe5b70cSMark Johnston 		return (ENOMEM);
5214fe5b70cSMark Johnston 	}
5224fe5b70cSMark Johnston 	error = qemu_fwcfg_add_file("etc/e820",
5234fe5b70cSMark Johnston 	    e820_fwcfg_item->size, e820_fwcfg_item->data);
5244fe5b70cSMark Johnston 	if (error != 0) {
5254fe5b70cSMark Johnston 		warnx("could not add qemu fwcfg etc/e820");
526e9806d21SPierre Pronchery 		free(e820_fwcfg_item->data);
527e9806d21SPierre Pronchery 		free(e820_fwcfg_item);
5284fe5b70cSMark Johnston 		return (error);
5294fe5b70cSMark Johnston 	}
5304fe5b70cSMark Johnston 	free(e820_fwcfg_item);
5314fe5b70cSMark Johnston 
5324fe5b70cSMark Johnston 	return (0);
5334fe5b70cSMark Johnston }
534