git: d85147f3d681 - main - bhyve: add cmdline option to enable qemu's fwcfg

From: Corvin Köhne <corvink_at_FreeBSD.org>
Date: Fri, 17 Mar 2023 08:35:43 UTC
The branch main has been updated by corvink:

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

commit d85147f3d6811c04e250bfb4597783d8844de315
Author:     Corvin Köhne <corvink@FreeBSD.org>
AuthorDate: 2021-08-18 07:31:59 +0000
Commit:     Corvin Köhne <corvink@FreeBSD.org>
CommitDate: 2023-03-17 08:35:36 +0000

    bhyve: add cmdline option to enable qemu's fwcfg
    
    Let the user decide if he wants to use bhyve's fwctl or qemu's fwcfg. He
    can set the interface by adding a fwcfg option to bootrom:
    
    -l bootrom,<path/to/rom>,fwcfg=bhyve
    -l bootrom,<path/to/rom>,fwcfg=qemu
    
    Reviewed by:            markj
    MFC after:              1 week
    Sponsored by:           Beckhoff Automation GmbH & Co. KG
    Differential Revision:  https://reviews.freebsd.org/D38337
---
 usr.sbin/bhyve/Makefile       |  1 +
 usr.sbin/bhyve/bhyve.8        | 29 ++++++++++++++++--
 usr.sbin/bhyve/bhyve_config.5 |  8 +++++
 usr.sbin/bhyve/bhyverun.c     | 16 +++++++++-
 usr.sbin/bhyve/pci_lpc.c      | 18 ++++++++++-
 usr.sbin/bhyve/pci_lpc.h      |  1 +
 usr.sbin/bhyve/qemu_fwcfg.c   | 71 +++++++++++++++++++++++++------------------
 7 files changed, 111 insertions(+), 33 deletions(-)

diff --git a/usr.sbin/bhyve/Makefile b/usr.sbin/bhyve/Makefile
index 1c679a3c1578..a5a1dafebcd9 100644
--- a/usr.sbin/bhyve/Makefile
+++ b/usr.sbin/bhyve/Makefile
@@ -63,6 +63,7 @@ SRCS=	\
 	post.c			\
 	ps2kbd.c		\
 	ps2mouse.c		\
+	qemu_fwcfg.c		\
 	rfb.c			\
 	rtc.c			\
 	smbiostbl.c		\
diff --git a/usr.sbin/bhyve/bhyve.8 b/usr.sbin/bhyve/bhyve.8
index 84e031f1340c..72018912e2c5 100644
--- a/usr.sbin/bhyve/bhyve.8
+++ b/usr.sbin/bhyve/bhyve.8
@@ -200,7 +200,9 @@ and
 .Cm com4 ,
 the boot ROM device
 .Cm bootrom ,
-and the debug/test device
+the
+.Cm fwcfg
+type and the debug/test device
 .Cm pc-testdev .
 .Pp
 The possible values for the
@@ -345,7 +347,7 @@ PCI 16550 serial device.
 .It Cm lpc
 LPC PCI-ISA bridge with COM1, COM2, COM3, and COM4 16550 serial ports,
 a boot ROM, and,
-optionally, the debug/test device.
+optionally, a fwcfg type and the debug/test device.
 The LPC bridge emulation can only be configured on bus 0.
 .It Cm fbuf
 Raw framebuffer device attached to VNC server.
@@ -520,6 +522,29 @@ address space, and any modifications the guest makes will be saved
 to that file.
 .El
 .Pp
+Fwcfg types:
+.Bl -tag -width 10n
+.It Ar fwcfg
+The fwcfg interface is used to pass information such as the CPU count or ACPI tables to the guest firmware.
+Supported values are
+.Ql bhyve
+and
+.Ql qemu .
+Due to backward compatibility reasons,
+.Ql bhyve
+is the default option.
+When
+.Ql bhyve
+is used, bhyve's fwctl interface is used.
+It currently reports only the CPU count to the guest firmware.
+The
+.Ql qemu
+option uses QEMU's fwcfg interface.
+This interface is widely used and allows user-defined information to be passed to the guest.
+It is used for passing the CPU count, ACPI tables, a boot order and many other things to the guest.
+Some operating systems such as Fedora CoreOS can be configured by qemu's fwcfg interface as well.
+.El
+.Pp
 Pass-through device backends:
 .Sm off
 .Bl -bullet
