Commit 81b2b810 authored by Gabriel L. Somlo's avatar Gabriel L. Somlo Committed by Gerd Hoffmann
Browse files

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: default avatarGabriel Somlo <somlo@cmu.edu>
Reviewed-by: default avatarLaszlo Ersek <lersek@redhat.com>
Signed-off-by: default avatarGerd Hoffmann <kraxel@redhat.com>
parent 0eb973f9
Loading
Loading
Loading
Loading
+21 −0
Original line number Diff line number Diff line
@@ -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().
+11 −0
Original line number Diff line number Diff line
@@ -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)
+63 −0
Original line number Diff line number Diff line
@@ -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)