From 13cc2c3e867605122351c68f34af838bc75d8692 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Thu, 27 Feb 2014 02:05:05 +0100 Subject: serial-pci: Set prog interface field of pci config to 16550 compatible Signed-off-by: BALATON Zoltan Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Reviewed-by: Gerd Hoffmann --- hw/char/serial-pci.c | 7 +++++++ include/hw/i386/pc.h | 15 +++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/hw/char/serial-pci.c b/hw/char/serial-pci.c index 991c99fa6e..acccc9cabd 100644 --- a/hw/char/serial-pci.c +++ b/hw/char/serial-pci.c @@ -34,6 +34,7 @@ typedef struct PCISerialState { PCIDevice dev; SerialState state; + uint8_t prog_if; } PCISerialState; typedef struct PCIMultiSerialState { @@ -44,6 +45,7 @@ typedef struct PCIMultiSerialState { SerialState state[PCI_SERIAL_MAX_PORTS]; uint32_t level[PCI_SERIAL_MAX_PORTS]; qemu_irq *irqs; + uint8_t prog_if; } PCIMultiSerialState; static int serial_pci_init(PCIDevice *dev) @@ -60,6 +62,7 @@ static int serial_pci_init(PCIDevice *dev) return -1; } + pci->dev.config[PCI_CLASS_PROG] = pci->prog_if; pci->dev.config[PCI_INTERRUPT_PIN] = 0x01; s->irq = pci_allocate_irq(&pci->dev); @@ -101,6 +104,7 @@ static int multi_serial_pci_init(PCIDevice *dev) assert(pci->ports > 0); assert(pci->ports <= PCI_SERIAL_MAX_PORTS); + pci->dev.config[PCI_CLASS_PROG] = pci->prog_if; pci->dev.config[PCI_INTERRUPT_PIN] = 0x01; memory_region_init(&pci->iobar, OBJECT(pci), "multiserial", 8 * pci->ports); pci_register_bar(&pci->dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &pci->iobar); @@ -177,12 +181,14 @@ static const VMStateDescription vmstate_pci_multi_serial = { static Property serial_pci_properties[] = { DEFINE_PROP_CHR("chardev", PCISerialState, state.chr), + DEFINE_PROP_UINT8("prog_if", PCISerialState, prog_if, 0x02), DEFINE_PROP_END_OF_LIST(), }; static Property multi_2x_serial_pci_properties[] = { DEFINE_PROP_CHR("chardev1", PCIMultiSerialState, state[0].chr), DEFINE_PROP_CHR("chardev2", PCIMultiSerialState, state[1].chr), + DEFINE_PROP_UINT8("prog_if", PCIMultiSerialState, prog_if, 0x02), DEFINE_PROP_END_OF_LIST(), }; @@ -191,6 +197,7 @@ static Property multi_4x_serial_pci_properties[] = { DEFINE_PROP_CHR("chardev2", PCIMultiSerialState, state[1].chr), DEFINE_PROP_CHR("chardev3", PCIMultiSerialState, state[2].chr), DEFINE_PROP_CHR("chardev4", PCIMultiSerialState, state[3].chr), + DEFINE_PROP_UINT8("prog_if", PCIMultiSerialState, prog_if, 0x02), DEFINE_PROP_END_OF_LIST(), }; diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index 32a76876c7..552fbd8243 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -271,6 +271,21 @@ bool e820_get_entry(int, uint32_t, uint64_t *, uint64_t *); .driver = "apic",\ .property = "version",\ .value = stringify(0x11),\ + },\ + {\ + .driver = "pci-serial",\ + .property = "prog_if",\ + .value = stringify(0),\ + },\ + {\ + .driver = "pci-serial-2x",\ + .property = "prof_if",\ + .value = stringify(0),\ + },\ + {\ + .driver = "pci-serial-4x",\ + .property = "prog_if",\ + .value = stringify(0),\ } #define PC_COMPAT_1_7 \ -- cgit v1.2.3-55-g7522 From fb5be2e833669dd95be1950ea7b1da4cf7f9556c Mon Sep 17 00:00:00 2001 From: Gabriel L. Somlo Date: Mon, 19 May 2014 10:09:53 -0400 Subject: SMBIOS: Fix endian-ness when populating multi-byte fields When i386 guests are emulated on big endian hosts, make sure multi-byte fields are populated safely via cpu_to_le*(). Signed-off-by: Gabriel Somlo Reviewed-by: Laszlo Ersek Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/smbios.c | 92 +++++++++++++++++++++++++++++--------------------------- 1 file changed, 47 insertions(+), 45 deletions(-) diff --git a/hw/i386/smbios.c b/hw/i386/smbios.c index 76607181c3..aeb94bc671 100644 --- a/hw/i386/smbios.c +++ b/hw/i386/smbios.c @@ -444,7 +444,7 @@ static bool smbios_skip_table(uint8_t type, bool required_table) \ t->header.type = tbl_type; \ t->header.length = sizeof(*t); \ - t->header.handle = tbl_handle; \ + t->header.handle = cpu_to_le16(tbl_handle); \ } while (0) #define SMBIOS_TABLE_SET_STR(tbl_type, field, value) \ @@ -491,7 +491,7 @@ static void smbios_build_type_0_table(void) SMBIOS_TABLE_SET_STR(0, vendor_str, type0.vendor); SMBIOS_TABLE_SET_STR(0, bios_version_str, type0.version); - t->bios_starting_address_segment = 0xE800; /* hardcoded in SeaBIOS */ + t->bios_starting_address_segment = cpu_to_le16(0xE800); /* from SeaBIOS */ SMBIOS_TABLE_SET_STR(0, bios_release_date_str, type0.date); @@ -551,7 +551,7 @@ static void smbios_build_type_2_table(void) SMBIOS_TABLE_SET_STR(2, asset_tag_number_str, type2.asset); t->feature_flags = 0x01; /* Motherboard */ SMBIOS_TABLE_SET_STR(2, location_str, type2.location); - t->chassis_handle = 0x300; /* Type 3 (System enclosure) */ + t->chassis_handle = cpu_to_le16(0x300); /* Type 3 (System enclosure) */ t->board_type = 0x0A; /* Motherboard */ t->contained_element_count = 0; @@ -571,7 +571,7 @@ static void smbios_build_type_3_table(void) t->power_supply_state = 0x03; /* Safe */ t->thermal_state = 0x03; /* Safe */ t->security_status = 0x02; /* Unknown */ - t->oem_defined = 0; + t->oem_defined = cpu_to_le32(0); t->height = 0; t->number_of_power_cords = 0; t->contained_element_count = 0; @@ -589,26 +589,27 @@ static void smbios_build_type_4_table(unsigned instance) snprintf(sock_str, sizeof(sock_str), "%s%2x", type4.sock_pfx, instance); SMBIOS_TABLE_SET_STR(4, socket_designation_str, sock_str); t->processor_type = 0x03; /* CPU */ + t->processor_family = 0x01; /* Other */ SMBIOS_TABLE_SET_STR(4, processor_manufacturer_str, type4.manufacturer); - t->processor_id[0] = smbios_cpuid_version; - t->processor_id[1] = smbios_cpuid_features; + t->processor_id[0] = cpu_to_le32(smbios_cpuid_version); + t->processor_id[1] = cpu_to_le32(smbios_cpuid_features); SMBIOS_TABLE_SET_STR(4, processor_version_str, type4.version); t->voltage = 0; - t->external_clock = 0; /* Unknown */ - t->max_speed = 0; /* Unknown */ - t->current_speed = 0; /* Unknown */ + t->external_clock = cpu_to_le16(0); /* Unknown */ + t->max_speed = cpu_to_le16(0); /* Unknown */ + t->current_speed = cpu_to_le16(0); /* Unknown */ t->status = 0x41; /* Socket populated, CPU enabled */ t->processor_upgrade = 0x01; /* Other */ - t->l1_cache_handle = 0xFFFF; /* N/A */ - t->l2_cache_handle = 0xFFFF; /* N/A */ - t->l3_cache_handle = 0xFFFF; /* N/A */ + t->l1_cache_handle = cpu_to_le16(0xFFFF); /* N/A */ + t->l2_cache_handle = cpu_to_le16(0xFFFF); /* N/A */ + t->l3_cache_handle = cpu_to_le16(0xFFFF); /* N/A */ SMBIOS_TABLE_SET_STR(4, serial_number_str, type4.serial); SMBIOS_TABLE_SET_STR(4, asset_tag_number_str, type4.asset); SMBIOS_TABLE_SET_STR(4, part_number_str, type4.part); t->core_count = t->core_enabled = smp_cores; t->thread_count = smp_threads; - t->processor_characteristics = 0x02; /* Unknown */ - t->processor_family = t->processor_family2 = 0x01; /* Other */ + t->processor_characteristics = cpu_to_le16(0x02); /* Unknown */ + t->processor_family2 = cpu_to_le16(0x01); /* Other */ SMBIOS_BUILD_TABLE_POST; smbios_type4_count++; @@ -631,14 +632,14 @@ static void smbios_build_type_16_table(unsigned dimm_cnt) t->error_correction = 0x06; /* Multi-bit ECC (for Microsoft, per SeaBIOS) */ size_kb = QEMU_ALIGN_UP(ram_size, ONE_KB) / ONE_KB; if (size_kb < MAX_T16_STD_SZ) { - t->maximum_capacity = size_kb; - t->extended_maximum_capacity = 0; + t->maximum_capacity = cpu_to_le32(size_kb); + t->extended_maximum_capacity = cpu_to_le64(0); } else { - t->maximum_capacity = MAX_T16_STD_SZ; - t->extended_maximum_capacity = ram_size; + t->maximum_capacity = cpu_to_le32(MAX_T16_STD_SZ); + t->extended_maximum_capacity = cpu_to_le64(ram_size); } - t->memory_error_information_handle = 0xFFFE; /* Not provided */ - t->number_of_memory_devices = dimm_cnt; + t->memory_error_information_handle = cpu_to_le16(0xFFFE); /* Not provided */ + t->number_of_memory_devices = cpu_to_le16(dimm_cnt); SMBIOS_BUILD_TABLE_POST; } @@ -653,18 +654,18 @@ static void smbios_build_type_17_table(unsigned instance, ram_addr_t size) SMBIOS_BUILD_TABLE_PRE(17, 0x1100 + instance, true); /* required */ - t->physical_memory_array_handle = 0x1000; /* Type 16 (Phys. Mem. Array) */ - t->memory_error_information_handle = 0xFFFE; /* Not provided */ - t->total_width = 0xFFFF; /* Unknown */ - t->data_width = 0xFFFF; /* Unknown */ + t->physical_memory_array_handle = cpu_to_le16(0x1000); /* Type 16 above */ + t->memory_error_information_handle = cpu_to_le16(0xFFFE); /* Not provided */ + t->total_width = cpu_to_le16(0xFFFF); /* Unknown */ + t->data_width = cpu_to_le16(0xFFFF); /* Unknown */ size_mb = QEMU_ALIGN_UP(size, ONE_MB) / ONE_MB; if (size_mb < MAX_T17_STD_SZ) { - t->size = size_mb; - t->extended_size = 0; + t->size = cpu_to_le16(size_mb); + t->extended_size = cpu_to_le32(0); } else { assert(size_mb < MAX_T17_EXT_SZ); - t->size = MAX_T17_STD_SZ; - t->extended_size = size_mb; + t->size = cpu_to_le16(MAX_T17_STD_SZ); + t->extended_size = cpu_to_le32(size_mb); } t->form_factor = 0x09; /* DIMM */ t->device_set = 0; /* Not in a set */ @@ -672,17 +673,17 @@ static void smbios_build_type_17_table(unsigned instance, ram_addr_t size) SMBIOS_TABLE_SET_STR(17, device_locator_str, loc_str); SMBIOS_TABLE_SET_STR(17, bank_locator_str, type17.bank); t->memory_type = 0x07; /* RAM */ - t->type_detail = 0x02; /* Other */ - t->speed = 0; /* Unknown */ + t->type_detail = cpu_to_le16(0x02); /* Other */ + t->speed = cpu_to_le16(0); /* Unknown */ SMBIOS_TABLE_SET_STR(17, manufacturer_str, type17.manufacturer); SMBIOS_TABLE_SET_STR(17, serial_number_str, type17.serial); SMBIOS_TABLE_SET_STR(17, asset_tag_number_str, type17.asset); SMBIOS_TABLE_SET_STR(17, part_number_str, type17.part); t->attributes = 0; /* Unknown */ - t->configured_clock_speed = 0; /* Unknown */ - t->minimum_voltage = 0; /* Unknown */ - t->maximum_voltage = 0; /* Unknown */ - t->configured_voltage = 0; /* Unknown */ + t->configured_clock_speed = cpu_to_le32(0); /* Unknown */ + t->minimum_voltage = cpu_to_le32(0); /* Unknown */ + t->maximum_voltage = cpu_to_le32(0); /* Unknown */ + t->configured_voltage = cpu_to_le32(0); /* Unknown */ SMBIOS_BUILD_TABLE_POST; } @@ -699,15 +700,16 @@ static void smbios_build_type_19_table(unsigned instance, start_kb = start / ONE_KB; end_kb = end / ONE_KB; if (start_kb < UINT32_MAX && end_kb < UINT32_MAX) { - t->starting_address = start_kb; - t->ending_address = end_kb; - t->extended_starting_address = t->extended_ending_address = 0; + t->starting_address = cpu_to_le32(start_kb); + t->ending_address = cpu_to_le32(end_kb); + t->extended_starting_address = + t->extended_ending_address = cpu_to_le64(0); } else { - t->starting_address = t->ending_address = UINT32_MAX; - t->extended_starting_address = start; - t->extended_ending_address = end; + t->starting_address = t->ending_address = cpu_to_le32(UINT32_MAX); + t->extended_starting_address = cpu_to_le64(start); + t->extended_ending_address = cpu_to_le64(end); } - t->memory_array_handle = 0x1000; /* Type 16 (Phys. Mem. Array) */ + t->memory_array_handle = cpu_to_le16(0x1000); /* Type 16 above */ t->partition_width = 1; /* One device per row */ SMBIOS_BUILD_TABLE_POST; @@ -794,14 +796,14 @@ static void smbios_entry_point_setup(void) ep.smbios_bcd_revision = 0x28; /* set during table construction, but BIOS may override: */ - ep.structure_table_length = smbios_tables_len; - ep.max_structure_size = smbios_table_max; - ep.number_of_structures = smbios_table_cnt; + ep.structure_table_length = cpu_to_le16(smbios_tables_len); + ep.max_structure_size = cpu_to_le16(smbios_table_max); + ep.number_of_structures = cpu_to_le16(smbios_table_cnt); /* BIOS must recalculate: */ ep.checksum = 0; ep.intermediate_checksum = 0; - ep.structure_table_address = 0; /* where BIOS has copied smbios_tables */ + ep.structure_table_address = cpu_to_le32(0); } void smbios_get_tables(uint8_t **tables, size_t *tables_len, -- cgit v1.2.3-55-g7522 From 84351843eba330022e245a742899cf71fc817ec5 Mon Sep 17 00:00:00 2001 From: Gabriel L. Somlo Date: Mon, 19 May 2014 10:09:54 -0400 Subject: SMBIOS: Update Type 0 struct generator for machines >= 2.1 Update how type 0 (bios info) structures are generated, as follows: - convert bios_characteristics field to uin64_t (instead of uint8_t[8]), as described in the current smbios spec (v2.8) - enable "virtual machine" bit in bios_characteristics_extension_bits - add command line option to enable "uefi supported" bit in bios_characteristics_extension_bits These updates should make this optional structure more useful when used with edk2/ovmf. Only pc machines >= 2.1 are affected, and only when a type 0 structure is explicitly specified on the command line. Signed-off-by: Gabriel Somlo Reviewed-by: Laszlo Ersek Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/smbios.c | 18 +++++++++++------- include/hw/i386/smbios.h | 2 +- qemu-options.hx | 4 ++-- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/hw/i386/smbios.c b/hw/i386/smbios.c index aeb94bc671..17938215f6 100644 --- a/hw/i386/smbios.c +++ b/hw/i386/smbios.c @@ -67,7 +67,7 @@ static DECLARE_BITMAP(have_fields_bitmap, SMBIOS_MAX_TYPE+1); static struct { const char *vendor, *version, *date; - bool have_major_minor; + bool have_major_minor, uefi; uint8_t major, minor; } type0; @@ -134,6 +134,10 @@ static const QemuOptDesc qemu_smbios_type0_opts[] = { .name = "release", .type = QEMU_OPT_STRING, .help = "revision number", + },{ + .name = "uefi", + .type = QEMU_OPT_BOOL, + .help = "uefi support", }, { /* end of list */ } }; @@ -497,13 +501,12 @@ static void smbios_build_type_0_table(void) t->bios_rom_size = 0; /* hardcoded in SeaBIOS with FIXME comment */ - /* BIOS characteristics not supported */ - memset(t->bios_characteristics, 0, 8); - t->bios_characteristics[0] = 0x08; - - /* Enable targeted content distribution (needed for SVVP, per SeaBIOS) */ + t->bios_characteristics = cpu_to_le64(0x08); /* Not supported */ t->bios_characteristics_extension_bytes[0] = 0; - t->bios_characteristics_extension_bytes[1] = 4; + t->bios_characteristics_extension_bytes[1] = 0x14; /* TCD/SVVP | VM */ + if (type0.uefi) { + t->bios_characteristics_extension_bytes[1] |= 0x08; /* |= UEFI */ + } if (type0.have_major_minor) { t->system_bios_major_release = type0.major; @@ -979,6 +982,7 @@ void smbios_entry_add(QemuOpts *opts) save_opt(&type0.vendor, opts, "vendor"); save_opt(&type0.version, opts, "version"); save_opt(&type0.date, opts, "date"); + type0.uefi = qemu_opt_get_bool(opts, "uefi", false); val = qemu_opt_get(opts, "release"); if (val) { diff --git a/include/hw/i386/smbios.h b/include/hw/i386/smbios.h index 6d854b7f1e..5583f60405 100644 --- a/include/hw/i386/smbios.h +++ b/include/hw/i386/smbios.h @@ -64,7 +64,7 @@ struct smbios_type_0 { uint16_t bios_starting_address_segment; uint8_t bios_release_date_str; uint8_t bios_rom_size; - uint8_t bios_characteristics[8]; + uint64_t bios_characteristics; uint8_t bios_characteristics_extension_bytes[2]; uint8_t system_bios_major_release; uint8_t system_bios_minor_release; diff --git a/qemu-options.hx b/qemu-options.hx index 781af14bf5..15779e723e 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -1335,7 +1335,7 @@ ETEXI DEF("smbios", HAS_ARG, QEMU_OPTION_smbios, "-smbios file=binary\n" " load SMBIOS entry from binary file\n" - "-smbios type=0[,vendor=str][,version=str][,date=str][,release=%d.%d]\n" + "-smbios type=0[,vendor=str][,version=str][,date=str][,release=%d.%d][,uefi=on|off]\n" " specify SMBIOS type 0 fields\n" "-smbios type=1[,manufacturer=str][,product=str][,version=str][,serial=str]\n" " [,uuid=uuid][,sku=str][,family=str]\n" @@ -1345,7 +1345,7 @@ STEXI @findex -smbios Load SMBIOS entry from binary file. -@item -smbios type=0[,vendor=@var{str}][,version=@var{str}][,date=@var{str}][,release=@var{%d.%d}] +@item -smbios type=0[,vendor=@var{str}][,version=@var{str}][,date=@var{str}][,release=@var{%d.%d}][,uefi=on|off] Specify SMBIOS type 0 fields @item -smbios type=1[,manufacturer=@var{str}][,product=@var{str}] [,version=@var{str}][,serial=@var{str}][,uuid=@var{uuid}][,sku=@var{str}] [,family=@var{str}] -- cgit v1.2.3-55-g7522 From 0d73394ad93aa12755316b3a90b3193aeeb95f90 Mon Sep 17 00:00:00 2001 From: Gabriel L. Somlo Date: Mon, 19 May 2014 10:09:55 -0400 Subject: SMBIOS: Fix type 17 field sizes Fields for configured_clock_speed and various voltage values introduced in spec v2.7+ should be "word", i.e. 16 bits. Reported-by: Laszlo Ersek Signed-off-by: Gabriel Somlo Reviewed-by: Laszlo Ersek Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/smbios.c | 8 ++++---- include/hw/i386/smbios.h | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/hw/i386/smbios.c b/hw/i386/smbios.c index 17938215f6..b3bedde8b9 100644 --- a/hw/i386/smbios.c +++ b/hw/i386/smbios.c @@ -683,10 +683,10 @@ static void smbios_build_type_17_table(unsigned instance, ram_addr_t size) SMBIOS_TABLE_SET_STR(17, asset_tag_number_str, type17.asset); SMBIOS_TABLE_SET_STR(17, part_number_str, type17.part); t->attributes = 0; /* Unknown */ - t->configured_clock_speed = cpu_to_le32(0); /* Unknown */ - t->minimum_voltage = cpu_to_le32(0); /* Unknown */ - t->maximum_voltage = cpu_to_le32(0); /* Unknown */ - t->configured_voltage = cpu_to_le32(0); /* Unknown */ + t->configured_clock_speed = cpu_to_le16(0); /* Unknown */ + t->minimum_voltage = cpu_to_le16(0); /* Unknown */ + t->maximum_voltage = cpu_to_le16(0); /* Unknown */ + t->configured_voltage = cpu_to_le16(0); /* Unknown */ SMBIOS_BUILD_TABLE_POST; } diff --git a/include/hw/i386/smbios.h b/include/hw/i386/smbios.h index 5583f60405..a3f4d88bf0 100644 --- a/include/hw/i386/smbios.h +++ b/include/hw/i386/smbios.h @@ -182,10 +182,10 @@ struct smbios_type_17 { uint8_t part_number_str; uint8_t attributes; uint32_t extended_size; - uint32_t configured_clock_speed; - uint32_t minimum_voltage; - uint32_t maximum_voltage; - uint32_t configured_voltage; + uint16_t configured_clock_speed; + uint16_t minimum_voltage; + uint16_t maximum_voltage; + uint16_t configured_voltage; } QEMU_PACKED; /* SMBIOS type 19 - Memory Array Mapped Address (v2.7) */ -- cgit v1.2.3-55-g7522 From 7c8b7248261bfa5e54eff1715b699caa8b3f1d77 Mon Sep 17 00:00:00 2001 From: Andreas Färber Date: Thu, 24 Apr 2014 17:26:51 +0200 Subject: pcie_host: Turn pcie_host_init() into an instance_init This assures the trivial field initialization is applied for any derived type - currently only Q35PCIHost. Signed-off-by: Andreas Färber Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/pci-host/q35.c | 4 ---- hw/pci/pcie_host.c | 7 ++++--- include/hw/pci/pcie_host.h | 1 - 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/hw/pci-host/q35.c b/hw/pci-host/q35.c index 8b8cc4e294..aa48b1c82f 100644 --- a/hw/pci-host/q35.c +++ b/hw/pci-host/q35.c @@ -47,10 +47,6 @@ static void q35_host_realize(DeviceState *dev, Error **errp) sysbus_add_io(sbd, MCH_HOST_BRIDGE_CONFIG_DATA, &pci->data_mem); sysbus_init_ioports(sbd, MCH_HOST_BRIDGE_CONFIG_DATA, 4); - if (pcie_host_init(PCIE_HOST_BRIDGE(s)) < 0) { - error_setg(errp, "failed to initialize pcie host"); - return; - } pci->bus = pci_bus_new(DEVICE(s), "pcie.0", s->mch.pci_address_space, s->mch.address_space_io, 0, TYPE_PCIE_BUS); diff --git a/hw/pci/pcie_host.c b/hw/pci/pcie_host.c index c6e1b573e1..7c88a1d091 100644 --- a/hw/pci/pcie_host.c +++ b/hw/pci/pcie_host.c @@ -83,11 +83,11 @@ static const MemoryRegionOps pcie_mmcfg_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -int pcie_host_init(PCIExpressHost *e) +static void pcie_host_init(Object *obj) { - e->base_addr = PCIE_BASE_ADDR_UNMAPPED; + PCIExpressHost *e = PCIE_HOST_BRIDGE(obj); - return 0; + e->base_addr = PCIE_BASE_ADDR_UNMAPPED; } void pcie_host_mmcfg_unmap(PCIExpressHost *e) @@ -128,6 +128,7 @@ static const TypeInfo pcie_host_type_info = { .parent = TYPE_PCI_HOST_BRIDGE, .abstract = true, .instance_size = sizeof(PCIExpressHost), + .instance_init = pcie_host_init, }; static void pcie_host_register_types(void) diff --git a/include/hw/pci/pcie_host.h b/include/hw/pci/pcie_host.h index acca45ed58..ff44ef6fca 100644 --- a/include/hw/pci/pcie_host.h +++ b/include/hw/pci/pcie_host.h @@ -49,7 +49,6 @@ struct PCIExpressHost { MemoryRegion mmio; }; -int pcie_host_init(PCIExpressHost *e); void pcie_host_mmcfg_unmap(PCIExpressHost *e); void pcie_host_mmcfg_map(PCIExpressHost *e, hwaddr addr, uint32_t size); void pcie_host_mmcfg_update(PCIExpressHost *e, -- cgit v1.2.3-55-g7522 From 38dbd48b247ebe05bdc6ef52ccdc60cc21274877 Mon Sep 17 00:00:00 2001 From: Ján Tomko Date: Wed, 21 May 2014 11:03:47 +0200 Subject: virtio-balloon: return empty data when no stats are available If the guest hasn't updated the stats yet, instead of returning an error, return '-1' for the stats and '0' as 'last-update'. This lets applications ignore this without parsing the error message. Related libvirt patch and discussion: https://www.redhat.com/archives/libvir-list/2014-May/msg00460.html Tested against current upstream libvirt - stat reporting works and it no longer logs errors when the stats are queried on domain startup. (Note: libvirt doesn't use the last-update field for anything yet) Signed-off-by: Ján Tomko Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Reviewed-by: Eric Blake --- docs/virtio-balloon-stats.txt | 5 +++-- hw/virtio/virtio-balloon.c | 7 ++----- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/docs/virtio-balloon-stats.txt b/docs/virtio-balloon-stats.txt index f74612f468..edff5f22a8 100644 --- a/docs/virtio-balloon-stats.txt +++ b/docs/virtio-balloon-stats.txt @@ -35,7 +35,8 @@ which will return a dictionary containing: o A key named last-update, which contains the last stats update timestamp in seconds. Since this timestamp is generated by the host, - a buggy guest can't influence its value + a buggy guest can't influence its value. The value is 0 if the guest + has not updated the stats (yet). It's also important to note the following: @@ -49,7 +50,7 @@ It's also important to note the following: - Polling can be enabled even if the guest doesn't have stats support or the balloon driver wasn't loaded in the guest. If this is the case - and stats are queried, an error will be returned + and stats are queried, last-update will be 0. - The polling timer is only re-armed when the guest responds to the statistics request. This means that if a (buggy) guest doesn't ever diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c index bf2b588b24..22cd52edee 100644 --- a/hw/virtio/virtio-balloon.c +++ b/hw/virtio/virtio-balloon.c @@ -112,11 +112,6 @@ static void balloon_stats_get_all(Object *obj, struct Visitor *v, VirtIOBalloon *s = opaque; int i; - if (!s->stats_last_update) { - error_setg(errp, "guest hasn't updated any stats yet"); - return; - } - visit_start_struct(v, NULL, "guest-stats", name, 0, &err); if (err) { goto out; @@ -378,6 +373,8 @@ static void virtio_balloon_device_realize(DeviceState *dev, Error **errp) s->dvq = virtio_add_queue(vdev, 128, virtio_balloon_handle_output); s->svq = virtio_add_queue(vdev, 128, virtio_balloon_receive_stats); + reset_stats(s); + register_savevm(dev, "virtio-balloon", -1, 1, virtio_balloon_save, virtio_balloon_load, s); -- cgit v1.2.3-55-g7522 From 501f28ca9db08e84819b26314525b6369e7704dd Mon Sep 17 00:00:00 2001 From: Gabriel L. Somlo Date: Tue, 27 May 2014 15:03:14 -0400 Subject: tests: rename acpi-test to bios-tables-test The test harness for acpi (generating a boot disk, starting qemu, waiting for the BIOS to finish booting before examining guest memory, etc.) is perfectly suited for testing other bios tables beside acpi, such as e.g., smbios. This patch renames acpi-test to bios-tables-test to reflect that, and in preparation for adding smbios tests. Signed-off-by: Gabriel Somlo Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/Makefile | 4 +- tests/acpi-test.c | 674 ----------------------------------------------- tests/bios-tables-test.c | 674 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 676 insertions(+), 676 deletions(-) delete mode 100644 tests/acpi-test.c create mode 100644 tests/bios-tables-test.c diff --git a/tests/Makefile b/tests/Makefile index 9f7ca61ae3..ba93e8a1d4 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -131,7 +131,7 @@ check-qtest-i386-y += tests/ide-test$(EXESUF) check-qtest-i386-y += tests/hd-geo-test$(EXESUF) gcov-files-i386-y += hw/block/hd-geometry.c check-qtest-i386-y += tests/boot-order-test$(EXESUF) -check-qtest-i386-y += tests/acpi-test$(EXESUF) +check-qtest-i386-y += tests/bios-tables-test$(EXESUF) check-qtest-i386-y += tests/rtc-test$(EXESUF) check-qtest-i386-y += tests/i440fx-test$(EXESUF) check-qtest-i386-y += tests/fw_cfg-test$(EXESUF) @@ -286,7 +286,7 @@ tests/fdc-test$(EXESUF): tests/fdc-test.o tests/ide-test$(EXESUF): tests/ide-test.o $(libqos-pc-obj-y) tests/hd-geo-test$(EXESUF): tests/hd-geo-test.o tests/boot-order-test$(EXESUF): tests/boot-order-test.o $(libqos-obj-y) -tests/acpi-test$(EXESUF): tests/acpi-test.o $(libqos-obj-y) +tests/bios-tables-test$(EXESUF): tests/bios-tables-test.o $(libqos-obj-y) tests/tmp105-test$(EXESUF): tests/tmp105-test.o $(libqos-omap-obj-y) tests/i440fx-test$(EXESUF): tests/i440fx-test.o $(libqos-pc-obj-y) tests/fw_cfg-test$(EXESUF): tests/fw_cfg-test.o $(libqos-pc-obj-y) diff --git a/tests/acpi-test.c b/tests/acpi-test.c deleted file mode 100644 index 76fbccfa4b..0000000000 --- a/tests/acpi-test.c +++ /dev/null @@ -1,674 +0,0 @@ -/* - * Boot order test cases. - * - * Copyright (c) 2013 Red Hat Inc. - * - * Authors: - * Michael S. Tsirkin , - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -#include -#include -#include -#include -#include "qemu-common.h" -#include "libqtest.h" -#include "qemu/compiler.h" -#include "hw/i386/acpi-defs.h" - -#define MACHINE_PC "pc" -#define MACHINE_Q35 "q35" - -#define ACPI_REBUILD_EXPECTED_AML "TEST_ACPI_REBUILD_AML" - -/* DSDT and SSDTs format */ -typedef struct { - AcpiTableHeader header; - gchar *aml; /* aml bytecode from guest */ - gsize aml_len; - gchar *aml_file; - gchar *asl; /* asl code generated from aml */ - gsize asl_len; - gchar *asl_file; - bool tmp_files_retain; /* do not delete the temp asl/aml */ -} QEMU_PACKED AcpiSdtTable; - -typedef struct { - const char *machine; - uint32_t rsdp_addr; - AcpiRsdpDescriptor rsdp_table; - AcpiRsdtDescriptorRev1 rsdt_table; - AcpiFadtDescriptorRev1 fadt_table; - AcpiFacsDescriptorRev1 facs_table; - uint32_t *rsdt_tables_addr; - int rsdt_tables_nr; - GArray *tables; -} test_data; - -#define LOW(x) ((x) & 0xff) -#define HIGH(x) ((x) >> 8) - -#define SIGNATURE 0xdead -#define SIGNATURE_OFFSET 0x10 -#define BOOT_SECTOR_ADDRESS 0x7c00 - -#define ACPI_READ_FIELD(field, addr) \ - do { \ - switch (sizeof(field)) { \ - case 1: \ - field = readb(addr); \ - break; \ - case 2: \ - field = readw(addr); \ - break; \ - case 4: \ - field = readl(addr); \ - break; \ - case 8: \ - field = readq(addr); \ - break; \ - default: \ - g_assert(false); \ - } \ - addr += sizeof(field); \ - } while (0); - -#define ACPI_READ_ARRAY_PTR(arr, length, addr) \ - do { \ - int idx; \ - for (idx = 0; idx < length; ++idx) { \ - ACPI_READ_FIELD(arr[idx], addr); \ - } \ - } while (0); - -#define ACPI_READ_ARRAY(arr, addr) \ - ACPI_READ_ARRAY_PTR(arr, sizeof(arr)/sizeof(arr[0]), addr) - -#define ACPI_READ_TABLE_HEADER(table, addr) \ - do { \ - ACPI_READ_FIELD((table)->signature, addr); \ - ACPI_READ_FIELD((table)->length, addr); \ - ACPI_READ_FIELD((table)->revision, addr); \ - ACPI_READ_FIELD((table)->checksum, addr); \ - ACPI_READ_ARRAY((table)->oem_id, addr); \ - ACPI_READ_ARRAY((table)->oem_table_id, addr); \ - ACPI_READ_FIELD((table)->oem_revision, addr); \ - ACPI_READ_ARRAY((table)->asl_compiler_id, addr); \ - ACPI_READ_FIELD((table)->asl_compiler_revision, addr); \ - } while (0); - -#define ACPI_ASSERT_CMP(actual, expected) do { \ - uint32_t ACPI_ASSERT_CMP_le = cpu_to_le32(actual); \ - char ACPI_ASSERT_CMP_str[5] = {}; \ - memcpy(ACPI_ASSERT_CMP_str, &ACPI_ASSERT_CMP_le, 4); \ - g_assert_cmpstr(ACPI_ASSERT_CMP_str, ==, expected); \ -} while (0) - -#define ACPI_ASSERT_CMP64(actual, expected) do { \ - uint64_t ACPI_ASSERT_CMP_le = cpu_to_le64(actual); \ - char ACPI_ASSERT_CMP_str[9] = {}; \ - memcpy(ACPI_ASSERT_CMP_str, &ACPI_ASSERT_CMP_le, 8); \ - g_assert_cmpstr(ACPI_ASSERT_CMP_str, ==, expected); \ -} while (0) - -/* Boot sector code: write SIGNATURE into memory, - * then halt. - * Q35 machine requires a minimum 0x7e000 bytes disk. - * (bug or feature?) - */ -static uint8_t boot_sector[0x7e000] = { - /* 7c00: mov $0xdead,%ax */ - [0x00] = 0xb8, - [0x01] = LOW(SIGNATURE), - [0x02] = HIGH(SIGNATURE), - /* 7c03: mov %ax,0x7c10 */ - [0x03] = 0xa3, - [0x04] = LOW(BOOT_SECTOR_ADDRESS + SIGNATURE_OFFSET), - [0x05] = HIGH(BOOT_SECTOR_ADDRESS + SIGNATURE_OFFSET), - /* 7c06: cli */ - [0x06] = 0xfa, - /* 7c07: hlt */ - [0x07] = 0xf4, - /* 7c08: jmp 0x7c07=0x7c0a-3 */ - [0x08] = 0xeb, - [0x09] = LOW(-3), - /* We mov 0xdead here: set value to make debugging easier */ - [SIGNATURE_OFFSET] = LOW(0xface), - [SIGNATURE_OFFSET + 1] = HIGH(0xface), - /* End of boot sector marker */ - [0x1FE] = 0x55, - [0x1FF] = 0xAA, -}; - -static const char *disk = "tests/acpi-test-disk.raw"; -static const char *data_dir = "tests/acpi-test-data"; -#ifdef CONFIG_IASL -static const char *iasl = stringify(CONFIG_IASL); -#else -static const char *iasl; -#endif - -static void free_test_data(test_data *data) -{ - AcpiSdtTable *temp; - int i; - - if (data->rsdt_tables_addr) { - g_free(data->rsdt_tables_addr); - } - - for (i = 0; i < data->tables->len; ++i) { - temp = &g_array_index(data->tables, AcpiSdtTable, i); - if (temp->aml) { - g_free(temp->aml); - } - if (temp->aml_file) { - if (!temp->tmp_files_retain && - g_strstr_len(temp->aml_file, -1, "aml-")) { - unlink(temp->aml_file); - } - g_free(temp->aml_file); - } - if (temp->asl) { - g_free(temp->asl); - } - if (temp->asl_file) { - if (!temp->tmp_files_retain) { - unlink(temp->asl_file); - } - g_free(temp->asl_file); - } - } - - g_array_free(data->tables, false); -} - -static uint8_t acpi_checksum(const uint8_t *data, int len) -{ - int i; - uint8_t sum = 0; - - for (i = 0; i < len; i++) { - sum += data[i]; - } - - return sum; -} - -static void test_acpi_rsdp_address(test_data *data) -{ - uint32_t off; - - /* OK, now find RSDP */ - for (off = 0xf0000; off < 0x100000; off += 0x10) { - uint8_t sig[] = "RSD PTR "; - int i; - - for (i = 0; i < sizeof sig - 1; ++i) { - sig[i] = readb(off + i); - } - - if (!memcmp(sig, "RSD PTR ", sizeof sig)) { - break; - } - } - - g_assert_cmphex(off, <, 0x100000); - data->rsdp_addr = off; -} - -static void test_acpi_rsdp_table(test_data *data) -{ - AcpiRsdpDescriptor *rsdp_table = &data->rsdp_table; - uint32_t addr = data->rsdp_addr; - - ACPI_READ_FIELD(rsdp_table->signature, addr); - ACPI_ASSERT_CMP64(rsdp_table->signature, "RSD PTR "); - - ACPI_READ_FIELD(rsdp_table->checksum, addr); - ACPI_READ_ARRAY(rsdp_table->oem_id, addr); - ACPI_READ_FIELD(rsdp_table->revision, addr); - ACPI_READ_FIELD(rsdp_table->rsdt_physical_address, addr); - ACPI_READ_FIELD(rsdp_table->length, addr); - - /* rsdp checksum is not for the whole table, but for the first 20 bytes */ - g_assert(!acpi_checksum((uint8_t *)rsdp_table, 20)); -} - -static void test_acpi_rsdt_table(test_data *data) -{ - AcpiRsdtDescriptorRev1 *rsdt_table = &data->rsdt_table; - uint32_t addr = data->rsdp_table.rsdt_physical_address; - uint32_t *tables; - int tables_nr; - uint8_t checksum; - - /* read the header */ - ACPI_READ_TABLE_HEADER(rsdt_table, addr); - ACPI_ASSERT_CMP(rsdt_table->signature, "RSDT"); - - /* compute the table entries in rsdt */ - tables_nr = (rsdt_table->length - sizeof(AcpiRsdtDescriptorRev1)) / - sizeof(uint32_t); - g_assert_cmpint(tables_nr, >, 0); - - /* get the addresses of the tables pointed by rsdt */ - tables = g_new0(uint32_t, tables_nr); - ACPI_READ_ARRAY_PTR(tables, tables_nr, addr); - - checksum = acpi_checksum((uint8_t *)rsdt_table, rsdt_table->length) + - acpi_checksum((uint8_t *)tables, tables_nr * sizeof(uint32_t)); - g_assert(!checksum); - - /* SSDT tables after FADT */ - data->rsdt_tables_addr = tables; - data->rsdt_tables_nr = tables_nr; -} - -static void test_acpi_fadt_table(test_data *data) -{ - AcpiFadtDescriptorRev1 *fadt_table = &data->fadt_table; - uint32_t addr; - - /* FADT table comes first */ - addr = data->rsdt_tables_addr[0]; - ACPI_READ_TABLE_HEADER(fadt_table, addr); - - ACPI_READ_FIELD(fadt_table->firmware_ctrl, addr); - ACPI_READ_FIELD(fadt_table->dsdt, addr); - ACPI_READ_FIELD(fadt_table->model, addr); - ACPI_READ_FIELD(fadt_table->reserved1, addr); - ACPI_READ_FIELD(fadt_table->sci_int, addr); - ACPI_READ_FIELD(fadt_table->smi_cmd, addr); - ACPI_READ_FIELD(fadt_table->acpi_enable, addr); - ACPI_READ_FIELD(fadt_table->acpi_disable, addr); - ACPI_READ_FIELD(fadt_table->S4bios_req, addr); - ACPI_READ_FIELD(fadt_table->reserved2, addr); - ACPI_READ_FIELD(fadt_table->pm1a_evt_blk, addr); - ACPI_READ_FIELD(fadt_table->pm1b_evt_blk, addr); - ACPI_READ_FIELD(fadt_table->pm1a_cnt_blk, addr); - ACPI_READ_FIELD(fadt_table->pm1b_cnt_blk, addr); - ACPI_READ_FIELD(fadt_table->pm2_cnt_blk, addr); - ACPI_READ_FIELD(fadt_table->pm_tmr_blk, addr); - ACPI_READ_FIELD(fadt_table->gpe0_blk, addr); - ACPI_READ_FIELD(fadt_table->gpe1_blk, addr); - ACPI_READ_FIELD(fadt_table->pm1_evt_len, addr); - ACPI_READ_FIELD(fadt_table->pm1_cnt_len, addr); - ACPI_READ_FIELD(fadt_table->pm2_cnt_len, addr); - ACPI_READ_FIELD(fadt_table->pm_tmr_len, addr); - ACPI_READ_FIELD(fadt_table->gpe0_blk_len, addr); - ACPI_READ_FIELD(fadt_table->gpe1_blk_len, addr); - ACPI_READ_FIELD(fadt_table->gpe1_base, addr); - ACPI_READ_FIELD(fadt_table->reserved3, addr); - ACPI_READ_FIELD(fadt_table->plvl2_lat, addr); - ACPI_READ_FIELD(fadt_table->plvl3_lat, addr); - ACPI_READ_FIELD(fadt_table->flush_size, addr); - ACPI_READ_FIELD(fadt_table->flush_stride, addr); - ACPI_READ_FIELD(fadt_table->duty_offset, addr); - ACPI_READ_FIELD(fadt_table->duty_width, addr); - ACPI_READ_FIELD(fadt_table->day_alrm, addr); - ACPI_READ_FIELD(fadt_table->mon_alrm, addr); - ACPI_READ_FIELD(fadt_table->century, addr); - ACPI_READ_FIELD(fadt_table->reserved4, addr); - ACPI_READ_FIELD(fadt_table->reserved4a, addr); - ACPI_READ_FIELD(fadt_table->reserved4b, addr); - ACPI_READ_FIELD(fadt_table->flags, addr); - - ACPI_ASSERT_CMP(fadt_table->signature, "FACP"); - g_assert(!acpi_checksum((uint8_t *)fadt_table, fadt_table->length)); -} - -static void test_acpi_facs_table(test_data *data) -{ - AcpiFacsDescriptorRev1 *facs_table = &data->facs_table; - uint32_t addr = data->fadt_table.firmware_ctrl; - - ACPI_READ_FIELD(facs_table->signature, addr); - ACPI_READ_FIELD(facs_table->length, addr); - ACPI_READ_FIELD(facs_table->hardware_signature, addr); - ACPI_READ_FIELD(facs_table->firmware_waking_vector, addr); - ACPI_READ_FIELD(facs_table->global_lock, addr); - ACPI_READ_FIELD(facs_table->flags, addr); - ACPI_READ_ARRAY(facs_table->resverved3, addr); - - ACPI_ASSERT_CMP(facs_table->signature, "FACS"); -} - -static void test_dst_table(AcpiSdtTable *sdt_table, uint32_t addr) -{ - uint8_t checksum; - - ACPI_READ_TABLE_HEADER(&sdt_table->header, addr); - - sdt_table->aml_len = sdt_table->header.length - sizeof(AcpiTableHeader); - sdt_table->aml = g_malloc0(sdt_table->aml_len); - ACPI_READ_ARRAY_PTR(sdt_table->aml, sdt_table->aml_len, addr); - - checksum = acpi_checksum((uint8_t *)sdt_table, sizeof(AcpiTableHeader)) + - acpi_checksum((uint8_t *)sdt_table->aml, sdt_table->aml_len); - g_assert(!checksum); -} - -static void test_acpi_dsdt_table(test_data *data) -{ - AcpiSdtTable dsdt_table; - uint32_t addr = data->fadt_table.dsdt; - - memset(&dsdt_table, 0, sizeof(dsdt_table)); - data->tables = g_array_new(false, true, sizeof(AcpiSdtTable)); - - test_dst_table(&dsdt_table, addr); - ACPI_ASSERT_CMP(dsdt_table.header.signature, "DSDT"); - - /* Place DSDT first */ - g_array_append_val(data->tables, dsdt_table); -} - -static void test_acpi_tables(test_data *data) -{ - int tables_nr = data->rsdt_tables_nr - 1; /* fadt is first */ - int i; - - for (i = 0; i < tables_nr; i++) { - AcpiSdtTable ssdt_table; - - memset(&ssdt_table, 0 , sizeof(ssdt_table)); - uint32_t addr = data->rsdt_tables_addr[i + 1]; /* fadt is first */ - test_dst_table(&ssdt_table, addr); - g_array_append_val(data->tables, ssdt_table); - } -} - -static void dump_aml_files(test_data *data, bool rebuild) -{ - AcpiSdtTable *sdt; - GError *error = NULL; - gchar *aml_file = NULL; - gint fd; - ssize_t ret; - int i; - - for (i = 0; i < data->tables->len; ++i) { - sdt = &g_array_index(data->tables, AcpiSdtTable, i); - g_assert(sdt->aml); - - if (rebuild) { - uint32_t signature = cpu_to_le32(sdt->header.signature); - aml_file = g_strdup_printf("%s/%s/%.4s", data_dir, data->machine, - (gchar *)&signature); - fd = g_open(aml_file, O_WRONLY|O_TRUNC|O_CREAT, - S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH); - } else { - fd = g_file_open_tmp("aml-XXXXXX", &sdt->aml_file, &error); - g_assert_no_error(error); - } - g_assert(fd >= 0); - - ret = qemu_write_full(fd, sdt, sizeof(AcpiTableHeader)); - g_assert(ret == sizeof(AcpiTableHeader)); - ret = qemu_write_full(fd, sdt->aml, sdt->aml_len); - g_assert(ret == sdt->aml_len); - - close(fd); - - if (aml_file) { - g_free(aml_file); - } - } -} - -static bool compare_signature(AcpiSdtTable *sdt, const char *signature) -{ - return !memcmp(&sdt->header.signature, signature, 4); -} - -static bool load_asl(GArray *sdts, AcpiSdtTable *sdt) -{ - AcpiSdtTable *temp; - GError *error = NULL; - GString *command_line = g_string_new(iasl); - gint fd; - gchar *out, *out_err; - gboolean ret; - int i; - - fd = g_file_open_tmp("asl-XXXXXX.dsl", &sdt->asl_file, &error); - g_assert_no_error(error); - close(fd); - - /* build command line */ - g_string_append_printf(command_line, " -p %s ", sdt->asl_file); - if (compare_signature(sdt, "DSDT") || - compare_signature(sdt, "SSDT")) { - for (i = 0; i < sdts->len; ++i) { - temp = &g_array_index(sdts, AcpiSdtTable, i); - if (compare_signature(temp, "DSDT") || - compare_signature(temp, "SSDT")) { - g_string_append_printf(command_line, "-e %s ", temp->aml_file); - } - } - } - g_string_append_printf(command_line, "-d %s", sdt->aml_file); - - /* pass 'out' and 'out_err' in order to be redirected */ - ret = g_spawn_command_line_sync(command_line->str, &out, &out_err, NULL, &error); - g_assert_no_error(error); - if (ret) { - ret = g_file_get_contents(sdt->asl_file, (gchar **)&sdt->asl, - &sdt->asl_len, &error); - g_assert(ret); - g_assert_no_error(error); - ret = (sdt->asl_len > 0); - } - - g_free(out); - g_free(out_err); - g_string_free(command_line, true); - - return !ret; -} - -#define COMMENT_END "*/" -#define DEF_BLOCK "DefinitionBlock (" -#define BLOCK_NAME_END ".aml" - -static GString *normalize_asl(gchar *asl_code) -{ - GString *asl = g_string_new(asl_code); - gchar *comment, *block_name; - - /* strip comments (different generation days) */ - comment = g_strstr_len(asl->str, asl->len, COMMENT_END); - if (comment) { - asl = g_string_erase(asl, 0, comment + sizeof(COMMENT_END) - asl->str); - } - - /* strip def block name (it has file path in it) */ - if (g_str_has_prefix(asl->str, DEF_BLOCK)) { - block_name = g_strstr_len(asl->str, asl->len, BLOCK_NAME_END); - g_assert(block_name); - asl = g_string_erase(asl, 0, - block_name + sizeof(BLOCK_NAME_END) - asl->str); - } - - return asl; -} - -static GArray *load_expected_aml(test_data *data) -{ - int i; - AcpiSdtTable *sdt; - gchar *aml_file; - GError *error = NULL; - gboolean ret; - - GArray *exp_tables = g_array_new(false, true, sizeof(AcpiSdtTable)); - for (i = 0; i < data->tables->len; ++i) { - AcpiSdtTable exp_sdt; - uint32_t signature; - - sdt = &g_array_index(data->tables, AcpiSdtTable, i); - - memset(&exp_sdt, 0, sizeof(exp_sdt)); - exp_sdt.header.signature = sdt->header.signature; - - signature = cpu_to_le32(sdt->header.signature); - aml_file = g_strdup_printf("%s/%s/%.4s", data_dir, data->machine, - (gchar *)&signature); - exp_sdt.aml_file = aml_file; - g_assert(g_file_test(aml_file, G_FILE_TEST_EXISTS)); - ret = g_file_get_contents(aml_file, &exp_sdt.aml, - &exp_sdt.aml_len, &error); - g_assert(ret); - g_assert_no_error(error); - g_assert(exp_sdt.aml); - g_assert(exp_sdt.aml_len); - - g_array_append_val(exp_tables, exp_sdt); - } - - return exp_tables; -} - -static void test_acpi_asl(test_data *data) -{ - int i; - AcpiSdtTable *sdt, *exp_sdt; - test_data exp_data; - gboolean exp_err, err; - - memset(&exp_data, 0, sizeof(exp_data)); - exp_data.tables = load_expected_aml(data); - dump_aml_files(data, false); - for (i = 0; i < data->tables->len; ++i) { - GString *asl, *exp_asl; - - sdt = &g_array_index(data->tables, AcpiSdtTable, i); - exp_sdt = &g_array_index(exp_data.tables, AcpiSdtTable, i); - - err = load_asl(data->tables, sdt); - asl = normalize_asl(sdt->asl); - - exp_err = load_asl(exp_data.tables, exp_sdt); - exp_asl = normalize_asl(exp_sdt->asl); - - /* TODO: check for warnings */ - g_assert(!err || exp_err); - - if (g_strcmp0(asl->str, exp_asl->str)) { - if (exp_err) { - fprintf(stderr, - "Warning! iasl couldn't parse the expected aml\n"); - } else { - uint32_t signature = cpu_to_le32(exp_sdt->header.signature); - sdt->tmp_files_retain = true; - exp_sdt->tmp_files_retain = true; - fprintf(stderr, - "acpi-test: Warning! %.4s mismatch. " - "Actual [asl:%s, aml:%s], Expected [asl:%s, aml:%s].\n", - (gchar *)&signature, - sdt->asl_file, sdt->aml_file, - exp_sdt->asl_file, exp_sdt->aml_file); - } - } - g_string_free(asl, true); - g_string_free(exp_asl, true); - } - - free_test_data(&exp_data); -} - -static void test_acpi_one(const char *params, test_data *data) -{ - char *args; - uint8_t signature_low; - uint8_t signature_high; - uint16_t signature; - int i; - const char *device = ""; - - if (!g_strcmp0(data->machine, MACHINE_Q35)) { - device = ",id=hd -device ide-hd,drive=hd"; - } - - args = g_strdup_printf("-net none -display none %s -drive file=%s%s,", - params ? params : "", disk, device); - qtest_start(args); - - /* Wait at most 1 minute */ -#define TEST_DELAY (1 * G_USEC_PER_SEC / 10) -#define TEST_CYCLES MAX((60 * G_USEC_PER_SEC / TEST_DELAY), 1) - - /* Poll until code has run and modified memory. Once it has we know BIOS - * initialization is done. TODO: check that IP reached the halt - * instruction. - */ - for (i = 0; i < TEST_CYCLES; ++i) { - signature_low = readb(BOOT_SECTOR_ADDRESS + SIGNATURE_OFFSET); - signature_high = readb(BOOT_SECTOR_ADDRESS + SIGNATURE_OFFSET + 1); - signature = (signature_high << 8) | signature_low; - if (signature == SIGNATURE) { - break; - } - g_usleep(TEST_DELAY); - } - g_assert_cmphex(signature, ==, SIGNATURE); - - test_acpi_rsdp_address(data); - test_acpi_rsdp_table(data); - test_acpi_rsdt_table(data); - test_acpi_fadt_table(data); - test_acpi_facs_table(data); - test_acpi_dsdt_table(data); - test_acpi_tables(data); - - if (iasl) { - if (getenv(ACPI_REBUILD_EXPECTED_AML)) { - dump_aml_files(data, true); - } else { - test_acpi_asl(data); - } - } - - qtest_quit(global_qtest); - g_free(args); -} - -static void test_acpi_tcg(void) -{ - test_data data; - - /* Supplying -machine accel argument overrides the default (qtest). - * This is to make guest actually run. - */ - memset(&data, 0, sizeof(data)); - data.machine = MACHINE_PC; - test_acpi_one("-machine accel=tcg", &data); - free_test_data(&data); - - memset(&data, 0, sizeof(data)); - data.machine = MACHINE_Q35; - test_acpi_one("-machine q35,accel=tcg", &data); - free_test_data(&data); -} - -int main(int argc, char *argv[]) -{ - const char *arch = qtest_get_arch(); - FILE *f = fopen(disk, "w"); - int ret; - fwrite(boot_sector, 1, sizeof boot_sector, f); - fclose(f); - - g_test_init(&argc, &argv, NULL); - - if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { - qtest_add_func("acpi/tcg", test_acpi_tcg); - } - ret = g_test_run(); - unlink(disk); - return ret; -} diff --git a/tests/bios-tables-test.c b/tests/bios-tables-test.c new file mode 100644 index 0000000000..76fbccfa4b --- /dev/null +++ b/tests/bios-tables-test.c @@ -0,0 +1,674 @@ +/* + * Boot order test cases. + * + * Copyright (c) 2013 Red Hat Inc. + * + * Authors: + * Michael S. Tsirkin , + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include +#include +#include +#include +#include "qemu-common.h" +#include "libqtest.h" +#include "qemu/compiler.h" +#include "hw/i386/acpi-defs.h" + +#define MACHINE_PC "pc" +#define MACHINE_Q35 "q35" + +#define ACPI_REBUILD_EXPECTED_AML "TEST_ACPI_REBUILD_AML" + +/* DSDT and SSDTs format */ +typedef struct { + AcpiTableHeader header; + gchar *aml; /* aml bytecode from guest */ + gsize aml_len; + gchar *aml_file; + gchar *asl; /* asl code generated from aml */ + gsize asl_len; + gchar *asl_file; + bool tmp_files_retain; /* do not delete the temp asl/aml */ +} QEMU_PACKED AcpiSdtTable; + +typedef struct { + const char *machine; + uint32_t rsdp_addr; + AcpiRsdpDescriptor rsdp_table; + AcpiRsdtDescriptorRev1 rsdt_table; + AcpiFadtDescriptorRev1 fadt_table; + AcpiFacsDescriptorRev1 facs_table; + uint32_t *rsdt_tables_addr; + int rsdt_tables_nr; + GArray *tables; +} test_data; + +#define LOW(x) ((x) & 0xff) +#define HIGH(x) ((x) >> 8) + +#define SIGNATURE 0xdead +#define SIGNATURE_OFFSET 0x10 +#define BOOT_SECTOR_ADDRESS 0x7c00 + +#define ACPI_READ_FIELD(field, addr) \ + do { \ + switch (sizeof(field)) { \ + case 1: \ + field = readb(addr); \ + break; \ + case 2: \ + field = readw(addr); \ + break; \ + case 4: \ + field = readl(addr); \ + break; \ + case 8: \ + field = readq(addr); \ + break; \ + default: \ + g_assert(false); \ + } \ + addr += sizeof(field); \ + } while (0); + +#define ACPI_READ_ARRAY_PTR(arr, length, addr) \ + do { \ + int idx; \ + for (idx = 0; idx < length; ++idx) { \ + ACPI_READ_FIELD(arr[idx], addr); \ + } \ + } while (0); + +#define ACPI_READ_ARRAY(arr, addr) \ + ACPI_READ_ARRAY_PTR(arr, sizeof(arr)/sizeof(arr[0]), addr) + +#define ACPI_READ_TABLE_HEADER(table, addr) \ + do { \ + ACPI_READ_FIELD((table)->signature, addr); \ + ACPI_READ_FIELD((table)->length, addr); \ + ACPI_READ_FIELD((table)->revision, addr); \ + ACPI_READ_FIELD((table)->checksum, addr); \ + ACPI_READ_ARRAY((table)->oem_id, addr); \ + ACPI_READ_ARRAY((table)->oem_table_id, addr); \ + ACPI_READ_FIELD((table)->oem_revision, addr); \ + ACPI_READ_ARRAY((table)->asl_compiler_id, addr); \ + ACPI_READ_FIELD((table)->asl_compiler_revision, addr); \ + } while (0); + +#define ACPI_ASSERT_CMP(actual, expected) do { \ + uint32_t ACPI_ASSERT_CMP_le = cpu_to_le32(actual); \ + char ACPI_ASSERT_CMP_str[5] = {}; \ + memcpy(ACPI_ASSERT_CMP_str, &ACPI_ASSERT_CMP_le, 4); \ + g_assert_cmpstr(ACPI_ASSERT_CMP_str, ==, expected); \ +} while (0) + +#define ACPI_ASSERT_CMP64(actual, expected) do { \ + uint64_t ACPI_ASSERT_CMP_le = cpu_to_le64(actual); \ + char ACPI_ASSERT_CMP_str[9] = {}; \ + memcpy(ACPI_ASSERT_CMP_str, &ACPI_ASSERT_CMP_le, 8); \ + g_assert_cmpstr(ACPI_ASSERT_CMP_str, ==, expected); \ +} while (0) + +/* Boot sector code: write SIGNATURE into memory, + * then halt. + * Q35 machine requires a minimum 0x7e000 bytes disk. + * (bug or feature?) + */ +static uint8_t boot_sector[0x7e000] = { + /* 7c00: mov $0xdead,%ax */ + [0x00] = 0xb8, + [0x01] = LOW(SIGNATURE), + [0x02] = HIGH(SIGNATURE), + /* 7c03: mov %ax,0x7c10 */ + [0x03] = 0xa3, + [0x04] = LOW(BOOT_SECTOR_ADDRESS + SIGNATURE_OFFSET), + [0x05] = HIGH(BOOT_SECTOR_ADDRESS + SIGNATURE_OFFSET), + /* 7c06: cli */ + [0x06] = 0xfa, + /* 7c07: hlt */ + [0x07] = 0xf4, + /* 7c08: jmp 0x7c07=0x7c0a-3 */ + [0x08] = 0xeb, + [0x09] = LOW(-3), + /* We mov 0xdead here: set value to make debugging easier */ + [SIGNATURE_OFFSET] = LOW(0xface), + [SIGNATURE_OFFSET + 1] = HIGH(0xface), + /* End of boot sector marker */ + [0x1FE] = 0x55, + [0x1FF] = 0xAA, +}; + +static const char *disk = "tests/acpi-test-disk.raw"; +static const char *data_dir = "tests/acpi-test-data"; +#ifdef CONFIG_IASL +static const char *iasl = stringify(CONFIG_IASL); +#else +static const char *iasl; +#endif + +static void free_test_data(test_data *data) +{ + AcpiSdtTable *temp; + int i; + + if (data->rsdt_tables_addr) { + g_free(data->rsdt_tables_addr); + } + + for (i = 0; i < data->tables->len; ++i) { + temp = &g_array_index(data->tables, AcpiSdtTable, i); + if (temp->aml) { + g_free(temp->aml); + } + if (temp->aml_file) { + if (!temp->tmp_files_retain && + g_strstr_len(temp->aml_file, -1, "aml-")) { + unlink(temp->aml_file); + } + g_free(temp->aml_file); + } + if (temp->asl) { + g_free(temp->asl); + } + if (temp->asl_file) { + if (!temp->tmp_files_retain) { + unlink(temp->asl_file); + } + g_free(temp->asl_file); + } + } + + g_array_free(data->tables, false); +} + +static uint8_t acpi_checksum(const uint8_t *data, int len) +{ + int i; + uint8_t sum = 0; + + for (i = 0; i < len; i++) { + sum += data[i]; + } + + return sum; +} + +static void test_acpi_rsdp_address(test_data *data) +{ + uint32_t off; + + /* OK, now find RSDP */ + for (off = 0xf0000; off < 0x100000; off += 0x10) { + uint8_t sig[] = "RSD PTR "; + int i; + + for (i = 0; i < sizeof sig - 1; ++i) { + sig[i] = readb(off + i); + } + + if (!memcmp(sig, "RSD PTR ", sizeof sig)) { + break; + } + } + + g_assert_cmphex(off, <, 0x100000); + data->rsdp_addr = off; +} + +static void test_acpi_rsdp_table(test_data *data) +{ + AcpiRsdpDescriptor *rsdp_table = &data->rsdp_table; + uint32_t addr = data->rsdp_addr; + + ACPI_READ_FIELD(rsdp_table->signature, addr); + ACPI_ASSERT_CMP64(rsdp_table->signature, "RSD PTR "); + + ACPI_READ_FIELD(rsdp_table->checksum, addr); + ACPI_READ_ARRAY(rsdp_table->oem_id, addr); + ACPI_READ_FIELD(rsdp_table->revision, addr); + ACPI_READ_FIELD(rsdp_table->rsdt_physical_address, addr); + ACPI_READ_FIELD(rsdp_table->length, addr); + + /* rsdp checksum is not for the whole table, but for the first 20 bytes */ + g_assert(!acpi_checksum((uint8_t *)rsdp_table, 20)); +} + +static void test_acpi_rsdt_table(test_data *data) +{ + AcpiRsdtDescriptorRev1 *rsdt_table = &data->rsdt_table; + uint32_t addr = data->rsdp_table.rsdt_physical_address; + uint32_t *tables; + int tables_nr; + uint8_t checksum; + + /* read the header */ + ACPI_READ_TABLE_HEADER(rsdt_table, addr); + ACPI_ASSERT_CMP(rsdt_table->signature, "RSDT"); + + /* compute the table entries in rsdt */ + tables_nr = (rsdt_table->length - sizeof(AcpiRsdtDescriptorRev1)) / + sizeof(uint32_t); + g_assert_cmpint(tables_nr, >, 0); + + /* get the addresses of the tables pointed by rsdt */ + tables = g_new0(uint32_t, tables_nr); + ACPI_READ_ARRAY_PTR(tables, tables_nr, addr); + + checksum = acpi_checksum((uint8_t *)rsdt_table, rsdt_table->length) + + acpi_checksum((uint8_t *)tables, tables_nr * sizeof(uint32_t)); + g_assert(!checksum); + + /* SSDT tables after FADT */ + data->rsdt_tables_addr = tables; + data->rsdt_tables_nr = tables_nr; +} + +static void test_acpi_fadt_table(test_data *data) +{ + AcpiFadtDescriptorRev1 *fadt_table = &data->fadt_table; + uint32_t addr; + + /* FADT table comes first */ + addr = data->rsdt_tables_addr[0]; + ACPI_READ_TABLE_HEADER(fadt_table, addr); + + ACPI_READ_FIELD(fadt_table->firmware_ctrl, addr); + ACPI_READ_FIELD(fadt_table->dsdt, addr); + ACPI_READ_FIELD(fadt_table->model, addr); + ACPI_READ_FIELD(fadt_table->reserved1, addr); + ACPI_READ_FIELD(fadt_table->sci_int, addr); + ACPI_READ_FIELD(fadt_table->smi_cmd, addr); + ACPI_READ_FIELD(fadt_table->acpi_enable, addr); + ACPI_READ_FIELD(fadt_table->acpi_disable, addr); + ACPI_READ_FIELD(fadt_table->S4bios_req, addr); + ACPI_READ_FIELD(fadt_table->reserved2, addr); + ACPI_READ_FIELD(fadt_table->pm1a_evt_blk, addr); + ACPI_READ_FIELD(fadt_table->pm1b_evt_blk, addr); + ACPI_READ_FIELD(fadt_table->pm1a_cnt_blk, addr); + ACPI_READ_FIELD(fadt_table->pm1b_cnt_blk, addr); + ACPI_READ_FIELD(fadt_table->pm2_cnt_blk, addr); + ACPI_READ_FIELD(fadt_table->pm_tmr_blk, addr); + ACPI_READ_FIELD(fadt_table->gpe0_blk, addr); + ACPI_READ_FIELD(fadt_table->gpe1_blk, addr); + ACPI_READ_FIELD(fadt_table->pm1_evt_len, addr); + ACPI_READ_FIELD(fadt_table->pm1_cnt_len, addr); + ACPI_READ_FIELD(fadt_table->pm2_cnt_len, addr); + ACPI_READ_FIELD(fadt_table->pm_tmr_len, addr); + ACPI_READ_FIELD(fadt_table->gpe0_blk_len, addr); + ACPI_READ_FIELD(fadt_table->gpe1_blk_len, addr); + ACPI_READ_FIELD(fadt_table->gpe1_base, addr); + ACPI_READ_FIELD(fadt_table->reserved3, addr); + ACPI_READ_FIELD(fadt_table->plvl2_lat, addr); + ACPI_READ_FIELD(fadt_table->plvl3_lat, addr); + ACPI_READ_FIELD(fadt_table->flush_size, addr); + ACPI_READ_FIELD(fadt_table->flush_stride, addr); + ACPI_READ_FIELD(fadt_table->duty_offset, addr); + ACPI_READ_FIELD(fadt_table->duty_width, addr); + ACPI_READ_FIELD(fadt_table->day_alrm, addr); + ACPI_READ_FIELD(fadt_table->mon_alrm, addr); + ACPI_READ_FIELD(fadt_table->century, addr); + ACPI_READ_FIELD(fadt_table->reserved4, addr); + ACPI_READ_FIELD(fadt_table->reserved4a, addr); + ACPI_READ_FIELD(fadt_table->reserved4b, addr); + ACPI_READ_FIELD(fadt_table->flags, addr); + + ACPI_ASSERT_CMP(fadt_table->signature, "FACP"); + g_assert(!acpi_checksum((uint8_t *)fadt_table, fadt_table->length)); +} + +static void test_acpi_facs_table(test_data *data) +{ + AcpiFacsDescriptorRev1 *facs_table = &data->facs_table; + uint32_t addr = data->fadt_table.firmware_ctrl; + + ACPI_READ_FIELD(facs_table->signature, addr); + ACPI_READ_FIELD(facs_table->length, addr); + ACPI_READ_FIELD(facs_table->hardware_signature, addr); + ACPI_READ_FIELD(facs_table->firmware_waking_vector, addr); + ACPI_READ_FIELD(facs_table->global_lock, addr); + ACPI_READ_FIELD(facs_table->flags, addr); + ACPI_READ_ARRAY(facs_table->resverved3, addr); + + ACPI_ASSERT_CMP(facs_table->signature, "FACS"); +} + +static void test_dst_table(AcpiSdtTable *sdt_table, uint32_t addr) +{ + uint8_t checksum; + + ACPI_READ_TABLE_HEADER(&sdt_table->header, addr); + + sdt_table->aml_len = sdt_table->header.length - sizeof(AcpiTableHeader); + sdt_table->aml = g_malloc0(sdt_table->aml_len); + ACPI_READ_ARRAY_PTR(sdt_table->aml, sdt_table->aml_len, addr); + + checksum = acpi_checksum((uint8_t *)sdt_table, sizeof(AcpiTableHeader)) + + acpi_checksum((uint8_t *)sdt_table->aml, sdt_table->aml_len); + g_assert(!checksum); +} + +static void test_acpi_dsdt_table(test_data *data) +{ + AcpiSdtTable dsdt_table; + uint32_t addr = data->fadt_table.dsdt; + + memset(&dsdt_table, 0, sizeof(dsdt_table)); + data->tables = g_array_new(false, true, sizeof(AcpiSdtTable)); + + test_dst_table(&dsdt_table, addr); + ACPI_ASSERT_CMP(dsdt_table.header.signature, "DSDT"); + + /* Place DSDT first */ + g_array_append_val(data->tables, dsdt_table); +} + +static void test_acpi_tables(test_data *data) +{ + int tables_nr = data->rsdt_tables_nr - 1; /* fadt is first */ + int i; + + for (i = 0; i < tables_nr; i++) { + AcpiSdtTable ssdt_table; + + memset(&ssdt_table, 0 , sizeof(ssdt_table)); + uint32_t addr = data->rsdt_tables_addr[i + 1]; /* fadt is first */ + test_dst_table(&ssdt_table, addr); + g_array_append_val(data->tables, ssdt_table); + } +} + +static void dump_aml_files(test_data *data, bool rebuild) +{ + AcpiSdtTable *sdt; + GError *error = NULL; + gchar *aml_file = NULL; + gint fd; + ssize_t ret; + int i; + + for (i = 0; i < data->tables->len; ++i) { + sdt = &g_array_index(data->tables, AcpiSdtTable, i); + g_assert(sdt->aml); + + if (rebuild) { + uint32_t signature = cpu_to_le32(sdt->header.signature); + aml_file = g_strdup_printf("%s/%s/%.4s", data_dir, data->machine, + (gchar *)&signature); + fd = g_open(aml_file, O_WRONLY|O_TRUNC|O_CREAT, + S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH); + } else { + fd = g_file_open_tmp("aml-XXXXXX", &sdt->aml_file, &error); + g_assert_no_error(error); + } + g_assert(fd >= 0); + + ret = qemu_write_full(fd, sdt, sizeof(AcpiTableHeader)); + g_assert(ret == sizeof(AcpiTableHeader)); + ret = qemu_write_full(fd, sdt->aml, sdt->aml_len); + g_assert(ret == sdt->aml_len); + + close(fd); + + if (aml_file) { + g_free(aml_file); + } + } +} + +static bool compare_signature(AcpiSdtTable *sdt, const char *signature) +{ + return !memcmp(&sdt->header.signature, signature, 4); +} + +static bool load_asl(GArray *sdts, AcpiSdtTable *sdt) +{ + AcpiSdtTable *temp; + GError *error = NULL; + GString *command_line = g_string_new(iasl); + gint fd; + gchar *out, *out_err; + gboolean ret; + int i; + + fd = g_file_open_tmp("asl-XXXXXX.dsl", &sdt->asl_file, &error); + g_assert_no_error(error); + close(fd); + + /* build command line */ + g_string_append_printf(command_line, " -p %s ", sdt->asl_file); + if (compare_signature(sdt, "DSDT") || + compare_signature(sdt, "SSDT")) { + for (i = 0; i < sdts->len; ++i) { + temp = &g_array_index(sdts, AcpiSdtTable, i); + if (compare_signature(temp, "DSDT") || + compare_signature(temp, "SSDT")) { + g_string_append_printf(command_line, "-e %s ", temp->aml_file); + } + } + } + g_string_append_printf(command_line, "-d %s", sdt->aml_file); + + /* pass 'out' and 'out_err' in order to be redirected */ + ret = g_spawn_command_line_sync(command_line->str, &out, &out_err, NULL, &error); + g_assert_no_error(error); + if (ret) { + ret = g_file_get_contents(sdt->asl_file, (gchar **)&sdt->asl, + &sdt->asl_len, &error); + g_assert(ret); + g_assert_no_error(error); + ret = (sdt->asl_len > 0); + } + + g_free(out); + g_free(out_err); + g_string_free(command_line, true); + + return !ret; +} + +#define COMMENT_END "*/" +#define DEF_BLOCK "DefinitionBlock (" +#define BLOCK_NAME_END ".aml" + +static GString *normalize_asl(gchar *asl_code) +{ + GString *asl = g_string_new(asl_code); + gchar *comment, *block_name; + + /* strip comments (different generation days) */ + comment = g_strstr_len(asl->str, asl->len, COMMENT_END); + if (comment) { + asl = g_string_erase(asl, 0, comment + sizeof(COMMENT_END) - asl->str); + } + + /* strip def block name (it has file path in it) */ + if (g_str_has_prefix(asl->str, DEF_BLOCK)) { + block_name = g_strstr_len(asl->str, asl->len, BLOCK_NAME_END); + g_assert(block_name); + asl = g_string_erase(asl, 0, + block_name + sizeof(BLOCK_NAME_END) - asl->str); + } + + return asl; +} + +static GArray *load_expected_aml(test_data *data) +{ + int i; + AcpiSdtTable *sdt; + gchar *aml_file; + GError *error = NULL; + gboolean ret; + + GArray *exp_tables = g_array_new(false, true, sizeof(AcpiSdtTable)); + for (i = 0; i < data->tables->len; ++i) { + AcpiSdtTable exp_sdt; + uint32_t signature; + + sdt = &g_array_index(data->tables, AcpiSdtTable, i); + + memset(&exp_sdt, 0, sizeof(exp_sdt)); + exp_sdt.header.signature = sdt->header.signature; + + signature = cpu_to_le32(sdt->header.signature); + aml_file = g_strdup_printf("%s/%s/%.4s", data_dir, data->machine, + (gchar *)&signature); + exp_sdt.aml_file = aml_file; + g_assert(g_file_test(aml_file, G_FILE_TEST_EXISTS)); + ret = g_file_get_contents(aml_file, &exp_sdt.aml, + &exp_sdt.aml_len, &error); + g_assert(ret); + g_assert_no_error(error); + g_assert(exp_sdt.aml); + g_assert(exp_sdt.aml_len); + + g_array_append_val(exp_tables, exp_sdt); + } + + return exp_tables; +} + +static void test_acpi_asl(test_data *data) +{ + int i; + AcpiSdtTable *sdt, *exp_sdt; + test_data exp_data; + gboolean exp_err, err; + + memset(&exp_data, 0, sizeof(exp_data)); + exp_data.tables = load_expected_aml(data); + dump_aml_files(data, false); + for (i = 0; i < data->tables->len; ++i) { + GString *asl, *exp_asl; + + sdt = &g_array_index(data->tables, AcpiSdtTable, i); + exp_sdt = &g_array_index(exp_data.tables, AcpiSdtTable, i); + + err = load_asl(data->tables, sdt); + asl = normalize_asl(sdt->asl); + + exp_err = load_asl(exp_data.tables, exp_sdt); + exp_asl = normalize_asl(exp_sdt->asl); + + /* TODO: check for warnings */ + g_assert(!err || exp_err); + + if (g_strcmp0(asl->str, exp_asl->str)) { + if (exp_err) { + fprintf(stderr, + "Warning! iasl couldn't parse the expected aml\n"); + } else { + uint32_t signature = cpu_to_le32(exp_sdt->header.signature); + sdt->tmp_files_retain = true; + exp_sdt->tmp_files_retain = true; + fprintf(stderr, + "acpi-test: Warning! %.4s mismatch. " + "Actual [asl:%s, aml:%s], Expected [asl:%s, aml:%s].\n", + (gchar *)&signature, + sdt->asl_file, sdt->aml_file, + exp_sdt->asl_file, exp_sdt->aml_file); + } + } + g_string_free(asl, true); + g_string_free(exp_asl, true); + } + + free_test_data(&exp_data); +} + +static void test_acpi_one(const char *params, test_data *data) +{ + char *args; + uint8_t signature_low; + uint8_t signature_high; + uint16_t signature; + int i; + const char *device = ""; + + if (!g_strcmp0(data->machine, MACHINE_Q35)) { + device = ",id=hd -device ide-hd,drive=hd"; + } + + args = g_strdup_printf("-net none -display none %s -drive file=%s%s,", + params ? params : "", disk, device); + qtest_start(args); + + /* Wait at most 1 minute */ +#define TEST_DELAY (1 * G_USEC_PER_SEC / 10) +#define TEST_CYCLES MAX((60 * G_USEC_PER_SEC / TEST_DELAY), 1) + + /* Poll until code has run and modified memory. Once it has we know BIOS + * initialization is done. TODO: check that IP reached the halt + * instruction. + */ + for (i = 0; i < TEST_CYCLES; ++i) { + signature_low = readb(BOOT_SECTOR_ADDRESS + SIGNATURE_OFFSET); + signature_high = readb(BOOT_SECTOR_ADDRESS + SIGNATURE_OFFSET + 1); + signature = (signature_high << 8) | signature_low; + if (signature == SIGNATURE) { + break; + } + g_usleep(TEST_DELAY); + } + g_assert_cmphex(signature, ==, SIGNATURE); + + test_acpi_rsdp_address(data); + test_acpi_rsdp_table(data); + test_acpi_rsdt_table(data); + test_acpi_fadt_table(data); + test_acpi_facs_table(data); + test_acpi_dsdt_table(data); + test_acpi_tables(data); + + if (iasl) { + if (getenv(ACPI_REBUILD_EXPECTED_AML)) { + dump_aml_files(data, true); + } else { + test_acpi_asl(data); + } + } + + qtest_quit(global_qtest); + g_free(args); +} + +static void test_acpi_tcg(void) +{ + test_data data; + + /* Supplying -machine accel argument overrides the default (qtest). + * This is to make guest actually run. + */ + memset(&data, 0, sizeof(data)); + data.machine = MACHINE_PC; + test_acpi_one("-machine accel=tcg", &data); + free_test_data(&data); + + memset(&data, 0, sizeof(data)); + data.machine = MACHINE_Q35; + test_acpi_one("-machine q35,accel=tcg", &data); + free_test_data(&data); +} + +int main(int argc, char *argv[]) +{ + const char *arch = qtest_get_arch(); + FILE *f = fopen(disk, "w"); + int ret; + fwrite(boot_sector, 1, sizeof boot_sector, f); + fclose(f); + + g_test_init(&argc, &argv, NULL); + + if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { + qtest_add_func("acpi/tcg", test_acpi_tcg); + } + ret = g_test_run(); + unlink(disk); + return ret; +} -- cgit v1.2.3-55-g7522 From eb386aaccc4a728f5429de06ef7e6c698603d173 Mon Sep 17 00:00:00 2001 From: Gabriel L. Somlo Date: Tue, 27 May 2014 15:03:15 -0400 Subject: tests: add smbios testing Add tests to find and verify the smbios entry point structure, and to walk and perform checks on the actual smbios tables. Suggested-by: Michael S. Tsirkin Signed-off-by: Gabriel Somlo Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/bios-tables-test.c | 126 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) diff --git a/tests/bios-tables-test.c b/tests/bios-tables-test.c index 76fbccfa4b..62771f7608 100644 --- a/tests/bios-tables-test.c +++ b/tests/bios-tables-test.c @@ -18,6 +18,8 @@ #include "libqtest.h" #include "qemu/compiler.h" #include "hw/i386/acpi-defs.h" +#include "hw/i386/smbios.h" +#include "qemu/bitmap.h" #define MACHINE_PC "pc" #define MACHINE_Q35 "q35" @@ -46,6 +48,8 @@ typedef struct { uint32_t *rsdt_tables_addr; int rsdt_tables_nr; GArray *tables; + uint32_t smbios_ep_addr; + struct smbios_entry_point smbios_ep_table; } test_data; #define LOW(x) ((x) & 0xff) @@ -581,6 +585,124 @@ static void test_acpi_asl(test_data *data) free_test_data(&exp_data); } +static void test_smbios_ep_address(test_data *data) +{ + uint32_t off; + + /* find smbios entry point structure */ + for (off = 0xf0000; off < 0x100000; off += 0x10) { + uint8_t sig[] = "_SM_"; + int i; + + for (i = 0; i < sizeof sig - 1; ++i) { + sig[i] = readb(off + i); + } + + if (!memcmp(sig, "_SM_", sizeof sig)) { + break; + } + } + + g_assert_cmphex(off, <, 0x100000); + data->smbios_ep_addr = off; +} + +static void test_smbios_ep_table(test_data *data) +{ + struct smbios_entry_point *ep_table = &data->smbios_ep_table; + uint32_t addr = data->smbios_ep_addr; + + ACPI_READ_ARRAY(ep_table->anchor_string, addr); + g_assert(!memcmp(ep_table->anchor_string, "_SM_", 4)); + ACPI_READ_FIELD(ep_table->checksum, addr); + ACPI_READ_FIELD(ep_table->length, addr); + ACPI_READ_FIELD(ep_table->smbios_major_version, addr); + ACPI_READ_FIELD(ep_table->smbios_minor_version, addr); + ACPI_READ_FIELD(ep_table->max_structure_size, addr); + ACPI_READ_FIELD(ep_table->entry_point_revision, addr); + ACPI_READ_ARRAY(ep_table->formatted_area, addr); + ACPI_READ_ARRAY(ep_table->intermediate_anchor_string, addr); + g_assert(!memcmp(ep_table->intermediate_anchor_string, "_DMI_", 5)); + ACPI_READ_FIELD(ep_table->intermediate_checksum, addr); + ACPI_READ_FIELD(ep_table->structure_table_length, addr); + g_assert_cmpuint(ep_table->structure_table_length, >, 0); + ACPI_READ_FIELD(ep_table->structure_table_address, addr); + ACPI_READ_FIELD(ep_table->number_of_structures, addr); + g_assert_cmpuint(ep_table->number_of_structures, >, 0); + ACPI_READ_FIELD(ep_table->smbios_bcd_revision, addr); + g_assert(!acpi_checksum((uint8_t *)ep_table, sizeof *ep_table)); + g_assert(!acpi_checksum((uint8_t *)ep_table + 0x10, + sizeof *ep_table - 0x10)); +} + +static inline bool smbios_single_instance(uint8_t type) +{ + switch (type) { + case 0: + case 1: + case 2: + case 3: + case 16: + case 32: + case 127: + return true; + default: + return false; + } +} + +static void test_smbios_structs(test_data *data) +{ + DECLARE_BITMAP(struct_bitmap, SMBIOS_MAX_TYPE+1) = { 0 }; + struct smbios_entry_point *ep_table = &data->smbios_ep_table; + uint32_t addr = ep_table->structure_table_address; + int i, len, max_len = 0; + uint8_t type, prv, crt; + uint8_t required_struct_types[] = {0, 1, 3, 4, 16, 17, 19, 32, 127}; + + /* walk the smbios tables */ + for (i = 0; i < ep_table->number_of_structures; i++) { + + /* grab type and formatted area length from struct header */ + type = readb(addr); + g_assert_cmpuint(type, <=, SMBIOS_MAX_TYPE); + len = readb(addr + 1); + + /* single-instance structs must not have been encountered before */ + if (smbios_single_instance(type)) { + g_assert(!test_bit(type, struct_bitmap)); + } + set_bit(type, struct_bitmap); + + /* seek to end of unformatted string area of this struct ("\0\0") */ + prv = crt = 1; + while (prv || crt) { + prv = crt; + crt = readb(addr + len); + len++; + } + + /* keep track of max. struct size */ + if (max_len < len) { + max_len = len; + g_assert_cmpuint(max_len, <=, ep_table->max_structure_size); + } + + /* start of next structure */ + addr += len; + } + + /* total table length and max struct size must match entry point values */ + g_assert_cmpuint(ep_table->structure_table_length, ==, + addr - ep_table->structure_table_address); + g_assert_cmpuint(ep_table->max_structure_size, ==, max_len); + + /* required struct types must all be present */ + for (i = 0; i < ARRAY_SIZE(required_struct_types); i++) { + g_assert(test_bit(required_struct_types[i], struct_bitmap)); + } +} + static void test_acpi_one(const char *params, test_data *data) { char *args; @@ -633,6 +755,10 @@ static void test_acpi_one(const char *params, test_data *data) } } + test_smbios_ep_address(data); + test_smbios_ep_table(data); + test_smbios_structs(data); + qtest_quit(global_qtest); g_free(args); } -- cgit v1.2.3-55-g7522 From 9f9260a3be3b7fbd0006a58773abdb164bf9f220 Mon Sep 17 00:00:00 2001 From: Don Slutz Date: Mon, 5 May 2014 14:03:06 -0400 Subject: qdev: Display warning about unused -global This can help a user understand why -global was ignored. For example: with "-vga cirrus"; "-global vga.vgamem_mb=16" is just ignored when "-global cirrus-vga.vgamem_mb=16" is not. This is currently clear when the wrong property is provided: out/x86_64-softmmu/qemu-system-x86_64 -global cirrus-vga.vram_size_mb=16 -monitor pty -vga cirrus char device redirected to /dev/pts/20 (label compat_monitor0) qemu-system-x86_64: Property '.vram_size_mb' not found Aborted (core dumped) vs out/x86_64-softmmu/qemu-system-x86_64 -global vga.vram_size_mb=16 -monitor pty -vga cirrus char device redirected to /dev/pts/20 (label compat_monitor0) VNC server running on `::1:5900' ^Cqemu: terminating on signal 2 Signed-off-by: Don Slutz Acked-by: Michael S. Tsirkin --- hw/core/qdev-properties-system.c | 16 ++++++++++++++++ hw/core/qdev-properties.c | 18 ++++++++++++++++++ include/hw/qdev-core.h | 8 ++++++++ include/hw/qdev-properties.h | 1 + vl.c | 2 ++ 5 files changed, 45 insertions(+) diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c index 404cf1843d..de433b2e38 100644 --- a/hw/core/qdev-properties-system.c +++ b/hw/core/qdev-properties-system.c @@ -439,11 +439,27 @@ PropertyInfo qdev_prop_iothread = { static int qdev_add_one_global(QemuOpts *opts, void *opaque) { GlobalProperty *g; + ObjectClass *oc; g = g_malloc0(sizeof(*g)); g->driver = qemu_opt_get(opts, "driver"); g->property = qemu_opt_get(opts, "property"); g->value = qemu_opt_get(opts, "value"); + oc = object_class_by_name(g->driver); + if (oc) { + DeviceClass *dc = DEVICE_CLASS(oc); + + if (dc->hotpluggable) { + /* If hotpluggable then skip not_used checking. */ + g->not_used = false; + } else { + /* Maybe a typo. */ + g->not_used = true; + } + } else { + /* Maybe a typo. */ + g->not_used = true; + } qdev_prop_register_global(g); return 0; } diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c index d8cb5408c3..3d12560f43 100644 --- a/hw/core/qdev-properties.c +++ b/hw/core/qdev-properties.c @@ -955,6 +955,23 @@ void qdev_prop_register_global_list(GlobalProperty *props) } } +int qdev_prop_check_global(void) +{ + GlobalProperty *prop; + int ret = 0; + + QTAILQ_FOREACH(prop, &global_props, next) { + if (!prop->not_used) { + continue; + } + ret = 1; + error_report("Warning: \"-global %s.%s=%s\" not used", + prop->driver, prop->property, prop->value); + + } + return ret; +} + void qdev_prop_set_globals_for_type(DeviceState *dev, const char *typename, Error **errp) { @@ -966,6 +983,7 @@ void qdev_prop_set_globals_for_type(DeviceState *dev, const char *typename, if (strcmp(typename, prop->driver) != 0) { continue; } + prop->not_used = false; object_property_parse(OBJECT(dev), prop->value, prop->property, &err); if (err != NULL) { error_propagate(errp, err); diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h index dbe473c344..bbed82951f 100644 --- a/include/hw/qdev-core.h +++ b/include/hw/qdev-core.h @@ -231,10 +231,18 @@ struct PropertyInfo { ObjectPropertyRelease *release; }; +/** + * GlobalProperty: + * @not_used: Track use of a global property. Defaults to false in all C99 + * struct initializations. + * + * This prevents reports of .compat_props when they are not used. + */ typedef struct GlobalProperty { const char *driver; const char *property; const char *value; + bool not_used; QTAILQ_ENTRY(GlobalProperty) next; } GlobalProperty; diff --git a/include/hw/qdev-properties.h b/include/hw/qdev-properties.h index c46e908d71..c962b6bbaa 100644 --- a/include/hw/qdev-properties.h +++ b/include/hw/qdev-properties.h @@ -180,6 +180,7 @@ void qdev_prop_set_ptr(DeviceState *dev, const char *name, void *value); void qdev_prop_register_global(GlobalProperty *prop); void qdev_prop_register_global_list(GlobalProperty *props); +int qdev_prop_check_global(void); void qdev_prop_set_globals(DeviceState *dev, Error **errp); void qdev_prop_set_globals_for_type(DeviceState *dev, const char *typename, Error **errp); diff --git a/vl.c b/vl.c index 709d8cda8d..bda88f7b01 100644 --- a/vl.c +++ b/vl.c @@ -4532,6 +4532,8 @@ int main(int argc, char **argv, char **envp) } } + qdev_prop_check_global(); + if (incoming) { Error *local_err = NULL; qemu_start_incoming_migration(incoming, &local_err); -- cgit v1.2.3-55-g7522 From 711e2f1e9ecad845e142fdddbbf1e8037bce902b Mon Sep 17 00:00:00 2001 From: Don Slutz Date: Mon, 5 May 2014 14:03:07 -0400 Subject: qdev: Add test of qdev_prop_check_global This will generate a warning from "make check": ... GTESTER tests/test-qdev-global-props Warning: "-global dynamic-prop-type-bad.prop3=103" not used GTESTER tests/check-qom-interface ... If the warning is not generated, the test will fail. Signed-off-by: Don Slutz Acked-by: Michael S. Tsirkin --- tests/test-qdev-global-props.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/test-qdev-global-props.c b/tests/test-qdev-global-props.c index e4ad173d72..2bef04c76f 100644 --- a/tests/test-qdev-global-props.c +++ b/tests/test-qdev-global-props.c @@ -150,8 +150,10 @@ static void test_dynamic_globalprop(void) static GlobalProperty props[] = { { TYPE_DYNAMIC_PROPS, "prop1", "101" }, { TYPE_DYNAMIC_PROPS, "prop2", "102" }, + { TYPE_DYNAMIC_PROPS"-bad", "prop3", "103", true }, {} }; + int all_used; qdev_prop_register_global_list(props); @@ -160,6 +162,8 @@ static void test_dynamic_globalprop(void) g_assert_cmpuint(mt->prop1, ==, 101); g_assert_cmpuint(mt->prop2, ==, 102); + all_used = qdev_prop_check_global(); + g_assert_cmpuint(all_used, ==, 1); } int main(int argc, char **argv) -- cgit v1.2.3-55-g7522