diff options
author | Chanho Park <chanho61.park@samsung.com> | 2014-06-26 20:28:10 +0900 |
---|---|---|
committer | Chanho Park <chanho61.park@samsung.com> | 2014-07-07 16:25:44 +0900 |
commit | a15119db2ff5c2fdfdeb913b297bf8aa3399132e (patch) | |
tree | 7d6f779408bb772b11c029ab88000fc01856b599 /hw/ppc | |
parent | 340f06c9eaee097e626c251bf7a013350649c091 (diff) | |
download | qemu-a15119db2ff5c2fdfdeb913b297bf8aa3399132e.tar.gz qemu-a15119db2ff5c2fdfdeb913b297bf8aa3399132e.tar.bz2 qemu-a15119db2ff5c2fdfdeb913b297bf8aa3399132e.zip |
Imported Upstream version 2.0.0upstream/2.0.0
Change-Id: I081766c4314e7893f54fec80b920b1638d15021f
Diffstat (limited to 'hw/ppc')
-rw-r--r-- | hw/ppc/Makefile.objs | 6 | ||||
-rw-r--r-- | hw/ppc/e500.c | 306 | ||||
-rw-r--r-- | hw/ppc/e500.h | 13 | ||||
-rw-r--r-- | hw/ppc/e500plat.c | 21 | ||||
-rw-r--r-- | hw/ppc/mac.h | 1 | ||||
-rw-r--r-- | hw/ppc/mac_newworld.c | 4 | ||||
-rw-r--r-- | hw/ppc/mac_oldworld.c | 4 | ||||
-rw-r--r-- | hw/ppc/mpc8544ds.c | 21 | ||||
-rw-r--r-- | hw/ppc/ppc.c | 186 | ||||
-rw-r--r-- | hw/ppc/ppc405_boards.c | 41 | ||||
-rw-r--r-- | hw/ppc/ppc405_uc.c | 71 | ||||
-rw-r--r-- | hw/ppc/ppc440_bamboo.c | 32 | ||||
-rw-r--r-- | hw/ppc/ppc4xx_devs.c | 4 | ||||
-rw-r--r-- | hw/ppc/ppc4xx_pci.c | 5 | ||||
-rw-r--r-- | hw/ppc/ppc_booke.c | 87 | ||||
-rw-r--r-- | hw/ppc/ppce500_spin.c | 8 | ||||
-rw-r--r-- | hw/ppc/prep.c | 155 | ||||
-rw-r--r-- | hw/ppc/spapr.c | 331 | ||||
-rw-r--r-- | hw/ppc/spapr_events.c | 6 | ||||
-rw-r--r-- | hw/ppc/spapr_hcall.c | 221 | ||||
-rw-r--r-- | hw/ppc/spapr_iommu.c | 108 | ||||
-rw-r--r-- | hw/ppc/spapr_pci.c | 232 | ||||
-rw-r--r-- | hw/ppc/spapr_rtas.c | 134 | ||||
-rw-r--r-- | hw/ppc/spapr_vio.c | 17 | ||||
-rw-r--r-- | hw/ppc/virtex_ml507.c | 97 |
25 files changed, 1259 insertions, 852 deletions
diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs index 7a1cd5d89..ea747f0a2 100644 --- a/hw/ppc/Makefile.objs +++ b/hw/ppc/Makefile.objs @@ -8,11 +8,11 @@ obj-$(CONFIG_PSERIES) += spapr_pci.o obj-y += ppc405_boards.o ppc4xx_devs.o ppc405_uc.o ppc440_bamboo.o obj-y += ppc4xx_pci.o # PReP -obj-y += prep.o +obj-$(CONFIG_PREP) += prep.o # OldWorld PowerMac -obj-y += mac_oldworld.o +obj-$(CONFIG_MAC) += mac_oldworld.o # NewWorld PowerMac -obj-y += mac_newworld.o +obj-$(CONFIG_MAC) += mac_newworld.o # e500 obj-$(CONFIG_E500) += e500.o mpc8544ds.o e500plat.o obj-$(CONFIG_E500) += mpc8544_guts.o ppce500_spin.o diff --git a/hw/ppc/e500.c b/hw/ppc/e500.c index f00a62a1c..f984b3e9a 100644 --- a/hw/ppc/e500.c +++ b/hw/ppc/e500.c @@ -108,29 +108,31 @@ static void dt_serial_create(void *fdt, unsigned long long offset, char ser[128]; snprintf(ser, sizeof(ser), "%s/serial@%llx", soc, offset); - qemu_devtree_add_subnode(fdt, ser); - qemu_devtree_setprop_string(fdt, ser, "device_type", "serial"); - qemu_devtree_setprop_string(fdt, ser, "compatible", "ns16550"); - qemu_devtree_setprop_cells(fdt, ser, "reg", offset, 0x100); - qemu_devtree_setprop_cell(fdt, ser, "cell-index", idx); - qemu_devtree_setprop_cell(fdt, ser, "clock-frequency", 0); - qemu_devtree_setprop_cells(fdt, ser, "interrupts", 42, 2); - qemu_devtree_setprop_phandle(fdt, ser, "interrupt-parent", mpic); - qemu_devtree_setprop_string(fdt, "/aliases", alias, ser); + qemu_fdt_add_subnode(fdt, ser); + qemu_fdt_setprop_string(fdt, ser, "device_type", "serial"); + qemu_fdt_setprop_string(fdt, ser, "compatible", "ns16550"); + qemu_fdt_setprop_cells(fdt, ser, "reg", offset, 0x100); + qemu_fdt_setprop_cell(fdt, ser, "cell-index", idx); + qemu_fdt_setprop_cell(fdt, ser, "clock-frequency", 0); + qemu_fdt_setprop_cells(fdt, ser, "interrupts", 42, 2); + qemu_fdt_setprop_phandle(fdt, ser, "interrupt-parent", mpic); + qemu_fdt_setprop_string(fdt, "/aliases", alias, ser); if (defcon) { - qemu_devtree_setprop_string(fdt, "/chosen", "linux,stdout-path", ser); + qemu_fdt_setprop_string(fdt, "/chosen", "linux,stdout-path", ser); } } -static int ppce500_load_device_tree(CPUPPCState *env, +static int ppce500_load_device_tree(QEMUMachineInitArgs *args, PPCE500Params *params, hwaddr addr, hwaddr initrd_base, - hwaddr initrd_size) + hwaddr initrd_size, + bool dry_run) { + CPUPPCState *env = first_cpu->env_ptr; int ret = -1; - uint64_t mem_reg_property[] = { 0, cpu_to_be64(params->ram_size) }; + uint64_t mem_reg_property[] = { 0, cpu_to_be64(args->ram_size) }; int fdt_size; void *fdt; uint8_t hypercall[16]; @@ -181,31 +183,31 @@ static int ppce500_load_device_tree(CPUPPCState *env, } /* Manipulate device tree in memory. */ - qemu_devtree_setprop_cell(fdt, "/", "#address-cells", 2); - qemu_devtree_setprop_cell(fdt, "/", "#size-cells", 2); + qemu_fdt_setprop_cell(fdt, "/", "#address-cells", 2); + qemu_fdt_setprop_cell(fdt, "/", "#size-cells", 2); - qemu_devtree_add_subnode(fdt, "/memory"); - qemu_devtree_setprop_string(fdt, "/memory", "device_type", "memory"); - qemu_devtree_setprop(fdt, "/memory", "reg", mem_reg_property, - sizeof(mem_reg_property)); + qemu_fdt_add_subnode(fdt, "/memory"); + qemu_fdt_setprop_string(fdt, "/memory", "device_type", "memory"); + qemu_fdt_setprop(fdt, "/memory", "reg", mem_reg_property, + sizeof(mem_reg_property)); - qemu_devtree_add_subnode(fdt, "/chosen"); + qemu_fdt_add_subnode(fdt, "/chosen"); if (initrd_size) { - ret = qemu_devtree_setprop_cell(fdt, "/chosen", "linux,initrd-start", - initrd_base); + ret = qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-start", + initrd_base); if (ret < 0) { fprintf(stderr, "couldn't set /chosen/linux,initrd-start\n"); } - ret = qemu_devtree_setprop_cell(fdt, "/chosen", "linux,initrd-end", - (initrd_base + initrd_size)); + ret = qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-end", + (initrd_base + initrd_size)); if (ret < 0) { fprintf(stderr, "couldn't set /chosen/linux,initrd-end\n"); } } - ret = qemu_devtree_setprop_string(fdt, "/chosen", "bootargs", - params->kernel_cmdline); + ret = qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", + args->kernel_cmdline); if (ret < 0) fprintf(stderr, "couldn't set /chosen/bootargs\n"); @@ -215,27 +217,28 @@ static int ppce500_load_device_tree(CPUPPCState *env, tb_freq = kvmppc_get_tbfreq(); /* indicate KVM hypercall interface */ - qemu_devtree_add_subnode(fdt, "/hypervisor"); - qemu_devtree_setprop_string(fdt, "/hypervisor", "compatible", - "linux,kvm"); + qemu_fdt_add_subnode(fdt, "/hypervisor"); + qemu_fdt_setprop_string(fdt, "/hypervisor", "compatible", + "linux,kvm"); kvmppc_get_hypercall(env, hypercall, sizeof(hypercall)); - qemu_devtree_setprop(fdt, "/hypervisor", "hcall-instructions", - hypercall, sizeof(hypercall)); + qemu_fdt_setprop(fdt, "/hypervisor", "hcall-instructions", + hypercall, sizeof(hypercall)); /* if KVM supports the idle hcall, set property indicating this */ if (kvmppc_get_hasidle(env)) { - qemu_devtree_setprop(fdt, "/hypervisor", "has-idle", NULL, 0); + qemu_fdt_setprop(fdt, "/hypervisor", "has-idle", NULL, 0); } } /* Create CPU nodes */ - qemu_devtree_add_subnode(fdt, "/cpus"); - qemu_devtree_setprop_cell(fdt, "/cpus", "#address-cells", 1); - qemu_devtree_setprop_cell(fdt, "/cpus", "#size-cells", 0); + qemu_fdt_add_subnode(fdt, "/cpus"); + qemu_fdt_setprop_cell(fdt, "/cpus", "#address-cells", 1); + qemu_fdt_setprop_cell(fdt, "/cpus", "#size-cells", 0); /* We need to generate the cpu nodes in reverse order, so Linux can pick the first node as boot node and be happy */ for (i = smp_cpus - 1; i >= 0; i--) { CPUState *cpu; + PowerPCCPU *pcpu; char cpu_name[128]; uint64_t cpu_release_addr = MPC8544_SPIN_BASE + (i * 0x20); @@ -244,58 +247,61 @@ static int ppce500_load_device_tree(CPUPPCState *env, continue; } env = cpu->env_ptr; + pcpu = POWERPC_CPU(cpu); snprintf(cpu_name, sizeof(cpu_name), "/cpus/PowerPC,8544@%x", - cpu->cpu_index); - qemu_devtree_add_subnode(fdt, cpu_name); - qemu_devtree_setprop_cell(fdt, cpu_name, "clock-frequency", clock_freq); - qemu_devtree_setprop_cell(fdt, cpu_name, "timebase-frequency", tb_freq); - qemu_devtree_setprop_string(fdt, cpu_name, "device_type", "cpu"); - qemu_devtree_setprop_cell(fdt, cpu_name, "reg", cpu->cpu_index); - qemu_devtree_setprop_cell(fdt, cpu_name, "d-cache-line-size", - env->dcache_line_size); - qemu_devtree_setprop_cell(fdt, cpu_name, "i-cache-line-size", - env->icache_line_size); - qemu_devtree_setprop_cell(fdt, cpu_name, "d-cache-size", 0x8000); - qemu_devtree_setprop_cell(fdt, cpu_name, "i-cache-size", 0x8000); - qemu_devtree_setprop_cell(fdt, cpu_name, "bus-frequency", 0); + ppc_get_vcpu_dt_id(pcpu)); + qemu_fdt_add_subnode(fdt, cpu_name); + qemu_fdt_setprop_cell(fdt, cpu_name, "clock-frequency", clock_freq); + qemu_fdt_setprop_cell(fdt, cpu_name, "timebase-frequency", tb_freq); + qemu_fdt_setprop_string(fdt, cpu_name, "device_type", "cpu"); + qemu_fdt_setprop_cell(fdt, cpu_name, "reg", + ppc_get_vcpu_dt_id(pcpu)); + qemu_fdt_setprop_cell(fdt, cpu_name, "d-cache-line-size", + env->dcache_line_size); + qemu_fdt_setprop_cell(fdt, cpu_name, "i-cache-line-size", + env->icache_line_size); + qemu_fdt_setprop_cell(fdt, cpu_name, "d-cache-size", 0x8000); + qemu_fdt_setprop_cell(fdt, cpu_name, "i-cache-size", 0x8000); + qemu_fdt_setprop_cell(fdt, cpu_name, "bus-frequency", 0); if (cpu->cpu_index) { - qemu_devtree_setprop_string(fdt, cpu_name, "status", "disabled"); - qemu_devtree_setprop_string(fdt, cpu_name, "enable-method", "spin-table"); - qemu_devtree_setprop_u64(fdt, cpu_name, "cpu-release-addr", - cpu_release_addr); + qemu_fdt_setprop_string(fdt, cpu_name, "status", "disabled"); + qemu_fdt_setprop_string(fdt, cpu_name, "enable-method", + "spin-table"); + qemu_fdt_setprop_u64(fdt, cpu_name, "cpu-release-addr", + cpu_release_addr); } else { - qemu_devtree_setprop_string(fdt, cpu_name, "status", "okay"); + qemu_fdt_setprop_string(fdt, cpu_name, "status", "okay"); } } - qemu_devtree_add_subnode(fdt, "/aliases"); + qemu_fdt_add_subnode(fdt, "/aliases"); /* XXX These should go into their respective devices' code */ snprintf(soc, sizeof(soc), "/soc@%llx", MPC8544_CCSRBAR_BASE); - qemu_devtree_add_subnode(fdt, soc); - qemu_devtree_setprop_string(fdt, soc, "device_type", "soc"); - qemu_devtree_setprop(fdt, soc, "compatible", compatible_sb, - sizeof(compatible_sb)); - qemu_devtree_setprop_cell(fdt, soc, "#address-cells", 1); - qemu_devtree_setprop_cell(fdt, soc, "#size-cells", 1); - qemu_devtree_setprop_cells(fdt, soc, "ranges", 0x0, - MPC8544_CCSRBAR_BASE >> 32, MPC8544_CCSRBAR_BASE, - MPC8544_CCSRBAR_SIZE); + qemu_fdt_add_subnode(fdt, soc); + qemu_fdt_setprop_string(fdt, soc, "device_type", "soc"); + qemu_fdt_setprop(fdt, soc, "compatible", compatible_sb, + sizeof(compatible_sb)); + qemu_fdt_setprop_cell(fdt, soc, "#address-cells", 1); + qemu_fdt_setprop_cell(fdt, soc, "#size-cells", 1); + qemu_fdt_setprop_cells(fdt, soc, "ranges", 0x0, + MPC8544_CCSRBAR_BASE >> 32, MPC8544_CCSRBAR_BASE, + MPC8544_CCSRBAR_SIZE); /* XXX should contain a reasonable value */ - qemu_devtree_setprop_cell(fdt, soc, "bus-frequency", 0); + qemu_fdt_setprop_cell(fdt, soc, "bus-frequency", 0); snprintf(mpic, sizeof(mpic), "%s/pic@%llx", soc, MPC8544_MPIC_REGS_OFFSET); - qemu_devtree_add_subnode(fdt, mpic); - qemu_devtree_setprop_string(fdt, mpic, "device_type", "open-pic"); - qemu_devtree_setprop_string(fdt, mpic, "compatible", "fsl,mpic"); - qemu_devtree_setprop_cells(fdt, mpic, "reg", MPC8544_MPIC_REGS_OFFSET, - 0x40000); - qemu_devtree_setprop_cell(fdt, mpic, "#address-cells", 0); - qemu_devtree_setprop_cell(fdt, mpic, "#interrupt-cells", 2); - mpic_ph = qemu_devtree_alloc_phandle(fdt); - qemu_devtree_setprop_cell(fdt, mpic, "phandle", mpic_ph); - qemu_devtree_setprop_cell(fdt, mpic, "linux,phandle", mpic_ph); - qemu_devtree_setprop(fdt, mpic, "interrupt-controller", NULL, 0); + qemu_fdt_add_subnode(fdt, mpic); + qemu_fdt_setprop_string(fdt, mpic, "device_type", "open-pic"); + qemu_fdt_setprop_string(fdt, mpic, "compatible", "fsl,mpic"); + qemu_fdt_setprop_cells(fdt, mpic, "reg", MPC8544_MPIC_REGS_OFFSET, + 0x40000); + qemu_fdt_setprop_cell(fdt, mpic, "#address-cells", 0); + qemu_fdt_setprop_cell(fdt, mpic, "#interrupt-cells", 2); + mpic_ph = qemu_fdt_alloc_phandle(fdt); + qemu_fdt_setprop_cell(fdt, mpic, "phandle", mpic_ph); + qemu_fdt_setprop_cell(fdt, mpic, "linux,phandle", mpic_ph); + qemu_fdt_setprop(fdt, mpic, "interrupt-controller", NULL, 0); /* * We have to generate ser1 first, because Linux takes the first @@ -309,19 +315,19 @@ static int ppce500_load_device_tree(CPUPPCState *env, snprintf(gutil, sizeof(gutil), "%s/global-utilities@%llx", soc, MPC8544_UTIL_OFFSET); - qemu_devtree_add_subnode(fdt, gutil); - qemu_devtree_setprop_string(fdt, gutil, "compatible", "fsl,mpc8544-guts"); - qemu_devtree_setprop_cells(fdt, gutil, "reg", MPC8544_UTIL_OFFSET, 0x1000); - qemu_devtree_setprop(fdt, gutil, "fsl,has-rstcr", NULL, 0); + qemu_fdt_add_subnode(fdt, gutil); + qemu_fdt_setprop_string(fdt, gutil, "compatible", "fsl,mpc8544-guts"); + qemu_fdt_setprop_cells(fdt, gutil, "reg", MPC8544_UTIL_OFFSET, 0x1000); + qemu_fdt_setprop(fdt, gutil, "fsl,has-rstcr", NULL, 0); snprintf(msi, sizeof(msi), "/%s/msi@%llx", soc, MPC8544_MSI_REGS_OFFSET); - qemu_devtree_add_subnode(fdt, msi); - qemu_devtree_setprop_string(fdt, msi, "compatible", "fsl,mpic-msi"); - qemu_devtree_setprop_cells(fdt, msi, "reg", MPC8544_MSI_REGS_OFFSET, 0x200); - msi_ph = qemu_devtree_alloc_phandle(fdt); - qemu_devtree_setprop_cells(fdt, msi, "msi-available-ranges", 0x0, 0x100); - qemu_devtree_setprop_phandle(fdt, msi, "interrupt-parent", mpic); - qemu_devtree_setprop_cells(fdt, msi, "interrupts", + qemu_fdt_add_subnode(fdt, msi); + qemu_fdt_setprop_string(fdt, msi, "compatible", "fsl,mpic-msi"); + qemu_fdt_setprop_cells(fdt, msi, "reg", MPC8544_MSI_REGS_OFFSET, 0x200); + msi_ph = qemu_fdt_alloc_phandle(fdt); + qemu_fdt_setprop_cells(fdt, msi, "msi-available-ranges", 0x0, 0x100); + qemu_fdt_setprop_phandle(fdt, msi, "interrupt-parent", mpic); + qemu_fdt_setprop_cells(fdt, msi, "interrupts", 0xe0, 0x0, 0xe1, 0x0, 0xe2, 0x0, @@ -330,50 +336,48 @@ static int ppce500_load_device_tree(CPUPPCState *env, 0xe5, 0x0, 0xe6, 0x0, 0xe7, 0x0); - qemu_devtree_setprop_cell(fdt, msi, "phandle", msi_ph); - qemu_devtree_setprop_cell(fdt, msi, "linux,phandle", msi_ph); + qemu_fdt_setprop_cell(fdt, msi, "phandle", msi_ph); + qemu_fdt_setprop_cell(fdt, msi, "linux,phandle", msi_ph); snprintf(pci, sizeof(pci), "/pci@%llx", MPC8544_PCI_REGS_BASE); - qemu_devtree_add_subnode(fdt, pci); - qemu_devtree_setprop_cell(fdt, pci, "cell-index", 0); - qemu_devtree_setprop_string(fdt, pci, "compatible", "fsl,mpc8540-pci"); - qemu_devtree_setprop_string(fdt, pci, "device_type", "pci"); - qemu_devtree_setprop_cells(fdt, pci, "interrupt-map-mask", 0xf800, 0x0, - 0x0, 0x7); - pci_map = pci_map_create(fdt, qemu_devtree_get_phandle(fdt, mpic), + qemu_fdt_add_subnode(fdt, pci); + qemu_fdt_setprop_cell(fdt, pci, "cell-index", 0); + qemu_fdt_setprop_string(fdt, pci, "compatible", "fsl,mpc8540-pci"); + qemu_fdt_setprop_string(fdt, pci, "device_type", "pci"); + qemu_fdt_setprop_cells(fdt, pci, "interrupt-map-mask", 0xf800, 0x0, + 0x0, 0x7); + pci_map = pci_map_create(fdt, qemu_fdt_get_phandle(fdt, mpic), params->pci_first_slot, params->pci_nr_slots, &len); - qemu_devtree_setprop(fdt, pci, "interrupt-map", pci_map, len); - qemu_devtree_setprop_phandle(fdt, pci, "interrupt-parent", mpic); - qemu_devtree_setprop_cells(fdt, pci, "interrupts", 24, 2); - qemu_devtree_setprop_cells(fdt, pci, "bus-range", 0, 255); + qemu_fdt_setprop(fdt, pci, "interrupt-map", pci_map, len); + qemu_fdt_setprop_phandle(fdt, pci, "interrupt-parent", mpic); + qemu_fdt_setprop_cells(fdt, pci, "interrupts", 24, 2); + qemu_fdt_setprop_cells(fdt, pci, "bus-range", 0, 255); for (i = 0; i < 14; i++) { pci_ranges[i] = cpu_to_be32(pci_ranges[i]); } - qemu_devtree_setprop_cell(fdt, pci, "fsl,msi", msi_ph); - qemu_devtree_setprop(fdt, pci, "ranges", pci_ranges, sizeof(pci_ranges)); - qemu_devtree_setprop_cells(fdt, pci, "reg", MPC8544_PCI_REGS_BASE >> 32, - MPC8544_PCI_REGS_BASE, 0, 0x1000); - qemu_devtree_setprop_cell(fdt, pci, "clock-frequency", 66666666); - qemu_devtree_setprop_cell(fdt, pci, "#interrupt-cells", 1); - qemu_devtree_setprop_cell(fdt, pci, "#size-cells", 2); - qemu_devtree_setprop_cell(fdt, pci, "#address-cells", 3); - qemu_devtree_setprop_string(fdt, "/aliases", "pci0", pci); + qemu_fdt_setprop_cell(fdt, pci, "fsl,msi", msi_ph); + qemu_fdt_setprop(fdt, pci, "ranges", pci_ranges, sizeof(pci_ranges)); + qemu_fdt_setprop_cells(fdt, pci, "reg", MPC8544_PCI_REGS_BASE >> 32, + MPC8544_PCI_REGS_BASE, 0, 0x1000); + qemu_fdt_setprop_cell(fdt, pci, "clock-frequency", 66666666); + qemu_fdt_setprop_cell(fdt, pci, "#interrupt-cells", 1); + qemu_fdt_setprop_cell(fdt, pci, "#size-cells", 2); + qemu_fdt_setprop_cell(fdt, pci, "#address-cells", 3); + qemu_fdt_setprop_string(fdt, "/aliases", "pci0", pci); params->fixup_devtree(params, fdt); if (toplevel_compat) { - qemu_devtree_setprop(fdt, "/", "compatible", toplevel_compat, - strlen(toplevel_compat) + 1); + qemu_fdt_setprop(fdt, "/", "compatible", toplevel_compat, + strlen(toplevel_compat) + 1); } done: - qemu_devtree_dumpdtb(fdt, fdt_size); - ret = rom_add_blob_fixed(BINARY_DEVICE_TREE_FILE, fdt, fdt_size, addr); - if (ret < 0) { - goto out; + if (!dry_run) { + qemu_fdt_dumpdtb(fdt, fdt_size); + cpu_physical_memory_write(addr, fdt, fdt_size); } - g_free(fdt); ret = fdt_size; out: @@ -382,6 +386,41 @@ out: return ret; } +typedef struct DeviceTreeParams { + QEMUMachineInitArgs args; + PPCE500Params params; + hwaddr addr; + hwaddr initrd_base; + hwaddr initrd_size; +} DeviceTreeParams; + +static void ppce500_reset_device_tree(void *opaque) +{ + DeviceTreeParams *p = opaque; + ppce500_load_device_tree(&p->args, &p->params, p->addr, p->initrd_base, + p->initrd_size, false); +} + +static int ppce500_prep_device_tree(QEMUMachineInitArgs *args, + PPCE500Params *params, + hwaddr addr, + hwaddr initrd_base, + hwaddr initrd_size) +{ + DeviceTreeParams *p = g_new(DeviceTreeParams, 1); + p->args = *args; + p->params = *params; + p->addr = addr; + p->initrd_base = initrd_base; + p->initrd_size = initrd_size; + + qemu_register_reset(ppce500_reset_device_tree, p); + + /* Issue the device tree loader once, so that we get the size of the blob */ + return ppce500_load_device_tree(args, params, addr, initrd_base, + initrd_size, true); +} + /* Create -kernel TLB entries for BookE. */ static inline hwaddr booke206_page_size_to_tlb(uint64_t size) { @@ -433,14 +472,13 @@ static void ppce500_cpu_reset_sec(void *opaque) { PowerPCCPU *cpu = opaque; CPUState *cs = CPU(cpu); - CPUPPCState *env = &cpu->env; cpu_reset(cs); /* Secondary CPU starts in halted state for now. Needs to change when implementing non-kernel boot. */ cs->halted = 1; - env->exception_index = EXCP_HLT; + cs->exception_index = EXCP_HLT; } static void ppce500_cpu_reset(void *opaque) @@ -505,7 +543,7 @@ static DeviceState *ppce500_init_mpic_kvm(PPCE500Params *params, return NULL; } - for (cs = first_cpu; cs != NULL; cs = cs->next_cpu) { + CPU_FOREACH(cs) { if (kvm_openpic_connect_vcpu(dev, cs)) { fprintf(stderr, "%s: failed to connect vcpu to irqchip\n", __func__); @@ -559,7 +597,7 @@ static qemu_irq *ppce500_init_mpic(PPCE500Params *params, MemoryRegion *ccsr, return mpic; } -void ppce500_init(PPCE500Params *params) +void ppce500_init(QEMUMachineInitArgs *args, PPCE500Params *params) { MemoryRegion *address_space_mem = get_system_memory(); MemoryRegion *ram = g_new(MemoryRegion, 1); @@ -584,8 +622,8 @@ void ppce500_init(PPCE500Params *params) PPCE500CCSRState *ccsr; /* Setup CPUs */ - if (params->cpu_model == NULL) { - params->cpu_model = "e500v2_v30"; + if (args->cpu_model == NULL) { + args->cpu_model = "e500v2_v30"; } irqs = g_malloc0(smp_cpus * sizeof(qemu_irq *)); @@ -595,7 +633,7 @@ void ppce500_init(PPCE500Params *params) CPUState *cs; qemu_irq *input; - cpu = cpu_ppc_init(params->cpu_model); + cpu = cpu_ppc_init(args->cpu_model); if (cpu == NULL) { fprintf(stderr, "Unable to initialize CPU!\n"); exit(1); @@ -611,7 +649,7 @@ void ppce500_init(PPCE500Params *params) input = (qemu_irq *)env->irq_inputs; irqs[i][OPENPIC_OUTPUT_INT] = input[PPCE500_INPUT_INT]; irqs[i][OPENPIC_OUTPUT_CINT] = input[PPCE500_INPUT_CINT]; - env->spr[SPR_BOOKE_PIR] = cs->cpu_index = i; + env->spr_cb[SPR_BOOKE_PIR].default_value = cs->cpu_index = i; env->mpic_iack = MPC8544_CCSRBAR_BASE + MPC8544_MPIC_REGS_OFFSET + 0xa0; @@ -634,7 +672,7 @@ void ppce500_init(PPCE500Params *params) /* Fixup Memory size on a alignment boundary */ ram_size &= ~(RAM_SIZES_ALIGN - 1); - params->ram_size = ram_size; + args->ram_size = ram_size; /* Register Memory */ memory_region_init_ram(ram, NULL, "mpc8544ds.ram", ram_size); @@ -701,11 +739,11 @@ void ppce500_init(PPCE500Params *params) sysbus_create_simple("e500-spin", MPC8544_SPIN_BASE, NULL); /* Load kernel. */ - if (params->kernel_filename) { - kernel_size = load_uimage(params->kernel_filename, &entry, + if (args->kernel_filename) { + kernel_size = load_uimage(args->kernel_filename, &entry, &loadaddr, NULL); if (kernel_size < 0) { - kernel_size = load_elf(params->kernel_filename, NULL, NULL, + kernel_size = load_elf(args->kernel_filename, NULL, NULL, &elf_entry, &elf_lowaddr, NULL, 1, ELF_MACHINE, 0); entry = elf_entry; @@ -714,7 +752,7 @@ void ppce500_init(PPCE500Params *params) /* XXX try again as binary */ if (kernel_size < 0) { fprintf(stderr, "qemu: could not load kernel '%s'\n", - params->kernel_filename); + args->kernel_filename); exit(1); } @@ -726,14 +764,14 @@ void ppce500_init(PPCE500Params *params) } /* Load initrd. */ - if (params->initrd_filename) { + if (args->initrd_filename) { initrd_base = (cur_base + INITRD_LOAD_PAD) & ~INITRD_PAD_MASK; - initrd_size = load_image_targphys(params->initrd_filename, initrd_base, + initrd_size = load_image_targphys(args->initrd_filename, initrd_base, ram_size - initrd_base); if (initrd_size < 0) { fprintf(stderr, "qemu: could not load initial ram disk '%s'\n", - params->initrd_filename); + args->initrd_filename); exit(1); } @@ -741,12 +779,12 @@ void ppce500_init(PPCE500Params *params) } /* If we're loading a kernel directly, we must load the device tree too. */ - if (params->kernel_filename) { + if (args->kernel_filename) { struct boot_info *boot_info; int dt_size; - dt_size = ppce500_load_device_tree(env, params, dt_base, initrd_base, - initrd_size); + dt_size = ppce500_prep_device_tree(args, params, dt_base, + initrd_base, initrd_size); if (dt_size < 0) { fprintf(stderr, "couldn't load device tree\n"); exit(1); diff --git a/hw/ppc/e500.h b/hw/ppc/e500.h index 226c93d24..52726a2ec 100644 --- a/hw/ppc/e500.h +++ b/hw/ppc/e500.h @@ -1,25 +1,18 @@ #ifndef PPCE500_H #define PPCE500_H +#include "hw/boards.h" + typedef struct PPCE500Params { - /* Standard QEMU machine init params */ - ram_addr_t ram_size; - const char *boot_device; - const char *kernel_filename; - const char *kernel_cmdline; - const char *initrd_filename; - const char *cpu_model; int pci_first_slot; int pci_nr_slots; - /* e500-specific params */ - /* required -- must at least add toplevel board compatible */ void (*fixup_devtree)(struct PPCE500Params *params, void *fdt); int mpic_version; } PPCE500Params; -void ppce500_init(PPCE500Params *params); +void ppce500_init(QEMUMachineInitArgs *args, PPCE500Params *params); #endif diff --git a/hw/ppc/e500plat.c b/hw/ppc/e500plat.c index c85299588..7d5357e83 100644 --- a/hw/ppc/e500plat.c +++ b/hw/ppc/e500plat.c @@ -23,26 +23,14 @@ static void e500plat_fixup_devtree(PPCE500Params *params, void *fdt) const char model[] = "QEMU ppce500"; const char compatible[] = "fsl,qemu-e500"; - qemu_devtree_setprop(fdt, "/", "model", model, sizeof(model)); - qemu_devtree_setprop(fdt, "/", "compatible", compatible, - sizeof(compatible)); + qemu_fdt_setprop(fdt, "/", "model", model, sizeof(model)); + qemu_fdt_setprop(fdt, "/", "compatible", compatible, + sizeof(compatible)); } static void e500plat_init(QEMUMachineInitArgs *args) { - ram_addr_t ram_size = args->ram_size; - const char *boot_device = args->boot_device; - const char *cpu_model = args->cpu_model; - const char *kernel_filename = args->kernel_filename; - const char *kernel_cmdline = args->kernel_cmdline; - const char *initrd_filename = args->initrd_filename; PPCE500Params params = { - .ram_size = ram_size, - .boot_device = boot_device, - .kernel_filename = kernel_filename, - .kernel_cmdline = kernel_cmdline, - .initrd_filename = initrd_filename, - .cpu_model = cpu_model, .pci_first_slot = 0x1, .pci_nr_slots = PCI_SLOT_MAX - 1, .fixup_devtree = e500plat_fixup_devtree, @@ -55,7 +43,7 @@ static void e500plat_init(QEMUMachineInitArgs *args) params.mpic_version = OPENPIC_MODEL_FSL_MPIC_20; } - ppce500_init(¶ms); + ppce500_init(args, ¶ms); } static QEMUMachine e500plat_machine = { @@ -63,7 +51,6 @@ static QEMUMachine e500plat_machine = { .desc = "generic paravirt e500 platform", .init = e500plat_init, .max_cpus = 32, - DEFAULT_MACHINE_OPTIONS, }; static void e500plat_machine_init(void) diff --git a/hw/ppc/mac.h b/hw/ppc/mac.h index 1e578dd59..c1faf9ce2 100644 --- a/hw/ppc/mac.h +++ b/hw/ppc/mac.h @@ -34,7 +34,6 @@ #define MAX_CPUS 1 #define BIOS_SIZE (1024 * 1024) -#define BIOS_FILENAME "ppc_rom.bin" #define NVRAM_SIZE 0x2000 #define PROM_FILENAME "openbios-ppc" #define PROM_ADDR 0xfff00000 diff --git a/hw/ppc/mac_newworld.c b/hw/ppc/mac_newworld.c index 7ef806ef7..5e7957516 100644 --- a/hw/ppc/mac_newworld.c +++ b/hw/ppc/mac_newworld.c @@ -147,7 +147,7 @@ static void ppc_core99_init(QEMUMachineInitArgs *args) const char *kernel_filename = args->kernel_filename; const char *kernel_cmdline = args->kernel_cmdline; const char *initrd_filename = args->initrd_filename; - const char *boot_device = args->boot_device; + const char *boot_device = args->boot_order; PowerPCCPU *cpu = NULL; CPUPPCState *env = NULL; char *filename; @@ -477,7 +477,7 @@ static QEMUMachine core99_machine = { .desc = "Mac99 based PowerMAC", .init = ppc_core99_init, .max_cpus = MAX_CPUS, - DEFAULT_MACHINE_OPTIONS, + .default_boot_order = "cd", }; static void core99_machine_init(void) diff --git a/hw/ppc/mac_oldworld.c b/hw/ppc/mac_oldworld.c index 42bb9d55c..2f27754c6 100644 --- a/hw/ppc/mac_oldworld.c +++ b/hw/ppc/mac_oldworld.c @@ -78,7 +78,7 @@ static void ppc_heathrow_init(QEMUMachineInitArgs *args) const char *kernel_filename = args->kernel_filename; const char *kernel_cmdline = args->kernel_cmdline; const char *initrd_filename = args->initrd_filename; - const char *boot_device = args->boot_device; + const char *boot_device = args->boot_order; MemoryRegion *sysmem = get_system_memory(); PowerPCCPU *cpu = NULL; CPUPPCState *env = NULL; @@ -350,7 +350,7 @@ static QEMUMachine heathrow_machine = { #ifndef TARGET_PPC64 .is_default = 1, #endif - DEFAULT_MACHINE_OPTIONS, + .default_boot_order = "cd", /* TOFIX "cad" when Mac floppy is implemented */ }; static void heathrow_machine_init(void) diff --git a/hw/ppc/mpc8544ds.c b/hw/ppc/mpc8544ds.c index 444da0246..292c70953 100644 --- a/hw/ppc/mpc8544ds.c +++ b/hw/ppc/mpc8544ds.c @@ -21,33 +21,21 @@ static void mpc8544ds_fixup_devtree(PPCE500Params *params, void *fdt) const char model[] = "MPC8544DS"; const char compatible[] = "MPC8544DS\0MPC85xxDS"; - qemu_devtree_setprop(fdt, "/", "model", model, sizeof(model)); - qemu_devtree_setprop(fdt, "/", "compatible", compatible, - sizeof(compatible)); + qemu_fdt_setprop(fdt, "/", "model", model, sizeof(model)); + qemu_fdt_setprop(fdt, "/", "compatible", compatible, + sizeof(compatible)); } static void mpc8544ds_init(QEMUMachineInitArgs *args) { - ram_addr_t ram_size = args->ram_size; - const char *boot_device = args->boot_device; - const char *cpu_model = args->cpu_model; - const char *kernel_filename = args->kernel_filename; - const char *kernel_cmdline = args->kernel_cmdline; - const char *initrd_filename = args->initrd_filename; PPCE500Params params = { - .ram_size = ram_size, - .boot_device = boot_device, - .kernel_filename = kernel_filename, - .kernel_cmdline = kernel_cmdline, - .initrd_filename = initrd_filename, - .cpu_model = cpu_model, .pci_first_slot = 0x11, .pci_nr_slots = 2, .fixup_devtree = mpc8544ds_fixup_devtree, .mpic_version = OPENPIC_MODEL_FSL_MPIC_20, }; - ppce500_init(¶ms); + ppce500_init(args, ¶ms); } @@ -56,7 +44,6 @@ static QEMUMachine ppce500_machine = { .desc = "mpc8544ds", .init = mpc8544ds_init, .max_cpus = 15, - DEFAULT_MACHINE_OPTIONS, }; static void ppce500_machine_init(void) diff --git a/hw/ppc/ppc.c b/hw/ppc/ppc.c index e1c095c7e..71df47174 100644 --- a/hw/ppc/ppc.c +++ b/hw/ppc/ppc.c @@ -26,6 +26,7 @@ #include "hw/ppc/ppc_e500.h" #include "qemu/timer.h" #include "sysemu/sysemu.h" +#include "sysemu/cpus.h" #include "hw/timer/m48t59.h" #include "qemu/log.h" #include "hw/loader.h" @@ -443,7 +444,7 @@ void ppce500_set_mpic_proxy(bool enabled) { CPUState *cs; - for (cs = first_cpu; cs != NULL; cs = cs->next_cpu) { + CPU_FOREACH(cs) { PowerPCCPU *cpu = POWERPC_CPU(cs); cpu->env.mpic_proxy = enabled; @@ -471,7 +472,7 @@ uint64_t cpu_ppc_load_tbl (CPUPPCState *env) return env->spr[SPR_TBL]; } - tb = cpu_ppc_get_tb(tb_env, qemu_get_clock_ns(vm_clock), tb_env->tb_offset); + tb = cpu_ppc_get_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), tb_env->tb_offset); LOG_TB("%s: tb %016" PRIx64 "\n", __func__, tb); return tb; @@ -482,7 +483,7 @@ static inline uint32_t _cpu_ppc_load_tbu(CPUPPCState *env) ppc_tb_t *tb_env = env->tb_env; uint64_t tb; - tb = cpu_ppc_get_tb(tb_env, qemu_get_clock_ns(vm_clock), tb_env->tb_offset); + tb = cpu_ppc_get_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), tb_env->tb_offset); LOG_TB("%s: tb %016" PRIx64 "\n", __func__, tb); return tb >> 32; @@ -510,9 +511,9 @@ void cpu_ppc_store_tbl (CPUPPCState *env, uint32_t value) ppc_tb_t *tb_env = env->tb_env; uint64_t tb; - tb = cpu_ppc_get_tb(tb_env, qemu_get_clock_ns(vm_clock), tb_env->tb_offset); + tb = cpu_ppc_get_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), tb_env->tb_offset); tb &= 0xFFFFFFFF00000000ULL; - cpu_ppc_store_tb(tb_env, qemu_get_clock_ns(vm_clock), + cpu_ppc_store_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), &tb_env->tb_offset, tb | (uint64_t)value); } @@ -521,9 +522,9 @@ static inline void _cpu_ppc_store_tbu(CPUPPCState *env, uint32_t value) ppc_tb_t *tb_env = env->tb_env; uint64_t tb; - tb = cpu_ppc_get_tb(tb_env, qemu_get_clock_ns(vm_clock), tb_env->tb_offset); + tb = cpu_ppc_get_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), tb_env->tb_offset); tb &= 0x00000000FFFFFFFFULL; - cpu_ppc_store_tb(tb_env, qemu_get_clock_ns(vm_clock), + cpu_ppc_store_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), &tb_env->tb_offset, ((uint64_t)value << 32) | tb); } @@ -537,7 +538,7 @@ uint64_t cpu_ppc_load_atbl (CPUPPCState *env) ppc_tb_t *tb_env = env->tb_env; uint64_t tb; - tb = cpu_ppc_get_tb(tb_env, qemu_get_clock_ns(vm_clock), tb_env->atb_offset); + tb = cpu_ppc_get_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), tb_env->atb_offset); LOG_TB("%s: tb %016" PRIx64 "\n", __func__, tb); return tb; @@ -548,7 +549,7 @@ uint32_t cpu_ppc_load_atbu (CPUPPCState *env) ppc_tb_t *tb_env = env->tb_env; uint64_t tb; - tb = cpu_ppc_get_tb(tb_env, qemu_get_clock_ns(vm_clock), tb_env->atb_offset); + tb = cpu_ppc_get_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), tb_env->atb_offset); LOG_TB("%s: tb %016" PRIx64 "\n", __func__, tb); return tb >> 32; @@ -559,9 +560,9 @@ void cpu_ppc_store_atbl (CPUPPCState *env, uint32_t value) ppc_tb_t *tb_env = env->tb_env; uint64_t tb; - tb = cpu_ppc_get_tb(tb_env, qemu_get_clock_ns(vm_clock), tb_env->atb_offset); + tb = cpu_ppc_get_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), tb_env->atb_offset); tb &= 0xFFFFFFFF00000000ULL; - cpu_ppc_store_tb(tb_env, qemu_get_clock_ns(vm_clock), + cpu_ppc_store_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), &tb_env->atb_offset, tb | (uint64_t)value); } @@ -570,9 +571,9 @@ void cpu_ppc_store_atbu (CPUPPCState *env, uint32_t value) ppc_tb_t *tb_env = env->tb_env; uint64_t tb; - tb = cpu_ppc_get_tb(tb_env, qemu_get_clock_ns(vm_clock), tb_env->atb_offset); + tb = cpu_ppc_get_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), tb_env->atb_offset); tb &= 0x00000000FFFFFFFFULL; - cpu_ppc_store_tb(tb_env, qemu_get_clock_ns(vm_clock), + cpu_ppc_store_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), &tb_env->atb_offset, ((uint64_t)value << 32) | tb); } @@ -583,7 +584,7 @@ static void cpu_ppc_tb_stop (CPUPPCState *env) /* If the time base is already frozen, do nothing */ if (tb_env->tb_freq != 0) { - vmclk = qemu_get_clock_ns(vm_clock); + vmclk = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); /* Get the time base */ tb = cpu_ppc_get_tb(tb_env, vmclk, tb_env->tb_offset); /* Get the alternate time base */ @@ -605,7 +606,7 @@ static void cpu_ppc_tb_start (CPUPPCState *env) /* If the time base is not frozen, do nothing */ if (tb_env->tb_freq == 0) { - vmclk = qemu_get_clock_ns(vm_clock); + vmclk = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); /* Get the time base from tb_offset */ tb = tb_env->tb_offset; /* Get the alternate time base from atb_offset */ @@ -619,13 +620,20 @@ static void cpu_ppc_tb_start (CPUPPCState *env) } } +bool ppc_decr_clear_on_delivery(CPUPPCState *env) +{ + ppc_tb_t *tb_env = env->tb_env; + int flags = PPC_DECR_UNDERFLOW_TRIGGERED | PPC_DECR_UNDERFLOW_LEVEL; + return ((tb_env->flags & flags) == PPC_DECR_UNDERFLOW_TRIGGERED); +} + static inline uint32_t _cpu_ppc_load_decr(CPUPPCState *env, uint64_t next) { ppc_tb_t *tb_env = env->tb_env; uint32_t decr; int64_t diff; - diff = next - qemu_get_clock_ns(vm_clock); + diff = next - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); if (diff >= 0) { decr = muldiv64(diff, tb_env->decr_freq, get_ticks_per_sec()); } else if (tb_env->flags & PPC_TIMER_BOOKE) { @@ -661,7 +669,7 @@ uint64_t cpu_ppc_load_purr (CPUPPCState *env) ppc_tb_t *tb_env = env->tb_env; uint64_t diff; - diff = qemu_get_clock_ns(vm_clock) - tb_env->purr_start; + diff = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - tb_env->purr_start; return tb_env->purr_load + muldiv64(diff, tb_env->tb_freq, get_ticks_per_sec()); } @@ -676,6 +684,11 @@ static inline void cpu_ppc_decr_excp(PowerPCCPU *cpu) ppc_set_irq(cpu, PPC_INTERRUPT_DECR, 1); } +static inline void cpu_ppc_decr_lower(PowerPCCPU *cpu) +{ + ppc_set_irq(cpu, PPC_INTERRUPT_DECR, 0); +} + static inline void cpu_ppc_hdecr_excp(PowerPCCPU *cpu) { /* Raise it */ @@ -683,11 +696,16 @@ static inline void cpu_ppc_hdecr_excp(PowerPCCPU *cpu) ppc_set_irq(cpu, PPC_INTERRUPT_HDECR, 1); } +static inline void cpu_ppc_hdecr_lower(PowerPCCPU *cpu) +{ + ppc_set_irq(cpu, PPC_INTERRUPT_HDECR, 0); +} + static void __cpu_ppc_store_decr(PowerPCCPU *cpu, uint64_t *nextp, - struct QEMUTimer *timer, - void (*raise_excp)(PowerPCCPU *), - uint32_t decr, uint32_t value, - int is_excp) + QEMUTimer *timer, + void (*raise_excp)(void *), + void (*lower_excp)(PowerPCCPU *), + uint32_t decr, uint32_t value) { CPUPPCState *env = &cpu->env; ppc_tb_t *tb_env = env->tb_env; @@ -701,59 +719,74 @@ static void __cpu_ppc_store_decr(PowerPCCPU *cpu, uint64_t *nextp, return; } - now = qemu_get_clock_ns(vm_clock); - next = now + muldiv64(value, get_ticks_per_sec(), tb_env->decr_freq); - if (is_excp) { - next += *nextp - now; + /* + * Going from 2 -> 1, 1 -> 0 or 0 -> -1 is the event to generate a DEC + * interrupt. + * + * If we get a really small DEC value, we can assume that by the time we + * handled it we should inject an interrupt already. + * + * On MSB level based DEC implementations the MSB always means the interrupt + * is pending, so raise it on those. + * + * On MSB edge based DEC implementations the MSB going from 0 -> 1 triggers + * an edge interrupt, so raise it here too. + */ + if ((value < 3) || + ((tb_env->flags & PPC_DECR_UNDERFLOW_LEVEL) && (value & 0x80000000)) || + ((tb_env->flags & PPC_DECR_UNDERFLOW_TRIGGERED) && (value & 0x80000000) + && !(decr & 0x80000000))) { + (*raise_excp)(cpu); + return; } - if (next == now) { - next++; + + /* On MSB level based systems a 0 for the MSB stops interrupt delivery */ + if (!(value & 0x80000000) && (tb_env->flags & PPC_DECR_UNDERFLOW_LEVEL)) { + (*lower_excp)(cpu); } + + /* Calculate the next timer event */ + now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + next = now + muldiv64(value, get_ticks_per_sec(), tb_env->decr_freq); *nextp = next; - /* Adjust timer */ - qemu_mod_timer(timer, next); - /* If we set a negative value and the decrementer was positive, raise an - * exception. - */ - if ((tb_env->flags & PPC_DECR_UNDERFLOW_TRIGGERED) - && (value & 0x80000000) - && !(decr & 0x80000000)) { - (*raise_excp)(cpu); - } + /* Adjust timer */ + timer_mod(timer, next); } static inline void _cpu_ppc_store_decr(PowerPCCPU *cpu, uint32_t decr, - uint32_t value, int is_excp) + uint32_t value) { ppc_tb_t *tb_env = cpu->env.tb_env; __cpu_ppc_store_decr(cpu, &tb_env->decr_next, tb_env->decr_timer, - &cpu_ppc_decr_excp, decr, value, is_excp); + tb_env->decr_timer->cb, &cpu_ppc_decr_lower, decr, + value); } void cpu_ppc_store_decr (CPUPPCState *env, uint32_t value) { PowerPCCPU *cpu = ppc_env_get_cpu(env); - _cpu_ppc_store_decr(cpu, cpu_ppc_load_decr(env), value, 0); + _cpu_ppc_store_decr(cpu, cpu_ppc_load_decr(env), value); } static void cpu_ppc_decr_cb(void *opaque) { PowerPCCPU *cpu = opaque; - _cpu_ppc_store_decr(cpu, 0x00000000, 0xFFFFFFFF, 1); + cpu_ppc_decr_excp(cpu); } static inline void _cpu_ppc_store_hdecr(PowerPCCPU *cpu, uint32_t hdecr, - uint32_t value, int is_excp) + uint32_t value) { ppc_tb_t *tb_env = cpu->env.tb_env; if (tb_env->hdecr_timer != NULL) { __cpu_ppc_store_decr(cpu, &tb_env->hdecr_next, tb_env->hdecr_timer, - &cpu_ppc_hdecr_excp, hdecr, value, is_excp); + tb_env->hdecr_timer->cb, &cpu_ppc_hdecr_lower, + hdecr, value); } } @@ -761,14 +794,14 @@ void cpu_ppc_store_hdecr (CPUPPCState *env, uint32_t value) { PowerPCCPU *cpu = ppc_env_get_cpu(env); - _cpu_ppc_store_hdecr(cpu, cpu_ppc_load_hdecr(env), value, 0); + _cpu_ppc_store_hdecr(cpu, cpu_ppc_load_hdecr(env), value); } static void cpu_ppc_hdecr_cb(void *opaque) { PowerPCCPU *cpu = opaque; - _cpu_ppc_store_hdecr(cpu, 0x00000000, 0xFFFFFFFF, 1); + cpu_ppc_hdecr_excp(cpu); } static void cpu_ppc_store_purr(PowerPCCPU *cpu, uint64_t value) @@ -776,7 +809,7 @@ static void cpu_ppc_store_purr(PowerPCCPU *cpu, uint64_t value) ppc_tb_t *tb_env = cpu->env.tb_env; tb_env->purr_load = value; - tb_env->purr_start = qemu_get_clock_ns(vm_clock); + tb_env->purr_start = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); } static void cpu_ppc_set_tb_clk (void *opaque, uint32_t freq) @@ -791,8 +824,8 @@ static void cpu_ppc_set_tb_clk (void *opaque, uint32_t freq) * if a decrementer exception is pending when it enables msr_ee at startup, * it's not ready to handle it... */ - _cpu_ppc_store_decr(cpu, 0xFFFFFFFF, 0xFFFFFFFF, 0); - _cpu_ppc_store_hdecr(cpu, 0xFFFFFFFF, 0xFFFFFFFF, 0); + _cpu_ppc_store_decr(cpu, 0xFFFFFFFF, 0xFFFFFFFF); + _cpu_ppc_store_hdecr(cpu, 0xFFFFFFFF, 0xFFFFFFFF); cpu_ppc_store_purr(cpu, 0x0000000000000000ULL); } @@ -805,12 +838,16 @@ clk_setup_cb cpu_ppc_tb_init (CPUPPCState *env, uint32_t freq) tb_env = g_malloc0(sizeof(ppc_tb_t)); env->tb_env = tb_env; tb_env->flags = PPC_DECR_UNDERFLOW_TRIGGERED; + if (env->insns_flags & PPC_SEGMENT_64B) { + /* All Book3S 64bit CPUs implement level based DEC logic */ + tb_env->flags |= PPC_DECR_UNDERFLOW_LEVEL; + } /* Create new timer */ - tb_env->decr_timer = qemu_new_timer_ns(vm_clock, &cpu_ppc_decr_cb, cpu); + tb_env->decr_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &cpu_ppc_decr_cb, cpu); if (0) { /* XXX: find a suitable condition to enable the hypervisor decrementer */ - tb_env->hdecr_timer = qemu_new_timer_ns(vm_clock, &cpu_ppc_hdecr_cb, + tb_env->hdecr_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &cpu_ppc_hdecr_cb, cpu); } else { tb_env->hdecr_timer = NULL; @@ -856,9 +893,9 @@ typedef struct ppc40x_timer_t ppc40x_timer_t; struct ppc40x_timer_t { uint64_t pit_reload; /* PIT auto-reload value */ uint64_t fit_next; /* Tick for next FIT interrupt */ - struct QEMUTimer *fit_timer; + QEMUTimer *fit_timer; uint64_t wdt_next; /* Tick for next WDT interrupt */ - struct QEMUTimer *wdt_timer; + QEMUTimer *wdt_timer; /* 405 have the PIT, 440 have a DECR. */ unsigned int decr_excp; @@ -877,7 +914,7 @@ static void cpu_4xx_fit_cb (void *opaque) cpu = ppc_env_get_cpu(env); tb_env = env->tb_env; ppc40x_timer = tb_env->opaque; - now = qemu_get_clock_ns(vm_clock); + now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); switch ((env->spr[SPR_40x_TCR] >> 24) & 0x3) { case 0: next = 1 << 9; @@ -898,7 +935,7 @@ static void cpu_4xx_fit_cb (void *opaque) next = now + muldiv64(next, get_ticks_per_sec(), tb_env->tb_freq); if (next == now) next++; - qemu_mod_timer(ppc40x_timer->fit_timer, next); + timer_mod(ppc40x_timer->fit_timer, next); env->spr[SPR_40x_TSR] |= 1 << 26; if ((env->spr[SPR_40x_TCR] >> 23) & 0x1) { ppc_set_irq(cpu, PPC_INTERRUPT_FIT, 1); @@ -920,18 +957,18 @@ static void start_stop_pit (CPUPPCState *env, ppc_tb_t *tb_env, int is_excp) (is_excp && !((env->spr[SPR_40x_TCR] >> 22) & 0x1))) { /* Stop PIT */ LOG_TB("%s: stop PIT\n", __func__); - qemu_del_timer(tb_env->decr_timer); + timer_del(tb_env->decr_timer); } else { LOG_TB("%s: start PIT %016" PRIx64 "\n", __func__, ppc40x_timer->pit_reload); - now = qemu_get_clock_ns(vm_clock); + now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); next = now + muldiv64(ppc40x_timer->pit_reload, get_ticks_per_sec(), tb_env->decr_freq); if (is_excp) next += tb_env->decr_next - now; if (next == now) next++; - qemu_mod_timer(tb_env->decr_timer, next); + timer_mod(tb_env->decr_timer, next); tb_env->decr_next = next; } } @@ -973,7 +1010,7 @@ static void cpu_4xx_wdt_cb (void *opaque) cpu = ppc_env_get_cpu(env); tb_env = env->tb_env; ppc40x_timer = tb_env->opaque; - now = qemu_get_clock_ns(vm_clock); + now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); switch ((env->spr[SPR_40x_TCR] >> 30) & 0x3) { case 0: next = 1 << 17; @@ -999,12 +1036,12 @@ static void cpu_4xx_wdt_cb (void *opaque) switch ((env->spr[SPR_40x_TSR] >> 30) & 0x3) { case 0x0: case 0x1: - qemu_mod_timer(ppc40x_timer->wdt_timer, next); + timer_mod(ppc40x_timer->wdt_timer, next); ppc40x_timer->wdt_next = next; - env->spr[SPR_40x_TSR] |= 1 << 31; + env->spr[SPR_40x_TSR] |= 1U << 31; break; case 0x2: - qemu_mod_timer(ppc40x_timer->wdt_timer, next); + timer_mod(ppc40x_timer->wdt_timer, next); ppc40x_timer->wdt_next = next; env->spr[SPR_40x_TSR] |= 1 << 30; if ((env->spr[SPR_40x_TCR] >> 27) & 0x1) { @@ -1076,11 +1113,11 @@ clk_setup_cb ppc_40x_timers_init (CPUPPCState *env, uint32_t freq, LOG_TB("%s freq %" PRIu32 "\n", __func__, freq); if (ppc40x_timer != NULL) { /* We use decr timer for PIT */ - tb_env->decr_timer = qemu_new_timer_ns(vm_clock, &cpu_4xx_pit_cb, env); + tb_env->decr_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &cpu_4xx_pit_cb, env); ppc40x_timer->fit_timer = - qemu_new_timer_ns(vm_clock, &cpu_4xx_fit_cb, env); + timer_new_ns(QEMU_CLOCK_VIRTUAL, &cpu_4xx_fit_cb, env); ppc40x_timer->wdt_timer = - qemu_new_timer_ns(vm_clock, &cpu_4xx_wdt_cb, env); + timer_new_ns(QEMU_CLOCK_VIRTUAL, &cpu_4xx_wdt_cb, env); ppc40x_timer->decr_excp = decr_excp; } @@ -1362,3 +1399,24 @@ int PPC_NVRAM_set_params (nvram_t *nvram, uint16_t NVRAM_size, return 0; } + +/* CPU device-tree ID helpers */ +int ppc_get_vcpu_dt_id(PowerPCCPU *cpu) +{ + return cpu->cpu_dt_id; +} + +PowerPCCPU *ppc_get_vcpu_by_dt_id(int cpu_dt_id) +{ + CPUState *cs; + + CPU_FOREACH(cs) { + PowerPCCPU *cpu = POWERPC_CPU(cs); + + if (cpu->cpu_dt_id == cpu_dt_id) { + return cpu; + } + } + + return NULL; +} diff --git a/hw/ppc/ppc405_boards.c b/hw/ppc/ppc405_boards.c index f74e5e52c..f1a8f6734 100644 --- a/hw/ppc/ppc405_boards.c +++ b/hw/ppc/ppc405_boards.c @@ -27,9 +27,11 @@ #include "hw/timer/m48t59.h" #include "hw/block/flash.h" #include "sysemu/sysemu.h" +#include "sysemu/qtest.h" #include "block/block.h" #include "hw/boards.h" #include "qemu/log.h" +#include "qemu/error-report.h" #include "hw/loader.h" #include "sysemu/blockdev.h" #include "exec/address-spaces.h" @@ -42,7 +44,7 @@ #define USE_FLASH_BIOS -#define DEBUG_BOARD_INIT +//#define DEBUG_BOARD_INIT /*****************************************************************************/ /* PPC405EP reference board (IBM) */ @@ -252,17 +254,20 @@ static void ref405ep_init(QEMUMachineInitArgs *args) if (filename) { bios_size = load_image(filename, memory_region_get_ram_ptr(bios)); g_free(filename); + if (bios_size < 0 || bios_size > BIOS_SIZE) { + error_report("Could not load PowerPC BIOS '%s'", bios_name); + exit(1); + } + bios_size = (bios_size + 0xfff) & ~0xfff; + memory_region_add_subregion(sysmem, (uint32_t)(-bios_size), bios); + } else if (!qtest_enabled() || kernel_filename != NULL) { + error_report("Could not load PowerPC BIOS '%s'", bios_name); + exit(1); } else { + /* Avoid an uninitialized variable warning */ bios_size = -1; } - if (bios_size < 0 || bios_size > BIOS_SIZE) { - fprintf(stderr, "qemu: could not load PowerPC bios '%s'\n", - bios_name); - exit(1); - } - bios_size = (bios_size + 0xfff) & ~0xfff; memory_region_set_readonly(bios, true); - memory_region_add_subregion(sysmem, (uint32_t)(-bios_size), bios); } /* Register FPGA */ #ifdef DEBUG_BOARD_INIT @@ -353,16 +358,15 @@ static void ref405ep_init(QEMUMachineInitArgs *args) bdloc = 0; } #ifdef DEBUG_BOARD_INIT + printf("bdloc " RAM_ADDR_FMT "\n", bdloc); printf("%s: Done\n", __func__); #endif - printf("bdloc " RAM_ADDR_FMT "\n", bdloc); } static QEMUMachine ref405ep_machine = { .name = "ref405ep", .desc = "ref405ep", .init = ref405ep_init, - DEFAULT_MACHINE_OPTIONS, }; /*****************************************************************************/ @@ -569,17 +573,17 @@ static void taihu_405ep_init(QEMUMachineInitArgs *args) if (filename) { bios_size = load_image(filename, memory_region_get_ram_ptr(bios)); g_free(filename); - } else { - bios_size = -1; - } - if (bios_size < 0 || bios_size > BIOS_SIZE) { - fprintf(stderr, "qemu: could not load PowerPC bios '%s'\n", - bios_name); + if (bios_size < 0 || bios_size > BIOS_SIZE) { + error_report("Could not load PowerPC BIOS '%s'", bios_name); + exit(1); + } + bios_size = (bios_size + 0xfff) & ~0xfff; + memory_region_add_subregion(sysmem, (uint32_t)(-bios_size), bios); + } else if (!qtest_enabled()) { + error_report("Could not load PowerPC BIOS '%s'", bios_name); exit(1); } - bios_size = (bios_size + 0xfff) & ~0xfff; memory_region_set_readonly(bios, true); - memory_region_add_subregion(sysmem, (uint32_t)(-bios_size), bios); } /* Register Linux flash */ dinfo = drive_get(IF_PFLASH, 0, fl_idx); @@ -650,7 +654,6 @@ static QEMUMachine taihu_machine = { .name = "taihu", .desc = "taihu", .init = taihu_405ep_init, - DEFAULT_MACHINE_OPTIONS, }; static void ppc405_machine_init(void) diff --git a/hw/ppc/ppc405_uc.c b/hw/ppc/ppc405_uc.c index 290f71ab6..54ba59e73 100644 --- a/hw/ppc/ppc405_uc.c +++ b/hw/ppc/ppc405_uc.c @@ -30,20 +30,21 @@ #include "qemu/log.h" #include "exec/address-spaces.h" -#define DEBUG_OPBA -#define DEBUG_SDRAM -#define DEBUG_GPIO -#define DEBUG_SERIAL -#define DEBUG_OCM +//#define DEBUG_OPBA +//#define DEBUG_SDRAM +//#define DEBUG_GPIO +//#define DEBUG_SERIAL +//#define DEBUG_OCM //#define DEBUG_I2C -#define DEBUG_GPT -#define DEBUG_MAL -#define DEBUG_CLOCKS +//#define DEBUG_GPT +//#define DEBUG_MAL +//#define DEBUG_CLOCKS //#define DEBUG_CLOCKS_LL ram_addr_t ppc405_set_bootinfo (CPUPPCState *env, ppc4xx_bd_info_t *bd, uint32_t flags) { + CPUState *cs = CPU(ppc_env_get_cpu(env)); ram_addr_t bdloc; int i, n; @@ -52,42 +53,42 @@ ram_addr_t ppc405_set_bootinfo (CPUPPCState *env, ppc4xx_bd_info_t *bd, bdloc = 0x01000000UL - sizeof(struct ppc4xx_bd_info_t); else bdloc = bd->bi_memsize - sizeof(struct ppc4xx_bd_info_t); - stl_be_phys(bdloc + 0x00, bd->bi_memstart); - stl_be_phys(bdloc + 0x04, bd->bi_memsize); - stl_be_phys(bdloc + 0x08, bd->bi_flashstart); - stl_be_phys(bdloc + 0x0C, bd->bi_flashsize); - stl_be_phys(bdloc + 0x10, bd->bi_flashoffset); - stl_be_phys(bdloc + 0x14, bd->bi_sramstart); - stl_be_phys(bdloc + 0x18, bd->bi_sramsize); - stl_be_phys(bdloc + 0x1C, bd->bi_bootflags); - stl_be_phys(bdloc + 0x20, bd->bi_ipaddr); + stl_be_phys(cs->as, bdloc + 0x00, bd->bi_memstart); + stl_be_phys(cs->as, bdloc + 0x04, bd->bi_memsize); + stl_be_phys(cs->as, bdloc + 0x08, bd->bi_flashstart); + stl_be_phys(cs->as, bdloc + 0x0C, bd->bi_flashsize); + stl_be_phys(cs->as, bdloc + 0x10, bd->bi_flashoffset); + stl_be_phys(cs->as, bdloc + 0x14, bd->bi_sramstart); + stl_be_phys(cs->as, bdloc + 0x18, bd->bi_sramsize); + stl_be_phys(cs->as, bdloc + 0x1C, bd->bi_bootflags); + stl_be_phys(cs->as, bdloc + 0x20, bd->bi_ipaddr); for (i = 0; i < 6; i++) { - stb_phys(bdloc + 0x24 + i, bd->bi_enetaddr[i]); + stb_phys(cs->as, bdloc + 0x24 + i, bd->bi_enetaddr[i]); } - stw_be_phys(bdloc + 0x2A, bd->bi_ethspeed); - stl_be_phys(bdloc + 0x2C, bd->bi_intfreq); - stl_be_phys(bdloc + 0x30, bd->bi_busfreq); - stl_be_phys(bdloc + 0x34, bd->bi_baudrate); + stw_be_phys(cs->as, bdloc + 0x2A, bd->bi_ethspeed); + stl_be_phys(cs->as, bdloc + 0x2C, bd->bi_intfreq); + stl_be_phys(cs->as, bdloc + 0x30, bd->bi_busfreq); + stl_be_phys(cs->as, bdloc + 0x34, bd->bi_baudrate); for (i = 0; i < 4; i++) { - stb_phys(bdloc + 0x38 + i, bd->bi_s_version[i]); + stb_phys(cs->as, bdloc + 0x38 + i, bd->bi_s_version[i]); } for (i = 0; i < 32; i++) { - stb_phys(bdloc + 0x3C + i, bd->bi_r_version[i]); + stb_phys(cs->as, bdloc + 0x3C + i, bd->bi_r_version[i]); } - stl_be_phys(bdloc + 0x5C, bd->bi_plb_busfreq); - stl_be_phys(bdloc + 0x60, bd->bi_pci_busfreq); + stl_be_phys(cs->as, bdloc + 0x5C, bd->bi_plb_busfreq); + stl_be_phys(cs->as, bdloc + 0x60, bd->bi_pci_busfreq); for (i = 0; i < 6; i++) { - stb_phys(bdloc + 0x64 + i, bd->bi_pci_enetaddr[i]); + stb_phys(cs->as, bdloc + 0x64 + i, bd->bi_pci_enetaddr[i]); } n = 0x6A; if (flags & 0x00000001) { for (i = 0; i < 6; i++) - stb_phys(bdloc + n++, bd->bi_pci_enetaddr2[i]); + stb_phys(cs->as, bdloc + n++, bd->bi_pci_enetaddr2[i]); } - stl_be_phys(bdloc + n, bd->bi_opbfreq); + stl_be_phys(cs->as, bdloc + n, bd->bi_opbfreq); n += 4; for (i = 0; i < 2; i++) { - stl_be_phys(bdloc + n, bd->bi_iic_fast[i]); + stl_be_phys(cs->as, bdloc + n, bd->bi_iic_fast[i]); n += 4; } @@ -1234,7 +1235,7 @@ struct ppc4xx_gpt_t { MemoryRegion iomem; int64_t tb_offset; uint32_t tb_freq; - struct QEMUTimer *timer; + QEMUTimer *timer; qemu_irq irqs[5]; uint32_t oe; uint32_t ol; @@ -1348,7 +1349,7 @@ static uint32_t ppc4xx_gpt_readl (void *opaque, hwaddr addr) switch (addr) { case 0x00: /* Time base counter */ - ret = muldiv64(qemu_get_clock_ns(vm_clock) + gpt->tb_offset, + ret = muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + gpt->tb_offset, gpt->tb_freq, get_ticks_per_sec()); break; case 0x10: @@ -1405,7 +1406,7 @@ static void ppc4xx_gpt_writel (void *opaque, case 0x00: /* Time base counter */ gpt->tb_offset = muldiv64(value, get_ticks_per_sec(), gpt->tb_freq) - - qemu_get_clock_ns(vm_clock); + - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); ppc4xx_gpt_compute_timer(gpt); break; case 0x10: @@ -1476,7 +1477,7 @@ static void ppc4xx_gpt_reset (void *opaque) int i; gpt = opaque; - qemu_del_timer(gpt->timer); + timer_del(gpt->timer); gpt->oe = 0x00000000; gpt->ol = 0x00000000; gpt->im = 0x00000000; @@ -1497,7 +1498,7 @@ static void ppc4xx_gpt_init(hwaddr base, qemu_irq irqs[5]) for (i = 0; i < 5; i++) { gpt->irqs[i] = irqs[i]; } - gpt->timer = qemu_new_timer_ns(vm_clock, &ppc4xx_gpt_cb, gpt); + gpt->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &ppc4xx_gpt_cb, gpt); #ifdef DEBUG_GPT printf("%s: offset " TARGET_FMT_plx "\n", __func__, base); #endif diff --git a/hw/ppc/ppc440_bamboo.c b/hw/ppc/ppc440_bamboo.c index 369ab9e26..2ddc2ed4b 100644 --- a/hw/ppc/ppc440_bamboo.c +++ b/hw/ppc/ppc440_bamboo.c @@ -77,23 +77,23 @@ static int bamboo_load_device_tree(hwaddr addr, /* Manipulate device tree in memory. */ - ret = qemu_devtree_setprop(fdt, "/memory", "reg", mem_reg_property, - sizeof(mem_reg_property)); + ret = qemu_fdt_setprop(fdt, "/memory", "reg", mem_reg_property, + sizeof(mem_reg_property)); if (ret < 0) fprintf(stderr, "couldn't set /memory/reg\n"); - ret = qemu_devtree_setprop_cell(fdt, "/chosen", "linux,initrd-start", - initrd_base); + ret = qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-start", + initrd_base); if (ret < 0) fprintf(stderr, "couldn't set /chosen/linux,initrd-start\n"); - ret = qemu_devtree_setprop_cell(fdt, "/chosen", "linux,initrd-end", - (initrd_base + initrd_size)); + ret = qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-end", + (initrd_base + initrd_size)); if (ret < 0) fprintf(stderr, "couldn't set /chosen/linux,initrd-end\n"); - ret = qemu_devtree_setprop_string(fdt, "/chosen", "bootargs", - kernel_cmdline); + ret = qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", + kernel_cmdline); if (ret < 0) fprintf(stderr, "couldn't set /chosen/bootargs\n"); @@ -105,13 +105,14 @@ static int bamboo_load_device_tree(hwaddr addr, clock_freq = kvmppc_get_clockfreq(); } - qemu_devtree_setprop_cell(fdt, "/cpus/cpu@0", "clock-frequency", - clock_freq); - qemu_devtree_setprop_cell(fdt, "/cpus/cpu@0", "timebase-frequency", - tb_freq); + qemu_fdt_setprop_cell(fdt, "/cpus/cpu@0", "clock-frequency", + clock_freq); + qemu_fdt_setprop_cell(fdt, "/cpus/cpu@0", "timebase-frequency", + tb_freq); - ret = rom_add_blob_fixed(BINARY_DEVICE_TREE_FILE, fdt, fdt_size, addr); + rom_add_blob_fixed(BINARY_DEVICE_TREE_FILE, fdt, fdt_size, addr); g_free(fdt); + return 0; out: @@ -127,7 +128,7 @@ static void mmubooke_create_initial_mapping(CPUPPCState *env, tlb->attr = 0; tlb->prot = PAGE_VALID | ((PAGE_READ | PAGE_WRITE | PAGE_EXEC) << 4); - tlb->size = 1 << 31; /* up to 0x80000000 */ + tlb->size = 1U << 31; /* up to 0x80000000 */ tlb->EPN = va & TARGET_PAGE_MASK; tlb->RPN = pa & TARGET_PAGE_MASK; tlb->PID = 0; @@ -135,7 +136,7 @@ static void mmubooke_create_initial_mapping(CPUPPCState *env, tlb = &env->tlb.tlbe[1]; tlb->attr = 0; tlb->prot = PAGE_VALID | ((PAGE_READ | PAGE_WRITE | PAGE_EXEC) << 4); - tlb->size = 1 << 31; /* up to 0xffffffff */ + tlb->size = 1U << 31; /* up to 0xffffffff */ tlb->EPN = 0x80000000 & TARGET_PAGE_MASK; tlb->RPN = 0x80000000 & TARGET_PAGE_MASK; tlb->PID = 0; @@ -296,7 +297,6 @@ static QEMUMachine bamboo_machine = { .name = "bamboo", .desc = "bamboo", .init = bamboo_init, - DEFAULT_MACHINE_OPTIONS, }; static void bamboo_machine_init(void) diff --git a/hw/ppc/ppc4xx_devs.c b/hw/ppc/ppc4xx_devs.c index 239aada19..8a43111a5 100644 --- a/hw/ppc/ppc4xx_devs.c +++ b/hw/ppc/ppc4xx_devs.c @@ -27,8 +27,6 @@ #include "qemu/log.h" #include "exec/address-spaces.h" -//#define DEBUG_MMIO -//#define DEBUG_UNASSIGNED #define DEBUG_UIC @@ -163,7 +161,7 @@ static void ppcuic_set_irq (void *opaque, int irq_num, int level) uint32_t mask, sr; uic = opaque; - mask = 1 << (31-irq_num); + mask = 1U << (31-irq_num); LOG_UIC("%s: irq %d level %d uicsr %08" PRIx32 " mask %08" PRIx32 " => %08" PRIx32 " %08" PRIx32 "\n", __func__, irq_num, level, diff --git a/hw/ppc/ppc4xx_pci.c b/hw/ppc/ppc4xx_pci.c index d2d6f65e6..4cb78518a 100644 --- a/hw/ppc/ppc4xx_pci.c +++ b/hw/ppc/ppc4xx_pci.c @@ -380,6 +380,11 @@ static void ppc4xx_host_bridge_class_init(ObjectClass *klass, void *data) k->vendor_id = PCI_VENDOR_ID_IBM; k->device_id = PCI_DEVICE_ID_IBM_440GX; k->class_id = PCI_CLASS_BRIDGE_OTHER; + /* + * PCI-facing part of the host bridge, not usable without the + * host-facing part, which can't be device_add'ed, yet. + */ + dc->cannot_instantiate_with_device_add_yet = true; } static const TypeInfo ppc4xx_host_bridge_info = { diff --git a/hw/ppc/ppc_booke.c b/hw/ppc/ppc_booke.c index 000c27f2e..8b94da6b0 100644 --- a/hw/ppc/ppc_booke.c +++ b/hw/ppc/ppc_booke.c @@ -34,15 +34,15 @@ /* Timer Control Register */ #define TCR_WP_SHIFT 30 /* Watchdog Timer Period */ -#define TCR_WP_MASK (0x3 << TCR_WP_SHIFT) +#define TCR_WP_MASK (0x3U << TCR_WP_SHIFT) #define TCR_WRC_SHIFT 28 /* Watchdog Timer Reset Control */ -#define TCR_WRC_MASK (0x3 << TCR_WRC_SHIFT) -#define TCR_WIE (1 << 27) /* Watchdog Timer Interrupt Enable */ -#define TCR_DIE (1 << 26) /* Decrementer Interrupt Enable */ +#define TCR_WRC_MASK (0x3U << TCR_WRC_SHIFT) +#define TCR_WIE (1U << 27) /* Watchdog Timer Interrupt Enable */ +#define TCR_DIE (1U << 26) /* Decrementer Interrupt Enable */ #define TCR_FP_SHIFT 24 /* Fixed-Interval Timer Period */ -#define TCR_FP_MASK (0x3 << TCR_FP_SHIFT) -#define TCR_FIE (1 << 23) /* Fixed-Interval Timer Interrupt Enable */ -#define TCR_ARE (1 << 22) /* Auto-Reload Enable */ +#define TCR_FP_MASK (0x3U << TCR_FP_SHIFT) +#define TCR_FIE (1U << 23) /* Fixed-Interval Timer Interrupt Enable */ +#define TCR_ARE (1U << 22) /* Auto-Reload Enable */ /* Timer Control Register (e500 specific fields) */ @@ -53,21 +53,21 @@ /* Timer Status Register */ -#define TSR_FIS (1 << 26) /* Fixed-Interval Timer Interrupt Status */ -#define TSR_DIS (1 << 27) /* Decrementer Interrupt Status */ +#define TSR_FIS (1U << 26) /* Fixed-Interval Timer Interrupt Status */ +#define TSR_DIS (1U << 27) /* Decrementer Interrupt Status */ #define TSR_WRS_SHIFT 28 /* Watchdog Timer Reset Status */ -#define TSR_WRS_MASK (0x3 << TSR_WRS_SHIFT) -#define TSR_WIS (1 << 30) /* Watchdog Timer Interrupt Status */ -#define TSR_ENW (1 << 31) /* Enable Next Watchdog Timer */ +#define TSR_WRS_MASK (0x3U << TSR_WRS_SHIFT) +#define TSR_WIS (1U << 30) /* Watchdog Timer Interrupt Status */ +#define TSR_ENW (1U << 31) /* Enable Next Watchdog Timer */ typedef struct booke_timer_t booke_timer_t; struct booke_timer_t { uint64_t fit_next; - struct QEMUTimer *fit_timer; + QEMUTimer *fit_timer; uint64_t wdt_next; - struct QEMUTimer *wdt_timer; + QEMUTimer *wdt_timer; uint32_t flags; }; @@ -128,7 +128,8 @@ static uint8_t booke_get_wdt_target(CPUPPCState *env, ppc_tb_t *tb_env) static void booke_update_fixed_timer(CPUPPCState *env, uint8_t target_bit, uint64_t *next, - struct QEMUTimer *timer) + QEMUTimer *timer, + int tsr_bit) { ppc_tb_t *tb_env = env->tb_env; uint64_t delta_tick, ticks = 0; @@ -136,7 +137,15 @@ static void booke_update_fixed_timer(CPUPPCState *env, uint64_t period; uint64_t now; - now = qemu_get_clock_ns(vm_clock); + if (!(env->spr[SPR_BOOKE_TSR] & tsr_bit)) { + /* + * Don't arm the timer again when the guest has the current + * interrupt still pending. Wait for it to ack it. + */ + return; + } + + now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); tb = cpu_ppc_get_tb(tb_env, now, tb_env->tb_offset); period = 1ULL << target_bit; delta_tick = period - (tb & (period - 1)); @@ -165,9 +174,16 @@ static void booke_update_fixed_timer(CPUPPCState *env, if (*next == now) { (*next)++; + } else { + /* + * There's no point to fake any granularity that's more fine grained + * than milliseconds. Anything beyond that just overloads the system. + */ + *next = MAX(*next, now + SCALE_MS); } - qemu_mod_timer(timer, *next); + /* Fire the next timer */ + timer_mod(timer, *next); } static void booke_decr_cb(void *opaque) @@ -200,7 +216,8 @@ static void booke_fit_cb(void *opaque) booke_update_fixed_timer(env, booke_get_fit_target(env, tb_env), &booke_timer->fit_next, - booke_timer->fit_timer); + booke_timer->fit_timer, + TSR_FIS); } static void booke_wdt_cb(void *opaque) @@ -220,15 +237,35 @@ static void booke_wdt_cb(void *opaque) booke_update_fixed_timer(env, booke_get_wdt_target(env, tb_env), &booke_timer->wdt_next, - booke_timer->wdt_timer); + booke_timer->wdt_timer, + TSR_WIS); } void store_booke_tsr(CPUPPCState *env, target_ulong val) { PowerPCCPU *cpu = ppc_env_get_cpu(env); + ppc_tb_t *tb_env = env->tb_env; + booke_timer_t *booke_timer = tb_env->opaque; env->spr[SPR_BOOKE_TSR] &= ~val; kvmppc_clear_tsr_bits(cpu, val); + + if (val & TSR_FIS) { + booke_update_fixed_timer(env, + booke_get_fit_target(env, tb_env), + &booke_timer->fit_next, + booke_timer->fit_timer, + TSR_FIS); + } + + if (val & TSR_WIS) { + booke_update_fixed_timer(env, + booke_get_wdt_target(env, tb_env), + &booke_timer->wdt_next, + booke_timer->wdt_timer, + TSR_WIS); + } + booke_update_irq(cpu); } @@ -247,12 +284,14 @@ void store_booke_tcr(CPUPPCState *env, target_ulong val) booke_update_fixed_timer(env, booke_get_fit_target(env, tb_env), &booke_timer->fit_next, - booke_timer->fit_timer); + booke_timer->fit_timer, + TSR_FIS); booke_update_fixed_timer(env, booke_get_wdt_target(env, tb_env), &booke_timer->wdt_next, - booke_timer->wdt_timer); + booke_timer->wdt_timer, + TSR_WIS); } static void ppc_booke_timer_reset_handle(void *opaque) @@ -303,12 +342,12 @@ void ppc_booke_timers_init(PowerPCCPU *cpu, uint32_t freq, uint32_t flags) tb_env->tb_freq = freq; tb_env->decr_freq = freq; tb_env->opaque = booke_timer; - tb_env->decr_timer = qemu_new_timer_ns(vm_clock, &booke_decr_cb, cpu); + tb_env->decr_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &booke_decr_cb, cpu); booke_timer->fit_timer = - qemu_new_timer_ns(vm_clock, &booke_fit_cb, cpu); + timer_new_ns(QEMU_CLOCK_VIRTUAL, &booke_fit_cb, cpu); booke_timer->wdt_timer = - qemu_new_timer_ns(vm_clock, &booke_wdt_cb, cpu); + timer_new_ns(QEMU_CLOCK_VIRTUAL, &booke_wdt_cb, cpu); ret = kvmppc_booke_watchdog_enable(cpu); diff --git a/hw/ppc/ppce500_spin.c b/hw/ppc/ppce500_spin.c index 78b23fa59..d49f2b880 100644 --- a/hw/ppc/ppce500_spin.c +++ b/hw/ppc/ppce500_spin.c @@ -65,9 +65,9 @@ static void spin_reset(void *opaque) for (i = 0; i < MAX_CPUS; i++) { SpinInfo *info = &s->spin[i]; - info->pir = i; - info->r3 = i; - info->addr = 1; + stl_p(&info->pir, i); + stq_p(&info->r3, i); + stq_p(&info->addr, 1); } } @@ -117,7 +117,7 @@ static void spin_kick(void *data) mmubooke_create_initial_mapping(env, 0, map_start, map_size); cpu->halted = 0; - env->exception_index = -1; + cpu->exception_index = -1; cpu->stopped = false; qemu_cpu_kick(cpu); } diff --git a/hw/ppc/prep.c b/hw/ppc/prep.c index 7e04b1ac8..e2436512f 100644 --- a/hw/ppc/prep.c +++ b/hw/ppc/prep.c @@ -185,6 +185,7 @@ typedef struct sysctrl_t { uint8_t state; uint8_t syscontrol; int contiguous_map; + qemu_irq contiguous_map_irq; int endian; } sysctrl_t; @@ -253,6 +254,7 @@ static void PREP_io_800_writeb (void *opaque, uint32_t addr, uint32_t val) case 0x0850: /* I/O map type register */ sysctrl->contiguous_map = val & 0x01; + qemu_set_irq(sysctrl->contiguous_map_irq, sysctrl->contiguous_map); break; default: printf("ERROR: unaffected IO port write: %04" PRIx32 @@ -327,91 +329,6 @@ static uint32_t PREP_io_800_readb (void *opaque, uint32_t addr) return retval; } -static inline hwaddr prep_IO_address(sysctrl_t *sysctrl, - hwaddr addr) -{ - if (sysctrl->contiguous_map == 0) { - /* 64 KB contiguous space for IOs */ - addr &= 0xFFFF; - } else { - /* 8 MB non-contiguous space for IOs */ - addr = (addr & 0x1F) | ((addr & 0x007FFF000) >> 7); - } - - return addr; -} - -static void PPC_prep_io_writeb (void *opaque, hwaddr addr, - uint32_t value) -{ - sysctrl_t *sysctrl = opaque; - - addr = prep_IO_address(sysctrl, addr); - cpu_outb(addr, value); -} - -static uint32_t PPC_prep_io_readb (void *opaque, hwaddr addr) -{ - sysctrl_t *sysctrl = opaque; - uint32_t ret; - - addr = prep_IO_address(sysctrl, addr); - ret = cpu_inb(addr); - - return ret; -} - -static void PPC_prep_io_writew (void *opaque, hwaddr addr, - uint32_t value) -{ - sysctrl_t *sysctrl = opaque; - - addr = prep_IO_address(sysctrl, addr); - PPC_IO_DPRINTF("0x" TARGET_FMT_plx " => 0x%08" PRIx32 "\n", addr, value); - cpu_outw(addr, value); -} - -static uint32_t PPC_prep_io_readw (void *opaque, hwaddr addr) -{ - sysctrl_t *sysctrl = opaque; - uint32_t ret; - - addr = prep_IO_address(sysctrl, addr); - ret = cpu_inw(addr); - PPC_IO_DPRINTF("0x" TARGET_FMT_plx " <= 0x%08" PRIx32 "\n", addr, ret); - - return ret; -} - -static void PPC_prep_io_writel (void *opaque, hwaddr addr, - uint32_t value) -{ - sysctrl_t *sysctrl = opaque; - - addr = prep_IO_address(sysctrl, addr); - PPC_IO_DPRINTF("0x" TARGET_FMT_plx " => 0x%08" PRIx32 "\n", addr, value); - cpu_outl(addr, value); -} - -static uint32_t PPC_prep_io_readl (void *opaque, hwaddr addr) -{ - sysctrl_t *sysctrl = opaque; - uint32_t ret; - - addr = prep_IO_address(sysctrl, addr); - ret = cpu_inl(addr); - PPC_IO_DPRINTF("0x" TARGET_FMT_plx " <= 0x%08" PRIx32 "\n", addr, ret); - - return ret; -} - -static const MemoryRegionOps PPC_prep_io_ops = { - .old_mmio = { - .read = { PPC_prep_io_readb, PPC_prep_io_readw, PPC_prep_io_readl }, - .write = { PPC_prep_io_writeb, PPC_prep_io_writew, PPC_prep_io_writel }, - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; #define NVRAM_SIZE 0x2000 @@ -452,21 +369,19 @@ static void ppc_prep_init(QEMUMachineInitArgs *args) const char *kernel_filename = args->kernel_filename; const char *kernel_cmdline = args->kernel_cmdline; const char *initrd_filename = args->initrd_filename; - const char *boot_device = args->boot_device; + const char *boot_device = args->boot_order; MemoryRegion *sysmem = get_system_memory(); PowerPCCPU *cpu = NULL; CPUPPCState *env = NULL; - char *filename; nvram_t nvram; M48t59State *m48t59; - MemoryRegion *PPC_io_memory = g_new(MemoryRegion, 1); PortioList *port_list = g_new(PortioList, 1); #if 0 MemoryRegion *xcsr = g_new(MemoryRegion, 1); #endif - int linux_boot, i, nb_nics1, bios_size; + int linux_boot, i, nb_nics1; MemoryRegion *ram = g_new(MemoryRegion, 1); - MemoryRegion *bios = g_new(MemoryRegion, 1); + MemoryRegion *vga = g_new(MemoryRegion, 1); uint32_t kernel_base, initrd_base; long kernel_size, initrd_size; DeviceState *dev; @@ -509,43 +424,6 @@ static void ppc_prep_init(QEMUMachineInitArgs *args) vmstate_register_ram_global(ram); memory_region_add_subregion(sysmem, 0, ram); - /* allocate and load BIOS */ - memory_region_init_ram(bios, NULL, "ppc_prep.bios", BIOS_SIZE); - memory_region_set_readonly(bios, true); - memory_region_add_subregion(sysmem, (uint32_t)(-BIOS_SIZE), bios); - vmstate_register_ram_global(bios); - if (bios_name == NULL) - bios_name = BIOS_FILENAME; - filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); - if (filename) { - bios_size = load_elf(filename, NULL, NULL, NULL, - NULL, NULL, 1, ELF_MACHINE, 0); - if (bios_size < 0) { - bios_size = get_image_size(filename); - if (bios_size > 0 && bios_size <= BIOS_SIZE) { - hwaddr bios_addr; - bios_size = (bios_size + 0xfff) & ~0xfff; - bios_addr = (uint32_t)(-bios_size); - bios_size = load_image_targphys(filename, bios_addr, bios_size); - } - if (bios_size > BIOS_SIZE) { - fprintf(stderr, "qemu: PReP bios '%s' is too large (0x%x)\n", - bios_name, bios_size); - exit(1); - } - } - } else { - bios_size = -1; - } - if (bios_size < 0 && !qtest_enabled()) { - fprintf(stderr, "qemu: could not load PPC PReP bios '%s'\n", - bios_name); - exit(1); - } - if (filename) { - g_free(filename); - } - if (linux_boot) { kernel_base = KERNEL_LOAD_ADDR; /* now we can load the kernel */ @@ -593,6 +471,11 @@ static void ppc_prep_init(QEMUMachineInitArgs *args) } dev = qdev_create(NULL, "raven-pcihost"); + if (bios_name == NULL) { + bios_name = BIOS_FILENAME; + } + qdev_prop_set_string(dev, "bios-name", bios_name); + qdev_prop_set_uint32(dev, "elf-machine", ELF_MACHINE); pcihost = PCI_HOST_BRIDGE(dev); object_property_add_child(qdev_get_machine(), "raven", OBJECT(dev), NULL); qdev_init_nofail(dev); @@ -601,6 +484,7 @@ static void ppc_prep_init(QEMUMachineInitArgs *args) fprintf(stderr, "Couldn't create PCI host controller.\n"); exit(1); } + sysctrl->contiguous_map_irq = qdev_get_gpio_in(dev, 0); /* PCI -> ISA bridge */ pci = pci_create_simple(pci_bus, PCI_DEVFN(1, 0), "i82378"); @@ -621,13 +505,16 @@ static void ppc_prep_init(QEMUMachineInitArgs *args) qdev_prop_set_uint8(dev, "config", 13); /* fdc, ser0, ser1, par0 */ qdev_init_nofail(dev); - /* Register 8 MB of ISA IO space (needed for non-contiguous map) */ - memory_region_init_io(PPC_io_memory, NULL, &PPC_prep_io_ops, sysctrl, - "ppc-io", 0x00800000); - memory_region_add_subregion(sysmem, 0x80000000, PPC_io_memory); - /* init basic PC hardware */ pci_vga_init(pci_bus); + /* Open Hack'Ware hack: PCI BAR#0 is programmed to 0xf0000000. + * While bios will access framebuffer at 0xf0000000, real physical + * address is 0xf0000000 + 0xc0000000 (PCI memory base). + * Alias the wrong memory accesses to the right place. + */ + memory_region_init_alias(vga, NULL, "vga-alias", pci_address_space(pci), + 0xf0000000, 0x1000000); + memory_region_add_subregion_overlap(sysmem, 0xf0000000, vga, 10); nb_nics1 = nb_nics; if (nb_nics1 > NE2000_NB_MAX) @@ -656,7 +543,7 @@ static void ppc_prep_init(QEMUMachineInitArgs *args) sysctrl->reset_irq = cpu->env.irq_inputs[PPC6xx_INPUT_HRESET]; portio_list_init(port_list, NULL, prep_portio_list, sysctrl, "prep"); - portio_list_add(port_list, get_system_io(), 0x0); + portio_list_add(port_list, isa_address_space_io(isa), 0x0); /* PowerPC control and status register group */ #if 0 @@ -691,7 +578,7 @@ static QEMUMachine prep_machine = { .desc = "PowerPC PREP platform", .init = ppc_prep_init, .max_cpus = MAX_CPUS, - DEFAULT_MACHINE_OPTIONS, + .default_boot_order = "cad", }; static void prep_machine_init(void) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 16bfab90b..a11e1217b 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -26,6 +26,7 @@ */ #include "sysemu/sysemu.h" #include "hw/hw.h" +#include "hw/fw-path-provider.h" #include "elf.h" #include "net/net.h" #include "sysemu/blockdev.h" @@ -45,10 +46,13 @@ #include "hw/pci/msi.h" #include "hw/pci/pci.h" +#include "hw/scsi/scsi.h" +#include "hw/virtio/virtio-scsi.h" #include "exec/address-spaces.h" #include "hw/usb.h" #include "qemu/config-file.h" +#include "qemu/error-report.h" #include <libfdt.h> @@ -62,7 +66,7 @@ * * We load our kernel at 4M, leaving space for SLOF initial image */ -#define FDT_MAX_SIZE 0x10000 +#define FDT_MAX_SIZE 0x40000 #define RTAS_MAX_SIZE 0x10000 #define FW_MAX_SIZE 0x400000 #define FW_FILE_NAME "slof.bin" @@ -80,6 +84,8 @@ #define HTAB_SIZE(spapr) (1ULL << ((spapr)->htab_shift)) +#define TYPE_SPAPR_MACHINE "spapr-machine" + sPAPREnvironment *spapr; int spapr_allocate_irq(int hint, bool lsi) @@ -88,6 +94,9 @@ int spapr_allocate_irq(int hint, bool lsi) if (hint) { irq = hint; + if (hint >= spapr->next_irq) { + spapr->next_irq = hint + 1; + } /* FIXME: we should probably check for collisions somehow */ } else { irq = spapr->next_irq++; @@ -103,22 +112,39 @@ int spapr_allocate_irq(int hint, bool lsi) return irq; } -/* Allocate block of consequtive IRQs, returns a number of the first */ -int spapr_allocate_irq_block(int num, bool lsi) +/* + * Allocate block of consequtive IRQs, returns a number of the first. + * If msi==true, aligns the first IRQ number to num. + */ +int spapr_allocate_irq_block(int num, bool lsi, bool msi) { int first = -1; - int i; + int i, hint = 0; + + /* + * MSIMesage::data is used for storing VIRQ so + * it has to be aligned to num to support multiple + * MSI vectors. MSI-X is not affected by this. + * The hint is used for the first IRQ, the rest should + * be allocated continuously. + */ + if (msi) { + assert((num == 1) || (num == 2) || (num == 4) || + (num == 8) || (num == 16) || (num == 32)); + hint = (spapr->next_irq + num - 1) & ~(num - 1); + } for (i = 0; i < num; ++i) { int irq; - irq = spapr_allocate_irq(0, lsi); + irq = spapr_allocate_irq(hint, lsi); if (!irq) { return -1; } if (0 == i) { first = irq; + hint = 0; } /* If the above doesn't create a consecutive block then that's @@ -141,14 +167,33 @@ static XICSState *try_create_xics(const char *type, int nr_servers, return NULL; } - return XICS(dev); + return XICS_COMMON(dev); } static XICSState *xics_system_init(int nr_servers, int nr_irqs) { XICSState *icp = NULL; - icp = try_create_xics(TYPE_XICS, nr_servers, nr_irqs); + if (kvm_enabled()) { + QemuOpts *machine_opts = qemu_get_machine_opts(); + bool irqchip_allowed = qemu_opt_get_bool(machine_opts, + "kernel_irqchip", true); + bool irqchip_required = qemu_opt_get_bool(machine_opts, + "kernel_irqchip", false); + if (irqchip_allowed) { + icp = try_create_xics(TYPE_KVM_XICS, nr_servers, nr_irqs); + } + + if (irqchip_required && !icp) { + perror("Failed to create in-kernel XICS\n"); + abort(); + } + } + + if (!icp) { + icp = try_create_xics(TYPE_XICS, nr_servers, nr_irqs); + } + if (!icp) { perror("Failed to create XICS\n"); abort(); @@ -165,22 +210,22 @@ static int spapr_fixup_cpu_dt(void *fdt, sPAPREnvironment *spapr) int smt = kvmppc_smt_threads(); uint32_t pft_size_prop[] = {0, cpu_to_be32(spapr->htab_shift)}; - assert(spapr->cpu_model); - - for (cpu = first_cpu; cpu != NULL; cpu = cpu->next_cpu) { + CPU_FOREACH(cpu) { + DeviceClass *dc = DEVICE_GET_CLASS(cpu); + int index = ppc_get_vcpu_dt_id(POWERPC_CPU(cpu)); uint32_t associativity[] = {cpu_to_be32(0x5), cpu_to_be32(0x0), cpu_to_be32(0x0), cpu_to_be32(0x0), cpu_to_be32(cpu->numa_node), - cpu_to_be32(cpu->cpu_index)}; + cpu_to_be32(index)}; - if ((cpu->cpu_index % smt) != 0) { + if ((index % smt) != 0) { continue; } - snprintf(cpu_model, 32, "/cpus/%s@%x", spapr->cpu_model, - cpu->cpu_index); + snprintf(cpu_model, 32, "/cpus/%s@%x", dc->fw_name, + index); offset = fdt_path_offset(fdt, cpu_model); if (offset < 0) { @@ -249,10 +294,10 @@ static size_t create_page_sizes_prop(CPUPPCState *env, uint32_t *prop, } while (0) -static void *spapr_create_fdt_skel(const char *cpu_model, - hwaddr initrd_base, +static void *spapr_create_fdt_skel(hwaddr initrd_base, hwaddr initrd_size, hwaddr kernel_size, + bool little_endian, const char *boot_device, const char *kernel_cmdline, uint32_t epow_irq) @@ -262,11 +307,10 @@ static void *spapr_create_fdt_skel(const char *cpu_model, uint32_t start_prop = cpu_to_be32(initrd_base); uint32_t end_prop = cpu_to_be32(initrd_base + initrd_size); char hypertas_prop[] = "hcall-pft\0hcall-term\0hcall-dabr\0hcall-interrupt" - "\0hcall-tce\0hcall-vio\0hcall-splpar\0hcall-bulk"; + "\0hcall-tce\0hcall-vio\0hcall-splpar\0hcall-bulk\0hcall-set-mode"; char qemu_hypertas_prop[] = "hcall-memop1"; uint32_t refpoints[] = {cpu_to_be32(0x4), cpu_to_be32(0x4)}; uint32_t interrupt_server_ranges_prop[] = {0, cpu_to_be32(smp_cpus)}; - char *modelname; int i, smt = kvmppc_smt_threads(); unsigned char vec5[] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x80}; @@ -306,6 +350,9 @@ static void *spapr_create_fdt_skel(const char *cpu_model, cpu_to_be64(kernel_size) }; _FDT((fdt_property(fdt, "qemu,boot-kernel", &kprop, sizeof(kprop)))); + if (little_endian) { + _FDT((fdt_property(fdt, "qemu,boot-kernel-le", NULL, 0))); + } } if (boot_device) { _FDT((fdt_property_string(fdt, "qemu,boot-device", boot_device))); @@ -322,20 +369,12 @@ static void *spapr_create_fdt_skel(const char *cpu_model, _FDT((fdt_property_cell(fdt, "#address-cells", 0x1))); _FDT((fdt_property_cell(fdt, "#size-cells", 0x0))); - modelname = g_strdup(cpu_model); - - for (i = 0; i < strlen(modelname); i++) { - modelname[i] = toupper(modelname[i]); - } - - /* This is needed during FDT finalization */ - spapr->cpu_model = g_strdup(modelname); - - for (cs = first_cpu; cs != NULL; cs = cs->next_cpu) { + CPU_FOREACH(cs) { PowerPCCPU *cpu = POWERPC_CPU(cs); CPUPPCState *env = &cpu->env; + DeviceClass *dc = DEVICE_GET_CLASS(cs); PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cs); - int index = cs->cpu_index; + int index = ppc_get_vcpu_dt_id(cpu); uint32_t servers_prop[smp_threads]; uint32_t gservers_prop[smp_threads * 2]; char *nodename; @@ -350,7 +389,7 @@ static void *spapr_create_fdt_skel(const char *cpu_model, continue; } - nodename = g_strdup_printf("%s@%x", modelname, index); + nodename = g_strdup_printf("%s@%x", dc->fw_name, index); _FDT((fdt_begin_node(fdt, nodename))); @@ -398,6 +437,10 @@ static void *spapr_create_fdt_skel(const char *cpu_model, _FDT((fdt_property(fdt, "ibm,ppc-interrupt-gserver#s", gservers_prop, sizeof(gservers_prop)))); + if (env->spr_cb[SPR_PURR].oea_read) { + _FDT((fdt_property(fdt, "ibm,purr", NULL, 0))); + } + if (env->mmu_model & POWERPC_MMU_1TSEG) { _FDT((fdt_property(fdt, "ibm,processor-segment-sizes", segs, sizeof(segs)))); @@ -430,8 +473,6 @@ static void *spapr_create_fdt_skel(const char *cpu_model, _FDT((fdt_end_node(fdt))); } - g_free(modelname); - _FDT((fdt_end_node(fdt))); /* RTAS */ @@ -492,14 +533,15 @@ static int spapr_populate_memory(sPAPREnvironment *spapr, void *fdt) cpu_to_be32(0x0), cpu_to_be32(0x0), cpu_to_be32(0x0)}; char mem_name[32]; - hwaddr node0_size, mem_start; + hwaddr node0_size, mem_start, node_size; uint64_t mem_reg_property[2]; int i, off; /* memory node(s) */ - node0_size = (nb_numa_nodes > 1) ? node_mem[0] : ram_size; - if (spapr->rma_size > node0_size) { - spapr->rma_size = node0_size; + if (nb_numa_nodes > 1 && node_mem[0] < ram_size) { + node0_size = node_mem[0]; + } else { + node0_size = ram_size; } /* RMA */ @@ -532,7 +574,15 @@ static int spapr_populate_memory(sPAPREnvironment *spapr, void *fdt) mem_start = node0_size; for (i = 1; i < nb_numa_nodes; i++) { mem_reg_property[0] = cpu_to_be64(mem_start); - mem_reg_property[1] = cpu_to_be64(node_mem[i]); + if (mem_start >= ram_size) { + node_size = 0; + } else { + node_size = node_mem[i]; + if (node_size > ram_size - mem_start) { + node_size = ram_size - mem_start; + } + } + mem_reg_property[1] = cpu_to_be64(node_size); associativity[3] = associativity[4] = cpu_to_be32(i); sprintf(mem_name, "memory@" TARGET_FMT_lx, mem_start); off = fdt_add_subnode(fdt, 0, mem_name); @@ -542,7 +592,7 @@ static int spapr_populate_memory(sPAPREnvironment *spapr, void *fdt) sizeof(mem_reg_property)))); _FDT((fdt_setprop(fdt, off, "ibm,associativity", associativity, sizeof(associativity)))); - mem_start += node_mem[i]; + mem_start += node_size; } return 0; @@ -553,7 +603,9 @@ static void spapr_finalize_fdt(sPAPREnvironment *spapr, hwaddr rtas_addr, hwaddr rtas_size) { - int ret; + int ret, i; + size_t cb = 0; + char *bootlist; void *fdt; sPAPRPHBState *phb; @@ -595,6 +647,21 @@ static void spapr_finalize_fdt(sPAPREnvironment *spapr, fprintf(stderr, "Couldn't finalize CPU device tree properties\n"); } + bootlist = get_boot_devices_list(&cb, true); + if (cb && bootlist) { + int offset = fdt_path_offset(fdt, "/chosen"); + if (offset < 0) { + exit(1); + } + for (i = 0; i < cb; i++) { + if (bootlist[i] == '\n') { + bootlist[i] = ' '; + } + + } + ret = fdt_setprop_string(fdt, offset, "qemu,boot-list", bootlist); + } + if (!spapr->has_graphics) { spapr_populate_chosen_stdout(fdt, spapr->vio_bus); } @@ -642,6 +709,7 @@ static void spapr_reset_htab(sPAPREnvironment *spapr) if (shift > 0) { /* Kernel handles htab, we don't need to allocate one */ spapr->htab_shift = shift; + kvmppc_kern_htab = true; } else { if (!spapr->htab) { /* Allocate an htab if we don't yet have one */ @@ -654,7 +722,8 @@ static void spapr_reset_htab(sPAPREnvironment *spapr) /* Update the RMA size if necessary */ if (spapr->vrma_adjust) { - spapr->rma_size = kvmppc_rma_size(ram_size, spapr->htab_shift); + hwaddr node0_size = (nb_numa_nodes > 1) ? node_mem[0] : ram_size; + spapr->rma_size = kvmppc_rma_size(node0_size, spapr->htab_shift); } } @@ -696,8 +765,21 @@ static void spapr_cpu_reset(void *opaque) env->spr[SPR_HIOR] = 0; env->external_htab = (uint8_t *)spapr->htab; + if (kvm_enabled() && !env->external_htab) { + /* + * HV KVM, set external_htab to 1 so our ppc_hash64_load_hpte* + * functions do the right thing. + */ + env->external_htab = (void *)1; + } env->htab_base = -1; - env->htab_mask = HTAB_SIZE(spapr) - 1; + /* + * htab_mask is the mask used to normalize hash value to PTEG index. + * htab_shift is log2 of hash table size. + * We have 8 hpte per group, and each hpte is 16 bytes. + * ie have 128 bytes per hpte entry. + */ + env->htab_mask = (1ULL << ((spapr)->htab_shift - 7)) - 1; env->spr[SPR_SDR1] = (target_ulong)(uintptr_t)spapr->htab | (spapr->htab_shift - 18); } @@ -705,18 +787,10 @@ static void spapr_cpu_reset(void *opaque) static void spapr_create_nvram(sPAPREnvironment *spapr) { DeviceState *dev = qdev_create(&spapr->vio_bus->bus, "spapr-nvram"); - const char *drivename = qemu_opt_get(qemu_get_machine_opts(), "nvram"); + DriveInfo *dinfo = drive_get(IF_PFLASH, 0, 0); - if (drivename) { - BlockDriverState *bs; - - bs = bdrv_find(drivename); - if (!bs) { - fprintf(stderr, "No such block device \"%s\" for nvram\n", - drivename); - exit(1); - } - qdev_prop_set_drive_nofail(dev, "drive", bs); + if (dinfo) { + qdev_prop_set_drive_nofail(dev, "drive", dinfo->bdrv); } qdev_init_nofail(dev); @@ -729,13 +803,15 @@ static int spapr_vga_init(PCIBus *pci_bus) { switch (vga_interface_type) { case VGA_NONE: + return false; + case VGA_DEVICE: + return true; case VGA_STD: return pci_vga_init(pci_bus) != NULL; default: fprintf(stderr, "This vga model is not supported," "currently it only supports -vga std\n"); exit(0); - break; } } @@ -789,7 +865,7 @@ static void htab_save_first_pass(QEMUFile *f, sPAPREnvironment *spapr, { int htabslots = HTAB_SIZE(spapr) / HASH_PTE_SIZE_64; int index = spapr->htab_save_index; - int64_t starttime = qemu_get_clock_ns(rt_clock); + int64_t starttime = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); assert(spapr->htab_first_pass); @@ -820,7 +896,7 @@ static void htab_save_first_pass(QEMUFile *f, sPAPREnvironment *spapr, qemu_put_buffer(f, HPTE(spapr->htab, chunkstart), HASH_PTE_SIZE_64 * n_valid); - if ((qemu_get_clock_ns(rt_clock) - starttime) > max_ns) { + if ((qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - starttime) > max_ns) { break; } } @@ -841,7 +917,7 @@ static int htab_save_later_pass(QEMUFile *f, sPAPREnvironment *spapr, int htabslots = HTAB_SIZE(spapr) / HASH_PTE_SIZE_64; int examined = 0, sent = 0; int index = spapr->htab_save_index; - int64_t starttime = qemu_get_clock_ns(rt_clock); + int64_t starttime = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); assert(!spapr->htab_first_pass); @@ -886,7 +962,7 @@ static int htab_save_later_pass(QEMUFile *f, sPAPREnvironment *spapr, HASH_PTE_SIZE_64 * n_valid); sent += index - chunkstart; - if (!final && (qemu_get_clock_ns(rt_clock) - starttime) > max_ns) { + if (!final && (qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - starttime) > max_ns) { break; } } @@ -1071,7 +1147,7 @@ static void ppc_spapr_init(QEMUMachineInitArgs *args) const char *kernel_filename = args->kernel_filename; const char *kernel_cmdline = args->kernel_cmdline; const char *initrd_filename = args->initrd_filename; - const char *boot_device = args->boot_device; + const char *boot_device = args->boot_order; PowerPCCPU *cpu; CPUPPCState *env; PCIHostState *phb; @@ -1079,9 +1155,11 @@ static void ppc_spapr_init(QEMUMachineInitArgs *args) MemoryRegion *sysmem = get_system_memory(); MemoryRegion *ram = g_new(MemoryRegion, 1); hwaddr rma_alloc_size; + hwaddr node0_size = (nb_numa_nodes > 1) ? node_mem[0] : ram_size; uint32_t initrd_base = 0; long kernel_size = 0, initrd_size = 0; long load_limit, rtas_limit, fw_size; + bool kernel_le = false; char *filename; msi_supported = true; @@ -1099,10 +1177,10 @@ static void ppc_spapr_init(QEMUMachineInitArgs *args) exit(1); } - if (rma_alloc_size && (rma_alloc_size < ram_size)) { + if (rma_alloc_size && (rma_alloc_size < node0_size)) { spapr->rma_size = rma_alloc_size; } else { - spapr->rma_size = ram_size; + spapr->rma_size = node0_size; /* With KVM, we don't actually know whether KVM supports an * unbounded RMA (PR KVM) or is limited by the hash table size @@ -1119,6 +1197,12 @@ static void ppc_spapr_init(QEMUMachineInitArgs *args) } } + if (spapr->rma_size > node0_size) { + fprintf(stderr, "Error: Numa node 0 has to span the RMA (%#08"HWADDR_PRIx")\n", + spapr->rma_size); + exit(1); + } + /* We place the device tree and RTAS just below either the top of the RMA, * or just below 2GB, whichever is lowere, so that it can be * processed with 32-bit real mode code if necessary */ @@ -1155,8 +1239,6 @@ static void ppc_spapr_init(QEMUMachineInitArgs *args) } env = &cpu->env; - xics_cpu_setup(spapr->icp, cpu); - /* Set time-base frequency to 512 MHz */ cpu_ppc_tb_init(env, TIMEBASE_FREQ); @@ -1170,6 +1252,8 @@ static void ppc_spapr_init(QEMUMachineInitArgs *args) kvmppc_set_papr(cpu); } + xics_cpu_setup(spapr->icp, cpu); + qemu_register_reset(spapr_cpu_reset, cpu); } @@ -1214,6 +1298,7 @@ static void ppc_spapr_init(QEMUMachineInitArgs *args) spapr_create_nvram(spapr); /* Set up PCI */ + spapr_pci_msi_init(spapr, SPAPR_PCI_MSI_WINDOW); spapr_pci_rtas_init(); phb = spapr_create_phb(spapr, 0); @@ -1260,14 +1345,15 @@ static void ppc_spapr_init(QEMUMachineInitArgs *args) kernel_size = load_elf(kernel_filename, translate_kernel_address, NULL, NULL, &lowaddr, NULL, 1, ELF_MACHINE, 0); - if (kernel_size < 0) { - kernel_size = load_image_targphys(kernel_filename, - KERNEL_LOAD_ADDR, - load_limit - KERNEL_LOAD_ADDR); + if (kernel_size == ELF_LOAD_WRONG_ENDIAN) { + kernel_size = load_elf(kernel_filename, + translate_kernel_address, NULL, + NULL, &lowaddr, NULL, 0, ELF_MACHINE, 0); + kernel_le = kernel_size > 0; } if (kernel_size < 0) { - fprintf(stderr, "qemu: could not load kernel '%s'\n", - kernel_filename); + fprintf(stderr, "qemu: error loading %s: %s\n", + kernel_filename, load_elf_strerror(kernel_size)); exit(1); } @@ -1308,14 +1394,31 @@ static void ppc_spapr_init(QEMUMachineInitArgs *args) &savevm_htab_handlers, spapr); /* Prepare the device tree */ - spapr->fdt_skel = spapr_create_fdt_skel(cpu_model, - initrd_base, initrd_size, - kernel_size, + spapr->fdt_skel = spapr_create_fdt_skel(initrd_base, initrd_size, + kernel_size, kernel_le, boot_device, kernel_cmdline, spapr->epow_irq); assert(spapr->fdt_skel != NULL); } +static int spapr_kvm_type(const char *vm_type) +{ + if (!vm_type) { + return 0; + } + + if (!strcmp(vm_type, "HV")) { + return 1; + } + + if (!strcmp(vm_type, "PR")) { + return 2; + } + + error_report("Unknown kvm-type specified '%s'", vm_type); + exit(1); +} + static QEMUMachine spapr_machine = { .name = "pseries", .desc = "pSeries Logical Partition (PAPR compliant)", @@ -1325,12 +1428,90 @@ static QEMUMachine spapr_machine = { .block_default_type = IF_SCSI, .max_cpus = MAX_CPUS, .no_parallel = 1, - .boot_order = NULL, + .default_boot_order = NULL, + .kvm_type = spapr_kvm_type, +}; + +/* + * Implementation of an interface to adjust firmware patch + * for the bootindex property handling. + */ +static char *spapr_get_fw_dev_path(FWPathProvider *p, BusState *bus, + DeviceState *dev) +{ +#define CAST(type, obj, name) \ + ((type *)object_dynamic_cast(OBJECT(obj), (name))) + SCSIDevice *d = CAST(SCSIDevice, dev, TYPE_SCSI_DEVICE); + sPAPRPHBState *phb = CAST(sPAPRPHBState, dev, TYPE_SPAPR_PCI_HOST_BRIDGE); + + if (d) { + void *spapr = CAST(void, bus->parent, "spapr-vscsi"); + VirtIOSCSI *virtio = CAST(VirtIOSCSI, bus->parent, TYPE_VIRTIO_SCSI); + USBDevice *usb = CAST(USBDevice, bus->parent, TYPE_USB_DEVICE); + + if (spapr) { + /* + * Replace "channel@0/disk@0,0" with "disk@8000000000000000": + * We use SRP luns of the form 8000 | (bus << 8) | (id << 5) | lun + * in the top 16 bits of the 64-bit LUN + */ + unsigned id = 0x8000 | (d->id << 8) | d->lun; + return g_strdup_printf("%s@%"PRIX64, qdev_fw_name(dev), + (uint64_t)id << 48); + } else if (virtio) { + /* + * We use SRP luns of the form 01000000 | (target << 8) | lun + * in the top 32 bits of the 64-bit LUN + * Note: the quote above is from SLOF and it is wrong, + * the actual binding is: + * swap 0100 or 10 << or 20 << ( target lun-id -- srplun ) + */ + unsigned id = 0x1000000 | (d->id << 16) | d->lun; + return g_strdup_printf("%s@%"PRIX64, qdev_fw_name(dev), + (uint64_t)id << 32); + } else if (usb) { + /* + * We use SRP luns of the form 01000000 | (usb-port << 16) | lun + * in the top 32 bits of the 64-bit LUN + */ + unsigned usb_port = atoi(usb->port->path); + unsigned id = 0x1000000 | (usb_port << 16) | d->lun; + return g_strdup_printf("%s@%"PRIX64, qdev_fw_name(dev), + (uint64_t)id << 32); + } + } + + if (phb) { + /* Replace "pci" with "pci@800000020000000" */ + return g_strdup_printf("pci@%"PRIX64, phb->buid); + } + + return NULL; +} + +static void spapr_machine_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + FWPathProviderClass *fwc = FW_PATH_PROVIDER_CLASS(oc); + + mc->qemu_machine = data; + fwc->get_dev_path = spapr_get_fw_dev_path; +} + +static const TypeInfo spapr_machine_info = { + .name = TYPE_SPAPR_MACHINE, + .parent = TYPE_MACHINE, + .class_init = spapr_machine_class_init, + .class_data = &spapr_machine, + .interfaces = (InterfaceInfo[]) { + { TYPE_FW_PATH_PROVIDER }, + { } + }, }; -static void spapr_machine_init(void) +static void spapr_machine_register_types(void) { - qemu_register_machine(&spapr_machine); + type_register_static(&spapr_machine_info); } -machine_init(spapr_machine_init); +type_init(spapr_machine_register_types) diff --git a/hw/ppc/spapr_events.c b/hw/ppc/spapr_events.c index a69390e54..16fa49e88 100644 --- a/hw/ppc/spapr_events.c +++ b/hw/ppc/spapr_events.c @@ -286,7 +286,7 @@ static void check_exception(PowerPCCPU *cpu, sPAPREnvironment *spapr, uint64_t xinfo; if ((nargs < 6) || (nargs > 7) || nret != 1) { - rtas_st(rets, 0, -3); + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); return; } @@ -306,9 +306,9 @@ static void check_exception(PowerPCCPU *cpu, sPAPREnvironment *spapr, cpu_physical_memory_write(buf, pending_epow, len); g_free(pending_epow); pending_epow = NULL; - rtas_st(rets, 0, 0); + rtas_st(rets, 0, RTAS_OUT_SUCCESS); } else { - rtas_st(rets, 0, 1); + rtas_st(rets, 0, RTAS_OUT_NO_ERRORS_FOUND); } } diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c index 67d6cd91d..0bae0535e 100644 --- a/hw/ppc/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -4,6 +4,36 @@ #include "hw/ppc/spapr.h" #include "mmu-hash64.h" +struct SPRSyncState { + CPUState *cs; + int spr; + target_ulong value; + target_ulong mask; +}; + +static void do_spr_sync(void *arg) +{ + struct SPRSyncState *s = arg; + PowerPCCPU *cpu = POWERPC_CPU(s->cs); + CPUPPCState *env = &cpu->env; + + cpu_synchronize_state(s->cs); + env->spr[s->spr] &= ~s->mask; + env->spr[s->spr] |= s->value; +} + +static void set_spr(CPUState *cs, int spr, target_ulong value, + target_ulong mask) +{ + struct SPRSyncState s = { + .cs = cs, + .spr = spr, + .value = value, + .mask = mask + }; + run_on_cpu(cs, do_spr_sync, &s); +} + static target_ulong compute_tlbie_rb(target_ulong v, target_ulong r, target_ulong pte_index) { @@ -40,6 +70,17 @@ static target_ulong compute_tlbie_rb(target_ulong v, target_ulong r, return rb; } +static inline bool valid_pte_index(CPUPPCState *env, target_ulong pte_index) +{ + /* + * hash value/pteg group index is normalized by htab_mask + */ + if (((pte_index & ~7ULL) / HPTES_PER_GROUP) & ~env->htab_mask) { + return false; + } + return true; +} + static target_ulong h_enter(PowerPCCPU *cpu, sPAPREnvironment *spapr, target_ulong opcode, target_ulong *args) { @@ -50,8 +91,8 @@ static target_ulong h_enter(PowerPCCPU *cpu, sPAPREnvironment *spapr, target_ulong ptel = args[3]; target_ulong page_shift = 12; target_ulong raddr; - target_ulong i; - hwaddr hpte; + target_ulong index; + uint64_t token; /* only handle 4k and 16M pages for now */ if (pteh & HPTE64_V_LARGE) { @@ -91,33 +132,36 @@ static target_ulong h_enter(PowerPCCPU *cpu, sPAPREnvironment *spapr, pteh &= ~0x60ULL; - if ((pte_index * HASH_PTE_SIZE_64) & ~env->htab_mask) { + if (!valid_pte_index(env, pte_index)) { return H_PARAMETER; } + + index = 0; if (likely((flags & H_EXACT) == 0)) { pte_index &= ~7ULL; - hpte = pte_index * HASH_PTE_SIZE_64; - for (i = 0; ; ++i) { - if (i == 8) { - return H_PTEG_FULL; - } - if ((ppc_hash64_load_hpte0(env, hpte) & HPTE64_V_VALID) == 0) { + token = ppc_hash64_start_access(cpu, pte_index); + for (; index < 8; index++) { + if ((ppc_hash64_load_hpte0(env, token, index) & HPTE64_V_VALID) == 0) { break; } - hpte += HASH_PTE_SIZE_64; + } + ppc_hash64_stop_access(token); + if (index == 8) { + return H_PTEG_FULL; } } else { - i = 0; - hpte = pte_index * HASH_PTE_SIZE_64; - if (ppc_hash64_load_hpte0(env, hpte) & HPTE64_V_VALID) { + token = ppc_hash64_start_access(cpu, pte_index); + if (ppc_hash64_load_hpte0(env, token, 0) & HPTE64_V_VALID) { + ppc_hash64_stop_access(token); return H_PTEG_FULL; } + ppc_hash64_stop_access(token); } - ppc_hash64_store_hpte1(env, hpte, ptel); - /* eieio(); FIXME: need some sort of barrier for smp? */ - ppc_hash64_store_hpte0(env, hpte, pteh | HPTE64_V_HPTE_DIRTY); - args[0] = pte_index + i; + ppc_hash64_store_hpte(env, pte_index + index, + pteh | HPTE64_V_HPTE_DIRTY, ptel); + + args[0] = pte_index + index; return H_SUCCESS; } @@ -133,17 +177,17 @@ static RemoveResult remove_hpte(CPUPPCState *env, target_ulong ptex, target_ulong flags, target_ulong *vp, target_ulong *rp) { - hwaddr hpte; + uint64_t token; target_ulong v, r, rb; - if ((ptex * HASH_PTE_SIZE_64) & ~env->htab_mask) { + if (!valid_pte_index(env, ptex)) { return REMOVE_PARM; } - hpte = ptex * HASH_PTE_SIZE_64; - - v = ppc_hash64_load_hpte0(env, hpte); - r = ppc_hash64_load_hpte1(env, hpte); + token = ppc_hash64_start_access(ppc_env_get_cpu(env), ptex); + v = ppc_hash64_load_hpte0(env, token, 0); + r = ppc_hash64_load_hpte1(env, token, 0); + ppc_hash64_stop_access(token); if ((v & HPTE64_V_VALID) == 0 || ((flags & H_AVPN) && (v & ~0x7fULL) != avpn) || @@ -152,7 +196,7 @@ static RemoveResult remove_hpte(CPUPPCState *env, target_ulong ptex, } *vp = v; *rp = r; - ppc_hash64_store_hpte0(env, hpte, HPTE64_V_HPTE_DIRTY); + ppc_hash64_store_hpte(env, ptex, HPTE64_V_HPTE_DIRTY, 0); rb = compute_tlbie_rb(v, r, ptex); ppc_tlb_invalidate_one(env, rb); return REMOVE_SUCCESS; @@ -259,17 +303,17 @@ static target_ulong h_protect(PowerPCCPU *cpu, sPAPREnvironment *spapr, target_ulong flags = args[0]; target_ulong pte_index = args[1]; target_ulong avpn = args[2]; - hwaddr hpte; + uint64_t token; target_ulong v, r, rb; - if ((pte_index * HASH_PTE_SIZE_64) & ~env->htab_mask) { + if (!valid_pte_index(env, pte_index)) { return H_PARAMETER; } - hpte = pte_index * HASH_PTE_SIZE_64; - - v = ppc_hash64_load_hpte0(env, hpte); - r = ppc_hash64_load_hpte1(env, hpte); + token = ppc_hash64_start_access(cpu, pte_index); + v = ppc_hash64_load_hpte0(env, token, 0); + r = ppc_hash64_load_hpte1(env, token, 0); + ppc_hash64_stop_access(token); if ((v & HPTE64_V_VALID) == 0 || ((flags & H_AVPN) && (v & ~0x7fULL) != avpn)) { @@ -282,11 +326,11 @@ static target_ulong h_protect(PowerPCCPU *cpu, sPAPREnvironment *spapr, r |= (flags << 48) & HPTE64_R_KEY_HI; r |= flags & (HPTE64_R_PP | HPTE64_R_N | HPTE64_R_KEY_LO); rb = compute_tlbie_rb(v, r, pte_index); - ppc_hash64_store_hpte0(env, hpte, (v & ~HPTE64_V_VALID) | HPTE64_V_HPTE_DIRTY); + ppc_hash64_store_hpte(env, pte_index, + (v & ~HPTE64_V_VALID) | HPTE64_V_HPTE_DIRTY, 0); ppc_tlb_invalidate_one(env, rb); - ppc_hash64_store_hpte1(env, hpte, r); /* Don't need a memory barrier, due to qemu's global lock */ - ppc_hash64_store_hpte0(env, hpte, v | HPTE64_V_HPTE_DIRTY); + ppc_hash64_store_hpte(env, pte_index, v | HPTE64_V_HPTE_DIRTY, r); return H_SUCCESS; } @@ -299,7 +343,7 @@ static target_ulong h_read(PowerPCCPU *cpu, sPAPREnvironment *spapr, uint8_t *hpte; int i, ridx, n_entries = 1; - if ((pte_index * HASH_PTE_SIZE_64) & ~env->htab_mask) { + if (!valid_pte_index(env, pte_index)) { return H_PARAMETER; } @@ -341,6 +385,7 @@ static target_ulong h_set_dabr(PowerPCCPU *cpu, sPAPREnvironment *spapr, static target_ulong register_vpa(CPUPPCState *env, target_ulong vpa) { + CPUState *cs = CPU(ppc_env_get_cpu(env)); uint16_t size; uint8_t tmp; @@ -354,7 +399,7 @@ static target_ulong register_vpa(CPUPPCState *env, target_ulong vpa) } /* FIXME: bounds check the address */ - size = lduw_be_phys(vpa + 0x4); + size = lduw_be_phys(cs->as, vpa + 0x4); if (size < VPA_MIN_SIZE) { return H_PARAMETER; @@ -367,9 +412,9 @@ static target_ulong register_vpa(CPUPPCState *env, target_ulong vpa) env->vpa_addr = vpa; - tmp = ldub_phys(env->vpa_addr + VPA_SHARED_PROC_OFFSET); + tmp = ldub_phys(cs->as, env->vpa_addr + VPA_SHARED_PROC_OFFSET); tmp |= VPA_SHARED_PROC_VAL; - stb_phys(env->vpa_addr + VPA_SHARED_PROC_OFFSET, tmp); + stb_phys(cs->as, env->vpa_addr + VPA_SHARED_PROC_OFFSET, tmp); return H_SUCCESS; } @@ -390,6 +435,7 @@ static target_ulong deregister_vpa(CPUPPCState *env, target_ulong vpa) static target_ulong register_slb_shadow(CPUPPCState *env, target_ulong addr) { + CPUState *cs = CPU(ppc_env_get_cpu(env)); uint32_t size; if (addr == 0) { @@ -397,7 +443,7 @@ static target_ulong register_slb_shadow(CPUPPCState *env, target_ulong addr) return H_HARDWARE; } - size = ldl_be_phys(addr + 0x4); + size = ldl_be_phys(cs->as, addr + 0x4); if (size < 0x8) { return H_PARAMETER; } @@ -425,6 +471,7 @@ static target_ulong deregister_slb_shadow(CPUPPCState *env, target_ulong addr) static target_ulong register_dtl(CPUPPCState *env, target_ulong addr) { + CPUState *cs = CPU(ppc_env_get_cpu(env)); uint32_t size; if (addr == 0) { @@ -432,7 +479,7 @@ static target_ulong register_dtl(CPUPPCState *env, target_ulong addr) return H_HARDWARE; } - size = ldl_be_phys(addr + 0x4); + size = ldl_be_phys(cs->as, addr + 0x4); if (size < 48) { return H_PARAMETER; @@ -464,13 +511,13 @@ static target_ulong h_register_vpa(PowerPCCPU *cpu, sPAPREnvironment *spapr, target_ulong vpa = args[2]; target_ulong ret = H_PARAMETER; CPUPPCState *tenv; - CPUState *tcpu; + PowerPCCPU *tcpu; - tcpu = qemu_get_cpu(procno); + tcpu = ppc_get_vcpu_by_dt_id(procno); if (!tcpu) { return H_PARAMETER; } - tenv = tcpu->env_ptr; + tenv = &tcpu->env; switch (flags) { case FLAGS_REGISTER_VPA: @@ -511,7 +558,7 @@ static target_ulong h_cede(PowerPCCPU *cpu, sPAPREnvironment *spapr, hreg_compute_hflags(env); if (!cpu_has_work(cs)) { cs->halted = 1; - env->exception_index = EXCP_HLT; + cs->exception_index = EXCP_HLT; cs->exit_request = 1; } return H_SUCCESS; @@ -521,9 +568,9 @@ static target_ulong h_rtas(PowerPCCPU *cpu, sPAPREnvironment *spapr, target_ulong opcode, target_ulong *args) { target_ulong rtas_r3 = args[0]; - uint32_t token = ldl_be_phys(rtas_r3); - uint32_t nargs = ldl_be_phys(rtas_r3 + 4); - uint32_t nret = ldl_be_phys(rtas_r3 + 8); + uint32_t token = rtas_ld(rtas_r3, 0); + uint32_t nargs = rtas_ld(rtas_r3, 1); + uint32_t nret = rtas_ld(rtas_r3, 2); return spapr_rtas_call(cpu, spapr, token, nargs, rtas_r3 + 12, nret, rtas_r3 + 12 + 4*nargs); @@ -532,21 +579,22 @@ static target_ulong h_rtas(PowerPCCPU *cpu, sPAPREnvironment *spapr, static target_ulong h_logical_load(PowerPCCPU *cpu, sPAPREnvironment *spapr, target_ulong opcode, target_ulong *args) { + CPUState *cs = CPU(cpu); target_ulong size = args[0]; target_ulong addr = args[1]; switch (size) { case 1: - args[0] = ldub_phys(addr); + args[0] = ldub_phys(cs->as, addr); return H_SUCCESS; case 2: - args[0] = lduw_phys(addr); + args[0] = lduw_phys(cs->as, addr); return H_SUCCESS; case 4: - args[0] = ldl_phys(addr); + args[0] = ldl_phys(cs->as, addr); return H_SUCCESS; case 8: - args[0] = ldq_phys(addr); + args[0] = ldq_phys(cs->as, addr); return H_SUCCESS; } return H_PARAMETER; @@ -555,22 +603,24 @@ static target_ulong h_logical_load(PowerPCCPU *cpu, sPAPREnvironment *spapr, static target_ulong h_logical_store(PowerPCCPU *cpu, sPAPREnvironment *spapr, target_ulong opcode, target_ulong *args) { + CPUState *cs = CPU(cpu); + target_ulong size = args[0]; target_ulong addr = args[1]; target_ulong val = args[2]; switch (size) { case 1: - stb_phys(addr, val); + stb_phys(cs->as, addr, val); return H_SUCCESS; case 2: - stw_phys(addr, val); + stw_phys(cs->as, addr, val); return H_SUCCESS; case 4: - stl_phys(addr, val); + stl_phys(cs->as, addr, val); return H_SUCCESS; case 8: - stq_phys(addr, val); + stq_phys(cs->as, addr, val); return H_SUCCESS; } return H_PARAMETER; @@ -579,6 +629,8 @@ static target_ulong h_logical_store(PowerPCCPU *cpu, sPAPREnvironment *spapr, static target_ulong h_logical_memop(PowerPCCPU *cpu, sPAPREnvironment *spapr, target_ulong opcode, target_ulong *args) { + CPUState *cs = CPU(cpu); + target_ulong dst = args[0]; /* Destination address */ target_ulong src = args[1]; /* Source address */ target_ulong esize = args[2]; /* Element size (0=1,1=2,2=4,3=8) */ @@ -605,16 +657,16 @@ static target_ulong h_logical_memop(PowerPCCPU *cpu, sPAPREnvironment *spapr, while (count--) { switch (esize) { case 0: - tmp = ldub_phys(src); + tmp = ldub_phys(cs->as, src); break; case 1: - tmp = lduw_phys(src); + tmp = lduw_phys(cs->as, src); break; case 2: - tmp = ldl_phys(src); + tmp = ldl_phys(cs->as, src); break; case 3: - tmp = ldq_phys(src); + tmp = ldq_phys(cs->as, src); break; default: return H_PARAMETER; @@ -624,16 +676,16 @@ static target_ulong h_logical_memop(PowerPCCPU *cpu, sPAPREnvironment *spapr, } switch (esize) { case 0: - stb_phys(dst, tmp); + stb_phys(cs->as, dst, tmp); break; case 1: - stw_phys(dst, tmp); + stw_phys(cs->as, dst, tmp); break; case 2: - stl_phys(dst, tmp); + stl_phys(cs->as, dst, tmp); break; case 3: - stq_phys(dst, tmp); + stq_phys(cs->as, dst, tmp); break; } dst = dst + step; @@ -657,6 +709,49 @@ static target_ulong h_logical_dcbf(PowerPCCPU *cpu, sPAPREnvironment *spapr, return H_SUCCESS; } +static target_ulong h_set_mode(PowerPCCPU *cpu, sPAPREnvironment *spapr, + target_ulong opcode, target_ulong *args) +{ + CPUState *cs; + target_ulong mflags = args[0]; + target_ulong resource = args[1]; + target_ulong value1 = args[2]; + target_ulong value2 = args[3]; + target_ulong ret = H_P2; + + if (resource == H_SET_MODE_RESOURCE_LE) { + if (value1) { + ret = H_P3; + goto out; + } + if (value2) { + ret = H_P4; + goto out; + } + switch (mflags) { + case H_SET_MODE_ENDIAN_BIG: + CPU_FOREACH(cs) { + set_spr(cs, SPR_LPCR, 0, LPCR_ILE); + } + ret = H_SUCCESS; + break; + + case H_SET_MODE_ENDIAN_LITTLE: + CPU_FOREACH(cs) { + set_spr(cs, SPR_LPCR, LPCR_ILE, LPCR_ILE); + } + ret = H_SUCCESS; + break; + + default: + ret = H_UNSUPPORTED_FLAG; + } + } + +out: + return ret; +} + static spapr_hcall_fn papr_hypercall_table[(MAX_HCALL_OPCODE / 4) + 1]; static spapr_hcall_fn kvmppc_hypercall_table[KVMPPC_HCALL_MAX - KVMPPC_HCALL_BASE + 1]; @@ -734,6 +829,8 @@ static void hypercall_register_types(void) /* qemu/KVM-PPC specific hcalls */ spapr_register_hypercall(KVMPPC_H_RTAS, h_rtas); + + spapr_register_hypercall(H_SET_MODE, h_set_mode); } type_init(hypercall_register_types) diff --git a/hw/ppc/spapr_iommu.c b/hw/ppc/spapr_iommu.c index 3d4a1fcfe..d9fe94681 100644 --- a/hw/ppc/spapr_iommu.c +++ b/hw/ppc/spapr_iommu.c @@ -22,13 +22,12 @@ #include "kvm_ppc.h" #include "sysemu/dma.h" #include "exec/address-spaces.h" +#include "trace.h" #include "hw/ppc/spapr.h" #include <libfdt.h> -/* #define DEBUG_TCE */ - enum sPAPRTCEAccess { SPAPR_TCE_FAULT = 0, SPAPR_TCE_RO = 1, @@ -61,44 +60,28 @@ static IOMMUTLBEntry spapr_tce_translate_iommu(MemoryRegion *iommu, hwaddr addr) { sPAPRTCETable *tcet = container_of(iommu, sPAPRTCETable, iommu); uint64_t tce; - -#ifdef DEBUG_TCE - fprintf(stderr, "spapr_tce_translate liobn=0x%" PRIx32 " addr=0x" - DMA_ADDR_FMT "\n", tcet->liobn, addr); -#endif + IOMMUTLBEntry ret = { + .target_as = &address_space_memory, + .iova = 0, + .translated_addr = 0, + .addr_mask = ~(hwaddr)0, + .perm = IOMMU_NONE, + }; if (tcet->bypass) { - return (IOMMUTLBEntry) { - .target_as = &address_space_memory, - .iova = 0, - .translated_addr = 0, - .addr_mask = ~(hwaddr)0, - .perm = IOMMU_RW, - }; + ret.perm = IOMMU_RW; + } else if (addr < tcet->window_size) { + /* Check if we are in bound */ + tce = tcet->table[addr >> SPAPR_TCE_PAGE_SHIFT]; + ret.iova = addr & ~SPAPR_TCE_PAGE_MASK; + ret.translated_addr = tce & ~SPAPR_TCE_PAGE_MASK; + ret.addr_mask = SPAPR_TCE_PAGE_MASK; + ret.perm = tce; } + trace_spapr_iommu_xlate(tcet->liobn, addr, ret.iova, ret.perm, + ret.addr_mask); - /* Check if we are in bound */ - if (addr >= tcet->window_size) { -#ifdef DEBUG_TCE - fprintf(stderr, "spapr_tce_translate out of bounds\n"); -#endif - return (IOMMUTLBEntry) { .perm = IOMMU_NONE }; - } - - tce = tcet->table[addr >> SPAPR_TCE_PAGE_SHIFT]; - -#ifdef DEBUG_TCE - fprintf(stderr, " -> *paddr=0x%llx, *len=0x%llx\n", - (tce & ~SPAPR_TCE_PAGE_MASK), SPAPR_TCE_PAGE_MASK + 1); -#endif - - return (IOMMUTLBEntry) { - .target_as = &address_space_memory, - .iova = addr & ~SPAPR_TCE_PAGE_MASK, - .translated_addr = tce & ~SPAPR_TCE_PAGE_MASK, - .addr_mask = SPAPR_TCE_PAGE_MASK, - .perm = tce, - }; + return ret; } static int spapr_tce_table_pre_load(void *opaque) @@ -150,10 +133,7 @@ static int spapr_tce_table_realize(DeviceState *dev) } tcet->nb_table = tcet->window_size >> SPAPR_TCE_PAGE_SHIFT; -#ifdef DEBUG_TCE - fprintf(stderr, "spapr_iommu: New TCE table @ %p, liobn=0x%x, " - "table @ %p, fd=%d\n", tcet, liobn, tcet->table, tcet->fd); -#endif + trace_spapr_iommu_new_table(tcet->liobn, tcet, tcet->table, tcet->fd); memory_region_init_iommu(&tcet->iommu, OBJECT(dev), &spapr_iommu_ops, "iommu-spapr", UINT64_MAX); @@ -250,20 +230,53 @@ static target_ulong h_put_tce(PowerPCCPU *cpu, sPAPREnvironment *spapr, target_ulong liobn = args[0]; target_ulong ioba = args[1]; target_ulong tce = args[2]; + target_ulong ret = H_PARAMETER; + sPAPRTCETable *tcet = spapr_tce_find_by_liobn(liobn); + + ioba &= ~(SPAPR_TCE_PAGE_SIZE - 1); + + if (tcet) { + ret = put_tce_emu(tcet, ioba, tce); + } + trace_spapr_iommu_put(liobn, ioba, tce, ret); + + return ret; +} + +static target_ulong get_tce_emu(sPAPRTCETable *tcet, target_ulong ioba, + target_ulong *tce) +{ + if (ioba >= tcet->window_size) { + hcall_dprintf("spapr_iommu_get_tce on out-of-bounds IOBA 0x" + TARGET_FMT_lx "\n", ioba); + return H_PARAMETER; + } + + *tce = tcet->table[ioba >> SPAPR_TCE_PAGE_SHIFT]; + + return H_SUCCESS; +} + +static target_ulong h_get_tce(PowerPCCPU *cpu, sPAPREnvironment *spapr, + target_ulong opcode, target_ulong *args) +{ + target_ulong liobn = args[0]; + target_ulong ioba = args[1]; + target_ulong tce = 0; + target_ulong ret = H_PARAMETER; sPAPRTCETable *tcet = spapr_tce_find_by_liobn(liobn); ioba &= ~(SPAPR_TCE_PAGE_SIZE - 1); if (tcet) { - return put_tce_emu(tcet, ioba, tce); + ret = get_tce_emu(tcet, ioba, &tce); + if (!ret) { + args[0] = tce; + } } -#ifdef DEBUG_TCE - fprintf(stderr, "%s on liobn=" TARGET_FMT_lx /*%s*/ - " ioba 0x" TARGET_FMT_lx " TCE 0x" TARGET_FMT_lx "\n", - __func__, liobn, /*dev->qdev.id, */ioba, tce); -#endif + trace_spapr_iommu_get(liobn, ioba, ret, tce); - return H_PARAMETER; + return ret; } int spapr_dma_dt(void *fdt, int node_off, const char *propname, @@ -318,6 +331,7 @@ static void spapr_tce_table_class_init(ObjectClass *klass, void *data) /* hcall-tce */ spapr_register_hypercall(H_PUT_TCE, h_put_tce); + spapr_register_hypercall(H_GET_TCE, h_get_tce); } static TypeInfo spapr_tce_table_info = { diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c index 1ca35a0a7..cbef09593 100644 --- a/hw/ppc/spapr_pci.c +++ b/hw/ppc/spapr_pci.c @@ -32,6 +32,7 @@ #include "exec/address-spaces.h" #include <libfdt.h> #include "trace.h" +#include "qemu/error-report.h" #include "hw/pci/pci_bus.h" @@ -65,22 +66,14 @@ static PCIDevice *find_dev(sPAPREnvironment *spapr, uint64_t buid, { sPAPRPHBState *sphb = find_phb(spapr, buid); PCIHostState *phb = PCI_HOST_BRIDGE(sphb); - BusState *bus = BUS(phb->bus); - BusChild *kid; + int bus_num = (config_addr >> 16) & 0xFF; int devfn = (config_addr >> 8) & 0xFF; if (!phb) { return NULL; } - QTAILQ_FOREACH(kid, &bus->children, sibling) { - PCIDevice *dev = (PCIDevice *)kid->child; - if (dev->devfn == devfn) { - return dev; - } - } - - return NULL; + return pci_find_device(phb->bus, bus_num, devfn); } static uint32_t rtas_pci_cfgaddr(uint32_t arg) @@ -98,7 +91,7 @@ static void finish_read_pci_config(sPAPREnvironment *spapr, uint64_t buid, if ((size != 1) && (size != 2) && (size != 4)) { /* access must be 1, 2 or 4 bytes */ - rtas_st(rets, 0, -1); + rtas_st(rets, 0, RTAS_OUT_HW_ERROR); return; } @@ -108,14 +101,14 @@ static void finish_read_pci_config(sPAPREnvironment *spapr, uint64_t buid, if (!pci_dev || (addr % size) || (addr >= pci_config_size(pci_dev))) { /* Access must be to a valid device, within bounds and * naturally aligned */ - rtas_st(rets, 0, -1); + rtas_st(rets, 0, RTAS_OUT_HW_ERROR); return; } val = pci_host_config_read_common(pci_dev, addr, pci_config_size(pci_dev), size); - rtas_st(rets, 0, 0); + rtas_st(rets, 0, RTAS_OUT_SUCCESS); rtas_st(rets, 1, val); } @@ -128,7 +121,7 @@ static void rtas_ibm_read_pci_config(PowerPCCPU *cpu, sPAPREnvironment *spapr, uint32_t size, addr; if ((nargs != 4) || (nret != 2)) { - rtas_st(rets, 0, -1); + rtas_st(rets, 0, RTAS_OUT_HW_ERROR); return; } @@ -147,7 +140,7 @@ static void rtas_read_pci_config(PowerPCCPU *cpu, sPAPREnvironment *spapr, uint32_t size, addr; if ((nargs != 2) || (nret != 2)) { - rtas_st(rets, 0, -1); + rtas_st(rets, 0, RTAS_OUT_HW_ERROR); return; } @@ -165,7 +158,7 @@ static void finish_write_pci_config(sPAPREnvironment *spapr, uint64_t buid, if ((size != 1) && (size != 2) && (size != 4)) { /* access must be 1, 2 or 4 bytes */ - rtas_st(rets, 0, -1); + rtas_st(rets, 0, RTAS_OUT_HW_ERROR); return; } @@ -175,14 +168,14 @@ static void finish_write_pci_config(sPAPREnvironment *spapr, uint64_t buid, if (!pci_dev || (addr % size) || (addr >= pci_config_size(pci_dev))) { /* Access must be to a valid device, within bounds and * naturally aligned */ - rtas_st(rets, 0, -1); + rtas_st(rets, 0, RTAS_OUT_HW_ERROR); return; } pci_host_config_write_common(pci_dev, addr, pci_config_size(pci_dev), val, size); - rtas_st(rets, 0, 0); + rtas_st(rets, 0, RTAS_OUT_SUCCESS); } static void rtas_ibm_write_pci_config(PowerPCCPU *cpu, sPAPREnvironment *spapr, @@ -194,7 +187,7 @@ static void rtas_ibm_write_pci_config(PowerPCCPU *cpu, sPAPREnvironment *spapr, uint32_t val, size, addr; if ((nargs != 5) || (nret != 1)) { - rtas_st(rets, 0, -1); + rtas_st(rets, 0, RTAS_OUT_HW_ERROR); return; } @@ -214,7 +207,7 @@ static void rtas_write_pci_config(PowerPCCPU *cpu, sPAPREnvironment *spapr, uint32_t val, size, addr; if ((nargs != 3) || (nret != 1)) { - rtas_st(rets, 0, -1); + rtas_st(rets, 0, RTAS_OUT_HW_ERROR); return; } @@ -258,11 +251,11 @@ static int spapr_msicfg_find(sPAPRPHBState *phb, uint32_t config_addr, * This is required for msi_notify()/msix_notify() which * will write at the addresses via spapr_msi_write(). */ -static void spapr_msi_setmsg(PCIDevice *pdev, hwaddr addr, - bool msix, unsigned req_num) +static void spapr_msi_setmsg(PCIDevice *pdev, hwaddr addr, bool msix, + unsigned first_irq, unsigned req_num) { unsigned i; - MSIMessage msg = { .address = addr, .data = 0 }; + MSIMessage msg = { .address = addr, .data = first_irq }; if (!msix) { msi_set_message(pdev, msg); @@ -270,8 +263,7 @@ static void spapr_msi_setmsg(PCIDevice *pdev, hwaddr addr, return; } - for (i = 0; i < req_num; ++i) { - msg.address = addr | (i << 2); + for (i = 0; i < req_num; ++i, ++msg.data) { msix_set_message(pdev, i, msg); trace_spapr_pci_msi_setup(pdev->name, i, msg.address); } @@ -301,8 +293,8 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPREnvironment *spapr, ret_intr_type = RTAS_TYPE_MSIX; break; default: - fprintf(stderr, "rtas_ibm_change_msi(%u) is not implemented\n", func); - rtas_st(rets, 0, -3); /* Parameter error */ + error_report("rtas_ibm_change_msi(%u) is not implemented", func); + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); return; } @@ -312,7 +304,7 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPREnvironment *spapr, pdev = find_dev(spapr, buid, config_addr); } if (!phb || !pdev) { - rtas_st(rets, 0, -3); /* Parameter error */ + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); return; } @@ -321,11 +313,11 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPREnvironment *spapr, ndev = spapr_msicfg_find(phb, config_addr, false); if (ndev < 0) { trace_spapr_pci_msi("MSI has not been enabled", -1, config_addr); - rtas_st(rets, 0, -1); /* Hardware error */ + rtas_st(rets, 0, RTAS_OUT_HW_ERROR); return; } trace_spapr_pci_msi("Released MSIs", ndev, config_addr); - rtas_st(rets, 0, 0); + rtas_st(rets, 0, RTAS_OUT_SUCCESS); rtas_st(rets, 1, 0); return; } @@ -335,8 +327,8 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPREnvironment *spapr, /* Find a device number in the map to add or reuse the existing one */ ndev = spapr_msicfg_find(phb, config_addr, true); if (ndev >= SPAPR_MSIX_MAX_DEVS || ndev < 0) { - fprintf(stderr, "No free entry for a new MSI device\n"); - rtas_st(rets, 0, -1); /* Hardware error */ + error_report("No free entry for a new MSI device"); + rtas_st(rets, 0, RTAS_OUT_HW_ERROR); return; } trace_spapr_pci_msi("Configuring MSI", ndev, config_addr); @@ -344,17 +336,18 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPREnvironment *spapr, /* Check if there is an old config and MSI number has not changed */ if (phb->msi_table[ndev].nvec && (req_num != phb->msi_table[ndev].nvec)) { /* Unexpected behaviour */ - fprintf(stderr, "Cannot reuse MSI config for device#%d", ndev); - rtas_st(rets, 0, -1); /* Hardware error */ + error_report("Cannot reuse MSI config for device#%d", ndev); + rtas_st(rets, 0, RTAS_OUT_HW_ERROR); return; } /* There is no cached config, allocate MSIs */ if (!phb->msi_table[ndev].nvec) { - irq = spapr_allocate_irq_block(req_num, false); + irq = spapr_allocate_irq_block(req_num, false, + ret_intr_type == RTAS_TYPE_MSI); if (irq < 0) { - fprintf(stderr, "Cannot allocate MSIs for device#%d", ndev); - rtas_st(rets, 0, -1); /* Hardware error */ + error_report("Cannot allocate MSIs for device#%d", ndev); + rtas_st(rets, 0, RTAS_OUT_HW_ERROR); return; } phb->msi_table[ndev].irq = irq; @@ -363,10 +356,10 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPREnvironment *spapr, } /* Setup MSI/MSIX vectors in the device (via cfgspace or MSIX BAR) */ - spapr_msi_setmsg(pdev, phb->msi_win_addr | (ndev << 16), - ret_intr_type == RTAS_TYPE_MSIX, req_num); + spapr_msi_setmsg(pdev, spapr->msi_win_addr, ret_intr_type == RTAS_TYPE_MSIX, + phb->msi_table[ndev].irq, req_num); - rtas_st(rets, 0, 0); + rtas_st(rets, 0, RTAS_OUT_SUCCESS); rtas_st(rets, 1, req_num); rtas_st(rets, 2, ++seq_num); rtas_st(rets, 3, ret_intr_type); @@ -391,7 +384,7 @@ static void rtas_ibm_query_interrupt_source_number(PowerPCCPU *cpu, /* Fins sPAPRPHBState */ phb = find_phb(spapr, buid); if (!phb) { - rtas_st(rets, 0, -3); /* Parameter error */ + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); return; } @@ -399,7 +392,7 @@ static void rtas_ibm_query_interrupt_source_number(PowerPCCPU *cpu, ndev = spapr_msicfg_find(phb, config_addr, false); if (ndev < 0) { trace_spapr_pci_msi("MSI has not been enabled", -1, config_addr); - rtas_st(rets, 0, -1); /* Hardware error */ + rtas_st(rets, 0, RTAS_OUT_HW_ERROR); return; } @@ -407,7 +400,7 @@ static void rtas_ibm_query_interrupt_source_number(PowerPCCPU *cpu, trace_spapr_pci_rtas_ibm_query_interrupt_source_number(ioa_intr_num, intr_src_num); - rtas_st(rets, 0, 0); + rtas_st(rets, 0, RTAS_OUT_SUCCESS); rtas_st(rets, 1, intr_src_num); rtas_st(rets, 2, 1);/* 0 == level; 1 == edge */ } @@ -440,6 +433,17 @@ static void pci_spapr_set_irq(void *opaque, int irq_num, int level) qemu_set_irq(spapr_phb_lsi_qirq(phb, irq_num), level); } +static PCIINTxRoute spapr_route_intx_pin_to_irq(void *opaque, int pin) +{ + sPAPRPHBState *sphb = SPAPR_PCI_HOST_BRIDGE(opaque); + PCIINTxRoute route; + + route.mode = PCI_INTX_ENABLED; + route.irq = sphb->lsi_table[pin].irq; + + return route; +} + /* * MSI/MSIX memory region implementation. * The handler handles both MSI and MSIX. @@ -450,10 +454,7 @@ static void pci_spapr_set_irq(void *opaque, int irq_num, int level) static void spapr_msi_write(void *opaque, hwaddr addr, uint64_t data, unsigned size) { - sPAPRPHBState *phb = opaque; - int ndev = addr >> 16; - int vec = ((addr & 0xFFFF) >> 2) | data; - uint32_t irq = phb->msi_table[ndev].irq + vec; + uint32_t irq = data; trace_spapr_pci_msi_write(addr, data, irq); @@ -467,6 +468,34 @@ static const MemoryRegionOps spapr_msi_ops = { .endianness = DEVICE_LITTLE_ENDIAN }; +void spapr_pci_msi_init(sPAPREnvironment *spapr, hwaddr addr) +{ + uint64_t window_size = 4096; + + /* + * As MSI/MSIX interrupts trigger by writing at MSI/MSIX vectors, + * we need to allocate some memory to catch those writes coming + * from msi_notify()/msix_notify(). + * As MSIMessage:addr is going to be the same and MSIMessage:data + * is going to be a VIRQ number, 4 bytes of the MSI MR will only + * be used. + * + * For KVM we want to ensure that this memory is a full page so that + * our memory slot is of page size granularity. + */ +#ifdef CONFIG_KVM + if (kvm_enabled()) { + window_size = getpagesize(); + } +#endif + + spapr->msi_win_addr = addr; + memory_region_init_io(&spapr->msiwindow, NULL, &spapr_msi_ops, spapr, + "msi", window_size); + memory_region_add_subregion(get_system_memory(), spapr->msi_win_addr, + &spapr->msiwindow); +} + /* * PHB PCI device */ @@ -477,12 +506,11 @@ static AddressSpace *spapr_pci_dma_iommu(PCIBus *bus, void *opaque, int devfn) return &phb->iommu_as; } -static int spapr_phb_init(SysBusDevice *s) +static void spapr_phb_realize(DeviceState *dev, Error **errp) { - DeviceState *dev = DEVICE(s); + SysBusDevice *s = SYS_BUS_DEVICE(dev); sPAPRPHBState *sphb = SPAPR_PCI_HOST_BRIDGE(s); PCIHostState *phb = PCI_HOST_BRIDGE(s); - const char *busname; char *namebuf; int i; PCIBus *bus; @@ -492,11 +520,10 @@ static int spapr_phb_init(SysBusDevice *s) if ((sphb->buid != -1) || (sphb->dma_liobn != -1) || (sphb->mem_win_addr != -1) - || (sphb->io_win_addr != -1) - || (sphb->msi_win_addr != -1)) { - fprintf(stderr, "Either \"index\" or other parameters must" - " be specified for PAPR PHB, not both\n"); - return -1; + || (sphb->io_win_addr != -1)) { + error_setg(errp, "Either \"index\" or other parameters must" + " be specified for PAPR PHB, not both"); + return; } sphb->buid = SPAPR_PCI_BASE_BUID + sphb->index; @@ -506,37 +533,31 @@ static int spapr_phb_init(SysBusDevice *s) + sphb->index * SPAPR_PCI_WINDOW_SPACING; sphb->mem_win_addr = windows_base + SPAPR_PCI_MMIO_WIN_OFF; sphb->io_win_addr = windows_base + SPAPR_PCI_IO_WIN_OFF; - sphb->msi_win_addr = windows_base + SPAPR_PCI_MSI_WIN_OFF; } if (sphb->buid == -1) { - fprintf(stderr, "BUID not specified for PHB\n"); - return -1; + error_setg(errp, "BUID not specified for PHB"); + return; } if (sphb->dma_liobn == -1) { - fprintf(stderr, "LIOBN not specified for PHB\n"); - return -1; + error_setg(errp, "LIOBN not specified for PHB"); + return; } if (sphb->mem_win_addr == -1) { - fprintf(stderr, "Memory window address not specified for PHB\n"); - return -1; + error_setg(errp, "Memory window address not specified for PHB"); + return; } if (sphb->io_win_addr == -1) { - fprintf(stderr, "IO window address not specified for PHB\n"); - return -1; - } - - if (sphb->msi_win_addr == -1) { - fprintf(stderr, "MSI window address not specified for PHB\n"); - return -1; + error_setg(errp, "IO window address not specified for PHB"); + return; } if (find_phb(spapr, sphb->buid)) { - fprintf(stderr, "PCI host bridges must have unique BUIDs\n"); - return -1; + error_setg(errp, "PCI host bridges must have unique BUIDs"); + return; } sphb->dtbusname = g_strdup_printf("pci@%" PRIx64, sphb->buid); @@ -545,7 +566,7 @@ static int spapr_phb_init(SysBusDevice *s) /* Initialize memory regions */ sprintf(namebuf, "%s.mmio", sphb->dtbusname); - memory_region_init(&sphb->memspace, OBJECT(sphb), namebuf, INT64_MAX); + memory_region_init(&sphb->memspace, OBJECT(sphb), namebuf, UINT64_MAX); sprintf(namebuf, "%s.mmio-alias", sphb->dtbusname); memory_region_init_alias(&sphb->memwindow, OBJECT(sphb), @@ -574,37 +595,7 @@ static int spapr_phb_init(SysBusDevice *s) memory_region_add_subregion(get_system_memory(), sphb->io_win_addr, &sphb->iowindow); - /* As MSI/MSIX interrupts trigger by writing at MSI/MSIX vectors, - * we need to allocate some memory to catch those writes coming - * from msi_notify()/msix_notify() */ - if (msi_supported) { - sprintf(namebuf, "%s.msi", sphb->dtbusname); - memory_region_init_io(&sphb->msiwindow, OBJECT(sphb), &spapr_msi_ops, sphb, - namebuf, SPAPR_MSIX_MAX_DEVS * 0x10000); - memory_region_add_subregion(get_system_memory(), sphb->msi_win_addr, - &sphb->msiwindow); - } - - /* - * Selecting a busname is more complex than you'd think, due to - * interacting constraints. If the user has specified an id - * explicitly for the phb , then we want to use the qdev default - * of naming the bus based on the bridge device (so the user can - * then assign devices to it in the way they expect). For the - * first / default PCI bus (index=0) we want to use just "pci" - * because libvirt expects there to be a bus called, simply, - * "pci". Otherwise, we use the same name as in the device tree, - * since it's unique by construction, and makes the guest visible - * BUID clear. - */ - if (dev->id) { - busname = NULL; - } else if (sphb->index == 0) { - busname = "pci"; - } else { - busname = sphb->dtbusname; - } - bus = pci_register_bus(dev, busname, + bus = pci_register_bus(dev, NULL, pci_spapr_set_irq, pci_spapr_map_irq, sphb, &sphb->memspace, &sphb->iospace, PCI_DEVFN(0, 0), PCI_NUM_PINS, TYPE_PCI_BUS); @@ -615,14 +606,17 @@ static int spapr_phb_init(SysBusDevice *s) sphb->tcet = spapr_tce_new_table(dev, sphb->dma_liobn, sphb->dma_window_size); if (!sphb->tcet) { - fprintf(stderr, "Unable to create TCE table for %s\n", sphb->dtbusname); - return -1; + error_setg(errp, "Unable to create TCE table for %s", + sphb->dtbusname); + return; } address_space_init(&sphb->iommu_as, spapr_tce_get_iommu(sphb->tcet), sphb->dtbusname); pci_setup_iommu(bus, spapr_pci_dma_iommu, sphb); + pci_bus_set_route_irq_fn(bus, spapr_route_intx_pin_to_irq); + QLIST_INSERT_HEAD(&spapr->phbs, sphb, list); /* Initialize the LSI table */ @@ -631,13 +625,12 @@ static int spapr_phb_init(SysBusDevice *s) irq = spapr_allocate_lsi(0); if (!irq) { - return -1; + error_setg(errp, "spapr_allocate_lsi failed"); + return; } sphb->lsi_table[i].irq = irq; } - - return 0; } static void spapr_phb_reset(DeviceState *qdev) @@ -651,15 +644,14 @@ static void spapr_phb_reset(DeviceState *qdev) static Property spapr_phb_properties[] = { DEFINE_PROP_INT32("index", sPAPRPHBState, index, -1), - DEFINE_PROP_HEX64("buid", sPAPRPHBState, buid, -1), - DEFINE_PROP_HEX32("liobn", sPAPRPHBState, dma_liobn, -1), - DEFINE_PROP_HEX64("mem_win_addr", sPAPRPHBState, mem_win_addr, -1), - DEFINE_PROP_HEX64("mem_win_size", sPAPRPHBState, mem_win_size, - SPAPR_PCI_MMIO_WIN_SIZE), - DEFINE_PROP_HEX64("io_win_addr", sPAPRPHBState, io_win_addr, -1), - DEFINE_PROP_HEX64("io_win_size", sPAPRPHBState, io_win_size, - SPAPR_PCI_IO_WIN_SIZE), - DEFINE_PROP_HEX64("msi_win_addr", sPAPRPHBState, msi_win_addr, -1), + DEFINE_PROP_UINT64("buid", sPAPRPHBState, buid, -1), + DEFINE_PROP_UINT32("liobn", sPAPRPHBState, dma_liobn, -1), + DEFINE_PROP_UINT64("mem_win_addr", sPAPRPHBState, mem_win_addr, -1), + DEFINE_PROP_UINT64("mem_win_size", sPAPRPHBState, mem_win_size, + SPAPR_PCI_MMIO_WIN_SIZE), + DEFINE_PROP_UINT64("io_win_addr", sPAPRPHBState, io_win_addr, -1), + DEFINE_PROP_UINT64("io_win_size", sPAPRPHBState, io_win_size, + SPAPR_PCI_IO_WIN_SIZE), DEFINE_PROP_END_OF_LIST(), }; @@ -701,7 +693,6 @@ static const VMStateDescription vmstate_spapr_pci = { VMSTATE_UINT64_EQUAL(mem_win_size, sPAPRPHBState), VMSTATE_UINT64_EQUAL(io_win_addr, sPAPRPHBState), VMSTATE_UINT64_EQUAL(io_win_size, sPAPRPHBState), - VMSTATE_UINT64_EQUAL(msi_win_addr, sPAPRPHBState), VMSTATE_STRUCT_ARRAY(lsi_table, sPAPRPHBState, PCI_NUM_PINS, 0, vmstate_spapr_pci_lsi, struct spapr_pci_lsi), VMSTATE_STRUCT_ARRAY(msi_table, sPAPRPHBState, SPAPR_MSIX_MAX_DEVS, 0, @@ -722,14 +713,15 @@ static const char *spapr_phb_root_bus_path(PCIHostState *host_bridge, static void spapr_phb_class_init(ObjectClass *klass, void *data) { PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(klass); - SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); hc->root_bus_path = spapr_phb_root_bus_path; - sdc->init = spapr_phb_init; + dc->realize = spapr_phb_realize; dc->props = spapr_phb_properties; dc->reset = spapr_phb_reset; dc->vmsd = &vmstate_spapr_pci; + set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); + dc->cannot_instantiate_with_device_add_yet = false; } static const TypeInfo spapr_phb_info = { diff --git a/hw/ppc/spapr_rtas.c b/hw/ppc/spapr_rtas.c index 394ce05ba..73860d048 100644 --- a/hw/ppc/spapr_rtas.c +++ b/hw/ppc/spapr_rtas.c @@ -47,10 +47,10 @@ static void rtas_display_character(PowerPCCPU *cpu, sPAPREnvironment *spapr, VIOsPAPRDevice *sdev = vty_lookup(spapr, 0); if (!sdev) { - rtas_st(rets, 0, -1); + rtas_st(rets, 0, RTAS_OUT_HW_ERROR); } else { vty_putchars(sdev, &c, sizeof(c)); - rtas_st(rets, 0, 0); + rtas_st(rets, 0, RTAS_OUT_SUCCESS); } } @@ -62,13 +62,13 @@ static void rtas_get_time_of_day(PowerPCCPU *cpu, sPAPREnvironment *spapr, struct tm tm; if (nret != 8) { - rtas_st(rets, 0, -3); + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); return; } qemu_get_timedate(&tm, spapr->rtc_offset); - rtas_st(rets, 0, 0); /* Success */ + rtas_st(rets, 0, RTAS_OUT_SUCCESS); rtas_st(rets, 1, tm.tm_year + 1900); rtas_st(rets, 2, tm.tm_mon + 1); rtas_st(rets, 3, tm.tm_mday); @@ -96,7 +96,7 @@ static void rtas_set_time_of_day(PowerPCCPU *cpu, sPAPREnvironment *spapr, rtc_change_mon_event(&tm); spapr->rtc_offset = qemu_timedate_diff(&tm); - rtas_st(rets, 0, 0); /* Success */ + rtas_st(rets, 0, RTAS_OUT_SUCCESS); } static void rtas_power_off(PowerPCCPU *cpu, sPAPREnvironment *spapr, @@ -104,11 +104,11 @@ static void rtas_power_off(PowerPCCPU *cpu, sPAPREnvironment *spapr, uint32_t nret, target_ulong rets) { if (nargs != 2 || nret != 1) { - rtas_st(rets, 0, -3); + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); return; } qemu_system_shutdown_request(); - rtas_st(rets, 0, 0); + rtas_st(rets, 0, RTAS_OUT_SUCCESS); } static void rtas_system_reboot(PowerPCCPU *cpu, sPAPREnvironment *spapr, @@ -117,11 +117,11 @@ static void rtas_system_reboot(PowerPCCPU *cpu, sPAPREnvironment *spapr, uint32_t nret, target_ulong rets) { if (nargs != 0 || nret != 1) { - rtas_st(rets, 0, -3); + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); return; } qemu_system_reset_request(); - rtas_st(rets, 0, 0); + rtas_st(rets, 0, RTAS_OUT_SUCCESS); } static void rtas_query_cpu_stopped_state(PowerPCCPU *cpu_, @@ -131,28 +131,28 @@ static void rtas_query_cpu_stopped_state(PowerPCCPU *cpu_, uint32_t nret, target_ulong rets) { target_ulong id; - CPUState *cpu; + PowerPCCPU *cpu; if (nargs != 1 || nret != 2) { - rtas_st(rets, 0, -3); + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); return; } id = rtas_ld(args, 0); - cpu = qemu_get_cpu(id); + cpu = ppc_get_vcpu_by_dt_id(id); if (cpu != NULL) { - if (cpu->halted) { + if (CPU(cpu)->halted) { rtas_st(rets, 1, 0); } else { rtas_st(rets, 1, 2); } - rtas_st(rets, 0, 0); + rtas_st(rets, 0, RTAS_OUT_SUCCESS); return; } /* Didn't find a matching cpu */ - rtas_st(rets, 0, -3); + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); } static void rtas_start_cpu(PowerPCCPU *cpu_, sPAPREnvironment *spapr, @@ -161,10 +161,10 @@ static void rtas_start_cpu(PowerPCCPU *cpu_, sPAPREnvironment *spapr, uint32_t nret, target_ulong rets) { target_ulong id, start, r3; - CPUState *cs; + PowerPCCPU *cpu; if (nargs != 3 || nret != 1) { - rtas_st(rets, 0, -3); + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); return; } @@ -172,13 +172,13 @@ static void rtas_start_cpu(PowerPCCPU *cpu_, sPAPREnvironment *spapr, start = rtas_ld(args, 1); r3 = rtas_ld(args, 2); - cs = qemu_get_cpu(id); - if (cs != NULL) { - PowerPCCPU *cpu = POWERPC_CPU(cs); + cpu = ppc_get_vcpu_by_dt_id(id); + if (cpu != NULL) { + CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; if (!cs->halted) { - rtas_st(rets, 0, -1); + rtas_st(rets, 0, RTAS_OUT_HW_ERROR); return; } @@ -194,12 +194,77 @@ static void rtas_start_cpu(PowerPCCPU *cpu_, sPAPREnvironment *spapr, qemu_cpu_kick(cs); - rtas_st(rets, 0, 0); + rtas_st(rets, 0, RTAS_OUT_SUCCESS); return; } /* Didn't find a matching cpu */ - rtas_st(rets, 0, -3); + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); +} + +static void rtas_stop_self(PowerPCCPU *cpu, sPAPREnvironment *spapr, + uint32_t token, uint32_t nargs, + target_ulong args, + uint32_t nret, target_ulong rets) +{ + CPUState *cs = CPU(cpu); + CPUPPCState *env = &cpu->env; + + cs->halted = 1; + cpu_exit(cs); + /* + * While stopping a CPU, the guest calls H_CPPR which + * effectively disables interrupts on XICS level. + * However decrementer interrupts in TCG can still + * wake the CPU up so here we disable interrupts in MSR + * as well. + * As rtas_start_cpu() resets the whole MSR anyway, there is + * no need to bother with specific bits, we just clear it. + */ + env->msr = 0; +} + +#define DIAGNOSTICS_RUN_MODE 42 + +static void rtas_ibm_get_system_parameter(PowerPCCPU *cpu, + sPAPREnvironment *spapr, + uint32_t token, uint32_t nargs, + target_ulong args, + uint32_t nret, target_ulong rets) +{ + target_ulong parameter = rtas_ld(args, 0); + target_ulong buffer = rtas_ld(args, 1); + target_ulong length = rtas_ld(args, 2); + target_ulong ret = RTAS_OUT_NOT_SUPPORTED; + + switch (parameter) { + case DIAGNOSTICS_RUN_MODE: + if (length == 1) { + rtas_st(buffer, 0, 0); + ret = RTAS_OUT_SUCCESS; + } + break; + } + + rtas_st(rets, 0, ret); +} + +static void rtas_ibm_set_system_parameter(PowerPCCPU *cpu, + sPAPREnvironment *spapr, + uint32_t token, uint32_t nargs, + target_ulong args, + uint32_t nret, target_ulong rets) +{ + target_ulong parameter = rtas_ld(args, 0); + target_ulong ret = RTAS_OUT_NOT_SUPPORTED; + + switch (parameter) { + case DIAGNOSTICS_RUN_MODE: + ret = RTAS_OUT_NOT_AUTHORIZED; + break; + } + + rtas_st(rets, 0, ret); } static struct rtas_call { @@ -233,7 +298,7 @@ target_ulong spapr_rtas_call(PowerPCCPU *cpu, sPAPREnvironment *spapr, } hcall_dprintf("Unknown RTAS token 0x%x\n", token); - rtas_st(rets, 0, -3); + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); return H_PARAMETER; } @@ -269,24 +334,24 @@ int spapr_rtas_device_tree_setup(void *fdt, hwaddr rtas_addr, return ret; } - ret = qemu_devtree_setprop_cell(fdt, "/rtas", "linux,rtas-base", - rtas_addr); + ret = qemu_fdt_setprop_cell(fdt, "/rtas", "linux,rtas-base", + rtas_addr); if (ret < 0) { fprintf(stderr, "Couldn't add linux,rtas-base property: %s\n", fdt_strerror(ret)); return ret; } - ret = qemu_devtree_setprop_cell(fdt, "/rtas", "linux,rtas-entry", - rtas_addr); + ret = qemu_fdt_setprop_cell(fdt, "/rtas", "linux,rtas-entry", + rtas_addr); if (ret < 0) { fprintf(stderr, "Couldn't add linux,rtas-entry property: %s\n", fdt_strerror(ret)); return ret; } - ret = qemu_devtree_setprop_cell(fdt, "/rtas", "rtas-size", - rtas_size); + ret = qemu_fdt_setprop_cell(fdt, "/rtas", "rtas-size", + rtas_size); if (ret < 0) { fprintf(stderr, "Couldn't add rtas-size property: %s\n", fdt_strerror(ret)); @@ -300,8 +365,8 @@ int spapr_rtas_device_tree_setup(void *fdt, hwaddr rtas_addr, continue; } - ret = qemu_devtree_setprop_cell(fdt, "/rtas", call->name, - i + TOKEN_BASE); + ret = qemu_fdt_setprop_cell(fdt, "/rtas", call->name, + i + TOKEN_BASE); if (ret < 0) { fprintf(stderr, "Couldn't add rtas token for %s: %s\n", call->name, fdt_strerror(ret)); @@ -322,6 +387,11 @@ static void core_rtas_register_types(void) spapr_rtas_register("query-cpu-stopped-state", rtas_query_cpu_stopped_state); spapr_rtas_register("start-cpu", rtas_start_cpu); + spapr_rtas_register("stop-self", rtas_stop_self); + spapr_rtas_register("ibm,get-system-parameter", + rtas_ibm_get_system_parameter); + spapr_rtas_register("ibm,set-system-parameter", + rtas_ibm_set_system_parameter); } type_init(core_rtas_register_types) diff --git a/hw/ppc/spapr_vio.c b/hw/ppc/spapr_vio.c index a6a0a5113..2ae06a335 100644 --- a/hw/ppc/spapr_vio.c +++ b/hw/ppc/spapr_vio.c @@ -68,6 +68,7 @@ static void spapr_vio_bus_class_init(ObjectClass *klass, void *data) BusClass *k = BUS_CLASS(klass); k->get_dev_path = spapr_vio_get_dev_name; + k->get_fw_dev_path = spapr_vio_get_dev_name; } static const TypeInfo spapr_vio_bus_info = { @@ -331,25 +332,25 @@ static void rtas_set_tce_bypass(PowerPCCPU *cpu, sPAPREnvironment *spapr, uint32_t unit, enable; if (nargs != 2) { - rtas_st(rets, 0, -3); + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); return; } unit = rtas_ld(args, 0); enable = rtas_ld(args, 1); dev = spapr_vio_find_by_reg(bus, unit); if (!dev) { - rtas_st(rets, 0, -3); + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); return; } if (!dev->tcet) { - rtas_st(rets, 0, -3); + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); return; } spapr_tce_set_bypass(dev->tcet, !!enable); - rtas_st(rets, 0, 0); + rtas_st(rets, 0, RTAS_OUT_SUCCESS); } static void rtas_quiesce(PowerPCCPU *cpu, sPAPREnvironment *spapr, @@ -362,7 +363,7 @@ static void rtas_quiesce(PowerPCCPU *cpu, sPAPREnvironment *spapr, VIOsPAPRDevice *dev = NULL; if (nargs != 0) { - rtas_st(rets, 0, -3); + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); return; } @@ -371,7 +372,7 @@ static void rtas_quiesce(PowerPCCPU *cpu, sPAPREnvironment *spapr, spapr_vio_quiesce_one(dev); } - rtas_st(rets, 0, 0); + rtas_st(rets, 0, RTAS_OUT_SUCCESS); } static VIOsPAPRDevice *reg_conflict(VIOsPAPRDevice *dev) @@ -528,11 +529,11 @@ static int spapr_vio_bridge_init(SysBusDevice *dev) static void spapr_vio_bridge_class_init(ObjectClass *klass, void *data) { - DeviceClass *dc = DEVICE_CLASS(klass); SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + dc->fw_name = "vdevice"; k->init = spapr_vio_bridge_init; - dc->no_user = 1; } static const TypeInfo spapr_vio_bridge_info = { diff --git a/hw/ppc/virtex_ml507.c b/hw/ppc/virtex_ml507.c index 08e77fbef..3e3569d4b 100644 --- a/hw/ppc/virtex_ml507.c +++ b/hw/ppc/virtex_ml507.c @@ -40,11 +40,19 @@ #include "ppc405.h" #include "sysemu/blockdev.h" -#include "hw/xilinx.h" +#include "qapi/qmp/qerror.h" #define EPAPR_MAGIC (0x45504150) #define FLASH_SIZE (16 * 1024 * 1024) +#define INTC_BASEADDR 0x81800000 +#define UART16550_BASEADDR 0x83e01003 +#define TIMER_BASEADDR 0x83c00000 +#define PFLASH_BASEADDR 0xfc000000 + +#define TIMER_IRQ 3 +#define UART16550_IRQ 9 + static struct boot_info { uint32_t bootstrap_pc; @@ -63,7 +71,7 @@ static void mmubooke_create_initial_mapping(CPUPPCState *env, tlb->attr = 0; tlb->prot = PAGE_VALID | ((PAGE_READ | PAGE_WRITE | PAGE_EXEC) << 4); - tlb->size = 1 << 31; /* up to 0x80000000 */ + tlb->size = 1U << 31; /* up to 0x80000000 */ tlb->EPN = va & TARGET_PAGE_MASK; tlb->RPN = pa & TARGET_PAGE_MASK; tlb->PID = 0; @@ -71,7 +79,7 @@ static void mmubooke_create_initial_mapping(CPUPPCState *env, tlb = &env->tlb.tlbe[1]; tlb->attr = 0; tlb->prot = PAGE_VALID | ((PAGE_READ | PAGE_WRITE | PAGE_EXEC) << 4); - tlb->size = 1 << 31; /* up to 0xffffffff */ + tlb->size = 1U << 31; /* up to 0xffffffff */ tlb->EPN = 0x80000000 & TARGET_PAGE_MASK; tlb->RPN = 0x80000000 & TARGET_PAGE_MASK; tlb->PID = 0; @@ -141,23 +149,45 @@ static int xilinx_load_device_tree(hwaddr addr, { char *path; int fdt_size; - void *fdt; + void *fdt = NULL; int r; + const char *dtb_filename; - /* Try the local "ppc.dtb" override. */ - fdt = load_device_tree("ppc.dtb", &fdt_size); - if (!fdt) { - path = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE); - if (path) { - fdt = load_device_tree(path, &fdt_size); - g_free(path); + dtb_filename = qemu_opt_get(qemu_get_machine_opts(), "dtb"); + if (dtb_filename) { + fdt = load_device_tree(dtb_filename, &fdt_size); + if (!fdt) { + error_report("Error while loading device tree file '%s'", + dtb_filename); } + } else { + /* Try the local "ppc.dtb" override. */ + fdt = load_device_tree("ppc.dtb", &fdt_size); if (!fdt) { - return 0; + path = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE); + if (path) { + fdt = load_device_tree(path, &fdt_size); + g_free(path); + } } } + if (!fdt) { + return 0; + } - r = qemu_devtree_setprop_string(fdt, "/chosen", "bootargs", kernel_cmdline); + r = qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-start", + initrd_base); + if (r < 0) { + error_report("couldn't set /chosen/linux,initrd-start"); + } + + r = qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-end", + (initrd_base + initrd_size)); + if (r < 0) { + error_report("couldn't set /chosen/linux,initrd-end"); + } + + r = qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", kernel_cmdline); if (r < 0) fprintf(stderr, "couldn't set /chosen/bootargs\n"); cpu_physical_memory_write(addr, fdt, fdt_size); @@ -170,6 +200,8 @@ static void virtex_init(QEMUMachineInitArgs *args) const char *cpu_model = args->cpu_model; const char *kernel_filename = args->kernel_filename; const char *kernel_cmdline = args->kernel_cmdline; + hwaddr initrd_base = 0; + int initrd_size = 0; MemoryRegion *address_space_mem = get_system_memory(); DeviceState *dev; PowerPCCPU *cpu; @@ -195,22 +227,31 @@ static void virtex_init(QEMUMachineInitArgs *args) memory_region_add_subregion(address_space_mem, ram_base, phys_ram); dinfo = drive_get(IF_PFLASH, 0, 0); - pflash_cfi01_register(0xfc000000, NULL, "virtex.flash", FLASH_SIZE, + pflash_cfi01_register(PFLASH_BASEADDR, NULL, "virtex.flash", FLASH_SIZE, dinfo ? dinfo->bdrv : NULL, (64 * 1024), FLASH_SIZE >> 16, 1, 0x89, 0x18, 0x0000, 0x0, 1); cpu_irq = (qemu_irq *) &env->irq_inputs[PPC40x_INPUT_INT]; - dev = xilinx_intc_create(0x81800000, cpu_irq[0], 0); + dev = qdev_create(NULL, "xlnx.xps-intc"); + qdev_prop_set_uint32(dev, "kind-of-intr", 0); + qdev_init_nofail(dev); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, INTC_BASEADDR); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, cpu_irq[0]); for (i = 0; i < 32; i++) { irq[i] = qdev_get_gpio_in(dev, i); } - serial_mm_init(address_space_mem, 0x83e01003ULL, 2, irq[9], 115200, - serial_hds[0], DEVICE_LITTLE_ENDIAN); + serial_mm_init(address_space_mem, UART16550_BASEADDR, 2, irq[UART16550_IRQ], + 115200, serial_hds[0], DEVICE_LITTLE_ENDIAN); /* 2 timers at irq 2 @ 62 Mhz. */ - xilinx_timer_create(0x83c00000, irq[3], 0, 62 * 1000000); + dev = qdev_create(NULL, "xlnx.xps-timer"); + qdev_prop_set_uint32(dev, "one-timer-only", 0); + qdev_prop_set_uint32(dev, "clock-frequency", 62 * 1000000); + qdev_init_nofail(dev); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, TIMER_BASEADDR); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq[TIMER_IRQ]); if (kernel_filename) { uint64_t entry, low, high; @@ -233,10 +274,27 @@ static void virtex_init(QEMUMachineInitArgs *args) boot_info.ima_size = kernel_size; + /* Load initrd. */ + if (args->initrd_filename) { + initrd_base = high = ROUND_UP(high, 4); + initrd_size = load_image_targphys(args->initrd_filename, + high, ram_size - high); + + if (initrd_size < 0) { + error_report("couldn't load ram disk '%s'", + args->initrd_filename); + exit(1); + } + high = ROUND_UP(high + initrd_size, 4); + } + /* Provide a device-tree. */ boot_info.fdt = high + (8192 * 2); boot_info.fdt &= ~8191; - xilinx_load_device_tree(boot_info.fdt, ram_size, 0, 0, kernel_cmdline); + + xilinx_load_device_tree(boot_info.fdt, ram_size, + initrd_base, initrd_size, + kernel_cmdline); } env->load_info = &boot_info; } @@ -245,7 +303,6 @@ static QEMUMachine virtex_machine = { .name = "virtex-ml507", .desc = "Xilinx Virtex ML507 reference design", .init = virtex_init, - DEFAULT_MACHINE_OPTIONS, }; static void virtex_machine_init(void) |