Message ID | AANLkTi=JiN99Ly4Tm6eV-YQGQNep-uYxgj0OqLct0WsM@mail.gmail.com |
---|---|
State | New |
Headers | show |
1, signed-off-by and reviewed-by should put after the introduction and before the code, not at first. 2, You'd better split the big patch to 2~3 small patches, E.g. CPU definition and board emulation should be split. The format of email title will be [Patch 0/3], [Patch 1/3] and so on. [Patch 0/3] give a general introduction and others are real patches. 3, You should tell us how to test you code. E.g., you should provide PMON/BIOS, OS kernel or tell us how to build them in [patch 0/3]. For more information, you can search for my patch series about Loongson-2E. Good luck! Huacai Chen On Tue, Dec 7, 2010 at 5:32 PM, Jin Guojie <jinguojie@loongson.cn> wrote: > Signed-off-by: "Jin Guojie" <jinguojie@loongson.cn> > Reviewed-by: "Gao Xiang" <gaoxiang@ict.ac.cn> > Reviewed-by: "Chen Huacai" <zltjiangshi@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 (gaoxiang@ict.ac.cn) > + * Copyright (c) 2010 Jin Guojie (jinguojie@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 > >
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 (gaoxiang@ict.ac.cn) + * Copyright (c) 2010 Jin Guojie (jinguojie@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 };