Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 18 Aug 2023 07:28:40 GMT
From:      Corvin =?utf-8?Q?K=C3=B6hne?= <corvink@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org
Subject:   git: b1fffed683ab - stable/13 - bhyve: add cmdline option for user defined fw_cfg items
Message-ID:  <202308180728.37I7SelL034235@gitrepo.freebsd.org>

next in thread | raw e-mail | index | archive | help
The branch stable/13 has been updated by corvink:

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

commit b1fffed683ab82b2693e7b5bb81ca72be4d348ba
Author:     Corvin Köhne <corvink@FreeBSD.org>
AuthorDate: 2021-09-08 09:31:21 +0000
Commit:     Corvin Köhne <corvink@FreeBSD.org>
CommitDate: 2023-08-18 07:24:54 +0000

    bhyve: add cmdline option for user defined fw_cfg items
    
    Some guest allow to configure themself by fw_cfg. E.g. Fedora CoreOs can
    be provisioned by adding a JSON file as fw_cfg item.
    
    Reviewed by:            jhb
    MFC after:              1 week
    Sponsored by:           Beckhoff Automation GmbH & Co. KG
    Differential Revision:  https://reviews.freebsd.org/D38338
    
    (cherry picked from commit ca14781c8170f3517ae79e198c0c880dbc3142dd)
    
    bhyve: error out if fwcfg user file isn't read completely
    
    At the moment, fwcfg reads the file once at startup and passes these
    data to the guest. Therefore, we should always read the whole file.
    Otherwise we should error out.
    
    Additionally, GCC12 complains that the comparison whether
    fwcfg_file->size is lower than 0 is always false due to the limited
    range of data type.
    
    Reviewed by:            markj
    Fixes:                  ca14781c8170f3517ae79e198c0c880dbc3142dd ("bhyve: add cmdline option for user defined fw_cfg items")
    MFC after:              1 week
    Sponsored by:           Beckhoff Automation GmbH & Co. KG
    Differential Revision:  https://reviews.freebsd.org/D40076
    
    (cherry picked from commit 26d9f973d8691eccc098ded7326137d6f76ad243)
---
 usr.sbin/bhyve/bhyve.8      |  19 ++++++
 usr.sbin/bhyve/bhyverun.c   |   9 ++-
 usr.sbin/bhyve/qemu_fwcfg.c | 145 ++++++++++++++++++++++++++++++++++++++++++++
 usr.sbin/bhyve/qemu_fwcfg.h |   1 +
 4 files changed, 172 insertions(+), 2 deletions(-)

diff --git a/usr.sbin/bhyve/bhyve.8 b/usr.sbin/bhyve/bhyve.8
index ed9c045c3159..b764807c7980 100644
--- a/usr.sbin/bhyve/bhyve.8
+++ b/usr.sbin/bhyve/bhyve.8
@@ -45,6 +45,15 @@
 .Op Cm ,threads= Ar n
 .Oc
 .Sm on
+.Oo Fl f
+.Sm off
+.Ar name Cm \&,
+.Oo
+.Cm string No | Cm file
+.Oc
+.Cm \&= Ar data
+.Sm on
+.Oc
 .Oo
 .Sm off
 .Fl G\~
@@ -145,6 +154,16 @@ Force
 .Nm
 to exit when a guest issues an access to an I/O port that is not emulated.
 This is intended for debug purposes.
+.It Fl f Ar name Ns Cm \&, Ns Oo Cm string Ns No | Ns Cm file Ns Oc Ns Cm \&= Ns Ar data
+Add a fw_cfg file
+.Ar name
+to the fw_cfg interface.
+If a
+.Cm string
+is specified, the fw_cfg file contains the string as data.
+If a
+.Cm file
+is specified, bhyve reads the file and adds the file content as fw_cfg data.
 .It Fl G Xo
 .Sm off
 .Oo Ar w Oc
diff --git a/usr.sbin/bhyve/bhyverun.c b/usr.sbin/bhyve/bhyverun.c
index 4db8274335cd..7cc293827518 100644
--- a/usr.sbin/bhyve/bhyverun.c
+++ b/usr.sbin/bhyve/bhyverun.c
@@ -1258,9 +1258,9 @@ main(int argc, char *argv[])
 	progname = basename(argv[0]);
 
 #ifdef BHYVE_SNAPSHOT
