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
This commit is contained in:
Mark Johnston 2024-04-03 13:44:40 -04:00
parent 11c79f9e35
commit 981f9f7495
4 changed files with 307 additions and 196 deletions

View file

@ -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)
{

View file

@ -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)
{

View file

@ -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();

View file

@ -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);