This post is a short introduction to the QemuOpts API inside QEMU. This is part of a series, see the introduction for other pointers and additional information.

QemuOpts was introduced in 2009. It is a simple abstraction that handles two tasks:

  1. Parsing of config files and command-line options
  2. Storage of configuration options

Data structures

The QemuOpts data model is pretty simple:

  • QemuOptsList carries the list of all options belonging to a given config group. Each entity is represented by a QemuOpts struct.

  • QemuOpts represents a set of key-value pairs. (Some of the code refers to that as a config group, but to avoid confusion with QemuOptsList, I will call them config sections).

  • QemuOpt is a single key=value pair.

Some config groups have multiple QemuOpts structs (e.g. “drive”, “object”, “device”, that represent multiple drives, multiple objects, and multiple devices, respectively), while others always have only one QemuOpts struct (e.g. the “machine” config group).

For example, the following command-line options:

-drive id=disk1,file=disk.raw,format=raw,if=ide \
-drive id=disk2,file=disk.qcow2,format=qcow2,if=virtio \
-machine usb=on -machine accel=kvm

are represented internally as:

Diagram showing two QemuOptsList objects: qemu_drive_opts and qemu_machine_opts. qemu_drive_opts has two QemuOpts entries: disk1 and disk2. disk2 has three QemuOpt entries: file=disk.raw, format=raw, if=ide. disk2 has three QemuOpt entries: file=disk.qcow2, format=qcow2, if=virtio. qemu_machine_opts has one QemuOpts entry. The QemuOpts entry for qemu_machine_opts has two QemuOpt entries: usb=on, accel=kvm

Data Types

QemuOpts supports a limited number of data types for option values:

  • Strings
  • Boolean options
  • Numbers (integers)
  • Sizes

Strings

Strings are just used as-is, after the command-line or config file is parsed.

Note: On the command-line, options are separated by commas, but commas inside option values can be escaped as ,,.

Boolean options

The QemuOpt parser accepts only “on” and “off” as values for this option.

Warning: note that this behavior is different from the QOM property parser. I plan to explore this in future posts.

Numbers (integers)

Numbers are supposed to be unsigned 64-bit integers. However, the code relies on the behavior of strtoull() and does not reject negative numbers. That means the parsed uint64_t value might be converted to a signed integer later. For example, the following command-line is not rejected by QEMU:

$ qemu-system-x86_64 -smp cpus=-18446744073709551615,cores=1,threads=1

I don’t know if there is existing code that requires negative numbers to be accepted by the QemuOpts parser. I assume it exists, so we couldn’t easily change the existing parsing rules without breaking existing code.

Sizes

Sizes are represented internally as integers, but the parser accept suffixes like K, M, G, T.

qemu-system-x86_64 -m size=2G

is equivalent to:

qemu-system-x86_64 -m size=2048M

Note: there are two different size-suffix parsers inside QEMU: one at util/cutils.c and another at util/qemu-option.c. Figuring out which one is going to be used is left as an exercise to the reader.

Working around the QemuOpts parsers

QEMU code sometimes uses tricks to avoid or work around the QemuOpts option value parsers:

Example 1: using the raw option value

It is possible to get the original raw option value as a string using qemu_opt_get(), even after it was already parsed. For example, the code that handles memory options in QEMU does that, to ensure a suffix-less number is interpreted as Mebibytes, not bytes:

    mem_str = qemu_opt_get(opts, "size");
    if (mem_str) {
        /* [...] */
        sz = qemu_opt_get_size(opts, "size", ram_size);
        /* Fix up legacy suffix-less format */
        if (g_ascii_isdigit(mem_str[strlen(mem_str) - 1])) {
            sz <<= 20;
            /* [...] */
        }
    }

Example 2: empty option name list

Some options do not use the QemuOpts value parsers at all, by not defining any option names in the QemuOptsList struct. In those cases, the option values are parsed and validated using different methods. Some examples:

static QemuOptsList qemu_machine_opts = {
    .name = "machine",
    .implied_opt_name = "type",
    .merge_lists = true,
    .head = QTAILQ_HEAD_INITIALIZER(qemu_machine_opts.head),
    .desc = {
        /*
         * no elements => accept any
         * sanity checking will happen later
         * when setting machine properties
         */
        { }
    },
};
static QemuOptsList qemu_acpi_opts = {
    .name = "acpi",
    .implied_opt_name = "data",
    .head = QTAILQ_HEAD_INITIALIZER(qemu_acpi_opts.head),
    .desc = { { 0 } } /* validated with OptsVisitor */
};

This is a common pattern when options are translated to other data representations: mostly QOM properties or QAPI structs. I plan to explore this in a future blog post.

The following config groups use this method and do their own parsing/validation of config options: acpi, device, drive, machine, net, netdev, numa, object, smbios, tpmdev

-writeconfig

The QemuOpts code is responsible for two tasks:

  1. Parsing command-line options and config files
  2. Storage of configuration options

This means sometimes config options are parsed by custom code and converted to QemuOpts data structures. Storage of config options inside QemuOpts allow the existing QEMU configuration to be written to a file using the -writeconfig command-line option.

The original commit introducing -writeconfig describes it this way:

In theory you should be able to do:

qemu < machine config cmd line switches here > -writeconfig vm.cfg
qemu -readconfig vm.cfg

In practice it will not work. Not all command line switches are converted to QemuOpts, so you’ll have to keep the not-yet converted ones on the second line. Also there might be bugs lurking which prevent even the converted ones from working correctly.

This has improved over the years, but the comment still applies today: most command-line options are converted to QemuOpts options, but not all of them.

Further reading