diff options
author | Peter Maydell <peter.maydell@linaro.org> | 2018-03-06 10:21:35 +0000 |
---|---|---|
committer | Peter Maydell <peter.maydell@linaro.org> | 2018-03-06 10:21:35 +0000 |
commit | e1ee9ee139ed7091c2c592d5b784fbb7227bfc0d (patch) | |
tree | e245e0c71a0b7f7c5bc1f0de6d5ba05034666f07 /hw | |
parent | f2bb2d14c2958f3f5aef456bd2cdb1ff99f4a562 (diff) | |
parent | 21b786f607b11d888f90bbb8c3414500515d11e7 (diff) |
Merge remote-tracking branch 'remotes/dgibson/tags/ppc-for-2.12-20180306' into staging
ppc patch queue 2018-03-06
This pull request supersedes ppc-for-2.12-20180302 which had compile
problems with some gcc versions. It also contains a few additional
patches.
Highlights are:
* New Sam460ex machine type
* Yet more fixes related to vcpu id allocation for spapr
* Numerous macio cleanupsr
* Some enhancements to the Spectre/Meltdown fixes for pseries,
allowing use of a better mitigation for indirect branch based
exploits
* New pseries machine types with Spectre/Meltdown mitigations
enabled (stop gap until libvirt and management understands the
machine options)
* A handful of other fixes
# gpg: Signature made Tue 06 Mar 2018 04:01:00 GMT
# gpg: using RSA key 6C38CACA20D9B392
# gpg: Good signature from "David Gibson <david@gibson.dropbear.id.au>"
# gpg: aka "David Gibson (Red Hat) <dgibson@redhat.com>"
# gpg: aka "David Gibson (ozlabs.org) <dgibson@ozlabs.org>"
# gpg: aka "David Gibson (kernel.org) <dwg@kernel.org>"
# Primary key fingerprint: 75F4 6586 AE61 A66C C44E 87DC 6C38 CACA 20D9 B392
* remotes/dgibson/tags/ppc-for-2.12-20180306: (30 commits)
PowerPC: Add TS bits into msr_mask
adb: add trace-events for monitoring keyboard/mouse during bus enumeration
PPC: e500: Fix duplicate kernel load and device tree overlap
hw/ppc/spapr,e500: Use new property "stdout-path" for boot console
ppc/spapr-caps: Define the pseries-2.12-sxxm machine type
ppc/spapr-caps: Convert cap-ibs to custom spapr-cap
ppc/spapr-caps: Convert cap-sbbc to custom spapr-cap
ppc/spapr-caps: Convert cap-cfpc to custom spapr-cap
ppc/spapr-caps: Add support for custom spapr_capabilities
target/ppc: Check mask when setting cap_ppc_safe_indirect_branch
macio: remove macio_init() function
macio: move setting of CUDA timebase frequency to macio_common_realize()
mac_newworld: use object link to pass OpenPIC object to macio
openpic: move OpenPIC state and related definitions to openpic.h
openpic: move KVM-specific declarations into separate openpic_kvm.h file
mac_oldworld: use object link to pass heathrow PIC object to macio
macio: move macio related structures and defines into separate macio.h file
heathrow: change heathrow_pic_init() to return the heathrow device
heathrow: convert to trace-events
heathrow: QOMify heathrow PIC
...
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'hw')
-rw-r--r-- | hw/input/adb-kbd.c | 4 | ||||
-rw-r--r-- | hw/input/adb-mouse.c | 5 | ||||
-rw-r--r-- | hw/input/trace-events | 5 | ||||
-rw-r--r-- | hw/intc/heathrow_pic.c | 166 | ||||
-rw-r--r-- | hw/intc/openpic.c | 157 | ||||
-rw-r--r-- | hw/intc/openpic_kvm.c | 1 | ||||
-rw-r--r-- | hw/intc/trace-events | 5 | ||||
-rw-r--r-- | hw/misc/macio/macio.c | 150 | ||||
-rw-r--r-- | hw/ppc/Makefile.objs | 3 | ||||
-rw-r--r-- | hw/ppc/e500.c | 124 | ||||
-rw-r--r-- | hw/ppc/mac.h | 10 | ||||
-rw-r--r-- | hw/ppc/mac_newworld.c | 56 | ||||
-rw-r--r-- | hw/ppc/mac_oldworld.c | 50 | ||||
-rw-r--r-- | hw/ppc/ppc440_pcix.c | 528 | ||||
-rw-r--r-- | hw/ppc/ppc440_uc.c | 3 | ||||
-rw-r--r-- | hw/ppc/sam460ex.c | 603 | ||||
-rw-r--r-- | hw/ppc/spapr.c | 176 | ||||
-rw-r--r-- | hw/ppc/spapr_caps.c | 153 | ||||
-rw-r--r-- | hw/ppc/spapr_hcall.c | 5 | ||||
-rw-r--r-- | hw/ppc/trace-events | 8 |
20 files changed, 1648 insertions, 564 deletions
diff --git a/hw/input/adb-kbd.c b/hw/input/adb-kbd.c index 266aed1b7b..50b62712c8 100644 --- a/hw/input/adb-kbd.c +++ b/hw/input/adb-kbd.c @@ -258,6 +258,7 @@ static int adb_kbd_request(ADBDevice *d, uint8_t *obuf, case ADB_CMD_CHANGE_ID_AND_ACT: case ADB_CMD_CHANGE_ID_AND_ENABLE: d->devaddr = buf[1] & 0xf; + trace_adb_kbd_request_change_addr(d->devaddr); break; default: d->devaddr = buf[1] & 0xf; @@ -269,6 +270,9 @@ static int adb_kbd_request(ADBDevice *d, uint8_t *obuf, 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); break; } } diff --git a/hw/input/adb-mouse.c b/hw/input/adb-mouse.c index 47e88faf25..3ba6027d33 100644 --- a/hw/input/adb-mouse.c +++ b/hw/input/adb-mouse.c @@ -118,6 +118,7 @@ static int adb_mouse_request(ADBDevice *d, uint8_t *obuf, s->dx = 0; s->dy = 0; s->dz = 0; + trace_adb_mouse_flush(); return 0; } @@ -138,6 +139,7 @@ static int adb_mouse_request(ADBDevice *d, uint8_t *obuf, case ADB_CMD_CHANGE_ID_AND_ACT: case ADB_CMD_CHANGE_ID_AND_ENABLE: d->devaddr = buf[1] & 0xf; + trace_adb_mouse_request_change_addr(d->devaddr); break; default: d->devaddr = buf[1] & 0xf; @@ -155,6 +157,9 @@ static int adb_mouse_request(ADBDevice *d, uint8_t *obuf, if (buf[2] == 1 || buf[2] == 2) { d->handler = buf[2]; } + + trace_adb_mouse_request_change_addr_and_handler(d->devaddr, + d->handler); break; } } diff --git a/hw/input/trace-events b/hw/input/trace-events index 5affabc81d..db72484a25 100644 --- a/hw/input/trace-events +++ b/hw/input/trace-events @@ -4,10 +4,15 @@ adb_kbd_no_key(void) "Ignoring NO_KEY" adb_kbd_writereg(int reg, uint8_t val) "reg %d val 0x%2.2x" adb_kbd_readreg(int reg, uint8_t val0, uint8_t val1) "reg %d obuf[0] 0x%2.2x obuf[1] 0x%2.2x" +adb_kbd_request_change_addr(int devaddr) "change addr to 0x%x" +adb_kbd_request_change_addr_and_handler(int devaddr, int handler) "change addr and handler to 0x%x, 0x%x" # hw/input/adb-mouse.c +adb_mouse_flush(void) "flush" adb_mouse_writereg(int reg, uint8_t val) "reg %d val 0x%2.2x" adb_mouse_readreg(int reg, uint8_t val0, uint8_t val1) "reg %d obuf[0] 0x%2.2x obuf[1] 0x%2.2x" +adb_mouse_request_change_addr(int devaddr) "change addr to 0x%x" +adb_mouse_request_change_addr_and_handler(int devaddr, int handler) "change addr and handler to 0x%x, 0x%x" # hw/input/ps2.c ps2_put_keycode(void *opaque, int keycode) "%p keycode 0x%02x" diff --git a/hw/intc/heathrow_pic.c b/hw/intc/heathrow_pic.c index 171f5ed814..393fdd7326 100644 --- a/hw/intc/heathrow_pic.c +++ b/hw/intc/heathrow_pic.c @@ -25,78 +25,58 @@ #include "qemu/osdep.h" #include "hw/hw.h" #include "hw/ppc/mac.h" +#include "hw/intc/heathrow_pic.h" +#include "trace.h" -/* debug PIC */ -//#define DEBUG_PIC - -#ifdef DEBUG_PIC -#define PIC_DPRINTF(fmt, ...) \ - do { printf("PIC: " fmt , ## __VA_ARGS__); } while (0) -#else -#define PIC_DPRINTF(fmt, ...) -#endif - -typedef struct HeathrowPIC { - uint32_t events; - uint32_t mask; - uint32_t levels; - uint32_t level_triggered; -} HeathrowPIC; - -typedef struct HeathrowPICS { - MemoryRegion mem; - HeathrowPIC pics[2]; - qemu_irq *irqs; -} HeathrowPICS; - -static inline int check_irq(HeathrowPIC *pic) +static inline int heathrow_check_irq(HeathrowPICState *pic) { return (pic->events | (pic->levels & pic->level_triggered)) & pic->mask; } /* update the CPU irq state */ -static void heathrow_pic_update(HeathrowPICS *s) +static void heathrow_update_irq(HeathrowState *s) { - if (check_irq(&s->pics[0]) || check_irq(&s->pics[1])) { + if (heathrow_check_irq(&s->pics[0]) || + heathrow_check_irq(&s->pics[1])) { qemu_irq_raise(s->irqs[0]); } else { qemu_irq_lower(s->irqs[0]); } } -static void pic_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) +static void heathrow_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size) { - HeathrowPICS *s = opaque; - HeathrowPIC *pic; + HeathrowState *s = opaque; + HeathrowPICState *pic; unsigned int n; n = ((addr & 0xfff) - 0x10) >> 4; - PIC_DPRINTF("writel: " TARGET_FMT_plx " %u: %08x\n", addr, n, value); + trace_heathrow_write(addr, n, value); if (n >= 2) return; pic = &s->pics[n]; switch(addr & 0xf) { case 0x04: pic->mask = value; - heathrow_pic_update(s); + heathrow_update_irq(s); break; case 0x08: /* do not reset level triggered IRQs */ value &= ~pic->level_triggered; pic->events &= ~value; - heathrow_pic_update(s); + heathrow_update_irq(s); break; default: break; } } -static uint64_t pic_read(void *opaque, hwaddr addr, - unsigned size) +static uint64_t heathrow_read(void *opaque, hwaddr addr, + unsigned size) { - HeathrowPICS *s = opaque; - HeathrowPIC *pic; + HeathrowState *s = opaque; + HeathrowPICState *pic; unsigned int n; uint32_t value; @@ -120,40 +100,39 @@ static uint64_t pic_read(void *opaque, hwaddr addr, break; } } - PIC_DPRINTF("readl: " TARGET_FMT_plx " %u: %08x\n", addr, n, value); + trace_heathrow_read(addr, n, value); return value; } -static const MemoryRegionOps heathrow_pic_ops = { - .read = pic_read, - .write = pic_write, +static const MemoryRegionOps heathrow_ops = { + .read = heathrow_read, + .write = heathrow_write, .endianness = DEVICE_LITTLE_ENDIAN, }; -static void heathrow_pic_set_irq(void *opaque, int num, int level) +static void heathrow_set_irq(void *opaque, int num, int level) { - HeathrowPICS *s = opaque; - HeathrowPIC *pic; + HeathrowState *s = opaque; + HeathrowPICState *pic; unsigned int irq_bit; + int last_level; -#if defined(DEBUG) - { - static int last_level[64]; - if (last_level[num] != level) { - PIC_DPRINTF("set_irq: num=0x%02x level=%d\n", num, level); - last_level[num] = level; - } - } -#endif pic = &s->pics[1 - (num >> 5)]; irq_bit = 1 << (num & 0x1f); + last_level = (pic->levels & irq_bit) ? 1 : 0; + if (level) { pic->events |= irq_bit & ~pic->level_triggered; pic->levels |= irq_bit; } else { pic->levels &= ~irq_bit; } - heathrow_pic_update(s); + + if (last_level != level) { + trace_heathrow_set_irq(num, level); + } + + heathrow_update_irq(s); } static const VMStateDescription vmstate_heathrow_pic_one = { @@ -161,54 +140,81 @@ static const VMStateDescription vmstate_heathrow_pic_one = { .version_id = 0, .minimum_version_id = 0, .fields = (VMStateField[]) { - VMSTATE_UINT32(events, HeathrowPIC), - VMSTATE_UINT32(mask, HeathrowPIC), - VMSTATE_UINT32(levels, HeathrowPIC), - VMSTATE_UINT32(level_triggered, HeathrowPIC), + VMSTATE_UINT32(events, HeathrowPICState), + VMSTATE_UINT32(mask, HeathrowPICState), + VMSTATE_UINT32(levels, HeathrowPICState), + VMSTATE_UINT32(level_triggered, HeathrowPICState), VMSTATE_END_OF_LIST() } }; -static const VMStateDescription vmstate_heathrow_pic = { +static const VMStateDescription vmstate_heathrow = { .name = "heathrow_pic", .version_id = 1, .minimum_version_id = 1, .fields = (VMStateField[]) { - VMSTATE_STRUCT_ARRAY(pics, HeathrowPICS, 2, 1, - vmstate_heathrow_pic_one, HeathrowPIC), + VMSTATE_STRUCT_ARRAY(pics, HeathrowState, 2, 1, + vmstate_heathrow_pic_one, HeathrowPICState), VMSTATE_END_OF_LIST() } }; -static void heathrow_pic_reset_one(HeathrowPIC *s) +static void heathrow_reset(DeviceState *d) { - memset(s, '\0', sizeof(HeathrowPIC)); + HeathrowState *s = HEATHROW(d); + + s->pics[0].level_triggered = 0; + s->pics[1].level_triggered = 0x1ff00000; } -static void heathrow_pic_reset(void *opaque) +static void heathrow_init(Object *obj) { - HeathrowPICS *s = opaque; - - heathrow_pic_reset_one(&s->pics[0]); - heathrow_pic_reset_one(&s->pics[1]); + HeathrowState *s = HEATHROW(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - s->pics[0].level_triggered = 0; - s->pics[1].level_triggered = 0x1ff00000; + memory_region_init_io(&s->mem, OBJECT(s), &heathrow_ops, s, + "heathrow-pic", 0x1000); + sysbus_init_mmio(sbd, &s->mem); } -qemu_irq *heathrow_pic_init(MemoryRegion **pmem, - int nb_cpus, qemu_irq **irqs) +DeviceState *heathrow_pic_init(int nb_cpus, qemu_irq **irqs, + qemu_irq **pic_irqs) { - HeathrowPICS *s; + DeviceState *d; + HeathrowState *s; - s = g_malloc0(sizeof(HeathrowPICS)); + d = qdev_create(NULL, TYPE_HEATHROW); + qdev_init_nofail(d); + + s = HEATHROW(d); /* only 1 CPU */ s->irqs = irqs[0]; - memory_region_init_io(&s->mem, NULL, &heathrow_pic_ops, s, - "heathrow-pic", 0x1000); - *pmem = &s->mem; - vmstate_register(NULL, -1, &vmstate_heathrow_pic, s); - qemu_register_reset(heathrow_pic_reset, s); - return qemu_allocate_irqs(heathrow_pic_set_irq, s, 64); + *pic_irqs = qemu_allocate_irqs(heathrow_set_irq, s, HEATHROW_NUM_IRQS); + + return d; +} + +static void heathrow_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->reset = heathrow_reset; + dc->vmsd = &vmstate_heathrow; + set_bit(DEVICE_CATEGORY_MISC, dc->categories); } + +static const TypeInfo heathrow_type_info = { + .name = TYPE_HEATHROW, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(HeathrowState), + .instance_init = heathrow_init, + .class_init = heathrow_class_init, +}; + +static void heathrow_register_types(void) +{ + type_register_static(&heathrow_type_info); +} + +type_init(heathrow_register_types) diff --git a/hw/intc/openpic.c b/hw/intc/openpic.c index 9159a06f07..811cee9b26 100644 --- a/hw/intc/openpic.c +++ b/hw/intc/openpic.c @@ -63,10 +63,6 @@ static int get_current_cpu(void); } \ } while (0) -#define MAX_CPU 32 -#define MAX_MSI 8 -#define VID 0x03 /* MPIC version ID */ - /* OpenPIC capability flags */ #define OPENPIC_FLAG_IDR_CRIT (1 << 0) #define OPENPIC_FLAG_ILR (2 << 0) @@ -85,35 +81,6 @@ static int get_current_cpu(void); #define OPENPIC_CPU_REG_START 0x20000 #define OPENPIC_CPU_REG_SIZE 0x100 + ((MAX_CPU - 1) * 0x1000) -/* Raven */ -#define RAVEN_MAX_CPU 2 -#define RAVEN_MAX_EXT 48 -#define RAVEN_MAX_IRQ 64 -#define RAVEN_MAX_TMR OPENPIC_MAX_TMR -#define RAVEN_MAX_IPI OPENPIC_MAX_IPI - -/* KeyLargo */ -#define KEYLARGO_MAX_CPU 4 -#define KEYLARGO_MAX_EXT 64 -#define KEYLARGO_MAX_IPI 4 -#define KEYLARGO_MAX_IRQ (64 + KEYLARGO_MAX_IPI) -#define KEYLARGO_MAX_TMR 0 -#define KEYLARGO_IPI_IRQ (KEYLARGO_MAX_EXT) /* First IPI IRQ */ -/* Timers don't exist but this makes the code happy... */ -#define KEYLARGO_TMR_IRQ (KEYLARGO_IPI_IRQ + KEYLARGO_MAX_IPI) - -/* Interrupt definitions */ -#define RAVEN_FE_IRQ (RAVEN_MAX_EXT) /* Internal functional IRQ */ -#define RAVEN_ERR_IRQ (RAVEN_MAX_EXT + 1) /* Error IRQ */ -#define RAVEN_TMR_IRQ (RAVEN_MAX_EXT + 2) /* First timer IRQ */ -#define RAVEN_IPI_IRQ (RAVEN_TMR_IRQ + RAVEN_MAX_TMR) /* First IPI IRQ */ -/* First doorbell IRQ */ -#define RAVEN_DBL_IRQ (RAVEN_IPI_IRQ + (RAVEN_MAX_CPU * RAVEN_MAX_IPI)) - -typedef struct FslMpicInfo { - int max_ext; -} FslMpicInfo; - static FslMpicInfo fsl_mpic_20 = { .max_ext = 12, }; @@ -211,55 +178,6 @@ static void openpic_cpu_write_internal(void *opaque, hwaddr addr, uint32_t val, int idx); static void openpic_reset(DeviceState *d); -typedef enum IRQType { - IRQ_TYPE_NORMAL = 0, - IRQ_TYPE_FSLINT, /* FSL internal interrupt -- level only */ - 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 { - unsigned long *queue; - int32_t queue_size; /* Only used for VMSTATE_BITMAP */ - int next; - int priority; -} IRQQueue; - -typedef struct IRQSource { - uint32_t ivpr; /* IRQ vector/priority register */ - uint32_t idr; /* IRQ destination register */ - uint32_t destmask; /* bitmap of CPU destinations */ - int last_cpu; - int output; /* IRQ level, e.g. OPENPIC_OUTPUT_INT */ - int pending; /* TRUE if IRQ is pending */ - IRQType type; - bool level:1; /* level-triggered */ - bool nomask:1; /* critical interrupts ignore mask on some FSL MPICs */ -} IRQSource; - -#define IVPR_MASK_SHIFT 31 -#define IVPR_MASK_MASK (1U << IVPR_MASK_SHIFT) -#define IVPR_ACTIVITY_SHIFT 30 -#define IVPR_ACTIVITY_MASK (1U << IVPR_ACTIVITY_SHIFT) -#define IVPR_MODE_SHIFT 29 -#define IVPR_MODE_MASK (1U << IVPR_MODE_SHIFT) -#define IVPR_POLARITY_SHIFT 23 -#define IVPR_POLARITY_MASK (1U << IVPR_POLARITY_SHIFT) -#define IVPR_SENSE_SHIFT 22 -#define IVPR_SENSE_MASK (1U << IVPR_SENSE_SHIFT) - -#define IVPR_PRIORITY_MASK (0xFU << 16) -#define IVPR_PRIORITY(_ivprr_) ((int)(((_ivprr_) & IVPR_PRIORITY_MASK) >> 16)) -#define IVPR_VECTOR(opp, _ivprr_) ((_ivprr_) & (opp)->vector_mask) - -/* IDR[EP/CI] are only for FSL MPIC prior to v4.0 */ -#define IDR_EP 0x80000000 /* external pin */ -#define IDR_CI 0x40000000 /* critical interrupt */ - /* Convert between openpic clock ticks and nanosecs. In the hardware the clock frequency is driven by board inputs to the PIC which the PIC would then divide by 4 or 8. For now hard code to 25MZ. @@ -275,81 +193,6 @@ static inline uint64_t ticks_to_ns(uint64_t ticks) return ticks * OPENPIC_TIMER_NS_PER_TICK; } -typedef struct OpenPICTimer { - uint32_t tccr; /* Global timer current count register */ - uint32_t tbcr; /* Global timer base count register */ - int n_IRQ; - bool qemu_timer_active; /* Is the qemu_timer is running? */ - struct QEMUTimer *qemu_timer; - struct OpenPICState *opp; /* Device timer is part of. */ - /* The QEMU_CLOCK_VIRTUAL time (in ns) corresponding to the last - current_count written or read, only defined if qemu_timer_active. */ - uint64_t origin_time; -} 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; - IRQQueue servicing; - qemu_irq *irqs; - - /* Count of IRQ sources asserting on non-INT outputs */ - uint32_t outputs_active[OPENPIC_OUTPUT_NB]; -} IRQDest; - -#define OPENPIC(obj) OBJECT_CHECK(OpenPICState, (obj), TYPE_OPENPIC) - -typedef struct OpenPICState { - /*< private >*/ - SysBusDevice parent_obj; - /*< public >*/ - - MemoryRegion mem; - - /* Behavior control */ - FslMpicInfo *fsl; - uint32_t model; - uint32_t flags; - uint32_t nb_irqs; - uint32_t vid; - uint32_t vir; /* Vendor identification register */ - uint32_t vector_mask; - uint32_t tfrr_reset; - uint32_t ivpr_reset; - uint32_t idr_reset; - uint32_t brr1; - uint32_t mpic_mode_mask; - - /* Sub-regions */ - MemoryRegion sub_io_mem[6]; - - /* Global registers */ - uint32_t frr; /* Feature reporting register */ - uint32_t gcr; /* Global configuration register */ - uint32_t pir; /* Processor initialization register */ - uint32_t spve; /* Spurious vector register */ - uint32_t tfrr; /* Timer frequency reporting register */ - /* Source registers */ - IRQSource src[OPENPIC_MAX_IRQ]; - /* Local registers per output pin */ - IRQDest dst[MAX_CPU]; - uint32_t nb_cpus; - /* Timer registers */ - OpenPICTimer timers[OPENPIC_MAX_TMR]; - uint32_t max_tmr; - - /* Shared MSI registers */ - OpenPICMSI msi[MAX_MSI]; - uint32_t max_irq; - uint32_t irq_ipi0; - uint32_t irq_tim0; - uint32_t irq_msi; -} OpenPICState; - static inline void IRQ_setbit(IRQQueue *q, int n_IRQ) { set_bit(n_IRQ, q->queue); diff --git a/hw/intc/openpic_kvm.c b/hw/intc/openpic_kvm.c index fa83420254..f1a59e5a85 100644 --- a/hw/intc/openpic_kvm.c +++ b/hw/intc/openpic_kvm.c @@ -30,6 +30,7 @@ #include "exec/address-spaces.h" #include "hw/hw.h" #include "hw/ppc/openpic.h" +#include "hw/ppc/openpic_kvm.h" #include "hw/pci/msi.h" #include "hw/sysbus.h" #include "sysemu/kvm.h" diff --git a/hw/intc/trace-events b/hw/intc/trace-events index 4092d2825e..55e8c2570c 100644 --- a/hw/intc/trace-events +++ b/hw/intc/trace-events @@ -186,3 +186,8 @@ nvic_complete_irq(int irq, bool secure) "NVIC complete IRQ %d (secure %d)" nvic_set_irq_level(int irq, int level) "NVIC external irq %d level set to %d" nvic_sysreg_read(uint64_t addr, uint32_t value, unsigned size) "NVIC sysreg read addr 0x%" PRIx64 " data 0x%" PRIx32 " size %u" nvic_sysreg_write(uint64_t addr, uint32_t value, unsigned size) "NVIC sysreg write addr 0x%" PRIx64 " data 0x%" PRIx32 " size %u" + +# hw/intc/heathrow_pic.c +heathrow_write(uint64_t addr, unsigned int n, uint64_t value) "0x%"PRIx64" %u: 0x%"PRIx64 +heathrow_read(uint64_t addr, unsigned int n, uint64_t value) "0x%"PRIx64" %u: 0x%"PRIx64 +heathrow_set_irq(int num, int level) "set_irq: num=0x%02x level=%d" diff --git a/hw/misc/macio/macio.c b/hw/misc/macio/macio.c index 024f8557ab..af1bd46b4b 100644 --- a/hw/misc/macio/macio.c +++ b/hw/misc/macio/macio.c @@ -30,48 +30,11 @@ #include "hw/pci/pci.h" #include "hw/ppc/mac_dbdma.h" #include "hw/char/escc.h" +#include "hw/misc/macio/macio.h" +#include "hw/intc/heathrow_pic.h" -#define TYPE_MACIO "macio" -#define MACIO(obj) OBJECT_CHECK(MacIOState, (obj), TYPE_MACIO) - -typedef struct MacIOState -{ - /*< private >*/ - PCIDevice parent; - /*< public >*/ - - MemoryRegion bar; - CUDAState cuda; - DBDMAState *dbdma; - MemoryRegion *pic_mem; - MemoryRegion *escc_mem; - uint64_t frequency; -} MacIOState; - -#define OLDWORLD_MACIO(obj) \ - OBJECT_CHECK(OldWorldMacIOState, (obj), TYPE_OLDWORLD_MACIO) - -typedef struct OldWorldMacIOState { - /*< private >*/ - MacIOState parent_obj; - /*< public >*/ - - qemu_irq irqs[5]; - - MacIONVRAMState nvram; - MACIOIDEState ide[2]; -} OldWorldMacIOState; - -#define NEWWORLD_MACIO(obj) \ - OBJECT_CHECK(NewWorldMacIOState, (obj), TYPE_NEWWORLD_MACIO) - -typedef struct NewWorldMacIOState { - /*< private >*/ - MacIOState parent_obj; - /*< public >*/ - qemu_irq irqs[5]; - MACIOIDEState ide[2]; -} NewWorldMacIOState; +/* Note: this code is strongly inspirated from the corresponding code + * in PearPC */ /* * The mac-io has two interfaces to the ESCC. One is called "escc-legacy", @@ -84,10 +47,12 @@ typedef struct NewWorldMacIOState { * * Reference: ftp://ftp.software.ibm.com/rs6000/technology/spec/chrp/inwork/CHRP_IORef_1.0.pdf */ -static void macio_escc_legacy_setup(MacIOState *macio_state) +static void macio_escc_legacy_setup(MacIOState *s) { + ESCCState *escc = ESCC(&s->escc); + SysBusDevice *sbd = SYS_BUS_DEVICE(escc); MemoryRegion *escc_legacy = g_new(MemoryRegion, 1); - MemoryRegion *bar = &macio_state->bar; + MemoryRegion *bar = &s->bar; int i; static const int maps[] = { 0x00, 0x00, /* Command B */ @@ -102,25 +67,26 @@ static void macio_escc_legacy_setup(MacIOState *macio_state) 0xb0, 0xb0, /* Detect AB */ }; - memory_region_init(escc_legacy, OBJECT(macio_state), "escc-legacy", 256); + memory_region_init(escc_legacy, OBJECT(s), "escc-legacy", 256); for (i = 0; i < ARRAY_SIZE(maps); i += 2) { MemoryRegion *port = g_new(MemoryRegion, 1); - memory_region_init_alias(port, OBJECT(macio_state), "escc-legacy-port", - macio_state->escc_mem, maps[i+1], 0x2); + memory_region_init_alias(port, OBJECT(s), "escc-legacy-port", + sysbus_mmio_get_region(sbd, 0), + maps[i + 1], 0x2); memory_region_add_subregion(escc_legacy, maps[i], port); } memory_region_add_subregion(bar, 0x12000, escc_legacy); } -static void macio_bar_setup(MacIOState *macio_state) +static void macio_bar_setup(MacIOState *s) { - MemoryRegion *bar = &macio_state->bar; + ESCCState *escc = ESCC(&s->escc); + SysBusDevice *sbd = SYS_BUS_DEVICE(escc); + MemoryRegion *bar = &s->bar; - if (macio_state->escc_mem) { - memory_region_add_subregion(bar, 0x13000, macio_state->escc_mem); - macio_escc_legacy_setup(macio_state); - } + memory_region_add_subregion(bar, 0x13000, sysbus_mmio_get_region(sbd, 0)); + macio_escc_legacy_setup(s); } static void macio_common_realize(PCIDevice *d, Error **errp) @@ -129,15 +95,17 @@ static void macio_common_realize(PCIDevice *d, Error **errp) SysBusDevice *sysbus_dev; Error *err = NULL; - object_property_set_bool(OBJECT(s->dbdma), true, "realized", &err); + object_property_set_bool(OBJECT(&s->dbdma), true, "realized", &err); if (err) { error_propagate(errp, err); return; } - sysbus_dev = SYS_BUS_DEVICE(s->dbdma); + sysbus_dev = SYS_BUS_DEVICE(&s->dbdma); 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); @@ -147,6 +115,12 @@ static void macio_common_realize(PCIDevice *d, Error **errp) memory_region_add_subregion(&s->bar, 0x16000, sysbus_mmio_get_region(sysbus_dev, 0)); + object_property_set_bool(OBJECT(&s->escc), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + macio_bar_setup(s); pci_register_bar(d, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->bar); } @@ -161,7 +135,7 @@ static void macio_realize_ide(MacIOState *s, MACIOIDEState *ide, sysbus_connect_irq(sysbus_dev, 0, irq0); sysbus_connect_irq(sysbus_dev, 1, irq1); qdev_prop_set_uint32(DEVICE(ide), "channel", dmaid); - object_property_set_link(OBJECT(ide), OBJECT(s->dbdma), "dbdma", errp); + object_property_set_link(OBJECT(ide), OBJECT(&s->dbdma), "dbdma", errp); macio_ide_register_dma(ide); object_property_set_bool(OBJECT(ide), true, "realized", errp); @@ -185,6 +159,10 @@ static void macio_oldworld_realize(PCIDevice *d, Error **errp) sysbus_dev = SYS_BUS_DEVICE(&s->cuda); sysbus_connect_irq(sysbus_dev, 0, os->irqs[cur_irq++]); + sysbus_dev = SYS_BUS_DEVICE(&s->escc); + sysbus_connect_irq(sysbus_dev, 0, os->irqs[cur_irq++]); + sysbus_connect_irq(sysbus_dev, 1, os->irqs[cur_irq++]); + object_property_set_bool(OBJECT(&os->nvram), true, "realized", &err); if (err) { error_propagate(errp, err); @@ -195,10 +173,10 @@ static void macio_oldworld_realize(PCIDevice *d, Error **errp) sysbus_mmio_get_region(sysbus_dev, 0)); pmac_format_nvram_partition(&os->nvram, os->nvram.size); - if (s->pic_mem) { - /* Heathrow PIC */ - memory_region_add_subregion(&s->bar, 0x00000, s->pic_mem); - } + /* Heathrow PIC */ + sysbus_dev = SYS_BUS_DEVICE(os->pic); + memory_region_add_subregion(&s->bar, 0x0, + sysbus_mmio_get_region(sysbus_dev, 0)); /* IDE buses */ for (i = 0; i < ARRAY_SIZE(os->ide); i++) { @@ -236,6 +214,11 @@ static void macio_oldworld_init(Object *obj) qdev_init_gpio_out(DEVICE(obj), os->irqs, ARRAY_SIZE(os->irqs)); + object_property_add_link(obj, "pic", TYPE_HEATHROW, + (Object **) &os->pic, + qdev_prop_allow_set_link_before_realize, + 0, NULL); + object_initialize(&os->nvram, sizeof(os->nvram), TYPE_MACIO_NVRAM); dev = DEVICE(&os->nvram); qdev_prop_set_uint32(dev, "size", 0x2000); @@ -297,10 +280,14 @@ static void macio_newworld_realize(PCIDevice *d, Error **errp) sysbus_dev = SYS_BUS_DEVICE(&s->cuda); sysbus_connect_irq(sysbus_dev, 0, ns->irqs[cur_irq++]); - if (s->pic_mem) { - /* OpenPIC */ - memory_region_add_subregion(&s->bar, 0x40000, s->pic_mem); - } + sysbus_dev = SYS_BUS_DEVICE(&s->escc); + sysbus_connect_irq(sysbus_dev, 0, ns->irqs[cur_irq++]); + sysbus_connect_irq(sysbus_dev, 1, ns->irqs[cur_irq++]); + + /* OpenPIC */ + sysbus_dev = SYS_BUS_DEVICE(ns->pic); + memory_region_add_subregion(&s->bar, 0x40000, + sysbus_mmio_get_region(sysbus_dev, 0)); /* IDE buses */ for (i = 0; i < ARRAY_SIZE(ns->ide); i++) { @@ -329,6 +316,11 @@ static void macio_newworld_init(Object *obj) qdev_init_gpio_out(DEVICE(obj), ns->irqs, ARRAY_SIZE(ns->irqs)); + object_property_add_link(obj, "pic", TYPE_OPENPIC, + (Object **) &ns->pic, + qdev_prop_allow_set_link_before_realize, + 0, NULL); + for (i = 0; i < 2; i++) { macio_init_ide(s, &ns->ide[i], sizeof(ns->ide[i]), i); } @@ -344,8 +336,20 @@ static void macio_instance_init(Object *obj) qdev_set_parent_bus(DEVICE(&s->cuda), sysbus_get_default()); object_property_add_child(obj, "cuda", OBJECT(&s->cuda), NULL); - s->dbdma = MAC_DBDMA(object_new(TYPE_MAC_DBDMA)); - object_property_add_child(obj, "dbdma", OBJECT(s->dbdma), 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); + + object_initialize(&s->escc, sizeof(s->escc), TYPE_ESCC); + 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); + qdev_prop_set_chr(DEVICE(&s->escc), "chrA", serial_hds[0]); + qdev_prop_set_chr(DEVICE(&s->escc), "chrB", serial_hds[1]); + qdev_prop_set_uint32(DEVICE(&s->escc), "chnBtype", escc_serial); + qdev_prop_set_uint32(DEVICE(&s->escc), "chnAtype", escc_serial); + qdev_set_parent_bus(DEVICE(&s->escc), sysbus_get_default()); + object_property_add_child(obj, "escc", OBJECT(&s->escc), NULL); } static const VMStateDescription vmstate_macio_oldworld = { @@ -441,19 +445,3 @@ static void macio_register_types(void) } type_init(macio_register_types) - -void macio_init(PCIDevice *d, - MemoryRegion *pic_mem, - MemoryRegion *escc_mem) -{ - MacIOState *macio_state = MACIO(d); - - macio_state->pic_mem = pic_mem; - macio_state->escc_mem = escc_mem; - /* Note: this code is strongly inspirated from the corresponding code - in PearPC */ - qdev_prop_set_uint64(DEVICE(&macio_state->cuda), "timebase-frequency", - macio_state->frequency); - - qdev_init_nofail(DEVICE(d)); -} diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs index ad1928c5d8..86d82a6ec3 100644 --- a/hw/ppc/Makefile.objs +++ b/hw/ppc/Makefile.objs @@ -13,7 +13,8 @@ endif obj-$(CONFIG_PSERIES) += spapr_rtas_ddw.o # PowerPC 4xx boards obj-y += ppc4xx_devs.o ppc405_uc.o -obj-$(CONFIG_PPC4XX) += ppc4xx_pci.o ppc405_boards.o ppc440_bamboo.o +obj-$(CONFIG_PPC4XX) += ppc4xx_pci.o ppc405_boards.o +obj-$(CONFIG_PPC4XX) += ppc440_bamboo.o ppc440_pcix.o ppc440_uc.o sam460ex.o # PReP obj-$(CONFIG_PREP) += prep.o obj-$(CONFIG_PREP) += prep_systemio.o diff --git a/hw/ppc/e500.c b/hw/ppc/e500.c index a40d3ec3e3..43c15d18c4 100644 --- a/hw/ppc/e500.c +++ b/hw/ppc/e500.c @@ -29,6 +29,7 @@ #include "kvm_ppc.h" #include "sysemu/device_tree.h" #include "hw/ppc/openpic.h" +#include "hw/ppc/openpic_kvm.h" #include "hw/ppc/ppc.h" #include "hw/loader.h" #include "elf.h" @@ -119,7 +120,14 @@ static void dt_serial_create(void *fdt, unsigned long long offset, qemu_fdt_setprop_string(fdt, "/aliases", alias, ser); if (defcon) { + /* + * "linux,stdout-path" and "stdout" properties are deprecated by linux + * kernel. New platforms should only use the "stdout-path" property. Set + * the new property and continue using older property to remain + * compatible with the existing firmware. + */ qemu_fdt_setprop_string(fdt, "/chosen", "linux,stdout-path", ser); + qemu_fdt_setprop_string(fdt, "/chosen", "stdout-path", ser); } } @@ -784,8 +792,10 @@ void ppce500_init(MachineState *machine, PPCE500Params *params) int initrd_size = 0; hwaddr cur_base = 0; char *filename; + const char *payload_name; + bool kernel_as_payload; hwaddr bios_entry = 0; - target_long bios_size; + target_long payload_size; struct boot_info *boot_info; int dt_size; int i; @@ -913,11 +923,6 @@ void ppce500_init(MachineState *machine, PPCE500Params *params) /* Register spinning region */ sysbus_create_simple("e500-spin", params->spin_base, NULL); - if (cur_base < (32 * 1024 * 1024)) { - /* u-boot occupies memory up to 32MB, so load blobs above */ - cur_base = (32 * 1024 * 1024); - } - if (params->has_mpc8xxx_gpio) { qemu_irq poweroff_irq; @@ -952,8 +957,61 @@ void ppce500_init(MachineState *machine, PPCE500Params *params) sysbus_mmio_get_region(s, 0)); } - /* Load kernel. */ - if (machine->kernel_filename) { + /* + * Smart firmware defaults ahead! + * + * We follow the following table to select which payload we execute. + * + * -kernel | -bios | payload + * ---------+-------+--------- + * N | Y | u-boot + * N | N | u-boot + * Y | Y | u-boot + * Y | N | kernel + * + * This ensures backwards compatibility with how we used to expose + * -kernel to users but allows them to run through u-boot as well. + */ + kernel_as_payload = false; + if (bios_name == NULL) { + if (machine->kernel_filename) { + payload_name = machine->kernel_filename; + kernel_as_payload = true; + } else { + payload_name = "u-boot.e500"; + } + } else { + payload_name = bios_name; + } + + filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, payload_name); + + payload_size = load_elf(filename, NULL, NULL, &bios_entry, &loadaddr, NULL, + 1, PPC_ELF_MACHINE, 0, 0); + if (payload_size < 0) { + /* + * Hrm. No ELF image? Try a uImage, maybe someone is giving us an + * ePAPR compliant kernel + */ + payload_size = load_uimage(filename, &bios_entry, &loadaddr, NULL, + NULL, NULL); + if (payload_size < 0) { + error_report("qemu: could not load firmware '%s'", filename); + exit(1); + } + } + + g_free(filename); + + if (kernel_as_payload) { + kernel_base = loadaddr; + kernel_size = payload_size; + } + + cur_base = loadaddr + payload_size; + + /* Load bare kernel only if no bios/u-boot has been provided */ + if (machine->kernel_filename && !kernel_as_payload) { kernel_base = cur_base; kernel_size = load_image_targphys(machine->kernel_filename, cur_base, @@ -967,6 +1025,11 @@ void ppce500_init(MachineState *machine, PPCE500Params *params) cur_base += kernel_size; } + if (cur_base < (32 * 1024 * 1024)) { + /* u-boot occupies memory up to 32MB, so load blobs above */ + cur_base = (32 * 1024 * 1024); + } + /* Load initrd. */ if (machine->initrd_filename) { initrd_base = (cur_base + INITRD_LOAD_PAD) & ~INITRD_PAD_MASK; @@ -983,47 +1046,16 @@ void ppce500_init(MachineState *machine, PPCE500Params *params) } /* - * Smart firmware defaults ahead! - * - * We follow the following table to select which payload we execute. - * - * -kernel | -bios | payload - * ---------+-------+--------- - * N | Y | u-boot - * N | N | u-boot - * Y | Y | u-boot - * Y | N | kernel - * - * This ensures backwards compatibility with how we used to expose - * -kernel to users but allows them to run through u-boot as well. + * Reserve space for dtb behind the kernel image because Linux has a bug + * where it can only handle the dtb if it's within the first 64MB of where + * <kernel> starts. dtb cannot not reach initrd_base because INITRD_LOAD_PAD + * ensures enough space between kernel and initrd. */ - if (bios_name == NULL) { - if (machine->kernel_filename) { - bios_name = machine->kernel_filename; - } else { - bios_name = "u-boot.e500"; - } - } - filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); - - bios_size = load_elf(filename, NULL, NULL, &bios_entry, &loadaddr, NULL, - 1, PPC_ELF_MACHINE, 0, 0); - if (bios_size < 0) { - /* - * Hrm. No ELF image? Try a uImage, maybe someone is giving us an - * ePAPR compliant kernel - */ - kernel_size = load_uimage(filename, &bios_entry, &loadaddr, NULL, - NULL, NULL); - if (kernel_size < 0) { - error_report("could not load firmware '%s'", filename); + dt_base = (loadaddr + payload_size + DTC_LOAD_PAD) & ~DTC_PAD_MASK; + if (dt_base + DTB_MAX_SIZE > ram_size) { + error_report("qemu: not enough memory for device tree"); exit(1); - } } - g_free(filename); - - /* Reserve space for dtb */ - dt_base = (loadaddr + bios_size + DTC_LOAD_PAD) & ~DTC_PAD_MASK; dt_size = ppce500_prep_device_tree(machine, params, dt_base, initrd_base, initrd_size, diff --git a/hw/ppc/mac.h b/hw/ppc/mac.h index 4702194f3f..a02f797598 100644 --- a/hw/ppc/mac.h +++ b/hw/ppc/mac.h @@ -47,9 +47,6 @@ /* MacIO */ -#define TYPE_OLDWORLD_MACIO "macio-oldworld" -#define TYPE_NEWWORLD_MACIO "macio-newworld" - #define TYPE_MACIO_IDE "macio-ide" #define MACIO_IDE(obj) OBJECT_CHECK(MACIOIDEState, (obj), TYPE_MACIO_IDE) @@ -76,12 +73,11 @@ void macio_ide_init_drives(MACIOIDEState *ide, DriveInfo **hd_table); void macio_ide_register_dma(MACIOIDEState *ide); void macio_init(PCIDevice *dev, - MemoryRegion *pic_mem, - MemoryRegion *escc_mem); + MemoryRegion *pic_mem); /* Heathrow PIC */ -qemu_irq *heathrow_pic_init(MemoryRegion **pmem, - int nb_cpus, qemu_irq **irqs); +DeviceState *heathrow_pic_init(int nb_cpus, qemu_irq **irqs, + qemu_irq **pic_irqs); /* Grackle PCI */ #define TYPE_GRACKLE_PCI_HOST_BRIDGE "grackle-pcihost" diff --git a/hw/ppc/mac_newworld.c b/hw/ppc/mac_newworld.c index 4e1298ee50..a749e2565d 100644 --- a/hw/ppc/mac_newworld.c +++ b/hw/ppc/mac_newworld.c @@ -60,6 +60,7 @@ #include "hw/boards.h" #include "hw/nvram/fw_cfg.h" #include "hw/char/escc.h" +#include "hw/misc/macio/macio.h" #include "hw/ppc/openpic.h" #include "hw/ide.h" #include "hw/loader.h" @@ -153,20 +154,18 @@ static void ppc_core99_init(MachineState *machine) hwaddr kernel_base, initrd_base, cmdline_base = 0; long kernel_size, initrd_size; PCIBus *pci_bus; - PCIDevice *macio; + NewWorldMacIOState *macio; MACIOIDEState *macio_ide; BusState *adb_bus; MacIONVRAMState *nvr; int bios_size, ndrv_size; uint8_t *ndrv_file; - MemoryRegion *pic_mem, *escc_mem; - MemoryRegion *escc_bar = g_new(MemoryRegion, 1); int ppc_boot_device; DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; void *fw_cfg; int machine_arch; SysBusDevice *s; - DeviceState *dev; + DeviceState *dev, *pic_dev; int *token = g_new(int, 1); hwaddr nvram_addr = 0xFFF04000; uint64_t tbfreq; @@ -333,11 +332,10 @@ static void ppc_core99_init(MachineState *machine) pic = g_new0(qemu_irq, 64); - dev = qdev_create(NULL, TYPE_OPENPIC); - qdev_prop_set_uint32(dev, "model", OPENPIC_MODEL_KEYLARGO); - qdev_init_nofail(dev); - s = SYS_BUS_DEVICE(dev); - pic_mem = s->mmio[0].memory; + pic_dev = qdev_create(NULL, TYPE_OPENPIC); + qdev_prop_set_uint32(pic_dev, "model", OPENPIC_MODEL_KEYLARGO); + qdev_init_nofail(pic_dev); + s = SYS_BUS_DEVICE(pic_dev); k = 0; for (i = 0; i < smp_cpus; i++) { for (j = 0; j < OPENPIC_OUTPUT_NB; j++) { @@ -346,7 +344,7 @@ static void ppc_core99_init(MachineState *machine) } for (i = 0; i < 64; i++) { - pic[i] = qdev_get_gpio_in(dev, i); + pic[i] = qdev_get_gpio_in(pic_dev, i); } if (PPC_INPUT(env) == PPC_FLAGS_INPUT_970) { @@ -368,36 +366,20 @@ static void ppc_core99_init(MachineState *machine) tbfreq = TBFREQ; } - /* init basic PC hardware */ - - dev = qdev_create(NULL, TYPE_ESCC); - qdev_prop_set_uint32(dev, "disabled", 0); - qdev_prop_set_uint32(dev, "frequency", ESCC_CLOCK); - qdev_prop_set_uint32(dev, "it_shift", 4); - qdev_prop_set_chr(dev, "chrA", serial_hds[0]); - qdev_prop_set_chr(dev, "chrB", serial_hds[1]); - qdev_prop_set_uint32(dev, "chnAtype", escc_serial); - qdev_prop_set_uint32(dev, "chnBtype", escc_serial); - qdev_init_nofail(dev); - - s = SYS_BUS_DEVICE(dev); - sysbus_connect_irq(s, 0, pic[0x24]); - sysbus_connect_irq(s, 1, pic[0x25]); - - escc_mem = &ESCC(s)->mmio; - - memory_region_init_alias(escc_bar, NULL, "escc-bar", - escc_mem, 0, memory_region_size(escc_mem)); - - macio = pci_create(pci_bus, -1, TYPE_NEWWORLD_MACIO); + /* MacIO */ + macio = NEWWORLD_MACIO(pci_create(pci_bus, -1, TYPE_NEWWORLD_MACIO)); dev = DEVICE(macio); qdev_connect_gpio_out(dev, 0, pic[0x19]); /* CUDA */ - qdev_connect_gpio_out(dev, 1, pic[0x0d]); /* IDE */ - qdev_connect_gpio_out(dev, 2, pic[0x02]); /* IDE DMA */ - qdev_connect_gpio_out(dev, 3, pic[0x0e]); /* IDE */ - qdev_connect_gpio_out(dev, 4, pic[0x03]); /* IDE DMA */ + qdev_connect_gpio_out(dev, 1, pic[0x24]); /* ESCC-B */ + qdev_connect_gpio_out(dev, 2, pic[0x25]); /* ESCC-A */ + qdev_connect_gpio_out(dev, 3, pic[0x0d]); /* IDE */ + qdev_connect_gpio_out(dev, 4, pic[0x02]); /* IDE DMA */ + qdev_connect_gpio_out(dev, 5, pic[0x0e]); /* IDE */ + qdev_connect_gpio_out(dev, 6, pic[0x03]); /* IDE DMA */ qdev_prop_set_uint64(dev, "frequency", tbfreq); - macio_init(macio, pic_mem, escc_bar); + object_property_set_link(OBJECT(macio), OBJECT(pic_dev), "pic", + &error_abort); + qdev_init_nofail(dev); /* We only emulate 2 out of 3 IDE controllers for now */ ide_drive_get(hd, ARRAY_SIZE(hd)); diff --git a/hw/ppc/mac_oldworld.c b/hw/ppc/mac_oldworld.c index d0d21d2392..935493c966 100644 --- a/hw/ppc/mac_oldworld.c +++ b/hw/ppc/mac_oldworld.c @@ -37,6 +37,7 @@ #include "hw/boards.h" #include "hw/nvram/fw_cfg.h" #include "hw/char/escc.h" +#include "hw/misc/macio/macio.h" #include "hw/ide.h" #include "hw/loader.h" #include "elf.h" @@ -92,19 +93,16 @@ static void ppc_heathrow_init(MachineState *machine) uint32_t kernel_base, initrd_base, cmdline_base = 0; int32_t kernel_size, initrd_size; PCIBus *pci_bus; - PCIDevice *macio; + OldWorldMacIOState *macio; MACIOIDEState *macio_ide; - DeviceState *dev; + DeviceState *dev, *pic_dev; BusState *adb_bus; int bios_size, ndrv_size; uint8_t *ndrv_file; - MemoryRegion *pic_mem; - MemoryRegion *escc_mem, *escc_bar = g_new(MemoryRegion, 1); uint16_t ppc_boot_device; DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; void *fw_cfg; uint64_t tbfreq; - SysBusDevice *s; linux_boot = (kernel_filename != NULL); @@ -259,46 +257,32 @@ static void ppc_heathrow_init(MachineState *machine) error_report("Only 6xx bus is supported on heathrow machine"); exit(1); } - pic = heathrow_pic_init(&pic_mem, 1, heathrow_irqs); + pic_dev = heathrow_pic_init(1, heathrow_irqs, &pic); pci_bus = pci_grackle_init(0xfec00000, pic, get_system_memory(), get_system_io()); pci_vga_init(pci_bus); - dev = qdev_create(NULL, TYPE_ESCC); - qdev_prop_set_uint32(dev, "disabled", 0); - qdev_prop_set_uint32(dev, "frequency", ESCC_CLOCK); - qdev_prop_set_uint32(dev, "it_shift", 4); - qdev_prop_set_chr(dev, "chrA", serial_hds[0]); - qdev_prop_set_chr(dev, "chrB", serial_hds[1]); - qdev_prop_set_uint32(dev, "chnBtype", escc_serial); - qdev_prop_set_uint32(dev, "chnAtype", escc_serial); - qdev_init_nofail(dev); - - s = SYS_BUS_DEVICE(dev); - sysbus_connect_irq(s, 0, pic[0x10]); - sysbus_connect_irq(s, 1, pic[0x0f]); - - escc_mem = &ESCC(s)->mmio; - - memory_region_init_alias(escc_bar, NULL, "escc-bar", - escc_mem, 0, memory_region_size(escc_mem)); - - for(i = 0; i < nb_nics; i++) + for (i = 0; i < nb_nics; i++) { pci_nic_init_nofail(&nd_table[i], pci_bus, "ne2k_pci", NULL); - + } ide_drive_get(hd, ARRAY_SIZE(hd)); - macio = pci_create(pci_bus, -1, TYPE_OLDWORLD_MACIO); + /* MacIO */ + macio = OLDWORLD_MACIO(pci_create(pci_bus, -1, TYPE_OLDWORLD_MACIO)); dev = DEVICE(macio); qdev_connect_gpio_out(dev, 0, pic[0x12]); /* CUDA */ - qdev_connect_gpio_out(dev, 1, pic[0x0D]); /* IDE-0 */ - qdev_connect_gpio_out(dev, 2, pic[0x02]); /* IDE-0 DMA */ - qdev_connect_gpio_out(dev, 3, pic[0x0E]); /* IDE-1 */ - qdev_connect_gpio_out(dev, 4, pic[0x03]); /* IDE-1 DMA */ + qdev_connect_gpio_out(dev, 1, pic[0x10]); /* ESCC-B */ + qdev_connect_gpio_out(dev, 2, pic[0x0F]); /* ESCC-A */ + qdev_connect_gpio_out(dev, 3, pic[0x0D]); /* IDE-0 */ + qdev_connect_gpio_out(dev, 4, pic[0x02]); /* IDE-0 DMA */ + qdev_connect_gpio_out(dev, 5, pic[0x0E]); /* IDE-1 */ + qdev_connect_gpio_out(dev, 6, pic[0x03]); /* IDE-1 DMA */ qdev_prop_set_uint64(dev, "frequency", tbfreq); - macio_init(macio, pic_mem, escc_bar); + object_property_set_link(OBJECT(macio), OBJECT(pic_dev), "pic", + &error_abort); + qdev_init_nofail(dev); macio_ide = MACIO_IDE(object_resolve_path_component(OBJECT(macio), "ide[0]")); diff --git a/hw/ppc/ppc440_pcix.c b/hw/ppc/ppc440_pcix.c new file mode 100644 index 0000000000..ab2626a9de --- /dev/null +++ b/hw/ppc/ppc440_pcix.c @@ -0,0 +1,528 @@ +/* + * Emulation of the ibm,plb-pcix PCI controller + * This is found in some 440 SoCs e.g. the 460EX. + * + * Copyright (c) 2016-2018 BALATON Zoltan + * + * Derived from ppc4xx_pci.c and pci-host/ppce500.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "hw/hw.h" +#include "hw/ppc/ppc.h" +#include "hw/ppc/ppc4xx.h" +#include "hw/pci/pci.h" +#include "hw/pci/pci_host.h" +#include "exec/address-spaces.h" +#include "trace.h" + +struct PLBOutMap { + uint64_t la; + uint64_t pcia; + uint32_t sa; + MemoryRegion mr; +}; + +struct PLBInMap { + uint64_t sa; + uint64_t la; + MemoryRegion mr; +}; + +#define TYPE_PPC440_PCIX_HOST_BRIDGE "ppc440-pcix-host" +#define PPC440_PCIX_HOST_BRIDGE(obj) \ + OBJECT_CHECK(PPC440PCIXState, (obj), TYPE_PPC440_PCIX_HOST_BRIDGE) + +#define PPC440_PCIX_NR_POMS 3 +#define PPC440_PCIX_NR_PIMS 3 + +typedef struct PPC440PCIXState { + PCIHostState parent_obj; + + PCIDevice *dev; + struct PLBOutMap pom[PPC440_PCIX_NR_POMS]; + struct PLBInMap pim[PPC440_PCIX_NR_PIMS]; + uint32_t sts; + qemu_irq irq[PCI_NUM_PINS]; + AddressSpace bm_as; + MemoryRegion bm; + + MemoryRegion container; + MemoryRegion iomem; + MemoryRegion busmem; +} PPC440PCIXState; + +#define PPC440_REG_BASE 0x80000 +#define PPC440_REG_SIZE 0xff + +#define PCIC0_CFGADDR 0x0 +#define PCIC0_CFGDATA 0x4 + +#define PCIX0_POM0LAL 0x68 +#define PCIX0_POM0LAH 0x6c +#define PCIX0_POM0SA 0x70 +#define PCIX0_POM0PCIAL 0x74 +#define PCIX0_POM0PCIAH 0x78 +#define PCIX0_POM1LAL 0x7c +#define PCIX0_POM1LAH 0x80 +#define PCIX0_POM1SA 0x84 +#define PCIX0_POM1PCIAL 0x88 +#define PCIX0_POM1PCIAH 0x8c +#define PCIX0_POM2SA 0x90 + +#define PCIX0_PIM0SAL 0x98 +#define PCIX0_PIM0LAL 0x9c +#define PCIX0_PIM0LAH 0xa0 +#define PCIX0_PIM1SA 0xa4 +#define PCIX0_PIM1LAL 0xa8 +#define PCIX0_PIM1LAH 0xac +#define PCIX0_PIM2SAL 0xb0 +#define PCIX0_PIM2LAL 0xb4 +#define PCIX0_PIM2LAH 0xb8 +#define PCIX0_PIM0SAH 0xf8 +#define PCIX0_PIM2SAH 0xfc + +#define PCIX0_STS 0xe0 + +#define PCI_ALL_SIZE (PPC440_REG_BASE + PPC440_REG_SIZE) + +static void ppc440_pcix_clear_region(MemoryRegion *parent, + MemoryRegion *mem) +{ + if (memory_region_is_mapped(mem)) { + memory_region_del_subregion(parent, mem); + object_unparent(OBJECT(mem)); + } +} + +/* DMA mapping */ +static void ppc440_pcix_update_pim(PPC440PCIXState *s, int idx) +{ + MemoryRegion *mem = &s->pim[idx].mr; + char *name; + uint64_t size; + + /* Before we modify anything, unmap and destroy the region */ + ppc440_pcix_clear_region(&s->bm, mem); + + if (!(s->pim[idx].sa & 1)) { + /* Not enabled, nothing to do */ + return; + } + + name = g_strdup_printf("PCI Inbound Window %d", idx); + size = ~(s->pim[idx].sa & ~7ULL) + 1; + memory_region_init_alias(mem, OBJECT(s), name, get_system_memory(), + s->pim[idx].la, size); + memory_region_add_subregion_overlap(&s->bm, 0, mem, -1); + g_free(name); + + trace_ppc440_pcix_update_pim(idx, size, s->pim[idx].la); +} + +/* BAR mapping */ +static void ppc440_pcix_update_pom(PPC440PCIXState *s, int idx) +{ + MemoryRegion *mem = &s->pom[idx].mr; + MemoryRegion *address_space_mem = get_system_memory(); + char *name; + uint32_t size; + + /* Before we modify anything, unmap and destroy the region */ + ppc440_pcix_clear_region(address_space_mem, mem); + + if (!(s->pom[idx].sa & 1)) { + /* Not enabled, nothing to do */ + return; + } + + name = g_strdup_printf("PCI Outbound Window %d", idx); + size = ~(s->pom[idx].sa & 0xfffffffe) + 1; + if (!size) { + size = 0xffffffff; + } + memory_region_init_alias(mem, OBJECT(s), name, &s->busmem, + s->pom[idx].pcia, size); + memory_region_add_subregion(address_space_mem, s->pom[idx].la, mem); + g_free(name); + + trace_ppc440_pcix_update_pom(idx, size, s->pom[idx].la, s->pom[idx].pcia); +} + +static void ppc440_pcix_reg_write4(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + struct PPC440PCIXState *s = opaque; + + trace_ppc440_pcix_reg_read(addr, val); + switch (addr) { + case PCI_VENDOR_ID ... PCI_MAX_LAT: + stl_le_p(s->dev->config + addr, val); + break; + + case PCIX0_POM0LAL: + s->pom[0].la &= 0xffffffff00000000ULL; + s->pom[0].la |= val; + ppc440_pcix_update_pom(s, 0); + break; + case PCIX0_POM0LAH: + s->pom[0].la &= 0xffffffffULL; + s->pom[0].la |= val << 32; + ppc440_pcix_update_pom(s, 0); + break; + case PCIX0_POM0SA: + s->pom[0].sa = val; + ppc440_pcix_update_pom(s, 0); + break; + case PCIX0_POM0PCIAL: + s->pom[0].pcia &= 0xffffffff00000000ULL; + s->pom[0].pcia |= val; + ppc440_pcix_update_pom(s, 0); + break; + case PCIX0_POM0PCIAH: + s->pom[0].pcia &= 0xffffffffULL; + s->pom[0].pcia |= val << 32; + ppc440_pcix_update_pom(s, 0); + break; + case PCIX0_POM1LAL: + s->pom[1].la &= 0xffffffff00000000ULL; + s->pom[1].la |= val; + ppc440_pcix_update_pom(s, 1); + break; + case PCIX0_POM1LAH: + s->pom[1].la &= 0xffffffffULL; + s->pom[1].la |= val << 32; + ppc440_pcix_update_pom(s, 1); + break; + case PCIX0_POM1SA: + s->pom[1].sa = val; + ppc440_pcix_update_pom(s, 1); + break; + case PCIX0_POM1PCIAL: + s->pom[1].pcia &= 0xffffffff00000000ULL; + s->pom[1].pcia |= val; + ppc440_pcix_update_pom(s, 1); + break; + case PCIX0_POM1PCIAH: + s->pom[1].pcia &= 0xffffffffULL; + s->pom[1].pcia |= val << 32; + ppc440_pcix_update_pom(s, 1); + break; + case PCIX0_POM2SA: + s->pom[2].sa = val; + break; + + case PCIX0_PIM0SAL: + s->pim[0].sa &= 0xffffffff00000000ULL; + s->pim[0].sa |= val; + ppc440_pcix_update_pim(s, 0); + break; + case PCIX0_PIM0LAL: + s->pim[0].la &= 0xffffffff00000000ULL; + s->pim[0].la |= val; + ppc440_pcix_update_pim(s, 0); + break; + case PCIX0_PIM0LAH: + s->pim[0].la &= 0xffffffffULL; + s->pim[0].la |= val << 32; + ppc440_pcix_update_pim(s, 0); + break; + case PCIX0_PIM1SA: + s->pim[1].sa = val; + ppc440_pcix_update_pim(s, 1); + break; + case PCIX0_PIM1LAL: + s->pim[1].la &= 0xffffffff00000000ULL; + s->pim[1].la |= val; + ppc440_pcix_update_pim(s, 1); + break; + case PCIX0_PIM1LAH: + s->pim[1].la &= 0xffffffffULL; + s->pim[1].la |= val << 32; + ppc440_pcix_update_pim(s, 1); + break; + case PCIX0_PIM2SAL: + s->pim[2].sa &= 0xffffffff00000000ULL; + s->pim[2].sa = val; + ppc440_pcix_update_pim(s, 2); + break; + case PCIX0_PIM2LAL: + s->pim[2].la &= 0xffffffff00000000ULL; + s->pim[2].la |= val; + ppc440_pcix_update_pim(s, 2); + break; + case PCIX0_PIM2LAH: + s->pim[2].la &= 0xffffffffULL; + s->pim[2].la |= val << 32; + ppc440_pcix_update_pim(s, 2); + break; + + case PCIX0_STS: + s->sts = val; + break; + + case PCIX0_PIM0SAH: + s->pim[0].sa &= 0xffffffffULL; + s->pim[0].sa |= val << 32; + ppc440_pcix_update_pim(s, 0); + break; + case PCIX0_PIM2SAH: + s->pim[2].sa &= 0xffffffffULL; + s->pim[2].sa |= val << 32; + ppc440_pcix_update_pim(s, 2); + break; + + default: + error_report("%s: unhandled PCI internal register 0x%lx", __func__, + (unsigned long)addr); + break; + } +} + +static uint64_t ppc440_pcix_reg_read4(void *opaque, hwaddr addr, + unsigned size) +{ + struct PPC440PCIXState *s = opaque; + uint32_t val; + + switch (addr) { + case PCI_VENDOR_ID ... PCI_MAX_LAT: + val = ldl_le_p(s->dev->config + addr); + break; + + case PCIX0_POM0LAL: + val = s->pom[0].la; + break; + case PCIX0_POM0LAH: + val = s->pom[0].la >> 32; + break; + case PCIX0_POM0SA: + val = s->pom[0].sa; + break; + case PCIX0_POM0PCIAL: + val = s->pom[0].pcia; + break; + case PCIX0_POM0PCIAH: + val = s->pom[0].pcia >> 32; + break; + case PCIX0_POM1LAL: + val = s->pom[1].la; + break; + case PCIX0_POM1LAH: + val = s->pom[1].la >> 32; + break; + case PCIX0_POM1SA: + val = s->pom[1].sa; + break; + case PCIX0_POM1PCIAL: + val = s->pom[1].pcia; + break; + case PCIX0_POM1PCIAH: + val = s->pom[1].pcia >> 32; + break; + case PCIX0_POM2SA: + val = s->pom[2].sa; + break; + + case PCIX0_PIM0SAL: + val = s->pim[0].sa; + break; + case PCIX0_PIM0LAL: + val = s->pim[0].la; + break; + case PCIX0_PIM0LAH: + val = s->pim[0].la >> 32; + break; + case PCIX0_PIM1SA: + val = s->pim[1].sa; + break; + case PCIX0_PIM1LAL: + val = s->pim[1].la; + break; + case PCIX0_PIM1LAH: + val = s->pim[1].la >> 32; + break; + case PCIX0_PIM2SAL: + val = s->pim[2].sa; + break; + case PCIX0_PIM2LAL: + val = s->pim[2].la; + break; + case PCIX0_PIM2LAH: + val = s->pim[2].la >> 32; + break; + + case PCIX0_STS: + val = s->sts; + break; + + case PCIX0_PIM0SAH: + val = s->pim[0].sa >> 32; + break; + case PCIX0_PIM2SAH: + val = s->pim[2].sa >> 32; + break; + + default: + error_report("%s: invalid PCI internal register 0x%lx", __func__, + (unsigned long)addr); + val = 0; + } + + trace_ppc440_pcix_reg_read(addr, val); + return val; +} + +static const MemoryRegionOps pci_reg_ops = { + .read = ppc440_pcix_reg_read4, + .write = ppc440_pcix_reg_write4, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void ppc440_pcix_reset(DeviceState *dev) +{ + struct PPC440PCIXState *s = PPC440_PCIX_HOST_BRIDGE(dev); + int i; + + for (i = 0; i < PPC440_PCIX_NR_POMS; i++) { + ppc440_pcix_clear_region(get_system_memory(), &s->pom[i].mr); + } + for (i = 0; i < PPC440_PCIX_NR_PIMS; i++) { + ppc440_pcix_clear_region(&s->bm, &s->pim[i].mr); + } + memset(s->pom, 0, sizeof(s->pom)); + memset(s->pim, 0, sizeof(s->pim)); + for (i = 0; i < PPC440_PCIX_NR_PIMS; i++) { + s->pim[i].sa = 0xffffffff00000000ULL; + } + s->sts = 0; +} + +/* All pins from each slot are tied to a single board IRQ. + * This may need further refactoring for other boards. */ +static int ppc440_pcix_map_irq(PCIDevice *pci_dev, int irq_num) +{ + int slot = pci_dev->devfn >> 3; + trace_ppc440_pcix_map_irq(pci_dev->devfn, irq_num, slot); + return slot - 1; +} + +static void ppc440_pcix_set_irq(void *opaque, int irq_num, int level) +{ + qemu_irq *pci_irqs = opaque; + + trace_ppc440_pcix_set_irq(irq_num); + if (irq_num < 0) { + error_report("%s: PCI irq %d", __func__, irq_num); + return; + } + qemu_set_irq(pci_irqs[irq_num], level); +} + +static AddressSpace *ppc440_pcix_set_iommu(PCIBus *b, void *opaque, int devfn) +{ + PPC440PCIXState *s = opaque; + + return &s->bm_as; +} + +/* The default pci_host_data_{read,write} functions in pci/pci_host.c + * deny access to registers without bit 31 set but our clients want + * this to work so we have to override these here */ +static void pci_host_data_write(void *opaque, hwaddr addr, + uint64_t val, unsigned len) +{ + PCIHostState *s = opaque; + pci_data_write(s->bus, s->config_reg | (addr & 3), val, len); +} + +static uint64_t pci_host_data_read(void *opaque, + hwaddr addr, unsigned len) +{ + PCIHostState *s = opaque; + uint32_t val; + val = pci_data_read(s->bus, s->config_reg | (addr & 3), len); + return val; +} + +const MemoryRegionOps ppc440_pcix_host_data_ops = { + .read = pci_host_data_read, + .write = pci_host_data_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static int ppc440_pcix_initfn(SysBusDevice *dev) +{ + PPC440PCIXState *s; + PCIHostState *h; + int i; + + h = PCI_HOST_BRIDGE(dev); + s = PPC440_PCIX_HOST_BRIDGE(dev); + + for (i = 0; i < ARRAY_SIZE(s->irq); i++) { + sysbus_init_irq(dev, &s->irq[i]); + } + + memory_region_init(&s->busmem, OBJECT(dev), "pci bus memory", UINT64_MAX); + h->bus = pci_register_root_bus(DEVICE(dev), NULL, ppc440_pcix_set_irq, + ppc440_pcix_map_irq, s->irq, &s->busmem, + get_system_io(), PCI_DEVFN(0, 0), 4, TYPE_PCI_BUS); + + s->dev = pci_create_simple(h->bus, PCI_DEVFN(0, 0), "ppc4xx-host-bridge"); + + memory_region_init(&s->bm, OBJECT(s), "bm-ppc440-pcix", UINT64_MAX); + memory_region_add_subregion(&s->bm, 0x0, &s->busmem); + address_space_init(&s->bm_as, &s->bm, "pci-bm"); + pci_setup_iommu(h->bus, ppc440_pcix_set_iommu, s); + + memory_region_init(&s->container, OBJECT(s), "pci-container", PCI_ALL_SIZE); + memory_region_init_io(&h->conf_mem, OBJECT(s), &pci_host_conf_le_ops, + h, "pci-conf-idx", 4); + memory_region_init_io(&h->data_mem, OBJECT(s), &ppc440_pcix_host_data_ops, + h, "pci-conf-data", 4); + memory_region_init_io(&s->iomem, OBJECT(s), &pci_reg_ops, s, + "pci.reg", PPC440_REG_SIZE); + memory_region_add_subregion(&s->container, PCIC0_CFGADDR, &h->conf_mem); + memory_region_add_subregion(&s->container, PCIC0_CFGDATA, &h->data_mem); + memory_region_add_subregion(&s->container, PPC440_REG_BASE, &s->iomem); + sysbus_init_mmio(dev, &s->container); + + return 0; +} + +static void ppc440_pcix_class_init(ObjectClass *klass, void *data) +{ + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + + k->init = ppc440_pcix_initfn; + dc->reset = ppc440_pcix_reset; +} + +static const TypeInfo ppc440_pcix_info = { + .name = TYPE_PPC440_PCIX_HOST_BRIDGE, + .parent = TYPE_PCI_HOST_BRIDGE, + .instance_size = sizeof(PPC440PCIXState), + .class_init = ppc440_pcix_class_init, +}; + +static void ppc440_pcix_register_types(void) +{ + type_register_static(&ppc440_pcix_info); +} + +type_init(ppc440_pcix_register_types) diff --git a/hw/ppc/ppc440_uc.c b/hw/ppc/ppc440_uc.c index 4e2523a64f..976ab2b5d8 100644 --- a/hw/ppc/ppc440_uc.c +++ b/hw/ppc/ppc440_uc.c @@ -1050,6 +1050,9 @@ static void ppc460ex_pcie_realize(DeviceState *dev, Error **errp) case DCRN_PCIE1_BASE: id = 1; break; + default: + error_setg(errp, "invalid PCIe DCRN base"); + return; } snprintf(buf, sizeof(buf), "pcie%d-io", id); memory_region_init(&s->iomem, OBJECT(s), buf, UINT64_MAX); diff --git a/hw/ppc/sam460ex.c b/hw/ppc/sam460ex.c new file mode 100644 index 0000000000..70b8e76d9c --- /dev/null +++ b/hw/ppc/sam460ex.c @@ -0,0 +1,603 @@ +/* + * QEMU aCube Sam460ex board emulation + * + * Copyright (c) 2012 François Revol + * Copyright (c) 2016-2018 BALATON Zoltan + * + * This file is derived from hw/ppc440_bamboo.c, + * the copyright for that material belongs to the original owners. + * + * This work is licensed under the GNU GPL license version 2 or later. + * + */ + +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "qemu/cutils.h" +#include "qemu/error-report.h" +#include "qapi/error.h" +#include "hw/hw.h" +#include "sysemu/blockdev.h" +#include "hw/boards.h" +#include "sysemu/kvm.h" +#include "kvm_ppc.h" +#include "sysemu/device_tree.h" +#include "sysemu/block-backend.h" +#include "hw/loader.h" +#include "elf.h" +#include "exec/address-spaces.h" +#include "exec/memory.h" +#include "hw/ppc/ppc440.h" +#include "hw/ppc/ppc405.h" +#include "hw/block/flash.h" +#include "sysemu/sysemu.h" +#include "sysemu/qtest.h" +#include "hw/sysbus.h" +#include "hw/char/serial.h" +#include "hw/i2c/ppc4xx_i2c.h" +#include "hw/i2c/smbus.h" +#include "hw/usb/hcd-ehci.h" + +#define BINARY_DEVICE_TREE_FILE "canyonlands.dtb" +#define UBOOT_FILENAME "u-boot-sam460-20100605.bin" +/* to extract the official U-Boot bin from the updater: */ +/* dd bs=1 skip=$(($(stat -c '%s' updater/updater-460) - 0x80000)) \ + if=updater/updater-460 of=u-boot-sam460-20100605.bin */ + +/* from Sam460 U-Boot include/configs/Sam460ex.h */ +#define FLASH_BASE 0xfff00000 +#define FLASH_BASE_H 0x4 +#define FLASH_SIZE (1 << 20) +#define UBOOT_LOAD_BASE 0xfff80000 +#define UBOOT_SIZE 0x00080000 +#define UBOOT_ENTRY 0xfffffffc + +/* from U-Boot */ +#define EPAPR_MAGIC (0x45504150) +#define KERNEL_ADDR 0x1000000 +#define FDT_ADDR 0x1800000 +#define RAMDISK_ADDR 0x1900000 + +/* Sam460ex IRQ MAP: + IRQ0 = ETH_INT + IRQ1 = FPGA_INT + IRQ2 = PCI_INT (PCIA, PCIB, PCIC, PCIB) + IRQ3 = FPGA_INT2 + IRQ11 = RTC_INT + IRQ12 = SM502_INT +*/ + +#define SDRAM_NR_BANKS 4 + +/* FIXME: See u-boot.git 8ac41e, also fix in ppc440_uc.c */ +static const unsigned int ppc460ex_sdram_bank_sizes[] = { + 1024 << 20, 512 << 20, 256 << 20, 128 << 20, 64 << 20, 32 << 20, 0 +}; + +struct boot_info { + uint32_t dt_base; + uint32_t dt_size; + uint32_t entry; +}; + +/*****************************************************************************/ +/* SPD eeprom content from mips_malta.c */ + +struct _eeprom24c0x_t { + uint8_t tick; + uint8_t address; + uint8_t command; + uint8_t ack; + uint8_t scl; + uint8_t sda; + uint8_t data; + uint8_t contents[256]; +}; + +typedef struct _eeprom24c0x_t eeprom24c0x_t; + +static eeprom24c0x_t spd_eeprom = { + .contents = { + /* 00000000: */ 0x80, 0x08, 0xFF, 0x0D, 0x0A, 0xFF, 0x40, 0x00, + /* 00000008: */ 0x04, 0x75, 0x54, 0x00, 0x82, 0x08, 0x00, 0x01, + /* 00000010: */ 0x8F, 0x04, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, + /* 00000018: */ 0x00, 0x00, 0x00, 0x14, 0x0F, 0x14, 0x2D, 0xFF, + /* 00000020: */ 0x15, 0x08, 0x15, 0x08, 0x00, 0x00, 0x00, 0x00, + /* 00000028: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 00000030: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 00000038: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0xD0, + /* 00000040: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 00000048: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 00000050: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 00000058: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 00000060: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 00000068: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 00000070: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 00000078: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0xF4, + }, +}; + +static void generate_eeprom_spd(uint8_t *eeprom, ram_addr_t ram_size) +{ + enum { SDR = 0x4, DDR1 = 0x7, DDR2 = 0x8 } type; + uint8_t *spd = spd_eeprom.contents; + uint8_t nbanks = 0; + uint16_t density = 0; + int i; + + /* work in terms of MB */ + ram_size >>= 20; + + while ((ram_size >= 4) && (nbanks <= 2)) { + int sz_log2 = MIN(31 - clz32(ram_size), 14); + nbanks++; + density |= 1 << (sz_log2 - 2); + ram_size -= 1 << sz_log2; + } + + /* split to 2 banks if possible */ + if ((nbanks == 1) && (density > 1)) { + nbanks++; + density >>= 1; + } + + if (density & 0xff00) { + density = (density & 0xe0) | ((density >> 8) & 0x1f); + type = DDR2; + } else if (!(density & 0x1f)) { + type = DDR2; + } else { + type = SDR; + } + + if (ram_size) { + warn_report("SPD cannot represent final " RAM_ADDR_FMT "MB" + " of SDRAM", ram_size); + } + + /* fill in SPD memory information */ + spd[2] = type; + spd[5] = nbanks; + spd[31] = density; + + /* XXX: this is totally random */ + spd[9] = 0x10; /* CAS tcyc */ + spd[18] = 0x20; /* CAS bit */ + spd[23] = 0x10; /* CAS tcyc */ + spd[25] = 0x10; /* CAS tcyc */ + + /* checksum */ + spd[63] = 0; + for (i = 0; i < 63; i++) { + spd[63] += spd[i]; + } + + /* copy for SMBUS */ + memcpy(eeprom, spd, sizeof(spd_eeprom.contents)); +} + +static void generate_eeprom_serial(uint8_t *eeprom) +{ + int i, pos = 0; + uint8_t mac[6] = { 0x00 }; + uint8_t sn[5] = { 0x01, 0x23, 0x45, 0x67, 0x89 }; + + /* version */ + eeprom[pos++] = 0x01; + + /* count */ + eeprom[pos++] = 0x02; + + /* MAC address */ + eeprom[pos++] = 0x01; /* MAC */ + eeprom[pos++] = 0x06; /* length */ + memcpy(&eeprom[pos], mac, sizeof(mac)); + pos += sizeof(mac); + + /* serial number */ + eeprom[pos++] = 0x02; /* serial */ + eeprom[pos++] = 0x05; /* length */ + memcpy(&eeprom[pos], sn, sizeof(sn)); + pos += sizeof(sn); + + /* checksum */ + eeprom[pos] = 0; + for (i = 0; i < pos; i++) { + eeprom[pos] += eeprom[i]; + } +} + +/*****************************************************************************/ + +static int sam460ex_load_uboot(void) +{ + DriveInfo *dinfo; + BlockBackend *blk = NULL; + hwaddr base = FLASH_BASE | ((hwaddr)FLASH_BASE_H << 32); + long bios_size = FLASH_SIZE; + int fl_sectors; + + dinfo = drive_get(IF_PFLASH, 0, 0); + if (dinfo) { + blk = blk_by_legacy_dinfo(dinfo); + bios_size = blk_getlength(blk); + } + fl_sectors = (bios_size + 65535) >> 16; + + if (!pflash_cfi01_register(base, NULL, "sam460ex.flash", bios_size, + blk, (64 * 1024), fl_sectors, + 1, 0x89, 0x18, 0x0000, 0x0, 1)) { + error_report("qemu: Error registering flash memory."); + /* XXX: return an error instead? */ + exit(1); + } + + if (!blk) { + /*error_report("No flash image given with the 'pflash' parameter," + " using default u-boot image");*/ + base = UBOOT_LOAD_BASE | ((hwaddr)FLASH_BASE_H << 32); + rom_add_file_fixed(UBOOT_FILENAME, base, -1); + } + + return 0; +} + +static int sam460ex_load_device_tree(hwaddr addr, + uint32_t ramsize, + hwaddr initrd_base, + hwaddr initrd_size, + const char *kernel_cmdline) +{ + int ret = -1; + uint32_t mem_reg_property[] = { 0, 0, cpu_to_be32(ramsize) }; + char *filename; + int fdt_size; + void *fdt; + uint32_t tb_freq = 50000000; + uint32_t clock_freq = 50000000; + + filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE); + if (!filename) { + goto out; + } + fdt = load_device_tree(filename, &fdt_size); + g_free(filename); + if (fdt == NULL) { + goto out; + } + + /* Manipulate device tree in memory. */ + + ret = qemu_fdt_setprop(fdt, "/memory", "reg", mem_reg_property, + sizeof(mem_reg_property)); + if (ret < 0) { + error_report("couldn't set /memory/reg"); + } + + /* default FDT doesn't have a /chosen node... */ + qemu_fdt_add_subnode(fdt, "/chosen"); + + ret = qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-start", + initrd_base); + if (ret < 0) { + error_report("couldn't set /chosen/linux,initrd-start"); + } + + ret = qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-end", + (initrd_base + initrd_size)); + if (ret < 0) { + error_report("couldn't set /chosen/linux,initrd-end"); + } + + ret = qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", + kernel_cmdline); + if (ret < 0) { + error_report("couldn't set /chosen/bootargs"); + } + + /* Copy data from the host device tree into the guest. Since the guest can + * directly access the timebase without host involvement, we must expose + * the correct frequencies. */ + if (kvm_enabled()) { + tb_freq = kvmppc_get_tbfreq(); + clock_freq = kvmppc_get_clockfreq(); + } + + qemu_fdt_setprop_cell(fdt, "/cpus/cpu@0", "clock-frequency", + clock_freq); + qemu_fdt_setprop_cell(fdt, "/cpus/cpu@0", "timebase-frequency", + tb_freq); + + rom_add_blob_fixed(BINARY_DEVICE_TREE_FILE, fdt, fdt_size, addr); + g_free(fdt); + ret = fdt_size; + +out: + + return ret; +} + +/* Create reset TLB entries for BookE, mapping only the flash memory. */ +static void mmubooke_create_initial_mapping_uboot(CPUPPCState *env) +{ + ppcemb_tlb_t *tlb = &env->tlb.tlbe[0]; + + /* on reset the flash is mapped by a shadow TLB, + * but since we don't implement them we need to use + * the same values U-Boot will use to avoid a fault. + */ + tlb->attr = 0; + tlb->prot = PAGE_VALID | ((PAGE_READ | PAGE_WRITE | PAGE_EXEC) << 4); + tlb->size = 0x10000000; /* up to 0xffffffff */ + tlb->EPN = 0xf0000000 & TARGET_PAGE_MASK; + tlb->RPN = (0xf0000000 & TARGET_PAGE_MASK) | 0x4; + tlb->PID = 0; +} + +/* Create reset TLB entries for BookE, spanning the 32bit addr space. */ +static void mmubooke_create_initial_mapping(CPUPPCState *env, + target_ulong va, + hwaddr pa) +{ + ppcemb_tlb_t *tlb = &env->tlb.tlbe[0]; + + tlb->attr = 0; + tlb->prot = PAGE_VALID | ((PAGE_READ | PAGE_WRITE | PAGE_EXEC) << 4); + tlb->size = 1 << 31; /* up to 0x80000000 */ + tlb->EPN = va & TARGET_PAGE_MASK; + tlb->RPN = pa & TARGET_PAGE_MASK; + tlb->PID = 0; +} + +static void main_cpu_reset(void *opaque) +{ + PowerPCCPU *cpu = opaque; + CPUPPCState *env = &cpu->env; + struct boot_info *bi = env->load_info; + + cpu_reset(CPU(cpu)); + + /* either we have a kernel to boot or we jump to U-Boot */ + if (bi->entry != UBOOT_ENTRY) { + env->gpr[1] = (16 << 20) - 8; + env->gpr[3] = FDT_ADDR; + env->nip = bi->entry; + + /* Create a mapping for the kernel. */ + mmubooke_create_initial_mapping(env, 0, 0); + env->gpr[6] = tswap32(EPAPR_MAGIC); + env->gpr[7] = (16 << 20) - 8; /*bi->ima_size;*/ + + } else { + env->nip = UBOOT_ENTRY; + mmubooke_create_initial_mapping_uboot(env); + } +} + +static void sam460ex_init(MachineState *machine) +{ + MemoryRegion *address_space_mem = get_system_memory(); + MemoryRegion *isa = g_new(MemoryRegion, 1); + MemoryRegion *ram_memories = g_new(MemoryRegion, SDRAM_NR_BANKS); + hwaddr ram_bases[SDRAM_NR_BANKS]; + hwaddr ram_sizes[SDRAM_NR_BANKS]; + MemoryRegion *l2cache_ram = g_new(MemoryRegion, 1); + qemu_irq *irqs, *uic[4]; + PCIBus *pci_bus; + PowerPCCPU *cpu; + CPUPPCState *env; + PPC4xxI2CState *i2c[2]; + hwaddr entry = UBOOT_ENTRY; + hwaddr loadaddr = 0; + target_long initrd_size = 0; + DeviceState *dev; + SysBusDevice *sbdev; + int success; + int i; + struct boot_info *boot_info; + const size_t smbus_eeprom_size = 8 * 256; + uint8_t *smbus_eeprom_buf = g_malloc0(smbus_eeprom_size); + + cpu = POWERPC_CPU(cpu_create(machine->cpu_type)); + env = &cpu->env; + if (env->mmu_model != POWERPC_MMU_BOOKE) { + error_report("Only MMU model BookE is supported by this machine."); + exit(1); + } + +#ifdef TARGET_PPCEMB + if (!qtest_enabled()) { + warn_report("qemu-system-ppcemb is deprecated, " + "please use qemu-system-ppc instead."); + } +#endif + + qemu_register_reset(main_cpu_reset, cpu); + boot_info = g_malloc0(sizeof(*boot_info)); + env->load_info = boot_info; + + ppc_booke_timers_init(cpu, 50000000, 0); + ppc_dcr_init(env, NULL, NULL); + + /* PLB arbitrer */ + ppc4xx_plb_init(env); + + /* interrupt controllers */ + irqs = g_malloc0(sizeof(*irqs) * PPCUIC_OUTPUT_NB); + irqs[PPCUIC_OUTPUT_INT] = ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_INT]; + irqs[PPCUIC_OUTPUT_CINT] = ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_CINT]; + uic[0] = ppcuic_init(env, irqs, 0xc0, 0, 1); + uic[1] = ppcuic_init(env, &uic[0][30], 0xd0, 0, 1); + uic[2] = ppcuic_init(env, &uic[0][10], 0xe0, 0, 1); + uic[3] = ppcuic_init(env, &uic[0][16], 0xf0, 0, 1); + + /* SDRAM controller */ + memset(ram_bases, 0, sizeof(ram_bases)); + memset(ram_sizes, 0, sizeof(ram_sizes)); + /* put all RAM on first bank because board has one slot + * and firmware only checks that */ + machine->ram_size = ppc4xx_sdram_adjust(machine->ram_size, 1, + ram_memories, ram_bases, ram_sizes, + ppc460ex_sdram_bank_sizes); + + /* FIXME: does 460EX have ECC interrupts? */ + ppc440_sdram_init(env, SDRAM_NR_BANKS, ram_memories, + ram_bases, ram_sizes, 1); + + /* generate SPD EEPROM data */ + for (i = 0; i < SDRAM_NR_BANKS; i++) { + generate_eeprom_spd(&smbus_eeprom_buf[i * 256], ram_sizes[i]); + } + generate_eeprom_serial(&smbus_eeprom_buf[4 * 256]); + generate_eeprom_serial(&smbus_eeprom_buf[6 * 256]); + + /* IIC controllers */ + dev = sysbus_create_simple(TYPE_PPC4xx_I2C, 0x4ef600700, uic[0][2]); + i2c[0] = PPC4xx_I2C(dev); + object_property_set_bool(OBJECT(dev), true, "realized", NULL); + smbus_eeprom_init(i2c[0]->bus, 8, smbus_eeprom_buf, smbus_eeprom_size); + g_free(smbus_eeprom_buf); + + dev = sysbus_create_simple(TYPE_PPC4xx_I2C, 0x4ef600800, uic[0][3]); + i2c[1] = PPC4xx_I2C(dev); + + /* External bus controller */ + ppc405_ebc_init(env); + + /* CPR */ + ppc4xx_cpr_init(env); + + /* PLB to AHB bridge */ + ppc4xx_ahb_init(env); + + /* System DCRs */ + ppc4xx_sdr_init(env); + + /* MAL */ + ppc4xx_mal_init(env, 4, 16, &uic[2][3]); + + /* 256K of L2 cache as memory */ + ppc4xx_l2sram_init(env); + /* FIXME: remove this after fixing l2sram mapping in ppc440_uc.c? */ + memory_region_init_ram(l2cache_ram, NULL, "ppc440.l2cache_ram", 256 << 10, + &error_abort); + memory_region_add_subregion(address_space_mem, 0x400000000LL, l2cache_ram); + + /* USB */ + sysbus_create_simple(TYPE_PPC4xx_EHCI, 0x4bffd0400, uic[2][29]); + dev = qdev_create(NULL, "sysbus-ohci"); + qdev_prop_set_string(dev, "masterbus", "usb-bus.0"); + qdev_prop_set_uint32(dev, "num-ports", 6); + qdev_init_nofail(dev); + sbdev = SYS_BUS_DEVICE(dev); + sysbus_mmio_map(sbdev, 0, 0x4bffd0000); + sysbus_connect_irq(sbdev, 0, uic[2][30]); + usb_create_simple(usb_bus_find(-1), "usb-kbd"); + usb_create_simple(usb_bus_find(-1), "usb-mouse"); + + /* PCI bus */ + ppc460ex_pcie_init(env); + /* FIXME: is this correct? */ + dev = sysbus_create_varargs("ppc440-pcix-host", 0xc0ec00000, + uic[1][0], uic[1][20], uic[1][21], uic[1][22], + NULL); + pci_bus = (PCIBus *)qdev_get_child_bus(dev, "pci.0"); + if (!pci_bus) { + error_report("couldn't create PCI controller!"); + exit(1); + } + memory_region_init_alias(isa, NULL, "isa_mmio", get_system_io(), + 0, 0x10000); + memory_region_add_subregion(get_system_memory(), 0xc08000000, isa); + + /* PCI devices */ + pci_create_simple(pci_bus, PCI_DEVFN(6, 0), "sm501"); + /* SoC has a single SATA port but we don't emulate that yet + * However, firmware and usual clients have driver for SiI311x + * so add one for convenience by default */ + if (defaults_enabled()) { + pci_create_simple(pci_bus, -1, "sii3112"); + } + + /* SoC has 4 UARTs + * but board has only one wired and two are present in fdt */ + if (serial_hds[0] != NULL) { + serial_mm_init(address_space_mem, 0x4ef600300, 0, uic[1][1], + PPC_SERIAL_MM_BAUDBASE, serial_hds[0], + DEVICE_BIG_ENDIAN); + } + if (serial_hds[1] != NULL) { + serial_mm_init(address_space_mem, 0x4ef600400, 0, uic[0][1], + PPC_SERIAL_MM_BAUDBASE, serial_hds[1], + DEVICE_BIG_ENDIAN); + } + + /* Load U-Boot image. */ + if (!machine->kernel_filename) { + success = sam460ex_load_uboot(); + if (success < 0) { + error_report("qemu: could not load firmware"); + exit(1); + } + } + + /* Load kernel. */ + if (machine->kernel_filename) { + success = load_uimage(machine->kernel_filename, &entry, &loadaddr, + NULL, NULL, NULL); + if (success < 0) { + uint64_t elf_entry, elf_lowaddr; + + success = load_elf(machine->kernel_filename, NULL, NULL, &elf_entry, + &elf_lowaddr, NULL, 1, PPC_ELF_MACHINE, 0, 0); + entry = elf_entry; + loadaddr = elf_lowaddr; + } + /* XXX try again as binary */ + if (success < 0) { + error_report("qemu: could not load kernel '%s'", + machine->kernel_filename); + exit(1); + } + } + + /* Load initrd. */ + if (machine->initrd_filename) { + initrd_size = load_image_targphys(machine->initrd_filename, + RAMDISK_ADDR, + machine->ram_size - RAMDISK_ADDR); + if (initrd_size < 0) { + error_report("qemu: could not load ram disk '%s' at %x", + machine->initrd_filename, RAMDISK_ADDR); + exit(1); + } + } + + /* If we're loading a kernel directly, we must load the device tree too. */ + if (machine->kernel_filename) { + int dt_size; + + dt_size = sam460ex_load_device_tree(FDT_ADDR, machine->ram_size, + RAMDISK_ADDR, initrd_size, + machine->kernel_cmdline); + if (dt_size < 0) { + error_report("couldn't load device tree"); + exit(1); + } + + boot_info->dt_base = FDT_ADDR; + boot_info->dt_size = dt_size; + } + + boot_info->entry = entry; +} + +static void sam460ex_machine_init(MachineClass *mc) +{ + mc->desc = "aCube Sam460ex"; + mc->init = sam460ex_init; + mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("460exb"); + mc->default_ram_size = 512 * M_BYTE; +} + +DEFINE_MACHINE("sam460ex", sam460ex_machine_init) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 83c9d66dd5..7e1c858566 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -105,12 +105,14 @@ */ static int spapr_vcpu_id(sPAPRMachineState *spapr, int cpu_index) { + assert(spapr->vsmt); return (cpu_index / smp_threads) * spapr->vsmt + cpu_index % smp_threads; } static bool spapr_is_thread0_in_vcore(sPAPRMachineState *spapr, PowerPCCPU *cpu) { + assert(spapr->vsmt); return spapr_get_vcpu_id(cpu) % spapr->vsmt == 0; } @@ -177,13 +179,13 @@ static void pre_2_10_vmstate_unregister_dummy_icp(int i) static int xics_max_server_number(sPAPRMachineState *spapr) { + assert(spapr->vsmt); return DIV_ROUND_UP(max_cpus * spapr->vsmt, smp_threads); } static void xics_system_init(MachineState *machine, int nr_irqs, Error **errp) { sPAPRMachineState *spapr = SPAPR_MACHINE(machine); - sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(machine); if (kvm_enabled()) { if (machine_kernel_irqchip_allowed(machine) && @@ -205,17 +207,6 @@ static void xics_system_init(MachineState *machine, int nr_irqs, Error **errp) return; } } - - if (smc->pre_2_10_has_unused_icps) { - int i; - - for (i = 0; i < xics_max_server_number(spapr); i++) { - /* Dummy entries get deregistered when real ICPState objects - * are registered during CPU core hotplug. - */ - pre_2_10_vmstate_register_dummy_icp(i); - } - } } static int spapr_fixup_cpu_smt_dt(void *fdt, int offset, PowerPCCPU *cpu, @@ -1062,7 +1053,14 @@ static void spapr_dt_chosen(sPAPRMachineState *spapr, void *fdt) } if (!spapr->has_graphics && stdout_path) { + /* + * "linux,stdout-path" and "stdout" properties are deprecated by linux + * kernel. New platforms should only use the "stdout-path" property. Set + * the new property and continue using older property to remain + * compatible with the existing firmware. + */ _FDT(fdt_setprop_string(fdt, chosen, "linux,stdout-path", stdout_path)); + _FDT(fdt_setprop_string(fdt, chosen, "stdout-path", stdout_path)); } spapr_dt_ov5_platform_support(fdt, chosen); @@ -2232,61 +2230,6 @@ static CPUArchId *spapr_find_cpu_slot(MachineState *ms, uint32_t id, int *idx) return &ms->possible_cpus->cpus[index]; } -static void spapr_init_cpus(sPAPRMachineState *spapr) -{ - MachineState *machine = MACHINE(spapr); - MachineClass *mc = MACHINE_GET_CLASS(machine); - const char *type = spapr_get_cpu_core_type(machine->cpu_type); - const CPUArchIdList *possible_cpus; - int boot_cores_nr = smp_cpus / smp_threads; - int i; - - possible_cpus = mc->possible_cpu_arch_ids(machine); - if (mc->has_hotpluggable_cpus) { - if (smp_cpus % smp_threads) { - error_report("smp_cpus (%u) must be multiple of threads (%u)", - smp_cpus, smp_threads); - exit(1); - } - if (max_cpus % smp_threads) { - error_report("max_cpus (%u) must be multiple of threads (%u)", - max_cpus, smp_threads); - exit(1); - } - } else { - if (max_cpus != smp_cpus) { - error_report("This machine version does not support CPU hotplug"); - exit(1); - } - boot_cores_nr = possible_cpus->len; - } - - for (i = 0; i < possible_cpus->len; i++) { - int core_id = i * smp_threads; - - if (mc->has_hotpluggable_cpus) { - spapr_dr_connector_new(OBJECT(spapr), TYPE_SPAPR_DRC_CPU, - spapr_vcpu_id(spapr, core_id)); - } - - if (i < boot_cores_nr) { - Object *core = object_new(type); - int nr_threads = smp_threads; - - /* Handle the partially filled core for older machine types */ - if ((i + 1) * smp_threads >= smp_cpus) { - nr_threads = smp_cpus - i * smp_threads; - } - - object_property_set_int(core, nr_threads, "nr-threads", - &error_fatal); - object_property_set_int(core, core_id, CPU_CORE_PROP_CORE_ID, - &error_fatal); - object_property_set_bool(core, true, "realized", &error_fatal); - } - } -} - static void spapr_set_vsmt_mode(sPAPRMachineState *spapr, Error **errp) { Error *local_err = NULL; @@ -2359,6 +2302,78 @@ out: error_propagate(errp, local_err); } +static void spapr_init_cpus(sPAPRMachineState *spapr) +{ + MachineState *machine = MACHINE(spapr); + MachineClass *mc = MACHINE_GET_CLASS(machine); + sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(machine); + const char *type = spapr_get_cpu_core_type(machine->cpu_type); + const CPUArchIdList *possible_cpus; + int boot_cores_nr = smp_cpus / smp_threads; + int i; + + possible_cpus = mc->possible_cpu_arch_ids(machine); + if (mc->has_hotpluggable_cpus) { + if (smp_cpus % smp_threads) { + error_report("smp_cpus (%u) must be multiple of threads (%u)", + smp_cpus, smp_threads); + exit(1); + } + if (max_cpus % smp_threads) { + error_report("max_cpus (%u) must be multiple of threads (%u)", + max_cpus, smp_threads); + exit(1); + } + } else { + if (max_cpus != smp_cpus) { + error_report("This machine version does not support CPU hotplug"); + exit(1); + } + boot_cores_nr = possible_cpus->len; + } + + /* VSMT must be set in order to be able to compute VCPU ids, ie to + * call xics_max_server_number() or spapr_vcpu_id(). + */ + spapr_set_vsmt_mode(spapr, &error_fatal); + + if (smc->pre_2_10_has_unused_icps) { + int i; + + for (i = 0; i < xics_max_server_number(spapr); i++) { + /* Dummy entries get deregistered when real ICPState objects + * are registered during CPU core hotplug. + */ + pre_2_10_vmstate_register_dummy_icp(i); + } + } + + for (i = 0; i < possible_cpus->len; i++) { + int core_id = i * smp_threads; + + if (mc->has_hotpluggable_cpus) { + spapr_dr_connector_new(OBJECT(spapr), TYPE_SPAPR_DRC_CPU, + spapr_vcpu_id(spapr, core_id)); + } + + if (i < boot_cores_nr) { + Object *core = object_new(type); + int nr_threads = smp_threads; + + /* Handle the partially filled core for older machine types */ + if ((i + 1) * smp_threads >= smp_cpus) { + nr_threads = smp_cpus - i * smp_threads; + } + + object_property_set_int(core, nr_threads, "nr-threads", + &error_fatal); + object_property_set_int(core, core_id, CPU_CORE_PROP_CORE_ID, + &error_fatal); + object_property_set_bool(core, true, "realized", &error_fatal); + } + } +} + /* pSeries LPAR / sPAPR hardware init */ static void spapr_machine_init(MachineState *machine) { @@ -2486,8 +2501,6 @@ static void spapr_machine_init(MachineState *machine) } /* init CPUs */ - spapr_set_vsmt_mode(spapr, &error_fatal); - spapr_init_cpus(spapr); if (kvm_enabled()) { @@ -3810,13 +3823,7 @@ static void spapr_pic_print_info(InterruptStatsProvider *obj, int spapr_get_vcpu_id(PowerPCCPU *cpu) { - CPUState *cs = CPU(cpu); - - if (kvm_enabled()) { - return kvm_arch_vcpu_id(cs); - } else { - return cs->cpu_index; - } + return cpu->vcpu_id; } void spapr_set_vcpu_id(PowerPCCPU *cpu, int cpu_index, Error **errp) @@ -3983,6 +3990,23 @@ static void spapr_machine_2_12_class_options(MachineClass *mc) DEFINE_SPAPR_MACHINE(2_12, "2.12", true); +static void spapr_machine_2_12_sxxm_instance_options(MachineState *machine) +{ + spapr_machine_2_12_instance_options(machine); +} + +static void spapr_machine_2_12_sxxm_class_options(MachineClass *mc) +{ + sPAPRMachineClass *smc = SPAPR_MACHINE_CLASS(mc); + + spapr_machine_2_12_class_options(mc); + smc->default_caps.caps[SPAPR_CAP_CFPC] = SPAPR_CAP_WORKAROUND; + smc->default_caps.caps[SPAPR_CAP_SBBC] = SPAPR_CAP_WORKAROUND; + smc->default_caps.caps[SPAPR_CAP_IBS] = SPAPR_CAP_FIXED_CCD; +} + +DEFINE_SPAPR_MACHINE(2_12_sxxm, "2.12-sxxm", false); + /* * pseries-2.11 */ diff --git a/hw/ppc/spapr_caps.c b/hw/ppc/spapr_caps.c index 99a4b71d19..531e145114 100644 --- a/hw/ppc/spapr_caps.c +++ b/hw/ppc/spapr_caps.c @@ -32,6 +32,20 @@ #include "hw/ppc/spapr.h" +typedef struct sPAPRCapPossible { + int num; /* size of vals array below */ + const char *help; /* help text for vals */ + /* + * Note: + * - because of the way compatibility is determined vals MUST be ordered + * such that later options are a superset of all preceding options. + * - the order of vals must be preserved, that is their index is important, + * however vals may be added to the end of the list so long as the above + * point is observed + */ + const char *vals[]; +} sPAPRCapPossible; + typedef struct sPAPRCapabilityInfo { const char *name; const char *description; @@ -41,6 +55,8 @@ typedef struct sPAPRCapabilityInfo { ObjectPropertyAccessor *get; ObjectPropertyAccessor *set; const char *type; + /* Possible values if this is a custom string type */ + sPAPRCapPossible *possible; /* Make sure the virtual hardware can support this capability */ void (*apply)(sPAPRMachineState *spapr, uint8_t val, Error **errp); } sPAPRCapabilityInfo; @@ -73,41 +89,34 @@ static void spapr_cap_set_bool(Object *obj, Visitor *v, const char *name, spapr->eff.caps[cap->index] = value ? SPAPR_CAP_ON : SPAPR_CAP_OFF; } -static void spapr_cap_get_tristate(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) + +static void spapr_cap_get_string(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { sPAPRCapabilityInfo *cap = opaque; sPAPRMachineState *spapr = SPAPR_MACHINE(obj); char *val = NULL; uint8_t value = spapr_get_cap(spapr, cap->index); - switch (value) { - case SPAPR_CAP_BROKEN: - val = g_strdup("broken"); - break; - case SPAPR_CAP_WORKAROUND: - val = g_strdup("workaround"); - break; - case SPAPR_CAP_FIXED: - val = g_strdup("fixed"); - break; - default: + if (value >= cap->possible->num) { error_setg(errp, "Invalid value (%d) for cap-%s", value, cap->name); return; } + val = g_strdup(cap->possible->vals[value]); + visit_type_str(v, name, &val, errp); g_free(val); } -static void spapr_cap_set_tristate(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) +static void spapr_cap_set_string(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { sPAPRCapabilityInfo *cap = opaque; sPAPRMachineState *spapr = SPAPR_MACHINE(obj); - char *val; Error *local_err = NULL; - uint8_t value; + uint8_t i; + char *val; visit_type_str(v, name, &val, &local_err); if (local_err) { @@ -115,20 +124,20 @@ static void spapr_cap_set_tristate(Object *obj, Visitor *v, const char *name, return; } - if (!strcasecmp(val, "broken")) { - value = SPAPR_CAP_BROKEN; - } else if (!strcasecmp(val, "workaround")) { - value = SPAPR_CAP_WORKAROUND; - } else if (!strcasecmp(val, "fixed")) { - value = SPAPR_CAP_FIXED; - } else { - error_setg(errp, "Invalid capability mode \"%s\" for cap-%s", val, - cap->name); + if (!strcmp(val, "?")) { + error_setg(errp, "%s", cap->possible->help); goto out; } + for (i = 0; i < cap->possible->num; i++) { + if (!strcasecmp(val, cap->possible->vals[i])) { + spapr->cmd_line_caps[cap->index] = true; + spapr->eff.caps[cap->index] = i; + goto out; + } + } - spapr->cmd_line_caps[cap->index] = true; - spapr->eff.caps[cap->index] = value; + error_setg(errp, "Invalid capability mode \"%s\" for cap-%s", val, + cap->name); out: g_free(val); } @@ -180,38 +189,77 @@ static void cap_dfp_apply(sPAPRMachineState *spapr, uint8_t val, Error **errp) } } +sPAPRCapPossible cap_cfpc_possible = { + .num = 3, + .vals = {"broken", "workaround", "fixed"}, + .help = "broken - no protection, workaround - workaround available," + " fixed - fixed in hardware", +}; + static void cap_safe_cache_apply(sPAPRMachineState *spapr, uint8_t val, Error **errp) { + uint8_t kvm_val = kvmppc_get_cap_safe_cache(); + if (tcg_enabled() && val) { /* TODO - for now only allow broken for TCG */ - error_setg(errp, "Requested safe cache capability level not supported by tcg, try a different value for cap-cfpc"); - } else if (kvm_enabled() && (val > kvmppc_get_cap_safe_cache())) { - error_setg(errp, "Requested safe cache capability level not supported by kvm, try a different value for cap-cfpc"); + error_setg(errp, +"Requested safe cache capability level not supported by tcg, try a different value for cap-cfpc"); + } else if (kvm_enabled() && (val > kvm_val)) { + error_setg(errp, +"Requested safe cache capability level not supported by kvm, try cap-cfpc=%s", + cap_cfpc_possible.vals[kvm_val]); } } +sPAPRCapPossible cap_sbbc_possible = { + .num = 3, + .vals = {"broken", "workaround", "fixed"}, + .help = "broken - no protection, workaround - workaround available," + " fixed - fixed in hardware", +}; + static void cap_safe_bounds_check_apply(sPAPRMachineState *spapr, uint8_t val, Error **errp) { + uint8_t kvm_val = kvmppc_get_cap_safe_bounds_check(); + if (tcg_enabled() && val) { /* TODO - for now only allow broken for TCG */ - error_setg(errp, "Requested safe bounds check capability level not supported by tcg, try a different value for cap-sbbc"); - } else if (kvm_enabled() && (val > kvmppc_get_cap_safe_bounds_check())) { - error_setg(errp, "Requested safe bounds check capability level not supported by kvm, try a different value for cap-sbbc"); + error_setg(errp, +"Requested safe bounds check capability level not supported by tcg, try a different value for cap-sbbc"); + } else if (kvm_enabled() && (val > kvm_val)) { + error_setg(errp, +"Requested safe bounds check capability level not supported by kvm, try cap-sbbc=%s", + cap_sbbc_possible.vals[kvm_val]); } } +sPAPRCapPossible cap_ibs_possible = { + .num = 4, + /* Note workaround only maintained for compatibility */ + .vals = {"broken", "workaround", "fixed-ibs", "fixed-ccd"}, + .help = "broken - no protection, fixed-ibs - indirect branch serialisation," + " fixed-ccd - cache count disabled", +}; + static void cap_safe_indirect_branch_apply(sPAPRMachineState *spapr, uint8_t val, Error **errp) { + uint8_t kvm_val = kvmppc_get_cap_safe_indirect_branch(); + if (val == SPAPR_CAP_WORKAROUND) { /* Can only be Broken or Fixed */ - error_setg(errp, "Requested safe indirect branch capability level \"workaround\" not valid, try cap-ibs=fixed"); + error_setg(errp, +"Requested safe indirect branch capability level \"workaround\" not valid, try cap-ibs=%s", + cap_ibs_possible.vals[kvm_val]); } else if (tcg_enabled() && val) { /* TODO - for now only allow broken for TCG */ - error_setg(errp, "Requested safe indirect branch capability level not supported by tcg, try a different value for cap-ibs"); - } else if (kvm_enabled() && (val > kvmppc_get_cap_safe_indirect_branch())) { - error_setg(errp, "Requested safe indirect branch capability level not supported by kvm, try a different value for cap-ibs"); + error_setg(errp, +"Requested safe indirect branch capability level not supported by tcg, try a different value for cap-ibs"); + } else if (kvm_enabled() && val && (val != kvm_val)) { + error_setg(errp, +"Requested safe indirect branch capability level not supported by kvm, try cap-ibs=%s", + cap_ibs_possible.vals[kvm_val]); } } @@ -249,27 +297,31 @@ sPAPRCapabilityInfo capability_table[SPAPR_CAP_NUM] = { .name = "cfpc", .description = "Cache Flush on Privilege Change" VALUE_DESC_TRISTATE, .index = SPAPR_CAP_CFPC, - .get = spapr_cap_get_tristate, - .set = spapr_cap_set_tristate, + .get = spapr_cap_get_string, + .set = spapr_cap_set_string, .type = "string", + .possible = &cap_cfpc_possible, .apply = cap_safe_cache_apply, }, [SPAPR_CAP_SBBC] = { .name = "sbbc", .description = "Speculation Barrier Bounds Checking" VALUE_DESC_TRISTATE, .index = SPAPR_CAP_SBBC, - .get = spapr_cap_get_tristate, - .set = spapr_cap_set_tristate, + .get = spapr_cap_get_string, + .set = spapr_cap_set_string, .type = "string", + .possible = &cap_sbbc_possible, .apply = cap_safe_bounds_check_apply, }, [SPAPR_CAP_IBS] = { .name = "ibs", - .description = "Indirect Branch Serialisation (broken, fixed)", + .description = + "Indirect Branch Speculation (broken, fixed-ibs, fixed-ccd)", .index = SPAPR_CAP_IBS, - .get = spapr_cap_get_tristate, - .set = spapr_cap_set_tristate, + .get = spapr_cap_get_string, + .set = spapr_cap_set_string, .type = "string", + .possible = &cap_ibs_possible, .apply = cap_safe_indirect_branch_apply, }, }; @@ -283,15 +335,26 @@ 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; } + if (!ppc_check_compat(cpu, CPU_POWERPC_LOGICAL_2_06_PLUS, + 0, spapr->max_compat_pvr)) { + caps.caps[SPAPR_CAP_SBBC] = SPAPR_CAP_BROKEN; + } + if (!ppc_check_compat(cpu, CPU_POWERPC_LOGICAL_2_06, 0, spapr->max_compat_pvr)) { caps.caps[SPAPR_CAP_VSX] = SPAPR_CAP_OFF; caps.caps[SPAPR_CAP_DFP] = SPAPR_CAP_OFF; + caps.caps[SPAPR_CAP_IBS] = SPAPR_CAP_BROKEN; } return caps; diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c index 1986560480..16bccdd5c0 100644 --- a/hw/ppc/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -1705,7 +1705,10 @@ static target_ulong h_get_cpu_characteristics(PowerPCCPU *cpu, } switch (safe_indirect_branch) { - case SPAPR_CAP_FIXED: + case SPAPR_CAP_FIXED_CCD: + characteristics |= H_CPU_CHAR_CACHE_COUNT_DIS; + break; + case SPAPR_CAP_FIXED_IBS: characteristics |= H_CPU_CHAR_BCCTRL_SERIALISED; break; default: /* broken */ diff --git a/hw/ppc/trace-events b/hw/ppc/trace-events index b7c3e64b5e..66ec7eda6e 100644 --- a/hw/ppc/trace-events +++ b/hw/ppc/trace-events @@ -99,3 +99,11 @@ mac99_uninorth_read(uint64_t addr, uint64_t value) "addr=0x%" PRIx64 " val=0x%"P # hw/ppc/ppc4xx_pci.c ppc4xx_pci_map_irq(int32_t devfn, int irq_num, int slot) "devfn 0x%x irq %d -> %d" ppc4xx_pci_set_irq(int irq_num) "PCI irq %d" + +# hw/ppc/ppc440_pcix.c +ppc440_pcix_map_irq(int32_t devfn, int irq_num, int slot) "devfn 0x%x irq %d -> %d" +ppc440_pcix_set_irq(int irq_num) "PCI irq %d" +ppc440_pcix_update_pim(int idx, uint64_t size, uint64_t la) "Added window %d of size=0x%" PRIx64 " to CPU=0x%" PRIx64 +ppc440_pcix_update_pom(int idx, uint32_t size, uint64_t la, uint64_t pcia) "Added window %d of size=0x%x from CPU=0x%" PRIx64 " to PCI=0x%" PRIx64 +ppc440_pcix_reg_read(uint64_t addr, uint32_t val) "addr 0x%" PRIx64 " = 0x%" PRIx32 +ppc440_pcix_reg_write(uint64_t addr, uint64_t val) "addr 0x%" PRIx64 " = 0x%" PRIx64 |