Signed-off-by: "Jin Guojie" <jinguo...@loongson.cn> Reviewed-by: "Gao Xiang" <gaoxi...@ict.ac.cn> Reviewed-by: "Chen Huacai" <zltjiang...@gmail.com>
A patch for Godson-3a CPU simulation. Godson-3a is a newly developed MIPS-III like, multicore CPU by ICT, China. We believe this patch could be helpful for other Godson developers. For you review. Any comment is welcomed. Jin Guojie www.loongson.cn --- Makefile.target | 2 +- hw/mips_godson3a.c | 507 ++++++++++++++++++++++++++++++++++++++++++ target-mips/mips-defs.h | 4 +- target-mips/translate_init.c | 26 +++ 4 files changed, 536 insertions(+), 3 deletions(-) create mode 100755 hw/mips_godson3a.c diff --git a/Makefile.target b/Makefile.target index 91e6e74..8f29aeb 100644 --- a/Makefile.target +++ b/Makefile.target @@ -230,7 +230,7 @@ obj-ppc-y += xilinx_timer.o obj-ppc-y += xilinx_uartlite.o obj-ppc-y += xilinx_ethlite.o -obj-mips-y = mips_r4k.o mips_jazz.o mips_malta.o mips_mipssim.o +obj-mips-y = mips_r4k.o mips_jazz.o mips_malta.o mips_mipssim.o mips_godson3a.o obj-mips-y += mips_addr.o mips_timer.o mips_int.o obj-mips-y += vga.o i8259.o obj-mips-y += g364fb.o jazz_led.o diff --git a/hw/mips_godson3a.c b/hw/mips_godson3a.c new file mode 100755 index 0000000..4085db2 --- /dev/null +++ b/hw/mips_godson3a.c @@ -0,0 +1,507 @@ +/* + * QEMU godson 3a developing board support + * + * Copyright (c) 2009 Gao Xiang (gaoxi...@ict.ac.cn) + * Copyright (c) 2010 Jin Guojie (jinguo...@loongson.cn) + * This code is licensed under the GNU GPL v2. + */ + +/* + * Godson 3a developing board is based on ICT/ST Godson-3a. + * Godson-3a CPU is a MIPS-III like, multicore processor. + * It can be configured to contain 4 or 8 cores. Every 4 + * cores are grouped into one on-chip 'node'. SMP mechanism + * is supported by Godson IPI(inter-processors interrupt) + * specification. + * + * Godson 3a CPU intro: + * http://en.wikipedia.org/wiki/Loongson + * + * Godson 3a user manual: + * http://www.loongsondeveloper.com/doc/Loongson3AUserGuide.pdf + */ +#include "hw.h" +#include "mips.h" +#include "pc.h" +#include "isa.h" +#include "net.h" +#include "sysemu.h" +#include "boards.h" +#include "ide.h" +#include "mips-bios.h" +#include "elf.h" +#include "loader.h" +#include "blockdev.h" +#include "mips_cpudevs.h" +#include "mc146818rtc.h" + +static target_ulong PHYS_TO_VIRT(target_ulong phys) +{ + if (smp_cpus > 1) + return ((phys) | 0x9800000000000000ULL); + else + return ((phys) | ~(target_ulong)0x7fffffff); +} + +#define VIRT_TO_PHYS_ADDEND (-((int64_t)(int32_t)0x80000000)) + +#define MAX_IDE_BUS 2 + +static const int ide_iobase[2] = { 0x1f0, 0x170 }; +static const int ide_iobase2[2] = { 0x3f6, 0x376 }; +static const int ide_irq[2] = { 14, 15 }; + +static int serial_io[MAX_SERIAL_PORTS] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 }; + +static PITState *pit; /* PIT i8254 */ + +/* i8254 PIT is attached to the IRQ0 at PIC i8259 */ + +static struct _loaderparams { + int ram_size; + const char *kernel_filename; + const char *kernel_cmdline; + const char *initrd_filename; +} loaderparams; + +static void mips_qemu_writel (void *opaque, target_phys_addr_t addr, + uint32_t val) +{ + if ((addr & 0xffff) == 0 && val == 42) + qemu_system_reset_request(); + else if ((addr & 0xffff) == 4 && val == 42) + qemu_system_shutdown_request(); +} + +static uint32_t mips_qemu_readl (void *opaque, target_phys_addr_t addr) +{ + return 0; +} + +static CPUWriteMemoryFunc *mips_qemu_write[] = { + &mips_qemu_writel, + &mips_qemu_writel, + &mips_qemu_writel, +}; + +static CPUReadMemoryFunc *mips_qemu_read[] = { + &mips_qemu_readl, + &mips_qemu_readl, + &mips_qemu_readl, +}; + +static int mips_qemu_iomemtype = 0; + +typedef struct ResetData { + CPUState *env; + uint64_t vector; +} ResetData; + +static int64_t load_kernel (CPUState *env) +{ + int64_t entry, kernel_high; + long kernel_size, initrd_size, params_size; + ram_addr_t initrd_offset; + uint32_t *params_buf; + int big_endian; + +#ifdef TARGET_WORDS_BIGENDIAN + big_endian = 1; +#else + big_endian = 0; +#endif + + kernel_size = load_elf(loaderparams.kernel_filename, cpu_mips_kseg0_to_phys, NULL, + (uint64_t *)&entry, NULL, (uint64_t *)&kernel_high, + big_endian, ELF_MACHINE, 1); + if (kernel_size >= 0) { + if ((entry & ~0x7fffffffULL) == 0x80000000) + entry = (int32_t)entry; + env->active_tc.PC = entry; + env = first_cpu; + } else { + fprintf(stderr, "qemu: could not load kernel '%s'\n", + loaderparams.kernel_filename); + exit(1); + } + + /* load initrd */ + initrd_size = 0; + initrd_offset = 0; + if (loaderparams.initrd_filename) { + initrd_size = get_image_size (loaderparams.initrd_filename); + if (initrd_size > 0) { + if(initrd_size < 0x10000000) + initrd_offset = 0x1000000; + else + initrd_offset = 0x20000000; + + if (initrd_offset + initrd_size > ram_size) { + fprintf(stderr, + "qemu: memory too small for initial ram disk '%s'\n", + loaderparams.initrd_filename); + exit(1); + } + + initrd_size = load_image_targphys(loaderparams.initrd_filename, + initrd_offset, + ram_size - initrd_offset); + } + + if (initrd_size == (target_ulong)-1) { + fprintf(stderr, "qemu: could not load initial ram disk '%s'\n", + loaderparams.initrd_filename); + exit(1); + } + } + + /* Store command line. */ + params_size = 264; + params_buf = qemu_malloc(params_size); + + params_buf[0] = tswap32(ram_size); + params_buf[1] = tswap32(0x12345678); + + if (initrd_size > 0) { + snprintf((char *)params_buf + 8, 256, + "rd_start=0x" TARGET_FMT_lx " rd_size=%li ramdisk_size=%li %s", + PHYS_TO_VIRT((uint32_t)initrd_offset), + initrd_size, initrd_size>>10, loaderparams.kernel_cmdline); + } else { + snprintf((char *)params_buf + 8, 256, "%s", loaderparams.kernel_cmdline); + } + + rom_add_blob_fixed("params", params_buf, params_size, (16 << 20) - 264); + return entry; +} + +static void main_cpu_reset(void *opaque) +{ + ResetData *s = (ResetData *)opaque; + CPUState *env = s->env; + + env->active_tc.PC = s->vector; +} + +/* + * Godson3A Inter-processor interrupt addresses: + * STATUS_OFF 0x000 + * EN_OFF 0x004 + * SET_OFF 0x008 + * CLEAR_OFF 0x00c + * BUF_20 0x020 + * BUF_28 0x028 + * BUF_30 0x030 + * BUF_38 0x038 +*/ +typedef struct { + uint32_t status; + uint32_t en; + uint32_t set; + uint32_t clear; + uint32_t buf[4]; + qemu_irq irq; +} GodsonCoreState; + +GodsonCoreState core_states[8]; + +static void gipi_writel(void *opaque, target_phys_addr_t addr, uint32_t val) +{ + GodsonCoreState * s = (GodsonCoreState *)((long)opaque & ~3); + int node = (long)opaque & 3; + + int core = (addr >> 8) & 3; + if (node == 1) + core += 4; + + switch(addr & 0xFF) + { + case 0x0: + hw_error("CORE: STATUS_OFF Can't be written\n"); + break; + case 0x04: + s[core].en = val; + break; + case 0x08: + s[core].status |= val; + qemu_irq_raise(s[core].irq); + break; + case 0x0C: + s[core].status ^= val; + qemu_irq_lower(s[core].irq); + break; + case 0x20: + s[core].buf[0] = val; + break; + case 0x28: + s[core].buf[1] = val; + break; + case 0x30: + s[core].buf[2] = val; + break; + case 0x38: + s[core].buf[3] = val; + break; + default: + break; + } +} + +static uint32_t gipi_readl(void *opaque, target_phys_addr_t addr) +{ + GodsonCoreState * s = (GodsonCoreState *)((long)opaque & ~3); + int node = (long)opaque & 3; + uint32_t ret = -1; + + int core = (addr >> 8) & 3; + if (node == 1) + core += 4; + + switch(addr & 0xFF) + { + case 0x0: + ret = s[core].status; + break; + case 0x04: + ret = s[core].en; + break; + case 0x08: + hw_error("CORE: SET_OFF Can't be read\n"); + break; + case 0x0C: + hw_error("CORE: CLEAR_OFF Can't be read\n"); + break; + case 0x20: + ret = s[core].buf[0]; + break; + case 0x28: + ret = s[core].buf[1]; + break; + case 0x30: + ret = s[core].buf[2]; + break; + case 0x38: + ret = s[core].buf[3]; + break; + default: + break; + } + + return ret; +} + +static void gipi_save(QEMUFile *f, void *opaque) +{ + /* TODO */ + hw_error("gipi_save not implemented\n"); +} + +static int gipi_load(QEMUFile *f, void *opaque, int version_id) +{ + /* TODO */ + hw_error("gipi_load not implemented\n"); +} + +static void gipi_reset(void *opaque) +{ +} + +static CPUWriteMemoryFunc *gipi_write[] = { + &gipi_writel, + &gipi_writel, + &gipi_writel, +}; + +static CPUReadMemoryFunc *gipi_read[] = { + &gipi_readl, + &gipi_readl, + &gipi_readl, +}; + +static int godson_ipi_init(qemu_irq parent_irq , int core, GodsonCoreState *s) +{ + int size = 0x1000; + target_phys_addr_t ipi_addr; + s[core].irq = parent_irq; + int gipi_iomemtype; + void *opaque; + + if(core == 0 || core == 4) { + opaque = (void *)((long)s | (core / 4)); + gipi_iomemtype = cpu_register_io_memory(gipi_read, gipi_write, opaque); + + ipi_addr = 0x3ff01000LL | ((target_phys_addr_t)(core/4) << 44); + cpu_register_physical_memory(ipi_addr, size, gipi_iomemtype); + } + + if(core == 0) { + register_savevm(NULL, "gipi", 0, 1, gipi_save, gipi_load, s); + qemu_register_reset(gipi_reset, s); + } + return 0; +} + +static CPUState *mycpu[8]; + +static +void mips_godson3a_init (ram_addr_t ram_size, + const char *boot_device, + const char *kernel_filename, const char *kernel_cmdline, + const char *initrd_filename, const char *cpu_model) +{ + int i; + char *filename; + ram_addr_t ram_offset; + ram_addr_t bios_offset; + int bios_size; + CPUState *env; + ResetData *reset_info = NULL; + ResetData *main_reset_info = NULL; + qemu_irq *i8259; + DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; + + /* init CPUs */ + if (cpu_model == NULL) { + cpu_model = "godson3a"; + } + + GodsonCoreState * gipis =qemu_mallocz(sizeof(core_states)); + + for(i = 0; i < smp_cpus; i++) { + env = cpu_init(cpu_model); + mycpu[i] = env; + + env->CP0_EBase |= env->cpu_index; + + if (i != 0) + env->halted = 0; + + register_savevm(NULL, "cpu", i, 3, cpu_save, cpu_load, env); + env->CP0_Status |= (1 << CP0St_KX); + env->CP0_PRid |= 0x6303; + + /* Init CPU internal devices */ + cpu_mips_irq_init_cpu(env); + cpu_mips_clock_init(env); + + godson_ipi_init(env->irq[6], env->cpu_index, gipis); + + reset_info = qemu_mallocz(sizeof(ResetData)); + reset_info->env = env; + reset_info->vector = env->active_tc.PC; + qemu_register_reset(main_cpu_reset, reset_info); + + if (i == 0) + main_reset_info = reset_info; + } + + env = mycpu[0]; + + /* allocate RAM */ + ram_offset = qemu_ram_alloc(NULL, "godson3a.ram", ram_size); + cpu_register_physical_memory(0, ram_size, ram_offset | IO_MEM_RAM); + + if (!mips_qemu_iomemtype) { + mips_qemu_iomemtype = cpu_register_io_memory(mips_qemu_read, + mips_qemu_write, NULL); + } + cpu_register_physical_memory(0x1fbf0000, 0x10000, mips_qemu_iomemtype); + + /* Try to load a BIOS image. If this fails, we continue regardless, + but initialize the hardware ourselves. When a kernel gets + preloaded we also initialize the hardware, since the BIOS wasn't + run. */ + if (bios_name == NULL) + bios_name = BIOS_FILENAME; + filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); + if (filename) { + bios_size = get_image_size(filename); + } else { + bios_size = -1; + } + + if ((bios_size > 0) && (bios_size <= BIOS_SIZE)) { + bios_offset = qemu_ram_alloc(NULL, "godson3a.bios", BIOS_SIZE); + cpu_register_physical_memory(0x1fc00000, BIOS_SIZE, + bios_offset | IO_MEM_ROM); + + load_image_targphys(filename, 0x1fc00000, BIOS_SIZE); + + for(i = 0; i < smp_cpus; i++) + mycpu[i]->active_tc.PC = (target_long)(int32_t)0xbfc00000; + + } else { + /* not fatal */ + fprintf(stderr, "qemu: Warning, could not load MIPS bios '%s'\n", + bios_name); + } + + if (filename) { + qemu_free(filename); + } + + if (kernel_filename) { + loaderparams.ram_size = ram_size * 2; + loaderparams.kernel_filename = kernel_filename; + loaderparams.kernel_cmdline = kernel_cmdline; + loaderparams.initrd_filename = initrd_filename; + main_reset_info->vector = load_kernel(env); + } + + /* The PIC is attached to the MIPS CPU INT0 pin */ + i8259 = i8259_init(env->irq[2]); + isa_bus_new(NULL); + isa_bus_irqs(i8259); + + rtc_init(2000, NULL); + + /* Register 64 KB of ISA IO space at 0x14000000 */ + isa_mmio_init(0x1fe00000, 0x00010000, 0); + isa_mmio_init(0x1ff00000, 0x00010000, 1); + isa_mem_base = 0x10000000; + + pit = pit_init(0x40, i8259[0]); + + serial_init(serial_io[0], env->irq[2], 115200, serial_hds[0]); + + if (nd_table[0].vlan) { + if (nd_table[0].model == NULL + || strcmp(nd_table[0].model, "ne2k_isa") == 0) { + isa_ne2000_init(0x300, 9, &nd_table[0]); + } else if (strcmp(nd_table[0].model, "?") == 0) { + fprintf(stderr, "qemu: Supported NICs: ne2k_isa\n"); + exit (1); + } else { + fprintf(stderr, "qemu: Unsupported NIC: %s\n", nd_table[0].model); + exit (1); + } + } + + if (drive_get_max_bus(IF_IDE) >= MAX_IDE_BUS) { + fprintf(stderr, "qemu: too many IDE bus\n"); + exit(1); + } + + for(i = 0; i < MAX_IDE_BUS * MAX_IDE_DEVS; i++) { + hd[i] = drive_get(IF_IDE, i / MAX_IDE_DEVS, i % MAX_IDE_DEVS); + } + + for(i = 0; i < MAX_IDE_BUS; i++) + isa_ide_init(ide_iobase[i], ide_iobase2[i], ide_irq[i], + hd[MAX_IDE_DEVS * i], + hd[MAX_IDE_DEVS * i + 1]); +} + +QEMUMachine mips_godson3a_machine = { + .name = "godson3a", + .desc = "Godson3A Multicore platform", + .init = mips_godson3a_init, + .max_cpus = 8, +}; + +static void mips_godson3a_machine_init(void) +{ + qemu_register_machine(&mips_godson3a_machine); +} + +machine_init(mips_godson3a_machine_init); diff --git a/target-mips/mips-defs.h b/target-mips/mips-defs.h index bf094a3..36444f0 100644 --- a/target-mips/mips-defs.h +++ b/target-mips/mips-defs.h @@ -10,8 +10,8 @@ #if defined(TARGET_MIPS64) #define TARGET_LONG_BITS 64 -#define TARGET_PHYS_ADDR_SPACE_BITS 36 -#define TARGET_VIRT_ADDR_SPACE_BITS 42 +#define TARGET_PHYS_ADDR_SPACE_BITS 48 +#define TARGET_VIRT_ADDR_SPACE_BITS 64 #else #define TARGET_LONG_BITS 32 #define TARGET_PHYS_ADDR_SPACE_BITS 36 diff --git a/target-mips/translate_init.c b/target-mips/translate_init.c index 590e092..fe02206 100644 --- a/target-mips/translate_init.c +++ b/target-mips/translate_init.c @@ -484,6 +484,32 @@ static const mips_def_t mips_defs[] = .insn_flags = CPU_LOONGSON2F, .mmu_type = MMU_TYPE_R4000, }, + { + /* godson3a CPU */ + .name = "godson3a", + .CP0_PRid = 0x6303, + .CP0_Config0 = MIPS_CONFIG0 | (0x1 << CP0C0_AR) | (0x2 << CP0C0_AT) | + (MMU_TYPE_R4000 << CP0C0_MT), + .CP0_Config1 = MIPS_CONFIG1 | (1 << CP0C1_FP) | (63 << CP0C1_MMU) | + (2 << CP0C1_IS) | (4 << CP0C1_IL) | (3 << CP0C1_IA) | + (2 << CP0C1_DS) | (4 << CP0C1_DL) | (3 << CP0C1_DA) | + (1 << CP0C1_PC) | (1 << CP0C1_WR) | (1 << CP0C1_EP), + .CP0_Config2 = MIPS_CONFIG2, + .CP0_Config3 = MIPS_CONFIG3 | (1 << CP0C3_LPA), + .SYNCI_Step = 32, + .CCRes = 2, + .CP0_Status_rw_bitmask = 0x36FBFFFF, + .CP1_fcr0 = (1 << FCR0_F64) | (1 << FCR0_3D) | (1 << FCR0_PS) | + (1 << FCR0_L) | (1 << FCR0_W) | (1 << FCR0_D) | + (1 << FCR0_S) | (0x00 << FCR0_PRID) | (0x0 << FCR0_REV), + .SEGBITS = 42, + /* The architectural limit is 59, but we have hardcoded 36 bit + in some places... + .PABITS = 59, */ /* the architectural limit */ + .PABITS = 48, + .insn_flags = CPU_MIPS64R2 | ASE_MIPS3D, + .mmu_type = MMU_TYPE_R4000, + }, #endif }; -- 1.5.2.3