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