-	optstr = "aehuwxACDHIPSWYk:o:p:G:c:s:m:l:K:U:r:";
+	optstr = "aehuwxACDHIPSWYk:f:o:p:G:c:s:m:l:K:U:r:";
 #else
-	optstr = "aehuwxACDHIPSWYk:o:p:G:c:s:m:l:K:U:";
+	optstr = "aehuwxACDHIPSWYk:f:o:p:G:c:s:m:l:K:U:";
 #endif
 	while ((c = getopt(argc, argv, optstr)) != -1) {
 		switch (c) {
@@ -1288,6 +1288,11 @@ main(int argc, char *argv[])
 		case 'C':
 			set_config_bool("memory.guest_in_core", true);
 			break;
+		case 'f':
+			if (qemu_fwcfg_parse_cmdline_arg(optarg) != 0) {
+			    errx(EX_USAGE, "invalid fwcfg item '%s'", optarg);
+			}
+			break;
 		case 'G':
 			parse_gdb_options(optarg);
 			break;
diff --git a/usr.sbin/bhyve/qemu_fwcfg.c b/usr.sbin/bhyve/qemu_fwcfg.c
index e88608d90cae..e845c70950b1 100644
--- a/usr.sbin/bhyve/qemu_fwcfg.c
+++ b/usr.sbin/bhyve/qemu_fwcfg.c
@@ -7,13 +7,18 @@
 
 #include <sys/param.h>
 #include <sys/endian.h>
+#include <sys/queue.h>
+#include <sys/stat.h>
 
 #include <machine/vmm.h>
 
 #include <err.h>
 #include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <unistd.h>
 
 #include "acpi_device.h"
 #include "bhyverun.h"
@@ -98,6 +103,15 @@ struct qemu_fwcfg_softc {
 
 static struct qemu_fwcfg_softc fwcfg_sc;
 
+struct qemu_fwcfg_user_file {
+	STAILQ_ENTRY(qemu_fwcfg_user_file) chain;
+	uint8_t name[QEMU_FWCFG_MAX_NAME];
+	uint32_t size;
+	void *data;
+};
+static STAILQ_HEAD(qemu_fwcfg_user_file_list,
+    qemu_fwcfg_user_file) user_files = STAILQ_HEAD_INITIALIZER(user_files);
+
 static int
 qemu_fwcfg_selector_port_handler(struct vmctx *const ctx __unused, const int in,
     const int port __unused, const int bytes, uint32_t *const eax,
@@ -384,6 +398,22 @@ qemu_fwcfg_add_file(const char *name, const uint32_t size, void *const data)
 	return (0);
 }
 
+static int
+qemu_fwcfg_add_user_files(void)
+{
+	const struct qemu_fwcfg_user_file *fwcfg_file;
+	int error;
+
+	STAILQ_FOREACH(fwcfg_file, &user_files, chain) {
+		error = qemu_fwcfg_add_file(fwcfg_file->name, fwcfg_file->size,
+		    fwcfg_file->data);
+		if (error)
+			return (error);
+	}
+
+	return (0);
+}
+
 static const struct acpi_device_emul qemu_fwcfg_acpi_device_emul = {
 	.name = QEMU_FWCFG_ACPI_DEVICE_NAME,
 	.hid = QEMU_FWCFG_ACPI_HARDWARE_ID,
@@ -458,6 +488,11 @@ qemu_fwcfg_init(struct vmctx *const ctx)
 	}
 	if ((error = qemu_fwcfg_add_item_file_dir()) != 0) {
 		warnx("%s: Unable to add file_dir item", __func__);
+	}
+
+	/* add user defined fwcfg files */
+	if ((error = qemu_fwcfg_add_user_files()) != 0) {
+		warnx("%s: Unable to add user files", __func__);
 		goto done;
 	}
 
@@ -468,3 +503,113 @@ done:
 
 	return (error);
 }
+
+static void
+qemu_fwcfg_usage(const char *opt)
+{
+	warnx("Invalid fw_cfg option \"%s\"", opt);
+	warnx("-f [name=]<name>,(string|file)=<value>");
+}
+
+/*
+ * Parses the cmdline argument for user defined fw_cfg items. The cmdline
+ * argument has the format:
+ * "-f [name=]<name>,(string|file)=<value>"
+ *
+ * E.g.: "-f opt/com.page/example,string=Hello"
+ */
+int
+qemu_fwcfg_parse_cmdline_arg(const char *opt)
+{
+	struct qemu_fwcfg_user_file *fwcfg_file;
+	struct stat sb;
+	const char *opt_ptr, *opt_end;
+	ssize_t bytes_read;
+	int fd;
+	
+	fwcfg_file = malloc(sizeof(*fwcfg_file));
+	if (fwcfg_file == NULL) {
+		warnx("Unable to allocate fw_cfg_user_file");
+		return (ENOMEM);
+	}
+
+	/* get pointer to <name> */
+	opt_ptr = opt;
+	/* If [name=] is specified, skip it */
+	if (strncmp(opt_ptr, "name=", sizeof("name=") - 1) == 0) {
+		opt_ptr += sizeof("name=") - 1;
+	}
+
+	/* get the end of <name> */
+	opt_end = strchr(opt_ptr, ',');
+	if (opt_end == NULL) {
+		qemu_fwcfg_usage(opt);
+		return (EINVAL);
+	}
+
+	/* check if <name> is too long */
+	if (opt_end - opt_ptr >= QEMU_FWCFG_MAX_NAME) {
+		warnx("fw_cfg name too long: \"%s\"", opt);
+		return (EINVAL);
+	}
+
+	/* save <name> */
+	strncpy(fwcfg_file->name, opt_ptr, opt_end - opt_ptr);
+	fwcfg_file->name[opt_end - opt_ptr] = '\0';
+
+	/* set opt_ptr and opt_end to <value> */
+	opt_ptr = opt_end + 1;
+	opt_end = opt_ptr + strlen(opt_ptr);
+
+	if (strncmp(opt_ptr, "string=", sizeof("string=") - 1) == 0) {
+		opt_ptr += sizeof("string=") - 1;
+		fwcfg_file->data = strdup(opt_ptr);
+		if (fwcfg_file->data == NULL) {
+			warnx("Can't duplicate fw_cfg_user_file string \"%s\"",
+			    opt_ptr);
+			return (ENOMEM);
+		}
+		fwcfg_file->size = strlen(opt_ptr) + 1;
+	} else if (strncmp(opt_ptr, "file=", sizeof("file=") - 1) == 0) {
+		opt_ptr += sizeof("file=") - 1;
+
+		fd = open(opt_ptr, O_RDONLY);
+		if (fd < 0) {
+			warn("Can't open fw_cfg_user_file file \"%s\"",
+			    opt_ptr);
+			return (EINVAL);
+		}
+
+		if (fstat(fd, &sb) < 0) {
+			warn("Unable to get size of file \"%s\"", opt_ptr);
+			close(fd);
+			return (-1);
+		}
+
+		fwcfg_file->data = malloc(sb.st_size);
+		if (fwcfg_file->data == NULL) {
+			warnx(
+			    "Can't allocate fw_cfg_user_file file \"%s\" (size: 0x%16lx)",
+			    opt_ptr, sb.st_size);
+			close(fd);
+			return (ENOMEM);
+		}
+		bytes_read = read(fd, fwcfg_file->data, sb.st_size);
+		if (bytes_read < 0 || bytes_read != sb.st_size) {
+			warn("Unable to read file \"%s\"", opt_ptr);
+			free(fwcfg_file->data);
+			close(fd);
+			return (-1);
+		}
+		fwcfg_file->size = bytes_read;
+
+		close(fd);
+	} else {
+		qemu_fwcfg_usage(opt);
+		return (EINVAL);
+	}
+
+	STAILQ_INSERT_TAIL(&user_files, fwcfg_file, chain);
+
+	return (0);
+}
diff --git a/usr.sbin/bhyve/qemu_fwcfg.h b/usr.sbin/bhyve/qemu_fwcfg.h
index def0487fdf02..5c73e8309c6e 100644
--- a/usr.sbin/bhyve/qemu_fwcfg.h
+++ b/usr.sbin/bhyve/qemu_fwcfg.h
@@ -23,3 +23,4 @@ struct qemu_fwcfg_item {
 int qemu_fwcfg_add_file(const char *name,
     const uint32_t size, void *const data);
 int qemu_fwcfg_init(struct vmctx *const ctx);
+int qemu_fwcfg_parse_cmdline_arg(const char *opt);



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202308180728.37I7SelL034235>