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