qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH] MIPS Initial support of Godson-3a multicore CPU


From: Jin Guojie
Subject: [Qemu-devel] [PATCH] MIPS Initial support of Godson-3a multicore CPU
Date: Tue, 7 Dec 2010 17:32:10 +0800

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



reply via email to

[Prev in Thread] Current Thread [Next in Thread]