diff options
Diffstat (limited to 'hw')
-rw-r--r-- | hw/a15mpcore.c | 1 | ||||
-rw-r--r-- | hw/a9mpcore.c | 2 | ||||
-rw-r--r-- | hw/arm-misc.h | 4 | ||||
-rw-r--r-- | hw/arm/Makefile.objs | 2 | ||||
-rw-r--r-- | hw/arm11mpcore.c | 2 | ||||
-rw-r--r-- | hw/arm_boot.c | 10 | ||||
-rw-r--r-- | hw/arm_gic.c | 366 | ||||
-rw-r--r-- | hw/arm_gic_common.c | 184 | ||||
-rw-r--r-- | hw/arm_gic_internal.h | 136 | ||||
-rw-r--r-- | hw/armv7m_nvic.c | 138 | ||||
-rw-r--r-- | hw/cadence_gem.c | 2 | ||||
-rw-r--r-- | hw/cadence_ttc.c | 2 | ||||
-rw-r--r-- | hw/exynos4210.c | 32 | ||||
-rw-r--r-- | hw/exynos4210.h | 2 | ||||
-rw-r--r-- | hw/exynos4210_gic.c | 78 | ||||
-rw-r--r-- | hw/fdc.c | 30 | ||||
-rw-r--r-- | hw/loader.c | 4 | ||||
-rw-r--r-- | hw/omap.h | 95 | ||||
-rw-r--r-- | hw/ppc/Makefile.objs | 2 | ||||
-rw-r--r-- | hw/ppce500_mpc8544ds.c | 283 | ||||
-rw-r--r-- | hw/pxa2xx.c | 285 | ||||
-rw-r--r-- | hw/pxa2xx_pic.c | 53 | ||||
-rw-r--r-- | hw/qdev-monitor.c | 27 | ||||
-rw-r--r-- | hw/spapr.c | 46 | ||||
-rw-r--r-- | hw/spapr.h | 3 | ||||
-rw-r--r-- | hw/spapr_hcall.c | 68 | ||||
-rw-r--r-- | hw/spapr_vscsi.c | 4 | ||||
-rw-r--r-- | hw/xilinx_timer.c | 3 |
28 files changed, 1117 insertions, 747 deletions
diff --git a/hw/a15mpcore.c b/hw/a15mpcore.c index 5a7b365548..fc0a02ae86 100644 --- a/hw/a15mpcore.c +++ b/hw/a15mpcore.c @@ -44,6 +44,7 @@ static int a15mp_priv_init(SysBusDevice *dev) s->gic = qdev_create(NULL, "arm_gic"); qdev_prop_set_uint32(s->gic, "num-cpu", s->num_cpu); qdev_prop_set_uint32(s->gic, "num-irq", s->num_irq); + qdev_prop_set_uint32(s->gic, "revision", 2); qdev_init_nofail(s->gic); busdev = sysbus_from_qdev(s->gic); diff --git a/hw/a9mpcore.c b/hw/a9mpcore.c index c2ff74d4b6..ebd5b29173 100644 --- a/hw/a9mpcore.c +++ b/hw/a9mpcore.c @@ -75,7 +75,7 @@ static void a9_scu_write(void *opaque, target_phys_addr_t offset, break; default: fprintf(stderr, "Invalid size %u in write to a9 scu register %x\n", - size, offset); + size, (unsigned)offset); return; } diff --git a/hw/arm-misc.h b/hw/arm-misc.h index 1d51570c88..1f96229d3c 100644 --- a/hw/arm-misc.h +++ b/hw/arm-misc.h @@ -45,9 +45,9 @@ struct arm_boot_info { /* multicore boards that use the default secondary core boot functions * can ignore these two function calls. If the default functions won't * work, then write_secondary_boot() should write a suitable blob of - * code mimicing the secondary CPU startup process used by the board's + * code mimicking the secondary CPU startup process used by the board's * boot loader/boot ROM code, and secondary_cpu_reset_hook() should - * perform any necessary CPU reset handling and set the PC for thei + * perform any necessary CPU reset handling and set the PC for the * secondary CPUs to point at this boot blob. */ void (*write_secondary_boot)(ARMCPU *cpu, diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs index a0ff6a62d6..88ff47d95e 100644 --- a/hw/arm/Makefile.objs +++ b/hw/arm/Makefile.objs @@ -6,7 +6,7 @@ obj-y += cadence_uart.o obj-y += cadence_ttc.o obj-y += cadence_gem.o obj-y += xilinx_zynq.o zynq_slcr.o -obj-y += arm_gic.o +obj-y += arm_gic.o arm_gic_common.o obj-y += realview_gic.o realview.o arm_sysctl.o arm11mpcore.o a9mpcore.o obj-y += exynos4210_gic.o exynos4210_combiner.o exynos4210.o obj-y += exynos4_boards.o exynos4210_uart.o exynos4210_pwm.o diff --git a/hw/arm11mpcore.c b/hw/arm11mpcore.c index c528d7aa01..1bff3d3282 100644 --- a/hw/arm11mpcore.c +++ b/hw/arm11mpcore.c @@ -123,6 +123,8 @@ static int mpcore_priv_init(SysBusDevice *dev) s->gic = qdev_create(NULL, "arm_gic"); qdev_prop_set_uint32(s->gic, "num-cpu", s->num_cpu); qdev_prop_set_uint32(s->gic, "num-irq", s->num_irq); + /* Request the legacy 11MPCore GIC behaviour: */ + qdev_prop_set_uint32(s->gic, "revision", 0); qdev_init_nofail(s->gic); /* Pass through outbound IRQ lines from the GIC */ diff --git a/hw/arm_boot.c b/hw/arm_boot.c index d0e643ba11..a1e6ddbc1c 100644 --- a/hw/arm_boot.c +++ b/hw/arm_boot.c @@ -242,10 +242,12 @@ static int load_dtb(target_phys_addr_t addr, const struct arm_boot_info *binfo) fprintf(stderr, "couldn't set /memory/reg\n"); } - rc = qemu_devtree_setprop_string(fdt, "/chosen", "bootargs", - binfo->kernel_cmdline); - if (rc < 0) { - fprintf(stderr, "couldn't set /chosen/bootargs\n"); + if (binfo->kernel_cmdline && *binfo->kernel_cmdline) { + rc = qemu_devtree_setprop_string(fdt, "/chosen", "bootargs", + binfo->kernel_cmdline); + if (rc < 0) { + fprintf(stderr, "couldn't set /chosen/bootargs\n"); + } } if (binfo->initrd_size) { diff --git a/hw/arm_gic.c b/hw/arm_gic.c index 72298b4b41..ec22322930 100644 --- a/hw/arm_gic.c +++ b/hw/arm_gic.c @@ -19,17 +19,7 @@ */ #include "sysbus.h" - -/* Maximum number of possible interrupts, determined by the GIC architecture */ -#define GIC_MAXIRQ 1020 -/* First 32 are private to each CPU (SGIs and PPIs). */ -#define GIC_INTERNAL 32 -/* Maximum number of possible CPU interfaces, determined by GIC architecture */ -#ifdef NVIC -#define NCPU 1 -#else -#define NCPU 8 -#endif +#include "arm_gic_internal.h" //#define DEBUG_GIC @@ -40,114 +30,23 @@ do { printf("arm_gic: " fmt , ## __VA_ARGS__); } while (0) #define DPRINTF(fmt, ...) do {} while(0) #endif -#ifdef NVIC -static const uint8_t gic_id[] = -{ 0x00, 0xb0, 0x1b, 0x00, 0x0d, 0xe0, 0x05, 0xb1 }; -/* The NVIC has 16 internal vectors. However these are not exposed - through the normal GIC interface. */ -#define GIC_BASE_IRQ 32 -#else -static const uint8_t gic_id[] = -{ 0x90, 0x13, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 }; -#define GIC_BASE_IRQ 0 -#endif - -#define FROM_SYSBUSGIC(type, dev) \ - DO_UPCAST(type, gic, FROM_SYSBUS(gic_state, dev)) +static const uint8_t gic_id[] = { + 0x90, 0x13, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 +}; -typedef struct gic_irq_state -{ - /* The enable bits are only banked for per-cpu interrupts. */ - unsigned enabled:NCPU; - unsigned pending:NCPU; - unsigned active:NCPU; - unsigned level:NCPU; - unsigned model:1; /* 0 = N:N, 1 = 1:N */ - unsigned trigger:1; /* nonzero = edge triggered. */ -} gic_irq_state; - -#define ALL_CPU_MASK ((unsigned)(((1 << NCPU) - 1))) -#if NCPU > 1 #define NUM_CPU(s) ((s)->num_cpu) -#else -#define NUM_CPU(s) 1 -#endif - -#define GIC_SET_ENABLED(irq, cm) s->irq_state[irq].enabled |= (cm) -#define GIC_CLEAR_ENABLED(irq, cm) s->irq_state[irq].enabled &= ~(cm) -#define GIC_TEST_ENABLED(irq, cm) ((s->irq_state[irq].enabled & (cm)) != 0) -#define GIC_SET_PENDING(irq, cm) s->irq_state[irq].pending |= (cm) -#define GIC_CLEAR_PENDING(irq, cm) s->irq_state[irq].pending &= ~(cm) -#define GIC_TEST_PENDING(irq, cm) ((s->irq_state[irq].pending & (cm)) != 0) -#define GIC_SET_ACTIVE(irq, cm) s->irq_state[irq].active |= (cm) -#define GIC_CLEAR_ACTIVE(irq, cm) s->irq_state[irq].active &= ~(cm) -#define GIC_TEST_ACTIVE(irq, cm) ((s->irq_state[irq].active & (cm)) != 0) -#define GIC_SET_MODEL(irq) s->irq_state[irq].model = 1 -#define GIC_CLEAR_MODEL(irq) s->irq_state[irq].model = 0 -#define GIC_TEST_MODEL(irq) s->irq_state[irq].model -#define GIC_SET_LEVEL(irq, cm) s->irq_state[irq].level = (cm) -#define GIC_CLEAR_LEVEL(irq, cm) s->irq_state[irq].level &= ~(cm) -#define GIC_TEST_LEVEL(irq, cm) ((s->irq_state[irq].level & (cm)) != 0) -#define GIC_SET_TRIGGER(irq) s->irq_state[irq].trigger = 1 -#define GIC_CLEAR_TRIGGER(irq) s->irq_state[irq].trigger = 0 -#define GIC_TEST_TRIGGER(irq) s->irq_state[irq].trigger -#define GIC_GET_PRIORITY(irq, cpu) (((irq) < GIC_INTERNAL) ? \ - s->priority1[irq][cpu] : \ - s->priority2[(irq) - GIC_INTERNAL]) -#ifdef NVIC -#define GIC_TARGET(irq) 1 -#else -#define GIC_TARGET(irq) s->irq_target[irq] -#endif - -typedef struct gic_state -{ - SysBusDevice busdev; - qemu_irq parent_irq[NCPU]; - int enabled; - int cpu_enabled[NCPU]; - - gic_irq_state irq_state[GIC_MAXIRQ]; -#ifndef NVIC - int irq_target[GIC_MAXIRQ]; -#endif - int priority1[GIC_INTERNAL][NCPU]; - int priority2[GIC_MAXIRQ - GIC_INTERNAL]; - int last_active[GIC_MAXIRQ][NCPU]; - - int priority_mask[NCPU]; - int running_irq[NCPU]; - int running_priority[NCPU]; - int current_pending[NCPU]; - -#if NCPU > 1 - uint32_t num_cpu; -#endif - - MemoryRegion iomem; /* Distributor */ -#ifndef NVIC - /* This is just so we can have an opaque pointer which identifies - * both this GIC and which CPU interface we should be accessing. - */ - struct gic_state *backref[NCPU]; - MemoryRegion cpuiomem[NCPU+1]; /* CPU interfaces */ -#endif - uint32_t num_irq; -} gic_state; static inline int gic_get_current_cpu(gic_state *s) { -#if NCPU > 1 if (s->num_cpu > 1) { return cpu_single_env->cpu_index; } -#endif return 0; } /* TODO: Many places that call this routine could be optimized. */ /* Update interrupt status after enabled or pending bits have been changed. */ -static void gic_update(gic_state *s) +void gic_update(gic_state *s) { int best_irq; int best_prio; @@ -185,8 +84,7 @@ static void gic_update(gic_state *s) } } -#ifdef NVIC -static void gic_set_pending_private(gic_state *s, int cpu, int irq) +void gic_set_pending_private(gic_state *s, int cpu, int irq) { int cm = 1 << cpu; @@ -197,7 +95,6 @@ static void gic_set_pending_private(gic_state *s, int cpu, int irq) GIC_SET_PENDING(irq, cm); gic_update(s); } -#endif /* Process a change in an external IRQ input. */ static void gic_set_irq(void *opaque, int irq, int level) @@ -251,7 +148,7 @@ static void gic_set_running_irq(gic_state *s, int cpu, int irq) gic_update(s); } -static uint32_t gic_acknowledge_irq(gic_state *s, int cpu) +uint32_t gic_acknowledge_irq(gic_state *s, int cpu) { int new_irq; int cm = 1 << cpu; @@ -270,7 +167,7 @@ static uint32_t gic_acknowledge_irq(gic_state *s, int cpu) return new_irq; } -static void gic_complete_irq(gic_state * s, int cpu, int irq) +void gic_complete_irq(gic_state *s, int cpu, int irq) { int update = 0; int cm = 1 << cpu; @@ -328,7 +225,6 @@ static uint32_t gic_dist_readb(void *opaque, target_phys_addr_t offset) cpu = gic_get_current_cpu(s); cm = 1 << cpu; if (offset < 0x100) { -#ifndef NVIC if (offset == 0) return s->enabled; if (offset == 4) @@ -339,7 +235,6 @@ static uint32_t gic_dist_readb(void *opaque, target_phys_addr_t offset) /* Interrupt Security , RAZ/WI */ return 0; } -#endif goto bad_reg; } else if (offset < 0x200) { /* Interrupt Set/Clear Enable. */ @@ -390,16 +285,21 @@ static uint32_t gic_dist_readb(void *opaque, target_phys_addr_t offset) if (irq >= s->num_irq) goto bad_reg; res = GIC_GET_PRIORITY(irq, cpu); -#ifndef NVIC } else if (offset < 0xc00) { /* Interrupt CPU Target. */ - irq = (offset - 0x800) + GIC_BASE_IRQ; - if (irq >= s->num_irq) - goto bad_reg; - if (irq >= 29 && irq <= 31) { - res = cm; + if (s->num_cpu == 1 && s->revision != REV_11MPCORE) { + /* For uniprocessor GICs these RAZ/WI */ + res = 0; } else { - res = GIC_TARGET(irq); + irq = (offset - 0x800) + GIC_BASE_IRQ; + if (irq >= s->num_irq) { + goto bad_reg; + } + if (irq >= 29 && irq <= 31) { + res = cm; + } else { + res = GIC_TARGET(irq); + } } } else if (offset < 0xf00) { /* Interrupt Configuration. */ @@ -413,7 +313,6 @@ static uint32_t gic_dist_readb(void *opaque, target_phys_addr_t offset) if (GIC_TEST_TRIGGER(irq + i)) res |= (2 << (i * 2)); } -#endif } else if (offset < 0xfe0) { goto bad_reg; } else /* offset >= 0xfe0 */ { @@ -440,13 +339,6 @@ static uint32_t gic_dist_readw(void *opaque, target_phys_addr_t offset) static uint32_t gic_dist_readl(void *opaque, target_phys_addr_t offset) { uint32_t val; -#ifdef NVIC - gic_state *s = (gic_state *)opaque; - uint32_t addr; - addr = offset; - if (addr < 0x100 || addr > 0xd00) - return nvic_readl(s, addr); -#endif val = gic_dist_readw(opaque, offset); val |= gic_dist_readw(opaque, offset + 2) << 16; return val; @@ -462,9 +354,6 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset, cpu = gic_get_current_cpu(s); if (offset < 0x100) { -#ifdef NVIC - goto bad_reg; -#else if (offset == 0) { s->enabled = (value & 1); DPRINTF("Distribution %sabled\n", s->enabled ? "En" : "Dis"); @@ -475,7 +364,6 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset, } else { goto bad_reg; } -#endif } else if (offset < 0x180) { /* Interrupt Set Enable. */ irq = (offset - 0x100) * 8 + GIC_BASE_IRQ; @@ -557,17 +445,22 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset, } else { s->priority2[irq - GIC_INTERNAL] = value; } -#ifndef NVIC } else if (offset < 0xc00) { - /* Interrupt CPU Target. */ - irq = (offset - 0x800) + GIC_BASE_IRQ; - if (irq >= s->num_irq) - goto bad_reg; - if (irq < 29) - value = 0; - else if (irq < GIC_INTERNAL) - value = ALL_CPU_MASK; - s->irq_target[irq] = value & ALL_CPU_MASK; + /* Interrupt CPU Target. RAZ/WI on uniprocessor GICs, with the + * annoying exception of the 11MPCore's GIC. + */ + if (s->num_cpu != 1 || s->revision == REV_11MPCORE) { + irq = (offset - 0x800) + GIC_BASE_IRQ; + if (irq >= s->num_irq) { + goto bad_reg; + } + if (irq < 29) { + value = 0; + } else if (irq < GIC_INTERNAL) { + value = ALL_CPU_MASK; + } + s->irq_target[irq] = value & ALL_CPU_MASK; + } } else if (offset < 0xf00) { /* Interrupt Configuration. */ irq = (offset - 0xc00) * 4 + GIC_BASE_IRQ; @@ -587,7 +480,6 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset, GIC_CLEAR_TRIGGER(irq + i); } } -#endif } else { /* 0xf00 is only handled for 32-bit writes. */ goto bad_reg; @@ -609,14 +501,6 @@ static void gic_dist_writel(void *opaque, target_phys_addr_t offset, uint32_t value) { gic_state *s = (gic_state *)opaque; -#ifdef NVIC - uint32_t addr; - addr = offset; - if (addr < 0x100 || (addr > 0xd00 && addr != 0xf00)) { - nvic_writel(s, addr, value); - return; - } -#endif if (offset == 0xf00) { int cpu; int irq; @@ -655,7 +539,6 @@ static const MemoryRegionOps gic_dist_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -#ifndef NVIC static uint32_t gic_cpu_read(gic_state *s, int cpu, int offset) { switch (offset) { @@ -747,141 +630,12 @@ static const MemoryRegionOps gic_cpu_ops = { .write = gic_do_cpu_write, .endianness = DEVICE_NATIVE_ENDIAN, }; -#endif - -static void gic_reset(DeviceState *dev) -{ - gic_state *s = FROM_SYSBUS(gic_state, sysbus_from_qdev(dev)); - int i; - memset(s->irq_state, 0, GIC_MAXIRQ * sizeof(gic_irq_state)); - for (i = 0 ; i < NUM_CPU(s); i++) { - s->priority_mask[i] = 0xf0; - s->current_pending[i] = 1023; - s->running_irq[i] = 1023; - s->running_priority[i] = 0x100; -#ifdef NVIC - /* The NVIC doesn't have per-cpu interfaces, so enable by default. */ - s->cpu_enabled[i] = 1; -#else - s->cpu_enabled[i] = 0; -#endif - } - for (i = 0; i < 16; i++) { - GIC_SET_ENABLED(i, ALL_CPU_MASK); - GIC_SET_TRIGGER(i); - } -#ifdef NVIC - /* The NVIC is always enabled. */ - s->enabled = 1; -#else - s->enabled = 0; -#endif -} -static void gic_save(QEMUFile *f, void *opaque) -{ - gic_state *s = (gic_state *)opaque; - int i; - int j; - - qemu_put_be32(f, s->enabled); - for (i = 0; i < NUM_CPU(s); i++) { - qemu_put_be32(f, s->cpu_enabled[i]); - for (j = 0; j < GIC_INTERNAL; j++) - qemu_put_be32(f, s->priority1[j][i]); - for (j = 0; j < s->num_irq; j++) - qemu_put_be32(f, s->last_active[j][i]); - qemu_put_be32(f, s->priority_mask[i]); - qemu_put_be32(f, s->running_irq[i]); - qemu_put_be32(f, s->running_priority[i]); - qemu_put_be32(f, s->current_pending[i]); - } - for (i = 0; i < s->num_irq - GIC_INTERNAL; i++) { - qemu_put_be32(f, s->priority2[i]); - } - for (i = 0; i < s->num_irq; i++) { -#ifndef NVIC - qemu_put_be32(f, s->irq_target[i]); -#endif - qemu_put_byte(f, s->irq_state[i].enabled); - qemu_put_byte(f, s->irq_state[i].pending); - qemu_put_byte(f, s->irq_state[i].active); - qemu_put_byte(f, s->irq_state[i].level); - qemu_put_byte(f, s->irq_state[i].model); - qemu_put_byte(f, s->irq_state[i].trigger); - } -} - -static int gic_load(QEMUFile *f, void *opaque, int version_id) -{ - gic_state *s = (gic_state *)opaque; - int i; - int j; - - if (version_id != 2) - return -EINVAL; - - s->enabled = qemu_get_be32(f); - for (i = 0; i < NUM_CPU(s); i++) { - s->cpu_enabled[i] = qemu_get_be32(f); - for (j = 0; j < GIC_INTERNAL; j++) - s->priority1[j][i] = qemu_get_be32(f); - for (j = 0; j < s->num_irq; j++) - s->last_active[j][i] = qemu_get_be32(f); - s->priority_mask[i] = qemu_get_be32(f); - s->running_irq[i] = qemu_get_be32(f); - s->running_priority[i] = qemu_get_be32(f); - s->current_pending[i] = qemu_get_be32(f); - } - for (i = 0; i < s->num_irq - GIC_INTERNAL; i++) { - s->priority2[i] = qemu_get_be32(f); - } - for (i = 0; i < s->num_irq; i++) { -#ifndef NVIC - s->irq_target[i] = qemu_get_be32(f); -#endif - s->irq_state[i].enabled = qemu_get_byte(f); - s->irq_state[i].pending = qemu_get_byte(f); - s->irq_state[i].active = qemu_get_byte(f); - s->irq_state[i].level = qemu_get_byte(f); - s->irq_state[i].model = qemu_get_byte(f); - s->irq_state[i].trigger = qemu_get_byte(f); - } - - return 0; -} - -#if NCPU > 1 -static void gic_init(gic_state *s, int num_cpu, int num_irq) -#else -static void gic_init(gic_state *s, int num_irq) -#endif +void gic_init_irqs_and_distributor(gic_state *s, int num_irq) { int i; -#if NCPU > 1 - s->num_cpu = num_cpu; - if (s->num_cpu > NCPU) { - hw_error("requested %u CPUs exceeds GIC maximum %d\n", - num_cpu, NCPU); - } -#endif - s->num_irq = num_irq + GIC_BASE_IRQ; - if (s->num_irq > GIC_MAXIRQ) { - hw_error("requested %u interrupt lines exceeds GIC maximum %d\n", - num_irq, GIC_MAXIRQ); - } - /* ITLinesNumber is represented as (N / 32) - 1 (see - * gic_dist_readb) so this is an implementation imposed - * restriction, not an architectural one: - */ - if (s->num_irq < 32 || (s->num_irq % 32)) { - hw_error("%d interrupt lines unsupported: not divisible by 32\n", - num_irq); - } - i = s->num_irq - GIC_INTERNAL; -#ifndef NVIC /* For the GIC, also expose incoming GPIO lines for PPIs for each CPU. * GPIO array layout is thus: * [0..N-1] SPIs @@ -889,14 +643,27 @@ static void gic_init(gic_state *s, int num_irq) * [N+32..N+63] PPIs for CPU 1 * ... */ - i += (GIC_INTERNAL * num_cpu); -#endif + if (s->revision != REV_NVIC) { + i += (GIC_INTERNAL * s->num_cpu); + } qdev_init_gpio_in(&s->busdev.qdev, gic_set_irq, i); for (i = 0; i < NUM_CPU(s); i++) { sysbus_init_irq(&s->busdev, &s->parent_irq[i]); } memory_region_init_io(&s->iomem, &gic_dist_ops, s, "gic_dist", 0x1000); -#ifndef NVIC +} + +static int arm_gic_init(SysBusDevice *dev) +{ + /* Device instance init function for the GIC sysbus device */ + int i; + gic_state *s = FROM_SYSBUS(gic_state, dev); + ARMGICClass *agc = ARM_GIC_GET_CLASS(s); + + agc->parent_init(dev); + + gic_init_irqs_and_distributor(s, s->num_irq); + /* Memory regions for the CPU interfaces (NVIC doesn't have these): * a region for "CPU interface for this core", then a region for * "CPU interface for core 0", "for core 1", ... @@ -912,19 +679,6 @@ static void gic_init(gic_state *s, int num_irq) memory_region_init_io(&s->cpuiomem[i+1], &gic_cpu_ops, &s->backref[i], "gic_cpu", 0x100); } -#endif - - register_savevm(NULL, "arm_gic", -1, 2, gic_save, gic_load, s); -} - -#ifndef NVIC - -static int arm_gic_init(SysBusDevice *dev) -{ - /* Device instance init function for the GIC sysbus device */ - int i; - gic_state *s = FROM_SYSBUS(gic_state, dev); - gic_init(s, s->num_cpu, s->num_irq); /* Distributor */ sysbus_init_mmio(dev, &s->iomem); /* cpu interfaces (one for "current cpu" plus one per cpu) */ @@ -934,25 +688,19 @@ static int arm_gic_init(SysBusDevice *dev) return 0; } -static Property arm_gic_properties[] = { - DEFINE_PROP_UINT32("num-cpu", gic_state, num_cpu, 1), - DEFINE_PROP_UINT32("num-irq", gic_state, num_irq, 32), - DEFINE_PROP_END_OF_LIST(), -}; - static void arm_gic_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass); + ARMGICClass *agc = ARM_GIC_CLASS(klass); + agc->parent_init = sbc->init; sbc->init = arm_gic_init; - dc->props = arm_gic_properties; - dc->reset = gic_reset; dc->no_user = 1; } static TypeInfo arm_gic_info = { - .name = "arm_gic", - .parent = TYPE_SYS_BUS_DEVICE, + .name = TYPE_ARM_GIC, + .parent = TYPE_ARM_GIC_COMMON, .instance_size = sizeof(gic_state), .class_init = arm_gic_class_init, }; @@ -963,5 +711,3 @@ static void arm_gic_register_types(void) } type_init(arm_gic_register_types) - -#endif diff --git a/hw/arm_gic_common.c b/hw/arm_gic_common.c new file mode 100644 index 0000000000..360e7823f7 --- /dev/null +++ b/hw/arm_gic_common.c @@ -0,0 +1,184 @@ +/* + * ARM GIC support - common bits of emulated and KVM kernel model + * + * Copyright (c) 2012 Linaro Limited + * Written by Peter Maydell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "arm_gic_internal.h" + +static void gic_save(QEMUFile *f, void *opaque) +{ + gic_state *s = (gic_state *)opaque; + int i; + int j; + + qemu_put_be32(f, s->enabled); + for (i = 0; i < s->num_cpu; i++) { + qemu_put_be32(f, s->cpu_enabled[i]); + for (j = 0; j < GIC_INTERNAL; j++) { + qemu_put_be32(f, s->priority1[j][i]); + } + for (j = 0; j < s->num_irq; j++) { + qemu_put_be32(f, s->last_active[j][i]); + } + qemu_put_be32(f, s->priority_mask[i]); + qemu_put_be32(f, s->running_irq[i]); + qemu_put_be32(f, s->running_priority[i]); + qemu_put_be32(f, s->current_pending[i]); + } + for (i = 0; i < s->num_irq - GIC_INTERNAL; i++) { + qemu_put_be32(f, s->priority2[i]); + } + for (i = 0; i < s->num_irq; i++) { + qemu_put_be32(f, s->irq_target[i]); + qemu_put_byte(f, s->irq_state[i].enabled); + qemu_put_byte(f, s->irq_state[i].pending); + qemu_put_byte(f, s->irq_state[i].active); + qemu_put_byte(f, s->irq_state[i].level); + qemu_put_byte(f, s->irq_state[i].model); + qemu_put_byte(f, s->irq_state[i].trigger); + } +} + +static int gic_load(QEMUFile *f, void *opaque, int version_id) +{ + gic_state *s = (gic_state *)opaque; + int i; + int j; + + if (version_id != 3) { + return -EINVAL; + } + + s->enabled = qemu_get_be32(f); + for (i = 0; i < s->num_cpu; i++) { + s->cpu_enabled[i] = qemu_get_be32(f); + for (j = 0; j < GIC_INTERNAL; j++) { + s->priority1[j][i] = qemu_get_be32(f); + } + for (j = 0; j < s->num_irq; j++) { + s->last_active[j][i] = qemu_get_be32(f); + } + s->priority_mask[i] = qemu_get_be32(f); + s->running_irq[i] = qemu_get_be32(f); + s->running_priority[i] = qemu_get_be32(f); + s->current_pending[i] = qemu_get_be32(f); + } + for (i = 0; i < s->num_irq - GIC_INTERNAL; i++) { + s->priority2[i] = qemu_get_be32(f); + } + for (i = 0; i < s->num_irq; i++) { + s->irq_target[i] = qemu_get_be32(f); + s->irq_state[i].enabled = qemu_get_byte(f); + s->irq_state[i].pending = qemu_get_byte(f); + s->irq_state[i].active = qemu_get_byte(f); + s->irq_state[i].level = qemu_get_byte(f); + s->irq_state[i].model = qemu_get_byte(f); + s->irq_state[i].trigger = qemu_get_byte(f); + } + + return 0; +} + +static int arm_gic_common_init(SysBusDevice *dev) +{ + gic_state *s = FROM_SYSBUS(gic_state, dev); + int num_irq = s->num_irq; + + if (s->num_cpu > NCPU) { + hw_error("requested %u CPUs exceeds GIC maximum %d\n", + s->num_cpu, NCPU); + } + s->num_irq += GIC_BASE_IRQ; + if (s->num_irq > GIC_MAXIRQ) { + hw_error("requested %u interrupt lines exceeds GIC maximum %d\n", + num_irq, GIC_MAXIRQ); + } + /* ITLinesNumber is represented as (N / 32) - 1 (see + * gic_dist_readb) so this is an implementation imposed + * restriction, not an architectural one: + */ + if (s->num_irq < 32 || (s->num_irq % 32)) { + hw_error("%d interrupt lines unsupported: not divisible by 32\n", + num_irq); + } + + register_savevm(NULL, "arm_gic", -1, 3, gic_save, gic_load, s); + return 0; +} + +static void arm_gic_common_reset(DeviceState *dev) +{ + gic_state *s = FROM_SYSBUS(gic_state, sysbus_from_qdev(dev)); + int i; + memset(s->irq_state, 0, GIC_MAXIRQ * sizeof(gic_irq_state)); + for (i = 0 ; i < s->num_cpu; i++) { + s->priority_mask[i] = 0xf0; + s->current_pending[i] = 1023; + s->running_irq[i] = 1023; + s->running_priority[i] = 0x100; + s->cpu_enabled[i] = 0; + } + for (i = 0; i < 16; i++) { + GIC_SET_ENABLED(i, ALL_CPU_MASK); + GIC_SET_TRIGGER(i); + } + if (s->num_cpu == 1) { + /* For uniprocessor GICs all interrupts always target the sole CPU */ + for (i = 0; i < GIC_MAXIRQ; i++) { + s->irq_target[i] = 1; + } + } + s->enabled = 0; +} + +static Property arm_gic_common_properties[] = { + DEFINE_PROP_UINT32("num-cpu", gic_state, num_cpu, 1), + DEFINE_PROP_UINT32("num-irq", gic_state, num_irq, 32), + /* Revision can be 1 or 2 for GIC architecture specification + * versions 1 or 2, or 0 to indicate the legacy 11MPCore GIC. + * (Internally, 0xffffffff also indicates "not a GIC but an NVIC".) + */ + DEFINE_PROP_UINT32("revision", gic_state, revision, 1), + DEFINE_PROP_END_OF_LIST(), +}; + +static void arm_gic_common_class_init(ObjectClass *klass, void *data) +{ + SysBusDeviceClass *sc = SYS_BUS_DEVICE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + dc->reset = arm_gic_common_reset; + dc->props = arm_gic_common_properties; + dc->no_user = 1; + sc->init = arm_gic_common_init; +} + +static TypeInfo arm_gic_common_type = { + .name = TYPE_ARM_GIC_COMMON, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(gic_state), + .class_size = sizeof(ARMGICCommonClass), + .class_init = arm_gic_common_class_init, + .abstract = true, +}; + +static void register_types(void) +{ + type_register_static(&arm_gic_common_type); +} + +type_init(register_types) diff --git a/hw/arm_gic_internal.h b/hw/arm_gic_internal.h new file mode 100644 index 0000000000..db4fad564f --- /dev/null +++ b/hw/arm_gic_internal.h @@ -0,0 +1,136 @@ +/* + * ARM GIC support - internal interfaces + * + * Copyright (c) 2012 Linaro Limited + * Written by Peter Maydell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef QEMU_ARM_GIC_INTERNAL_H +#define QEMU_ARM_GIC_INTERNAL_H + +#include "sysbus.h" + +/* Maximum number of possible interrupts, determined by the GIC architecture */ +#define GIC_MAXIRQ 1020 +/* First 32 are private to each CPU (SGIs and PPIs). */ +#define GIC_INTERNAL 32 +/* Maximum number of possible CPU interfaces, determined by GIC architecture */ +#define NCPU 8 + +#define ALL_CPU_MASK ((unsigned)(((1 << NCPU) - 1))) + +/* The NVIC has 16 internal vectors. However these are not exposed + through the normal GIC interface. */ +#define GIC_BASE_IRQ ((s->revision == REV_NVIC) ? 32 : 0) + +#define GIC_SET_ENABLED(irq, cm) s->irq_state[irq].enabled |= (cm) +#define GIC_CLEAR_ENABLED(irq, cm) s->irq_state[irq].enabled &= ~(cm) +#define GIC_TEST_ENABLED(irq, cm) ((s->irq_state[irq].enabled & (cm)) != 0) +#define GIC_SET_PENDING(irq, cm) s->irq_state[irq].pending |= (cm) +#define GIC_CLEAR_PENDING(irq, cm) s->irq_state[irq].pending &= ~(cm) +#define GIC_TEST_PENDING(irq, cm) ((s->irq_state[irq].pending & (cm)) != 0) +#define GIC_SET_ACTIVE(irq, cm) s->irq_state[irq].active |= (cm) +#define GIC_CLEAR_ACTIVE(irq, cm) s->irq_state[irq].active &= ~(cm) +#define GIC_TEST_ACTIVE(irq, cm) ((s->irq_state[irq].active & (cm)) != 0) +#define GIC_SET_MODEL(irq) s->irq_state[irq].model = 1 +#define GIC_CLEAR_MODEL(irq) s->irq_state[irq].model = 0 +#define GIC_TEST_MODEL(irq) s->irq_state[irq].model +#define GIC_SET_LEVEL(irq, cm) s->irq_state[irq].level = (cm) +#define GIC_CLEAR_LEVEL(irq, cm) s->irq_state[irq].level &= ~(cm) +#define GIC_TEST_LEVEL(irq, cm) ((s->irq_state[irq].level & (cm)) != 0) +#define GIC_SET_TRIGGER(irq) s->irq_state[irq].trigger = 1 +#define GIC_CLEAR_TRIGGER(irq) s->irq_state[irq].trigger = 0 +#define GIC_TEST_TRIGGER(irq) s->irq_state[irq].trigger +#define GIC_GET_PRIORITY(irq, cpu) (((irq) < GIC_INTERNAL) ? \ + s->priority1[irq][cpu] : \ + s->priority2[(irq) - GIC_INTERNAL]) +#define GIC_TARGET(irq) s->irq_target[irq] + +typedef struct gic_irq_state { + /* The enable bits are only banked for per-cpu interrupts. */ + unsigned enabled:NCPU; + unsigned pending:NCPU; + unsigned active:NCPU; + unsigned level:NCPU; + unsigned model:1; /* 0 = N:N, 1 = 1:N */ + unsigned trigger:1; /* nonzero = edge triggered. */ +} gic_irq_state; + +typedef struct gic_state { + SysBusDevice busdev; + qemu_irq parent_irq[NCPU]; + int enabled; + int cpu_enabled[NCPU]; + + gic_irq_state irq_state[GIC_MAXIRQ]; + int irq_target[GIC_MAXIRQ]; + int priority1[GIC_INTERNAL][NCPU]; + int priority2[GIC_MAXIRQ - GIC_INTERNAL]; + int last_active[GIC_MAXIRQ][NCPU]; + + int priority_mask[NCPU]; + int running_irq[NCPU]; + int running_priority[NCPU]; + int current_pending[NCPU]; + + uint32_t num_cpu; + + MemoryRegion iomem; /* Distributor */ + /* This is just so we can have an opaque pointer which identifies + * both this GIC and which CPU interface we should be accessing. + */ + struct gic_state *backref[NCPU]; + MemoryRegion cpuiomem[NCPU+1]; /* CPU interfaces */ + uint32_t num_irq; + uint32_t revision; +} gic_state; + +/* The special cases for the revision property: */ +#define REV_11MPCORE 0 +#define REV_NVIC 0xffffffff + +void gic_set_pending_private(gic_state *s, int cpu, int irq); +uint32_t gic_acknowledge_irq(gic_state *s, int cpu); +void gic_complete_irq(gic_state *s, int cpu, int irq); +void gic_update(gic_state *s); +void gic_init_irqs_and_distributor(gic_state *s, int num_irq); + +#define TYPE_ARM_GIC_COMMON "arm_gic_common" +#define ARM_GIC_COMMON(obj) \ + OBJECT_CHECK(gic_state, (obj), TYPE_ARM_GIC_COMMON) +#define ARM_GIC_COMMON_CLASS(klass) \ + OBJECT_CLASS_CHECK(ARMGICCommonClass, (klass), TYPE_ARM_GIC_COMMON) +#define ARM_GIC_COMMON_GET_CLASS(obj) \ + OBJECT_GET_CLASS(ARMGICCommonClass, (obj), TYPE_ARM_GIC_COMMON) + +typedef struct ARMGICCommonClass { + SysBusDeviceClass parent_class; +} ARMGICCommonClass; + +#define TYPE_ARM_GIC "arm_gic" +#define ARM_GIC(obj) \ + OBJECT_CHECK(gic_state, (obj), TYPE_ARM_GIC) +#define ARM_GIC_CLASS(klass) \ + OBJECT_CLASS_CHECK(ARMGICClass, (klass), TYPE_ARM_GIC) +#define ARM_GIC_GET_CLASS(obj) \ + OBJECT_GET_CLASS(ARMGICClass, (obj), TYPE_ARM_GIC) + +typedef struct ARMGICClass { + ARMGICCommonClass parent_class; + int (*parent_init)(SysBusDevice *dev); +} ARMGICClass; + +#endif /* !QEMU_ARM_GIC_INTERNAL_H */ diff --git a/hw/armv7m_nvic.c b/hw/armv7m_nvic.c index 986a6bbd0c..4867c1d5fa 100644 --- a/hw/armv7m_nvic.c +++ b/hw/armv7m_nvic.c @@ -14,13 +14,7 @@ #include "qemu-timer.h" #include "arm-misc.h" #include "exec-memory.h" - -#define NVIC 1 - -static uint32_t nvic_readl(void *opaque, uint32_t offset); -static void nvic_writel(void *opaque, uint32_t offset, uint32_t value); - -#include "arm_gic.c" +#include "arm_gic_internal.h" typedef struct { gic_state gic; @@ -30,9 +24,38 @@ typedef struct { int64_t tick; QEMUTimer *timer; } systick; + MemoryRegion sysregmem; + MemoryRegion gic_iomem_alias; + MemoryRegion container; uint32_t num_irq; } nvic_state; +#define TYPE_NVIC "armv7m_nvic" +/** + * NVICClass: + * @parent_reset: the parent class' reset handler. + * + * A model of the v7M NVIC and System Controller + */ +typedef struct NVICClass { + /*< private >*/ + ARMGICClass parent_class; + /*< public >*/ + int (*parent_init)(SysBusDevice *dev); + void (*parent_reset)(DeviceState *dev); +} NVICClass; + +#define NVIC_CLASS(klass) \ + OBJECT_CLASS_CHECK(NVICClass, (klass), TYPE_NVIC) +#define NVIC_GET_CLASS(obj) \ + OBJECT_GET_CLASS(NVICClass, (obj), TYPE_NVIC) +#define NVIC(obj) \ + OBJECT_CHECK(nvic_state, (obj), TYPE_NVIC) + +static const uint8_t nvic_id[] = { + 0x00, 0xb0, 0x1b, 0x00, 0x0d, 0xe0, 0x05, 0xb1 +}; + /* qemu timers run at 1GHz. We want something closer to 1MHz. */ #define SYSTICK_SCALE 1000ULL @@ -358,12 +381,54 @@ static void nvic_writel(void *opaque, uint32_t offset, uint32_t value) case 0xd38: /* Bus Fault Address. */ case 0xd3c: /* Aux Fault Status. */ goto bad_reg; + case 0xf00: /* Software Triggered Interrupt Register */ + if ((value & 0x1ff) < s->num_irq) { + gic_set_pending_private(&s->gic, 0, value & 0x1ff); + } + break; default: bad_reg: hw_error("NVIC: Bad write offset 0x%x\n", offset); } } +static uint64_t nvic_sysreg_read(void *opaque, target_phys_addr_t addr, + unsigned size) +{ + /* At the moment we only support the ID registers for byte/word access. + * This is not strictly correct as a few of the other registers also + * allow byte access. + */ + uint32_t offset = addr; + if (offset >= 0xfe0) { + if (offset & 3) { + return 0; + } + return nvic_id[(offset - 0xfe0) >> 2]; + } + if (size == 4) { + return nvic_readl(opaque, offset); + } + hw_error("NVIC: Bad read of size %d at offset 0x%x\n", size, offset); +} + +static void nvic_sysreg_write(void *opaque, target_phys_addr_t addr, + uint64_t value, unsigned size) +{ + uint32_t offset = addr; + if (size == 4) { + nvic_writel(opaque, offset, value); + return; + } + hw_error("NVIC: Bad write of size %d at offset 0x%x\n", size, offset); +} + +static const MemoryRegionOps nvic_sysreg_ops = { + .read = nvic_sysreg_read, + .write = nvic_sysreg_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + static const VMStateDescription vmstate_nvic = { .name = "armv7m_nvic", .version_id = 1, @@ -380,20 +445,55 @@ static const VMStateDescription vmstate_nvic = { static void armv7m_nvic_reset(DeviceState *dev) { - nvic_state *s = FROM_SYSBUSGIC(nvic_state, sysbus_from_qdev(dev)); - gic_reset(&s->gic.busdev.qdev); + nvic_state *s = NVIC(dev); + NVICClass *nc = NVIC_GET_CLASS(s); + nc->parent_reset(dev); + /* Common GIC reset resets to disabled; the NVIC doesn't have + * per-CPU interfaces so mark our non-existent CPU interface + * as enabled by default. + */ + s->gic.cpu_enabled[0] = 1; + /* The NVIC as a whole is always enabled. */ + s->gic.enabled = 1; systick_reset(s); } static int armv7m_nvic_init(SysBusDevice *dev) { - nvic_state *s= FROM_SYSBUSGIC(nvic_state, dev); + nvic_state *s = NVIC(dev); + NVICClass *nc = NVIC_GET_CLASS(s); - /* note that for the M profile gic_init() takes the number of external - * interrupt lines only. - */ - gic_init(&s->gic, s->num_irq); - memory_region_add_subregion(get_system_memory(), 0xe000e000, &s->gic.iomem); + /* The NVIC always has only one CPU */ + s->gic.num_cpu = 1; + /* Tell the common code we're an NVIC */ + s->gic.revision = 0xffffffff; + s->gic.num_irq = s->num_irq; + nc->parent_init(dev); + gic_init_irqs_and_distributor(&s->gic, s->num_irq); + /* The NVIC and system controller register area looks like this: + * 0..0xff : system control registers, including systick + * 0x100..0xcff : GIC-like registers + * 0xd00..0xfff : system control registers + * We use overlaying to put the GIC like registers + * over the top of the system control register region. + */ + memory_region_init(&s->container, "nvic", 0x1000); + /* The system register region goes at the bottom of the priority + * stack as it covers the whole page. + */ + memory_region_init_io(&s->sysregmem, &nvic_sysreg_ops, s, + "nvic_sysregs", 0x1000); + memory_region_add_subregion(&s->container, 0, &s->sysregmem); + /* Alias the GIC region so we can get only the section of it + * we need, and layer it on top of the system register region. + */ + memory_region_init_alias(&s->gic_iomem_alias, "nvic-gic", &s->gic.iomem, + 0x100, 0xc00); + memory_region_add_subregion_overlap(&s->container, 0x100, &s->gic.iomem, 1); + /* Map the whole thing into system memory at the location required + * by the v7M architecture. + */ + memory_region_add_subregion(get_system_memory(), 0xe000e000, &s->container); s->systick.timer = qemu_new_timer_ns(vm_clock, systick_timer_tick, s); return 0; } @@ -409,9 +509,12 @@ static Property armv7m_nvic_properties[] = { static void armv7m_nvic_class_init(ObjectClass *klass, void *data) { + NVICClass *nc = NVIC_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); + nc->parent_reset = dc->reset; + nc->parent_init = sdc->init; sdc->init = armv7m_nvic_init; dc->vmsd = &vmstate_nvic; dc->reset = armv7m_nvic_reset; @@ -419,10 +522,11 @@ static void armv7m_nvic_class_init(ObjectClass *klass, void *data) } static TypeInfo armv7m_nvic_info = { - .name = "armv7m_nvic", - .parent = TYPE_SYS_BUS_DEVICE, + .name = TYPE_NVIC, + .parent = TYPE_ARM_GIC_COMMON, .instance_size = sizeof(nvic_state), .class_init = armv7m_nvic_class_init, + .class_size = sizeof(NVICClass), }; static void armv7m_nvic_register_types(void) diff --git a/hw/cadence_gem.c b/hw/cadence_gem.c index e2140aea2b..dbde3920d0 100644 --- a/hw/cadence_gem.c +++ b/hw/cadence_gem.c @@ -664,7 +664,7 @@ static ssize_t gem_receive(VLANClientState *nc, const uint8_t *buf, size_t size) */ memcpy(rxbuf, buf, size); - memset(rxbuf + size, 0, sizeof(rxbuf - size)); + memset(rxbuf + size, 0, sizeof(rxbuf) - size); rxbuf_ptr = rxbuf; crc_val = cpu_to_le32(crc32(0, rxbuf, MAX(size, 60))); if (size < 60) { diff --git a/hw/cadence_ttc.c b/hw/cadence_ttc.c index 2b5477b688..dd02f86eb9 100644 --- a/hw/cadence_ttc.c +++ b/hw/cadence_ttc.c @@ -405,7 +405,7 @@ static int cadence_ttc_init(SysBusDevice *dev) int i; for (i = 0; i < 3; ++i) { - cadence_timer_init(2500000, &s->timer[i]); + cadence_timer_init(133000000, &s->timer[i]); sysbus_init_irq(dev, &s->timer[i].irq); } diff --git a/hw/exynos4210.c b/hw/exynos4210.c index dd14d01b01..9c20b3f22d 100644 --- a/hw/exynos4210.c +++ b/hw/exynos4210.c @@ -97,11 +97,11 @@ void exynos4210_write_secondary(ARMCPU *cpu, Exynos4210State *exynos4210_init(MemoryRegion *system_mem, unsigned long ram_size) { - qemu_irq cpu_irq[4]; - int n; + qemu_irq cpu_irq[EXYNOS4210_NCPUS]; + int i, n; Exynos4210State *s = g_new(Exynos4210State, 1); qemu_irq *irqp; - qemu_irq gate_irq[EXYNOS4210_IRQ_GATE_NINPUTS]; + qemu_irq gate_irq[EXYNOS4210_NCPUS][EXYNOS4210_IRQ_GATE_NINPUTS]; unsigned long mem_size; DeviceState *dev; SysBusDevice *busdev; @@ -128,16 +128,18 @@ Exynos4210State *exynos4210_init(MemoryRegion *system_mem, s->irq_table = exynos4210_init_irq(&s->irqs); /* IRQ Gate */ - dev = qdev_create(NULL, "exynos4210.irq_gate"); - qdev_init_nofail(dev); - /* Get IRQ Gate input in gate_irq */ - for (n = 0; n < EXYNOS4210_IRQ_GATE_NINPUTS; n++) { - gate_irq[n] = qdev_get_gpio_in(dev, n); - } - busdev = sysbus_from_qdev(dev); - /* Connect IRQ Gate output to cpu_irq */ - for (n = 0; n < EXYNOS4210_NCPUS; n++) { - sysbus_connect_irq(busdev, n, cpu_irq[n]); + for (i = 0; i < EXYNOS4210_NCPUS; i++) { + dev = qdev_create(NULL, "exynos4210.irq_gate"); + qdev_prop_set_uint32(dev, "n_in", EXYNOS4210_IRQ_GATE_NINPUTS); + qdev_init_nofail(dev); + /* Get IRQ Gate input in gate_irq */ + for (n = 0; n < EXYNOS4210_IRQ_GATE_NINPUTS; n++) { + gate_irq[i][n] = qdev_get_gpio_in(dev, n); + } + busdev = sysbus_from_qdev(dev); + + /* Connect IRQ Gate output to cpu_irq */ + sysbus_connect_irq(busdev, 0, cpu_irq[i]); } /* Private memory region and Internal GIC */ @@ -147,7 +149,7 @@ Exynos4210State *exynos4210_init(MemoryRegion *system_mem, busdev = sysbus_from_qdev(dev); sysbus_mmio_map(busdev, 0, EXYNOS4210_SMP_PRIVATE_BASE_ADDR); for (n = 0; n < EXYNOS4210_NCPUS; n++) { - sysbus_connect_irq(busdev, n, gate_irq[n * 2]); + sysbus_connect_irq(busdev, n, gate_irq[n][0]); } for (n = 0; n < EXYNOS4210_INT_GIC_NIRQ; n++) { s->irqs.int_gic_irq[n] = qdev_get_gpio_in(dev, n); @@ -166,7 +168,7 @@ Exynos4210State *exynos4210_init(MemoryRegion *system_mem, /* Map Distributer interface */ sysbus_mmio_map(busdev, 1, EXYNOS4210_EXT_GIC_DIST_BASE_ADDR); for (n = 0; n < EXYNOS4210_NCPUS; n++) { - sysbus_connect_irq(busdev, n, gate_irq[n * 2 + 1]); + sysbus_connect_irq(busdev, n, gate_irq[n][1]); } for (n = 0; n < EXYNOS4210_EXT_GIC_NIRQ; n++) { s->irqs.ext_gic_irq[n] = qdev_get_gpio_in(dev, n); diff --git a/hw/exynos4210.h b/hw/exynos4210.h index b1b4609054..9b1ae4c8b1 100644 --- a/hw/exynos4210.h +++ b/hw/exynos4210.h @@ -56,7 +56,7 @@ /* * exynos4210 IRQ subsystem stub definitions. */ -#define EXYNOS4210_IRQ_GATE_NINPUTS 8 +#define EXYNOS4210_IRQ_GATE_NINPUTS 2 /* Internal and External GIC */ #define EXYNOS4210_MAX_INT_COMBINER_OUT_IRQ 64 #define EXYNOS4210_MAX_EXT_COMBINER_OUT_IRQ 16 diff --git a/hw/exynos4210_gic.c b/hw/exynos4210_gic.c index e1b215eff0..7d03dd9ae3 100644 --- a/hw/exynos4210_gic.c +++ b/hw/exynos4210_gic.c @@ -362,61 +362,64 @@ static void exynos4210_gic_register_types(void) type_init(exynos4210_gic_register_types) -/* - * IRQGate struct. - * IRQ Gate represents OR gate between GICs to pass IRQ to PIC. +/* IRQ OR Gate struct. + * + * This device models an OR gate. There are n_in input qdev gpio lines and one + * output sysbus IRQ line. The output IRQ level is formed as OR between all + * gpio inputs. */ typedef struct { SysBusDevice busdev; - qemu_irq pic_irq[EXYNOS4210_NCPUS]; /* output IRQs to PICs */ - uint32_t gpio_level[EXYNOS4210_IRQ_GATE_NINPUTS]; /* Input levels */ + uint32_t n_in; /* inputs amount */ + uint32_t *level; /* input levels */ + qemu_irq out; /* output IRQ */ } Exynos4210IRQGateState; +static Property exynos4210_irq_gate_properties[] = { + DEFINE_PROP_UINT32("n_in", Exynos4210IRQGateState, n_in, 1), + DEFINE_PROP_END_OF_LIST(), +}; + static const VMStateDescription vmstate_exynos4210_irq_gate = { .name = "exynos4210.irq_gate", - .version_id = 1, - .minimum_version_id = 1, - .minimum_version_id_old = 1, + .version_id = 2, + .minimum_version_id = 2, + .minimum_version_id_old = 2, .fields = (VMStateField[]) { - VMSTATE_UINT32_ARRAY(gpio_level, Exynos4210IRQGateState, - EXYNOS4210_IRQ_GATE_NINPUTS), + VMSTATE_VBUFFER_UINT32(level, Exynos4210IRQGateState, 1, NULL, 0, n_in), VMSTATE_END_OF_LIST() } }; -/* Process a change in an external IRQ input. */ +/* Process a change in IRQ input. */ static void exynos4210_irq_gate_handler(void *opaque, int irq, int level) { - Exynos4210IRQGateState *s = - (Exynos4210IRQGateState *)opaque; - uint32_t odd, even; - - if (irq & 1) { - odd = irq; - even = irq & ~1; - } else { - even = irq; - odd = irq | 1; - } + Exynos4210IRQGateState *s = (Exynos4210IRQGateState *)opaque; + uint32_t i; - assert(irq < EXYNOS4210_IRQ_GATE_NINPUTS); - s->gpio_level[irq] = level; + assert(irq < s->n_in); - if (s->gpio_level[odd] >= 1 || s->gpio_level[even] >= 1) { - qemu_irq_raise(s->pic_irq[even >> 1]); - } else { - qemu_irq_lower(s->pic_irq[even >> 1]); + s->level[irq] = level; + + for (i = 0; i < s->n_in; i++) { + if (s->level[i] >= 1) { + qemu_irq_raise(s->out); + return; + } } + qemu_irq_lower(s->out); + return; } static void exynos4210_irq_gate_reset(DeviceState *d) { - Exynos4210IRQGateState *s = (Exynos4210IRQGateState *)d; + Exynos4210IRQGateState *s = + DO_UPCAST(Exynos4210IRQGateState, busdev.qdev, d); - memset(&s->gpio_level, 0, sizeof(s->gpio_level)); + memset(s->level, 0, s->n_in * sizeof(*s->level)); } /* @@ -424,19 +427,15 @@ static void exynos4210_irq_gate_reset(DeviceState *d) */ static int exynos4210_irq_gate_init(SysBusDevice *dev) { - unsigned int i; - Exynos4210IRQGateState *s = - FROM_SYSBUS(Exynos4210IRQGateState, dev); + Exynos4210IRQGateState *s = FROM_SYSBUS(Exynos4210IRQGateState, dev); /* Allocate general purpose input signals and connect a handler to each of * them */ - qdev_init_gpio_in(&s->busdev.qdev, exynos4210_irq_gate_handler, - EXYNOS4210_IRQ_GATE_NINPUTS); + qdev_init_gpio_in(&s->busdev.qdev, exynos4210_irq_gate_handler, s->n_in); - /* Connect SysBusDev irqs to device specific irqs */ - for (i = 0; i < EXYNOS4210_NCPUS; i++) { - sysbus_init_irq(dev, &s->pic_irq[i]); - } + s->level = g_malloc0(s->n_in * sizeof(*s->level)); + + sysbus_init_irq(dev, &s->out); return 0; } @@ -449,6 +448,7 @@ static void exynos4210_irq_gate_class_init(ObjectClass *klass, void *data) k->init = exynos4210_irq_gate_init; dc->reset = exynos4210_irq_gate_reset; dc->vmsd = &vmstate_exynos4210_irq_gate; + dc->props = exynos4210_irq_gate_properties; } static TypeInfo exynos4210_irq_gate_info = { @@ -36,6 +36,7 @@ #include "qdev-addr.h" #include "blockdev.h" #include "sysemu.h" +#include "qemu-log.h" /********************************************************/ /* debug Floppy devices */ @@ -48,9 +49,6 @@ #define FLOPPY_DPRINTF(fmt, ...) #endif -#define FLOPPY_ERROR(fmt, ...) \ - do { printf("FLOPPY ERROR: %s: " fmt, __func__ , ## __VA_ARGS__); } while (0) - /********************************************************/ /* Floppy drive emulation */ @@ -147,8 +145,10 @@ static int fd_seek(FDrive *drv, uint8_t head, uint8_t track, uint8_t sect, if (sector != fd_sector(drv)) { #if 0 if (!enable_seek) { - FLOPPY_ERROR("no implicit seek %d %02x %02x (max=%d %02x %02x)\n", - head, track, sect, 1, drv->max_track, drv->last_sect); + FLOPPY_DPRINTF("error: no implicit seek %d %02x %02x" + " (max=%d %02x %02x)\n", + head, track, sect, 1, drv->max_track, + drv->last_sect); return 4; } #endif @@ -991,7 +991,8 @@ static void fdctrl_set_fifo(FDCtrl *fdctrl, int fifo_len, int do_irq) /* Set an error: unimplemented/unknown command */ static void fdctrl_unimplemented(FDCtrl *fdctrl, int direction) { - FLOPPY_ERROR("unimplemented command 0x%02x\n", fdctrl->fifo[0]); + qemu_log_mask(LOG_UNIMP, "fdc: unimplemented command 0x%02x\n", + fdctrl->fifo[0]); fdctrl->fifo[0] = FD_SR0_INVCMD; fdctrl_set_fifo(fdctrl, 1, 0); } @@ -1159,7 +1160,8 @@ static void fdctrl_start_transfer(FDCtrl *fdctrl, int direction) DMA_schedule(fdctrl->dma_chann); return; } else { - FLOPPY_ERROR("dma_mode=%d direction=%d\n", dma_mode, direction); + FLOPPY_DPRINTF("bad dma_mode=%d direction=%d\n", dma_mode, + direction); } } FLOPPY_DPRINTF("start non-DMA transfer\n"); @@ -1175,7 +1177,7 @@ static void fdctrl_start_transfer(FDCtrl *fdctrl, int direction) /* Prepare a transfer of deleted data */ static void fdctrl_start_transfer_del(FDCtrl *fdctrl, int direction) { - FLOPPY_ERROR("fdctrl_start_transfer_del() unimplemented\n"); + qemu_log_mask(LOG_UNIMP, "fdctrl_start_transfer_del() unimplemented\n"); /* We don't handle deleted data, * so we don't return *ANYTHING* @@ -1254,7 +1256,8 @@ static int fdctrl_transfer_handler (void *opaque, int nchan, fdctrl->data_pos, len); if (bdrv_write(cur_drv->bs, fd_sector(cur_drv), fdctrl->fifo, 1) < 0) { - FLOPPY_ERROR("writing sector %d\n", fd_sector(cur_drv)); + FLOPPY_DPRINTF("error writing sector %d\n", + fd_sector(cur_drv)); fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK, 0x00, 0x00); goto transfer_error; } @@ -1313,7 +1316,7 @@ static uint32_t fdctrl_read_data(FDCtrl *fdctrl) cur_drv = get_cur_drv(fdctrl); fdctrl->dsr &= ~FD_DSR_PWRDOWN; if (!(fdctrl->msr & FD_MSR_RQM) || !(fdctrl->msr & FD_MSR_DIO)) { - FLOPPY_ERROR("controller not ready for reading\n"); + FLOPPY_DPRINTF("error: controller not ready for reading\n"); return 0; } pos = fdctrl->data_pos; @@ -1397,7 +1400,7 @@ static void fdctrl_format_sector(FDCtrl *fdctrl) memset(fdctrl->fifo, 0, FD_SECTOR_LEN); if (cur_drv->bs == NULL || bdrv_write(cur_drv->bs, fd_sector(cur_drv), fdctrl->fifo, 1) < 0) { - FLOPPY_ERROR("formatting sector %d\n", fd_sector(cur_drv)); + FLOPPY_DPRINTF("error formatting sector %d\n", fd_sector(cur_drv)); fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK, 0x00, 0x00); } else { if (cur_drv->sect == cur_drv->last_sect) { @@ -1772,7 +1775,7 @@ static void fdctrl_write_data(FDCtrl *fdctrl, uint32_t value) return; } if (!(fdctrl->msr & FD_MSR_RQM) || (fdctrl->msr & FD_MSR_DIO)) { - FLOPPY_ERROR("controller not ready for writing\n"); + FLOPPY_DPRINTF("error: controller not ready for writing\n"); return; } fdctrl->dsr &= ~FD_DSR_PWRDOWN; @@ -1786,7 +1789,8 @@ static void fdctrl_write_data(FDCtrl *fdctrl, uint32_t value) fdctrl->data_pos == fdctrl->data_len) { cur_drv = get_cur_drv(fdctrl); if (bdrv_write(cur_drv->bs, fd_sector(cur_drv), fdctrl->fifo, 1) < 0) { - FLOPPY_ERROR("writing sector %d\n", fd_sector(cur_drv)); + FLOPPY_DPRINTF("error writing sector %d\n", + fd_sector(cur_drv)); return; } if (!fdctrl_seek_to_next_sect(fdctrl, cur_drv)) { diff --git a/hw/loader.c b/hw/loader.c index 7d64113e7f..33acc2fdab 100644 --- a/hw/loader.c +++ b/hw/loader.c @@ -377,9 +377,9 @@ static void zfree(void *x, void *addr) #define DEFLATED 8 -/* This is the maximum in uboot, so if a uImage overflows this, it would +/* This is the usual maximum in uboot, so if a uImage overflows this, it would * overflow on real hardware too. */ -#define UBOOT_MAX_GUNZIP_BYTES 0x800000 +#define UBOOT_MAX_GUNZIP_BYTES (64 << 20) static ssize_t gunzip(void *dst, size_t dstlen, uint8_t *src, size_t srclen) @@ -998,7 +998,6 @@ enum { #define OMAP_GPIOSW_OUTPUT 0x0002 # define TCMI_VERBOSE 1 -//# define MEM_VERBOSE 1 # ifdef TCMI_VERBOSE # define OMAP_8B_REG(paddr) \ @@ -1018,98 +1017,4 @@ enum { # define OMAP_MPUI_REG_MASK 0x000007ff -# ifdef MEM_VERBOSE -struct io_fn { - CPUReadMemoryFunc * const *mem_read; - CPUWriteMemoryFunc * const *mem_write; - void *opaque; - int in; -}; - -static uint32_t io_readb(void *opaque, target_phys_addr_t addr) -{ - struct io_fn *s = opaque; - uint32_t ret; - - s->in ++; - ret = s->mem_read[0](s->opaque, addr); - s->in --; - if (!s->in) - fprintf(stderr, "%08x ---> %02x\n", (uint32_t) addr, ret); - return ret; -} -static uint32_t io_readh(void *opaque, target_phys_addr_t addr) -{ - struct io_fn *s = opaque; - uint32_t ret; - - s->in ++; - ret = s->mem_read[1](s->opaque, addr); - s->in --; - if (!s->in) - fprintf(stderr, "%08x ---> %04x\n", (uint32_t) addr, ret); - return ret; -} -static uint32_t io_readw(void *opaque, target_phys_addr_t addr) -{ - struct io_fn *s = opaque; - uint32_t ret; - - s->in ++; - ret = s->mem_read[2](s->opaque, addr); - s->in --; - if (!s->in) - fprintf(stderr, "%08x ---> %08x\n", (uint32_t) addr, ret); - return ret; -} -static void io_writeb(void *opaque, target_phys_addr_t addr, uint32_t value) -{ - struct io_fn *s = opaque; - - if (!s->in) - fprintf(stderr, "%08x <--- %02x\n", (uint32_t) addr, value); - s->in ++; - s->mem_write[0](s->opaque, addr, value); - s->in --; -} -static void io_writeh(void *opaque, target_phys_addr_t addr, uint32_t value) -{ - struct io_fn *s = opaque; - - if (!s->in) - fprintf(stderr, "%08x <--- %04x\n", (uint32_t) addr, value); - s->in ++; - s->mem_write[1](s->opaque, addr, value); - s->in --; -} -static void io_writew(void *opaque, target_phys_addr_t addr, uint32_t value) -{ - struct io_fn *s = opaque; - - if (!s->in) - fprintf(stderr, "%08x <--- %08x\n", (uint32_t) addr, value); - s->in ++; - s->mem_write[2](s->opaque, addr, value); - s->in --; -} - -static CPUReadMemoryFunc * const io_readfn[] = { io_readb, io_readh, io_readw, }; -static CPUWriteMemoryFunc * const io_writefn[] = { io_writeb, io_writeh, io_writew, }; - -inline static int debug_register_io_memory(CPUReadMemoryFunc * const *mem_read, - CPUWriteMemoryFunc * const *mem_write, - void *opaque) -{ - struct io_fn *s = g_malloc(sizeof(struct io_fn)); - - s->mem_read = mem_read; - s->mem_write = mem_write; - s->opaque = opaque; - s->in = 0; - return cpu_register_io_memory(io_readfn, io_writefn, s, - DEVICE_NATIVE_ENDIAN); -} -# define cpu_register_io_memory debug_register_io_memory -# endif - #endif /* hw_omap_h */ diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs index 44a1e8cdab..d18dbaf6cc 100644 --- a/hw/ppc/Makefile.objs +++ b/hw/ppc/Makefile.objs @@ -15,7 +15,7 @@ obj-$(CONFIG_PSERIES) += spapr_pci.o pci-hotplug.o obj-y += ppc4xx_devs.o ppc4xx_pci.o ppc405_uc.o ppc405_boards.o obj-y += ppc440_bamboo.o # PowerPC E500 boards -obj-y += ppce500_mpc8544ds.o mpc8544_guts.o ppce500_spin.o +obj-$(CONFIG_FDT) += ppce500_mpc8544ds.o mpc8544_guts.o ppce500_spin.o # PowerPC 440 Xilinx ML507 reference board. obj-y += virtex_ml507.o # PowerPC OpenPIC diff --git a/hw/ppce500_mpc8544ds.c b/hw/ppce500_mpc8544ds.c index 3eb8a23779..8b9fd83ce1 100644 --- a/hw/ppce500_mpc8544ds.c +++ b/hw/ppce500_mpc8544ds.c @@ -31,6 +31,7 @@ #include "elf.h" #include "sysbus.h" #include "exec-memory.h" +#include "host-utils.h" #define BINARY_DEVICE_TREE_FILE "mpc8544ds.dtb" #define UIMAGE_LOAD_BASE 0 @@ -41,57 +42,150 @@ #define RAM_SIZES_ALIGN (64UL << 20) -#define MPC8544_CCSRBAR_BASE 0xE0000000 -#define MPC8544_MPIC_REGS_BASE (MPC8544_CCSRBAR_BASE + 0x40000) -#define MPC8544_SERIAL0_REGS_BASE (MPC8544_CCSRBAR_BASE + 0x4500) -#define MPC8544_SERIAL1_REGS_BASE (MPC8544_CCSRBAR_BASE + 0x4600) -#define MPC8544_PCI_REGS_BASE (MPC8544_CCSRBAR_BASE + 0x8000) -#define MPC8544_PCI_REGS_SIZE 0x1000 -#define MPC8544_PCI_IO 0xE1000000 -#define MPC8544_PCI_IOLEN 0x10000 -#define MPC8544_UTIL_BASE (MPC8544_CCSRBAR_BASE + 0xe0000) -#define MPC8544_SPIN_BASE 0xEF000000 +#define MPC8544_CCSRBAR_BASE 0xE0000000ULL +#define MPC8544_CCSRBAR_SIZE 0x00100000ULL +#define MPC8544_MPIC_REGS_BASE (MPC8544_CCSRBAR_BASE + 0x40000ULL) +#define MPC8544_SERIAL0_REGS_BASE (MPC8544_CCSRBAR_BASE + 0x4500ULL) +#define MPC8544_SERIAL1_REGS_BASE (MPC8544_CCSRBAR_BASE + 0x4600ULL) +#define MPC8544_PCI_REGS_BASE (MPC8544_CCSRBAR_BASE + 0x8000ULL) +#define MPC8544_PCI_REGS_SIZE 0x1000ULL +#define MPC8544_PCI_IO 0xE1000000ULL +#define MPC8544_PCI_IOLEN 0x10000ULL +#define MPC8544_UTIL_BASE (MPC8544_CCSRBAR_BASE + 0xe0000ULL) +#define MPC8544_SPIN_BASE 0xEF000000ULL struct boot_info { uint32_t dt_base; + uint32_t dt_size; uint32_t entry; }; +static void pci_map_create(void *fdt, uint32_t *pci_map, uint32_t mpic) +{ + int i; + const uint32_t tmp[] = { + /* IDSEL 0x11 J17 Slot 1 */ + 0x8800, 0x0, 0x0, 0x1, mpic, 0x2, 0x1, 0x0, 0x0, + 0x8800, 0x0, 0x0, 0x2, mpic, 0x3, 0x1, 0x0, 0x0, + 0x8800, 0x0, 0x0, 0x3, mpic, 0x4, 0x1, 0x0, 0x0, + 0x8800, 0x0, 0x0, 0x4, mpic, 0x1, 0x1, 0x0, 0x0, + + /* IDSEL 0x12 J16 Slot 2 */ + 0x9000, 0x0, 0x0, 0x1, mpic, 0x3, 0x1, 0x0, 0x0, + 0x9000, 0x0, 0x0, 0x2, mpic, 0x4, 0x1, 0x0, 0x0, + 0x9000, 0x0, 0x0, 0x3, mpic, 0x2, 0x1, 0x0, 0x0, + 0x9000, 0x0, 0x0, 0x4, mpic, 0x1, 0x1, 0x0, 0x0, + }; + for (i = 0; i < ARRAY_SIZE(tmp); i++) { + pci_map[i] = cpu_to_be32(tmp[i]); + } +} + +static void dt_serial_create(void *fdt, unsigned long long offset, + const char *soc, const char *mpic, + const char *alias, int idx, bool defcon) +{ + char ser[128]; + + snprintf(ser, sizeof(ser), "%s/serial@%llx", soc, offset); + qemu_devtree_add_subnode(fdt, ser); + qemu_devtree_setprop_string(fdt, ser, "device_type", "serial"); + qemu_devtree_setprop_string(fdt, ser, "compatible", "ns16550"); + qemu_devtree_setprop_cells(fdt, ser, "reg", offset, 0x100); + qemu_devtree_setprop_cell(fdt, ser, "cell-index", idx); + qemu_devtree_setprop_cell(fdt, ser, "clock-frequency", 0); + qemu_devtree_setprop_cells(fdt, ser, "interrupts", 42, 2, 0, 0); + qemu_devtree_setprop_phandle(fdt, ser, "interrupt-parent", mpic); + qemu_devtree_setprop_string(fdt, "/aliases", alias, ser); + + if (defcon) { + qemu_devtree_setprop_string(fdt, "/chosen", "linux,stdout-path", ser); + } +} + static int mpc8544_load_device_tree(CPUPPCState *env, target_phys_addr_t addr, - uint32_t ramsize, + target_phys_addr_t ramsize, target_phys_addr_t initrd_base, target_phys_addr_t initrd_size, const char *kernel_cmdline) { int ret = -1; -#ifdef CONFIG_FDT - uint32_t mem_reg_property[] = {0, cpu_to_be32(ramsize)}; - char *filename; + uint64_t mem_reg_property[] = { 0, cpu_to_be64(ramsize) }; int fdt_size; void *fdt; uint8_t hypercall[16]; uint32_t clock_freq = 400000000; uint32_t tb_freq = 400000000; int i; + const char *compatible = "MPC8544DS\0MPC85xxDS"; + int compatible_len = sizeof("MPC8544DS\0MPC85xxDS"); + char compatible_sb[] = "fsl,mpc8544-immr\0simple-bus"; + char model[] = "MPC8544DS"; + char soc[128]; + char mpic[128]; + uint32_t mpic_ph; + char gutil[128]; + char pci[128]; + uint32_t pci_map[9 * 8]; + uint32_t pci_ranges[14] = + { + 0x2000000, 0x0, 0xc0000000, + 0x0, 0xc0000000, + 0x0, 0x20000000, + + 0x1000000, 0x0, 0x0, + 0x0, 0xe1000000, + 0x0, 0x10000, + }; + QemuOpts *machine_opts; + const char *dumpdtb = NULL; + const char *dtb_file = NULL; + + machine_opts = qemu_opts_find(qemu_find_opts("machine"), 0); + if (machine_opts) { + const char *tmp; + dumpdtb = qemu_opt_get(machine_opts, "dumpdtb"); + dtb_file = qemu_opt_get(machine_opts, "dtb"); + tmp = qemu_opt_get(machine_opts, "dt_compatible"); + if (tmp) { + compatible = tmp; + compatible_len = strlen(compatible) + 1; + } + } - filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE); - if (!filename) { - goto out; + if (dtb_file) { + char *filename; + filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, dtb_file); + if (!filename) { + goto out; + } + + fdt = load_device_tree(filename, &fdt_size); + if (!fdt) { + goto out; + } + goto done; } - fdt = load_device_tree(filename, &fdt_size); - g_free(filename); + + fdt = create_device_tree(&fdt_size); if (fdt == NULL) { goto out; } /* Manipulate device tree in memory. */ - ret = qemu_devtree_setprop(fdt, "/memory", "reg", mem_reg_property, - sizeof(mem_reg_property)); - if (ret < 0) - fprintf(stderr, "couldn't set /memory/reg\n"); + qemu_devtree_setprop_string(fdt, "/", "model", model); + qemu_devtree_setprop(fdt, "/", "compatible", compatible, compatible_len); + qemu_devtree_setprop_cell(fdt, "/", "#address-cells", 2); + qemu_devtree_setprop_cell(fdt, "/", "#size-cells", 2); + + qemu_devtree_add_subnode(fdt, "/memory"); + qemu_devtree_setprop_string(fdt, "/memory", "device_type", "memory"); + qemu_devtree_setprop(fdt, "/memory", "reg", mem_reg_property, + sizeof(mem_reg_property)); + qemu_devtree_add_subnode(fdt, "/chosen"); if (initrd_size) { ret = qemu_devtree_setprop_cell(fdt, "/chosen", "linux,initrd-start", initrd_base); @@ -117,6 +211,7 @@ static int mpc8544_load_device_tree(CPUPPCState *env, tb_freq = kvmppc_get_tbfreq(); /* indicate KVM hypercall interface */ + qemu_devtree_add_subnode(fdt, "/hypervisor"); qemu_devtree_setprop_string(fdt, "/hypervisor", "compatible", "linux,kvm"); kvmppc_get_hypercall(env, hypercall, sizeof(hypercall)); @@ -124,11 +219,16 @@ static int mpc8544_load_device_tree(CPUPPCState *env, hypercall, sizeof(hypercall)); } + /* Create CPU nodes */ + qemu_devtree_add_subnode(fdt, "/cpus"); + qemu_devtree_setprop_cell(fdt, "/cpus", "#address-cells", 1); + qemu_devtree_setprop_cell(fdt, "/cpus", "#size-cells", 0); + /* We need to generate the cpu nodes in reverse order, so Linux can pick the first node as boot node and be happy */ for (i = smp_cpus - 1; i >= 0; i--) { char cpu_name[128]; - uint64_t cpu_release_addr = cpu_to_be64(MPC8544_SPIN_BASE + (i * 0x20)); + uint64_t cpu_release_addr = MPC8544_SPIN_BASE + (i * 0x20); for (env = first_cpu; env != NULL; env = env->next_cpu) { if (env->cpu_index == i) { @@ -156,39 +256,133 @@ static int mpc8544_load_device_tree(CPUPPCState *env, if (env->cpu_index) { qemu_devtree_setprop_string(fdt, cpu_name, "status", "disabled"); qemu_devtree_setprop_string(fdt, cpu_name, "enable-method", "spin-table"); - qemu_devtree_setprop(fdt, cpu_name, "cpu-release-addr", - &cpu_release_addr, sizeof(cpu_release_addr)); + qemu_devtree_setprop_u64(fdt, cpu_name, "cpu-release-addr", + cpu_release_addr); } else { qemu_devtree_setprop_string(fdt, cpu_name, "status", "okay"); } } + qemu_devtree_add_subnode(fdt, "/aliases"); + /* XXX These should go into their respective devices' code */ + snprintf(soc, sizeof(soc), "/soc@%llx", MPC8544_CCSRBAR_BASE); + qemu_devtree_add_subnode(fdt, soc); + qemu_devtree_setprop_string(fdt, soc, "device_type", "soc"); + qemu_devtree_setprop(fdt, soc, "compatible", compatible_sb, + sizeof(compatible_sb)); + qemu_devtree_setprop_cell(fdt, soc, "#address-cells", 1); + qemu_devtree_setprop_cell(fdt, soc, "#size-cells", 1); + qemu_devtree_setprop_cells(fdt, soc, "ranges", 0x0, + MPC8544_CCSRBAR_BASE >> 32, MPC8544_CCSRBAR_BASE, + MPC8544_CCSRBAR_SIZE); + /* XXX should contain a reasonable value */ + qemu_devtree_setprop_cell(fdt, soc, "bus-frequency", 0); + + snprintf(mpic, sizeof(mpic), "%s/pic@%llx", soc, + MPC8544_MPIC_REGS_BASE - MPC8544_CCSRBAR_BASE); + qemu_devtree_add_subnode(fdt, mpic); + qemu_devtree_setprop_string(fdt, mpic, "device_type", "open-pic"); + qemu_devtree_setprop_string(fdt, mpic, "compatible", "fsl,mpic"); + qemu_devtree_setprop_cells(fdt, mpic, "reg", MPC8544_MPIC_REGS_BASE - + MPC8544_CCSRBAR_BASE, 0x40000); + qemu_devtree_setprop_cell(fdt, mpic, "#address-cells", 0); + qemu_devtree_setprop_cell(fdt, mpic, "#interrupt-cells", 4); + mpic_ph = qemu_devtree_alloc_phandle(fdt); + qemu_devtree_setprop_cell(fdt, mpic, "phandle", mpic_ph); + qemu_devtree_setprop_cell(fdt, mpic, "linux,phandle", mpic_ph); + qemu_devtree_setprop(fdt, mpic, "interrupt-controller", NULL, 0); + qemu_devtree_setprop(fdt, mpic, "big-endian", NULL, 0); + qemu_devtree_setprop(fdt, mpic, "single-cpu-affinity", NULL, 0); + qemu_devtree_setprop_cell(fdt, mpic, "last-interrupt-source", 255); + + /* + * We have to generate ser1 first, because Linux takes the first + * device it finds in the dt as serial output device. And we generate + * devices in reverse order to the dt. + */ + dt_serial_create(fdt, MPC8544_SERIAL1_REGS_BASE - MPC8544_CCSRBAR_BASE, + soc, mpic, "serial1", 1, false); + dt_serial_create(fdt, MPC8544_SERIAL0_REGS_BASE - MPC8544_CCSRBAR_BASE, + soc, mpic, "serial0", 0, true); + + snprintf(gutil, sizeof(gutil), "%s/global-utilities@%llx", soc, + MPC8544_UTIL_BASE - MPC8544_CCSRBAR_BASE); + qemu_devtree_add_subnode(fdt, gutil); + qemu_devtree_setprop_string(fdt, gutil, "compatible", "fsl,mpc8544-guts"); + qemu_devtree_setprop_cells(fdt, gutil, "reg", MPC8544_UTIL_BASE - + MPC8544_CCSRBAR_BASE, 0x1000); + qemu_devtree_setprop(fdt, gutil, "fsl,has-rstcr", NULL, 0); + + snprintf(pci, sizeof(pci), "/pci@%llx", MPC8544_PCI_REGS_BASE); + qemu_devtree_add_subnode(fdt, pci); + qemu_devtree_setprop_cell(fdt, pci, "cell-index", 0); + qemu_devtree_setprop_string(fdt, pci, "compatible", "fsl,mpc8540-pci"); + qemu_devtree_setprop_string(fdt, pci, "device_type", "pci"); + qemu_devtree_setprop_cells(fdt, pci, "interrupt-map-mask", 0xf800, 0x0, + 0x0, 0x7); + pci_map_create(fdt, pci_map, qemu_devtree_get_phandle(fdt, mpic)); + qemu_devtree_setprop(fdt, pci, "interrupt-map", pci_map, sizeof(pci_map)); + qemu_devtree_setprop_phandle(fdt, pci, "interrupt-parent", mpic); + qemu_devtree_setprop_cells(fdt, pci, "interrupts", 24, 2, 0, 0); + qemu_devtree_setprop_cells(fdt, pci, "bus-range", 0, 255); + for (i = 0; i < 14; i++) { + pci_ranges[i] = cpu_to_be32(pci_ranges[i]); + } + qemu_devtree_setprop(fdt, pci, "ranges", pci_ranges, sizeof(pci_ranges)); + qemu_devtree_setprop_cells(fdt, pci, "reg", MPC8544_PCI_REGS_BASE >> 32, + MPC8544_PCI_REGS_BASE, 0, 0x1000); + qemu_devtree_setprop_cell(fdt, pci, "clock-frequency", 66666666); + qemu_devtree_setprop_cell(fdt, pci, "#interrupt-cells", 1); + qemu_devtree_setprop_cell(fdt, pci, "#size-cells", 2); + qemu_devtree_setprop_cell(fdt, pci, "#address-cells", 3); + qemu_devtree_setprop_string(fdt, "/aliases", "pci0", pci); + +done: + if (dumpdtb) { + /* Dump the dtb to a file and quit */ + FILE *f = fopen(dumpdtb, "wb"); + size_t len; + len = fwrite(fdt, fdt_size, 1, f); + fclose(f); + if (len != fdt_size) { + exit(1); + } + exit(0); + } + ret = rom_add_blob_fixed(BINARY_DEVICE_TREE_FILE, fdt, fdt_size, addr); + if (ret < 0) { + goto out; + } g_free(fdt); + ret = fdt_size; out: -#endif return ret; } -/* Create -kernel TLB entries for BookE, linearly spanning 256MB. */ +/* Create -kernel TLB entries for BookE. */ static inline target_phys_addr_t booke206_page_size_to_tlb(uint64_t size) { - return ffs(size >> 10) - 1; + return 63 - clz64(size >> 10); } -static void mmubooke_create_initial_mapping(CPUPPCState *env, - target_ulong va, - target_phys_addr_t pa) +static void mmubooke_create_initial_mapping(CPUPPCState *env) { + struct boot_info *bi = env->load_info; ppcmas_tlb_t *tlb = booke206_get_tlbm(env, 1, 0, 0); - target_phys_addr_t size; - - size = (booke206_page_size_to_tlb(256 * 1024 * 1024) << MAS1_TSIZE_SHIFT); + target_phys_addr_t size, dt_end; + int ps; + + /* Our initial TLB entry needs to cover everything from 0 to + the device tree top */ + dt_end = bi->dt_base + bi->dt_size; + ps = booke206_page_size_to_tlb(dt_end) + 1; + size = (ps << MAS1_TSIZE_SHIFT); tlb->mas1 = MAS1_VALID | size; - tlb->mas2 = va & TARGET_PAGE_MASK; - tlb->mas7_3 = pa & TARGET_PAGE_MASK; + tlb->mas2 = 0; + tlb->mas7_3 = 0; tlb->mas7_3 |= MAS3_UR | MAS3_UW | MAS3_UX | MAS3_SR | MAS3_SW | MAS3_SX; env->tlb_dirty = true; @@ -220,7 +414,7 @@ static void mpc8544ds_cpu_reset(void *opaque) env->gpr[1] = (16<<20) - 8; env->gpr[3] = bi->dt_base; env->nip = bi->entry; - mmubooke_create_initial_mapping(env, 0, 0); + mmubooke_create_initial_mapping(env); } static void mpc8544ds_init(ram_addr_t ram_size, @@ -275,6 +469,7 @@ static void mpc8544ds_init(ram_addr_t ram_size, irqs[i][OPENPIC_OUTPUT_INT] = input[PPCE500_INPUT_INT]; irqs[i][OPENPIC_OUTPUT_CINT] = input[PPCE500_INPUT_CINT]; env->spr[SPR_BOOKE_PIR] = env->cpu_index = i; + env->mpic_cpu_base = MPC8544_MPIC_REGS_BASE + 0x20000; ppc_booke_timers_init(env, 400000000, PPC_TIMER_E500); @@ -379,13 +574,12 @@ static void mpc8544ds_init(ram_addr_t ram_size, /* If we're loading a kernel directly, we must load the device tree too. */ if (kernel_filename) { struct boot_info *boot_info; + int dt_size; -#ifndef CONFIG_FDT - cpu_abort(env, "Compiled without FDT support - can't load kernel\n"); -#endif - dt_base = (kernel_size + DTC_LOAD_PAD) & ~DTC_PAD_MASK; - if (mpc8544_load_device_tree(env, dt_base, ram_size, - initrd_base, initrd_size, kernel_cmdline) < 0) { + dt_base = (loadaddr + kernel_size + DTC_LOAD_PAD) & ~DTC_PAD_MASK; + dt_size = mpc8544_load_device_tree(env, dt_base, ram_size, initrd_base, + initrd_size, kernel_cmdline); + if (dt_size < 0) { fprintf(stderr, "couldn't load device tree\n"); exit(1); } @@ -393,6 +587,7 @@ static void mpc8544ds_init(ram_addr_t ram_size, boot_info = env->load_info; boot_info->entry = entry; boot_info->dt_base = dt_base; + boot_info->dt_size = dt_size; } if (kvm_enabled()) { diff --git a/hw/pxa2xx.c b/hw/pxa2xx.c index 7958d14003..d5f1420ed9 100644 --- a/hw/pxa2xx.c +++ b/hw/pxa2xx.c @@ -224,210 +224,161 @@ static const VMStateDescription vmstate_pxa2xx_cm = { } }; -static uint32_t pxa2xx_clkpwr_read(void *opaque, int op2, int reg, int crm) +static int pxa2xx_clkcfg_read(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t *value) { - PXA2xxState *s = (PXA2xxState *) opaque; - - switch (reg) { - case 6: /* Clock Configuration register */ - return s->clkcfg; - - case 7: /* Power Mode register */ - return 0; + PXA2xxState *s = (PXA2xxState *)ri->opaque; + *value = s->clkcfg; + return 0; +} - default: - printf("%s: Bad register 0x%x\n", __FUNCTION__, reg); - break; +static int pxa2xx_clkcfg_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + PXA2xxState *s = (PXA2xxState *)ri->opaque; + s->clkcfg = value & 0xf; + if (value & 2) { + printf("%s: CPU frequency change attempt\n", __func__); } return 0; } -static void pxa2xx_clkpwr_write(void *opaque, int op2, int reg, int crm, - uint32_t value) +static int pxa2xx_pwrmode_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) { - PXA2xxState *s = (PXA2xxState *) opaque; + PXA2xxState *s = (PXA2xxState *)ri->opaque; static const char *pwrmode[8] = { "Normal", "Idle", "Deep-idle", "Standby", "Sleep", "reserved (!)", "reserved (!)", "Deep-sleep", }; - switch (reg) { - case 6: /* Clock Configuration register */ - s->clkcfg = value & 0xf; - if (value & 2) - printf("%s: CPU frequency change attempt\n", __FUNCTION__); + if (value & 8) { + printf("%s: CPU voltage change attempt\n", __func__); + } + switch (value & 7) { + case 0: + /* Do nothing */ break; - case 7: /* Power Mode register */ - if (value & 8) - printf("%s: CPU voltage change attempt\n", __FUNCTION__); - switch (value & 7) { - case 0: - /* Do nothing */ + case 1: + /* Idle */ + if (!(s->cm_regs[CCCR >> 2] & (1 << 31))) { /* CPDIS */ + cpu_interrupt(&s->cpu->env, CPU_INTERRUPT_HALT); break; + } + /* Fall through. */ - case 1: - /* Idle */ - if (!(s->cm_regs[CCCR >> 2] & (1 << 31))) { /* CPDIS */ - cpu_interrupt(&s->cpu->env, CPU_INTERRUPT_HALT); - break; - } - /* Fall through. */ - - case 2: - /* Deep-Idle */ - cpu_interrupt(&s->cpu->env, CPU_INTERRUPT_HALT); - s->pm_regs[RCSR >> 2] |= 0x8; /* Set GPR */ - goto message; - - case 3: - s->cpu->env.uncached_cpsr = - ARM_CPU_MODE_SVC | CPSR_A | CPSR_F | CPSR_I; - s->cpu->env.cp15.c1_sys = 0; - s->cpu->env.cp15.c1_coproc = 0; - s->cpu->env.cp15.c2_base0 = 0; - s->cpu->env.cp15.c3 = 0; - s->pm_regs[PSSR >> 2] |= 0x8; /* Set STS */ - s->pm_regs[RCSR >> 2] |= 0x8; /* Set GPR */ - - /* - * The scratch-pad register is almost universally used - * for storing the return address on suspend. For the - * lack of a resuming bootloader, perform a jump - * directly to that address. - */ - memset(s->cpu->env.regs, 0, 4 * 15); - s->cpu->env.regs[15] = s->pm_regs[PSPR >> 2]; + case 2: + /* Deep-Idle */ + cpu_interrupt(&s->cpu->env, CPU_INTERRUPT_HALT); + s->pm_regs[RCSR >> 2] |= 0x8; /* Set GPR */ + goto message; + + case 3: + s->cpu->env.uncached_cpsr = + ARM_CPU_MODE_SVC | CPSR_A | CPSR_F | CPSR_I; + s->cpu->env.cp15.c1_sys = 0; + s->cpu->env.cp15.c1_coproc = 0; + s->cpu->env.cp15.c2_base0 = 0; + s->cpu->env.cp15.c3 = 0; + s->pm_regs[PSSR >> 2] |= 0x8; /* Set STS */ + s->pm_regs[RCSR >> 2] |= 0x8; /* Set GPR */ + + /* + * The scratch-pad register is almost universally used + * for storing the return address on suspend. For the + * lack of a resuming bootloader, perform a jump + * directly to that address. + */ + memset(s->cpu->env.regs, 0, 4 * 15); + s->cpu->env.regs[15] = s->pm_regs[PSPR >> 2]; #if 0 - buffer = 0xe59ff000; /* ldr pc, [pc, #0] */ - cpu_physical_memory_write(0, &buffer, 4); - buffer = s->pm_regs[PSPR >> 2]; - cpu_physical_memory_write(8, &buffer, 4); + buffer = 0xe59ff000; /* ldr pc, [pc, #0] */ + cpu_physical_memory_write(0, &buffer, 4); + buffer = s->pm_regs[PSPR >> 2]; + cpu_physical_memory_write(8, &buffer, 4); #endif - /* Suspend */ - cpu_interrupt(cpu_single_env, CPU_INTERRUPT_HALT); + /* Suspend */ + cpu_interrupt(cpu_single_env, CPU_INTERRUPT_HALT); - goto message; - - default: - message: - printf("%s: machine entered %s mode\n", __FUNCTION__, - pwrmode[value & 7]); - } - break; + goto message; default: - printf("%s: Bad register 0x%x\n", __FUNCTION__, reg); - break; + message: + printf("%s: machine entered %s mode\n", __func__, + pwrmode[value & 7]); } -} - -/* Performace Monitoring Registers */ -#define CPPMNC 0 /* Performance Monitor Control register */ -#define CPCCNT 1 /* Clock Counter register */ -#define CPINTEN 4 /* Interrupt Enable register */ -#define CPFLAG 5 /* Overflow Flag register */ -#define CPEVTSEL 8 /* Event Selection register */ -#define CPPMN0 0 /* Performance Count register 0 */ -#define CPPMN1 1 /* Performance Count register 1 */ -#define CPPMN2 2 /* Performance Count register 2 */ -#define CPPMN3 3 /* Performance Count register 3 */ + return 0; +} -static uint32_t pxa2xx_perf_read(void *opaque, int op2, int reg, int crm) +static int pxa2xx_cppmnc_read(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t *value) { - PXA2xxState *s = (PXA2xxState *) opaque; - - switch (reg) { - case CPPMNC: - return s->pmnc; - case CPCCNT: - if (s->pmnc & 1) - return qemu_get_clock_ns(vm_clock); - else - return 0; - case CPINTEN: - case CPFLAG: - case CPEVTSEL: - return 0; - - default: - printf("%s: Bad register 0x%x\n", __FUNCTION__, reg); - break; - } + PXA2xxState *s = (PXA2xxState *)ri->opaque; + *value = s->pmnc; return 0; } -static void pxa2xx_perf_write(void *opaque, int op2, int reg, int crm, - uint32_t value) +static int pxa2xx_cppmnc_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) { - PXA2xxState *s = (PXA2xxState *) opaque; - - switch (reg) { - case CPPMNC: - s->pmnc = value; - break; - - case CPCCNT: - case CPINTEN: - case CPFLAG: - case CPEVTSEL: - break; - - default: - printf("%s: Bad register 0x%x\n", __FUNCTION__, reg); - break; - } + PXA2xxState *s = (PXA2xxState *)ri->opaque; + s->pmnc = value; + return 0; } -static uint32_t pxa2xx_cp14_read(void *opaque, int op2, int reg, int crm) +static int pxa2xx_cpccnt_read(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t *value) { - switch (crm) { - case 0: - return pxa2xx_clkpwr_read(opaque, op2, reg, crm); - case 1: - return pxa2xx_perf_read(opaque, op2, reg, crm); - case 2: - switch (reg) { - case CPPMN0: - case CPPMN1: - case CPPMN2: - case CPPMN3: - return 0; - } - /* Fall through */ - default: - printf("%s: Bad register 0x%x\n", __FUNCTION__, reg); - break; + PXA2xxState *s = (PXA2xxState *)ri->opaque; + if (s->pmnc & 1) { + *value = qemu_get_clock_ns(vm_clock); + } else { + *value = 0; } return 0; } -static void pxa2xx_cp14_write(void *opaque, int op2, int reg, int crm, - uint32_t value) +static const ARMCPRegInfo pxa_cp_reginfo[] = { + /* cp14 crn==1: perf registers */ + { .name = "CPPMNC", .cp = 14, .crn = 1, .crm = 0, .opc1 = 0, .opc2 = 0, + .access = PL1_RW, + .readfn = pxa2xx_cppmnc_read, .writefn = pxa2xx_cppmnc_write }, + { .name = "CPCCNT", .cp = 14, .crn = 1, .crm = 1, .opc1 = 0, .opc2 = 0, + .access = PL1_RW, + .readfn = pxa2xx_cpccnt_read, .writefn = arm_cp_write_ignore }, + { .name = "CPINTEN", .cp = 14, .crn = 1, .crm = 4, .opc1 = 0, .opc2 = 0, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "CPFLAG", .cp = 14, .crn = 1, .crm = 5, .opc1 = 0, .opc2 = 0, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "CPEVTSEL", .cp = 14, .crn = 1, .crm = 8, .opc1 = 0, .opc2 = 0, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + /* cp14 crn==2: performance count registers */ + { .name = "CPPMN0", .cp = 14, .crn = 2, .crm = 0, .opc1 = 0, .opc2 = 0, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "CPPMN1", .cp = 14, .crn = 2, .crm = 1, .opc1 = 0, .opc2 = 0, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "CPPMN2", .cp = 14, .crn = 2, .crm = 2, .opc1 = 0, .opc2 = 0, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "CPPMN3", .cp = 14, .crn = 2, .crm = 3, .opc1 = 0, .opc2 = 0, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + /* cp14 crn==6: CLKCFG */ + { .name = "CLKCFG", .cp = 14, .crn = 6, .crm = 0, .opc1 = 0, .opc2 = 0, + .access = PL1_RW, + .readfn = pxa2xx_clkcfg_read, .writefn = pxa2xx_clkcfg_write }, + /* cp14 crn==7: PWRMODE */ + { .name = "PWRMODE", .cp = 14, .crn = 7, .crm = 0, .opc1 = 0, .opc2 = 0, + .access = PL1_RW, + .readfn = arm_cp_read_zero, .writefn = pxa2xx_pwrmode_write }, + REGINFO_SENTINEL +}; + +static void pxa2xx_setup_cp14(PXA2xxState *s) { - switch (crm) { - case 0: - pxa2xx_clkpwr_write(opaque, op2, reg, crm, value); - break; - case 1: - pxa2xx_perf_write(opaque, op2, reg, crm, value); - break; - case 2: - switch (reg) { - case CPPMN0: - case CPPMN1: - case CPPMN2: - case CPPMN3: - return; - } - /* Fall through */ - default: - printf("%s: Bad register 0x%x\n", __FUNCTION__, reg); - break; - } + define_arm_cp_regs_with_opaque(s->cpu, pxa_cp_reginfo, s); } #define MDCNFG 0x00 /* SDRAM Configuration register */ @@ -2133,7 +2084,7 @@ PXA2xxState *pxa270_init(MemoryRegion *address_space, memory_region_add_subregion(address_space, s->cm_base, &s->cm_iomem); vmstate_register(NULL, 0, &vmstate_pxa2xx_cm, s); - cpu_arm_set_cp_io(&s->cpu->env, 14, pxa2xx_cp14_read, pxa2xx_cp14_write, s); + pxa2xx_setup_cp14(s); s->mm_base = 0x48000000; s->mm_regs[MDMRS >> 2] = 0x00020002; @@ -2264,7 +2215,7 @@ PXA2xxState *pxa255_init(MemoryRegion *address_space, unsigned int sdram_size) memory_region_add_subregion(address_space, s->cm_base, &s->cm_iomem); vmstate_register(NULL, 0, &vmstate_pxa2xx_cm, s); - cpu_arm_set_cp_io(&s->cpu->env, 14, pxa2xx_cp14_read, pxa2xx_cp14_write, s); + pxa2xx_setup_cp14(s); s->mm_base = 0x48000000; s->mm_regs[MDMRS >> 2] = 0x00020002; diff --git a/hw/pxa2xx_pic.c b/hw/pxa2xx_pic.c index c560133930..e1e8830ff0 100644 --- a/hw/pxa2xx_pic.c +++ b/hw/pxa2xx_pic.c @@ -209,33 +209,42 @@ static const int pxa2xx_cp_reg_map[0x10] = { [0xa] = ICPR2, }; -static uint32_t pxa2xx_pic_cp_read(void *opaque, int op2, int reg, int crm) +static int pxa2xx_pic_cp_read(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t *value) { - target_phys_addr_t offset; - - if (pxa2xx_cp_reg_map[reg] == -1) { - printf("%s: Bad register 0x%x\n", __FUNCTION__, reg); - return 0; - } - - offset = pxa2xx_cp_reg_map[reg]; - return pxa2xx_pic_mem_read(opaque, offset, 4); + int offset = pxa2xx_cp_reg_map[ri->crn]; + *value = pxa2xx_pic_mem_read(ri->opaque, offset, 4); + return 0; } -static void pxa2xx_pic_cp_write(void *opaque, int op2, int reg, int crm, - uint32_t value) +static int pxa2xx_pic_cp_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) { - target_phys_addr_t offset; - - if (pxa2xx_cp_reg_map[reg] == -1) { - printf("%s: Bad register 0x%x\n", __FUNCTION__, reg); - return; - } - - offset = pxa2xx_cp_reg_map[reg]; - pxa2xx_pic_mem_write(opaque, offset, value, 4); + int offset = pxa2xx_cp_reg_map[ri->crn]; + pxa2xx_pic_mem_write(ri->opaque, offset, value, 4); + return 0; } +#define REGINFO_FOR_PIC_CP(NAME, CRN) \ + { .name = NAME, .cp = 6, .crn = CRN, .crm = 0, .opc1 = 0, .opc2 = 0, \ + .access = PL1_RW, \ + .readfn = pxa2xx_pic_cp_read, .writefn = pxa2xx_pic_cp_write } + +static const ARMCPRegInfo pxa_pic_cp_reginfo[] = { + REGINFO_FOR_PIC_CP("ICIP", 0), + REGINFO_FOR_PIC_CP("ICMR", 1), + REGINFO_FOR_PIC_CP("ICLR", 2), + REGINFO_FOR_PIC_CP("ICFP", 3), + REGINFO_FOR_PIC_CP("ICPR", 4), + REGINFO_FOR_PIC_CP("ICHP", 5), + REGINFO_FOR_PIC_CP("ICIP2", 6), + REGINFO_FOR_PIC_CP("ICMR2", 7), + REGINFO_FOR_PIC_CP("ICLR2", 8), + REGINFO_FOR_PIC_CP("ICFP2", 9), + REGINFO_FOR_PIC_CP("ICPR2", 0xa), + REGINFO_SENTINEL +}; + static const MemoryRegionOps pxa2xx_pic_ops = { .read = pxa2xx_pic_mem_read, .write = pxa2xx_pic_mem_write, @@ -274,7 +283,7 @@ DeviceState *pxa2xx_pic_init(target_phys_addr_t base, ARMCPU *cpu) sysbus_mmio_map(sysbus_from_qdev(dev), 0, base); /* Enable IC coprocessor access. */ - cpu_arm_set_cp_io(env, 6, pxa2xx_pic_cp_read, pxa2xx_pic_cp_write, s); + define_arm_cp_regs_with_opaque(arm_env_get_cpu(env), pxa_pic_cp_reginfo, s); return dev; } diff --git a/hw/qdev-monitor.c b/hw/qdev-monitor.c index 17452c8c01..7915b4500d 100644 --- a/hw/qdev-monitor.c +++ b/hw/qdev-monitor.c @@ -20,6 +20,7 @@ #include "qdev.h" #include "monitor.h" #include "qmp-commands.h" +#include "arch_init.h" /* * Aliases were a bad idea from the start. Let's keep them @@ -29,16 +30,18 @@ typedef struct QDevAlias { const char *typename; const char *alias; + uint32_t arch_mask; } QDevAlias; static const QDevAlias qdev_alias_table[] = { - { "virtio-blk-pci", "virtio-blk" }, - { "virtio-net-pci", "virtio-net" }, - { "virtio-serial-pci", "virtio-serial" }, - { "virtio-balloon-pci", "virtio-balloon" }, - { "virtio-blk-s390", "virtio-blk" }, - { "virtio-net-s390", "virtio-net" }, - { "virtio-serial-s390", "virtio-serial" }, + { "virtio-blk-pci", "virtio-blk", QEMU_ARCH_ALL & ~QEMU_ARCH_S390X }, + { "virtio-net-pci", "virtio-net", QEMU_ARCH_ALL & ~QEMU_ARCH_S390X }, + { "virtio-serial-pci", "virtio-serial", QEMU_ARCH_ALL & ~QEMU_ARCH_S390X }, + { "virtio-balloon-pci", "virtio-balloon", + QEMU_ARCH_ALL & ~QEMU_ARCH_S390X }, + { "virtio-blk-s390", "virtio-blk", QEMU_ARCH_S390X }, + { "virtio-net-s390", "virtio-net", QEMU_ARCH_S390X }, + { "virtio-serial-s390", "virtio-serial", QEMU_ARCH_S390X }, { "lsi53c895a", "lsi" }, { "ich9-ahci", "ahci" }, { } @@ -50,6 +53,11 @@ static const char *qdev_class_get_alias(DeviceClass *dc) int i; for (i = 0; qdev_alias_table[i].typename; i++) { + if (qdev_alias_table[i].arch_mask && + !(qdev_alias_table[i].arch_mask & arch_type)) { + continue; + } + if (strcmp(qdev_alias_table[i].typename, typename) == 0) { return qdev_alias_table[i].alias; } @@ -110,6 +118,11 @@ static const char *find_typename_by_alias(const char *alias) int i; for (i = 0; qdev_alias_table[i].alias; i++) { + if (qdev_alias_table[i].arch_mask && + !(qdev_alias_table[i].arch_mask & arch_type)) { + continue; + } + if (strcmp(qdev_alias_table[i].alias, alias) == 0) { return qdev_alias_table[i].typename; } diff --git a/hw/spapr.c b/hw/spapr.c index d0bddbce95..09a23ff092 100644 --- a/hw/spapr.c +++ b/hw/spapr.c @@ -146,6 +146,40 @@ static int spapr_set_associativity(void *fdt, sPAPREnvironment *spapr) return ret; } + +static size_t create_page_sizes_prop(CPUPPCState *env, uint32_t *prop, + size_t maxsize) +{ + size_t maxcells = maxsize / sizeof(uint32_t); + int i, j, count; + uint32_t *p = prop; + + for (i = 0; i < PPC_PAGE_SIZES_MAX_SZ; i++) { + struct ppc_one_seg_page_size *sps = &env->sps.sps[i]; + + if (!sps->page_shift) { + break; + } + for (count = 0; count < PPC_PAGE_SIZES_MAX_SZ; count++) { + if (sps->enc[count].page_shift == 0) { + break; + } + } + if ((p - prop) >= (maxcells - 3 - count * 2)) { + break; + } + *(p++) = cpu_to_be32(sps->page_shift); + *(p++) = cpu_to_be32(sps->slb_enc); + *(p++) = cpu_to_be32(count); + for (j = 0; j < count; j++) { + *(p++) = cpu_to_be32(sps->enc[j].page_shift); + *(p++) = cpu_to_be32(sps->enc[j].pte_enc); + } + } + + return (p - prop) * sizeof(uint32_t); +} + static void *spapr_create_fdt_skel(const char *cpu_model, target_phys_addr_t rma_size, target_phys_addr_t initrd_base, @@ -163,6 +197,7 @@ static void *spapr_create_fdt_skel(const char *cpu_model, uint32_t pft_size_prop[] = {0, cpu_to_be32(hash_shift)}; char hypertas_prop[] = "hcall-pft\0hcall-term\0hcall-dabr\0hcall-interrupt" "\0hcall-tce\0hcall-vio\0hcall-splpar\0hcall-bulk"; + char qemu_hypertas_prop[] = "hcall-memop1"; uint32_t interrupt_server_ranges_prop[] = {0, cpu_to_be32(smp_cpus)}; int i; char *modelname; @@ -298,6 +333,8 @@ static void *spapr_create_fdt_skel(const char *cpu_model, 0xffffffff, 0xffffffff}; uint32_t tbfreq = kvm_enabled() ? kvmppc_get_tbfreq() : TIMEBASE_FREQ; uint32_t cpufreq = kvm_enabled() ? kvmppc_get_clockfreq() : 1000000000; + uint32_t page_sizes_prop[64]; + size_t page_sizes_prop_size; if ((index % smt) != 0) { continue; @@ -362,6 +399,13 @@ static void *spapr_create_fdt_skel(const char *cpu_model, _FDT((fdt_property_cell(fdt, "ibm,dfp", 1))); } + page_sizes_prop_size = create_page_sizes_prop(env, page_sizes_prop, + sizeof(page_sizes_prop)); + if (page_sizes_prop_size) { + _FDT((fdt_property(fdt, "ibm,segment-page-sizes", + page_sizes_prop, page_sizes_prop_size))); + } + _FDT((fdt_end_node(fdt))); } @@ -374,6 +418,8 @@ static void *spapr_create_fdt_skel(const char *cpu_model, _FDT((fdt_property(fdt, "ibm,hypertas-functions", hypertas_prop, sizeof(hypertas_prop)))); + _FDT((fdt_property(fdt, "qemu,hypertas-functions", qemu_hypertas_prop, + sizeof(qemu_hypertas_prop)))); _FDT((fdt_property(fdt, "ibm,associativity-reference-points", refpoints, sizeof(refpoints)))); diff --git a/hw/spapr.h b/hw/spapr.h index 654a7a8a34..c75172e0c0 100644 --- a/hw/spapr.h +++ b/hw/spapr.h @@ -264,7 +264,8 @@ typedef struct sPAPREnvironment { */ #define KVMPPC_HCALL_BASE 0xf000 #define KVMPPC_H_RTAS (KVMPPC_HCALL_BASE + 0x0) -#define KVMPPC_HCALL_MAX KVMPPC_H_RTAS +#define KVMPPC_H_LOGICAL_MEMOP (KVMPPC_HCALL_BASE + 0x1) +#define KVMPPC_HCALL_MAX KVMPPC_H_LOGICAL_MEMOP extern sPAPREnvironment *spapr; diff --git a/hw/spapr_hcall.c b/hw/spapr_hcall.c index 94bb504ca6..a5990a9617 100644 --- a/hw/spapr_hcall.c +++ b/hw/spapr_hcall.c @@ -608,6 +608,73 @@ static target_ulong h_logical_store(CPUPPCState *env, sPAPREnvironment *spapr, return H_PARAMETER; } +static target_ulong h_logical_memop(CPUPPCState *env, sPAPREnvironment *spapr, + target_ulong opcode, target_ulong *args) +{ + target_ulong dst = args[0]; /* Destination address */ + target_ulong src = args[1]; /* Source address */ + target_ulong esize = args[2]; /* Element size (0=1,1=2,2=4,3=8) */ + target_ulong count = args[3]; /* Element count */ + target_ulong op = args[4]; /* 0 = copy, 1 = invert */ + uint64_t tmp; + unsigned int mask = (1 << esize) - 1; + int step = 1 << esize; + + if (count > 0x80000000) { + return H_PARAMETER; + } + + if ((dst & mask) || (src & mask) || (op > 1)) { + return H_PARAMETER; + } + + if (dst >= src && dst < (src + (count << esize))) { + dst = dst + ((count - 1) << esize); + src = src + ((count - 1) << esize); + step = -step; + } + + while (count--) { + switch (esize) { + case 0: + tmp = ldub_phys(src); + break; + case 1: + tmp = lduw_phys(src); + break; + case 2: + tmp = ldl_phys(src); + break; + case 3: + tmp = ldq_phys(src); + break; + default: + return H_PARAMETER; + } + if (op == 1) { + tmp = ~tmp; + } + switch (esize) { + case 0: + stb_phys(dst, tmp); + break; + case 1: + stw_phys(dst, tmp); + break; + case 2: + stl_phys(dst, tmp); + break; + case 3: + stq_phys(dst, tmp); + break; + } + dst = dst + step; + src = src + step; + } + + return H_SUCCESS; +} + static target_ulong h_logical_icbi(CPUPPCState *env, sPAPREnvironment *spapr, target_ulong opcode, target_ulong *args) { @@ -700,6 +767,7 @@ static void hypercall_register_types(void) spapr_register_hypercall(H_LOGICAL_CACHE_STORE, h_logical_store); spapr_register_hypercall(H_LOGICAL_ICBI, h_logical_icbi); spapr_register_hypercall(H_LOGICAL_DCBF, h_logical_dcbf); + spapr_register_hypercall(KVMPPC_H_LOGICAL_MEMOP, h_logical_memop); /* qemu/KVM-PPC specific hcalls */ spapr_register_hypercall(KVMPPC_H_RTAS, h_rtas); diff --git a/hw/spapr_vscsi.c b/hw/spapr_vscsi.c index 037867ab4f..2f09616dd5 100644 --- a/hw/spapr_vscsi.c +++ b/hw/spapr_vscsi.c @@ -800,6 +800,7 @@ static void vscsi_got_payload(VSCSIState *s, vscsi_crq *crq) if (crq->s.IU_length > sizeof(union viosrp_iu)) { fprintf(stderr, "VSCSI: SRP IU too long (%d bytes) !\n", crq->s.IU_length); + vscsi_put_req(req); return; } @@ -807,7 +808,8 @@ static void vscsi_got_payload(VSCSIState *s, vscsi_crq *crq) if (spapr_tce_dma_read(&s->vdev, crq->s.IU_data_ptr, &req->iu, crq->s.IU_length)) { fprintf(stderr, "vscsi_got_payload: DMA read failure !\n"); - g_free(req); + vscsi_put_req(req); + return; } memcpy(&req->crq, crq, sizeof(vscsi_crq)); diff --git a/hw/xilinx_timer.c b/hw/xilinx_timer.c index 0683ce1ecf..b562bd065e 100644 --- a/hw/xilinx_timer.c +++ b/hw/xilinx_timer.c @@ -23,7 +23,6 @@ */ #include "sysbus.h" -#include "qemu-timer.h" #include "ptimer.h" #define D(x) @@ -137,7 +136,7 @@ static void timer_enable(struct xlx_timer *xt) count = xt->regs[R_TLR]; else count = ~0 - xt->regs[R_TLR]; - ptimer_set_count(xt->ptimer, count); + ptimer_set_limit(xt->ptimer, count, 1); ptimer_run(xt->ptimer, 1); } |