git: 981f9f7495bb - main - bhyve: Push option parsing down into bhyverun_machdep.c

From: Mark Johnston <markj_at_FreeBSD.org>
Date: Wed, 10 Apr 2024 15:19:21 UTC
The branch main has been updated by markj:

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

commit 981f9f7495bb8247e0eba48e15dcc7f2e0b1b342
Author:     Mark Johnston <markj@FreeBSD.org>
AuthorDate: 2024-04-03 17:44:40 +0000
Commit:     Mark Johnston <markj@FreeBSD.org>
CommitDate: 2024-04-10 15:17:56 +0000

    bhyve: Push option parsing down into bhyverun_machdep.c
    
    After a couple of attempts I think this is the cleanest approach despite
    the expense of some code duplication.  Quite a few of the single-letter
    bhyve options are x86-specific.
    
    I think that going forward we should strongly discourage the addition of
    new options and instead configure guests using the more general
    configuration file syntax.
    
    Reviewed by:    corvink, jhb
    MFC after:      2 weeks
    Sponsored by:   Innovate UK
    Differential Revision:  https://reviews.freebsd.org/D41753
---
 usr.sbin/bhyve/aarch64/bhyverun_machdep.c |  98 ++++++++++++++
 usr.sbin/bhyve/amd64/bhyverun_machdep.c   | 181 +++++++++++++++++++++++++
 usr.sbin/bhyve/bhyverun.c                 | 212 +++---------------------------
 usr.sbin/bhyve/bhyverun.h                 |  12 ++
 4 files changed, 307 insertions(+), 196 deletions(-)

diff --git a/usr.sbin/bhyve/aarch64/bhyverun_machdep.c b/usr.sbin/bhyve/aarch64/bhyverun_machdep.c
index 9b0010a78b47..22b8b85e6b9b 100644
--- a/usr.sbin/bhyve/aarch64/bhyverun_machdep.c
+++ b/usr.sbin/bhyve/aarch64/bhyverun_machdep.c
@@ -34,7 +34,9 @@
 #include <err.h>
 #include <errno.h>
 #include <fcntl.h>
+#include <stdlib.h>
 #include <string.h>
+#include <sysexits.h>
 #include <unistd.h>
 
 #include <vmmapi.h>
@@ -44,6 +46,7 @@
 #include "debug.h"
 #include "fdt.h"
 #include "mem.h"
+#include "pci_emul.h"
 #include "uart_emul.h"
 
 /* Start of mem + 1M */
@@ -73,6 +76,101 @@ bhyve_init_config(void)
 	set_config_value("memory.size", "256M");
 }
 
