Page MenuHomeFreeBSD

D26035.id75706.diff
No OneTemporary

D26035.id75706.diff

Index: usr.sbin/bhyve/Makefile
===================================================================
--- usr.sbin/bhyve/Makefile
+++ usr.sbin/bhyve/Makefile
@@ -21,6 +21,7 @@
bhyverun.c \
block_if.c \
bootrom.c \
+ config.c \
console.c \
consport.c \
ctl_util.c \
@@ -82,7 +83,7 @@
.PATH: ${BHYVE_SYSDIR}/sys/amd64/vmm
SRCS+= vmm_instruction_emul.c
-LIBADD= vmmapi md pthread z util sbuf cam
+LIBADD= vmmapi md nv pthread z util sbuf cam
.if ${MK_BHYVE_SNAPSHOT} != "no"
LIBADD+= ucl xo
.endif
Index: usr.sbin/bhyve/bhyverun.h
===================================================================
--- usr.sbin/bhyve/bhyverun.h
+++ usr.sbin/bhyve/bhyverun.h
@@ -37,8 +37,6 @@
struct vmctx;
extern int guest_ncpus;
extern uint16_t cores, sockets, threads;
-extern char *guest_uuid_str;
-extern const char *vmname;
void *paddr_guest2host(struct vmctx *ctx, uintptr_t addr, size_t len);
#ifdef BHYVE_SNAPSHOT
@@ -47,9 +45,6 @@
void fbsdrun_set_capabilities(struct vmctx *ctx, int cpu);
void fbsdrun_addcpu(struct vmctx *ctx, int fromcpu, int newcpu, uint64_t rip);
-int fbsdrun_muxed(void);
-int fbsdrun_vmexit_on_hlt(void);
-int fbsdrun_vmexit_on_pause(void);
-int fbsdrun_disable_x2apic(void);
int fbsdrun_virtio_msix(void);
+
#endif
Index: usr.sbin/bhyve/bhyverun.c
===================================================================
--- usr.sbin/bhyve/bhyverun.c
+++ usr.sbin/bhyve/bhyverun.c
@@ -87,6 +87,7 @@
#include "acpi.h"
#include "atkbdc.h"
#include "bootrom.h"
+#include "config.h"
#include "inout.h"
#include "dbgport.h"
#include "debug.h"
@@ -184,26 +185,11 @@
typedef int (*vmexit_handler_t)(struct vmctx *, struct vm_exit *, int *vcpu);
extern int vmexit_task_switch(struct vmctx *, struct vm_exit *, int *vcpu);
-const char *vmname;
-
int guest_ncpus;
uint16_t cores, maxcpus, sockets, threads;
-char *guest_uuid_str;
-
int raw_stdio = 0;
-static int gdb_port = 0;
-static int guest_vmexit_on_hlt, guest_vmexit_on_pause;
-static int virtio_msix = 1;
-static int x2apic_mode = 0; /* default is xAPIC */
-static int destroy_on_poweroff = 0;
-
-static int strictio;
-static int strictmsr = 1;
-
-static int acpi;
-
static char *progname;
static const int BSP = 0;
@@ -239,24 +225,26 @@
fprintf(stderr,
"Usage: %s [-abehuwxACDHPSWY]\n"
" %*s [-c [[cpus=]numcpus][,sockets=n][,cores=n][,threads=n]]\n"
- " %*s [-g <gdb port>] [-l <lpc>]\n"
- " %*s [-m mem] [-p vcpu:hostcpu] [-s <pci>] [-U uuid] <vm>\n"
+ " %*s [-f <file>] [-g <gdb port>] [-l <lpc>] [-o <var>=<value>]\n"
+ " %*s [-m mem] [-p vcpu:hostcpu] [-s <pci>] [-U uuid] [<vm>]\n"
" -a: local apic is in xAPIC mode (deprecated)\n"
" -A: create ACPI tables\n"
" -c: number of cpus and/or topology specification\n"
" -C: include guest memory in core file\n"
" -D: destroy on power-off\n"
" -e: exit on unhandled I/O access\n"
+ " -f: config file\n"
" -g: gdb port\n"
" -h: help\n"
" -H: vmexit from the guest on hlt\n"
" -l: LPC device configuration\n"
" -m: memory size in MB\n"
-#ifdef BHYVE_SNAPSHOT
- " -r: path to checkpoint file\n"
-#endif
+ " -o: set config 'var' to 'value'\n"
" -p: pin 'vcpu' to 'hostcpu'\n"
" -P: vmexit from the guest on pause\n"
+#ifdef BHYVE_SNAPSHOT
+ " -r: path to checkpoint file\n"
+#endif
" -s: <slot,driver,configinfo> PCI slot config\n"
" -S: guest memory cannot be swapped\n"
" -u: RTC keeps UTC time\n"
@@ -273,11 +261,8 @@
/*
* XXX This parser is known to have the following issues:
- * 1. It accepts null key=value tokens ",,".
- * 2. It accepts whitespace after = and before value.
- * 3. Values out of range of INT are silently wrapped.
- * 4. It doesn't check non-final values.
- * 5. The apparently bogus limits of UINT16_MAX are for future expansion.
+ * 1. It accepts null key=value tokens ",," as setting "cpus" to an
+ * empty string.
*
* 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.
@@ -285,83 +270,122 @@
static int
topology_parse(const char *opt)
{
- uint64_t ncpus;
- int c, chk, n, s, t, tmp;
char *cp, *str;
- bool ns, scts;
- c = 1, n = 1, s = 1, t = 1;
- ns = false, scts = false;
+ if (*opt == '\0') {
+ set_config_value("sockets", "1");
+ set_config_value("cores", "1");
+ set_config_value("threads", "1");
+ set_config_value("cpus", "1");
+ return (0);
+ }
+
str = strdup(opt);
if (str == NULL)
- goto out;
+ errx(4, "Failed to allocate memory");
while ((cp = strsep(&str, ",")) != NULL) {
- if (sscanf(cp, "%i%n", &tmp, &chk) == 1) {
- n = tmp;
- ns = true;
- } else if (sscanf(cp, "cpus=%i%n", &tmp, &chk) == 1) {
- n = tmp;
- ns = true;
- } else if (sscanf(cp, "sockets=%i%n", &tmp, &chk) == 1) {
- s = tmp;
- scts = true;
- } else if (sscanf(cp, "cores=%i%n", &tmp, &chk) == 1) {
- c = tmp;
- scts = true;
- } else if (sscanf(cp, "threads=%i%n", &tmp, &chk) == 1) {
- t = tmp;
- scts = true;
+ if (strncmp(cp, "cpus=", strlen("cpus=")) == 0)
+ set_config_value("cpus", cp + strlen("cpus="));
+ else if (strncmp(cp, "sockets=", strlen("sockets=")) == 0)
+ set_config_value("sockets", cp + strlen("sockets="));
+ else if (strncmp(cp, "cores=", strlen("cores=")) == 0)
+ set_config_value("cores", cp + strlen("cores="));
+ else if (strncmp(cp, "threads=", strlen("threads=")) == 0)
+ set_config_value("threads", cp + strlen("threads="));
#ifdef notyet /* Do not expose this until vmm.ko implements it */
- } else if (sscanf(cp, "maxcpus=%i%n", &tmp, &chk) == 1) {
- m = tmp;
+ else if (strncmp(cp, "maxcpus=", strlen("maxcpus=")) == 0)
+ set_config_value("maxcpus", cp + strlen("maxcpus="));
#endif
- /* Skip the empty argument case from -c "" */
- } else if (cp[0] == '\0')
- continue;
+ else if (strchr(cp, '=') != NULL)
+ goto out;
else
- goto out;
- /* Any trailing garbage causes an error */
- if (cp[chk] != '\0')
- goto out;
+ set_config_value("cpus", cp);
}
free(str);
- str = NULL;
-
- /*
- * Range check 1 <= n <= UINT16_MAX all values
- */
- if (n < 1 || s < 1 || c < 1 || t < 1 ||
- n > UINT16_MAX || s > UINT16_MAX || c > UINT16_MAX ||
- t > UINT16_MAX)
- return (-1);
-
- /* If only the cpus was specified, use that as sockets */
- if (!scts)
- s = n;
- /*
- * Compute sockets * cores * threads avoiding overflow
- * The range check above insures these are 16 bit values
- * If n was specified check it against computed ncpus
- */
- ncpus = (uint64_t)s * c * t;
- if (ncpus > UINT16_MAX || (ns && n != ncpus))
- return (-1);
-
- guest_ncpus = ncpus;
- sockets = s;
- cores = c;
- threads = t;
- return(0);
+ return (0);
out:
free(str);
return (-1);
}
+static int
+parse_int_value(const char *key, const char *value, int minval, int maxval)
+{
+ char *cp;
+ long lval;
+
+ errno = 0;
+ lval = strtol(value, &cp, 0);
+ if (errno != 0 || *cp != '\0' || cp == value || lval < minval ||
+ lval > maxval)
+ errx(4, "Invalid value for %s: '%s'", key, value);
+ return (lval);
+}
+
+/*
+ * Set the sockets, cores, threads, and guest_cpus variables based on
+ * the configured topology.
+ *
+ * The limits of UINT16_MAX are due to the types passed to
+ * vm_set_topology(). vmm.ko may enforce tighter limits.
+ */
+static void
+calc_topolopgy(void)
+{
+ const char *value;
+ bool explicit_cpus;
+ uint64_t ncpus;
+
+ value = get_config_value("cpus");
+ if (value != NULL) {
+ guest_ncpus = parse_int_value("cpus", value, 1, UINT16_MAX);
+ explicit_cpus = true;
+ } else {
+ guest_ncpus = 1;
+ explicit_cpus = false;
+ }
+ value = get_config_value("cores");
+ if (value != NULL)
+ cores = parse_int_value("cores", value, 1, UINT16_MAX);
+ else
+ cores = 1;
+ value = get_config_value("threads");
+ if (value != NULL)
+ threads = parse_int_value("threads", value, 1, UINT16_MAX);
+ else
+ threads = 1;
+ value = get_config_value("sockets");
+ if (value != NULL)
+ sockets = parse_int_value("sockets", value, 1, UINT16_MAX);
+ else
+ sockets = guest_ncpus;
+
+ /*
+ * Compute sockets * cores * threads avoiding overflow. The
+ * range check above insures these are 16 bit values.
+ */
+ ncpus = (uint64_t)sockets * cores * threads;
+ if (ncpus > UINT16_MAX)
+ errx(4, "Computed number of vCPUs too high: %ju",
+ (uintmax_t)ncpus);
+
+ if (explicit_cpus) {
+ if (guest_ncpus != ncpus)
+ errx(4, "Topology (%d sockets, %d cores, %d threads) "
+ "does not match %d vCPUs", sockets, cores, threads,
+ guest_ncpus);
+ } else
+ guest_ncpus = ncpus;
+}
+
static int
pincpu_parse(const char *opt)
{
+ const char *value;
+ char *newval;
+ char key[16];
int vcpu, pcpu;
if (sscanf(opt, "%d:%d", &vcpu, &pcpu) != 2) {
@@ -381,17 +405,85 @@
return (-1);
}
- if (vcpumap[vcpu] == NULL) {
- if ((vcpumap[vcpu] = malloc(sizeof(cpuset_t))) == NULL) {
- perror("malloc");
- return (-1);
- }
- CPU_ZERO(vcpumap[vcpu]);
+ snprintf(key, sizeof(key), "vcpu.%d.cpuset", vcpu);
+ value = get_config_value(key);
+
+ if (asprintf(&newval, "%s%s%d", value != NULL ? value : "",
+ value != NULL ? "," : "", pcpu) == -1) {
+ perror("failed to build new cpuset string");
+ return (-1);
}
- CPU_SET(pcpu, vcpumap[vcpu]);
+
+ set_config_value(key, newval);
+ free(newval);
return (0);
}
+static void
+parse_cpuset(int vcpu, const char *list, cpuset_t *set)
+{
+ char *cp, *token;
+ int pcpu, start;
+
+ CPU_ZERO(set);
+ start = -1;
+ token = __DECONST(char *, list);
+ for (;;) {
+ pcpu = strtoul(token, &cp, 0);
+ if (cp == token)
+ errx(4, "invalid cpuset for vcpu %d: '%s'", vcpu, list);
+ if (pcpu < 0 || pcpu >= CPU_SETSIZE)
+ errx(4, "hostcpu '%d' outside valid range from 0 to %d",
+ pcpu, CPU_SETSIZE - 1);
+ switch (*cp) {
+ case ',':
+ case '\0':
+ if (start >= 0) {
+ if (start > pcpu)
+ errx(4, "Invalid hostcpu range %d-%d",
+ start, pcpu);
+ while (start < pcpu) {
+ CPU_SET(start, vcpumap[vcpu]);
+ start++;
+ }
+ start = -1;
+ }
+ CPU_SET(pcpu, vcpumap[vcpu]);
+ break;
+ case '-':
+ if (start >= 0)
+ errx(4, "invalid cpuset for vcpu %d: '%s'",
+ vcpu, list);
+ start = pcpu;
+ break;
+ default:
+ errx(4, "invalid cpuset for vcpu %d: '%s'", vcpu, list);
+ }
+ if (*cp == '\0')
+ break;
+ token = cp + 1;
+ }
+}
+
+static void
+build_vcpumaps(void)
+{
+ char key[16];
+ const char *value;
+ int vcpu;
+
+ for (vcpu = 0; vcpu < guest_ncpus; vcpu++) {
+ snprintf(key, sizeof(key), "vcpu.%d.cpuset", vcpu);
+ value = get_config_value(key);
+ if (value == NULL)
+ continue;
+ vcpumap[vcpu] = malloc(sizeof(cpuset_t));
+ if (vcpumap[vcpu] == NULL)
+ err(4, "Failed to allocate cpuset for vcpu %d", vcpu);
+ parse_cpuset(vcpu, value, vcpumap[vcpu]);
+ }
+}
+
void
vm_inject_fault(void *arg, int vcpu, int vector, int errcode_valid,
int errcode)
@@ -422,25 +514,11 @@
}
#endif
-int
-fbsdrun_vmexit_on_pause(void)
-{
-
- return (guest_vmexit_on_pause);
-}
-
-int
-fbsdrun_vmexit_on_hlt(void)
-{
-
- return (guest_vmexit_on_hlt);
-}
-
int
fbsdrun_virtio_msix(void)
{
- return (virtio_msix);
+ return (get_config_bool("virtio_msix"));
}
static void *
@@ -459,8 +537,7 @@
#ifdef BHYVE_SNAPSHOT
checkpoint_cpu_add(vcpu);
#endif
- if (gdb_port != 0)
- gdb_cpu_add(vcpu);
+ gdb_cpu_add(vcpu);
vm_loop(mtp->mt_ctx, vcpu, vmexit[vcpu].rip);
@@ -548,7 +625,7 @@
return (error);
}
- error = emulate_inout(ctx, vcpu, vme, strictio);
+ error = emulate_inout(ctx, vcpu, vme);
if (error) {
fprintf(stderr, "Unhandled %s%c 0x%04x at 0x%lx\n",
in ? "in" : "out",
@@ -572,7 +649,7 @@
if (error != 0) {
fprintf(stderr, "rdmsr to register %#x on vcpu %d\n",
vme->u.msr.code, *pvcpu);
- if (strictmsr) {
+ if (get_config_bool("x86.strictmsr")) {
vm_inject_gp(ctx, *pvcpu);
return (VMEXIT_CONTINUE);
}
@@ -598,7 +675,7 @@
if (error != 0) {
fprintf(stderr, "wrmsr to register %#x(%#lx) on vcpu %d\n",
vme->u.msr.code, vme->u.msr.wval, *pvcpu);
- if (strictmsr) {
+ if (get_config_bool("x86.strictmsr")) {
vm_inject_gp(ctx, *pvcpu);
return (VMEXIT_CONTINUE);
}
@@ -737,8 +814,7 @@
#ifdef BHYVE_SNAPSHOT
checkpoint_cpu_suspend(*pvcpu);
#endif
- if (gdb_port != 0)
- gdb_cpu_mtrap(*pvcpu);
+ gdb_cpu_mtrap(*pvcpu);
#ifdef BHYVE_SNAPSHOT
checkpoint_cpu_resume(*pvcpu);
#endif
@@ -817,7 +893,7 @@
case VM_SUSPEND_RESET:
exit(0);
case VM_SUSPEND_POWEROFF:
- if (destroy_on_poweroff)
+ if (get_config_bool("destroy_on_poweroff"))
vm_destroy(ctx);
exit(1);
case VM_SUSPEND_HALT:
@@ -838,8 +914,7 @@
#ifdef BHYVE_SNAPSHOT
checkpoint_cpu_suspend(*pvcpu);
#endif
- if (gdb_port != 0)
- gdb_cpu_suspend(*pvcpu);
+ gdb_cpu_suspend(*pvcpu);
#ifdef BHYVE_SNAPSHOT
checkpoint_cpu_resume(*pvcpu);
#endif
@@ -850,10 +925,6 @@
vmexit_breakpoint(struct vmctx *ctx, struct vm_exit *vmexit, int *pvcpu)
{
- if (gdb_port == 0) {
- fprintf(stderr, "vm_loop: unexpected VMEXIT_DEBUG\n");
- exit(4);
- }
gdb_cpu_breakpoint(*pvcpu, vmexit);
return (VMEXIT_CONTINUE);
}
@@ -943,7 +1014,7 @@
{
int err, tmp;
- if (fbsdrun_vmexit_on_hlt()) {
+ if (get_config_bool("x86.vmexit_on_hlt")) {
err = vm_get_capability(ctx, cpu, VM_CAP_HALT_EXIT, &tmp);
if (err < 0) {
fprintf(stderr, "VM exit on HLT not supported\n");
@@ -954,7 +1025,7 @@
handler[VM_EXITCODE_HLT] = vmexit_hlt;
}
- if (fbsdrun_vmexit_on_pause()) {
+ if (get_config_bool("x86.vmexit_on_pause")) {
/*
* pause exit support required for this mode
*/
@@ -969,7 +1040,7 @@
handler[VM_EXITCODE_PAUSE] = vmexit_pause;
}
- if (x2apic_mode)
+ if (get_config_bool("x86.x2apic"))
err = vm_set_x2apic_state(ctx, cpu, X2APIC_ENABLED);
else
err = vm_set_x2apic_state(ctx, cpu, X2APIC_DISABLED);
@@ -1073,16 +1144,80 @@
fbsdrun_addcpu(ctx, BSP, vcpu, rip);
}
+static bool
+parse_config_option(const char *option)
+{
+ const char *value;
+ char *path;
+
+ value = strchr(option, '=');
+ if (value == NULL || value[1] == '\0')
+ return (false);
+ path = strndup(option, value - option);
+ if (path == NULL)
+ err(4, "Failed to allocate memory");
+ set_config_value(path, value + 1);
+ return (true);
+}
+
+static void
+parse_simple_config_file(const char *path)
+{
+ FILE *fp;
+ char *line, *cp;
+ size_t linecap;
+ unsigned int lineno;
+
+ fp = fopen(path, "r");
+ if (fp == NULL)
+ err(4, "Failed to open configuration file %s", path);
+ line = NULL;
+ linecap = 0;
+ lineno = 1;
+ for (lineno = 1; getline(&line, &linecap, fp) > 0; lineno++) {
+ if (*line == '#' || *line == '\n')
+ continue;
+ cp = strchr(line, '\n');
+ if (cp != NULL)
+ *cp = '\0';
+ if (!parse_config_option(line))
+ errx(4, "%s line %u: invalid config option '%s'", path,
+ lineno, line);
+ }
+ free(line);
+ fclose(fp);
+}
+
+static void
+set_defaults(void)
+{
+
+ set_config_bool("acpi_tables", false);
+ set_config_bool("bvmconsole", false);
+ set_config_bool("destroy_on_poweroff", false);
+ set_config_bool("gdb.wait", false);
+ set_config_bool("memory.guest_in_core", false);
+ set_config_value("memory.size", "256M");
+ set_config_bool("memory.wired", false);
+ set_config_bool("x86.mptable", true);
+ set_config_bool("rtc.use_localtime", true);
+ set_config_bool("x86.x2apic", false);
+ set_config_bool("x86.strictio", false);
+ set_config_bool("x86.strictmsr", true);
+ set_config_bool("x86.vmexit_on_hlt", false);
+ set_config_bool("x86.vmexit_on_pause", false);
+ set_config_bool("virtio_msix", true);
+}
+
int
main(int argc, char *argv[])
{
- int c, error, dbg_port, err, bvmcons;
- int max_vcpus, mptgen, memflags;
- int rtc_localtime;
- bool gdb_stop;
+ int c, error, err;
+ int max_vcpus, memflags;
struct vmctx *ctx;
uint64_t rip;
size_t memsize;
+ const char *value, *vmname;
char *optstr;
#ifdef BHYVE_SNAPSHOT
char *restore_file;
@@ -1092,36 +1227,28 @@
restore_file = NULL;
#endif
- bvmcons = 0;
+ init_config();
+ set_defaults();
progname = basename(argv[0]);
- dbg_port = 0;
- gdb_stop = false;
- guest_ncpus = 1;
- sockets = cores = threads = 1;
- maxcpus = 0;
- memsize = 256 * MB;
- mptgen = 1;
- rtc_localtime = 1;
- memflags = 0;
#ifdef BHYVE_SNAPSHOT
- optstr = "abehuwxACDHIPSWYp:g:G:c:s:m:l:U:r:";
+ optstr = "abehuwxACDHIPSWYf:o:p:g:G:c:s:m:l:U:r:";
#else
- optstr = "abehuwxACDHIPSWYp:g:G:c:s:m:l:U:";
+ optstr = "abehuwxACDHIPSWYf:o:p:g:G:c:s:m:l:U:";
#endif
while ((c = getopt(argc, argv, optstr)) != -1) {
switch (c) {
case 'a':
- x2apic_mode = 0;
+ set_config_bool("x86.x2apic", false);
break;
case 'A':
- acpi = 1;
+ set_config_bool("acpi_tables", true);
break;
case 'b':
- bvmcons = 1;
+ set_config_bool("bvmconsole", true);
break;
case 'D':
- destroy_on_poweroff = 1;
+ set_config_bool("destroy_on_poweroff", true);
break;
case 'p':
if (pincpu_parse(optarg) != 0) {
@@ -1136,17 +1263,20 @@
}
break;
case 'C':
- memflags |= VM_MEM_F_INCORE;
+ set_config_bool("memory.guest_in_core", true);
+ break;
+ case 'f':
+ parse_simple_config_file(optarg);
break;
case 'g':
- dbg_port = atoi(optarg);
+ set_config_value("bvmdebug.port", optarg);
break;
case 'G':
if (optarg[0] == 'w') {
- gdb_stop = true;
+ set_config_bool("gdb.wait", true);
optarg++;
}
- gdb_port = atoi(optarg);
+ set_config_value("gdb.port", optarg);
break;
case 'l':
if (strncmp(optarg, "help", strlen(optarg)) == 0) {
@@ -1171,15 +1301,17 @@
else
break;
case 'S':
- memflags |= VM_MEM_F_WIRED;
+ set_config_bool("memory.wired", true);
break;
case 'm':
- error = vm_parse_memsize(optarg, &memsize);
- if (error)
- errx(EX_USAGE, "invalid memsize '%s'", optarg);
+ set_config_value("memory.size", optarg);
+ break;
+ case 'o':
+ if (!parse_config_option(optarg))
+ errx(EX_USAGE, "invalid configuration option '%s'", optarg);
break;
case 'H':
- guest_vmexit_on_hlt = 1;
+ set_config_bool("x86.vmexit_on_hlt", true);
break;
case 'I':
/*
@@ -1191,28 +1323,28 @@
*/
break;
case 'P':
- guest_vmexit_on_pause = 1;
+ set_config_bool("x86.vmexit_on_pause", true);
break;
case 'e':
- strictio = 1;
+ set_config_bool("x86.strictio", true);
break;
case 'u':
- rtc_localtime = 0;
+ set_config_bool("rtc.use_localtime", false);
break;
case 'U':
- guest_uuid_str = optarg;
+ set_config_value("uuid", optarg);
break;
case 'w':
- strictmsr = 0;
+ set_config_bool("x86.strictmsr", false);
break;
case 'W':
- virtio_msix = 0;
+ set_config_bool("virtio_msix", false);
break;
case 'x':
- x2apic_mode = 1;
+ set_config_bool("x86.x2apic", true);
break;
case 'Y':
- mptgen = 0;
+ set_config_bool("x86.mptable", false);
break;
case 'h':
usage(0);
@@ -1223,10 +1355,10 @@
argc -= optind;
argv += optind;
-#ifdef BHYVE_SNAPSHOT
- if (argc > 1 || (argc == 0 && restore_file == NULL))
+ if (argc > 1)
usage(1);
+#ifdef BHYVE_SNAPSHOT
if (restore_file != NULL) {
error = load_restore_file(restore_file, &rstate);
if (error) {
@@ -1234,24 +1366,34 @@
"file: '%s'.\n", restore_file);
exit(1);
}
- }
-
- if (argc == 1) {
- vmname = argv[0];
- } else {
vmname = lookup_vmname(&rstate);
- if (vmname == NULL) {
- fprintf(stderr, "Cannot find VM name in restore file. "
- "Please specify one.\n");
- exit(1);
- }
+ if (vmname != NULL)
+ set_config_value_path("name", vmname);
}
-#else
- if (argc != 1)
+#endif
+
+ if (argc == 1)
+ set_config_value("name", argv[0]);
+
+ vmname = get_config_value("name");
+ if (vmname == NULL)
usage(1);
- vmname = argv[0];
+#if 1
+ if (get_config_value("config.dump")) {
+ dump_config();
+ exit(1);
+ }
#endif
+
+ calc_topolopgy();
+ build_vcpumaps();
+
+ value = get_config_value("memory.size");
+ error = vm_parse_memsize(value, &memsize);
+ if (error)
+ errx(EX_USAGE, "invalid memsize '%s'", value);
+
ctx = do_open(vmname);
#ifdef BHYVE_SNAPSHOT
@@ -1276,6 +1418,11 @@
fbsdrun_set_capabilities(ctx, BSP);
+ memflags = 0;
+ if (get_config_bool("memory.wired"))
+ memflags |= VM_MEM_F_WIRED;
+ if (get_config_bool("memory.guest_in_core"))
+ memflags |= VM_MEM_F_INCORE;
vm_set_memflags(ctx, memflags);
err = vm_setup_memory(ctx, memsize, VM_MMAP_ALL);
if (err) {
@@ -1297,7 +1444,7 @@
pci_irq_init(ctx);
ioapic_init(ctx);
- rtc_init(ctx, rtc_localtime);
+ rtc_init(ctx);
sci_init(ctx);
/*
@@ -1312,16 +1459,18 @@
* Initialize after PCI, to allow a bootrom file to reserve the high
* region.
*/
- if (acpi)
+ if (get_config_bool("acpi_tables"))
vmgenc_init(ctx);
- if (dbg_port != 0)
- init_dbgport(dbg_port);
+ value = get_config_value("bvmdebug.port");
+ if (value != NULL)
+ init_dbgport(atoi(value));
- if (gdb_port != 0)
- init_gdb(ctx, gdb_port, gdb_stop);
+ value = get_config_value("gdb.port");
+ if (value != NULL)
+ init_gdb(ctx, atoi(value), get_config_bool("gdb.wait"));
- if (bvmcons)
+ if (get_config_bool("bvmconsole"))
init_bvmcons();
if (lpc_bootrom()) {
@@ -1374,7 +1523,7 @@
/*
* build the guest tables, MP etc.
*/
- if (mptgen) {
+ if (get_config_bool("x86.mptable")) {
error = mptable_build(ctx, guest_ncpus);
if (error) {
perror("error to build the guest tables");
@@ -1385,7 +1534,7 @@
error = smbios_build(ctx);
assert(error == 0);
- if (acpi) {
+ if (get_config_bool("acpi_tables")) {
error = acpi_build(ctx, guest_ncpus);
assert(error == 0);
}
Index: usr.sbin/bhyve/block_if.h
===================================================================
--- usr.sbin/bhyve/block_if.h
+++ usr.sbin/bhyve/block_if.h
@@ -38,6 +38,7 @@
#ifndef _BLOCK_IF_H_
#define _BLOCK_IF_H_
+#include <sys/nv.h>
#include <sys/uio.h>
#include <sys/unistd.h>
@@ -62,7 +63,8 @@
};
struct blockif_ctxt;
-struct blockif_ctxt *blockif_open(const char *optstr, const char *ident);
+int blockif_legacy_config(nvlist_t *nvl, const char *opts);
+struct blockif_ctxt *blockif_open(nvlist_t *nvl, const char *ident);
off_t blockif_size(struct blockif_ctxt *bc);
void blockif_chs(struct blockif_ctxt *bc, uint16_t *c, uint8_t *h,
uint8_t *s);
Index: usr.sbin/bhyve/block_if.c
===================================================================
--- usr.sbin/bhyve/block_if.c
+++ usr.sbin/bhyve/block_if.c
@@ -61,8 +61,10 @@
#include <machine/vmm_snapshot.h>
#include "bhyverun.h"
+#include "config.h"
#include "debug.h"
#include "mevent.h"
+#include "pci_emul.h"
#include "block_if.h"
#define BLOCKIF_SIG 0xb109b109
@@ -427,18 +429,44 @@
(void) signal(SIGCONT, SIG_IGN);
}
+int
+blockif_legacy_config(nvlist_t *nvl, const char *opts)
+{
+ char *cp, *path;
+
+ cp = strchr(opts, ',');
+ if (cp == NULL) {
+ set_config_value_node(nvl, "path", opts);
+ return (0);
+ }
+ path = strndup(opts, cp - opts);
+ set_config_value_node(nvl, "path", path);
+ free(path);
+ return (pci_parse_legacy_config(nvl, cp + 1));
+}
+
+static bool
+get_default_false_knob(nvlist_t *nvl, const char *name)
+{
+
+ if (get_config_value_node(nvl, name) == NULL)
+ return (false);
+ return (get_config_bool_node(nvl, name));
+}
+
struct blockif_ctxt *
-blockif_open(const char *optstr, const char *ident)
+blockif_open(nvlist_t *nvl, const char *ident)
{
char tname[MAXCOMLEN + 1];
char name[MAXPATHLEN];
- char *nopt, *xopts, *cp;
+ const char *path, *pssval, *ssval;
+ char *cp;
struct blockif_ctxt *bc;
struct stat sbuf;
struct diocgattr_arg arg;
off_t size, psectsz, psectoff;
int extra, fd, i, sectsz;
- int nocache, sync, ro, candelete, geom, ssopt, pssopt;
+ int ro, candelete, geom, ssopt, pssopt;
int nodelete;
#ifndef WITHOUT_CAPSICUM
@@ -449,59 +477,62 @@
pthread_once(&blockif_once, blockif_init);
fd = -1;
+ extra = 0;
ssopt = 0;
- nocache = 0;
- sync = 0;
ro = 0;
nodelete = 0;
- /*
- * The first element in the optstring is always a pathname.
- * Optional elements follow
- */
- nopt = xopts = strdup(optstr);
- while (xopts != NULL) {
- cp = strsep(&xopts, ",");
- if (cp == nopt) /* file or device pathname */
- continue;
- else if (!strcmp(cp, "nocache"))
- nocache = 1;
- else if (!strcmp(cp, "nodelete"))
- nodelete = 1;
- else if (!strcmp(cp, "sync") || !strcmp(cp, "direct"))
- sync = 1;
- else if (!strcmp(cp, "ro"))
- ro = 1;
- else if (sscanf(cp, "sectorsize=%d/%d", &ssopt, &pssopt) == 2)
- ;
- else if (sscanf(cp, "sectorsize=%d", &ssopt) == 1)
- pssopt = ssopt;
- else {
- EPRINTLN("Invalid device option \"%s\"", cp);
- goto err;
- }
- }
-
- extra = 0;
- if (nocache)
+ if (get_default_false_knob(nvl, "nocache"))
extra |= O_DIRECT;
- if (sync)
+ if (get_default_false_knob(nvl, "nodelete"))
+ nodelete = 1;
+ if (get_default_false_knob(nvl, "sync") ||
+ get_default_false_knob(nvl, "direct"))
extra |= O_SYNC;
+ if (get_default_false_knob(nvl, "ro"))
+ ro = 1;
+ ssval = get_config_value_node(nvl, "sectorsize");
+ if (ssval != NULL) {
+ ssopt = strtol(ssval, &cp, 10);
+ if (cp == ssval) {
+ EPRINTLN("Invalid sector size \"%s\"", ssval);
+ goto err;
+ }
+ if (*cp == '\0') {
+ pssopt = ssopt;
+ } else if (*cp == '/') {
+ pssval = cp + 1;
+ pssopt = strtol(pssval, &cp, 10);
+ if (cp == pssval || *cp != '\0') {
+ EPRINTLN("Invalid sector size \"%s\"", ssval);
+ goto err;
+ }
+ } else {
+ EPRINTLN("Invalid sector size \"%s\"", ssval);
+ goto err;
+ }
+ }
- fd = open(nopt, (ro ? O_RDONLY : O_RDWR) | extra);
+ path = get_config_value_node(nvl, "path");
+ if (path == NULL) {
+ EPRINTLN("Missing \"path\" for block device.");
+ goto err;
+ }
+
+ fd = open(path, (ro ? O_RDONLY : O_RDWR) | extra);
if (fd < 0 && !ro) {
/* Attempt a r/w fail with a r/o open */
- fd = open(nopt, O_RDONLY | extra);
+ fd = open(path, O_RDONLY | extra);
ro = 1;
}
if (fd < 0) {
- warn("Could not open backing file: %s", nopt);
+ warn("Could not open backing file: %s", path);
goto err;
}
if (fstat(fd, &sbuf) < 0) {
- warn("Could not stat backing file %s", nopt);
+ warn("Could not stat backing file %s", path);
goto err;
}
@@ -615,7 +646,6 @@
err:
if (fd >= 0)
close(fd);
- free(nopt);
return (NULL);
}
Index: usr.sbin/bhyve/config.h
===================================================================
--- /dev/null
+++ usr.sbin/bhyve/config.h
@@ -0,0 +1,116 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2019 John H. Baldwin <jhb@FreeBSD.org>
+ *
+ * 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 AND 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$
+ */
+
+#ifndef __CONFIG_H__
+#define __CONFIG_H__
+
+#include <sys/nv.h>
+
+/*-
+ * Manages a configuration database backed by an nv(9) list.
+ *
+ * The database only stores string values. Callers should parse
+ * values into other types if needed. String values can reference
+ * other configuration variables using a '%(name)' syntax. In this
+ * case, the name must be the the full path of the configuration
+ * variable. The % character can be escaped with a preceding \ to
+ * avoid expansion. Any \ characters must be escaped.
+ *
+ * Configuration variables are stored in a tree. The full path of a
+ * variable is specified as a dot-separated name similar to sysctl(8)
+ * OIDs.
+ */
+
+/*
+ * Fetches the value of a configuration variable. If the "raw" value
+ * contains references to other configuration variables, this function
+ * expands those references and returns a pointer to the parsed
+ * string. The string's storage is only stable until the next call to
+ * this function.
+ *
+ * If no node is found, returns NULL.
+ *
+ * If 'parent' is NULL, 'name' is assumed to be a top-level variable.
+ */
+const char *get_config_value_node(const nvlist_t *parent, const char *name);
+
+/*
+ * Similar to get_config_value_node but expects a full path to the
+ * leaf node.
+ */
+const char *get_config_value(const char *path);
+
+/* Initializes the tree to an empty state. */
+void init_config(void);
+
+/*
+ * Creates an existing configuration node via a dot-separated OID
+ * path. Will fail if the path names an existing leaf configuration
+ * variable. If the node already exists, this returns a pointer to
+ * the existing node.
+ */
+nvlist_t *create_config_node(const char *path);
+
+/*
+ * Looks for an existing configuration node via a dot-separated OID
+ * path. Will fail if the path names an existing leaf configuration
+ * variable.
+ */
+nvlist_t *find_config_node(const char *path);
+
+/*
+ * Similar to the above, but treats the path relative to an existing
+ * 'parent' node rather than as an absolute path.
+ */
+nvlist_t *create_relative_config_node(nvlist_t *parent, const char *path);
+nvlist_t *find_relative_config_node(nvlist_t *parent, const char *path);
+
+/*
+ * Adds or replaces the value of the specified variable.
+ *
+ * If 'parent' is NULL, 'name' is assumed to be a top-level variable.
+ */
+void set_config_value_node(nvlist_t *parent, const char *name,
+ const char *value);
+
+/*
+ * Similar to set_config_value_node but expects a full path to the
+ * leaf node.
+ */
+void set_config_value(const char *path, const char *value);
+
+/* Convenience wrappers for boolean variables. */
+bool get_config_bool(const char *path);
+bool get_config_bool_node(const nvlist_t *parent, const char *name);
+void set_config_bool(const char *path, bool value);
+void set_config_bool_node(nvlist_t *parent, const char *name, bool value);
+
+void dump_config(void);
+
+#endif /* !__CONFIG_H__ */
Index: usr.sbin/bhyve/config.c
===================================================================
--- /dev/null
+++ usr.sbin/bhyve/config.c
@@ -0,0 +1,416 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2019 John H. Baldwin <jhb@FreeBSD.org>
+ *
+ * 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 AND 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <assert.h>
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "config.h"
+
+static nvlist_t *config_root;
+
+void
+init_config(void)
+{
+
+ config_root = nvlist_create(0);
+ if (config_root == NULL)
+ err(4, "Failed to create configuration root nvlist");
+}
+
+static nvlist_t *
+_lookup_config_node(nvlist_t *parent, const char *path, bool create)
+{
+ char *copy, *name, *tofree;
+ nvlist_t *nvl, *new_nvl;
+
+ copy = strdup(path);
+ if (copy == NULL)
+ errx(4, "Failed to allocate memory");
+ tofree = copy;
+ nvl = parent;
+ while ((name = strsep(&copy, ".")) != NULL) {
+ if (*name == '\0') {
+ warnx("Invalid configuration node: %s", path);
+ nvl = NULL;
+ break;
+ }
+ if (nvlist_exists_nvlist(nvl, name))
+ nvl = (nvlist_t *)nvlist_get_nvlist(nvl, name);
+ else if (nvlist_exists(nvl, name)) {
+ for (copy = tofree; copy < name; copy++)
+ if (*copy == '\0')
+ *copy = '.';
+ warnx(
+ "Configuration node %s is a child of existing variable %s",
+ path, tofree);
+ nvl = NULL;
+ break;
+ } else if (create) {
+ new_nvl = nvlist_create(0);
+ if (new_nvl == NULL)
+ errx(4, "Failed to allocate memory");
+ nvlist_move_nvlist(nvl, name, new_nvl);
+ nvl = new_nvl;
+ } else {
+ nvl = NULL;
+ break;
+ }
+ }
+ free(tofree);
+ return (nvl);
+}
+
+nvlist_t *
+create_config_node(const char *path)
+{
+
+ return (_lookup_config_node(config_root, path, true));
+}
+
+nvlist_t *
+find_config_node(const char *path)
+{
+
+ return (_lookup_config_node(config_root, path, false));
+}
+
+nvlist_t *
+create_relative_config_node(nvlist_t *parent, const char *path)
+{
+
+ return (_lookup_config_node(parent, path, true));
+}
+
+nvlist_t *
+find_relative_config_node(nvlist_t *parent, const char *path)
+{
+
+ return (_lookup_config_node(parent, path, false));
+}
+
+void
+set_config_value_node(nvlist_t *parent, const char *name, const char *value)
+{
+
+ if (strchr(name, '.') != NULL)
+ errx(4, "Invalid config node name %s", name);
+ if (parent == NULL)
+ parent = config_root;
+ if (nvlist_exists_string(parent, name))
+ nvlist_free_string(parent, name);
+ else if (nvlist_exists(parent, name))
+ errx(4,
+ "Attemping to add value %s to existing node %s of list %p",
+ value, name, parent);
+ nvlist_add_string(parent, name, value);
+}
+
+void
+set_config_value(const char *path, const char *value)
+{
+ const char *name;
+ char *node_name;
+ nvlist_t *nvl;
+
+ /* Look for last separator. */
+ name = strrchr(path, '.');
+ if (name == NULL) {
+ nvl = config_root;
+ name = path;
+ } else {
+ node_name = strndup(path, name - path);
+ if (node_name == NULL)
+ errx(4, "Failed to allocate memory");
+ nvl = create_config_node(node_name);
+ if (nvl == NULL)
+ errx(4, "Failed to create configuration node %s",
+ node_name);
+ free(node_name);
+
+ /* Skip over '.'. */
+ name++;
+ }
+
+ if (nvlist_exists_nvlist(nvl, name))
+ errx(4, "Attempting to add value %s to existing node %s",
+ value, path);
+ set_config_value_node(nvl, name, value);
+}
+
+static const char *
+get_raw_config_value(const char *path)
+{
+ const char *name;
+ char *node_name;
+ nvlist_t *nvl;
+
+ /* Look for last separator. */
+ name = strrchr(path, '.');
+ if (name == NULL) {
+ nvl = config_root;
+ name = path;
+ } else {
+ node_name = strndup(path, name - path);
+ if (node_name == NULL)
+ errx(4, "Failed to allocate memory");
+ nvl = find_config_node(node_name);
+ free(node_name);
+ if (nvl == NULL)
+ return (NULL);
+
+ /* Skip over '.'. */
+ name++;
+ }
+
+ if (nvlist_exists_string(nvl, name))
+ return (nvlist_get_string(nvl, name));
+ if (nvlist_exists_nvlist(nvl, name))
+ warnx("Attempting to fetch value of node %s", path);
+ return (NULL);
+}
+
+static char *
+_expand_config_value(const char *value, int depth)
+{
+ FILE *valfp;
+ const char *cp, *vp;
+ char *nestedval, *path, *valbuf;
+ size_t valsize;
+
+ valfp = open_memstream(&valbuf, &valsize);
+ if (valfp == NULL)
+ errx(4, "Failed to allocate memory");
+
+ vp = value;
+ while (*vp != '\0') {
+ switch (*vp) {
+ case '%':
+ if (depth > 15) {
+ warnx(
+ "Too many recursive references in configuration value");
+ fputc('%', valfp);
+ vp++;
+ break;
+ }
+ if (vp[1] != '(' || vp[2] == '\0')
+ cp = NULL;
+ else
+ cp = strchr(vp + 2, ')');
+ if (cp == NULL) {
+ warnx(
+ "Invalid reference in configuration value \"%s\"",
+ value);
+ fputc('%', valfp);
+ vp++;
+ break;
+ }
+ vp += 2;
+
+ if (cp == vp) {
+ warnx(
+ "Empty reference in configuration value \"%s\"",
+ value);
+ vp++;
+ break;
+ }
+
+ /* Allocate a C string holding the path. */
+ path = strndup(vp, cp - vp);
+ if (path == NULL)
+ errx(4, "Failed to allocate memory");
+
+ /* Advance 'vp' past the reference. */
+ vp = cp + 1;
+
+ /* Fetch the referenced value. */
+ cp = get_raw_config_value(path);
+ if (cp == NULL)
+ warnx(
+ "Failed to fetch referenced configuration variable %s",
+ path);
+ else {
+ nestedval = _expand_config_value(cp, depth + 1);
+ fputs(nestedval, valfp);
+ free(nestedval);
+ }
+ free(path);
+ break;
+ case '\\':
+ vp++;
+ if (*vp == '\0') {
+ warnx(
+ "Trailing \\ in configuration value \"%s\"",
+ value);
+ break;
+ }
+ /* FALLTHROUGH */
+ default:
+ fputc(*vp, valfp);
+ vp++;
+ break;
+ }
+ }
+ fclose(valfp);
+ return (valbuf);
+}
+
+const char *
+expand_config_value(const char *value)
+{
+ static char *valbuf;
+
+ if (strchr(value, '%') == NULL)
+ return (value);
+
+ free(valbuf);
+ valbuf = _expand_config_value(value, 0);
+ return (valbuf);
+}
+
+const char *
+get_config_value(const char *path)
+{
+ const char *value;
+
+ value = get_raw_config_value(path);
+ if (value == NULL)
+ return (NULL);
+ return (expand_config_value(value));
+}
+
+const char *
+get_config_value_node(const nvlist_t *parent, const char *name)
+{
+
+ if (strchr(name, '.') != NULL)
+ errx(4, "Invalid config node name %s", name);
+ if (parent == NULL)
+ parent = config_root;
+ if (nvlist_exists_nvlist(parent, name))
+ warnx("Attempt to fetch value of node %s of list %p", name,
+ parent);
+ if (!nvlist_exists_string(parent, name))
+ return (NULL);
+
+ return (expand_config_value(nvlist_get_string(parent, name)));
+}
+
+bool
+_bool_value(const char *name, const char *value)
+{
+
+ if (strcasecmp(value, "true") == 0 ||
+ strcasecmp(value, "on") == 0 ||
+ strcasecmp(value, "yes") == 0 ||
+ strcmp(value, "1") == 0)
+ return (true);
+ if (strcasecmp(value, "false") == 0 ||
+ strcasecmp(value, "off") == 0 ||
+ strcasecmp(value, "no") == 0 ||
+ strcmp(value, "0") == 0)
+ return (false);
+ err(4, "Invalid value %s for boolean variable %s", value, name);
+}
+
+bool
+get_config_bool(const char *path)
+{
+ const char *value;
+
+ value = get_config_value(path);
+ if (value == NULL)
+ err(4, "Failed to fetch boolean variable %s", path);
+ return (_bool_value(path, value));
+}
+
+bool
+get_config_bool_node(const nvlist_t *parent, const char *name)
+{
+ const char *value;
+
+ value = get_config_value_node(parent, name);
+ if (value == NULL)
+ err(4, "Failed to fetch boolean variable %s", name);
+ return (_bool_value(name, value));
+}
+
+void
+set_config_bool(const char *path, bool value)
+{
+
+ set_config_value(path, value ? "true" : "false");
+}
+
+void
+set_config_bool_node(nvlist_t *parent, const char *name, bool value)
+{
+
+ set_config_value_node(parent, name, value ? "true" : "false");
+}
+
+void
+dump_config(void)
+{
+ const nvlist_t *nvl;
+ void *cookie;
+ const char *name, *value, *expval;
+ unsigned int depth;
+ int type;
+
+ nvl = config_root;
+ cookie = NULL;
+ depth = 0;
+ for (;;) {
+ while ((name = nvlist_next(nvl, &type, &cookie)) != NULL) {
+ printf("%*s%s", (int)(depth * 4), "", name);
+ if (type == NV_TYPE_NVLIST) {
+ depth++;
+ nvl = nvlist_get_nvlist(nvl, name);
+ cookie = NULL;
+ } else {
+ assert(type == NV_TYPE_STRING);
+ value = nvlist_get_string(nvl, name);
+ printf("=%s", value);
+ expval = expand_config_value(value);
+ if (strcmp(value, expval) != 0)
+ printf(" (%s)", expval);
+ }
+ printf("\n");
+ }
+
+ nvl = nvlist_get_parent(nvl, &cookie);
+ if (nvl == NULL)
+ break;
+ depth--;
+ }
+}
Index: usr.sbin/bhyve/gdb.c
===================================================================
--- usr.sbin/bhyve/gdb.c
+++ usr.sbin/bhyve/gdb.c
@@ -131,6 +131,7 @@
static TAILQ_HEAD(, breakpoint) breakpoints;
static struct vcpu_state *vcpu_state;
static int cur_vcpu, stopped_vcpu;
+static bool gdb_active = false;
const int gdb_regset[] = {
VM_REG_GUEST_RAX,
@@ -729,6 +730,8 @@
_gdb_cpu_suspend(int vcpu, bool report_stop)
{
+ if (!gdb_active)
+ return;
debug("$vCPU %d suspending\n", vcpu);
CPU_SET(vcpu, &vcpus_waiting);
if (report_stop && CPU_CMP(&vcpus_waiting, &vcpus_suspended) == 0)
@@ -747,6 +750,8 @@
gdb_cpu_add(int vcpu)
{
+ if (!gdb_active)
+ return;
debug("$vCPU %d starting\n", vcpu);
pthread_mutex_lock(&gdb_lock);
assert(vcpu < guest_ncpus);
@@ -828,6 +833,8 @@
{
struct vcpu_state *vs;
+ if (!gdb_active)
+ return;
debug("$vCPU %d MTRAP\n", vcpu);
pthread_mutex_lock(&gdb_lock);
vs = &vcpu_state[vcpu];
@@ -868,6 +875,10 @@
uint64_t gpa;
int error;
+ if (!gdb_active) {
+ fprintf(stderr, "vm_loop: unexpected VMEXIT_DEBUG\n");
+ exit(4);
+ }
pthread_mutex_lock(&gdb_lock);
error = guest_vaddr2paddr(vcpu, vmexit->rip, &gpa);
assert(error == 1);
@@ -1858,4 +1869,5 @@
limit_gdb_socket(s);
#endif
mevent_add(s, EVF_READ, new_connection, NULL);
+ gdb_active = true;
}
Index: usr.sbin/bhyve/hda_codec.c
===================================================================
--- usr.sbin/bhyve/hda_codec.c
+++ usr.sbin/bhyve/hda_codec.c
@@ -205,7 +205,7 @@
* HDA Codec module function declarations
*/
static int hda_codec_init(struct hda_codec_inst *hci, const char *play,
- const char *rec, const char *opts);
+ const char *rec);
static int hda_codec_reset(struct hda_codec_inst *hci);
static int hda_codec_command(struct hda_codec_inst *hci, uint32_t cmd_data);
static int hda_codec_notify(struct hda_codec_inst *hci, uint8_t run,
@@ -391,7 +391,7 @@
static int
hda_codec_init(struct hda_codec_inst *hci, const char *play,
- const char *rec, const char *opts)
+ const char *rec)
{
struct hda_codec_softc *sc = NULL;
struct hda_codec_stream *st = NULL;
@@ -400,8 +400,6 @@
if (!(play || rec))
return (-1);
- DPRINTF("cad: 0x%x opts: %s", hci->cad, opts);
-
sc = calloc(1, sizeof(*sc));
if (!sc)
return (-1);
Index: usr.sbin/bhyve/inout.h
===================================================================
--- usr.sbin/bhyve/inout.h
+++ usr.sbin/bhyve/inout.h
@@ -72,8 +72,7 @@
DATA_SET(inout_port_set, __CONCAT(__inout_port, __LINE__))
void init_inout(void);
-int emulate_inout(struct vmctx *, int vcpu, struct vm_exit *vmexit,
- int strict);
+int emulate_inout(struct vmctx *, int vcpu, struct vm_exit *vmexit);
int register_inout(struct inout_port *iop);
int unregister_inout(struct inout_port *iop);
void init_bvmcons(void);
Index: usr.sbin/bhyve/inout.c
===================================================================
--- usr.sbin/bhyve/inout.c
+++ usr.sbin/bhyve/inout.c
@@ -48,6 +48,7 @@
#include <assert.h>
#include "bhyverun.h"
+#include "config.h"
#include "inout.h"
SET_DECLARE(inout_port_set, struct inout_port);
@@ -103,7 +104,7 @@
}
int
-emulate_inout(struct vmctx *ctx, int vcpu, struct vm_exit *vmexit, int strict)
+emulate_inout(struct vmctx *ctx, int vcpu, struct vm_exit *vmexit)
{
int addrsize, bytes, flags, in, port, prot, rep;
uint32_t eax, val;
@@ -124,7 +125,7 @@
handler = inout_handlers[port].handler;
- if (strict && handler == default_inout)
+ if (handler == default_inout && get_config_bool("x86.strictio"))
return (-1);
flags = inout_handlers[port].flags;
Index: usr.sbin/bhyve/mevent.c
===================================================================
--- usr.sbin/bhyve/mevent.c
+++ usr.sbin/bhyve/mevent.c
@@ -63,8 +63,6 @@
#define MEVENT_MAX 64
-extern const char *vmname;
-
static pthread_t mevent_tid;
static int mevent_timid = 43;
static int mevent_pipefd[2];
Index: usr.sbin/bhyve/mevent_test.c
===================================================================
--- usr.sbin/bhyve/mevent_test.c
+++ usr.sbin/bhyve/mevent_test.c
@@ -56,8 +56,6 @@
static struct mevent *tevp;
-char *vmname = "test vm";
-
#define MEVENT_ECHO
Index: usr.sbin/bhyve/net_backends.h
===================================================================
--- usr.sbin/bhyve/net_backends.h
+++ usr.sbin/bhyve/net_backends.h
@@ -37,8 +37,9 @@
/* Interface between network frontends and the network backends. */
typedef void (*net_be_rxeof_t)(int, enum ev_type, void *param);
-int netbe_init(net_backend_t **be, const char *opts, net_be_rxeof_t cb,
+int netbe_init(net_backend_t **be, nvlist_t *nvl, net_be_rxeof_t cb,
void *param);
+int netbe_legacy_config(nvlist_t *nvl, const char *opts);
void netbe_cleanup(net_backend_t *be);
uint64_t netbe_get_cap(net_backend_t *be);
int netbe_set_cap(net_backend_t *be, uint64_t cap,
Index: usr.sbin/bhyve/net_backends.c
===================================================================
--- usr.sbin/bhyve/net_backends.c
+++ usr.sbin/bhyve/net_backends.c
@@ -75,10 +75,12 @@
#include <netgraph.h>
#endif
+#include "config.h"
#include "debug.h"
#include "iov.h"
#include "mevent.h"
#include "net_backends.h"
+#include "pci_emul.h"
#include <sys/linker_set.h>
@@ -96,7 +98,7 @@
* and should not be called by the frontend.
*/
int (*init)(struct net_backend *be, const char *devname,
- const char *opts, net_be_rxeof_t cb, void *param);
+ nvlist_t *nvl, net_be_rxeof_t cb, void *param);
void (*cleanup)(struct net_backend *be);
/*
@@ -204,7 +206,7 @@
static int
tap_init(struct net_backend *be, const char *devname,
- const char *opts, net_be_rxeof_t cb, void *param)
+ nvlist_t *nvl, net_be_rxeof_t cb, void *param)
{
struct tap_priv *priv = (struct tap_priv *)be->opaque;
char tbuf[80];
@@ -398,18 +400,14 @@
static int
ng_init(struct net_backend *be, const char *devname,
- const char *opts, net_be_rxeof_t cb, void *param)
+ nvlist_t *nvl, net_be_rxeof_t cb, void *param)
{
struct tap_priv *p = (struct tap_priv *)be->opaque;
struct ngm_connect ngc;
- char *ngopts, *tofree;
- char nodename[NG_NODESIZ];
+ const char *value, *nodename;
int sbsz;
int ctrl_sock;
int flags;
- int path_provided;
- int peerhook_provided;
- int socket_provided;
unsigned long maxsbsz;
size_t msbsz;
#ifndef WITHOUT_CAPSICUM
@@ -425,56 +423,27 @@
memset(&ngc, 0, sizeof(ngc));
- strncpy(ngc.ourhook, "vmlink", NG_HOOKSIZ - 1);
-
- tofree = ngopts = strdup(opts);
-
- if (ngopts == NULL) {
- WPRINTF(("strdup error"));
- return (-1);
- }
-
- socket_provided = 0;
- path_provided = 0;
- peerhook_provided = 0;
-
- while (ngopts != NULL) {
- char *value = ngopts;
- char *key;
-
- key = strsep(&value, "=");
- if (value == NULL)
- break;
- ngopts = value;
- (void) strsep(&ngopts, ",");
-
- if (strcmp(key, "socket") == 0) {
- strncpy(nodename, value, NG_NODESIZ - 1);
- socket_provided = 1;
- } else if (strcmp(key, "path") == 0) {
- strncpy(ngc.path, value, NG_PATHSIZ - 1);
- path_provided = 1;
- } else if (strcmp(key, "hook") == 0) {
- strncpy(ngc.ourhook, value, NG_HOOKSIZ - 1);
- } else if (strcmp(key, "peerhook") == 0) {
- strncpy(ngc.peerhook, value, NG_HOOKSIZ - 1);
- peerhook_provided = 1;
- }
- }
-
- free(tofree);
-
- if (!path_provided) {
+ value = get_config_value_node(nvl, "path");
+ if (value == NULL) {
WPRINTF(("path must be provided"));
return (-1);
}
+ strncpy(ngc.path, value, NG_PATHSIZ - 1);
- if (!peerhook_provided) {
+ value = get_config_value_node(nvl, "hook");
+ if (value == NULL)
+ value = "vmlink";
+ strncpy(ngc.ourhook, value, NG_HOOKSIZ - 1);
+
+ value = get_config_value_node(nvl, "peerhook");
+ if (value == NULL) {
WPRINTF(("peer hook must be provided"));
return (-1);
}
+ strncpy(ngc.peerhook, value, NG_HOOKSIZ - 1);
- if (NgMkSockNode(socket_provided ? nodename : NULL,
+ nodename = get_config_value_node(nvl, "socket");
+ if (NgMkSockNode(nodename,
&ctrl_sock, &be->fd) < 0) {
WPRINTF(("can't get Netgraph sockets"));
return (-1);
@@ -664,7 +633,7 @@
static int
netmap_init(struct net_backend *be, const char *devname,
- const char *opts, net_be_rxeof_t cb, void *param)
+ nvlist_t *nvl, net_be_rxeof_t cb, void *param)
{
struct netmap_priv *priv = (struct netmap_priv *)be->opaque;
@@ -924,10 +893,26 @@
DATA_SET(net_backend_set, netmap_backend);
DATA_SET(net_backend_set, vale_backend);
+int
+netbe_legacy_config(nvlist_t *nvl, const char *opts)
+{
+ char *backend, *cp;
+
+ cp = strchr(opts, ',');
+ if (cp == NULL) {
+ set_config_value_node(nvl, "backend", opts);
+ return (0);
+ }
+ backend = strndup(opts, cp - opts);
+ set_config_value_node(nvl, "backend", backend);
+ free(backend);
+ return (pci_parse_legacy_config(nvl, cp + 1));
+}
+
/*
* Initialize a backend and attach to the frontend.
* This is called during frontend initialization.
- * @pbe is a pointer to the backend to be initialized
+ * @ret is a pointer to the backend to be initialized
* @devname is the backend-name as supplied on the command line,
* e.g. -s 2:0,frontend-name,backend-name[,other-args]
* @cb is the receive callback supplied by the frontend,
@@ -937,21 +922,19 @@
* the argument for the callback.
*/
int
-netbe_init(struct net_backend **ret, const char *opts, net_be_rxeof_t cb,
+netbe_init(struct net_backend **ret, nvlist_t *nvl, net_be_rxeof_t cb,
void *param)
{
struct net_backend **pbe, *nbe, *tbe = NULL;
+ const char *value;
char *devname;
- char *options;
int err;
- devname = options = strdup(opts);
-
+ value = get_config_value_node(nvl, "backend");
if (devname == NULL) {
return (-1);
}
-
- devname = strsep(&options, ",");
+ devname = strdup(value);
/*
* Find the network backend that matches the user-provided
@@ -985,7 +968,7 @@
nbe->fe_vnet_hdr_len = 0;
/* Initialize the backend. */
- err = nbe->init(nbe, devname, options, cb, param);
+ err = nbe->init(nbe, devname, nvl, cb, param);
if (err) {
free(devname);
free(nbe);
Index: usr.sbin/bhyve/net_utils.h
===================================================================
--- usr.sbin/bhyve/net_utils.h
+++ usr.sbin/bhyve/net_utils.h
@@ -34,7 +34,7 @@
#include "pci_emul.h"
void net_genmac(struct pci_devinst *pi, uint8_t *macaddr);
-int net_parsemac(char *mac_str, uint8_t *mac_addr);
+int net_parsemac(const char *mac_str, uint8_t *mac_addr);
int net_parsemtu(const char *mtu_str, unsigned long *mtu);
#endif /* _NET_UTILS_H_ */
Index: usr.sbin/bhyve/net_utils.c
===================================================================
--- usr.sbin/bhyve/net_utils.c
+++ usr.sbin/bhyve/net_utils.c
@@ -40,11 +40,12 @@
#include <string.h>
#include "bhyverun.h"
+#include "config.h"
#include "debug.h"
#include "net_utils.h"
int
-net_parsemac(char *mac_str, uint8_t *mac_addr)
+net_parsemac(const char *mac_str, uint8_t *mac_addr)
{
struct ether_addr *ea;
char zero_addr[ETHER_ADDR_LEN] = { 0, 0, 0, 0, 0, 0 };
@@ -107,7 +108,7 @@
char nstr[80];
snprintf(nstr, sizeof(nstr), "%d-%d-%s", pi->pi_slot,
- pi->pi_func, vmname);
+ pi->pi_func, get_config_value("name"));
MD5Init(&mdctx);
MD5Update(&mdctx, nstr, (unsigned int)strlen(nstr));
Index: usr.sbin/bhyve/pci_ahci.c
===================================================================
--- usr.sbin/bhyve/pci_ahci.c
+++ usr.sbin/bhyve/pci_ahci.c
@@ -58,6 +58,8 @@
#include <md5.h>
#include "bhyverun.h"
+#include "config.h"
+#include "debug.h"
#include "pci_emul.h"
#include "ahci.h"
#include "block_if.h"
@@ -2314,20 +2316,114 @@
return (value);
}
+/*
+ * Each AHCI controller has a "port" node which contains nodes for
+ * each port named after the decimal number of the port (no leading
+ * zeroes). Port nodes contain a "type" ("hd" or "cd"), as well as
+ * options for blockif. For example:
+ *
+ * pci.0.1.0
+ * .device="ahci"
+ * .port
+ * .0
+ * .type="hd"
+ * .path="/path/to/image"
+ */
static int
-pci_ahci_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts, int atapi)
+pci_ahci_legacy_config_port(nvlist_t *nvl, int port, const char *type,
+ const char *opts)
+{
+ char node_name[sizeof("XX")];
+ nvlist_t *port_nvl;
+
+ snprintf(node_name, sizeof(node_name), "%d", port);
+ port_nvl = create_relative_config_node(nvl, node_name);
+ set_config_value_node(port_nvl, "type", type);
+ if (opts == NULL)
+ return (0);
+ return (blockif_legacy_config(port_nvl, opts));
+}
+
+static int
+pci_ahci_legacy_config(nvlist_t *nvl, const char *opts)
+{
+ nvlist_t *ports_nvl;
+ const char *type;
+ char *next, *next2, *str, *tofree;
+ int p, ret;
+
+ ports_nvl = create_relative_config_node(nvl, "port");
+ ret = 1;
+ tofree = str = strdup(opts);
+ for (p = 0; p < MAX_PORTS && str != NULL; p++, str = next) {
+ /* Identify and cut off type of present port. */
+ if (strncmp(str, "hd:", 3) == 0) {
+ type = "hd";
+ str += 3;
+ } else if (strncmp(str, "cd:", 3) == 0) {
+ type = "cd";
+ str += 3;
+ } else
+ type = NULL;
+
+ /* Find and cut off the next port options. */
+ next = strstr(str, ",hd:");
+ next2 = strstr(str, ",cd:");
+ if (next == NULL || (next2 != NULL && next2 < next))
+ next = next2;
+ if (next != NULL) {
+ next[0] = 0;
+ next++;
+ }
+
+ if (str[0] == 0)
+ continue;
+
+ if (type == NULL) {
+ EPRINTLN("Missing or invalid type for port %d: \"%s\"",
+ p, str);
+ goto out;
+ }
+
+ if (pci_ahci_legacy_config_port(ports_nvl, p, type, str) != 0)
+ goto out;
+ }
+ ret = 0;
+out:
+ free(tofree);
+ return (ret);
+}
+
+static int
+pci_ahci_cd_legacy_config(nvlist_t *nvl, const char *opts)
+{
+ nvlist_t *ports_nvl;
+
+ ports_nvl = create_relative_config_node(nvl, "port");
+ return (pci_ahci_legacy_config_port(ports_nvl, 0, "cd", opts));
+}
+
+static int
+pci_ahci_hd_legacy_config(nvlist_t *nvl, const char *opts)
+{
+ nvlist_t *ports_nvl;
+
+ ports_nvl = create_relative_config_node(nvl, "port");
+ return (pci_ahci_legacy_config_port(ports_nvl, 0, "hd", opts));
+}
+
+static int
+pci_ahci_init(struct vmctx *ctx, struct pci_devinst *pi, nvlist_t *nvl)
{
char bident[sizeof("XX:XX:XX")];
+ char node_name[sizeof("XX")];
struct blockif_ctxt *bctxt;
struct pci_ahci_softc *sc;
- int ret, slots, p;
+ int atapi, ret, slots, p;
MD5_CTX mdctx;
u_char digest[16];
- char *next, *next2;
- char *bopt, *uopt, *xopts, *config;
- FILE* fp;
- size_t block_len;
- int comma, optpos;
+ const char *path, *type, *value;
+ nvlist_t *ports_nvl, *port_nvl;
ret = 0;
@@ -2343,98 +2439,24 @@
sc->pi = 0;
slots = 32;
- for (p = 0; p < MAX_PORTS && opts != NULL; p++, opts = next) {
+ ports_nvl = find_relative_config_node(nvl, "port");
+ for (p = 0; p < MAX_PORTS; p++) {
struct ata_params *ata_ident = &sc->port[p].ata_ident;
- memset(ata_ident, 0, sizeof(struct ata_params));
+ char ident[AHCI_PORT_IDENT];
- /* Identify and cut off type of present port. */
- if (strncmp(opts, "hd:", 3) == 0) {
+ snprintf(node_name, sizeof(node_name), "%d", p);
+ port_nvl = find_relative_config_node(ports_nvl, node_name);
+ if (port_nvl == NULL)
+ continue;
+
+ type = get_config_value_node(port_nvl, "type");
+ if (type == NULL)
+ continue;
+
+ if (strcmp(type, "hd") == 0)
atapi = 0;
- opts += 3;
- } else if (strncmp(opts, "cd:", 3) == 0) {
+ else
atapi = 1;
- opts += 3;
- }
-
- /* Find and cut off the next port options. */
- next = strstr(opts, ",hd:");
- next2 = strstr(opts, ",cd:");
- if (next == NULL || (next2 != NULL && next2 < next))
- next = next2;
- if (next != NULL) {
- next[0] = 0;
- next++;
- }
-
- if (opts[0] == 0)
- continue;
-
- uopt = strdup(opts);
- bopt = NULL;
- fp = open_memstream(&bopt, &block_len);
- comma = 0;
- optpos = 0;
-
- for (xopts = strtok(uopt, ",");
- xopts != NULL;
- xopts = strtok(NULL, ",")) {
-
- /* First option assume as block filename. */
- if (optpos == 0) {
- /*
- * Create an identifier for the backing file.
- * Use parts of the md5 sum of the filename
- */
- char ident[AHCI_PORT_IDENT];
- MD5Init(&mdctx);
- MD5Update(&mdctx, opts, strlen(opts));
- MD5Final(digest, &mdctx);
- snprintf(ident, AHCI_PORT_IDENT,
- "BHYVE-%02X%02X-%02X%02X-%02X%02X",
- digest[0], digest[1], digest[2], digest[3], digest[4],
- digest[5]);
- ata_string((uint8_t*)&ata_ident->serial, ident, 20);
- ata_string((uint8_t*)&ata_ident->revision, "001", 8);
- if (atapi) {
- ata_string((uint8_t*)&ata_ident->model, "BHYVE SATA DVD ROM", 40);
- }
- else {
- ata_string((uint8_t*)&ata_ident->model, "BHYVE SATA DISK", 40);
- }
- }
-
- if ((config = strchr(xopts, '=')) != NULL) {
- *config++ = '\0';
- if (!strcmp("nmrr", xopts)) {
- ata_ident->media_rotation_rate = atoi(config);
- }
- else if (!strcmp("ser", xopts)) {
- ata_string((uint8_t*)(&ata_ident->serial), config, 20);
- }
- else if (!strcmp("rev", xopts)) {
- ata_string((uint8_t*)(&ata_ident->revision), config, 8);
- }
- else if (!strcmp("model", xopts)) {
- ata_string((uint8_t*)(&ata_ident->model), config, 40);
- }
- else {
- /* Pass all other options to blockif_open. */
- *--config = '=';
- fprintf(fp, "%s%s", comma ? "," : "", xopts);
- comma = 1;
- }
- }
- else {
- /* Pass all other options to blockif_open. */
- fprintf(fp, "%s%s", comma ? "," : "", xopts);
- comma = 1;
- }
- optpos++;
- }
- free(uopt);
- fclose(fp);
-
- DPRINTF("%s\n", bopt);
/*
* Attempt to open the backing image. Use the PCI slot/func
@@ -2442,9 +2464,8 @@
*/
snprintf(bident, sizeof(bident), "%d:%d:%d", pi->pi_slot,
pi->pi_func, p);
- bctxt = blockif_open(bopt, bident);
- free(bopt);
+ bctxt = blockif_open(port_nvl, bident);
if (bctxt == NULL) {
sc->ports = p;
ret = 1;
@@ -2455,6 +2476,38 @@
sc->port[p].port = p;
sc->port[p].atapi = atapi;
+ /*
+ * Create an identifier for the backing file.
+ * Use parts of the md5 sum of the filename
+ */
+ path = get_config_value_node(port_nvl, "path");
+ MD5Init(&mdctx);
+ MD5Update(&mdctx, path, strlen(path));
+ MD5Final(digest, &mdctx);
+ snprintf(ident, AHCI_PORT_IDENT,
+ "BHYVE-%02X%02X-%02X%02X-%02X%02X",
+ digest[0], digest[1], digest[2], digest[3], digest[4],
+ digest[5]);
+
+ memset(ata_ident, 0, sizeof(struct ata_params));
+ ata_string((uint8_t*)&ata_ident->serial, ident, 20);
+ ata_string((uint8_t*)&ata_ident->revision, "001", 8);
+ if (atapi)
+ ata_string((uint8_t*)&ata_ident->model, "BHYVE SATA DVD ROM", 40);
+ else
+ ata_string((uint8_t*)&ata_ident->model, "BHYVE SATA DISK", 40);
+ value = get_config_value_node(port_nvl, "nmrr");
+ if (value != NULL)
+ ata_ident->media_rotation_rate = atoi(value);
+ value = get_config_value_node(port_nvl, "ser");
+ if (value != NULL)
+ ata_string((uint8_t*)(&ata_ident->serial), value, 20);
+ value = get_config_value_node(port_nvl, "rev");
+ if (value != NULL)
+ ata_string((uint8_t*)(&ata_ident->revision), value, 8);
+ value = get_config_value_node(port_nvl, "model");
+ if (value != NULL)
+ ata_string((uint8_t*)(&ata_ident->model), value, 40);
ata_identify_init(&sc->port[p], atapi);
/*
@@ -2508,20 +2561,6 @@
return (ret);
}
-static int
-pci_ahci_hd_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
-{
-
- return (pci_ahci_init(ctx, pi, opts, 0));
-}
-
-static int
-pci_ahci_atapi_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
-{
-
- return (pci_ahci_init(ctx, pi, opts, 1));
-}
-
#ifdef BHYVE_SNAPSHOT
static int
pci_ahci_snapshot_save_queues(struct ahci_port *port,
@@ -2803,7 +2842,8 @@
*/
struct pci_devemu pci_de_ahci = {
.pe_emu = "ahci",
- .pe_init = pci_ahci_hd_init,
+ .pe_init = pci_ahci_init,
+ .pe_legacy_config = pci_ahci_legacy_config,
.pe_barwrite = pci_ahci_write,
.pe_barread = pci_ahci_read,
#ifdef BHYVE_SNAPSHOT
@@ -2816,7 +2856,8 @@
struct pci_devemu pci_de_ahci_hd = {
.pe_emu = "ahci-hd",
- .pe_init = pci_ahci_hd_init,
+ .pe_init = pci_ahci_init,
+ .pe_legacy_config = pci_ahci_hd_legacy_config,
.pe_barwrite = pci_ahci_write,
.pe_barread = pci_ahci_read,
#ifdef BHYVE_SNAPSHOT
@@ -2829,7 +2870,8 @@
struct pci_devemu pci_de_ahci_cd = {
.pe_emu = "ahci-cd",
- .pe_init = pci_ahci_atapi_init,
+ .pe_init = pci_ahci_init,
+ .pe_legacy_config = pci_ahci_cd_legacy_config,
.pe_barwrite = pci_ahci_write,
.pe_barread = pci_ahci_read,
#ifdef BHYVE_SNAPSHOT
Index: usr.sbin/bhyve/pci_e82545.c
===================================================================
--- usr.sbin/bhyve/pci_e82545.c
+++ usr.sbin/bhyve/pci_e82545.c
@@ -65,6 +65,7 @@
#include "mii.h"
#include "bhyverun.h"
+#include "config.h"
#include "debug.h"
#include "pci_emul.h"
#include "mevent.h"
@@ -2277,15 +2278,12 @@
}
static int
-e82545_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
+e82545_init(struct vmctx *ctx, struct pci_devinst *pi, nvlist_t *nvl)
{
char nstr[80];
struct e82545_softc *sc;
- char *optscopy;
- char *vtopts;
- int mac_provided;
-
- DPRINTF("Loading with options: %s", opts);
+ const char *mac;
+ int err;
/* Setup our softc */
sc = calloc(1, sizeof(*sc));
@@ -2323,56 +2321,20 @@
pci_emul_alloc_bar(pi, E82545_BAR_IO, PCIBAR_IO,
E82545_BAR_IO_LEN);
- /*
- * Attempt to open the net backend and read the MAC address
- * if specified. Copied from virtio-net, slightly modified.
- */
- mac_provided = 0;
- sc->esc_be = NULL;
- if (opts != NULL) {
- int err = 0;
-
- optscopy = vtopts = strdup(opts);
- (void) strsep(&vtopts, ",");
-
- /*
- * Parse the list of options in the form
- * key1=value1,...,keyN=valueN.
- */
- while (vtopts != NULL) {
- char *value = vtopts;
- char *key;
-
- key = strsep(&value, "=");
- if (value == NULL)
- break;
- vtopts = value;
- (void) strsep(&vtopts, ",");
-
- if (strcmp(key, "mac") == 0) {
- err = net_parsemac(value, sc->esc_mac.octet);
- if (err)
- break;
- mac_provided = 1;
- }
- }
-
- free(optscopy);
-
- if (err) {
- free(sc);
- return (err);
- }
-
- err = netbe_init(&sc->esc_be, opts, e82545_rx_callback, sc);
+ mac = get_config_value_node(nvl, "mac");
+ if (mac != NULL) {
+ err = net_parsemac(mac, sc->esc_mac.octet);
if (err) {
free(sc);
return (err);
}
- }
-
- if (!mac_provided) {
+ } else
net_genmac(pi, sc->esc_mac.octet);
+
+ err = netbe_init(&sc->esc_be, nvl, e82545_rx_callback, sc);
+ if (err) {
+ free(sc);
+ return (err);
}
netbe_rx_enable(sc->esc_be);
@@ -2540,6 +2502,7 @@
struct pci_devemu pci_de_e82545 = {
.pe_emu = "e1000",
.pe_init = e82545_init,
+ .pe_legacy_config = netbe_legacy_config,
.pe_barwrite = e82545_write,
.pe_barread = e82545_read,
#ifdef BHYVE_SNAPSHOT
Index: usr.sbin/bhyve/pci_emul.h
===================================================================
--- usr.sbin/bhyve/pci_emul.h
+++ usr.sbin/bhyve/pci_emul.h
@@ -34,6 +34,7 @@
#include <sys/types.h>
#include <sys/queue.h>
#include <sys/kernel.h>
+#include <sys/nv.h>
#include <sys/_pthreadtypes.h>
#include <dev/pci/pcireg.h>
@@ -52,7 +53,8 @@
/* instance creation */
int (*pe_init)(struct vmctx *, struct pci_devinst *,
- char *opts);
+ nvlist_t *);
+ int (*pe_legacy_config)(nvlist_t *, const char *);
/* ACPI DSDT enumeration */
void (*pe_write_dsdt)(struct pci_devinst *);
@@ -238,6 +240,7 @@
int pci_msix_table_bar(struct pci_devinst *pi);
int pci_msix_pba_bar(struct pci_devinst *pi);
int pci_msi_maxmsgnum(struct pci_devinst *pi);
+int pci_parse_legacy_config(nvlist_t *nvl, const char *opt);
int pci_parse_slot(char *opt);
void pci_print_supported_devices();
void pci_populate_msicap(struct msicap *cap, int msgs, int nextptr);
Index: usr.sbin/bhyve/pci_emul.c
===================================================================
--- usr.sbin/bhyve/pci_emul.c
+++ usr.sbin/bhyve/pci_emul.c
@@ -50,6 +50,7 @@
#include "acpi.h"
#include "bhyverun.h"
+#include "config.h"
#include "debug.h"
#include "inout.h"
#include "ioapic.h"
@@ -68,8 +69,8 @@
#define MAXFUNCS (PCI_FUNCMAX + 1)
struct funcinfo {
- char *fi_name;
- char *fi_param;
+ nvlist_t *fi_config;
+ struct pci_devemu *fi_pde;
struct pci_devinst *fi_devi;
};
@@ -111,7 +112,7 @@
#define PCI_EMUL_MEMBASE64 0xD000000000UL
#define PCI_EMUL_MEMLIMIT64 0xFD00000000UL
-static struct pci_devemu *pci_emul_finddev(char *name);
+static struct pci_devemu *pci_emul_finddev(const char *name);
static void pci_lintr_route(struct pci_devinst *pi);
static void pci_lintr_update(struct pci_devinst *pi);
static void pci_cfgrw(struct vmctx *ctx, int vcpu, int in, int bus, int slot,
@@ -167,13 +168,53 @@
EPRINTLN("Invalid PCI slot info field \"%s\"", aopt);
}
+/*
+ * Helper function to parse a list of comma-separated options where
+ * each option is formatted as "name[=value]". If no value is
+ * provided, the option is treated as a boolean and is given a value
+ * of true.
+ */
+int
+pci_parse_legacy_config(nvlist_t *nvl, const char *opt)
+{
+ char *config, *name, *tofree, *value;
+
+ config = tofree = strdup(opt);
+ while ((name = strsep(&config, ",")) != NULL) {
+ value = strchr(name, '=');
+ if (value != NULL) {
+ *value = '\0';
+ value++;
+ set_config_value_node(nvl, name, value);
+ } else
+ set_config_bool_node(nvl, name, true);
+ }
+ free(tofree);
+ return (0);
+}
+
+/*
+ * PCI device configuration is stored in MIBs that encode the device's
+ * location:
+ *
+ * pci.<bus>.<slot>.<func>
+ *
+ * Where "bus", "slot", and "func" are all decimal values without
+ * leading zeroes. Each valid device must have a "device" node which
+ * identifies the driver model of the device.
+ *
+ * Device backends can provide a parser for the "config" string. If
+ * a custom parser is not provided, pci_parse_legacy_config() is used
+ * to parse the string.
+ */
int
pci_parse_slot(char *opt)
{
- struct businfo *bi;
- struct slotinfo *si;
+ char node_name[sizeof("pci.XXX.XX.X")];
+ struct pci_devemu *pde;
char *emul, *config, *str, *cp;
int error, bnum, snum, fnum;
+ nvlist_t *nvl;
error = -1;
str = strdup(opt);
@@ -210,32 +251,33 @@
goto done;
}
- if (pci_businfo[bnum] == NULL)
- pci_businfo[bnum] = calloc(1, sizeof(struct businfo));
-
- bi = pci_businfo[bnum];
- si = &bi->slotinfo[snum];
-
- if (si->si_funcs[fnum].fi_name != NULL) {
- EPRINTLN("pci slot %d:%d already occupied!",
- snum, fnum);
+ snprintf(node_name, sizeof(node_name), "pci.%d.%d.%d", bnum, snum,
+ fnum);
+ nvl = find_config_node(node_name);
+ if (nvl != NULL) {
+ EPRINTLN("pci slot %d:%d:%d already occupied!", bnum, snum,
+ fnum);
goto done;
}
+ nvl = create_config_node(node_name);
+ set_config_value_node(nvl, "device", emul);
- if (pci_emul_finddev(emul) == NULL) {
- EPRINTLN("pci slot %d:%d: unknown device \"%s\"",
- snum, fnum, emul);
+ pde = pci_emul_finddev(emul);
+ if (pde == NULL) {
+ EPRINTLN("pci slot %d:%d:%d: unknown device \"%s\"", bnum, snum,
+ fnum, emul);
goto done;
}
- error = 0;
- si->si_funcs[fnum].fi_name = emul;
- si->si_funcs[fnum].fi_param = config;
-
+ if (config != NULL) {
+ if (pde->pe_legacy_config != NULL)
+ error = pde->pe_legacy_config(nvl, config);
+ else
+ error = pci_parse_legacy_config(nvl, config);
+ } else
+ error = 0;
done:
- if (error)
- free(str);
-
+ free(str);
return (error);
}
@@ -725,7 +767,7 @@
}
static struct pci_devemu *
-pci_emul_finddev(char *name)
+pci_emul_finddev(const char *name)
{
struct pci_devemu **pdpp, *pdp;
@@ -766,7 +808,7 @@
pci_set_cfgdata8(pdi, PCIR_COMMAND, PCIM_CMD_BUSMASTEREN);
- err = (*pde->pe_init)(ctx, pdi, fi->fi_param);
+ err = (*pde->pe_init)(ctx, pdi, fi->fi_config);
if (err == 0)
fi->fi_devi = pdi;
else
@@ -1095,11 +1137,14 @@
int
init_pci(struct vmctx *ctx)
{
+ char node_name[sizeof("pci.XXX.XX.X")];
struct mem_range mr;
struct pci_devemu *pde;
struct businfo *bi;
struct slotinfo *si;
struct funcinfo *fi;
+ nvlist_t *nvl;
+ const char *emul;
size_t lowmem;
int bus, slot, func;
int error;
@@ -1109,8 +1154,13 @@
pci_emul_membase64 = PCI_EMUL_MEMBASE64;
for (bus = 0; bus < MAXBUSES; bus++) {
- if ((bi = pci_businfo[bus]) == NULL)
+ snprintf(node_name, sizeof(node_name), "pci.%d", bus);
+ nvl = find_config_node(node_name);
+ if (nvl == NULL)
continue;
+ pci_businfo[bus] = calloc(1, sizeof(struct businfo));
+ bi = pci_businfo[bus];
+
/*
* Keep track of the i/o and memory resources allocated to
* this bus.
@@ -1123,10 +1173,27 @@
si = &bi->slotinfo[slot];
for (func = 0; func < MAXFUNCS; func++) {
fi = &si->si_funcs[func];
- if (fi->fi_name == NULL)
+ snprintf(node_name, sizeof(node_name),
+ "pci.%d.%d.%d", bus, slot, func);
+ nvl = find_config_node(node_name);
+ if (nvl == NULL)
continue;
- pde = pci_emul_finddev(fi->fi_name);
- assert(pde != NULL);
+
+ fi->fi_config = nvl;
+ emul = get_config_value_node(nvl, "device");
+ if (emul == NULL) {
+ EPRINTLN("pci slot %d:%d:%d: missing "
+ "\"device\" value", bus, slot, func);
+ return (EINVAL);
+ }
+ pde = pci_emul_finddev(emul);
+ if (pde == NULL) {
+ EPRINTLN("pci slot %d:%d:%d: unknown"
+ " device \"%s\"", bus, slot, func,
+ emul);
+ return (EINVAL);
+ }
+ fi->fi_pde = pde;
error = pci_emul_init(ctx, pde, bus, slot,
func, fi);
if (error)
@@ -2038,14 +2105,12 @@
si = &bi->slotinfo[slot];
for (func = 0; func < MAXFUNCS; func++) {
fi = &si->si_funcs[func];
- if (fi->fi_name == NULL)
+ if (fi->fi_pde == NULL)
continue;
- if (strcmp(dev_name, fi->fi_name))
+ if (strcmp(dev_name, fi->fi_pde->pe_emu) != 0)
continue;
- *pde = pci_emul_finddev(fi->fi_name);
- assert(*pde != NULL);
-
+ *pde = fi->fi_pde;
*pdi = fi->fi_devi;
return (0);
}
@@ -2167,7 +2232,7 @@
#define PCI_EMUL_MSIX_MSGS 16
static int
-pci_emul_dinit(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
+pci_emul_dinit(struct vmctx *ctx, struct pci_devinst *pi, nvlist_t *nvl)
{
int error;
struct pci_emul_dsoftc *sc;
Index: usr.sbin/bhyve/pci_fbuf.c
===================================================================
--- usr.sbin/bhyve/pci_fbuf.c
+++ usr.sbin/bhyve/pci_fbuf.c
@@ -47,6 +47,7 @@
#include "bhyvegc.h"
#include "bhyverun.h"
+#include "config.h"
#include "debug.h"
#include "console.h"
#include "inout.h"
@@ -115,15 +116,6 @@
#define PCI_FBUF_MSI_MSGS 4
-static void
-pci_fbuf_usage(char *opt)
-{
-
- EPRINTLN("Invalid fbuf emulation option \"%s\"", opt);
- EPRINTLN("fbuf: {wait,}{vga=on|io|off,}rfb=<ip>:port"
- "{,w=width}{,h=height}");
-}
-
static void
pci_fbuf_write(struct vmctx *ctx, int vcpu, struct pci_devinst *pi,
int baridx, uint64_t offset, int size, uint64_t value)
@@ -225,102 +217,110 @@
}
static int
-pci_fbuf_parse_opts(struct pci_fbuf_softc *sc, char *opts)
+pci_fbuf_parse_config(struct pci_fbuf_softc *sc, nvlist_t *nvl)
{
- char *uopts, *uoptsbak, *xopts, *config;
- char *tmpstr;
- int ret;
+ const char *value;
+ char *cp;
- ret = 0;
- uoptsbak = uopts = strdup(opts);
- while ((xopts = strsep(&uopts, ",")) != NULL) {
- if (strcmp(xopts, "wait") == 0) {
- sc->rfb_wait = 1;
- continue;
- }
-
- if ((config = strchr(xopts, '=')) == NULL) {
- pci_fbuf_usage(xopts);
- ret = -1;
- goto done;
- }
-
- *config++ = '\0';
-
- DPRINTF(DEBUG_VERBOSE, ("pci_fbuf option %s = %s",
- xopts, config));
+ value = get_config_value_node(nvl, "wait");
+ if (value != NULL)
+ sc->rfb_wait = get_config_bool_node(nvl, "wait");
- if (!strcmp(xopts, "tcp") || !strcmp(xopts, "rfb")) {
- /*
- * IPv4 -- host-ip:port
- * IPv6 -- [host-ip%zone]:port
- * XXX for now port is mandatory.
- */
- tmpstr = strsep(&config, "]");
- if (config) {
- if (tmpstr[0] == '[')
- tmpstr++;
- sc->rfb_host = strdup(tmpstr);
- if (config[0] == ':')
- config++;
- else {
- pci_fbuf_usage(xopts);
- ret = -1;
- goto done;
- }
- sc->rfb_port = atoi(config);
- } else {
- config = tmpstr;
- tmpstr = strsep(&config, ":");
- if (!config)
- sc->rfb_port = atoi(tmpstr);
- else {
- sc->rfb_port = atoi(config);
- sc->rfb_host = strdup(tmpstr);
+ /* Prefer "rfb" to "tcp". */
+ value = get_config_value_node(nvl, "rfb");
+ if (value == NULL)
+ value = get_config_value_node(nvl, "tcp");
+ if (value != NULL) {
+ /*
+ * IPv4 -- host-ip:port
+ * IPv6 -- [host-ip%zone]:port
+ * XXX for now port is mandatory for IPv4.
+ */
+ if (value[0] == '[') {
+ cp = strchr(value + 1, ']');
+ if (cp == NULL || cp == value + 1) {
+ EPRINTLN("fbuf: Invalid IPv6 address: \"%s\"",
+ value);
+ return (-1);
+ }
+ sc->rfb_host = strndup(value + 1, cp - (value + 1));
+ cp++;
+ if (*cp == ':') {
+ cp++;
+ if (*cp == '\0') {
+ EPRINTLN(
+ "fbuf: Missing port number: \"%s\"",
+ value);
+ return (-1);
}
+ sc->rfb_port = atoi(cp + 1);
+ } else if (*cp != '\0') {
+ EPRINTLN("fbuf: Invalid IPv6 address: \"%s\"",
+ value);
+ return (-1);
}
- } else if (!strcmp(xopts, "vga")) {
- if (!strcmp(config, "off")) {
- sc->vga_enabled = 0;
- } else if (!strcmp(config, "io")) {
- sc->vga_enabled = 1;
- sc->vga_full = 0;
- } else if (!strcmp(config, "on")) {
- sc->vga_enabled = 1;
- sc->vga_full = 1;
+ } else {
+ cp = strchr(value, ':');
+ if (cp == NULL) {
+ sc->rfb_port = atoi(value);
} else {
- pci_fbuf_usage(xopts);
- ret = -1;
- goto done;
+ sc->rfb_host = strndup(value, cp - value);
+ cp++;
+ if (*cp == '\0') {
+ EPRINTLN(
+ "fbuf: Missing port number: \"%s\"",
+ value);
+ return (-1);
+ }
+ sc->rfb_port = atoi(cp + 1);
}
- } else if (!strcmp(xopts, "w")) {
- sc->memregs.width = atoi(config);
- if (sc->memregs.width > COLS_MAX) {
- pci_fbuf_usage(xopts);
- ret = -1;
- goto done;
- } else if (sc->memregs.width == 0)
- sc->memregs.width = 1920;
- } else if (!strcmp(xopts, "h")) {
- sc->memregs.height = atoi(config);
- if (sc->memregs.height > ROWS_MAX) {
- pci_fbuf_usage(xopts);
- ret = -1;
- goto done;
- } else if (sc->memregs.height == 0)
- sc->memregs.height = 1080;
- } else if (!strcmp(xopts, "password")) {
- sc->rfb_password = strdup(config);
+ }
+ }
+
+ value = get_config_value_node(nvl, "vga");
+ if (value != NULL) {
+ if (strcmp(value, "off") == 0) {
+ sc->vga_enabled = 0;
+ } else if (strcmp(value, "io") == 0) {
+ sc->vga_enabled = 1;
+ sc->vga_full = 0;
+ } else if (strcmp(value, "on") == 0) {
+ sc->vga_enabled = 1;
+ sc->vga_full = 1;
} else {
- pci_fbuf_usage(xopts);
- ret = -1;
- goto done;
+ EPRINTLN("fbuf: Invalid vga setting: \"%s\"", value);
+ return (-1);
+ }
+ }
+
+ value = get_config_value_node(nvl, "w");
+ if (value != NULL) {
+ sc->memregs.width = atoi(value);
+ if (sc->memregs.width > COLS_MAX) {
+ EPRINTLN("fbuf: width %d too large", sc->memregs.width);
+ return (-1);
}
+ if (sc->memregs.width == 0)
+ sc->memregs.width = 1920;
}
-done:
- free(uoptsbak);
- return (ret);
+ value = get_config_value_node(nvl, "h");
+ if (value != NULL) {
+ sc->memregs.height = atoi(value);
+ if (sc->memregs.height > ROWS_MAX) {
+ EPRINTLN("fbuf: height %d too large",
+ sc->memregs.height);
+ return (-1);
+ }
+ if (sc->memregs.height == 0)
+ sc->memregs.height = 1080;
+ }
+
+ value = get_config_value_node(nvl, "password");
+ if (value != NULL)
+ sc->rfb_password = strdup(value);
+
+ return (0);
}
@@ -351,7 +351,7 @@
}
static int
-pci_fbuf_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
+pci_fbuf_init(struct vmctx *ctx, struct pci_devinst *pi, nvlist_t *nvl)
{
int error, prot;
struct pci_fbuf_softc *sc;
@@ -391,7 +391,7 @@
sc->fsc_pi = pi;
- error = pci_fbuf_parse_opts(sc, opts);
+ error = pci_fbuf_parse_config(sc, nvl);
if (error != 0)
goto done;
Index: usr.sbin/bhyve/pci_hda.h
===================================================================
--- usr.sbin/bhyve/pci_hda.h
+++ usr.sbin/bhyve/pci_hda.h
@@ -72,7 +72,7 @@
struct hda_codec_class {
char *name;
int (*init)(struct hda_codec_inst *hci, const char *play,
- const char *rec, const char *opts);
+ const char *rec);
int (*reset)(struct hda_codec_inst *hci);
int (*command)(struct hda_codec_inst *hci, uint32_t cmd_data);
int (*notify)(struct hda_codec_inst *hci, uint8_t run, uint8_t stream,
Index: usr.sbin/bhyve/pci_hda.c
===================================================================
--- usr.sbin/bhyve/pci_hda.c
+++ usr.sbin/bhyve/pci_hda.c
@@ -34,6 +34,7 @@
#include "pci_hda.h"
#include "bhyverun.h"
+#include "config.h"
#include "pci_emul.h"
#include "hdac_reg.h"
@@ -147,13 +148,11 @@
static inline void hda_set_field_by_offset(struct hda_softc *sc,
uint32_t offset, uint32_t mask, uint32_t value);
-static uint8_t hda_parse_config(const char *opts, const char *key, char *val);
-static struct hda_softc *hda_init(const char *opts);
+static struct hda_softc *hda_init(nvlist_t *nvl);
static void hda_update_intr(struct hda_softc *sc);
static void hda_response_interrupt(struct hda_softc *sc);
static int hda_codec_constructor(struct hda_softc *sc,
- struct hda_codec_class *codec, const char *play, const char *rec,
- const char *opts);
+ struct hda_codec_class *codec, const char *play, const char *rec);
static struct hda_codec_class *hda_find_codec_class(const char *name);
static int hda_send_command(struct hda_softc *sc, uint32_t verb);
@@ -210,7 +209,7 @@
/*
* PCI HDA function declarations
*/
-static int pci_hda_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts);
+static int pci_hda_init(struct vmctx *ctx, struct pci_devinst *pi, nvlist_t *nvl);
static void pci_hda_write(struct vmctx *ctx, int vcpu, struct pci_devinst *pi,
int baridx, uint64_t offset, int size, uint64_t value);
static uint64_t pci_hda_read(struct vmctx *ctx, int vcpu, struct pci_devinst *pi,
@@ -318,67 +317,20 @@
hda_set_reg_by_offset(sc, offset, reg_value);
}
-static uint8_t
-hda_parse_config(const char *opts, const char *key, char *val)
-{
- char buf[64];
- char *s = buf;
- char *tmp = NULL;
- size_t len;
- int i;
-
- if (!opts)
- return (0);
-
- len = strlen(opts);
- if (len >= sizeof(buf)) {
- DPRINTF("Opts too big");
- return (0);
- }
-
- DPRINTF("opts: %s", opts);
-
- strcpy(buf, opts);
-
- for (i = 0; i < len; i++)
- if (buf[i] == ',') {
- buf[i] = 0;
- tmp = buf + i + 1;
- break;
- }
-
- if (!memcmp(s, key, strlen(key))) {
- strncpy(val, s + strlen(key), 64);
- return (1);
- }
-
- if (!tmp)
- return (0);
-
- s = tmp;
- if (!memcmp(s, key, strlen(key))) {
- strncpy(val, s + strlen(key), 64);
- return (1);
- }
-
- return (0);
-}
-
static struct hda_softc *
-hda_init(const char *opts)
+hda_init(nvlist_t *nvl)
{
struct hda_softc *sc = NULL;
struct hda_codec_class *codec = NULL;
- char play[64];
- char rec[64];
- int err, p, r;
+ const char *value;
+ char *play;
+ char *rec;
+ int err;
#if DEBUG_HDA == 1
dbg = fopen("/tmp/bhyve_hda.log", "w+");
#endif
- DPRINTF("opts: %s", opts);
-
sc = calloc(1, sizeof(*sc));
if (!sc)
return (NULL);
@@ -386,19 +338,28 @@
hda_reset_regs(sc);
/*
- * TODO search all the codecs declared in opts
+ * TODO search all configured codecs
* For now we play with one single codec
*/
codec = hda_find_codec_class("hda_codec");
if (codec) {
- p = hda_parse_config(opts, "play=", play);
- r = hda_parse_config(opts, "rec=", rec);
+ value = get_config_value_node(nvl, "play");
+ if (value == NULL)
+ play = NULL;
+ else
+ play = strdup(value);
+ value = get_config_value_node(nvl, "rec");
+ if (value == NULL)
+ rec = NULL;
+ else
+ rec = strdup(value);
DPRINTF("play: %s rec: %s", play, rec);
- if (p | r) {
- err = hda_codec_constructor(sc, codec, p ? \
- play : NULL, r ? rec : NULL, NULL);
+ if (play != NULL || rec != NULL) {
+ err = hda_codec_constructor(sc, codec, play, rec);
assert(!err);
}
+ free(play);
+ free(rec);
}
return (sc);
@@ -470,7 +431,7 @@
static int
hda_codec_constructor(struct hda_softc *sc, struct hda_codec_class *codec,
- const char *play, const char *rec, const char *opts)
+ const char *play, const char *rec)
{
struct hda_codec_inst *hci = NULL;
@@ -493,7 +454,7 @@
return (-1);
}
- return (codec->init(hci, play, rec, opts));
+ return (codec->init(hci, play, rec));
}
static struct hda_codec_class *
@@ -1263,7 +1224,7 @@
* PCI HDA function definitions
*/
static int
-pci_hda_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
+pci_hda_init(struct vmctx *ctx, struct pci_devinst *pi, nvlist_t *nvl)
{
struct hda_softc *sc = NULL;
@@ -1285,7 +1246,7 @@
/* allocate an IRQ pin for our slot */
pci_lintr_request(pi);
- sc = hda_init(opts);
+ sc = hda_init(nvl);
if (!sc)
return (-1);
Index: usr.sbin/bhyve/pci_hostbridge.c
===================================================================
--- usr.sbin/bhyve/pci_hostbridge.c
+++ usr.sbin/bhyve/pci_hostbridge.c
@@ -34,7 +34,7 @@
#include "pci_emul.h"
static int
-pci_hostbridge_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
+pci_hostbridge_init(struct vmctx *ctx, struct pci_devinst *pi, nvlist_t *nvl)
{
/* config space */
@@ -50,9 +50,9 @@
}
static int
-pci_amd_hostbridge_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
+pci_amd_hostbridge_init(struct vmctx *ctx, struct pci_devinst *pi, nvlist_t *nvl)
{
- (void) pci_hostbridge_init(ctx, pi, opts);
+ (void) pci_hostbridge_init(ctx, pi, nvl);
pci_set_cfgdata16(pi, PCIR_VENDOR, 0x1022); /* AMD */
pci_set_cfgdata16(pi, PCIR_DEVICE, 0x7432); /* made up */
Index: usr.sbin/bhyve/pci_lpc.c
===================================================================
--- usr.sbin/bhyve/pci_lpc.c
+++ usr.sbin/bhyve/pci_lpc.c
@@ -45,6 +45,7 @@
#include "acpi.h"
#include "debug.h"
#include "bootrom.h"
+#include "config.h"
#include "inout.h"
#include "pci_emul.h"
#include "pci_irq.h"
@@ -67,18 +68,15 @@
static struct pci_devinst *lpc_bridge;
-static const char *romfile;
-
#define LPC_UART_NUM 2
static struct lpc_uart_softc {
struct uart_softc *uart_softc;
- const char *opts;
int iobase;
int irq;
int enabled;
} lpc_uart_softc[LPC_UART_NUM];
-static const char *lpc_uart_names[LPC_UART_NUM] = { "COM1", "COM2" };
+static const char *lpc_uart_names[LPC_UART_NUM] = { "com1", "com2" };
/*
* LPC device configuration is in the following form:
@@ -89,20 +87,23 @@
lpc_device_parse(const char *opts)
{
int unit, error;
- char *str, *cpy, *lpcdev;
+ char *str, *cpy, *lpcdev, *node_name;
error = -1;
str = cpy = strdup(opts);
lpcdev = strsep(&str, ",");
if (lpcdev != NULL) {
if (strcasecmp(lpcdev, "bootrom") == 0) {
- romfile = str;
+ set_config_value("lpc.bootrom", str);
error = 0;
goto done;
}
for (unit = 0; unit < LPC_UART_NUM; unit++) {
if (strcasecmp(lpcdev, lpc_uart_names[unit]) == 0) {
- lpc_uart_softc[unit].opts = str;
+ asprintf(&node_name, "lpc.%s.device",
+ lpc_uart_names[unit]);
+ set_config_value(node_name, str);
+ free(node_name);
error = 0;
goto done;
}
@@ -110,8 +111,7 @@
}
done:
- if (error)
- free(cpy);
+ free(cpy);
return (error);
}
@@ -130,7 +130,7 @@
lpc_bootrom(void)
{
- return (romfile);
+ return (get_config_value("lpc.bootrom"));
}
static void
@@ -189,9 +189,11 @@
{
struct lpc_uart_softc *sc;
struct inout_port iop;
- const char *name;
+ const char *backend, *name, *romfile;
+ char *node_name;
int unit, error;
+ romfile = get_config_value("lpc.bootrom");
if (romfile != NULL) {
error = bootrom_loadrom(ctx, romfile);
if (error)
@@ -213,9 +215,12 @@
sc->uart_softc = uart_init(lpc_uart_intr_assert,
lpc_uart_intr_deassert, sc);
- if (uart_set_backend(sc->uart_softc, sc->opts) != 0) {
+ asprintf(&node_name, "lpc.%s.device", name);
+ backend = get_config_value(node_name);
+ free(node_name);
+ if (uart_set_backend(sc->uart_softc, backend) != 0) {
EPRINTLN("Unable to initialize backend '%s' "
- "for LPC device %s", sc->opts, name);
+ "for LPC device %s", backend, name);
return (-1);
}
@@ -393,7 +398,7 @@
#define LPC_VENDOR 0x8086
static int
-pci_lpc_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
+pci_lpc_init(struct vmctx *ctx, struct pci_devinst *pi, nvlist_t *nvl)
{
/*
Index: usr.sbin/bhyve/pci_nvme.c
===================================================================
--- usr.sbin/bhyve/pci_nvme.c
+++ usr.sbin/bhyve/pci_nvme.c
@@ -81,6 +81,7 @@
#include "bhyverun.h"
#include "block_if.h"
+#include "config.h"
#include "debug.h"
#include "pci_emul.h"
@@ -578,8 +579,9 @@
char *data = NULL;
uint64_t eui64 = nvstore->eui64;
- asprintf(&data, "%s%u%u%u", vmname, sc->nsc_pi->pi_bus,
- sc->nsc_pi->pi_slot, sc->nsc_pi->pi_func);
+ asprintf(&data, "%s%u%u%u", get_config_value("name"),
+ sc->nsc_pi->pi_bus, sc->nsc_pi->pi_slot,
+ sc->nsc_pi->pi_func);
if (data != NULL) {
eui64 = OUI_FREEBSD_NVME_LOW | crc16(0, data, strlen(data));
@@ -2602,14 +2604,12 @@
return (0);
}
-
static int
-pci_nvme_parse_opts(struct pci_nvme_softc *sc, char *opts)
+pci_nvme_parse_config(struct pci_nvme_softc *sc, nvlist_t *nvl)
{
char bident[sizeof("XX:X:X")];
- char *uopt, *xopts, *config;
+ const char *value;
uint32_t sectsz;
- int optidx;
sc->max_queues = NVME_QUEUES;
sc->max_qentries = NVME_MAX_QENTRIES;
@@ -2618,81 +2618,81 @@
sc->num_cqueues = sc->max_queues;
sc->dataset_management = NVME_DATASET_MANAGEMENT_AUTO;
sectsz = 0;
-
- uopt = strdup(opts);
- optidx = 0;
snprintf(sc->ctrldata.sn, sizeof(sc->ctrldata.sn),
"NVME-%d-%d", sc->nsc_pi->pi_slot, sc->nsc_pi->pi_func);
- for (xopts = strtok(uopt, ",");
- xopts != NULL;
- xopts = strtok(NULL, ",")) {
- if ((config = strchr(xopts, '=')) != NULL)
- *config++ = '\0';
+ value = get_config_value_node(nvl, "maxq");
+ if (value != NULL)
+ sc->max_queues = atoi(value);
+ value = get_config_value_node(nvl, "qsz");
+ if (value != NULL) {
+ sc->max_qentries = atoi(value);
+ if (sc->max_qentries <= 0) {
+ EPRINTLN("nvme: Invalid qsz option %d",
+ sc->max_qentries);
+ return (-1);
+ }
+ }
+ value = get_config_value_node(nvl, "ioslots");
+ if (value != NULL) {
+ sc->ioslots = atoi(value);
+ if (sc->ioslots <= 0) {
+ EPRINTLN("Invalid ioslots option %d", sc->ioslots);
+ return (-1);
+ }
+ }
+ value = get_config_value_node(nvl, "sectsz");
+ if (value != NULL)
+ sectsz = atoi(value);
+ value = get_config_value_node(nvl, "ser");
+ if (value != NULL) {
+ /*
+ * This field indicates the Product Serial Number in
+ * 7-bit ASCII, unused bytes should be space characters.
+ * Ref: NVMe v1.3c.
+ */
+ cpywithpad((char *)sc->ctrldata.sn,
+ sizeof(sc->ctrldata.sn), value, ' ');
+ }
+ value = get_config_value_node(nvl, "eui64");
+ if (value != NULL)
+ sc->nvstore.eui64 = htobe64(strtoull(value, NULL, 0));
+ value = get_config_value_node(nvl, "dsm");
+ if (value != NULL) {
+ if (strcmp(value, "auto") == 0)
+ sc->dataset_management = NVME_DATASET_MANAGEMENT_AUTO;
+ else if (strcmp(value, "enable") == 0)
+ sc->dataset_management = NVME_DATASET_MANAGEMENT_ENABLE;
+ else if (strcmp(value, "disable") == 0)
+ sc->dataset_management = NVME_DATASET_MANAGEMENT_DISABLE;
+ }
- if (!strcmp("maxq", xopts)) {
- sc->max_queues = atoi(config);
- } else if (!strcmp("qsz", xopts)) {
- sc->max_qentries = atoi(config);
- } else if (!strcmp("ioslots", xopts)) {
- sc->ioslots = atoi(config);
- } else if (!strcmp("sectsz", xopts)) {
- sectsz = atoi(config);
- } else if (!strcmp("ser", xopts)) {
- /*
- * This field indicates the Product Serial Number in
- * 7-bit ASCII, unused bytes should be space characters.
- * Ref: NVMe v1.3c.
- */
- cpywithpad((char *)sc->ctrldata.sn,
- sizeof(sc->ctrldata.sn), config, ' ');
- } else if (!strcmp("ram", xopts)) {
- uint64_t sz = strtoull(&xopts[4], NULL, 10);
+ value = get_config_value_node(nvl, "ram");
+ if (value != NULL) {
+ uint64_t sz = strtoull(value, NULL, 10);
- sc->nvstore.type = NVME_STOR_RAM;
- sc->nvstore.size = sz * 1024 * 1024;
- sc->nvstore.ctx = calloc(1, sc->nvstore.size);
- sc->nvstore.sectsz = 4096;
- sc->nvstore.sectsz_bits = 12;
- if (sc->nvstore.ctx == NULL) {
- perror("Unable to allocate RAM");
- free(uopt);
- return (-1);
- }
- } else if (!strcmp("eui64", xopts)) {
- sc->nvstore.eui64 = htobe64(strtoull(config, NULL, 0));
- } else if (!strcmp("dsm", xopts)) {
- if (!strcmp("auto", config))
- sc->dataset_management = NVME_DATASET_MANAGEMENT_AUTO;
- else if (!strcmp("enable", config))
- sc->dataset_management = NVME_DATASET_MANAGEMENT_ENABLE;
- else if (!strcmp("disable", config))
- sc->dataset_management = NVME_DATASET_MANAGEMENT_DISABLE;
- } else if (optidx == 0) {
- snprintf(bident, sizeof(bident), "%d:%d",
- sc->nsc_pi->pi_slot, sc->nsc_pi->pi_func);
- sc->nvstore.ctx = blockif_open(xopts, bident);
- if (sc->nvstore.ctx == NULL) {
- perror("Could not open backing file");
- free(uopt);
- return (-1);
- }
- sc->nvstore.type = NVME_STOR_BLOCKIF;
- sc->nvstore.size = blockif_size(sc->nvstore.ctx);
- } else {
- EPRINTLN("Invalid option %s", xopts);
- free(uopt);
+ sc->nvstore.type = NVME_STOR_RAM;
+ sc->nvstore.size = sz * 1024 * 1024;
+ sc->nvstore.ctx = calloc(1, sc->nvstore.size);
+ sc->nvstore.sectsz = 4096;
+ sc->nvstore.sectsz_bits = 12;
+ if (sc->nvstore.ctx == NULL) {
+ EPRINTLN("nvme: Unable to allocate RAM");
return (-1);
}
-
- optidx++;
+ } else {
+ snprintf(bident, sizeof(bident), "%d:%d",
+ sc->nsc_pi->pi_slot, sc->nsc_pi->pi_func);
+ sc->nvstore.ctx = blockif_open(nvl, bident);
+ if (sc->nvstore.ctx == NULL) {
+ EPRINTLN("nvme: Could not open backing file: %s",
+ strerror(errno));
+ return (-1);
+ }
+ sc->nvstore.type = NVME_STOR_BLOCKIF;
+ sc->nvstore.size = blockif_size(sc->nvstore.ctx);
}
- free(uopt);
- if (sc->nvstore.ctx == NULL || sc->nvstore.size == 0) {
- EPRINTLN("backing store not specified");
- return (-1);
- }
if (sectsz == 512 || sectsz == 4096 || sectsz == 8192)
sc->nvstore.sectsz = sectsz;
else if (sc->nvstore.type != NVME_STOR_RAM)
@@ -2704,20 +2704,11 @@
if (sc->max_queues <= 0 || sc->max_queues > NVME_QUEUES)
sc->max_queues = NVME_QUEUES;
- if (sc->max_qentries <= 0) {
- EPRINTLN("Invalid qsz option");
- return (-1);
- }
- if (sc->ioslots <= 0) {
- EPRINTLN("Invalid ioslots option");
- return (-1);
- }
-
return (0);
}
static int
-pci_nvme_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
+pci_nvme_init(struct vmctx *ctx, struct pci_devinst *pi, nvlist_t *nvl)
{
struct pci_nvme_softc *sc;
uint32_t pci_membar_sz;
@@ -2729,7 +2720,7 @@
pi->pi_arg = sc;
sc->nsc_pi = pi;
- error = pci_nvme_parse_opts(sc, opts);
+ error = pci_nvme_parse_config(sc, nvl);
if (error < 0)
goto done;
else
@@ -2806,6 +2797,7 @@
struct pci_devemu pci_de_nvme = {
.pe_emu = "nvme",
.pe_init = pci_nvme_init,
+ .pe_legacy_config = blockif_legacy_config,
.pe_barwrite = pci_nvme_write,
.pe_barread = pci_nvme_read
};
Index: usr.sbin/bhyve/pci_passthru.c
===================================================================
--- usr.sbin/bhyve/pci_passthru.c
+++ usr.sbin/bhyve/pci_passthru.c
@@ -59,6 +59,9 @@
#include <machine/vmm.h>
#include <vmmapi.h>
+
+#include "config.h"
+#include "debug.h"
#include "pci_emul.h"
#include "mem.h"
@@ -648,10 +651,31 @@
}
static int
-passthru_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
+passthru_legacy_config(nvlist_t *nvl, const char *opts)
+{
+ char value[16];
+ int bus, slot, func;
+
+ if (sscanf(opts, "%d/%d/%d", &bus, &slot, &func) != 3) {
+ EPRINTLN("passthru: invalid options \"%s\"", opts);
+ return (-1);
+ }
+
+ snprintf(value, sizeof(value), "%d", bus);
+ set_config_value_node(nvl, "bus", value);
+ snprintf(value, sizeof(value), "%d", slot);
+ set_config_value_node(nvl, "slot", value);
+ snprintf(value, sizeof(value), "%d", func);
+ set_config_value_node(nvl, "func", value);
+ return (0);
+}
+
+static int
+passthru_init(struct vmctx *ctx, struct pci_devinst *pi, nvlist_t *nvl)
{
int bus, slot, func, error, memflags;
struct passthru_softc *sc;
+ const char *value;
#ifndef WITHOUT_CAPSICUM
cap_rights_t rights;
cap_ioctl_t pci_ioctls[] = { PCIOCREAD, PCIOCWRITE, PCIOCGETBAR };
@@ -716,11 +740,18 @@
errx(EX_OSERR, "Unable to apply rights for sandbox");
#endif
- if (opts == NULL ||
- sscanf(opts, "%d/%d/%d", &bus, &slot, &func) != 3) {
- warnx("invalid passthru options");
- return (error);
- }
+#define GET_INT_CONFIG(var, name) do { \
+ value = get_config_value_node(nvl, name); \
+ if (value == NULL) { \
+ EPRINTLN("passthru: missing required %s setting", name); \
+ return (error); \
+ } \
+ var = atoi(value); \
+} while (0)
+
+ GET_INT_CONFIG(bus, "bus");
+ GET_INT_CONFIG(slot, "slot");
+ GET_INT_CONFIG(func, "func");
if (vm_assign_pptdev(ctx, bus, slot, func) != 0) {
warnx("PCI device at %d/%d/%d is not using the ppt(4) driver",
@@ -952,6 +983,7 @@
struct pci_devemu passthru = {
.pe_emu = "passthru",
.pe_init = passthru_init,
+ .pe_legacy_config = passthru_legacy_config,
.pe_cfgwrite = passthru_cfgwrite,
.pe_cfgread = passthru_cfgread,
.pe_barwrite = passthru_write,
Index: usr.sbin/bhyve/pci_uart.c
===================================================================
--- usr.sbin/bhyve/pci_uart.c
+++ usr.sbin/bhyve/pci_uart.c
@@ -36,6 +36,7 @@
#include <stdio.h>
#include "bhyverun.h"
+#include "config.h"
#include "debug.h"
#include "pci_emul.h"
#include "uart_emul.h"
@@ -89,9 +90,18 @@
}
static int
-pci_uart_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
+pci_uart_legacy_config(nvlist_t *nvl, const char *opts)
+{
+
+ set_config_value_node(nvl, "device", opts);
+ return (0);
+}
+
+static int
+pci_uart_init(struct vmctx *ctx, struct pci_devinst *pi, nvlist_t *nvl)
{
struct uart_softc *sc;
+ const char *device;
pci_emul_alloc_bar(pi, 0, PCIBAR_IO, UART_IO_BAR_SIZE);
pci_lintr_request(pi);
@@ -104,9 +114,10 @@
sc = uart_init(pci_uart_intr_assert, pci_uart_intr_deassert, pi);
pi->pi_arg = sc;
- if (uart_set_backend(sc, opts) != 0) {
+ device = get_config_value_node(nvl, "device");
+ if (uart_set_backend(sc, device) != 0) {
EPRINTLN("Unable to initialize backend '%s' for "
- "pci uart at %d:%d", opts, pi->pi_slot, pi->pi_func);
+ "pci uart at %d:%d", device, pi->pi_slot, pi->pi_func);
return (-1);
}
@@ -116,6 +127,7 @@
struct pci_devemu pci_de_com = {
.pe_emu = "uart",
.pe_init = pci_uart_init,
+ .pe_legacy_config = pci_uart_legacy_config,
.pe_barwrite = pci_uart_write,
.pe_barread = pci_uart_read
};
Index: usr.sbin/bhyve/pci_virtio_block.c
===================================================================
--- usr.sbin/bhyve/pci_virtio_block.c
+++ usr.sbin/bhyve/pci_virtio_block.c
@@ -54,6 +54,7 @@
#include <md5.h>
#include "bhyverun.h"
+#include "config.h"
#include "debug.h"
#include "pci_emul.h"
#include "virtio.h"
@@ -435,26 +436,22 @@
}
static int
-pci_vtblk_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
+pci_vtblk_init(struct vmctx *ctx, struct pci_devinst *pi, nvlist_t *nvl)
{
char bident[sizeof("XX:X:X")];
struct blockif_ctxt *bctxt;
+ const char *path;
MD5_CTX mdctx;
u_char digest[16];
struct pci_vtblk_softc *sc;
off_t size;
int i, sectsz, sts, sto;
- if (opts == NULL) {
- WPRINTF(("virtio-block: backing device required"));
- return (1);
- }
-
/*
* The supplied backing file has to exist
*/
snprintf(bident, sizeof(bident), "%d:%d", pi->pi_slot, pi->pi_func);
- bctxt = blockif_open(opts, bident);
+ bctxt = blockif_open(nvl, bident);
if (bctxt == NULL) {
perror("Could not open backing file");
return (1);
@@ -491,8 +488,9 @@
* Create an identifier for the backing file. Use parts of the
* md5 sum of the filename
*/
+ path = get_config_value_node(nvl, "path");
MD5Init(&mdctx);
- MD5Update(&mdctx, opts, strlen(opts));
+ MD5Update(&mdctx, path, strlen(path));
MD5Final(digest, &mdctx);
snprintf(sc->vbsc_ident, VTBLK_BLK_ID_BYTES,
"BHYVE-%02X%02X-%02X%02X-%02X%02X",
@@ -568,6 +566,7 @@
struct pci_devemu pci_de_vblk = {
.pe_emu = "virtio-blk",
.pe_init = pci_vtblk_init,
+ .pe_legacy_config = blockif_legacy_config,
.pe_barwrite = vi_pci_write,
.pe_barread = vi_pci_read,
#ifdef BHYVE_SNAPSHOT
Index: usr.sbin/bhyve/pci_virtio_console.c
===================================================================
--- usr.sbin/bhyve/pci_virtio_console.c
+++ usr.sbin/bhyve/pci_virtio_console.c
@@ -60,6 +60,7 @@
#include <sysexits.h>
#include "bhyverun.h"
+#include "config.h"
#include "debug.h"
#include "pci_emul.h"
#include "virtio.h"
@@ -616,13 +617,32 @@
}
static int
-pci_vtcon_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
+pci_vtcon_legacy_config(nvlist_t *nvl, const char *opts)
+{
+ char *name, *opt, *path, *str, *tofree;
+ nvlist_t *ports_nvl;
+
+ ports_nvl = create_relative_config_node(nvl, "ports");
+ tofree = str = strdup(opts);
+ while ((opt = strsep(&str, ",")) != NULL) {
+ name = strsep(&opt, "=");
+ path = opt;
+ if (path != NULL) {
+ EPRINTLN("vtcon: port %s requires a path", name);
+ return (-1);
+ }
+ set_config_value_node(ports_nvl, name, path);
+ }
+ free(tofree);
+ return (0);
+}
+
+static int
+pci_vtcon_init(struct vmctx *ctx, struct pci_devinst *pi, nvlist_t *nvl)
{
struct pci_vtcon_softc *sc;
- char *portname = NULL;
- char *portpath = NULL;
- char *opt;
- int i;
+ nvlist_t *ports_nvl;
+ int i;
sc = calloc(1, sizeof(struct pci_vtcon_softc));
sc->vsc_config = calloc(1, sizeof(struct pci_vtcon_config));
@@ -658,15 +678,26 @@
sc->vsc_control_port.vsp_cb = pci_vtcon_control_tx;
sc->vsc_control_port.vsp_enabled = true;
- while ((opt = strsep(&opts, ",")) != NULL) {
- portname = strsep(&opt, "=");
- portpath = opt;
+ ports_nvl = find_relative_config_node(nvl, "ports");
+ if (ports_nvl != NULL) {
+ const char *name;
+ void *cookie;
+ int type;
- /* create port */
- if (pci_vtcon_sock_add(sc, portname, portpath) < 0) {
- EPRINTLN("cannot create port %s: %s",
- portname, strerror(errno));
- return (1);
+ cookie = NULL;
+ while ((name = nvlist_next(ports_nvl, &type, &cookie)) !=
+ NULL) {
+ const char *path;
+
+ if (type != NV_TYPE_STRING)
+ continue;
+ path = get_config_value_node(ports_nvl, name);
+
+ if (pci_vtcon_sock_add(sc, name, path) < 0) {
+ EPRINTLN("cannot create port %s: %s",
+ name, strerror(errno));
+ return (1);
+ }
}
}
Index: usr.sbin/bhyve/pci_virtio_net.c
===================================================================
--- usr.sbin/bhyve/pci_virtio_net.c
+++ usr.sbin/bhyve/pci_virtio_net.c
@@ -54,6 +54,7 @@
#include <pthread_np.h>
#include "bhyverun.h"
+#include "config.h"
#include "debug.h"
#include "pci_emul.h"
#include "mevent.h"
@@ -543,13 +544,13 @@
#endif
static int
-pci_vtnet_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
+pci_vtnet_init(struct vmctx *ctx, struct pci_devinst *pi, nvlist_t *nvl)
{
struct pci_vtnet_softc *sc;
+ const char *value;
char tname[MAXCOMLEN + 1];
- int mac_provided;
- int mtu_provided;
unsigned long mtu = ETHERMTU;
+ int err;
/*
* Allocate data structures for further virtio initializations.
@@ -569,81 +570,46 @@
sc->vsc_queues[VTNET_CTLQ].vq_qsize = VTNET_RINGSZ;
sc->vsc_queues[VTNET_CTLQ].vq_notify = pci_vtnet_ping_ctlq;
#endif
-
- /*
- * Attempt to open the backend device and read the MAC address
- * if specified.
- */
- mac_provided = 0;
- mtu_provided = 0;
- if (opts != NULL) {
- char *optscopy;
- char *vtopts;
- int err = 0;
-
- /* Get the device name. */
- optscopy = vtopts = strdup(opts);
- (void) strsep(&vtopts, ",");
-
- /*
- * Parse the list of options in the form
- * key1=value1,...,keyN=valueN.
- */
- while (vtopts != NULL) {
- char *value = vtopts;
- char *key;
-
- key = strsep(&value, "=");
- if (value == NULL)
- break;
- vtopts = value;
- (void) strsep(&vtopts, ",");
-
- if (strcmp(key, "mac") == 0) {
- err = net_parsemac(value, sc->vsc_config.mac);
- if (err)
- break;
- mac_provided = 1;
- } else if (strcmp(key, "mtu") == 0) {
- err = net_parsemtu(value, &mtu);
- if (err)
- break;
-
- if (mtu < VTNET_MIN_MTU || mtu > VTNET_MAX_MTU) {
- err = EINVAL;
- errno = EINVAL;
- break;
- }
- mtu_provided = 1;
- }
- }
-
- free(optscopy);
+ value = get_config_value_node(nvl, "mac");
+ if (value != NULL) {
+ err = net_parsemac(value, sc->vsc_config.mac);
if (err) {
free(sc);
return (err);
}
-
- err = netbe_init(&sc->vsc_be, opts, pci_vtnet_rx_callback,
- sc);
-
- if (err) {
- free(sc);
- return (err);
- }
- sc->vsc_consts.vc_hv_caps |= VIRTIO_NET_F_MRG_RXBUF |
- netbe_get_cap(sc->vsc_be);
- }
-
- if (!mac_provided) {
+ } else
net_genmac(pi, sc->vsc_config.mac);
- }
- sc->vsc_config.mtu = mtu;
- if (mtu_provided) {
+ value = get_config_value_node(nvl, "mtu");
+ if (value != NULL) {
+ err = net_parsemtu(value, &mtu);
+ if (err) {
+ free(sc);
+ return (err);
+ }
+
+ if (mtu < VTNET_MIN_MTU || mtu > VTNET_MAX_MTU) {
+ err = EINVAL;
+ errno = EINVAL;
+ free(sc);
+ return (err);
+ }
sc->vsc_consts.vc_hv_caps |= VIRTIO_NET_F_MTU;
}
+ sc->vsc_config.mtu = mtu;
+
+ /* Permit interfaces without a configured backend. */
+ if (get_config_value_node(nvl, "backend") != NULL) {
+ err = netbe_init(&sc->vsc_be, nvl, pci_vtnet_rx_callback, sc);
+ if (err) {
+ free(sc);
+ return (err);
+ }
+ }
+
+ sc->vsc_consts.vc_hv_caps |= VIRTIO_NET_F_MRG_RXBUF |
+ netbe_get_cap(sc->vsc_be);
/*
* Since we do not actually support multiqueue,
@@ -658,8 +624,8 @@
pci_set_cfgdata16(pi, PCIR_SUBDEV_0, VIRTIO_TYPE_NET);
pci_set_cfgdata16(pi, PCIR_SUBVEND_0, VIRTIO_VENDOR);
- /* Link is up if we managed to open backend device. */
- sc->vsc_config.status = (opts == NULL || sc->vsc_be);
+ /* Link is always up. */
+ sc->vsc_config.status = 1;
vi_softc_linkup(&sc->vsc_vs, &sc->vsc_consts, sc, pi, sc->vsc_queues);
sc->vsc_vs.vs_mtx = &sc->vsc_mtx;
@@ -820,6 +786,7 @@
static struct pci_devemu pci_de_vnet = {
.pe_emu = "virtio-net",
.pe_init = pci_vtnet_init,
+ .pe_legacy_config = netbe_legacy_config,
.pe_barwrite = vi_pci_write,
.pe_barread = vi_pci_read,
#ifdef BHYVE_SNAPSHOT
Index: usr.sbin/bhyve/pci_virtio_rnd.c
===================================================================
--- usr.sbin/bhyve/pci_virtio_rnd.c
+++ usr.sbin/bhyve/pci_virtio_rnd.c
@@ -143,7 +143,7 @@
static int
-pci_vtrnd_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
+pci_vtrnd_init(struct vmctx *ctx, struct pci_devinst *pi, nvlist_t *nvl)
{
struct pci_vtrnd_softc *sc;
int fd;
Index: usr.sbin/bhyve/pci_virtio_scsi.c
===================================================================
--- usr.sbin/bhyve/pci_virtio_scsi.c
+++ usr.sbin/bhyve/pci_virtio_scsi.c
@@ -61,6 +61,7 @@
#include <camlib.h>
#include "bhyverun.h"
+#include "config.h"
#include "debug.h"
#include "pci_emul.h"
#include "virtio.h"
@@ -244,7 +245,7 @@
static void pci_vtscsi_requestq_notify(void *, struct vqueue_info *);
static int pci_vtscsi_init_queue(struct pci_vtscsi_softc *,
struct pci_vtscsi_queue *, int);
-static int pci_vtscsi_init(struct vmctx *, struct pci_devinst *, char *);
+static int pci_vtscsi_init(struct vmctx *, struct pci_devinst *, nvlist_t *);
static struct virtio_consts vtscsi_vi_consts = {
"vtscsi", /* our name */
@@ -665,32 +666,36 @@
}
static int
-pci_vtscsi_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
+pci_vtscsi_legacy_config(nvlist_t *nvl, const char *opts)
+{
+ char *cp, *devname;
+
+ cp = strchr(opts, ',');
+ if (cp == NULL) {
+ set_config_value_node(nvl, "dev", opts);
+ return (0);
+ }
+ devname = strndup(opts, cp - opts);
+ set_config_value_node(nvl, "dev", devname);
+ free(devname);
+ return (pci_parse_legacy_config(nvl, cp + 1));
+}
+
+static int
+pci_vtscsi_init(struct vmctx *ctx, struct pci_devinst *pi, nvlist_t *nvl)
{
struct pci_vtscsi_softc *sc;
- char *opt, *optname;
- const char *devname;
- int i, optidx = 0;
+ const char *devname, *value;;
+ int i;
sc = calloc(1, sizeof(struct pci_vtscsi_softc));
- devname = "/dev/cam/ctl";
- while ((opt = strsep(&opts, ",")) != NULL) {
- optname = strsep(&opt, "=");
- if (opt == NULL && optidx == 0) {
- if (optname[0] != 0)
- devname = optname;
- } else if (strcmp(optname, "dev") == 0 && opt != NULL) {
- devname = opt;
- } else if (strcmp(optname, "iid") == 0 && opt != NULL) {
- sc->vss_iid = strtoul(opt, NULL, 10);
- } else {
- EPRINTLN("Invalid option %s", optname);
- free(sc);
- return (1);
- }
- optidx++;
- }
+ value = get_config_value_node(nvl, "iid");
+ if (value != NULL)
+ sc->vss_iid = strtoul(value, NULL, 10);
+ devname = get_config_value_node(nvl, "dev");
+ if (devname == NULL)
+ devname = "/dev/cam/ctl";
sc->vss_ctl_fd = open(devname, O_RDWR);
if (sc->vss_ctl_fd < 0) {
WPRINTF(("cannot open %s: %s", devname, strerror(errno)));
Index: usr.sbin/bhyve/pci_xhci.c
===================================================================
--- usr.sbin/bhyve/pci_xhci.c
+++ usr.sbin/bhyve/pci_xhci.c
@@ -56,6 +56,7 @@
#include <xhcireg.h>
#include "bhyverun.h"
+#include "config.h"
#include "debug.h"
#include "pci_emul.h"
#include "pci_xhci.h"
@@ -2644,31 +2645,72 @@
return (0);
}
-
-
-static void
-pci_xhci_device_usage(char *opt)
+/*
+ * Each controller contains a "devices" node which contains a list of
+ * child nodes each of which is a device. The legacy config names
+ * these nodes as integers starting at 0. Each USB device node
+ * contains a "device" variable identifying the device model of the
+ * USB device. For example:
+ *
+ * pci.0.1.0
+ * .device="xhci"
+ * .devices
+ * .0
+ * .device="tablet"
+ */
+static int
+pci_xhci_legacy_config(nvlist_t *nvl, const char *opts)
{
+ char node_name[16];
+ nvlist_t *devices_nvl, *device_nvl;
+ char *cp, *opt, *str, *tofree;
+ int count;
- EPRINTLN("Invalid USB emulation \"%s\"", opt);
+ devices_nvl = create_relative_config_node(nvl, "devices");
+ count = 0;
+ tofree = str = strdup(opts);
+ while ((opt = strsep(&str, ",")) != NULL) {
+ /* device[=<config>] */
+ cp = strchr(opt, '=');
+ if (cp != NULL) {
+ *cp = '\0';
+ cp++;
+ }
+
+ snprintf(node_name, sizeof(node_name), "%d", count);
+ count++;
+ device_nvl = create_relative_config_node(devices_nvl,
+ node_name);
+ set_config_value_node(device_nvl, "device", opt);
+
+ /*
+ * NB: Given that we split on commas above, the legacy
+ * format only supports a single option.
+ */
+ if (cp != NULL && *cp != '\0')
+ pci_parse_legacy_config(device_nvl, cp);
+ }
+ free(tofree);
+ return (0);
}
static int
-pci_xhci_parse_opts(struct pci_xhci_softc *sc, char *opts)
+pci_xhci_parse_devices(struct pci_xhci_softc *sc, nvlist_t *nvl)
{
struct pci_xhci_dev_emu **devices;
struct pci_xhci_dev_emu *dev;
struct usb_devemu *ue;
- void *devsc;
- char *uopt, *xopts, *config;
- int usb3_port, usb2_port, i;
+ const nvlist_t *devices_nvl, *device_nvl;
+ const char *name, *device;
+ void *devsc, *cookie;
+ int type, usb3_port, usb2_port, i;
- uopt = NULL;
usb3_port = sc->usb3_port_start - 1;
usb2_port = sc->usb2_port_start - 1;
devices = NULL;
- if (opts == NULL)
+ devices_nvl = find_relative_config_node(nvl, "devices");
+ if (devices_nvl == NULL)
goto portsfinal;
devices = calloc(XHCI_MAX_DEVS, sizeof(struct pci_xhci_dev_emu *));
@@ -2677,10 +2719,8 @@
sc->devices = devices;
sc->ndevices = 0;
- uopt = strdup(opts);
- for (xopts = strtok(uopt, ",");
- xopts != NULL;
- xopts = strtok(NULL, ",")) {
+ cookie = NULL;
+ while ((name = nvlist_next(devices_nvl, &type, &cookie)) != NULL) {
if (usb2_port == ((sc->usb2_port_start-1) + XHCI_MAX_DEVS/2) ||
usb3_port == ((sc->usb3_port_start-1) + XHCI_MAX_DEVS/2)) {
WPRINTF(("pci_xhci max number of USB 2 or 3 "
@@ -2689,22 +2729,31 @@
goto done;
}
- /* device[=<config>] */
- if ((config = strchr(xopts, '=')) == NULL)
- config = ""; /* no config */
- else
- *config++ = '\0';
+ if (type != NV_TYPE_NVLIST) {
+ EPRINTLN(
+ "pci_xhci: config variable '%s' under devices node",
+ name);
+ return (-1);
+ }
- ue = usb_emu_finddev(xopts);
+ device_nvl = nvlist_get_nvlist(devices_nvl, name);
+ device = get_config_value_node(device_nvl, "device");
+ if (device == NULL) {
+ EPRINTLN(
+ "pci_xhci: missing \"device\" value for device '%s'",
+ name);
+ return (-1);
+ }
+
+ ue = usb_emu_finddev(device);
if (ue == NULL) {
- pci_xhci_device_usage(xopts);
- DPRINTF(("pci_xhci device not found %s", xopts));
+ EPRINTLN("pci_xhci: unknown device model \"%s\"",
+ device);
usb2_port = usb3_port = -1;
- goto done;
+ return (-1);
}
- DPRINTF(("pci_xhci adding device %s, opts \"%s\"",
- xopts, config));
+ DPRINTF(("pci_xhci adding device %s", device));
dev = calloc(1, sizeof(struct pci_xhci_dev_emu));
dev->xsc = sc;
@@ -2712,6 +2761,12 @@
dev->hci.hci_intr = pci_xhci_dev_intr;
dev->hci.hci_event = pci_xhci_dev_event;
+ /*
+ * XXX: This seems broken if you mix USB 2 and 3
+ * devices on a single controller in that the
+ * 'devices' array won't be consistent. Perhaps we
+ * need separate USB 2 and USB 3 port arrays instead?
+ */
if (ue->ue_usbver == 2) {
dev->hci.hci_port = usb2_port + 1;
devices[usb2_port] = dev;
@@ -2723,9 +2778,8 @@
}
dev->hci.hci_address = 0;
- devsc = ue->ue_init(&dev->hci, config);
+ devsc = ue->ue_init(&dev->hci, nvl);
if (devsc == NULL) {
- pci_xhci_device_usage(xopts);
usb2_port = usb3_port = -1;
goto done;
}
@@ -2767,12 +2821,11 @@
free(devices);
}
}
- free(uopt);
return (sc->ndevices);
}
static int
-pci_xhci_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
+pci_xhci_init(struct vmctx *ctx, struct pci_devinst *pi, nvlist_t *nvl)
{
struct pci_xhci_softc *sc;
int error;
@@ -2791,7 +2844,7 @@
sc->usb3_port_start = 1;
/* discover devices */
- error = pci_xhci_parse_opts(sc, opts);
+ error = pci_xhci_parse_devices(sc, nvl);
if (error < 0)
goto done;
else
@@ -3120,6 +3173,7 @@
struct pci_devemu pci_de_xhci = {
.pe_emu = "xhci",
.pe_init = pci_xhci_init,
+ .pe_legacy_config = pci_xhci_legacy_config,
.pe_barwrite = pci_xhci_write,
.pe_barread = pci_xhci_read,
#ifdef BHYVE_SNAPSHOT
Index: usr.sbin/bhyve/rtc.h
===================================================================
--- usr.sbin/bhyve/rtc.h
+++ usr.sbin/bhyve/rtc.h
@@ -31,6 +31,6 @@
#ifndef _RTC_H_
#define _RTC_H_
-void rtc_init(struct vmctx *ctx, int use_localtime);
+void rtc_init(struct vmctx *ctx);
#endif /* _RTC_H_ */
Index: usr.sbin/bhyve/rtc.c
===================================================================
--- usr.sbin/bhyve/rtc.c
+++ usr.sbin/bhyve/rtc.c
@@ -40,6 +40,7 @@
#include <vmmapi.h>
#include "acpi.h"
+#include "config.h"
#include "pci_lpc.h"
#include "rtc.h"
@@ -59,13 +60,13 @@
* Returns the current RTC time as number of seconds since 00:00:00 Jan 1, 1970
*/
static time_t
-rtc_time(struct vmctx *ctx, int use_localtime)
+rtc_time(struct vmctx *ctx)
{
struct tm tm;
time_t t;
time(&t);
- if (use_localtime) {
+ if (get_config_bool("rtc.use_localtime")) {
localtime_r(&t, &tm);
t = timegm(&tm);
}
@@ -73,7 +74,7 @@
}
void
-rtc_init(struct vmctx *ctx, int use_localtime)
+rtc_init(struct vmctx *ctx)
{
size_t himem;
size_t lomem;
@@ -101,7 +102,7 @@
err = vm_rtc_write(ctx, RTC_HMEM_MSB, himem >> 16);
assert(err == 0);
- err = vm_rtc_settime(ctx, rtc_time(ctx, use_localtime));
+ err = vm_rtc_settime(ctx, rtc_time(ctx));
assert(err == 0);
}
Index: usr.sbin/bhyve/smbiostbl.c
===================================================================
--- usr.sbin/bhyve/smbiostbl.c
+++ usr.sbin/bhyve/smbiostbl.c
@@ -43,6 +43,7 @@
#include <vmmapi.h>
#include "bhyverun.h"
+#include "config.h"
#include "debug.h"
#include "smbiostbl.h"
@@ -584,11 +585,13 @@
uint16_t *n, uint16_t *size)
{
struct smbios_table_type1 *type1;
+ const char *guest_uuid_str;
smbios_generic_initializer(template_entry, template_strings,
curaddr, endaddr, n, size);
type1 = (struct smbios_table_type1 *)curaddr;
+ guest_uuid_str = get_config_value("uuid");
if (guest_uuid_str != NULL) {
uuid_t uuid;
uint32_t status;
@@ -602,6 +605,7 @@
MD5_CTX mdctx;
u_char digest[16];
char hostname[MAXHOSTNAMELEN];
+ const char *vmname;
/*
* Universally unique and yet reproducible are an
@@ -612,6 +616,7 @@
return (-1);
MD5Init(&mdctx);
+ vmname = get_config_value("name");
MD5Update(&mdctx, vmname, strlen(vmname));
MD5Update(&mdctx, hostname, sizeof(hostname));
MD5Final(digest, &mdctx);
Index: usr.sbin/bhyve/uart_emul.h
===================================================================
--- usr.sbin/bhyve/uart_emul.h
+++ usr.sbin/bhyve/uart_emul.h
@@ -43,7 +43,7 @@
int uart_legacy_alloc(int unit, int *ioaddr, int *irq);
uint8_t uart_read(struct uart_softc *sc, int offset);
void uart_write(struct uart_softc *sc, int offset, uint8_t value);
-int uart_set_backend(struct uart_softc *sc, const char *opt);
+int uart_set_backend(struct uart_softc *sc, const char *device);
#ifdef BHYVE_SNAPSHOT
int uart_snapshot(struct uart_softc *sc, struct vm_snapshot_meta *meta);
#endif
Index: usr.sbin/bhyve/uart_emul.c
===================================================================
--- usr.sbin/bhyve/uart_emul.c
+++ usr.sbin/bhyve/uart_emul.c
@@ -673,7 +673,7 @@
}
static int
-uart_tty_backend(struct uart_softc *sc, const char *opts)
+uart_tty_backend(struct uart_softc *sc, const char *path)
{
#ifndef WITHOUT_CAPSICUM
cap_rights_t rights;
@@ -681,7 +681,7 @@
#endif
int fd;
- fd = open(opts, O_RDWR | O_NONBLOCK);
+ fd = open(path, O_RDWR | O_NONBLOCK);
if (fd < 0)
return (-1);
@@ -705,17 +705,17 @@
}
int
-uart_set_backend(struct uart_softc *sc, const char *opts)
+uart_set_backend(struct uart_softc *sc, const char *device)
{
int retval;
- if (opts == NULL)
+ if (device == NULL)
return (0);
- if (strcmp("stdio", opts) == 0)
+ if (strcmp("stdio", device) == 0)
retval = uart_stdio_backend(sc);
else
- retval = uart_tty_backend(sc, opts);
+ retval = uart_tty_backend(sc, device);
if (retval == 0)
uart_opentty(sc);
Index: usr.sbin/bhyve/usb_emul.h
===================================================================
--- usr.sbin/bhyve/usb_emul.h
+++ usr.sbin/bhyve/usb_emul.h
@@ -31,6 +31,7 @@
#ifndef _USB_EMUL_H_
#define _USB_EMUL_H_
+#include <sys/nv.h>
#include <stdlib.h>
#include <sys/linker_set.h>
#include <pthread.h>
@@ -53,7 +54,7 @@
int ue_usbspeed; /* usb device speed */
/* instance creation */
- void *(*ue_init)(struct usb_hci *hci, char *opt);
+ void *(*ue_init)(struct usb_hci *hci, nvlist_t *nvl);
/* handlers */
int (*ue_request)(void *sc, struct usb_data_xfer *xfer);
@@ -149,7 +150,7 @@
pthread_mutex_unlock(&((x)->mtx)); \
} while (0)
-struct usb_devemu *usb_emu_finddev(char *name);
+struct usb_devemu *usb_emu_finddev(const char *name);
struct usb_data_xfer_block *usb_data_xfer_append(struct usb_data_xfer *xfer,
void *buf, int blen, void *hci_data, int ccs);
Index: usr.sbin/bhyve/usb_emul.c
===================================================================
--- usr.sbin/bhyve/usb_emul.c
+++ usr.sbin/bhyve/usb_emul.c
@@ -43,7 +43,7 @@
SET_DECLARE(usb_emu_set, struct usb_devemu);
struct usb_devemu *
-usb_emu_finddev(char *name)
+usb_emu_finddev(const char *name)
{
struct usb_devemu **udpp, *udp;
Index: usr.sbin/bhyve/usb_mouse.c
===================================================================
--- usr.sbin/bhyve/usb_mouse.c
+++ usr.sbin/bhyve/usb_mouse.c
@@ -241,8 +241,6 @@
struct umouse_softc {
struct usb_hci *hci;
- char *opt;
-
struct umouse_report um_report;
int newdata;
struct {
@@ -299,7 +297,7 @@
}
static void *
-umouse_init(struct usb_hci *hci, char *opt)
+umouse_init(struct usb_hci *hci, nvlist_t *nvl)
{
struct umouse_softc *sc;
@@ -307,7 +305,6 @@
sc->hci = hci;
sc->hid.protocol = 1; /* REPORT protocol */
- sc->opt = strdup(opt);
pthread_mutex_init(&sc->mtx, NULL);
pthread_mutex_init(&sc->ev_mtx, NULL);

File Metadata

Mime Type
text/plain
Expires
Sat, Apr 5, 3:34 PM (2 h, 9 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
17395761
Default Alt Text
D26035.id75706.diff (115 KB)

Event Timeline