Commit baf2d5bf authored by Michael S. Tsirkin's avatar Michael S. Tsirkin
Browse files

fw-cfg: support writeable blobs

Useful to send guest data back to QEMU.

Changes from Laszlo Ersek <lersek@redhat.com>:
- rebase the patch from Michael Tsirkin's original postings at [1] and [2]
  to the following patches:
  - loader: Allow a custom AddressSpace when loading ROMs
  - loader: Add AddressSpace loading support to uImages
  - loader: fix handling of custom address spaces when adding ROM blobs
- reject such writes immediately that would exceed the end of the array,
  rather than performing a partial write before setting the error bit: see
  the (len != dma.length) condition
- document the write interface

[1] http://lists.nongnu.org/archive/html/qemu-devel/2016-02/msg04968.html
[2] http://lists.nongnu.org/archive/html/qemu-devel/2016-03/msg02735.html



Cc: "Gabriel L. Somlo" <somlo@cmu.edu>
Cc: "Michael S. Tsirkin" <mst@redhat.com>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Cc: Igor Mammedov <imammedo@redhat.com>
Cc: Michael Walle <michael@walle.cc>
Cc: Paolo Bonzini <pbonzini@redhat.com>
Cc: Peter Maydell <peter.maydell@linaro.org>
Cc: Shannon Zhao <zhaoshenglong@huawei.com>
Cc: qemu-arm@nongnu.org
Signed-off-by: default avatarMichael S. Tsirkin <mst@redhat.com>
Signed-off-by: default avatarLaszlo Ersek <lersek@redhat.com>
Reviewed-by: default avatarMarcel Apfelbaum <marcel@redhat.com>
Acked-by: default avatarGabriel Somlo <somlo@cmu.edu>
Tested-by: default avatarGabriel Somlo <somlo@cmu.edu>
Reviewed-by: default avatarMichael S. Tsirkin <mst@redhat.com>
Signed-off-by: default avatarMichael S. Tsirkin <mst@redhat.com>
Reviewed-by: default avatarEduardo Habkost <ehabkost@redhat.com>
parent c471ad0e
Loading
Loading
Loading
Loading
+25 −7
Original line number Diff line number Diff line
@@ -33,6 +33,10 @@ the selector value is between 0x4000-0x7fff or 0xc000-0xffff.
NOTE: As of QEMU v2.4, writes to the fw_cfg data register are no
      longer supported, and will be ignored (treated as no-ops)!

NOTE: As of QEMU v2.9, writes are reinstated, but only through the DMA
      interface (see below). Furthermore, writeability of any specific item is
      governed independently of Bit14 in the selector key value.

Bit15 of the selector register indicates whether the configuration
setting is architecture specific. A value of 0 means the item is a
generic configuration item. A value of 1 means the item is specific
@@ -43,7 +47,7 @@ value between 0x8000-0xffff.

== Data Register ==

* Read/Write (writes ignored as of QEMU v2.4)
* Read/Write (writes ignored as of QEMU v2.4, but see the DMA interface)
* Location: platform dependent (IOport [*] or MMIO)
* Width: 8-bit (if IOport), 8/16/32/64-bit (if MMIO)
* Endianness: string-preserving
@@ -134,8 +138,8 @@ struct FWCfgFile { /* an individual file entry, 64 bytes total */

=== All Other Data Items ===

Please consult the QEMU source for the most up-to-date and authoritative
list of selector keys and their respective items' purpose and format.
Please consult the QEMU source for the most up-to-date and authoritative list
of selector keys and their respective items' purpose, format and writeability.

=== Ranges ===

@@ -144,9 +148,11 @@ items, and up to 0x4000 architecturally specific ones.

Selector Reg.    Range Usage
---------------  -----------
0x0000 - 0x3fff  Generic (0x0000 - 0x3fff, RO)
0x0000 - 0x3fff  Generic (0x0000 - 0x3fff, generally RO, possibly RW through
                          the DMA interface in QEMU v2.9+)
0x4000 - 0x7fff  Generic (0x0000 - 0x3fff, RW, ignored in QEMU v2.4+)
0x8000 - 0xbfff  Arch. Specific (0x0000 - 0x3fff, RO)
0x8000 - 0xbfff  Arch. Specific (0x0000 - 0x3fff, generally RO, possibly RW
                                 through the DMA interface in QEMU v2.9+)
0xc000 - 0xffff  Arch. Specific (0x0000 - 0x3fff, RW, ignored in v2.4+)

In practice, the number of allowed firmware configuration items is given
@@ -182,6 +188,7 @@ The "control" field has the following bits:
 - Bit 1: Read
 - Bit 2: Skip
 - Bit 3: Select. The upper 16 bits are the selected index.
 - Bit 4: Write

When an operation is triggered, if the "control" field has bit 3 set, the
upper 16 bits are interpreted as an index of a firmware configuration item.
@@ -191,8 +198,17 @@ If the "control" field has bit 1 set, a read operation will be performed.
"length" bytes for the current selector and offset will be copied into the
physical RAM address specified by the "address" field.

If the "control" field has bit 2 set (and not bit 1), a skip operation will be
performed. The offset for the current selector will be advanced "length" bytes.
If the "control" field has bit 4 set (and not bit 1), a write operation will be
performed. "length" bytes will be copied from the physical RAM address
specified by the "address" field to the current selector and offset. QEMU
prevents starting or finishing the write beyond the end of the item associated
with the current selector (i.e., the item cannot be resized). Truncated writes
are dropped entirely. Writes to read-only items are also rejected. All of these
write errors set bit 0 (the error bit) in the "control" field.

If the "control" field has bit 2 set (and neither bit 1 nor bit 4), a skip
operation will be performed. The offset for the current selector will be
advanced "length" bytes.

To check the result, read the "control" field:
   error bit set        ->  something went wrong.
@@ -234,3 +250,5 @@ Prefix "opt/org.qemu/" is reserved for QEMU itself.

Use of names not beginning with "opt/" is potentially dangerous and
entirely unsupported.  QEMU will warn if you try.

All externally provided fw_cfg items are read-only to the guest.
+1 −1
Original line number Diff line number Diff line
@@ -818,7 +818,7 @@ static MemoryRegion *acpi_add_rom_blob(AcpiBuildState *build_state,
                                       uint64_t max_size)
{
    return rom_add_blob(name, blob->data, acpi_data_len(blob), max_size, -1,
                        name, virt_acpi_build_update, build_state, NULL);
                        name, virt_acpi_build_update, build_state, NULL, true);
}