+void
+bhyve_usage(int code)
+{
+	const char *progname;
+
+	progname = getprogname();
+
+	fprintf(stderr,
+	    "Usage: %s [-CDHhSW]\n"
+	    "       %*s [-c [[cpus=]numcpus][,sockets=n][,cores=n][,threads=n]]\n"
+	    "       %*s [-k config_file] [-m mem] [-o var=value]\n"
+	    "       %*s [-p vcpu:hostcpu] [-r file] [-s pci] [-U uuid] vmname\n"
+	    "       -C: include guest memory in core file\n"
+	    "       -c: number of CPUs and/or topology specification\n"
+	    "       -D: destroy on power-off\n"
+	    "       -h: help\n"
+	    "       -k: key=value flat config file\n"
+	    "       -m: memory size\n"
+	    "       -o: set config 'var' to 'value'\n"
+	    "       -p: pin 'vcpu' to 'hostcpu'\n"
+	    "       -S: guest memory cannot be swapped\n"
+	    "       -s: <slot,driver,configinfo> PCI slot config\n"
+	    "       -U: UUID\n"
+	    "       -W: force virtio to use single-vector MSI\n",
+	    progname, (int)strlen(progname), "", (int)strlen(progname), "",
+	    (int)strlen(progname), "");
+	exit(code);
+}
+
+void
+bhyve_optparse(int argc, char **argv)
+{
+	const char *optstr;
+	int c;
+
+	optstr = "hCDSWk:f:o:p:c:s:m:U:";
+	while ((c = getopt(argc, argv, optstr)) != -1) {
+		switch (c) {
+		case 'c':
+			if (bhyve_topology_parse(optarg) != 0) {
+				errx(EX_USAGE, "invalid cpu topology '%s'",
+				    optarg);
+			}
+			break;
+		case 'C':
+			set_config_bool("memory.guest_in_core", true);
+			break;
+		case 'D':
+			set_config_bool("destroy_on_poweroff", true);
+			break;
+		case 'k':
+			bhyve_parse_simple_config_file(optarg);
+			break;
+		case 'm':
+			set_config_value("memory.size", optarg);
+			break;
+		case 'o':
+			if (!bhyve_parse_config_option(optarg)) {
+				errx(EX_USAGE,
+				    "invalid configuration option '%s'",
+				    optarg);
+			}
+			break;
+		case 'p':
+			if (bhyve_pincpu_parse(optarg) != 0) {
+				errx(EX_USAGE,
+				    "invalid vcpu pinning configuration '%s'",
+				    optarg);
+			}
+			break;
+		case 's':
+			if (strncmp(optarg, "help", strlen(optarg)) == 0) {
+				pci_print_supported_devices();
+				exit(0);
+			} else if (pci_parse_slot(optarg) != 0)
+				exit(4);
+			else
+				break;
+		case 'S':
+			set_config_bool("memory.wired", true);
+			break;
+		case 'U':
+			set_config_value("uuid", optarg);
+			break;
+		case 'W':
+			set_config_bool("virtio_msix", false);
+			break;
+		case 'h':
+			bhyve_usage(0);
+		default:
+			bhyve_usage(1);
+		}
+	}
+}
+
 void
 bhyve_init_vcpu(struct vcpu *vcpu __unused)
 {
diff --git a/usr.sbin/bhyve/amd64/bhyverun_machdep.c b/usr.sbin/bhyve/amd64/bhyverun_machdep.c
index 4f482586f900..fc2f91e31db7 100644
--- a/usr.sbin/bhyve/amd64/bhyverun_machdep.c
+++ b/usr.sbin/bhyve/amd64/bhyverun_machdep.c
@@ -30,6 +30,7 @@
 #include <err.h>
 #include <stdbool.h>
 #include <stdlib.h>
+#include <sysexits.h>
 
 #include <vmmapi.h>
 
@@ -44,6 +45,7 @@
 #include "inout.h"
 #include "kernemu_dev.h"
 #include "mptbl.h"
+#include "pci_emul.h"
 #include "pci_irq.h"
 #include "pci_lpc.h"
 #include "rtc.h"
@@ -63,6 +65,185 @@ bhyve_init_config(void)
 	set_config_value("lpc.fwcfg", "bhyve");
 }
 
