diff --git a/default-configs/or1k-softmmu.mak b/default-configs/or1k-softmmu.mak index 10bfa7abb898ceddad15ccd514d97611f1efff85..6f5824fd488828c87b1f4168e3753473e602ceeb 100644 --- a/default-configs/or1k-softmmu.mak +++ b/default-configs/or1k-softmmu.mak @@ -2,3 +2,4 @@ CONFIG_SERIAL=y CONFIG_OPENCORES_ETH=y +CONFIG_OMPIC=y diff --git a/hw/intc/Makefile.objs b/hw/intc/Makefile.objs index 78426a7dafcd4a5718823c8fd550d5d0c44380d8..ae358569a155df4ed351b78ee7e38f258ee849db 100644 --- a/hw/intc/Makefile.objs +++ b/hw/intc/Makefile.objs @@ -43,3 +43,4 @@ obj-$(CONFIG_ASPEED_SOC) += aspeed_vic.o obj-$(CONFIG_ARM_GIC) += arm_gicv3_cpuif.o obj-$(CONFIG_MIPS_CPS) += mips_gic.o obj-$(CONFIG_NIOS2) += nios2_iic.o +obj-$(CONFIG_OMPIC) += ompic.o diff --git a/hw/intc/ompic.c b/hw/intc/ompic.c new file mode 100644 index 0000000000000000000000000000000000000000..c0e34d1268a437a07b20544682cfa83925ffd2a3 --- /dev/null +++ b/hw/intc/ompic.c @@ -0,0 +1,179 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Authors: Stafford Horne + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qapi/error.h" +#include "hw/hw.h" +#include "hw/sysbus.h" +#include "exec/memory.h" + +#define TYPE_OR1K_OMPIC "or1k-ompic" +#define OR1K_OMPIC(obj) OBJECT_CHECK(OR1KOMPICState, (obj), TYPE_OR1K_OMPIC) + +#define OMPIC_CTRL_IRQ_ACK (1 << 31) +#define OMPIC_CTRL_IRQ_GEN (1 << 30) +#define OMPIC_CTRL_DST(cpu) (((cpu) >> 16) & 0x3fff) + +#define OMPIC_REG(addr) (((addr) >> 2) & 0x1) +#define OMPIC_SRC_CPU(addr) (((addr) >> 3) & 0x4f) +#define OMPIC_DST_CPU(addr) (((addr) >> 3) & 0x4f) + +#define OMPIC_STATUS_IRQ_PENDING (1 << 30) +#define OMPIC_STATUS_SRC(cpu) (((cpu) & 0x3fff) << 16) +#define OMPIC_STATUS_DATA(data) ((data) & 0xffff) + +#define OMPIC_CONTROL 0 +#define OMPIC_STATUS 1 + +#define OMPIC_MAX_CPUS 4 /* Real max is much higher, but dont waste memory */ +#define OMPIC_ADDRSPACE_SZ (OMPIC_MAX_CPUS * 2 * 4) /* 2 32-bit regs per cpu */ + +typedef struct OR1KOMPICState OR1KOMPICState; +typedef struct OR1KOMPICCPUState OR1KOMPICCPUState; + +struct OR1KOMPICCPUState { + qemu_irq irq; + uint32_t status; + uint32_t control; +}; + +struct OR1KOMPICState { + SysBusDevice parent_obj; + MemoryRegion mr; + + OR1KOMPICCPUState cpus[OMPIC_MAX_CPUS]; + + uint32_t num_cpus; +}; + +static uint64_t ompic_read(void *opaque, hwaddr addr, unsigned size) +{ + OR1KOMPICState *s = opaque; + int src_cpu = OMPIC_SRC_CPU(addr); + + /* We can only write to control control, write control + update status */ + if (OMPIC_REG(addr) == OMPIC_CONTROL) { + return s->cpus[src_cpu].control; + } else { + return s->cpus[src_cpu].status; + } + +} + +static void ompic_write(void *opaque, hwaddr addr, uint64_t data, unsigned size) +{ + OR1KOMPICState *s = opaque; + /* We can only write to control control, write control + update status */ + if (OMPIC_REG(addr) == OMPIC_CONTROL) { + int src_cpu = OMPIC_SRC_CPU(addr); + + s->cpus[src_cpu].control = data; + + if (data & OMPIC_CTRL_IRQ_GEN) { + int dst_cpu = OMPIC_CTRL_DST(data); + + s->cpus[dst_cpu].status = OMPIC_STATUS_IRQ_PENDING | + OMPIC_STATUS_SRC(src_cpu) | + OMPIC_STATUS_DATA(data); + + qemu_irq_raise(s->cpus[dst_cpu].irq); + } + if (data & OMPIC_CTRL_IRQ_ACK) { + s->cpus[src_cpu].status &= ~OMPIC_STATUS_IRQ_PENDING; + qemu_irq_lower(s->cpus[src_cpu].irq); + } + } +} + +static const MemoryRegionOps ompic_ops = { + .read = ompic_read, + .write = ompic_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .impl = { + .max_access_size = 8, + }, +}; + +static void or1k_ompic_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + OR1KOMPICState *s = OR1K_OMPIC(obj); + + memory_region_init_io(&s->mr, OBJECT(s), &ompic_ops, s, + "or1k-ompic", OMPIC_ADDRSPACE_SZ); + sysbus_init_mmio(sbd, &s->mr); +} + +static void or1k_ompic_realize(DeviceState *dev, Error **errp) +{ + OR1KOMPICState *s = OR1K_OMPIC(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + int i; + + if (s->num_cpus > OMPIC_MAX_CPUS) { + error_setg(errp, "Exceeded maximum CPUs %d", s->num_cpus); + return; + } + /* Init IRQ sources for all CPUs */ + for (i = 0; i < s->num_cpus; i++) { + sysbus_init_irq(sbd, &s->cpus[i].irq); + } +} + +static Property or1k_ompic_properties[] = { + DEFINE_PROP_UINT32("num-cpus", OR1KOMPICState, num_cpus, 1), + DEFINE_PROP_END_OF_LIST(), +}; + +static const VMStateDescription vmstate_or1k_ompic_cpu = { + .name = "or1k_ompic_cpu", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(status, OR1KOMPICCPUState), + VMSTATE_UINT32(control, OR1KOMPICCPUState), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_or1k_ompic = { + .name = TYPE_OR1K_OMPIC, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_STRUCT_ARRAY(cpus, OR1KOMPICState, OMPIC_MAX_CPUS, 1, + vmstate_or1k_ompic_cpu, OR1KOMPICCPUState), + VMSTATE_UINT32(num_cpus, OR1KOMPICState), + VMSTATE_END_OF_LIST() + } +}; + +static void or1k_ompic_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->props = or1k_ompic_properties; + dc->realize = or1k_ompic_realize; + dc->vmsd = &vmstate_or1k_ompic; +} + +static const TypeInfo or1k_ompic_info = { + .name = TYPE_OR1K_OMPIC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(OR1KOMPICState), + .instance_init = or1k_ompic_init, + .class_init = or1k_ompic_class_init, +}; + +static void or1k_ompic_register_types(void) +{ + type_register_static(&or1k_ompic_info); +} + +type_init(or1k_ompic_register_types) diff --git a/hw/openrisc/cputimer.c b/hw/openrisc/cputimer.c index febc469170e74f75c0f2e587bb42db7e473483d6..850f88761c8493e0d9fe807a0152494672dfa7df 100644 --- a/hw/openrisc/cputimer.c +++ b/hw/openrisc/cputimer.c @@ -25,48 +25,64 @@ #define TIMER_PERIOD 50 /* 50 ns period for 20 MHz timer */ -/* The time when TTCR changes */ -static uint64_t last_clk; -static int is_counting; +/* Tick Timer global state to allow all cores to be in sync */ +typedef struct OR1KTimerState { + uint32_t ttcr; + uint64_t last_clk; +} OR1KTimerState; +static OR1KTimerState *or1k_timer; + +void cpu_openrisc_count_set(OpenRISCCPU *cpu, uint32_t val) +{ + or1k_timer->ttcr = val; +} + +uint32_t cpu_openrisc_count_get(OpenRISCCPU *cpu) +{ + return or1k_timer->ttcr; +} + +/* Add elapsed ticks to ttcr */ void cpu_openrisc_count_update(OpenRISCCPU *cpu) { uint64_t now; - if (!is_counting) { + if (!cpu->env.is_counting) { return; } now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - cpu->env.ttcr += (uint32_t)((now - last_clk) / TIMER_PERIOD); - last_clk = now; + or1k_timer->ttcr += (uint32_t)((now - or1k_timer->last_clk) + / TIMER_PERIOD); + or1k_timer->last_clk = now; } +/* Update the next timeout time as difference between ttmr and ttcr */ void cpu_openrisc_timer_update(OpenRISCCPU *cpu) { uint32_t wait; uint64_t now, next; - if (!is_counting) { + if (!cpu->env.is_counting) { return; } cpu_openrisc_count_update(cpu); - now = last_clk; + now = or1k_timer->last_clk; - if ((cpu->env.ttmr & TTMR_TP) <= (cpu->env.ttcr & TTMR_TP)) { - wait = TTMR_TP - (cpu->env.ttcr & TTMR_TP) + 1; + if ((cpu->env.ttmr & TTMR_TP) <= (or1k_timer->ttcr & TTMR_TP)) { + wait = TTMR_TP - (or1k_timer->ttcr & TTMR_TP) + 1; wait += cpu->env.ttmr & TTMR_TP; } else { - wait = (cpu->env.ttmr & TTMR_TP) - (cpu->env.ttcr & TTMR_TP); + wait = (cpu->env.ttmr & TTMR_TP) - (or1k_timer->ttcr & TTMR_TP); } next = now + (uint64_t)wait * TIMER_PERIOD; timer_mod(cpu->env.timer, next); - qemu_cpu_kick(CPU(cpu)); } void cpu_openrisc_count_start(OpenRISCCPU *cpu) { - is_counting = 1; + cpu->env.is_counting = 1; cpu_openrisc_count_update(cpu); } @@ -74,7 +90,7 @@ void cpu_openrisc_count_stop(OpenRISCCPU *cpu) { timer_del(cpu->env.timer); cpu_openrisc_count_update(cpu); - is_counting = 0; + cpu->env.is_counting = 0; } static void openrisc_timer_cb(void *opaque) @@ -93,7 +109,7 @@ static void openrisc_timer_cb(void *opaque) case TIMER_NONE: break; case TIMER_INTR: - cpu->env.ttcr = 0; + or1k_timer->ttcr = 0; break; case TIMER_SHOT: cpu_openrisc_count_stop(cpu); @@ -103,11 +119,27 @@ static void openrisc_timer_cb(void *opaque) } cpu_openrisc_timer_update(cpu); + qemu_cpu_kick(CPU(cpu)); } +static const VMStateDescription vmstate_or1k_timer = { + .name = "or1k_timer", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(ttcr, OR1KTimerState), + VMSTATE_UINT64(last_clk, OR1KTimerState), + VMSTATE_END_OF_LIST() + } +}; + void cpu_openrisc_clock_init(OpenRISCCPU *cpu) { cpu->env.timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &openrisc_timer_cb, cpu); cpu->env.ttmr = 0x00000000; - cpu->env.ttcr = 0x00000000; + + if (or1k_timer == NULL) { + or1k_timer = g_new0(OR1KTimerState, 1); + vmstate_register(NULL, 0, &vmstate_or1k_timer, or1k_timer); + } } diff --git a/hw/openrisc/openrisc_sim.c b/hw/openrisc/openrisc_sim.c index 86bf2849c4d711c0cdec8d276ab79bb1b0bf93e0..58638c6ecdf7fdd12ea9025e9a58907eedcc40dd 100644 --- a/hw/openrisc/openrisc_sim.c +++ b/hw/openrisc/openrisc_sim.c @@ -35,36 +35,60 @@ #define KERNEL_LOAD_ADDR 0x100 +static struct openrisc_boot_info { + uint32_t bootstrap_pc; +} boot_info; + static void main_cpu_reset(void *opaque) { OpenRISCCPU *cpu = opaque; + CPUState *cs = CPU(cpu); cpu_reset(CPU(cpu)); + + cpu_set_pc(cs, boot_info.bootstrap_pc); } -static void openrisc_sim_net_init(MemoryRegion *address_space, - hwaddr base, - hwaddr descriptors, - qemu_irq irq, NICInfo *nd) +static void openrisc_sim_net_init(hwaddr base, hwaddr descriptors, + int num_cpus, qemu_irq **cpu_irqs, + int irq_pin, NICInfo *nd) { DeviceState *dev; SysBusDevice *s; + int i; dev = qdev_create(NULL, "open_eth"); qdev_set_nic_properties(dev, nd); qdev_init_nofail(dev); s = SYS_BUS_DEVICE(dev); - sysbus_connect_irq(s, 0, irq); - memory_region_add_subregion(address_space, base, - sysbus_mmio_get_region(s, 0)); - memory_region_add_subregion(address_space, descriptors, - sysbus_mmio_get_region(s, 1)); + for (i = 0; i < num_cpus; i++) { + sysbus_connect_irq(s, 0, cpu_irqs[i][irq_pin]); + } + sysbus_mmio_map(s, 0, base); + sysbus_mmio_map(s, 1, descriptors); +} + +static void openrisc_sim_ompic_init(hwaddr base, int num_cpus, + qemu_irq **cpu_irqs, int irq_pin) +{ + DeviceState *dev; + SysBusDevice *s; + int i; + + dev = qdev_create(NULL, "or1k-ompic"); + qdev_prop_set_uint32(dev, "num-cpus", num_cpus); + qdev_init_nofail(dev); + + s = SYS_BUS_DEVICE(dev); + for (i = 0; i < num_cpus; i++) { + sysbus_connect_irq(s, i, cpu_irqs[i][irq_pin]); + } + sysbus_mmio_map(s, 0, base); } -static void cpu_openrisc_load_kernel(ram_addr_t ram_size, - const char *kernel_filename, - OpenRISCCPU *cpu) +static void openrisc_load_kernel(ram_addr_t ram_size, + const char *kernel_filename) { long kernel_size; uint64_t elf_entry; @@ -83,6 +107,9 @@ static void cpu_openrisc_load_kernel(ram_addr_t ram_size, kernel_size = load_image_targphys(kernel_filename, KERNEL_LOAD_ADDR, ram_size - KERNEL_LOAD_ADDR); + } + + if (entry <= 0) { entry = KERNEL_LOAD_ADDR; } @@ -91,7 +118,7 @@ static void cpu_openrisc_load_kernel(ram_addr_t ram_size, kernel_filename); exit(1); } - cpu->env.pc = entry; + boot_info.bootstrap_pc = entry; } } @@ -102,6 +129,8 @@ static void openrisc_sim_init(MachineState *machine) const char *kernel_filename = machine->kernel_filename; OpenRISCCPU *cpu = NULL; MemoryRegion *ram; + qemu_irq *cpu_irqs[2]; + qemu_irq serial_irq; int n; if (!cpu_model) { @@ -110,33 +139,46 @@ static void openrisc_sim_init(MachineState *machine) for (n = 0; n < smp_cpus; n++) { cpu = OPENRISC_CPU(cpu_generic_init(TYPE_OPENRISC_CPU, cpu_model)); + if (cpu == NULL) { + fprintf(stderr, "Unable to find CPU definition!\n"); + exit(1); + } + cpu_openrisc_pic_init(cpu); + cpu_irqs[n] = (qemu_irq *) cpu->env.irq; + + cpu_openrisc_clock_init(cpu); + qemu_register_reset(main_cpu_reset, cpu); - main_cpu_reset(cpu); } ram = g_malloc(sizeof(*ram)); memory_region_init_ram(ram, NULL, "openrisc.ram", ram_size, &error_fatal); memory_region_add_subregion(get_system_memory(), 0, ram); - cpu_openrisc_pic_init(cpu); - cpu_openrisc_clock_init(cpu); + if (nd_table[0].used) { + openrisc_sim_net_init(0x92000000, 0x92000400, smp_cpus, + cpu_irqs, 4, nd_table); + } - serial_mm_init(get_system_memory(), 0x90000000, 0, cpu->env.irq[2], - 115200, serial_hds[0], DEVICE_NATIVE_ENDIAN); + if (smp_cpus > 1) { + openrisc_sim_ompic_init(0x98000000, smp_cpus, cpu_irqs, 1); - if (nd_table[0].used) { - openrisc_sim_net_init(get_system_memory(), 0x92000000, - 0x92000400, cpu->env.irq[4], nd_table); + serial_irq = qemu_irq_split(cpu_irqs[0][2], cpu_irqs[1][2]); + } else { + serial_irq = cpu_irqs[0][2]; } - cpu_openrisc_load_kernel(ram_size, kernel_filename, cpu); + serial_mm_init(get_system_memory(), 0x90000000, 0, serial_irq, + 115200, serial_hds[0], DEVICE_NATIVE_ENDIAN); + + openrisc_load_kernel(ram_size, kernel_filename); } static void openrisc_sim_machine_init(MachineClass *mc) { mc->desc = "or1k simulation"; mc->init = openrisc_sim_init; - mc->max_cpus = 1; + mc->max_cpus = 2; mc->is_default = 1; } diff --git a/target/openrisc/cpu.c b/target/openrisc/cpu.c index af9cdcc10249b6fe161558b2a80114a4cd81188b..a6d20496848e42242b2909f9d8095b5aebfff0e7 100644 --- a/target/openrisc/cpu.c +++ b/target/openrisc/cpu.c @@ -61,7 +61,6 @@ static void openrisc_cpu_reset(CPUState *s) cpu->env.picsr = 0x00000000; cpu->env.ttmr = 0x00000000; - cpu->env.ttcr = 0x00000000; #endif } diff --git a/target/openrisc/cpu.h b/target/openrisc/cpu.h index f51b89a11ce633c2e6749ccfaa7e94631a2dc8a3..892dc4210fa71ce4880c7ea89279e642107c4666 100644 --- a/target/openrisc/cpu.h +++ b/target/openrisc/cpu.h @@ -315,7 +315,7 @@ typedef struct CPUOpenRISCState { QEMUTimer *timer; uint32_t ttmr; /* Timer tick mode register */ - uint32_t ttcr; /* Timer tick count register */ + int is_counting; uint32_t picmr; /* Interrupt mask register */ uint32_t picsr; /* Interrupt contrl register*/ @@ -371,6 +371,8 @@ void cpu_openrisc_pic_init(OpenRISCCPU *cpu); /* hw/openrisc_timer.c */ void cpu_openrisc_clock_init(OpenRISCCPU *cpu); +uint32_t cpu_openrisc_count_get(OpenRISCCPU *cpu); +void cpu_openrisc_count_set(OpenRISCCPU *cpu, uint32_t val); void cpu_openrisc_count_update(OpenRISCCPU *cpu); void cpu_openrisc_timer_update(OpenRISCCPU *cpu); void cpu_openrisc_count_start(OpenRISCCPU *cpu); diff --git a/target/openrisc/machine.c b/target/openrisc/machine.c index a20cce705d3d551daaccd653d925373b995149d1..0a793eb14d0388043bce74bbeadd6525b3fe9100 100644 --- a/target/openrisc/machine.c +++ b/target/openrisc/machine.c @@ -147,7 +147,6 @@ static const VMStateDescription vmstate_env = { VMSTATE_TIMER_PTR(timer, CPUOpenRISCState), VMSTATE_UINT32(ttmr, CPUOpenRISCState), - VMSTATE_UINT32(ttcr, CPUOpenRISCState), VMSTATE_UINT32(picmr, CPUOpenRISCState), VMSTATE_UINT32(picsr, CPUOpenRISCState), diff --git a/target/openrisc/sys_helper.c b/target/openrisc/sys_helper.c index abdef5d6a51c3dc5f3efbb4716865c29e444efa9..9fb7d86b4bae939837115eda86e31f18c99a1ba9 100644 --- a/target/openrisc/sys_helper.c +++ b/target/openrisc/sys_helper.c @@ -23,6 +23,7 @@ #include "exec/exec-all.h" #include "exec/helper-proto.h" #include "exception.h" +#include "sysemu/sysemu.h" #define TO_SPR(group, number) (((group) << 11) + (number)) @@ -188,7 +189,7 @@ void HELPER(mtspr)(CPUOpenRISCState *env, break; case TO_SPR(10, 1): /* TTCR */ - env->ttcr = rb; + cpu_openrisc_count_set(cpu, rb); if (env->ttmr & TIMER_NONE) { return; } @@ -249,10 +250,10 @@ target_ulong HELPER(mfspr)(CPUOpenRISCState *env, return env->esr; case TO_SPR(0, 128): /* COREID */ - return 0; + return cpu->parent_obj.cpu_index; case TO_SPR(0, 129): /* NUMCORES */ - return 1; + return max_cpus; case TO_SPR(0, 1024) ... TO_SPR(0, 1024 + (16 * 32)): /* Shadow GPRs */ idx = (spr - 1024); @@ -311,7 +312,7 @@ target_ulong HELPER(mfspr)(CPUOpenRISCState *env, case TO_SPR(10, 1): /* TTCR */ cpu_openrisc_count_update(cpu); - return env->ttcr; + return cpu_openrisc_count_get(cpu); default: break;