From nobody Mon May 08 08:25:25 2023 X-Original-To: dev-commits-src-branches@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4QFDr60WKdz49Z8B; Mon, 8 May 2023 08:25:26 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4QFDr574b3z3l8p; Mon, 8 May 2023 08:25:25 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1683534326; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=6zp96DrePx3dy47XjNTaIwJZnZkg9PNr1Z+md2FaK5c=; b=ath9jmQAaGl7BTJ3K7EW0m5pCV5BrcwcS5uPF4XzTvwrloUZ5xC4ZEs2T2Uq43vDMsnY2r Axtww1XG5GdE9GQgPYGmqXAoXonE6Ds281zXiKmiGLGB5E51GV1RkIxInfCbsRi+d75w0g UYJmBxYV29x830MNKJMadxbAWTbUsw4mmuMLlmV7wq33kyePPilo2xmbU0V6Nj2j5TQonp URJdwvLi5e6aQ7D817Jo49u1Njl6qbjJ3hwWOARmBGlXqsTIuEBw9KzXehfhRoTLXjqVRX 137UHPLWYPHnYCUxtk84xNjuSOVd9jAi8qzaANjatFoB4XEgA+VZiTQpkc7XkA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1683534326; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=6zp96DrePx3dy47XjNTaIwJZnZkg9PNr1Z+md2FaK5c=; b=VkNL0TfsrhtDKghszVG2ma2NShqzDmS/DClekvyk8plrs7P5dWxpKcaxxaVFaFe5NdT5+V QVe39AkGF+l0N14ej0+krwpqdoDCWl++4A1cWqMrjQbOCh4xCpLUCBxD0i43n1nhRNjTbL +8DiRnxR9kjuoImt0on4Os3N+igsZIn35taHlC6eQZIlO3Vc0+Xfm9JIEFkSsFoBR+TMtS D9kmg0LRXnokp/qfWlIgGDUyGuTU0k0B+ftZUM3eHFYHRNJXuHtk47O8gimX26KVRnm9uj BbecTbn3oodSNvTUFLVcQ7I6eaVnZwH23ihUYfJAqDhW7xiQoWXXI9UYWut1Kg== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1683534326; a=rsa-sha256; cv=none; b=sSlQOPcjlwDqG6LobHEdpK3nIWTpASObjNWv+GiofixA9lD+n829y0FIoYYlNB1MmrYUR0 wI4IYBX+pwF9Yiw7PohRgJPcIbMlUDg1FKjti9YHXpVhcs8ddbaMT7eJ1+gXrct0AppQIA DznyU+1P4s8gPILaayox0dwUGb2DfXyDlAkfWsfdqqqVYDiNikLVLZUJjxQuSxASm1cjjw OnmwOlgK2DkLmiDtWi72odUNm2+v0lm5H17Ku0GDQxiP5dOEdKtD5+sO5Ezx9HMYLUfpPY hGtpkJPEeKrheQNPYDBCY9vvVxEkb7Gq5fV5peDPTH1ypT5QQfMwMLM7Ezcu6w== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 4QFDr567QDztLb; Mon, 8 May 2023 08:25:25 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org ([127.0.1.44]) by gitrepo.freebsd.org (8.16.1/8.16.1) with ESMTP id 3488PPFg050305; Mon, 8 May 2023 08:25:25 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.16.1/8.16.1/Submit) id 3488PP3x050304; Mon, 8 May 2023 08:25:25 GMT (envelope-from git) Date: Mon, 8 May 2023 08:25:25 GMT Message-Id: <202305080825.3488PP3x050304@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org From: =?utf-8?Q?Corvin=20K=C3=B6hne?= Subject: git: d71c3dd112f6 - stable/13 - bhyve: add helper struct for qemus acpi table loader List-Id: Commits to the stable branches of the FreeBSD src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-branches List-Help: List-Post: List-Subscribe: List-Unsubscribe: Sender: owner-dev-commits-src-branches@freebsd.org X-BeenThere: dev-commits-src-branches@freebsd.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: corvink X-Git-Repository: src X-Git-Refname: refs/heads/stable/13 X-Git-Reftype: branch X-Git-Commit: d71c3dd112f68dc581e9053ffbe44e264a835d0f Auto-Submitted: auto-generated X-ThisMailContainsUnwantedMimeParts: N The branch stable/13 has been updated by corvink: URL: https://cgit.FreeBSD.org/src/commit/?id=d71c3dd112f68dc581e9053ffbe44e264a835d0f commit d71c3dd112f68dc581e9053ffbe44e264a835d0f Author: Corvin Köhne AuthorDate: 2022-04-06 09:10:36 +0000 Commit: Corvin Köhne CommitDate: 2023-05-08 08:21:29 +0000 bhyve: add helper struct for qemus acpi table loader The hypervisor is aware of all system properties. For the guest bios it's hard and complex to detect all system properties. For that reason, it would be better if the hypervisor creates acpi tables instead of the guest. Therefore, the hypervisor has to send the acpi tables to the guest. At the moment, bhyve just copies the acpi tables into the guest memory. This approach has some restrictions. You have to keep sure that the guest doesn't overwrite them accidentally. Additionally, the size of acpi tables is limited. Providing a plain copy of all acpi tables by fwcfg isn't possible. Acpi tables have to point to each other. So, if the guest copies the acpi tables into memory by it's own, it has to patch the tables. Due to different layouts for different acpi tables, there's no generic way to do that. For that reason, qemu created a table loader interface. It contains commands for the guest for loading specific blobs into guest memory and patching those blobs. This commit adds a qemu_loader class which handles the creation of qemu loader commands. At the moment, the WRITE_POINTER command isn't implement. It won't be required by bhyve's acpi table generation yet. Reviewed by: markj MFC after: 1 week Sponsored by: Beckhoff Automation GmbH & Co. KG Differential Revision: https://reviews.freebsd.org/D38438 (cherry picked from commit f565b4d6303f4798a2cb0dc51ef88de9e4a24047) --- usr.sbin/bhyve/Makefile | 1 + usr.sbin/bhyve/qemu_loader.c | 274 +++++++++++++++++++++++++++++++++++++++++++ usr.sbin/bhyve/qemu_loader.h | 77 ++++++++++++ 3 files changed, 352 insertions(+) diff --git a/usr.sbin/bhyve/Makefile b/usr.sbin/bhyve/Makefile index 967ea790caa5..f5031a5bae07 100644 --- a/usr.sbin/bhyve/Makefile +++ b/usr.sbin/bhyve/Makefile @@ -64,6 +64,7 @@ SRCS= \ ps2kbd.c \ ps2mouse.c \ qemu_fwcfg.c \ + qemu_loader.c \ rfb.c \ rtc.c \ smbiostbl.c \ diff --git a/usr.sbin/bhyve/qemu_loader.c b/usr.sbin/bhyve/qemu_loader.c new file mode 100644 index 000000000000..b0e1197a4b6b --- /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 + */ + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#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)); +} diff --git a/usr.sbin/bhyve/qemu_loader.h b/usr.sbin/bhyve/qemu_loader.h new file mode 100644 index 000000000000..b6ce48ad0871 --- /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 + */ + +#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);