[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [Qemu-devel] [PATCH] MIPS Initial support of Godson-3a multicore CPU
From: |
chen huacai |
Subject: |
Re: [Qemu-devel] [PATCH] MIPS Initial support of Godson-3a multicore CPU |
Date: |
Tue, 7 Dec 2010 22:13:07 +0800 |
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 <address@hidden> wrote:
> Signed-off-by: "Jin Guojie" <address@hidden>
> Reviewed-by: "Gao Xiang" <address@hidden>
> Reviewed-by: "Chen Huacai" <address@hidden>
>
> 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 (address@hidden)
> + * Copyright (c) 2010 Jin Guojie (address@hidden)
> + * 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
>
>
--
Huacai Chen