+void
+bhyve_usage(int code)
+{
+	const char *progname;
+
+	progname = getprogname();
+
+	fprintf(stderr,
+	    "Usage: %s [-AaCDeHhPSuWwxY]\n"
+	    "       %*s [-c [[cpus=]numcpus][,sockets=n][,cores=n][,threads=n]]\n"
+	    "       %*s [-G port] [-k config_file] [-l lpc] [-m mem] [-o var=value]\n"
+	    "       %*s [-p vcpu:hostcpu] [-r file] [-s pci] [-U uuid] vmname\n"
+	    "       -A: create ACPI tables\n"
+	    "       -a: local apic is in xAPIC mode (deprecated)\n"
+	    "       -C: include guest memory in core file\n"
+	    "       -c: number of CPUs and/or topology specification\n"
+	    "       -D: destroy on power-off\n"
+	    "       -e: exit on unhandled I/O access\n"
+	    "       -G: start a debug server\n"
+	    "       -H: vmexit from the guest on HLT\n"
+	    "       -h: help\n"
+	    "       -k: key=value flat config file\n"
+	    "       -K: PS2 keyboard layout\n"
+	    "       -l: LPC device configuration\n"
+	    "       -m: memory size\n"
+	    "       -o: set config 'var' to 'value'\n"
+	    "       -P: vmexit from the guest on pause\n"
+	    "       -p: pin 'vcpu' to 'hostcpu'\n"
+#ifdef BHYVE_SNAPSHOT
+	    "       -r: path to checkpoint file\n"
+#endif
+	    "       -S: guest memory cannot be swapped\n"
+	    "       -s: <slot,driver,configinfo> PCI slot config\n"
+	    "       -U: UUID\n"
+	    "       -u: RTC keeps UTC time\n"
+	    "       -W: force virtio to use single-vector MSI\n"
+	    "       -w: ignore unimplemented MSRs\n"
+	    "       -x: local APIC is in x2APIC mode\n"
+	    "       -Y: disable MPtable generation\n",
+	    progname, (int)strlen(progname), "", (int)strlen(progname), "",
+	    (int)strlen(progname), "");
+	exit(code);
+}
+
+void
+bhyve_optparse(int argc, char **argv)
+{
+	const char *optstr;
+	int c;
+
+#ifdef BHYVE_SNAPSHOT
+	optstr = "aehuwxACDHIPSWYk:f:o:p:G:c:s:m:l:K:U:r:";
+#else
+	optstr = "aehuwxACDHIPSWYk:f:o:p:G:c:s:m:l:K:U:";
+#endif
+	while ((c = getopt(argc, argv, optstr)) != -1) {
+		switch (c) {
+		case 'a':
+			set_config_bool("x86.x2apic", false);
+			break;
+		case 'A':
+			/*
+			 * NOP. For backward compatibility. Most systems don't
+			 * work properly without sane ACPI tables. Therefore,
+			 * we're always generating them.
+			 */
+			break;
+		case 'D':
+			set_config_bool("destroy_on_poweroff", true);
+			break;
+		case 'p':
+			if (bhyve_pincpu_parse(optarg) != 0) {
+				errx(EX_USAGE, "invalid vcpu pinning "
+				    "configuration '%s'", optarg);
+			}
+			break;
+		case 'c':
+			if (bhyve_topology_parse(optarg) != 0) {
+			    errx(EX_USAGE, "invalid cpu topology "
+				"'%s'", optarg);
+			}
+			break;
+		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':
+			bhyve_parse_gdb_options(optarg);
+			break;
+		case 'k':
+			bhyve_parse_simple_config_file(optarg);
+			break;
+		case 'K':
+			set_config_value("keyboard.layout", optarg);
+			break;
+		case 'l':
+			if (strncmp(optarg, "help", strlen(optarg)) == 0) {
+				lpc_print_supported_devices();
+				exit(0);
+			} else if (lpc_device_parse(optarg) != 0) {
+				errx(EX_USAGE, "invalid lpc device "
+				    "configuration '%s'", optarg);
+			}
+			break;
+#ifdef BHYVE_SNAPSHOT
+		case 'r':
+			restore_file = optarg;
+			break;
+#endif
+		case 's':
+			if (strncmp(optarg, "help", strlen(optarg)) == 0) {
+				pci_print_supported_devices();
+				exit(0);
+			} else if (pci_parse_slot(optarg) != 0)
+				exit(4);
+			else
+				break;
+		case 'S':
+			set_config_bool("memory.wired", true);
+			break;
+		case 'm':
+			set_config_value("memory.size", optarg);
+			break;
+		case 'o':
+			if (!bhyve_parse_config_option(optarg)) {
+				errx(EX_USAGE,
+				    "invalid configuration option '%s'",
+				    optarg);
+			}
+			break;
+		case 'H':
+			set_config_bool("x86.vmexit_on_hlt", true);
+			break;
+		case 'I':
+			/*
+			 * The "-I" option was used to add an ioapic to the
+			 * virtual machine.
+			 *
+			 * An ioapic is now provided unconditionally for each
+			 * virtual machine and this option is now deprecated.
+			 */
+			break;
+		case 'P':
+			set_config_bool("x86.vmexit_on_pause", true);
+			break;
+		case 'e':
+			set_config_bool("x86.strictio", true);
+			break;
+		case 'u':
+			set_config_bool("rtc.use_localtime", false);
+			break;
+		case 'U':
+			set_config_value("uuid", optarg);
+			break;
+		case 'w':
+			set_config_bool("x86.strictmsr", false);
+			break;
+		case 'W':
+			set_config_bool("virtio_msix", false);
+			break;
+		case 'x':
+			set_config_bool("x86.x2apic", true);
+			break;
+		case 'Y':
+			set_config_bool("x86.mptable", false);
+			break;
+		case 'h':
+			bhyve_usage(0);
+		default:
+			bhyve_usage(1);
+		}
+	}
+}
+
 void
 bhyve_init_vcpu(struct vcpu *vcpu)
 {
diff --git a/usr.sbin/bhyve/bhyverun.c b/usr.sbin/bhyve/bhyverun.c
index b9f00385d9e8..1c3b810efb02 100644
--- a/usr.sbin/bhyve/bhyverun.c
+++ b/usr.sbin/bhyve/bhyverun.c
@@ -100,7 +100,6 @@ uint16_t cpu_cores, cpu_sockets, cpu_threads;
 
 int raw_stdio = 0;
 
-static char *progname;
 static const int BSP = 0;
 
 static cpuset_t cpumask;
@@ -115,48 +114,6 @@ static struct vcpu_info {
 
 static cpuset_t **vcpumap;
 
-static void
-usage(int code)
-{
-
-	fprintf(stderr,
-		"Usage: %s [-AaCDeHhPSuWwxY]\n"
-		"       %*s [-c [[cpus=]numcpus][,sockets=n][,cores=n][,threads=n]]\n"
-		"       %*s [-G port] [-k config_file] [-l lpc] [-m mem] [-o var=value]\n"
-		"       %*s [-p vcpu:hostcpu] [-r file] [-s pci] [-U uuid] vmname\n"
-		"       -A: create ACPI tables\n"
-		"       -a: local apic is in xAPIC mode (deprecated)\n"
-		"       -C: include guest memory in core file\n"
-		"       -c: number of CPUs and/or topology specification\n"
-		"       -D: destroy on power-off\n"
-		"       -e: exit on unhandled I/O access\n"
-		"       -G: start a debug server\n"
-		"       -H: vmexit from the guest on HLT\n"
-		"       -h: help\n"
-		"       -k: key=value flat config file\n"
-		"       -K: PS2 keyboard layout\n"
-		"       -l: LPC device configuration\n"
-		"       -m: memory size\n"
-		"       -o: set config 'var' to 'value'\n"
-		"       -P: vmexit from the guest on pause\n"
-		"       -p: pin 'vcpu' to 'hostcpu'\n"
-#ifdef BHYVE_SNAPSHOT
-		"       -r: path to checkpoint file\n"
-#endif
-		"       -S: guest memory cannot be swapped\n"
-		"       -s: <slot,driver,configinfo> PCI slot config\n"
-		"       -U: UUID\n"
-		"       -u: RTC keeps UTC time\n"
-		"       -W: force virtio to use single-vector MSI\n"
-		"       -w: ignore unimplemented MSRs\n"
-		"       -x: local APIC is in x2APIC mode\n"
-		"       -Y: disable MPtable generation\n",
-		progname, (int)strlen(progname), "", (int)strlen(progname), "",
-		(int)strlen(progname), "");
-
-	exit(code);
-}
-
 /*
  * XXX This parser is known to have the following issues:
  * 1.  It accepts null key=value tokens ",," as setting "cpus" to an
@@ -165,8 +122,8 @@ usage(int code)
  * The acceptance of a null specification ('-c ""') is by design to match the
  * manual page syntax specification, this results in a topology of 1 vCPU.
  */
-static int
-topology_parse(const char *opt)
+int
+bhyve_topology_parse(const char *opt)
 {
 	char *cp, *str, *tofree;
 
@@ -275,8 +232,8 @@ calc_topology(void)
 		guest_ncpus = ncpus;
 }
 
-static int
-pincpu_parse(const char *opt)
+int
+bhyve_pincpu_parse(const char *opt)
 {
 	const char *value;
 	char *newval;
@@ -619,8 +576,8 @@ do_open(const char *vmname)
 	return (ctx);
 }
 
-static bool
-parse_config_option(const char *option)
+bool
+bhyve_parse_config_option(const char *option)
 {
 	const char *value;
 	char *path;
@@ -635,8 +592,8 @@ parse_config_option(const char *option)
 	return (true);
 }
 
-static void
-parse_simple_config_file(const char *path)
+void
+bhyve_parse_simple_config_file(const char *path)
 {
 	FILE *fp;
 	char *line, *cp;
@@ -655,7 +612,7 @@ parse_simple_config_file(const char *path)
 		cp = strchr(line, '\n');
 		if (cp != NULL)
 			*cp = '\0';
-		if (!parse_config_option(line))
+		if (!bhyve_parse_config_option(line))
 			errx(4, "%s line %u: invalid config option '%s'", path,
 			    lineno, line);
 	}
@@ -664,8 +621,8 @@ parse_simple_config_file(const char *path)
 }
 
 #ifdef BHYVE_GDB
-static void
-parse_gdb_options(const char *opt)
+void
+bhyve_parse_gdb_options(const char *opt)
 {
 	const char *sport;
 	char *colon;
@@ -692,12 +649,12 @@ parse_gdb_options(const char *opt)
 int
 main(int argc, char *argv[])
 {
-	int c, error;
+	int error;
 	int max_vcpus, memflags;
 	struct vcpu *bsp;
 	struct vmctx *ctx;
 	size_t memsize;
-	const char *optstr, *value, *vmname;
+	const char *value, *vmname;
 #ifdef BHYVE_SNAPSHOT
 	char *restore_file;
 	struct restore_state rstate;
@@ -706,149 +663,12 @@ main(int argc, char *argv[])
 #endif
 
 	bhyve_init_config();
-
-	progname = basename(argv[0]);
-
-#ifdef BHYVE_SNAPSHOT
-	optstr = "aehuwxACDHIPSWYk:f:o:p:G:c:s:m:l:K:U:r:";
-#else
-	optstr = "aehuwxACDHIPSWYk:f:o:p:G:c:s:m:l:K:U:";
-#endif
-	while ((c = getopt(argc, argv, optstr)) != -1) {
-		switch (c) {
-#ifdef __amd64__
-		case 'a':
-			set_config_bool("x86.x2apic", false);
-			break;
-#endif
-		case 'A':
-			/*
-			 * NOP. For backward compatibility. Most systems don't
-			 * work properly without sane ACPI tables. Therefore,
-			 * we're always generating them.
-			 */
-			break;
-		case 'D':
-			set_config_bool("destroy_on_poweroff", true);
-			break;
-		case 'p':
-			if (pincpu_parse(optarg) != 0) {
-				errx(EX_USAGE, "invalid vcpu pinning "
-				    "configuration '%s'", optarg);
-			}
-			break;
-		case 'c':
-			if (topology_parse(optarg) != 0) {
-			    errx(EX_USAGE, "invalid cpu topology "
-				"'%s'", optarg);
-			}
-			break;
-		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;
-#ifdef BHYVE_GDB
-		case 'G':
-			parse_gdb_options(optarg);
-			break;
-#endif
-		case 'k':
-			parse_simple_config_file(optarg);
-			break;
-		case 'K':
-			set_config_value("keyboard.layout", optarg);
-			break;
-#ifdef __amd64__
-		case 'l':
-			if (strncmp(optarg, "help", strlen(optarg)) == 0) {
-				lpc_print_supported_devices();
-				exit(0);
-			} else if (lpc_device_parse(optarg) != 0) {
-				errx(EX_USAGE, "invalid lpc device "
-				    "configuration '%s'", optarg);
-			}
-			break;
-#endif
-#ifdef BHYVE_SNAPSHOT
-		case 'r':
-			restore_file = optarg;
-			break;
-#endif
-		case 's':
-			if (strncmp(optarg, "help", strlen(optarg)) == 0) {
-				pci_print_supported_devices();
-				exit(0);
-			} else if (pci_parse_slot(optarg) != 0)
-				exit(4);
-			else
-				break;
-		case 'S':
-			set_config_bool("memory.wired", true);
-			break;
-		case 'm':
-			set_config_value("memory.size", optarg);
-			break;
-		case 'o':
-			if (!parse_config_option(optarg))
-				errx(EX_USAGE, "invalid configuration option '%s'", optarg);
-			break;
-#ifdef __amd64__
-		case 'H':
-			set_config_bool("x86.vmexit_on_hlt", true);
-			break;
-		case 'I':
-			/*
-			 * The "-I" option was used to add an ioapic to the
-			 * virtual machine.
-			 *
-			 * An ioapic is now provided unconditionally for each
-			 * virtual machine and this option is now deprecated.
-			 */
-			break;
-		case 'P':
-			set_config_bool("x86.vmexit_on_pause", true);
-			break;
-		case 'e':
-			set_config_bool("x86.strictio", true);
-			break;
-		case 'u':
-			set_config_bool("rtc.use_localtime", false);
-			break;
-#endif
-		case 'U':
-			set_config_value("uuid", optarg);
-			break;
-#ifdef __amd64__
-		case 'w':
-			set_config_bool("x86.strictmsr", false);
-			break;
-#endif
-		case 'W':
-			set_config_bool("virtio_msix", false);
-			break;
-#ifdef __amd64__
-		case 'x':
-			set_config_bool("x86.x2apic", true);
-			break;
-		case 'Y':
-			set_config_bool("x86.mptable", false);
-			break;
-#endif
-		case 'h':
-			usage(0);
-		default:
-			usage(1);
-		}
-	}
+	bhyve_optparse(argc, argv);
 	argc -= optind;
 	argv += optind;
 
 	if (argc > 1)
-		usage(1);
+		bhyve_usage(1);
 
 #ifdef BHYVE_SNAPSHOT
 	if (restore_file != NULL) {
@@ -869,7 +689,7 @@ main(int argc, char *argv[])
 
 	vmname = get_config_value("name");
 	if (vmname == NULL)
-		usage(1);
+		bhyve_usage(1);
 
 	if (get_config_bool_default("config.dump", false)) {
 		dump_config();
diff --git a/usr.sbin/bhyve/bhyverun.h b/usr.sbin/bhyve/bhyverun.h
index 5fe97ca07f0b..e6ff90e5daa0 100644
--- a/usr.sbin/bhyve/bhyverun.h
+++ b/usr.sbin/bhyve/bhyverun.h
@@ -58,6 +58,18 @@ typedef int (*vmexit_handler_t)(struct vmctx *, struct vcpu *, struct vm_run *);
 
 /* Interfaces implemented by machine-dependent code. */
 void bhyve_init_config(void);
+void bhyve_optparse(int argc, char **argv);
+void bhyve_usage(int code);
+
+/* Interfaces used by command-line option-parsing code. */
+bool bhyve_parse_config_option(const char *option);
+void bhyve_parse_simple_config_file(const char *path);
+#ifdef BHYVE_GDB
+void bhyve_parse_gdb_options(const char *opt);
+#endif
+int bhyve_pincpu_parse(const char *opt);
+int bhyve_topology_parse(const char *opt);
+
 void bhyve_init_vcpu(struct vcpu *vcpu);
 void bhyve_start_vcpu(struct vcpu *vcpu, bool bsp);
 int bhyve_init_platform(struct vmctx *ctx, struct vcpu *bsp);