diff --git a/usr.sbin/bhyve/bhyve_config.5 b/usr.sbin/bhyve/bhyve_config.5
index 94a8a4d5cb1d..32658c11f9e2 100644
--- a/usr.sbin/bhyve/bhyve_config.5
+++ b/usr.sbin/bhyve/bhyve_config.5
@@ -259,6 +259,7 @@ VGA framebuffer device attached to VNC server.
 .It Li lpc
 LPC PCI-ISA bridge with COM1-COM4 16550 serial ports,
 a boot ROM,
+an optional fwcfg type,
 and an optional debug/test device.
 This device must be configured on bus 0.
 .It Li hda
@@ -529,6 +530,13 @@ Settings for the COM2 serial port device.
 Settings for the COM3 serial port device.
 .It Va com4 Ta node Ta Ta
 Settings for the COM4 serial port device.
+.It Va fwcfg Ta string Ta bhyve Ta
+The fwcfg type to be used.
+Supported values are
+.Dq bhyve
+for fwctl and
+.Dq qemu
+for fwcfg.
 .It Va pc-testdev Ta bool Ta false Ta
 Enable the PC debug/test device.
 .El
diff --git a/usr.sbin/bhyve/bhyverun.c b/usr.sbin/bhyve/bhyverun.c
index af4d15666ae3..665fec73e48c 100644
--- a/usr.sbin/bhyve/bhyverun.c
+++ b/usr.sbin/bhyve/bhyverun.c
@@ -101,6 +101,7 @@ __FBSDID("$FreeBSD$");
 #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"
@@ -1231,6 +1232,7 @@ set_defaults(void)
 	set_config_bool("acpi_tables", false);
 	set_config_value("memory.size", "256M");
 	set_config_bool("x86.strictmsr", true);
+	set_config_value("lpc.fwcfg", "bhyve");
 }
 
 int
@@ -1460,6 +1462,17 @@ main(int argc, char *argv[])
 	rtc_init(ctx);
 	sci_init(ctx);
 
+	if (qemu_fwcfg_init(ctx) != 0) {
+		fprintf(stderr, "qemu fwcfg initialization error");
+		exit(4);
+	}
+
+	if (qemu_fwcfg_add_file("opt/bhyve/hw.ncpu", sizeof(guest_ncpus),
+	    &guest_ncpus) != 0) {
+		fprintf(stderr, "Could not add qemu fwcfg opt/bhyve/hw.ncpu");
+		exit(4);
+	}
+
 	/*
 	 * Exit if a device emulation finds an error in its initilization
 	 */
@@ -1554,8 +1567,9 @@ main(int argc, char *argv[])
 		assert(error == 0);
 	}
 
-	if (lpc_bootrom())
+	if (lpc_bootrom() && strcmp(lpc_fwcfg(), "bhyve") == 0) {
 		fwctl_init();
+	}
 
 	/*
 	 * Change the proc title to include the VM name.
diff --git a/usr.sbin/bhyve/pci_lpc.c b/usr.sbin/bhyve/pci_lpc.c
index 548726e27d0d..ad47230c005e 100644
--- a/usr.sbin/bhyve/pci_lpc.c
+++ b/usr.sbin/bhyve/pci_lpc.c
@@ -110,10 +110,20 @@ lpc_device_parse(const char *opts)
 			set_config_value("lpc.bootrom", romfile);
 
 			varfile = strsep(&str, ",");
-			if (varfile != NULL) {
+			if (varfile == NULL) {
+				error = 0;
+				goto done;
+			}
+			if (strchr(varfile, '=') == NULL) {
 				set_config_value("lpc.bootvars", varfile);
+			} else {
+				/* varfile doesn't exist, it's another config
+				 * option */
+				pci_parse_legacy_config(find_config_node("lpc"),
+				    varfile);
 			}
 