static const VMStateDescription vmstate_virt_acpi_build = {
+11 −7
Original line number Diff line number Diff line
@@ -853,7 +853,7 @@ static void fw_cfg_resized(const char *id, uint64_t length, void *host)
    }
}

static void *rom_set_mr(Rom *rom, Object *owner, const char *name)
static void *rom_set_mr(Rom *rom, Object *owner, const char *name, bool ro)
{
    void *data;

@@ -862,7 +862,7 @@ static void *rom_set_mr(Rom *rom, Object *owner, const char *name)
                                      rom->datasize, rom->romsize,
                                      fw_cfg_resized,
                                      &error_fatal);
    memory_region_set_readonly(rom->mr, true);
    memory_region_set_readonly(rom->mr, ro);
    vmstate_register_ram_global(rom->mr);

    data = memory_region_get_ram_ptr(rom->mr);
@@ -942,7 +942,7 @@ int rom_add_file(const char *file, const char *fw_dir,
        snprintf(devpath, sizeof(devpath), "/rom@%s", fw_file_name);

        if ((!option_rom || mc->option_rom_has_mr) && mc->rom_file_has_mr) {
            data = rom_set_mr(rom, OBJECT(fw_cfg), devpath);
            data = rom_set_mr(rom, OBJECT(fw_cfg), devpath, true);
        } else {
            data = rom->data;
        }
@@ -979,7 +979,7 @@ err:
MemoryRegion *rom_add_blob(const char *name, const void *blob, size_t len,
                   size_t max_len, hwaddr addr, const char *fw_file_name,
                   FWCfgReadCallback fw_callback, void *callback_opaque,
                   AddressSpace *as)
                   AddressSpace *as, bool read_only)
{
    MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine());
    Rom *rom;
@@ -998,10 +998,14 @@ MemoryRegion *rom_add_blob(const char *name, const void *blob, size_t len,
        char devpath[100];
        void *data;

        if (read_only) {
            snprintf(devpath, sizeof(devpath), "/rom@%s", fw_file_name);
        } else {
            snprintf(devpath, sizeof(devpath), "/ram@%s", fw_file_name);
        }

        if (mc->rom_file_has_mr) {
            data = rom_set_mr(rom, OBJECT(fw_cfg), devpath);
            data = rom_set_mr(rom, OBJECT(fw_cfg), devpath, read_only);
            mr = rom->mr;
        } else {
            data = rom->data;
@@ -1009,7 +1013,7 @@ MemoryRegion *rom_add_blob(const char *name, const void *blob, size_t len,

        fw_cfg_add_file_callback(fw_cfg, fw_file_name,
                                 fw_callback, callback_opaque,
                                 data, rom->datasize);
                                 data, rom->datasize, read_only);
    }
    return mr;
}
+2 −2
Original line number Diff line number Diff line
@@ -2806,7 +2806,7 @@ static MemoryRegion *acpi_add_rom_blob(AcpiBuildState *build_state,
                                       uint64_t max_size)
{
    return rom_add_blob(name, blob->data, acpi_data_len(blob), max_size, -1,
                        name, acpi_build_update, build_state, NULL);
                        name, acpi_build_update, build_state, NULL, true);
}

static const VMStateDescription vmstate_acpi_build = {
@@ -2872,7 +2872,7 @@ void acpi_setup(void)
        build_state->rsdp = g_memdup(tables.rsdp->data, rsdp_size);
        fw_cfg_add_file_callback(pcms->fw_cfg, ACPI_BUILD_RSDP_FILE,
                                 acpi_build_update, build_state,
                                 build_state->rsdp, rsdp_size);
                                 build_state->rsdp, rsdp_size, true);
        build_state->rsdp_mr = NULL;
    } else {
        build_state->rsdp = NULL;
+1 −1
Original line number Diff line number Diff line
@@ -75,7 +75,7 @@ static inline void hwsetup_create_rom(HWSetup *hw,
        hwaddr base)
{
    rom_add_blob("hwsetup", hw->data, TARGET_PAGE_SIZE,
                 TARGET_PAGE_SIZE, base, NULL, NULL, NULL, NULL);
                 TARGET_PAGE_SIZE, base, NULL, NULL, NULL, NULL, true);
}

static inline void hwsetup_add_u8(HWSetup *hw, uint8_t u)
Loading