Page MenuHomeFreeBSD

D38438.diff
No OneTemporary

D38438.diff

diff --git a/usr.sbin/bhyve/Makefile b/usr.sbin/bhyve/Makefile
--- a/usr.sbin/bhyve/Makefile
+++ b/usr.sbin/bhyve/Makefile
@@ -65,6 +65,7 @@
ps2kbd.c \
ps2mouse.c \
qemu_fwcfg.c \
+ qemu_loader.c \
rfb.c \
rtc.c \
smbiostbl.c \
diff --git a/usr.sbin/bhyve/qemu_loader.h b/usr.sbin/bhyve/qemu_loader.h
new file mode 100644
--- /dev/null
+++ b/usr.sbin/bhyve/qemu_loader.h
@@ -0,0 +1,77 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2022 Beckhoff Automation GmbH & Co. KG
+ * Author: Corvin Köhne <c.koehne@beckhoff.com>
+ */
+
+#pragma once
+
+#include "qemu_fwcfg.h"
+
+struct qemu_loader;
+
+/*
+ * Some guest bios like seabios assume the RSDP to be located in the FSEG. Bhyve
+ * only supports OVMF which has no such requirement.
+ */
+enum qemu_loader_zone {
+ QEMU_LOADER_ALLOC_HIGH = 1,
+ QEMU_LOADER_ALLOC_FSEG, /* 0x0F000000 - 0x100000 */
+};
+
+/**
+ * Loads a fwcfg item into guest memory. This command has to be issued before
+ * any subsequent command can be used.
+ *
+ * @param loader Qemu loader instance the command should be added to.
+ * @param name Name of the fwcfg item which should be allocated.
+ * @param alignment Alignment required by the data.
+ * @param zone Memory zone in which it should be loaded.
+ */
+int qemu_loader_alloc(struct qemu_loader *loader, const uint8_t *name,
+ uint32_t alignment, enum qemu_loader_zone zone);
+/**
+ * Calculates a checksum for @p name and writes it to @p name + @p off . The
+ * checksum calculation ranges from @p start to @p start + @p len. The checksum
+ * field is always one byte large and all bytes in the specified range,
+ * including the checksum, have to sum up to 0.
+ *
+ * @param loader Qemu loader instance the command should be added to.
+ * @param name Name of the fwcfg item which should be patched.
+ * @param off Offset into @p name .
+ * @param start Start offset of checksum calculation.
+ * @param len Length of the checksum calculation.
+ */
+int qemu_loader_add_checksum(struct qemu_loader *loader, const uint8_t *name,
+ uint32_t off, uint32_t start, uint32_t len);
+/**
+ * Adds the address of @p src_name to the value at @p dest_name + @p off . The
+ * size of the pointer is determined by @p dest_size and should be 1, 2, 4 or 8.
+ *
+ * @param loader Qemu loader instance the command should be added to.
+ * @param dest_name Name of the fwcfg item which should be patched.
+ * @param src_name Name of the fwcfg item which address should be written to
+ * @p dest_name + @p off.
+ * @param off Offset into @p dest_name .
+ * @param size Size of the pointer (1, 2, 4 or 8).
+ */
+int qemu_loader_add_pointer(struct qemu_loader *loader,
+ const uint8_t *dest_name, const uint8_t *src_name, uint32_t off,
+ uint8_t size);
+
+/**
+ * Creates a qemu loader instance.
+ *
+ * @param new_loader Returns the newly created qemu loader instance.
+ * @param fwcfg_name Name of the FwCfg item which represents the qemu loader
+ */
+int qemu_loader_create(struct qemu_loader **new_loader,
+ const uint8_t *fwcfg_name);
+/**
+ * Signals that all commands are written to the qemu loader. This function
+ * creates a proper FwCfg item and registers it.
+ *
+ * @param loader Qemu loader instance which should be finished.
+ */
+int qemu_loader_finish(struct qemu_loader *loader);
diff --git a/usr.sbin/bhyve/qemu_loader.c b/usr.sbin/bhyve/qemu_loader.c
new file mode 100644
--- /dev/null
+++ b/usr.sbin/bhyve/qemu_loader.c
@@ -0,0 +1,274 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2022 Beckhoff Automation GmbH & Co. KG
+ * Author: Corvin Köhne <c.koehne@beckhoff.com>
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/endian.h>
+#include <sys/queue.h>
+
+#include <machine/vmm.h>
+
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <vmmapi.h>
+
+#include "qemu_fwcfg.h"
+#include "qemu_loader.h"
+
+struct qemu_loader_entry {
+ uint32_t cmd_le;
+ union {
+ struct {
+ uint8_t name[QEMU_FWCFG_MAX_NAME];
+ uint32_t alignment_le;
+ uint8_t zone;
+ } alloc;
+ struct {
+ uint8_t dest_name[QEMU_FWCFG_MAX_NAME];
+ uint8_t src_name[QEMU_FWCFG_MAX_NAME];
+ uint32_t off_le;
+ uint8_t size;
+ } add_pointer;
+ struct {
+ uint8_t name[QEMU_FWCFG_MAX_NAME];
+ uint32_t off_le;
+ uint32_t start_le;
+ uint32_t len_le;
+ } add_checksum;
+ struct {
+ uint8_t dest_name[QEMU_FWCFG_MAX_NAME];
+ uint8_t src_name[QEMU_FWCFG_MAX_NAME];
+ uint32_t dest_off_le;
+ uint32_t src_off_le;
+ uint8_t size;
+ } write_pointer;
+
+ /* padding */
+ uint8_t pad[124];
+ };
+} __packed;
+
+enum qemu_loader_command {
+ QEMU_LOADER_CMD_ALLOC = 0x1,
+ QEMU_LOADER_CMD_ADD_POINTER = 0x2,
+ QEMU_LOADER_CMD_ADD_CHECKSUM = 0x3,
+ QEMU_LOADER_CMD_WRITE_POINTER = 0x4,
+};
+
+struct qemu_loader_element {
+ STAILQ_ENTRY(qemu_loader_element) chain;
+ struct qemu_loader_entry entry;
+};
+
+struct qemu_loader {
+ uint8_t fwcfg_name[QEMU_FWCFG_MAX_NAME];
+ STAILQ_HEAD(qemu_loader_list, qemu_loader_element) list;
+};
+
+int
+qemu_loader_alloc(struct qemu_loader *const loader, const uint8_t *name,
+ const uint32_t alignment, const enum qemu_loader_zone zone)
+{
+ struct qemu_loader_element *element;
+
+ if (strlen(name) >= QEMU_FWCFG_MAX_NAME)
+ return (EINVAL);
+
+ element = calloc(1, sizeof(struct qemu_loader_element));
+ if (element == NULL) {
+ warnx("%s: failed to allocate command", __func__);
+ return (ENOMEM);
+ }
+
+ element->entry.cmd_le = htole32(QEMU_LOADER_CMD_ALLOC);
+ strncpy(element->entry.alloc.name, name, QEMU_FWCFG_MAX_NAME);
+ element->entry.alloc.alignment_le = htole32(alignment);
+ element->entry.alloc.zone = zone;
+
+ /*
+ * The guest always works on copies of the fwcfg item, which where
+ * loaded into guest memory. Loading a fwcfg item is caused by ALLOC.
+ * For that reason, ALLOC should be scheduled in front of any other
+ * commands.
+ */
+ STAILQ_INSERT_HEAD(&loader->list, element, chain);
+
+ return (0);
+}
+
+int
+qemu_loader_add_checksum(struct qemu_loader *const loader, const uint8_t *name,
+ const uint32_t off, const uint32_t start, const uint32_t len)
+{
+ struct qemu_loader_element *element;
+
+ if (strlen(name) >= QEMU_FWCFG_MAX_NAME)
+ return (EINVAL);
+
+ element = calloc(1, sizeof(struct qemu_loader_element));
+ if (element == NULL) {
+ warnx("%s: failed to allocate command", __func__);
+ return (ENOMEM);
+ }
+
+ element->entry.cmd_le = htole32(QEMU_LOADER_CMD_ADD_CHECKSUM);
+ strncpy(element->entry.add_checksum.name, name, QEMU_FWCFG_MAX_NAME);
+ element->entry.add_checksum.off_le = htole32(off);
+ element->entry.add_checksum.start_le = htole32(start);
+ element->entry.add_checksum.len_le = htole32(len);
+
+ STAILQ_INSERT_TAIL(&loader->list, element, chain);
+
+ return (0);
+}
+
+int
+qemu_loader_add_pointer(struct qemu_loader *const loader,
+ const uint8_t *dest_name, const uint8_t *src_name, const uint32_t off,
+ const uint8_t size)
+{
+ struct qemu_loader_element *element;
+
+ if (strlen(dest_name) >= QEMU_FWCFG_MAX_NAME ||
+ strlen(src_name) >= QEMU_FWCFG_MAX_NAME)
+ return (EINVAL);
+
+ element = calloc(1, sizeof(struct qemu_loader_element));
+ if (element == NULL) {
+ warnx("%s: failed to allocate command", __func__);
+ return (ENOMEM);
+ }
+
+ element->entry.cmd_le = htole32(QEMU_LOADER_CMD_ADD_POINTER);
+ strncpy(element->entry.add_pointer.dest_name, dest_name,
+ QEMU_FWCFG_MAX_NAME);
+ strncpy(element->entry.add_pointer.src_name, src_name,
+ QEMU_FWCFG_MAX_NAME);
+ element->entry.add_pointer.off_le = htole32(off);
+ element->entry.add_pointer.size = size;
+
+ STAILQ_INSERT_TAIL(&loader->list, element, chain);
+
+ return (0);
+}
+
+int
+qemu_loader_create(struct qemu_loader **const new_loader,
+ const uint8_t *fwcfg_name)
+{
+ struct qemu_loader *loader;
+
+ if (new_loader == NULL || strlen(fwcfg_name) >= QEMU_FWCFG_MAX_NAME) {
+ return (EINVAL);
+ }
+
+ loader = calloc(1, sizeof(struct qemu_loader));
+ if (loader == NULL) {
+ warnx("%s: failed to allocate loader", __func__);
+ return (ENOMEM);
+ }
+
+ strncpy(loader->fwcfg_name, fwcfg_name, QEMU_FWCFG_MAX_NAME);
+ STAILQ_INIT(&loader->list);
+
+ *new_loader = loader;
+
+ return (0);
+}
+
+static const uint8_t *
+qemu_loader_get_zone_name(const enum qemu_loader_zone zone)
+{
+ switch (zone) {
+ case QEMU_LOADER_ALLOC_HIGH:
+ return ("HIGH");
+ case QEMU_LOADER_ALLOC_FSEG:
+ return ("FSEG");
+ default:
+ return ("Unknown");
+ }
+}
+
+static void __unused
+qemu_loader_dump_entry(const struct qemu_loader_entry *const entry)
+{
+ switch (le32toh(entry->cmd_le)) {
+ case QEMU_LOADER_CMD_ALLOC:
+ printf("CMD_ALLOC\n\r");
+ printf(" name : %s\n\r", entry->alloc.name);
+ printf(" alignment: %8x\n\r",
+ le32toh(entry->alloc.alignment_le));
+ printf(" zone : %s\n\r",
+ qemu_loader_get_zone_name(entry->alloc.zone));
+ break;
+ case QEMU_LOADER_CMD_ADD_POINTER:
+ printf("CMD_ADD_POINTER\n\r");
+ printf(" dest_name: %s\n\r", entry->add_pointer.dest_name);
+ printf(" src_name : %s\n\r", entry->add_pointer.src_name);
+ printf(" off : %8x\n\r",
+ le32toh(entry->add_pointer.off_le));
+ printf(" size : %8x\n\r", entry->add_pointer.size);
+ break;
+ case QEMU_LOADER_CMD_ADD_CHECKSUM:
+ printf("CMD_ADD_CHECKSUM\n\r");
+ printf(" name : %s\n\r", entry->add_checksum.name);
+ printf(" off : %8x\n\r",
+ le32toh(entry->add_checksum.off_le));
+ printf(" start : %8x\n\r",
+ le32toh(entry->add_checksum.start_le));
+ printf(" length : %8x\n\r",
+ le32toh(entry->add_checksum.len_le));
+ break;
+ case QEMU_LOADER_CMD_WRITE_POINTER:
+ printf("CMD_WRITE_POINTER\n\r");
+ printf(" dest_name: %s\n\r", entry->write_pointer.dest_name);
+ printf(" src_name : %s\n\r", entry->write_pointer.src_name);
+ printf(" dest_off : %8x\n\r",
+ le32toh(entry->write_pointer.dest_off_le));
+ printf(" src_off : %8x\n\r",
+ le32toh(entry->write_pointer.src_off_le));
+ printf(" size : %8x\n\r", entry->write_pointer.size);
+ break;
+ default:
+ printf("UNKNOWN\n\r");
+ break;
+ }
+}
+
+int
+qemu_loader_finish(struct qemu_loader *const loader)
+{
+ struct qemu_loader_element *element;
+ struct qemu_loader_entry *data;
+ size_t len = 0;
+
+ STAILQ_FOREACH(element, &loader->list, chain) {
+ len += sizeof(struct qemu_loader_entry);
+ }
+ if (len == 0) {
+ warnx("%s: bios loader empty", __func__);
+ return (EFAULT);
+ }
+
+ data = calloc(1, len);
+ if (data == NULL) {
+ warnx("%s: failed to allocate fwcfg data", __func__);
+ return (ENOMEM);
+ }
+
+ int i = 0;
+ STAILQ_FOREACH(element, &loader->list, chain) {
+ memcpy(&data[i], &element->entry,
+ sizeof(struct qemu_loader_entry));
+ ++i;
+ }
+
+ return (qemu_fwcfg_add_file(loader->fwcfg_name, len, data));
+}

File Metadata

Mime Type
text/plain
Expires
Fri, Oct 24, 1:44 PM (12 h, 40 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
24133796
Default Alt Text
D38438.diff (10 KB)

Event Timeline