git: 0f8a17795d21 - stable/13 - bhyve: add allocation function to E820

From: Corvin Köhne <corvink_at_FreeBSD.org>
Date: Mon, 08 May 2023 08:25:30 UTC
The branch stable/13 has been updated by corvink:

URL: https://cgit.FreeBSD.org/src/commit/?id=0f8a17795d21bbd90eda7e17e98847adca011176

commit 0f8a17795d21bbd90eda7e17e98847adca011176
Author:     Corvin Köhne <corvink@FreeBSD.org>
AuthorDate: 2021-09-09 09:37:03 +0000
Commit:     Corvin Köhne <corvink@FreeBSD.org>
CommitDate: 2023-05-08 08:21:31 +0000

    bhyve: add allocation function to E820
    
    This function makes it easy to allocate new E820 entries. It will be
    used to allocate graphics memory for Intel integrated graphic devices.
    
    Reviewed by:            markj
    MFC after:              1 week
    Sponsored by:           Beckhoff Automation GmbH & Co. KG
    Differential Revision:  https://reviews.freebsd.org/D39547
    
    (cherry picked from commit 5597f564870e94d56111dec638b8859423c936a9)
---
 usr.sbin/bhyve/e820.c | 104 ++++++++++++++++++++++++++++++++++++++++++++++++++
 usr.sbin/bhyve/e820.h |  16 ++++++++
 2 files changed, 120 insertions(+)

diff --git a/usr.sbin/bhyve/e820.c b/usr.sbin/bhyve/e820.c
index 95b83c056b9b..922381d032ce 100644
--- a/usr.sbin/bhyve/e820.c
+++ b/usr.sbin/bhyve/e820.c
@@ -20,6 +20,14 @@
 #include "e820.h"
 #include "qemu_fwcfg.h"
 
+/*
+ * E820 always uses 64 bit entries. Emulation code will use vm_paddr_t since it
+ * works on physical addresses. If vm_paddr_t is larger than uint64_t E820 can't
+ * hold all possible physical addresses and we can get into trouble.
+ */
+static_assert(sizeof(vm_paddr_t) <= sizeof(uint64_t),
+    "Unable to represent physical memory by E820 table");
+
 #define E820_FWCFG_FILE_NAME "etc/e820"
 
 #define KB (1024UL)
@@ -282,6 +290,102 @@ e820_add_memory_hole(const uint64_t base, const uint64_t end)
 	return (0);
 }
 
+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)
+{
+	struct e820_element *element;
+
+	TAILQ_FOREACH_REVERSE(element, &e820_table, e820_table, chain) {
+		uint64_t address, base, end;
+
+		end = MIN(max_address, element->end);
+		base = roundup2(element->base, alignment);
+
+		/*
+		 * If end - length == 0, we would allocate memory at address 0. This
+		 * address is mostly unusable and we should avoid allocating it.
+		 * Therefore, search for another block in that case.
+		 */
+		if (element->type != E820_TYPE_MEMORY || end < base ||
+		    end - base < length || end - length == 0) {
+			continue;
+		}
+
+		address = rounddown2(end - length, alignment);
+
+		if (e820_add_entry(address, address + length, type) != 0) {
+			return (0);
+		}
+
+		return (address);
+	}
+
+	return (0);
+}
+
+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)
+{
+	struct e820_element *element;
+
+	TAILQ_FOREACH(element, &e820_table, chain) {
+		uint64_t base, end;
+
+		end = element->end;
+		base = MAX(min_address, roundup2(element->base, alignment));
+
+		/*
+		 * If base == 0, we would allocate memory at address 0. This
+		 * address is mostly unusable and we should avoid allocating it.
+		 * Therefore, search for another block in that case.
+		 */
+		if (element->type != E820_TYPE_MEMORY || end < base ||
+		    end - base < length || base == 0) {
+			continue;
+		}
+
+		if (e820_add_entry(base, base + length, type) != 0) {
+			return (0);
+		}
+
+		return (base);
+	}
+
+	return (0);
+}
+
+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)
+{
+	assert(powerof2(alignment));
+	assert((address & (alignment - 1)) == 0);
+
+	switch (strategy) {
+	case E820_ALLOCATE_ANY:
+		/*
+		 * Allocate any address. Therefore, ignore the address parameter
+		 * and reuse the code path for allocating the lowest address.
+		 */
+		return (e820_alloc_lowest(0, length, alignment, type));
+	case E820_ALLOCATE_LOWEST:
+		return (e820_alloc_lowest(address, length, alignment, type));
+	case E820_ALLOCATE_HIGHEST:
+		return (e820_alloc_highest(address, length, alignment, type));
+	case E820_ALLOCATE_SPECIFIC:
+		if (e820_add_entry(address, address + length, type) != 0) {
+			return (0);
+		}
+
+		return (address);
+	}
+
+	return (0);
+}
+
 int
 e820_init(struct vmctx *const ctx)
 {
diff --git a/usr.sbin/bhyve/e820.h b/usr.sbin/bhyve/e820.h
index 6843ad5dc736..8b8e23422e1f 100644
--- a/usr.sbin/bhyve/e820.h
+++ b/usr.sbin/bhyve/e820.h
@@ -18,11 +18,27 @@ enum e820_memory_type {
 	E820_TYPE_NVS = 4
 };
 
+enum e820_allocation_strategy {
+	/* allocate any address */
+	E820_ALLOCATE_ANY,
+	/* allocate lowest address larger than address */
+	E820_ALLOCATE_LOWEST,
+	/* allocate highest address lower than address */
+	E820_ALLOCATE_HIGHEST,
+	/* allocate a specific address */
+	E820_ALLOCATE_SPECIFIC
+};
+
 struct e820_entry {
 	uint64_t base;
 	uint64_t length;
 	uint32_t type;
 } __packed;
 
+#define E820_ALIGNMENT_NONE 1
+
+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);
 struct qemu_fwcfg_item *e820_get_fwcfg_item(void);
 int e820_init(struct vmctx *const ctx);