From b2540203bdf4a390c3489146eae82ce237303653 Mon Sep 17 00:00:00 2001 From: Suraj Jitindar Singh Date: Tue, 12 Jun 2018 15:16:30 +1000 Subject: ppc/spapr_caps: Don't disable cap_cfpc on POWER8 by default In default_caps_with_cpu() we set spapr_cap_cfpc to broken for POWER8 processors and before. Since we no longer require private l1d cache on POWER8 for this cap to be set to workaround change this to default to broken for POWER7 processors and before. Signed-off-by: Suraj Jitindar Singh Reviewed-by: David Gibson Signed-off-by: David Gibson --- hw/ppc/spapr_caps.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'hw') diff --git a/hw/ppc/spapr_caps.c b/hw/ppc/spapr_caps.c index 531e145..00e43a9 100644 --- a/hw/ppc/spapr_caps.c +++ b/hw/ppc/spapr_caps.c @@ -335,14 +335,10 @@ static sPAPRCapabilities default_caps_with_cpu(sPAPRMachineState *spapr, caps = smc->default_caps; - if (!ppc_check_compat(cpu, CPU_POWERPC_LOGICAL_3_00, - 0, spapr->max_compat_pvr)) { - caps.caps[SPAPR_CAP_CFPC] = SPAPR_CAP_BROKEN; - } - if (!ppc_check_compat(cpu, CPU_POWERPC_LOGICAL_2_07, 0, spapr->max_compat_pvr)) { caps.caps[SPAPR_CAP_HTM] = SPAPR_CAP_OFF; + caps.caps[SPAPR_CAP_CFPC] = SPAPR_CAP_BROKEN; } if (!ppc_check_compat(cpu, CPU_POWERPC_LOGICAL_2_06_PLUS, -- cgit v1.1 From 2c9dfdacc5a9f4db941c8f80597abae4658954ac Mon Sep 17 00:00:00 2001 From: Greg Kurz Date: Tue, 12 Jun 2018 19:01:26 +0200 Subject: spapr: fix leak in h_client_architecture_support() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the negotiated compat mode can't be set, but raw mode is supported, we decide to ignore the error. An so, we should free it to prevent a memory leak. Signed-off-by: Greg Kurz Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: David Gibson --- hw/ppc/spapr_hcall.c | 1 + 1 file changed, 1 insertion(+) (limited to 'hw') diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c index 022f6d8..8b9a4b5 100644 --- a/hw/ppc/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -1547,6 +1547,7 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu, error_report_err(local_err); return H_HARDWARE; } + error_free(local_err); local_err = NULL; } } -- cgit v1.1 From 06fe3a5bf107e93a4478e9cb5ef306ca65a0dcc1 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 12 Jun 2018 17:43:56 +0100 Subject: ppc: introduce Core99MachinesState for the mac99 machine This is in preparation for adding configuration controlled via machine options. Signed-off-by: Mark Cave-Ayland Signed-off-by: David Gibson --- hw/ppc/mac.h | 11 +++++++++++ hw/ppc/mac_newworld.c | 7 +++++++ 2 files changed, 18 insertions(+) (limited to 'hw') diff --git a/hw/ppc/mac.h b/hw/ppc/mac.h index 89fa8bb..8046cd8a 100644 --- a/hw/ppc/mac.h +++ b/hw/ppc/mac.h @@ -27,6 +27,7 @@ #define PPC_MAC_H #include "exec/memory.h" +#include "hw/boards.h" #include "hw/sysbus.h" #include "hw/ide/internal.h" #include "hw/input/adb.h" @@ -65,6 +66,16 @@ #define NEWWORLD_IDE1_IRQ 0xe #define NEWWORLD_IDE1_DMA_IRQ 0x3 +/* Core99 machine */ +#define TYPE_CORE99_MACHINE MACHINE_TYPE_NAME("mac99") +#define CORE99_MACHINE(obj) OBJECT_CHECK(Core99MachineState, (obj), \ + TYPE_CORE99_MACHINE) + +typedef struct Core99MachineState { + /*< private >*/ + MachineState parent; +} Core99MachineState; + /* MacIO */ #define TYPE_MACIO_IDE "macio-ide" #define MACIO_IDE(obj) OBJECT_CHECK(MACIOIDEState, (obj), TYPE_MACIO_IDE) diff --git a/hw/ppc/mac_newworld.c b/hw/ppc/mac_newworld.c index 744acdf..5331aa0 100644 --- a/hw/ppc/mac_newworld.c +++ b/hw/ppc/mac_newworld.c @@ -515,10 +515,17 @@ static void core99_machine_class_init(ObjectClass *oc, void *data) #endif } +static void core99_instance_init(Object *obj) +{ + return; +} + static const TypeInfo core99_machine_info = { .name = MACHINE_TYPE_NAME("mac99"), .parent = TYPE_MACHINE, .class_init = core99_machine_class_init, + .instance_init = core99_instance_init, + .instance_size = sizeof(Core99MachineState) }; static void mac_machine_register_types(void) -- cgit v1.1 From f1114c17eeda907d0a290f25f970d695fc3b16de Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 12 Jun 2018 17:43:57 +0100 Subject: mac_newworld: add via machine option to control mac99 VIA/ADB configuration This option allows the VIA configuration to be controlled between 3 different possible setups: cuda, pmu-adb and pmu with USB rather than ADB keyboard/mouse. For the moment we don't do anything with the configuration except to pass it to the macio device (the via-cuda parent) and also to the firmware via the fw_cfg interface so that it can present the correct device tree. The default is cuda which is the current default and so will have no change in behaviour. Signed-off-by: Mark Cave-Ayland Signed-off-by: David Gibson --- hw/misc/macio/macio.c | 7 ++++++ hw/ppc/mac.h | 6 +++++ hw/ppc/mac_newworld.c | 69 +++++++++++++++++++++++++++++++++++++++++++++------ 3 files changed, 75 insertions(+), 7 deletions(-) (limited to 'hw') diff --git a/hw/misc/macio/macio.c b/hw/misc/macio/macio.c index f9a40ee..dddf743 100644 --- a/hw/misc/macio/macio.c +++ b/hw/misc/macio/macio.c @@ -399,6 +399,12 @@ static const VMStateDescription vmstate_macio_newworld = { } }; +static Property macio_newworld_properties[] = { + DEFINE_PROP_BOOL("has-pmu", NewWorldMacIOState, has_pmu, false), + DEFINE_PROP_BOOL("has-adb", NewWorldMacIOState, has_adb, false), + DEFINE_PROP_END_OF_LIST() +}; + static void macio_newworld_class_init(ObjectClass *oc, void *data) { PCIDeviceClass *pdc = PCI_DEVICE_CLASS(oc); @@ -407,6 +413,7 @@ static void macio_newworld_class_init(ObjectClass *oc, void *data) pdc->realize = macio_newworld_realize; pdc->device_id = PCI_DEVICE_ID_APPLE_UNI_N_KEYL; dc->vmsd = &vmstate_macio_newworld; + dc->props = macio_newworld_properties; } static Property macio_properties[] = { diff --git a/hw/ppc/mac.h b/hw/ppc/mac.h index 8046cd8a..4c08f52 100644 --- a/hw/ppc/mac.h +++ b/hw/ppc/mac.h @@ -71,9 +71,15 @@ #define CORE99_MACHINE(obj) OBJECT_CHECK(Core99MachineState, (obj), \ TYPE_CORE99_MACHINE) +#define CORE99_VIA_CONFIG_CUDA 0x0 +#define CORE99_VIA_CONFIG_PMU 0x1 +#define CORE99_VIA_CONFIG_PMU_ADB 0x2 + typedef struct Core99MachineState { /*< private >*/ MachineState parent; + + uint8_t via_config; } Core99MachineState; /* MacIO */ diff --git a/hw/ppc/mac_newworld.c b/hw/ppc/mac_newworld.c index 5331aa0..ca21d47 100644 --- a/hw/ppc/mac_newworld.c +++ b/hw/ppc/mac_newworld.c @@ -111,6 +111,7 @@ static void ppc_core99_init(MachineState *machine) const char *kernel_cmdline = machine->kernel_cmdline; const char *initrd_filename = machine->initrd_filename; const char *boot_device = machine->boot_order; + Core99MachineState *core99_machine = CORE99_MACHINE(machine); PowerPCCPU *cpu = NULL; CPUPPCState *env = NULL; char *filename; @@ -122,6 +123,7 @@ static void ppc_core99_init(MachineState *machine) UNINHostState *uninorth_pci; PCIBus *pci_bus; NewWorldMacIOState *macio; + bool has_pmu, has_adb; MACIOIDEState *macio_ide; BusState *adb_bus; MacIONVRAMState *nvr; @@ -361,6 +363,9 @@ static void ppc_core99_init(MachineState *machine) } machine->usb |= defaults_enabled() && !machine->usb_disabled; + has_pmu = (core99_machine->via_config != CORE99_VIA_CONFIG_CUDA); + has_adb = (core99_machine->via_config == CORE99_VIA_CONFIG_CUDA || + core99_machine->via_config == CORE99_VIA_CONFIG_PMU_ADB); /* Timebase Frequency */ if (kvm_enabled()) { @@ -376,6 +381,8 @@ static void ppc_core99_init(MachineState *machine) macio = NEWWORLD_MACIO(pci_create(pci_bus, -1, TYPE_NEWWORLD_MACIO)); dev = DEVICE(macio); qdev_prop_set_uint64(dev, "frequency", tbfreq); + qdev_prop_set_bit(dev, "has-pmu", has_pmu); + qdev_prop_set_bit(dev, "has-adb", has_adb); object_property_set_link(OBJECT(macio), OBJECT(pic_dev), "pic", &error_abort); qdev_init_nofail(dev); @@ -391,19 +398,21 @@ static void ppc_core99_init(MachineState *machine) "ide[1]")); macio_ide_init_drives(macio_ide, &hd[MAX_IDE_DEVS]); - dev = DEVICE(object_resolve_path_component(OBJECT(macio), "cuda")); - adb_bus = qdev_get_child_bus(dev, "adb.0"); - dev = qdev_create(adb_bus, TYPE_ADB_KEYBOARD); - qdev_init_nofail(dev); - dev = qdev_create(adb_bus, TYPE_ADB_MOUSE); - qdev_init_nofail(dev); + if (has_adb) { + dev = DEVICE(object_resolve_path_component(OBJECT(macio), "cuda")); + adb_bus = qdev_get_child_bus(dev, "adb.0"); + dev = qdev_create(adb_bus, TYPE_ADB_KEYBOARD); + qdev_init_nofail(dev); + dev = qdev_create(adb_bus, TYPE_ADB_MOUSE); + qdev_init_nofail(dev); + } if (machine->usb) { pci_create_simple(pci_bus, -1, "pci-ohci"); /* U3 needs to use USB for input because Linux doesn't support via-cuda on PPC64 */ - if (machine_arch == ARCH_MAC99_U3) { + if (!has_adb || machine_arch == ARCH_MAC99_U3) { USBBus *usb_bus = usb_bus_find(-1); usb_create_simple(usb_bus, "usb-kbd"); @@ -459,6 +468,8 @@ static void ppc_core99_init(MachineState *machine) 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_VIACONFIG, core99_machine->via_config); + fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_IS_KVM, kvm_enabled()); if (kvm_enabled()) { #ifdef CONFIG_KVM @@ -515,8 +526,52 @@ static void core99_machine_class_init(ObjectClass *oc, void *data) #endif } +static char *core99_get_via_config(Object *obj, Error **errp) +{ + Core99MachineState *cms = CORE99_MACHINE(obj); + + switch (cms->via_config) { + default: + case CORE99_VIA_CONFIG_CUDA: + return g_strdup("cuda"); + + case CORE99_VIA_CONFIG_PMU: + return g_strdup("pmu"); + + case CORE99_VIA_CONFIG_PMU_ADB: + return g_strdup("pmu-adb"); + } +} + +static void core99_set_via_config(Object *obj, const char *value, Error **errp) +{ + Core99MachineState *cms = CORE99_MACHINE(obj); + + if (!strcmp(value, "cuda")) { + cms->via_config = CORE99_VIA_CONFIG_CUDA; + } else if (!strcmp(value, "pmu")) { + cms->via_config = CORE99_VIA_CONFIG_PMU; + } else if (!strcmp(value, "pmu-adb")) { + cms->via_config = CORE99_VIA_CONFIG_PMU_ADB; + } else { + error_setg(errp, "Invalid via value"); + error_append_hint(errp, "Valid values are cuda, pmu, pmu-adb.\n"); + } +} + static void core99_instance_init(Object *obj) { + Core99MachineState *cms = CORE99_MACHINE(obj); + + /* Default via_config is CORE99_VIA_CONFIG_CUDA */ + cms->via_config = CORE99_VIA_CONFIG_CUDA; + object_property_add_str(obj, "via", core99_get_via_config, + core99_set_via_config, NULL); + object_property_set_description(obj, "via", + "Set VIA configuration. " + "Valid values are cuda, pmu and pmu-adb", + NULL); + return; } -- cgit v1.1 From 7c4166a971b54a65900d9624ccd9669ba99d75ad Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 12 Jun 2018 17:43:58 +0100 Subject: mac_newworld: add gpios to macio devices with PMU enabled PMU-enabled New World Macs expose their GPIOs via a separate memory region within the macio device. Signed-off-by: Mark Cave-Ayland Signed-off-by: David Gibson --- hw/misc/macio/Makefile.objs | 1 + hw/misc/macio/gpio.c | 218 ++++++++++++++++++++++++++++++++++++++++++++ hw/misc/macio/macio.c | 13 +++ hw/misc/macio/trace-events | 7 ++ hw/ppc/mac.h | 2 + 5 files changed, 241 insertions(+) create mode 100644 hw/misc/macio/gpio.c (limited to 'hw') diff --git a/hw/misc/macio/Makefile.objs b/hw/misc/macio/Makefile.objs index ef7ac24..fb9dbf9 100644 --- a/hw/misc/macio/Makefile.objs +++ b/hw/misc/macio/Makefile.objs @@ -1,3 +1,4 @@ common-obj-y += macio.o common-obj-$(CONFIG_CUDA) += cuda.o common-obj-$(CONFIG_MAC_DBDMA) += mac_dbdma.o +common-obj-$(CONFIG_MACIO_GPIO) += gpio.o diff --git a/hw/misc/macio/gpio.c b/hw/misc/macio/gpio.c new file mode 100644 index 0000000..5630afd --- /dev/null +++ b/hw/misc/macio/gpio.c @@ -0,0 +1,218 @@ +/* + * PowerMac NewWorld MacIO GPIO emulation + * + * Copyright (c) 2016 Benjamin Herrenschmidt + * Copyright (c) 2018 Mark Cave-Ayland + * + * 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/hw.h" +#include "hw/ppc/mac.h" +#include "hw/misc/macio/macio.h" +#include "hw/misc/macio/gpio.h" +#include "qemu/log.h" +#include "trace.h" + + +void macio_set_gpio(MacIOGPIOState *s, uint32_t gpio, bool state) +{ + uint8_t new_reg; + + trace_macio_set_gpio(gpio, state); + + if (s->gpio_regs[gpio] & 4) { + qemu_log_mask(LOG_GUEST_ERROR, + "GPIO: Setting GPIO %d while it's an output\n", gpio); + } + + new_reg = s->gpio_regs[gpio] & ~2; + if (state) { + new_reg |= 2; + } + + if (new_reg == s->gpio_regs[gpio]) { + return; + } + + s->gpio_regs[gpio] = new_reg; + + /* This is will work until we fix the binding between MacIO and + * the MPIC properly so we can route all GPIOs and avoid going + * via the top level platform code. + * + * Note that we probably need to get access to the MPIC config to + * decode polarity since qemu always use "raise" regardless. + * + * For now, we hard wire known GPIOs + */ + + switch (gpio) { + case 1: + /* Level low */ + if (!state) { + trace_macio_gpio_irq_assert(gpio); + qemu_irq_raise(s->gpio_extirqs[gpio]); + } else { + trace_macio_gpio_irq_deassert(gpio); + qemu_irq_lower(s->gpio_extirqs[gpio]); + } + break; + + case 9: + /* Edge, triggered by NMI below */ + if (state) { + trace_macio_gpio_irq_assert(gpio); + qemu_irq_raise(s->gpio_extirqs[gpio]); + } else { + trace_macio_gpio_irq_deassert(gpio); + qemu_irq_lower(s->gpio_extirqs[gpio]); + } + break; + + default: + qemu_log_mask(LOG_UNIMP, "GPIO: setting unimplemented GPIO %d", gpio); + } +} + +static void macio_gpio_write(void *opaque, hwaddr addr, uint64_t value, + unsigned size) +{ + MacIOGPIOState *s = opaque; + uint8_t ibit; + + trace_macio_gpio_write(addr, value); + + /* Levels regs are read-only */ + if (addr < 8) { + return; + } + + addr -= 8; + if (addr < 36) { + value &= ~2; + + if (value & 4) { + ibit = (value & 1) << 1; + } else { + ibit = s->gpio_regs[addr] & 2; + } + + s->gpio_regs[addr] = value | ibit; + } +} + +static uint64_t macio_gpio_read(void *opaque, hwaddr addr, unsigned size) +{ + MacIOGPIOState *s = opaque; + uint64_t val = 0; + + /* Levels regs */ + if (addr < 8) { + val = s->gpio_levels[addr]; + } else { + addr -= 8; + + if (addr < 36) { + val = s->gpio_regs[addr]; + } + } + + trace_macio_gpio_write(addr, val); + return val; +} + +static const MemoryRegionOps macio_gpio_ops = { + .read = macio_gpio_read, + .write = macio_gpio_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + .min_access_size = 1, + .max_access_size = 1, + }, +}; + +static void macio_gpio_realize(DeviceState *dev, Error **errp) +{ + MacIOGPIOState *s = MACIO_GPIO(dev); + + s->gpio_extirqs[1] = qdev_get_gpio_in(DEVICE(s->pic), + NEWWORLD_EXTING_GPIO1); + s->gpio_extirqs[9] = qdev_get_gpio_in(DEVICE(s->pic), + NEWWORLD_EXTING_GPIO9); +} + +static void macio_gpio_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + MacIOGPIOState *s = MACIO_GPIO(obj); + + object_property_add_link(obj, "pic", TYPE_OPENPIC, + (Object **) &s->pic, + qdev_prop_allow_set_link_before_realize, + 0, NULL); + + memory_region_init_io(&s->gpiomem, OBJECT(s), &macio_gpio_ops, obj, + "gpio", 0x30); + sysbus_init_mmio(sbd, &s->gpiomem); +} + +static const VMStateDescription vmstate_macio_gpio = { + .name = "macio_gpio", + .version_id = 0, + .minimum_version_id = 0, + .fields = (VMStateField[]) { + VMSTATE_UINT8_ARRAY(gpio_levels, MacIOGPIOState, 8), + VMSTATE_UINT8_ARRAY(gpio_regs, MacIOGPIOState, 36), + VMSTATE_END_OF_LIST() + } +}; + +static void macio_gpio_reset(DeviceState *dev) +{ + MacIOGPIOState *s = MACIO_GPIO(dev); + + /* GPIO 1 is up by default */ + macio_set_gpio(s, 1, true); +} + +static void macio_gpio_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = macio_gpio_realize; + dc->reset = macio_gpio_reset; + dc->vmsd = &vmstate_macio_gpio; +} + +static const TypeInfo macio_gpio_init_info = { + .name = TYPE_MACIO_GPIO, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(MacIOGPIOState), + .instance_init = macio_gpio_init, + .class_init = macio_gpio_class_init, +}; + +static void macio_gpio_register_types(void) +{ + type_register_static(&macio_gpio_init_info); +} + +type_init(macio_gpio_register_types) diff --git a/hw/misc/macio/macio.c b/hw/misc/macio/macio.c index dddf743..8dfcbc3 100644 --- a/hw/misc/macio/macio.c +++ b/hw/misc/macio/macio.c @@ -332,6 +332,16 @@ static void macio_newworld_realize(PCIDevice *d, Error **errp) memory_region_init_io(timer_memory, OBJECT(s), &timer_ops, NULL, "timer", 0x1000); memory_region_add_subregion(&s->bar, 0x15000, timer_memory); + + if (ns->has_pmu) { + /* GPIOs */ + sysbus_dev = SYS_BUS_DEVICE(&ns->gpio); + object_property_set_link(OBJECT(&ns->gpio), OBJECT(pic_dev), "pic", + &error_abort); + memory_region_add_subregion(&s->bar, 0x50, + sysbus_mmio_get_region(sysbus_dev, 0)); + object_property_set_bool(OBJECT(&ns->gpio), true, "realized", &err); + } } static void macio_newworld_init(Object *obj) @@ -345,6 +355,9 @@ static void macio_newworld_init(Object *obj) qdev_prop_allow_set_link_before_realize, 0, NULL); + object_initialize(&ns->gpio, sizeof(ns->gpio), TYPE_MACIO_GPIO); + qdev_set_parent_bus(DEVICE(&ns->gpio), sysbus_get_default()); + for (i = 0; i < 2; i++) { macio_init_ide(s, &ns->ide[i], sizeof(ns->ide[i]), i); } diff --git a/hw/misc/macio/trace-events b/hw/misc/macio/trace-events index d499d78..71c4752 100644 --- a/hw/misc/macio/trace-events +++ b/hw/misc/macio/trace-events @@ -13,3 +13,10 @@ cuda_packet_send_data(int i, const uint8_t data) "[%d] 0x%02x" # hw/misc/macio/macio.c macio_timer_write(uint64_t addr, unsigned len, uint64_t val) "write addr 0x%"PRIx64 " len %d val 0x%"PRIx64 macio_timer_read(uint64_t addr, unsigned len, uint32_t val) "read addr 0x%"PRIx64 " len %d val 0x%"PRIx32 + +# hw/misc/macio/gpio.c +macio_set_gpio(int gpio, bool state) "setting GPIO %d to %d" +macio_gpio_irq_assert(int gpio) "asserting GPIO %d" +macio_gpio_irq_deassert(int gpio) "deasserting GPIO %d" +macio_gpio_write(uint64_t addr, uint64_t val) "addr: 0x%"PRIx64" value: 0x%"PRIx64 +macio_gpio_read(uint64_t addr, uint64_t val) "addr: 0x%"PRIx64" value: 0x%"PRIx64 diff --git a/hw/ppc/mac.h b/hw/ppc/mac.h index 4c08f52..b3b7f9d 100644 --- a/hw/ppc/mac.h +++ b/hw/ppc/mac.h @@ -65,6 +65,8 @@ #define NEWWORLD_IDE0_DMA_IRQ 0x2 #define NEWWORLD_IDE1_IRQ 0xe #define NEWWORLD_IDE1_DMA_IRQ 0x3 +#define NEWWORLD_EXTING_GPIO1 0x2f +#define NEWWORLD_EXTING_GPIO9 0x37 /* Core99 machine */ #define TYPE_CORE99_MACHINE MACHINE_TYPE_NAME("mac99") -- cgit v1.1 From 8f55ac13049f3c737373d9de8598a2a03e6a03f9 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 12 Jun 2018 17:43:59 +0100 Subject: mac_newworld: wire up programmer switch to NMI handler The programmer switch is wired up via an external GPIO pin and can be used to aid debugging Mac guests. Signed-off-by: Mark Cave-Ayland Signed-off-by: David Gibson --- hw/misc/macio/gpio.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'hw') diff --git a/hw/misc/macio/gpio.c b/hw/misc/macio/gpio.c index 5630afd..9317df7 100644 --- a/hw/misc/macio/gpio.c +++ b/hw/misc/macio/gpio.c @@ -28,6 +28,7 @@ #include "hw/ppc/mac.h" #include "hw/misc/macio/macio.h" #include "hw/misc/macio/gpio.h" +#include "hw/nmi.h" #include "qemu/log.h" #include "trace.h" @@ -193,13 +194,21 @@ static void macio_gpio_reset(DeviceState *dev) macio_set_gpio(s, 1, true); } +static void macio_gpio_nmi(NMIState *n, int cpu_index, Error **errp) +{ + macio_set_gpio(MACIO_GPIO(n), 9, true); + macio_set_gpio(MACIO_GPIO(n), 9, false); +} + static void macio_gpio_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); + NMIClass *nc = NMI_CLASS(oc); dc->realize = macio_gpio_realize; dc->reset = macio_gpio_reset; dc->vmsd = &vmstate_macio_gpio; + nc->nmi_monitor_handler = macio_gpio_nmi; } static const TypeInfo macio_gpio_init_info = { @@ -208,6 +217,10 @@ static const TypeInfo macio_gpio_init_info = { .instance_size = sizeof(MacIOGPIOState), .instance_init = macio_gpio_init, .class_init = macio_gpio_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_NMI }, + { } + }, }; static void macio_gpio_register_types(void) -- cgit v1.1 From fb6649f172d6ea1a8d8980b7f93d31808eb06ff8 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 12 Jun 2018 17:44:00 +0100 Subject: adb: fix read reg 3 byte ordering According to the Apple ADB documentation, register 3 is a 2-byte register with the device address in the first byte, and the handler ID in the second byte. This is currently the opposite away to which QEMU returns them so switch the order around. Signed-off-by: Mark Cave-Ayland Signed-off-by: David Gibson --- hw/input/adb-kbd.c | 4 ++-- hw/input/adb-mouse.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'hw') diff --git a/hw/input/adb-kbd.c b/hw/input/adb-kbd.c index 50b6271..0ad384d 100644 --- a/hw/input/adb-kbd.c +++ b/hw/input/adb-kbd.c @@ -290,8 +290,8 @@ static int adb_kbd_request(ADBDevice *d, uint8_t *obuf, olen = 2; break; case 3: - obuf[0] = d->handler; - obuf[1] = d->devaddr; + obuf[0] = d->devaddr; + obuf[1] = d->handler; olen = 2; break; } diff --git a/hw/input/adb-mouse.c b/hw/input/adb-mouse.c index 3ba6027..473045f 100644 --- a/hw/input/adb-mouse.c +++ b/hw/input/adb-mouse.c @@ -172,8 +172,8 @@ static int adb_mouse_request(ADBDevice *d, uint8_t *obuf, case 1: break; case 3: - obuf[0] = d->handler; - obuf[1] = d->devaddr; + obuf[0] = d->devaddr; + obuf[1] = d->handler; olen = 2; break; } -- cgit v1.1 From 84051eb400495745035b52e27fe67b962b7a58fa Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 12 Jun 2018 17:44:01 +0100 Subject: adb: add property to disable direct reg 3 writes MacOS 9 has a bug in its PMU driver whereby after configuring the ADB bus devices it sends another write to reg 3 on both devices resetting them both back to the same address. Add a new disable_direct_reg3_writes property to ADBDevice to disable these direct writes which can enabled just for the upcoming pmu-adb support. Signed-off-by: Mark Cave-Ayland Signed-off-by: David Gibson --- hw/input/adb-kbd.c | 25 ++++++++++++++----------- hw/input/adb-mouse.c | 37 ++++++++++++++++++++----------------- hw/input/adb.c | 7 +++++++ 3 files changed, 41 insertions(+), 28 deletions(-) (limited to 'hw') diff --git a/hw/input/adb-kbd.c b/hw/input/adb-kbd.c index 0ad384d..b026e9d 100644 --- a/hw/input/adb-kbd.c +++ b/hw/input/adb-kbd.c @@ -261,18 +261,21 @@ static int adb_kbd_request(ADBDevice *d, uint8_t *obuf, trace_adb_kbd_request_change_addr(d->devaddr); break; default: - d->devaddr = buf[1] & 0xf; - /* we support handlers: - * 1: Apple Standard Keyboard - * 2: Apple Extended Keyboard (LShift = RShift) - * 3: Apple Extended Keyboard (LShift != RShift) - */ - if (buf[2] == 1 || buf[2] == 2 || buf[2] == 3) { - d->handler = buf[2]; + if (!d->disable_direct_reg3_writes) { + d->devaddr = buf[1] & 0xf; + + /* we support handlers: + * 1: Apple Standard Keyboard + * 2: Apple Extended Keyboard (LShift = RShift) + * 3: Apple Extended Keyboard (LShift != RShift) + */ + if (buf[2] == 1 || buf[2] == 2 || buf[2] == 3) { + d->handler = buf[2]; + } + + trace_adb_kbd_request_change_addr_and_handler(d->devaddr, + d->handler); } - - trace_adb_kbd_request_change_addr_and_handler(d->devaddr, - d->handler); break; } } diff --git a/hw/input/adb-mouse.c b/hw/input/adb-mouse.c index 473045f..83833b0 100644 --- a/hw/input/adb-mouse.c +++ b/hw/input/adb-mouse.c @@ -142,24 +142,27 @@ static int adb_mouse_request(ADBDevice *d, uint8_t *obuf, trace_adb_mouse_request_change_addr(d->devaddr); break; default: - d->devaddr = buf[1] & 0xf; - /* we support handlers: - * 0x01: Classic Apple Mouse Protocol / 100 cpi operations - * 0x02: Classic Apple Mouse Protocol / 200 cpi operations - * we don't support handlers (at least): - * 0x03: Mouse systems A3 trackball - * 0x04: Extended Apple Mouse Protocol - * 0x2f: Microspeed mouse - * 0x42: Macally - * 0x5f: Microspeed mouse - * 0x66: Microspeed mouse - */ - if (buf[2] == 1 || buf[2] == 2) { - d->handler = buf[2]; + if (!d->disable_direct_reg3_writes) { + d->devaddr = buf[1] & 0xf; + + /* we support handlers: + * 0x01: Classic Apple Mouse Protocol / 100 cpi operations + * 0x02: Classic Apple Mouse Protocol / 200 cpi operations + * we don't support handlers (at least): + * 0x03: Mouse systems A3 trackball + * 0x04: Extended Apple Mouse Protocol + * 0x2f: Microspeed mouse + * 0x42: Macally + * 0x5f: Microspeed mouse + * 0x66: Microspeed mouse + */ + if (buf[2] == 1 || buf[2] == 2) { + d->handler = buf[2]; + } + + trace_adb_mouse_request_change_addr_and_handler( + d->devaddr, d->handler); } - - trace_adb_mouse_request_change_addr_and_handler(d->devaddr, - d->handler); break; } } diff --git a/hw/input/adb.c b/hw/input/adb.c index 23ae6f0..bbb40ae 100644 --- a/hw/input/adb.c +++ b/hw/input/adb.c @@ -113,11 +113,18 @@ static void adb_device_realizefn(DeviceState *dev, Error **errp) bus->devices[bus->nb_devices++] = d; } +static Property adb_device_properties[] = { + DEFINE_PROP_BOOL("disable-direct-reg3-writes", ADBDevice, + disable_direct_reg3_writes, false), + DEFINE_PROP_END_OF_LIST(), +}; + static void adb_device_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); dc->realize = adb_device_realizefn; + dc->props = adb_device_properties; dc->bus_type = TYPE_ADB_BUS; } -- cgit v1.1 From d811d61fbc6ca5f2be2185fd7cfa916e7ba613ce Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 12 Jun 2018 17:44:02 +0100 Subject: mac_newworld: add PMU device The PMU device supercedes the CUDA device found on older New World Macs and is supported by a larger number of guest OSs from OS 9 to OS X 10.5. Signed-off-by: Mark Cave-Ayland Signed-off-by: David Gibson --- hw/misc/macio/Makefile.objs | 1 + hw/misc/macio/macio.c | 69 +++- hw/misc/macio/pmu.c | 871 ++++++++++++++++++++++++++++++++++++++++++++ hw/misc/macio/trace-events | 21 ++ hw/ppc/mac.h | 1 + hw/ppc/mac_newworld.c | 10 +- 6 files changed, 953 insertions(+), 20 deletions(-) create mode 100644 hw/misc/macio/pmu.c (limited to 'hw') diff --git a/hw/misc/macio/Makefile.objs b/hw/misc/macio/Makefile.objs index fb9dbf9..07fdb32 100644 --- a/hw/misc/macio/Makefile.objs +++ b/hw/misc/macio/Makefile.objs @@ -1,4 +1,5 @@ common-obj-y += macio.o common-obj-$(CONFIG_CUDA) += cuda.o +common-obj-$(CONFIG_MAC_PMU) += pmu.o common-obj-$(CONFIG_MAC_DBDMA) += mac_dbdma.o common-obj-$(CONFIG_MACIO_GPIO) += gpio.o diff --git a/hw/misc/macio/macio.c b/hw/misc/macio/macio.c index 8dfcbc3..d135e3b 100644 --- a/hw/misc/macio/macio.c +++ b/hw/misc/macio/macio.c @@ -105,17 +105,6 @@ static void macio_common_realize(PCIDevice *d, Error **errp) memory_region_add_subregion(&s->bar, 0x08000, sysbus_mmio_get_region(sysbus_dev, 0)); - qdev_prop_set_uint64(DEVICE(&s->cuda), "timebase-frequency", - s->frequency); - object_property_set_bool(OBJECT(&s->cuda), true, "realized", &err); - if (err) { - error_propagate(errp, err); - return; - } - sysbus_dev = SYS_BUS_DEVICE(&s->cuda); - memory_region_add_subregion(&s->bar, 0x16000, - sysbus_mmio_get_region(sysbus_dev, 0)); - qdev_prop_set_uint32(DEVICE(&s->escc), "disabled", 0); qdev_prop_set_uint32(DEVICE(&s->escc), "frequency", ESCC_CLOCK); qdev_prop_set_uint32(DEVICE(&s->escc), "it_shift", 4); @@ -163,7 +152,16 @@ static void macio_oldworld_realize(PCIDevice *d, Error **errp) return; } + qdev_prop_set_uint64(DEVICE(&s->cuda), "timebase-frequency", + s->frequency); + object_property_set_bool(OBJECT(&s->cuda), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } sysbus_dev = SYS_BUS_DEVICE(&s->cuda); + memory_region_add_subregion(&s->bar, 0x16000, + sysbus_mmio_get_region(sysbus_dev, 0)); sysbus_connect_irq(sysbus_dev, 0, qdev_get_gpio_in(pic_dev, OLDWORLD_CUDA_IRQ)); @@ -234,6 +232,10 @@ static void macio_oldworld_init(Object *obj) qdev_prop_allow_set_link_before_realize, 0, NULL); + object_initialize(&s->cuda, sizeof(s->cuda), TYPE_CUDA); + qdev_set_parent_bus(DEVICE(&s->cuda), sysbus_get_default()); + object_property_add_child(obj, "cuda", OBJECT(&s->cuda), NULL); + object_initialize(&os->nvram, sizeof(os->nvram), TYPE_MACIO_NVRAM); dev = DEVICE(&os->nvram); qdev_prop_set_uint32(dev, "size", 0x2000); @@ -293,10 +295,6 @@ static void macio_newworld_realize(PCIDevice *d, Error **errp) return; } - sysbus_dev = SYS_BUS_DEVICE(&s->cuda); - sysbus_connect_irq(sysbus_dev, 0, qdev_get_gpio_in(pic_dev, - NEWWORLD_CUDA_IRQ)); - sysbus_dev = SYS_BUS_DEVICE(&s->escc); sysbus_connect_irq(sysbus_dev, 0, qdev_get_gpio_in(pic_dev, NEWWORLD_ESCCB_IRQ)); @@ -341,6 +339,43 @@ static void macio_newworld_realize(PCIDevice *d, Error **errp) memory_region_add_subregion(&s->bar, 0x50, sysbus_mmio_get_region(sysbus_dev, 0)); object_property_set_bool(OBJECT(&ns->gpio), true, "realized", &err); + + /* PMU */ + object_initialize(&s->pmu, sizeof(s->pmu), TYPE_VIA_PMU); + object_property_set_link(OBJECT(&s->pmu), OBJECT(sysbus_dev), "gpio", + &error_abort); + qdev_prop_set_bit(DEVICE(&s->pmu), "has-adb", ns->has_adb); + qdev_set_parent_bus(DEVICE(&s->pmu), sysbus_get_default()); + object_property_add_child(OBJECT(s), "pmu", OBJECT(&s->pmu), NULL); + + object_property_set_bool(OBJECT(&s->pmu), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_dev = SYS_BUS_DEVICE(&s->pmu); + sysbus_connect_irq(sysbus_dev, 0, qdev_get_gpio_in(pic_dev, + NEWWORLD_PMU_IRQ)); + memory_region_add_subregion(&s->bar, 0x16000, + sysbus_mmio_get_region(sysbus_dev, 0)); + } else { + /* CUDA */ + object_initialize(&s->cuda, sizeof(s->cuda), TYPE_CUDA); + qdev_set_parent_bus(DEVICE(&s->cuda), sysbus_get_default()); + object_property_add_child(OBJECT(s), "cuda", OBJECT(&s->cuda), NULL); + qdev_prop_set_uint64(DEVICE(&s->cuda), "timebase-frequency", + s->frequency); + + object_property_set_bool(OBJECT(&s->cuda), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_dev = SYS_BUS_DEVICE(&s->cuda); + sysbus_connect_irq(sysbus_dev, 0, qdev_get_gpio_in(pic_dev, + NEWWORLD_CUDA_IRQ)); + memory_region_add_subregion(&s->bar, 0x16000, + sysbus_mmio_get_region(sysbus_dev, 0)); } } @@ -369,10 +404,6 @@ static void macio_instance_init(Object *obj) memory_region_init(&s->bar, obj, "macio", 0x80000); - object_initialize(&s->cuda, sizeof(s->cuda), TYPE_CUDA); - qdev_set_parent_bus(DEVICE(&s->cuda), sysbus_get_default()); - object_property_add_child(obj, "cuda", OBJECT(&s->cuda), NULL); - object_initialize(&s->dbdma, sizeof(s->dbdma), TYPE_MAC_DBDMA); qdev_set_parent_bus(DEVICE(&s->dbdma), sysbus_get_default()); object_property_add_child(obj, "dbdma", OBJECT(&s->dbdma), NULL); diff --git a/hw/misc/macio/pmu.c b/hw/misc/macio/pmu.c new file mode 100644 index 0000000..e246b0f --- /dev/null +++ b/hw/misc/macio/pmu.c @@ -0,0 +1,871 @@ +/* + * QEMU PowerMac PMU device support + * + * Copyright (c) 2016 Benjamin Herrenschmidt, IBM Corp. + * Copyright (c) 2018 Mark Cave-Ayland + * + * Based on the CUDA device by: + * + * Copyright (c) 2004-2007 Fabrice Bellard + * Copyright (c) 2007 Jocelyn Mayer + * + * 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/hw.h" +#include "hw/ppc/mac.h" +#include "hw/input/adb.h" +#include "hw/misc/mos6522.h" +#include "hw/misc/macio/gpio.h" +#include "hw/misc/macio/pmu.h" +#include "qemu/timer.h" +#include "sysemu/sysemu.h" +#include "qemu/cutils.h" +#include "qemu/log.h" +#include "trace.h" + + +/* Bits in B data register: all active low */ +#define TACK 0x08 /* Transfer request (input) */ +#define TREQ 0x10 /* Transfer acknowledge (output) */ + +/* PMU returns time_t's offset from Jan 1, 1904, not 1970 */ +#define RTC_OFFSET 2082844800 + +#define VIA_TIMER_FREQ (4700000 / 6) + +static void via_update_irq(PMUState *s) +{ + MOS6522PMUState *mps = MOS6522_PMU(&s->mos6522_pmu); + MOS6522State *ms = MOS6522(mps); + + bool new_state = !!(ms->ifr & ms->ier & (SR_INT | T1_INT | T2_INT)); + + if (new_state != s->via_irq_state) { + s->via_irq_state = new_state; + qemu_set_irq(s->via_irq, new_state); + } +} + +static void via_set_sr_int(void *opaque) +{ + PMUState *s = opaque; + MOS6522PMUState *mps = MOS6522_PMU(&s->mos6522_pmu); + MOS6522State *ms = MOS6522(mps); + MOS6522DeviceClass *mdc = MOS6522_DEVICE_GET_CLASS(ms); + + mdc->set_sr_int(ms); +} + +static void pmu_update_extirq(PMUState *s) +{ + if ((s->intbits & s->intmask) != 0) { + macio_set_gpio(s->gpio, 1, false); + } else { + macio_set_gpio(s->gpio, 1, true); + } +} + +static void pmu_adb_poll(void *opaque) +{ + PMUState *s = opaque; + int olen; + + if (!(s->intbits & PMU_INT_ADB)) { + olen = adb_poll(&s->adb_bus, s->adb_reply, s->adb_poll_mask); + trace_pmu_adb_poll(olen); + + if (olen > 0) { + s->adb_reply_size = olen; + s->intbits |= PMU_INT_ADB | PMU_INT_ADB_AUTO; + pmu_update_extirq(s); + } + } + + timer_mod(s->adb_poll_timer, + qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 30); +} + +static void pmu_one_sec_timer(void *opaque) +{ + PMUState *s = opaque; + + trace_pmu_one_sec_timer(); + + s->intbits |= PMU_INT_TICK; + pmu_update_extirq(s); + s->one_sec_target += 1000; + + timer_mod(s->one_sec_timer, s->one_sec_target); +} + +static void pmu_cmd_int_ack(PMUState *s, + const uint8_t *in_data, uint8_t in_len, + uint8_t *out_data, uint8_t *out_len) +{ + if (in_len != 0) { + qemu_log_mask(LOG_GUEST_ERROR, + "PMU: INT_ACK command, invalid len: %d want: 0\n", + in_len); + return; + } + + /* Make appropriate reply packet */ + if (s->intbits & PMU_INT_ADB) { + if (!s->adb_reply_size) { + qemu_log_mask(LOG_GUEST_ERROR, + "Odd, PMU_INT_ADB set with no reply in buffer\n"); + } + + memcpy(out_data + 1, s->adb_reply, s->adb_reply_size); + out_data[0] = s->intbits & (PMU_INT_ADB | PMU_INT_ADB_AUTO); + *out_len = s->adb_reply_size + 1; + s->intbits &= ~(PMU_INT_ADB | PMU_INT_ADB_AUTO); + s->adb_reply_size = 0; + } else { + out_data[0] = s->intbits; + s->intbits = 0; + *out_len = 1; + } + + pmu_update_extirq(s); +} + +static void pmu_cmd_set_int_mask(PMUState *s, + const uint8_t *in_data, uint8_t in_len, + uint8_t *out_data, uint8_t *out_len) +{ + if (in_len != 1) { + qemu_log_mask(LOG_GUEST_ERROR, + "PMU: SET_INT_MASK command, invalid len: %d want: 1\n", + in_len); + return; + } + + trace_pmu_cmd_set_int_mask(s->intmask); + s->intmask = in_data[0]; + + pmu_update_extirq(s); +} + +static void pmu_cmd_set_adb_autopoll(PMUState *s, uint16_t mask) +{ + trace_pmu_cmd_set_adb_autopoll(mask); + + if (s->autopoll_mask == mask) { + return; + } + + s->autopoll_mask = mask; + if (mask) { + timer_mod(s->adb_poll_timer, + qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 30); + } else { + timer_del(s->adb_poll_timer); + } +} + +static void pmu_cmd_adb(PMUState *s, + const uint8_t *in_data, uint8_t in_len, + uint8_t *out_data, uint8_t *out_len) +{ + int len, adblen; + uint8_t adb_cmd[255]; + + if (in_len < 2) { + qemu_log_mask(LOG_GUEST_ERROR, + "PMU: ADB PACKET, invalid len: %d want at least 2\n", + in_len); + return; + } + + *out_len = 0; + + if (!s->has_adb) { + trace_pmu_cmd_adb_nobus(); + return; + } + + /* Set autopoll is a special form of the command */ + if (in_data[0] == 0 && in_data[1] == 0x86) { + uint16_t mask = in_data[2]; + mask = (mask << 8) | in_data[3]; + if (in_len != 4) { + qemu_log_mask(LOG_GUEST_ERROR, + "PMU: ADB Autopoll requires 4 bytes, got %d\n", + in_len); + return; + } + + pmu_cmd_set_adb_autopoll(s, mask); + return; + } + + trace_pmu_cmd_adb_request(in_len, in_data[0], in_data[1], in_data[2], + in_data[3], in_data[4]); + + *out_len = 0; + + /* Check ADB len */ + adblen = in_data[2]; + if (adblen > (in_len - 3)) { + qemu_log_mask(LOG_GUEST_ERROR, + "PMU: ADB len is %d > %d (in_len -3)...erroring\n", + adblen, in_len - 3); + len = -1; + } else if (adblen > 252) { + qemu_log_mask(LOG_GUEST_ERROR, "PMU: ADB command too big!\n"); + len = -1; + } else { + /* Format command */ + adb_cmd[0] = in_data[0]; + memcpy(&adb_cmd[1], &in_data[3], in_len - 3); + len = adb_request(&s->adb_bus, s->adb_reply + 2, adb_cmd, in_len - 2); + + trace_pmu_cmd_adb_reply(len); + } + + if (len > 0) { + /* XXX Check this */ + s->adb_reply_size = len + 2; + s->adb_reply[0] = 0x01; + s->adb_reply[1] = len; + } else { + /* XXX Check this */ + s->adb_reply_size = 1; + s->adb_reply[0] = 0x00; + } + + s->intbits |= PMU_INT_ADB; + pmu_update_extirq(s); +} + +static void pmu_cmd_adb_poll_off(PMUState *s, + const uint8_t *in_data, uint8_t in_len, + uint8_t *out_data, uint8_t *out_len) +{ + if (in_len != 0) { + qemu_log_mask(LOG_GUEST_ERROR, + "PMU: ADB POLL OFF command, invalid len: %d want: 0\n", + in_len); + return; + } + + if (s->has_adb && s->autopoll_mask) { + timer_del(s->adb_poll_timer); + s->autopoll_mask = false; + } +} + +static void pmu_cmd_shutdown(PMUState *s, + const uint8_t *in_data, uint8_t in_len, + uint8_t *out_data, uint8_t *out_len) +{ + if (in_len != 4) { + qemu_log_mask(LOG_GUEST_ERROR, + "PMU: SHUTDOWN command, invalid len: %d want: 4\n", + in_len); + return; + } + + *out_len = 1; + out_data[0] = 0; + + if (in_data[0] != 'M' || in_data[1] != 'A' || in_data[2] != 'T' || + in_data[3] != 'T') { + + qemu_log_mask(LOG_GUEST_ERROR, + "PMU: SHUTDOWN command, Bad MATT signature\n"); + return; + } + + qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); +} + +static void pmu_cmd_reset(PMUState *s, + const uint8_t *in_data, uint8_t in_len, + uint8_t *out_data, uint8_t *out_len) +{ + if (in_len != 0) { + qemu_log_mask(LOG_GUEST_ERROR, + "PMU: RESET command, invalid len: %d want: 0\n", + in_len); + return; + } + + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); +} + +static void pmu_cmd_get_rtc(PMUState *s, + const uint8_t *in_data, uint8_t in_len, + uint8_t *out_data, uint8_t *out_len) +{ + uint32_t ti; + + if (in_len != 0) { + qemu_log_mask(LOG_GUEST_ERROR, + "PMU: GET_RTC command, invalid len: %d want: 0\n", + in_len); + return; + } + + ti = s->tick_offset + (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + / NANOSECONDS_PER_SECOND); + out_data[0] = ti >> 24; + out_data[1] = ti >> 16; + out_data[2] = ti >> 8; + out_data[3] = ti; + *out_len = 4; +} + +static void pmu_cmd_set_rtc(PMUState *s, + const uint8_t *in_data, uint8_t in_len, + uint8_t *out_data, uint8_t *out_len) +{ + uint32_t ti; + + if (in_len != 4) { + qemu_log_mask(LOG_GUEST_ERROR, + "PMU: SET_RTC command, invalid len: %d want: 4\n", + in_len); + return; + } + + ti = (((uint32_t)in_data[0]) << 24) + (((uint32_t)in_data[1]) << 16) + + (((uint32_t)in_data[2]) << 8) + in_data[3]; + + s->tick_offset = ti - (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + / NANOSECONDS_PER_SECOND); +} + +static void pmu_cmd_system_ready(PMUState *s, + const uint8_t *in_data, uint8_t in_len, + uint8_t *out_data, uint8_t *out_len) +{ + /* Do nothing */ +} + +static void pmu_cmd_get_version(PMUState *s, + const uint8_t *in_data, uint8_t in_len, + uint8_t *out_data, uint8_t *out_len) +{ + *out_len = 1; + *out_data = 1; /* ??? Check what Apple does */ +} + +static void pmu_cmd_power_events(PMUState *s, + const uint8_t *in_data, uint8_t in_len, + uint8_t *out_data, uint8_t *out_len) +{ + if (in_len < 1) { + qemu_log_mask(LOG_GUEST_ERROR, + "PMU: POWER EVENTS command, invalid len %d, want at least 1\n", + in_len); + return; + } + + switch (in_data[0]) { + /* Dummies for now */ + case PMU_PWR_GET_POWERUP_EVENTS: + *out_len = 2; + out_data[0] = 0; + out_data[1] = 0; + break; + case PMU_PWR_SET_POWERUP_EVENTS: + case PMU_PWR_CLR_POWERUP_EVENTS: + break; + case PMU_PWR_GET_WAKEUP_EVENTS: + *out_len = 2; + out_data[0] = 0; + out_data[1] = 0; + break; + case PMU_PWR_SET_WAKEUP_EVENTS: + case PMU_PWR_CLR_WAKEUP_EVENTS: + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "PMU: POWER EVENTS unknown subcommand 0x%02x\n", + in_data[0]); + } +} + +static void pmu_cmd_get_cover(PMUState *s, + const uint8_t *in_data, uint8_t in_len, + uint8_t *out_data, uint8_t *out_len) +{ + /* Not 100% sure here, will have to check what a real Mac + * returns other than byte 0 bit 0 is LID closed on laptops + */ + *out_len = 1; + *out_data = 0x00; +} + +static void pmu_cmd_download_status(PMUState *s, + const uint8_t *in_data, uint8_t in_len, + uint8_t *out_data, uint8_t *out_len) +{ + /* This has to do with PMU firmware updates as far as I can tell. + * + * We return 0x62 which is what OpenPMU expects + */ + *out_len = 1; + *out_data = 0x62; +} + +static void pmu_cmd_read_pmu_ram(PMUState *s, + const uint8_t *in_data, uint8_t in_len, + uint8_t *out_data, uint8_t *out_len) +{ + if (in_len < 3) { + qemu_log_mask(LOG_GUEST_ERROR, + "PMU: READ_PMU_RAM command, invalid len %d, expected 3\n", + in_len); + return; + } + + qemu_log_mask(LOG_GUEST_ERROR, + "PMU: Unsupported READ_PMU_RAM, args: %02x %02x %02x\n", + in_data[0], in_data[1], in_data[2]); + + *out_len = 0; +} + +/* description of commands */ +typedef struct PMUCmdHandler { + uint8_t command; + const char *name; + void (*handler)(PMUState *s, + const uint8_t *in_args, uint8_t in_len, + uint8_t *out_args, uint8_t *out_len); +} PMUCmdHandler; + +static const PMUCmdHandler PMUCmdHandlers[] = { + { PMU_INT_ACK, "INT ACK", pmu_cmd_int_ack }, + { PMU_SET_INTR_MASK, "SET INT MASK", pmu_cmd_set_int_mask }, + { PMU_ADB_CMD, "ADB COMMAND", pmu_cmd_adb }, + { PMU_ADB_POLL_OFF, "ADB POLL OFF", pmu_cmd_adb_poll_off }, + { PMU_RESET, "REBOOT", pmu_cmd_reset }, + { PMU_SHUTDOWN, "SHUTDOWN", pmu_cmd_shutdown }, + { PMU_READ_RTC, "GET RTC", pmu_cmd_get_rtc }, + { PMU_SET_RTC, "SET RTC", pmu_cmd_set_rtc }, + { PMU_SYSTEM_READY, "SYSTEM READY", pmu_cmd_system_ready }, + { PMU_GET_VERSION, "GET VERSION", pmu_cmd_get_version }, + { PMU_POWER_EVENTS, "POWER EVENTS", pmu_cmd_power_events }, + { PMU_GET_COVER, "GET_COVER", pmu_cmd_get_cover }, + { PMU_DOWNLOAD_STATUS, "DOWNLOAD STATUS", pmu_cmd_download_status }, + { PMU_READ_PMU_RAM, "READ PMGR RAM", pmu_cmd_read_pmu_ram }, +}; + +static void pmu_dispatch_cmd(PMUState *s) +{ + unsigned int i; + + /* No response by default */ + s->cmd_rsp_sz = 0; + + for (i = 0; i < ARRAY_SIZE(PMUCmdHandlers); i++) { + const PMUCmdHandler *desc = &PMUCmdHandlers[i]; + + if (desc->command != s->cmd) { + continue; + } + + trace_pmu_dispatch_cmd(desc->name); + desc->handler(s, s->cmd_buf, s->cmd_buf_pos, + s->cmd_rsp, &s->cmd_rsp_sz); + + if (s->rsplen != -1 && s->rsplen != s->cmd_rsp_sz) { + trace_pmu_debug_protocol_string("QEMU internal cmd resp mismatch!"); + } else { + trace_pmu_debug_protocol_resp_size(s->cmd_rsp_sz); + } + + return; + } + + trace_pmu_dispatch_unknown_cmd(s->cmd); + + /* Manufacture fake response with 0's */ + if (s->rsplen == -1) { + s->cmd_rsp_sz = 0; + } else { + s->cmd_rsp_sz = s->rsplen; + memset(s->cmd_rsp, 0, s->rsplen); + } +} + +static void pmu_update(PMUState *s) +{ + MOS6522PMUState *mps = &s->mos6522_pmu; + MOS6522State *ms = MOS6522(mps); + + /* Only react to changes in reg B */ + if (ms->b == s->last_b) { + return; + } + s->last_b = ms->b; + + /* Check the TREQ / TACK state */ + switch (ms->b & (TREQ | TACK)) { + case TREQ: + /* This is an ack release, handle it and bail out */ + ms->b |= TACK; + s->last_b = ms->b; + + trace_pmu_debug_protocol_string("handshake: TREQ high, setting TACK"); + return; + case TACK: + /* This is a valid request, handle below */ + break; + case TREQ | TACK: + /* This is an idle state */ + return; + default: + /* Invalid state, log and ignore */ + trace_pmu_debug_protocol_error(ms->b); + return; + } + + /* If we wanted to handle commands asynchronously, this is where + * we would delay the clearing of TACK until we are ready to send + * the response + */ + + /* We have a request, handshake TACK so we don't stay in + * an invalid state. If we were concurrent with the OS we + * should only do this after we grabbed the SR but that isn't + * a problem here. + */ + + trace_pmu_debug_protocol_clear_treq(s->cmd_state); + + ms->b &= ~TACK; + s->last_b = ms->b; + + /* Act according to state */ + switch (s->cmd_state) { + case pmu_state_idle: + if (!(ms->acr & SR_OUT)) { + trace_pmu_debug_protocol_string("protocol error! " + "state idle, ACR reading"); + break; + } + + s->cmd = ms->sr; + via_set_sr_int(s); + s->cmdlen = pmu_data_len[s->cmd][0]; + s->rsplen = pmu_data_len[s->cmd][1]; + s->cmd_buf_pos = 0; + s->cmd_rsp_pos = 0; + s->cmd_state = pmu_state_cmd; + + trace_pmu_debug_protocol_cmd(s->cmd, s->cmdlen, s->rsplen); + break; + + case pmu_state_cmd: + if (!(ms->acr & SR_OUT)) { + trace_pmu_debug_protocol_string("protocol error! " + "state cmd, ACR reading"); + break; + } + + if (s->cmdlen == -1) { + trace_pmu_debug_protocol_cmdlen(ms->sr); + + s->cmdlen = ms->sr; + if (s->cmdlen > sizeof(s->cmd_buf)) { + trace_pmu_debug_protocol_cmd_toobig(s->cmdlen); + } + } else if (s->cmd_buf_pos < sizeof(s->cmd_buf)) { + s->cmd_buf[s->cmd_buf_pos++] = ms->sr; + } + + via_set_sr_int(s); + break; + + case pmu_state_rsp: + if (ms->acr & SR_OUT) { + trace_pmu_debug_protocol_string("protocol error! " + "state resp, ACR writing"); + break; + } + + if (s->rsplen == -1) { + trace_pmu_debug_protocol_cmd_send_resp_size(s->cmd_rsp_sz); + + ms->sr = s->cmd_rsp_sz; + s->rsplen = s->cmd_rsp_sz; + } else if (s->cmd_rsp_pos < s->cmd_rsp_sz) { + trace_pmu_debug_protocol_cmd_send_resp(s->cmd_rsp_pos, s->rsplen); + + ms->sr = s->cmd_rsp[s->cmd_rsp_pos++]; + } + + via_set_sr_int(s); + break; + } + + /* Check for state completion */ + if (s->cmd_state == pmu_state_cmd && s->cmdlen == s->cmd_buf_pos) { + trace_pmu_debug_protocol_string("Command reception complete, " + "dispatching..."); + + pmu_dispatch_cmd(s); + s->cmd_state = pmu_state_rsp; + } + + if (s->cmd_state == pmu_state_rsp && s->rsplen == s->cmd_rsp_pos) { + trace_pmu_debug_protocol_cmd_resp_complete(ms->ier); + + s->cmd_state = pmu_state_idle; + } +} + +static uint64_t mos6522_pmu_read(void *opaque, hwaddr addr, unsigned size) +{ + PMUState *s = opaque; + MOS6522PMUState *mps = &s->mos6522_pmu; + MOS6522State *ms = MOS6522(mps); + + addr = (addr >> 9) & 0xf; + return mos6522_read(ms, addr, size); +} + +static void mos6522_pmu_write(void *opaque, hwaddr addr, uint64_t val, + unsigned size) +{ + PMUState *s = opaque; + MOS6522PMUState *mps = &s->mos6522_pmu; + MOS6522State *ms = MOS6522(mps); + + addr = (addr >> 9) & 0xf; + mos6522_write(ms, addr, val, size); +} + +static const MemoryRegionOps mos6522_pmu_ops = { + .read = mos6522_pmu_read, + .write = mos6522_pmu_write, + .endianness = DEVICE_BIG_ENDIAN, + .impl = { + .min_access_size = 1, + .max_access_size = 1, + }, +}; + +static bool pmu_adb_state_needed(void *opaque) +{ + PMUState *s = opaque; + + return s->has_adb; +} + +static const VMStateDescription vmstate_pmu_adb = { + .name = "pmu/adb", + .version_id = 0, + .minimum_version_id = 0, + .needed = pmu_adb_state_needed, + .fields = (VMStateField[]) { + VMSTATE_UINT16(adb_poll_mask, PMUState), + VMSTATE_TIMER_PTR(adb_poll_timer, PMUState), + VMSTATE_UINT8(adb_reply_size, PMUState), + VMSTATE_BUFFER(adb_reply, PMUState), + } +}; + +static const VMStateDescription vmstate_pmu = { + .name = "pmu", + .version_id = 0, + .minimum_version_id = 0, + .fields = (VMStateField[]) { + VMSTATE_STRUCT(mos6522_pmu.parent_obj, PMUState, 0, vmstate_mos6522, + MOS6522State), + VMSTATE_UINT8(last_b, PMUState), + VMSTATE_UINT8(cmd, PMUState), + VMSTATE_UINT32(cmdlen, PMUState), + VMSTATE_UINT32(rsplen, PMUState), + VMSTATE_UINT8(cmd_buf_pos, PMUState), + VMSTATE_BUFFER(cmd_buf, PMUState), + VMSTATE_UINT8(cmd_rsp_pos, PMUState), + VMSTATE_UINT8(cmd_rsp_sz, PMUState), + VMSTATE_BUFFER(cmd_rsp, PMUState), + VMSTATE_UINT8(intbits, PMUState), + VMSTATE_UINT8(intmask, PMUState), + VMSTATE_UINT8(autopoll_rate_ms, PMUState), + VMSTATE_UINT8(autopoll_mask, PMUState), + VMSTATE_UINT32(tick_offset, PMUState), + VMSTATE_TIMER_PTR(one_sec_timer, PMUState), + VMSTATE_INT64(one_sec_target, PMUState), + VMSTATE_END_OF_LIST() + }, + .subsections = (const VMStateDescription * []) { + &vmstate_pmu_adb, + } +}; + +static void pmu_reset(DeviceState *dev) +{ + PMUState *s = VIA_PMU(dev); + + /* OpenBIOS needs to do this? MacOS 9 needs it */ + s->intmask = PMU_INT_ADB | PMU_INT_TICK; + s->intbits = 0; + + s->cmd_state = pmu_state_idle; + s->autopoll_mask = 0; +} + +static void pmu_realize(DeviceState *dev, Error **errp) +{ + PMUState *s = VIA_PMU(dev); + SysBusDevice *sbd; + MOS6522State *ms; + DeviceState *d; + struct tm tm; + + /* Pass IRQ from 6522 */ + d = DEVICE(&s->mos6522_pmu); + ms = MOS6522(d); + sbd = SYS_BUS_DEVICE(s); + sysbus_pass_irq(sbd, SYS_BUS_DEVICE(ms)); + + qemu_get_timedate(&tm, 0); + s->tick_offset = (uint32_t)mktimegm(&tm) + RTC_OFFSET; + s->one_sec_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, pmu_one_sec_timer, s); + s->one_sec_target = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 1000; + timer_mod(s->one_sec_timer, s->one_sec_target); + + if (s->has_adb) { + qbus_create_inplace(&s->adb_bus, sizeof(s->adb_bus), TYPE_ADB_BUS, + DEVICE(dev), "adb.0"); + s->adb_poll_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, pmu_adb_poll, s); + s->adb_poll_mask = 0xffff; + s->autopoll_rate_ms = 20; + } +} + +static void pmu_init(Object *obj) +{ + SysBusDevice *d = SYS_BUS_DEVICE(obj); + PMUState *s = VIA_PMU(obj); + + object_property_add_link(obj, "gpio", TYPE_MACIO_GPIO, + (Object **) &s->gpio, + qdev_prop_allow_set_link_before_realize, + 0, NULL); + + object_initialize(&s->mos6522_pmu, sizeof(s->mos6522_pmu), + TYPE_MOS6522_PMU); + qdev_set_parent_bus(DEVICE(&s->mos6522_pmu), sysbus_get_default()); + + memory_region_init_io(&s->mem, obj, &mos6522_pmu_ops, s, "via-pmu", + 0x2000); + sysbus_init_mmio(d, &s->mem); +} + +static Property pmu_properties[] = { + DEFINE_PROP_BOOL("has-adb", PMUState, has_adb, true), + DEFINE_PROP_END_OF_LIST() +}; + +static void pmu_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = pmu_realize; + dc->reset = pmu_reset; + dc->vmsd = &vmstate_pmu; + dc->props = pmu_properties; + set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); +} + +static const TypeInfo pmu_type_info = { + .name = TYPE_VIA_PMU, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(PMUState), + .instance_init = pmu_init, + .class_init = pmu_class_init, +}; + +static void mos6522_pmu_portB_write(MOS6522State *s) +{ + MOS6522PMUState *mps = container_of(s, MOS6522PMUState, parent_obj); + PMUState *ps = container_of(mps, PMUState, mos6522_pmu); + + if ((s->pcr & 0xe0) == 0x20 || (s->pcr & 0xe0) == 0x60) { + s->ifr &= ~CB2_INT; + } + s->ifr &= ~CB1_INT; + + via_update_irq(ps); + pmu_update(ps); +} + +static void mos6522_pmu_portA_write(MOS6522State *s) +{ + MOS6522PMUState *mps = container_of(s, MOS6522PMUState, parent_obj); + PMUState *ps = container_of(mps, PMUState, mos6522_pmu); + + if ((s->pcr & 0x0e) == 0x02 || (s->pcr & 0x0e) == 0x06) { + s->ifr &= ~CA2_INT; + } + s->ifr &= ~CA1_INT; + + via_update_irq(ps); +} + +static void mos6522_pmu_reset(DeviceState *dev) +{ + MOS6522State *ms = MOS6522(dev); + MOS6522PMUState *mps = container_of(ms, MOS6522PMUState, parent_obj); + PMUState *s = container_of(mps, PMUState, mos6522_pmu); + MOS6522DeviceClass *mdc = MOS6522_DEVICE_GET_CLASS(ms); + + mdc->parent_reset(dev); + + ms->timers[0].frequency = VIA_TIMER_FREQ; + ms->timers[1].frequency = (SCALE_US * 6000) / 4700; + + s->last_b = ms->b = TACK | TREQ; +} + +static void mos6522_pmu_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + MOS6522DeviceClass *mdc = MOS6522_DEVICE_CLASS(oc); + + dc->reset = mos6522_pmu_reset; + mdc->portB_write = mos6522_pmu_portB_write; + mdc->portA_write = mos6522_pmu_portA_write; +} + +static const TypeInfo mos6522_pmu_type_info = { + .name = TYPE_MOS6522_PMU, + .parent = TYPE_MOS6522, + .instance_size = sizeof(MOS6522PMUState), + .class_init = mos6522_pmu_class_init, +}; + +static void pmu_register_types(void) +{ + type_register_static(&pmu_type_info); + type_register_static(&mos6522_pmu_type_info); +} + +type_init(pmu_register_types) diff --git a/hw/misc/macio/trace-events b/hw/misc/macio/trace-events index 71c4752..0501926 100644 --- a/hw/misc/macio/trace-events +++ b/hw/misc/macio/trace-events @@ -20,3 +20,24 @@ macio_gpio_irq_assert(int gpio) "asserting GPIO %d" macio_gpio_irq_deassert(int gpio) "deasserting GPIO %d" macio_gpio_write(uint64_t addr, uint64_t val) "addr: 0x%"PRIx64" value: 0x%"PRIx64 macio_gpio_read(uint64_t addr, uint64_t val) "addr: 0x%"PRIx64" value: 0x%"PRIx64 + +# hw/misc/macio/pmu.c +pmu_adb_poll(int olen) "ADB autopoll, olen=%d" +pmu_one_sec_timer(void) "PMU one sec..." +pmu_cmd_set_int_mask(int intmask) "Setting PMU int mask to 0x%02x" +pmu_cmd_set_adb_autopoll(int mask) "ADB set autopoll, mask=0x%04x" +pmu_cmd_adb_nobus(void) "ADB PACKET with no ADB bus!" +pmu_cmd_adb_request(int inlen, int indata0, int indata1, int indata2, int indata3, int indata4) "ADB request: len=%d, cmd=0x%02x, pflags=0x%02x, adblen=%d: 0x%02x 0x%02x..." +pmu_cmd_adb_reply(int len) "ADB reply is %d bytes" +pmu_dispatch_cmd(const char *name) "handling command %s" +pmu_dispatch_unknown_cmd(int cmd) "Unknown PMU command 0x%02x" +pmu_debug_protocol_string(const char *str) "%s" +pmu_debug_protocol_resp_size(int size) "sending %d resp bytes" +pmu_debug_protocol_error(int portB) "protocol error! portB=0x%02x" +pmu_debug_protocol_clear_treq(int state) "TREQ cleared, clearing TACK, state: %d" +pmu_debug_protocol_cmd(int cmd, int cmdlen, int rsplen) "Got command byte 0x%02x, clen=%d, rlen=%d" +pmu_debug_protocol_cmdlen(int len) "got cmd length byte: %d" +pmu_debug_protocol_cmd_toobig(int len) "command too big (%d bytes)" +pmu_debug_protocol_cmd_send_resp_size(int len) "sending length byte: %d" +pmu_debug_protocol_cmd_send_resp(int pos, int len) "sending byte: %d/%d" +pmu_debug_protocol_cmd_resp_complete(int ier) "Response send complete. IER=0x%02x" diff --git a/hw/ppc/mac.h b/hw/ppc/mac.h index b3b7f9d..c0217e6 100644 --- a/hw/ppc/mac.h +++ b/hw/ppc/mac.h @@ -59,6 +59,7 @@ /* New World IRQs */ #define NEWWORLD_CUDA_IRQ 0x19 +#define NEWWORLD_PMU_IRQ 0x19 #define NEWWORLD_ESCCB_IRQ 0x24 #define NEWWORLD_ESCCA_IRQ 0x25 #define NEWWORLD_IDE0_IRQ 0xd diff --git a/hw/ppc/mac_newworld.c b/hw/ppc/mac_newworld.c index ca21d47..ff715ff 100644 --- a/hw/ppc/mac_newworld.c +++ b/hw/ppc/mac_newworld.c @@ -399,11 +399,19 @@ static void ppc_core99_init(MachineState *machine) macio_ide_init_drives(macio_ide, &hd[MAX_IDE_DEVS]); if (has_adb) { - dev = DEVICE(object_resolve_path_component(OBJECT(macio), "cuda")); + if (has_pmu) { + dev = DEVICE(object_resolve_path_component(OBJECT(macio), "pmu")); + } else { + dev = DEVICE(object_resolve_path_component(OBJECT(macio), "cuda")); + } + adb_bus = qdev_get_child_bus(dev, "adb.0"); dev = qdev_create(adb_bus, TYPE_ADB_KEYBOARD); + qdev_prop_set_bit(dev, "disable-direct-reg3-writes", has_pmu); qdev_init_nofail(dev); + dev = qdev_create(adb_bus, TYPE_ADB_MOUSE); + qdev_prop_set_bit(dev, "disable-direct-reg3-writes", has_pmu); qdev_init_nofail(dev); } -- cgit v1.1 From 52b438815ecc1cb799d943ef4fd710fe67cc7702 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Tue, 12 Jun 2018 12:11:35 +0200 Subject: xics_kvm: fix a build break MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On CentOS 7.5, gcc-4.8.5-28.el7_5.1.ppc64le fails to build QEMU due to : hw/intc/xics_kvm.c: In function ‘ics_set_kvm_state’: hw/intc/xics_kvm.c:281:13: error: ‘ret’ may be used uninitialized in this function [-Werror=maybe-uninitialized] return ret; Fix the breakage and also remove the extra error reporting as kvm_device_access() already provides a substantial error message. Signed-off-by: Cédric Le Goater Signed-off-by: David Gibson --- hw/intc/xics_kvm.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'hw') diff --git a/hw/intc/xics_kvm.c b/hw/intc/xics_kvm.c index 8bdf6af..8dba2f8 100644 --- a/hw/intc/xics_kvm.c +++ b/hw/intc/xics_kvm.c @@ -186,8 +186,7 @@ static void ics_get_kvm_state(ICSState *ics) kvm_device_access(kernel_xics_fd, KVM_DEV_XICS_GRP_SOURCES, i + ics->offset, &state, false, &local_err); if (local_err) { - error_report("Unable to retrieve KVM interrupt controller state" - " for IRQ %d: %s", i + ics->offset, strerror(errno)); + error_report_err(local_err); exit(1); } @@ -273,11 +272,10 @@ static int ics_set_kvm_state(ICSState *ics, int version_id) state |= KVM_XICS_QUEUED; } - kvm_device_access(kernel_xics_fd, KVM_DEV_XICS_GRP_SOURCES, - i + ics->offset, &state, true, &local_err); + ret = kvm_device_access(kernel_xics_fd, KVM_DEV_XICS_GRP_SOURCES, + i + ics->offset, &state, true, &local_err); if (local_err) { - error_report("Unable to restore KVM interrupt controller state" - " for IRQs %d: %s", i + ics->offset, strerror(errno)); + error_report_err(local_err); return ret; } } -- cgit v1.1 From 7f5d6517e303e39d79f57ca92919725e03c9fad8 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Wed, 13 Jun 2018 09:30:13 +0100 Subject: mos6522: only clear the shift register interrupt upon write According to the 6522 datasheet the shift register (SR) interrupt flag is cleared upon write with no mention of any other interrupt flags. Signed-off-by: Mark Cave-Ayland Signed-off-by: David Gibson --- hw/misc/mos6522.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'hw') diff --git a/hw/misc/mos6522.c b/hw/misc/mos6522.c index 44eb306..ad5041d 100644 --- a/hw/misc/mos6522.c +++ b/hw/misc/mos6522.c @@ -241,7 +241,7 @@ uint64_t mos6522_read(void *opaque, hwaddr addr, unsigned size) break; case VIA_REG_SR: val = s->sr; - s->ifr &= ~(SR_INT | CB1_INT | CB2_INT); + s->ifr &= ~SR_INT; mos6522_update_irq(s); break; case VIA_REG_ACR: -- cgit v1.1 From 32a8c27b5dfc834abf7ada7c55fcc69c97ae0140 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Wed, 13 Jun 2018 09:30:14 +0100 Subject: mos6522: remove additional interrupt flag filter from mos6522_update_irq() The datasheet indicates that the interrupt is generated by ANDing the interrupt flags register (IFR) with the interrupt enable register (IER) but currently there is an extra filter for the SR and timer interrupts. Remove this extra filter to allow interrupts to be generated by external inputs on bits 1 and 2 of ports A and B. Signed-off-by: Mark Cave-Ayland Signed-off-by: David Gibson --- hw/misc/mos6522.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'hw') diff --git a/hw/misc/mos6522.c b/hw/misc/mos6522.c index ad5041d..8d5b419 100644 --- a/hw/misc/mos6522.c +++ b/hw/misc/mos6522.c @@ -40,7 +40,7 @@ static void mos6522_timer_update(MOS6522State *s, MOS6522Timer *ti, static void mos6522_update_irq(MOS6522State *s) { - if (s->ifr & s->ier & (SR_INT | T1_INT | T2_INT)) { + if (s->ifr & s->ier) { qemu_irq_raise(s->irq); } else { qemu_irq_lower(s->irq); -- cgit v1.1 From b6c7e42f74ab244545e157a0f90a31c70a66f3eb Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Wed, 13 Jun 2018 09:30:15 +0100 Subject: mos6522: expose mos6522_update_irq() through MOS6522DeviceClass In the case where we have an interrupt generated externally from inputs to bits 1 and 2 of port A and/or port B, it is necessary to expose mos6522_update_irq() so it can be called by the interrupt source. Signed-off-by: Mark Cave-Ayland Signed-off-by: David Gibson --- hw/misc/mos6522.c | 1 + 1 file changed, 1 insertion(+) (limited to 'hw') diff --git a/hw/misc/mos6522.c b/hw/misc/mos6522.c index 8d5b419..14cff26 100644 --- a/hw/misc/mos6522.c +++ b/hw/misc/mos6522.c @@ -463,6 +463,7 @@ static void mos6522_class_init(ObjectClass *oc, void *data) mdc->set_sr_int = mos6522_set_sr_int; mdc->portB_write = mos6522_portB_write; mdc->portA_write = mos6522_portA_write; + mdc->update_irq = mos6522_update_irq; mdc->get_timer1_counter_value = mos6522_get_counter_value; mdc->get_timer2_counter_value = mos6522_get_counter_value; mdc->get_timer1_load_time = mos6522_get_load_time; -- cgit v1.1 From 2100b6b21ef9c64a3cca1582dbd573c17c97bc4a Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Thu, 14 Jun 2018 02:17:00 +0200 Subject: sm501: Do not clear read only bits when writing registers When writing registers that have read only bits we have to avoid changing these bits as they may have non zero values. Make sure we use the correct masks to mask out read only and reserved bits when changing registers. Also remove extra spaces from dram_control and arbitration_control assignments. Signed-off-by: BALATON Zoltan Signed-off-by: David Gibson --- hw/display/sm501.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'hw') diff --git a/hw/display/sm501.c b/hw/display/sm501.c index e47be99..ca0840f 100644 --- a/hw/display/sm501.c +++ b/hw/display/sm501.c @@ -836,27 +836,30 @@ static void sm501_system_config_write(void *opaque, hwaddr addr, switch (addr) { case SM501_SYSTEM_CONTROL: - s->system_control = value & 0xE300B8F7; + s->system_control &= 0x10DB0000; + s->system_control |= value & 0xEF00B8F7; break; case SM501_MISC_CONTROL: - s->misc_control = value & 0xFF7FFF20; + s->misc_control &= 0xEF; + s->misc_control |= value & 0xFF7FFF10; break; case SM501_GPIO31_0_CONTROL: s->gpio_31_0_control = value; break; case SM501_GPIO63_32_CONTROL: - s->gpio_63_32_control = value; + s->gpio_63_32_control = value & 0xFF80FFFF; break; case SM501_DRAM_CONTROL: s->local_mem_size_index = (value >> 13) & 0x7; /* TODO : check validity of size change */ - s->dram_control |= value & 0x7FFFFFC3; + s->dram_control &= 0x80000000; + s->dram_control |= value & 0x7FFFFFC3; break; case SM501_ARBTRTN_CONTROL: - s->arbitration_control = value & 0x37777777; + s->arbitration_control = value & 0x37777777; break; case SM501_IRQ_MASK: - s->irq_mask = value; + s->irq_mask = value & 0xFFDF3F5F; break; case SM501_MISC_TIMING: s->misc_timing = value & 0xF31F1FFF; -- cgit v1.1 From b1d40d6e09c205dac108e0c21ec2fdaeb1bbaae8 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Wed, 13 Jun 2018 11:48:26 +1000 Subject: spapr: Clean up cpu realize/unrealize paths MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit spapr_cpu_init() and spapr_cpu_destroy() are only called from the spapr cpu core realize/unrealize paths, and really can only be called from there. Those are all short functions, so fold the pairs together for simplicity. While we're there rename some functions and change some parameter types for brevity and clarity. Signed-off-by: David Gibson Reviewed-by: Cédric Le Goater Reviewed-by: Greg Kurz --- hw/ppc/spapr_cpu_core.c | 69 ++++++++++++++++++------------------------------- 1 file changed, 25 insertions(+), 44 deletions(-) (limited to 'hw') diff --git a/hw/ppc/spapr_cpu_core.c b/hw/ppc/spapr_cpu_core.c index f3e9b87..7fdb3b6 100644 --- a/hw/ppc/spapr_cpu_core.c +++ b/hw/ppc/spapr_cpu_core.c @@ -83,26 +83,6 @@ void spapr_cpu_set_entry_state(PowerPCCPU *cpu, target_ulong nip, target_ulong r ppc_store_lpcr(cpu, env->spr[SPR_LPCR] | pcc->lpcr_pm); } -static void spapr_cpu_destroy(PowerPCCPU *cpu) -{ - qemu_unregister_reset(spapr_cpu_reset, cpu); -} - -static void spapr_cpu_init(sPAPRMachineState *spapr, PowerPCCPU *cpu, - Error **errp) -{ - CPUPPCState *env = &cpu->env; - - /* Set time-base frequency to 512 MHz */ - cpu_ppc_tb_init(env, SPAPR_TIMEBASE_FREQ); - - cpu_ppc_set_vhyp(cpu, PPC_VIRTUAL_HYPERVISOR(spapr)); - kvmppc_set_papr(cpu); - - qemu_register_reset(spapr_cpu_reset, cpu); - spapr_cpu_reset(cpu); -} - /* * Return the sPAPR CPU core type for @model which essentially is the CPU * model specified with -cpu cmdline option. @@ -122,44 +102,47 @@ const char *spapr_get_cpu_core_type(const char *cpu_type) return object_class_get_name(oc); } -static void spapr_cpu_core_unrealizefn(DeviceState *dev, Error **errp) +static void spapr_unrealize_vcpu(PowerPCCPU *cpu) +{ + qemu_unregister_reset(spapr_cpu_reset, cpu); + object_unparent(cpu->intc); + cpu_remove_sync(CPU(cpu)); + object_unparent(OBJECT(cpu)); +} + +static void spapr_cpu_core_unrealize(DeviceState *dev, Error **errp) { sPAPRCPUCore *sc = SPAPR_CPU_CORE(OBJECT(dev)); CPUCore *cc = CPU_CORE(dev); int i; for (i = 0; i < cc->nr_threads; i++) { - Object *obj = OBJECT(sc->threads[i]); - DeviceState *dev = DEVICE(obj); - CPUState *cs = CPU(dev); - PowerPCCPU *cpu = POWERPC_CPU(cs); - - spapr_cpu_destroy(cpu); - object_unparent(cpu->intc); - cpu_remove_sync(cs); - object_unparent(obj); + spapr_unrealize_vcpu(sc->threads[i]); } g_free(sc->threads); } -static void spapr_cpu_core_realize_child(Object *child, - sPAPRMachineState *spapr, Error **errp) +static void spapr_realize_vcpu(PowerPCCPU *cpu, sPAPRMachineState *spapr, + Error **errp) { + CPUPPCState *env = &cpu->env; Error *local_err = NULL; - CPUState *cs = CPU(child); - PowerPCCPU *cpu = POWERPC_CPU(cs); - object_property_set_bool(child, true, "realized", &local_err); + object_property_set_bool(OBJECT(cpu), true, "realized", &local_err); if (local_err) { goto error; } - spapr_cpu_init(spapr, cpu, &local_err); - if (local_err) { - goto error; - } + /* Set time-base frequency to 512 MHz */ + cpu_ppc_tb_init(env, SPAPR_TIMEBASE_FREQ); + + cpu_ppc_set_vhyp(cpu, PPC_VIRTUAL_HYPERVISOR(spapr)); + kvmppc_set_papr(cpu); - cpu->intc = icp_create(child, spapr->icp_type, XICS_FABRIC(spapr), + qemu_register_reset(spapr_cpu_reset, cpu); + spapr_cpu_reset(cpu); + + cpu->intc = icp_create(OBJECT(cpu), spapr->icp_type, XICS_FABRIC(spapr), &local_err); if (local_err) { goto error; @@ -220,9 +203,7 @@ static void spapr_cpu_core_realize(DeviceState *dev, Error **errp) } for (j = 0; j < cc->nr_threads; j++) { - obj = OBJECT(sc->threads[j]); - - spapr_cpu_core_realize_child(obj, spapr, &local_err); + spapr_realize_vcpu(sc->threads[j], spapr, &local_err); if (local_err) { goto err; } @@ -249,7 +230,7 @@ static void spapr_cpu_core_class_init(ObjectClass *oc, void *data) sPAPRCPUCoreClass *scc = SPAPR_CPU_CORE_CLASS(oc); dc->realize = spapr_cpu_core_realize; - dc->unrealize = spapr_cpu_core_unrealizefn; + dc->unrealize = spapr_cpu_core_unrealize; dc->props = spapr_cpu_core_properties; scc->cpu_type = data; } -- cgit v1.1 From 937c2146a6694b5bc987c2fa89917db4acc9ae39 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Wed, 13 Jun 2018 11:55:31 +1000 Subject: pnv: Fix some error handling cpu realize() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In pnv_core_realize() we call two functions with an Error * parameter in succession, which will go badly if they both cause errors. In fact, a failure in either of them indicates a qemu internal error, so we can just use &error_abort in both cases. Signed-off-by: David Gibson Reviewed-by: Cédric Le Goater Reviewed-by: Greg Kurz --- hw/ppc/pnv_core.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'hw') diff --git a/hw/ppc/pnv_core.c b/hw/ppc/pnv_core.c index 13ad7d9..01f47c8 100644 --- a/hw/ppc/pnv_core.c +++ b/hw/ppc/pnv_core.c @@ -172,12 +172,9 @@ static void pnv_core_realize(DeviceState *dev, Error **errp) object_initialize(obj, size, typename); snprintf(name, sizeof(name), "thread[%d]", i); - object_property_add_child(OBJECT(pc), name, obj, &local_err); + object_property_add_child(OBJECT(pc), name, obj, &error_abort); object_property_add_alias(obj, "core-pir", OBJECT(pc), - "pir", &local_err); - if (local_err) { - goto err; - } + "pir", &error_abort); object_unref(obj); } -- cgit v1.1 From 08304a8689ef726fe1e3f61645a870fb53f67895 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Wed, 13 Jun 2018 11:57:37 +1000 Subject: pnv_core: Allocate cpu thread objects individually MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, we allocate space for all the cpu objects within a single core in one big block. This was copied from an older version of the spapr code and requires some ugly pointer manipulation to extract the individual objects. This design was due to a misunderstanding of qemu lifetime conventions and has already been changed in spapr (in 94ad93bd "spapr_cpu_core: instantiate CPUs separately". Make an equivalent change in pnv_core to get rid of the nasty pointer arithmetic. Signed-off-by: David Gibson Reviewed-by: Cédric Le Goater Reviewed-by: Greg Kurz --- hw/ppc/pnv.c | 4 ++-- hw/ppc/pnv_core.c | 11 +++++------ 2 files changed, 7 insertions(+), 8 deletions(-) (limited to 'hw') diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 0314881..0b9508d 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -121,9 +121,9 @@ static int get_cpus_node(void *fdt) */ static void pnv_dt_core(PnvChip *chip, PnvCore *pc, void *fdt) { - CPUState *cs = CPU(DEVICE(pc->threads)); + PowerPCCPU *cpu = pc->threads[0]; + CPUState *cs = CPU(cpu); DeviceClass *dc = DEVICE_GET_CLASS(cs); - PowerPCCPU *cpu = POWERPC_CPU(cs); int smt_threads = CPU_CORE(pc)->nr_threads; CPUPPCState *env = &cpu->env; PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cs); diff --git a/hw/ppc/pnv_core.c b/hw/ppc/pnv_core.c index 01f47c8..1e40f01 100644 --- a/hw/ppc/pnv_core.c +++ b/hw/ppc/pnv_core.c @@ -151,7 +151,6 @@ static void pnv_core_realize(DeviceState *dev, Error **errp) PnvCore *pc = PNV_CORE(OBJECT(dev)); CPUCore *cc = CPU_CORE(OBJECT(dev)); const char *typename = pnv_core_cpu_typename(pc); - size_t size = object_type_get_instance_size(typename); Error *local_err = NULL; void *obj; int i, j; @@ -165,11 +164,11 @@ static void pnv_core_realize(DeviceState *dev, Error **errp) return; } - pc->threads = g_malloc0(size * cc->nr_threads); + pc->threads = g_new(PowerPCCPU *, cc->nr_threads); for (i = 0; i < cc->nr_threads; i++) { - obj = pc->threads + i * size; + obj = object_new(typename); - object_initialize(obj, size, typename); + pc->threads[i] = POWERPC_CPU(obj); snprintf(name, sizeof(name), "thread[%d]", i); object_property_add_child(OBJECT(pc), name, obj, &error_abort); @@ -179,7 +178,7 @@ static void pnv_core_realize(DeviceState *dev, Error **errp) } for (j = 0; j < cc->nr_threads; j++) { - obj = pc->threads + j * size; + obj = OBJECT(pc->threads[j]); pnv_core_realize_child(obj, XICS_FABRIC(xi), &local_err); if (local_err) { @@ -194,7 +193,7 @@ static void pnv_core_realize(DeviceState *dev, Error **errp) err: while (--i >= 0) { - obj = pc->threads + i * size; + obj = OBJECT(pc->threads[i]); object_unparent(obj); } g_free(pc->threads); -- cgit v1.1 From 3a24752112a046a5f9745b6b72e16646b4b0bcfd Mon Sep 17 00:00:00 2001 From: David Gibson Date: Wed, 13 Jun 2018 13:34:36 +1000 Subject: pnv: Clean up cpu realize path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pnv_cpu_init() is only called from the the pnv cpu core realize path, and really only can be called from there. So fold it into its caller, which we also rename for brevity. Signed-off-by: David Gibson Reviewed-by: Cédric Le Goater Reviewed-by: Greg Kurz --- hw/ppc/pnv_core.c | 56 +++++++++++++++++++++---------------------------------- 1 file changed, 21 insertions(+), 35 deletions(-) (limited to 'hw') diff --git a/hw/ppc/pnv_core.c b/hw/ppc/pnv_core.c index 1e40f01..f4c41d8 100644 --- a/hw/ppc/pnv_core.c +++ b/hw/ppc/pnv_core.c @@ -54,28 +54,6 @@ static void pnv_cpu_reset(void *opaque) env->msr |= MSR_HVB; /* Hypervisor mode */ } -static void pnv_cpu_init(PowerPCCPU *cpu, Error **errp) -{ - CPUPPCState *env = &cpu->env; - int core_pir; - int thread_index = 0; /* TODO: TCG supports only one thread */ - ppc_spr_t *pir = &env->spr_cb[SPR_PIR]; - - core_pir = object_property_get_uint(OBJECT(cpu), "core-pir", &error_abort); - - /* - * The PIR of a thread is the core PIR + the thread index. We will - * need to find a way to get the thread index when TCG supports - * more than 1. We could use the object name ? - */ - pir->default_value = core_pir + thread_index; - - /* Set time-base frequency to 512 MHz */ - cpu_ppc_tb_init(env, PNV_TIMEBASE_FREQ); - - qemu_register_reset(pnv_cpu_reset, cpu); -} - /* * These values are read by the PowerNV HW monitors under Linux */ @@ -121,29 +99,39 @@ static const MemoryRegionOps pnv_core_xscom_ops = { .endianness = DEVICE_BIG_ENDIAN, }; -static void pnv_core_realize_child(Object *child, XICSFabric *xi, Error **errp) +static void pnv_realize_vcpu(PowerPCCPU *cpu, XICSFabric *xi, Error **errp) { + CPUPPCState *env = &cpu->env; + int core_pir; + int thread_index = 0; /* TODO: TCG supports only one thread */ + ppc_spr_t *pir = &env->spr_cb[SPR_PIR]; Error *local_err = NULL; - CPUState *cs = CPU(child); - PowerPCCPU *cpu = POWERPC_CPU(cs); - object_property_set_bool(child, true, "realized", &local_err); + object_property_set_bool(OBJECT(cpu), true, "realized", &local_err); if (local_err) { error_propagate(errp, local_err); return; } - cpu->intc = icp_create(child, TYPE_PNV_ICP, xi, &local_err); + cpu->intc = icp_create(OBJECT(cpu), TYPE_PNV_ICP, xi, &local_err); if (local_err) { error_propagate(errp, local_err); return; } - pnv_cpu_init(cpu, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } + core_pir = object_property_get_uint(OBJECT(cpu), "core-pir", &error_abort); + + /* + * The PIR of a thread is the core PIR + the thread index. We will + * need to find a way to get the thread index when TCG supports + * more than 1. We could use the object name ? + */ + pir->default_value = core_pir + thread_index; + + /* Set time-base frequency to 512 MHz */ + cpu_ppc_tb_init(env, PNV_TIMEBASE_FREQ); + + qemu_register_reset(pnv_cpu_reset, cpu); } static void pnv_core_realize(DeviceState *dev, Error **errp) @@ -178,9 +166,7 @@ static void pnv_core_realize(DeviceState *dev, Error **errp) } for (j = 0; j < cc->nr_threads; j++) { - obj = OBJECT(pc->threads[j]); - - pnv_core_realize_child(obj, XICS_FABRIC(xi), &local_err); + pnv_realize_vcpu(pc->threads[j], XICS_FABRIC(xi), &local_err); if (local_err) { goto err; } -- cgit v1.1 From 5e22e29201d80124bca0124f2034e72b698cbb6f Mon Sep 17 00:00:00 2001 From: David Gibson Date: Wed, 13 Jun 2018 12:08:42 +1000 Subject: pnv: Add cpu unrealize path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently we don't have any unrealize path for pnv cpu cores. We get away with this because we don't yet support cpu hotplug for pnv. However, we're going to want it eventually, and in the meantime, it makes it non-obvious why there are a bunch of allocations on the realize() path that don't have matching frees. So, implement the missing unrealize path. Signed-off-by: David Gibson Reviewed-by: Cédric Le Goater Reviewed-by: Greg Kurz --- hw/ppc/pnv_core.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'hw') diff --git a/hw/ppc/pnv_core.c b/hw/ppc/pnv_core.c index f4c41d8..f7cf33f 100644 --- a/hw/ppc/pnv_core.c +++ b/hw/ppc/pnv_core.c @@ -186,6 +186,26 @@ err: error_propagate(errp, local_err); } +static void pnv_unrealize_vcpu(PowerPCCPU *cpu) +{ + qemu_unregister_reset(pnv_cpu_reset, cpu); + object_unparent(cpu->intc); + cpu_remove_sync(CPU(cpu)); + object_unparent(OBJECT(cpu)); +} + +static void pnv_core_unrealize(DeviceState *dev, Error **errp) +{ + PnvCore *pc = PNV_CORE(dev); + CPUCore *cc = CPU_CORE(dev); + int i; + + for (i = 0; i < cc->nr_threads; i++) { + pnv_unrealize_vcpu(pc->threads[i]); + } + g_free(pc->threads); +} + static Property pnv_core_properties[] = { DEFINE_PROP_UINT32("pir", PnvCore, pir, 0), DEFINE_PROP_END_OF_LIST(), @@ -196,6 +216,7 @@ static void pnv_core_class_init(ObjectClass *oc, void *data) DeviceClass *dc = DEVICE_CLASS(oc); dc->realize = pnv_core_realize; + dc->unrealize = pnv_core_unrealize; dc->props = pnv_core_properties; } -- cgit v1.1 From dbb3e8d5da028a6cc4c576c6a0960bcf740cb035 Mon Sep 17 00:00:00 2001 From: Greg Kurz Date: Thu, 14 Jun 2018 23:50:11 +0200 Subject: spapr_cpu_core: convert last snprintf() to g_strdup_printf() Because this is the preferred practice in QEMU. Signed-off-by: Greg Kurz Signed-off-by: David Gibson --- hw/ppc/spapr_cpu_core.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'hw') diff --git a/hw/ppc/spapr_cpu_core.c b/hw/ppc/spapr_cpu_core.c index 7fdb3b6..ad404d1 100644 --- a/hw/ppc/spapr_cpu_core.c +++ b/hw/ppc/spapr_cpu_core.c @@ -176,7 +176,7 @@ static void spapr_cpu_core_realize(DeviceState *dev, Error **errp) sc->threads = g_new(PowerPCCPU *, cc->nr_threads); for (i = 0; i < cc->nr_threads; i++) { - char id[32]; + char *id; CPUState *cs; PowerPCCPU *cpu; @@ -194,8 +194,9 @@ static void spapr_cpu_core_realize(DeviceState *dev, Error **errp) /* Set NUMA node for the threads belonged to core */ cpu->node_id = sc->node_id; - snprintf(id, sizeof(id), "thread[%d]", i); + id = g_strdup_printf("thread[%d]", i); object_property_add_child(OBJECT(sc), id, obj, &local_err); + g_free(id); if (local_err) { goto err; } -- cgit v1.1 From 27607c1cdc0d2939cc3059106f919bf6271ae652 Mon Sep 17 00:00:00 2001 From: Greg Kurz Date: Thu, 14 Jun 2018 23:50:27 +0200 Subject: spapr_cpu_core: fix potential leak in spapr_cpu_core_realize() Commit 94ad93bd97684 (QEMU 2.12) switched to instantiate CPUs separately but it missed to adapt the error path accordingly. If something fails in the CPU creation loop, then the CPU object that was just created is leaked. The error paths in this function are a bit obfuscated, and adding yet another label to free this CPU object makes it worse. We should move the block of the loop to a separate function, with a proper rollback path, but this is a bigger cleanup. For now, let's just fix the bug by adding the missing calls to object_unref(). This will allow easier backport to older QEMU versions. Signed-off-by: Greg Kurz Signed-off-by: David Gibson --- hw/ppc/spapr_cpu_core.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'hw') diff --git a/hw/ppc/spapr_cpu_core.c b/hw/ppc/spapr_cpu_core.c index ad404d1..a9bb2d6 100644 --- a/hw/ppc/spapr_cpu_core.c +++ b/hw/ppc/spapr_cpu_core.c @@ -187,6 +187,7 @@ static void spapr_cpu_core_realize(DeviceState *dev, Error **errp) cs->cpu_index = cc->core_id + i; spapr_set_vcpu_id(cpu, cs->cpu_index, &local_err); if (local_err) { + object_unref(obj); goto err; } @@ -198,6 +199,7 @@ static void spapr_cpu_core_realize(DeviceState *dev, Error **errp) object_property_add_child(OBJECT(sc), id, obj, &local_err); g_free(id); if (local_err) { + object_unref(obj); goto err; } object_unref(obj); -- cgit v1.1 From 9986ddec4ccdc1f6320c158be50274fe390b7bb2 Mon Sep 17 00:00:00 2001 From: Greg Kurz Date: Thu, 14 Jun 2018 23:50:42 +0200 Subject: spapr_cpu_core: add missing rollback on realization path The spapr_realize_vcpu() function doesn't rollback in case of error. This isn't a problem with coldplugged CPUs because the machine won't start and QEMU will exit. Hotplug is a different story though: the CPU thread is started under object_property_set_bool() and it assumes it can access the CPU object. If icp_create() fails, we return an error without unregistering the reset handler for this CPU, and we let the underlying QEMU thread for this CPU alive. Since spapr_cpu_core_realize() doesn't care to unrealize already realized CPUs either, but happily frees all of them anyway, the CPU thread crashes instantly: (qemu) device_add host-spapr-cpu-core,core-id=1,id=gku GKU: failing icp_create (cpu 0x11497fd0) ^^^^^^^^^^ Program received signal SIGSEGV, Segmentation fault. [Switching to Thread 0x7fffee3feaa0 (LWP 24725)] 0x00000000104c8374 in object_dynamic_cast_assert (obj=0x11497fd0, ^^^^^^^^^^^^^^ pointer to the CPU object 623 trace_object_dynamic_cast_assert(obj ? obj->class->type->name (gdb) p obj->class->type $1 = (Type) 0x0 (gdb) p * obj $2 = {class = 0x10ea9c10, free = 0x11244620, ^^^^^^^^^^ should be g_free (gdb) p g_free $3 = {} 0x7ffff282bef0 obj is a dangling pointer to the CPU that was just destroyed in spapr_cpu_core_realize(). This patch adds proper rollback to both spapr_realize_vcpu() and spapr_cpu_core_realize(). Signed-off-by: Greg Kurz [dwg: Fixed a conflict due to a change in my tree] Signed-off-by: David Gibson --- hw/ppc/spapr_cpu_core.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'hw') diff --git a/hw/ppc/spapr_cpu_core.c b/hw/ppc/spapr_cpu_core.c index a9bb2d6..30a7cf9 100644 --- a/hw/ppc/spapr_cpu_core.c +++ b/hw/ppc/spapr_cpu_core.c @@ -145,11 +145,14 @@ static void spapr_realize_vcpu(PowerPCCPU *cpu, sPAPRMachineState *spapr, cpu->intc = icp_create(OBJECT(cpu), spapr->icp_type, XICS_FABRIC(spapr), &local_err); if (local_err) { - goto error; + goto error_unregister; } return; +error_unregister: + qemu_unregister_reset(spapr_cpu_reset, cpu); + cpu_remove_sync(CPU(cpu)); error: error_propagate(errp, local_err); } @@ -208,11 +211,15 @@ static void spapr_cpu_core_realize(DeviceState *dev, Error **errp) for (j = 0; j < cc->nr_threads; j++) { spapr_realize_vcpu(sc->threads[j], spapr, &local_err); if (local_err) { - goto err; + goto err_unrealize; } } return; +err_unrealize: + while (--j >= 0) { + spapr_unrealize_vcpu(sc->threads[j]); + } err: while (--i >= 0) { obj = OBJECT(sc->threads[i]); -- cgit v1.1 From d9f0e34cb7174aefa2a1f4a7d6631c62b1e0d05d Mon Sep 17 00:00:00 2001 From: Greg Kurz Date: Thu, 14 Jun 2018 23:50:57 +0200 Subject: spapr_cpu_core: introduce spapr_create_vcpu() This moves some code out from spapr_cpu_core_realize() for clarity. No functional change. Signed-off-by: Greg Kurz Signed-off-by: David Gibson --- hw/ppc/spapr_cpu_core.c | 73 ++++++++++++++++++++++++++++++------------------- 1 file changed, 45 insertions(+), 28 deletions(-) (limited to 'hw') diff --git a/hw/ppc/spapr_cpu_core.c b/hw/ppc/spapr_cpu_core.c index 30a7cf9..98a018c 100644 --- a/hw/ppc/spapr_cpu_core.c +++ b/hw/ppc/spapr_cpu_core.c @@ -157,6 +157,49 @@ error: error_propagate(errp, local_err); } +static PowerPCCPU *spapr_create_vcpu(sPAPRCPUCore *sc, int i, Error **errp) +{ + sPAPRCPUCoreClass *scc = SPAPR_CPU_CORE_GET_CLASS(sc); + CPUCore *cc = CPU_CORE(sc); + Object *obj; + char *id; + CPUState *cs; + PowerPCCPU *cpu; + Error *local_err = NULL; + + obj = object_new(scc->cpu_type); + + cs = CPU(obj); + cpu = POWERPC_CPU(obj); + cs->cpu_index = cc->core_id + i; + spapr_set_vcpu_id(cpu, cs->cpu_index, &local_err); + if (local_err) { + goto err; + } + + cpu->node_id = sc->node_id; + + id = g_strdup_printf("thread[%d]", i); + object_property_add_child(OBJECT(sc), id, obj, &local_err); + g_free(id); + if (local_err) { + goto err; + } + + object_unref(obj); + return cpu; + +err: + object_unref(obj); + error_propagate(errp, local_err); + return NULL; +} + +static void spapr_delete_vcpu(PowerPCCPU *cpu) +{ + object_unparent(OBJECT(cpu)); +} + static void spapr_cpu_core_realize(DeviceState *dev, Error **errp) { /* We don't use SPAPR_MACHINE() in order to exit gracefully if the user @@ -166,10 +209,8 @@ static void spapr_cpu_core_realize(DeviceState *dev, Error **errp) (sPAPRMachineState *) object_dynamic_cast(qdev_get_machine(), TYPE_SPAPR_MACHINE); sPAPRCPUCore *sc = SPAPR_CPU_CORE(OBJECT(dev)); - sPAPRCPUCoreClass *scc = SPAPR_CPU_CORE_GET_CLASS(OBJECT(dev)); CPUCore *cc = CPU_CORE(OBJECT(dev)); Error *local_err = NULL; - Object *obj; int i, j; if (!spapr) { @@ -179,33 +220,10 @@ static void spapr_cpu_core_realize(DeviceState *dev, Error **errp) sc->threads = g_new(PowerPCCPU *, cc->nr_threads); for (i = 0; i < cc->nr_threads; i++) { - char *id; - CPUState *cs; - PowerPCCPU *cpu; - - obj = object_new(scc->cpu_type); - - cs = CPU(obj); - cpu = sc->threads[i] = POWERPC_CPU(obj); - cs->cpu_index = cc->core_id + i; - spapr_set_vcpu_id(cpu, cs->cpu_index, &local_err); - if (local_err) { - object_unref(obj); - goto err; - } - - - /* Set NUMA node for the threads belonged to core */ - cpu->node_id = sc->node_id; - - id = g_strdup_printf("thread[%d]", i); - object_property_add_child(OBJECT(sc), id, obj, &local_err); - g_free(id); + sc->threads[i] = spapr_create_vcpu(sc, i, &local_err); if (local_err) { - object_unref(obj); goto err; } - object_unref(obj); } for (j = 0; j < cc->nr_threads; j++) { @@ -222,8 +240,7 @@ err_unrealize: } err: while (--i >= 0) { - obj = OBJECT(sc->threads[i]); - object_unparent(obj); + spapr_delete_vcpu(sc->threads[i]); } g_free(sc->threads); error_propagate(errp, local_err); -- cgit v1.1 From 51c047283c567ea27084827c979681f3e28b920e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Thu, 14 Jun 2018 16:00:41 +0200 Subject: ppc/pnv: introduce a pnv_chip_core_realize() routine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This extracts from the PvChip realize routine the part creating the cores. On Power9, we will need to create the cores after the Xive interrupt controller is created. Signed-off-by: Cédric Le Goater Signed-off-by: David Gibson --- hw/ppc/pnv.c | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) (limited to 'hw') diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 0b9508d..0d2b79f 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -849,9 +849,8 @@ static void pnv_chip_icp_realize(PnvChip *chip, Error **errp) } } -static void pnv_chip_realize(DeviceState *dev, Error **errp) +static void pnv_chip_core_realize(PnvChip *chip, Error **errp) { - PnvChip *chip = PNV_CHIP(dev); Error *error = NULL; PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip); const char *typename = pnv_chip_core_typename(chip); @@ -863,14 +862,6 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp) return; } - /* XSCOM bridge */ - pnv_xscom_realize(chip, &error); - if (error) { - error_propagate(errp, error); - return; - } - sysbus_mmio_map(SYS_BUS_DEVICE(chip), 0, PNV_XSCOM_BASE(chip)); - /* Cores */ pnv_chip_core_sanitize(chip, &error); if (error) { @@ -918,6 +909,27 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp) &PNV_CORE(pnv_core)->xscom_regs); i++; } +} + +static void pnv_chip_realize(DeviceState *dev, Error **errp) +{ + PnvChip *chip = PNV_CHIP(dev); + Error *error = NULL; + + /* XSCOM bridge */ + pnv_xscom_realize(chip, &error); + if (error) { + error_propagate(errp, error); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(chip), 0, PNV_XSCOM_BASE(chip)); + + /* Cores */ + pnv_chip_core_realize(chip, &error); + if (error) { + error_propagate(errp, error); + return; + } /* Create LPC controller */ object_property_set_bool(OBJECT(&chip->lpc), true, "realized", -- cgit v1.1 From 7388efafc27c2f45d22c8edbc14b3154c0381c2e Mon Sep 17 00:00:00 2001 From: David Gibson Date: Wed, 13 Jun 2018 16:22:18 +1000 Subject: target/ppc, spapr: Move VPA information to machine_data CPUPPCState currently contains a number of fields containing the state of the VPA. The VPA is a PAPR specific concept covering several guest/host shared memory areas used to communicate some information with the hypervisor. As a PAPR concept this is really machine specific information, although it is per-cpu, so it doesn't really belong in the core CPU state structure. There's also other information that's per-cpu, but platform/machine specific. So create a (void *)machine_data in PowerPCCPU which can be used by the machine to locate per-cpu data. Intialization, lifetime and cleanup of machine_data is entirely up to the machine type. Signed-off-by: David Gibson Reviewed-by: Greg Kurz Tested-by: Greg Kurz --- hw/ppc/spapr_cpu_core.c | 13 +++++++++ hw/ppc/spapr_hcall.c | 77 +++++++++++++++++++++++++++---------------------- 2 files changed, 55 insertions(+), 35 deletions(-) (limited to 'hw') diff --git a/hw/ppc/spapr_cpu_core.c b/hw/ppc/spapr_cpu_core.c index 98a018c..aef3be3 100644 --- a/hw/ppc/spapr_cpu_core.c +++ b/hw/ppc/spapr_cpu_core.c @@ -28,6 +28,7 @@ static void spapr_cpu_reset(void *opaque) CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); + sPAPRCPUState *spapr_cpu = spapr_cpu_state(cpu); target_ulong lpcr; cpu_reset(cs); @@ -69,6 +70,12 @@ static void spapr_cpu_reset(void *opaque) /* Set a full AMOR so guest can use the AMR as it sees fit */ env->spr[SPR_AMOR] = 0xffffffffffffffffull; + + spapr_cpu->vpa_addr = 0; + spapr_cpu->slb_shadow_addr = 0; + spapr_cpu->slb_shadow_size = 0; + spapr_cpu->dtl_addr = 0; + spapr_cpu->dtl_size = 0; } void spapr_cpu_set_entry_state(PowerPCCPU *cpu, target_ulong nip, target_ulong r3) @@ -186,6 +193,8 @@ static PowerPCCPU *spapr_create_vcpu(sPAPRCPUCore *sc, int i, Error **errp) goto err; } + cpu->machine_data = g_new0(sPAPRCPUState, 1); + object_unref(obj); return cpu; @@ -197,6 +206,10 @@ err: static void spapr_delete_vcpu(PowerPCCPU *cpu) { + sPAPRCPUState *spapr_cpu = spapr_cpu_state(cpu); + + cpu->machine_data = NULL; + g_free(spapr_cpu); object_unparent(OBJECT(cpu)); } diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c index 8b9a4b5..ae913d0 100644 --- a/hw/ppc/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -8,6 +8,7 @@ #include "exec/exec-all.h" #include "helper_regs.h" #include "hw/ppc/spapr.h" +#include "hw/ppc/spapr_cpu_core.h" #include "mmu-hash64.h" #include "cpu-models.h" #include "trace.h" @@ -908,9 +909,11 @@ unmap_out: #define VPA_SHARED_PROC_OFFSET 0x9 #define VPA_SHARED_PROC_VAL 0x2 -static target_ulong register_vpa(CPUPPCState *env, target_ulong vpa) +static target_ulong register_vpa(PowerPCCPU *cpu, target_ulong vpa) { - CPUState *cs = CPU(ppc_env_get_cpu(env)); + CPUState *cs = CPU(cpu); + CPUPPCState *env = &cpu->env; + sPAPRCPUState *spapr_cpu = spapr_cpu_state(cpu); uint16_t size; uint8_t tmp; @@ -935,32 +938,34 @@ static target_ulong register_vpa(CPUPPCState *env, target_ulong vpa) return H_PARAMETER; } - env->vpa_addr = vpa; + spapr_cpu->vpa_addr = vpa; - tmp = ldub_phys(cs->as, env->vpa_addr + VPA_SHARED_PROC_OFFSET); + tmp = ldub_phys(cs->as, spapr_cpu->vpa_addr + VPA_SHARED_PROC_OFFSET); tmp |= VPA_SHARED_PROC_VAL; - stb_phys(cs->as, env->vpa_addr + VPA_SHARED_PROC_OFFSET, tmp); + stb_phys(cs->as, spapr_cpu->vpa_addr + VPA_SHARED_PROC_OFFSET, tmp); return H_SUCCESS; } -static target_ulong deregister_vpa(CPUPPCState *env, target_ulong vpa) +static target_ulong deregister_vpa(PowerPCCPU *cpu, target_ulong vpa) { - if (env->slb_shadow_addr) { + sPAPRCPUState *spapr_cpu = spapr_cpu_state(cpu); + + if (spapr_cpu->slb_shadow_addr) { return H_RESOURCE; } - if (env->dtl_addr) { + if (spapr_cpu->dtl_addr) { return H_RESOURCE; } - env->vpa_addr = 0; + spapr_cpu->vpa_addr = 0; return H_SUCCESS; } -static target_ulong register_slb_shadow(CPUPPCState *env, target_ulong addr) +static target_ulong register_slb_shadow(PowerPCCPU *cpu, target_ulong addr) { - CPUState *cs = CPU(ppc_env_get_cpu(env)); + sPAPRCPUState *spapr_cpu = spapr_cpu_state(cpu); uint32_t size; if (addr == 0) { @@ -968,7 +973,7 @@ static target_ulong register_slb_shadow(CPUPPCState *env, target_ulong addr) return H_HARDWARE; } - size = ldl_be_phys(cs->as, addr + 0x4); + size = ldl_be_phys(CPU(cpu)->as, addr + 0x4); if (size < 0x8) { return H_PARAMETER; } @@ -977,26 +982,28 @@ static target_ulong register_slb_shadow(CPUPPCState *env, target_ulong addr) return H_PARAMETER; } - if (!env->vpa_addr) { + if (!spapr_cpu->vpa_addr) { return H_RESOURCE; } - env->slb_shadow_addr = addr; - env->slb_shadow_size = size; + spapr_cpu->slb_shadow_addr = addr; + spapr_cpu->slb_shadow_size = size; return H_SUCCESS; } -static target_ulong deregister_slb_shadow(CPUPPCState *env, target_ulong addr) +static target_ulong deregister_slb_shadow(PowerPCCPU *cpu, target_ulong addr) { - env->slb_shadow_addr = 0; - env->slb_shadow_size = 0; + sPAPRCPUState *spapr_cpu = spapr_cpu_state(cpu); + + spapr_cpu->slb_shadow_addr = 0; + spapr_cpu->slb_shadow_size = 0; return H_SUCCESS; } -static target_ulong register_dtl(CPUPPCState *env, target_ulong addr) +static target_ulong register_dtl(PowerPCCPU *cpu, target_ulong addr) { - CPUState *cs = CPU(ppc_env_get_cpu(env)); + sPAPRCPUState *spapr_cpu = spapr_cpu_state(cpu); uint32_t size; if (addr == 0) { @@ -1004,26 +1011,28 @@ static target_ulong register_dtl(CPUPPCState *env, target_ulong addr) return H_HARDWARE; } - size = ldl_be_phys(cs->as, addr + 0x4); + size = ldl_be_phys(CPU(cpu)->as, addr + 0x4); if (size < 48) { return H_PARAMETER; } - if (!env->vpa_addr) { + if (!spapr_cpu->vpa_addr) { return H_RESOURCE; } - env->dtl_addr = addr; - env->dtl_size = size; + spapr_cpu->dtl_addr = addr; + spapr_cpu->dtl_size = size; return H_SUCCESS; } -static target_ulong deregister_dtl(CPUPPCState *env, target_ulong addr) +static target_ulong deregister_dtl(PowerPCCPU *cpu, target_ulong addr) { - env->dtl_addr = 0; - env->dtl_size = 0; + sPAPRCPUState *spapr_cpu = spapr_cpu_state(cpu); + + spapr_cpu->dtl_addr = 0; + spapr_cpu->dtl_size = 0; return H_SUCCESS; } @@ -1035,38 +1044,36 @@ static target_ulong h_register_vpa(PowerPCCPU *cpu, sPAPRMachineState *spapr, target_ulong procno = args[1]; target_ulong vpa = args[2]; target_ulong ret = H_PARAMETER; - CPUPPCState *tenv; PowerPCCPU *tcpu; tcpu = spapr_find_cpu(procno); if (!tcpu) { return H_PARAMETER; } - tenv = &tcpu->env; switch (flags) { case FLAGS_REGISTER_VPA: - ret = register_vpa(tenv, vpa); + ret = register_vpa(tcpu, vpa); break; case FLAGS_DEREGISTER_VPA: - ret = deregister_vpa(tenv, vpa); + ret = deregister_vpa(tcpu, vpa); break; case FLAGS_REGISTER_SLBSHADOW: - ret = register_slb_shadow(tenv, vpa); + ret = register_slb_shadow(tcpu, vpa); break; case FLAGS_DEREGISTER_SLBSHADOW: - ret = deregister_slb_shadow(tenv, vpa); + ret = deregister_slb_shadow(tcpu, vpa); break; case FLAGS_REGISTER_DTL: - ret = register_dtl(tenv, vpa); + ret = register_dtl(tcpu, vpa); break; case FLAGS_DEREGISTER_DTL: - ret = deregister_dtl(tenv, vpa); + ret = deregister_dtl(tcpu, vpa); break; } -- cgit v1.1 From 844afc54ae229515a37f63519855661ad2d01d19 Mon Sep 17 00:00:00 2001 From: Greg Kurz Date: Fri, 15 Jun 2018 18:58:00 +0200 Subject: spapr: fix xics_system_init() error path Commit 3d85885a1b1f3 tried to fix error handling, but it actually went into the wrong direction by dropping the local Error *. In the default KVM case, the rationale is to try the in-kernel XICS first, and if not possible, to fallback to userland XICS. Passing errp everywhere makes this fallback impossible if errp is &error_fatal (which happens to be the case). And anyway, if the caller would pass a regular &local_err, things would be worse: we could possibly pass an already set *errp to error_setg() and crash, or return an error even in case of success. So we definitely need a local Error * and only propagate it when we're done with the fallback logic. This is what this patch does. Signed-off-by: Greg Kurz Signed-off-by: David Gibson --- hw/ppc/spapr.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) (limited to 'hw') diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index f59999d..db0fb38 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -186,27 +186,33 @@ static int xics_max_server_number(sPAPRMachineState *spapr) static void xics_system_init(MachineState *machine, int nr_irqs, Error **errp) { sPAPRMachineState *spapr = SPAPR_MACHINE(machine); + Error *local_err = NULL; if (kvm_enabled()) { if (machine_kernel_irqchip_allowed(machine) && - !xics_kvm_init(spapr, errp)) { + !xics_kvm_init(spapr, &local_err)) { spapr->icp_type = TYPE_KVM_ICP; - spapr->ics = spapr_ics_create(spapr, TYPE_ICS_KVM, nr_irqs, errp); + spapr->ics = spapr_ics_create(spapr, TYPE_ICS_KVM, nr_irqs, + &local_err); } if (machine_kernel_irqchip_required(machine) && !spapr->ics) { - error_prepend(errp, "kernel_irqchip requested but unavailable: "); - return; + error_prepend(&local_err, + "kernel_irqchip requested but unavailable: "); + goto error; } + error_free(local_err); + local_err = NULL; } if (!spapr->ics) { xics_spapr_init(spapr); spapr->icp_type = TYPE_ICP; - spapr->ics = spapr_ics_create(spapr, TYPE_ICS_SIMPLE, nr_irqs, errp); - if (!spapr->ics) { - return; - } + spapr->ics = spapr_ics_create(spapr, TYPE_ICS_SIMPLE, nr_irqs, + &local_err); } + +error: + error_propagate(errp, local_err); } static int spapr_fixup_cpu_smt_dt(void *fdt, int offset, PowerPCCPU *cpu, -- cgit v1.1