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 142c9fade2Saurel32 #include "config.h" 152c9fade2Saurel32 #include "qemu-common.h" 161422e32dSPaolo Bonzini #include "net/net.h" 1783c9f4caSPaolo Bonzini #include "hw/hw.h" 1883c9f4caSPaolo Bonzini #include "hw/pci/pci.h" 1983c9f4caSPaolo Bonzini #include "hw/boards.h" 209c17d615SPaolo Bonzini #include "sysemu/kvm.h" 212c9fade2Saurel32 #include "kvm_ppc.h" 229c17d615SPaolo Bonzini #include "sysemu/device_tree.h" 2383c9f4caSPaolo Bonzini #include "hw/loader.h" 24ca20cf32SBlue Swirl #include "elf.h" 25022c62cbSPaolo Bonzini #include "exec/address-spaces.h" 260d09e41aSPaolo Bonzini #include "hw/char/serial.h" 270d09e41aSPaolo Bonzini #include "hw/ppc/ppc.h" 2847b43a1fSPaolo Bonzini #include "ppc405.h" 299c17d615SPaolo Bonzini #include "sysemu/sysemu.h" 3083c9f4caSPaolo Bonzini #include "hw/sysbus.h" 312c9fade2Saurel32 322c9fade2Saurel32 #define BINARY_DEVICE_TREE_FILE "bamboo.dtb" 332c9fade2Saurel32 34ceee6da6SHollis Blanchard /* from u-boot */ 35ceee6da6SHollis Blanchard #define KERNEL_ADDR 0x1000000 36ceee6da6SHollis Blanchard #define FDT_ADDR 0x1800000 37ceee6da6SHollis Blanchard #define RAMDISK_ADDR 0x1900000 38ceee6da6SHollis Blanchard 393960b04dSAlexander Graf #define PPC440EP_PCI_CONFIG 0xeec00000 403960b04dSAlexander Graf #define PPC440EP_PCI_INTACK 0xeed00000 413960b04dSAlexander Graf #define PPC440EP_PCI_SPECIAL 0xeed00000 423960b04dSAlexander Graf #define PPC440EP_PCI_REGS 0xef400000 433960b04dSAlexander Graf #define PPC440EP_PCI_IO 0xe8000000 443960b04dSAlexander Graf #define PPC440EP_PCI_IOLEN 0x00010000 453960b04dSAlexander Graf 463960b04dSAlexander Graf #define PPC440EP_SDRAM_NR_BANKS 4 473960b04dSAlexander Graf 483960b04dSAlexander Graf static const unsigned int ppc440ep_sdram_bank_sizes[] = { 493960b04dSAlexander Graf 256<<20, 128<<20, 64<<20, 32<<20, 16<<20, 8<<20, 0 503960b04dSAlexander Graf }; 513960b04dSAlexander Graf 52a8170e5eSAvi Kivity static hwaddr entry; 53b10a04b5SAlexander Graf 54a8170e5eSAvi Kivity static int bamboo_load_device_tree(hwaddr addr, 552c9fade2Saurel32 uint32_t ramsize, 56a8170e5eSAvi Kivity hwaddr initrd_base, 57a8170e5eSAvi Kivity hwaddr initrd_size, 582c9fade2Saurel32 const char *kernel_cmdline) 592c9fade2Saurel32 { 60dbf916d8SAurelien Jarno int ret = -1; 615232fa59SAlexander Graf uint32_t mem_reg_property[] = { 0, 0, cpu_to_be32(ramsize) }; 625cea8590SPaul Brook char *filename; 637ec632b4Spbrook int fdt_size; 64dbf916d8SAurelien Jarno void *fdt; 657dadd40cSAlexander Graf uint32_t tb_freq = 400000000; 667dadd40cSAlexander Graf uint32_t clock_freq = 400000000; 672c9fade2Saurel32 685cea8590SPaul Brook filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE); 695cea8590SPaul Brook if (!filename) { 702c9fade2Saurel32 goto out; 715cea8590SPaul Brook } 725cea8590SPaul Brook fdt = load_device_tree(filename, &fdt_size); 737267c094SAnthony Liguori g_free(filename); 745cea8590SPaul Brook if (fdt == NULL) { 755cea8590SPaul Brook goto out; 765cea8590SPaul Brook } 772c9fade2Saurel32 782c9fade2Saurel32 /* Manipulate device tree in memory. */ 792c9fade2Saurel32 805a4348d1SPeter Crosthwaite ret = qemu_fdt_setprop(fdt, "/memory", "reg", mem_reg_property, 812c9fade2Saurel32 sizeof(mem_reg_property)); 822c9fade2Saurel32 if (ret < 0) 832c9fade2Saurel32 fprintf(stderr, "couldn't set /memory/reg\n"); 842c9fade2Saurel32 855a4348d1SPeter Crosthwaite ret = qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-start", 862c9fade2Saurel32 initrd_base); 872c9fade2Saurel32 if (ret < 0) 882c9fade2Saurel32 fprintf(stderr, "couldn't set /chosen/linux,initrd-start\n"); 892c9fade2Saurel32 905a4348d1SPeter Crosthwaite ret = qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-end", 912c9fade2Saurel32 (initrd_base + initrd_size)); 922c9fade2Saurel32 if (ret < 0) 932c9fade2Saurel32 fprintf(stderr, "couldn't set /chosen/linux,initrd-end\n"); 942c9fade2Saurel32 955a4348d1SPeter Crosthwaite ret = qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", 962c9fade2Saurel32 kernel_cmdline); 972c9fade2Saurel32 if (ret < 0) 982c9fade2Saurel32 fprintf(stderr, "couldn't set /chosen/bootargs\n"); 992c9fade2Saurel32 1007dadd40cSAlexander Graf /* Copy data from the host device tree into the guest. Since the guest can 1017dadd40cSAlexander Graf * directly access the timebase without host involvement, we must expose 1027dadd40cSAlexander Graf * the correct frequencies. */ 103a489f7f7SAlexander Graf if (kvm_enabled()) { 1047dadd40cSAlexander Graf tb_freq = kvmppc_get_tbfreq(); 1057dadd40cSAlexander Graf clock_freq = kvmppc_get_clockfreq(); 106a489f7f7SAlexander Graf } 1072c9fade2Saurel32 1085a4348d1SPeter Crosthwaite qemu_fdt_setprop_cell(fdt, "/cpus/cpu@0", "clock-frequency", 1097dadd40cSAlexander Graf clock_freq); 1105a4348d1SPeter Crosthwaite qemu_fdt_setprop_cell(fdt, "/cpus/cpu@0", "timebase-frequency", 1117dadd40cSAlexander Graf tb_freq); 1122c9fade2Saurel32 113fe1479aaSMichael S. Tsirkin rom_add_blob_fixed(BINARY_DEVICE_TREE_FILE, fdt, fdt_size, addr); 1147267c094SAnthony Liguori g_free(fdt); 115fe1479aaSMichael S. Tsirkin return 0; 1167ec632b4Spbrook 1172c9fade2Saurel32 out: 1182c9fade2Saurel32 11904088adbSLiu Yu return ret; 1202c9fade2Saurel32 } 1212c9fade2Saurel32 12272718e9aSAlexander Graf /* Create reset TLB entries for BookE, spanning the 32bit addr space. */ 123e2684c0bSAndreas Färber static void mmubooke_create_initial_mapping(CPUPPCState *env, 12472718e9aSAlexander Graf target_ulong va, 125a8170e5eSAvi Kivity hwaddr pa) 12672718e9aSAlexander Graf { 12772718e9aSAlexander Graf ppcemb_tlb_t *tlb = &env->tlb.tlbe[0]; 12872718e9aSAlexander Graf 12972718e9aSAlexander Graf tlb->attr = 0; 13072718e9aSAlexander Graf tlb->prot = PAGE_VALID | ((PAGE_READ | PAGE_WRITE | PAGE_EXEC) << 4); 131*a1f7f97bSPeter Maydell tlb->size = 1U << 31; /* up to 0x80000000 */ 13272718e9aSAlexander Graf tlb->EPN = va & TARGET_PAGE_MASK; 13372718e9aSAlexander Graf tlb->RPN = pa & TARGET_PAGE_MASK; 13472718e9aSAlexander Graf tlb->PID = 0; 13572718e9aSAlexander Graf 13672718e9aSAlexander Graf tlb = &env->tlb.tlbe[1]; 13772718e9aSAlexander Graf tlb->attr = 0; 13872718e9aSAlexander Graf tlb->prot = PAGE_VALID | ((PAGE_READ | PAGE_WRITE | PAGE_EXEC) << 4); 139*a1f7f97bSPeter Maydell tlb->size = 1U << 31; /* up to 0xffffffff */ 14072718e9aSAlexander Graf tlb->EPN = 0x80000000 & TARGET_PAGE_MASK; 14172718e9aSAlexander Graf tlb->RPN = 0x80000000 & TARGET_PAGE_MASK; 14272718e9aSAlexander Graf tlb->PID = 0; 14372718e9aSAlexander Graf } 14472718e9aSAlexander Graf 145b10a04b5SAlexander Graf static void main_cpu_reset(void *opaque) 146b10a04b5SAlexander Graf { 147182fbbf2SAndreas Färber PowerPCCPU *cpu = opaque; 148182fbbf2SAndreas Färber CPUPPCState *env = &cpu->env; 149b10a04b5SAlexander Graf 150182fbbf2SAndreas Färber cpu_reset(CPU(cpu)); 151b10a04b5SAlexander Graf env->gpr[1] = (16<<20) - 8; 152b10a04b5SAlexander Graf env->gpr[3] = FDT_ADDR; 153b10a04b5SAlexander Graf env->nip = entry; 15472718e9aSAlexander Graf 15572718e9aSAlexander Graf /* Create a mapping for the kernel. */ 15672718e9aSAlexander Graf mmubooke_create_initial_mapping(env, 0, 0); 157b10a04b5SAlexander Graf } 158b10a04b5SAlexander Graf 1595f072e1fSEduardo Habkost static void bamboo_init(QEMUMachineInitArgs *args) 1602c9fade2Saurel32 { 1615f072e1fSEduardo Habkost ram_addr_t ram_size = args->ram_size; 1625f072e1fSEduardo Habkost const char *cpu_model = args->cpu_model; 1635f072e1fSEduardo Habkost const char *kernel_filename = args->kernel_filename; 1645f072e1fSEduardo Habkost const char *kernel_cmdline = args->kernel_cmdline; 1655f072e1fSEduardo Habkost const char *initrd_filename = args->initrd_filename; 1662c9fade2Saurel32 unsigned int pci_irq_nrs[4] = { 28, 27, 26, 25 }; 1673e9f0113SRichard Henderson MemoryRegion *address_space_mem = get_system_memory(); 16868501502SPaolo Bonzini MemoryRegion *isa = g_new(MemoryRegion, 1); 16934ba1dc8SAlexander Graf MemoryRegion *ram_memories 17034ba1dc8SAlexander Graf = g_malloc(PPC440EP_SDRAM_NR_BANKS * sizeof(*ram_memories)); 171a8170e5eSAvi Kivity hwaddr ram_bases[PPC440EP_SDRAM_NR_BANKS]; 172a8170e5eSAvi Kivity hwaddr ram_sizes[PPC440EP_SDRAM_NR_BANKS]; 17334ba1dc8SAlexander Graf qemu_irq *pic; 17434ba1dc8SAlexander Graf qemu_irq *irqs; 1752c9fade2Saurel32 PCIBus *pcibus; 176322164e0SAndreas Färber PowerPCCPU *cpu; 177e2684c0bSAndreas Färber CPUPPCState *env; 1782c9fade2Saurel32 uint64_t elf_entry; 1792c9fade2Saurel32 uint64_t elf_lowaddr; 180a8170e5eSAvi Kivity hwaddr loadaddr = 0; 1812c9fade2Saurel32 target_long initrd_size = 0; 18234ba1dc8SAlexander Graf DeviceState *dev; 183ceee6da6SHollis Blanchard int success; 1842c9fade2Saurel32 int i; 1852c9fade2Saurel32 1862c9fade2Saurel32 /* Setup CPU. */ 18734ba1dc8SAlexander Graf if (cpu_model == NULL) { 18834ba1dc8SAlexander Graf cpu_model = "440EP"; 18934ba1dc8SAlexander Graf } 190322164e0SAndreas Färber cpu = cpu_ppc_init(cpu_model); 191322164e0SAndreas Färber if (cpu == NULL) { 19234ba1dc8SAlexander Graf fprintf(stderr, "Unable to initialize CPU!\n"); 19334ba1dc8SAlexander Graf exit(1); 19434ba1dc8SAlexander Graf } 195322164e0SAndreas Färber env = &cpu->env; 19634ba1dc8SAlexander Graf 197182fbbf2SAndreas Färber qemu_register_reset(main_cpu_reset, cpu); 198a34a92b9SAndreas Färber ppc_booke_timers_init(cpu, 400000000, 0); 19934ba1dc8SAlexander Graf ppc_dcr_init(env, NULL, NULL); 20034ba1dc8SAlexander Graf 20134ba1dc8SAlexander Graf /* interrupt controller */ 20234ba1dc8SAlexander Graf irqs = g_malloc0(sizeof(qemu_irq) * PPCUIC_OUTPUT_NB); 20334ba1dc8SAlexander Graf irqs[PPCUIC_OUTPUT_INT] = ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_INT]; 20434ba1dc8SAlexander Graf irqs[PPCUIC_OUTPUT_CINT] = ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_CINT]; 20534ba1dc8SAlexander Graf pic = ppcuic_init(env, irqs, 0x0C0, 0, 1); 20634ba1dc8SAlexander Graf 20734ba1dc8SAlexander Graf /* SDRAM controller */ 20834ba1dc8SAlexander Graf memset(ram_bases, 0, sizeof(ram_bases)); 20934ba1dc8SAlexander Graf memset(ram_sizes, 0, sizeof(ram_sizes)); 21034ba1dc8SAlexander Graf ram_size = ppc4xx_sdram_adjust(ram_size, PPC440EP_SDRAM_NR_BANKS, 21134ba1dc8SAlexander Graf ram_memories, 21234ba1dc8SAlexander Graf ram_bases, ram_sizes, 21334ba1dc8SAlexander Graf ppc440ep_sdram_bank_sizes); 21434ba1dc8SAlexander Graf /* XXX 440EP's ECC interrupts are on UIC1, but we've only created UIC0. */ 21534ba1dc8SAlexander Graf ppc4xx_sdram_init(env, pic[14], PPC440EP_SDRAM_NR_BANKS, ram_memories, 21634ba1dc8SAlexander Graf ram_bases, ram_sizes, 1); 21734ba1dc8SAlexander Graf 21834ba1dc8SAlexander Graf /* PCI */ 21942c281a2SAndreas Färber dev = sysbus_create_varargs(TYPE_PPC4xx_PCI_HOST_BRIDGE, 22042c281a2SAndreas Färber PPC440EP_PCI_CONFIG, 22134ba1dc8SAlexander Graf pic[pci_irq_nrs[0]], pic[pci_irq_nrs[1]], 22234ba1dc8SAlexander Graf pic[pci_irq_nrs[2]], pic[pci_irq_nrs[3]], 22334ba1dc8SAlexander Graf NULL); 22434ba1dc8SAlexander Graf pcibus = (PCIBus *)qdev_get_child_bus(dev, "pci.0"); 22534ba1dc8SAlexander Graf if (!pcibus) { 22634ba1dc8SAlexander Graf fprintf(stderr, "couldn't create PCI controller!\n"); 22734ba1dc8SAlexander Graf exit(1); 22834ba1dc8SAlexander Graf } 22934ba1dc8SAlexander Graf 23068501502SPaolo Bonzini memory_region_init_alias(isa, NULL, "isa_mmio", 23168501502SPaolo Bonzini get_system_io(), 0, PPC440EP_PCI_IOLEN); 23268501502SPaolo Bonzini memory_region_add_subregion(get_system_memory(), PPC440EP_PCI_IO, isa); 23334ba1dc8SAlexander Graf 23434ba1dc8SAlexander Graf if (serial_hds[0] != NULL) { 23534ba1dc8SAlexander Graf serial_mm_init(address_space_mem, 0xef600300, 0, pic[0], 23634ba1dc8SAlexander Graf PPC_SERIAL_MM_BAUDBASE, serial_hds[0], 23734ba1dc8SAlexander Graf DEVICE_BIG_ENDIAN); 23834ba1dc8SAlexander Graf } 23934ba1dc8SAlexander Graf if (serial_hds[1] != NULL) { 24034ba1dc8SAlexander Graf serial_mm_init(address_space_mem, 0xef600400, 0, pic[1], 24134ba1dc8SAlexander Graf PPC_SERIAL_MM_BAUDBASE, serial_hds[1], 24234ba1dc8SAlexander Graf DEVICE_BIG_ENDIAN); 24334ba1dc8SAlexander Graf } 2442c9fade2Saurel32 2452c9fade2Saurel32 if (pcibus) { 2462c9fade2Saurel32 /* Register network interfaces. */ 2472c9fade2Saurel32 for (i = 0; i < nb_nics; i++) { 2482c9fade2Saurel32 /* There are no PCI NICs on the Bamboo board, but there are 249cb457d76Saliguori * PCI slots, so we can pick whatever default model we want. */ 25029b358f9SDavid Gibson pci_nic_init_nofail(&nd_table[i], pcibus, "e1000", NULL); 2512c9fade2Saurel32 } 2522c9fade2Saurel32 } 2532c9fade2Saurel32 2542c9fade2Saurel32 /* Load kernel. */ 2552c9fade2Saurel32 if (kernel_filename) { 256ceee6da6SHollis Blanchard success = load_uimage(kernel_filename, &entry, &loadaddr, NULL); 257ceee6da6SHollis Blanchard if (success < 0) { 258ceee6da6SHollis Blanchard success = load_elf(kernel_filename, NULL, NULL, &elf_entry, 259409dbce5SAurelien Jarno &elf_lowaddr, NULL, 1, ELF_MACHINE, 0); 2602c9fade2Saurel32 entry = elf_entry; 2612c9fade2Saurel32 loadaddr = elf_lowaddr; 2622c9fade2Saurel32 } 2632c9fade2Saurel32 /* XXX try again as binary */ 264ceee6da6SHollis Blanchard if (success < 0) { 2652c9fade2Saurel32 fprintf(stderr, "qemu: could not load kernel '%s'\n", 2662c9fade2Saurel32 kernel_filename); 2672c9fade2Saurel32 exit(1); 2682c9fade2Saurel32 } 2692c9fade2Saurel32 } 2702c9fade2Saurel32 2712c9fade2Saurel32 /* Load initrd. */ 2722c9fade2Saurel32 if (initrd_filename) { 273ceee6da6SHollis Blanchard initrd_size = load_image_targphys(initrd_filename, RAMDISK_ADDR, 274ceee6da6SHollis Blanchard ram_size - RAMDISK_ADDR); 2752c9fade2Saurel32 2762c9fade2Saurel32 if (initrd_size < 0) { 277ceee6da6SHollis Blanchard fprintf(stderr, "qemu: could not load ram disk '%s' at %x\n", 278ceee6da6SHollis Blanchard initrd_filename, RAMDISK_ADDR); 2792c9fade2Saurel32 exit(1); 2802c9fade2Saurel32 } 2812c9fade2Saurel32 } 2822c9fade2Saurel32 2832c9fade2Saurel32 /* If we're loading a kernel directly, we must load the device tree too. */ 2842c9fade2Saurel32 if (kernel_filename) { 285ceee6da6SHollis Blanchard if (bamboo_load_device_tree(FDT_ADDR, ram_size, RAMDISK_ADDR, 286ceee6da6SHollis Blanchard initrd_size, kernel_cmdline) < 0) { 2872c9fade2Saurel32 fprintf(stderr, "couldn't load device tree\n"); 2882c9fade2Saurel32 exit(1); 2892c9fade2Saurel32 } 2902c9fade2Saurel32 } 2912c9fade2Saurel32 2922c9fade2Saurel32 if (kvm_enabled()) 2932c9fade2Saurel32 kvmppc_init(); 2942c9fade2Saurel32 } 2952c9fade2Saurel32 296f80f9ec9SAnthony Liguori static QEMUMachine bamboo_machine = { 297d3c4548bSAlexander Graf .name = "bamboo", 298977b6b91SAmit Shah .desc = "bamboo", 299977b6b91SAmit Shah .init = bamboo_init, 300977b6b91SAmit Shah }; 301977b6b91SAmit Shah 302f80f9ec9SAnthony Liguori static void bamboo_machine_init(void) 303f80f9ec9SAnthony Liguori { 304f80f9ec9SAnthony Liguori qemu_register_machine(&bamboo_machine); 305f80f9ec9SAnthony Liguori } 306f80f9ec9SAnthony Liguori 307f80f9ec9SAnthony Liguori machine_init(bamboo_machine_init); 308