Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F139456786
D31578.id93806.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
25 KB
Referenced Files
None
Subscribers
None
D31578.id93806.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D31578: bhyve: add support for QEMU's fwcfg
Attached
Detach File
Event Timeline
Log In to Comment