12c9fade2Saurel32 /* 25cbdb3a3SStefan Weil * QEMU PowerPC 440 Bamboo board emulation 32c9fade2Saurel32 * 42c9fade2Saurel32 * Copyright 2007 IBM Corporation. 52c9fade2Saurel32 * Authors: 62c9fade2Saurel32 * Jerone Young <jyoung5@us.ibm.com> 72c9fade2Saurel32 * Christian Ehrhardt <ehrhardt@linux.vnet.ibm.com> 82c9fade2Saurel32 * Hollis Blanchard <hollisb@us.ibm.com> 92c9fade2Saurel32 * 102c9fade2Saurel32 * This work is licensed under the GNU GPL license version 2 or later. 112c9fade2Saurel32 * 122c9fade2Saurel32 */ 132c9fade2Saurel32 140d75590dSPeter Maydell #include "qemu/osdep.h" 15ab3dd749SPhilippe Mathieu-Daudé #include "qemu/units.h" 162c65db5eSPaolo Bonzini #include "qemu/datadir.h" 1764b47457Sthuth@redhat.com #include "qemu/error-report.h" 181422e32dSPaolo Bonzini #include "net/net.h" 1983c9f4caSPaolo Bonzini #include "hw/pci/pci.h" 2083c9f4caSPaolo Bonzini #include "hw/boards.h" 219c17d615SPaolo Bonzini #include "sysemu/kvm.h" 229c17d615SPaolo Bonzini #include "sysemu/device_tree.h" 2383c9f4caSPaolo Bonzini #include "hw/loader.h" 24ca20cf32SBlue Swirl #include "elf.h" 250d09e41aSPaolo Bonzini #include "hw/char/serial.h" 260d09e41aSPaolo Bonzini #include "hw/ppc/ppc.h" 279c17d615SPaolo Bonzini #include "sysemu/sysemu.h" 2871e8a915SMarkus Armbruster #include "sysemu/reset.h" 2983c9f4caSPaolo Bonzini #include "hw/sysbus.h" 300270d74eSPeter Maydell #include "hw/intc/ppc-uic.h" 310270d74eSPeter Maydell #include "hw/qdev-properties.h" 320270d74eSPeter Maydell #include "qapi/error.h" 332c9fade2Saurel32 348d42c851SDaniel Henrique Barboza #include <libfdt.h> 358d42c851SDaniel Henrique Barboza 362c9fade2Saurel32 #define BINARY_DEVICE_TREE_FILE "bamboo.dtb" 372c9fade2Saurel32 38ceee6da6SHollis Blanchard /* from u-boot */ 39ceee6da6SHollis Blanchard #define KERNEL_ADDR 0x1000000 40ceee6da6SHollis Blanchard #define FDT_ADDR 0x1800000 41ceee6da6SHollis Blanchard #define RAMDISK_ADDR 0x1900000 42ceee6da6SHollis Blanchard 433960b04dSAlexander Graf #define PPC440EP_PCI_CONFIG 0xeec00000 443960b04dSAlexander Graf #define PPC440EP_PCI_INTACK 0xeed00000 453960b04dSAlexander Graf #define PPC440EP_PCI_SPECIAL 0xeed00000 463960b04dSAlexander Graf #define PPC440EP_PCI_REGS 0xef400000 473960b04dSAlexander Graf #define PPC440EP_PCI_IO 0xe8000000 483960b04dSAlexander Graf #define PPC440EP_PCI_IOLEN 0x00010000 493960b04dSAlexander Graf 50a8170e5eSAvi Kivity static hwaddr entry; 51b10a04b5SAlexander Graf 528d42c851SDaniel Henrique Barboza static int bamboo_load_device_tree(MachineState *machine, 538d42c851SDaniel Henrique Barboza hwaddr addr, 54a8170e5eSAvi Kivity hwaddr initrd_base, 558d42c851SDaniel Henrique Barboza hwaddr initrd_size) 562c9fade2Saurel32 { 57dbf916d8SAurelien Jarno int ret = -1; 588d42c851SDaniel Henrique Barboza uint32_t mem_reg_property[] = { 0, 0, cpu_to_be32(machine->ram_size) }; 595cea8590SPaul Brook char *filename; 607ec632b4Spbrook int fdt_size; 61dbf916d8SAurelien Jarno void *fdt; 627dadd40cSAlexander Graf uint32_t tb_freq = 400000000; 637dadd40cSAlexander Graf uint32_t clock_freq = 400000000; 642c9fade2Saurel32 655cea8590SPaul Brook filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE); 665cea8590SPaul Brook if (!filename) { 67400431efSDaniel Henrique Barboza return -1; 685cea8590SPaul Brook } 695cea8590SPaul Brook fdt = load_device_tree(filename, &fdt_size); 707267c094SAnthony Liguori g_free(filename); 715cea8590SPaul Brook if (fdt == NULL) { 72400431efSDaniel Henrique Barboza return -1; 735cea8590SPaul Brook } 742c9fade2Saurel32 752c9fade2Saurel32 /* Manipulate device tree in memory. */ 762c9fade2Saurel32 775a4348d1SPeter Crosthwaite ret = qemu_fdt_setprop(fdt, "/memory", "reg", mem_reg_property, 782c9fade2Saurel32 sizeof(mem_reg_property)); 7995e22932SBALATON Zoltan if (ret < 0) { 802c9fade2Saurel32 fprintf(stderr, "couldn't set /memory/reg\n"); 8195e22932SBALATON Zoltan } 825a4348d1SPeter Crosthwaite ret = qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-start", 832c9fade2Saurel32 initrd_base); 8495e22932SBALATON Zoltan if (ret < 0) { 852c9fade2Saurel32 fprintf(stderr, "couldn't set /chosen/linux,initrd-start\n"); 8695e22932SBALATON Zoltan } 875a4348d1SPeter Crosthwaite ret = qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-end", 882c9fade2Saurel32 (initrd_base + initrd_size)); 8995e22932SBALATON Zoltan if (ret < 0) { 902c9fade2Saurel32 fprintf(stderr, "couldn't set /chosen/linux,initrd-end\n"); 9195e22932SBALATON Zoltan } 925a4348d1SPeter Crosthwaite ret = qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", 938d42c851SDaniel Henrique Barboza machine->kernel_cmdline); 9495e22932SBALATON Zoltan if (ret < 0) { 952c9fade2Saurel32 fprintf(stderr, "couldn't set /chosen/bootargs\n"); 9695e22932SBALATON Zoltan } 972c9fade2Saurel32 985a4348d1SPeter Crosthwaite qemu_fdt_setprop_cell(fdt, "/cpus/cpu@0", "clock-frequency", 997dadd40cSAlexander Graf clock_freq); 1005a4348d1SPeter Crosthwaite qemu_fdt_setprop_cell(fdt, "/cpus/cpu@0", "timebase-frequency", 1017dadd40cSAlexander Graf tb_freq); 1022c9fade2Saurel32 103fe1479aaSMichael S. Tsirkin rom_add_blob_fixed(BINARY_DEVICE_TREE_FILE, fdt, fdt_size, addr); 1048d42c851SDaniel Henrique Barboza 1058d42c851SDaniel Henrique Barboza /* Set ms->fdt for 'dumpdtb' QMP/HMP command */ 1068d42c851SDaniel Henrique Barboza machine->fdt = fdt; 1078d42c851SDaniel Henrique Barboza 108fe1479aaSMichael S. Tsirkin return 0; 1092c9fade2Saurel32 } 1102c9fade2Saurel32 11172718e9aSAlexander Graf /* Create reset TLB entries for BookE, spanning the 32bit addr space. */ 112e2684c0bSAndreas Färber static void mmubooke_create_initial_mapping(CPUPPCState *env, 11372718e9aSAlexander Graf target_ulong va, 114a8170e5eSAvi Kivity hwaddr pa) 11572718e9aSAlexander Graf { 11672718e9aSAlexander Graf ppcemb_tlb_t *tlb = &env->tlb.tlbe[0]; 11772718e9aSAlexander Graf 11872718e9aSAlexander Graf tlb->attr = 0; 11972718e9aSAlexander Graf tlb->prot = PAGE_VALID | ((PAGE_READ | PAGE_WRITE | PAGE_EXEC) << 4); 120a1f7f97bSPeter Maydell tlb->size = 1U << 31; /* up to 0x80000000 */ 12172718e9aSAlexander Graf tlb->EPN = va & TARGET_PAGE_MASK; 12272718e9aSAlexander Graf tlb->RPN = pa & TARGET_PAGE_MASK; 12372718e9aSAlexander Graf tlb->PID = 0; 12472718e9aSAlexander Graf 12572718e9aSAlexander Graf tlb = &env->tlb.tlbe[1]; 12672718e9aSAlexander Graf tlb->attr = 0; 12772718e9aSAlexander Graf tlb->prot = PAGE_VALID | ((PAGE_READ | PAGE_WRITE | PAGE_EXEC) << 4); 128a1f7f97bSPeter Maydell tlb->size = 1U << 31; /* up to 0xffffffff */ 12972718e9aSAlexander Graf tlb->EPN = 0x80000000 & TARGET_PAGE_MASK; 13072718e9aSAlexander Graf tlb->RPN = 0x80000000 & TARGET_PAGE_MASK; 13172718e9aSAlexander Graf tlb->PID = 0; 13272718e9aSAlexander Graf } 13372718e9aSAlexander Graf 134b10a04b5SAlexander Graf static void main_cpu_reset(void *opaque) 135b10a04b5SAlexander Graf { 136182fbbf2SAndreas Färber PowerPCCPU *cpu = opaque; 137182fbbf2SAndreas Färber CPUPPCState *env = &cpu->env; 138b10a04b5SAlexander Graf 139182fbbf2SAndreas Färber cpu_reset(CPU(cpu)); 140ab3dd749SPhilippe Mathieu-Daudé env->gpr[1] = (16 * MiB) - 8; 141b10a04b5SAlexander Graf env->gpr[3] = FDT_ADDR; 142b10a04b5SAlexander Graf env->nip = entry; 14372718e9aSAlexander Graf 14472718e9aSAlexander Graf /* Create a mapping for the kernel. */ 14572718e9aSAlexander Graf mmubooke_create_initial_mapping(env, 0, 0); 146b10a04b5SAlexander Graf } 147b10a04b5SAlexander Graf 1483ef96221SMarcel Apfelbaum static void bamboo_init(MachineState *machine) 1492c9fade2Saurel32 { 1503ef96221SMarcel Apfelbaum const char *kernel_filename = machine->kernel_filename; 1513ef96221SMarcel Apfelbaum const char *initrd_filename = machine->initrd_filename; 152053b7086SThomas Huth MachineClass *mc = MACHINE_GET_CLASS(machine); 1532c9fade2Saurel32 unsigned int pci_irq_nrs[4] = { 28, 27, 26, 25 }; 1543e9f0113SRichard Henderson MemoryRegion *address_space_mem = get_system_memory(); 15568501502SPaolo Bonzini MemoryRegion *isa = g_new(MemoryRegion, 1); 1562c9fade2Saurel32 PCIBus *pcibus; 157322164e0SAndreas Färber PowerPCCPU *cpu; 158e2684c0bSAndreas Färber CPUPPCState *env; 1592c9fade2Saurel32 target_long initrd_size = 0; 16034ba1dc8SAlexander Graf DeviceState *dev; 1610270d74eSPeter Maydell DeviceState *uicdev; 1620270d74eSPeter Maydell SysBusDevice *uicsbd; 163ceee6da6SHollis Blanchard int success; 1642c9fade2Saurel32 16574b2fd63SCédric Le Goater if (kvm_enabled()) { 16674b2fd63SCédric Le Goater error_report("machine %s does not support the KVM accelerator", 16774b2fd63SCédric Le Goater MACHINE_GET_CLASS(machine)->name); 16874b2fd63SCédric Le Goater exit(EXIT_FAILURE); 16974b2fd63SCédric Le Goater } 17074b2fd63SCédric Le Goater 171376d7a2aSIgor Mammedov cpu = POWERPC_CPU(cpu_create(machine->cpu_type)); 172322164e0SAndreas Färber env = &cpu->env; 17334ba1dc8SAlexander Graf 17400469dc3SValentin Plotkin if (env->mmu_model != POWERPC_MMU_BOOKE) { 1756f76b817SAlistair Francis error_report("MMU model %i not supported by this machine", 17600469dc3SValentin Plotkin env->mmu_model); 17700469dc3SValentin Plotkin exit(1); 17800469dc3SValentin Plotkin } 17900469dc3SValentin Plotkin 180182fbbf2SAndreas Färber qemu_register_reset(main_cpu_reset, cpu); 181a34a92b9SAndreas Färber ppc_booke_timers_init(cpu, 400000000, 0); 18234ba1dc8SAlexander Graf ppc_dcr_init(env, NULL, NULL); 18334ba1dc8SAlexander Graf 18434ba1dc8SAlexander Graf /* interrupt controller */ 1850270d74eSPeter Maydell uicdev = qdev_new(TYPE_PPC_UIC); 186a55b2136SBALATON Zoltan ppc4xx_dcr_realize(PPC4xx_DCR_DEVICE(uicdev), cpu, &error_fatal); 187a55b2136SBALATON Zoltan object_unref(OBJECT(uicdev)); 1880270d74eSPeter Maydell uicsbd = SYS_BUS_DEVICE(uicdev); 1890270d74eSPeter Maydell sysbus_connect_irq(uicsbd, PPCUIC_OUTPUT_INT, 19047b60fc6SCédric Le Goater qdev_get_gpio_in(DEVICE(cpu), PPC40x_INPUT_INT)); 1910270d74eSPeter Maydell sysbus_connect_irq(uicsbd, PPCUIC_OUTPUT_CINT, 19247b60fc6SCédric Le Goater qdev_get_gpio_in(DEVICE(cpu), PPC40x_INPUT_CINT)); 19334ba1dc8SAlexander Graf 19434ba1dc8SAlexander Graf /* SDRAM controller */ 1954fc30e15SBALATON Zoltan dev = qdev_new(TYPE_PPC4xx_SDRAM_DDR); 1964fc30e15SBALATON Zoltan object_property_set_link(OBJECT(dev), "dram", OBJECT(machine->ram), 1974fc30e15SBALATON Zoltan &error_abort); 1984fc30e15SBALATON Zoltan ppc4xx_dcr_realize(PPC4xx_DCR_DEVICE(dev), cpu, &error_fatal); 1994fc30e15SBALATON Zoltan object_unref(OBJECT(dev)); 20034ba1dc8SAlexander Graf /* XXX 440EP's ECC interrupts are on UIC1, but we've only created UIC0. */ 2014fc30e15SBALATON Zoltan sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, qdev_get_gpio_in(uicdev, 14)); 20268b9a2e3SBALATON Zoltan /* Enable SDRAM memory regions, this should be done by the firmware */ 2031e545fbcSBALATON Zoltan ppc4xx_sdram_ddr_enable(PPC4xx_SDRAM_DDR(dev)); 20434ba1dc8SAlexander Graf 20534ba1dc8SAlexander Graf /* PCI */ 206e75a951bSBALATON Zoltan dev = sysbus_create_varargs(TYPE_PPC4xx_PCI_HOST, PPC440EP_PCI_CONFIG, 2070270d74eSPeter Maydell qdev_get_gpio_in(uicdev, pci_irq_nrs[0]), 2080270d74eSPeter Maydell qdev_get_gpio_in(uicdev, pci_irq_nrs[1]), 2090270d74eSPeter Maydell qdev_get_gpio_in(uicdev, pci_irq_nrs[2]), 2100270d74eSPeter Maydell qdev_get_gpio_in(uicdev, pci_irq_nrs[3]), 21134ba1dc8SAlexander Graf NULL); 21234ba1dc8SAlexander Graf pcibus = (PCIBus *)qdev_get_child_bus(dev, "pci.0"); 21334ba1dc8SAlexander Graf if (!pcibus) { 2146f76b817SAlistair Francis error_report("couldn't create PCI controller"); 21534ba1dc8SAlexander Graf exit(1); 21634ba1dc8SAlexander Graf } 21734ba1dc8SAlexander Graf 21868501502SPaolo Bonzini memory_region_init_alias(isa, NULL, "isa_mmio", 21968501502SPaolo Bonzini get_system_io(), 0, PPC440EP_PCI_IOLEN); 22068501502SPaolo Bonzini memory_region_add_subregion(get_system_memory(), PPC440EP_PCI_IO, isa); 22134ba1dc8SAlexander Graf 2229bca0edbSPeter Maydell if (serial_hd(0) != NULL) { 2230270d74eSPeter Maydell serial_mm_init(address_space_mem, 0xef600300, 0, 2240270d74eSPeter Maydell qdev_get_gpio_in(uicdev, 0), 2259bca0edbSPeter Maydell PPC_SERIAL_MM_BAUDBASE, serial_hd(0), 22634ba1dc8SAlexander Graf DEVICE_BIG_ENDIAN); 22734ba1dc8SAlexander Graf } 2289bca0edbSPeter Maydell if (serial_hd(1) != NULL) { 2290270d74eSPeter Maydell serial_mm_init(address_space_mem, 0xef600400, 0, 2300270d74eSPeter Maydell qdev_get_gpio_in(uicdev, 1), 2319bca0edbSPeter Maydell PPC_SERIAL_MM_BAUDBASE, serial_hd(1), 23234ba1dc8SAlexander Graf DEVICE_BIG_ENDIAN); 23334ba1dc8SAlexander Graf } 2342c9fade2Saurel32 2352c9fade2Saurel32 if (pcibus) { 23695e22932SBALATON Zoltan /* 23795e22932SBALATON Zoltan * There are no PCI NICs on the Bamboo board, but there are 23895e22932SBALATON Zoltan * PCI slots, so we can pick whatever default model we want. 23995e22932SBALATON Zoltan */ 240*36b6968dSDavid Woodhouse pci_init_nic_devices(pcibus, mc->default_nic); 2412c9fade2Saurel32 } 2422c9fade2Saurel32 2432c9fade2Saurel32 /* Load kernel. */ 2442c9fade2Saurel32 if (kernel_filename) { 245617160c9SBALATON Zoltan hwaddr loadaddr = LOAD_UIMAGE_LOADADDR_INVALID; 24625bda50aSMax Filippov success = load_uimage(kernel_filename, &entry, &loadaddr, NULL, 24725bda50aSMax Filippov NULL, NULL); 248ceee6da6SHollis Blanchard if (success < 0) { 249617160c9SBALATON Zoltan uint64_t elf_entry; 2504366e1dbSLiam Merwick success = load_elf(kernel_filename, NULL, NULL, NULL, &elf_entry, 251617160c9SBALATON Zoltan NULL, NULL, NULL, 1, PPC_ELF_MACHINE, 0, 0); 2522c9fade2Saurel32 entry = elf_entry; 2532c9fade2Saurel32 } 2542c9fade2Saurel32 /* XXX try again as binary */ 255ceee6da6SHollis Blanchard if (success < 0) { 2566f76b817SAlistair Francis error_report("could not load kernel '%s'", kernel_filename); 2572c9fade2Saurel32 exit(1); 2582c9fade2Saurel32 } 2592c9fade2Saurel32 } 2602c9fade2Saurel32 2612c9fade2Saurel32 /* Load initrd. */ 2622c9fade2Saurel32 if (initrd_filename) { 263ceee6da6SHollis Blanchard initrd_size = load_image_targphys(initrd_filename, RAMDISK_ADDR, 264a0258e4aSIgor Mammedov machine->ram_size - RAMDISK_ADDR); 2652c9fade2Saurel32 2662c9fade2Saurel32 if (initrd_size < 0) { 2676f76b817SAlistair Francis error_report("could not load ram disk '%s' at %x", 268ceee6da6SHollis Blanchard initrd_filename, RAMDISK_ADDR); 2692c9fade2Saurel32 exit(1); 2702c9fade2Saurel32 } 2712c9fade2Saurel32 } 2722c9fade2Saurel32 2732c9fade2Saurel32 /* If we're loading a kernel directly, we must load the device tree too. */ 2742c9fade2Saurel32 if (kernel_filename) { 2758d42c851SDaniel Henrique Barboza if (bamboo_load_device_tree(machine, FDT_ADDR, 2768d42c851SDaniel Henrique Barboza RAMDISK_ADDR, initrd_size) < 0) { 2776f76b817SAlistair Francis error_report("couldn't load device tree"); 2782c9fade2Saurel32 exit(1); 2792c9fade2Saurel32 } 2802c9fade2Saurel32 } 2812c9fade2Saurel32 } 2822c9fade2Saurel32 283e264d29dSEduardo Habkost static void bamboo_machine_init(MachineClass *mc) 284f80f9ec9SAnthony Liguori { 285e264d29dSEduardo Habkost mc->desc = "bamboo"; 286e264d29dSEduardo Habkost mc->init = bamboo_init; 287376d7a2aSIgor Mammedov mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("440epb"); 288b28f0188SIgor Mammedov mc->default_ram_id = "ppc4xx.sdram"; 289053b7086SThomas Huth mc->default_nic = "e1000"; 290f80f9ec9SAnthony Liguori } 291f80f9ec9SAnthony Liguori 292e264d29dSEduardo Habkost DEFINE_MACHINE("bamboo", bamboo_machine_init) 293