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" 156f76b817SAlistair Francis #include "qemu/error-report.h" 162c9fade2Saurel32 #include "qemu-common.h" 1764b47457Sthuth@redhat.com #include "qemu/error-report.h" 181422e32dSPaolo Bonzini #include "net/net.h" 1983c9f4caSPaolo Bonzini #include "hw/hw.h" 2083c9f4caSPaolo Bonzini #include "hw/pci/pci.h" 2183c9f4caSPaolo Bonzini #include "hw/boards.h" 229c17d615SPaolo Bonzini #include "sysemu/kvm.h" 232c9fade2Saurel32 #include "kvm_ppc.h" 249c17d615SPaolo Bonzini #include "sysemu/device_tree.h" 2583c9f4caSPaolo Bonzini #include "hw/loader.h" 26ca20cf32SBlue Swirl #include "elf.h" 27022c62cbSPaolo Bonzini #include "exec/address-spaces.h" 280d09e41aSPaolo Bonzini #include "hw/char/serial.h" 290d09e41aSPaolo Bonzini #include "hw/ppc/ppc.h" 3047b43a1fSPaolo Bonzini #include "ppc405.h" 319c17d615SPaolo Bonzini #include "sysemu/sysemu.h" 3264b47457Sthuth@redhat.com #include "sysemu/qtest.h" 3383c9f4caSPaolo Bonzini #include "hw/sysbus.h" 342c9fade2Saurel32 352c9fade2Saurel32 #define BINARY_DEVICE_TREE_FILE "bamboo.dtb" 362c9fade2Saurel32 37ceee6da6SHollis Blanchard /* from u-boot */ 38ceee6da6SHollis Blanchard #define KERNEL_ADDR 0x1000000 39ceee6da6SHollis Blanchard #define FDT_ADDR 0x1800000 40ceee6da6SHollis Blanchard #define RAMDISK_ADDR 0x1900000 41ceee6da6SHollis Blanchard 423960b04dSAlexander Graf #define PPC440EP_PCI_CONFIG 0xeec00000 433960b04dSAlexander Graf #define PPC440EP_PCI_INTACK 0xeed00000 443960b04dSAlexander Graf #define PPC440EP_PCI_SPECIAL 0xeed00000 453960b04dSAlexander Graf #define PPC440EP_PCI_REGS 0xef400000 463960b04dSAlexander Graf #define PPC440EP_PCI_IO 0xe8000000 473960b04dSAlexander Graf #define PPC440EP_PCI_IOLEN 0x00010000 483960b04dSAlexander Graf 493960b04dSAlexander Graf #define PPC440EP_SDRAM_NR_BANKS 4 503960b04dSAlexander Graf 513960b04dSAlexander Graf static const unsigned int ppc440ep_sdram_bank_sizes[] = { 523960b04dSAlexander Graf 256<<20, 128<<20, 64<<20, 32<<20, 16<<20, 8<<20, 0 533960b04dSAlexander Graf }; 543960b04dSAlexander Graf 55a8170e5eSAvi Kivity static hwaddr entry; 56b10a04b5SAlexander Graf 57a8170e5eSAvi Kivity static int bamboo_load_device_tree(hwaddr addr, 582c9fade2Saurel32 uint32_t ramsize, 59a8170e5eSAvi Kivity hwaddr initrd_base, 60a8170e5eSAvi Kivity hwaddr initrd_size, 612c9fade2Saurel32 const char *kernel_cmdline) 622c9fade2Saurel32 { 63dbf916d8SAurelien Jarno int ret = -1; 645232fa59SAlexander Graf uint32_t mem_reg_property[] = { 0, 0, cpu_to_be32(ramsize) }; 655cea8590SPaul Brook char *filename; 667ec632b4Spbrook int fdt_size; 67dbf916d8SAurelien Jarno void *fdt; 687dadd40cSAlexander Graf uint32_t tb_freq = 400000000; 697dadd40cSAlexander Graf uint32_t clock_freq = 400000000; 702c9fade2Saurel32 715cea8590SPaul Brook filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE); 725cea8590SPaul Brook if (!filename) { 732c9fade2Saurel32 goto out; 745cea8590SPaul Brook } 755cea8590SPaul Brook fdt = load_device_tree(filename, &fdt_size); 767267c094SAnthony Liguori g_free(filename); 775cea8590SPaul Brook if (fdt == NULL) { 785cea8590SPaul Brook goto out; 795cea8590SPaul Brook } 802c9fade2Saurel32 812c9fade2Saurel32 /* Manipulate device tree in memory. */ 822c9fade2Saurel32 835a4348d1SPeter Crosthwaite ret = qemu_fdt_setprop(fdt, "/memory", "reg", mem_reg_property, 842c9fade2Saurel32 sizeof(mem_reg_property)); 852c9fade2Saurel32 if (ret < 0) 862c9fade2Saurel32 fprintf(stderr, "couldn't set /memory/reg\n"); 872c9fade2Saurel32 885a4348d1SPeter Crosthwaite ret = qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-start", 892c9fade2Saurel32 initrd_base); 902c9fade2Saurel32 if (ret < 0) 912c9fade2Saurel32 fprintf(stderr, "couldn't set /chosen/linux,initrd-start\n"); 922c9fade2Saurel32 935a4348d1SPeter Crosthwaite ret = qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-end", 942c9fade2Saurel32 (initrd_base + initrd_size)); 952c9fade2Saurel32 if (ret < 0) 962c9fade2Saurel32 fprintf(stderr, "couldn't set /chosen/linux,initrd-end\n"); 972c9fade2Saurel32 985a4348d1SPeter Crosthwaite ret = qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", 992c9fade2Saurel32 kernel_cmdline); 1002c9fade2Saurel32 if (ret < 0) 1012c9fade2Saurel32 fprintf(stderr, "couldn't set /chosen/bootargs\n"); 1022c9fade2Saurel32 1037dadd40cSAlexander Graf /* Copy data from the host device tree into the guest. Since the guest can 1047dadd40cSAlexander Graf * directly access the timebase without host involvement, we must expose 1057dadd40cSAlexander Graf * the correct frequencies. */ 106a489f7f7SAlexander Graf if (kvm_enabled()) { 1077dadd40cSAlexander Graf tb_freq = kvmppc_get_tbfreq(); 1087dadd40cSAlexander Graf clock_freq = kvmppc_get_clockfreq(); 109a489f7f7SAlexander Graf } 1102c9fade2Saurel32 1115a4348d1SPeter Crosthwaite qemu_fdt_setprop_cell(fdt, "/cpus/cpu@0", "clock-frequency", 1127dadd40cSAlexander Graf clock_freq); 1135a4348d1SPeter Crosthwaite qemu_fdt_setprop_cell(fdt, "/cpus/cpu@0", "timebase-frequency", 1147dadd40cSAlexander Graf tb_freq); 1152c9fade2Saurel32 116fe1479aaSMichael S. Tsirkin rom_add_blob_fixed(BINARY_DEVICE_TREE_FILE, fdt, fdt_size, addr); 1177267c094SAnthony Liguori g_free(fdt); 118fe1479aaSMichael S. Tsirkin return 0; 1197ec632b4Spbrook 1202c9fade2Saurel32 out: 1212c9fade2Saurel32 12204088adbSLiu Yu return ret; 1232c9fade2Saurel32 } 1242c9fade2Saurel32 12572718e9aSAlexander Graf /* Create reset TLB entries for BookE, spanning the 32bit addr space. */ 126e2684c0bSAndreas Färber static void mmubooke_create_initial_mapping(CPUPPCState *env, 12772718e9aSAlexander Graf target_ulong va, 128a8170e5eSAvi Kivity hwaddr pa) 12972718e9aSAlexander Graf { 13072718e9aSAlexander Graf ppcemb_tlb_t *tlb = &env->tlb.tlbe[0]; 13172718e9aSAlexander Graf 13272718e9aSAlexander Graf tlb->attr = 0; 13372718e9aSAlexander Graf tlb->prot = PAGE_VALID | ((PAGE_READ | PAGE_WRITE | PAGE_EXEC) << 4); 134a1f7f97bSPeter Maydell tlb->size = 1U << 31; /* up to 0x80000000 */ 13572718e9aSAlexander Graf tlb->EPN = va & TARGET_PAGE_MASK; 13672718e9aSAlexander Graf tlb->RPN = pa & TARGET_PAGE_MASK; 13772718e9aSAlexander Graf tlb->PID = 0; 13872718e9aSAlexander Graf 13972718e9aSAlexander Graf tlb = &env->tlb.tlbe[1]; 14072718e9aSAlexander Graf tlb->attr = 0; 14172718e9aSAlexander Graf tlb->prot = PAGE_VALID | ((PAGE_READ | PAGE_WRITE | PAGE_EXEC) << 4); 142a1f7f97bSPeter Maydell tlb->size = 1U << 31; /* up to 0xffffffff */ 14372718e9aSAlexander Graf tlb->EPN = 0x80000000 & TARGET_PAGE_MASK; 14472718e9aSAlexander Graf tlb->RPN = 0x80000000 & TARGET_PAGE_MASK; 14572718e9aSAlexander Graf tlb->PID = 0; 14672718e9aSAlexander Graf } 14772718e9aSAlexander Graf 148b10a04b5SAlexander Graf static void main_cpu_reset(void *opaque) 149b10a04b5SAlexander Graf { 150182fbbf2SAndreas Färber PowerPCCPU *cpu = opaque; 151182fbbf2SAndreas Färber CPUPPCState *env = &cpu->env; 152b10a04b5SAlexander Graf 153182fbbf2SAndreas Färber cpu_reset(CPU(cpu)); 154b10a04b5SAlexander Graf env->gpr[1] = (16<<20) - 8; 155b10a04b5SAlexander Graf env->gpr[3] = FDT_ADDR; 156b10a04b5SAlexander Graf env->nip = entry; 15772718e9aSAlexander Graf 15872718e9aSAlexander Graf /* Create a mapping for the kernel. */ 15972718e9aSAlexander Graf mmubooke_create_initial_mapping(env, 0, 0); 160b10a04b5SAlexander Graf } 161b10a04b5SAlexander Graf 1623ef96221SMarcel Apfelbaum static void bamboo_init(MachineState *machine) 1632c9fade2Saurel32 { 1643ef96221SMarcel Apfelbaum ram_addr_t ram_size = machine->ram_size; 1653ef96221SMarcel Apfelbaum const char *kernel_filename = machine->kernel_filename; 1663ef96221SMarcel Apfelbaum const char *kernel_cmdline = machine->kernel_cmdline; 1673ef96221SMarcel Apfelbaum const char *initrd_filename = machine->initrd_filename; 1682c9fade2Saurel32 unsigned int pci_irq_nrs[4] = { 28, 27, 26, 25 }; 1693e9f0113SRichard Henderson MemoryRegion *address_space_mem = get_system_memory(); 17068501502SPaolo Bonzini MemoryRegion *isa = g_new(MemoryRegion, 1); 17134ba1dc8SAlexander Graf MemoryRegion *ram_memories 17234ba1dc8SAlexander Graf = g_malloc(PPC440EP_SDRAM_NR_BANKS * sizeof(*ram_memories)); 173a8170e5eSAvi Kivity hwaddr ram_bases[PPC440EP_SDRAM_NR_BANKS]; 174a8170e5eSAvi Kivity hwaddr ram_sizes[PPC440EP_SDRAM_NR_BANKS]; 17534ba1dc8SAlexander Graf qemu_irq *pic; 17634ba1dc8SAlexander Graf qemu_irq *irqs; 1772c9fade2Saurel32 PCIBus *pcibus; 178322164e0SAndreas Färber PowerPCCPU *cpu; 179e2684c0bSAndreas Färber CPUPPCState *env; 1802c9fade2Saurel32 uint64_t elf_entry; 1812c9fade2Saurel32 uint64_t elf_lowaddr; 182a8170e5eSAvi Kivity hwaddr loadaddr = 0; 1832c9fade2Saurel32 target_long initrd_size = 0; 18434ba1dc8SAlexander Graf DeviceState *dev; 185ceee6da6SHollis Blanchard int success; 1862c9fade2Saurel32 int i; 1872c9fade2Saurel32 188376d7a2aSIgor Mammedov cpu = POWERPC_CPU(cpu_create(machine->cpu_type)); 189322164e0SAndreas Färber env = &cpu->env; 19034ba1dc8SAlexander Graf 19100469dc3SValentin Plotkin if (env->mmu_model != POWERPC_MMU_BOOKE) { 1926f76b817SAlistair Francis error_report("MMU model %i not supported by this machine", 19300469dc3SValentin Plotkin env->mmu_model); 19400469dc3SValentin Plotkin exit(1); 19500469dc3SValentin Plotkin } 19600469dc3SValentin Plotkin 19764b47457Sthuth@redhat.com #ifdef TARGET_PPCEMB 19864b47457Sthuth@redhat.com if (!qtest_enabled()) { 19964b47457Sthuth@redhat.com warn_report("qemu-system-ppcemb is deprecated, " 20064b47457Sthuth@redhat.com "please use qemu-system-ppc instead."); 20164b47457Sthuth@redhat.com } 20264b47457Sthuth@redhat.com #endif 20364b47457Sthuth@redhat.com 204182fbbf2SAndreas Färber qemu_register_reset(main_cpu_reset, cpu); 205a34a92b9SAndreas Färber ppc_booke_timers_init(cpu, 400000000, 0); 20634ba1dc8SAlexander Graf ppc_dcr_init(env, NULL, NULL); 20734ba1dc8SAlexander Graf 20834ba1dc8SAlexander Graf /* interrupt controller */ 20934ba1dc8SAlexander Graf irqs = g_malloc0(sizeof(qemu_irq) * PPCUIC_OUTPUT_NB); 21034ba1dc8SAlexander Graf irqs[PPCUIC_OUTPUT_INT] = ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_INT]; 21134ba1dc8SAlexander Graf irqs[PPCUIC_OUTPUT_CINT] = ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_CINT]; 21234ba1dc8SAlexander Graf pic = ppcuic_init(env, irqs, 0x0C0, 0, 1); 21334ba1dc8SAlexander Graf 21434ba1dc8SAlexander Graf /* SDRAM controller */ 21534ba1dc8SAlexander Graf memset(ram_bases, 0, sizeof(ram_bases)); 21634ba1dc8SAlexander Graf memset(ram_sizes, 0, sizeof(ram_sizes)); 21734ba1dc8SAlexander Graf ram_size = ppc4xx_sdram_adjust(ram_size, PPC440EP_SDRAM_NR_BANKS, 21834ba1dc8SAlexander Graf ram_memories, 21934ba1dc8SAlexander Graf ram_bases, ram_sizes, 22034ba1dc8SAlexander Graf ppc440ep_sdram_bank_sizes); 22134ba1dc8SAlexander Graf /* XXX 440EP's ECC interrupts are on UIC1, but we've only created UIC0. */ 22234ba1dc8SAlexander Graf ppc4xx_sdram_init(env, pic[14], PPC440EP_SDRAM_NR_BANKS, ram_memories, 22334ba1dc8SAlexander Graf ram_bases, ram_sizes, 1); 22434ba1dc8SAlexander Graf 22534ba1dc8SAlexander Graf /* PCI */ 22642c281a2SAndreas Färber dev = sysbus_create_varargs(TYPE_PPC4xx_PCI_HOST_BRIDGE, 22742c281a2SAndreas Färber PPC440EP_PCI_CONFIG, 22834ba1dc8SAlexander Graf pic[pci_irq_nrs[0]], pic[pci_irq_nrs[1]], 22934ba1dc8SAlexander Graf pic[pci_irq_nrs[2]], pic[pci_irq_nrs[3]], 23034ba1dc8SAlexander Graf NULL); 23134ba1dc8SAlexander Graf pcibus = (PCIBus *)qdev_get_child_bus(dev, "pci.0"); 23234ba1dc8SAlexander Graf if (!pcibus) { 2336f76b817SAlistair Francis error_report("couldn't create PCI controller"); 23434ba1dc8SAlexander Graf exit(1); 23534ba1dc8SAlexander Graf } 23634ba1dc8SAlexander Graf 23768501502SPaolo Bonzini memory_region_init_alias(isa, NULL, "isa_mmio", 23868501502SPaolo Bonzini get_system_io(), 0, PPC440EP_PCI_IOLEN); 23968501502SPaolo Bonzini memory_region_add_subregion(get_system_memory(), PPC440EP_PCI_IO, isa); 24034ba1dc8SAlexander Graf 241*9bca0edbSPeter Maydell if (serial_hd(0) != NULL) { 24234ba1dc8SAlexander Graf serial_mm_init(address_space_mem, 0xef600300, 0, pic[0], 243*9bca0edbSPeter Maydell PPC_SERIAL_MM_BAUDBASE, serial_hd(0), 24434ba1dc8SAlexander Graf DEVICE_BIG_ENDIAN); 24534ba1dc8SAlexander Graf } 246*9bca0edbSPeter Maydell if (serial_hd(1) != NULL) { 24734ba1dc8SAlexander Graf serial_mm_init(address_space_mem, 0xef600400, 0, pic[1], 248*9bca0edbSPeter Maydell PPC_SERIAL_MM_BAUDBASE, serial_hd(1), 24934ba1dc8SAlexander Graf DEVICE_BIG_ENDIAN); 25034ba1dc8SAlexander Graf } 2512c9fade2Saurel32 2522c9fade2Saurel32 if (pcibus) { 2532c9fade2Saurel32 /* Register network interfaces. */ 2542c9fade2Saurel32 for (i = 0; i < nb_nics; i++) { 2552c9fade2Saurel32 /* There are no PCI NICs on the Bamboo board, but there are 256cb457d76Saliguori * PCI slots, so we can pick whatever default model we want. */ 25729b358f9SDavid Gibson pci_nic_init_nofail(&nd_table[i], pcibus, "e1000", NULL); 2582c9fade2Saurel32 } 2592c9fade2Saurel32 } 2602c9fade2Saurel32 2612c9fade2Saurel32 /* Load kernel. */ 2622c9fade2Saurel32 if (kernel_filename) { 26325bda50aSMax Filippov success = load_uimage(kernel_filename, &entry, &loadaddr, NULL, 26425bda50aSMax Filippov NULL, NULL); 265ceee6da6SHollis Blanchard if (success < 0) { 266ceee6da6SHollis Blanchard success = load_elf(kernel_filename, NULL, NULL, &elf_entry, 2677ef295eaSPeter Crosthwaite &elf_lowaddr, NULL, 1, PPC_ELF_MACHINE, 2687ef295eaSPeter Crosthwaite 0, 0); 2692c9fade2Saurel32 entry = elf_entry; 2702c9fade2Saurel32 loadaddr = elf_lowaddr; 2712c9fade2Saurel32 } 2722c9fade2Saurel32 /* XXX try again as binary */ 273ceee6da6SHollis Blanchard if (success < 0) { 2746f76b817SAlistair Francis error_report("could not load kernel '%s'", kernel_filename); 2752c9fade2Saurel32 exit(1); 2762c9fade2Saurel32 } 2772c9fade2Saurel32 } 2782c9fade2Saurel32 2792c9fade2Saurel32 /* Load initrd. */ 2802c9fade2Saurel32 if (initrd_filename) { 281ceee6da6SHollis Blanchard initrd_size = load_image_targphys(initrd_filename, RAMDISK_ADDR, 282ceee6da6SHollis Blanchard ram_size - RAMDISK_ADDR); 2832c9fade2Saurel32 2842c9fade2Saurel32 if (initrd_size < 0) { 2856f76b817SAlistair Francis error_report("could not load ram disk '%s' at %x", 286ceee6da6SHollis Blanchard initrd_filename, RAMDISK_ADDR); 2872c9fade2Saurel32 exit(1); 2882c9fade2Saurel32 } 2892c9fade2Saurel32 } 2902c9fade2Saurel32 2912c9fade2Saurel32 /* If we're loading a kernel directly, we must load the device tree too. */ 2922c9fade2Saurel32 if (kernel_filename) { 293ceee6da6SHollis Blanchard if (bamboo_load_device_tree(FDT_ADDR, ram_size, RAMDISK_ADDR, 294ceee6da6SHollis Blanchard initrd_size, kernel_cmdline) < 0) { 2956f76b817SAlistair Francis error_report("couldn't load device tree"); 2962c9fade2Saurel32 exit(1); 2972c9fade2Saurel32 } 2982c9fade2Saurel32 } 2992c9fade2Saurel32 } 3002c9fade2Saurel32 301e264d29dSEduardo Habkost static void bamboo_machine_init(MachineClass *mc) 302f80f9ec9SAnthony Liguori { 303e264d29dSEduardo Habkost mc->desc = "bamboo"; 304e264d29dSEduardo Habkost mc->init = bamboo_init; 305376d7a2aSIgor Mammedov mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("440epb"); 306f80f9ec9SAnthony Liguori } 307f80f9ec9SAnthony Liguori 308e264d29dSEduardo Habkost DEFINE_MACHINE("bamboo", bamboo_machine_init) 309