aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGabriel L. Somlo <somlo@cmu.edu>2015-04-29 11:21:53 -0400
committerGerd Hoffmann <kraxel@redhat.com>2015-06-10 08:06:46 +0200
commit81b2b81062612ebeac4cd5333a3b15c7d79a5a3d (patch)
treec505eeb49687c998e3f536e8e7fe0fbfbf5303b1
parent0eb973f91521c6bcb6399d25327711d083f6eb10 (diff)
fw_cfg: insert fw_cfg file blobs via qemu cmdline
Allow user supplied files to be inserted into the fw_cfg device before starting the guest. Since fw_cfg_add_file() already disallows duplicate fw_cfg file names, qemu will exit with an error message if the user supplies multiple blobs with the same fw_cfg file name, or if a blob name collides with a fw_cfg name programmatically added from within the QEMU source code. A warning message will be printed if the fw_cfg item name does not begin with the prefix "opt/", which is recommended for external, user provided blobs. Signed-off-by: Gabriel Somlo <somlo@cmu.edu> Reviewed-by: Laszlo Ersek <lersek@redhat.com> Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
-rw-r--r--docs/specs/fw_cfg.txt21
-rw-r--r--qemu-options.hx11
-rw-r--r--vl.c63
3 files changed, 95 insertions, 0 deletions
diff --git a/docs/specs/fw_cfg.txt b/docs/specs/fw_cfg.txt
index 6accd924bd..74351dd18f 100644
--- a/docs/specs/fw_cfg.txt
+++ b/docs/specs/fw_cfg.txt
@@ -203,3 +203,24 @@ completes fully overwriting the item's data.
NOTE: This function is deprecated, and will be completely removed
starting with QEMU v2.4.
+
+== Externally Provided Items ==
+
+As of v2.4, "file" fw_cfg items (i.e., items with selector keys above
+FW_CFG_FILE_FIRST, and with a corresponding entry in the fw_cfg file
+directory structure) may be inserted via the QEMU command line, using
+the following syntax:
+
+ -fw_cfg [name=]<item_name>,file=<path>
+
+where <item_name> is the fw_cfg item name, and <path> is the location
+on the host file system of a file containing the data to be inserted.
+
+NOTE: Users *SHOULD* choose item names beginning with the prefix "opt/"
+when using the "-fw_cfg" command line option, to avoid conflicting with
+item names used internally by QEMU. For instance:
+
+ -fw_cfg name=opt/my_item_name,file=./my_blob.bin
+
+Similarly, QEMU developers *SHOULD NOT* use item names prefixed with
+"opt/" when inserting items programmatically, e.g. via fw_cfg_add_file().
diff --git a/qemu-options.hx b/qemu-options.hx
index 4be98f71df..1d281f6818 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -2686,6 +2686,17 @@ STEXI
@table @option
ETEXI
+DEF("fw_cfg", HAS_ARG, QEMU_OPTION_fwcfg,
+ "-fw_cfg [name=]<name>,file=<file>\n"
+ " add named fw_cfg entry from file\n",
+ QEMU_ARCH_ALL)
+STEXI
+@item -fw_cfg [name=]@var{name},file=@var{file}
+@findex -fw_cfg
+Add named fw_cfg entry from file. @var{name} determines the name of
+the entry in the fw_cfg file directory exposed to the guest.
+ETEXI
+
DEF("serial", HAS_ARG, QEMU_OPTION_serial, \
"-serial dev redirect the serial port to char device 'dev'\n",
QEMU_ARCH_ALL)
diff --git a/vl.c b/vl.c
index d4b2d03e0e..9542095d95 100644
--- a/vl.c
+++ b/vl.c
@@ -492,6 +492,25 @@ static QemuOptsList qemu_semihosting_config_opts = {
},
};
+static QemuOptsList qemu_fw_cfg_opts = {
+ .name = "fw_cfg",
+ .implied_opt_name = "name",
+ .head = QTAILQ_HEAD_INITIALIZER(qemu_fw_cfg_opts.head),
+ .desc = {
+ {
+ .name = "name",
+ .type = QEMU_OPT_STRING,
+ .help = "Sets the fw_cfg name of the blob to be inserted",
+ }, {
+ .name = "file",
+ .type = QEMU_OPT_STRING,
+ .help = "Sets the name of the file from which\n"
+ "the fw_cfg blob will be loaded",
+ },
+ { /* end of list */ }
+ },
+};
+
/**
* Get machine options
*
@@ -2127,6 +2146,38 @@ char *qemu_find_file(int type, const char *name)
return NULL;
}
+static int parse_fw_cfg(void *opaque, QemuOpts *opts, Error **errp)
+{
+ gchar *buf;
+ size_t size;
+ const char *name, *file;
+
+ if (opaque == NULL) {
+ error_report("fw_cfg device not available");
+ return -1;
+ }
+ name = qemu_opt_get(opts, "name");
+ file = qemu_opt_get(opts, "file");
+ if (name == NULL || *name == '\0' || file == NULL || *file == '\0') {
+ error_report("invalid argument value");
+ return -1;
+ }
+ if (strlen(name) > FW_CFG_MAX_FILE_PATH - 1) {
+ error_report("name too long (max. %d char)", FW_CFG_MAX_FILE_PATH - 1);
+ return -1;
+ }
+ if (strncmp(name, "opt/", 4) != 0) {
+ error_report("WARNING: externally provided fw_cfg item names "
+ "should be prefixed with \"opt/\"!");
+ }
+ if (!g_file_get_contents(file, &buf, &size, NULL)) {
+ error_report("can't load %s", file);
+ return -1;
+ }
+ fw_cfg_add_file((FWCfgState *)opaque, name, buf, size);
+ return 0;
+}
+
static int device_help_func(void *opaque, QemuOpts *opts, Error **errp)
{
return qdev_device_help(opts);
@@ -2822,6 +2873,7 @@ int main(int argc, char **argv, char **envp)
qemu_add_opts(&qemu_numa_opts);
qemu_add_opts(&qemu_icount_opts);
qemu_add_opts(&qemu_semihosting_config_opts);
+ qemu_add_opts(&qemu_fw_cfg_opts);
runstate_init();
@@ -3438,6 +3490,12 @@ int main(int argc, char **argv, char **envp)
}
do_smbios_option(opts);
break;
+ case QEMU_OPTION_fwcfg:
+ opts = qemu_opts_parse(qemu_find_opts("fw_cfg"), optarg, 1);
+ if (opts == NULL) {
+ exit(1);
+ }
+ break;
case QEMU_OPTION_enable_kvm:
olist = qemu_find_opts("machine");
qemu_opts_parse(olist, "accel=kvm", 0);
@@ -4274,6 +4332,11 @@ int main(int argc, char **argv, char **envp)
numa_post_machine_init();
+ if (qemu_opts_foreach(qemu_find_opts("fw_cfg"),
+ parse_fw_cfg, fw_cfg_find(), NULL) != 0) {
+ exit(1);
+ }
+
/* init USB devices */
if (usb_enabled()) {
if (foreach_device_config(DEV_USB, usb_parse) < 0)