diff options
90 files changed, 2129 insertions, 501 deletions
diff --git a/.gitlab-ci.d/edk2.yml b/.gitlab-ci.d/edk2.yml index 62497ba47f..13d0f8b019 100644 --- a/.gitlab-ci.d/edk2.yml +++ b/.gitlab-ci.d/edk2.yml @@ -50,7 +50,11 @@ build-edk2: GIT_DEPTH: 3 script: # Clone the required submodules and build EDK2 - git submodule update --init roms/edk2 - - git -C roms/edk2 submodule update --init + - git -C roms/edk2 submodule update --init -- + ArmPkg/Library/ArmSoftFloatLib/berkeley-softfloat-3 + BaseTools/Source/C/BrotliCompress/brotli + CryptoPkg/Library/OpensslLib/openssl + MdeModulePkg/Library/BrotliCustomDecompressLib/brotli - export JOBS=$(($(getconf _NPROCESSORS_ONLN) + 1)) - echo "=== Using ${JOBS} simultaneous jobs ===" - make -j${JOBS} -C roms efi 2>&1 1>edk2-stdout.log | tee -a edk2-stderr.log >&2 diff --git a/MAINTAINERS b/MAINTAINERS index 9e9f489a41..4e77d03651 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1245,6 +1245,7 @@ Bamboo L: qemu-ppc@nongnu.org S: Orphan F: hw/ppc/ppc440_bamboo.c +F: tests/acceptance/ppc_bamboo.py e500 L: qemu-ppc@nongnu.org diff --git a/docs/system/device-emulation.rst b/docs/system/device-emulation.rst index 7afcfd8064..19944f526c 100644 --- a/docs/system/device-emulation.rst +++ b/docs/system/device-emulation.rst @@ -88,3 +88,4 @@ Emulated Devices devices/usb.rst devices/vhost-user.rst devices/virtio-pmem.rst + devices/vhost-user-rng.rst diff --git a/docs/system/devices/vhost-user-rng.rst b/docs/system/devices/vhost-user-rng.rst new file mode 100644 index 0000000000..a145d4105c --- /dev/null +++ b/docs/system/devices/vhost-user-rng.rst @@ -0,0 +1,39 @@ +QEMU vhost-user-rng - RNG emulation +=================================== + +Background +---------- + +What follows builds on the material presented in vhost-user.rst - it should +be reviewed before moving forward with the content in this file. + +Description +----------- + +The vhost-user-rng device implementation was designed to work with a random +number generator daemon such as the one found in the vhost-device crate of +the rust-vmm project available on github [1]. + +[1]. https://github.com/rust-vmm/vhost-device + +Examples +-------- + +The daemon should be started first: + +:: + + host# vhost-device-rng --socket-path=rng.sock -c 1 -m 512 -p 1000 + +The QEMU invocation needs to create a chardev socket the device can +use to communicate as well as share the guests memory over a memfd. + +:: + + host# qemu-system \ + -chardev socket,path=$(PATH)/rng.sock,id=rng0 \ + -device vhost-user-rng-pci,chardev=rng0 \ + -m 4096 \ + -object memory-backend-file,id=mem,size=4G,mem-path=/dev/shm,share=on \ + -numa node,memdev=mem \ + ... diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c index 76af0ebaf9..b3b3310df3 100644 --- a/hw/acpi/aml-build.c +++ b/hw/acpi/aml-build.c @@ -1964,6 +1964,95 @@ void build_slit(GArray *table_data, BIOSLinker *linker, MachineState *ms, acpi_table_end(linker, &table); } +/* + * ACPI spec, Revision 6.3 + * 5.2.29.1 Processor hierarchy node structure (Type 0) + */ +static void build_processor_hierarchy_node(GArray *tbl, uint32_t flags, + uint32_t parent, uint32_t id, + uint32_t *priv_rsrc, + uint32_t priv_num) +{ + int i; + + build_append_byte(tbl, 0); /* Type 0 - processor */ + build_append_byte(tbl, 20 + priv_num * 4); /* Length */ + build_append_int_noprefix(tbl, 0, 2); /* Reserved */ + build_append_int_noprefix(tbl, flags, 4); /* Flags */ + build_append_int_noprefix(tbl, parent, 4); /* Parent */ + build_append_int_noprefix(tbl, id, 4); /* ACPI Processor ID */ + + /* Number of private resources */ + build_append_int_noprefix(tbl, priv_num, 4); + + /* Private resources[N] */ + if (priv_num > 0) { + assert(priv_rsrc); + for (i = 0; i < priv_num; i++) { + build_append_int_noprefix(tbl, priv_rsrc[i], 4); + } + } +} + +/* + * ACPI spec, Revision 6.3 + * 5.2.29 Processor Properties Topology Table (PPTT) + */ +void build_pptt(GArray *table_data, BIOSLinker *linker, MachineState *ms, + const char *oem_id, const char *oem_table_id) +{ + int pptt_start = table_data->len; + int uid = 0; + int socket; + AcpiTable table = { .sig = "PPTT", .rev = 2, + .oem_id = oem_id, .oem_table_id = oem_table_id }; + + acpi_table_begin(&table, table_data); + + for (socket = 0; socket < ms->smp.sockets; socket++) { + uint32_t socket_offset = table_data->len - pptt_start; + int core; + + build_processor_hierarchy_node( + table_data, + /* + * Physical package - represents the boundary + * of a physical package + */ + (1 << 0), + 0, socket, NULL, 0); + + for (core = 0; core < ms->smp.cores; core++) { + uint32_t core_offset = table_data->len - pptt_start; + int thread; + + if (ms->smp.threads > 1) { + build_processor_hierarchy_node( + table_data, + (0 << 0), /* not a physical package */ + socket_offset, core, NULL, 0); + + for (thread = 0; thread < ms->smp.threads; thread++) { + build_processor_hierarchy_node( + table_data, + (1 << 1) | /* ACPI Processor ID valid */ + (1 << 2) | /* Processor is a Thread */ + (1 << 3), /* Node is a Leaf */ + core_offset, uid++, NULL, 0); + } + } else { + build_processor_hierarchy_node( + table_data, + (1 << 1) | /* ACPI Processor ID valid */ + (1 << 3), /* Node is a Leaf */ + socket_offset, uid++, NULL, 0); + } + } + } + + acpi_table_end(linker, &table); +} + /* build rev1/rev3/rev5.1 FADT */ void build_fadt(GArray *tbl, BIOSLinker *linker, const AcpiFadtData *f, const char *oem_id, const char *oem_table_id) diff --git a/hw/arm/boot.c b/hw/arm/boot.c index 57efb61ee4..74ad397b1f 100644 --- a/hw/arm/boot.c +++ b/hw/arm/boot.c @@ -599,10 +599,23 @@ int arm_load_dtb(hwaddr addr, const struct arm_boot_info *binfo, } g_strfreev(node_path); + /* + * We drop all the memory nodes which correspond to empty NUMA nodes + * from the device tree, because the Linux NUMA binding document + * states they should not be generated. Linux will get the NUMA node + * IDs of the empty NUMA nodes from the distance map if they are needed. + * This means QEMU users may be obliged to provide command lines which + * configure distance maps when the empty NUMA node IDs are needed and + * Linux's default distance map isn't sufficient. + */ if (ms->numa_state != NULL && ms->numa_state->num_nodes > 0) { mem_base = binfo->loader_start; for (i = 0; i < ms->numa_state->num_nodes; i++) { mem_len = ms->numa_state->nodes[i].node_mem; + if (!mem_len) { + continue; + } + rc = fdt_add_memory_node(fdt, acells, mem_base, scells, mem_len, i); if (rc < 0) { diff --git a/hw/arm/sbsa-ref.c b/hw/arm/sbsa-ref.c index 509c5f09b4..358714bd3e 100644 --- a/hw/arm/sbsa-ref.c +++ b/hw/arm/sbsa-ref.c @@ -670,7 +670,7 @@ static void sbsa_ref_init(MachineState *machine) int n, sbsa_max_cpus; if (!cpu_type_valid(machine->cpu_type)) { - error_report("mach-virt: CPU type %s not supported", machine->cpu_type); + error_report("sbsa-ref: CPU type %s not supported", machine->cpu_type); exit(1); } diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index 6cec97352b..674f902652 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -241,19 +241,20 @@ static void acpi_dsdt_add_tpm(Aml *scope, VirtMachineState *vms) #endif #define ID_MAPPING_ENTRY_SIZE 20 -#define SMMU_V3_ENTRY_SIZE 60 -#define ROOT_COMPLEX_ENTRY_SIZE 32 +#define SMMU_V3_ENTRY_SIZE 68 +#define ROOT_COMPLEX_ENTRY_SIZE 36 #define IORT_NODE_OFFSET 48 static void build_iort_id_mapping(GArray *table_data, uint32_t input_base, uint32_t id_count, uint32_t out_ref) { - /* Identity RID mapping covering the whole input RID range */ + /* Table 4 ID mapping format */ build_append_int_noprefix(table_data, input_base, 4); /* Input base */ build_append_int_noprefix(table_data, id_count, 4); /* Number of IDs */ build_append_int_noprefix(table_data, input_base, 4); /* Output base */ build_append_int_noprefix(table_data, out_ref, 4); /* Output Reference */ - build_append_int_noprefix(table_data, 0, 4); /* Flags */ + /* Flags */ + build_append_int_noprefix(table_data, 0 /* Single mapping (disabled) */, 4); } struct AcpiIortIdMapping { @@ -298,7 +299,7 @@ static int iort_idmap_compare(gconstpointer a, gconstpointer b) /* * Input Output Remapping Table (IORT) * Conforms to "IO Remapping Table System Software on ARM Platforms", - * Document number: ARM DEN 0049B, October 2015 + * Document number: ARM DEN 0049E.b, Feb 2021 */ static void build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) @@ -307,10 +308,11 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) const uint32_t iort_node_offset = IORT_NODE_OFFSET; size_t node_size, smmu_offset = 0; AcpiIortIdMapping *idmap; + uint32_t id = 0; GArray *smmu_idmaps = g_array_new(false, true, sizeof(AcpiIortIdMapping)); GArray *its_idmaps = g_array_new(false, true, sizeof(AcpiIortIdMapping)); - AcpiTable table = { .sig = "IORT", .rev = 0, .oem_id = vms->oem_id, + AcpiTable table = { .sig = "IORT", .rev = 3, .oem_id = vms->oem_id, .oem_table_id = vms->oem_table_id }; /* Table 2 The IORT */ acpi_table_begin(&table, table_data); @@ -358,12 +360,12 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) build_append_int_noprefix(table_data, IORT_NODE_OFFSET, 4); build_append_int_noprefix(table_data, 0, 4); /* Reserved */ - /* 3.1.1.3 ITS group node */ + /* Table 12 ITS Group Format */ build_append_int_noprefix(table_data, 0 /* ITS Group */, 1); /* Type */ node_size = 20 /* fixed header size */ + 4 /* 1 GIC ITS Identifier */; build_append_int_noprefix(table_data, node_size, 2); /* Length */ - build_append_int_noprefix(table_data, 0, 1); /* Revision */ - build_append_int_noprefix(table_data, 0, 4); /* Reserved */ + build_append_int_noprefix(table_data, 1, 1); /* Revision */ + build_append_int_noprefix(table_data, id++, 4); /* Identifier */ build_append_int_noprefix(table_data, 0, 4); /* Number of ID mappings */ build_append_int_noprefix(table_data, 0, 4); /* Reference to ID Array */ build_append_int_noprefix(table_data, 1, 4); /* Number of ITSs */ @@ -374,19 +376,19 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) int irq = vms->irqmap[VIRT_SMMU] + ARM_SPI_BASE; smmu_offset = table_data->len - table.table_offset; - /* 3.1.1.2 SMMUv3 */ + /* Table 9 SMMUv3 Format */ build_append_int_noprefix(table_data, 4 /* SMMUv3 */, 1); /* Type */ node_size = SMMU_V3_ENTRY_SIZE + ID_MAPPING_ENTRY_SIZE; build_append_int_noprefix(table_data, node_size, 2); /* Length */ - build_append_int_noprefix(table_data, 0, 1); /* Revision */ - build_append_int_noprefix(table_data, 0, 4); /* Reserved */ + build_append_int_noprefix(table_data, 4, 1); /* Revision */ + build_append_int_noprefix(table_data, id++, 4); /* Identifier */ build_append_int_noprefix(table_data, 1, 4); /* Number of ID mappings */ /* Reference to ID Array */ build_append_int_noprefix(table_data, SMMU_V3_ENTRY_SIZE, 4); /* Base address */ build_append_int_noprefix(table_data, vms->memmap[VIRT_SMMU].base, 8); /* Flags */ - build_append_int_noprefix(table_data, 1 /* COHACC OverrideNote */, 4); + build_append_int_noprefix(table_data, 1 /* COHACC Override */, 4); build_append_int_noprefix(table_data, 0, 4); /* Reserved */ build_append_int_noprefix(table_data, 0, 8); /* VATOS address */ /* Model */ @@ -395,35 +397,43 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) build_append_int_noprefix(table_data, irq + 1, 4); /* PRI */ build_append_int_noprefix(table_data, irq + 3, 4); /* GERR */ build_append_int_noprefix(table_data, irq + 2, 4); /* Sync */ + build_append_int_noprefix(table_data, 0, 4); /* Proximity domain */ + /* DeviceID mapping index (ignored since interrupts are GSIV based) */ + build_append_int_noprefix(table_data, 0, 4); /* output IORT node is the ITS group node (the first node) */ build_iort_id_mapping(table_data, 0, 0xFFFF, IORT_NODE_OFFSET); } - /* Table 16 Root Complex Node */ + /* Table 17 Root Complex Node */ build_append_int_noprefix(table_data, 2 /* Root complex */, 1); /* Type */ node_size = ROOT_COMPLEX_ENTRY_SIZE + ID_MAPPING_ENTRY_SIZE * rc_mapping_count; build_append_int_noprefix(table_data, node_size, 2); /* Length */ - build_append_int_noprefix(table_data, 0, 1); /* Revision */ - build_append_int_noprefix(table_data, 0, 4); /* Reserved */ + build_append_int_noprefix(table_data, 3, 1); /* Revision */ + build_append_int_noprefix(table_data, id++, 4); /* Identifier */ /* Number of ID mappings */ build_append_int_noprefix(table_data, rc_mapping_count, 4); /* Reference to ID Array */ build_append_int_noprefix(table_data, ROOT_COMPLEX_ENTRY_SIZE, 4); - /* Table 13 Memory access properties */ + /* Table 14 Memory access properties */ /* CCA: Cache Coherent Attribute */ build_append_int_noprefix(table_data, 1 /* fully coherent */, 4); build_append_int_noprefix(table_data, 0, 1); /* AH: Note Allocation Hints */ build_append_int_noprefix(table_data, 0, 2); /* Reserved */ - /* MAF: Note Memory Access Flags */ - build_append_int_noprefix(table_data, 0x3 /* CCA = CPM = DCAS = 1 */, 1); + /* Table 15 Memory Access Flags */ + build_append_int_noprefix(table_data, 0x3 /* CCA = CPM = DACS = 1 */, 1); build_append_int_noprefix(table_data, 0, 4); /* ATS Attribute */ /* MCFG pci_segment */ build_append_int_noprefix(table_data, 0, 4); /* PCI Segment number */ + /* Memory address size limit */ + build_append_int_noprefix(table_data, 64, 1); + + build_append_int_noprefix(table_data, 0, 3); /* Reserved */ + /* Output Reference */ if (vms->iommu == VIRT_IOMMU_SMMUV3) { AcpiIortIdMapping *range; @@ -616,6 +626,64 @@ build_gtdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) acpi_table_end(linker, &table); } +/* Debug Port Table 2 (DBG2) */ +static void +build_dbg2(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) +{ + AcpiTable table = { .sig = "DBG2", .rev = 0, .oem_id = vms->oem_id, + .oem_table_id = vms->oem_table_id }; + int dbg2devicelength; + const char name[] = "COM0"; + const int namespace_length = sizeof(name); + + acpi_table_begin(&table, table_data); + + dbg2devicelength = 22 + /* BaseAddressRegister[] offset */ + 12 + /* BaseAddressRegister[] */ + 4 + /* AddressSize[] */ + namespace_length /* NamespaceString[] */; + + /* OffsetDbgDeviceInfo */ + build_append_int_noprefix(table_data, 44, 4); + /* NumberDbgDeviceInfo */ + build_append_int_noprefix(table_data, 1, 4); + + /* Table 2. Debug Device Information structure format */ + build_append_int_noprefix(table_data, 0, 1); /* Revision */ + build_append_int_noprefix(table_data, dbg2devicelength, 2); /* Length */ + /* NumberofGenericAddressRegisters */ + build_append_int_noprefix(table_data, 1, 1); + /* NameSpaceStringLength */ + build_append_int_noprefix(table_data, namespace_length, 2); + build_append_int_noprefix(table_data, 38, 2); /* NameSpaceStringOffset */ + build_append_int_noprefix(table_data, 0, 2); /* OemDataLength */ + /* OemDataOffset (0 means no OEM data) */ + build_append_int_noprefix(table_data, 0, 2); + + /* Port Type */ + build_append_int_noprefix(table_data, 0x8000 /* Serial */, 2); + /* Port Subtype */ + build_append_int_noprefix(table_data, 0x3 /* ARM PL011 UART */, 2); + build_append_int_noprefix(table_data, 0, 2); /* Reserved */ + /* BaseAddressRegisterOffset */ + build_append_int_noprefix(table_data, 22, 2); + /* AddressSizeOffset */ + build_append_int_noprefix(table_data, 34, 2); + + /* BaseAddressRegister[] */ + build_append_gas(table_data, AML_AS_SYSTEM_MEMORY, 8, 0, 1, + vms->memmap[VIRT_UART].base); + + /* AddressSize[] */ + build_append_int_noprefix(table_data, + vms->memmap[VIRT_UART].size, 4); + + /* NamespaceString[] */ + g_array_append_vals(table_data, name, namespace_length); + + acpi_table_end(linker, &table); +}; + /* * ACPI spec, Revision 5.1 Errata A * 5.2.12 Multiple APIC Description Table (MADT) @@ -875,13 +943,19 @@ void virt_acpi_build(VirtMachineState *vms, AcpiBuildTables *tables) dsdt = tables_blob->len; build_dsdt(tables_blob, tables->linker, vms); - /* FADT MADT GTDT MCFG SPCR pointed to by RSDT */ + /* FADT MADT PPTT GTDT MCFG SPCR DBG2 pointed to by RSDT */ acpi_add_table(table_offsets, tables_blob); build_fadt_rev5(tables_blob, tables->linker, vms, dsdt); acpi_add_table(table_offsets, tables_blob); build_madt(tables_blob, tables->linker, vms); + if (!vmc->no_cpu_topology) { + acpi_add_table(table_offsets, tables_blob); + build_pptt(tables_blob, tables->linker, ms, + vms->oem_id, vms->oem_table_id); + } + acpi_add_table(table_offsets, tables_blob); build_gtdt(tables_blob, tables->linker, vms); @@ -898,6 +972,9 @@ void virt_acpi_build(VirtMachineState *vms, AcpiBuildTables *tables) acpi_add_table(table_offsets, tables_blob); build_spcr(tables_blob, tables->linker, vms); + acpi_add_table(table_offsets, tables_blob); + build_dbg2(tables_blob, tables->linker, vms); + if (vms->ras) { build_ghes_error_table(tables->hardware_errors, tables->linker); acpi_add_table(table_offsets, tables_blob); diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 4160d49688..ca433adb5b 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -351,20 +351,21 @@ static void fdt_add_cpu_nodes(const VirtMachineState *vms) int cpu; int addr_cells = 1; const MachineState *ms = MACHINE(vms); + const VirtMachineClass *vmc = VIRT_MACHINE_GET_CLASS(vms); int smp_cpus = ms->smp.cpus; /* - * From Documentation/devicetree/bindings/arm/cpus.txt - * On ARM v8 64-bit systems value should be set to 2, - * that corresponds to the MPIDR_EL1 register size. - * If MPIDR_EL1[63:32] value is equal to 0 on all CPUs - * in the system, #address-cells can be set to 1, since - * MPIDR_EL1[63:32] bits are not used for CPUs - * identification. + * See Linux Documentation/devicetree/bindings/arm/cpus.yaml + * On ARM v8 64-bit systems value should be set to 2, + * that corresponds to the MPIDR_EL1 register size. + * If MPIDR_EL1[63:32] value is equal to 0 on all CPUs + * in the system, #address-cells can be set to 1, since + * MPIDR_EL1[63:32] bits are not used for CPUs + * identification. * - * Here we actually don't know whether our system is 32- or 64-bit one. - * The simplest way to go is to examine affinity IDs of all our CPUs. If - * at least one of them has Aff3 populated, we set #address-cells to 2. + * Here we actually don't know whether our system is 32- or 64-bit one. + * The simplest way to go is to examine affinity IDs of all our CPUs. If + * at least one of them has Aff3 populated, we set #address-cells to 2. */ for (cpu = 0; cpu < smp_cpus; cpu++) { ARMCPU *armcpu = ARM_CPU(qemu_get_cpu(cpu)); @@ -407,8 +408,57 @@ static void fdt_add_cpu_nodes(const VirtMachineState *vms) ms->possible_cpus->cpus[cs->cpu_index].props.node_id); } + if (!vmc->no_cpu_topology) { + qemu_fdt_setprop_cell(ms->fdt, nodename, "phandle", + qemu_fdt_alloc_phandle(ms->fdt)); + } + g_free(nodename); } + + if (!vmc->no_cpu_topology) { + /* + * Add vCPU topology description through fdt node cpu-map. + * + * See Linux Documentation/devicetree/bindings/cpu/cpu-topology.txt + * In a SMP system, the hierarchy of CPUs can be defined through + * four entities that are used to describe the layout of CPUs in + * the system: socket/cluster/core/thread. + * + * A socket node represents the boundary of system physical package + * and its child nodes must be one or more cluster nodes. A system + * can contain several layers of clustering within a single physical + * package and cluster nodes can be contained in parent cluster nodes. + * + * Given that cluster is not yet supported in the vCPU topology, + * we currently generate one cluster node within each socket node + * by default. + */ + qemu_fdt_add_subnode(ms->fdt, "/cpus/cpu-map"); + + for (cpu = smp_cpus - 1; cpu >= 0; cpu--) { + char *cpu_path = g_strdup_printf("/cpus/cpu@%d", cpu); + char *map_path; + + if (ms->smp.threads > 1) { + map_path = g_strdup_printf( + "/cpus/cpu-map/socket%d/cluster0/core%d/thread%d", + cpu / (ms->smp.cores * ms->smp.threads), + (cpu / ms->smp.threads) % ms->smp.cores, + cpu % ms->smp.threads); + } else { + map_path = g_strdup_printf( + "/cpus/cpu-map/socket%d/cluster0/core%d", + cpu / ms->smp.cores, + cpu % ms->smp.cores); + } + qemu_fdt_add_path(ms->fdt, map_path); + qemu_fdt_setprop_phandle(ms->fdt, map_path, "cpu", cpu_path); + + g_free(map_path); + g_free(cpu_path); + } + } } static void fdt_add_its_gic_node(VirtMachineState *vms) @@ -2816,6 +2866,7 @@ static void virt_machine_6_1_options(MachineClass *mc) virt_machine_6_2_options(mc); compat_props_add(mc->compat_props, hw_compat_6_1, hw_compat_6_1_len); mc->smp_props.prefer_sockets = true; + vmc->no_cpu_topology = true; /* qemu ITS was introduced with 6.2 */ vmc->no_tcg_its = true; diff --git a/hw/core/loader.c b/hw/core/loader.c index c623318b73..c7f97fdce8 100644 --- a/hw/core/loader.c +++ b/hw/core/loader.c @@ -326,7 +326,7 @@ static void *load_at(int fd, off_t offset, size_t size) #define SZ 64 #include "hw/elf_ops.h" -const char *load_elf_strerror(int error) +const char *load_elf_strerror(ssize_t error) { switch (error) { case 0: @@ -402,12 +402,12 @@ fail: } /* return < 0 if error, otherwise the number of bytes loaded in memory */ -int load_elf(const char *filename, - uint64_t (*elf_note_fn)(void *, void *, bool), - uint64_t (*translate_fn)(void *, uint64_t), - void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr, - uint64_t *highaddr, uint32_t *pflags, int big_endian, - int elf_machine, int clear_lsb, int data_swab) +ssize_t load_elf(const char *filename, + uint64_t (*elf_note_fn)(void *, void *, bool), + uint64_t (*translate_fn)(void *, uint64_t), + void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr, + uint64_t *highaddr, uint32_t *pflags, int big_endian, + int elf_machine, int clear_lsb, int data_swab) { return load_elf_as(filename, elf_note_fn, translate_fn, translate_opaque, pentry, lowaddr, highaddr, pflags, big_endian, @@ -415,12 +415,13 @@ int load_elf(const char *filename, } /* return < 0 if error, otherwise the number of bytes loaded in memory */ -int load_elf_as(const char *filename, - uint64_t (*elf_note_fn)(void *, void *, bool), - uint64_t (*translate_fn)(void *, uint64_t), - void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr, - uint64_t *highaddr, uint32_t *pflags, int big_endian, - int elf_machine, int clear_lsb, int data_swab, AddressSpace *as) +ssize_t load_elf_as(const char *filename, + uint64_t (*elf_note_fn)(void *, void *, bool), + uint64_t (*translate_fn)(void *, uint64_t), + void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr, + uint64_t *highaddr, uint32_t *pflags, int big_endian, + int elf_machine, int clear_lsb, int data_swab, + AddressSpace *as) { return load_elf_ram(filename, elf_note_fn, translate_fn, translate_opaque, pentry, lowaddr, highaddr, pflags, big_endian, @@ -428,13 +429,13 @@ int load_elf_as(const char *filename, } /* return < 0 if error, otherwise the number of bytes loaded in memory */ -int load_elf_ram(const char *filename, - uint64_t (*elf_note_fn)(void *, void *, bool), - uint64_t (*translate_fn)(void *, uint64_t), - void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr, - uint64_t *highaddr, uint32_t *pflags, int big_endian, - int elf_machine, int clear_lsb, int data_swab, - AddressSpace *as, bool load_rom) +ssize_t load_elf_ram(const char *filename, + uint64_t (*elf_note_fn)(void *, void *, bool), + uint64_t (*translate_fn)(void *, uint64_t), + void *translate_opaque, uint64_t *pentry, + uint64_t *lowaddr, uint64_t *highaddr, uint32_t *pflags, + int big_endian, int elf_machine, int clear_lsb, + int data_swab, AddressSpace *as, bool load_rom) { return load_elf_ram_sym(filename, elf_note_fn, translate_fn, translate_opaque, @@ -444,16 +445,17 @@ int load_elf_ram(const char *filename, } /* return < 0 if error, otherwise the number of bytes loaded in memory */ -int load_elf_ram_sym(const char *filename, - uint64_t (*elf_note_fn)(void *, void *, bool), - uint64_t (*translate_fn)(void *, uint64_t), - void *translate_opaque, uint64_t *pentry, - uint64_t *lowaddr, uint64_t *highaddr, uint32_t *pflags, - int big_endian, int elf_machine, - int clear_lsb, int data_swab, - AddressSpace *as, bool load_rom, symbol_fn_t sym_cb) +ssize_t load_elf_ram_sym(const char *filename, + uint64_t (*elf_note_fn)(void *, void *, bool), + uint64_t (*translate_fn)(void *, uint64_t), + void *translate_opaque, uint64_t *pentry, + uint64_t *lowaddr, uint64_t *highaddr, + uint32_t *pflags, int big_endian, int elf_machine, + int clear_lsb, int data_swab, + AddressSpace *as, bool load_rom, symbol_fn_t sym_cb) { - int fd, data_order, target_data_order, must_swab, ret = ELF_LOAD_FAILED; + int fd, data_order, target_data_order, must_swab; + ssize_t ret = ELF_LOAD_FAILED; uint8_t e_ident[EI_NIDENT]; fd = open(filename, O_RDONLY | O_BINARY); diff --git a/hw/intc/spapr_xive.c b/hw/intc/spapr_xive.c index 89cfa018f5..4ec659b93e 100644 --- a/hw/intc/spapr_xive.c +++ b/hw/intc/spapr_xive.c @@ -185,7 +185,7 @@ static void spapr_xive_pic_print_info(SpaprXive *xive, Monitor *mon) xive_source_irq_is_lsi(xsrc, i) ? "LSI" : "MSI", pq & XIVE_ESB_VAL_P ? 'P' : '-', pq & XIVE_ESB_VAL_Q ? 'Q' : '-', - xsrc->status[i] & XIVE_STATUS_ASSERTED ? 'A' : ' ', + xive_source_is_asserted(xsrc, i) ? 'A' : ' ', xive_eas_is_masked(eas) ? "M" : " ", (int) xive_get_field64(EAS_END_DATA, eas->w)); diff --git a/hw/intc/spapr_xive_kvm.c b/hw/intc/spapr_xive_kvm.c index 6d4909d0a8..61fe7bd2d3 100644 --- a/hw/intc/spapr_xive_kvm.c +++ b/hw/intc/spapr_xive_kvm.c @@ -242,7 +242,7 @@ int kvmppc_xive_source_reset_one(XiveSource *xsrc, int srcno, Error **errp) if (xive_source_irq_is_lsi(xsrc, srcno)) { state |= KVM_XIVE_LEVEL_SENSITIVE; - if (xsrc->status[srcno] & XIVE_STATUS_ASSERTED) { + if (xive_source_is_asserted(xsrc, srcno)) { state |= KVM_XIVE_LEVEL_ASSERTED; } } @@ -301,9 +301,7 @@ static uint8_t xive_esb_read(XiveSource *xsrc, int srcno, uint32_t offset) static void kvmppc_xive_esb_trigger(XiveSource *xsrc, int srcno) { - uint64_t *addr = xsrc->esb_mmap + xive_source_esb_page(xsrc, srcno); - - *addr = 0x0; + xive_esb_rw(xsrc, srcno, 0, 0, true); } uint64_t kvmppc_xive_esb_rw(XiveSource *xsrc, int srcno, uint32_t offset, @@ -321,7 +319,7 @@ uint64_t kvmppc_xive_esb_rw(XiveSource *xsrc, int srcno, uint32_t offset, if (xive_source_irq_is_lsi(xsrc, srcno) && offset == XIVE_ESB_LOAD_EOI) { xive_esb_read(xsrc, srcno, XIVE_ESB_SET_PQ_00); - if (xsrc->status[srcno] & XIVE_STATUS_ASSERTED) { + if (xive_source_is_asserted(xsrc, srcno)) { kvmppc_xive_esb_trigger(xsrc, srcno); } return 0; @@ -359,11 +357,7 @@ void kvmppc_xive_source_set_irq(void *opaque, int srcno, int val) return; } } else { - if (val) { - xsrc->status[srcno] |= XIVE_STATUS_ASSERTED; - } else { - xsrc->status[srcno] &= ~XIVE_STATUS_ASSERTED; - } + xive_source_set_asserted(xsrc, srcno, val); } kvmppc_xive_esb_trigger(xsrc, srcno); diff --git a/hw/intc/xive.c b/hw/intc/xive.c index 6c82326ec7..190194d27f 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -875,7 +875,7 @@ static bool xive_source_lsi_trigger(XiveSource *xsrc, uint32_t srcno) { uint8_t old_pq = xive_source_esb_get(xsrc, srcno); - xsrc->status[srcno] |= XIVE_STATUS_ASSERTED; + xive_source_set_asserted(xsrc, srcno, true); switch (old_pq) { case XIVE_ESB_RESET: @@ -923,7 +923,7 @@ static bool xive_source_esb_eoi(XiveSource *xsrc, uint32_t srcno) * notification */ if (xive_source_irq_is_lsi(xsrc, srcno) && - xsrc->status[srcno] & XIVE_STATUS_ASSERTED) { + xive_source_is_asserted(xsrc, srcno)) { ret = xive_source_lsi_trigger(xsrc, srcno); } @@ -1104,7 +1104,7 @@ void xive_source_set_irq(void *opaque, int srcno, int val) if (val) { notify = xive_source_lsi_trigger(xsrc, srcno); } else { - xsrc->status[srcno] &= ~XIVE_STATUS_ASSERTED; + xive_source_set_asserted(xsrc, srcno, false); } } else { if (val) { @@ -1133,7 +1133,7 @@ void xive_source_pic_print_info(XiveSource *xsrc, uint32_t offset, Monitor *mon) xive_source_irq_is_lsi(xsrc, i) ? "LSI" : "MSI", pq & XIVE_ESB_VAL_P ? 'P' : '-', pq & XIVE_ESB_VAL_Q ? 'Q' : '-', - xsrc->status[i] & XIVE_STATUS_ASSERTED ? 'A' : ' '); + xive_source_is_asserted(xsrc, i) ? 'A' : ' '); } } diff --git a/hw/net/vhost_net-stub.c b/hw/net/vhost_net-stub.c index a7f4252630..89d71cfb8e 100644 --- a/hw/net/vhost_net-stub.c +++ b/hw/net/vhost_net-stub.c @@ -33,13 +33,13 @@ struct vhost_net *vhost_net_init(VhostNetOptions *options) int vhost_net_start(VirtIODevice *dev, NetClientState *ncs, - int total_queues) + int data_queue_pairs, int cvq) { return -ENOSYS; } void vhost_net_stop(VirtIODevice *dev, NetClientState *ncs, - int total_queues) + int data_queue_pairs, int cvq) { } diff --git a/hw/net/vhost_net.c b/hw/net/vhost_net.c index 386ec2eaa2..0d888f29a6 100644 --- a/hw/net/vhost_net.c +++ b/hw/net/vhost_net.c @@ -231,9 +231,11 @@ fail: return NULL; } -static void vhost_net_set_vq_index(struct vhost_net *net, int vq_index) +static void vhost_net_set_vq_index(struct vhost_net *net, int vq_index, + int last_index) { net->dev.vq_index = vq_index; + net->dev.last_index = last_index; } static int vhost_net_start_one(struct vhost_net *net, @@ -315,25 +317,37 @@ static void vhost_net_stop_one(struct vhost_net *net, } int vhost_net_start(VirtIODevice *dev, NetClientState *ncs, - int total_queues) + int data_queue_pairs, int cvq) { BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(dev))); VirtioBusState *vbus = VIRTIO_BUS(qbus); VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(vbus); + int total_notifiers = data_queue_pairs * 2 + cvq; + VirtIONet *n = VIRTIO_NET(dev); + int nvhosts = data_queue_pairs + cvq; struct vhost_net *net; - int r, e, i; + int r, e, i, last_index = data_queue_pairs * 2; NetClientState *peer; + if (!cvq) { + last_index -= 1; + } + if (!k->set_guest_notifiers) { error_report("binding does not support guest notifiers"); return -ENOSYS; } - for (i = 0; i < total_queues; i++) { + for (i = 0; i < nvhosts; i++) { + + if (i < data_queue_pairs) { + peer = qemu_get_peer(ncs, i); + } else { /* Control Virtqueue */ + peer = qemu_get_peer(ncs, n->max_queue_pairs); + } - peer = qemu_get_peer(ncs, i); net = get_vhost_net(peer); - vhost_net_set_vq_index(net, i * 2); + vhost_net_set_vq_index(net, i * 2, last_index); /* Suppress the masking guest notifiers on vhost user * because vhost user doesn't interrupt masking/unmasking @@ -344,14 +358,18 @@ int vhost_net_start(VirtIODevice *dev, NetClientState *ncs, } } - r = k->set_guest_notifiers(qbus->parent, total_queues * 2, true); + r = k->set_guest_notifiers(qbus->parent, total_notifiers, true); if (r < 0) { error_report("Error binding guest notifier: %d", -r); goto err; } - for (i = 0; i < total_queues; i++) { - peer = qemu_get_peer(ncs, i); + for (i = 0; i < nvhosts; i++) { + if (i < data_queue_pairs) { + peer = qemu_get_peer(ncs, i); + } else { + peer = qemu_get_peer(ncs, n->max_queue_pairs); + } r = vhost_net_start_one(get_vhost_net(peer), dev); if (r < 0) { @@ -375,7 +393,7 @@ err_start: peer = qemu_get_peer(ncs , i); vhost_net_stop_one(get_vhost_net(peer), dev); } - e = k->set_guest_notifiers(qbus->parent, total_queues * 2, false); + e = k->set_guest_notifiers(qbus->parent, total_notifiers, false); if (e < 0) { fprintf(stderr, "vhost guest notifier cleanup failed: %d\n", e); fflush(stderr); @@ -385,18 +403,27 @@ err: } void vhost_net_stop(VirtIODevice *dev, NetClientState *ncs, - int total_queues) + int data_queue_pairs, int cvq) { BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(dev))); VirtioBusState *vbus = VIRTIO_BUS(qbus); VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(vbus); + VirtIONet *n = VIRTIO_NET(dev); + NetClientState *peer; + int total_notifiers = data_queue_pairs * 2 + cvq; + int nvhosts = data_queue_pairs + cvq; int i, r; - for (i = 0; i < total_queues; i++) { - vhost_net_stop_one(get_vhost_net(ncs[i].peer), dev); + for (i = 0; i < nvhosts; i++) { + if (i < data_queue_pairs) { + peer = qemu_get_peer(ncs, i); + } else { + peer = qemu_get_peer(ncs, n->max_queue_pairs); + } + vhost_net_stop_one(get_vhost_net(peer), dev); } - r = k->set_guest_notifiers(qbus->parent, total_queues * 2, false); + r = k->set_guest_notifiers(qbus->parent, total_notifiers, false); if (r < 0) { fprintf(stderr, "vhost guest notifier cleanup failed: %d\n", r); fflush(stderr); diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index 09e173a558..f2014d5ea0 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -54,7 +54,7 @@ #define VIRTIO_NET_RX_QUEUE_DEFAULT_SIZE 256 #define VIRTIO_NET_TX_QUEUE_DEFAULT_SIZE 256 -/* for now, only allow larger queues; with virtio-1, guest can downsize */ +/* for now, only allow larger queue_pairs; with virtio-1, guest can downsize */ #define VIRTIO_NET_RX_QUEUE_MIN_SIZE VIRTIO_NET_RX_QUEUE_DEFAULT_SIZE #define VIRTIO_NET_TX_QUEUE_MIN_SIZE VIRTIO_NET_TX_QUEUE_DEFAULT_SIZE @@ -131,7 +131,7 @@ static void virtio_net_get_config(VirtIODevice *vdev, uint8_t *config) int ret = 0; memset(&netcfg, 0 , sizeof(struct virtio_net_config)); virtio_stw_p(vdev, &netcfg.status, n->status); - virtio_stw_p(vdev, &netcfg.max_virtqueue_pairs, n->max_queues); + virtio_stw_p(vdev, &netcfg.max_virtqueue_pairs, n->max_queue_pairs); virtio_stw_p(vdev, &netcfg.mtu, n->net_conf.mtu); memcpy(netcfg.mac, n->mac, ETH_ALEN); virtio_stl_p(vdev, &netcfg.speed, n->net_conf.speed); @@ -243,7 +243,8 @@ static void virtio_net_vhost_status(VirtIONet *n, uint8_t status) { VirtIODevice *vdev = VIRTIO_DEVICE(n); NetClientState *nc = qemu_get_queue(n->nic); - int queues = n->multiqueue ? n->max_queues : 1; + int queue_pairs = n->multiqueue ? n->max_queue_pairs : 1; + int cvq = n->max_ncs - n->max_queue_pairs; if (!get_vhost_net(nc->peer)) { return; @@ -266,7 +267,7 @@ static void virtio_net_vhost_status(VirtIONet *n, uint8_t status) /* Any packets outstanding? Purge them to avoid touching rings * when vhost is running. */ - for (i = 0; i < queues; i++) { + for (i = 0; i < queue_pairs; i++) { NetClientState *qnc = qemu_get_subqueue(n->nic, i); /* Purge both directions: TX and RX. */ @@ -285,14 +286,14 @@ static void virtio_net_vhost_status(VirtIONet *n, uint8_t status) } n->vhost_started = 1; - r = vhost_net_start(vdev, n->nic->ncs, queues); + r = vhost_net_start(vdev, n->nic->ncs, queue_pairs, cvq); if (r < 0) { error_report("unable to start vhost net: %d: " "falling back on userspace virtio", -r); n->vhost_started = 0; } } else { - vhost_net_stop(vdev, n->nic->ncs, queues); + vhost_net_stop(vdev, n->nic->ncs, queue_pairs, cvq); n->vhost_started = 0; } } @@ -309,11 +310,11 @@ static int virtio_net_set_vnet_endian_one(VirtIODevice *vdev, } static bool virtio_net_set_vnet_endian(VirtIODevice *vdev, NetClientState *ncs, - int queues, bool enable) + int queue_pairs, bool enable) { int i; - for (i = 0; i < queues; i++) { + for (i = 0; i < queue_pairs; i++) { if (virtio_net_set_vnet_endian_one(vdev, ncs[i].peer, enable) < 0 && enable) { while (--i >= 0) { @@ -330,7 +331,7 @@ static bool virtio_net_set_vnet_endian(VirtIODevice *vdev, NetClientState *ncs, static void virtio_net_vnet_endian_status(VirtIONet *n, uint8_t status) { VirtIODevice *vdev = VIRTIO_DEVICE(n); - int queues = n->multiqueue ? n->max_queues : 1; + int queue_pairs = n->multiqueue ? n->max_queue_pairs : 1; if (virtio_net_started(n, status)) { /* Before using the device, we tell the network backend about the @@ -339,14 +340,14 @@ static void virtio_net_vnet_endian_status(VirtIONet *n, uint8_t status) * virtio-net code. */ n->needs_vnet_hdr_swap = virtio_net_set_vnet_endian(vdev, n->nic->ncs, - queues, true); + queue_pairs, true); } else if (virtio_net_started(n, vdev->status)) { /* After using the device, we need to reset the network backend to * the default (guest native endianness), otherwise the guest may * lose network connectivity if it is rebooted into a different * endianness. */ - virtio_net_set_vnet_endian(vdev, n->nic->ncs, queues, false); + virtio_net_set_vnet_endian(vdev, n->nic->ncs, queue_pairs, false); } } @@ -368,12 +369,12 @@ static void virtio_net_set_status(struct VirtIODevice *vdev, uint8_t status) virtio_net_vnet_endian_status(n, status); virtio_net_vhost_status(n, status); - for (i = 0; i < n->max_queues; i++) { + for (i = 0; i < n->max_queue_pairs; i++) { NetClientState *ncs = qemu_get_subqueue(n->nic, i); bool queue_started; q = &n->vqs[i]; - if ((!n->multiqueue && i != 0) || i >= n->curr_queues) { + if ((!n->multiqueue && i != 0) || i >= n->curr_queue_pairs) { queue_status = 0; } else { queue_status = status; @@ -540,7 +541,7 @@ static void virtio_net_reset(VirtIODevice *vdev) n->nouni = 0; n->nobcast = 0; /* multiqueue is disabled by default */ - n->curr_queues = 1; + n->curr_queue_pairs = 1; timer_del(n->announce_timer.tm); n->announce_timer.round = 0; n->status &= ~VIRTIO_NET_S_ANNOUNCE; @@ -556,7 +557,7 @@ static void virtio_net_reset(VirtIODevice *vdev) memset(n->vlans, 0, MAX_VLAN >> 3); /* Flush any async TX */ - for (i = 0; i < n->max_queues; i++) { + for (i = 0; i < n->max_queue_pairs; i++) { NetClientState *nc = qemu_get_subqueue(n->nic, i); if (nc->peer) { @@ -610,7 +611,7 @@ static void virtio_net_set_mrg_rx_bufs(VirtIONet *n, int mergeable_rx_bufs, sizeof(struct virtio_net_hdr); } - for (i = 0; i < n->max_queues; i++) { + for (i = 0; i < n->max_queue_pairs; i++) { nc = qemu_get_subqueue(n->nic, i); if (peer_has_vnet_hdr(n) && @@ -655,7 +656,7 @@ static int peer_attach(VirtIONet *n, int index) return 0; } - if (n->max_queues == 1) { + if (n->max_queue_pairs == 1) { return 0; } @@ -681,7 +682,7 @@ static int peer_detach(VirtIONet *n, int index) return tap_disable(nc->peer); } -static void virtio_net_set_queues(VirtIONet *n) +static void virtio_net_set_queue_pairs(VirtIONet *n) { int i; int r; @@ -690,8 +691,8 @@ static void virtio_net_set_queues(VirtIONet *n) return; } - for (i = 0; i < n->max_queues; i++) { - if (i < n->curr_queues) { + for (i = 0; i < n->max_queue_pairs; i++) { + if (i < n->curr_queue_pairs) { r = peer_attach(n, i); assert(!r); } else { @@ -905,7 +906,7 @@ static void virtio_net_set_features(VirtIODevice *vdev, uint64_t features) virtio_net_apply_guest_offloads(n); } - for (i = 0; i < n->max_queues; i++) { + for (i = 0; i < n->max_queue_pairs; i++) { NetClientState *nc = qemu_get_subqueue(n->nic, i); if (!get_vhost_net(nc->peer)) { @@ -1232,7 +1233,7 @@ static uint16_t virtio_net_handle_rss(VirtIONet *n, VirtIODevice *vdev = VIRTIO_DEVICE(n); struct virtio_net_rss_config cfg; size_t s, offset = 0, size_get; - uint16_t queues, i; + uint16_t queue_pairs, i; struct { uint16_t us; uint8_t b; @@ -1274,7 +1275,7 @@ static uint16_t virtio_net_handle_rss(VirtIONet *n, } n->rss_data.default_queue = do_rss ? virtio_lduw_p(vdev, &cfg.unclassified_queue) : 0; - if (n->rss_data.default_queue >= n->max_queues) { + if (n->rss_data.default_queue >= n->max_queue_pairs) { err_msg = "Invalid default queue"; err_value = n->rss_data.default_queue; goto error; @@ -1303,14 +1304,14 @@ static uint16_t virtio_net_handle_rss(VirtIONet *n, size_get = sizeof(temp); s = iov_to_buf(iov, iov_cnt, offset, &temp, size_get); if (s != size_get) { - err_msg = "Can't get queues"; + err_msg = "Can't get queue_pairs"; err_value = (uint32_t)s; goto error; } - queues = do_rss ? virtio_lduw_p(vdev, &temp.us) : n->curr_queues; - if (queues == 0 || queues > n->max_queues) { - err_msg = "Invalid number of queues"; - err_value = queues; + queue_pairs = do_rss ? virtio_lduw_p(vdev, &temp.us) : n->curr_queue_pairs; + if (queue_pairs == 0 || queue_pairs > n->max_queue_pairs) { + err_msg = "Invalid number of queue_pairs"; + err_value = queue_pairs; goto error; } if (temp.b > VIRTIO_NET_RSS_MAX_KEY_SIZE) { @@ -1325,7 +1326,7 @@ static uint16_t virtio_net_handle_rss(VirtIONet *n, } if (!temp.b && !n->rss_data.hash_types) { virtio_net_disable_rss(n); - return queues; + return queue_pairs; } offset += size_get; size_get = temp.b; @@ -1358,7 +1359,7 @@ static uint16_t virtio_net_handle_rss(VirtIONet *n, trace_virtio_net_rss_enable(n->rss_data.hash_types, n->rss_data.indirections_len, temp.b); - return queues; + return queue_pairs; error: trace_virtio_net_rss_error(err_msg, err_value); virtio_net_disable_rss(n); @@ -1369,15 +1370,15 @@ static int virtio_net_handle_mq(VirtIONet *n, uint8_t cmd, struct iovec *iov, unsigned int iov_cnt) { VirtIODevice *vdev = VIRTIO_DEVICE(n); - uint16_t queues; + uint16_t queue_pairs; virtio_net_disable_rss(n); if (cmd == VIRTIO_NET_CTRL_MQ_HASH_CONFIG) { - queues = virtio_net_handle_rss(n, iov, iov_cnt, false); - return queues ? VIRTIO_NET_OK : VIRTIO_NET_ERR; + queue_pairs = virtio_net_handle_rss(n, iov, iov_cnt, false); + return queue_pairs ? VIRTIO_NET_OK : VIRTIO_NET_ERR; } if (cmd == VIRTIO_NET_CTRL_MQ_RSS_CONFIG) { - queues = virtio_net_handle_rss(n, iov, iov_cnt, true); + queue_pairs = virtio_net_handle_rss(n, iov, iov_cnt, true); } else if (cmd == VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET) { struct virtio_net_ctrl_mq mq; size_t s; @@ -1388,24 +1389,24 @@ static int virtio_net_handle_mq(VirtIONet *n, uint8_t cmd, if (s != sizeof(mq)) { return VIRTIO_NET_ERR; } - queues = virtio_lduw_p(vdev, &mq.virtqueue_pairs); + queue_pairs = virtio_lduw_p(vdev, &mq.virtqueue_pairs); } else { return VIRTIO_NET_ERR; } - if (queues < VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN || - queues > VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX || - queues > n->max_queues || + if (queue_pairs < VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN || + queue_pairs > VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX || + queue_pairs > n->max_queue_pairs || !n->multiqueue) { return VIRTIO_NET_ERR; } - n->curr_queues = queues; - /* stop the backend before changing the number of queues to avoid handling a + n->curr_queue_pairs = queue_pairs; + /* stop the backend before changing the number of queue_pairs to avoid handling a * disabled queue */ virtio_net_set_status(vdev, vdev->status); - virtio_net_set_queues(n); + virtio_net_set_queue_pairs(n); return VIRTIO_NET_OK; } @@ -1483,7 +1484,7 @@ static bool virtio_net_can_receive(NetClientState *nc) return false; } - if (nc->queue_index >= n->curr_queues) { + if (nc->queue_index >= n->curr_queue_pairs) { return false; } @@ -2763,11 +2764,11 @@ static void virtio_net_del_queue(VirtIONet *n, int index) virtio_del_queue(vdev, index * 2 + 1); } -static void virtio_net_change_num_queues(VirtIONet *n, int new_max_queues) +static void virtio_net_change_num_queue_pairs(VirtIONet *n, int new_max_queue_pairs) { VirtIODevice *vdev = VIRTIO_DEVICE(n); int old_num_queues = virtio_get_num_queues(vdev); - int new_num_queues = new_max_queues * 2 + 1; + int new_num_queues = new_max_queue_pairs * 2 + 1; int i; assert(old_num_queues >= 3); @@ -2800,12 +2801,12 @@ static void virtio_net_change_num_queues(VirtIONet *n, int new_max_queues) static void virtio_net_set_multiqueue(VirtIONet *n, int multiqueue) { - int max = multiqueue ? n->max_queues : 1; + int max = multiqueue ? n->max_queue_pairs : 1; n->multiqueue = multiqueue; - virtio_net_change_num_queues(n, max); + virtio_net_change_num_queue_pairs(n, max); - virtio_net_set_queues(n); + virtio_net_set_queue_pairs(n); } static int virtio_net_post_load_device(void *opaque, int version_id) @@ -2838,7 +2839,7 @@ static int virtio_net_post_load_device(void *opaque, int version_id) */ n->saved_guest_offloads = n->curr_guest_offloads; - virtio_net_set_queues(n); + virtio_net_set_queue_pairs(n); /* Find the first multicast entry in the saved MAC filter */ for (i = 0; i < n->mac_table.in_use; i++) { @@ -2851,7 +2852,7 @@ static int virtio_net_post_load_device(void *opaque, int version_id) /* nc.link_down can't be migrated, so infer link_down according * to link status bit in n->status */ link_down = (n->status & VIRTIO_NET_S_LINK_UP) == 0; - for (i = 0; i < n->max_queues; i++) { + for (i = 0; i < n->max_queue_pairs; i++) { qemu_get_subqueue(n->nic, i)->link_down = link_down; } @@ -2916,9 +2917,9 @@ static const VMStateDescription vmstate_virtio_net_queue_tx_waiting = { }, }; -static bool max_queues_gt_1(void *opaque, int version_id) +static bool max_queue_pairs_gt_1(void *opaque, int version_id) { - return VIRTIO_NET(opaque)->max_queues > 1; + return VIRTIO_NET(opaque)->max_queue_pairs > 1; } static bool has_ctrl_guest_offloads(void *opaque, int version_id) @@ -2943,13 +2944,13 @@ static bool mac_table_doesnt_fit(void *opaque, int version_id) struct VirtIONetMigTmp { VirtIONet *parent; VirtIONetQueue *vqs_1; - uint16_t curr_queues_1; + uint16_t curr_queue_pairs_1; uint8_t has_ufo; uint32_t has_vnet_hdr; }; /* The 2nd and subsequent tx_waiting flags are loaded later than - * the 1st entry in the queues and only if there's more than one + * the 1st entry in the queue_pairs and only if there's more than one * entry. We use the tmp mechanism to calculate a temporary * pointer and count and also validate the count. */ @@ -2959,9 +2960,9 @@ static int virtio_net_tx_waiting_pre_save(void *opaque) struct VirtIONetMigTmp *tmp = opaque; tmp->vqs_1 = tmp->parent->vqs + 1; - tmp->curr_queues_1 = tmp->parent->curr_queues - 1; - if (tmp->parent->curr_queues == 0) { - tmp->curr_queues_1 = 0; + tmp->curr_queue_pairs_1 = tmp->parent->curr_queue_pairs - 1; + if (tmp->parent->curr_queue_pairs == 0) { + tmp->curr_queue_pairs_1 = 0; } return 0; @@ -2974,9 +2975,9 @@ static int virtio_net_tx_waiting_pre_load(void *opaque) /* Reuse the pointer setup from save */ virtio_net_tx_waiting_pre_save(opaque); - if (tmp->parent->curr_queues > tmp->parent->max_queues) { - error_report("virtio-net: curr_queues %x > max_queues %x", - tmp->parent->curr_queues, tmp->parent->max_queues); + if (tmp->parent->curr_queue_pairs > tmp->parent->max_queue_pairs) { + error_report("virtio-net: curr_queue_pairs %x > max_queue_pairs %x", + tmp->parent->curr_queue_pairs, tmp->parent->max_queue_pairs); return -EINVAL; } @@ -2990,7 +2991,7 @@ static const VMStateDescription vmstate_virtio_net_tx_waiting = { .pre_save = virtio_net_tx_waiting_pre_save, .fields = (VMStateField[]) { VMSTATE_STRUCT_VARRAY_POINTER_UINT16(vqs_1, struct VirtIONetMigTmp, - curr_queues_1, + curr_queue_pairs_1, vmstate_virtio_net_queue_tx_waiting, struct VirtIONetQueue), VMSTATE_END_OF_LIST() @@ -3132,9 +3133,9 @@ static const VMStateDescription vmstate_virtio_net_device = { VMSTATE_UINT8(nobcast, VirtIONet), VMSTATE_WITH_TMP(VirtIONet, struct VirtIONetMigTmp, vmstate_virtio_net_has_ufo), - VMSTATE_SINGLE_TEST(max_queues, VirtIONet, max_queues_gt_1, 0, + VMSTATE_SINGLE_TEST(max_queue_pairs, VirtIONet, max_queue_pairs_gt_1, 0, vmstate_info_uint16_equal, uint16_t), - VMSTATE_UINT16_TEST(curr_queues, VirtIONet, max_queues_gt_1), + VMSTATE_UINT16_TEST(curr_queue_pairs, VirtIONet, max_queue_pairs_gt_1), VMSTATE_WITH_TMP(VirtIONet, struct VirtIONetMigTmp, vmstate_virtio_net_tx_waiting), VMSTATE_UINT64_TEST(curr_guest_offloads, VirtIONet, @@ -3299,19 +3300,41 @@ static bool failover_hide_primary_device(DeviceListener *listener, if (!device_opts) { return false; } - standby_id = qdict_get_try_str(device_opts, "failover_pair_id"); - if (g_strcmp0(standby_id, n->netclient_name) != 0) { + + if (!qdict_haskey(device_opts, "failover_pair_id")) { return false; } - if (n->primary_opts) { - error_setg(errp, "Cannot attach more than one primary device to '%s'", - n->netclient_name); + if (!qdict_haskey(device_opts, "id")) { + error_setg(errp, "Device with failover_pair_id needs to have id"); return false; } - n->primary_opts = qdict_clone_shallow(device_opts); - n->primary_opts_from_json = from_json; + standby_id = qdict_get_str(device_opts, "failover_pair_id"); + if (g_strcmp0(standby_id, n->netclient_name) != 0) { + return false; + } + + /* + * The hide helper can be called several times for a given device. + * Check there is only one primary for a virtio-net device but + * don't duplicate the qdict several times if it's called for the same + * device. + */ + if (n->primary_opts) { + const char *old, *new; + /* devices with failover_pair_id always have an id */ + old = qdict_get_str(n->primary_opts, "id"); + new = qdict_get_str(device_opts, "id"); + if (strcmp(old, new) != 0) { + error_setg(errp, "Cannot attach more than one primary device to " + "'%s': '%s' and '%s'", n->netclient_name, old, new); + return false; + } + } else { + n->primary_opts = qdict_clone_shallow(device_opts); + n->primary_opts_from_json = from_json; + } /* failover_primary_hidden is set during feature negotiation */ return qatomic_read(&n->failover_primary_hidden); @@ -3389,16 +3412,30 @@ static void virtio_net_device_realize(DeviceState *dev, Error **errp) return; } - n->max_queues = MAX(n->nic_conf.peers.queues, 1); - if (n->max_queues * 2 + 1 > VIRTIO_QUEUE_MAX) { - error_setg(errp, "Invalid number of queues (= %" PRIu32 "), " + n->max_ncs = MAX(n->nic_conf.peers.queues, 1); + + /* + * Figure out the datapath queue pairs since the backend could + * provide control queue via peers as well. + */ + if (n->nic_conf.peers.queues) { + for (i = 0; i < n->max_ncs; i++) { + if (n->nic_conf.peers.ncs[i]->is_datapath) { + ++n->max_queue_pairs; + } + } + } + n->max_queue_pairs = MAX(n->max_queue_pairs, 1); + + if (n->max_queue_pairs * 2 + 1 > VIRTIO_QUEUE_MAX) { + error_setg(errp, "Invalid number of queue pairs (= %" PRIu32 "), " "must be a positive integer less than %d.", - n->max_queues, (VIRTIO_QUEUE_MAX - 1) / 2); + n->max_queue_pairs, (VIRTIO_QUEUE_MAX - 1) / 2); virtio_cleanup(vdev); return; } - n->vqs = g_malloc0(sizeof(VirtIONetQueue) * n->max_queues); - n->curr_queues = 1; + n->vqs = g_malloc0(sizeof(VirtIONetQueue) * n->max_queue_pairs); + n->curr_queue_pairs = 1; n->tx_timeout = n->net_conf.txtimer; if (n->net_conf.tx && strcmp(n->net_conf.tx, "timer") @@ -3412,7 +3449,7 @@ static void virtio_net_device_realize(DeviceState *dev, Error **errp) n->net_conf.tx_queue_size = MIN(virtio_net_max_tx_queue_size(n), n->net_conf.tx_queue_size); - for (i = 0; i < n->max_queues; i++) { + for (i = 0; i < n->max_queue_pairs; i++) { virtio_net_add_queue(n, i); } @@ -3436,13 +3473,13 @@ static void virtio_net_device_realize(DeviceState *dev, Error **errp) object_get_typename(OBJECT(dev)), dev->id, n); } - for (i = 0; i < n->max_queues; i++) { + for (i = 0; i < n->max_queue_pairs; i++) { n->nic->ncs[i].do_not_pad = true; } peer_test_vnet_hdr(n); if (peer_has_vnet_hdr(n)) { - for (i = 0; i < n->max_queues; i++) { + for (i = 0; i < n->max_queue_pairs; i++) { qemu_using_vnet_hdr(qemu_get_subqueue(n->nic, i)->peer, true); } n->host_hdr_len = sizeof(struct virtio_net_hdr); @@ -3484,7 +3521,7 @@ static void virtio_net_device_unrealize(DeviceState *dev) { VirtIODevice *vdev = VIRTIO_DEVICE(dev); VirtIONet *n = VIRTIO_NET(dev); - int i, max_queues; + int i, max_queue_pairs; if (virtio_has_feature(n->host_features, VIRTIO_NET_F_RSS)) { virtio_net_unload_ebpf(n); @@ -3509,12 +3546,12 @@ static void virtio_net_device_unrealize(DeviceState *dev) assert(n->primary_opts == NULL); } - max_queues = n->multiqueue ? n->max_queues : 1; - for (i = 0; i < max_queues; i++) { + max_queue_pairs = n->multiqueue ? n->max_queue_pairs : 1; + for (i = 0; i < max_queue_pairs; i++) { virtio_net_del_queue(n, i); } /* delete also control vq */ - virtio_del_queue(vdev, max_queues * 2); + virtio_del_queue(vdev, max_queue_pairs * 2); qemu_announce_timer_del(&n->announce_timer, false); g_free(n->vqs); qemu_del_nic(n->nic); diff --git a/hw/pci-host/mv64361.c b/hw/pci-host/mv64361.c index 92b0f5d047..00b3ff7d90 100644 --- a/hw/pci-host/mv64361.c +++ b/hw/pci-host/mv64361.c @@ -869,6 +869,7 @@ static void mv64361_realize(DeviceState *dev, Error **errp) s->base_addr_enable = 0x1fffff; memory_region_init_io(&s->regs, OBJECT(s), &mv64361_ops, s, TYPE_MV64361, 0x10000); + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->regs); for (i = 0; i < 2; i++) { g_autofree char *name = g_strdup_printf("pcihost%d", i); object_initialize_child(OBJECT(dev), name, &s->pci[i], diff --git a/hw/pci/pci_bridge.c b/hw/pci/pci_bridge.c index d1f902ee86..da34c8ebcd 100644 --- a/hw/pci/pci_bridge.c +++ b/hw/pci/pci_bridge.c @@ -448,11 +448,11 @@ int pci_bridge_qemu_reserve_cap_init(PCIDevice *dev, int cap_offset, PCIBridgeQemuCap cap = { .len = cap_len, .type = REDHAT_PCI_CAP_RESOURCE_RESERVE, - .bus_res = res_reserve.bus, - .io = res_reserve.io, - .mem = res_reserve.mem_non_pref, - .mem_pref_32 = res_reserve.mem_pref_32, - .mem_pref_64 = res_reserve.mem_pref_64 + .bus_res = cpu_to_le32(res_reserve.bus), + .io = cpu_to_le64(res_reserve.io), + .mem = cpu_to_le32(res_reserve.mem_non_pref), + .mem_pref_32 = cpu_to_le32(res_reserve.mem_pref_32), + .mem_pref_64 = cpu_to_le64(res_reserve.mem_pref_64) }; int offset = pci_add_capability(dev, PCI_CAP_ID_VNDR, diff --git a/hw/ppc/pegasos2.c b/hw/ppc/pegasos2.c index b8ce859f1a..e427ac2fe0 100644 --- a/hw/ppc/pegasos2.c +++ b/hw/ppc/pegasos2.c @@ -22,6 +22,7 @@ #include "hw/i2c/smbus_eeprom.h" #include "hw/qdev-properties.h" #include "sysemu/reset.h" +#include "sysemu/runstate.h" #include "hw/boards.h" #include "hw/loader.h" #include "hw/fw-path-provider.h" @@ -31,6 +32,8 @@ #include "sysemu/kvm.h" #include "kvm_ppc.h" #include "exec/address-spaces.h" +#include "qom/qom-qobject.h" +#include "qapi/qmp/qdict.h" #include "trace.h" #include "qemu/datadir.h" #include "sysemu/device_tree.h" @@ -52,11 +55,13 @@ #define BUS_FREQ_HZ 133333333 +#define PCI0_CFG_ADDR 0xcf8 #define PCI0_MEM_BASE 0xc0000000 #define PCI0_MEM_SIZE 0x20000000 #define PCI0_IO_BASE 0xf8000000 #define PCI0_IO_SIZE 0x10000 +#define PCI1_CFG_ADDR 0xc78 #define PCI1_MEM_BASE 0x80000000 #define PCI1_MEM_SIZE 0x40000000 #define PCI1_IO_BASE 0xfe000000 @@ -117,6 +122,10 @@ static void pegasos2_init(MachineState *machine) qemu_register_reset(pegasos2_cpu_reset, pm->cpu); /* RAM */ + if (machine->ram_size > 2 * GiB) { + error_report("RAM size more than 2 GiB is not supported"); + exit(1); + } memory_region_add_subregion(get_system_memory(), 0, machine->ram); /* allocate and load firmware */ @@ -190,62 +199,58 @@ static void pegasos2_init(MachineState *machine) if (!pm->vof) { warn_report("Option -kernel may be ineffective with -bios."); } + } else if (pm->vof) { + warn_report("Using Virtual OpenFirmware but no -kernel option."); } + if (!pm->vof && machine->kernel_cmdline && machine->kernel_cmdline[0]) { warn_report("Option -append may be ineffective with -bios."); } } -static uint32_t pegasos2_pci_config_read(AddressSpace *as, int bus, +static uint32_t pegasos2_mv_reg_read(Pegasos2MachineState *pm, + uint32_t addr, uint32_t len) +{ + MemoryRegion *r = sysbus_mmio_get_region(SYS_BUS_DEVICE(pm->mv), 0); + uint64_t val = 0xffffffffULL; + memory_region_dispatch_read(r, addr, &val, size_memop(len) | MO_LE, + MEMTXATTRS_UNSPECIFIED); + return val; +} + +static void pegasos2_mv_reg_write(Pegasos2MachineState *pm, uint32_t addr, + uint32_t len, uint32_t val) +{ + MemoryRegion *r = sysbus_mmio_get_region(SYS_BUS_DEVICE(pm->mv), 0); + memory_region_dispatch_write(r, addr, val, size_memop(len) | MO_LE, + MEMTXATTRS_UNSPECIFIED); +} + +static uint32_t pegasos2_pci_config_read(Pegasos2MachineState *pm, int bus, uint32_t addr, uint32_t len) { - hwaddr pcicfg = (bus ? 0xf1000c78 : 0xf1000cf8); - uint32_t val = 0xffffffff; - - stl_le_phys(as, pcicfg, addr | BIT(31)); - switch (len) { - case 4: - val = ldl_le_phys(as, pcicfg + 4); - break; - case 2: - val = lduw_le_phys(as, pcicfg + 4); - break; - case 1: - val = ldub_phys(as, pcicfg + 4); - break; - default: - qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid length\n", __func__); - break; + hwaddr pcicfg = bus ? PCI1_CFG_ADDR : PCI0_CFG_ADDR; + uint64_t val = 0xffffffffULL; + + if (len <= 4) { + pegasos2_mv_reg_write(pm, pcicfg, 4, addr | BIT(31)); + val = pegasos2_mv_reg_read(pm, pcicfg + 4, len); } return val; } -static void pegasos2_pci_config_write(AddressSpace *as, int bus, uint32_t addr, - uint32_t len, uint32_t val) +static void pegasos2_pci_config_write(Pegasos2MachineState *pm, int bus, + uint32_t addr, uint32_t len, uint32_t val) { - hwaddr pcicfg = (bus ? 0xf1000c78 : 0xf1000cf8); - - stl_le_phys(as, pcicfg, addr | BIT(31)); - switch (len) { - case 4: - stl_le_phys(as, pcicfg + 4, val); - break; - case 2: - stw_le_phys(as, pcicfg + 4, val); - break; - case 1: - stb_phys(as, pcicfg + 4, val); - break; - default: - qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid length\n", __func__); - break; - } + hwaddr pcicfg = bus ? PCI1_CFG_ADDR : PCI0_CFG_ADDR; + + pegasos2_mv_reg_write(pm, pcicfg, 4, addr | BIT(31)); + pegasos2_mv_reg_write(pm, pcicfg + 4, len, val); } static void pegasos2_machine_reset(MachineState *machine) { Pegasos2MachineState *pm = PEGASOS2_MACHINE(machine); - AddressSpace *as = CPU(pm->cpu)->as; void *fdt; uint64_t d[2]; int sz; @@ -256,51 +261,51 @@ static void pegasos2_machine_reset(MachineState *machine) } /* Otherwise, set up devices that board firmware would normally do */ - stl_le_phys(as, 0xf1000000, 0x28020ff); - stl_le_phys(as, 0xf1000278, 0xa31fc); - stl_le_phys(as, 0xf100f300, 0x11ff0400); - stl_le_phys(as, 0xf100f10c, 0x80000000); - stl_le_phys(as, 0xf100001c, 0x8000000); - pegasos2_pci_config_write(as, 0, PCI_COMMAND, 2, PCI_COMMAND_IO | + pegasos2_mv_reg_write(pm, 0, 4, 0x28020ff); + pegasos2_mv_reg_write(pm, 0x278, 4, 0xa31fc); + pegasos2_mv_reg_write(pm, 0xf300, 4, 0x11ff0400); + pegasos2_mv_reg_write(pm, 0xf10c, 4, 0x80000000); + pegasos2_mv_reg_write(pm, 0x1c, 4, 0x8000000); + pegasos2_pci_config_write(pm, 0, PCI_COMMAND, 2, PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); - pegasos2_pci_config_write(as, 1, PCI_COMMAND, 2, PCI_COMMAND_IO | + pegasos2_pci_config_write(pm, 1, PCI_COMMAND, 2, PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); - pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 0) << 8) | + pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 0) << 8) | PCI_INTERRUPT_LINE, 2, 0x9); - pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 0) << 8) | + pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 0) << 8) | 0x50, 1, 0x2); - pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 1) << 8) | + pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 1) << 8) | PCI_INTERRUPT_LINE, 2, 0x109); - pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 1) << 8) | + pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 1) << 8) | PCI_CLASS_PROG, 1, 0xf); - pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 1) << 8) | + pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 1) << 8) | 0x40, 1, 0xb); - pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 1) << 8) | + pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 1) << 8) | 0x50, 4, 0x17171717); - pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 1) << 8) | + pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 1) << 8) | PCI_COMMAND, 2, 0x87); - pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 2) << 8) | + pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 2) << 8) | PCI_INTERRUPT_LINE, 2, 0x409); - pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 3) << 8) | + pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 3) << 8) | PCI_INTERRUPT_LINE, 2, 0x409); - pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 4) << 8) | + pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 4) << 8) | PCI_INTERRUPT_LINE, 2, 0x9); - pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 4) << 8) | + pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 4) << 8) | 0x48, 4, 0xf00); - pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 4) << 8) | + pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 4) << 8) | 0x40, 4, 0x558020); - pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 4) << 8) | + pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 4) << 8) | 0x90, 4, 0xd00); - pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 5) << 8) | + pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 5) << 8) | PCI_INTERRUPT_LINE, 2, 0x309); - pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 6) << 8) | + pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 6) << 8) | PCI_INTERRUPT_LINE, 2, 0x309); /* Device tree and VOF set up */ @@ -362,6 +367,29 @@ static target_ulong pegasos2_rtas(PowerPCCPU *cpu, Pegasos2MachineState *pm, return H_PARAMETER; } switch (token) { + case RTAS_GET_TIME_OF_DAY: + { + QObject *qo = object_property_get_qobject(qdev_get_machine(), + "rtc-time", &error_fatal); + QDict *qd = qobject_to(QDict, qo); + + if (nargs != 0 || nrets != 8 || !qd) { + stl_be_phys(as, rets, -1); + qobject_unref(qo); + return H_PARAMETER; + } + + stl_be_phys(as, rets, 0); + stl_be_phys(as, rets + 4, qdict_get_int(qd, "tm_year") + 1900); + stl_be_phys(as, rets + 8, qdict_get_int(qd, "tm_mon") + 1); + stl_be_phys(as, rets + 12, qdict_get_int(qd, "tm_mday")); + stl_be_phys(as, rets + 16, qdict_get_int(qd, "tm_hour")); + stl_be_phys(as, rets + 20, qdict_get_int(qd, "tm_min")); + stl_be_phys(as, rets + 24, qdict_get_int(qd, "tm_sec")); + stl_be_phys(as, rets + 28, 0); + qobject_unref(qo); + return H_SUCCESS; + } case RTAS_READ_PCI_CONFIG: { uint32_t addr, len, val; @@ -372,7 +400,7 @@ static target_ulong pegasos2_rtas(PowerPCCPU *cpu, Pegasos2MachineState *pm, } addr = ldl_be_phys(as, args); len = ldl_be_phys(as, args + 4); - val = pegasos2_pci_config_read(as, !(addr >> 24), + val = pegasos2_pci_config_read(pm, !(addr >> 24), addr & 0x0fffffff, len); stl_be_phys(as, rets, 0); stl_be_phys(as, rets + 4, val); @@ -389,7 +417,7 @@ static target_ulong pegasos2_rtas(PowerPCCPU *cpu, Pegasos2MachineState *pm, addr = ldl_be_phys(as, args); len = ldl_be_phys(as, args + 4); val = ldl_be_phys(as, args + 8); - pegasos2_pci_config_write(as, !(addr >> 24), + pegasos2_pci_config_write(pm, !(addr >> 24), addr & 0x0fffffff, len, val); stl_be_phys(as, rets, 0); return H_SUCCESS; @@ -402,6 +430,16 @@ static target_ulong pegasos2_rtas(PowerPCCPU *cpu, Pegasos2MachineState *pm, qemu_log_mask(LOG_UNIMP, "%c", ldl_be_phys(as, args)); stl_be_phys(as, rets, 0); return H_SUCCESS; + case RTAS_POWER_OFF: + { + if (nargs != 2 || nrets != 1) { + stl_be_phys(as, rets, -1); + return H_PARAMETER; + } + qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); + stl_be_phys(as, rets, 0); + return H_SUCCESS; + } default: qemu_log_mask(LOG_UNIMP, "Unknown RTAS token %u (args=%u, rets=%u)\n", token, nargs, nrets); diff --git a/hw/ppc/ppc.c b/hw/ppc/ppc.c index f5d012f860..e8127599c9 100644 --- a/hw/ppc/ppc.c +++ b/hw/ppc/ppc.c @@ -336,6 +336,8 @@ void store_40x_dbcr0(CPUPPCState *env, uint32_t val) { PowerPCCPU *cpu = env_archcpu(env); + qemu_mutex_lock_iothread(); + switch ((val >> 28) & 0x3) { case 0x0: /* No action */ @@ -353,6 +355,8 @@ void store_40x_dbcr0(CPUPPCState *env, uint32_t val) ppc40x_system_reset(cpu); break; } + + qemu_mutex_unlock_iothread(); } /* PowerPC 40x internal IRQ controller */ @@ -848,7 +852,7 @@ static void __cpu_ppc_store_decr(PowerPCCPU *cpu, uint64_t *nextp, * On MSB edge based DEC implementations the MSB going from 0 -> 1 triggers * an edge interrupt, so raise it here too. */ - if ((signed_value < 3) || + if ((value < 3) || ((tb_env->flags & PPC_DECR_UNDERFLOW_LEVEL) && signed_value < 0) || ((tb_env->flags & PPC_DECR_UNDERFLOW_TRIGGERED) && signed_value < 0 && signed_decr >= 0)) { diff --git a/hw/ppc/ppc4xx_pci.c b/hw/ppc/ppc4xx_pci.c index 8147ba6f94..304a29349c 100644 --- a/hw/ppc/ppc4xx_pci.c +++ b/hw/ppc/ppc4xx_pci.c @@ -48,12 +48,14 @@ OBJECT_DECLARE_SIMPLE_TYPE(PPC4xxPCIState, PPC4xx_PCI_HOST_BRIDGE) #define PPC4xx_PCI_NR_PMMS 3 #define PPC4xx_PCI_NR_PTMS 2 +#define PPC4xx_PCI_NUM_DEVS 5 + struct PPC4xxPCIState { PCIHostState parent_obj; struct PCIMasterMap pmm[PPC4xx_PCI_NR_PMMS]; struct PCITargetMap ptm[PPC4xx_PCI_NR_PTMS]; - qemu_irq irq[PCI_NUM_PINS]; + qemu_irq irq[PPC4xx_PCI_NUM_DEVS]; MemoryRegion container; MemoryRegion iomem; @@ -246,7 +248,7 @@ static int ppc4xx_pci_map_irq(PCIDevice *pci_dev, int irq_num) trace_ppc4xx_pci_map_irq(pci_dev->devfn, irq_num, slot); - return slot - 1; + return slot > 0 ? slot - 1 : PPC4xx_PCI_NUM_DEVS - 1; } static void ppc4xx_pci_set_irq(void *opaque, int irq_num, int level) @@ -254,7 +256,7 @@ static void ppc4xx_pci_set_irq(void *opaque, int irq_num, int level) qemu_irq *pci_irqs = opaque; trace_ppc4xx_pci_set_irq(irq_num); - assert(irq_num >= 0); + assert(irq_num >= 0 && irq_num < PPC4xx_PCI_NUM_DEVS); qemu_set_irq(pci_irqs[irq_num], level); } diff --git a/hw/ppc/spapr_softmmu.c b/hw/ppc/spapr_softmmu.c index 6c6b86dd3c..f8924270ef 100644 --- a/hw/ppc/spapr_softmmu.c +++ b/hw/ppc/spapr_softmmu.c @@ -1,25 +1,10 @@ #include "qemu/osdep.h" #include "qemu/cutils.h" -#include "qapi/error.h" -#include "sysemu/hw_accel.h" -#include "sysemu/runstate.h" -#include "qemu/log.h" -#include "qemu/main-loop.h" -#include "qemu/module.h" -#include "qemu/error-report.h" #include "cpu.h" -#include "exec/exec-all.h" #include "helper_regs.h" #include "hw/ppc/spapr.h" -#include "hw/ppc/spapr_cpu_core.h" #include "mmu-hash64.h" -#include "cpu-models.h" -#include "trace.h" -#include "kvm_ppc.h" -#include "hw/ppc/fdt.h" -#include "hw/ppc/spapr_ovec.h" #include "mmu-book3s-v3.h" -#include "hw/mem/memory-device.h" static inline bool valid_ptex(PowerPCCPU *cpu, target_ulong ptex) { diff --git a/hw/virtio/Kconfig b/hw/virtio/Kconfig index 35ab45e209..c144d42f9b 100644 --- a/hw/virtio/Kconfig +++ b/hw/virtio/Kconfig @@ -63,3 +63,8 @@ config VHOST_USER_I2C bool default y depends on VIRTIO && VHOST_USER + +config VHOST_USER_RNG + bool + default y + depends on VIRTIO && VHOST_USER diff --git a/hw/virtio/meson.build b/hw/virtio/meson.build index bc352a6009..521f7d64a8 100644 --- a/hw/virtio/meson.build +++ b/hw/virtio/meson.build @@ -27,6 +27,8 @@ virtio_ss.add(when: 'CONFIG_VIRTIO_IOMMU', if_true: files('virtio-iommu.c')) virtio_ss.add(when: 'CONFIG_VIRTIO_MEM', if_true: files('virtio-mem.c')) virtio_ss.add(when: 'CONFIG_VHOST_USER_I2C', if_true: files('vhost-user-i2c.c')) virtio_ss.add(when: ['CONFIG_VIRTIO_PCI', 'CONFIG_VHOST_USER_I2C'], if_true: files('vhost-user-i2c-pci.c')) +virtio_ss.add(when: 'CONFIG_VHOST_USER_RNG', if_true: files('vhost-user-rng.c')) +virtio_ss.add(when: ['CONFIG_VHOST_USER_RNG', 'CONFIG_VIRTIO_PCI'], if_true: files('vhost-user-rng-pci.c')) virtio_pci_ss = ss.source_set() virtio_pci_ss.add(when: 'CONFIG_VHOST_VSOCK', if_true: files('vhost-vsock-pci.c')) diff --git a/hw/virtio/trace-events b/hw/virtio/trace-events index 8ed19e9d0c..650e521e35 100644 --- a/hw/virtio/trace-events +++ b/hw/virtio/trace-events @@ -52,6 +52,7 @@ vhost_vdpa_set_vring_call(void *dev, unsigned int index, int fd) "dev: %p index: vhost_vdpa_get_features(void *dev, uint64_t features) "dev: %p features: 0x%"PRIx64 vhost_vdpa_set_owner(void *dev) "dev: %p" vhost_vdpa_vq_get_addr(void *dev, void *vq, uint64_t desc_user_addr, uint64_t avail_user_addr, uint64_t used_user_addr) "dev: %p vq: %p desc_user_addr: 0x%"PRIx64" avail_user_addr: 0x%"PRIx64" used_user_addr: 0x%"PRIx64 +vhost_vdpa_get_iova_range(void *dev, uint64_t first, uint64_t last) "dev: %p first: 0x%"PRIx64" last: 0x%"PRIx64 # virtio.c virtqueue_alloc_element(void *elem, size_t sz, unsigned in_num, unsigned out_num) "elem %p size %zd in_num %u out_num %u" diff --git a/hw/virtio/vhost-user-rng-pci.c b/hw/virtio/vhost-user-rng-pci.c new file mode 100644 index 0000000000..c83dc86813 --- /dev/null +++ b/hw/virtio/vhost-user-rng-pci.c @@ -0,0 +1,79 @@ +/* + * Vhost-user RNG virtio device PCI glue + * + * Copyright (c) 2021 Mathieu Poirier <mathieu.poirier@linaro.org> + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "hw/qdev-properties.h" +#include "hw/virtio/vhost-user-rng.h" +#include "virtio-pci.h" + +struct VHostUserRNGPCI { + VirtIOPCIProxy parent_obj; + VHostUserRNG vdev; +}; + +typedef struct VHostUserRNGPCI VHostUserRNGPCI; + +#define TYPE_VHOST_USER_RNG_PCI "vhost-user-rng-pci-base" + +DECLARE_INSTANCE_CHECKER(VHostUserRNGPCI, VHOST_USER_RNG_PCI, + TYPE_VHOST_USER_RNG_PCI) + +static Property vhost_user_rng_pci_properties[] = { + DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, + DEV_NVECTORS_UNSPECIFIED), + DEFINE_PROP_END_OF_LIST(), +}; + +static void vhost_user_rng_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) +{ + VHostUserRNGPCI *dev = VHOST_USER_RNG_PCI(vpci_dev); + DeviceState *vdev = DEVICE(&dev->vdev); + + if (vpci_dev->nvectors == DEV_NVECTORS_UNSPECIFIED) { + vpci_dev->nvectors = 1; + } + + qdev_realize(vdev, BUS(&vpci_dev->bus), errp); +} + +static void vhost_user_rng_pci_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); + PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); + k->realize = vhost_user_rng_pci_realize; + set_bit(DEVICE_CATEGORY_INPUT, dc->categories); + device_class_set_props(dc, vhost_user_rng_pci_properties); + pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; + pcidev_k->device_id = 0; /* Set by virtio-pci based on virtio id */ + pcidev_k->revision = 0x00; + pcidev_k->class_id = PCI_CLASS_OTHERS; +} + +static void vhost_user_rng_pci_instance_init(Object *obj) +{ + VHostUserRNGPCI *dev = VHOST_USER_RNG_PCI(obj); + + virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), + TYPE_VHOST_USER_RNG); +} + +static const VirtioPCIDeviceTypeInfo vhost_user_rng_pci_info = { + .base_name = TYPE_VHOST_USER_RNG_PCI, + .non_transitional_name = "vhost-user-rng-pci", + .instance_size = sizeof(VHostUserRNGPCI), + .instance_init = vhost_user_rng_pci_instance_init, + .class_init = vhost_user_rng_pci_class_init, +}; + +static void vhost_user_rng_pci_register(void) +{ + virtio_pci_types_register(&vhost_user_rng_pci_info); +} + +type_init(vhost_user_rng_pci_register); diff --git a/hw/virtio/vhost-user-rng.c b/hw/virtio/vhost-user-rng.c new file mode 100644 index 0000000000..209ee5bf9a --- /dev/null +++ b/hw/virtio/vhost-user-rng.c @@ -0,0 +1,289 @@ +/* + * Vhost-user RNG virtio device + * + * Copyright (c) 2021 Mathieu Poirier <mathieu.poirier@linaro.org> + * + * Implementation seriously tailored on vhost-user-i2c.c + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/qdev-properties.h" +#include "hw/virtio/virtio-bus.h" +#include "hw/virtio/vhost-user-rng.h" +#include "qemu/error-report.h" +#include "standard-headers/linux/virtio_ids.h" + +static void vu_rng_start(VirtIODevice *vdev) +{ + VHostUserRNG *rng = VHOST_USER_RNG(vdev); + BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); + VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); + int ret; + int i; + + if (!k->set_guest_notifiers) { + error_report("binding does not support guest notifiers"); + return; + } + + ret = vhost_dev_enable_notifiers(&rng->vhost_dev, vdev); + if (ret < 0) { + error_report("Error enabling host notifiers: %d", -ret); + return; + } + + ret = k->set_guest_notifiers(qbus->parent, rng->vhost_dev.nvqs, true); + if (ret < 0) { + error_report("Error binding guest notifier: %d", -ret); + goto err_host_notifiers; + } + + rng->vhost_dev.acked_features = vdev->guest_features; + ret = vhost_dev_start(&rng->vhost_dev, vdev); + if (ret < 0) { + error_report("Error starting vhost-user-rng: %d", -ret); + goto err_guest_notifiers; + } + + /* + * guest_notifier_mask/pending not used yet, so just unmask + * everything here. virtio-pci will do the right thing by + * enabling/disabling irqfd. + */ + for (i = 0; i < rng->vhost_dev.nvqs; i++) { + vhost_virtqueue_mask(&rng->vhost_dev, vdev, i, false); + } + + return; + +err_guest_notifiers: + k->set_guest_notifiers(qbus->parent, rng->vhost_dev.nvqs, false); +err_host_notifiers: + vhost_dev_disable_notifiers(&rng->vhost_dev, vdev); +} + +static void vu_rng_stop(VirtIODevice *vdev) +{ + VHostUserRNG *rng = VHOST_USER_RNG(vdev); + BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); + VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); + int ret; + + if (!k->set_guest_notifiers) { + return; + } + + vhost_dev_stop(&rng->vhost_dev, vdev); + + ret = k->set_guest_notifiers(qbus->parent, rng->vhost_dev.nvqs, false); + if (ret < 0) { + error_report("vhost guest notifier cleanup failed: %d", ret); + return; + } + + vhost_dev_disable_notifiers(&rng->vhost_dev, vdev); +} + +static void vu_rng_set_status(VirtIODevice *vdev, uint8_t status) +{ + VHostUserRNG *rng = VHOST_USER_RNG(vdev); + bool should_start = status & VIRTIO_CONFIG_S_DRIVER_OK; + + if (!vdev->vm_running) { + should_start = false; + } + + if (rng->vhost_dev.started == should_start) { + return; + } + + if (should_start) { + vu_rng_start(vdev); + } else { + vu_rng_stop(vdev); + } +} + +static uint64_t vu_rng_get_features(VirtIODevice *vdev, + uint64_t requested_features, Error **errp) +{ + /* No feature bits used yet */ + return requested_features; +} + +static void vu_rng_handle_output(VirtIODevice *vdev, VirtQueue *vq) +{ + /* + * Not normally called; it's the daemon that handles the queue; + * however virtio's cleanup path can call this. + */ +} + +static void vu_rng_guest_notifier_mask(VirtIODevice *vdev, int idx, bool mask) +{ + VHostUserRNG *rng = VHOST_USER_RNG(vdev); + + vhost_virtqueue_mask(&rng->vhost_dev, vdev, idx, mask); +} + +static bool vu_rng_guest_notifier_pending(VirtIODevice *vdev, int idx) +{ + VHostUserRNG *rng = VHOST_USER_RNG(vdev); + + return vhost_virtqueue_pending(&rng->vhost_dev, idx); +} + +static void vu_rng_connect(DeviceState *dev) +{ + VirtIODevice *vdev = VIRTIO_DEVICE(dev); + VHostUserRNG *rng = VHOST_USER_RNG(vdev); + + if (rng->connected) { + return; + } + + rng->connected = true; + + /* restore vhost state */ + if (virtio_device_started(vdev, vdev->status)) { + vu_rng_start(vdev); + } +} + +static void vu_rng_disconnect(DeviceState *dev) +{ + VirtIODevice *vdev = VIRTIO_DEVICE(dev); + VHostUserRNG *rng = VHOST_USER_RNG(vdev); + + if (!rng->connected) { + return; + } + + rng->connected = false; + + if (rng->vhost_dev.started) { + vu_rng_stop(vdev); + } +} + +static void vu_rng_event(void *opaque, QEMUChrEvent event) +{ + DeviceState *dev = opaque; + + switch (event) { + case CHR_EVENT_OPENED: + vu_rng_connect(dev); + break; + case CHR_EVENT_CLOSED: + vu_rng_disconnect(dev); + break; + case CHR_EVENT_BREAK: + case CHR_EVENT_MUX_IN: + case CHR_EVENT_MUX_OUT: + /* Ignore */ + break; + } +} + +static void vu_rng_device_realize(DeviceState *dev, Error **errp) +{ + VirtIODevice *vdev = VIRTIO_DEVICE(dev); + VHostUserRNG *rng = VHOST_USER_RNG(dev); + int ret; + + if (!rng->chardev.chr) { + error_setg(errp, "missing chardev"); + return; + } + + if (!vhost_user_init(&rng->vhost_user, &rng->chardev, errp)) { + return; + } + + virtio_init(vdev, "vhost-user-rng", VIRTIO_ID_RNG, 0); + + rng->req_vq = virtio_add_queue(vdev, 4, vu_rng_handle_output); + if (!rng->req_vq) { + error_setg_errno(errp, -1, "virtio_add_queue() failed"); + goto virtio_add_queue_failed; + } + + rng->vhost_dev.nvqs = 1; + rng->vhost_dev.vqs = g_new0(struct vhost_virtqueue, rng->vhost_dev.nvqs); + ret = vhost_dev_init(&rng->vhost_dev, &rng->vhost_user, + VHOST_BACKEND_TYPE_USER, 0, errp); + if (ret < 0) { + error_setg_errno(errp, -ret, "vhost_dev_init() failed"); + goto vhost_dev_init_failed; + } + + qemu_chr_fe_set_handlers(&rng->chardev, NULL, NULL, vu_rng_event, NULL, + dev, NULL, true); + + return; + +vhost_dev_init_failed: + virtio_delete_queue(rng->req_vq); +virtio_add_queue_failed: + virtio_cleanup(vdev); + vhost_user_cleanup(&rng->vhost_user); +} + +static void vu_rng_device_unrealize(DeviceState *dev) +{ + VirtIODevice *vdev = VIRTIO_DEVICE(dev); + VHostUserRNG *rng = VHOST_USER_RNG(dev); + + vu_rng_set_status(vdev, 0); + + vhost_dev_cleanup(&rng->vhost_dev); + g_free(rng->vhost_dev.vqs); + rng->vhost_dev.vqs = NULL; + virtio_delete_queue(rng->req_vq); + virtio_cleanup(vdev); + vhost_user_cleanup(&rng->vhost_user); +} + +static const VMStateDescription vu_rng_vmstate = { + .name = "vhost-user-rng", + .unmigratable = 1, +}; + +static Property vu_rng_properties[] = { + DEFINE_PROP_CHR("chardev", VHostUserRNG, chardev), + DEFINE_PROP_END_OF_LIST(), +}; + +static void vu_rng_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); + + device_class_set_props(dc, vu_rng_properties); + dc->vmsd = &vu_rng_vmstate; + set_bit(DEVICE_CATEGORY_INPUT, dc->categories); + + vdc->realize = vu_rng_device_realize; + vdc->unrealize = vu_rng_device_unrealize; + vdc->get_features = vu_rng_get_features; + vdc->set_status = vu_rng_set_status; + vdc->guest_notifier_mask = vu_rng_guest_notifier_mask; + vdc->guest_notifier_pending = vu_rng_guest_notifier_pending; +} + +static const TypeInfo vu_rng_info = { + .name = TYPE_VHOST_USER_RNG, + .parent = TYPE_VIRTIO_DEVICE, + .instance_size = sizeof(VHostUserRNG), + .class_init = vu_rng_class_init, +}; + +static void vu_rng_register_types(void) +{ + type_register_static(&vu_rng_info); +} + +type_init(vu_rng_register_types) diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c index 2c8556237f..bf6e50223c 100644 --- a/hw/virtio/vhost-user.c +++ b/hw/virtio/vhost-user.c @@ -1526,8 +1526,9 @@ static int vhost_user_slave_handle_vring_host_notifier(struct vhost_dev *dev, name = g_strdup_printf("vhost-user/host-notifier@%p mmaps[%d]", user, queue_idx); - memory_region_init_ram_device_ptr(&n->mr, OBJECT(vdev), name, - page_size, addr); + if (!n->mr.ram) /* Don't init again after suspend. */ + memory_region_init_ram_device_ptr(&n->mr, OBJECT(vdev), name, + page_size, addr); g_free(name); if (virtio_queue_set_host_notifier_mr(vdev, queue_idx, &n->mr, true)) { diff --git a/hw/virtio/vhost-vdpa.c b/hw/virtio/vhost-vdpa.c index 47d7a5a23d..12661fd5b1 100644 --- a/hw/virtio/vhost-vdpa.c +++ b/hw/virtio/vhost-vdpa.c @@ -24,19 +24,49 @@ #include "trace.h" #include "qemu-common.h" -static bool vhost_vdpa_listener_skipped_section(MemoryRegionSection *section) +/* + * Return one past the end of the end of section. Be careful with uint64_t + * conversions! + */ +static Int128 vhost_vdpa_section_end(const MemoryRegionSection *section) +{ + Int128 llend = int128_make64(section->offset_within_address_space); + llend = int128_add(llend, section->size); + llend = int128_and(llend, int128_exts64(TARGET_PAGE_MASK)); + + return llend; +} + +static bool vhost_vdpa_listener_skipped_section(MemoryRegionSection *section, + uint64_t iova_min, + uint64_t iova_max) { - return (!memory_region_is_ram(section->mr) && - !memory_region_is_iommu(section->mr)) || - /* vhost-vDPA doesn't allow MMIO to be mapped */ - memory_region_is_ram_device(section->mr) || - /* - * Sizing an enabled 64-bit BAR can cause spurious mappings to - * addresses in the upper part of the 64-bit address space. These - * are never accessed by the CPU and beyond the address width of - * some IOMMU hardware. TODO: VDPA should tell us the IOMMU width. - */ - section->offset_within_address_space & (1ULL << 63); + Int128 llend; + + if ((!memory_region_is_ram(section->mr) && + !memory_region_is_iommu(section->mr)) || + memory_region_is_protected(section->mr) || + /* vhost-vDPA doesn't allow MMIO to be mapped */ + memory_region_is_ram_device(section->mr)) { + return true; + } + + if (section->offset_within_address_space < iova_min) { + error_report("RAM section out of device range (min=0x%" PRIx64 + ", addr=0x%" HWADDR_PRIx ")", + iova_min, section->offset_within_address_space); + return true; + } + + llend = vhost_vdpa_section_end(section); + if (int128_gt(llend, int128_make64(iova_max))) { + error_report("RAM section out of device range (max=0x%" PRIx64 + ", end addr=0x%" PRIx64 ")", + iova_max, int128_get64(llend)); + return true; + } + + return false; } static int vhost_vdpa_dma_map(struct vhost_vdpa *v, hwaddr iova, hwaddr size, @@ -148,7 +178,8 @@ static void vhost_vdpa_listener_region_add(MemoryListener *listener, void *vaddr; int ret; - if (vhost_vdpa_listener_skipped_section(section)) { + if (vhost_vdpa_listener_skipped_section(section, v->iova_range.first, + v->iova_range.last)) { return; } @@ -159,10 +190,7 @@ static void vhost_vdpa_listener_region_add(MemoryListener *listener, } iova = TARGET_PAGE_ALIGN(section->offset_within_address_space); - llend = int128_make64(section->offset_within_address_space); - llend = int128_add(llend, section->size); - llend = int128_and(llend, int128_exts64(TARGET_PAGE_MASK)); - + llend = vhost_vdpa_section_end(section); if (int128_ge(int128_make64(iova), llend)) { return; } @@ -209,7 +237,8 @@ static void vhost_vdpa_listener_region_del(MemoryListener *listener, Int128 llend, llsize; int ret; - if (vhost_vdpa_listener_skipped_section(section)) { + if (vhost_vdpa_listener_skipped_section(section, v->iova_range.first, + v->iova_range.last)) { return; } @@ -220,9 +249,7 @@ static void vhost_vdpa_listener_region_del(MemoryListener *listener, } iova = TARGET_PAGE_ALIGN(section->offset_within_address_space); - llend = int128_make64(section->offset_within_address_space); - llend = int128_add(llend, section->size); - llend = int128_and(llend, int128_exts64(TARGET_PAGE_MASK)); + llend = vhost_vdpa_section_end(section); trace_vhost_vdpa_listener_region_del(v, iova, int128_get64(llend)); @@ -279,6 +306,26 @@ static void vhost_vdpa_add_status(struct vhost_dev *dev, uint8_t status) vhost_vdpa_call(dev, VHOST_VDPA_SET_STATUS, &s); } +static void vhost_vdpa_get_iova_range(struct vhost_vdpa *v) +{ + int ret = vhost_vdpa_call(v->dev, VHOST_VDPA_GET_IOVA_RANGE, + &v->iova_range); + if (ret != 0) { + v->iova_range.first = 0; + v->iova_range.last = UINT64_MAX; + } + + trace_vhost_vdpa_get_iova_range(v->dev, v->iova_range.first, + v->iova_range.last); +} + +static bool vhost_vdpa_one_time_request(struct vhost_dev *dev) +{ + struct vhost_vdpa *v = dev->opaque; + + return v->index != 0; +} + static int vhost_vdpa_init(struct vhost_dev *dev, void *opaque, Error **errp) { struct vhost_vdpa *v; @@ -291,6 +338,12 @@ static int vhost_vdpa_init(struct vhost_dev *dev, void *opaque, Error **errp) v->listener = vhost_vdpa_memory_listener; v->msg_type = VHOST_IOTLB_MSG_V2; + vhost_vdpa_get_iova_range(v); + + if (vhost_vdpa_one_time_request(dev)) { + return 0; + } + vhost_vdpa_add_status(dev, VIRTIO_CONFIG_S_ACKNOWLEDGE | VIRTIO_CONFIG_S_DRIVER); @@ -401,6 +454,10 @@ static int vhost_vdpa_memslots_limit(struct vhost_dev *dev) static int vhost_vdpa_set_mem_table(struct vhost_dev *dev, struct vhost_memory *mem) { + if (vhost_vdpa_one_time_request(dev)) { + return 0; + } + trace_vhost_vdpa_set_mem_table(dev, mem->nregions, mem->padding); if (trace_event_get_state_backends(TRACE_VHOST_VDPA_SET_MEM_TABLE) && trace_event_get_state_backends(TRACE_VHOST_VDPA_DUMP_REGIONS)) { @@ -424,6 +481,11 @@ static int vhost_vdpa_set_features(struct vhost_dev *dev, uint64_t features) { int ret; + + if (vhost_vdpa_one_time_request(dev)) { + return 0; + } + trace_vhost_vdpa_set_features(dev, features); ret = vhost_vdpa_call(dev, VHOST_SET_FEATURES, &features); uint8_t status = 0; @@ -448,9 +510,12 @@ static int vhost_vdpa_set_backend_cap(struct vhost_dev *dev) } features &= f; - r = vhost_vdpa_call(dev, VHOST_SET_BACKEND_FEATURES, &features); - if (r) { - return -EFAULT; + + if (vhost_vdpa_one_time_request(dev)) { + r = vhost_vdpa_call(dev, VHOST_SET_BACKEND_FEATURES, &features); + if (r) { + return -EFAULT; + } } dev->backend_cap = features; @@ -481,8 +546,8 @@ static int vhost_vdpa_get_vq_index(struct vhost_dev *dev, int idx) { assert(idx >= dev->vq_index && idx < dev->vq_index + dev->nvqs); - trace_vhost_vdpa_get_vq_index(dev, idx, idx - dev->vq_index); - return idx - dev->vq_index; + trace_vhost_vdpa_get_vq_index(dev, idx, idx); + return idx; } static int vhost_vdpa_set_vring_ready(struct vhost_dev *dev) @@ -559,11 +624,21 @@ static int vhost_vdpa_dev_start(struct vhost_dev *dev, bool started) { struct vhost_vdpa *v = dev->opaque; trace_vhost_vdpa_dev_start(dev, started); + if (started) { - uint8_t status = 0; - memory_listener_register(&v->listener, &address_space_memory); vhost_vdpa_host_notifiers_init(dev); vhost_vdpa_set_vring_ready(dev); + } else { + vhost_vdpa_host_notifiers_uninit(dev, dev->nvqs); + } + + if (dev->vq_index + dev->nvqs != dev->last_index) { + return 0; + } + + if (started) { + uint8_t status = 0; + memory_listener_register(&v->listener, &address_space_memory); vhost_vdpa_add_status(dev, VIRTIO_CONFIG_S_DRIVER_OK); vhost_vdpa_call(dev, VHOST_VDPA_GET_STATUS, &status); @@ -572,7 +647,6 @@ static int vhost_vdpa_dev_start(struct vhost_dev *dev, bool started) vhost_vdpa_reset_device(dev); vhost_vdpa_add_status(dev, VIRTIO_CONFIG_S_ACKNOWLEDGE | VIRTIO_CONFIG_S_DRIVER); - vhost_vdpa_host_notifiers_uninit(dev, dev->nvqs); memory_listener_unregister(&v->listener); return 0; @@ -582,6 +656,10 @@ static int vhost_vdpa_dev_start(struct vhost_dev *dev, bool started) static int vhost_vdpa_set_log_base(struct vhost_dev *dev, uint64_t base, struct vhost_log *log) { + if (vhost_vdpa_one_time_request(dev)) { + return 0; + } + trace_vhost_vdpa_set_log_base(dev, base, log->size, log->refcnt, log->fd, log->log); return vhost_vdpa_call(dev, VHOST_SET_LOG_BASE, &base); @@ -647,6 +725,10 @@ static int vhost_vdpa_get_features(struct vhost_dev *dev, static int vhost_vdpa_set_owner(struct vhost_dev *dev) { + if (vhost_vdpa_one_time_request(dev)) { + return 0; + } + trace_vhost_vdpa_set_owner(dev); return vhost_vdpa_call(dev, VHOST_SET_OWNER, NULL); } diff --git a/hw/virtio/virtio-iommu-pci.c b/hw/virtio/virtio-iommu-pci.c index 770c286be7..a160ae6b41 100644 --- a/hw/virtio/virtio-iommu-pci.c +++ b/hw/virtio/virtio-iommu-pci.c @@ -98,9 +98,7 @@ static void virtio_iommu_pci_instance_init(Object *obj) } static const VirtioPCIDeviceTypeInfo virtio_iommu_pci_info = { - .base_name = TYPE_VIRTIO_IOMMU_PCI, - .generic_name = "virtio-iommu-pci", - .non_transitional_name = "virtio-iommu-pci-non-transitional", + .generic_name = TYPE_VIRTIO_IOMMU_PCI, .instance_size = sizeof(VirtIOIOMMUPCI), .instance_init = virtio_iommu_pci_instance_init, .class_init = virtio_iommu_pci_class_init, diff --git a/include/hw/acpi/aml-build.h b/include/hw/acpi/aml-build.h index 3cf6f2c1b9..8346003a22 100644 --- a/include/hw/acpi/aml-build.h +++ b/include/hw/acpi/aml-build.h @@ -489,6 +489,9 @@ void build_srat_memory(GArray *table_data, uint64_t base, void build_slit(GArray *table_data, BIOSLinker *linker, MachineState *ms, const char *oem_id, const char *oem_table_id); +void build_pptt(GArray *table_data, BIOSLinker *linker, MachineState *ms, + const char *oem_id, const char *oem_table_id); + void build_fadt(GArray *tbl, BIOSLinker *linker, const AcpiFadtData *f, const char *oem_id, const char *oem_table_id); diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h index b461b8d261..dc6b66ffc8 100644 --- a/include/hw/arm/virt.h +++ b/include/hw/arm/virt.h @@ -125,11 +125,13 @@ struct VirtMachineClass { bool claim_edge_triggered_timers; bool smbios_old_sys_ver; bool no_highmem_ecam; - bool no_ged; /* Machines < 4.2 has no support for ACPI GED device */ + bool no_ged; /* Machines < 4.2 have no support for ACPI GED device */ bool kvm_no_adjvtime; bool no_kvm_steal_time; bool acpi_expose_flash; bool no_secure_gpio; + /* Machines < 6.2 have no support for describing cpu topology to guest */ + bool no_cpu_topology; }; struct VirtMachineState { diff --git a/include/hw/elf_ops.h b/include/hw/elf_ops.h index 1c37cec4ae..995de8495c 100644 --- a/include/hw/elf_ops.h +++ b/include/hw/elf_ops.h @@ -312,25 +312,26 @@ static struct elf_note *glue(get_elf_note_type, SZ)(struct elf_note *nhdr, return nhdr; } -static int glue(load_elf, SZ)(const char *name, int fd, - uint64_t (*elf_note_fn)(void *, void *, bool), - uint64_t (*translate_fn)(void *, uint64_t), - void *translate_opaque, - int must_swab, uint64_t *pentry, - uint64_t *lowaddr, uint64_t *highaddr, - uint32_t *pflags, int elf_machine, - int clear_lsb, int data_swab, - AddressSpace *as, bool load_rom, - symbol_fn_t sym_cb) +static ssize_t glue(load_elf, SZ)(const char *name, int fd, + uint64_t (*elf_note_fn)(void *, void *, bool), + uint64_t (*translate_fn)(void *, uint64_t), + void *translate_opaque, + int must_swab, uint64_t *pentry, + uint64_t *lowaddr, uint64_t *highaddr, + uint32_t *pflags, int elf_machine, + int clear_lsb, int data_swab, + AddressSpace *as, bool load_rom, + symbol_fn_t sym_cb) { struct elfhdr ehdr; struct elf_phdr *phdr = NULL, *ph; - int size, i, total_size; + int size, i; + ssize_t total_size; elf_word mem_size, file_size, data_offset; uint64_t addr, low = (uint64_t)-1, high = 0; GMappedFile *mapped_file = NULL; uint8_t *data = NULL; - int ret = ELF_LOAD_FAILED; + ssize_t ret = ELF_LOAD_FAILED; if (read(fd, &ehdr, sizeof(ehdr)) != sizeof(ehdr)) goto fail; @@ -482,7 +483,7 @@ static int glue(load_elf, SZ)(const char *name, int fd, } } - if (mem_size > INT_MAX - total_size) { + if (mem_size > SSIZE_MAX - total_size) { ret = ELF_LOAD_TOO_BIG; goto fail; } diff --git a/include/hw/loader.h b/include/hw/loader.h index 81104cb02f..4fa485bd61 100644 --- a/include/hw/loader.h +++ b/include/hw/loader.h @@ -90,7 +90,7 @@ int load_image_gzipped(const char *filename, hwaddr addr, uint64_t max_sz); #define ELF_LOAD_WRONG_ARCH -3 #define ELF_LOAD_WRONG_ENDIAN -4 #define ELF_LOAD_TOO_BIG -5 -const char *load_elf_strerror(int error); +const char *load_elf_strerror(ssize_t error); /** load_elf_ram_sym: * @filename: Path of ELF file @@ -128,48 +128,48 @@ const char *load_elf_strerror(int error); typedef void (*symbol_fn_t)(const char *st_name, int st_info, uint64_t st_value, uint64_t st_size); -int load_elf_ram_sym(const char *filename, - uint64_t (*elf_note_fn)(void *, void *, bool), - uint64_t (*translate_fn)(void *, uint64_t), - void *translate_opaque, uint64_t *pentry, - uint64_t *lowaddr, uint64_t *highaddr, uint32_t *pflags, - int big_endian, int elf_machine, - int clear_lsb, int data_swab, - AddressSpace *as, bool load_rom, symbol_fn_t sym_cb); +ssize_t load_elf_ram_sym(const char *filename, + uint64_t (*elf_note_fn)(void *, void *, bool), + uint64_t (*translate_fn)(void *, uint64_t), + void *translate_opaque, uint64_t *pentry, + uint64_t *lowaddr, uint64_t *highaddr, + uint32_t *pflags, int big_endian, int elf_machine, + int clear_lsb, int data_swab, + AddressSpace *as, bool load_rom, symbol_fn_t sym_cb); /** load_elf_ram: * Same as load_elf_ram_sym(), but doesn't allow the caller to specify a * symbol callback function */ -int load_elf_ram(const char *filename, - uint64_t (*elf_note_fn)(void *, void *, bool), - uint64_t (*translate_fn)(void *, uint64_t), - void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr, - uint64_t *highaddr, uint32_t *pflags, int big_endian, - int elf_machine, int clear_lsb, int data_swab, - AddressSpace *as, bool load_rom); +ssize_t load_elf_ram(const char *filename, + uint64_t (*elf_note_fn)(void *, void *, bool), + uint64_t (*translate_fn)(void *, uint64_t), + void *translate_opaque, uint64_t *pentry, + uint64_t *lowaddr, uint64_t *highaddr, uint32_t *pflags, + int big_endian, int elf_machine, int clear_lsb, + int data_swab, AddressSpace *as, bool load_rom); /** load_elf_as: * Same as load_elf_ram(), but always loads the elf as ROM */ -int load_elf_as(const char *filename, - uint64_t (*elf_note_fn)(void *, void *, bool), - uint64_t (*translate_fn)(void *, uint64_t), - void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr, - uint64_t *highaddr, uint32_t *pflags, int big_endian, - int elf_machine, int clear_lsb, int data_swab, - AddressSpace *as); +ssize_t load_elf_as(const char *filename, + uint64_t (*elf_note_fn)(void *, void *, bool), + uint64_t (*translate_fn)(void *, uint64_t), + void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr, + uint64_t *highaddr, uint32_t *pflags, int big_endian, + int elf_machine, int clear_lsb, int data_swab, + AddressSpace *as); /** load_elf: * Same as load_elf_as(), but doesn't allow the caller to specify an * AddressSpace. */ -int load_elf(const char *filename, - uint64_t (*elf_note_fn)(void *, void *, bool), - uint64_t (*translate_fn)(void *, uint64_t), - void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr, - uint64_t *highaddr, uint32_t *pflags, int big_endian, - int elf_machine, int clear_lsb, int data_swab); +ssize_t load_elf(const char *filename, + uint64_t (*elf_note_fn)(void *, void *, bool), + uint64_t (*translate_fn)(void *, uint64_t), + void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr, + uint64_t *highaddr, uint32_t *pflags, int big_endian, + int elf_machine, int clear_lsb, int data_swab); /** load_elf_hdr: * @filename: Path of ELF file diff --git a/include/hw/ppc/xive.h b/include/hw/ppc/xive.h index 252c58a1d6..b8ab0bf749 100644 --- a/include/hw/ppc/xive.h +++ b/include/hw/ppc/xive.h @@ -286,6 +286,30 @@ uint8_t xive_esb_set(uint8_t *pq, uint8_t value); uint8_t xive_source_esb_get(XiveSource *xsrc, uint32_t srcno); uint8_t xive_source_esb_set(XiveSource *xsrc, uint32_t srcno, uint8_t pq); +/* + * Source status helpers + */ +static inline void xive_source_set_status(XiveSource *xsrc, uint32_t srcno, + uint8_t status, bool enable) +{ + if (enable) { + xsrc->status[srcno] |= status; + } else { + xsrc->status[srcno] &= ~status; + } +} + +static inline void xive_source_set_asserted(XiveSource *xsrc, uint32_t srcno, + bool enable) +{ + xive_source_set_status(xsrc, srcno, XIVE_STATUS_ASSERTED, enable); +} + +static inline bool xive_source_is_asserted(XiveSource *xsrc, uint32_t srcno) +{ + return xsrc->status[srcno] & XIVE_STATUS_ASSERTED; +} + void xive_source_pic_print_info(XiveSource *xsrc, uint32_t offset, Monitor *mon); diff --git a/include/hw/virtio/vhost-user-rng.h b/include/hw/virtio/vhost-user-rng.h new file mode 100644 index 0000000000..071539996d --- /dev/null +++ b/include/hw/virtio/vhost-user-rng.h @@ -0,0 +1,33 @@ +/* + * Vhost-user RNG virtio device + * + * Copyright (c) 2021 Mathieu Poirier <mathieu.poirier@linaro.org> + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef _QEMU_VHOST_USER_RNG_H +#define _QEMU_VHOST_USER_RNG_H + +#include "hw/virtio/virtio.h" +#include "hw/virtio/vhost.h" +#include "hw/virtio/vhost-user.h" +#include "chardev/char-fe.h" + +#define TYPE_VHOST_USER_RNG "vhost-user-rng" +OBJECT_DECLARE_SIMPLE_TYPE(VHostUserRNG, VHOST_USER_RNG) + +struct VHostUserRNG { + /*< private >*/ + VirtIODevice parent; + CharBackend chardev; + struct vhost_virtqueue *vhost_vq; + struct vhost_dev vhost_dev; + VhostUserState vhost_user; + VirtQueue *req_vq; + bool connected; + + /*< public >*/ +}; + +#endif /* _QEMU_VHOST_USER_RNG_H */ diff --git a/include/hw/virtio/vhost-vdpa.h b/include/hw/virtio/vhost-vdpa.h index a8963da2d9..3ce79a646d 100644 --- a/include/hw/virtio/vhost-vdpa.h +++ b/include/hw/virtio/vhost-vdpa.h @@ -13,6 +13,7 @@ #define HW_VIRTIO_VHOST_VDPA_H #include "hw/virtio/virtio.h" +#include "standard-headers/linux/vhost_types.h" typedef struct VhostVDPAHostNotifier { MemoryRegion mr; @@ -21,9 +22,11 @@ typedef struct VhostVDPAHostNotifier { typedef struct vhost_vdpa { int device_fd; + int index; uint32_t msg_type; bool iotlb_batch_begin_sent; MemoryListener listener; + struct vhost_vdpa_iova_range iova_range; struct vhost_dev *dev; VhostVDPAHostNotifier notifier[VIRTIO_QUEUE_MAX]; } VhostVDPA; diff --git a/include/hw/virtio/vhost.h b/include/hw/virtio/vhost.h index 1a9fc65089..3fa0b554ef 100644 --- a/include/hw/virtio/vhost.h +++ b/include/hw/virtio/vhost.h @@ -74,6 +74,8 @@ struct vhost_dev { unsigned int nvqs; /* the first virtqueue which would be used by this vhost dev */ int vq_index; + /* the last vq index for the virtio device (not vhost) */ + int last_index; /* if non-zero, minimum required value for max_queues */ int num_queues; uint64_t features; diff --git a/include/hw/virtio/virtio-iommu.h b/include/hw/virtio/virtio-iommu.h index 273e35c04b..e2339e5b72 100644 --- a/include/hw/virtio/virtio-iommu.h +++ b/include/hw/virtio/virtio-iommu.h @@ -26,7 +26,7 @@ #include "qom/object.h" #define TYPE_VIRTIO_IOMMU "virtio-iommu-device" -#define TYPE_VIRTIO_IOMMU_PCI "virtio-iommu-device-base" +#define TYPE_VIRTIO_IOMMU_PCI "virtio-iommu-pci" OBJECT_DECLARE_SIMPLE_TYPE(VirtIOIOMMU, VIRTIO_IOMMU) #define TYPE_VIRTIO_IOMMU_MEMORY_REGION "virtio-iommu-memory-region" diff --git a/include/hw/virtio/virtio-net.h b/include/hw/virtio/virtio-net.h index 74a10ebe85..eb87032627 100644 --- a/include/hw/virtio/virtio-net.h +++ b/include/hw/virtio/virtio-net.h @@ -194,8 +194,9 @@ struct VirtIONet { NICConf nic_conf; DeviceState *qdev; int multiqueue; - uint16_t max_queues; - uint16_t curr_queues; + uint16_t max_queue_pairs; + uint16_t curr_queue_pairs; + uint16_t max_ncs; size_t config_size; char *netclient_name; char *netclient_type; diff --git a/include/net/net.h b/include/net/net.h index 986288eb07..523136c7ac 100644 --- a/include/net/net.h +++ b/include/net/net.h @@ -105,6 +105,7 @@ struct NetClientState { int vnet_hdr_len; bool is_netdev; bool do_not_pad; /* do not pad to the minimum ethernet frame length */ + bool is_datapath; QTAILQ_HEAD(, NetFilterState) filters; }; @@ -136,6 +137,10 @@ NetClientState *qemu_new_net_client(NetClientInfo *info, NetClientState *peer, const char *model, const char *name); +NetClientState *qemu_new_net_control_client(NetClientInfo *info, + NetClientState *peer, + const char *model, + const char *name); NICState *qemu_new_nic(NetClientInfo *info, NICConf *conf, const char *model, diff --git a/include/net/vhost_net.h b/include/net/vhost_net.h index fba40cf695..387e913e4e 100644 --- a/include/net/vhost_net.h +++ b/include/net/vhost_net.h @@ -21,8 +21,10 @@ typedef struct VhostNetOptions { uint64_t vhost_net_get_max_queues(VHostNetState *net); struct vhost_net *vhost_net_init(VhostNetOptions *options); -int vhost_net_start(VirtIODevice *dev, NetClientState *ncs, int total_queues); -void vhost_net_stop(VirtIODevice *dev, NetClientState *ncs, int total_queues); +int vhost_net_start(VirtIODevice *dev, NetClientState *ncs, + int data_queue_pairs, int cvq); +void vhost_net_stop(VirtIODevice *dev, NetClientState *ncs, + int data_queue_pairs, int cvq); void vhost_net_cleanup(VHostNetState *net); diff --git a/include/sysemu/device_tree.h b/include/sysemu/device_tree.h index 8a2fe55622..ef060a9759 100644 --- a/include/sysemu/device_tree.h +++ b/include/sysemu/device_tree.h @@ -121,6 +121,7 @@ uint32_t qemu_fdt_get_phandle(void *fdt, const char *path); uint32_t qemu_fdt_alloc_phandle(void *fdt); int qemu_fdt_nop_node(void *fdt, const char *node_path); int qemu_fdt_add_subnode(void *fdt, const char *name); +int qemu_fdt_add_path(void *fdt, const char *path); #define qemu_fdt_setprop_cells(fdt, node_path, property, ...) \ do { \ diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 2404d482ba..eb32f3e2cb 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -901,7 +901,7 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUPPCState *en (*regs)[33] = tswapreg(env->msr); (*regs)[35] = tswapreg(env->ctr); (*regs)[36] = tswapreg(env->lr); - (*regs)[37] = tswapreg(env->xer); + (*regs)[37] = tswapreg(cpu_read_xer(env)); for (i = 0; i < ARRAY_SIZE(env->crf); i++) { ccr |= env->crf[i] << (32 - ((i + 1) * 4)); diff --git a/linux-user/ppc/signal.c b/linux-user/ppc/signal.c index c37744c8fc..90a0369632 100644 --- a/linux-user/ppc/signal.c +++ b/linux-user/ppc/signal.c @@ -242,7 +242,7 @@ static void save_user_regs(CPUPPCState *env, struct target_mcontext *frame) __put_user(env->nip, &frame->mc_gregs[TARGET_PT_NIP]); __put_user(env->ctr, &frame->mc_gregs[TARGET_PT_CTR]); __put_user(env->lr, &frame->mc_gregs[TARGET_PT_LNK]); - __put_user(env->xer, &frame->mc_gregs[TARGET_PT_XER]); + __put_user(cpu_read_xer(env), &frame->mc_gregs[TARGET_PT_XER]); for (i = 0; i < ARRAY_SIZE(env->crf); i++) { ccr |= env->crf[i] << (32 - ((i + 1) * 4)); @@ -315,6 +315,7 @@ static void restore_user_regs(CPUPPCState *env, { target_ulong save_r2 = 0; target_ulong msr; + target_ulong xer; target_ulong ccr; int i; @@ -330,9 +331,11 @@ static void restore_user_regs(CPUPPCState *env, __get_user(env->nip, &frame->mc_gregs[TARGET_PT_NIP]); __get_user(env->ctr, &frame->mc_gregs[TARGET_PT_CTR]); __get_user(env->lr, &frame->mc_gregs[TARGET_PT_LNK]); - __get_user(env->xer, &frame->mc_gregs[TARGET_PT_XER]); - __get_user(ccr, &frame->mc_gregs[TARGET_PT_CCR]); + __get_user(xer, &frame->mc_gregs[TARGET_PT_XER]); + cpu_write_xer(env, xer); + + __get_user(ccr, &frame->mc_gregs[TARGET_PT_CCR]); for (i = 0; i < ARRAY_SIZE(env->crf); i++) { env->crf[i] = (ccr >> (32 - ((i + 1) * 4))) & 0xf; } diff --git a/meson.build b/meson.build index 9ed9a993e2..2c5b53cbe2 100644 --- a/meson.build +++ b/meson.build @@ -75,6 +75,12 @@ else kvm_targets = [] endif +kvm_targets_c = '' +if not get_option('kvm').disabled() and targetos == 'linux' + kvm_targets_c = '"' + '" ,"'.join(kvm_targets) + '"' +endif +config_host_data.set('CONFIG_KVM_TARGETS', kvm_targets_c) + accelerator_targets = { 'CONFIG_KVM': kvm_targets } if cpu in ['aarch64'] @@ -239,7 +239,8 @@ static void qemu_net_client_setup(NetClientState *nc, NetClientState *peer, const char *model, const char *name, - NetClientDestructor *destructor) + NetClientDestructor *destructor, + bool is_datapath) { nc->info = info; nc->model = g_strdup(model); @@ -258,6 +259,7 @@ static void qemu_net_client_setup(NetClientState *nc, nc->incoming_queue = qemu_new_net_queue(qemu_deliver_packet_iov, nc); nc->destructor = destructor; + nc->is_datapath = is_datapath; QTAILQ_INIT(&nc->filters); } @@ -272,7 +274,23 @@ NetClientState *qemu_new_net_client(NetClientInfo *info, nc = g_malloc0(info->size); qemu_net_client_setup(nc, info, peer, model, name, - qemu_net_client_destructor); + qemu_net_client_destructor, true); + + return nc; +} + +NetClientState *qemu_new_net_control_client(NetClientInfo *info, + NetClientState *peer, + const char *model, + const char *name) +{ + NetClientState *nc; + + assert(info->size >= sizeof(NetClientState)); + + nc = g_malloc0(info->size); + qemu_net_client_setup(nc, info, peer, model, name, + qemu_net_client_destructor, false); return nc; } @@ -297,7 +315,7 @@ NICState *qemu_new_nic(NetClientInfo *info, for (i = 0; i < queues; i++) { qemu_net_client_setup(&nic->ncs[i], info, peers[i], model, name, - NULL); + NULL, true); nic->ncs[i].queue_index = i; } diff --git a/net/vhost-vdpa.c b/net/vhost-vdpa.c index 6dc68d8677..49ab322511 100644 --- a/net/vhost-vdpa.c +++ b/net/vhost-vdpa.c @@ -18,6 +18,7 @@ #include "qemu/error-report.h" #include "qemu/option.h" #include "qapi/error.h" +#include <linux/vhost.h> #include <sys/ioctl.h> #include <err.h> #include "standard-headers/linux/virtio_net.h" @@ -51,6 +52,14 @@ const int vdpa_feature_bits[] = { VIRTIO_NET_F_HOST_UFO, VIRTIO_NET_F_MRG_RXBUF, VIRTIO_NET_F_MTU, + VIRTIO_NET_F_CTRL_RX, + VIRTIO_NET_F_CTRL_RX_EXTRA, + VIRTIO_NET_F_CTRL_VLAN, + VIRTIO_NET_F_GUEST_ANNOUNCE, + VIRTIO_NET_F_CTRL_MAC_ADDR, + VIRTIO_NET_F_RSS, + VIRTIO_NET_F_MQ, + VIRTIO_NET_F_CTRL_VQ, VIRTIO_F_IOMMU_PLATFORM, VIRTIO_F_RING_PACKED, VIRTIO_NET_F_RSS, @@ -81,7 +90,8 @@ static int vhost_vdpa_net_check_device_id(struct vhost_net *net) return ret; } -static int vhost_vdpa_add(NetClientState *ncs, void *be) +static int vhost_vdpa_add(NetClientState *ncs, void *be, + int queue_pair_index, int nvqs) { VhostNetOptions options; struct vhost_net *net = NULL; @@ -94,7 +104,7 @@ static int vhost_vdpa_add(NetClientState *ncs, void *be) options.net_backend = ncs; options.opaque = be; options.busyloop_timeout = 0; - options.nvqs = 2; + options.nvqs = nvqs; net = vhost_net_init(&options); if (!net) { @@ -169,36 +179,125 @@ static NetClientInfo net_vhost_vdpa_info = { .check_peer_type = vhost_vdpa_check_peer_type, }; -static int net_vhost_vdpa_init(NetClientState *peer, const char *device, - const char *name, const char *vhostdev) +static NetClientState *net_vhost_vdpa_init(NetClientState *peer, + const char *device, + const char *name, + int vdpa_device_fd, + int queue_pair_index, + int nvqs, + bool is_datapath) { NetClientState *nc = NULL; VhostVDPAState *s; - int vdpa_device_fd = -1; int ret = 0; assert(name); - nc = qemu_new_net_client(&net_vhost_vdpa_info, peer, device, name); + if (is_datapath) { + nc = qemu_new_net_client(&net_vhost_vdpa_info, peer, device, + name); + } else { + nc = qemu_new_net_control_client(&net_vhost_vdpa_info, peer, + device, name); + } snprintf(nc->info_str, sizeof(nc->info_str), TYPE_VHOST_VDPA); s = DO_UPCAST(VhostVDPAState, nc, nc); - vdpa_device_fd = qemu_open_old(vhostdev, O_RDWR); - if (vdpa_device_fd == -1) { - return -errno; - } + s->vhost_vdpa.device_fd = vdpa_device_fd; - ret = vhost_vdpa_add(nc, (void *)&s->vhost_vdpa); + s->vhost_vdpa.index = queue_pair_index; + ret = vhost_vdpa_add(nc, (void *)&s->vhost_vdpa, queue_pair_index, nvqs); if (ret) { - qemu_close(vdpa_device_fd); qemu_del_net_client(nc); + return NULL; } - return ret; + return nc; +} + +static int vhost_vdpa_get_max_queue_pairs(int fd, int *has_cvq, Error **errp) +{ + unsigned long config_size = offsetof(struct vhost_vdpa_config, buf); + struct vhost_vdpa_config *config; + __virtio16 *max_queue_pairs; + uint64_t features; + int ret; + + ret = ioctl(fd, VHOST_GET_FEATURES, &features); + if (ret) { + error_setg(errp, "Fail to query features from vhost-vDPA device"); + return ret; + } + + if (features & (1 << VIRTIO_NET_F_CTRL_VQ)) { + *has_cvq = 1; + } else { + *has_cvq = 0; + } + + if (features & (1 << VIRTIO_NET_F_MQ)) { + config = g_malloc0(config_size + sizeof(*max_queue_pairs)); + config->off = offsetof(struct virtio_net_config, max_virtqueue_pairs); + config->len = sizeof(*max_queue_pairs); + + ret = ioctl(fd, VHOST_VDPA_GET_CONFIG, config); + if (ret) { + error_setg(errp, "Fail to get config from vhost-vDPA device"); + return -ret; + } + + max_queue_pairs = (__virtio16 *)&config->buf; + + return lduw_le_p(max_queue_pairs); + } + + return 1; } int net_init_vhost_vdpa(const Netdev *netdev, const char *name, NetClientState *peer, Error **errp) { const NetdevVhostVDPAOptions *opts; + int vdpa_device_fd; + NetClientState **ncs, *nc; + int queue_pairs, i, has_cvq = 0; assert(netdev->type == NET_CLIENT_DRIVER_VHOST_VDPA); opts = &netdev->u.vhost_vdpa; - return net_vhost_vdpa_init(peer, TYPE_VHOST_VDPA, name, opts->vhostdev); + + vdpa_device_fd = qemu_open_old(opts->vhostdev, O_RDWR); + if (vdpa_device_fd == -1) { + return -errno; + } + + queue_pairs = vhost_vdpa_get_max_queue_pairs(vdpa_device_fd, + &has_cvq, errp); + if (queue_pairs < 0) { + qemu_close(vdpa_device_fd); + return queue_pairs; + } + + ncs = g_malloc0(sizeof(*ncs) * queue_pairs); + + for (i = 0; i < queue_pairs; i++) { + ncs[i] = net_vhost_vdpa_init(peer, TYPE_VHOST_VDPA, name, + vdpa_device_fd, i, 2, true); + if (!ncs[i]) + goto err; + } + + if (has_cvq) { + nc = net_vhost_vdpa_init(peer, TYPE_VHOST_VDPA, name, + vdpa_device_fd, i, 1, false); + if (!nc) + goto err; + } + + g_free(ncs); + return 0; + +err: + if (i) { + qemu_del_net_client(ncs[0]); + } + qemu_close(vdpa_device_fd); + g_free(ncs); + + return -1; } diff --git a/roms/Makefile b/roms/Makefile index eeb5970348..b967b53bb7 100644 --- a/roms/Makefile +++ b/roms/Makefile @@ -143,7 +143,8 @@ build-efi-roms: build-pxe-roms # efirom # edk2-basetools: - cd edk2/BaseTools && git submodule update --init --force + cd edk2/BaseTools && git submodule update --init --force \ + Source/C/BrotliCompress/brotli $(MAKE) -C edk2/BaseTools \ PYTHON_COMMAND=$${EDK2_PYTHON_COMMAND:-python3} \ EXTRA_OPTFLAGS='$(EDK2_BASETOOLS_OPTFLAGS)' \ diff --git a/roms/Makefile.edk2 b/roms/Makefile.edk2 index a8ed325575..fdae0b511f 100644 --- a/roms/Makefile.edk2 +++ b/roms/Makefile.edk2 @@ -51,7 +51,12 @@ all: $(foreach flashdev,$(flashdevs),../pc-bios/edk2-$(flashdev).fd.bz2) \ # make-release/tarball scripts. submodules: if test -d edk2/.git; then \ - cd edk2 && git submodule update --init --force; \ + cd edk2 && git submodule update --init --force -- \ + ArmPkg/Library/ArmSoftFloatLib/berkeley-softfloat-3 \ + BaseTools/Source/C/BrotliCompress/brotli \ + CryptoPkg/Library/OpensslLib/openssl \ + MdeModulePkg/Library/BrotliCustomDecompressLib/brotli \ + ; \ fi # See notes on the ".NOTPARALLEL" target and the "+" indicator in diff --git a/scripts/make-release b/scripts/make-release index a2a8cda33c..05b14ecc95 100755 --- a/scripts/make-release +++ b/scripts/make-release @@ -27,7 +27,12 @@ git submodule update --init # don't necessarily have much control over how a submodule handles its # submodule dependencies, so we continue to handle these on a case-by-case # basis for now. -(cd roms/edk2 && git submodule update --init) +(cd roms/edk2 && \ + git submodule update --init -- \ + ArmPkg/Library/ArmSoftFloatLib/berkeley-softfloat-3 \ + BaseTools/Source/C/BrotliCompress/brotli \ + CryptoPkg/Library/OpensslLib/openssl \ + MdeModulePkg/Library/BrotliCustomDecompressLib/brotli) popd tar --exclude=.git -cjf ${destination}.tar.bz2 ${destination} rm -rf ${destination} diff --git a/softmmu/device_tree.c b/softmmu/device_tree.c index b621f63fba..3965c834ca 100644 --- a/softmmu/device_tree.c +++ b/softmmu/device_tree.c @@ -540,8 +540,8 @@ int qemu_fdt_add_subnode(void *fdt, const char *name) retval = fdt_add_subnode(fdt, parent, basename); if (retval < 0) { - error_report("FDT: Failed to create subnode %s: %s", name, - fdt_strerror(retval)); + error_report("%s: Failed to create subnode %s: %s", + __func__, name, fdt_strerror(retval)); exit(1); } @@ -549,6 +549,46 @@ int qemu_fdt_add_subnode(void *fdt, const char *name) return retval; } +/* + * qemu_fdt_add_path: Like qemu_fdt_add_subnode(), but will add + * all missing subnodes from the given path. + */ +int qemu_fdt_add_path(void *fdt, const char *path) +{ + const char *name; + const char *p = path; + int namelen, retval; + int parent = 0; + + if (path[0] != '/') { + return -1; + } + + while (p) { + name = p + 1; + p = strchr(name, '/'); + namelen = p != NULL ? p - name : strlen(name); + + retval = fdt_subnode_offset_namelen(fdt, parent, name, namelen); + if (retval < 0 && retval != -FDT_ERR_NOTFOUND) { + error_report("%s: Unexpected error in finding subnode %.*s: %s", + __func__, namelen, name, fdt_strerror(retval)); + exit(1); + } else if (retval == -FDT_ERR_NOTFOUND) { + retval = fdt_add_subnode_namelen(fdt, parent, name, namelen); + if (retval < 0) { + error_report("%s: Failed to create subnode %.*s: %s", + __func__, namelen, name, fdt_strerror(retval)); + exit(1); + } + } + + parent = retval; + } + + return retval; +} + void qemu_fdt_dumpdtb(void *fdt, int size) { const char *dumpdtb = current_machine->dumpdtb; diff --git a/softmmu/qdev-monitor.c b/softmmu/qdev-monitor.c index 89c473cb22..4851de51a5 100644 --- a/softmmu/qdev-monitor.c +++ b/softmmu/qdev-monitor.c @@ -639,19 +639,13 @@ DeviceState *qdev_device_add_from_qdict(const QDict *opts, } } - if (qdict_haskey(opts, "failover_pair_id")) { - if (!qdict_haskey(opts, "id")) { - error_setg(errp, "Device with failover_pair_id don't have id"); - return NULL; - } - if (qdev_should_hide_device(opts, from_json, errp)) { - if (bus && !qbus_is_hotpluggable(bus)) { - error_setg(errp, QERR_BUS_NO_HOTPLUG, bus->name); - } - return NULL; - } else if (*errp) { - return NULL; + if (qdev_should_hide_device(opts, from_json, errp)) { + if (bus && !qbus_is_hotpluggable(bus)) { + error_setg(errp, QERR_BUS_NO_HOTPLUG, bus->name); } + return NULL; + } else if (*errp) { + return NULL; } if (phase_check(PHASE_MACHINE_READY) && bus && !qbus_is_hotpluggable(bus)) { diff --git a/subprojects/libvhost-user/libvhost-user.c b/subprojects/libvhost-user/libvhost-user.c index bf09693255..787f4d2d4f 100644 --- a/subprojects/libvhost-user/libvhost-user.c +++ b/subprojects/libvhost-user/libvhost-user.c @@ -816,6 +816,7 @@ vu_rem_mem_reg(VuDev *dev, VhostUserMsg *vmsg) { shadow_regions[j].gpa = dev->regions[i].gpa; shadow_regions[j].size = dev->regions[i].size; shadow_regions[j].qva = dev->regions[i].qva; + shadow_regions[j].mmap_addr = dev->regions[i].mmap_addr; shadow_regions[j].mmap_offset = dev->regions[i].mmap_offset; j++; } else { diff --git a/target/ppc/cpu.c b/target/ppc/cpu.c index 7ad9bd6044..f933d9f2bd 100644 --- a/target/ppc/cpu.c +++ b/target/ppc/cpu.c @@ -27,7 +27,7 @@ #include "helper_regs.h" #include "sysemu/tcg.h" -target_ulong cpu_read_xer(CPUPPCState *env) +target_ulong cpu_read_xer(const CPUPPCState *env) { if (is_isa300(env)) { return env->xer | (env->so << XER_SO) | diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index baa4e7c34d..0472ec9154 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -314,6 +314,7 @@ typedef struct ppc_v3_pate_t { #define MSR_AP 23 /* Access privilege state on 602 hflags */ #define MSR_VSX 23 /* Vector Scalar Extension (ISA 2.06 and later) x hflags */ #define MSR_SA 22 /* Supervisor access mode on 602 hflags */ +#define MSR_S 22 /* Secure state */ #define MSR_KEY 19 /* key bit on 603e */ #define MSR_POW 18 /* Power management */ #define MSR_TGPR 17 /* TGPR usage on 602/603 x */ @@ -342,6 +343,26 @@ typedef struct ppc_v3_pate_t { #define MSR_RI 1 /* Recoverable interrupt 1 */ #define MSR_LE 0 /* Little-endian mode 1 hflags */ +/* PMU bits */ +#define MMCR0_FC PPC_BIT(32) /* Freeze Counters */ +#define MMCR0_PMAO PPC_BIT(56) /* Perf Monitor Alert Ocurred */ +#define MMCR0_PMAE PPC_BIT(37) /* Perf Monitor Alert Enable */ +#define MMCR0_EBE PPC_BIT(43) /* Perf Monitor EBB Enable */ +#define MMCR0_FCECE PPC_BIT(38) /* FC on Enabled Cond or Event */ +#define MMCR0_PMCC0 PPC_BIT(44) /* PMC Control bit 0 */ +#define MMCR0_PMCC1 PPC_BIT(45) /* PMC Control bit 1 */ +/* MMCR0 userspace r/w mask */ +#define MMCR0_UREG_MASK (MMCR0_FC | MMCR0_PMAO | MMCR0_PMAE) +/* MMCR2 userspace r/w mask */ +#define MMCR2_FC1P0 PPC_BIT(1) /* MMCR2 FCnP0 for PMC1 */ +#define MMCR2_FC2P0 PPC_BIT(10) /* MMCR2 FCnP0 for PMC2 */ +#define MMCR2_FC3P0 PPC_BIT(19) /* MMCR2 FCnP0 for PMC3 */ +#define MMCR2_FC4P0 PPC_BIT(28) /* MMCR2 FCnP0 for PMC4 */ +#define MMCR2_FC5P0 PPC_BIT(37) /* MMCR2 FCnP0 for PMC5 */ +#define MMCR2_FC6P0 PPC_BIT(46) /* MMCR2 FCnP0 for PMC6 */ +#define MMCR2_UREG_MASK (MMCR2_FC1P0 | MMCR2_FC2P0 | MMCR2_FC3P0 | \ + MMCR2_FC4P0 | MMCR2_FC5P0 | MMCR2_FC6P0) + /* LPCR bits */ #define LPCR_VPM0 PPC_BIT(0) #define LPCR_VPM1 PPC_BIT(1) @@ -607,6 +628,8 @@ enum { HFLAGS_SE = 10, /* MSR_SE -- from elsewhere on embedded ppc */ HFLAGS_FP = 13, /* MSR_FP */ HFLAGS_PR = 14, /* MSR_PR */ + HFLAGS_PMCC0 = 15, /* MMCR0 PMCC bit 0 */ + HFLAGS_PMCC1 = 16, /* MMCR0 PMCC bit 1 */ HFLAGS_VSX = 23, /* MSR_VSX if cpu has VSX */ HFLAGS_VR = 25, /* MSR_VR if cpu has VRE */ @@ -2412,7 +2435,7 @@ enum { /*****************************************************************************/ #define is_isa300(ctx) (!!(ctx->insns_flags2 & PPC2_ISA300)) -target_ulong cpu_read_xer(CPUPPCState *env); +target_ulong cpu_read_xer(const CPUPPCState *env); void cpu_write_xer(CPUPPCState *env, target_ulong xer); /* diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index 6aad01d1d3..65545ba9ca 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -6867,7 +6867,7 @@ static void register_book3s_pmu_sup_sprs(CPUPPCState *env) static void register_book3s_pmu_user_sprs(CPUPPCState *env) { spr_register(env, SPR_POWER_UMMCR0, "UMMCR0", - &spr_read_ureg, SPR_NOACCESS, + &spr_read_MMCR0_ureg, &spr_write_MMCR0_ureg, &spr_read_ureg, &spr_write_ureg, 0x00000000); spr_register(env, SPR_POWER_UMMCR1, "UMMCR1", @@ -6879,27 +6879,27 @@ static void register_book3s_pmu_user_sprs(CPUPPCState *env) &spr_read_ureg, &spr_write_ureg, 0x00000000); spr_register(env, SPR_POWER_UPMC1, "UPMC1", - &spr_read_ureg, SPR_NOACCESS, + &spr_read_PMC14_ureg, &spr_write_PMC14_ureg, &spr_read_ureg, &spr_write_ureg, 0x00000000); spr_register(env, SPR_POWER_UPMC2, "UPMC2", - &spr_read_ureg, SPR_NOACCESS, + &spr_read_PMC14_ureg, &spr_write_PMC14_ureg, &spr_read_ureg, &spr_write_ureg, 0x00000000); spr_register(env, SPR_POWER_UPMC3, "UPMC3", - &spr_read_ureg, SPR_NOACCESS, + &spr_read_PMC14_ureg, &spr_write_PMC14_ureg, &spr_read_ureg, &spr_write_ureg, 0x00000000); spr_register(env, SPR_POWER_UPMC4, "UPMC4", - &spr_read_ureg, SPR_NOACCESS, + &spr_read_PMC14_ureg, &spr_write_PMC14_ureg, &spr_read_ureg, &spr_write_ureg, 0x00000000); spr_register(env, SPR_POWER_UPMC5, "UPMC5", - &spr_read_ureg, SPR_NOACCESS, + &spr_read_PMC56_ureg, &spr_write_PMC56_ureg, &spr_read_ureg, &spr_write_ureg, 0x00000000); spr_register(env, SPR_POWER_UPMC6, "UPMC6", - &spr_read_ureg, SPR_NOACCESS, + &spr_read_PMC56_ureg, &spr_write_PMC56_ureg, &spr_read_ureg, &spr_write_ureg, 0x00000000); spr_register(env, SPR_POWER_USIAR, "USIAR", @@ -6975,7 +6975,7 @@ static void register_power8_pmu_sup_sprs(CPUPPCState *env) static void register_power8_pmu_user_sprs(CPUPPCState *env) { spr_register(env, SPR_POWER_UMMCR2, "UMMCR2", - &spr_read_ureg, SPR_NOACCESS, + &spr_read_MMCR2_ureg, &spr_write_MMCR2_ureg, &spr_read_ureg, &spr_write_ureg, 0x00000000); spr_register(env, SPR_POWER_USIER, "USIER", diff --git a/target/ppc/gdbstub.c b/target/ppc/gdbstub.c index 1808a150e4..105c2f7dd1 100644 --- a/target/ppc/gdbstub.c +++ b/target/ppc/gdbstub.c @@ -159,7 +159,7 @@ int ppc_cpu_gdb_read_register(CPUState *cs, GByteArray *buf, int n) gdb_get_regl(buf, env->ctr); break; case 69: - gdb_get_reg32(buf, env->xer); + gdb_get_reg32(buf, cpu_read_xer(env)); break; case 70: gdb_get_reg32(buf, env->fpscr); @@ -217,7 +217,7 @@ int ppc_cpu_gdb_read_register_apple(CPUState *cs, GByteArray *buf, int n) gdb_get_reg64(buf, env->ctr); break; case 69 + 32: - gdb_get_reg32(buf, env->xer); + gdb_get_reg32(buf, cpu_read_xer(env)); break; case 70 + 32: gdb_get_reg64(buf, env->fpscr); @@ -269,7 +269,7 @@ int ppc_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) env->ctr = ldtul_p(mem_buf); break; case 69: - env->xer = ldl_p(mem_buf); + cpu_write_xer(env, ldl_p(mem_buf)); break; case 70: /* fpscr */ @@ -319,7 +319,7 @@ int ppc_cpu_gdb_write_register_apple(CPUState *cs, uint8_t *mem_buf, int n) env->ctr = ldq_p(mem_buf); break; case 69 + 32: - env->xer = ldl_p(mem_buf); + cpu_write_xer(env, ldl_p(mem_buf)); break; case 70 + 32: /* fpscr */ diff --git a/target/ppc/helper_regs.c b/target/ppc/helper_regs.c index 1bfb480ecf..99562edd57 100644 --- a/target/ppc/helper_regs.c +++ b/target/ppc/helper_regs.c @@ -109,6 +109,12 @@ static uint32_t hreg_compute_hflags_value(CPUPPCState *env) if (env->spr[SPR_LPCR] & LPCR_HR) { hflags |= 1 << HFLAGS_HR; } + if (env->spr[SPR_POWER_MMCR0] & MMCR0_PMCC0) { + hflags |= 1 << HFLAGS_PMCC0; + } + if (env->spr[SPR_POWER_MMCR0] & MMCR0_PMCC1) { + hflags |= 1 << HFLAGS_PMCC1; + } #ifndef CONFIG_USER_ONLY if (!env->has_hv_mode || (msr & (1ull << MSR_HV))) { diff --git a/target/ppc/monitor.c b/target/ppc/monitor.c index a475108b2d..0b805ef6e9 100644 --- a/target/ppc/monitor.c +++ b/target/ppc/monitor.c @@ -44,6 +44,13 @@ static target_long monitor_get_ccr(Monitor *mon, const struct MonitorDef *md, return u; } +static target_long monitor_get_xer(Monitor *mon, const struct MonitorDef *md, + int val) +{ + CPUArchState *env = mon_get_cpu_env(mon); + return cpu_read_xer(env); +} + static target_long monitor_get_decr(Monitor *mon, const struct MonitorDef *md, int val) { @@ -85,7 +92,7 @@ const MonitorDef monitor_defs[] = { { "decr", 0, &monitor_get_decr, }, { "ccr|cr", 0, &monitor_get_ccr, }, /* Machine state register */ - { "xer", offsetof(CPUPPCState, xer) }, + { "xer", 0, &monitor_get_xer }, { "msr", offsetof(CPUPPCState, msr) }, { "tbu", 0, &monitor_get_tbu, }, { "tbl", 0, &monitor_get_tbl, }, diff --git a/target/ppc/power8-pmu-regs.c.inc b/target/ppc/power8-pmu-regs.c.inc new file mode 100644 index 0000000000..7391851238 --- /dev/null +++ b/target/ppc/power8-pmu-regs.c.inc @@ -0,0 +1,262 @@ +/* + * PMU register read/write functions for TCG IBM POWER chips + * + * Copyright IBM Corp. 2021 + * + * Authors: + * Daniel Henrique Barboza <danielhb413@gmail.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) + +/* + * Checks whether the Group A SPR (MMCR0, MMCR2, MMCRA, and the + * PMCs) has problem state read access. + * + * Read acccess is granted for all PMCC values but 0b01, where a + * Facility Unavailable Interrupt will occur. + */ +static bool spr_groupA_read_allowed(DisasContext *ctx) +{ + if (!ctx->mmcr0_pmcc0 && ctx->mmcr0_pmcc1) { + gen_hvpriv_exception(ctx, POWERPC_EXCP_FU); + return false; + } + + return true; +} + +/* + * Checks whether the Group A SPR (MMCR0, MMCR2, MMCRA, and the + * PMCs) has problem state write access. + * + * Write acccess is granted for PMCC values 0b10 and 0b11. Userspace + * writing with PMCC 0b00 will generate a Hypervisor Emulation + * Assistance Interrupt. Userspace writing with PMCC 0b01 will + * generate a Facility Unavailable Interrupt. + */ +static bool spr_groupA_write_allowed(DisasContext *ctx) +{ + if (ctx->mmcr0_pmcc0) { + return true; + } + + if (ctx->mmcr0_pmcc1) { + /* PMCC = 0b01 */ + gen_hvpriv_exception(ctx, POWERPC_EXCP_FU); + } else { + /* PMCC = 0b00 */ + gen_hvpriv_exception(ctx, POWERPC_EXCP_INVAL_SPR); + } + + return false; +} + +/* + * Helper function to avoid code repetition between MMCR0 and + * MMCR2 problem state write functions. + * + * 'ret' must be tcg_temp_freed() by the caller. + */ +static TCGv masked_gprn_for_spr_write(int gprn, int sprn, + uint64_t spr_mask) +{ + TCGv ret = tcg_temp_new(); + TCGv t0 = tcg_temp_new(); + + /* 'ret' starts with all mask bits cleared */ + gen_load_spr(ret, sprn); + tcg_gen_andi_tl(ret, ret, ~(spr_mask)); + + /* Apply the mask into 'gprn' in a temp var */ + tcg_gen_andi_tl(t0, cpu_gpr[gprn], spr_mask); + + /* Add the masked gprn bits into 'ret' */ + tcg_gen_or_tl(ret, ret, t0); + + tcg_temp_free(t0); + + return ret; +} + +void spr_read_MMCR0_ureg(DisasContext *ctx, int gprn, int sprn) +{ + TCGv t0; + + if (!spr_groupA_read_allowed(ctx)) { + return; + } + + t0 = tcg_temp_new(); + + /* + * Filter out all bits but FC, PMAO, and PMAE, according + * to ISA v3.1, in 10.4.4 Monitor Mode Control Register 0, + * fourth paragraph. + */ + gen_load_spr(t0, SPR_POWER_MMCR0); + tcg_gen_andi_tl(t0, t0, MMCR0_UREG_MASK); + tcg_gen_mov_tl(cpu_gpr[gprn], t0); + + tcg_temp_free(t0); +} + +void spr_write_MMCR0_ureg(DisasContext *ctx, int sprn, int gprn) +{ + TCGv masked_gprn; + + if (!spr_groupA_write_allowed(ctx)) { + return; + } + + /* + * Filter out all bits but FC, PMAO, and PMAE, according + * to ISA v3.1, in 10.4.4 Monitor Mode Control Register 0, + * fourth paragraph. + */ + masked_gprn = masked_gprn_for_spr_write(gprn, SPR_POWER_MMCR0, + MMCR0_UREG_MASK); + gen_store_spr(SPR_POWER_MMCR0, masked_gprn); + + tcg_temp_free(masked_gprn); +} + +void spr_read_MMCR2_ureg(DisasContext *ctx, int gprn, int sprn) +{ + TCGv t0; + + if (!spr_groupA_read_allowed(ctx)) { + return; + } + + t0 = tcg_temp_new(); + + /* + * On read, filter out all bits that are not FCnP0 bits. + * When MMCR0[PMCC] is set to 0b10 or 0b11, providing + * problem state programs read/write access to MMCR2, + * only the FCnP0 bits can be accessed. All other bits are + * not changed when mtspr is executed in problem state, and + * all other bits return 0s when mfspr is executed in problem + * state, according to ISA v3.1, section 10.4.6 Monitor Mode + * Control Register 2, p. 1316, third paragraph. + */ + gen_load_spr(t0, SPR_POWER_MMCR2); + tcg_gen_andi_tl(t0, t0, MMCR2_UREG_MASK); + tcg_gen_mov_tl(cpu_gpr[gprn], t0); + + tcg_temp_free(t0); +} + +void spr_write_MMCR2_ureg(DisasContext *ctx, int sprn, int gprn) +{ + TCGv masked_gprn; + + if (!spr_groupA_write_allowed(ctx)) { + return; + } + + /* + * Filter the bits that can be written using MMCR2_UREG_MASK, + * similar to what is done in spr_write_MMCR0_ureg(). + */ + masked_gprn = masked_gprn_for_spr_write(gprn, SPR_POWER_MMCR2, + MMCR2_UREG_MASK); + gen_store_spr(SPR_POWER_MMCR2, masked_gprn); + + tcg_temp_free(masked_gprn); +} + +void spr_read_PMC14_ureg(DisasContext *ctx, int gprn, int sprn) +{ + if (!spr_groupA_read_allowed(ctx)) { + return; + } + + spr_read_ureg(ctx, gprn, sprn); +} + +void spr_read_PMC56_ureg(DisasContext *ctx, int gprn, int sprn) +{ + /* + * If PMCC = 0b11, PMC5 and PMC6 aren't included in the Performance + * Monitor, and a read attempt results in a Facility Unavailable + * Interrupt. + */ + if (ctx->mmcr0_pmcc0 && ctx->mmcr0_pmcc1) { + gen_hvpriv_exception(ctx, POWERPC_EXCP_FU); + return; + } + + /* The remaining steps are similar to PMCs 1-4 userspace read */ + spr_read_PMC14_ureg(ctx, gprn, sprn); +} + +void spr_write_PMC14_ureg(DisasContext *ctx, int sprn, int gprn) +{ + if (!spr_groupA_write_allowed(ctx)) { + return; + } + + spr_write_ureg(ctx, sprn, gprn); +} + +void spr_write_PMC56_ureg(DisasContext *ctx, int sprn, int gprn) +{ + /* + * If PMCC = 0b11, PMC5 and PMC6 aren't included in the Performance + * Monitor, and a write attempt results in a Facility Unavailable + * Interrupt. + */ + if (ctx->mmcr0_pmcc0 && ctx->mmcr0_pmcc1) { + gen_hvpriv_exception(ctx, POWERPC_EXCP_FU); + return; + } + + /* The remaining steps are similar to PMCs 1-4 userspace write */ + spr_write_PMC14_ureg(ctx, sprn, gprn); +} +#else +void spr_read_MMCR0_ureg(DisasContext *ctx, int gprn, int sprn) +{ + spr_read_ureg(ctx, gprn, sprn); +} + +void spr_write_MMCR0_ureg(DisasContext *ctx, int sprn, int gprn) +{ + spr_noaccess(ctx, gprn, sprn); +} + +void spr_read_MMCR2_ureg(DisasContext *ctx, int gprn, int sprn) +{ + spr_read_ureg(ctx, gprn, sprn); +} + +void spr_write_MMCR2_ureg(DisasContext *ctx, int sprn, int gprn) +{ + spr_noaccess(ctx, gprn, sprn); +} + +void spr_read_PMC14_ureg(DisasContext *ctx, int gprn, int sprn) +{ + spr_read_ureg(ctx, gprn, sprn); +} + +void spr_read_PMC56_ureg(DisasContext *ctx, int gprn, int sprn) +{ + spr_read_ureg(ctx, gprn, sprn); +} + +void spr_write_PMC14_ureg(DisasContext *ctx, int sprn, int gprn) +{ + spr_noaccess(ctx, gprn, sprn); +} + +void spr_write_PMC56_ureg(DisasContext *ctx, int sprn, int gprn) +{ + spr_noaccess(ctx, gprn, sprn); +} +#endif /* defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) */ diff --git a/target/ppc/spr_tcg.h b/target/ppc/spr_tcg.h index 0be5f347d5..520f1ef233 100644 --- a/target/ppc/spr_tcg.h +++ b/target/ppc/spr_tcg.h @@ -32,6 +32,10 @@ void spr_write_lr(DisasContext *ctx, int sprn, int gprn); void spr_read_ctr(DisasContext *ctx, int gprn, int sprn); void spr_write_ctr(DisasContext *ctx, int sprn, int gprn); void spr_read_ureg(DisasContext *ctx, int gprn, int sprn); +void spr_read_MMCR0_ureg(DisasContext *ctx, int gprn, int sprn); +void spr_read_MMCR2_ureg(DisasContext *ctx, int gprn, int sprn); +void spr_read_PMC14_ureg(DisasContext *ctx, int gprn, int sprn); +void spr_read_PMC56_ureg(DisasContext *ctx, int gprn, int sprn); void spr_read_tbl(DisasContext *ctx, int gprn, int sprn); void spr_read_tbu(DisasContext *ctx, int gprn, int sprn); void spr_read_atbl(DisasContext *ctx, int gprn, int sprn); @@ -40,6 +44,10 @@ void spr_read_601_rtcl(DisasContext *ctx, int gprn, int sprn); void spr_read_601_rtcu(DisasContext *ctx, int gprn, int sprn); void spr_read_spefscr(DisasContext *ctx, int gprn, int sprn); void spr_write_spefscr(DisasContext *ctx, int sprn, int gprn); +void spr_write_MMCR0_ureg(DisasContext *ctx, int sprn, int gprn); +void spr_write_MMCR2_ureg(DisasContext *ctx, int sprn, int gprn); +void spr_write_PMC14_ureg(DisasContext *ctx, int sprn, int gprn); +void spr_write_PMC56_ureg(DisasContext *ctx, int sprn, int gprn); #ifndef CONFIG_USER_ONLY void spr_write_generic32(DisasContext *ctx, int sprn, int gprn); diff --git a/target/ppc/translate.c b/target/ppc/translate.c index c3c6cb9589..518337bcb7 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -175,6 +175,8 @@ struct DisasContext { bool tm_enabled; bool gtse; bool hr; + bool mmcr0_pmcc0; + bool mmcr0_pmcc1; ppc_spr_t *spr_cb; /* Needed to check rights for mfspr/mtspr */ int singlestep_enabled; uint32_t flags; @@ -4934,32 +4936,40 @@ static void gen_mtmsrd(DisasContext *ctx) CHK_SV; #if !defined(CONFIG_USER_ONLY) + TCGv t0, t1; + target_ulong mask; + + t0 = tcg_temp_new(); + t1 = tcg_temp_new(); + gen_icount_io_start(ctx); + if (ctx->opcode & 0x00010000) { /* L=1 form only updates EE and RI */ - TCGv t0 = tcg_temp_new(); - TCGv t1 = tcg_temp_new(); - tcg_gen_andi_tl(t0, cpu_gpr[rS(ctx->opcode)], - (1 << MSR_RI) | (1 << MSR_EE)); - tcg_gen_andi_tl(t1, cpu_msr, - ~(target_ulong)((1 << MSR_RI) | (1 << MSR_EE))); - tcg_gen_or_tl(t1, t1, t0); - - gen_helper_store_msr(cpu_env, t1); - tcg_temp_free(t0); - tcg_temp_free(t1); - + mask = (1ULL << MSR_RI) | (1ULL << MSR_EE); } else { + /* mtmsrd does not alter HV, S, ME, or LE */ + mask = ~((1ULL << MSR_LE) | (1ULL << MSR_ME) | (1ULL << MSR_S) | + (1ULL << MSR_HV)); /* * XXX: we need to update nip before the store if we enter * power saving mode, we will exit the loop directly from * ppc_store_msr */ gen_update_nip(ctx, ctx->base.pc_next); - gen_helper_store_msr(cpu_env, cpu_gpr[rS(ctx->opcode)]); } + + tcg_gen_andi_tl(t0, cpu_gpr[rS(ctx->opcode)], mask); + tcg_gen_andi_tl(t1, cpu_msr, ~mask); + tcg_gen_or_tl(t0, t0, t1); + + gen_helper_store_msr(cpu_env, t0); + /* Must stop the translation as machine state (may have) changed */ ctx->base.is_jmp = DISAS_EXIT_UPDATE; + + tcg_temp_free(t0); + tcg_temp_free(t1); #endif /* !defined(CONFIG_USER_ONLY) */ } #endif /* defined(TARGET_PPC64) */ @@ -4969,23 +4979,19 @@ static void gen_mtmsr(DisasContext *ctx) CHK_SV; #if !defined(CONFIG_USER_ONLY) + TCGv t0, t1; + target_ulong mask = 0xFFFFFFFF; + + t0 = tcg_temp_new(); + t1 = tcg_temp_new(); + gen_icount_io_start(ctx); if (ctx->opcode & 0x00010000) { /* L=1 form only updates EE and RI */ - TCGv t0 = tcg_temp_new(); - TCGv t1 = tcg_temp_new(); - tcg_gen_andi_tl(t0, cpu_gpr[rS(ctx->opcode)], - (1 << MSR_RI) | (1 << MSR_EE)); - tcg_gen_andi_tl(t1, cpu_msr, - ~(target_ulong)((1 << MSR_RI) | (1 << MSR_EE))); - tcg_gen_or_tl(t1, t1, t0); - - gen_helper_store_msr(cpu_env, t1); - tcg_temp_free(t0); - tcg_temp_free(t1); - + mask &= (1ULL << MSR_RI) | (1ULL << MSR_EE); } else { - TCGv msr = tcg_temp_new(); + /* mtmsr does not alter S, ME, or LE */ + mask &= ~((1ULL << MSR_LE) | (1ULL << MSR_ME) | (1ULL << MSR_S)); /* * XXX: we need to update nip before the store if we enter @@ -4993,16 +4999,19 @@ static void gen_mtmsr(DisasContext *ctx) * ppc_store_msr */ gen_update_nip(ctx, ctx->base.pc_next); -#if defined(TARGET_PPC64) - tcg_gen_deposit_tl(msr, cpu_msr, cpu_gpr[rS(ctx->opcode)], 0, 32); -#else - tcg_gen_mov_tl(msr, cpu_gpr[rS(ctx->opcode)]); -#endif - gen_helper_store_msr(cpu_env, msr); - tcg_temp_free(msr); } + + tcg_gen_andi_tl(t0, cpu_gpr[rS(ctx->opcode)], mask); + tcg_gen_andi_tl(t1, cpu_msr, ~mask); + tcg_gen_or_tl(t0, t0, t1); + + gen_helper_store_msr(cpu_env, t0); + /* Must stop the translation as machine state (may have) changed */ ctx->base.is_jmp = DISAS_EXIT_UPDATE; + + tcg_temp_free(t0); + tcg_temp_free(t1); #endif } @@ -5068,19 +5077,15 @@ static void gen_mtspr(DisasContext *ctx) static void gen_setb(DisasContext *ctx) { TCGv_i32 t0 = tcg_temp_new_i32(); - TCGv_i32 t8 = tcg_temp_new_i32(); - TCGv_i32 tm1 = tcg_temp_new_i32(); + TCGv_i32 t8 = tcg_constant_i32(8); + TCGv_i32 tm1 = tcg_constant_i32(-1); int crf = crfS(ctx->opcode); tcg_gen_setcondi_i32(TCG_COND_GEU, t0, cpu_crf[crf], 4); - tcg_gen_movi_i32(t8, 8); - tcg_gen_movi_i32(tm1, -1); tcg_gen_movcond_i32(TCG_COND_GEU, t0, cpu_crf[crf], t8, tm1, t0); tcg_gen_ext_i32_tl(cpu_gpr[rD(ctx->opcode)], t0); tcg_temp_free_i32(t0); - tcg_temp_free_i32(t8); - tcg_temp_free_i32(tm1); } #endif @@ -7481,6 +7486,8 @@ static int times_4(DisasContext *ctx, int x) #include "decode-insn32.c.inc" #include "decode-insn64.c.inc" +#include "power8-pmu-regs.c.inc" + #include "translate/fixedpoint-impl.c.inc" #include "translate/fp-impl.c.inc" @@ -7573,18 +7580,16 @@ static void gen_brw(DisasContext *ctx) /* brh */ static void gen_brh(DisasContext *ctx) { - TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 mask = tcg_constant_i64(0x00ff00ff00ff00ffull); TCGv_i64 t1 = tcg_temp_new_i64(); TCGv_i64 t2 = tcg_temp_new_i64(); - tcg_gen_movi_i64(t0, 0x00ff00ff00ff00ffull); tcg_gen_shri_i64(t1, cpu_gpr[rS(ctx->opcode)], 8); - tcg_gen_and_i64(t2, t1, t0); - tcg_gen_and_i64(t1, cpu_gpr[rS(ctx->opcode)], t0); + tcg_gen_and_i64(t2, t1, mask); + tcg_gen_and_i64(t1, cpu_gpr[rS(ctx->opcode)], mask); tcg_gen_shli_i64(t1, t1, 8); tcg_gen_or_i64(cpu_gpr[rA(ctx->opcode)], t1, t2); - tcg_temp_free_i64(t0); tcg_temp_free_i64(t1); tcg_temp_free_i64(t2); } @@ -8551,6 +8556,8 @@ static void ppc_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) ctx->tm_enabled = (hflags >> HFLAGS_TM) & 1; ctx->gtse = (hflags >> HFLAGS_GTSE) & 1; ctx->hr = (hflags >> HFLAGS_HR) & 1; + ctx->mmcr0_pmcc0 = (hflags >> HFLAGS_PMCC0) & 1; + ctx->mmcr0_pmcc1 = (hflags >> HFLAGS_PMCC1) & 1; ctx->singlestep_enabled = 0; if ((hflags >> HFLAGS_SE) & 1) { diff --git a/tests/acceptance/ppc_405.py b/tests/acceptance/ppc_405.py new file mode 100644 index 0000000000..c534d5d32f --- /dev/null +++ b/tests/acceptance/ppc_405.py @@ -0,0 +1,42 @@ +# Test that the U-Boot firmware boots on ppc 405 machines and check the console +# +# Copyright (c) 2021 Red Hat, Inc. +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + +from avocado.utils import archive +from avocado_qemu import Test +from avocado_qemu import wait_for_console_pattern +from avocado_qemu import exec_command_and_wait_for_pattern + +class Ppc405Machine(Test): + + timeout = 90 + + def do_test_ppc405(self): + uboot_url = ('https://gitlab.com/huth/u-boot/-/raw/' + 'taihu-2021-10-09/u-boot-taihu.bin') + uboot_hash = ('3208940e908a5edc7c03eab072c60f0dcfadc2ab'); + file_path = self.fetch_asset(uboot_url, asset_hash=uboot_hash) + self.vm.set_console(console_index=1) + self.vm.add_args('-bios', file_path) + self.vm.launch() + wait_for_console_pattern(self, 'AMCC PPC405EP Evaluation Board') + exec_command_and_wait_for_pattern(self, 'reset', 'AMCC PowerPC 405EP') + + def test_ppc_taihu(self): + """ + :avocado: tags=arch:ppc + :avocado: tags=machine:taihu + :avocado: tags=cpu:405ep + """ + self.do_test_ppc405() + + def test_ppc_ref405ep(self): + """ + :avocado: tags=arch:ppc + :avocado: tags=machine:ref405ep + :avocado: tags=cpu:405ep + """ + self.do_test_ppc405() diff --git a/tests/acceptance/ppc_bamboo.py b/tests/acceptance/ppc_bamboo.py new file mode 100644 index 0000000000..dd33bf66f3 --- /dev/null +++ b/tests/acceptance/ppc_bamboo.py @@ -0,0 +1,39 @@ +# Test that Linux kernel boots on the ppc bamboo board and check the console +# +# Copyright (c) 2021 Red Hat +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + +from avocado.utils import archive +from avocado_qemu import Test +from avocado_qemu import wait_for_console_pattern +from avocado_qemu import exec_command_and_wait_for_pattern + +class BambooMachine(Test): + + timeout = 90 + + def test_ppc_bamboo(self): + """ + :avocado: tags=arch:ppc + :avocado: tags=machine:bamboo + :avocado: tags=cpu:440epb + :avocado: tags=device:rtl8139 + """ + tar_url = ('http://landley.net/aboriginal/downloads/binaries/' + 'system-image-powerpc-440fp.tar.gz') + tar_hash = '53e5f16414b195b82d2c70272f81c2eedb39bad9' + file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) + archive.extract(file_path, self.workdir) + self.vm.set_console() + self.vm.add_args('-kernel', self.workdir + + '/system-image-powerpc-440fp/linux', + '-initrd', self.workdir + + '/system-image-powerpc-440fp/rootfs.cpio.gz', + '-nic', 'user,model=rtl8139,restrict=on') + self.vm.launch() + wait_for_console_pattern(self, 'Type exit when done') + exec_command_and_wait_for_pattern(self, 'ping 10.0.2.2', + '10.0.2.2 is alive!') + exec_command_and_wait_for_pattern(self, 'halt', 'System Halted') diff --git a/tests/data/acpi/q35/APIC.xapic b/tests/data/acpi/q35/APIC.xapic Binary files differnew file mode 100644 index 0000000000..c1969c35aa --- /dev/null +++ b/tests/data/acpi/q35/APIC.xapic diff --git a/tests/data/acpi/q35/DMAR.dmar b/tests/data/acpi/q35/DMAR.dmar Binary files differnew file mode 100644 index 0000000000..0dca6e68ad --- /dev/null +++ b/tests/data/acpi/q35/DMAR.dmar diff --git a/tests/data/acpi/q35/DSDT.ivrs b/tests/data/acpi/q35/DSDT.ivrs Binary files differnew file mode 100644 index 0000000000..b0eafe90e5 --- /dev/null +++ b/tests/data/acpi/q35/DSDT.ivrs diff --git a/tests/data/acpi/q35/DSDT.multi-bridge b/tests/data/acpi/q35/DSDT.multi-bridge Binary files differnew file mode 100644 index 0000000000..a24c713d22 --- /dev/null +++ b/tests/data/acpi/q35/DSDT.multi-bridge diff --git a/tests/data/acpi/q35/DSDT.xapic b/tests/data/acpi/q35/DSDT.xapic Binary files differnew file mode 100644 index 0000000000..119fc90f1f --- /dev/null +++ b/tests/data/acpi/q35/DSDT.xapic diff --git a/tests/data/acpi/q35/FACP.xapic b/tests/data/acpi/q35/FACP.xapic Binary files differnew file mode 100644 index 0000000000..2d3659c9c6 --- /dev/null +++ b/tests/data/acpi/q35/FACP.xapic diff --git a/tests/data/acpi/q35/IVRS.ivrs b/tests/data/acpi/q35/IVRS.ivrs Binary files differnew file mode 100644 index 0000000000..17611202e5 --- /dev/null +++ b/tests/data/acpi/q35/IVRS.ivrs diff --git a/tests/data/acpi/q35/SRAT.xapic b/tests/data/acpi/q35/SRAT.xapic Binary files differnew file mode 100644 index 0000000000..1a91cfa65f --- /dev/null +++ b/tests/data/acpi/q35/SRAT.xapic diff --git a/tests/data/acpi/rebuild-expected-aml.sh b/tests/data/acpi/rebuild-expected-aml.sh index fc78770544..dcf2e2f221 100755 --- a/tests/data/acpi/rebuild-expected-aml.sh +++ b/tests/data/acpi/rebuild-expected-aml.sh @@ -12,7 +12,7 @@ # This work is licensed under the terms of the GNU GPLv2. # See the COPYING.LIB file in the top-level directory. -qemu_bins="./qemu-system-x86_64 ./qemu-system-aarch64" +qemu_arches="x86_64 aarch64" if [ ! -e "tests/qtest/bios-tables-test" ]; then echo "Test: bios-tables-test is required! Run make check before this script." @@ -20,6 +20,26 @@ if [ ! -e "tests/qtest/bios-tables-test" ]; then exit 1; fi +if grep TARGET_DIRS= config-host.mak; then + for arch in $qemu_arches; do + if grep TARGET_DIRS= config-host.mak | grep "$arch"-softmmu; + then + qemu_bins="$qemu_bins ./qemu-system-$arch" + fi + done +else + echo "config-host.mak missing!" + echo "Run this script from the build directory." + exit 1; +fi + +if [ -z "$qemu_bins" ]; then + echo "Only the following architectures are currently supported: $qemu_arches" + echo "None of these configured!" + echo "To fix, run configure --target-list=x86_64-softmmu,aarch64-softmmu" + exit 1; +fi + for qemu in $qemu_bins; do if [ ! -e $qemu ]; then echo "Run 'make' to build the following QEMU executables: $qemu_bins" diff --git a/tests/data/acpi/virt/DBG2 b/tests/data/acpi/virt/DBG2 Binary files differnew file mode 100644 index 0000000000..86e6314f7b --- /dev/null +++ b/tests/data/acpi/virt/DBG2 diff --git a/tests/data/acpi/virt/IORT b/tests/data/acpi/virt/IORT Binary files differindex 521acefe9b..7efd0ce8a6 100644 --- a/tests/data/acpi/virt/IORT +++ b/tests/data/acpi/virt/IORT diff --git a/tests/data/acpi/virt/IORT.memhp b/tests/data/acpi/virt/IORT.memhp Binary files differindex 521acefe9b..7efd0ce8a6 100644 --- a/tests/data/acpi/virt/IORT.memhp +++ b/tests/data/acpi/virt/IORT.memhp diff --git a/tests/data/acpi/virt/IORT.numamem b/tests/data/acpi/virt/IORT.numamem Binary files differindex 521acefe9b..7efd0ce8a6 100644 --- a/tests/data/acpi/virt/IORT.numamem +++ b/tests/data/acpi/virt/IORT.numamem diff --git a/tests/data/acpi/virt/IORT.pxb b/tests/data/acpi/virt/IORT.pxb Binary files differindex 521acefe9b..7efd0ce8a6 100644 --- a/tests/data/acpi/virt/IORT.pxb +++ b/tests/data/acpi/virt/IORT.pxb diff --git a/tests/data/acpi/virt/PPTT b/tests/data/acpi/virt/PPTT Binary files differnew file mode 100644 index 0000000000..7a1258ecf1 --- /dev/null +++ b/tests/data/acpi/virt/PPTT diff --git a/tests/qtest/acpi-utils.c b/tests/qtest/acpi-utils.c index d2a202efca..766c48e3a6 100644 --- a/tests/qtest/acpi-utils.c +++ b/tests/qtest/acpi-utils.c @@ -98,6 +98,20 @@ void acpi_fetch_table(QTestState *qts, uint8_t **aml, uint32_t *aml_len, ACPI_ASSERT_CMP(**aml, sig); } if (verify_checksum) { + if (acpi_calc_checksum(*aml, *aml_len)) { + gint fd, ret; + char *fname = NULL; + GError *error = NULL; + + fprintf(stderr, "Invalid '%.4s'(%d)\n", *aml, *aml_len); + fd = g_file_open_tmp("malformed-XXXXXX.dat", &fname, &error); + g_assert_no_error(error); + fprintf(stderr, "Dumping invalid table into '%s'\n", fname); + ret = qemu_write_full(fd, *aml, *aml_len); + g_assert(ret == *aml_len); + close(fd); + g_free(fname); + } g_assert(!acpi_calc_checksum(*aml, *aml_len)); } } diff --git a/tests/qtest/arm-cpu-features.c b/tests/qtest/arm-cpu-features.c index 90a87f0ea9..f76652143a 100644 --- a/tests/qtest/arm-cpu-features.c +++ b/tests/qtest/arm-cpu-features.c @@ -26,21 +26,6 @@ " 'arguments': { 'type': 'full', " #define QUERY_TAIL "}}" -static bool kvm_enabled(QTestState *qts) -{ - QDict *resp, *qdict; - bool enabled; - - resp = qtest_qmp(qts, "{ 'execute': 'query-kvm' }"); - g_assert(qdict_haskey(resp, "return")); - qdict = qdict_get_qdict(resp, "return"); - g_assert(qdict_haskey(qdict, "enabled")); - enabled = qdict_get_bool(qdict, "enabled"); - qobject_unref(resp); - - return enabled; -} - static QDict *do_query_no_props(QTestState *qts, const char *cpu_type) { return qtest_qmp(qts, QUERY_HEAD "'model': { 'name': %s }" @@ -506,14 +491,6 @@ static void test_query_cpu_model_expansion_kvm(const void *data) qts = qtest_init(MACHINE_KVM "-cpu max"); - /* - * These tests target the 'host' CPU type, so KVM must be enabled. - */ - if (!kvm_enabled(qts)) { - qtest_quit(qts); - return; - } - /* Enabling and disabling kvm-no-adjvtime should always work. */ assert_has_feature_disabled(qts, "host", "kvm-no-adjvtime"); assert_set_feature(qts, "host", "kvm-no-adjvtime", true); @@ -637,7 +614,11 @@ int main(int argc, char **argv) * order avoid attempting to run an AArch32 QEMU with KVM on * AArch64 hosts. That won't work and isn't easy to detect. */ - if (g_str_equal(qtest_get_arch(), "aarch64")) { + if (g_str_equal(qtest_get_arch(), "aarch64") && qtest_has_accel("kvm")) { + /* + * This tests target the 'host' CPU type, so register it only if + * KVM is available. + */ qtest_add_data_func("/arm/kvm/query-cpu-model-expansion", NULL, test_query_cpu_model_expansion_kvm); } diff --git a/tests/qtest/bios-tables-test.c b/tests/qtest/bios-tables-test.c index 4f11d03055..258874167e 100644 --- a/tests/qtest/bios-tables-test.c +++ b/tests/qtest/bios-tables-test.c @@ -271,19 +271,28 @@ static void dump_aml_files(test_data *data, bool rebuild) } } +static bool create_tmp_asl(AcpiSdtTable *sdt) +{ + GError *error = NULL; + gint fd; + + fd = g_file_open_tmp("asl-XXXXXX.dsl", &sdt->asl_file, &error); + g_assert_no_error(error); + close(fd); + + return false; +} + 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); + create_tmp_asl(sdt); /* build command line */ g_string_append_printf(command_line, " -p %s ", sdt->asl_file); @@ -463,11 +472,20 @@ static void test_acpi_asl(test_data *data) 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); + /* + * If expected file is empty - it's likely that it was a stub just + * created for step 1 above: we do want to decompile the actual one. + */ + if (exp_sdt->aml_len) { + exp_err = load_asl(exp_data.tables, exp_sdt); + exp_asl = normalize_asl(exp_sdt->asl); + } else { + exp_err = create_tmp_asl(exp_sdt); + exp_asl = g_string_new(""); + } /* TODO: check for warnings */ - g_assert(!err || exp_err); + g_assert(!err || exp_err || !exp_sdt->aml_len); if (g_strcmp0(asl->str, exp_asl->str)) { sdt->tmp_files_retain = true; @@ -722,13 +740,6 @@ static void test_acpi_one(const char *params, test_data *data) char *args; bool use_uefi = data->uefi_fl1 && data->uefi_fl2; -#ifndef CONFIG_TCG - if (data->tcg_only) { - g_test_skip("TCG disabled, skipping ACPI tcg_only test"); - return; - } -#endif /* CONFIG_TCG */ - args = test_acpi_create_args(data, params, use_uefi); data->qts = qtest_init(args); test_acpi_load_tables(data, use_uefi); @@ -859,6 +870,23 @@ static void test_acpi_q35_tcg_bridge(void) free_test_data(&data); } +static void test_acpi_q35_multif_bridge(void) +{ + test_data data = { + .machine = MACHINE_Q35, + .variant = ".multi-bridge", + }; + test_acpi_one("-device pcie-root-port,id=pcie-root-port-0," + "multifunction=on," + "port=0x0,chassis=1,addr=0x2,bus=pcie.0 " + "-device pcie-root-port,id=pcie-root-port-1," + "port=0x1,chassis=2,addr=0x3.0x1,bus=pcie.0 " + "-device virtio-balloon,id=balloon0," + "bus=pcie.0,addr=0x4.0x2", + &data); + free_test_data(&data); +} + static void test_acpi_q35_tcg_mmio64(void) { test_data data = { @@ -1033,6 +1061,19 @@ static void test_acpi_q35_tcg_numamem(void) free_test_data(&data); } +static void test_acpi_q35_kvm_xapic(void) +{ + test_data data; + + memset(&data, 0, sizeof(data)); + data.machine = MACHINE_Q35; + data.variant = ".xapic"; + test_acpi_one(" -object memory-backend-ram,id=ram0,size=128M" + " -numa node -numa node,memdev=ram0" + " -machine kernel-irqchip=on -smp 1,maxcpus=288", &data); + free_test_data(&data); +} + static void test_acpi_q35_tcg_nosmm(void) { test_data data; @@ -1077,6 +1118,30 @@ static void test_acpi_q35_tcg_nohpet(void) free_test_data(&data); } +static void test_acpi_q35_kvm_dmar(void) +{ + test_data data; + + memset(&data, 0, sizeof(data)); + data.machine = MACHINE_Q35; + data.variant = ".dmar"; + test_acpi_one("-machine kernel-irqchip=split -accel kvm" + " -device intel-iommu,intremap=on,device-iotlb=on", &data); + free_test_data(&data); +} + +static void test_acpi_q35_tcg_ivrs(void) +{ + test_data data; + + memset(&data, 0, sizeof(data)); + data.machine = MACHINE_Q35; + data.variant = ".ivrs"; + data.tcg_only = true, + test_acpi_one(" -device amd-iommu", &data); + free_test_data(&data); +} + static void test_acpi_piix4_tcg_numamem(void) { test_data data; @@ -1393,9 +1458,6 @@ static void test_acpi_virt_tcg(void) .scan_len = 128ULL * 1024 * 1024, }; - test_acpi_one("-cpu cortex-a57", &data); - free_test_data(&data); - data.smbios_cpu_max_speed = 2900; data.smbios_cpu_curr_speed = 2700; test_acpi_one("-cpu cortex-a57 " @@ -1509,6 +1571,8 @@ static void test_acpi_oem_fields_virt(void) int main(int argc, char *argv[]) { const char *arch = qtest_get_arch(); + const bool has_kvm = qtest_has_accel("kvm"); + const bool has_tcg = qtest_has_accel("tcg"); int ret; g_test_init(&argc, &argv, NULL); @@ -1534,6 +1598,7 @@ int main(int argc, char *argv[]) test_acpi_piix4_no_acpi_pci_hotplug); qtest_add_func("acpi/q35", test_acpi_q35_tcg); qtest_add_func("acpi/q35/bridge", test_acpi_q35_tcg_bridge); + qtest_add_func("acpi/q35/multif-bridge", test_acpi_q35_multif_bridge); qtest_add_func("acpi/q35/mmio64", test_acpi_q35_tcg_mmio64); qtest_add_func("acpi/piix4/ipmi", test_acpi_piix4_tcg_ipmi); qtest_add_func("acpi/q35/ipmi", test_acpi_q35_tcg_ipmi); @@ -1564,15 +1629,24 @@ int main(int argc, char *argv[]) qtest_add_func("acpi/microvm/rtc", test_acpi_microvm_rtc_tcg); qtest_add_func("acpi/microvm/ioapic2", test_acpi_microvm_ioapic2_tcg); qtest_add_func("acpi/microvm/oem-fields", test_acpi_oem_fields_microvm); - if (strcmp(arch, "x86_64") == 0) { - qtest_add_func("acpi/microvm/pcie", test_acpi_microvm_pcie_tcg); + if (has_tcg) { + qtest_add_func("acpi/q35/ivrs", test_acpi_q35_tcg_ivrs); + if (strcmp(arch, "x86_64") == 0) { + qtest_add_func("acpi/microvm/pcie", test_acpi_microvm_pcie_tcg); + } + } + if (has_kvm) { + qtest_add_func("acpi/q35/kvm/xapic", test_acpi_q35_kvm_xapic); + qtest_add_func("acpi/q35/kvm/dmar", test_acpi_q35_kvm_dmar); } } else if (strcmp(arch, "aarch64") == 0) { - qtest_add_func("acpi/virt", test_acpi_virt_tcg); - qtest_add_func("acpi/virt/numamem", test_acpi_virt_tcg_numamem); - qtest_add_func("acpi/virt/memhp", test_acpi_virt_tcg_memhp); - qtest_add_func("acpi/virt/pxb", test_acpi_virt_tcg_pxb); - qtest_add_func("acpi/virt/oem-fields", test_acpi_oem_fields_virt); + if (has_tcg) { + qtest_add_func("acpi/virt", test_acpi_virt_tcg); + qtest_add_func("acpi/virt/numamem", test_acpi_virt_tcg_numamem); + qtest_add_func("acpi/virt/memhp", test_acpi_virt_tcg_memhp); + qtest_add_func("acpi/virt/pxb", test_acpi_virt_tcg_pxb); + qtest_add_func("acpi/virt/oem-fields", test_acpi_oem_fields_virt); + } } ret = g_test_run(); boot_sector_cleanup(disk); diff --git a/tests/qtest/libqos/libqtest.h b/tests/qtest/libqos/libqtest.h index a68dcd79d4..59e9271195 100644 --- a/tests/qtest/libqos/libqtest.h +++ b/tests/qtest/libqos/libqtest.h @@ -589,6 +589,14 @@ bool qtest_big_endian(QTestState *s); const char *qtest_get_arch(void); /** + * qtest_has_accel: + * @accel_name: Accelerator name to check for. + * + * Returns: true if the accelerator is built in. + */ +bool qtest_has_accel(const char *accel_name); + +/** * qtest_add_func: * @str: Test case path. * @fn: Test case function diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c index 73f6b977a6..25aeea385b 100644 --- a/tests/qtest/libqtest.c +++ b/tests/qtest/libqtest.c @@ -922,6 +922,33 @@ const char *qtest_get_arch(void) return end + 1; } +bool qtest_has_accel(const char *accel_name) +{ + if (g_str_equal(accel_name, "tcg")) { +#if defined(CONFIG_TCG) + return true; +#else + return false; +#endif + } else if (g_str_equal(accel_name, "kvm")) { + int i; + const char *arch = qtest_get_arch(); + const char *targets[] = { CONFIG_KVM_TARGETS }; + + for (i = 0; i < ARRAY_SIZE(targets); i++) { + if (!strncmp(targets[i], arch, strlen(arch))) { + if (!access("/dev/kvm", R_OK | W_OK)) { + return true; + } + } + } + } else { + /* not implemented */ + g_assert_not_reached(); + } + return false; +} + bool qtest_get_irq(QTestState *s, int num) { /* dummy operation in order to make sure irq is up to date */ diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index cc5e83d98a..7b42f6fd90 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -1420,6 +1420,7 @@ static bool kvm_dirty_ring_supported(void) int main(int argc, char **argv) { char template[] = "/tmp/migration-test-XXXXXX"; + const bool has_kvm = qtest_has_accel("kvm"); int ret; g_test_init(&argc, &argv, NULL); @@ -1434,8 +1435,7 @@ int main(int argc, char **argv) * some reason) */ if (g_str_equal(qtest_get_arch(), "ppc64") && - (access("/sys/module/kvm_hv", F_OK) || - access("/dev/kvm", R_OK | W_OK))) { + (!has_kvm || access("/sys/module/kvm_hv", F_OK))) { g_test_message("Skipping test: kvm_hv not available"); return g_test_run(); } @@ -1444,16 +1444,9 @@ int main(int argc, char **argv) * Similar to ppc64, s390x seems to be touchy with TCG, so disable it * there until the problems are resolved */ - if (g_str_equal(qtest_get_arch(), "s390x")) { -#if defined(HOST_S390X) - if (access("/dev/kvm", R_OK | W_OK)) { - g_test_message("Skipping test: kvm not available"); - return g_test_run(); - } -#else - g_test_message("Skipping test: Need s390x host to work properly"); + if (g_str_equal(qtest_get_arch(), "s390x") && !has_kvm) { + g_test_message("Skipping test: s390x host with KVM is required"); return g_test_run(); -#endif } tmpfs = mkdtemp(template); diff --git a/tests/qtest/vhost-user-blk-test.c b/tests/qtest/vhost-user-blk-test.c index 6f108a1b62..62e670f39b 100644 --- a/tests/qtest/vhost-user-blk-test.c +++ b/tests/qtest/vhost-user-blk-test.c @@ -906,9 +906,9 @@ static void start_vhost_user_blk(GString *cmd_line, int vus_instances, img_path = drive_create(); g_string_append_printf(storage_daemon_command, "--blockdev driver=file,node-name=disk%d,filename=%s " - "--export type=vhost-user-blk,id=disk%d,addr.type=unix,addr.path=%s," + "--export type=vhost-user-blk,id=disk%d,addr.type=fd,addr.str=%d," "node-name=disk%i,writable=on,num-queues=%d ", - i, img_path, i, sock_path, i, num_queues); + i, img_path, i, fd, i, num_queues); g_string_append_printf(cmd_line, "-chardev socket,id=char%d,path=%s ", i + 1, sock_path); diff --git a/tests/tcg/ppc64/Makefile.target b/tests/tcg/ppc64/Makefile.target index a6a4ddaeca..6ab7934fdf 100644 --- a/tests/tcg/ppc64/Makefile.target +++ b/tests/tcg/ppc64/Makefile.target @@ -23,4 +23,6 @@ run-plugin-byte_reverse-with-%: $(call skip-test, "RUN of byte_reverse ($*)", "not built") endif +PPC64_TESTS += signal_save_restore_xer + TESTS += $(PPC64_TESTS) diff --git a/tests/tcg/ppc64le/Makefile.target b/tests/tcg/ppc64le/Makefile.target index c0c14ffbad..5e65b1590d 100644 --- a/tests/tcg/ppc64le/Makefile.target +++ b/tests/tcg/ppc64le/Makefile.target @@ -22,4 +22,6 @@ run-plugin-byte_reverse-with-%: $(call skip-test, "RUN of byte_reverse ($*)", "not built") endif +PPC64LE_TESTS += signal_save_restore_xer + TESTS += $(PPC64LE_TESTS) diff --git a/tests/tcg/ppc64le/signal_save_restore_xer.c b/tests/tcg/ppc64le/signal_save_restore_xer.c new file mode 100644 index 0000000000..e4f8a07dd7 --- /dev/null +++ b/tests/tcg/ppc64le/signal_save_restore_xer.c @@ -0,0 +1,42 @@ +#include <assert.h> +#include <stdint.h> +#include <signal.h> +#include <sys/user.h> + +#define XER_SO (1 << 31) +#define XER_OV (1 << 30) +#define XER_CA (1 << 29) +#define XER_OV32 (1 << 19) +#define XER_CA32 (1 << 18) + +uint64_t saved; + +void sigill_handler(int sig, siginfo_t *si, void *ucontext) +{ + ucontext_t *uc = ucontext; + uc->uc_mcontext.regs->nip += 4; + saved = uc->uc_mcontext.regs->xer; + uc->uc_mcontext.regs->xer |= XER_OV | XER_OV32; +} + +int main(void) +{ + uint64_t initial = XER_CA | XER_CA32, restored; + struct sigaction sa = { + .sa_sigaction = sigill_handler, + .sa_flags = SA_SIGINFO + }; + + sigaction(SIGILL, &sa, NULL); + + asm("mtspr 1, %1\n\t" + ".long 0x0\n\t" + "mfspr %0, 1\n\t" + : "=r" (restored) + : "r" (initial)); + + assert(saved == initial); + assert(restored == (XER_OV | XER_OV32 | XER_CA | XER_CA32)); + + return 0; +} |