diff options
author | Peter Maydell <peter.maydell@linaro.org> | 2017-02-02 18:48:06 +0000 |
---|---|---|
committer | Peter Maydell <peter.maydell@linaro.org> | 2017-02-02 18:48:06 +0000 |
commit | 5459ef3bff961bc462ac89460ab6b08a14624c8d (patch) | |
tree | 9308ed3504343e7bc9b4fc1b71a2b8fa7ae68592 /hw | |
parent | 4e9f5244e1945b2852b9ddcd7f023a7d19c9ecd7 (diff) | |
parent | 7c6e8797337c24520b48d8b50a900a747e50f974 (diff) | |
download | qemu-5459ef3bff961bc462ac89460ab6b08a14624c8d.zip qemu-5459ef3bff961bc462ac89460ab6b08a14624c8d.tar.gz qemu-5459ef3bff961bc462ac89460ab6b08a14624c8d.tar.bz2 |
Merge remote-tracking branch 'remotes/dgibson/tags/ppc-for-2.9-20170202' into staging
ppc patch queue 2017-02-02
This obsoletes ppc-for-2.9-20170112, which had a MacOS build bug.
This is a long overdue ppc pull request for qemu-2.9. It's been a
long time coming due to some holidays and inconveniently timed
problems with testing. So, there's a lot in here:
* More POWER9 instruction implementations for TCG
* The simpler parts of my CPU compatibility mode cleanup
* This changes behaviour to prefer compatibility modes over
"raW" mode for new machine type versions
* New "40p" machine type which is essentially a modernized and
cleaned up "prep". The intention is that it will replace "prep"
once it has some more testing and polish.
* Add pseries-2.9 machine type
* Implement H_SIGNAL_SYS_RESET hypercall
* Consolidate the two alternate CPU init paths in pseries by
making it always go through CPU core objects to initialize CPU
* A number of bugfixes and cleanups
* Stop the guest timebase when the guest is stopped under KVM.
This makes the guest system clock also stop when paused, which
matches the x86 behaviour.
* Some preliminary cleanups leading towards implementation of the
POWER9 MMU.
There are also some changes not strictly related to ppc code, but for
its benefit:
* Limit the pxi-expander-bridge (PXB) device to x86 guests only
(it's essentially a hack to work around historical x86
limitations)
* Some additions to the 128-bit math in host_utils, necessary for
some of the new instructions.
* Revise a number of qtests and enable them for ppc
# gpg: Signature made Thu 02 Feb 2017 01:40:16 GMT
# gpg: using RSA key 0x6C38CACA20D9B392
# gpg: Good signature from "David Gibson <david@gibson.dropbear.id.au>"
# gpg: aka "David Gibson (Red Hat) <dgibson@redhat.com>"
# gpg: aka "David Gibson (ozlabs.org) <dgibson@ozlabs.org>"
# gpg: aka "David Gibson (kernel.org) <dwg@kernel.org>"
# Primary key fingerprint: 75F4 6586 AE61 A66C C44E 87DC 6C38 CACA 20D9 B392
* remotes/dgibson/tags/ppc-for-2.9-20170202: (107 commits)
hw/ppc/pnv: Use error_report instead of hw_error if a ROM file can't be found
ppc/kvm: Handle the "family" CPU via alias instead of registering new types
target/ppc/mmu_hash64: Fix incorrect shift value in amr calculation
target/ppc/mmu_hash64: Fix printing unsigned as signed int
tcg/POWER9: NOOP the cp_abort instruction
target/ppc/debug: Print LPCR register value if register exists
target-ppc: Add xststdc[sp, dp, qp] instructions
target-ppc: Add xvtstdc[sp,dp] instructions
target-ppc: Add MMU model check for booke machines
ppc: switch to constants within BUILD_BUG_ON
target/ppc/cpu-models: Fix/remove bad CPU aliases
target/ppc: Remove unused POWERPC_FAMILY(POWER)
spapr: clock should count only if vm is running
ppc: Remove unused function cpu_ppc601_rtc_init()
target/ppc: Add pcr_supported to POWER9 cpu class definition
powerpc/cpu-models: rename ISAv3.00 logical PVR definition
target-ppc: Add xvcv[hpsp, sphp] instructions
target-ppc: Add xsmulqp instruction
target-ppc: Add xsdivqp instruction
target-ppc: Add xscvsdqp and xscvudqp instructions
...
# Conflicts:
# hw/pci-bridge/Makefile.objs
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'hw')
-rw-r--r-- | hw/gpio/mpc8xxx.c | 20 | ||||
-rw-r--r-- | hw/pci-bridge/Makefile.objs | 2 | ||||
-rw-r--r-- | hw/ppc/Makefile.objs | 2 | ||||
-rw-r--r-- | hw/ppc/e500.c | 23 | ||||
-rw-r--r-- | hw/ppc/pnv.c | 6 | ||||
-rw-r--r-- | hw/ppc/ppc.c | 73 | ||||
-rw-r--r-- | hw/ppc/ppc440_bamboo.c | 6 | ||||
-rw-r--r-- | hw/ppc/ppc_booke.c | 8 | ||||
-rw-r--r-- | hw/ppc/ppce500_spin.c | 18 | ||||
-rw-r--r-- | hw/ppc/prep.c | 234 | ||||
-rw-r--r-- | hw/ppc/prep_systemio.c | 303 | ||||
-rw-r--r-- | hw/ppc/rs6000_mc.c | 232 | ||||
-rw-r--r-- | hw/ppc/spapr.c | 196 | ||||
-rw-r--r-- | hw/ppc/spapr_cpu_core.c | 38 | ||||
-rw-r--r-- | hw/ppc/spapr_hcall.c | 160 | ||||
-rw-r--r-- | hw/ppc/spapr_vio.c | 10 | ||||
-rw-r--r-- | hw/ppc/trace-events | 13 | ||||
-rw-r--r-- | hw/ppc/virtex_ml507.c | 7 | ||||
-rw-r--r-- | hw/usb/host-libusb.c | 29 | ||||
-rw-r--r-- | hw/usb/host-stub.c | 5 |
20 files changed, 1124 insertions, 261 deletions
diff --git a/hw/gpio/mpc8xxx.c b/hw/gpio/mpc8xxx.c index d149719..e12edb4 100644 --- a/hw/gpio/mpc8xxx.c +++ b/hw/gpio/mpc8xxx.c @@ -143,8 +143,10 @@ static void mpc8xxx_gpio_write(void *opaque, hwaddr offset, mpc8xxx_gpio_update(s); } -static void mpc8xxx_gpio_reset(MPC8XXXGPIOState *s) +static void mpc8xxx_gpio_reset(DeviceState *dev) { + MPC8XXXGPIOState *s = MPC8XXX_GPIO(dev); + s->dir = 0; s->odr = 0; s->dat = 0; @@ -180,33 +182,33 @@ static const MemoryRegionOps mpc8xxx_gpio_ops = { .endianness = DEVICE_BIG_ENDIAN, }; -static int mpc8xxx_gpio_initfn(SysBusDevice *sbd) +static void mpc8xxx_gpio_initfn(Object *obj) { - DeviceState *dev = DEVICE(sbd); - MPC8XXXGPIOState *s = MPC8XXX_GPIO(dev); + DeviceState *dev = DEVICE(obj); + MPC8XXXGPIOState *s = MPC8XXX_GPIO(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - memory_region_init_io(&s->iomem, OBJECT(s), &mpc8xxx_gpio_ops, s, "mpc8xxx_gpio", 0x1000); + memory_region_init_io(&s->iomem, obj, &mpc8xxx_gpio_ops, + s, "mpc8xxx_gpio", 0x1000); sysbus_init_mmio(sbd, &s->iomem); sysbus_init_irq(sbd, &s->irq); qdev_init_gpio_in(dev, mpc8xxx_gpio_set_irq, 32); qdev_init_gpio_out(dev, s->out, 32); - mpc8xxx_gpio_reset(s); - return 0; } static void mpc8xxx_gpio_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = mpc8xxx_gpio_initfn; dc->vmsd = &vmstate_mpc8xxx_gpio; + dc->reset = mpc8xxx_gpio_reset; } static const TypeInfo mpc8xxx_gpio_info = { .name = TYPE_MPC8XXX_GPIO, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(MPC8XXXGPIOState), + .instance_init = mpc8xxx_gpio_initfn, .class_init = mpc8xxx_gpio_class_init, }; diff --git a/hw/pci-bridge/Makefile.objs b/hw/pci-bridge/Makefile.objs index 85e8e39..c4683cf 100644 --- a/hw/pci-bridge/Makefile.objs +++ b/hw/pci-bridge/Makefile.objs @@ -1,6 +1,6 @@ common-obj-y += pci_bridge_dev.o -common-obj-y += pci_expander_bridge.o common-obj-$(CONFIG_PCIE_PORT) += pcie_root_port.o gen_pcie_root_port.o +common-obj-$(CONFIG_PXB) += pci_expander_bridge.o common-obj-$(CONFIG_XIO3130) += xio3130_upstream.o xio3130_downstream.o common-obj-$(CONFIG_IOH3420) += ioh3420.o common-obj-$(CONFIG_I82801B11) += i82801b11.o diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs index 8025129..0012934 100644 --- a/hw/ppc/Makefile.objs +++ b/hw/ppc/Makefile.objs @@ -16,6 +16,8 @@ obj-y += ppc405_boards.o ppc4xx_devs.o ppc405_uc.o ppc440_bamboo.o obj-y += ppc4xx_pci.o # PReP obj-$(CONFIG_PREP) += prep.o +obj-$(CONFIG_PREP) += prep_systemio.o +obj-${CONFIG_RS6000_MC} += rs6000_mc.o # OldWorld PowerMac obj-$(CONFIG_MAC) += mac_oldworld.o # NewWorld PowerMac diff --git a/hw/ppc/e500.c b/hw/ppc/e500.c index cf8b122..f7df238 100644 --- a/hw/ppc/e500.c +++ b/hw/ppc/e500.c @@ -827,6 +827,12 @@ void ppce500_init(MachineState *machine, PPCE500Params *params) env = &cpu->env; cs = CPU(cpu); + if (env->mmu_model != POWERPC_MMU_BOOKE206) { + fprintf(stderr, "MMU model %i not supported by this machine.\n", + env->mmu_model); + exit(1); + } + if (!firstenv) { firstenv = env; } @@ -1049,27 +1055,18 @@ void ppce500_init(MachineState *machine, PPCE500Params *params) boot_info->dt_size = dt_size; } -static int e500_ccsr_initfn(SysBusDevice *dev) +static void e500_ccsr_initfn(Object *obj) { - PPCE500CCSRState *ccsr; - - ccsr = CCSR(dev); - memory_region_init(&ccsr->ccsr_space, OBJECT(ccsr), "e500-ccsr", + PPCE500CCSRState *ccsr = CCSR(obj); + memory_region_init(&ccsr->ccsr_space, obj, "e500-ccsr", MPC8544_CCSRBAR_SIZE); - return 0; -} - -static void e500_ccsr_class_init(ObjectClass *klass, void *data) -{ - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = e500_ccsr_initfn; } static const TypeInfo e500_ccsr_info = { .name = TYPE_CCSR, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(PPCE500CCSRState), - .class_init = e500_ccsr_class_init, + .instance_init = e500_ccsr_initfn, }; static void e500_register_types(void) diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 83597fe..4fab5c0 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -381,7 +381,7 @@ static void ppc_powernv_init(MachineState *machine) fw_size = load_image_targphys(fw_filename, FW_LOAD_ADDR, FW_MAX_SIZE); if (fw_size < 0) { - hw_error("qemu: could not load OPAL '%s'\n", fw_filename); + error_report("qemu: could not load OPAL '%s'", fw_filename); exit(1); } g_free(fw_filename); @@ -393,8 +393,8 @@ static void ppc_powernv_init(MachineState *machine) kernel_size = load_image_targphys(machine->kernel_filename, KERNEL_LOAD_ADDR, 0x2000000); if (kernel_size < 0) { - hw_error("qemu: could not load kernel'%s'\n", - machine->kernel_filename); + error_report("qemu: could not load kernel'%s'", + machine->kernel_filename); exit(1); } } diff --git a/hw/ppc/ppc.c b/hw/ppc/ppc.c index 8945869..d171e60 100644 --- a/hw/ppc/ppc.c +++ b/hw/ppc/ppc.c @@ -847,9 +847,8 @@ static void cpu_ppc_set_tb_clk (void *opaque, uint32_t freq) cpu_ppc_store_purr(cpu, 0x0000000000000000ULL); } -static void timebase_pre_save(void *opaque) +static void timebase_save(PPCTimebase *tb) { - PPCTimebase *tb = opaque; uint64_t ticks = cpu_get_host_ticks(); PowerPCCPU *first_ppc_cpu = POWERPC_CPU(first_cpu); @@ -858,43 +857,30 @@ static void timebase_pre_save(void *opaque) return; } + /* not used anymore, we keep it for compatibility */ tb->time_of_the_day_ns = qemu_clock_get_ns(QEMU_CLOCK_HOST); /* - * tb_offset is only expected to be changed by migration so + * tb_offset is only expected to be changed by QEMU so * there is no need to update it from KVM here */ tb->guest_timebase = ticks + first_ppc_cpu->env.tb_env->tb_offset; } -static int timebase_post_load(void *opaque, int version_id) +static void timebase_load(PPCTimebase *tb) { - PPCTimebase *tb_remote = opaque; CPUState *cpu; PowerPCCPU *first_ppc_cpu = POWERPC_CPU(first_cpu); - int64_t tb_off_adj, tb_off, ns_diff; - int64_t migration_duration_ns, migration_duration_tb, guest_tb, host_ns; + int64_t tb_off_adj, tb_off; unsigned long freq; if (!first_ppc_cpu->env.tb_env) { error_report("No timebase object"); - return -1; + return; } freq = first_ppc_cpu->env.tb_env->tb_freq; - /* - * Calculate timebase on the destination side of migration. - * The destination timebase must be not less than the source timebase. - * We try to adjust timebase by downtime if host clocks are not - * too much out of sync (1 second for now). - */ - host_ns = qemu_clock_get_ns(QEMU_CLOCK_HOST); - ns_diff = MAX(0, host_ns - tb_remote->time_of_the_day_ns); - migration_duration_ns = MIN(NANOSECONDS_PER_SECOND, ns_diff); - migration_duration_tb = muldiv64(freq, migration_duration_ns, - NANOSECONDS_PER_SECOND); - guest_tb = tb_remote->guest_timebase + MIN(0, migration_duration_tb); - tb_off_adj = guest_tb - cpu_get_host_ticks(); + tb_off_adj = tb->guest_timebase - cpu_get_host_ticks(); tb_off = first_ppc_cpu->env.tb_env->tb_offset; trace_ppc_tb_adjust(tb_off, tb_off_adj, tb_off_adj - tb_off, @@ -904,9 +890,44 @@ static int timebase_post_load(void *opaque, int version_id) CPU_FOREACH(cpu) { PowerPCCPU *pcpu = POWERPC_CPU(cpu); pcpu->env.tb_env->tb_offset = tb_off_adj; +#if defined(CONFIG_KVM) + kvm_set_one_reg(cpu, KVM_REG_PPC_TB_OFFSET, + &pcpu->env.tb_env->tb_offset); +#endif } +} - return 0; +void cpu_ppc_clock_vm_state_change(void *opaque, int running, + RunState state) +{ + PPCTimebase *tb = opaque; + + if (running) { + timebase_load(tb); + } else { + timebase_save(tb); + } +} + +/* + * When migrating, read the clock just before migration, + * so that the guest clock counts during the events + * between: + * + * * vm_stop() + * * + * * pre_save() + * + * This reduces clock difference on migration from 5s + * to 0.1s (when max_downtime == 5s), because sending the + * final pages of memory (which happens between vm_stop() + * and pre_save()) takes max_downtime. + */ +static void timebase_pre_save(void *opaque) +{ + PPCTimebase *tb = opaque; + + timebase_save(tb); } const VMStateDescription vmstate_ppc_timebase = { @@ -915,7 +936,6 @@ const VMStateDescription vmstate_ppc_timebase = { .minimum_version_id = 1, .minimum_version_id_old = 1, .pre_save = timebase_pre_save, - .post_load = timebase_post_load, .fields = (VMStateField []) { VMSTATE_UINT64(guest_timebase, PPCTimebase), VMSTATE_INT64(time_of_the_day_ns, PPCTimebase), @@ -950,13 +970,6 @@ clk_setup_cb cpu_ppc_tb_init (CPUPPCState *env, uint32_t freq) } /* Specific helpers for POWER & PowerPC 601 RTC */ -#if 0 -static clk_setup_cb cpu_ppc601_rtc_init (CPUPPCState *env) -{ - return cpu_ppc_tb_init(env, 7812500); -} -#endif - void cpu_ppc601_store_rtcu (CPUPPCState *env, uint32_t value) { _cpu_ppc_store_tbu(env, value); diff --git a/hw/ppc/ppc440_bamboo.c b/hw/ppc/ppc440_bamboo.c index 5c535b1..9d997bf 100644 --- a/hw/ppc/ppc440_bamboo.c +++ b/hw/ppc/ppc440_bamboo.c @@ -193,6 +193,12 @@ static void bamboo_init(MachineState *machine) } env = &cpu->env; + if (env->mmu_model != POWERPC_MMU_BOOKE) { + fprintf(stderr, "MMU model %i not supported by this machine.\n", + env->mmu_model); + exit(1); + } + qemu_register_reset(main_cpu_reset, cpu); ppc_booke_timers_init(cpu, 400000000, 0); ppc_dcr_init(env, NULL, NULL); diff --git a/hw/ppc/ppc_booke.c b/hw/ppc/ppc_booke.c index ab8d026..60baffa 100644 --- a/hw/ppc/ppc_booke.c +++ b/hw/ppc/ppc_booke.c @@ -198,8 +198,12 @@ static void booke_decr_cb(void *opaque) booke_update_irq(cpu); if (env->spr[SPR_BOOKE_TCR] & TCR_ARE) { - /* Auto Reload */ - cpu_ppc_store_decr(env, env->spr[SPR_BOOKE_DECAR]); + /* Do not reload 0, it is already there. It would just trigger + * the timer again and lead to infinite loop */ + if (env->spr[SPR_BOOKE_DECAR] != 0) { + /* Auto Reload */ + cpu_ppc_store_decr(env, env->spr[SPR_BOOKE_DECAR]); + } } } diff --git a/hw/ppc/ppce500_spin.c b/hw/ppc/ppce500_spin.c index eb219ab..69ca2d0 100644 --- a/hw/ppc/ppce500_spin.c +++ b/hw/ppc/ppce500_spin.c @@ -54,9 +54,9 @@ typedef struct SpinState { SpinInfo spin[MAX_CPUS]; } SpinState; -static void spin_reset(void *opaque) +static void spin_reset(DeviceState *dev) { - SpinState *s = opaque; + SpinState *s = E500_SPIN(dev); int i; for (i = 0; i < MAX_CPUS; i++) { @@ -174,30 +174,28 @@ static const MemoryRegionOps spin_rw_ops = { .endianness = DEVICE_BIG_ENDIAN, }; -static int ppce500_spin_initfn(SysBusDevice *dev) +static void ppce500_spin_initfn(Object *obj) { + SysBusDevice *dev = SYS_BUS_DEVICE(obj); SpinState *s = E500_SPIN(dev); - memory_region_init_io(&s->iomem, OBJECT(s), &spin_rw_ops, s, + memory_region_init_io(&s->iomem, obj, &spin_rw_ops, s, "e500 spin pv device", sizeof(SpinInfo) * MAX_CPUS); sysbus_init_mmio(dev, &s->iomem); - - qemu_register_reset(spin_reset, s); - - return 0; } static void ppce500_spin_class_init(ObjectClass *klass, void *data) { - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); - k->init = ppce500_spin_initfn; + dc->reset = spin_reset; } static const TypeInfo ppce500_spin_info = { .name = TYPE_E500_SPIN, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(SpinState), + .instance_init = ppce500_spin_initfn, .class_init = ppce500_spin_class_init, }; diff --git a/hw/ppc/prep.c b/hw/ppc/prep.c index 054af1e..ca7959c 100644 --- a/hw/ppc/prep.c +++ b/hw/ppc/prep.c @@ -2,6 +2,7 @@ * QEMU PPC PREP hardware System Emulator * * Copyright (c) 2003-2007 Jocelyn Mayer + * Copyright (c) 2017 Hervé Poussineau * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -43,17 +44,21 @@ #include "hw/isa/pc87312.h" #include "sysemu/block-backend.h" #include "sysemu/arch_init.h" +#include "sysemu/kvm.h" #include "sysemu/qtest.h" #include "exec/address-spaces.h" #include "trace.h" #include "elf.h" #include "qemu/cutils.h" +#include "kvm_ppc.h" /* SMP is not enabled, for now */ #define MAX_CPUS 1 #define MAX_IDE_BUS 2 +#define CFG_ADDR 0xf0000510 + #define BIOS_SIZE (1024 * 1024) #define BIOS_FILENAME "ppc_rom.bin" #define KERNEL_LOAD_ADDR 0x01000000 @@ -316,6 +321,12 @@ static uint32_t PREP_io_800_readb (void *opaque, uint32_t addr) #define NVRAM_SIZE 0x2000 +static void fw_cfg_boot_set(void *opaque, const char *boot_device, + Error **errp) +{ + fw_cfg_modify_i16(opaque, FW_CFG_BOOT_DEVICE, boot_device[0]); +} + static void ppc_prep_reset(void *opaque) { PowerPCCPU *cpu = opaque; @@ -339,13 +350,13 @@ static PortioList prep_port_list; /* NVRAM helpers */ static inline uint32_t nvram_read(Nvram *nvram, uint32_t addr) { - NvramClass *k = NVRAM_GET_CLASS(sysctrl->nvram); + NvramClass *k = NVRAM_GET_CLASS(nvram); return (k->read)(nvram, addr); } static inline void nvram_write(Nvram *nvram, uint32_t addr, uint32_t val) { - NvramClass *k = NVRAM_GET_CLASS(sysctrl->nvram); + NvramClass *k = NVRAM_GET_CLASS(nvram); (k->write)(nvram, addr, val); } @@ -677,4 +688,223 @@ static void prep_machine_init(MachineClass *mc) mc->default_boot_order = "cad"; } +static int prep_set_cmos_checksum(DeviceState *dev, void *opaque) +{ + uint16_t checksum = *(uint16_t *)opaque; + ISADevice *rtc; + + if (object_dynamic_cast(OBJECT(dev), "mc146818rtc")) { + rtc = ISA_DEVICE(dev); + rtc_set_memory(rtc, 0x2e, checksum & 0xff); + rtc_set_memory(rtc, 0x3e, checksum & 0xff); + rtc_set_memory(rtc, 0x2f, checksum >> 8); + rtc_set_memory(rtc, 0x3f, checksum >> 8); + } + return 0; +} + +static void ibm_40p_init(MachineState *machine) +{ + CPUPPCState *env = NULL; + uint16_t cmos_checksum; + PowerPCCPU *cpu; + DeviceState *dev; + SysBusDevice *pcihost; + Nvram *m48t59 = NULL; + PCIBus *pci_bus; + ISABus *isa_bus; + void *fw_cfg; + int i; + uint32_t kernel_base = 0, initrd_base = 0; + long kernel_size = 0, initrd_size = 0; + char boot_device; + + /* init CPU */ + if (!machine->cpu_model) { + machine->cpu_model = "604"; + } + cpu = cpu_ppc_init(machine->cpu_model); + if (!cpu) { + error_report("could not initialize CPU '%s'", + machine->cpu_model); + exit(1); + } + env = &cpu->env; + if (PPC_INPUT(env) != PPC_FLAGS_INPUT_6xx) { + error_report("only 6xx bus is supported on this machine"); + exit(1); + } + + if (env->flags & POWERPC_FLAG_RTC_CLK) { + /* POWER / PowerPC 601 RTC clock frequency is 7.8125 MHz */ + cpu_ppc_tb_init(env, 7812500UL); + } else { + /* Set time-base frequency to 100 Mhz */ + cpu_ppc_tb_init(env, 100UL * 1000UL * 1000UL); + } + qemu_register_reset(ppc_prep_reset, cpu); + + /* PCI host */ + dev = qdev_create(NULL, "raven-pcihost"); + if (!bios_name) { + bios_name = BIOS_FILENAME; + } + qdev_prop_set_string(dev, "bios-name", bios_name); + qdev_prop_set_uint32(dev, "elf-machine", PPC_ELF_MACHINE); + pcihost = SYS_BUS_DEVICE(dev); + object_property_add_child(qdev_get_machine(), "raven", OBJECT(dev), NULL); + qdev_init_nofail(dev); + pci_bus = PCI_BUS(qdev_get_child_bus(dev, "pci.0")); + if (!pci_bus) { + error_report("could not create PCI host controller"); + exit(1); + } + + /* PCI -> ISA bridge */ + dev = DEVICE(pci_create_simple(pci_bus, PCI_DEVFN(11, 0), "i82378")); + qdev_connect_gpio_out(dev, 0, + cpu->env.irq_inputs[PPC6xx_INPUT_INT]); + sysbus_connect_irq(pcihost, 0, qdev_get_gpio_in(dev, 15)); + sysbus_connect_irq(pcihost, 1, qdev_get_gpio_in(dev, 13)); + sysbus_connect_irq(pcihost, 2, qdev_get_gpio_in(dev, 15)); + sysbus_connect_irq(pcihost, 3, qdev_get_gpio_in(dev, 13)); + isa_bus = ISA_BUS(qdev_get_child_bus(dev, "isa.0")); + + /* Memory controller */ + dev = DEVICE(isa_create(isa_bus, "rs6000-mc")); + qdev_prop_set_uint32(dev, "ram-size", machine->ram_size); + qdev_init_nofail(dev); + + /* initialize CMOS checksums */ + cmos_checksum = 0x6aa9; + qbus_walk_children(BUS(isa_bus), prep_set_cmos_checksum, NULL, NULL, NULL, + &cmos_checksum); + + /* initialize audio subsystem */ + audio_init(); + + /* add some more devices */ + if (defaults_enabled()) { + isa_create_simple(isa_bus, "i8042"); + m48t59 = NVRAM(isa_create_simple(isa_bus, "isa-m48t59")); + + dev = DEVICE(isa_create(isa_bus, "cs4231a")); + qdev_prop_set_uint32(dev, "iobase", 0x830); + qdev_prop_set_uint32(dev, "irq", 10); + qdev_init_nofail(dev); + + dev = DEVICE(isa_create(isa_bus, "pc87312")); + qdev_prop_set_uint32(dev, "config", 12); + qdev_init_nofail(dev); + + dev = DEVICE(isa_create(isa_bus, "prep-systemio")); + qdev_prop_set_uint32(dev, "ibm-planar-id", 0xfc); + qdev_prop_set_uint32(dev, "equipment", 0xc0); + qdev_init_nofail(dev); + + pci_create_simple(pci_bus, PCI_DEVFN(1, 0), "lsi53c810"); + + /* XXX: s3-trio at PCI_DEVFN(2, 0) */ + pci_vga_init(pci_bus); + + for (i = 0; i < nb_nics; i++) { + pci_nic_init_nofail(&nd_table[i], pci_bus, "pcnet", + i == 0 ? "3" : NULL); + } + } + + /* Prepare firmware configuration for OpenBIOS */ + fw_cfg = fw_cfg_init_mem(CFG_ADDR, CFG_ADDR + 2); + + if (machine->kernel_filename) { + /* load kernel */ + kernel_base = KERNEL_LOAD_ADDR; + kernel_size = load_image_targphys(machine->kernel_filename, + kernel_base, + machine->ram_size - kernel_base); + if (kernel_size < 0) { + error_report("could not load kernel '%s'", + machine->kernel_filename); + exit(1); + } + fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ADDR, kernel_base); + fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_SIZE, kernel_size); + /* load initrd */ + if (machine->initrd_filename) { + initrd_base = INITRD_LOAD_ADDR; + initrd_size = load_image_targphys(machine->initrd_filename, + initrd_base, + machine->ram_size - initrd_base); + if (initrd_size < 0) { + error_report("could not load initial ram disk '%s'", + machine->initrd_filename); + exit(1); + } + fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_ADDR, initrd_base); + fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_SIZE, initrd_size); + } + if (machine->kernel_cmdline && *machine->kernel_cmdline) { + fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_CMDLINE, CMDLINE_ADDR); + pstrcpy_targphys("cmdline", CMDLINE_ADDR, TARGET_PAGE_SIZE, + machine->kernel_cmdline); + fw_cfg_add_string(fw_cfg, FW_CFG_CMDLINE_DATA, + machine->kernel_cmdline); + fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE, + strlen(machine->kernel_cmdline) + 1); + } + boot_device = 'm'; + } else { + boot_device = machine->boot_order[0]; + } + + fw_cfg_add_i16(fw_cfg, FW_CFG_MAX_CPUS, (uint16_t)max_cpus); + fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)machine->ram_size); + fw_cfg_add_i16(fw_cfg, FW_CFG_MACHINE_ID, ARCH_PREP); + + fw_cfg_add_i16(fw_cfg, FW_CFG_PPC_WIDTH, graphic_width); + fw_cfg_add_i16(fw_cfg, FW_CFG_PPC_HEIGHT, graphic_height); + fw_cfg_add_i16(fw_cfg, FW_CFG_PPC_DEPTH, graphic_depth); + + fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_IS_KVM, kvm_enabled()); + if (kvm_enabled()) { +#ifdef CONFIG_KVM + uint8_t *hypercall; + + fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_TBFREQ, kvmppc_get_tbfreq()); + hypercall = g_malloc(16); + kvmppc_get_hypercall(env, hypercall, 16); + fw_cfg_add_bytes(fw_cfg, FW_CFG_PPC_KVM_HC, hypercall, 16); + fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_KVM_PID, getpid()); +#endif + } else { + fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_TBFREQ, NANOSECONDS_PER_SECOND); + } + fw_cfg_add_i16(fw_cfg, FW_CFG_BOOT_DEVICE, boot_device); + qemu_register_boot_set(fw_cfg_boot_set, fw_cfg); + + /* Prepare firmware configuration for Open Hack'Ware */ + if (m48t59) { + PPC_NVRAM_set_params(m48t59, NVRAM_SIZE, "PREP", ram_size, + boot_device, + kernel_base, kernel_size, + machine->kernel_cmdline, + initrd_base, initrd_size, + /* XXX: need an option to load a NVRAM image */ + 0, + graphic_width, graphic_height, graphic_depth); + } +} + +static void ibm_40p_machine_init(MachineClass *mc) +{ + mc->desc = "IBM RS/6000 7020 (40p)", + mc->init = ibm_40p_init; + mc->max_cpus = 1; + mc->pci_allow_0_address = true; + mc->default_ram_size = 128 * M_BYTE; + mc->block_default_type = IF_SCSI; + mc->default_boot_order = "c"; +} + +DEFINE_MACHINE("40p", ibm_40p_machine_init) DEFINE_MACHINE("prep", prep_machine_init) diff --git a/hw/ppc/prep_systemio.c b/hw/ppc/prep_systemio.c new file mode 100644 index 0000000..50893ec --- /dev/null +++ b/hw/ppc/prep_systemio.c @@ -0,0 +1,303 @@ +/* + * QEMU PReP System I/O emulation + * + * Copyright (c) 2017 Hervé Poussineau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "hw/isa/isa.h" +#include "exec/address-spaces.h" +#include "qemu/error-report.h" /* for error_report() */ +#include "sysemu/sysemu.h" /* for vm_stop() */ +#include "cpu.h" +#include "trace.h" + +#define TYPE_PREP_SYSTEMIO "prep-systemio" +#define PREP_SYSTEMIO(obj) \ + OBJECT_CHECK(PrepSystemIoState, (obj), TYPE_PREP_SYSTEMIO) + +/* Bit as defined in PowerPC Reference Plaform v1.1, sect. 6.1.5, p. 132 */ +#define PREP_BIT(n) (1 << (7 - (n))) + +typedef struct PrepSystemIoState { + ISADevice parent_obj; + MemoryRegion ppc_parity_mem; + + qemu_irq non_contiguous_io_map_irq; + uint8_t sreset; /* 0x0092 */ + uint8_t equipment; /* 0x080c */ + uint8_t system_control; /* 0x081c */ + uint8_t iomap_type; /* 0x0850 */ + uint8_t ibm_planar_id; /* 0x0852 */ + qemu_irq softreset_irq; + PortioList portio; +} PrepSystemIoState; + +/* PORT 0092 -- Special Port 92 (Read/Write) */ + +enum { + PORT0092_SOFTRESET = PREP_BIT(7), + PORT0092_LE_MODE = PREP_BIT(6), +}; + +static void prep_port0092_write(void *opaque, uint32_t addr, uint32_t val) +{ + PrepSystemIoState *s = opaque; + + trace_prep_systemio_write(addr, val); + + s->sreset = val & PORT0092_SOFTRESET; + qemu_set_irq(s->softreset_irq, s->sreset); + + if ((val & PORT0092_LE_MODE) != 0) { + /* XXX Not supported yet */ + error_report("little-endian mode not supported"); + vm_stop(RUN_STATE_PAUSED); + } else { + /* Nothing to do */ + } +} + +static uint32_t prep_port0092_read(void *opaque, uint32_t addr) +{ + PrepSystemIoState *s = opaque; + trace_prep_systemio_read(addr, s->sreset); + return s->sreset; +} + +/* PORT 0808 -- Hardfile Light Register (Write Only) */ + +enum { + PORT0808_HARDFILE_LIGHT_ON = PREP_BIT(7), +}; + +static void prep_port0808_write(void *opaque, uint32_t addr, uint32_t val) +{ + trace_prep_systemio_write(addr, val); +} + +/* PORT 0810 -- Password Protect 1 Register (Write Only) */ + +/* reset by port 0x4D in the SIO */ +static void prep_port0810_write(void *opaque, uint32_t addr, uint32_t val) +{ + trace_prep_systemio_write(addr, val); +} + +/* PORT 0812 -- Password Protect 2 Register (Write Only) */ + +/* reset by port 0x4D in the SIO */ +static void prep_port0812_write(void *opaque, uint32_t addr, uint32_t val) +{ + trace_prep_systemio_write(addr, val); +} + +/* PORT 0814 -- L2 Invalidate Register (Write Only) */ + +static void prep_port0814_write(void *opaque, uint32_t addr, uint32_t val) +{ + trace_prep_systemio_write(addr, val); +} + +/* PORT 0818 -- Reserved for Keylock (Read Only) */ + +enum { + PORT0818_KEYLOCK_SIGNAL_HIGH = PREP_BIT(7), +}; + +static uint32_t prep_port0818_read(void *opaque, uint32_t addr) +{ + uint32_t val = 0; + trace_prep_systemio_read(addr, val); + return val; +} + +/* PORT 080C -- Equipment */ + +enum { + PORT080C_SCSIFUSE = PREP_BIT(1), + PORT080C_L2_COPYBACK = PREP_BIT(4), + PORT080C_L2_256 = PREP_BIT(5), + PORT080C_UPGRADE_CPU = PREP_BIT(6), + PORT080C_L2 = PREP_BIT(7), +}; + +static uint32_t prep_port080c_read(void *opaque, uint32_t addr) +{ + PrepSystemIoState *s = opaque; + trace_prep_systemio_read(addr, s->equipment); + return s->equipment; +} + +/* PORT 081C -- System Control Register (Read/Write) */ + +enum { + PORT081C_FLOPPY_MOTOR_INHIBIT = PREP_BIT(3), + PORT081C_MASK_TEA = PREP_BIT(2), + PORT081C_L2_UPDATE_INHIBIT = PREP_BIT(1), + PORT081C_L2_CACHEMISS_INHIBIT = PREP_BIT(0), +}; + +static void prep_port081c_write(void *opaque, uint32_t addr, uint32_t val) +{ + static const uint8_t mask = PORT081C_FLOPPY_MOTOR_INHIBIT | + PORT081C_MASK_TEA | + PORT081C_L2_UPDATE_INHIBIT | + PORT081C_L2_CACHEMISS_INHIBIT; + PrepSystemIoState *s = opaque; + trace_prep_systemio_write(addr, val); + s->system_control = val & mask; +} + +static uint32_t prep_port081c_read(void *opaque, uint32_t addr) +{ + PrepSystemIoState *s = opaque; + trace_prep_systemio_read(addr, s->system_control); + return s->system_control; +} + +/* System Board Identification */ + +static uint32_t prep_port0852_read(void *opaque, uint32_t addr) +{ + PrepSystemIoState *s = opaque; + trace_prep_systemio_read(addr, s->ibm_planar_id); + return s->ibm_planar_id; +} + +/* PORT 0850 -- I/O Map Type Register (Read/Write) */ + +enum { + PORT0850_IOMAP_NONCONTIGUOUS = PREP_BIT(7), +}; + +static uint32_t prep_port0850_read(void *opaque, uint32_t addr) +{ + PrepSystemIoState *s = opaque; + trace_prep_systemio_read(addr, s->iomap_type); + return s->iomap_type; +} + +static void prep_port0850_write(void *opaque, uint32_t addr, uint32_t val) +{ + PrepSystemIoState *s = opaque; + + trace_prep_systemio_write(addr, val); + qemu_set_irq(s->non_contiguous_io_map_irq, + val & PORT0850_IOMAP_NONCONTIGUOUS); + s->iomap_type = val & PORT0850_IOMAP_NONCONTIGUOUS; +} + +static const MemoryRegionPortio ppc_io800_port_list[] = { + { 0x092, 1, 1, .read = prep_port0092_read, + .write = prep_port0092_write, }, + { 0x808, 1, 1, .write = prep_port0808_write, }, + { 0x80c, 1, 1, .read = prep_port080c_read, }, + { 0x810, 1, 1, .write = prep_port0810_write, }, + { 0x812, 1, 1, .write = prep_port0812_write, }, + { 0x814, 1, 1, .write = prep_port0814_write, }, + { 0x818, 1, 1, .read = prep_port0818_read }, + { 0x81c, 1, 1, .read = prep_port081c_read, + .write = prep_port081c_write, }, + { 0x850, 1, 1, .read = prep_port0850_read, + .write = prep_port0850_write, }, + { 0x852, 1, 1, .read = prep_port0852_read, }, + PORTIO_END_OF_LIST() +}; + +static uint64_t ppc_parity_error_readl(void *opaque, hwaddr addr, + unsigned int size) +{ + uint32_t val = 0; + trace_prep_systemio_read((unsigned int)addr, val); + return val; +} + +static const MemoryRegionOps ppc_parity_error_ops = { + .read = ppc_parity_error_readl, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static void prep_systemio_realize(DeviceState *dev, Error **errp) +{ + ISADevice *isa = ISA_DEVICE(dev); + PrepSystemIoState *s = PREP_SYSTEMIO(dev); + PowerPCCPU *cpu; + + qdev_init_gpio_out(dev, &s->non_contiguous_io_map_irq, 1); + s->iomap_type = PORT0850_IOMAP_NONCONTIGUOUS; + qemu_set_irq(s->non_contiguous_io_map_irq, + s->iomap_type & PORT0850_IOMAP_NONCONTIGUOUS); + cpu = POWERPC_CPU(first_cpu); + s->softreset_irq = cpu->env.irq_inputs[PPC6xx_INPUT_HRESET]; + + isa_register_portio_list(isa, &s->portio, 0x0, ppc_io800_port_list, s, + "systemio800"); + + memory_region_init_io(&s->ppc_parity_mem, OBJECT(dev), + &ppc_parity_error_ops, s, "ppc-parity", 0x4); + memory_region_add_subregion(get_system_memory(), 0xbfffeff0, + &s->ppc_parity_mem); +} + +static const VMStateDescription vmstate_prep_systemio = { + .name = "prep_systemio", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT8(sreset, PrepSystemIoState), + VMSTATE_UINT8(system_control, PrepSystemIoState), + VMSTATE_UINT8(iomap_type, PrepSystemIoState), + VMSTATE_END_OF_LIST() + }, +}; + +static Property prep_systemio_properties[] = { + DEFINE_PROP_UINT8("ibm-planar-id", PrepSystemIoState, ibm_planar_id, 0), + DEFINE_PROP_UINT8("equipment", PrepSystemIoState, equipment, 0), + DEFINE_PROP_END_OF_LIST() +}; + +static void prep_systemio_class_initfn(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = prep_systemio_realize; + dc->vmsd = &vmstate_prep_systemio; + dc->props = prep_systemio_properties; +} + +static TypeInfo prep_systemio800_info = { + .name = TYPE_PREP_SYSTEMIO, + .parent = TYPE_ISA_DEVICE, + .instance_size = sizeof(PrepSystemIoState), + .class_init = prep_systemio_class_initfn, +}; + +static void prep_systemio_register_types(void) +{ + type_register_static(&prep_systemio800_info); +} + +type_init(prep_systemio_register_types) diff --git a/hw/ppc/rs6000_mc.c b/hw/ppc/rs6000_mc.c new file mode 100644 index 0000000..b613565 --- /dev/null +++ b/hw/ppc/rs6000_mc.c @@ -0,0 +1,232 @@ +/* + * QEMU RS/6000 memory controller + * + * Copyright (c) 2017 Hervé Poussineau + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) version 3 or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include "hw/isa/isa.h" +#include "exec/address-spaces.h" +#include "hw/boards.h" +#include "qapi/error.h" +#include "trace.h" + +#define TYPE_RS6000MC "rs6000-mc" +#define RS6000MC_DEVICE(obj) \ + OBJECT_CHECK(RS6000MCState, (obj), TYPE_RS6000MC) + +typedef struct RS6000MCState { + ISADevice parent_obj; + /* see US patent 5,684,979 for details (expired 2001-11-04) */ + uint32_t ram_size; + bool autoconfigure; + MemoryRegion simm[6]; + unsigned int simm_size[6]; + uint32_t end_address[8]; + uint8_t port0820_index; + PortioList portio; +} RS6000MCState; + +/* P0RT 0803 -- SIMM ID Register (32/8 MB) (Read Only) */ + +static uint32_t rs6000mc_port0803_read(void *opaque, uint32_t addr) +{ + RS6000MCState *s = opaque; + uint32_t val = 0; + int socket; + + /* (1 << socket) indicates 32 MB SIMM at given socket */ + for (socket = 0; socket < 6; socket++) { + if (s->simm_size[socket] == 32) { + val |= (1 << socket); + } + } + + trace_rs6000mc_id_read(addr, val); + return val; +} + +/* PORT 0804 -- SIMM Presence Register (Read Only) */ + +static uint32_t rs6000mc_port0804_read(void *opaque, uint32_t addr) +{ + RS6000MCState *s = opaque; + uint32_t val = 0xff; + int socket; + + /* (1 << socket) indicates SIMM absence at given socket */ + for (socket = 0; socket < 6; socket++) { + if (s->simm_size[socket]) { + val &= ~(1 << socket); + } + } + s->port0820_index = 0; + + trace_rs6000mc_presence_read(addr, val); + return val; +} + +/* Memory Controller Size Programming Register */ + +static uint32_t rs6000mc_port0820_read(void *opaque, uint32_t addr) +{ + RS6000MCState *s = opaque; + uint32_t val = s->end_address[s->port0820_index] & 0x1f; + s->port0820_index = (s->port0820_index + 1) & 7; + trace_rs6000mc_size_read(addr, val); + return val; +} + +static void rs6000mc_port0820_write(void *opaque, uint32_t addr, uint32_t val) +{ + RS6000MCState *s = opaque; + uint8_t socket = val >> 5; + uint32_t end_address = val & 0x1f; + + trace_rs6000mc_size_write(addr, val); + s->end_address[socket] = end_address; + if (socket > 0 && socket < 7) { + if (s->simm_size[socket - 1]) { + uint32_t size; + uint32_t start_address = 0; + if (socket > 1) { + start_address = s->end_address[socket - 1]; + } + + size = end_address - start_address; + memory_region_set_enabled(&s->simm[socket - 1], size != 0); + memory_region_set_address(&s->simm[socket - 1], + start_address * 8 * 1024 * 1024); + } + } +} + +/* Read Memory Parity Error */ + +enum { + PORT0841_NO_ERROR_DETECTED = 0x01, +}; + +static uint32_t rs6000mc_port0841_read(void *opaque, uint32_t addr) +{ + uint32_t val = PORT0841_NO_ERROR_DETECTED; + trace_rs6000mc_parity_read(addr, val); + return val; +} + +static const MemoryRegionPortio rs6000mc_port_list[] = { + { 0x803, 1, 1, .read = rs6000mc_port0803_read }, + { 0x804, 1, 1, .read = rs6000mc_port0804_read }, + { 0x820, 1, 1, .read = rs6000mc_port0820_read, + .write = rs6000mc_port0820_write, }, + { 0x841, 1, 1, .read = rs6000mc_port0841_read }, + PORTIO_END_OF_LIST() +}; + +static void rs6000mc_realize(DeviceState *dev, Error **errp) +{ + RS6000MCState *s = RS6000MC_DEVICE(dev); + int socket = 0; + unsigned int ram_size = s->ram_size / (1024 * 1024); + + while (socket < 6) { + if (ram_size >= 64) { + s->simm_size[socket] = 32; + s->simm_size[socket + 1] = 32; + ram_size -= 64; + } else if (ram_size >= 16) { + s->simm_size[socket] = 8; + s->simm_size[socket + 1] = 8; + ram_size -= 16; + } else { + /* Not enough memory */ + break; + } + socket += 2; + } + + for (socket = 0; socket < 6; socket++) { + if (s->simm_size[socket]) { + char name[] = "simm.?"; + name[5] = socket + '0'; + memory_region_allocate_system_memory(&s->simm[socket], OBJECT(dev), + name, s->simm_size[socket] + * 1024 * 1024); + memory_region_add_subregion_overlap(get_system_memory(), 0, + &s->simm[socket], socket); + } + } + if (ram_size) { + /* unable to push all requested RAM in SIMMs */ + error_setg(errp, "RAM size incompatible with this board. " + "Try again with something else, like %d MB", + s->ram_size / 1024 / 1024 - ram_size); + return; + } + + if (s->autoconfigure) { + uint32_t start_address = 0; + for (socket = 0; socket < 6; socket++) { + if (s->simm_size[socket]) { + memory_region_set_enabled(&s->simm[socket], true); + memory_region_set_address(&s->simm[socket], start_address); + start_address += memory_region_size(&s->simm[socket]); + } + } + } + + isa_register_portio_list(ISA_DEVICE(dev), &s->portio, 0x0, + rs6000mc_port_list, s, "rs6000mc"); +} + +static const VMStateDescription vmstate_rs6000mc = { + .name = "rs6000-mc", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT8(port0820_index, RS6000MCState), + VMSTATE_END_OF_LIST() + }, +}; + +static Property rs6000mc_properties[] = { + DEFINE_PROP_UINT32("ram-size", RS6000MCState, ram_size, 0), + DEFINE_PROP_BOOL("auto-configure", RS6000MCState, autoconfigure, true), + DEFINE_PROP_END_OF_LIST() +}; + +static void rs6000mc_class_initfn(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = rs6000mc_realize; + dc->vmsd = &vmstate_rs6000mc; + dc->props = rs6000mc_properties; +} + +static const TypeInfo rs6000mc_info = { + .name = TYPE_RS6000MC, + .parent = TYPE_ISA_DEVICE, + .instance_size = sizeof(RS6000MCState), + .class_init = rs6000mc_class_initfn, +}; + +static void rs6000mc_types(void) +{ + type_register_static(&rs6000mc_info); +} + +type_init(rs6000mc_types) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 45b7d99..e465d7a 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -148,8 +148,8 @@ static int spapr_fixup_cpu_smt_dt(void *fdt, int offset, PowerPCCPU *cpu, uint32_t gservers_prop[smt_threads * 2]; int index = ppc_get_vcpu_dt_id(cpu); - if (cpu->cpu_version) { - ret = fdt_setprop_cell(fdt, offset, "cpu-version", cpu->cpu_version); + if (cpu->compat_pvr) { + ret = fdt_setprop_cell(fdt, offset, "cpu-version", cpu->compat_pvr); if (ret < 0) { return ret; } @@ -206,6 +206,7 @@ static int spapr_fixup_cpu_dt(void *fdt, sPAPRMachineState *spapr) PowerPCCPU *cpu = POWERPC_CPU(cs); DeviceClass *dc = DEVICE_GET_CLASS(cs); int index = ppc_get_vcpu_dt_id(cpu); + int compat_smt = MIN(smp_threads, ppc_compat_max_threads(cpu)); if ((index % smt) != 0) { continue; @@ -240,8 +241,7 @@ static int spapr_fixup_cpu_dt(void *fdt, sPAPRMachineState *spapr) return ret; } - ret = spapr_fixup_cpu_smt_dt(fdt, offset, cpu, - ppc_get_compat_smt_threads(cpu)); + ret = spapr_fixup_cpu_smt_dt(fdt, offset, cpu, compat_smt); if (ret < 0) { return ret; } @@ -407,6 +407,7 @@ static void spapr_populate_cpu_dt(CPUState *cs, void *fdt, int offset, size_t page_sizes_prop_size; uint32_t vcpus_per_socket = smp_threads * smp_cores; uint32_t pft_size_prop[] = {0, cpu_to_be32(spapr->htab_shift)}; + int compat_smt = MIN(smp_threads, ppc_compat_max_threads(cpu)); sPAPRDRConnector *drc; sPAPRDRConnectorClass *drck; int drc_index; @@ -494,8 +495,7 @@ static void spapr_populate_cpu_dt(CPUState *cs, void *fdt, int offset, _FDT(spapr_fixup_cpu_numa_dt(fdt, offset, cs)); - _FDT(spapr_fixup_cpu_smt_dt(fdt, offset, cpu, - ppc_get_compat_smt_threads(cpu))); + _FDT(spapr_fixup_cpu_smt_dt(fdt, offset, cpu, compat_smt)); } static void spapr_populate_cpus_dt_node(void *fdt, sPAPRMachineState *spapr) @@ -685,7 +685,6 @@ out: int spapr_h_cas_compose_response(sPAPRMachineState *spapr, target_ulong addr, target_ulong size, - bool cpu_update, sPAPROptionVector *ov5_updates) { void *fdt, *fdt_skel; @@ -704,9 +703,7 @@ int spapr_h_cas_compose_response(sPAPRMachineState *spapr, g_free(fdt_skel); /* Fixup cpu nodes */ - if (cpu_update) { - _FDT((spapr_fixup_cpu_dt(fdt, spapr))); - } + _FDT((spapr_fixup_cpu_dt(fdt, spapr))); if (spapr_dt_cas_updates(spapr, fdt, ov5_updates)) { return -1; @@ -1008,7 +1005,8 @@ static uint64_t translate_kernel_address(void *opaque, uint64_t addr) return (addr & 0x0fffffff) + KERNEL_LOAD_ADDR; } -static void emulate_spapr_hypercall(PowerPCCPU *cpu) +static void emulate_spapr_hypercall(PPCVirtualHypervisor *vhyp, + PowerPCCPU *cpu) { CPUPPCState *env = &cpu->env; @@ -1753,11 +1751,80 @@ static void spapr_validate_node_memory(MachineState *machine, Error **errp) } } +static void spapr_init_cpus(sPAPRMachineState *spapr) +{ + MachineState *machine = MACHINE(spapr); + MachineClass *mc = MACHINE_GET_CLASS(machine); + char *type = spapr_get_cpu_core_type(machine->cpu_model); + int smt = kvmppc_smt_threads(); + int spapr_max_cores, spapr_cores; + int i; + + if (!type) { + error_report("Unable to find sPAPR CPU Core definition"); + exit(1); + } + + if (mc->query_hotpluggable_cpus) { + if (smp_cpus % smp_threads) { + error_report("smp_cpus (%u) must be multiple of threads (%u)", + smp_cpus, smp_threads); + exit(1); + } + if (max_cpus % smp_threads) { + error_report("max_cpus (%u) must be multiple of threads (%u)", + max_cpus, smp_threads); + exit(1); + } + + spapr_max_cores = max_cpus / smp_threads; + spapr_cores = smp_cpus / smp_threads; + } else { + if (max_cpus != smp_cpus) { + error_report("This machine version does not support CPU hotplug"); + exit(1); + } + + spapr_max_cores = QEMU_ALIGN_UP(smp_cpus, smp_threads) / smp_threads; + spapr_cores = spapr_max_cores; + } + + spapr->cores = g_new0(Object *, spapr_max_cores); + for (i = 0; i < spapr_max_cores; i++) { + int core_id = i * smp_threads; + + if (mc->query_hotpluggable_cpus) { + sPAPRDRConnector *drc = + spapr_dr_connector_new(OBJECT(spapr), + SPAPR_DR_CONNECTOR_TYPE_CPU, + (core_id / smp_threads) * smt); + + qemu_register_reset(spapr_drc_reset, drc); + } + + if (i < spapr_cores) { + Object *core = object_new(type); + int nr_threads = smp_threads; + + /* Handle the partially filled core for older machine types */ + if ((i + 1) * smp_threads >= smp_cpus) { + nr_threads = smp_cpus - i * smp_threads; + } + + object_property_set_int(core, nr_threads, "nr-threads", + &error_fatal); + object_property_set_int(core, core_id, CPU_CORE_PROP_CORE_ID, + &error_fatal); + object_property_set_bool(core, true, "realized", &error_fatal); + } + } + g_free(type); +} + /* pSeries LPAR / sPAPR hardware init */ static void ppc_spapr_init(MachineState *machine) { sPAPRMachineState *spapr = SPAPR_MACHINE(machine); - MachineClass *mc = MACHINE_GET_CLASS(machine); sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(machine); const char *kernel_filename = machine->kernel_filename; const char *initrd_filename = machine->initrd_filename; @@ -1772,28 +1839,11 @@ static void ppc_spapr_init(MachineState *machine) long load_limit, fw_size; char *filename; int smt = kvmppc_smt_threads(); - int spapr_cores = smp_cpus / smp_threads; - int spapr_max_cores = max_cpus / smp_threads; - - if (mc->query_hotpluggable_cpus) { - if (smp_cpus % smp_threads) { - error_report("smp_cpus (%u) must be multiple of threads (%u)", - smp_cpus, smp_threads); - exit(1); - } - if (max_cpus % smp_threads) { - error_report("max_cpus (%u) must be multiple of threads (%u)", - max_cpus, smp_threads); - exit(1); - } - } msi_nonbroken = true; QLIST_INIT(&spapr->phbs); - cpu_ppc_hypercall = emulate_spapr_hypercall; - /* Allocate RMA if necessary */ rma_alloc_size = kvmppc_alloc_rma(&rma); @@ -1866,44 +1916,7 @@ static void ppc_spapr_init(MachineState *machine) ppc_cpu_parse_features(machine->cpu_model); - if (mc->query_hotpluggable_cpus) { - char *type = spapr_get_cpu_core_type(machine->cpu_model); - - if (type == NULL) { - error_report("Unable to find sPAPR CPU Core definition"); - exit(1); - } - - spapr->cores = g_new0(Object *, spapr_max_cores); - for (i = 0; i < spapr_max_cores; i++) { - int core_id = i * smp_threads; - sPAPRDRConnector *drc = - spapr_dr_connector_new(OBJECT(spapr), - SPAPR_DR_CONNECTOR_TYPE_CPU, - (core_id / smp_threads) * smt); - - qemu_register_reset(spapr_drc_reset, drc); - - if (i < spapr_cores) { - Object *core = object_new(type); - object_property_set_int(core, smp_threads, "nr-threads", - &error_fatal); - object_property_set_int(core, core_id, CPU_CORE_PROP_CORE_ID, - &error_fatal); - object_property_set_bool(core, true, "realized", &error_fatal); - } - } - g_free(type); - } else { - for (i = 0; i < smp_cpus; i++) { - PowerPCCPU *cpu = cpu_ppc_init(machine->cpu_model); - if (cpu == NULL) { - error_report("Unable to find PowerPC CPU definition"); - exit(1); - } - spapr_cpu_init(spapr, cpu, &error_fatal); - } - } + spapr_init_cpus(spapr); if (kvm_enabled()) { /* Enable H_LOGICAL_CI_* so SLOF can talk to in-kernel devices */ @@ -2116,6 +2129,12 @@ static void ppc_spapr_init(MachineState *machine) qemu_register_reset(spapr_ccs_reset_hook, spapr); qemu_register_boot_set(spapr_boot_set, spapr); + + /* to stop and start vmclock */ + if (kvm_enabled()) { + qemu_add_vm_change_state_handler(cpu_ppc_clock_vm_state_change, + &spapr->tb); + } } static int spapr_kvm_type(const char *vm_type) @@ -2185,6 +2204,19 @@ static char *spapr_get_fw_dev_path(FWPathProvider *p, BusState *bus, } } + /* + * SLOF probes the USB devices, and if it recognizes that the device is a + * storage device, it changes its name to "storage" instead of "usb-host", + * and additionally adds a child node for the SCSI LUN, so the correct + * boot path in SLOF is something like .../storage@1/disk@xxx" instead. + */ + if (strcmp("usb-host", qdev_fw_name(dev)) == 0) { + USBDevice *usbdev = CAST(USBDevice, dev, TYPE_USB_DEVICE); + if (usb_host_dev_is_scsi_storage(usbdev)) { + return g_strdup_printf("storage@%s/disk", usbdev->port->path); + } + } + if (phb) { /* Replace "pci" with "pci@800000020000000" */ return g_strdup_printf("pci@%"PRIX64, phb->buid); @@ -2252,7 +2284,7 @@ static void spapr_machine_finalizefn(Object *obj) g_free(spapr->kvm_type); } -static void ppc_cpu_do_nmi_on_cpu(CPUState *cs, run_on_cpu_data arg) +void spapr_do_system_reset_on_cpu(CPUState *cs, run_on_cpu_data arg) { cpu_synchronize_state(cs); ppc_cpu_do_system_reset(cs); @@ -2263,7 +2295,7 @@ static void spapr_nmi(NMIState *n, int cpu_index, Error **errp) CPUState *cs; CPU_FOREACH(cs) { - async_run_on_cpu(cs, ppc_cpu_do_nmi_on_cpu, RUN_ON_CPU_NULL); + async_run_on_cpu(cs, spapr_do_system_reset_on_cpu, RUN_ON_CPU_NULL); } } @@ -2668,6 +2700,7 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data) FWPathProviderClass *fwc = FW_PATH_PROVIDER_CLASS(oc); NMIClass *nc = NMI_CLASS(oc); HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc); + PPCVirtualHypervisorClass *vhc = PPC_VIRTUAL_HYPERVISOR_CLASS(oc); mc->desc = "pSeries Logical Partition (PAPR compliant)"; @@ -2699,6 +2732,7 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data) fwc->get_dev_path = spapr_get_fw_dev_path; nc->nmi_monitor_handler = spapr_nmi; smc->phb_placement = spapr_phb_placement; + vhc->hypercall = emulate_spapr_hypercall; } static const TypeInfo spapr_machine_info = { @@ -2714,6 +2748,7 @@ static const TypeInfo spapr_machine_info = { { TYPE_FW_PATH_PROVIDER }, { TYPE_NMI }, { TYPE_HOTPLUG_HANDLER }, + { TYPE_PPC_VIRTUAL_HYPERVISOR }, { } }, }; @@ -2747,18 +2782,37 @@ static const TypeInfo spapr_machine_info = { type_init(spapr_machine_register_##suffix) /* + * pseries-2.9 + */ +static void spapr_machine_2_9_instance_options(MachineState *machine) +{ +} + +static void spapr_machine_2_9_class_options(MachineClass *mc) +{ + /* Defaults for the latest behaviour inherited from the base class */ +} + +DEFINE_SPAPR_MACHINE(2_9, "2.9", true); + +/* * pseries-2.8 */ +#define SPAPR_COMPAT_2_8 \ + HW_COMPAT_2_8 + static void spapr_machine_2_8_instance_options(MachineState *machine) { + spapr_machine_2_9_instance_options(machine); } static void spapr_machine_2_8_class_options(MachineClass *mc) { - /* Defaults for the latest behaviour inherited from the base class */ + spapr_machine_2_9_class_options(mc); + SET_MACHINE_COMPAT(mc, SPAPR_COMPAT_2_8); } -DEFINE_SPAPR_MACHINE(2_8, "2.8", true); +DEFINE_SPAPR_MACHINE(2_8, "2.8", false); /* * pseries-2.7 diff --git a/hw/ppc/spapr_cpu_core.c b/hw/ppc/spapr_cpu_core.c index c18632b..9dddaeb 100644 --- a/hw/ppc/spapr_cpu_core.c +++ b/hw/ppc/spapr_cpu_core.c @@ -46,7 +46,8 @@ static void spapr_cpu_destroy(PowerPCCPU *cpu) qemu_unregister_reset(spapr_cpu_reset, cpu); } -void spapr_cpu_init(sPAPRMachineState *spapr, PowerPCCPU *cpu, Error **errp) +static void spapr_cpu_init(sPAPRMachineState *spapr, PowerPCCPU *cpu, + Error **errp) { CPUPPCState *env = &cpu->env; CPUState *cs = CPU(cpu); @@ -56,6 +57,7 @@ void spapr_cpu_init(sPAPRMachineState *spapr, PowerPCCPU *cpu, Error **errp) cpu_ppc_tb_init(env, SPAPR_TIMEBASE_FREQ); /* Enable PAPR mode in TCG or KVM */ + cpu_ppc_set_vhyp(cpu, PPC_VIRTUAL_HYPERVISOR(spapr)); cpu_ppc_set_papr(cpu); if (cpu->max_compat) { @@ -166,11 +168,11 @@ void spapr_core_plug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { sPAPRMachineState *spapr = SPAPR_MACHINE(OBJECT(hotplug_dev)); + MachineClass *mc = MACHINE_GET_CLASS(spapr); sPAPRCPUCore *core = SPAPR_CPU_CORE(OBJECT(dev)); CPUCore *cc = CPU_CORE(dev); CPUState *cs = CPU(core->threads); sPAPRDRConnector *drc; - sPAPRDRConnectorClass *drck; Error *local_err = NULL; void *fdt = NULL; int fdt_offset = 0; @@ -180,7 +182,7 @@ void spapr_core_plug(HotplugHandler *hotplug_dev, DeviceState *dev, drc = spapr_dr_connector_by_id(SPAPR_DR_CONNECTOR_TYPE_CPU, index * smt); spapr->cores[index] = OBJECT(dev); - g_assert(drc); + g_assert(drc || !mc->query_hotpluggable_cpus); /* * Setup CPU DT entries only for hotplugged CPUs. For boot time or @@ -190,13 +192,15 @@ void spapr_core_plug(HotplugHandler *hotplug_dev, DeviceState *dev, fdt = spapr_populate_hotplug_cpu_dt(cs, &fdt_offset, spapr); } - drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); - drck->attach(drc, dev, fdt, fdt_offset, !dev->hotplugged, &local_err); - if (local_err) { - g_free(fdt); - spapr->cores[index] = NULL; - error_propagate(errp, local_err); - return; + if (drc) { + sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); + drck->attach(drc, dev, fdt, fdt_offset, !dev->hotplugged, &local_err); + if (local_err) { + g_free(fdt); + spapr->cores[index] = NULL; + error_propagate(errp, local_err); + return; + } } if (dev->hotplugged) { @@ -209,8 +213,11 @@ void spapr_core_plug(HotplugHandler *hotplug_dev, DeviceState *dev, /* * Set the right DRC states for cold plugged CPU. */ - drck->set_allocation_state(drc, SPAPR_DR_ALLOCATION_STATE_USABLE); - drck->set_isolation_state(drc, SPAPR_DR_ISOLATION_STATE_UNISOLATED); + if (drc) { + sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); + drck->set_allocation_state(drc, SPAPR_DR_ALLOCATION_STATE_USABLE); + drck->set_isolation_state(drc, SPAPR_DR_ISOLATION_STATE_UNISOLATED); + } } } @@ -227,7 +234,7 @@ void spapr_core_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, char *base_core_type = spapr_get_cpu_core_type(machine->cpu_model); const char *type = object_get_typename(OBJECT(dev)); - if (!mc->query_hotpluggable_cpus) { + if (dev->hotplugged && !mc->query_hotpluggable_cpus) { error_setg(&local_err, "CPU hotplug not supported for this machine"); goto out; } @@ -237,11 +244,6 @@ void spapr_core_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, goto out; } - if (cc->nr_threads != smp_threads) { - error_setg(&local_err, "threads must be %d", smp_threads); - goto out; - } - if (cc->core_id % smp_threads) { error_setg(&local_err, "invalid core id %d", cc->core_id); goto out; diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c index b2a8e48..42d20e0 100644 --- a/hw/ppc/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -881,128 +881,105 @@ static target_ulong h_set_mode(PowerPCCPU *cpu, sPAPRMachineState *spapr, return ret; } -typedef struct { - uint32_t cpu_version; - Error *err; -} SetCompatState; +#define H_SIGNAL_SYS_RESET_ALL -1 +#define H_SIGNAL_SYS_RESET_ALLBUTSELF -2 -static void do_set_compat(CPUState *cs, run_on_cpu_data arg) +static target_ulong h_signal_sys_reset(PowerPCCPU *cpu, + sPAPRMachineState *spapr, + target_ulong opcode, target_ulong *args) { - PowerPCCPU *cpu = POWERPC_CPU(cs); - SetCompatState *s = arg.host_ptr; + target_long target = args[0]; + CPUState *cs; - cpu_synchronize_state(cs); - ppc_set_compat(cpu, s->cpu_version, &s->err); -} + if (target < 0) { + /* Broadcast */ + if (target < H_SIGNAL_SYS_RESET_ALLBUTSELF) { + return H_PARAMETER; + } -#define get_compat_level(cpuver) ( \ - ((cpuver) == CPU_POWERPC_LOGICAL_2_05) ? 2050 : \ - ((cpuver) == CPU_POWERPC_LOGICAL_2_06) ? 2060 : \ - ((cpuver) == CPU_POWERPC_LOGICAL_2_06_PLUS) ? 2061 : \ - ((cpuver) == CPU_POWERPC_LOGICAL_2_07) ? 2070 : 0) + CPU_FOREACH(cs) { + PowerPCCPU *c = POWERPC_CPU(cs); -static void cas_handle_compat_cpu(PowerPCCPUClass *pcc, uint32_t pvr, - unsigned max_lvl, unsigned *compat_lvl, - unsigned *cpu_version) -{ - unsigned lvl = get_compat_level(pvr); - bool is205, is206, is207; - - if (!lvl) { - return; - } - - /* If it is a logical PVR, try to determine the highest level */ - is205 = (pcc->pcr_supported & PCR_COMPAT_2_05) && - (lvl == get_compat_level(CPU_POWERPC_LOGICAL_2_05)); - is206 = (pcc->pcr_supported & PCR_COMPAT_2_06) && - ((lvl == get_compat_level(CPU_POWERPC_LOGICAL_2_06)) || - (lvl == get_compat_level(CPU_POWERPC_LOGICAL_2_06_PLUS))); - is207 = (pcc->pcr_supported & PCR_COMPAT_2_07) && - (lvl == get_compat_level(CPU_POWERPC_LOGICAL_2_07)); - - if (is205 || is206 || is207) { - if (!max_lvl) { - /* User did not set the level, choose the highest */ - if (*compat_lvl <= lvl) { - *compat_lvl = lvl; - *cpu_version = pvr; + if (target == H_SIGNAL_SYS_RESET_ALLBUTSELF) { + if (c == cpu) { + continue; + } } - } else if (max_lvl >= lvl) { - /* User chose the level, don't set higher than this */ - *compat_lvl = lvl; - *cpu_version = pvr; + run_on_cpu(cs, spapr_do_system_reset_on_cpu, RUN_ON_CPU_NULL); } + return H_SUCCESS; + + } else { + /* Unicast */ + CPU_FOREACH(cs) { + if (cpu->cpu_dt_id == target) { + run_on_cpu(cs, spapr_do_system_reset_on_cpu, RUN_ON_CPU_NULL); + return H_SUCCESS; + } + } + return H_PARAMETER; } } -static target_ulong h_client_architecture_support(PowerPCCPU *cpu_, +static target_ulong h_client_architecture_support(PowerPCCPU *cpu, sPAPRMachineState *spapr, target_ulong opcode, target_ulong *args) { target_ulong list = ppc64_phys_to_real(args[0]); target_ulong ov_table; - PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu_); - CPUState *cs; - bool cpu_match = false, cpu_update = true; - unsigned old_cpu_version = cpu_->cpu_version; - unsigned compat_lvl = 0, cpu_version = 0; - unsigned max_lvl = get_compat_level(cpu_->max_compat); - int counter; + bool explicit_match = false; /* Matched the CPU's real PVR */ + uint32_t max_compat = cpu->max_compat; + uint32_t best_compat = 0; + int i; sPAPROptionVector *ov5_guest, *ov5_cas_old, *ov5_updates; - /* Parse PVR list */ - for (counter = 0; counter < 512; ++counter) { + /* + * We scan the supplied table of PVRs looking for two things + * 1. Is our real CPU PVR in the list? + * 2. What's the "best" listed logical PVR + */ + for (i = 0; i < 512; ++i) { uint32_t pvr, pvr_mask; pvr_mask = ldl_be_phys(&address_space_memory, list); - list += 4; - pvr = ldl_be_phys(&address_space_memory, list); - list += 4; - - trace_spapr_cas_pvr_try(pvr); - if (!max_lvl && - ((cpu_->env.spr[SPR_PVR] & pvr_mask) == (pvr & pvr_mask))) { - cpu_match = true; - cpu_version = 0; - } else if (pvr == cpu_->cpu_version) { - cpu_match = true; - cpu_version = cpu_->cpu_version; - } else if (!cpu_match) { - cas_handle_compat_cpu(pcc, pvr, max_lvl, &compat_lvl, &cpu_version); - } - /* Terminator record */ + pvr = ldl_be_phys(&address_space_memory, list + 4); + list += 8; + if (~pvr_mask & pvr) { - break; + break; /* Terminator record */ + } + + if ((cpu->env.spr[SPR_PVR] & pvr_mask) == (pvr & pvr_mask)) { + explicit_match = true; + } else { + if (ppc_check_compat(cpu, pvr, best_compat, max_compat)) { + best_compat = pvr; + } } } + if ((best_compat == 0) && (!explicit_match || max_compat)) { + /* We couldn't find a suitable compatibility mode, and either + * the guest doesn't support "raw" mode for this CPU, or raw + * mode is disabled because a maximum compat mode is set */ + return H_HARDWARE; + } + /* Parsing finished */ - trace_spapr_cas_pvr(cpu_->cpu_version, cpu_match, - cpu_version, pcc->pcr_mask); + trace_spapr_cas_pvr(cpu->compat_pvr, explicit_match, best_compat); /* Update CPUs */ - if (old_cpu_version != cpu_version) { - CPU_FOREACH(cs) { - SetCompatState s = { - .cpu_version = cpu_version, - .err = NULL, - }; + if (cpu->compat_pvr != best_compat) { + Error *local_err = NULL; - run_on_cpu(cs, do_set_compat, RUN_ON_CPU_HOST_PTR(&s)); - - if (s.err) { - error_report_err(s.err); - return H_HARDWARE; - } + ppc_set_compat_all(best_compat, &local_err); + if (local_err) { + error_report_err(local_err); + return H_HARDWARE; } } - if (!cpu_version) { - cpu_update = false; - } - /* For the future use: here @ov_table points to the first option vector */ ov_table = list; @@ -1028,7 +1005,7 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu_, if (!spapr->cas_reboot) { spapr->cas_reboot = - (spapr_h_cas_compose_response(spapr, args[1], args[2], cpu_update, + (spapr_h_cas_compose_response(spapr, args[1], args[2], ov5_updates) != 0); } spapr_ovec_cleanup(ov5_updates); @@ -1101,6 +1078,7 @@ static void hypercall_register_types(void) /* hcall-splpar */ spapr_register_hypercall(H_REGISTER_VPA, h_register_vpa); spapr_register_hypercall(H_CEDE, h_cede); + spapr_register_hypercall(H_SIGNAL_SYS_RESET, h_signal_sys_reset); /* processor register resource access h-calls */ spapr_register_hypercall(H_SET_SPRG0, h_set_sprg0); diff --git a/hw/ppc/spapr_vio.c b/hw/ppc/spapr_vio.c index cc1e09c..8bfc5f9 100644 --- a/hw/ppc/spapr_vio.c +++ b/hw/ppc/spapr_vio.c @@ -538,21 +538,11 @@ VIOsPAPRBus *spapr_vio_bus_init(void) return bus; } -/* Represents sPAPR hcall VIO devices */ - -static int spapr_vio_bridge_init(SysBusDevice *dev) -{ - /* nothing */ - return 0; -} - static void spapr_vio_bridge_class_init(ObjectClass *klass, void *data) { - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); dc->fw_name = "vdevice"; - k->init = spapr_vio_bridge_init; } static const TypeInfo spapr_vio_bridge_info = { diff --git a/hw/ppc/trace-events b/hw/ppc/trace-events index 2297ead..f46995c 100644 --- a/hw/ppc/trace-events +++ b/hw/ppc/trace-events @@ -15,7 +15,7 @@ spapr_cas_continue(unsigned long n) "Copy changes to the guest: %ld bytes" # hw/ppc/spapr_hcall.c spapr_cas_pvr_try(uint32_t pvr) "%x" -spapr_cas_pvr(uint32_t cur_pvr, bool cpu_match, uint32_t new_pvr, uint64_t pcr) "current=%x, cpu_match=%u, new=%x, compat flags=%"PRIx64 +spapr_cas_pvr(uint32_t cur_pvr, bool explicit_match, uint32_t new_pvr) "current=%x, explicit_match=%u, new=%x" # hw/ppc/spapr_iommu.c spapr_iommu_put(uint64_t liobn, uint64_t ioba, uint64_t tce, uint64_t ret) "liobn=%"PRIx64" ioba=0x%"PRIx64" tce=0x%"PRIx64" ret=%"PRId64 @@ -74,3 +74,14 @@ ppc_tb_adjust(uint64_t offs1, uint64_t offs2, int64_t diff, int64_t seconds) "ad # hw/ppc/prep.c prep_io_800_writeb(uint32_t addr, uint32_t val) "0x%08" PRIx32 " => 0x%02" PRIx32 prep_io_800_readb(uint32_t addr, uint32_t retval) "0x%08" PRIx32 " <= 0x%02" PRIx32 + +# hw/ppc/prep_systemio.c +prep_systemio_read(uint32_t addr, uint32_t val) "read addr=%x val=%x" +prep_systemio_write(uint32_t addr, uint32_t val) "write addr=%x val=%x" + +# hw/ppc/rs6000_mc.c +rs6000mc_id_read(uint32_t addr, uint32_t val) "read addr=%x val=%x" +rs6000mc_presence_read(uint32_t addr, uint32_t val) "read addr=%x val=%x" +rs6000mc_size_read(uint32_t addr, uint32_t val) "read addr=%x val=%x" +rs6000mc_size_write(uint32_t addr, uint32_t val) "write addr=%x val=%x" +rs6000mc_parity_read(uint32_t addr, uint32_t val) "read addr=%x val=%x" diff --git a/hw/ppc/virtex_ml507.c b/hw/ppc/virtex_ml507.c index b97d966..fdbcf22 100644 --- a/hw/ppc/virtex_ml507.c +++ b/hw/ppc/virtex_ml507.c @@ -221,6 +221,13 @@ static void virtex_init(MachineState *machine) cpu = ppc440_init_xilinx(&ram_size, 1, machine->cpu_model, 400000000); env = &cpu->env; + + if (env->mmu_model != POWERPC_MMU_BOOKE) { + fprintf(stderr, "MMU model %i not supported by this machine.\n", + env->mmu_model); + exit(1); + } + qemu_register_reset(main_cpu_reset, cpu); memory_region_allocate_system_memory(phys_ram, NULL, "ram", ram_size); diff --git a/hw/usb/host-libusb.c b/hw/usb/host-libusb.c index bd81d71..7791c6d 100644 --- a/hw/usb/host-libusb.c +++ b/hw/usb/host-libusb.c @@ -1707,6 +1707,35 @@ static void usb_host_auto_check(void *unused) timer_mod(usb_auto_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + 2000); } +/** + * Check whether USB host device has a USB mass storage SCSI interface + */ +bool usb_host_dev_is_scsi_storage(USBDevice *ud) +{ + USBHostDevice *uhd = USB_HOST_DEVICE(ud); + struct libusb_config_descriptor *conf; + const struct libusb_interface_descriptor *intf; + bool is_scsi_storage = false; + int i; + + if (!uhd || libusb_get_active_config_descriptor(uhd->dev, &conf) != 0) { + return false; + } + + for (i = 0; i < conf->bNumInterfaces; i++) { + intf = &conf->interface[i].altsetting[ud->altsetting[i]]; + if (intf->bInterfaceClass == LIBUSB_CLASS_MASS_STORAGE && + intf->bInterfaceSubClass == 6) { /* 6 means SCSI */ + is_scsi_storage = true; + break; + } + } + + libusb_free_config_descriptor(conf); + + return is_scsi_storage; +} + void hmp_info_usbhost(Monitor *mon, const QDict *qdict) { libusb_device **devs = NULL; diff --git a/hw/usb/host-stub.c b/hw/usb/host-stub.c index 6ba65a1..d0268ba 100644 --- a/hw/usb/host-stub.c +++ b/hw/usb/host-stub.c @@ -46,3 +46,8 @@ USBDevice *usb_host_device_open(USBBus *bus, const char *devname) { return NULL; } + +bool usb_host_dev_is_scsi_storage(USBDevice *ud) +{ + return false; +} |