From: Claudiu Zissulescu <claz...@synopsys.com> Add the Synopsys ARC boards, arc_sim for testing, sim-hs main emulation board using standard UART and nsim which includes a Synopsys ARC specific UART implementation.
Signed-off-by: Claudiu Zissulescu <claz...@synopsys.com> --- hw/arc/Makefile.objs | 21 +++++ hw/arc/arc_sim.c | 124 +++++++++++++++++++++++++++ hw/arc/boot.c | 100 ++++++++++++++++++++++ hw/arc/boot.h | 21 +++++ hw/arc/meson.build | 13 +++ hw/arc/pic_cpu.c | 113 ++++++++++++++++++++++++ hw/arc/virt.c | 180 +++++++++++++++++++++++++++++++++++++++ include/hw/arc/cpudevs.h | 30 +++++++ 8 files changed, 602 insertions(+) create mode 100644 hw/arc/Makefile.objs create mode 100644 hw/arc/arc_sim.c create mode 100644 hw/arc/boot.c create mode 100644 hw/arc/boot.h create mode 100644 hw/arc/meson.build create mode 100644 hw/arc/pic_cpu.c create mode 100644 hw/arc/virt.c create mode 100644 include/hw/arc/cpudevs.h diff --git a/hw/arc/Makefile.objs b/hw/arc/Makefile.objs new file mode 100644 index 0000000000..28d7766cd9 --- /dev/null +++ b/hw/arc/Makefile.objs @@ -0,0 +1,21 @@ +# +# QEMU ARC CPU +# +# Copyright (c) 2019 +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, see +# http://www.gnu.org/licenses/lgpl-2.1.html +# + +obj-y = arc_sim.o arc_uart.o sample.o pic_cpu.o boot.o board-hsdk.o sim-hs.o nsim.o diff --git a/hw/arc/arc_sim.c b/hw/arc/arc_sim.c new file mode 100644 index 0000000000..64db440454 --- /dev/null +++ b/hw/arc/arc_sim.c @@ -0,0 +1,124 @@ +/* + * QEMU ARC CPU + * + * Copyright (c) 2020 Synppsys Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * http://www.gnu.org/licenses/lgpl-2.1.html + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "cpu.h" +#include "hw/hw.h" +#include "hw/boards.h" +#include "elf.h" +#include "hw/char/serial.h" +#include "net/net.h" +#include "hw/loader.h" +#include "exec/memory.h" +#include "exec/address-spaces.h" +#include "sysemu/reset.h" +#include "sysemu/runstate.h" +#include "sysemu/sysemu.h" +#include "hw/sysbus.h" +#include "hw/arc/cpudevs.h" +#include "boot.h" + + +static uint64_t arc_io_read(void *opaque, hwaddr addr, unsigned size) +{ + return 0; +} + +static void arc_io_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + switch (addr) { + case 0x08: /* board reset. */ + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); + break; + default: + break; + } +} + +static const MemoryRegionOps arc_io_ops = { + .read = arc_io_read, + .write = arc_io_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static void arc_sim_init(MachineState *machine) +{ + static struct arc_boot_info boot_info; + unsigned int smp_cpus = machine->smp.cpus; + ram_addr_t ram_base = 0; + ram_addr_t ram_size = machine->ram_size; + ARCCPU *cpu = NULL; + MemoryRegion *ram, *system_io; + int n; + + boot_info.ram_start = ram_base; + boot_info.ram_size = ram_size; + boot_info.kernel_filename = machine->kernel_filename; + + for (n = 0; n < smp_cpus; n++) { + cpu = ARC_CPU(object_new(machine->cpu_type)); + if (cpu == NULL) { + fprintf(stderr, "Unable to find CPU definition!\n"); + exit(1); + } + + /* Set the initial CPU properties. */ + object_property_set_uint(OBJECT(cpu), "freq_hz", 1000000, &error_fatal); + object_property_set_bool(OBJECT(cpu), "rtc-opt", true, &error_fatal); + object_property_set_bool(OBJECT(cpu), "realized", true, &error_fatal); + + /* Initialize internal devices. */ + cpu_arc_pic_init(cpu); + cpu_arc_clock_init(cpu); + + qemu_register_reset(arc_cpu_reset, cpu); + } + + ram = g_new(MemoryRegion, 1); + memory_region_init_ram(ram, NULL, "arc.ram", ram_size, &error_fatal); + memory_region_add_subregion(get_system_memory(), ram_base, ram); + + system_io = g_new(MemoryRegion, 1); + memory_region_init_io(system_io, NULL, &arc_io_ops, NULL, "arc.io", + 1024); + memory_region_add_subregion(get_system_memory(), 0xf0000000, system_io); + + serial_mm_init(get_system_memory(), 0x90000000, 2, cpu->env.irq[20], + 115200, serial_hd(0), DEVICE_NATIVE_ENDIAN); + + arc_load_kernel(cpu, &boot_info); +} + +static void arc_sim_machine_init(MachineClass *mc) +{ + mc->desc = "ARCxx simulation"; + mc->init = arc_sim_init; + mc->max_cpus = 1; + mc->is_default = false; + mc->default_cpu_type = ARC_CPU_TYPE_NAME("archs"); +} + +DEFINE_MACHINE("arc-sim", arc_sim_machine_init) + + +/*-*-indent-tabs-mode:nil;tab-width:4;indent-line-function:'insert-tab'-*-*/ +/* vim: set ts=4 sw=4 et: */ diff --git a/hw/arc/boot.c b/hw/arc/boot.c new file mode 100644 index 0000000000..962fc03b03 --- /dev/null +++ b/hw/arc/boot.c @@ -0,0 +1,100 @@ +/* + * QEMU ARC CPU + * + * Copyright (c) 2020 Synppsys Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * http://www.gnu.org/licenses/lgpl-2.1.html + */ + +#include "qemu/osdep.h" +#include "boot.h" +#include "elf.h" +#include "hw/loader.h" +#include "qemu/error-report.h" +#include "qemu/units.h" + +void arc_cpu_reset(void *opaque) +{ + ARCCPU *cpu = opaque; + CPUARCState *env = &cpu->env; + const struct arc_boot_info *info = env->boot_info; + + cpu_reset(CPU(cpu)); + + /* + * Right before start CPU gets reset wiping out everything + * but PC which we set on Elf load. + * + * And if we still want to pass something like U-Boot data + * via CPU registers we have to do it here. + */ + + if (info->kernel_cmdline && strlen(info->kernel_cmdline)) { + /* + * Load "cmdline" far enough from the kernel image. + * Round by MAX page size for ARC - 16 KiB. + */ + hwaddr cmdline_addr = info->ram_start + + QEMU_ALIGN_UP(info->ram_size / 2, 16 * KiB); + cpu_physical_memory_write(cmdline_addr, info->kernel_cmdline, + strlen(info->kernel_cmdline)); + + /* We're passing "cmdline" */ + cpu->env.r[0] = ARC_UBOOT_CMDLINE; + cpu->env.r[2] = cmdline_addr; + } +} + + +void arc_load_kernel(ARCCPU *cpu, struct arc_boot_info *info) +{ + hwaddr entry; + int elf_machine, kernel_size; + + if (!info->kernel_filename) { + error_report("missing kernel file"); + exit(EXIT_FAILURE); + } + + elf_machine = cpu->family > 2 ? EM_ARC_COMPACT2 : EM_ARC_COMPACT; + kernel_size = load_elf(info->kernel_filename, NULL, NULL, NULL, + &entry, NULL, NULL, NULL, ARC_ENDIANNESS_LE, + elf_machine, 1, 0); + + if (kernel_size < 0) { + int is_linux; + + kernel_size = load_uimage(info->kernel_filename, &entry, NULL, + &is_linux, NULL, NULL); + if (!is_linux) { + error_report("Wrong U-Boot image, only Linux kernel is supported"); + exit(EXIT_FAILURE); + } + } + + if (kernel_size < 0) { + error_report("No kernel image found"); + exit(EXIT_FAILURE); + } + + cpu->env.boot_info = info; + + /* Set CPU's PC to point to the entry-point */ + cpu->env.pc = entry; +} + + +/*-*-indent-tabs-mode:nil;tab-width:4;indent-line-function:'insert-tab'-*-*/ +/* vim: set ts=4 sw=4 et: */ diff --git a/hw/arc/boot.h b/hw/arc/boot.h new file mode 100644 index 0000000000..e46aa16fc6 --- /dev/null +++ b/hw/arc/boot.h @@ -0,0 +1,21 @@ +#ifndef ARC_BOOT_H +#define ARC_BOOT_H + +#include "hw/hw.h" +#include "cpu.h" + +struct arc_boot_info { + hwaddr ram_start; + uint64_t ram_size; + const char *kernel_filename; + const char *kernel_cmdline; +}; + +void arc_cpu_reset(void *opaque); +void arc_load_kernel(ARCCPU *cpu, struct arc_boot_info *boot_info); + +#endif /* ARC_BOOT_H */ + + +/*-*-indent-tabs-mode:nil;tab-width:4;indent-line-function:'insert-tab'-*-*/ +/* vim: set ts=4 sw=4 et: */ diff --git a/hw/arc/meson.build b/hw/arc/meson.build new file mode 100644 index 0000000000..6a587307a4 --- /dev/null +++ b/hw/arc/meson.build @@ -0,0 +1,13 @@ +arc_ss = ss.source_set() +arc_ss.add(files( + 'arc_sim.c', + 'arc_uart.c', + 'sample.c', + 'pic_cpu.c', + 'boot.c', + 'board-hsdk.c', + 'sim-hs.c', + 'nsim.c', +)) + +hw_arch += {'arc': arc_ss} diff --git a/hw/arc/pic_cpu.c b/hw/arc/pic_cpu.c new file mode 100644 index 0000000000..d41fa0fe4b --- /dev/null +++ b/hw/arc/pic_cpu.c @@ -0,0 +1,113 @@ +/* + * ARC Programmable Interrupt Controller support. + * + * Copyright (c) 2020 Synppsys Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * http://www.gnu.org/licenses/lgpl-2.1.html + */ + + +#include "qemu/osdep.h" +#include "cpu.h" +#include "hw/hw.h" +#include "hw/irq.h" +#include "qemu/log.h" +#include "hw/arc/cpudevs.h" + +/* + * ARC pic handler + */ +static void arc_pic_cpu_handler(void *opaque, int irq, int level) +{ + ARCCPU *cpu = (ARCCPU *) opaque; + CPUState *cs = CPU(cpu); + CPUARCState *env = &cpu->env; + int i; + bool clear = false; + uint32_t irq_bit; + + /* Assert if this handler is called in a system without interrupts. */ + assert(cpu->cfg.has_interrupts); + + /* Assert if the IRQ is not within the cpu configuration bounds. */ + assert(irq >= 16 && irq < (cpu->cfg.number_of_interrupts + 15)); + + irq_bit = 1 << env->irq_bank[irq].priority; + if (level) { + /* + * An interrupt is enabled, update irq_priority_pendig and rise + * the qemu interrupt line. + */ + env->irq_bank[irq].pending = 1; + qatomic_or(&env->irq_priority_pending, irq_bit); + cpu_interrupt(cs, CPU_INTERRUPT_HARD); + } else { + env->irq_bank[irq].pending = 0; + + /* + * First, check if we still have any pending interrupt at the + * given priority. + */ + clear = true; + for (i = 16; i < cpu->cfg.number_of_interrupts; i++) { + if (env->irq_bank[i].pending + && env->irq_bank[i].priority == env->irq_bank[irq].priority) { + clear = false; + break; + } + } + + /* If not, update (clear) irq_priority_pending. */ + if (clear) { + qatomic_and(&env->irq_priority_pending, ~irq_bit); + } + + /* + * If we don't have any pending priority, lower the qemu irq + * line. N.B. we can also check more here like IE bit, but we + * need to add a cpu_interrupt call when we enable the + * interrupts (e.g., sleep, seti). + */ + if (!env->irq_priority_pending) { + cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); + } + } + qemu_log_mask(CPU_LOG_INT, + "[IRQ] level = %d, clear = %d, irq = %d, priority = " TARGET_FMT_lu ", " + "pending = %08x, pc = " TARGET_FMT_lx "\n", + level, clear, irq, env->irq_bank[irq].priority, + env->irq_priority_pending, env->pc); +} + +/* + * ARC PIC initialization helper + */ +void cpu_arc_pic_init(ARCCPU *cpu) +{ + CPUARCState *env = &cpu->env; + int i; + qemu_irq *qi; + + qi = qemu_allocate_irqs(arc_pic_cpu_handler, cpu, + 16 + cpu->cfg.number_of_interrupts); + + for (i = 0; i < cpu->cfg.number_of_interrupts; i++) { + env->irq[16 + i] = qi[16 + i]; + } +} + + +/*-*-indent-tabs-mode:nil;tab-width:4;indent-line-function:'insert-tab'-*-*/ +/* vim: set ts=4 sw=4 et: */ diff --git a/hw/arc/virt.c b/hw/arc/virt.c new file mode 100644 index 0000000000..8f7d7cbd5d --- /dev/null +++ b/hw/arc/virt.c @@ -0,0 +1,180 @@ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "boot.h" +#include "hw/boards.h" +#include "hw/char/serial.h" +#include "exec/address-spaces.h" +#include "sysemu/reset.h" +#include "sysemu/sysemu.h" +#include "hw/arc/cpudevs.h" +#include "hw/pci-host/gpex.h" +#include "hw/sysbus.h" + +#define VIRT_RAM_BASE 0x80000000 +#define VIRT_RAM_SIZE 0x80000000 +#define VIRT_IO_BASE 0xf0000000 +#define VIRT_IO_SIZE 0x10000000 +#define VIRT_UART0_OFFSET 0x0 +#define VIRT_UART0_IRQ 24 + +/* VirtIO */ +#define VIRT_VIRTIO_NUMBER 5 +#define VIRT_VIRTIO_OFFSET 0x100000 +#define VIRT_VIRTIO_BASE (VIRT_IO_BASE + VIRT_VIRTIO_OFFSET) +#define VIRT_VIRTIO_SIZE 0x2000 +#define VIRT_VIRTIO_IRQ 31 + +/* PCI */ +#define VIRT_PCI_ECAM_BASE 0xe0000000 +#define VIRT_PCI_ECAM_SIZE 0x01000000 +#define VIRT_PCI_MMIO_BASE 0xd0000000 +#define VIRT_PCI_MMIO_SIZE 0x10000000 +#define VIRT_PCI_PIO_BASE 0xc0000000 +#define VIRT_PCI_PIO_SIZE 0x00004000 +#define PCIE_IRQ 40 /* IRQs 40-43 as GPEX_NUM_IRQS=4 */ + +static void create_pcie(ARCCPU *cpu) +{ + hwaddr base_ecam = VIRT_PCI_ECAM_BASE; + hwaddr size_ecam = VIRT_PCI_ECAM_SIZE; + hwaddr base_pio = VIRT_PCI_PIO_BASE; + hwaddr size_pio = VIRT_PCI_PIO_SIZE; + hwaddr base_mmio = VIRT_PCI_MMIO_BASE; + hwaddr size_mmio = VIRT_PCI_MMIO_SIZE; + + MemoryRegion *ecam_alias; + MemoryRegion *ecam_reg; + MemoryRegion *pio_alias; + MemoryRegion *pio_reg; + MemoryRegion *mmio_alias; + MemoryRegion *mmio_reg; + + DeviceState *dev; + int i; + + dev = qdev_new(TYPE_GPEX_HOST); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + + /* Map only the first size_ecam bytes of ECAM space. */ + ecam_alias = g_new0(MemoryRegion, 1); + ecam_reg = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0); + memory_region_init_alias(ecam_alias, OBJECT(dev), "pcie-ecam", + ecam_reg, 0, size_ecam); + memory_region_add_subregion(get_system_memory(), base_ecam, ecam_alias); + + /* + * Map the MMIO window into system address space so as to expose + * the section of PCI MMIO space which starts at the same base address + * (ie 1:1 mapping for that part of PCI MMIO space visible through + * the window). + */ + mmio_alias = g_new0(MemoryRegion, 1); + mmio_reg = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 1); + memory_region_init_alias(mmio_alias, OBJECT(dev), "pcie-mmio", + mmio_reg, base_mmio, size_mmio); + memory_region_add_subregion(get_system_memory(), base_mmio, mmio_alias); + + /* Map IO port space. */ + pio_alias = g_new0(MemoryRegion, 1); + pio_reg = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 2); + memory_region_init_alias(pio_alias, OBJECT(dev), "pcie-pio", + pio_reg, 0, size_pio); + memory_region_add_subregion(get_system_memory(), base_pio, pio_alias); + + /* Connect IRQ lines. */ + for (i = 0; i < GPEX_NUM_IRQS; i++) { + sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, cpu->env.irq[PCIE_IRQ + i]); + gpex_set_irq_num(GPEX_HOST(dev), i, PCIE_IRQ + i); + } +} + +static void virt_init(MachineState *machine) +{ + static struct arc_boot_info boot_info; + unsigned int smp_cpus = machine->smp.cpus; + MemoryRegion *system_memory = get_system_memory(); + MemoryRegion *system_ram; + MemoryRegion *system_ram0; + MemoryRegion *system_io; + ARCCPU *cpu = NULL; + int n; + + boot_info.ram_start = VIRT_RAM_BASE; + boot_info.ram_size = VIRT_RAM_SIZE; + boot_info.kernel_filename = machine->kernel_filename; + boot_info.kernel_cmdline = machine->kernel_cmdline; + + for (n = 0; n < smp_cpus; n++) { + cpu = ARC_CPU(cpu_create("archs-" TYPE_ARC_CPU)); + if (cpu == NULL) { + fprintf(stderr, "Unable to find CPU definition!\n"); + exit(1); + } + + /* Initialize internal devices. */ + cpu_arc_pic_init(cpu); + cpu_arc_clock_init(cpu); + + qemu_register_reset(arc_cpu_reset, cpu); + } + + /* Init system DDR */ + system_ram = g_new(MemoryRegion, 1); + memory_region_init_ram(system_ram, NULL, "arc.ram", VIRT_RAM_SIZE, + &error_fatal); + memory_region_add_subregion(system_memory, VIRT_RAM_BASE, system_ram); + + system_ram0 = g_new(MemoryRegion, 1); + memory_region_init_ram(system_ram0, NULL, "arc.ram0", 0x1000000, + &error_fatal); + memory_region_add_subregion(system_memory, 0, system_ram0); + + /* Init IO area */ + system_io = g_new(MemoryRegion, 1); + memory_region_init_io(system_io, NULL, NULL, NULL, "arc.io", + VIRT_IO_SIZE); + memory_region_add_subregion(system_memory, VIRT_IO_BASE, system_io); + + serial_mm_init(system_io, VIRT_UART0_OFFSET, 2, + cpu->env.irq[VIRT_UART0_IRQ], 115200, serial_hd(0), + DEVICE_NATIVE_ENDIAN); + + for (n = 0; n < VIRT_VIRTIO_NUMBER; n++) { + sysbus_create_simple("virtio-mmio", + VIRT_VIRTIO_BASE + VIRT_VIRTIO_SIZE * n, + cpu->env.irq[VIRT_VIRTIO_IRQ + n]); + } + + create_pcie(cpu); + + arc_load_kernel(cpu, &boot_info); +} + +static void virt_machine_init(MachineClass *mc) +{ + mc->desc = "ARC Virtual Machine"; + mc->init = virt_init; + mc->max_cpus = 1; + mc->is_default = true; +} + +DEFINE_MACHINE("virt", virt_machine_init) + + +/*-*-indent-tabs-mode:nil;tab-width:4;indent-line-function:'insert-tab'-*-*/ +/* vim: set ts=4 sw=4 et: */ diff --git a/include/hw/arc/cpudevs.h b/include/hw/arc/cpudevs.h new file mode 100644 index 0000000000..2e155b6437 --- /dev/null +++ b/include/hw/arc/cpudevs.h @@ -0,0 +1,30 @@ +/* + * QEMU ARC CPU + * + * Copyright (c) 2020 Synppsys Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * http://www.gnu.org/licenses/lgpl-2.1.html + */ + +#ifndef HW_ARC_CPUDEVS_H +#define HW_ARC_CPUDEVS_H + +/* Timer service routines. */ +extern void cpu_arc_clock_init(ARCCPU *); + +/* PIC service routines. */ +extern void cpu_arc_pic_init(ARCCPU *); + +#endif /* !HW_ARC_CPUDEVS_H */ -- 2.20.1 _______________________________________________ linux-snps-arc mailing list linux-snps-arc@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-snps-arc