diff mbox

MIPS Initial support of Godson-3a multicore CPU

Message ID AANLkTi=JiN99Ly4Tm6eV-YQGQNep-uYxgj0OqLct0WsM@mail.gmail.com
State New
Headers show

Commit Message

Jin Guojie Dec. 7, 2010, 9:32 a.m. UTC
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

Comments

chen huacai Dec. 7, 2010, 2:13 p.m. UTC | #1
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 mbox

Patch

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
 };