All MPC85xx boards use E500v1/v2 core. This patch add emulation of a virtual MPC85xx board, so that any MPC85xx host could run this emulation.
Only tested it on MPC8544DS and MPC8572DS hosts, but it should work on other MPC85xx boards. Signed-off-by: Liu Yu <yu....@freescale.com> --- Makefile.target | 2 +- hw/boards.h | 1 + hw/ppce500.h | 22 +++ hw/ppce500_mpc85xx.c | 346 ++++++++++++++++++++++++++++++++++++++++++++++++++ target-ppc/machine.c | 1 + 5 files changed, 371 insertions(+), 1 deletions(-) create mode 100644 hw/ppce500.h create mode 100644 hw/ppce500_mpc85xx.c diff --git a/Makefile.target b/Makefile.target index 5cae257..3852f53 100644 --- a/Makefile.target +++ b/Makefile.target @@ -599,7 +599,7 @@ OBJS+= unin_pci.o ppc_chrp.o OBJS+= pflash_cfi02.o ppc4xx_devs.o ppc4xx_pci.o ppc405_uc.o ppc405_boards.o OBJS+= ppc440.o ppc440_bamboo.o # PowerPC E500 boards -OBJS+= ppce500_pci.o +OBJS+= ppce500_pci.o ppce500_mpc85xx.o ifdef FDT_LIBS OBJS+= device_tree.o LIBS+= $(FDT_LIBS) diff --git a/hw/boards.h b/hw/boards.h index 0577f06..1939f78 100644 --- a/hw/boards.h +++ b/hw/boards.h @@ -40,6 +40,7 @@ extern QEMUMachine heathrow_machine; extern QEMUMachine ref405ep_machine; extern QEMUMachine taihu_machine; extern QEMUMachine bamboo_machine; +extern QEMUMachine mpc85xx_machine; /* mips_r4k.c */ extern QEMUMachine mips_machine; diff --git a/hw/ppce500.h b/hw/ppce500.h new file mode 100644 index 0000000..24d49bb --- /dev/null +++ b/hw/ppce500.h @@ -0,0 +1,22 @@ +/* + * QEMU PowerPC E500 emulation shared definitions + * + * Copyright (C) 2009 Freescale Semiconductor, Inc. All rights reserved. + * + * Author: Yu Liu, <yu....@freescale.com> + * + * This file is derived from hw/ppc440.h + * the copyright for that material belongs to the original owners. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#if !defined(PPC_E500_H) +#define PPC_E500_H + +PCIBus *ppce500_pci_init(qemu_irq *pic, target_phys_addr_t registers); + +#endif /* !defined(PPC_E500_H) */ diff --git a/hw/ppce500_mpc85xx.c b/hw/ppce500_mpc85xx.c new file mode 100644 index 0000000..9dd619d --- /dev/null +++ b/hw/ppce500_mpc85xx.c @@ -0,0 +1,346 @@ +/* + * Qemu PowerPC MPC85xx board emualtion + * + * Copyright (C) 2009 Freescale Semiconductor, Inc. All rights reserved. + * + * Author: Yu Liu, <yu....@freescale.com> + * + * This file is derived from hw/ppc440_bamboo.c, + * the copyright for that material belongs to the original owners. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <dirent.h> + +#include "config.h" +#include "qemu-common.h" +#include "net.h" +#include "hw.h" +#include "pc.h" +#include "pci.h" +#include "virtio-blk.h" +#include "boards.h" +#include "sysemu.h" +#include "kvm.h" +#include "kvm_ppc.h" +#include "device_tree.h" +#include "openpic.h" +#include "ppce500.h" + +#define BINARY_DEVICE_TREE_FILE "mpc85xx.dtb" +#define UIMAGE_LOAD_BASE 0 +#define DTB_LOAD_BASE 0x600000 +#define INITRD_LOAD_BASE 0x2000000 + +#define RAM_SIZES_ALIGN (64UL << 20) + +#define MPC85xx_CCSRBAR_BASE 0xE0000000 +#define MPC85xx_MPIC_REGS_BASE (MPC85xx_CCSRBAR_BASE + 0x40000) +#define MPC85xx_SERIAL0_REGS_BASE (MPC85xx_CCSRBAR_BASE + 0x4500) +#define MPC85xx_SERIAL1_REGS_BASE (MPC85xx_CCSRBAR_BASE + 0x4600) +#define MPC85xx_PCI_REGS_BASE (MPC85xx_CCSRBAR_BASE + 0x8000) +#define MPC85xx_PCI_REGS_SIZE 0x1000 +#define MPC85xx_PCI_IO 0xE1000000 +#define MPC85xx_PCI_IOLEN 0x10000 + +struct board { + const char *model; + const char *compatible; +}; + +#define BOARD_DEF(_model, _compatible) \ + { \ + .model = _model, \ + .compatible = _compatible, \ + } + +/* Supported host boards */ +static const struct board mpc85xx_table[] = { + BOARD_DEF("MPC8544DS", "MPC8544DS"), /* MPC8544DS */ + BOARD_DEF("fsl,MPC8572DS", "fsl,MPC8572DS"), /* MPC8572DS */ + BOARD_DEF("fsl,mpc8536ds", "fsl,mpc8536ds"), /* MPC8536DS */ + BOARD_DEF("MPC8548CDS", "MPC8548CDS"), /* MPC8548CDS */ + BOARD_DEF("MPC8555CDS", "MPC8555CDS"), /* MPC8555CDS */ + BOARD_DEF("MPC8541CDS", "MPC8541CDS"), /* MPC8541CDS */ + BOARD_DEF("MPC8540ADS", "MPC8540ADS"), /* MPC8540ADS */ + BOARD_DEF("MPC8560ADS", "MPC8560ADS"), /* MPC8560ADS */ + BOARD_DEF("MPC8568EMDS", "MPC8568EMDS"), /* MPC8568EMDS */ +}; + +#define BOARDS_NUM (sizeof(mpc85xx_table)/sizeof(struct board)) + +static int mpc85xx_copy_host_cell(void *fdt, const char *node, const char *prop) +{ + uint32_t cell; + int ret; + + ret = kvmppc_read_host_property(node, prop, &cell, sizeof(cell)); + if (ret < 0) { + fprintf(stderr, "couldn't read host %s/%s\n", node, prop); + goto out; + } + + ret = qemu_devtree_setprop_cell(fdt, "/cpus/PowerPC,8...@0", + prop, cell); + if (ret < 0) { + fprintf(stderr, "couldn't set guest /cpus/PowerPC,8...@0/%s\n", prop); + goto out; + } + +out: + return ret; +} + +static void *mpc85xx_load_device_tree(void *addr, + uint32_t ramsize, + target_phys_addr_t initrd_base, + target_phys_addr_t initrd_size, + const char *kernel_cmdline) +{ + void *fdt = NULL; +#ifdef HAVE_FDT + uint32_t mem_reg_property[] = {0, ramsize}; + char *path; + int pathlen; + int ret; + + pathlen = snprintf(NULL, 0, "%s/%s", bios_dir, BINARY_DEVICE_TREE_FILE) + 1; + path = qemu_malloc(pathlen); + if (path == NULL) + goto out; + + snprintf(path, pathlen, "%s/%s", bios_dir, BINARY_DEVICE_TREE_FILE); + + fdt = load_device_tree(path, addr); + free(path); + if (fdt == NULL) + goto out; + + /* Manipulate device tree in memory. */ + + ret = qemu_devtree_setprop(fdt, "/memory", "reg", mem_reg_property, + sizeof(mem_reg_property)); + if (ret < 0) + fprintf(stderr, "couldn't set /memory/reg\n"); + + ret = qemu_devtree_setprop_cell(fdt, "/chosen", "linux,initrd-start", + initrd_base); + if (ret < 0) + fprintf(stderr, "couldn't set /chosen/linux,initrd-start\n"); + + ret = qemu_devtree_setprop_cell(fdt, "/chosen", "linux,initrd-end", + (initrd_base + initrd_size)); + if (ret < 0) + fprintf(stderr, "couldn't set /chosen/linux,initrd-end\n"); + + ret = qemu_devtree_setprop_string(fdt, "/chosen", "bootargs", + kernel_cmdline); + if (ret < 0) + fprintf(stderr, "couldn't set /chosen/bootargs\n"); + + if (kvm_enabled()) { + FILE *fp; + char *model; + char const *compatible = NULL; + struct dirent *dirp; + DIR *dp; + int i; + char buf[128]; + + if ((fp = fopen("/proc/cpuinfo", "r")) == NULL) { + printf("Can't open file /proc/cpuinfo\n"); + goto out; + } + while (fgets(buf, 128, fp) != NULL) { + if (strncmp(buf, "model", 5) == 0) { + model = buf + 9; + break; + } + } + fclose(fp); + + for (i = 0; i < BOARDS_NUM; i++) { + if (strncmp(model, mpc85xx_table[i].model, + strlen(mpc85xx_table[i].model)) == 0) { + compatible = mpc85xx_table[i].compatible; + } + } + + if (compatible == NULL) { + printf("Unknow host board!\n"); + goto out; + } + + ret = qemu_devtree_setprop_string(fdt, "/", "compatible", compatible); + if (ret < 0) + fprintf(stderr, "couldn't set /compatible = %s\n", compatible); + + if ((dp = opendir("/proc/device-tree/cpus/")) == NULL) { + printf("Can't open directory /proc/device-tree/cpus/\n"); + goto out; + } + + buf[0] = '\0'; + while ((dirp = readdir(dp)) != NULL) { + if (strncmp(dirp->d_name, "PowerPC", 7) == 0) { + sprintf(buf, "/proc/device-tree/cpus/%s", dirp->d_name); + break; + } + } + closedir(dp); + if (buf[0] == '\0') { + printf("Unknow host!\n"); + goto out; + } + path = buf + 17; + + mpc85xx_copy_host_cell(fdt, path, "clock-frequency"); + mpc85xx_copy_host_cell(fdt, path, "timebase-frequency"); + } + +out: +#endif + + return fdt; +} + +static void mpc85xx_init(ram_addr_t ram_size, int vga_ram_size, + const char *boot_device, + const char *kernel_filename, + const char *kernel_cmdline, + const char *initrd_filename, + const char *cpu_model) +{ + PCIBus *pci_bus; + CPUState *env; + uint64_t elf_entry; + uint64_t elf_lowaddr; + target_ulong entry=0; + target_ulong loadaddr=UIMAGE_LOAD_BASE; + target_long kernel_size=0; + target_ulong dt_base=DTB_LOAD_BASE; + target_ulong initrd_base=INITRD_LOAD_BASE; + target_long initrd_size=0; + void *fdt; + int i=0; + unsigned int pci_irq_nrs[4] = {1, 2, 3, 4}; + qemu_irq *irqs, *mpic, *pci_irqs; + SerialState * serial[2]; + + /* Setup CPU */ + env = cpu_ppc_init("e500v2_v30"); + if (!env) { + fprintf(stderr, "Unable to initialize CPU!\n"); + exit(1); + } + + /* Fixup Memory size on a alignment boundary */ + ram_size &= ~(RAM_SIZES_ALIGN - 1); + + /* Register Memory */ + cpu_register_physical_memory(0, ram_size, 0); + + /* MPIC */ + irqs = qemu_mallocz(sizeof(qemu_irq) * OPENPIC_OUTPUT_NB); + irqs[OPENPIC_OUTPUT_INT] = ((qemu_irq *)env->irq_inputs)[PPCE500_INPUT_INT]; + irqs[OPENPIC_OUTPUT_CINT] = ((qemu_irq *)env->irq_inputs)[PPCE500_INPUT_CINT]; + mpic = mpic_init(MPC85xx_MPIC_REGS_BASE, 1, &irqs, NULL); + + /* Serial */ + if (serial_hds[0]) + serial[0] = serial_mm_init(MPC85xx_SERIAL0_REGS_BASE, + 0, mpic[12+26], 399193, + serial_hds[0], 1); + + if (serial_hds[1]) + serial[0] = serial_mm_init(MPC85xx_SERIAL1_REGS_BASE, + 0, mpic[12+26], 399193, + serial_hds[0], 1); + + /* PCI */ + pci_irqs = qemu_malloc(sizeof(qemu_irq) * 4); + pci_irqs[0] = mpic[pci_irq_nrs[0]]; + pci_irqs[1] = mpic[pci_irq_nrs[1]]; + pci_irqs[2] = mpic[pci_irq_nrs[2]]; + pci_irqs[3] = mpic[pci_irq_nrs[3]]; + pci_bus = ppce500_pci_init(pci_irqs, MPC85xx_PCI_REGS_BASE); + if (!pci_bus) + printf("couldn't create PCI controller!\n"); + + isa_mmio_init(MPC85xx_PCI_IO, MPC85xx_PCI_IOLEN); + + if (pci_bus) { + int unit_id = 0; + + /* Add virtio block devices. */ + while ((i = drive_get_index(IF_VIRTIO, 0, unit_id)) != -1) { + virtio_blk_init(pci_bus, drives_table[i].bdrv); + unit_id++; + } + + /* Register network interfaces. */ + for (i = 0; i < nb_nics; i++) { + pci_nic_init(pci_bus, &nd_table[i], -1, "virtio"); + } + } + + /* Load kernel. */ + if (kernel_filename) { + kernel_size = load_uimage(kernel_filename, &entry, &loadaddr, NULL); + if (kernel_size < 0) { + kernel_size = load_elf(kernel_filename, 0, &elf_entry, &elf_lowaddr, + NULL); + entry = elf_entry; + loadaddr = elf_lowaddr; + } + /* XXX try again as binary */ + if (kernel_size < 0) { + fprintf(stderr, "qemu: could not load kernel '%s'\n", + kernel_filename); + exit(1); + } + } + + /* Load initrd. */ + if (initrd_filename) { + initrd_size = load_image(initrd_filename, phys_ram_base + initrd_base); + + if (initrd_size < 0) { + fprintf(stderr, "qemu: could not load initial ram disk '%s'\n", + initrd_filename); + exit(1); + } + } + + /* If we're loading a kernel directly, we must load the device tree too. */ + if (kernel_filename) { + fdt = mpc85xx_load_device_tree(phys_ram_base + dt_base, ram_size, + initrd_base, initrd_size, kernel_cmdline); + if (fdt == NULL) { + fprintf(stderr, "couldn't load device tree\n"); + exit(1); + } + + /* Set initial guest state. */ + env->gpr[1] = (16<<20) - 8; + env->gpr[3] = dt_base; + env->nip = entry; + /* XXX we currently depend on KVM to create some initial TLB entries. */ + } + + if (kvm_enabled()) + kvmppc_init(); + + return; +} + +QEMUMachine mpc85xx_machine = { + .name = "mpc85xx", + .desc = "mpc85xx", + .init = mpc85xx_init, + .ram_require = 64<<20 | RAMSIZE_FIXED, +}; diff --git a/target-ppc/machine.c b/target-ppc/machine.c index 4f9a191..eb64232 100644 --- a/target-ppc/machine.c +++ b/target-ppc/machine.c @@ -9,6 +9,7 @@ void register_machines(void) qemu_register_machine(&ref405ep_machine); qemu_register_machine(&taihu_machine); qemu_register_machine(&bamboo_machine); + qemu_register_machine(&mpc85xx_machine); } void cpu_save(QEMUFile *f, void *opaque) -- 1.5.4 -- To unsubscribe from this list: send the line "unsubscribe kvm-ppc" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html