Page MenuHomeFreeBSD

D31578.id93806.diff
No OneTemporary

D31578.id93806.diff

diff --git a/usr.sbin/bhyve/Makefile b/usr.sbin/bhyve/Makefile
--- a/usr.sbin/bhyve/Makefile
+++ b/usr.sbin/bhyve/Makefile
@@ -26,7 +26,6 @@
console.c \
ctl_util.c \
ctl_scsi_all.c \
- fwctl.c \
gdb.c \
hda_codec.c \
inout.c \
@@ -61,6 +60,7 @@
post.c \
ps2kbd.c \
ps2mouse.c \
+ qemu_fwcfg.c \
rfb.c \
rtc.c \
smbiostbl.c \
diff --git a/usr.sbin/bhyve/bhyverun.c b/usr.sbin/bhyve/bhyverun.c
--- a/usr.sbin/bhyve/bhyverun.c
+++ b/usr.sbin/bhyve/bhyverun.c
@@ -90,7 +90,6 @@
#include "config.h"
#include "inout.h"
#include "debug.h"
-#include "fwctl.h"
#include "gdb.h"
#include "ioapic.h"
#include "kernemu_dev.h"
@@ -100,6 +99,7 @@
#include "pci_emul.h"
#include "pci_irq.h"
#include "pci_lpc.h"
+#include "qemu_fwcfg.h"
#include "smbiostbl.h"
#ifdef BHYVE_SNAPSHOT
#include "snapshot.h"
@@ -1432,6 +1432,36 @@
rtc_init(ctx);
sci_init(ctx);
+ if (qemu_fwcfg_init(ctx) != 0) {
+ perror("qemu fwcfg initialization error");
+ exit(4);
+ }
+
+ /*
+ * QEMU uses fwcfg item 0x0f (FW_CFG_MAX_CPUS) to report the number of
+ * cpus to the guest but states that it has a special meaning for x86.
+ * Don't know yet if that can cause unintented side-effects. Use an own
+ * fwcfg item to be safe.
+ *
+ * QEMU comment:
+ * FW_CFG_MAX_CPUS is a bit confusing/problematic on x86:
+ *
+ * For machine types prior to 1.8, SeaBIOS needs FW_CFG_MAX_CPUS
+ * for building MPTable, ACPI MADT, ACPI CPU hotplug and ACPI SRAT
+ * table, that tables are based on xAPIC ID and QEMU<->SeaBIOS
+ * interface for CPU hotplug also uses APIC ID and not "CPU index".
+ * This means that FW_CFG_MAX_CPUS is not the "maximum number of
+ * CPUs", but the "limit to the APIC ID values SeaBIOS may see".
+ *
+ * So for compatibility reasons with old BIOSes we are stuck with
+ * "etc/max-cpus" actually being apic_id_limit
+ */
+ if (qemu_fwcfg_add_file("opt/bhyve/hw.ncpu", sizeof(guest_ncpus),
+ &guest_ncpus) != 0) {
+ perror("could not add qemu fwcfg opt/bhyve/hw.ncpu");
+ exit(4);
+ }
+
/*
* Exit if a device emulation finds an error in its initilization
*/
@@ -1518,9 +1548,6 @@
assert(error == 0);
}
- if (lpc_bootrom())
- fwctl_init();
-
/*
* Change the proc title to include the VM name.
*/
diff --git a/usr.sbin/bhyve/fwctl.c b/usr.sbin/bhyve/fwctl.c
deleted file mode 100644
--- a/usr.sbin/bhyve/fwctl.c
+++ /dev/null
@@ -1,552 +0,0 @@
-/*-
- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
- *
- * Copyright (c) 2015 Peter Grehan <grehan@freebsd.org>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * $FreeBSD$
- */
-
-/*
- * Guest firmware interface. Uses i/o ports x510/x511 as Qemu does,
- * but with a request/response messaging protocol.
- */
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
-#include <sys/param.h>
-#include <sys/types.h>
-#include <sys/errno.h>
-#include <sys/uio.h>
-
-#include <assert.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "bhyverun.h"
-#include "inout.h"
-#include "fwctl.h"
-
-/*
- * Messaging protocol base operations
- */
-#define OP_NULL 1
-#define OP_ECHO 2
-#define OP_GET 3
-#define OP_GET_LEN 4
-#define OP_SET 5
-#define OP_MAX OP_SET
-
-/* I/O ports */
-#define FWCTL_OUT 0x510
-#define FWCTL_IN 0x511
-
-/*
- * Back-end state-machine
- */
-enum state {
- DORMANT,
- IDENT_WAIT,
- IDENT_SEND,
- REQ,
- RESP
-} be_state = DORMANT;
-
-static uint8_t sig[] = { 'B', 'H', 'Y', 'V' };
-static u_int ident_idx;
-
-struct op_info {
- int op;
- int (*op_start)(uint32_t len);
- void (*op_data)(uint32_t data, uint32_t len);
- int (*op_result)(struct iovec **data);
- void (*op_done)(struct iovec *data);
-};
-static struct op_info *ops[OP_MAX+1];
-
-/* Return 0-padded uint32_t */
-static uint32_t
-fwctl_send_rest(uint32_t *data, size_t len)
-{
- union {
- uint8_t c[4];
- uint32_t w;
- } u;
- uint8_t *cdata;
- int i;
-
- cdata = (uint8_t *) data;
- u.w = 0;
-
- for (i = 0, u.w = 0; i < len; i++)
- u.c[i] = *cdata++;
-
- return (u.w);
-}
-
-/*
- * error op dummy proto - drop all data sent and return an error
-*/
-static int errop_code;
-
-static void
-errop_set(int err)
-{
-
- errop_code = err;
-}
-
-static int
-errop_start(uint32_t len)
-{
- errop_code = ENOENT;
-
- /* accept any length */
- return (errop_code);
-}
-
-static void
-errop_data(uint32_t data, uint32_t len)
-{
-
- /* ignore */
-}
-
-static int
-errop_result(struct iovec **data)
-{
-
- /* no data to send back; always successful */
- *data = NULL;
- return (errop_code);
-}
-
-static void
-errop_done(struct iovec *data)
-{
-
- /* assert data is NULL */
-}
-
-static struct op_info errop_info = {
- .op_start = errop_start,
- .op_data = errop_data,
- .op_result = errop_result,
- .op_done = errop_done
-};
-
-/* OID search */
-SET_DECLARE(ctl_set, struct ctl);
-
-CTL_NODE("hw.ncpu", &guest_ncpus, sizeof(guest_ncpus));
-
-static struct ctl *
-ctl_locate(const char *str, int maxlen)
-{
- struct ctl *cp, **cpp;
-
- SET_FOREACH(cpp, ctl_set) {
- cp = *cpp;
- if (!strncmp(str, cp->c_oid, maxlen))
- return (cp);
- }
-
- return (NULL);
-}
-
-/* uefi-sysctl get-len */
-#define FGET_STRSZ 80
-static struct iovec fget_biov[2];
-static char fget_str[FGET_STRSZ];
-static struct {
- size_t f_sz;
- uint32_t f_data[1024];
-} fget_buf;
-static int fget_cnt;
-static size_t fget_size;
-
-static int
-fget_start(uint32_t len)
-{
-
- if (len > FGET_STRSZ)
- return(E2BIG);
-
- fget_cnt = 0;
-
- return (0);
-}
-
-static void
-fget_data(uint32_t data, uint32_t len)
-{
-
- *((uint32_t *) &fget_str[fget_cnt]) = data;
- fget_cnt += sizeof(uint32_t);
-}
-
-static int
-fget_result(struct iovec **data, int val)
-{
- struct ctl *cp;
- int err;
-
- err = 0;
-
- /* Locate the OID */
- cp = ctl_locate(fget_str, fget_cnt);
- if (cp == NULL) {
- *data = NULL;
- err = ENOENT;
- } else {
- if (val) {
- /* For now, copy the len/data into a buffer */
- memset(&fget_buf, 0, sizeof(fget_buf));
- fget_buf.f_sz = cp->c_len;
- memcpy(fget_buf.f_data, cp->c_data, cp->c_len);
- fget_biov[0].iov_base = (char *)&fget_buf;
- fget_biov[0].iov_len = sizeof(fget_buf.f_sz) +
- cp->c_len;
- } else {
- fget_size = cp->c_len;
- fget_biov[0].iov_base = (char *)&fget_size;
- fget_biov[0].iov_len = sizeof(fget_size);
- }
-
- fget_biov[1].iov_base = NULL;
- fget_biov[1].iov_len = 0;
- *data = fget_biov;
- }
-
- return (err);
-}
-
-static void
-fget_done(struct iovec *data)
-{
-
- /* nothing needs to be freed */
-}
-
-static int
-fget_len_result(struct iovec **data)
-{
- return (fget_result(data, 0));
-}
-
-static int
-fget_val_result(struct iovec **data)
-{
- return (fget_result(data, 1));
-}
-
-static struct op_info fgetlen_info = {
- .op_start = fget_start,
- .op_data = fget_data,
- .op_result = fget_len_result,
- .op_done = fget_done
-};
-
-static struct op_info fgetval_info = {
- .op_start = fget_start,
- .op_data = fget_data,
- .op_result = fget_val_result,
- .op_done = fget_done
-};
-
-static struct req_info {
- int req_error;
- u_int req_count;
- uint32_t req_size;
- uint32_t req_type;
- uint32_t req_txid;
- struct op_info *req_op;
- int resp_error;
- int resp_count;
- size_t resp_size;
- size_t resp_off;
- struct iovec *resp_biov;
-} rinfo;
-
-static void
-fwctl_response_done(void)
-{
-
- (*rinfo.req_op->op_done)(rinfo.resp_biov);
-
- /* reinit the req data struct */
- memset(&rinfo, 0, sizeof(rinfo));
-}
-
-static void
-fwctl_request_done(void)
-{
-
- rinfo.resp_error = (*rinfo.req_op->op_result)(&rinfo.resp_biov);
-
- /* XXX only a single vector supported at the moment */
- rinfo.resp_off = 0;
- if (rinfo.resp_biov == NULL) {
- rinfo.resp_size = 0;
- } else {
- rinfo.resp_size = rinfo.resp_biov[0].iov_len;
- }
-}
-
-static int
-fwctl_request_start(void)
-{
- int err;
-
- /* Data size doesn't include header */
- rinfo.req_size -= 12;
-
- rinfo.req_op = &errop_info;
- if (rinfo.req_type <= OP_MAX && ops[rinfo.req_type] != NULL)
- rinfo.req_op = ops[rinfo.req_type];
-
- err = (*rinfo.req_op->op_start)(rinfo.req_size);
-
- if (err) {
- errop_set(err);
- rinfo.req_op = &errop_info;
- }
-
- /* Catch case of zero-length message here */
- if (rinfo.req_size == 0) {
- fwctl_request_done();
- return (1);
- }
-
- return (0);
-}
-
-static int
-fwctl_request_data(uint32_t value)
-{
-
- /* Make sure remaining size is >= 0 */
- if (rinfo.req_size <= sizeof(uint32_t))
- rinfo.req_size = 0;
- else
- rinfo.req_size -= sizeof(uint32_t);
-
- (*rinfo.req_op->op_data)(value, rinfo.req_size);
-
- if (rinfo.req_size < sizeof(uint32_t)) {
- fwctl_request_done();
- return (1);
- }
-
- return (0);
-}
-
-static int
-fwctl_request(uint32_t value)
-{
-
- int ret;
-
- ret = 0;
-
- switch (rinfo.req_count) {
- case 0:
- /* Verify size */
- if (value < 12) {
- printf("msg size error");
- exit(4);
- }
- rinfo.req_size = value;
- rinfo.req_count = 1;
- break;
- case 1:
- rinfo.req_type = value;
- rinfo.req_count++;
- break;
- case 2:
- rinfo.req_txid = value;
- rinfo.req_count++;
- ret = fwctl_request_start();
- break;
- default:
- ret = fwctl_request_data(value);
- break;
- }
-
- return (ret);
-}
-
-static int
-fwctl_response(uint32_t *retval)
-{
- uint32_t *dp;
- ssize_t remlen;
-
- switch(rinfo.resp_count) {
- case 0:
- /* 4 x u32 header len + data */
- *retval = 4*sizeof(uint32_t) +
- roundup(rinfo.resp_size, sizeof(uint32_t));
- rinfo.resp_count++;
- break;
- case 1:
- *retval = rinfo.req_type;
- rinfo.resp_count++;
- break;
- case 2:
- *retval = rinfo.req_txid;
- rinfo.resp_count++;
- break;
- case 3:
- *retval = rinfo.resp_error;
- rinfo.resp_count++;
- break;
- default:
- remlen = rinfo.resp_size - rinfo.resp_off;
- dp = (uint32_t *)
- ((uint8_t *)rinfo.resp_biov->iov_base + rinfo.resp_off);
- if (remlen >= sizeof(uint32_t)) {
- *retval = *dp;
- } else if (remlen > 0) {
- *retval = fwctl_send_rest(dp, remlen);
- }
- rinfo.resp_off += sizeof(uint32_t);
- break;
- }
-
- if (rinfo.resp_count > 3 &&
- rinfo.resp_off >= rinfo.resp_size) {
- fwctl_response_done();
- return (1);
- }
-
- return (0);
-}
-
-
-/*
- * i/o port handling.
- */
-static uint8_t
-fwctl_inb(void)
-{
- uint8_t retval;
-
- retval = 0xff;
-
- switch (be_state) {
- case IDENT_SEND:
- retval = sig[ident_idx++];
- if (ident_idx >= sizeof(sig))
- be_state = REQ;
- break;
- default:
- break;
- }
-
- return (retval);
-}
-
-static void
-fwctl_outw(uint16_t val)
-{
- switch (be_state) {
- case IDENT_WAIT:
- if (val == 0) {
- be_state = IDENT_SEND;
- ident_idx = 0;
- }
- break;
- default:
- /* ignore */
- break;
- }
-}
-
-static uint32_t
-fwctl_inl(void)
-{
- uint32_t retval;
-
- switch (be_state) {
- case RESP:
- if (fwctl_response(&retval))
- be_state = REQ;
- break;
- default:
- retval = 0xffffffff;
- break;
- }
-
- return (retval);
-}
-
-static void
-fwctl_outl(uint32_t val)
-{
-
- switch (be_state) {
- case REQ:
- if (fwctl_request(val))
- be_state = RESP;
- default:
- break;
- }
-
-}
-
-static int
-fwctl_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
- uint32_t *eax, void *arg)
-{
-
- if (in) {
- if (bytes == 1)
- *eax = fwctl_inb();
- else if (bytes == 4)
- *eax = fwctl_inl();
- else
- *eax = 0xffff;
- } else {
- if (bytes == 2)
- fwctl_outw(*eax);
- else if (bytes == 4)
- fwctl_outl(*eax);
- }
-
- return (0);
-}
-INOUT_PORT(fwctl_wreg, FWCTL_OUT, IOPORT_F_INOUT, fwctl_handler);
-INOUT_PORT(fwctl_rreg, FWCTL_IN, IOPORT_F_IN, fwctl_handler);
-
-void
-fwctl_init(void)
-{
-
- ops[OP_GET_LEN] = &fgetlen_info;
- ops[OP_GET] = &fgetval_info;
-
- be_state = IDENT_WAIT;
-}
diff --git a/usr.sbin/bhyve/fwctl.h b/usr.sbin/bhyve/qemu_fwcfg.h
rename from usr.sbin/bhyve/fwctl.h
rename to usr.sbin/bhyve/qemu_fwcfg.h
--- a/usr.sbin/bhyve/fwctl.h
+++ b/usr.sbin/bhyve/qemu_fwcfg.h
@@ -1,7 +1,7 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
- * Copyright (c) 2015 Peter Grehan <grehan@freebsd.org>
+ * Copyright (c) 2021 Beckhoff Automation GmbH & Co. KG
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -13,10 +13,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR OR CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
@@ -28,29 +28,14 @@
* $FreeBSD$
*/
-#ifndef _FWCTL_H_
-#define _FWCTL_H_
+#pragma once
-#include <sys/linker_set.h>
+#include <vmmapi.h>
-/*
- * Linker set api for export of information to guest firmware via
- * a sysctl-like OID interface
- */
-struct ctl {
- const char *c_oid;
- const void *c_data;
- const int c_len;
-};
-
-#define CTL_NODE(oid, data, len) \
- static struct ctl __CONCAT(__ctl, __LINE__) = { \
- oid, \
- (data), \
- (len), \
- }; \
- DATA_SET(ctl_set, __CONCAT(__ctl, __LINE__))
-
-void fwctl_init(void);
+#define QEMU_FWCFG_MAX_ARCHS 0x2
+#define QEMU_FWCFG_MAX_ENTRIES 0x3FFF
+#define QEMU_FWCFG_MAX_NAME 56
-#endif /* _FWCTL_H_ */
+int qemu_fwcfg_add_file(uint8_t name[QEMU_FWCFG_MAX_NAME], uint32_t size,
+ void *data);
+int qemu_fwcfg_init(struct vmctx *ctx);
diff --git a/usr.sbin/bhyve/qemu_fwcfg.c b/usr.sbin/bhyve/qemu_fwcfg.c
new file mode 100644
--- /dev/null
+++ b/usr.sbin/bhyve/qemu_fwcfg.c
@@ -0,0 +1,402 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2021 Beckhoff Automation GmbH & Co. KG
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR OR CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/endian.h>
+
+#include <machine/vmm.h>
+
+#include <err.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "inout.h"
+#include "qemu_fwcfg.h"
+
+#define QEMU_FWCFG_SELECTOR_PORT_NUMBER 0x510
+#define QEMU_FWCFG_SELECTOR_PORT_SIZE 1
+#define QEMU_FWCFG_SELECTOR_PORT_FLAGS IOPORT_F_INOUT
+#define QEMU_FWCFG_DATA_PORT_NUMBER 0x511
+#define QEMU_FWCFG_DATA_PORT_SIZE 1
+#define QEMU_FWCFG_DATA_PORT_FLAGS \
+ IOPORT_F_INOUT /* QEMU v2.4+ ignores writes */
+
+#define QEMU_FWCFG_ARCHITECTURE_MASK 0x0001
+#define QEMU_FWCFG_INDEX_MASK 0x3FFF
+
+#define QEMU_FWCFG_SELECT_READ 0
+#define QEMU_FWCFG_SELECT_WRITE 1
+
+#define QEMU_FWCFG_ARCHITECTURE_GENERIC 0
+#define QEMU_FWCFG_ARCHITECTURE_SPECIFIC 1
+
+#define QEMU_FWCFG_INDEX_SIGNATURE 0x00
+#define QEMU_FWCFG_INDEX_ID 0x01
+#define QEMU_FWCFG_INDEX_FILE_DIR 0x19
+
+#define QEMU_FWCFG_FIRST_FILE_INDEX 0x20
+
+#define QEMU_FWCFG_MIN_FILES 10
+
+#pragma pack(push, 1)
+struct qemu_fwcfg_selector {
+ uint16_t index : 14;
+ uint16_t writeable : 1;
+ /*
+ * 0 = generic | for all architectures
+ * 1 = specific | only for current architecture
+ */
+ uint16_t architecture : 1;
+};
+
+struct qemu_fwcfg_item {
+ uint32_t size;
+ uint8_t *data;
+};
+
+struct qemu_fwcfg_signature {
+ uint8_t signature[4];
+};
+
+struct qemu_fwcfg_id {
+ uint32_t interface : 1; /* always set */
+ uint32_t DMA : 1;
+ uint32_t reserved : 30;
+};
+
+struct qemu_fwcfg_file {
+ uint32_t be_size;
+ uint16_t be_selector;
+ uint16_t reserved;
+ uint8_t name[QEMU_FWCFG_MAX_NAME];
+};
+
+struct qemu_fwcfg_directory {
+ uint32_t be_count;
+ struct qemu_fwcfg_file files[QEMU_FWCFG_MIN_FILES];
+};
+
+struct qemu_fwcfg_softc {
+ uint32_t data_offset;
+ struct qemu_fwcfg_selector selector;
+ struct qemu_fwcfg_item items[QEMU_FWCFG_MAX_ARCHS]
+ [QEMU_FWCFG_MAX_ENTRIES];
+ struct qemu_fwcfg_directory *directory;
+};
+#pragma pack(pop)
+
+static struct qemu_fwcfg_softc sc;
+
+static int
+qemu_fwcfg_selector_port_handler(struct vmctx *ctx, int vcpu, int in, int port,
+ int bytes, uint32_t *eax, void *arg)
+{
+ if (in) {
+ *eax = *(uint16_t *)&sc.selector;
+ return (0);
+ }
+
+ sc.data_offset = 0;
+ sc.selector = *(struct qemu_fwcfg_selector *)eax;
+
+ return (0);
+}
+
+static int
+qemu_fwcfg_data_port_handler(struct vmctx *ctx, int vcpu, int in, int port,
+ int bytes, uint32_t *eax, void *arg)
+{
+ if (!in) {
+ warnx("%s: Writes to qemu fwcfg data port aren't allowed",
+ __func__);
+ return (-1);
+ }
+
+ /* get fwcfg item */
+ struct qemu_fwcfg_item *item =
+ &sc.items[sc.selector.architecture][sc.selector.index];
+ if (item->data == NULL) {
+ warnx(
+ "%s: qemu fwcfg item doesn't exist (architecture %s index 0x%x)",
+ __func__, sc.selector.architecture ? "specific" : "generic",
+ sc.selector.index);
+ *eax = 0x00;
+ return (0);
+ } else if (sc.data_offset >= item->size) {
+ warnx(
+ "%s: qemu fwcfg item read exceeds size (architecture %s index 0x%x size 0x%x offset 0x%x)",
+ __func__, sc.selector.architecture ? "specific" : "generic",
+ sc.selector.index, item->size, sc.data_offset);
+ *eax = 0x00;
+ return (0);
+ }
+
+ /* return item data */
+ *eax = item->data[sc.data_offset];
+ sc.data_offset++;
+
+ return (0);
+}
+
+static int
+qemu_fwcfg_add_item(uint16_t architecture, uint16_t index, uint32_t size,
+ void *data)
+{
+ /* truncate architecture and index to their desired size */
+ architecture &= QEMU_FWCFG_ARCHITECTURE_MASK;
+ index &= QEMU_FWCFG_INDEX_MASK;
+
+ /* get pointer to item specified by selector */
+ struct qemu_fwcfg_item *fwcfg_item = &sc.items[architecture][index];
+
+ /* check if item is already used */
+ if (fwcfg_item->data != NULL) {
+ warnx("%s: qemu fwcfg item exists (architecture %s index 0x%x)",
+ __func__, architecture ? "specific" : "generic", index);
+ return (-1);
+ }
+
+ /* save data of the item */
+ fwcfg_item->size = size;
+ fwcfg_item->data = data;
+
+ return (0);
+}
+
+static int
+qemu_fwcfg_add_item_file_dir()
+{
+ /* alloc directory */
+ struct qemu_fwcfg_directory *fwcfg_directory = calloc(1,
+ sizeof(struct qemu_fwcfg_directory));
+ if (fwcfg_directory == NULL) {
+ return (-ENOMEM);
+ }
+
+ /* init directory */
+ sc.directory = fwcfg_directory;
+
+ /* add directory */
+ return qemu_fwcfg_add_item(QEMU_FWCFG_ARCHITECTURE_GENERIC,
+ QEMU_FWCFG_INDEX_FILE_DIR, sizeof(struct qemu_fwcfg_directory),
+ (uint8_t *)sc.directory);
+}
+
+static int
+qemu_fwcfg_add_item_id()
+{
+ /* alloc id */
+ struct qemu_fwcfg_id *fwcfg_id = calloc(1,
+ sizeof(struct qemu_fwcfg_id));
+ if (fwcfg_id == NULL) {
+ return (-ENOMEM);
+ }
+
+ /* init id */
+ fwcfg_id->interface = 1;
+ fwcfg_id->DMA = 0;
+
+ /*
+ * QEMU specifies ID as little endian.
+ * Convert fwcfg_id to little endian.
+ */
+ uint32_t *le_fwcfg_id_ptr = (uint32_t *)fwcfg_id;
+ *le_fwcfg_id_ptr = htole32(*le_fwcfg_id_ptr);
+
+ /* add id */
+ return qemu_fwcfg_add_item(QEMU_FWCFG_ARCHITECTURE_GENERIC,
+ QEMU_FWCFG_INDEX_ID, sizeof(struct qemu_fwcfg_id),
+ (uint8_t *)fwcfg_id);
+}
+
+static int
+qemu_fwcfg_add_item_signature()
+{
+ /* alloc signature */
+ struct qemu_fwcfg_signature *fwcfg_signature = calloc(1,
+ sizeof(struct qemu_fwcfg_signature));
+ if (fwcfg_signature == NULL) {
+ return (-ENOMEM);
+ }
+
+ /* init signature */
+ fwcfg_signature->signature[0] = 'Q';
+ fwcfg_signature->signature[1] = 'E';
+ fwcfg_signature->signature[2] = 'M';
+ fwcfg_signature->signature[3] = 'U';
+
+ /* add signature */
+ return qemu_fwcfg_add_item(QEMU_FWCFG_ARCHITECTURE_GENERIC,
+ QEMU_FWCFG_INDEX_SIGNATURE, sizeof(struct qemu_fwcfg_signature),
+ (uint8_t *)fwcfg_signature);
+}
+
+static int
+qemu_fwcfg_register_port(const char *name, int port, int size, int flags,
+ inout_func_t handler)
+{
+ struct inout_port iop;
+
+ bzero(&iop, sizeof(iop));
+ iop.name = name;
+ iop.port = port;
+ iop.size = size;
+ iop.flags = flags;
+ iop.handler = handler;
+
+ return register_inout(&iop);
+}
+
+int
+qemu_fwcfg_add_file(uint8_t name[QEMU_FWCFG_MAX_NAME], uint32_t size,
+ void *data)
+{
+ /*
+ * QEMU specifies count as big endian.
+ * Convert it to host endian to work with it.
+ */
+ uint32_t count = be32toh(sc.directory->be_count);
+
+ /* add file to items list */
+ uint32_t index = QEMU_FWCFG_FIRST_FILE_INDEX + count;
+ const int error = qemu_fwcfg_add_item(QEMU_FWCFG_ARCHITECTURE_GENERIC,
+ index, size, data);
+ if (error != 0) {
+ return (error);
+ }
+
+ /*
+ * files should be sorted alphabetical, get index for new file
+ */
+ uint32_t file_index;
+ for (file_index = 0; file_index < count; ++file_index) {
+ if (strcmp(name, sc.directory->files[file_index].name) < 0)
+ break;
+ }
+
+ ++count;
+ if (count > QEMU_FWCFG_MIN_FILES) {
+ /* alloc new file directory */
+ uint64_t new_size = sizeof(struct qemu_fwcfg_directory) +
+ (count - QEMU_FWCFG_MIN_FILES) *
+ sizeof(struct qemu_fwcfg_file);
+ struct qemu_fwcfg_directory *new_directory = calloc(1,
+ new_size);
+ if (new_directory == NULL) {
+ warnx(
+ "%s: Unable to allocate a new qemu fwcfg files directory (count %d)",
+ __func__, count);
+ return (-ENOMEM);
+ }
+
+ /* copy files below i to new directory */
+ memcpy(new_directory->files, sc.directory->files,
+ file_index * sizeof(struct qemu_fwcfg_file));
+
+ /* copy files behind i to directory */
+ memcpy(&new_directory->files[file_index + 1],
+ &sc.directory->files[file_index],
+ (count - file_index) * sizeof(struct qemu_fwcfg_file));
+
+ /* free old directory */
+ free(sc.directory);
+
+ /* set directory pointer to new directory */
+ sc.directory = new_directory;
+
+ /* adjust directory pointer */
+ sc.items[0][QEMU_FWCFG_INDEX_FILE_DIR].data = (uint8_t *)
+ sc.directory;
+ } else {
+ /* shift files behind i */
+ for (int32_t i = QEMU_FWCFG_MIN_FILES - 2; i >= 0; --i) {
+ memcpy(&sc.directory->files[i + 1],
+ &sc.directory->files[i],
+ sizeof(struct qemu_fwcfg_file));
+ }
+ }
+
+ /*
+ * QEMU specifies count, size and index as big endian.
+ * Save these values in big endian to simplify guest reads of these
+ * values.
+ */
+ sc.directory->be_count = htobe32(count);
+ sc.directory->files[file_index].be_size = htobe32(size);
+ sc.directory->files[file_index].be_selector = htobe16(index);
+ strcpy(sc.directory->files[file_index].name, name);
+
+ return (0);
+}
+
+int
+qemu_fwcfg_init(struct vmctx *ctx)
+{
+ int error;
+
+ /* add common fwcfg items */
+ if ((error = qemu_fwcfg_add_item_signature()) != 0) {
+ warnx("%s: Unable to add signature item", __func__);
+ return (error);
+ }
+ if ((error = qemu_fwcfg_add_item_id()) != 0) {
+ warnx("%s: Unable to add id item", __func__);
+ return (error);
+ }
+ if ((error = qemu_fwcfg_add_item_file_dir()) != 0) {
+ warnx("%s: Unable to add file_dir item", __func__);
+ return (error);
+ }
+
+ /* add handlers for fwcfg ports */
+ if ((error = qemu_fwcfg_register_port("qemu_fwcfg_selector",
+ QEMU_FWCFG_SELECTOR_PORT_NUMBER, QEMU_FWCFG_SELECTOR_PORT_SIZE,
+ QEMU_FWCFG_SELECTOR_PORT_FLAGS,
+ qemu_fwcfg_selector_port_handler)) != 0) {
+ warnx("%s: Unable to register qemu fwcfg selector port 0x%x",
+ __func__, QEMU_FWCFG_SELECTOR_PORT_NUMBER);
+ return (error);
+ }
+ if ((error = qemu_fwcfg_register_port("qemu_fwcfg_data",
+ QEMU_FWCFG_DATA_PORT_NUMBER, QEMU_FWCFG_DATA_PORT_SIZE,
+ QEMU_FWCFG_DATA_PORT_FLAGS, qemu_fwcfg_data_port_handler)) !=
+ 0) {
+ warnx("%s: Unable to register qemu fwcfg data port 0x%x",
+ __func__, QEMU_FWCFG_DATA_PORT_NUMBER);
+ return (error);
+ }
+
+ return (0);
+}

File Metadata

Mime Type
text/plain
Expires
Sat, Dec 13, 7:41 AM (20 h, 8 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
26925363
Default Alt Text
D31578.id93806.diff (25 KB)

Event Timeline