+			pci_parse_legacy_config(find_config_node("lpc"), str);
 			error = 0;
 			goto done;
 		}
@@ -160,6 +170,12 @@ lpc_bootrom(void)
 	return (get_config_value("lpc.bootrom"));
 }
 
+const char *
+lpc_fwcfg(void)
+{
+	return (get_config_value("lpc.fwcfg"));
+}
+
 static void
 lpc_uart_intr_assert(void *arg)
 {
diff --git a/usr.sbin/bhyve/pci_lpc.h b/usr.sbin/bhyve/pci_lpc.h
index 611b025d4386..ff3ea98b4f9c 100644
--- a/usr.sbin/bhyve/pci_lpc.h
+++ b/usr.sbin/bhyve/pci_lpc.h
@@ -72,5 +72,6 @@ void    lpc_print_supported_devices(void);
 char	*lpc_pirq_name(int pin);
 void	lpc_pirq_routed(void);
 const char *lpc_bootrom(void);
+const char *lpc_fwcfg(void);
 
 #endif
diff --git a/usr.sbin/bhyve/qemu_fwcfg.c b/usr.sbin/bhyve/qemu_fwcfg.c
index 5ee35d6764e6..1b0b5e3e9931 100644
--- a/usr.sbin/bhyve/qemu_fwcfg.c
+++ b/usr.sbin/bhyve/qemu_fwcfg.c
@@ -17,6 +17,7 @@
 
 #include "acpi_device.h"
 #include "inout.h"
+#include "pci_lpc.h"
 #include "qemu_fwcfg.h"
 
 #define QEMU_FWCFG_ACPI_DEVICE_NAME "FWCF"
@@ -351,37 +352,49 @@ qemu_fwcfg_init(struct vmctx *const ctx)
 {
 	int error;
 
-	error = acpi_device_create(&fwcfg_sc.acpi_dev, ctx,
-	    QEMU_FWCFG_ACPI_DEVICE_NAME, QEMU_FWCFG_ACPI_HARDWARE_ID);
-	if (error) {
-		warnx("%s: failed to create ACPI device for QEMU FwCfg",
-		    __func__);
-		goto done;
-	}
+	/*
+	 * Bhyve supports fwctl (bhyve) and fwcfg (qemu) as firmware interfaces.
+	 * Both are using the same ports. So, it's not possible to provide both
+	 * interfaces at the same time to the guest. Therefore, only create acpi
+	 * tables and register io ports for fwcfg, if it's used.
+	 */
+	if (strcmp(lpc_fwcfg(), "qemu") == 0) {
+		error = acpi_device_create(&fwcfg_sc.acpi_dev, ctx,
+		    QEMU_FWCFG_ACPI_DEVICE_NAME, QEMU_FWCFG_ACPI_HARDWARE_ID);
+		if (error) {
+			warnx("%s: failed to create ACPI device for QEMU FwCfg",
+			    __func__);
+			goto done;
+		}
 
-	error = acpi_device_add_res_fixed_ioport(fwcfg_sc.acpi_dev,
-	    QEMU_FWCFG_SELECTOR_PORT_NUMBER, 2);
-	if (error) {
-		warnx("%s: failed to add fixed IO port for QEMU FwCfg",
-		    __func__);
-		goto done;
-	}
+		error = acpi_device_add_res_fixed_ioport(fwcfg_sc.acpi_dev,
+		    QEMU_FWCFG_SELECTOR_PORT_NUMBER, 2);
+		if (error) {
+			warnx("%s: failed to add fixed IO port for QEMU FwCfg",
+			    __func__);
+			goto done;
+		}
 
-	/* 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);
-		goto done;
-	}
-	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);
-		goto done;
+		/* 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);
+			goto done;
+		}
+		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);
+			goto done;
+		}
 	}
 
 	/* add common fwcfg items */