aboutsummaryrefslogtreecommitdiff
path: root/hw
diff options
context:
space:
mode:
Diffstat (limited to 'hw')
-rw-r--r--hw/acpi/aml-build.c89
-rw-r--r--hw/arm/boot.c13
-rw-r--r--hw/arm/sbsa-ref.c2
-rw-r--r--hw/arm/virt-acpi-build.c117
-rw-r--r--hw/arm/virt.c71
-rw-r--r--hw/core/loader.c60
-rw-r--r--hw/intc/spapr_xive.c2
-rw-r--r--hw/intc/spapr_xive_kvm.c14
-rw-r--r--hw/intc/xive.c8
-rw-r--r--hw/m68k/q800.c169
-rw-r--r--hw/misc/mac_via.c23
-rw-r--r--hw/misc/trace-events1
-rw-r--r--hw/pci-host/mv64361.c1
-rw-r--r--hw/ppc/pegasos2.c162
-rw-r--r--hw/ppc/ppc.c6
-rw-r--r--hw/ppc/ppc4xx_pci.c8
-rw-r--r--hw/ppc/spapr_softmmu.c15
17 files changed, 600 insertions, 161 deletions
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/m68k/q800.c b/hw/m68k/q800.c
index fd4855047e..e4c7c9b88a 100644
--- a/hw/m68k/q800.c
+++ b/hw/m68k/q800.c
@@ -28,6 +28,7 @@
#include "cpu.h"
#include "hw/boards.h"
#include "hw/or-irq.h"
+#include "hw/nmi.h"
#include "elf.h"
#include "hw/loader.h"
#include "ui/console.h"
@@ -100,13 +101,110 @@ struct GLUEState {
SysBusDevice parent_obj;
M68kCPU *cpu;
uint8_t ipr;
+ uint8_t auxmode;
+ qemu_irq irqs[1];
+ QEMUTimer *nmi_release;
};
+#define GLUE_IRQ_IN_VIA1 0
+#define GLUE_IRQ_IN_VIA2 1
+#define GLUE_IRQ_IN_SONIC 2
+#define GLUE_IRQ_IN_ESCC 3
+#define GLUE_IRQ_IN_NMI 4
+
+#define GLUE_IRQ_NUBUS_9 0
+
+/*
+ * The GLUE logic on the Quadra 800 supports 2 different IRQ routing modes
+ * controlled from the VIA1 auxmode GPIO (port B bit 6) which are documented
+ * in NetBSD as follows:
+ *
+ * A/UX mode (Linux, NetBSD, auxmode GPIO low)
+ *
+ * Level 0: Spurious: ignored
+ * Level 1: Software
+ * Level 2: VIA2 (except ethernet, sound)
+ * Level 3: Ethernet
+ * Level 4: Serial (SCC)
+ * Level 5: Sound
+ * Level 6: VIA1
+ * Level 7: NMIs: parity errors, RESET button, YANCC error
+ *
+ * Classic mode (default: used by MacOS, A/UX 3.0.1, auxmode GPIO high)
+ *
+ * Level 0: Spurious: ignored
+ * Level 1: VIA1 (clock, ADB)
+ * Level 2: VIA2 (NuBus, SCSI)
+ * Level 3:
+ * Level 4: Serial (SCC)
+ * Level 5:
+ * Level 6:
+ * Level 7: Non-maskable: parity errors, RESET button
+ *
+ * Note that despite references to A/UX mode in Linux and NetBSD, at least
+ * A/UX 3.0.1 still uses Classic mode.
+ */
+
static void GLUE_set_irq(void *opaque, int irq, int level)
{
GLUEState *s = opaque;
int i;
+ if (s->auxmode) {
+ /* Classic mode */
+ switch (irq) {
+ case GLUE_IRQ_IN_VIA1:
+ irq = 0;
+ break;
+
+ case GLUE_IRQ_IN_VIA2:
+ irq = 1;
+ break;
+
+ case GLUE_IRQ_IN_SONIC:
+ /* Route to VIA2 instead */
+ qemu_set_irq(s->irqs[GLUE_IRQ_NUBUS_9], level);
+ return;
+
+ case GLUE_IRQ_IN_ESCC:
+ irq = 3;
+ break;
+
+ case GLUE_IRQ_IN_NMI:
+ irq = 6;
+ break;
+
+ default:
+ g_assert_not_reached();
+ }
+ } else {
+ /* A/UX mode */
+ switch (irq) {
+ case GLUE_IRQ_IN_VIA1:
+ irq = 5;
+ break;
+
+ case GLUE_IRQ_IN_VIA2:
+ irq = 1;
+ break;
+
+ case GLUE_IRQ_IN_SONIC:
+ irq = 2;
+ break;
+
+ case GLUE_IRQ_IN_ESCC:
+ irq = 3;
+ break;
+
+ case GLUE_IRQ_IN_NMI:
+ irq = 6;
+ break;
+
+ default:
+ g_assert_not_reached();
+ }
+ }
+
if (level) {
s->ipr |= 1 << irq;
} else {
@@ -122,11 +220,37 @@ static void GLUE_set_irq(void *opaque, int irq, int level)
m68k_set_irq_level(s->cpu, 0, 0);
}
+static void glue_auxmode_set_irq(void *opaque, int irq, int level)
+{
+ GLUEState *s = GLUE(opaque);
+
+ s->auxmode = level;
+}
+
+static void glue_nmi(NMIState *n, int cpu_index, Error **errp)
+{
+ GLUEState *s = GLUE(n);
+
+ /* Hold NMI active for 100ms */
+ GLUE_set_irq(s, GLUE_IRQ_IN_NMI, 1);
+ timer_mod(s->nmi_release, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 100);
+}
+
+static void glue_nmi_release(void *opaque)
+{
+ GLUEState *s = GLUE(opaque);
+
+ GLUE_set_irq(s, GLUE_IRQ_IN_NMI, 0);
+}
+
static void glue_reset(DeviceState *dev)
{
GLUEState *s = GLUE(dev);
s->ipr = 0;
+ s->auxmode = 0;
+
+ timer_del(s->nmi_release);
}
static const VMStateDescription vmstate_glue = {
@@ -135,6 +259,8 @@ static const VMStateDescription vmstate_glue = {
.minimum_version_id = 0,
.fields = (VMStateField[]) {
VMSTATE_UINT8(ipr, GLUEState),
+ VMSTATE_UINT8(auxmode, GLUEState),
+ VMSTATE_TIMER_PTR(nmi_release, GLUEState),
VMSTATE_END_OF_LIST(),
},
};
@@ -150,20 +276,36 @@ static Property glue_properties[] = {
DEFINE_PROP_END_OF_LIST(),
};
+static void glue_finalize(Object *obj)
+{
+ GLUEState *s = GLUE(obj);
+
+ timer_free(s->nmi_release);
+}
+
static void glue_init(Object *obj)
{
DeviceState *dev = DEVICE(obj);
+ GLUEState *s = GLUE(dev);
qdev_init_gpio_in(dev, GLUE_set_irq, 8);
+ qdev_init_gpio_in_named(dev, glue_auxmode_set_irq, "auxmode", 1);
+
+ qdev_init_gpio_out(dev, s->irqs, 1);
+
+ /* NMI release timer */
+ s->nmi_release = timer_new_ms(QEMU_CLOCK_VIRTUAL, glue_nmi_release, s);
}
static void glue_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
+ NMIClass *nc = NMI_CLASS(klass);
dc->vmsd = &vmstate_glue;
dc->reset = glue_reset;
device_class_set_props(dc, glue_properties);
+ nc->nmi_monitor_handler = glue_nmi;
}
static const TypeInfo glue_info = {
@@ -171,7 +313,12 @@ static const TypeInfo glue_info = {
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(GLUEState),
.instance_init = glue_init,
+ .instance_finalize = glue_finalize,
.class_init = glue_class_init,
+ .interfaces = (InterfaceInfo[]) {
+ { TYPE_NMI },
+ { }
+ },
};
static void main_cpu_reset(void *opaque)
@@ -284,7 +431,10 @@ static void q800_init(MachineState *machine)
sysbus = SYS_BUS_DEVICE(via1_dev);
sysbus_realize_and_unref(sysbus, &error_fatal);
sysbus_mmio_map(sysbus, 1, VIA_BASE);
- sysbus_connect_irq(sysbus, 0, qdev_get_gpio_in(glue, 0));
+ sysbus_connect_irq(sysbus, 0, qdev_get_gpio_in(glue, GLUE_IRQ_IN_VIA1));
+ /* A/UX mode */
+ qdev_connect_gpio_out(via1_dev, 0,
+ qdev_get_gpio_in_named(glue, "auxmode", 0));
adb_bus = qdev_get_child_bus(via1_dev, "adb.0");
dev = qdev_new(TYPE_ADB_KEYBOARD);
@@ -297,7 +447,7 @@ static void q800_init(MachineState *machine)
sysbus = SYS_BUS_DEVICE(via2_dev);
sysbus_realize_and_unref(sysbus, &error_fatal);
sysbus_mmio_map(sysbus, 1, VIA_BASE + VIA_SIZE);
- sysbus_connect_irq(sysbus, 0, qdev_get_gpio_in(glue, 1));
+ sysbus_connect_irq(sysbus, 0, qdev_get_gpio_in(glue, GLUE_IRQ_IN_VIA2));
/* MACSONIC */
@@ -330,7 +480,7 @@ static void q800_init(MachineState *machine)
sysbus = SYS_BUS_DEVICE(dev);
sysbus_realize_and_unref(sysbus, &error_fatal);
sysbus_mmio_map(sysbus, 0, SONIC_BASE);
- sysbus_connect_irq(sysbus, 0, qdev_get_gpio_in(glue, 2));
+ sysbus_connect_irq(sysbus, 0, qdev_get_gpio_in(glue, GLUE_IRQ_IN_SONIC));
memory_region_init_rom(dp8393x_prom, NULL, "dp8393x-q800.prom",
SONIC_PROM_SIZE, &error_fatal);
@@ -366,7 +516,8 @@ static void q800_init(MachineState *machine)
qdev_realize_and_unref(escc_orgate, NULL, &error_fatal);
sysbus_connect_irq(sysbus, 0, qdev_get_gpio_in(escc_orgate, 0));
sysbus_connect_irq(sysbus, 1, qdev_get_gpio_in(escc_orgate, 1));
- qdev_connect_gpio_out(DEVICE(escc_orgate), 0, qdev_get_gpio_in(glue, 3));
+ qdev_connect_gpio_out(DEVICE(escc_orgate), 0,
+ qdev_get_gpio_in(glue, GLUE_IRQ_IN_ESCC));
sysbus_mmio_map(sysbus, 0, SCC_BASE);
/* SCSI */
@@ -416,6 +567,14 @@ static void q800_init(MachineState *machine)
VIA2_NUBUS_IRQ_9 + i));
}
+ /*
+ * Since the framebuffer in slot 0x9 uses a separate IRQ, wire the unused
+ * IRQ via GLUE for use by SONIC Ethernet in classic mode
+ */
+ qdev_connect_gpio_out(glue, GLUE_IRQ_NUBUS_9,
+ qdev_get_gpio_in_named(via2_dev, "nubus-irq",
+ VIA2_NUBUS_IRQ_9));
+
nubus = &NUBUS_BRIDGE(dev)->bus;
/* framebuffer in nubus slot #9 */
@@ -425,7 +584,7 @@ static void q800_init(MachineState *machine)
qdev_prop_set_uint32(dev, "width", graphic_width);
qdev_prop_set_uint32(dev, "height", graphic_height);
qdev_prop_set_uint8(dev, "depth", graphic_depth);
- if (graphic_width == 1152 && graphic_height == 870 && graphic_depth == 8) {
+ if (graphic_width == 1152 && graphic_height == 870) {
qdev_prop_set_uint8(dev, "display", MACFB_DISPLAY_APPLE_21_COLOR);
} else {
qdev_prop_set_uint8(dev, "display", MACFB_DISPLAY_VGA);
diff --git a/hw/misc/mac_via.c b/hw/misc/mac_via.c
index 993bac017d..b378e6b305 100644
--- a/hw/misc/mac_via.c
+++ b/hw/misc/mac_via.c
@@ -130,6 +130,10 @@
* On SE/30, vertical sync interrupt enable.
* 0=enabled. This vSync interrupt shows up
* as a slot $E interrupt.
+ * On Quadra 800 this bit toggles A/UX mode which
+ * configures the glue logic to deliver some IRQs
+ * at different levels compared to a classic
+ * Mac.
*/
#define VIA1B_vADBS2 0x20 /* ADB state input bit 1 (unused on IIfx) */
#define VIA1B_vADBS1 0x10 /* ADB state input bit 0 (unused on IIfx) */
@@ -876,6 +880,21 @@ static void via1_adb_update(MOS6522Q800VIA1State *v1s)
}
}
+static void via1_auxmode_update(MOS6522Q800VIA1State *v1s)
+{
+ MOS6522State *s = MOS6522(v1s);
+ int oldirq, irq;
+
+ oldirq = (v1s->last_b & VIA1B_vMystery) ? 1 : 0;
+ irq = (s->b & VIA1B_vMystery) ? 1 : 0;
+
+ /* Check to see if the A/UX mode bit has changed */
+ if (irq != oldirq) {
+ trace_via1_auxmode(irq);
+ qemu_set_irq(v1s->auxmode_irq, irq);
+ }
+}
+
static uint64_t mos6522_q800_via1_read(void *opaque, hwaddr addr, unsigned size)
{
MOS6522Q800VIA1State *s = MOS6522_Q800_VIA1(opaque);
@@ -898,6 +917,7 @@ static void mos6522_q800_via1_write(void *opaque, hwaddr addr, uint64_t val,
case VIA_REG_B:
via1_rtc_update(v1s);
via1_adb_update(v1s);
+ via1_auxmode_update(v1s);
v1s->last_b = ms->b;
break;
@@ -1042,6 +1062,9 @@ static void mos6522_q800_via1_init(Object *obj)
TYPE_ADB_BUS, DEVICE(v1s), "adb.0");
qdev_init_gpio_in(DEVICE(obj), via1_irq_request, VIA1_IRQ_NB);
+
+ /* A/UX mode */
+ qdev_init_gpio_out(DEVICE(obj), &v1s->auxmode_irq, 1);
}
static const VMStateDescription vmstate_q800_via1 = {
diff --git a/hw/misc/trace-events b/hw/misc/trace-events
index ede413965b..2da96d167a 100644
--- a/hw/misc/trace-events
+++ b/hw/misc/trace-events
@@ -228,6 +228,7 @@ via1_rtc_cmd_pram_sect_write(int sector, int offset, int addr, int value) "secto
via1_adb_send(const char *state, uint8_t data, const char *vadbint) "state %s data=0x%02x vADBInt=%s"
via1_adb_receive(const char *state, uint8_t data, const char *vadbint, int status, int index, int size) "state %s data=0x%02x vADBInt=%s status=0x%x index=%d size=%d"
via1_adb_poll(uint8_t data, const char *vadbint, int status, int index, int size) "data=0x%02x vADBInt=%s status=0x%x index=%d size=%d"
+via1_auxmode(int mode) "setting auxmode to %d"
# grlib_ahb_apb_pnp.c
grlib_ahb_pnp_read(uint64_t addr, uint32_t value) "AHB PnP read addr:0x%03"PRIx64" data:0x%08x"
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/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)
{