diff options
41 files changed, 1284 insertions, 383 deletions
diff --git a/default-configs/ppc-softmmu.mak b/default-configs/ppc-softmmu.mak index 4b60e699ff..4befde3c7c 100644 --- a/default-configs/ppc-softmmu.mak +++ b/default-configs/ppc-softmmu.mak @@ -38,7 +38,6 @@ CONFIG_PTIMER=y CONFIG_I8259=y CONFIG_XILINX=y CONFIG_XILINX_ETHLITE=y -CONFIG_OPENPIC=y CONFIG_PREP=y CONFIG_MAC=y CONFIG_E500=y diff --git a/default-configs/ppc64-softmmu.mak b/default-configs/ppc64-softmmu.mak index de71e41b00..ab62cc7ff0 100644 --- a/default-configs/ppc64-softmmu.mak +++ b/default-configs/ppc64-softmmu.mak @@ -38,7 +38,6 @@ CONFIG_PTIMER=y CONFIG_I8259=y CONFIG_XILINX=y CONFIG_XILINX_ETHLITE=y -CONFIG_OPENPIC=y CONFIG_PSERIES=y CONFIG_PREP=y CONFIG_MAC=y @@ -51,11 +50,5 @@ CONFIG_LIBDECNUMBER=y CONFIG_XICS=$(CONFIG_PSERIES) CONFIG_XICS_KVM=$(and $(CONFIG_PSERIES),$(CONFIG_KVM)) # For PReP -CONFIG_I82378=y -CONFIG_I8259=y -CONFIG_I8254=y -CONFIG_PCSPK=y -CONFIG_I82374=y -CONFIG_I8257=y CONFIG_MC146818RTC=y CONFIG_ISA_TESTDEV=y diff --git a/default-configs/ppcemb-softmmu.mak b/default-configs/ppcemb-softmmu.mak index a1b3d5f35d..54acc4d58b 100644 --- a/default-configs/ppcemb-softmmu.mak +++ b/default-configs/ppcemb-softmmu.mak @@ -13,5 +13,4 @@ CONFIG_PTIMER=y CONFIG_I8259=y CONFIG_XILINX=y CONFIG_XILINX_ETHLITE=y -CONFIG_OPENPIC=y CONFIG_LIBDECNUMBER=y diff --git a/hw/char/spapr_vty.c b/hw/char/spapr_vty.c index 0adf096ae0..4e464bd15a 100644 --- a/hw/char/spapr_vty.c +++ b/hw/char/spapr_vty.c @@ -60,19 +60,17 @@ void vty_putchars(VIOsPAPRDevice *sdev, uint8_t *buf, int len) qemu_chr_fe_write(dev->chardev, buf, len); } -static int spapr_vty_init(VIOsPAPRDevice *sdev) +static void spapr_vty_realize(VIOsPAPRDevice *sdev, Error **errp) { VIOsPAPRVTYDevice *dev = VIO_SPAPR_VTY_DEVICE(sdev); if (!dev->chardev) { - fprintf(stderr, "spapr-vty: Can't create vty without a chardev!\n"); - exit(1); + error_setg(errp, "chardev property not set"); + return; } qemu_chr_add_handlers(dev->chardev, vty_can_receive, vty_receive, NULL, dev); - - return 0; } /* Forward declaration */ @@ -163,7 +161,7 @@ static void spapr_vty_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); VIOsPAPRDeviceClass *k = VIO_SPAPR_DEVICE_CLASS(klass); - k->init = spapr_vty_init; + k->realize = spapr_vty_realize; k->dt_name = "vty"; k->dt_type = "serial"; k->dt_compatible = "hvterm1"; diff --git a/hw/display/vga-pci.c b/hw/display/vga-pci.c index d67f748af4..aabfc23cd9 100644 --- a/hw/display/vga-pci.c +++ b/hw/display/vga-pci.c @@ -181,6 +181,20 @@ static void pci_vga_qext_write(void *ptr, hwaddr addr, } } +static bool vga_get_big_endian_fb(Object *obj, Error **errp) +{ + PCIVGAState *d = DO_UPCAST(PCIVGAState, dev, PCI_DEVICE(obj)); + + return d->vga.big_endian_fb; +} + +static void vga_set_big_endian_fb(Object *obj, bool value, Error **errp) +{ + PCIVGAState *d = DO_UPCAST(PCIVGAState, dev, PCI_DEVICE(obj)); + + d->vga.big_endian_fb = value; +} + static const MemoryRegionOps pci_vga_qext_ops = { .read = pci_vga_qext_read, .write = pci_vga_qext_write, @@ -234,6 +248,13 @@ static void pci_std_vga_realize(PCIDevice *dev, Error **errp) } } +static void pci_std_vga_init(Object *obj) +{ + /* Expose framebuffer byteorder via QOM */ + object_property_add_bool(obj, "big-endian-framebuffer", + vga_get_big_endian_fb, vga_set_big_endian_fb, NULL); +} + static void pci_secondary_vga_realize(PCIDevice *dev, Error **errp) { PCIVGAState *d = DO_UPCAST(PCIVGAState, dev, dev); @@ -265,7 +286,13 @@ static void pci_secondary_vga_realize(PCIDevice *dev, Error **errp) pci_register_bar(&d->dev, 0, PCI_BASE_ADDRESS_MEM_PREFETCH, &s->vram); pci_register_bar(&d->dev, 2, PCI_BASE_ADDRESS_SPACE_MEMORY, &d->mmio); +} +static void pci_secondary_vga_init(Object *obj) +{ + /* Expose framebuffer byteorder via QOM */ + object_property_add_bool(obj, "big-endian-framebuffer", + vga_get_big_endian_fb, vga_set_big_endian_fb, NULL); } static void pci_secondary_vga_reset(DeviceState *dev) @@ -324,6 +351,7 @@ static void secondary_class_init(ObjectClass *klass, void *data) static const TypeInfo vga_info = { .name = "VGA", .parent = TYPE_PCI_DEVICE, + .instance_init = pci_std_vga_init, .instance_size = sizeof(PCIVGAState), .class_init = vga_class_init, }; @@ -331,6 +359,7 @@ static const TypeInfo vga_info = { static const TypeInfo secondary_info = { .name = "secondary-vga", .parent = TYPE_PCI_DEVICE, + .instance_init = pci_secondary_vga_init, .instance_size = sizeof(PCIVGAState), .class_init = secondary_class_init, }; diff --git a/hw/input/adb.c b/hw/input/adb.c index 34c8058fc2..a18eea2652 100644 --- a/hw/input/adb.c +++ b/hw/input/adb.c @@ -118,6 +118,17 @@ static const TypeInfo adb_bus_type_info = { .instance_size = sizeof(ADBBusState), }; +static const VMStateDescription vmstate_adb_device = { + .name = "adb_device", + .version_id = 0, + .minimum_version_id = 0, + .fields = (VMStateField[]) { + VMSTATE_INT32(devaddr, ADBDevice), + VMSTATE_INT32(handler, ADBDevice), + VMSTATE_END_OF_LIST() + } +}; + static void adb_device_realizefn(DeviceState *dev, Error **errp) { ADBDevice *d = ADB_DEVICE(dev); @@ -301,9 +312,10 @@ static int adb_kbd_request(ADBDevice *d, uint8_t *obuf, static const VMStateDescription vmstate_adb_kbd = { .name = "adb_kbd", - .version_id = 1, - .minimum_version_id = 1, + .version_id = 2, + .minimum_version_id = 2, .fields = (VMStateField[]) { + VMSTATE_STRUCT(parent_obj, KBDState, 0, vmstate_adb_device, ADBDevice), VMSTATE_BUFFER(data, KBDState), VMSTATE_INT32(rptr, KBDState), VMSTATE_INT32(wptr, KBDState), @@ -515,9 +527,11 @@ static void adb_mouse_reset(DeviceState *dev) static const VMStateDescription vmstate_adb_mouse = { .name = "adb_mouse", - .version_id = 1, - .minimum_version_id = 1, + .version_id = 2, + .minimum_version_id = 2, .fields = (VMStateField[]) { + VMSTATE_STRUCT(parent_obj, MouseState, 0, vmstate_adb_device, + ADBDevice), VMSTATE_INT32(buttons_state, MouseState), VMSTATE_INT32(last_buttons_state, MouseState), VMSTATE_INT32(dx, MouseState), diff --git a/hw/intc/openpic.c b/hw/intc/openpic.c index 7d1f3b9497..87fe2e865d 100644 --- a/hw/intc/openpic.c +++ b/hw/intc/openpic.c @@ -200,11 +200,14 @@ typedef enum IRQType { IRQ_TYPE_FSLSPECIAL, /* FSL timer/IPI interrupt, edge, no polarity */ } IRQType; +/* Round up to the nearest 64 IRQs so that the queue length + * won't change when moving between 32 and 64 bit hosts. + */ +#define IRQQUEUE_SIZE_BITS ((OPENPIC_MAX_IRQ + 63) & ~63) + typedef struct IRQQueue { - /* Round up to the nearest 64 IRQs so that the queue length - * won't change when moving between 32 and 64 bit hosts. - */ - unsigned long queue[BITS_TO_LONGS((OPENPIC_MAX_IRQ + 63) & ~63)]; + unsigned long *queue; + int32_t queue_size; /* Only used for VMSTATE_BITMAP */ int next; int priority; } IRQQueue; @@ -240,6 +243,15 @@ typedef struct IRQSource { #define IDR_EP 0x80000000 /* external pin */ #define IDR_CI 0x40000000 /* critical interrupt */ +typedef struct OpenPICTimer { + uint32_t tccr; /* Global timer current count register */ + uint32_t tbcr; /* Global timer base count register */ +} OpenPICTimer; + +typedef struct OpenPICMSI { + uint32_t msir; /* Shared Message Signaled Interrupt Register */ +} OpenPICMSI; + typedef struct IRQDest { int32_t ctpr; /* CPU current task priority */ IRQQueue raised; @@ -288,14 +300,9 @@ typedef struct OpenPICState { IRQDest dst[MAX_CPU]; uint32_t nb_cpus; /* Timer registers */ - struct { - uint32_t tccr; /* Global timer current count register */ - uint32_t tbcr; /* Global timer base count register */ - } timers[OPENPIC_MAX_TMR]; + OpenPICTimer timers[OPENPIC_MAX_TMR]; /* Shared MSI registers */ - struct { - uint32_t msir; /* Shared Message Signaled Interrupt Register */ - } msi[MAX_MSI]; + OpenPICMSI msi[MAX_MSI]; uint32_t max_irq; uint32_t irq_ipi0; uint32_t irq_tim0; @@ -1013,7 +1020,7 @@ static void openpic_cpu_write_internal(void *opaque, hwaddr addr, DPRINTF("%s: cpu %d addr %#" HWADDR_PRIx " <= 0x%08x\n", __func__, idx, addr, val); - if (idx < 0) { + if (idx < 0 || idx >= opp->nb_cpus) { return; } @@ -1152,7 +1159,7 @@ static uint32_t openpic_cpu_read_internal(void *opaque, hwaddr addr, DPRINTF("%s: cpu %d addr %#" HWADDR_PRIx "\n", __func__, idx, addr); retval = 0xFFFFFFFF; - if (idx < 0) { + if (idx < 0 || idx >= opp->nb_cpus) { return retval; } @@ -1287,132 +1294,6 @@ static const MemoryRegionOps openpic_summary_ops_be = { }, }; -static void openpic_save_IRQ_queue(QEMUFile* f, IRQQueue *q) -{ - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(q->queue); i++) { - /* Always put the lower half of a 64-bit long first, in case we - * restore on a 32-bit host. The least significant bits correspond - * to lower IRQ numbers in the bitmap. - */ - qemu_put_be32(f, (uint32_t)q->queue[i]); -#if LONG_MAX > 0x7FFFFFFF - qemu_put_be32(f, (uint32_t)(q->queue[i] >> 32)); -#endif - } - - qemu_put_sbe32s(f, &q->next); - qemu_put_sbe32s(f, &q->priority); -} - -static void openpic_save(QEMUFile* f, void *opaque) -{ - OpenPICState *opp = (OpenPICState *)opaque; - unsigned int i; - - qemu_put_be32s(f, &opp->gcr); - qemu_put_be32s(f, &opp->vir); - qemu_put_be32s(f, &opp->pir); - qemu_put_be32s(f, &opp->spve); - qemu_put_be32s(f, &opp->tfrr); - - qemu_put_be32s(f, &opp->nb_cpus); - - for (i = 0; i < opp->nb_cpus; i++) { - qemu_put_sbe32s(f, &opp->dst[i].ctpr); - openpic_save_IRQ_queue(f, &opp->dst[i].raised); - openpic_save_IRQ_queue(f, &opp->dst[i].servicing); - qemu_put_buffer(f, (uint8_t *)&opp->dst[i].outputs_active, - sizeof(opp->dst[i].outputs_active)); - } - - for (i = 0; i < OPENPIC_MAX_TMR; i++) { - qemu_put_be32s(f, &opp->timers[i].tccr); - qemu_put_be32s(f, &opp->timers[i].tbcr); - } - - for (i = 0; i < opp->max_irq; i++) { - qemu_put_be32s(f, &opp->src[i].ivpr); - qemu_put_be32s(f, &opp->src[i].idr); - qemu_get_be32s(f, &opp->src[i].destmask); - qemu_put_sbe32s(f, &opp->src[i].last_cpu); - qemu_put_sbe32s(f, &opp->src[i].pending); - } -} - -static void openpic_load_IRQ_queue(QEMUFile* f, IRQQueue *q) -{ - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(q->queue); i++) { - unsigned long val; - - val = qemu_get_be32(f); -#if LONG_MAX > 0x7FFFFFFF - val <<= 32; - val |= qemu_get_be32(f); -#endif - - q->queue[i] = val; - } - - qemu_get_sbe32s(f, &q->next); - qemu_get_sbe32s(f, &q->priority); -} - -static int openpic_load(QEMUFile* f, void *opaque, int version_id) -{ - OpenPICState *opp = (OpenPICState *)opaque; - unsigned int i, nb_cpus; - - if (version_id != 1) { - return -EINVAL; - } - - qemu_get_be32s(f, &opp->gcr); - qemu_get_be32s(f, &opp->vir); - qemu_get_be32s(f, &opp->pir); - qemu_get_be32s(f, &opp->spve); - qemu_get_be32s(f, &opp->tfrr); - - qemu_get_be32s(f, &nb_cpus); - if (opp->nb_cpus != nb_cpus) { - return -EINVAL; - } - assert(nb_cpus > 0 && nb_cpus <= MAX_CPU); - - for (i = 0; i < opp->nb_cpus; i++) { - qemu_get_sbe32s(f, &opp->dst[i].ctpr); - openpic_load_IRQ_queue(f, &opp->dst[i].raised); - openpic_load_IRQ_queue(f, &opp->dst[i].servicing); - qemu_get_buffer(f, (uint8_t *)&opp->dst[i].outputs_active, - sizeof(opp->dst[i].outputs_active)); - } - - for (i = 0; i < OPENPIC_MAX_TMR; i++) { - qemu_get_be32s(f, &opp->timers[i].tccr); - qemu_get_be32s(f, &opp->timers[i].tbcr); - } - - for (i = 0; i < opp->max_irq; i++) { - uint32_t val; - - val = qemu_get_be32(f); - write_IRQreg_idr(opp, i, val); - val = qemu_get_be32(f); - write_IRQreg_ivpr(opp, i, val); - - qemu_get_be32s(f, &opp->src[i].ivpr); - qemu_get_be32s(f, &opp->src[i].idr); - qemu_get_be32s(f, &opp->src[i].destmask); - qemu_get_sbe32s(f, &opp->src[i].last_cpu); - qemu_get_sbe32s(f, &opp->src[i].pending); - } - - return 0; -} - static void openpic_reset(DeviceState *d) { OpenPICState *opp = OPENPIC(d); @@ -1446,12 +1327,14 @@ static void openpic_reset(DeviceState *d) write_IRQreg_idr(opp, i, opp->idr_reset); } /* Initialise IRQ destinations */ - for (i = 0; i < MAX_CPU; i++) { + for (i = 0; i < opp->nb_cpus; i++) { opp->dst[i].ctpr = 15; - memset(&opp->dst[i].raised, 0, sizeof(IRQQueue)); opp->dst[i].raised.next = -1; - memset(&opp->dst[i].servicing, 0, sizeof(IRQQueue)); + opp->dst[i].raised.priority = 0; + bitmap_clear(opp->dst[i].raised.queue, 0, IRQQUEUE_SIZE_BITS); opp->dst[i].servicing.next = -1; + opp->dst[i].servicing.priority = 0; + bitmap_clear(opp->dst[i].servicing.queue, 0, IRQQUEUE_SIZE_BITS); } /* Initialise timers */ for (i = 0; i < OPENPIC_MAX_TMR; i++) { @@ -1525,6 +1408,110 @@ static void map_list(OpenPICState *opp, const MemReg *list, int *count) } } +static const VMStateDescription vmstate_openpic_irq_queue = { + .name = "openpic_irq_queue", + .version_id = 0, + .minimum_version_id = 0, + .fields = (VMStateField[]) { + VMSTATE_BITMAP(queue, IRQQueue, 0, queue_size), + VMSTATE_INT32(next, IRQQueue), + VMSTATE_INT32(priority, IRQQueue), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_openpic_irqdest = { + .name = "openpic_irqdest", + .version_id = 0, + .minimum_version_id = 0, + .fields = (VMStateField[]) { + VMSTATE_INT32(ctpr, IRQDest), + VMSTATE_STRUCT(raised, IRQDest, 0, vmstate_openpic_irq_queue, + IRQQueue), + VMSTATE_STRUCT(servicing, IRQDest, 0, vmstate_openpic_irq_queue, + IRQQueue), + VMSTATE_UINT32_ARRAY(outputs_active, IRQDest, OPENPIC_OUTPUT_NB), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_openpic_irqsource = { + .name = "openpic_irqsource", + .version_id = 0, + .minimum_version_id = 0, + .fields = (VMStateField[]) { + VMSTATE_UINT32(ivpr, IRQSource), + VMSTATE_UINT32(idr, IRQSource), + VMSTATE_UINT32(destmask, IRQSource), + VMSTATE_INT32(last_cpu, IRQSource), + VMSTATE_INT32(pending, IRQSource), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_openpic_timer = { + .name = "openpic_timer", + .version_id = 0, + .minimum_version_id = 0, + .fields = (VMStateField[]) { + VMSTATE_UINT32(tccr, OpenPICTimer), + VMSTATE_UINT32(tbcr, OpenPICTimer), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_openpic_msi = { + .name = "openpic_msi", + .version_id = 0, + .minimum_version_id = 0, + .fields = (VMStateField[]) { + VMSTATE_UINT32(msir, OpenPICMSI), + VMSTATE_END_OF_LIST() + } +}; + +static int openpic_post_load(void *opaque, int version_id) +{ + OpenPICState *opp = (OpenPICState *)opaque; + int i; + + /* Update internal ivpr and idr variables */ + for (i = 0; i < opp->max_irq; i++) { + write_IRQreg_idr(opp, i, opp->src[i].idr); + write_IRQreg_ivpr(opp, i, opp->src[i].ivpr); + } + + return 0; +} + +static const VMStateDescription vmstate_openpic = { + .name = "openpic", + .version_id = 3, + .minimum_version_id = 3, + .post_load = openpic_post_load, + .fields = (VMStateField[]) { + VMSTATE_UINT32(gcr, OpenPICState), + VMSTATE_UINT32(vir, OpenPICState), + VMSTATE_UINT32(pir, OpenPICState), + VMSTATE_UINT32(spve, OpenPICState), + VMSTATE_UINT32(tfrr, OpenPICState), + VMSTATE_UINT32(max_irq, OpenPICState), + VMSTATE_STRUCT_VARRAY_UINT32(src, OpenPICState, max_irq, 0, + vmstate_openpic_irqsource, IRQSource), + VMSTATE_UINT32_EQUAL(nb_cpus, OpenPICState), + VMSTATE_STRUCT_VARRAY_UINT32(dst, OpenPICState, nb_cpus, 0, + vmstate_openpic_irqdest, IRQDest), + VMSTATE_STRUCT_ARRAY(timers, OpenPICState, OPENPIC_MAX_TMR, 0, + vmstate_openpic_timer, OpenPICTimer), + VMSTATE_STRUCT_ARRAY(msi, OpenPICState, MAX_MSI, 0, + vmstate_openpic_msi, OpenPICMSI), + VMSTATE_UINT32(irq_ipi0, OpenPICState), + VMSTATE_UINT32(irq_tim0, OpenPICState), + VMSTATE_UINT32(irq_msi, OpenPICState), + VMSTATE_END_OF_LIST() + } +}; + static void openpic_init(Object *obj) { OpenPICState *opp = OPENPIC(obj); @@ -1631,10 +1618,12 @@ static void openpic_realize(DeviceState *dev, Error **errp) for (j = 0; j < OPENPIC_OUTPUT_NB; j++) { sysbus_init_irq(d, &opp->dst[i].irqs[j]); } - } - register_savevm(dev, "openpic", 0, 2, - openpic_save, openpic_load, opp); + opp->dst[i].raised.queue_size = IRQQUEUE_SIZE_BITS; + opp->dst[i].raised.queue = bitmap_new(IRQQUEUE_SIZE_BITS); + opp->dst[i].servicing.queue_size = IRQQUEUE_SIZE_BITS; + opp->dst[i].servicing.queue = bitmap_new(IRQQUEUE_SIZE_BITS); + } sysbus_init_mmio(d, &opp->mem); qdev_init_gpio_in(dev, openpic_set_irq, opp->max_irq); @@ -1653,6 +1642,7 @@ static void openpic_class_init(ObjectClass *oc, void *data) dc->realize = openpic_realize; dc->props = openpic_properties; dc->reset = openpic_reset; + dc->vmsd = &vmstate_openpic; } static const TypeInfo openpic_info = { diff --git a/hw/misc/macio/cuda.c b/hw/misc/macio/cuda.c index 47d9771a04..f3984e3a20 100644 --- a/hw/misc/macio/cuda.c +++ b/hw/misc/macio/cuda.c @@ -638,8 +638,8 @@ static const VMStateDescription vmstate_cuda_timer = { static const VMStateDescription vmstate_cuda = { .name = "cuda", - .version_id = 1, - .minimum_version_id = 1, + .version_id = 2, + .minimum_version_id = 2, .fields = (VMStateField[]) { VMSTATE_UINT8(a, CUDAState), VMSTATE_UINT8(b, CUDAState), @@ -660,6 +660,7 @@ static const VMStateDescription vmstate_cuda = { VMSTATE_UINT32(tick_offset, CUDAState), VMSTATE_STRUCT_ARRAY(timers, CUDAState, 2, 1, vmstate_cuda_timer, CUDATimer), + VMSTATE_TIMER_PTR(adb_poll_timer, CUDAState), VMSTATE_END_OF_LIST() } }; diff --git a/hw/misc/macio/macio.c b/hw/misc/macio/macio.c index e0f1e88f54..9bc3f2d908 100644 --- a/hw/misc/macio/macio.c +++ b/hw/misc/macio/macio.c @@ -336,20 +336,44 @@ static void macio_instance_init(Object *obj) memory_region_add_subregion(&s->bar, 0x08000, dbdma_mem); } +static const VMStateDescription vmstate_macio_oldworld = { + .name = "macio-oldworld", + .version_id = 0, + .minimum_version_id = 0, + .fields = (VMStateField[]) { + VMSTATE_PCI_DEVICE(parent_obj.parent, OldWorldMacIOState), + VMSTATE_END_OF_LIST() + } +}; + static void macio_oldworld_class_init(ObjectClass *oc, void *data) { PCIDeviceClass *pdc = PCI_DEVICE_CLASS(oc); + DeviceClass *dc = DEVICE_CLASS(oc); pdc->init = macio_oldworld_initfn; pdc->device_id = PCI_DEVICE_ID_APPLE_343S1201; + dc->vmsd = &vmstate_macio_oldworld; } +static const VMStateDescription vmstate_macio_newworld = { + .name = "macio-newworld", + .version_id = 0, + .minimum_version_id = 0, + .fields = (VMStateField[]) { + VMSTATE_PCI_DEVICE(parent_obj.parent, NewWorldMacIOState), + VMSTATE_END_OF_LIST() + } +}; + static void macio_newworld_class_init(ObjectClass *oc, void *data) { PCIDeviceClass *pdc = PCI_DEVICE_CLASS(oc); + DeviceClass *dc = DEVICE_CLASS(oc); pdc->init = macio_newworld_initfn; pdc->device_id = PCI_DEVICE_ID_APPLE_UNI_N_KEYL; + dc->vmsd = &vmstate_macio_newworld; } static Property macio_properties[] = { diff --git a/hw/net/spapr_llan.c b/hw/net/spapr_llan.c index c255d925a7..2dd5ec1117 100644 --- a/hw/net/spapr_llan.c +++ b/hw/net/spapr_llan.c @@ -203,7 +203,7 @@ static void spapr_vlan_reset(VIOsPAPRDevice *sdev) dev->isopen = 0; } -static int spapr_vlan_init(VIOsPAPRDevice *sdev) +static void spapr_vlan_realize(VIOsPAPRDevice *sdev, Error **errp) { VIOsPAPRVLANDevice *dev = VIO_SPAPR_VLAN_DEVICE(sdev); @@ -212,8 +212,6 @@ static int spapr_vlan_init(VIOsPAPRDevice *sdev) dev->nic = qemu_new_nic(&net_spapr_vlan_info, &dev->nicconf, object_get_typename(OBJECT(sdev)), sdev->qdev.id, dev); qemu_format_nic_info_str(qemu_get_queue(dev->nic), dev->nicconf.macaddr.a); - - return 0; } static void spapr_vlan_instance_init(Object *obj) @@ -534,7 +532,7 @@ static void spapr_vlan_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); VIOsPAPRDeviceClass *k = VIO_SPAPR_DEVICE_CLASS(klass); - k->init = spapr_vlan_init; + k->realize = spapr_vlan_realize; k->reset = spapr_vlan_reset; k->devnode = spapr_vlan_devnode; k->dt_name = "l-lan"; diff --git a/hw/nvram/spapr_nvram.c b/hw/nvram/spapr_nvram.c index 35dc6d5684..11332d14ea 100644 --- a/hw/nvram/spapr_nvram.c +++ b/hw/nvram/spapr_nvram.c @@ -132,7 +132,7 @@ static void rtas_nvram_store(PowerPCCPU *cpu, sPAPREnvironment *spapr, rtas_st(rets, 1, (alen < 0) ? 0 : alen); } -static int spapr_nvram_init(VIOsPAPRDevice *dev) +static void spapr_nvram_realize(VIOsPAPRDevice *dev, Error **errp) { sPAPRNVRAM *nvram = VIO_SPAPR_NVRAM(dev); @@ -145,23 +145,22 @@ static int spapr_nvram_init(VIOsPAPRDevice *dev) nvram->buf = g_malloc0(nvram->size); if ((nvram->size < MIN_NVRAM_SIZE) || (nvram->size > MAX_NVRAM_SIZE)) { - fprintf(stderr, "spapr-nvram must be between %d and %d bytes in size\n", - MIN_NVRAM_SIZE, MAX_NVRAM_SIZE); - return -1; + error_setg(errp, "spapr-nvram must be between %d and %d bytes in size", + MIN_NVRAM_SIZE, MAX_NVRAM_SIZE); + return; } if (nvram->blk) { int alen = blk_pread(nvram->blk, 0, nvram->buf, nvram->size); if (alen != nvram->size) { - return -1; + error_setg(errp, "can't read spapr-nvram contents"); + return; } } spapr_rtas_register(RTAS_NVRAM_FETCH, "nvram-fetch", rtas_nvram_fetch); spapr_rtas_register(RTAS_NVRAM_STORE, "nvram-store", rtas_nvram_store); - - return 0; } static int spapr_nvram_devnode(VIOsPAPRDevice *dev, void *fdt, int node_off) @@ -224,7 +223,7 @@ static void spapr_nvram_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); VIOsPAPRDeviceClass *k = VIO_SPAPR_DEVICE_CLASS(klass); - k->init = spapr_nvram_init; + k->realize = spapr_nvram_realize; k->devnode = spapr_nvram_devnode; k->dt_name = "nvram"; k->dt_type = "nvram"; diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs index 19d99200ae..437955d1d5 100644 --- a/hw/ppc/Makefile.objs +++ b/hw/ppc/Makefile.objs @@ -3,7 +3,7 @@ obj-y += ppc.o ppc_booke.o # IBM pSeries (sPAPR) obj-$(CONFIG_PSERIES) += spapr.o spapr_vio.o spapr_events.o obj-$(CONFIG_PSERIES) += spapr_hcall.o spapr_iommu.o spapr_rtas.o -obj-$(CONFIG_PSERIES) += spapr_pci.o +obj-$(CONFIG_PSERIES) += spapr_pci.o spapr_rtc.o ifeq ($(CONFIG_PCI)$(CONFIG_PSERIES)$(CONFIG_LINUX), yyy) obj-y += spapr_pci_vfio.o endif diff --git a/hw/ppc/e500.c b/hw/ppc/e500.c index 7e17d180c6..fd0d138a1b 100644 --- a/hw/ppc/e500.c +++ b/hw/ppc/e500.c @@ -706,17 +706,19 @@ static DeviceState *ppce500_init_mpic_qemu(PPCE500Params *params, } static DeviceState *ppce500_init_mpic_kvm(PPCE500Params *params, - qemu_irq **irqs) + qemu_irq **irqs, Error **errp) { + Error *err = NULL; DeviceState *dev; CPUState *cs; - int r; dev = qdev_create(NULL, TYPE_KVM_OPENPIC); qdev_prop_set_uint32(dev, "model", params->mpic_version); - r = qdev_init(dev); - if (r) { + object_property_set_bool(OBJECT(dev), true, "realized", &err); + if (err) { + error_propagate(errp, err); + object_unparent(OBJECT(dev)); return NULL; } @@ -747,15 +749,15 @@ static qemu_irq *ppce500_init_mpic(PPCE500Params *params, MemoryRegion *ccsr, "kernel_irqchip", true); bool irqchip_required = qemu_opt_get_bool(machine_opts, "kernel_irqchip", false); + Error *err = NULL; if (irqchip_allowed) { - dev = ppce500_init_mpic_kvm(params, irqs); + dev = ppce500_init_mpic_kvm(params, irqs, &err); } - if (irqchip_required && !dev) { - fprintf(stderr, "%s: irqchip requested but unavailable\n", - __func__); - abort(); + error_report("kernel_irqchip requested but unavailable: %s", + error_get_pretty(err)); + exit(1); } } diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 23cde20019..4aa979fbbf 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -110,17 +110,20 @@ struct sPAPRMachineState { sPAPREnvironment *spapr; static XICSState *try_create_xics(const char *type, int nr_servers, - int nr_irqs) + int nr_irqs, Error **errp) { + Error *err = NULL; DeviceState *dev; dev = qdev_create(NULL, type); qdev_prop_set_uint32(dev, "nr_servers", nr_servers); qdev_prop_set_uint32(dev, "nr_irqs", nr_irqs); - if (qdev_init(dev) < 0) { + object_property_set_bool(OBJECT(dev), true, "realized", &err); + if (err) { + error_propagate(errp, err); + object_unparent(OBJECT(dev)); return NULL; } - return XICS_COMMON(dev); } @@ -134,23 +137,19 @@ static XICSState *xics_system_init(int nr_servers, int nr_irqs) "kernel_irqchip", true); bool irqchip_required = qemu_opt_get_bool(machine_opts, "kernel_irqchip", false); + Error *err = NULL; + if (irqchip_allowed) { - icp = try_create_xics(TYPE_KVM_XICS, nr_servers, nr_irqs); + icp = try_create_xics(TYPE_KVM_XICS, nr_servers, nr_irqs, &err); } - if (irqchip_required && !icp) { - perror("Failed to create in-kernel XICS\n"); - abort(); + error_report("kernel_irqchip requested but unavailable: %s", + error_get_pretty(err)); } } if (!icp) { - icp = try_create_xics(TYPE_XICS, nr_servers, nr_irqs); - } - - if (!icp) { - perror("Failed to create XICS\n"); - abort(); + icp = try_create_xics(TYPE_XICS, nr_servers, nr_irqs, &error_abort); } return icp; @@ -994,6 +993,17 @@ static void spapr_create_nvram(sPAPREnvironment *spapr) spapr->nvram = (struct sPAPRNVRAM *)dev; } +static void spapr_rtc_create(sPAPREnvironment *spapr) +{ + DeviceState *dev = qdev_create(NULL, TYPE_SPAPR_RTC); + + qdev_init_nofail(dev); + spapr->rtc = dev; + + object_property_add_alias(qdev_get_machine(), "rtc-time", + OBJECT(spapr->rtc), "date", NULL); +} + /* Returns whether we want to use VGA or not */ static int spapr_vga_init(PCIBus *pci_bus) { @@ -1011,15 +1021,39 @@ static int spapr_vga_init(PCIBus *pci_bus) } } +static int spapr_post_load(void *opaque, int version_id) +{ + sPAPREnvironment *spapr = (sPAPREnvironment *)opaque; + int err = 0; + + /* In earlier versions, there was no seperate qdev for the PAPR + * RTC, so the RTC offset was stored directly in sPAPREnvironment. + * So when migrating from those versions, poke the incoming offset + * value into the RTC device */ + if (version_id < 3) { + err = spapr_rtc_import_offset(spapr->rtc, spapr->rtc_offset); + } + + return err; +} + +static bool version_before_3(void *opaque, int version_id) +{ + return version_id < 3; +} + static const VMStateDescription vmstate_spapr = { .name = "spapr", - .version_id = 2, + .version_id = 3, .minimum_version_id = 1, + .post_load = spapr_post_load, .fields = (VMStateField[]) { - VMSTATE_UNUSED(4), /* used to be @next_irq */ + /* used to be @next_irq */ + VMSTATE_UNUSED_BUFFER(version_before_3, 0, 4), /* RTC offset */ - VMSTATE_UINT64(rtc_offset, sPAPREnvironment), + VMSTATE_UINT64_TEST(rtc_offset, sPAPREnvironment, version_before_3), + VMSTATE_PPC_TIMEBASE_V(tb, sPAPREnvironment, 2), VMSTATE_END_OF_LIST() }, @@ -1491,6 +1525,9 @@ static void ppc_spapr_init(MachineState *machine) /* Set up EPOW events infrastructure */ spapr_events_init(spapr); + /* Set up the RTC RTAS interfaces */ + spapr_rtc_create(spapr); + /* Set up VIO bus */ spapr->vio_bus = spapr_vio_bus_init(); @@ -1759,11 +1796,22 @@ static const TypeInfo spapr_machine_info = { }, }; +#define SPAPR_COMPAT_2_2 \ + {\ + .driver = TYPE_SPAPR_PCI_HOST_BRIDGE,\ + .property = "mem_win_size",\ + .value = "0x20000000",\ + } + +#define SPAPR_COMPAT_2_1 \ + SPAPR_COMPAT_2_2 + static void spapr_machine_2_1_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); static GlobalProperty compat_props[] = { HW_COMPAT_2_1, + SPAPR_COMPAT_2_1, { /* end of list */ } }; @@ -1780,12 +1828,15 @@ static const TypeInfo spapr_machine_2_1_info = { static void spapr_machine_2_2_class_init(ObjectClass *oc, void *data) { + static GlobalProperty compat_props[] = { + SPAPR_COMPAT_2_2, + { /* end of list */ } + }; MachineClass *mc = MACHINE_CLASS(oc); mc->name = "pseries-2.2"; mc->desc = "pSeries Logical Partition (PAPR compliant) v2.2"; - mc->alias = "pseries"; - mc->is_default = 1; + mc->compat_props = compat_props; } static const TypeInfo spapr_machine_2_2_info = { @@ -1794,11 +1845,28 @@ static const TypeInfo spapr_machine_2_2_info = { .class_init = spapr_machine_2_2_class_init, }; +static void spapr_machine_2_3_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->name = "pseries-2.3"; + mc->desc = "pSeries Logical Partition (PAPR compliant) v2.3"; + mc->alias = "pseries"; + mc->is_default = 1; +} + +static const TypeInfo spapr_machine_2_3_info = { + .name = TYPE_SPAPR_MACHINE "2.3", + .parent = TYPE_SPAPR_MACHINE, + .class_init = spapr_machine_2_3_class_init, +}; + static void spapr_machine_register_types(void) { type_register_static(&spapr_machine_info); type_register_static(&spapr_machine_2_1_info); type_register_static(&spapr_machine_2_2_info); + type_register_static(&spapr_machine_2_3_info); } type_init(spapr_machine_register_types) diff --git a/hw/ppc/spapr_events.c b/hw/ppc/spapr_events.c index 1b6157dec4..283e96bca1 100644 --- a/hw/ppc/spapr_events.c +++ b/hw/ppc/spapr_events.c @@ -246,7 +246,7 @@ static void spapr_powerdown_req(Notifier *n, void *opaque) maina->hdr.section_id = cpu_to_be16(RTAS_LOG_V6_SECTION_ID_MAINA); maina->hdr.section_length = cpu_to_be16(sizeof(*maina)); /* FIXME: section version, subtype and creator id? */ - qemu_get_timedate(&tm, spapr->rtc_offset); + spapr_rtc_read(spapr->rtc, &tm, NULL); year = tm.tm_year + 1900; maina->creation_date = cpu_to_be32((to_bcd(year / 100) << 24) | (to_bcd(year % 100) << 16) diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c index 86514472fe..4f76f1cbfe 100644 --- a/hw/ppc/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -731,12 +731,14 @@ static target_ulong h_set_mode_resource_le(PowerPCCPU *cpu, CPU_FOREACH(cs) { set_spr(cs, SPR_LPCR, 0, LPCR_ILE); } + spapr_pci_switch_vga(true); return H_SUCCESS; case H_SET_MODE_ENDIAN_LITTLE: CPU_FOREACH(cs) { set_spr(cs, SPR_LPCR, LPCR_ILE, LPCR_ILE); } + spapr_pci_switch_vga(false); return H_SUCCESS; } diff --git a/hw/ppc/spapr_iommu.c b/hw/ppc/spapr_iommu.c index ba003da39e..f3990fdc32 100644 --- a/hw/ppc/spapr_iommu.c +++ b/hw/ppc/spapr_iommu.c @@ -25,6 +25,7 @@ #include "trace.h" #include "hw/ppc/spapr.h" +#include "hw/ppc/spapr_vio.h" #include <libfdt.h> @@ -73,9 +74,7 @@ static IOMMUTLBEntry spapr_tce_translate_iommu(MemoryRegion *iommu, hwaddr addr, .perm = IOMMU_NONE, }; - if (tcet->bypass) { - ret.perm = IOMMU_RW; - } else if ((addr >> tcet->page_shift) < tcet->nb_table) { + if ((addr >> tcet->page_shift) < tcet->nb_table) { /* Check if we are in bound */ hwaddr page_mask = IOMMU_PAGE_MASK(tcet->page_shift); @@ -91,10 +90,22 @@ static IOMMUTLBEntry spapr_tce_translate_iommu(MemoryRegion *iommu, hwaddr addr, return ret; } +static int spapr_tce_table_post_load(void *opaque, int version_id) +{ + sPAPRTCETable *tcet = SPAPR_TCE_TABLE(opaque); + + if (tcet->vdev) { + spapr_vio_set_bypass(tcet->vdev, tcet->bypass); + } + + return 0; +} + static const VMStateDescription vmstate_spapr_tce_table = { .name = "spapr_iommu", .version_id = 2, .minimum_version_id = 2, + .post_load = spapr_tce_table_post_load, .fields = (VMStateField []) { /* Sanity check */ VMSTATE_UINT32_EQUAL(liobn, sPAPRTCETable), @@ -132,7 +143,8 @@ static int spapr_tce_table_realize(DeviceState *dev) trace_spapr_iommu_new_table(tcet->liobn, tcet, tcet->table, tcet->fd); memory_region_init_iommu(&tcet->iommu, OBJECT(dev), &spapr_iommu_ops, - "iommu-spapr", ram_size); + "iommu-spapr", + (uint64_t)tcet->nb_table << tcet->page_shift); QLIST_INSERT_HEAD(&spapr_tce_tables, tcet, list); @@ -192,17 +204,11 @@ MemoryRegion *spapr_tce_get_iommu(sPAPRTCETable *tcet) return &tcet->iommu; } -void spapr_tce_set_bypass(sPAPRTCETable *tcet, bool bypass) -{ - tcet->bypass = bypass; -} - static void spapr_tce_reset(DeviceState *dev) { sPAPRTCETable *tcet = SPAPR_TCE_TABLE(dev); size_t table_size = tcet->nb_table * sizeof(uint64_t); - tcet->bypass = false; memset(tcet->table, 0, table_size); } diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c index 21b95b342c..05f4faca6e 100644 --- a/hw/ppc/spapr_pci.c +++ b/hw/ppc/spapr_pci.c @@ -406,6 +406,258 @@ static void rtas_ibm_query_interrupt_source_number(PowerPCCPU *cpu, rtas_st(rets, 2, 1);/* 0 == level; 1 == edge */ } +static void rtas_ibm_set_eeh_option(PowerPCCPU *cpu, + sPAPREnvironment *spapr, + uint32_t token, uint32_t nargs, + target_ulong args, uint32_t nret, + target_ulong rets) +{ + sPAPRPHBState *sphb; + sPAPRPHBClass *spc; + uint32_t addr, option; + uint64_t buid; + int ret; + + if ((nargs != 4) || (nret != 1)) { + goto param_error_exit; + } + + buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2); + addr = rtas_ld(args, 0); + option = rtas_ld(args, 3); + + sphb = find_phb(spapr, buid); + if (!sphb) { + goto param_error_exit; + } + + spc = SPAPR_PCI_HOST_BRIDGE_GET_CLASS(sphb); + if (!spc->eeh_set_option) { + goto param_error_exit; + } + + ret = spc->eeh_set_option(sphb, addr, option); + rtas_st(rets, 0, ret); + return; + +param_error_exit: + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); +} + +static void rtas_ibm_get_config_addr_info2(PowerPCCPU *cpu, + sPAPREnvironment *spapr, + uint32_t token, uint32_t nargs, + target_ulong args, uint32_t nret, + target_ulong rets) +{ + sPAPRPHBState *sphb; + sPAPRPHBClass *spc; + PCIDevice *pdev; + uint32_t addr, option; + uint64_t buid; + + if ((nargs != 4) || (nret != 2)) { + goto param_error_exit; + } + + buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2); + sphb = find_phb(spapr, buid); + if (!sphb) { + goto param_error_exit; + } + + spc = SPAPR_PCI_HOST_BRIDGE_GET_CLASS(sphb); + if (!spc->eeh_set_option) { + goto param_error_exit; + } + + /* + * We always have PE address of form "00BB0001". "BB" + * represents the bus number of PE's primary bus. + */ + option = rtas_ld(args, 3); + switch (option) { + case RTAS_GET_PE_ADDR: + addr = rtas_ld(args, 0); + pdev = find_dev(spapr, buid, addr); + if (!pdev) { + goto param_error_exit; + } + + rtas_st(rets, 1, (pci_bus_num(pdev->bus) << 16) + 1); + break; + case RTAS_GET_PE_MODE: + rtas_st(rets, 1, RTAS_PE_MODE_SHARED); + break; + default: + goto param_error_exit; + } + + rtas_st(rets, 0, RTAS_OUT_SUCCESS); + return; + +param_error_exit: + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); +} + +static void rtas_ibm_read_slot_reset_state2(PowerPCCPU *cpu, + sPAPREnvironment *spapr, + uint32_t token, uint32_t nargs, + target_ulong args, uint32_t nret, + target_ulong rets) +{ + sPAPRPHBState *sphb; + sPAPRPHBClass *spc; + uint64_t buid; + int state, ret; + + if ((nargs != 3) || (nret != 4 && nret != 5)) { + goto param_error_exit; + } + + buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2); + sphb = find_phb(spapr, buid); + if (!sphb) { + goto param_error_exit; + } + + spc = SPAPR_PCI_HOST_BRIDGE_GET_CLASS(sphb); + if (!spc->eeh_get_state) { + goto param_error_exit; + } + + ret = spc->eeh_get_state(sphb, &state); + rtas_st(rets, 0, ret); + if (ret != RTAS_OUT_SUCCESS) { + return; + } + + rtas_st(rets, 1, state); + rtas_st(rets, 2, RTAS_EEH_SUPPORT); + rtas_st(rets, 3, RTAS_EEH_PE_UNAVAIL_INFO); + if (nret >= 5) { + rtas_st(rets, 4, RTAS_EEH_PE_RECOVER_INFO); + } + return; + +param_error_exit: + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); +} + +static void rtas_ibm_set_slot_reset(PowerPCCPU *cpu, + sPAPREnvironment *spapr, + uint32_t token, uint32_t nargs, + target_ulong args, uint32_t nret, + target_ulong rets) +{ + sPAPRPHBState *sphb; + sPAPRPHBClass *spc; + uint32_t option; + uint64_t buid; + int ret; + + if ((nargs != 4) || (nret != 1)) { + goto param_error_exit; + } + + buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2); + option = rtas_ld(args, 3); + sphb = find_phb(spapr, buid); + if (!sphb) { + goto param_error_exit; + } + + spc = SPAPR_PCI_HOST_BRIDGE_GET_CLASS(sphb); + if (!spc->eeh_reset) { + goto param_error_exit; + } + + ret = spc->eeh_reset(sphb, option); + rtas_st(rets, 0, ret); + return; + +param_error_exit: + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); +} + +static void rtas_ibm_configure_pe(PowerPCCPU *cpu, + sPAPREnvironment *spapr, + uint32_t token, uint32_t nargs, + target_ulong args, uint32_t nret, + target_ulong rets) +{ + sPAPRPHBState *sphb; + sPAPRPHBClass *spc; + uint64_t buid; + int ret; + + if ((nargs != 3) || (nret != 1)) { + goto param_error_exit; + } + + buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2); + sphb = find_phb(spapr, buid); + if (!sphb) { + goto param_error_exit; + } + + spc = SPAPR_PCI_HOST_BRIDGE_GET_CLASS(sphb); + if (!spc->eeh_configure) { + goto param_error_exit; + } + + ret = spc->eeh_configure(sphb); + rtas_st(rets, 0, ret); + return; + +param_error_exit: + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); +} + +/* To support it later */ +static void rtas_ibm_slot_error_detail(PowerPCCPU *cpu, + sPAPREnvironment *spapr, + uint32_t token, uint32_t nargs, + target_ulong args, uint32_t nret, + target_ulong rets) +{ + sPAPRPHBState *sphb; + sPAPRPHBClass *spc; + int option; + uint64_t buid; + + if ((nargs != 8) || (nret != 1)) { + goto param_error_exit; + } + + buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2); + sphb = find_phb(spapr, buid); + if (!sphb) { + goto param_error_exit; + } + + spc = SPAPR_PCI_HOST_BRIDGE_GET_CLASS(sphb); + if (!spc->eeh_set_option) { + goto param_error_exit; + } + + option = rtas_ld(args, 7); + switch (option) { + case RTAS_SLOT_TEMP_ERR_LOG: + case RTAS_SLOT_PERM_ERR_LOG: + break; + default: + goto param_error_exit; + } + + /* We don't have error log yet */ + rtas_st(rets, 0, RTAS_OUT_NO_ERRORS_FOUND); + return; + +param_error_exit: + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); +} + static int pci_spapr_swizzle(int slot, int pin) { return (slot + pin) % PCI_NUM_PINS; @@ -501,6 +753,12 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp) return; } + if (sphb->index > SPAPR_PCI_MAX_INDEX) { + error_setg(errp, "\"index\" for PAPR PHB is too large (max %u)", + SPAPR_PCI_MAX_INDEX); + return; + } + sphb->buid = SPAPR_PCI_BASE_BUID + sphb->index; sphb->dma_liobn = SPAPR_PCI_BASE_LIOBN + sphb->index; @@ -669,7 +927,7 @@ static void spapr_phb_reset(DeviceState *qdev) } static Property spapr_phb_properties[] = { - DEFINE_PROP_INT32("index", sPAPRPHBState, index, -1), + DEFINE_PROP_UINT32("index", sPAPRPHBState, index, -1), DEFINE_PROP_UINT64("buid", sPAPRPHBState, buid, -1), DEFINE_PROP_UINT32("liobn", sPAPRPHBState, dma_liobn, -1), DEFINE_PROP_UINT64("mem_win_addr", sPAPRPHBState, mem_win_addr, -1), @@ -862,6 +1120,10 @@ int spapr_populate_pci_dt(sPAPRPHBState *phb, int bus_off, i, j; char nodename[256]; uint32_t bus_range[] = { cpu_to_be32(0), cpu_to_be32(0xff) }; + const uint64_t mmiosize = memory_region_size(&phb->memwindow); + const uint64_t w32max = (1ULL << 32) - SPAPR_PCI_MEM_WIN_BUS_OFFSET; + const uint64_t w32size = MIN(w32max, mmiosize); + const uint64_t w64size = (mmiosize > w32size) ? (mmiosize - w32size) : 0; struct { uint32_t hi; uint64_t child; @@ -876,9 +1138,15 @@ int spapr_populate_pci_dt(sPAPRPHBState *phb, { cpu_to_be32(b_ss(2)), cpu_to_be64(SPAPR_PCI_MEM_WIN_BUS_OFFSET), cpu_to_be64(phb->mem_win_addr), - cpu_to_be64(memory_region_size(&phb->memwindow)), + cpu_to_be64(w32size), + }, + { + cpu_to_be32(b_ss(3)), cpu_to_be64(1ULL << 32), + cpu_to_be64(phb->mem_win_addr + w32size), + cpu_to_be64(w64size) }, }; + const unsigned sizeof_ranges = (w64size ? 3 : 2) * sizeof(ranges[0]); uint64_t bus_reg[] = { cpu_to_be64(phb->buid), 0 }; uint32_t interrupt_map_mask[] = { cpu_to_be32(b_ddddd(-1)|b_fff(0)), 0x0, 0x0, cpu_to_be32(-1)}; @@ -907,7 +1175,7 @@ int spapr_populate_pci_dt(sPAPRPHBState *phb, _FDT(fdt_setprop_cell(fdt, bus_off, "#interrupt-cells", 0x1)); _FDT(fdt_setprop(fdt, bus_off, "used-by-rtas", NULL, 0)); _FDT(fdt_setprop(fdt, bus_off, "bus-range", &bus_range, sizeof(bus_range))); - _FDT(fdt_setprop(fdt, bus_off, "ranges", &ranges, sizeof(ranges))); + _FDT(fdt_setprop(fdt, bus_off, "ranges", &ranges, sizeof_ranges)); _FDT(fdt_setprop(fdt, bus_off, "reg", &bus_reg, sizeof(bus_reg))); _FDT(fdt_setprop_cell(fdt, bus_off, "ibm,pci-config-space-type", 0x1)); _FDT(fdt_setprop_cell(fdt, bus_off, "ibm,pe-total-#msi", XICS_IRQS)); @@ -958,6 +1226,25 @@ void spapr_pci_rtas_init(void) spapr_rtas_register(RTAS_IBM_CHANGE_MSI, "ibm,change-msi", rtas_ibm_change_msi); } + + spapr_rtas_register(RTAS_IBM_SET_EEH_OPTION, + "ibm,set-eeh-option", + rtas_ibm_set_eeh_option); + spapr_rtas_register(RTAS_IBM_GET_CONFIG_ADDR_INFO2, + "ibm,get-config-addr-info2", + rtas_ibm_get_config_addr_info2); + spapr_rtas_register(RTAS_IBM_READ_SLOT_RESET_STATE2, + "ibm,read-slot-reset-state2", + rtas_ibm_read_slot_reset_state2); + spapr_rtas_register(RTAS_IBM_SET_SLOT_RESET, + "ibm,set-slot-reset", + rtas_ibm_set_slot_reset); + spapr_rtas_register(RTAS_IBM_CONFIGURE_PE, + "ibm,configure-pe", + rtas_ibm_configure_pe); + spapr_rtas_register(RTAS_IBM_SLOT_ERROR_DETAIL, + "ibm,slot-error-detail", + rtas_ibm_slot_error_detail); } static void spapr_pci_register_types(void) @@ -966,3 +1253,31 @@ static void spapr_pci_register_types(void) } type_init(spapr_pci_register_types) + +static int spapr_switch_one_vga(DeviceState *dev, void *opaque) +{ + bool be = *(bool *)opaque; + + if (object_dynamic_cast(OBJECT(dev), "VGA") + || object_dynamic_cast(OBJECT(dev), "secondary-vga")) { + object_property_set_bool(OBJECT(dev), be, "big-endian-framebuffer", + &error_abort); + } + return 0; +} + +void spapr_pci_switch_vga(bool big_endian) +{ + sPAPRPHBState *sphb; + + /* + * For backward compatibility with existing guests, we switch + * the endianness of the VGA controller when changing the guest + * interrupt mode + */ + QLIST_FOREACH(sphb, &spapr->phbs, list) { + BusState *bus = &PCI_HOST_BRIDGE(sphb)->bus->qbus; + qbus_walk_children(bus, spapr_switch_one_vga, NULL, NULL, NULL, + &big_endian); + } +} diff --git a/hw/ppc/spapr_pci_vfio.c b/hw/ppc/spapr_pci_vfio.c index 144912bf54..99a1be5113 100644 --- a/hw/ppc/spapr_pci_vfio.c +++ b/hw/ppc/spapr_pci_vfio.c @@ -76,6 +76,117 @@ static void spapr_phb_vfio_reset(DeviceState *qdev) /* Do nothing */ } +static int spapr_phb_vfio_eeh_set_option(sPAPRPHBState *sphb, + unsigned int addr, int option) +{ + sPAPRPHBVFIOState *svphb = SPAPR_PCI_VFIO_HOST_BRIDGE(sphb); + struct vfio_eeh_pe_op op = { .argsz = sizeof(op) }; + int ret; + + switch (option) { + case RTAS_EEH_DISABLE: + op.op = VFIO_EEH_PE_DISABLE; + break; + case RTAS_EEH_ENABLE: { + PCIHostState *phb; + PCIDevice *pdev; + + /* + * The EEH functionality is enabled on basis of PCI device, + * instead of PE. We need check the validity of the PCI + * device address. + */ + phb = PCI_HOST_BRIDGE(sphb); + pdev = pci_find_device(phb->bus, + (addr >> 16) & 0xFF, (addr >> 8) & 0xFF); + if (!pdev) { + return RTAS_OUT_PARAM_ERROR; + } + + op.op = VFIO_EEH_PE_ENABLE; + break; + } + case RTAS_EEH_THAW_IO: + op.op = VFIO_EEH_PE_UNFREEZE_IO; + break; + case RTAS_EEH_THAW_DMA: + op.op = VFIO_EEH_PE_UNFREEZE_DMA; + break; + default: + return RTAS_OUT_PARAM_ERROR; + } + + ret = vfio_container_ioctl(&svphb->phb.iommu_as, svphb->iommugroupid, + VFIO_EEH_PE_OP, &op); + if (ret < 0) { + return RTAS_OUT_HW_ERROR; + } + + return RTAS_OUT_SUCCESS; +} + +static int spapr_phb_vfio_eeh_get_state(sPAPRPHBState *sphb, int *state) +{ + sPAPRPHBVFIOState *svphb = SPAPR_PCI_VFIO_HOST_BRIDGE(sphb); + struct vfio_eeh_pe_op op = { .argsz = sizeof(op) }; + int ret; + + op.op = VFIO_EEH_PE_GET_STATE; + ret = vfio_container_ioctl(&svphb->phb.iommu_as, svphb->iommugroupid, + VFIO_EEH_PE_OP, &op); + if (ret < 0) { + return RTAS_OUT_PARAM_ERROR; + } + + *state = ret; + return RTAS_OUT_SUCCESS; +} + +static int spapr_phb_vfio_eeh_reset(sPAPRPHBState *sphb, int option) +{ + sPAPRPHBVFIOState *svphb = SPAPR_PCI_VFIO_HOST_BRIDGE(sphb); + struct vfio_eeh_pe_op op = { .argsz = sizeof(op) }; + int ret; + + switch (option) { + case RTAS_SLOT_RESET_DEACTIVATE: + op.op = VFIO_EEH_PE_RESET_DEACTIVATE; + break; + case RTAS_SLOT_RESET_HOT: + op.op = VFIO_EEH_PE_RESET_HOT; + break; + case RTAS_SLOT_RESET_FUNDAMENTAL: + op.op = VFIO_EEH_PE_RESET_FUNDAMENTAL; + break; + default: + return RTAS_OUT_PARAM_ERROR; + } + + ret = vfio_container_ioctl(&svphb->phb.iommu_as, svphb->iommugroupid, + VFIO_EEH_PE_OP, &op); + if (ret < 0) { + return RTAS_OUT_HW_ERROR; + } + + return RTAS_OUT_SUCCESS; +} + +static int spapr_phb_vfio_eeh_configure(sPAPRPHBState *sphb) +{ + sPAPRPHBVFIOState *svphb = SPAPR_PCI_VFIO_HOST_BRIDGE(sphb); + struct vfio_eeh_pe_op op = { .argsz = sizeof(op) }; + int ret; + + op.op = VFIO_EEH_PE_CONFIGURE; + ret = vfio_container_ioctl(&svphb->phb.iommu_as, svphb->iommugroupid, + VFIO_EEH_PE_OP, &op); + if (ret < 0) { + return RTAS_OUT_PARAM_ERROR; + } + + return RTAS_OUT_SUCCESS; +} + static void spapr_phb_vfio_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -84,6 +195,10 @@ static void spapr_phb_vfio_class_init(ObjectClass *klass, void *data) dc->props = spapr_phb_vfio_properties; dc->reset = spapr_phb_vfio_reset; spc->finish_realize = spapr_phb_vfio_finish_realize; + spc->eeh_set_option = spapr_phb_vfio_eeh_set_option; + spc->eeh_get_state = spapr_phb_vfio_eeh_get_state; + spc->eeh_reset = spapr_phb_vfio_eeh_reset; + spc->eeh_configure = spapr_phb_vfio_eeh_configure; } static const TypeInfo spapr_phb_vfio_info = { diff --git a/hw/ppc/spapr_rtas.c b/hw/ppc/spapr_rtas.c index 2ec2a8e4d1..0f1ae55828 100644 --- a/hw/ppc/spapr_rtas.c +++ b/hw/ppc/spapr_rtas.c @@ -52,51 +52,6 @@ static void rtas_display_character(PowerPCCPU *cpu, sPAPREnvironment *spapr, } } -static void rtas_get_time_of_day(PowerPCCPU *cpu, sPAPREnvironment *spapr, - uint32_t token, uint32_t nargs, - target_ulong args, - uint32_t nret, target_ulong rets) -{ - struct tm tm; - - if (nret != 8) { - rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); - return; - } - - qemu_get_timedate(&tm, spapr->rtc_offset); - - rtas_st(rets, 0, RTAS_OUT_SUCCESS); - rtas_st(rets, 1, tm.tm_year + 1900); - rtas_st(rets, 2, tm.tm_mon + 1); - rtas_st(rets, 3, tm.tm_mday); - rtas_st(rets, 4, tm.tm_hour); - rtas_st(rets, 5, tm.tm_min); - rtas_st(rets, 6, tm.tm_sec); - rtas_st(rets, 7, 0); /* we don't do nanoseconds */ -} - -static void rtas_set_time_of_day(PowerPCCPU *cpu, sPAPREnvironment *spapr, - uint32_t token, uint32_t nargs, - target_ulong args, - uint32_t nret, target_ulong rets) -{ - struct tm tm; - - tm.tm_year = rtas_ld(args, 0) - 1900; - tm.tm_mon = rtas_ld(args, 1) - 1; - tm.tm_mday = rtas_ld(args, 2); - tm.tm_hour = rtas_ld(args, 3); - tm.tm_min = rtas_ld(args, 4); - tm.tm_sec = rtas_ld(args, 5); - - /* Just generate a monitor event for the change */ - qapi_event_send_rtc_change(qemu_timedate_diff(&tm), &error_abort); - spapr->rtc_offset = qemu_timedate_diff(&tm); - - rtas_st(rets, 0, RTAS_OUT_SUCCESS); -} - static void rtas_power_off(PowerPCCPU *cpu, sPAPREnvironment *spapr, uint32_t token, uint32_t nargs, target_ulong args, uint32_t nret, target_ulong rets) @@ -400,10 +355,6 @@ static void core_rtas_register_types(void) { spapr_rtas_register(RTAS_DISPLAY_CHARACTER, "display-character", rtas_display_character); - spapr_rtas_register(RTAS_GET_TIME_OF_DAY, "get-time-of-day", - rtas_get_time_of_day); - spapr_rtas_register(RTAS_SET_TIME_OF_DAY, "set-time-of-day", - rtas_set_time_of_day); spapr_rtas_register(RTAS_POWER_OFF, "power-off", rtas_power_off); spapr_rtas_register(RTAS_SYSTEM_REBOOT, "system-reboot", rtas_system_reboot); diff --git a/hw/ppc/spapr_rtc.c b/hw/ppc/spapr_rtc.c new file mode 100644 index 0000000000..83eb7c186f --- /dev/null +++ b/hw/ppc/spapr_rtc.c @@ -0,0 +1,212 @@ +/* + * QEMU PowerPC pSeries Logical Partition (aka sPAPR) hardware System Emulator + * + * RTAS Real Time Clock + * + * Copyright (c) 2010-2011 David Gibson, IBM Corporation. + * Copyright 2014 David Gibson, Red Hat. + * + * 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 "cpu.h" +#include "sysemu/sysemu.h" +#include "hw/ppc/spapr.h" +#include "qapi-event.h" + +#define SPAPR_RTC(obj) \ + OBJECT_CHECK(sPAPRRTCState, (obj), TYPE_SPAPR_RTC) + +typedef struct sPAPRRTCState sPAPRRTCState; +struct sPAPRRTCState { + /*< private >*/ + SysBusDevice parent_obj; + int64_t ns_offset; +}; + +#define NSEC_PER_SEC 1000000000LL + +void spapr_rtc_read(DeviceState *dev, struct tm *tm, uint32_t *ns) +{ + sPAPRRTCState *rtc = SPAPR_RTC(dev); + int64_t host_ns = qemu_clock_get_ns(rtc_clock); + int64_t guest_ns; + time_t guest_s; + + assert(rtc); + + guest_ns = host_ns + rtc->ns_offset; + guest_s = guest_ns / NSEC_PER_SEC; + + if (tm) { + gmtime_r(&guest_s, tm); + } + if (ns) { + *ns = guest_ns; + } +} + +int spapr_rtc_import_offset(DeviceState *dev, int64_t legacy_offset) +{ + sPAPRRTCState *rtc; + + if (!dev) { + return -ENODEV; + } + + rtc = SPAPR_RTC(dev); + + rtc->ns_offset = legacy_offset * NSEC_PER_SEC; + + return 0; +} + +static void rtas_get_time_of_day(PowerPCCPU *cpu, sPAPREnvironment *spapr, + uint32_t token, uint32_t nargs, + target_ulong args, + uint32_t nret, target_ulong rets) +{ + struct tm tm; + uint32_t ns; + + if ((nargs != 0) || (nret != 8)) { + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); + return; + } + + if (!spapr->rtc) { + rtas_st(rets, 0, RTAS_OUT_HW_ERROR); + return; + } + + spapr_rtc_read(spapr->rtc, &tm, &ns); + + rtas_st(rets, 0, RTAS_OUT_SUCCESS); + rtas_st(rets, 1, tm.tm_year + 1900); + rtas_st(rets, 2, tm.tm_mon + 1); + rtas_st(rets, 3, tm.tm_mday); + rtas_st(rets, 4, tm.tm_hour); + rtas_st(rets, 5, tm.tm_min); + rtas_st(rets, 6, tm.tm_sec); + rtas_st(rets, 7, ns); +} + +static void rtas_set_time_of_day(PowerPCCPU *cpu, sPAPREnvironment *spapr, + uint32_t token, uint32_t nargs, + target_ulong args, + uint32_t nret, target_ulong rets) +{ + sPAPRRTCState *rtc; + struct tm tm; + time_t new_s; + int64_t host_ns; + + if ((nargs != 7) || (nret != 1)) { + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); + return; + } + + if (!spapr->rtc) { + rtas_st(rets, 0, RTAS_OUT_HW_ERROR); + return; + } + + tm.tm_year = rtas_ld(args, 0) - 1900; + tm.tm_mon = rtas_ld(args, 1) - 1; + tm.tm_mday = rtas_ld(args, 2); + tm.tm_hour = rtas_ld(args, 3); + tm.tm_min = rtas_ld(args, 4); + tm.tm_sec = rtas_ld(args, 5); + + new_s = mktimegm(&tm); + if (new_s == -1) { + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); + return; + } + + /* Generate a monitor event for the change */ + qapi_event_send_rtc_change(qemu_timedate_diff(&tm), &error_abort); + + rtc = SPAPR_RTC(spapr->rtc); + + host_ns = qemu_clock_get_ns(rtc_clock); + + rtc->ns_offset = (new_s * NSEC_PER_SEC) - host_ns; + + rtas_st(rets, 0, RTAS_OUT_SUCCESS); +} + +static void spapr_rtc_qom_date(Object *obj, struct tm *current_tm, Error **errp) +{ + spapr_rtc_read(DEVICE(obj), current_tm, NULL); +} + +static void spapr_rtc_realize(DeviceState *dev, Error **errp) +{ + sPAPRRTCState *rtc = SPAPR_RTC(dev); + struct tm tm; + time_t host_s; + int64_t rtc_ns; + + /* Initialize the RTAS RTC from host time */ + + qemu_get_timedate(&tm, 0); + host_s = mktimegm(&tm); + rtc_ns = qemu_clock_get_ns(rtc_clock); + rtc->ns_offset = host_s * NSEC_PER_SEC - rtc_ns; + + object_property_add_tm(OBJECT(rtc), "date", spapr_rtc_qom_date, NULL); +} + +static const VMStateDescription vmstate_spapr_rtc = { + .name = "spapr/rtc", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_INT64(ns_offset, sPAPRRTCState), + VMSTATE_END_OF_LIST() + }, +}; + +static void spapr_rtc_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = spapr_rtc_realize; + dc->vmsd = &vmstate_spapr_rtc; + + spapr_rtas_register(RTAS_GET_TIME_OF_DAY, "get-time-of-day", + rtas_get_time_of_day); + spapr_rtas_register(RTAS_SET_TIME_OF_DAY, "set-time-of-day", + rtas_set_time_of_day); +} + +static const TypeInfo spapr_rtc_info = { + .name = TYPE_SPAPR_RTC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(sPAPRRTCState), + .class_size = sizeof(XICSStateClass), + .class_init = spapr_rtc_class_init, +}; + +static void spapr_rtc_register_types(void) +{ + type_register_static(&spapr_rtc_info); +} +type_init(spapr_rtc_register_types) diff --git a/hw/ppc/spapr_vio.c b/hw/ppc/spapr_vio.c index 032ee1a5f1..1360b97ab0 100644 --- a/hw/ppc/spapr_vio.c +++ b/hw/ppc/spapr_vio.c @@ -322,6 +322,18 @@ static void spapr_vio_quiesce_one(VIOsPAPRDevice *dev) free_crq(dev); } +void spapr_vio_set_bypass(VIOsPAPRDevice *dev, bool bypass) +{ + if (!dev->tcet) { + return; + } + + memory_region_set_enabled(&dev->mrbypass, bypass); + memory_region_set_enabled(spapr_tce_get_iommu(dev->tcet), !bypass); + + dev->tcet->bypass = bypass; +} + static void rtas_set_tce_bypass(PowerPCCPU *cpu, sPAPREnvironment *spapr, uint32_t token, uint32_t nargs, target_ulong args, @@ -348,7 +360,7 @@ static void rtas_set_tce_bypass(PowerPCCPU *cpu, sPAPREnvironment *spapr, return; } - spapr_tce_set_bypass(dev->tcet, !!enable); + spapr_vio_set_bypass(dev, !!enable); rtas_st(rets, 0, RTAS_OUT_SUCCESS); } @@ -407,12 +419,13 @@ static void spapr_vio_busdev_reset(DeviceState *qdev) dev->signal_state = 0; + spapr_vio_set_bypass(dev, false); if (pc->reset) { pc->reset(dev); } } -static int spapr_vio_busdev_init(DeviceState *qdev) +static void spapr_vio_busdev_realize(DeviceState *qdev, Error **errp) { VIOsPAPRDevice *dev = (VIOsPAPRDevice *)qdev; VIOsPAPRDeviceClass *pc = VIO_SPAPR_DEVICE_GET_CLASS(dev); @@ -428,11 +441,11 @@ static int spapr_vio_busdev_init(DeviceState *qdev) VIOsPAPRDevice *other = reg_conflict(dev); if (other) { - fprintf(stderr, "vio: %s and %s devices conflict at address %#x\n", - object_get_typename(OBJECT(qdev)), - object_get_typename(OBJECT(&other->qdev)), - dev->reg); - return -1; + error_setg(errp, "%s and %s devices conflict at address %#x", + object_get_typename(OBJECT(qdev)), + object_get_typename(OBJECT(&other->qdev)), + dev->reg); + return; } } else { /* Need to assign an address */ @@ -451,20 +464,32 @@ static int spapr_vio_busdev_init(DeviceState *qdev) dev->irq = xics_alloc(spapr->icp, 0, dev->irq, false); if (!dev->irq) { - return -1; + error_setg(errp, "can't allocate IRQ"); + return; } if (pc->rtce_window_size) { uint32_t liobn = SPAPR_VIO_BASE_LIOBN | dev->reg; + + memory_region_init(&dev->mrroot, OBJECT(dev), "iommu-spapr-root", + ram_size); + memory_region_init_alias(&dev->mrbypass, OBJECT(dev), + "iommu-spapr-bypass", get_system_memory(), + 0, ram_size); + memory_region_add_subregion_overlap(&dev->mrroot, 0, &dev->mrbypass, 1); + address_space_init(&dev->as, &dev->mrroot, qdev->id); + dev->tcet = spapr_tce_new_table(qdev, liobn, 0, SPAPR_TCE_PAGE_SHIFT, pc->rtce_window_size >> SPAPR_TCE_PAGE_SHIFT, false); - address_space_init(&dev->as, spapr_tce_get_iommu(dev->tcet), qdev->id); + dev->tcet->vdev = dev; + memory_region_add_subregion_overlap(&dev->mrroot, 0, + spapr_tce_get_iommu(dev->tcet), 2); } - return pc->init(dev); + pc->realize(dev, errp); } static target_ulong h_vio_signal(PowerPCCPU *cpu, sPAPREnvironment *spapr, @@ -570,7 +595,7 @@ const VMStateDescription vmstate_spapr_vio = { static void vio_spapr_device_class_init(ObjectClass *klass, void *data) { DeviceClass *k = DEVICE_CLASS(klass); - k->init = spapr_vio_busdev_init; + k->realize = spapr_vio_busdev_realize; k->reset = spapr_vio_busdev_reset; k->bus_type = TYPE_SPAPR_VIO_BUS; k->props = spapr_vio_props; diff --git a/hw/scsi/spapr_vscsi.c b/hw/scsi/spapr_vscsi.c index 36392359e3..891424fae9 100644 --- a/hw/scsi/spapr_vscsi.c +++ b/hw/scsi/spapr_vscsi.c @@ -1212,24 +1212,17 @@ static void spapr_vscsi_reset(VIOsPAPRDevice *dev) } } -static int spapr_vscsi_init(VIOsPAPRDevice *dev) +static void spapr_vscsi_realize(VIOsPAPRDevice *dev, Error **errp) { VSCSIState *s = VIO_SPAPR_VSCSI_DEVICE(dev); - Error *err = NULL; dev->crq.SendFunc = vscsi_do_crq; scsi_bus_new(&s->bus, sizeof(s->bus), DEVICE(dev), &vscsi_scsi_info, NULL); if (!dev->qdev.hotplugged) { - scsi_bus_legacy_handle_cmdline(&s->bus, &err); - if (err != NULL) { - error_free(err); - return -1; - } + scsi_bus_legacy_handle_cmdline(&s->bus, errp); } - - return 0; } void spapr_vscsi_create(VIOsPAPRBus *bus) @@ -1281,7 +1274,7 @@ static void spapr_vscsi_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); VIOsPAPRDeviceClass *k = VIO_SPAPR_DEVICE_CLASS(klass); - k->init = spapr_vscsi_init; + k->realize = spapr_vscsi_realize; k->reset = spapr_vscsi_reset; k->devnode = spapr_vscsi_devnode; k->dt_name = "v-scsi"; diff --git a/hw/timer/mc146818rtc.c b/hw/timer/mc146818rtc.c index 0600c9a1fa..f2b77fa118 100644 --- a/hw/timer/mc146818rtc.c +++ b/hw/timer/mc146818rtc.c @@ -831,49 +831,12 @@ static const MemoryRegionOps cmos_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; -static void rtc_get_date(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void rtc_get_date(Object *obj, struct tm *current_tm, Error **errp) { - Error *err = NULL; RTCState *s = MC146818_RTC(obj); - struct tm current_tm; rtc_update_time(s); - rtc_get_time(s, ¤t_tm); - visit_start_struct(v, NULL, "struct tm", name, 0, &err); - if (err) { - goto out; - } - visit_type_int32(v, ¤t_tm.tm_year, "tm_year", &err); - if (err) { - goto out_end; - } - visit_type_int32(v, ¤t_tm.tm_mon, "tm_mon", &err); - if (err) { - goto out_end; - } - visit_type_int32(v, ¤t_tm.tm_mday, "tm_mday", &err); - if (err) { - goto out_end; - } - visit_type_int32(v, ¤t_tm.tm_hour, "tm_hour", &err); - if (err) { - goto out_end; - } - visit_type_int32(v, ¤t_tm.tm_min, "tm_min", &err); - if (err) { - goto out_end; - } - visit_type_int32(v, ¤t_tm.tm_sec, "tm_sec", &err); - if (err) { - goto out_end; - } -out_end: - error_propagate(errp, err); - err = NULL; - visit_end_struct(v, errp); -out: - error_propagate(errp, err); + rtc_get_time(s, current_tm); } static void rtc_realizefn(DeviceState *dev, Error **errp) @@ -932,8 +895,7 @@ static void rtc_realizefn(DeviceState *dev, Error **errp) qdev_set_legacy_instance_id(dev, base, 3); qemu_register_reset(rtc_reset, s); - object_property_add(OBJECT(s), "date", "struct tm", - rtc_get_date, NULL, NULL, s, NULL); + object_property_add_tm(OBJECT(s), "date", rtc_get_date, NULL); object_property_add_alias(qdev_get_machine(), "rtc-time", OBJECT(s), "date", NULL); diff --git a/hw/vfio/common.c b/hw/vfio/common.c index 9db7d8da17..148eb53fc6 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -949,6 +949,7 @@ int vfio_container_ioctl(AddressSpace *as, int32_t groupid, switch (req) { case VFIO_CHECK_EXTENSION: case VFIO_IOMMU_SPAPR_TCE_GET_INFO: + case VFIO_EEH_PE_OP: break; default: /* Return an error on unknown requests */ diff --git a/include/hw/pci-host/spapr.h b/include/hw/pci-host/spapr.h index 4ea2a0d14a..895d273fee 100644 --- a/include/hw/pci-host/spapr.h +++ b/include/hw/pci-host/spapr.h @@ -49,6 +49,10 @@ struct sPAPRPHBClass { PCIHostBridgeClass parent_class; void (*finish_realize)(sPAPRPHBState *sphb, Error **errp); + int (*eeh_set_option)(sPAPRPHBState *sphb, unsigned int addr, int option); + int (*eeh_get_state)(sPAPRPHBState *sphb, int *state); + int (*eeh_reset)(sPAPRPHBState *sphb, int option); + int (*eeh_configure)(sPAPRPHBState *sphb); }; typedef struct spapr_pci_msi { @@ -64,7 +68,7 @@ typedef struct spapr_pci_msi_mig { struct sPAPRPHBState { PCIHostState parent_obj; - int32_t index; + uint32_t index; uint64_t buid; char *dtbusname; @@ -94,19 +98,22 @@ struct sPAPRPHBVFIOState { int32_t iommugroupid; }; +#define SPAPR_PCI_MAX_INDEX 255 + #define SPAPR_PCI_BASE_BUID 0x800000020000000ULL +#define SPAPR_PCI_MEM_WIN_BUS_OFFSET 0x80000000ULL + #define SPAPR_PCI_WINDOW_BASE 0x10000000000ULL #define SPAPR_PCI_WINDOW_SPACING 0x1000000000ULL #define SPAPR_PCI_MMIO_WIN_OFF 0xA0000000 -#define SPAPR_PCI_MMIO_WIN_SIZE 0x20000000 +#define SPAPR_PCI_MMIO_WIN_SIZE (SPAPR_PCI_WINDOW_SPACING - \ + SPAPR_PCI_MEM_WIN_BUS_OFFSET) #define SPAPR_PCI_IO_WIN_OFF 0x80000000 #define SPAPR_PCI_IO_WIN_SIZE 0x10000 #define SPAPR_PCI_MSI_WINDOW 0x40000000000ULL -#define SPAPR_PCI_MEM_WIN_BUS_OFFSET 0x80000000ULL - static inline qemu_irq spapr_phb_lsi_qirq(struct sPAPRPHBState *phb, int pin) { return xics_get_qirq(spapr->icp, phb->lsi_table[pin].irq); diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h index 716bff43bf..af71e8b0d5 100644 --- a/include/hw/ppc/spapr.h +++ b/include/hw/ppc/spapr.h @@ -15,6 +15,7 @@ typedef struct sPAPREnvironment { QLIST_HEAD(, sPAPRPHBState) phbs; struct sPAPRNVRAM *nvram; XICSState *icp; + DeviceState *rtc; hwaddr ram_limit; void *htab; @@ -26,7 +27,7 @@ typedef struct sPAPREnvironment { void *rtas_blob; void *fdt_skel; target_ulong entry_point; - uint64_t rtc_offset; + uint64_t rtc_offset; /* Now used only during incoming migration */ struct PPCTimebase tb; bool has_graphics; @@ -338,6 +339,39 @@ target_ulong spapr_hypercall(PowerPCCPU *cpu, target_ulong opcode, int spapr_allocate_irq(int hint, bool lsi); int spapr_allocate_irq_block(int num, bool lsi, bool msi); +/* ibm,set-eeh-option */ +#define RTAS_EEH_DISABLE 0 +#define RTAS_EEH_ENABLE 1 +#define RTAS_EEH_THAW_IO 2 +#define RTAS_EEH_THAW_DMA 3 + +/* ibm,get-config-addr-info2 */ +#define RTAS_GET_PE_ADDR 0 +#define RTAS_GET_PE_MODE 1 +#define RTAS_PE_MODE_NONE 0 +#define RTAS_PE_MODE_NOT_SHARED 1 +#define RTAS_PE_MODE_SHARED 2 + +/* ibm,read-slot-reset-state2 */ +#define RTAS_EEH_PE_STATE_NORMAL 0 +#define RTAS_EEH_PE_STATE_RESET 1 +#define RTAS_EEH_PE_STATE_STOPPED_IO_DMA 2 +#define RTAS_EEH_PE_STATE_STOPPED_DMA 4 +#define RTAS_EEH_PE_STATE_UNAVAIL 5 +#define RTAS_EEH_NOT_SUPPORT 0 +#define RTAS_EEH_SUPPORT 1 +#define RTAS_EEH_PE_UNAVAIL_INFO 1000 +#define RTAS_EEH_PE_RECOVER_INFO 0 + +/* ibm,set-slot-reset */ +#define RTAS_SLOT_RESET_DEACTIVATE 0 +#define RTAS_SLOT_RESET_HOT 1 +#define RTAS_SLOT_RESET_FUNDAMENTAL 3 + +/* ibm,slot-error-detail */ +#define RTAS_SLOT_TEMP_ERR_LOG 1 +#define RTAS_SLOT_PERM_ERR_LOG 2 + /* RTAS return codes */ #define RTAS_OUT_SUCCESS 0 #define RTAS_OUT_NO_ERRORS_FOUND 1 @@ -382,8 +416,14 @@ int spapr_allocate_irq_block(int num, bool lsi, bool msi); #define RTAS_GET_SENSOR_STATE (RTAS_TOKEN_BASE + 0x1D) #define RTAS_IBM_CONFIGURE_CONNECTOR (RTAS_TOKEN_BASE + 0x1E) #define RTAS_IBM_OS_TERM (RTAS_TOKEN_BASE + 0x1F) +#define RTAS_IBM_SET_EEH_OPTION (RTAS_TOKEN_BASE + 0x20) +#define RTAS_IBM_GET_CONFIG_ADDR_INFO2 (RTAS_TOKEN_BASE + 0x21) +#define RTAS_IBM_READ_SLOT_RESET_STATE2 (RTAS_TOKEN_BASE + 0x22) +#define RTAS_IBM_SET_SLOT_RESET (RTAS_TOKEN_BASE + 0x23) +#define RTAS_IBM_CONFIGURE_PE (RTAS_TOKEN_BASE + 0x24) +#define RTAS_IBM_SLOT_ERROR_DETAIL (RTAS_TOKEN_BASE + 0x25) -#define RTAS_TOKEN_MAX (RTAS_TOKEN_BASE + 0x20) +#define RTAS_TOKEN_MAX (RTAS_TOKEN_BASE + 0x26) /* RTAS ibm,get-system-parameter token values */ #define RTAS_SYSPARM_SPLPAR_CHARACTERISTICS 20 @@ -463,6 +503,7 @@ struct sPAPRTCETable { bool vfio_accel; int fd; MemoryRegion iommu; + struct VIOsPAPRDevice *vdev; /* for @bypass migration compatibility only */ QLIST_ENTRY(sPAPRTCETable) list; }; @@ -475,10 +516,15 @@ sPAPRTCETable *spapr_tce_new_table(DeviceState *owner, uint32_t liobn, uint32_t nb_table, bool vfio_accel); MemoryRegion *spapr_tce_get_iommu(sPAPRTCETable *tcet); -void spapr_tce_set_bypass(sPAPRTCETable *tcet, bool bypass); int spapr_dma_dt(void *fdt, int node_off, const char *propname, uint32_t liobn, uint64_t window, uint32_t size); int spapr_tcet_dma_dt(void *fdt, int node_off, const char *propname, sPAPRTCETable *tcet); +void spapr_pci_switch_vga(bool big_endian); + +#define TYPE_SPAPR_RTC "spapr-rtc" + +void spapr_rtc_read(DeviceState *dev, struct tm *tm, uint32_t *ns); +int spapr_rtc_import_offset(DeviceState *dev, int64_t legacy_offset); #endif /* !defined (__HW_SPAPR_H__) */ diff --git a/include/hw/ppc/spapr_vio.h b/include/hw/ppc/spapr_vio.h index 46edc2a20c..f95016a92e 100644 --- a/include/hw/ppc/spapr_vio.h +++ b/include/hw/ppc/spapr_vio.h @@ -52,7 +52,7 @@ typedef struct VIOsPAPRDeviceClass { const char *dt_name, *dt_type, *dt_compatible; target_ulong signal_mask; uint32_t rtce_window_size; - int (*init)(VIOsPAPRDevice *dev); + void (*realize)(VIOsPAPRDevice *dev, Error **errp); void (*reset)(VIOsPAPRDevice *dev); int (*devnode)(VIOsPAPRDevice *dev, void *fdt, int node_off); } VIOsPAPRDeviceClass; @@ -64,6 +64,8 @@ struct VIOsPAPRDevice { target_ulong signal_state; VIOsPAPR_CRQ crq; AddressSpace as; + MemoryRegion mrroot; + MemoryRegion mrbypass; sPAPRTCETable *tcet; }; @@ -139,4 +141,6 @@ extern const VMStateDescription vmstate_spapr_vio; #define VMSTATE_SPAPR_VIO(_f, _s) \ VMSTATE_STRUCT(_f, _s, 0, vmstate_spapr_vio, VIOsPAPRDevice) +void spapr_vio_set_bypass(VIOsPAPRDevice *dev, bool bypass); + #endif /* _HW_SPAPR_VIO_H */ diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h index c20f2d1fee..bc7616aaa8 100644 --- a/include/migration/vmstate.h +++ b/include/migration/vmstate.h @@ -635,6 +635,18 @@ extern const VMStateInfo vmstate_info_bitmap; #define VMSTATE_INT32_POSITIVE_LE(_f, _s) \ VMSTATE_SINGLE(_f, _s, 0, vmstate_info_int32_le, int32_t) +#define VMSTATE_INT8_TEST(_f, _s, _t) \ + VMSTATE_SINGLE_TEST(_f, _s, _t, 0, vmstate_info_int8, int8_t) + +#define VMSTATE_INT16_TEST(_f, _s, _t) \ + VMSTATE_SINGLE_TEST(_f, _s, _t, 0, vmstate_info_int16, int16_t) + +#define VMSTATE_INT32_TEST(_f, _s, _t) \ + VMSTATE_SINGLE_TEST(_f, _s, _t, 0, vmstate_info_int32, int32_t) + +#define VMSTATE_INT64_TEST(_f, _s, _t) \ + VMSTATE_SINGLE_TEST(_f, _s, _t, 0, vmstate_info_int64, int64_t) + #define VMSTATE_UINT8_TEST(_f, _s, _t) \ VMSTATE_SINGLE_TEST(_f, _s, _t, 0, vmstate_info_uint8, uint8_t) @@ -644,6 +656,9 @@ extern const VMStateInfo vmstate_info_bitmap; #define VMSTATE_UINT32_TEST(_f, _s, _t) \ VMSTATE_SINGLE_TEST(_f, _s, _t, 0, vmstate_info_uint32, uint32_t) +#define VMSTATE_UINT64_TEST(_f, _s, _t) \ + VMSTATE_SINGLE_TEST(_f, _s, _t, 0, vmstate_info_uint64, uint64_t) + #define VMSTATE_FLOAT64_V(_f, _s, _v) \ VMSTATE_SINGLE(_f, _s, _v, vmstate_info_float64, float64) diff --git a/include/qom/object.h b/include/qom/object.h index 87575735fe..d2d7748f62 100644 --- a/include/qom/object.h +++ b/include/qom/object.h @@ -1204,6 +1204,20 @@ void object_property_add_bool(Object *obj, const char *name, Error **errp); /** + * object_property_add_tm: + * @obj: the object to add a property to + * @name: the name of the property + * @get: the getter or NULL if the property is write-only. + * @errp: if an error occurs, a pointer to an area to store the error + * + * Add a read-only struct tm valued property using a getter function. + * This function will add a property of type 'struct tm'. + */ +void object_property_add_tm(Object *obj, const char *name, + void (*get)(Object *, struct tm *, Error **), + Error **errp); + +/** * object_property_add_uint8_ptr: * @obj: the object to add a property to * @name: the name of the property diff --git a/qom/object.c b/qom/object.c index 1812c73327..d1670387b4 100644 --- a/qom/object.c +++ b/qom/object.c @@ -1543,6 +1543,85 @@ void object_property_add_bool(Object *obj, const char *name, } } +typedef struct TMProperty { + void (*get)(Object *, struct tm *, Error **); +} TMProperty; + +static void property_get_tm(Object *obj, Visitor *v, void *opaque, + const char *name, Error **errp) +{ + TMProperty *prop = opaque; + Error *err = NULL; + struct tm value; + + prop->get(obj, &value, &err); + if (err) { + goto out; + } + + visit_start_struct(v, NULL, "struct tm", name, 0, &err); + if (err) { + goto out; + } + visit_type_int32(v, &value.tm_year, "tm_year", &err); + if (err) { + goto out_end; + } + visit_type_int32(v, &value.tm_mon, "tm_mon", &err); + if (err) { + goto out_end; + } + visit_type_int32(v, &value.tm_mday, "tm_mday", &err); + if (err) { + goto out_end; + } + visit_type_int32(v, &value.tm_hour, "tm_hour", &err); + if (err) { + goto out_end; + } + visit_type_int32(v, &value.tm_min, "tm_min", &err); + if (err) { + goto out_end; + } + visit_type_int32(v, &value.tm_sec, "tm_sec", &err); + if (err) { + goto out_end; + } +out_end: + error_propagate(errp, err); + err = NULL; + visit_end_struct(v, errp); +out: + error_propagate(errp, err); + +} + +static void property_release_tm(Object *obj, const char *name, + void *opaque) +{ + TMProperty *prop = opaque; + g_free(prop); +} + +void object_property_add_tm(Object *obj, const char *name, + void (*get)(Object *, struct tm *, Error **), + Error **errp) +{ + Error *local_err = NULL; + TMProperty *prop = g_malloc0(sizeof(*prop)); + + prop->get = get; + + object_property_add(obj, name, "struct tm", + get ? property_get_tm : NULL, NULL, + property_release_tm, + prop, &local_err); + if (local_err) { + error_propagate(errp, local_err); + g_free(prop); + } +} + static char *qdev_get_type(Object *obj, Error **errp) { return g_strdup(object_get_typename(obj)); diff --git a/target-ppc/cpu-models.c b/target-ppc/cpu-models.c index 3f18996bb0..2b560a4757 100644 --- a/target-ppc/cpu-models.c +++ b/target-ppc/cpu-models.c @@ -1124,8 +1124,8 @@ POWERPC_DEF("POWER5", CPU_POWERPC_POWER5, POWER5, "POWER5") #endif - POWERPC_DEF("POWER5+", CPU_POWERPC_POWER5P, POWER5P, - "POWER5+") + POWERPC_DEF("POWER5+_v0.0", CPU_POWERPC_POWER5P_v00, POWER5P, + "POWER5+ v0.0") POWERPC_DEF("POWER5+_v2.1", CPU_POWERPC_POWER5P_v21, POWER5P, "POWER5+ v2.1") #if defined(TODO) @@ -1144,8 +1144,8 @@ "POWER8E v1.0") POWERPC_DEF("POWER8_v1.0", CPU_POWERPC_POWER8_v10, POWER8, "POWER8 v1.0") - POWERPC_DEF("970", CPU_POWERPC_970, 970, - "PowerPC 970") + POWERPC_DEF("970_v2.2", CPU_POWERPC_970_v22, 970, + "PowerPC 970 v2.2") POWERPC_DEF("970fx_v1.0", CPU_POWERPC_970FX_v10, 970, "PowerPC 970FX v1.0 (G5)") POWERPC_DEF("970fx_v2.0", CPU_POWERPC_970FX_v20, 970, @@ -1387,11 +1387,13 @@ PowerPCCPUAlias ppc_cpu_aliases[] = { { "Dino", "POWER3" }, { "POWER3+", "631" }, { "POWER5gr", "POWER5" }, - { "POWER5gs", "POWER5+" }, + { "POWER5+", "POWER5+_v0.0" }, + { "POWER5gs", "POWER5+_v0.0" }, { "POWER7", "POWER7_v2.3" }, { "POWER7+", "POWER7+_v2.1" }, { "POWER8E", "POWER8E_v1.0" }, { "POWER8", "POWER8_v1.0" }, + { "970", "970_v2.2" }, { "970fx", "970fx_v3.1" }, { "970mp", "970mp_v1.1" }, { "Apache", "RS64" }, diff --git a/target-ppc/cpu-models.h b/target-ppc/cpu-models.h index 290a7597dc..ee693af873 100644 --- a/target-ppc/cpu-models.h +++ b/target-ppc/cpu-models.h @@ -547,7 +547,7 @@ enum { CPU_POWERPC_POWER4P = 0x00380000, /* XXX: missing 0x003A0201 */ CPU_POWERPC_POWER5 = 0x003A0203, - CPU_POWERPC_POWER5P = 0x003B0000, + CPU_POWERPC_POWER5P_v00 = 0x003B0000, CPU_POWERPC_POWER5P_v21 = 0x003B0201, CPU_POWERPC_POWER6 = 0x003E0000, CPU_POWERPC_POWER6_5 = 0x0F000001, /* POWER6 in POWER5 mode */ @@ -561,7 +561,7 @@ enum { CPU_POWERPC_POWER8E_v10 = 0x004B0100, CPU_POWERPC_POWER8_BASE = 0x004D0000, CPU_POWERPC_POWER8_v10 = 0x004D0100, - CPU_POWERPC_970 = 0x00390202, + CPU_POWERPC_970_v22 = 0x00390202, CPU_POWERPC_970FX_v10 = 0x00391100, CPU_POWERPC_970FX_v20 = 0x003C0200, CPU_POWERPC_970FX_v21 = 0x003C0201, diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h index aae33a9237..abc3545846 100644 --- a/target-ppc/cpu.h +++ b/target-ppc/cpu.h @@ -45,6 +45,7 @@ # define TARGET_VIRT_ADDR_SPACE_BITS 64 #endif +#define TARGET_PAGE_BITS_64K 16 #define TARGET_PAGE_BITS_16M 24 #else /* defined (TARGET_PPC64) */ @@ -1623,6 +1624,7 @@ static inline int cpu_mmu_index (CPUPPCState *env) #define SPR_MPC_MD_DBRAM1 (0x32A) #define SPR_RCPU_L2U_RA3 (0x32B) #define SPR_TAR (0x32F) +#define SPR_VTB (0x351) #define SPR_440_INV0 (0x370) #define SPR_440_INV1 (0x371) #define SPR_440_INV2 (0x372) diff --git a/target-ppc/machine.c b/target-ppc/machine.c index c801b822c9..3921012063 100644 --- a/target-ppc/machine.c +++ b/target-ppc/machine.c @@ -159,6 +159,7 @@ static int cpu_post_load(void *opaque, int version_id) PowerPCCPU *cpu = opaque; CPUPPCState *env = &cpu->env; int i; + target_ulong msr; /* * We always ignore the source PVR. The user or management @@ -190,7 +191,12 @@ static int cpu_post_load(void *opaque, int version_id) /* Restore htab_base and htab_mask variables */ ppc_store_sdr1(env, env->spr[SPR_SDR1]); } - hreg_compute_hflags(env); + + /* Mark msr bits except MSR_TGPR invalid before restoring */ + msr = env->msr; + env->msr ^= ~(1ULL << MSR_TGPR); + ppc_store_msr(env, msr); + hreg_compute_mem_idx(env); return 0; diff --git a/target-ppc/misc_helper.c b/target-ppc/misc_helper.c index a577b3afd1..6b12ca86af 100644 --- a/target-ppc/misc_helper.c +++ b/target-ppc/misc_helper.c @@ -77,8 +77,13 @@ void helper_msr_facility_check(CPUPPCState *env, uint32_t bit, void helper_store_sdr1(CPUPPCState *env, target_ulong val) { + PowerPCCPU *cpu = ppc_env_get_cpu(env); + if (!env->external_htab) { - ppc_store_sdr1(env, val); + if (env->spr[SPR_SDR1] != val) { + ppc_store_sdr1(env, val); + tlb_flush(CPU(cpu), 1); + } } } diff --git a/target-ppc/mmu-hash64.c b/target-ppc/mmu-hash64.c index b0278c95e1..7df6edebf2 100644 --- a/target-ppc/mmu-hash64.c +++ b/target-ppc/mmu-hash64.c @@ -350,7 +350,7 @@ uint64_t ppc_hash64_start_access(PowerPCCPU *cpu, target_ulong pte_index) void ppc_hash64_stop_access(uint64_t token) { if (kvmppc_kern_htab) { - return kvmppc_hash64_free_pteg(token); + kvmppc_hash64_free_pteg(token); } } @@ -388,6 +388,24 @@ static hwaddr ppc_hash64_pteg_search(CPUPPCState *env, hwaddr hash, return -1; } +static uint64_t ppc_hash64_page_shift(ppc_slb_t *slb) +{ + uint64_t epnshift; + + /* Page size according to the SLB, which we use to generate the + * EPN for hash table lookup.. When we implement more recent MMU + * extensions this might be different from the actual page size + * encoded in the PTE */ + if ((slb->vsid & SLB_VSID_LLP_MASK) == SLB_VSID_4K) { + epnshift = TARGET_PAGE_BITS; + } else if ((slb->vsid & SLB_VSID_LLP_MASK) == SLB_VSID_64K) { + epnshift = TARGET_PAGE_BITS_64K; + } else { + epnshift = TARGET_PAGE_BITS_16M; + } + return epnshift; +} + static hwaddr ppc_hash64_htab_lookup(CPUPPCState *env, ppc_slb_t *slb, target_ulong eaddr, ppc_hash_pte64_t *pte) @@ -396,12 +414,7 @@ static hwaddr ppc_hash64_htab_lookup(CPUPPCState *env, hwaddr hash; uint64_t vsid, epnshift, epnmask, epn, ptem; - /* Page size according to the SLB, which we use to generate the - * EPN for hash table lookup.. When we implement more recent MMU - * extensions this might be different from the actual page size - * encoded in the PTE */ - epnshift = (slb->vsid & SLB_VSID_L) - ? TARGET_PAGE_BITS_16M : TARGET_PAGE_BITS; + epnshift = ppc_hash64_page_shift(slb); epnmask = ~((1ULL << epnshift) - 1); if (slb->vsid & SLB_VSID_B) { @@ -448,12 +461,14 @@ static hwaddr ppc_hash64_htab_lookup(CPUPPCState *env, static hwaddr ppc_hash64_pte_raddr(ppc_slb_t *slb, ppc_hash_pte64_t pte, target_ulong eaddr) { + hwaddr mask; + int target_page_bits; hwaddr rpn = pte.pte1 & HPTE64_R_RPN; - /* FIXME: Add support for SLLP extended page sizes */ - int target_page_bits = (slb->vsid & SLB_VSID_L) - ? TARGET_PAGE_BITS_16M : TARGET_PAGE_BITS; - hwaddr mask = (1ULL << target_page_bits) - 1; - + /* + * We support 4K, 64K and 16M now + */ + target_page_bits = ppc_hash64_page_shift(slb); + mask = (1ULL << target_page_bits) - 1; return (rpn & ~mask) | (eaddr & mask); } @@ -617,7 +632,8 @@ void ppc_hash64_store_hpte(CPUPPCState *env, CPUState *cs = CPU(ppc_env_get_cpu(env)); if (kvmppc_kern_htab) { - return kvmppc_hash64_write_pte(env, pte_index, pte0, pte1); + kvmppc_hash64_write_pte(env, pte_index, pte0, pte1); + return; } pte_index *= HASH_PTE_SIZE_64; diff --git a/target-ppc/mmu-hash64.h b/target-ppc/mmu-hash64.h index 49e385db90..291750f3e5 100644 --- a/target-ppc/mmu-hash64.h +++ b/target-ppc/mmu-hash64.h @@ -37,6 +37,9 @@ void ppc_hash64_store_hpte(CPUPPCState *env, target_ulong index, #define SLB_VSID_C 0x0000000000000080ULL /* class */ #define SLB_VSID_LP 0x0000000000000030ULL #define SLB_VSID_ATTR 0x0000000000000FFFULL +#define SLB_VSID_LLP_MASK (SLB_VSID_L | SLB_VSID_LP) +#define SLB_VSID_4K 0x0000000000000000ULL +#define SLB_VSID_64K 0x0000000000000110ULL /* * Hash page table definitions diff --git a/target-ppc/mmu_helper.c b/target-ppc/mmu_helper.c index 660be7f18c..527c6adca3 100644 --- a/target-ppc/mmu_helper.c +++ b/target-ppc/mmu_helper.c @@ -2036,31 +2036,26 @@ void ppc_tlb_invalidate_one(CPUPPCState *env, target_ulong addr) /* Special registers manipulation */ void ppc_store_sdr1(CPUPPCState *env, target_ulong value) { - PowerPCCPU *cpu = ppc_env_get_cpu(env); - qemu_log_mask(CPU_LOG_MMU, "%s: " TARGET_FMT_lx "\n", __func__, value); assert(!env->external_htab); - if (env->spr[SPR_SDR1] != value) { - env->spr[SPR_SDR1] = value; + env->spr[SPR_SDR1] = value; #if defined(TARGET_PPC64) - if (env->mmu_model & POWERPC_MMU_64) { - target_ulong htabsize = value & SDR_64_HTABSIZE; + if (env->mmu_model & POWERPC_MMU_64) { + target_ulong htabsize = value & SDR_64_HTABSIZE; - if (htabsize > 28) { - fprintf(stderr, "Invalid HTABSIZE 0x" TARGET_FMT_lx - " stored in SDR1\n", htabsize); - htabsize = 28; - } - env->htab_mask = (1ULL << (htabsize + 18 - 7)) - 1; - env->htab_base = value & SDR_64_HTABORG; - } else -#endif /* defined(TARGET_PPC64) */ - { - /* FIXME: Should check for valid HTABMASK values */ - env->htab_mask = ((value & SDR_32_HTABMASK) << 16) | 0xFFFF; - env->htab_base = value & SDR_32_HTABORG; + if (htabsize > 28) { + fprintf(stderr, "Invalid HTABSIZE 0x" TARGET_FMT_lx + " stored in SDR1\n", htabsize); + htabsize = 28; } - tlb_flush(CPU(cpu), 1); + env->htab_mask = (1ULL << (htabsize + 18 - 7)) - 1; + env->htab_base = value & SDR_64_HTABORG; + } else +#endif /* defined(TARGET_PPC64) */ + { + /* FIXME: Should check for valid HTABMASK values */ + env->htab_mask = ((value & SDR_32_HTABMASK) << 16) | 0xFFFF; + env->htab_base = value & SDR_32_HTABORG; } } diff --git a/target-ppc/translate.c b/target-ppc/translate.c index 88c18e37b3..2a78e99d83 100644 --- a/target-ppc/translate.c +++ b/target-ppc/translate.c @@ -11214,8 +11214,9 @@ void ppc_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, int i; cpu_fprintf(f, "NIP " TARGET_FMT_lx " LR " TARGET_FMT_lx " CTR " - TARGET_FMT_lx " XER " TARGET_FMT_lx "\n", - env->nip, env->lr, env->ctr, cpu_read_xer(env)); + TARGET_FMT_lx " XER " TARGET_FMT_lx " CPU#%d\n", + env->nip, env->lr, env->ctr, cpu_read_xer(env), + cs->cpu_index); cpu_fprintf(f, "MSR " TARGET_FMT_lx " HID0 " TARGET_FMT_lx " HF " TARGET_FMT_lx " idx %d\n", env->msr, env->spr[SPR_HID0], env->hflags, env->mmu_idx); diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c index df1a62c4c6..d74f4f024d 100644 --- a/target-ppc/translate_init.c +++ b/target-ppc/translate_init.c @@ -7819,6 +7819,15 @@ static void gen_spr_power8_ebb(CPUPPCState *env) KVM_REG_PPC_BESCR, 0x00000000); } +/* Virtual Time Base */ +static void gen_spr_vtb(CPUPPCState *env) +{ + spr_register(env, SPR_VTB, "VTB", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_tbl, SPR_NOACCESS, + 0x00000000); +} + static void gen_spr_power8_fscr(CPUPPCState *env) { #if defined(CONFIG_USER_ONLY) @@ -7881,6 +7890,7 @@ static void init_proc_book3s_64(CPUPPCState *env, int version) gen_spr_power8_pmu_sup(env); gen_spr_power8_pmu_user(env); gen_spr_power8_tm(env); + gen_spr_vtb(env); } if (version < BOOK3S_CPU_POWER8) { gen_spr_book3s_dbg(env); |