diff options
Diffstat (limited to 'hw/ppc')
-rw-r--r-- | hw/ppc/Makefile.objs | 16 | ||||
-rw-r--r-- | hw/ppc/e500-ccsr.h | 17 | ||||
-rw-r--r-- | hw/ppc/e500.c | 264 | ||||
-rw-r--r-- | hw/ppc/e500.h | 4 | ||||
-rw-r--r-- | hw/ppc/e500plat.c | 10 | ||||
-rw-r--r-- | hw/ppc/mac.h | 181 | ||||
-rw-r--r-- | hw/ppc/mac_newworld.c | 470 | ||||
-rw-r--r-- | hw/ppc/mac_oldworld.c | 356 | ||||
-rw-r--r-- | hw/ppc/mpc8544ds.c | 7 | ||||
-rw-r--r-- | hw/ppc/prep.c | 680 |
10 files changed, 1926 insertions, 79 deletions
diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs index 8fe2123..f762050 100644 --- a/hw/ppc/Makefile.objs +++ b/hw/ppc/Makefile.objs @@ -2,16 +2,11 @@ obj-y = ppc.o ppc_booke.o # PREP target obj-y += mc146818rtc.o -obj-y += ppc_prep.o -# OldWorld PowerMac -obj-y += ppc_oldworld.o -# NewWorld PowerMac -obj-y += ppc_newworld.o # IBM pSeries (sPAPR) obj-$(CONFIG_PSERIES) += spapr.o spapr_hcall.o spapr_rtas.o spapr_vio.o obj-$(CONFIG_PSERIES) += xics.o spapr_vty.o spapr_llan.o spapr_vscsi.o -obj-$(CONFIG_PSERIES) += spapr_pci.o pci-hotplug.o spapr_iommu.o -obj-$(CONFIG_PSERIES) += spapr_events.o +obj-$(CONFIG_PSERIES) += spapr_pci.o pci/pci-hotplug.o spapr_iommu.o +obj-$(CONFIG_PSERIES) += spapr_events.o spapr_nvram.o # PowerPC 4xx boards obj-y += ppc4xx_devs.o ppc4xx_pci.o ppc405_uc.o ppc405_boards.o obj-y += ppc440_bamboo.o @@ -28,4 +23,11 @@ obj-y += xilinx_ethlite.o obj-y := $(addprefix ../,$(obj-y)) +# PReP +obj-y += prep.o +# OldWorld PowerMac +obj-y += mac_oldworld.o +# NewWorld PowerMac +obj-y += mac_newworld.o +# e500 obj-$(CONFIG_FDT) += e500.o mpc8544ds.o e500plat.o diff --git a/hw/ppc/e500-ccsr.h b/hw/ppc/e500-ccsr.h new file mode 100644 index 0000000..f20f51b --- /dev/null +++ b/hw/ppc/e500-ccsr.h @@ -0,0 +1,17 @@ +#ifndef E500_CCSR_H +#define E500_CCSR_H + +#include "../sysbus.h" + +typedef struct PPCE500CCSRState { + /*< private >*/ + SysBusDevice parent; + /*< public >*/ + + MemoryRegion ccsr_space; +} PPCE500CCSRState; + +#define TYPE_CCSR "e500-ccsr" +#define CCSR(obj) OBJECT_CHECK(PPCE500CCSRState, (obj), TYPE_CCSR) + +#endif /* E500_CCSR_H */ diff --git a/hw/ppc/e500.c b/hw/ppc/e500.c index 6749fff..b7474c0 100644 --- a/hw/ppc/e500.c +++ b/hw/ppc/e500.c @@ -17,27 +17,31 @@ #include "config.h" #include "qemu-common.h" #include "e500.h" -#include "net.h" +#include "e500-ccsr.h" +#include "net/net.h" +#include "qemu/config-file.h" #include "hw/hw.h" #include "hw/serial.h" -#include "hw/pci.h" +#include "hw/pci/pci.h" #include "hw/boards.h" -#include "sysemu.h" -#include "kvm.h" +#include "sysemu/sysemu.h" +#include "sysemu/kvm.h" #include "kvm_ppc.h" -#include "device_tree.h" +#include "sysemu/device_tree.h" #include "hw/openpic.h" #include "hw/ppc.h" #include "hw/loader.h" #include "elf.h" #include "hw/sysbus.h" -#include "exec-memory.h" -#include "host-utils.h" +#include "exec/address-spaces.h" +#include "qemu/host-utils.h" +#include "hw/ppce500_pci.h" #define BINARY_DEVICE_TREE_FILE "mpc8544ds.dtb" #define UIMAGE_LOAD_BASE 0 #define DTC_LOAD_PAD 0x1800000 #define DTC_PAD_MASK 0xFFFFF +#define DTB_MAX_SIZE (8 * 1024 * 1024) #define INITRD_LOAD_PAD 0x2000000 #define INITRD_PAD_MASK 0xFFFFFF @@ -46,13 +50,16 @@ /* TODO: parameterize */ #define MPC8544_CCSRBAR_BASE 0xE0000000ULL #define MPC8544_CCSRBAR_SIZE 0x00100000ULL -#define MPC8544_MPIC_REGS_BASE (MPC8544_CCSRBAR_BASE + 0x40000ULL) -#define MPC8544_SERIAL0_REGS_BASE (MPC8544_CCSRBAR_BASE + 0x4500ULL) -#define MPC8544_SERIAL1_REGS_BASE (MPC8544_CCSRBAR_BASE + 0x4600ULL) -#define MPC8544_PCI_REGS_BASE (MPC8544_CCSRBAR_BASE + 0x8000ULL) +#define MPC8544_MPIC_REGS_OFFSET 0x40000ULL +#define MPC8544_MSI_REGS_OFFSET 0x41600ULL +#define MPC8544_SERIAL0_REGS_OFFSET 0x4500ULL +#define MPC8544_SERIAL1_REGS_OFFSET 0x4600ULL +#define MPC8544_PCI_REGS_OFFSET 0x8000ULL +#define MPC8544_PCI_REGS_BASE (MPC8544_CCSRBAR_BASE + \ + MPC8544_PCI_REGS_OFFSET) #define MPC8544_PCI_REGS_SIZE 0x1000ULL #define MPC8544_PCI_IO 0xE1000000ULL -#define MPC8544_UTIL_BASE (MPC8544_CCSRBAR_BASE + 0xe0000ULL) +#define MPC8544_UTIL_OFFSET 0xe0000ULL #define MPC8544_SPIN_BASE 0xEF000000ULL struct boot_info @@ -62,25 +69,35 @@ struct boot_info uint32_t entry; }; -static void pci_map_create(void *fdt, uint32_t *pci_map, uint32_t mpic) +static uint32_t *pci_map_create(void *fdt, uint32_t mpic, int first_slot, + int nr_slots, int *len) { - int i; - const uint32_t tmp[] = { - /* IDSEL 0x11 J17 Slot 1 */ - 0x8800, 0x0, 0x0, 0x1, mpic, 0x2, 0x1, - 0x8800, 0x0, 0x0, 0x2, mpic, 0x3, 0x1, - 0x8800, 0x0, 0x0, 0x3, mpic, 0x4, 0x1, - 0x8800, 0x0, 0x0, 0x4, mpic, 0x1, 0x1, - - /* IDSEL 0x12 J16 Slot 2 */ - 0x9000, 0x0, 0x0, 0x1, mpic, 0x3, 0x1, - 0x9000, 0x0, 0x0, 0x2, mpic, 0x4, 0x1, - 0x9000, 0x0, 0x0, 0x3, mpic, 0x2, 0x1, - 0x9000, 0x0, 0x0, 0x4, mpic, 0x1, 0x1, - }; - for (i = 0; i < (7 * 8); i++) { - pci_map[i] = cpu_to_be32(tmp[i]); + int i = 0; + int slot; + int pci_irq; + int host_irq; + int last_slot = first_slot + nr_slots; + uint32_t *pci_map; + + *len = nr_slots * 4 * 7 * sizeof(uint32_t); + pci_map = g_malloc(*len); + + for (slot = first_slot; slot < last_slot; slot++) { + for (pci_irq = 0; pci_irq < 4; pci_irq++) { + pci_map[i++] = cpu_to_be32(slot << 11); + pci_map[i++] = cpu_to_be32(0x0); + pci_map[i++] = cpu_to_be32(0x0); + pci_map[i++] = cpu_to_be32(pci_irq + 1); + pci_map[i++] = cpu_to_be32(mpic); + host_irq = ppce500_pci_map_irq_slot(slot, pci_irq); + pci_map[i++] = cpu_to_be32(host_irq + 1); + pci_map[i++] = cpu_to_be32(0x1); + } } + + assert((i * sizeof(uint32_t)) == *len); + + return pci_map; } static void dt_serial_create(void *fdt, unsigned long long offset, @@ -124,9 +141,12 @@ static int ppce500_load_device_tree(CPUPPCState *env, char soc[128]; char mpic[128]; uint32_t mpic_ph; + uint32_t msi_ph; char gutil[128]; char pci[128]; - uint32_t pci_map[7 * 8]; + char msi[128]; + uint32_t *pci_map = NULL; + int len; uint32_t pci_ranges[14] = { 0x2000000, 0x0, 0xc0000000, @@ -206,6 +226,10 @@ static int ppce500_load_device_tree(CPUPPCState *env, kvmppc_get_hypercall(env, hypercall, sizeof(hypercall)); qemu_devtree_setprop(fdt, "/hypervisor", "hcall-instructions", hypercall, sizeof(hypercall)); + /* if KVM supports the idle hcall, set property indicating this */ + if (kvmppc_get_hasidle(env)) { + qemu_devtree_setprop(fdt, "/hypervisor", "has-idle", NULL, 0); + } } /* Create CPU nodes */ @@ -216,25 +240,28 @@ static int ppce500_load_device_tree(CPUPPCState *env, /* We need to generate the cpu nodes in reverse order, so Linux can pick the first node as boot node and be happy */ for (i = smp_cpus - 1; i >= 0; i--) { + CPUState *cpu = NULL; char cpu_name[128]; uint64_t cpu_release_addr = MPC8544_SPIN_BASE + (i * 0x20); for (env = first_cpu; env != NULL; env = env->next_cpu) { - if (env->cpu_index == i) { + cpu = ENV_GET_CPU(env); + if (cpu->cpu_index == i) { break; } } - if (!env) { + if (cpu == NULL) { continue; } - snprintf(cpu_name, sizeof(cpu_name), "/cpus/PowerPC,8544@%x", env->cpu_index); + snprintf(cpu_name, sizeof(cpu_name), "/cpus/PowerPC,8544@%x", + cpu->cpu_index); qemu_devtree_add_subnode(fdt, cpu_name); qemu_devtree_setprop_cell(fdt, cpu_name, "clock-frequency", clock_freq); qemu_devtree_setprop_cell(fdt, cpu_name, "timebase-frequency", tb_freq); qemu_devtree_setprop_string(fdt, cpu_name, "device_type", "cpu"); - qemu_devtree_setprop_cell(fdt, cpu_name, "reg", env->cpu_index); + qemu_devtree_setprop_cell(fdt, cpu_name, "reg", cpu->cpu_index); qemu_devtree_setprop_cell(fdt, cpu_name, "d-cache-line-size", env->dcache_line_size); qemu_devtree_setprop_cell(fdt, cpu_name, "i-cache-line-size", @@ -242,7 +269,7 @@ static int ppce500_load_device_tree(CPUPPCState *env, qemu_devtree_setprop_cell(fdt, cpu_name, "d-cache-size", 0x8000); qemu_devtree_setprop_cell(fdt, cpu_name, "i-cache-size", 0x8000); qemu_devtree_setprop_cell(fdt, cpu_name, "bus-frequency", 0); - if (env->cpu_index) { + if (cpu->cpu_index) { qemu_devtree_setprop_string(fdt, cpu_name, "status", "disabled"); qemu_devtree_setprop_string(fdt, cpu_name, "enable-method", "spin-table"); qemu_devtree_setprop_u64(fdt, cpu_name, "cpu-release-addr", @@ -267,13 +294,12 @@ static int ppce500_load_device_tree(CPUPPCState *env, /* XXX should contain a reasonable value */ qemu_devtree_setprop_cell(fdt, soc, "bus-frequency", 0); - snprintf(mpic, sizeof(mpic), "%s/pic@%llx", soc, - MPC8544_MPIC_REGS_BASE - MPC8544_CCSRBAR_BASE); + snprintf(mpic, sizeof(mpic), "%s/pic@%llx", soc, MPC8544_MPIC_REGS_OFFSET); qemu_devtree_add_subnode(fdt, mpic); qemu_devtree_setprop_string(fdt, mpic, "device_type", "open-pic"); - qemu_devtree_setprop_string(fdt, mpic, "compatible", "chrp,open-pic"); - qemu_devtree_setprop_cells(fdt, mpic, "reg", MPC8544_MPIC_REGS_BASE - - MPC8544_CCSRBAR_BASE, 0x40000); + qemu_devtree_setprop_string(fdt, mpic, "compatible", "fsl,mpic"); + qemu_devtree_setprop_cells(fdt, mpic, "reg", MPC8544_MPIC_REGS_OFFSET, + 0x40000); qemu_devtree_setprop_cell(fdt, mpic, "#address-cells", 0); qemu_devtree_setprop_cell(fdt, mpic, "#interrupt-cells", 2); mpic_ph = qemu_devtree_alloc_phandle(fdt); @@ -286,19 +312,37 @@ static int ppce500_load_device_tree(CPUPPCState *env, * device it finds in the dt as serial output device. And we generate * devices in reverse order to the dt. */ - dt_serial_create(fdt, MPC8544_SERIAL1_REGS_BASE - MPC8544_CCSRBAR_BASE, + dt_serial_create(fdt, MPC8544_SERIAL1_REGS_OFFSET, soc, mpic, "serial1", 1, false); - dt_serial_create(fdt, MPC8544_SERIAL0_REGS_BASE - MPC8544_CCSRBAR_BASE, + dt_serial_create(fdt, MPC8544_SERIAL0_REGS_OFFSET, soc, mpic, "serial0", 0, true); snprintf(gutil, sizeof(gutil), "%s/global-utilities@%llx", soc, - MPC8544_UTIL_BASE - MPC8544_CCSRBAR_BASE); + MPC8544_UTIL_OFFSET); qemu_devtree_add_subnode(fdt, gutil); qemu_devtree_setprop_string(fdt, gutil, "compatible", "fsl,mpc8544-guts"); - qemu_devtree_setprop_cells(fdt, gutil, "reg", MPC8544_UTIL_BASE - - MPC8544_CCSRBAR_BASE, 0x1000); + qemu_devtree_setprop_cells(fdt, gutil, "reg", MPC8544_UTIL_OFFSET, 0x1000); qemu_devtree_setprop(fdt, gutil, "fsl,has-rstcr", NULL, 0); + snprintf(msi, sizeof(msi), "/%s/msi@%llx", soc, MPC8544_MSI_REGS_OFFSET); + qemu_devtree_add_subnode(fdt, msi); + qemu_devtree_setprop_string(fdt, msi, "compatible", "fsl,mpic-msi"); + qemu_devtree_setprop_cells(fdt, msi, "reg", MPC8544_MSI_REGS_OFFSET, 0x200); + msi_ph = qemu_devtree_alloc_phandle(fdt); + qemu_devtree_setprop_cells(fdt, msi, "msi-available-ranges", 0x0, 0x100); + qemu_devtree_setprop_phandle(fdt, msi, "interrupt-parent", mpic); + qemu_devtree_setprop_cells(fdt, msi, "interrupts", + 0xe0, 0x0, + 0xe1, 0x0, + 0xe2, 0x0, + 0xe3, 0x0, + 0xe4, 0x0, + 0xe5, 0x0, + 0xe6, 0x0, + 0xe7, 0x0); + qemu_devtree_setprop_cell(fdt, msi, "phandle", msi_ph); + qemu_devtree_setprop_cell(fdt, msi, "linux,phandle", msi_ph); + snprintf(pci, sizeof(pci), "/pci@%llx", MPC8544_PCI_REGS_BASE); qemu_devtree_add_subnode(fdt, pci); qemu_devtree_setprop_cell(fdt, pci, "cell-index", 0); @@ -306,14 +350,17 @@ static int ppce500_load_device_tree(CPUPPCState *env, qemu_devtree_setprop_string(fdt, pci, "device_type", "pci"); qemu_devtree_setprop_cells(fdt, pci, "interrupt-map-mask", 0xf800, 0x0, 0x0, 0x7); - pci_map_create(fdt, pci_map, qemu_devtree_get_phandle(fdt, mpic)); - qemu_devtree_setprop(fdt, pci, "interrupt-map", pci_map, sizeof(pci_map)); + pci_map = pci_map_create(fdt, qemu_devtree_get_phandle(fdt, mpic), + params->pci_first_slot, params->pci_nr_slots, + &len); + qemu_devtree_setprop(fdt, pci, "interrupt-map", pci_map, len); qemu_devtree_setprop_phandle(fdt, pci, "interrupt-parent", mpic); qemu_devtree_setprop_cells(fdt, pci, "interrupts", 24, 2); qemu_devtree_setprop_cells(fdt, pci, "bus-range", 0, 255); for (i = 0; i < 14; i++) { pci_ranges[i] = cpu_to_be32(pci_ranges[i]); } + qemu_devtree_setprop_cell(fdt, pci, "fsl,msi", msi_ph); qemu_devtree_setprop(fdt, pci, "ranges", pci_ranges, sizeof(pci_ranges)); qemu_devtree_setprop_cells(fdt, pci, "reg", MPC8544_PCI_REGS_BASE >> 32, MPC8544_PCI_REGS_BASE, 0, 0x1000); @@ -340,6 +387,7 @@ done: ret = fdt_size; out: + g_free(pci_map); return ret; } @@ -416,12 +464,16 @@ void ppce500_init(PPCE500Params *params) target_long kernel_size=0; target_ulong dt_base = 0; target_ulong initrd_base = 0; - target_long initrd_size=0; - int i=0; + target_long initrd_size = 0; + target_ulong cur_base = 0; + int i = 0, j, k; unsigned int pci_irq_nrs[4] = {1, 2, 3, 4}; qemu_irq **irqs, *mpic; DeviceState *dev; CPUPPCState *firstenv = NULL; + MemoryRegion *ccsr_addr_space; + SysBusDevice *s; + PPCE500CCSRState *ccsr; /* Setup CPUs */ if (params->cpu_model == NULL) { @@ -432,6 +484,7 @@ void ppce500_init(PPCE500Params *params) irqs[0] = g_malloc0(smp_cpus * sizeof(qemu_irq) * OPENPIC_OUTPUT_NB); for (i = 0; i < smp_cpus; i++) { PowerPCCPU *cpu; + CPUState *cs; qemu_irq *input; cpu = cpu_ppc_init(params->cpu_model); @@ -440,6 +493,7 @@ void ppce500_init(PPCE500Params *params) exit(1); } env = &cpu->env; + cs = CPU(cpu); if (!firstenv) { firstenv = env; @@ -449,10 +503,11 @@ void ppce500_init(PPCE500Params *params) input = (qemu_irq *)env->irq_inputs; irqs[i][OPENPIC_OUTPUT_INT] = input[PPCE500_INPUT_INT]; irqs[i][OPENPIC_OUTPUT_CINT] = input[PPCE500_INPUT_CINT]; - env->spr[SPR_BOOKE_PIR] = env->cpu_index = i; - env->mpic_cpu_base = MPC8544_MPIC_REGS_BASE + 0x20000; + env->spr[SPR_BOOKE_PIR] = cs->cpu_index = i; + env->mpic_iack = MPC8544_CCSRBAR_BASE + + MPC8544_MPIC_REGS_OFFSET + 0xa0; - ppc_booke_timers_init(env, 400000000, PPC_TIMER_E500); + ppc_booke_timers_init(cpu, 400000000, PPC_TIMER_E500); /* Register reset handler */ if (!i) { @@ -477,40 +532,74 @@ void ppce500_init(PPCE500Params *params) vmstate_register_ram_global(ram); memory_region_add_subregion(address_space_mem, 0, ram); + dev = qdev_create(NULL, "e500-ccsr"); + object_property_add_child(qdev_get_machine(), "e500-ccsr", + OBJECT(dev), NULL); + qdev_init_nofail(dev); + ccsr = CCSR(dev); + ccsr_addr_space = &ccsr->ccsr_space; + memory_region_add_subregion(address_space_mem, MPC8544_CCSRBAR_BASE, + ccsr_addr_space); + /* MPIC */ - mpic = mpic_init(address_space_mem, MPC8544_MPIC_REGS_BASE, - smp_cpus, irqs, NULL); + mpic = g_new(qemu_irq, 256); + dev = qdev_create(NULL, "openpic"); + qdev_prop_set_uint32(dev, "nb_cpus", smp_cpus); + qdev_prop_set_uint32(dev, "model", params->mpic_version); + qdev_init_nofail(dev); + s = SYS_BUS_DEVICE(dev); + + k = 0; + for (i = 0; i < smp_cpus; i++) { + for (j = 0; j < OPENPIC_OUTPUT_NB; j++) { + sysbus_connect_irq(s, k++, irqs[i][j]); + } + } - if (!mpic) { - cpu_abort(env, "MPIC failed to initialize\n"); + for (i = 0; i < 256; i++) { + mpic[i] = qdev_get_gpio_in(dev, i); } + memory_region_add_subregion(ccsr_addr_space, MPC8544_MPIC_REGS_OFFSET, + s->mmio[0].memory); + /* Serial */ if (serial_hds[0]) { - serial_mm_init(address_space_mem, MPC8544_SERIAL0_REGS_BASE, - 0, mpic[12+26], 399193, + serial_mm_init(ccsr_addr_space, MPC8544_SERIAL0_REGS_OFFSET, + 0, mpic[42], 399193, serial_hds[0], DEVICE_BIG_ENDIAN); } if (serial_hds[1]) { - serial_mm_init(address_space_mem, MPC8544_SERIAL1_REGS_BASE, - 0, mpic[12+26], 399193, + serial_mm_init(ccsr_addr_space, MPC8544_SERIAL1_REGS_OFFSET, + 0, mpic[42], 399193, serial_hds[1], DEVICE_BIG_ENDIAN); } /* General Utility device */ - sysbus_create_simple("mpc8544-guts", MPC8544_UTIL_BASE, NULL); + dev = qdev_create(NULL, "mpc8544-guts"); + qdev_init_nofail(dev); + s = SYS_BUS_DEVICE(dev); + memory_region_add_subregion(ccsr_addr_space, MPC8544_UTIL_OFFSET, + sysbus_mmio_get_region(s, 0)); /* PCI */ - dev = sysbus_create_varargs("e500-pcihost", MPC8544_PCI_REGS_BASE, - mpic[pci_irq_nrs[0]], mpic[pci_irq_nrs[1]], - mpic[pci_irq_nrs[2]], mpic[pci_irq_nrs[3]], - NULL); + dev = qdev_create(NULL, "e500-pcihost"); + qdev_prop_set_uint32(dev, "first_slot", params->pci_first_slot); + qdev_init_nofail(dev); + s = SYS_BUS_DEVICE(dev); + sysbus_connect_irq(s, 0, mpic[pci_irq_nrs[0]]); + sysbus_connect_irq(s, 1, mpic[pci_irq_nrs[1]]); + sysbus_connect_irq(s, 2, mpic[pci_irq_nrs[2]]); + sysbus_connect_irq(s, 3, mpic[pci_irq_nrs[3]]); + memory_region_add_subregion(ccsr_addr_space, MPC8544_PCI_REGS_OFFSET, + sysbus_mmio_get_region(s, 0)); + pci_bus = (PCIBus *)qdev_get_child_bus(dev, "pci.0"); if (!pci_bus) printf("couldn't create PCI controller!\n"); - sysbus_mmio_map(sysbus_from_qdev(dev), 1, MPC8544_PCI_IO); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 1, MPC8544_PCI_IO); if (pci_bus) { /* Register network interfaces. */ @@ -539,12 +628,17 @@ void ppce500_init(PPCE500Params *params) params->kernel_filename); exit(1); } + + cur_base = loadaddr + kernel_size; + + /* Reserve space for dtb */ + dt_base = (cur_base + DTC_LOAD_PAD) & ~DTC_PAD_MASK; + cur_base += DTB_MAX_SIZE; } /* Load initrd. */ if (params->initrd_filename) { - initrd_base = (loadaddr + kernel_size + INITRD_LOAD_PAD) & - ~INITRD_PAD_MASK; + initrd_base = (cur_base + INITRD_LOAD_PAD) & ~INITRD_PAD_MASK; initrd_size = load_image_targphys(params->initrd_filename, initrd_base, ram_size - initrd_base); @@ -553,6 +647,8 @@ void ppce500_init(PPCE500Params *params) params->initrd_filename); exit(1); } + + cur_base = initrd_base + initrd_size; } /* If we're loading a kernel directly, we must load the device tree too. */ @@ -560,13 +656,13 @@ void ppce500_init(PPCE500Params *params) struct boot_info *boot_info; int dt_size; - dt_base = (loadaddr + kernel_size + DTC_LOAD_PAD) & ~DTC_PAD_MASK; dt_size = ppce500_load_device_tree(env, params, dt_base, initrd_base, initrd_size); if (dt_size < 0) { fprintf(stderr, "couldn't load device tree\n"); exit(1); } + assert(dt_size < DTB_MAX_SIZE); boot_info = env->load_info; boot_info->entry = entry; @@ -578,3 +674,33 @@ void ppce500_init(PPCE500Params *params) kvmppc_init(); } } + +static int e500_ccsr_initfn(SysBusDevice *dev) +{ + PPCE500CCSRState *ccsr; + + ccsr = CCSR(dev); + memory_region_init(&ccsr->ccsr_space, "e500-ccsr", + MPC8544_CCSRBAR_SIZE); + return 0; +} + +static void e500_ccsr_class_init(ObjectClass *klass, void *data) +{ + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + k->init = e500_ccsr_initfn; +} + +static const TypeInfo e500_ccsr_info = { + .name = TYPE_CCSR, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(PPCE500CCSRState), + .class_init = e500_ccsr_class_init, +}; + +static void e500_register_types(void) +{ + type_register_static(&e500_ccsr_info); +} + +type_init(e500_register_types) diff --git a/hw/ppc/e500.h b/hw/ppc/e500.h index 7ae87f4..226c93d 100644 --- a/hw/ppc/e500.h +++ b/hw/ppc/e500.h @@ -9,11 +9,15 @@ typedef struct PPCE500Params { const char *kernel_cmdline; const char *initrd_filename; const char *cpu_model; + int pci_first_slot; + int pci_nr_slots; /* e500-specific params */ /* required -- must at least add toplevel board compatible */ void (*fixup_devtree)(struct PPCE500Params *params, void *fdt); + + int mpic_version; } PPCE500Params; void ppce500_init(PPCE500Params *params); diff --git a/hw/ppc/e500plat.c b/hw/ppc/e500plat.c index 4cfb940..25ac4b1 100644 --- a/hw/ppc/e500plat.c +++ b/hw/ppc/e500plat.c @@ -13,7 +13,9 @@ #include "qemu-common.h" #include "e500.h" #include "../boards.h" -#include "device_tree.h" +#include "sysemu/device_tree.h" +#include "hw/pci/pci.h" +#include "hw/openpic.h" static void e500plat_fixup_devtree(PPCE500Params *params, void *fdt) { @@ -40,7 +42,10 @@ static void e500plat_init(QEMUMachineInitArgs *args) .kernel_cmdline = kernel_cmdline, .initrd_filename = initrd_filename, .cpu_model = cpu_model, + .pci_first_slot = 0x1, + .pci_nr_slots = PCI_SLOT_MAX - 1, .fixup_devtree = e500plat_fixup_devtree, + .mpic_version = OPENPIC_MODEL_FSL_MPIC_42, }; ppce500_init(¶ms); @@ -50,7 +55,8 @@ static QEMUMachine e500plat_machine = { .name = "ppce500", .desc = "generic paravirt e500 platform", .init = e500plat_init, - .max_cpus = 15, + .max_cpus = 32, + DEFAULT_MACHINE_OPTIONS, }; static void e500plat_machine_init(void) diff --git a/hw/ppc/mac.h b/hw/ppc/mac.h new file mode 100644 index 0000000..b17107b --- /dev/null +++ b/hw/ppc/mac.h @@ -0,0 +1,181 @@ +/* + * QEMU PowerMac emulation shared definitions and prototypes + * + * Copyright (c) 2004-2007 Fabrice Bellard + * Copyright (c) 2007 Jocelyn Mayer + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#if !defined(__PPC_MAC_H__) +#define __PPC_MAC_H__ + +#include "exec/memory.h" +#include "hw/sysbus.h" +#include "hw/ide/internal.h" +#include "hw/adb.h" + +/* SMP is not enabled, for now */ +#define MAX_CPUS 1 + +#define BIOS_SIZE (1024 * 1024) +#define BIOS_FILENAME "ppc_rom.bin" +#define NVRAM_SIZE 0x2000 +#define PROM_FILENAME "openbios-ppc" +#define PROM_ADDR 0xfff00000 + +#define KERNEL_LOAD_ADDR 0x01000000 +#define KERNEL_GAP 0x00100000 + +#define ESCC_CLOCK 3686400 + +/* Cuda */ +#define TYPE_CUDA "cuda" +#define CUDA(obj) OBJECT_CHECK(CUDAState, (obj), TYPE_CUDA) + +/** + * CUDATimer: + * @counter_value: counter value at load time + */ +typedef struct CUDATimer { + int index; + uint16_t latch; + uint16_t counter_value; + int64_t load_time; + int64_t next_irq_time; + QEMUTimer *timer; +} CUDATimer; + +/** + * CUDAState: + * @b: B-side data + * @a: A-side data + * @dirb: B-side direction (1=output) + * @dira: A-side direction (1=output) + * @sr: Shift register + * @acr: Auxiliary control register + * @pcr: Peripheral control register + * @ifr: Interrupt flag register + * @ier: Interrupt enable register + * @anh: A-side data, no handshake + * @last_b: last value of B register + * @last_acr: last value of ACR register + */ +typedef struct CUDAState { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + + MemoryRegion mem; + /* cuda registers */ + uint8_t b; + uint8_t a; + uint8_t dirb; + uint8_t dira; + uint8_t sr; + uint8_t acr; + uint8_t pcr; + uint8_t ifr; + uint8_t ier; + uint8_t anh; + + ADBBusState adb_bus; + CUDATimer timers[2]; + + uint32_t tick_offset; + + uint8_t last_b; + uint8_t last_acr; + + int data_in_size; + int data_in_index; + int data_out_index; + + qemu_irq irq; + uint8_t autopoll; + uint8_t data_in[128]; + uint8_t data_out[16]; + QEMUTimer *adb_poll_timer; +} CUDAState; + +/* MacIO */ +#define TYPE_OLDWORLD_MACIO "macio-oldworld" +#define TYPE_NEWWORLD_MACIO "macio-newworld" + +#define TYPE_MACIO_IDE "macio-ide" +#define MACIO_IDE(obj) OBJECT_CHECK(MACIOIDEState, (obj), TYPE_MACIO_IDE) + +typedef struct MACIOIDEState { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + + qemu_irq irq; + qemu_irq dma_irq; + + MemoryRegion mem; + IDEBus bus; + BlockDriverAIOCB *aiocb; +} MACIOIDEState; + +void macio_ide_init_drives(MACIOIDEState *ide, DriveInfo **hd_table); +void macio_ide_register_dma(MACIOIDEState *ide, void *dbdma, int channel); + +void macio_init(PCIDevice *dev, + MemoryRegion *pic_mem, + MemoryRegion *escc_mem); + +/* Heathrow PIC */ +qemu_irq *heathrow_pic_init(MemoryRegion **pmem, + int nb_cpus, qemu_irq **irqs); + +/* Grackle PCI */ +#define TYPE_GRACKLE_PCI_HOST_BRIDGE "grackle-pcihost" +PCIBus *pci_grackle_init(uint32_t base, qemu_irq *pic, + MemoryRegion *address_space_mem, + MemoryRegion *address_space_io); + +/* UniNorth PCI */ +PCIBus *pci_pmac_init(qemu_irq *pic, + MemoryRegion *address_space_mem, + MemoryRegion *address_space_io); +PCIBus *pci_pmac_u3_init(qemu_irq *pic, + MemoryRegion *address_space_mem, + MemoryRegion *address_space_io); + +/* Mac NVRAM */ +#define TYPE_MACIO_NVRAM "macio-nvram" +#define MACIO_NVRAM(obj) \ + OBJECT_CHECK(MacIONVRAMState, (obj), TYPE_MACIO_NVRAM) + +typedef struct MacIONVRAMState { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + + uint32_t size; + uint32_t it_shift; + + MemoryRegion mem; + uint8_t *data; +} MacIONVRAMState; + +void pmac_format_nvram_partition (MacIONVRAMState *nvr, int len); +uint8_t macio_nvram_read(MacIONVRAMState *s, uint32_t addr); +void macio_nvram_write(MacIONVRAMState *s, uint32_t addr, uint8_t val); +#endif /* !defined(__PPC_MAC_H__) */ diff --git a/hw/ppc/mac_newworld.c b/hw/ppc/mac_newworld.c new file mode 100644 index 0000000..065ea87 --- /dev/null +++ b/hw/ppc/mac_newworld.c @@ -0,0 +1,470 @@ +/* + * QEMU PowerPC CHRP (currently NewWorld PowerMac) hardware System Emulator + * + * Copyright (c) 2004-2007 Fabrice Bellard + * Copyright (c) 2007 Jocelyn Mayer + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * PCI bus layout on a real G5 (U3 based): + * + * 0000:f0:0b.0 Host bridge [0600]: Apple Computer Inc. U3 AGP [106b:004b] + * 0000:f0:10.0 VGA compatible controller [0300]: ATI Technologies Inc RV350 AP [Radeon 9600] [1002:4150] + * 0001:00:00.0 Host bridge [0600]: Apple Computer Inc. CPC945 HT Bridge [106b:004a] + * 0001:00:01.0 PCI bridge [0604]: Advanced Micro Devices [AMD] AMD-8131 PCI-X Bridge [1022:7450] (rev 12) + * 0001:00:02.0 PCI bridge [0604]: Advanced Micro Devices [AMD] AMD-8131 PCI-X Bridge [1022:7450] (rev 12) + * 0001:00:03.0 PCI bridge [0604]: Apple Computer Inc. K2 HT-PCI Bridge [106b:0045] + * 0001:00:04.0 PCI bridge [0604]: Apple Computer Inc. K2 HT-PCI Bridge [106b:0046] + * 0001:00:05.0 PCI bridge [0604]: Apple Computer Inc. K2 HT-PCI Bridge [106b:0047] + * 0001:00:06.0 PCI bridge [0604]: Apple Computer Inc. K2 HT-PCI Bridge [106b:0048] + * 0001:00:07.0 PCI bridge [0604]: Apple Computer Inc. K2 HT-PCI Bridge [106b:0049] + * 0001:01:07.0 Class [ff00]: Apple Computer Inc. K2 KeyLargo Mac/IO [106b:0041] (rev 20) + * 0001:01:08.0 USB Controller [0c03]: Apple Computer Inc. K2 KeyLargo USB [106b:0040] + * 0001:01:09.0 USB Controller [0c03]: Apple Computer Inc. K2 KeyLargo USB [106b:0040] + * 0001:02:0b.0 USB Controller [0c03]: NEC Corporation USB [1033:0035] (rev 43) + * 0001:02:0b.1 USB Controller [0c03]: NEC Corporation USB [1033:0035] (rev 43) + * 0001:02:0b.2 USB Controller [0c03]: NEC Corporation USB 2.0 [1033:00e0] (rev 04) + * 0001:03:0d.0 Class [ff00]: Apple Computer Inc. K2 ATA/100 [106b:0043] + * 0001:03:0e.0 FireWire (IEEE 1394) [0c00]: Apple Computer Inc. K2 FireWire [106b:0042] + * 0001:04:0f.0 Ethernet controller [0200]: Apple Computer Inc. K2 GMAC (Sun GEM) [106b:004c] + * 0001:05:0c.0 IDE interface [0101]: Broadcom K2 SATA [1166:0240] + * + */ +#include "hw/hw.h" +#include "hw/ppc.h" +#include "hw/ppc/mac.h" +#include "hw/adb.h" +#include "hw/mac_dbdma.h" +#include "hw/nvram.h" +#include "hw/pci/pci.h" +#include "net/net.h" +#include "sysemu/sysemu.h" +#include "hw/boards.h" +#include "hw/fw_cfg.h" +#include "hw/escc.h" +#include "hw/openpic.h" +#include "hw/ide.h" +#include "hw/loader.h" +#include "elf.h" +#include "sysemu/kvm.h" +#include "kvm_ppc.h" +#include "hw/usb.h" +#include "sysemu/blockdev.h" +#include "exec/address-spaces.h" +#include "hw/sysbus.h" + +#define MAX_IDE_BUS 2 +#define CFG_ADDR 0xf0000510 + +/* debug UniNorth */ +//#define DEBUG_UNIN + +#ifdef DEBUG_UNIN +#define UNIN_DPRINTF(fmt, ...) \ + do { printf("UNIN: " fmt , ## __VA_ARGS__); } while (0) +#else +#define UNIN_DPRINTF(fmt, ...) +#endif + +/* UniN device */ +static void unin_write(void *opaque, hwaddr addr, uint64_t value, + unsigned size) +{ + UNIN_DPRINTF("write addr " TARGET_FMT_plx " val %"PRIx64"\n", addr, value); +} + +static uint64_t unin_read(void *opaque, hwaddr addr, unsigned size) +{ + uint32_t value; + + value = 0; + UNIN_DPRINTF("readl addr " TARGET_FMT_plx " val %x\n", addr, value); + + return value; +} + +static const MemoryRegionOps unin_ops = { + .read = unin_read, + .write = unin_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static int fw_cfg_boot_set(void *opaque, const char *boot_device) +{ + fw_cfg_add_i16(opaque, FW_CFG_BOOT_DEVICE, boot_device[0]); + return 0; +} + +static uint64_t translate_kernel_address(void *opaque, uint64_t addr) +{ + return (addr & 0x0fffffff) + KERNEL_LOAD_ADDR; +} + +static hwaddr round_page(hwaddr addr) +{ + return (addr + TARGET_PAGE_SIZE - 1) & TARGET_PAGE_MASK; +} + +static void ppc_core99_reset(void *opaque) +{ + PowerPCCPU *cpu = opaque; + + cpu_reset(CPU(cpu)); +} + +/* PowerPC Mac99 hardware initialisation */ +static void ppc_core99_init(QEMUMachineInitArgs *args) +{ + ram_addr_t ram_size = args->ram_size; + const char *cpu_model = args->cpu_model; + const char *kernel_filename = args->kernel_filename; + const char *kernel_cmdline = args->kernel_cmdline; + const char *initrd_filename = args->initrd_filename; + const char *boot_device = args->boot_device; + PowerPCCPU *cpu = NULL; + CPUPPCState *env = NULL; + char *filename; + qemu_irq *pic, **openpic_irqs; + MemoryRegion *unin_memory = g_new(MemoryRegion, 1); + int linux_boot, i, j, k; + MemoryRegion *ram = g_new(MemoryRegion, 1), *bios = g_new(MemoryRegion, 1); + hwaddr kernel_base, initrd_base, cmdline_base = 0; + long kernel_size, initrd_size; + PCIBus *pci_bus; + PCIDevice *macio; + MACIOIDEState *macio_ide; + BusState *adb_bus; + MacIONVRAMState *nvr; + int bios_size; + MemoryRegion *pic_mem, *escc_mem; + MemoryRegion *escc_bar = g_new(MemoryRegion, 1); + int ppc_boot_device; + DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; + void *fw_cfg; + int machine_arch; + SysBusDevice *s; + DeviceState *dev; + + linux_boot = (kernel_filename != NULL); + + /* init CPUs */ + if (cpu_model == NULL) +#ifdef TARGET_PPC64 + cpu_model = "970fx"; +#else + cpu_model = "G4"; +#endif + for (i = 0; i < smp_cpus; i++) { + cpu = cpu_ppc_init(cpu_model); + if (cpu == NULL) { + fprintf(stderr, "Unable to find PowerPC CPU definition\n"); + exit(1); + } + env = &cpu->env; + + /* Set time-base frequency to 100 Mhz */ + cpu_ppc_tb_init(env, 100UL * 1000UL * 1000UL); + qemu_register_reset(ppc_core99_reset, cpu); + } + + /* allocate RAM */ + memory_region_init_ram(ram, "ppc_core99.ram", ram_size); + vmstate_register_ram_global(ram); + memory_region_add_subregion(get_system_memory(), 0, ram); + + /* allocate and load BIOS */ + memory_region_init_ram(bios, "ppc_core99.bios", BIOS_SIZE); + vmstate_register_ram_global(bios); + if (bios_name == NULL) + bios_name = PROM_FILENAME; + filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); + memory_region_set_readonly(bios, true); + memory_region_add_subregion(get_system_memory(), PROM_ADDR, bios); + + /* Load OpenBIOS (ELF) */ + if (filename) { + bios_size = load_elf(filename, NULL, NULL, NULL, + NULL, NULL, 1, ELF_MACHINE, 0); + + g_free(filename); + } else { + bios_size = -1; + } + if (bios_size < 0 || bios_size > BIOS_SIZE) { + hw_error("qemu: could not load PowerPC bios '%s'\n", bios_name); + exit(1); + } + + if (linux_boot) { + uint64_t lowaddr = 0; + int bswap_needed; + +#ifdef BSWAP_NEEDED + bswap_needed = 1; +#else + bswap_needed = 0; +#endif + kernel_base = KERNEL_LOAD_ADDR; + + kernel_size = load_elf(kernel_filename, translate_kernel_address, NULL, + NULL, &lowaddr, NULL, 1, ELF_MACHINE, 0); + if (kernel_size < 0) + kernel_size = load_aout(kernel_filename, kernel_base, + ram_size - kernel_base, bswap_needed, + TARGET_PAGE_SIZE); + if (kernel_size < 0) + kernel_size = load_image_targphys(kernel_filename, + kernel_base, + ram_size - kernel_base); + if (kernel_size < 0) { + hw_error("qemu: could not load kernel '%s'\n", kernel_filename); + exit(1); + } + /* load initrd */ + if (initrd_filename) { + initrd_base = round_page(kernel_base + kernel_size + KERNEL_GAP); + initrd_size = load_image_targphys(initrd_filename, initrd_base, + ram_size - initrd_base); + if (initrd_size < 0) { + hw_error("qemu: could not load initial ram disk '%s'\n", + initrd_filename); + exit(1); + } + cmdline_base = round_page(initrd_base + initrd_size); + } else { + initrd_base = 0; + initrd_size = 0; + cmdline_base = round_page(kernel_base + kernel_size + KERNEL_GAP); + } + ppc_boot_device = 'm'; + } else { + kernel_base = 0; + kernel_size = 0; + initrd_base = 0; + initrd_size = 0; + ppc_boot_device = '\0'; + /* We consider that NewWorld PowerMac never have any floppy drive + * For now, OHW cannot boot from the network. + */ + for (i = 0; boot_device[i] != '\0'; i++) { + if (boot_device[i] >= 'c' && boot_device[i] <= 'f') { + ppc_boot_device = boot_device[i]; + break; + } + } + if (ppc_boot_device == '\0') { + fprintf(stderr, "No valid boot device for Mac99 machine\n"); + exit(1); + } + } + + /* Register 8 MB of ISA IO space */ + isa_mmio_init(0xf2000000, 0x00800000); + + /* UniN init */ + memory_region_init_io(unin_memory, &unin_ops, NULL, "unin", 0x1000); + memory_region_add_subregion(get_system_memory(), 0xf8000000, unin_memory); + + openpic_irqs = g_malloc0(smp_cpus * sizeof(qemu_irq *)); + openpic_irqs[0] = + g_malloc0(smp_cpus * sizeof(qemu_irq) * OPENPIC_OUTPUT_NB); + for (i = 0; i < smp_cpus; i++) { + /* Mac99 IRQ connection between OpenPIC outputs pins + * and PowerPC input pins + */ + switch (PPC_INPUT(env)) { + case PPC_FLAGS_INPUT_6xx: + openpic_irqs[i] = openpic_irqs[0] + (i * OPENPIC_OUTPUT_NB); + openpic_irqs[i][OPENPIC_OUTPUT_INT] = + ((qemu_irq *)env->irq_inputs)[PPC6xx_INPUT_INT]; + openpic_irqs[i][OPENPIC_OUTPUT_CINT] = + ((qemu_irq *)env->irq_inputs)[PPC6xx_INPUT_INT]; + openpic_irqs[i][OPENPIC_OUTPUT_MCK] = + ((qemu_irq *)env->irq_inputs)[PPC6xx_INPUT_MCP]; + /* Not connected ? */ + openpic_irqs[i][OPENPIC_OUTPUT_DEBUG] = NULL; + /* Check this */ + openpic_irqs[i][OPENPIC_OUTPUT_RESET] = + ((qemu_irq *)env->irq_inputs)[PPC6xx_INPUT_HRESET]; + break; +#if defined(TARGET_PPC64) + case PPC_FLAGS_INPUT_970: + openpic_irqs[i] = openpic_irqs[0] + (i * OPENPIC_OUTPUT_NB); + openpic_irqs[i][OPENPIC_OUTPUT_INT] = + ((qemu_irq *)env->irq_inputs)[PPC970_INPUT_INT]; + openpic_irqs[i][OPENPIC_OUTPUT_CINT] = + ((qemu_irq *)env->irq_inputs)[PPC970_INPUT_INT]; + openpic_irqs[i][OPENPIC_OUTPUT_MCK] = + ((qemu_irq *)env->irq_inputs)[PPC970_INPUT_MCP]; + /* Not connected ? */ + openpic_irqs[i][OPENPIC_OUTPUT_DEBUG] = NULL; + /* Check this */ + openpic_irqs[i][OPENPIC_OUTPUT_RESET] = + ((qemu_irq *)env->irq_inputs)[PPC970_INPUT_HRESET]; + break; +#endif /* defined(TARGET_PPC64) */ + default: + hw_error("Bus model not supported on mac99 machine\n"); + exit(1); + } + } + + pic = g_new(qemu_irq, 64); + + dev = qdev_create(NULL, "openpic"); + qdev_prop_set_uint32(dev, "model", OPENPIC_MODEL_RAVEN); + qdev_init_nofail(dev); + s = SYS_BUS_DEVICE(dev); + pic_mem = s->mmio[0].memory; + k = 0; + for (i = 0; i < smp_cpus; i++) { + for (j = 0; j < OPENPIC_OUTPUT_NB; j++) { + sysbus_connect_irq(s, k++, openpic_irqs[i][j]); + } + } + + for (i = 0; i < 64; i++) { + pic[i] = qdev_get_gpio_in(dev, i); + } + + if (PPC_INPUT(env) == PPC_FLAGS_INPUT_970) { + /* 970 gets a U3 bus */ + pci_bus = pci_pmac_u3_init(pic, get_system_memory(), get_system_io()); + machine_arch = ARCH_MAC99_U3; + } else { + pci_bus = pci_pmac_init(pic, get_system_memory(), get_system_io()); + machine_arch = ARCH_MAC99; + } + /* init basic PC hardware */ + pci_vga_init(pci_bus); + + escc_mem = escc_init(0, pic[0x25], pic[0x24], + serial_hds[0], serial_hds[1], ESCC_CLOCK, 4); + memory_region_init_alias(escc_bar, "escc-bar", + escc_mem, 0, memory_region_size(escc_mem)); + + for(i = 0; i < nb_nics; i++) + pci_nic_init_nofail(&nd_table[i], "ne2k_pci", NULL); + + ide_drive_get(hd, MAX_IDE_BUS); + + macio = pci_create(pci_bus, -1, TYPE_NEWWORLD_MACIO); + dev = DEVICE(macio); + qdev_connect_gpio_out(dev, 0, pic[0x19]); /* CUDA */ + qdev_connect_gpio_out(dev, 1, pic[0x0d]); /* IDE */ + qdev_connect_gpio_out(dev, 2, pic[0x02]); /* IDE DMA */ + qdev_connect_gpio_out(dev, 3, pic[0x0e]); /* IDE */ + qdev_connect_gpio_out(dev, 4, pic[0x02]); /* IDE DMA */ + macio_init(macio, pic_mem, escc_bar); + + /* We only emulate 2 out of 3 IDE controllers for now */ + macio_ide = MACIO_IDE(object_resolve_path_component(OBJECT(macio), + "ide[0]")); + macio_ide_init_drives(macio_ide, hd); + + macio_ide = MACIO_IDE(object_resolve_path_component(OBJECT(macio), + "ide[1]")); + macio_ide_init_drives(macio_ide, &hd[MAX_IDE_DEVS]); + + dev = DEVICE(object_resolve_path_component(OBJECT(macio), "cuda")); + adb_bus = qdev_get_child_bus(dev, "adb.0"); + dev = qdev_create(adb_bus, TYPE_ADB_KEYBOARD); + qdev_init_nofail(dev); + dev = qdev_create(adb_bus, TYPE_ADB_MOUSE); + qdev_init_nofail(dev); + + if (usb_enabled(machine_arch == ARCH_MAC99_U3)) { + pci_create_simple(pci_bus, -1, "pci-ohci"); + /* U3 needs to use USB for input because Linux doesn't support via-cuda + on PPC64 */ + if (machine_arch == ARCH_MAC99_U3) { + usbdevice_create("keyboard"); + usbdevice_create("mouse"); + } + } + + if (graphic_depth != 15 && graphic_depth != 32 && graphic_depth != 8) + graphic_depth = 15; + + /* The NewWorld NVRAM is not located in the MacIO device */ + dev = qdev_create(NULL, TYPE_MACIO_NVRAM); + qdev_prop_set_uint32(dev, "size", 0x2000); + qdev_prop_set_uint32(dev, "it_shift", 1); + qdev_init_nofail(dev); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0xFFF04000); + nvr = MACIO_NVRAM(dev); + pmac_format_nvram_partition(nvr, 0x2000); + /* No PCI init: the BIOS will do it */ + + fw_cfg = fw_cfg_init(0, 0, CFG_ADDR, CFG_ADDR + 2); + fw_cfg_add_i16(fw_cfg, FW_CFG_MAX_CPUS, (uint16_t)max_cpus); + fw_cfg_add_i32(fw_cfg, FW_CFG_ID, 1); + fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)ram_size); + fw_cfg_add_i16(fw_cfg, FW_CFG_MACHINE_ID, machine_arch); + fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ADDR, kernel_base); + fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_SIZE, kernel_size); + if (kernel_cmdline) { + fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_CMDLINE, cmdline_base); + pstrcpy_targphys("cmdline", cmdline_base, TARGET_PAGE_SIZE, kernel_cmdline); + } else { + fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_CMDLINE, 0); + } + fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_ADDR, initrd_base); + fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_SIZE, initrd_size); + fw_cfg_add_i16(fw_cfg, FW_CFG_BOOT_DEVICE, ppc_boot_device); + + fw_cfg_add_i16(fw_cfg, FW_CFG_PPC_WIDTH, graphic_width); + fw_cfg_add_i16(fw_cfg, FW_CFG_PPC_HEIGHT, graphic_height); + fw_cfg_add_i16(fw_cfg, FW_CFG_PPC_DEPTH, graphic_depth); + + fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_IS_KVM, kvm_enabled()); + if (kvm_enabled()) { +#ifdef CONFIG_KVM + uint8_t *hypercall; + + fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_TBFREQ, kvmppc_get_tbfreq()); + hypercall = g_malloc(16); + kvmppc_get_hypercall(env, hypercall, 16); + fw_cfg_add_bytes(fw_cfg, FW_CFG_PPC_KVM_HC, hypercall, 16); + fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_KVM_PID, getpid()); +#endif + } else { + fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_TBFREQ, get_ticks_per_sec()); + } + + qemu_register_boot_set(fw_cfg_boot_set, fw_cfg); +} + +static QEMUMachine core99_machine = { + .name = "mac99", + .desc = "Mac99 based PowerMAC", + .init = ppc_core99_init, + .max_cpus = MAX_CPUS, +#ifdef TARGET_PPC64 + .is_default = 1, +#endif + DEFAULT_MACHINE_OPTIONS, +}; + +static void core99_machine_init(void) +{ + qemu_register_machine(&core99_machine); +} + +machine_init(core99_machine_init); diff --git a/hw/ppc/mac_oldworld.c b/hw/ppc/mac_oldworld.c new file mode 100644 index 0000000..2778e45 --- /dev/null +++ b/hw/ppc/mac_oldworld.c @@ -0,0 +1,356 @@ + +/* + * QEMU OldWorld PowerMac (currently ~G3 Beige) hardware System Emulator + * + * Copyright (c) 2004-2007 Fabrice Bellard + * Copyright (c) 2007 Jocelyn Mayer + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "hw/hw.h" +#include "hw/ppc.h" +#include "mac.h" +#include "hw/adb.h" +#include "hw/nvram.h" +#include "sysemu/sysemu.h" +#include "net/net.h" +#include "hw/isa.h" +#include "hw/pci/pci.h" +#include "hw/boards.h" +#include "hw/fw_cfg.h" +#include "hw/escc.h" +#include "hw/ide.h" +#include "hw/loader.h" +#include "elf.h" +#include "sysemu/kvm.h" +#include "kvm_ppc.h" +#include "sysemu/blockdev.h" +#include "exec/address-spaces.h" + +#define MAX_IDE_BUS 2 +#define CFG_ADDR 0xf0000510 + +static int fw_cfg_boot_set(void *opaque, const char *boot_device) +{ + fw_cfg_add_i16(opaque, FW_CFG_BOOT_DEVICE, boot_device[0]); + return 0; +} + + +static uint64_t translate_kernel_address(void *opaque, uint64_t addr) +{ + return (addr & 0x0fffffff) + KERNEL_LOAD_ADDR; +} + +static hwaddr round_page(hwaddr addr) +{ + return (addr + TARGET_PAGE_SIZE - 1) & TARGET_PAGE_MASK; +} + +static void ppc_heathrow_reset(void *opaque) +{ + PowerPCCPU *cpu = opaque; + + cpu_reset(CPU(cpu)); +} + +static void ppc_heathrow_init(QEMUMachineInitArgs *args) +{ + ram_addr_t ram_size = args->ram_size; + const char *cpu_model = args->cpu_model; + const char *kernel_filename = args->kernel_filename; + const char *kernel_cmdline = args->kernel_cmdline; + const char *initrd_filename = args->initrd_filename; + const char *boot_device = args->boot_device; + MemoryRegion *sysmem = get_system_memory(); + PowerPCCPU *cpu = NULL; + CPUPPCState *env = NULL; + char *filename; + qemu_irq *pic, **heathrow_irqs; + int linux_boot, i; + MemoryRegion *ram = g_new(MemoryRegion, 1); + MemoryRegion *bios = g_new(MemoryRegion, 1); + uint32_t kernel_base, initrd_base, cmdline_base = 0; + int32_t kernel_size, initrd_size; + PCIBus *pci_bus; + PCIDevice *macio; + MACIOIDEState *macio_ide; + DeviceState *dev; + BusState *adb_bus; + int bios_size; + MemoryRegion *pic_mem; + MemoryRegion *escc_mem, *escc_bar = g_new(MemoryRegion, 1); + uint16_t ppc_boot_device; + DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; + void *fw_cfg; + + linux_boot = (kernel_filename != NULL); + + /* init CPUs */ + if (cpu_model == NULL) + cpu_model = "G3"; + for (i = 0; i < smp_cpus; i++) { + cpu = cpu_ppc_init(cpu_model); + if (cpu == NULL) { + fprintf(stderr, "Unable to find PowerPC CPU definition\n"); + exit(1); + } + env = &cpu->env; + + /* Set time-base frequency to 16.6 Mhz */ + cpu_ppc_tb_init(env, 16600000UL); + qemu_register_reset(ppc_heathrow_reset, cpu); + } + + /* allocate RAM */ + if (ram_size > (2047 << 20)) { + fprintf(stderr, + "qemu: Too much memory for this machine: %d MB, maximum 2047 MB\n", + ((unsigned int)ram_size / (1 << 20))); + exit(1); + } + + memory_region_init_ram(ram, "ppc_heathrow.ram", ram_size); + vmstate_register_ram_global(ram); + memory_region_add_subregion(sysmem, 0, ram); + + /* allocate and load BIOS */ + memory_region_init_ram(bios, "ppc_heathrow.bios", BIOS_SIZE); + vmstate_register_ram_global(bios); + if (bios_name == NULL) + bios_name = PROM_FILENAME; + filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); + memory_region_set_readonly(bios, true); + memory_region_add_subregion(sysmem, PROM_ADDR, bios); + + /* Load OpenBIOS (ELF) */ + if (filename) { + bios_size = load_elf(filename, 0, NULL, NULL, NULL, NULL, + 1, ELF_MACHINE, 0); + g_free(filename); + } else { + bios_size = -1; + } + if (bios_size < 0 || bios_size > BIOS_SIZE) { + hw_error("qemu: could not load PowerPC bios '%s'\n", bios_name); + exit(1); + } + + if (linux_boot) { + uint64_t lowaddr = 0; + int bswap_needed; + +#ifdef BSWAP_NEEDED + bswap_needed = 1; +#else + bswap_needed = 0; +#endif + kernel_base = KERNEL_LOAD_ADDR; + kernel_size = load_elf(kernel_filename, translate_kernel_address, NULL, + NULL, &lowaddr, NULL, 1, ELF_MACHINE, 0); + if (kernel_size < 0) + kernel_size = load_aout(kernel_filename, kernel_base, + ram_size - kernel_base, bswap_needed, + TARGET_PAGE_SIZE); + if (kernel_size < 0) + kernel_size = load_image_targphys(kernel_filename, + kernel_base, + ram_size - kernel_base); + if (kernel_size < 0) { + hw_error("qemu: could not load kernel '%s'\n", + kernel_filename); + exit(1); + } + /* load initrd */ + if (initrd_filename) { + initrd_base = round_page(kernel_base + kernel_size + KERNEL_GAP); + initrd_size = load_image_targphys(initrd_filename, initrd_base, + ram_size - initrd_base); + if (initrd_size < 0) { + hw_error("qemu: could not load initial ram disk '%s'\n", + initrd_filename); + exit(1); + } + cmdline_base = round_page(initrd_base + initrd_size); + } else { + initrd_base = 0; + initrd_size = 0; + cmdline_base = round_page(kernel_base + kernel_size + KERNEL_GAP); + } + ppc_boot_device = 'm'; + } else { + kernel_base = 0; + kernel_size = 0; + initrd_base = 0; + initrd_size = 0; + ppc_boot_device = '\0'; + for (i = 0; boot_device[i] != '\0'; i++) { + /* TOFIX: for now, the second IDE channel is not properly + * used by OHW. The Mac floppy disk are not emulated. + * For now, OHW cannot boot from the network. + */ +#if 0 + if (boot_device[i] >= 'a' && boot_device[i] <= 'f') { + ppc_boot_device = boot_device[i]; + break; + } +#else + if (boot_device[i] >= 'c' && boot_device[i] <= 'd') { + ppc_boot_device = boot_device[i]; + break; + } +#endif + } + if (ppc_boot_device == '\0') { + fprintf(stderr, "No valid boot device for G3 Beige machine\n"); + exit(1); + } + } + + /* Register 2 MB of ISA IO space */ + isa_mmio_init(0xfe000000, 0x00200000); + + /* XXX: we register only 1 output pin for heathrow PIC */ + heathrow_irqs = g_malloc0(smp_cpus * sizeof(qemu_irq *)); + heathrow_irqs[0] = + g_malloc0(smp_cpus * sizeof(qemu_irq) * 1); + /* Connect the heathrow PIC outputs to the 6xx bus */ + for (i = 0; i < smp_cpus; i++) { + switch (PPC_INPUT(env)) { + case PPC_FLAGS_INPUT_6xx: + heathrow_irqs[i] = heathrow_irqs[0] + (i * 1); + heathrow_irqs[i][0] = + ((qemu_irq *)env->irq_inputs)[PPC6xx_INPUT_INT]; + break; + default: + hw_error("Bus model not supported on OldWorld Mac machine\n"); + } + } + + /* init basic PC hardware */ + if (PPC_INPUT(env) != PPC_FLAGS_INPUT_6xx) { + hw_error("Only 6xx bus is supported on heathrow machine\n"); + } + pic = heathrow_pic_init(&pic_mem, 1, heathrow_irqs); + pci_bus = pci_grackle_init(0xfec00000, pic, + get_system_memory(), + get_system_io()); + pci_vga_init(pci_bus); + + escc_mem = escc_init(0, pic[0x0f], pic[0x10], serial_hds[0], + serial_hds[1], ESCC_CLOCK, 4); + memory_region_init_alias(escc_bar, "escc-bar", + escc_mem, 0, memory_region_size(escc_mem)); + + for(i = 0; i < nb_nics; i++) + pci_nic_init_nofail(&nd_table[i], "ne2k_pci", NULL); + + + ide_drive_get(hd, MAX_IDE_BUS); + + macio = pci_create(pci_bus, -1, TYPE_OLDWORLD_MACIO); + dev = DEVICE(macio); + qdev_connect_gpio_out(dev, 0, pic[0x12]); /* CUDA */ + qdev_connect_gpio_out(dev, 1, pic[0x0D]); /* IDE */ + qdev_connect_gpio_out(dev, 2, pic[0x02]); /* IDE DMA */ + macio_init(macio, pic_mem, escc_bar); + + /* First IDE channel is a MAC IDE on the MacIO bus */ + macio_ide = MACIO_IDE(object_resolve_path_component(OBJECT(macio), + "ide")); + macio_ide_init_drives(macio_ide, hd); + + /* Second IDE channel is a CMD646 on the PCI bus */ + hd[0] = hd[MAX_IDE_DEVS]; + hd[1] = hd[MAX_IDE_DEVS + 1]; + hd[3] = hd[2] = NULL; + pci_cmd646_ide_init(pci_bus, hd, 0); + + dev = DEVICE(object_resolve_path_component(OBJECT(macio), "cuda")); + adb_bus = qdev_get_child_bus(dev, "adb.0"); + dev = qdev_create(adb_bus, TYPE_ADB_KEYBOARD); + qdev_init_nofail(dev); + dev = qdev_create(adb_bus, TYPE_ADB_MOUSE); + qdev_init_nofail(dev); + + if (usb_enabled(false)) { + pci_create_simple(pci_bus, -1, "pci-ohci"); + } + + if (graphic_depth != 15 && graphic_depth != 32 && graphic_depth != 8) + graphic_depth = 15; + + /* No PCI init: the BIOS will do it */ + + fw_cfg = fw_cfg_init(0, 0, CFG_ADDR, CFG_ADDR + 2); + fw_cfg_add_i16(fw_cfg, FW_CFG_MAX_CPUS, (uint16_t)max_cpus); + fw_cfg_add_i32(fw_cfg, FW_CFG_ID, 1); + fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)ram_size); + fw_cfg_add_i16(fw_cfg, FW_CFG_MACHINE_ID, ARCH_HEATHROW); + fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ADDR, kernel_base); + fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_SIZE, kernel_size); + if (kernel_cmdline) { + fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_CMDLINE, cmdline_base); + pstrcpy_targphys("cmdline", cmdline_base, TARGET_PAGE_SIZE, kernel_cmdline); + } else { + fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_CMDLINE, 0); + } + fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_ADDR, initrd_base); + fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_SIZE, initrd_size); + fw_cfg_add_i16(fw_cfg, FW_CFG_BOOT_DEVICE, ppc_boot_device); + + fw_cfg_add_i16(fw_cfg, FW_CFG_PPC_WIDTH, graphic_width); + fw_cfg_add_i16(fw_cfg, FW_CFG_PPC_HEIGHT, graphic_height); + fw_cfg_add_i16(fw_cfg, FW_CFG_PPC_DEPTH, graphic_depth); + + fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_IS_KVM, kvm_enabled()); + if (kvm_enabled()) { +#ifdef CONFIG_KVM + uint8_t *hypercall; + + fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_TBFREQ, kvmppc_get_tbfreq()); + hypercall = g_malloc(16); + kvmppc_get_hypercall(env, hypercall, 16); + fw_cfg_add_bytes(fw_cfg, FW_CFG_PPC_KVM_HC, hypercall, 16); + fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_KVM_PID, getpid()); +#endif + } else { + fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_TBFREQ, get_ticks_per_sec()); + } + + qemu_register_boot_set(fw_cfg_boot_set, fw_cfg); +} + +static QEMUMachine heathrow_machine = { + .name = "g3beige", + .desc = "Heathrow based PowerMAC", + .init = ppc_heathrow_init, + .max_cpus = MAX_CPUS, +#ifndef TARGET_PPC64 + .is_default = 1, +#endif + DEFAULT_MACHINE_OPTIONS, +}; + +static void heathrow_machine_init(void) +{ + qemu_register_machine(&heathrow_machine); +} + +machine_init(heathrow_machine_init); diff --git a/hw/ppc/mpc8544ds.c b/hw/ppc/mpc8544ds.c index e651661..e25c70b 100644 --- a/hw/ppc/mpc8544ds.c +++ b/hw/ppc/mpc8544ds.c @@ -13,7 +13,8 @@ #include "qemu-common.h" #include "e500.h" #include "../boards.h" -#include "device_tree.h" +#include "sysemu/device_tree.h" +#include "hw/openpic.h" static void mpc8544ds_fixup_devtree(PPCE500Params *params, void *fdt) { @@ -40,7 +41,10 @@ static void mpc8544ds_init(QEMUMachineInitArgs *args) .kernel_cmdline = kernel_cmdline, .initrd_filename = initrd_filename, .cpu_model = cpu_model, + .pci_first_slot = 0x11, + .pci_nr_slots = 2, .fixup_devtree = mpc8544ds_fixup_devtree, + .mpic_version = OPENPIC_MODEL_FSL_MPIC_20, }; ppce500_init(¶ms); @@ -52,6 +56,7 @@ static QEMUMachine ppce500_machine = { .desc = "mpc8544ds", .init = mpc8544ds_init, .max_cpus = 15, + DEFAULT_MACHINE_OPTIONS, }; static void ppce500_machine_init(void) diff --git a/hw/ppc/prep.c b/hw/ppc/prep.c new file mode 100644 index 0000000..e06dded --- /dev/null +++ b/hw/ppc/prep.c @@ -0,0 +1,680 @@ +/* + * QEMU PPC PREP hardware System Emulator + * + * Copyright (c) 2003-2007 Jocelyn Mayer + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "hw/hw.h" +#include "hw/nvram.h" +#include "hw/pc.h" +#include "hw/serial.h" +#include "hw/fdc.h" +#include "net/net.h" +#include "sysemu/sysemu.h" +#include "hw/isa.h" +#include "hw/pci/pci.h" +#include "hw/pci/pci_host.h" +#include "hw/ppc.h" +#include "hw/boards.h" +#include "qemu/log.h" +#include "hw/ide.h" +#include "hw/loader.h" +#include "hw/mc146818rtc.h" +#include "hw/pc87312.h" +#include "sysemu/blockdev.h" +#include "sysemu/arch_init.h" +#include "exec/address-spaces.h" + +//#define HARD_DEBUG_PPC_IO +//#define DEBUG_PPC_IO + +/* SMP is not enabled, for now */ +#define MAX_CPUS 1 + +#define MAX_IDE_BUS 2 + +#define BIOS_SIZE (1024 * 1024) +#define BIOS_FILENAME "ppc_rom.bin" +#define KERNEL_LOAD_ADDR 0x01000000 +#define INITRD_LOAD_ADDR 0x01800000 + +#if defined (HARD_DEBUG_PPC_IO) && !defined (DEBUG_PPC_IO) +#define DEBUG_PPC_IO +#endif + +#if defined (HARD_DEBUG_PPC_IO) +#define PPC_IO_DPRINTF(fmt, ...) \ +do { \ + if (qemu_loglevel_mask(CPU_LOG_IOPORT)) { \ + qemu_log("%s: " fmt, __func__ , ## __VA_ARGS__); \ + } else { \ + printf("%s : " fmt, __func__ , ## __VA_ARGS__); \ + } \ +} while (0) +#elif defined (DEBUG_PPC_IO) +#define PPC_IO_DPRINTF(fmt, ...) \ +qemu_log_mask(CPU_LOG_IOPORT, fmt, ## __VA_ARGS__) +#else +#define PPC_IO_DPRINTF(fmt, ...) do { } while (0) +#endif + +/* Constants for devices init */ +static const int ide_iobase[2] = { 0x1f0, 0x170 }; +static const int ide_iobase2[2] = { 0x3f6, 0x376 }; +static const int ide_irq[2] = { 13, 13 }; + +#define NE2000_NB_MAX 6 + +static uint32_t ne2000_io[NE2000_NB_MAX] = { 0x300, 0x320, 0x340, 0x360, 0x280, 0x380 }; +static int ne2000_irq[NE2000_NB_MAX] = { 9, 10, 11, 3, 4, 5 }; + +/* ISA IO ports bridge */ +#define PPC_IO_BASE 0x80000000 + +/* PowerPC control and status registers */ +#if 0 // Not used +static struct { + /* IDs */ + uint32_t veni_devi; + uint32_t revi; + /* Control and status */ + uint32_t gcsr; + uint32_t xcfr; + uint32_t ct32; + uint32_t mcsr; + /* General purpose registers */ + uint32_t gprg[6]; + /* Exceptions */ + uint32_t feen; + uint32_t fest; + uint32_t fema; + uint32_t fecl; + uint32_t eeen; + uint32_t eest; + uint32_t eecl; + uint32_t eeint; + uint32_t eemck0; + uint32_t eemck1; + /* Error diagnostic */ +} XCSR; + +static void PPC_XCSR_writeb (void *opaque, + hwaddr addr, uint32_t value) +{ + printf("%s: 0x" TARGET_FMT_plx " => 0x%08" PRIx32 "\n", __func__, addr, + value); +} + +static void PPC_XCSR_writew (void *opaque, + hwaddr addr, uint32_t value) +{ + printf("%s: 0x" TARGET_FMT_plx " => 0x%08" PRIx32 "\n", __func__, addr, + value); +} + +static void PPC_XCSR_writel (void *opaque, + hwaddr addr, uint32_t value) +{ + printf("%s: 0x" TARGET_FMT_plx " => 0x%08" PRIx32 "\n", __func__, addr, + value); +} + +static uint32_t PPC_XCSR_readb (void *opaque, hwaddr addr) +{ + uint32_t retval = 0; + + printf("%s: 0x" TARGET_FMT_plx " <= %08" PRIx32 "\n", __func__, addr, + retval); + + return retval; +} + +static uint32_t PPC_XCSR_readw (void *opaque, hwaddr addr) +{ + uint32_t retval = 0; + + printf("%s: 0x" TARGET_FMT_plx " <= %08" PRIx32 "\n", __func__, addr, + retval); + + return retval; +} + +static uint32_t PPC_XCSR_readl (void *opaque, hwaddr addr) +{ + uint32_t retval = 0; + + printf("%s: 0x" TARGET_FMT_plx " <= %08" PRIx32 "\n", __func__, addr, + retval); + + return retval; +} + +static const MemoryRegionOps PPC_XCSR_ops = { + .old_mmio = { + .read = { PPC_XCSR_readb, PPC_XCSR_readw, PPC_XCSR_readl, }, + .write = { PPC_XCSR_writeb, PPC_XCSR_writew, PPC_XCSR_writel, }, + }, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +#endif + +/* Fake super-io ports for PREP platform (Intel 82378ZB) */ +typedef struct sysctrl_t { + qemu_irq reset_irq; + M48t59State *nvram; + uint8_t state; + uint8_t syscontrol; + int contiguous_map; + int endian; +} sysctrl_t; + +enum { + STATE_HARDFILE = 0x01, +}; + +static sysctrl_t *sysctrl; + +static void PREP_io_800_writeb (void *opaque, uint32_t addr, uint32_t val) +{ + sysctrl_t *sysctrl = opaque; + + PPC_IO_DPRINTF("0x%08" PRIx32 " => 0x%02" PRIx32 "\n", + addr - PPC_IO_BASE, val); + switch (addr) { + case 0x0092: + /* Special port 92 */ + /* Check soft reset asked */ + if (val & 0x01) { + qemu_irq_raise(sysctrl->reset_irq); + } else { + qemu_irq_lower(sysctrl->reset_irq); + } + /* Check LE mode */ + if (val & 0x02) { + sysctrl->endian = 1; + } else { + sysctrl->endian = 0; + } + break; + case 0x0800: + /* Motorola CPU configuration register : read-only */ + break; + case 0x0802: + /* Motorola base module feature register : read-only */ + break; + case 0x0803: + /* Motorola base module status register : read-only */ + break; + case 0x0808: + /* Hardfile light register */ + if (val & 1) + sysctrl->state |= STATE_HARDFILE; + else + sysctrl->state &= ~STATE_HARDFILE; + break; + case 0x0810: + /* Password protect 1 register */ + if (sysctrl->nvram != NULL) + m48t59_toggle_lock(sysctrl->nvram, 1); + break; + case 0x0812: + /* Password protect 2 register */ + if (sysctrl->nvram != NULL) + m48t59_toggle_lock(sysctrl->nvram, 2); + break; + case 0x0814: + /* L2 invalidate register */ + // tlb_flush(first_cpu, 1); + break; + case 0x081C: + /* system control register */ + sysctrl->syscontrol = val & 0x0F; + break; + case 0x0850: + /* I/O map type register */ + sysctrl->contiguous_map = val & 0x01; + break; + default: + printf("ERROR: unaffected IO port write: %04" PRIx32 + " => %02" PRIx32"\n", addr, val); + break; + } +} + +static uint32_t PREP_io_800_readb (void *opaque, uint32_t addr) +{ + sysctrl_t *sysctrl = opaque; + uint32_t retval = 0xFF; + + switch (addr) { + case 0x0092: + /* Special port 92 */ + retval = 0x00; + break; + case 0x0800: + /* Motorola CPU configuration register */ + retval = 0xEF; /* MPC750 */ + break; + case 0x0802: + /* Motorola Base module feature register */ + retval = 0xAD; /* No ESCC, PMC slot neither ethernet */ + break; + case 0x0803: + /* Motorola base module status register */ + retval = 0xE0; /* Standard MPC750 */ + break; + case 0x080C: + /* Equipment present register: + * no L2 cache + * no upgrade processor + * no cards in PCI slots + * SCSI fuse is bad + */ + retval = 0x3C; + break; + case 0x0810: + /* Motorola base module extended feature register */ + retval = 0x39; /* No USB, CF and PCI bridge. NVRAM present */ + break; + case 0x0814: + /* L2 invalidate: don't care */ + break; + case 0x0818: + /* Keylock */ + retval = 0x00; + break; + case 0x081C: + /* system control register + * 7 - 6 / 1 - 0: L2 cache enable + */ + retval = sysctrl->syscontrol; + break; + case 0x0823: + /* */ + retval = 0x03; /* no L2 cache */ + break; + case 0x0850: + /* I/O map type register */ + retval = sysctrl->contiguous_map; + break; + default: + printf("ERROR: unaffected IO port: %04" PRIx32 " read\n", addr); + break; + } + PPC_IO_DPRINTF("0x%08" PRIx32 " <= 0x%02" PRIx32 "\n", + addr - PPC_IO_BASE, retval); + + return retval; +} + +static inline hwaddr prep_IO_address(sysctrl_t *sysctrl, + hwaddr addr) +{ + if (sysctrl->contiguous_map == 0) { + /* 64 KB contiguous space for IOs */ + addr &= 0xFFFF; + } else { + /* 8 MB non-contiguous space for IOs */ + addr = (addr & 0x1F) | ((addr & 0x007FFF000) >> 7); + } + + return addr; +} + +static void PPC_prep_io_writeb (void *opaque, hwaddr addr, + uint32_t value) +{ + sysctrl_t *sysctrl = opaque; + + addr = prep_IO_address(sysctrl, addr); + cpu_outb(addr, value); +} + +static uint32_t PPC_prep_io_readb (void *opaque, hwaddr addr) +{ + sysctrl_t *sysctrl = opaque; + uint32_t ret; + + addr = prep_IO_address(sysctrl, addr); + ret = cpu_inb(addr); + + return ret; +} + +static void PPC_prep_io_writew (void *opaque, hwaddr addr, + uint32_t value) +{ + sysctrl_t *sysctrl = opaque; + + addr = prep_IO_address(sysctrl, addr); + PPC_IO_DPRINTF("0x" TARGET_FMT_plx " => 0x%08" PRIx32 "\n", addr, value); + cpu_outw(addr, value); +} + +static uint32_t PPC_prep_io_readw (void *opaque, hwaddr addr) +{ + sysctrl_t *sysctrl = opaque; + uint32_t ret; + + addr = prep_IO_address(sysctrl, addr); + ret = cpu_inw(addr); + PPC_IO_DPRINTF("0x" TARGET_FMT_plx " <= 0x%08" PRIx32 "\n", addr, ret); + + return ret; +} + +static void PPC_prep_io_writel (void *opaque, hwaddr addr, + uint32_t value) +{ + sysctrl_t *sysctrl = opaque; + + addr = prep_IO_address(sysctrl, addr); + PPC_IO_DPRINTF("0x" TARGET_FMT_plx " => 0x%08" PRIx32 "\n", addr, value); + cpu_outl(addr, value); +} + +static uint32_t PPC_prep_io_readl (void *opaque, hwaddr addr) +{ + sysctrl_t *sysctrl = opaque; + uint32_t ret; + + addr = prep_IO_address(sysctrl, addr); + ret = cpu_inl(addr); + PPC_IO_DPRINTF("0x" TARGET_FMT_plx " <= 0x%08" PRIx32 "\n", addr, ret); + + return ret; +} + +static const MemoryRegionOps PPC_prep_io_ops = { + .old_mmio = { + .read = { PPC_prep_io_readb, PPC_prep_io_readw, PPC_prep_io_readl }, + .write = { PPC_prep_io_writeb, PPC_prep_io_writew, PPC_prep_io_writel }, + }, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +#define NVRAM_SIZE 0x2000 + +static void cpu_request_exit(void *opaque, int irq, int level) +{ + CPUPPCState *env = cpu_single_env; + + if (env && level) { + cpu_exit(env); + } +} + +static void ppc_prep_reset(void *opaque) +{ + PowerPCCPU *cpu = opaque; + + cpu_reset(CPU(cpu)); +} + +/* PowerPC PREP hardware initialisation */ +static void ppc_prep_init(QEMUMachineInitArgs *args) +{ + ram_addr_t ram_size = args->ram_size; + const char *cpu_model = args->cpu_model; + const char *kernel_filename = args->kernel_filename; + const char *kernel_cmdline = args->kernel_cmdline; + const char *initrd_filename = args->initrd_filename; + const char *boot_device = args->boot_device; + MemoryRegion *sysmem = get_system_memory(); + PowerPCCPU *cpu = NULL; + CPUPPCState *env = NULL; + char *filename; + nvram_t nvram; + M48t59State *m48t59; + MemoryRegion *PPC_io_memory = g_new(MemoryRegion, 1); +#if 0 + MemoryRegion *xcsr = g_new(MemoryRegion, 1); +#endif + int linux_boot, i, nb_nics1, bios_size; + MemoryRegion *ram = g_new(MemoryRegion, 1); + MemoryRegion *bios = g_new(MemoryRegion, 1); + uint32_t kernel_base, initrd_base; + long kernel_size, initrd_size; + DeviceState *dev; + PCIHostState *pcihost; + PCIBus *pci_bus; + PCIDevice *pci; + ISABus *isa_bus; + ISADevice *isa; + qemu_irq *cpu_exit_irq; + int ppc_boot_device; + DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; + + sysctrl = g_malloc0(sizeof(sysctrl_t)); + + linux_boot = (kernel_filename != NULL); + + /* init CPUs */ + if (cpu_model == NULL) + cpu_model = "602"; + for (i = 0; i < smp_cpus; i++) { + cpu = cpu_ppc_init(cpu_model); + if (cpu == NULL) { + fprintf(stderr, "Unable to find PowerPC CPU definition\n"); + exit(1); + } + env = &cpu->env; + + if (env->flags & POWERPC_FLAG_RTC_CLK) { + /* POWER / PowerPC 601 RTC clock frequency is 7.8125 MHz */ + cpu_ppc_tb_init(env, 7812500UL); + } else { + /* Set time-base frequency to 100 Mhz */ + cpu_ppc_tb_init(env, 100UL * 1000UL * 1000UL); + } + qemu_register_reset(ppc_prep_reset, cpu); + } + + /* allocate RAM */ + memory_region_init_ram(ram, "ppc_prep.ram", ram_size); + vmstate_register_ram_global(ram); + memory_region_add_subregion(sysmem, 0, ram); + + /* allocate and load BIOS */ + memory_region_init_ram(bios, "ppc_prep.bios", BIOS_SIZE); + memory_region_set_readonly(bios, true); + memory_region_add_subregion(sysmem, (uint32_t)(-BIOS_SIZE), bios); + vmstate_register_ram_global(bios); + 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) { + hwaddr bios_addr; + bios_size = (bios_size + 0xfff) & ~0xfff; + bios_addr = (uint32_t)(-bios_size); + bios_size = load_image_targphys(filename, bios_addr, bios_size); + } + if (bios_size < 0 || bios_size > BIOS_SIZE) { + hw_error("qemu: could not load PPC PREP bios '%s'\n", bios_name); + } + if (filename) { + g_free(filename); + } + + if (linux_boot) { + kernel_base = KERNEL_LOAD_ADDR; + /* now we can load the kernel */ + kernel_size = load_image_targphys(kernel_filename, kernel_base, + ram_size - kernel_base); + if (kernel_size < 0) { + hw_error("qemu: could not load kernel '%s'\n", kernel_filename); + exit(1); + } + /* load initrd */ + if (initrd_filename) { + initrd_base = INITRD_LOAD_ADDR; + initrd_size = load_image_targphys(initrd_filename, initrd_base, + ram_size - initrd_base); + if (initrd_size < 0) { + hw_error("qemu: could not load initial ram disk '%s'\n", + initrd_filename); + } + } else { + initrd_base = 0; + initrd_size = 0; + } + ppc_boot_device = 'm'; + } else { + kernel_base = 0; + kernel_size = 0; + initrd_base = 0; + initrd_size = 0; + ppc_boot_device = '\0'; + /* For now, OHW cannot boot from the network. */ + for (i = 0; boot_device[i] != '\0'; i++) { + if (boot_device[i] >= 'a' && boot_device[i] <= 'f') { + ppc_boot_device = boot_device[i]; + break; + } + } + if (ppc_boot_device == '\0') { + fprintf(stderr, "No valid boot device for Mac99 machine\n"); + exit(1); + } + } + + if (PPC_INPUT(env) != PPC_FLAGS_INPUT_6xx) { + hw_error("Only 6xx bus is supported on PREP machine\n"); + } + + dev = qdev_create(NULL, "raven-pcihost"); + pcihost = PCI_HOST_BRIDGE(dev); + pcihost->address_space = get_system_memory(); + object_property_add_child(qdev_get_machine(), "raven", OBJECT(dev), NULL); + qdev_init_nofail(dev); + pci_bus = (PCIBus *)qdev_get_child_bus(dev, "pci.0"); + if (pci_bus == NULL) { + fprintf(stderr, "Couldn't create PCI host controller.\n"); + exit(1); + } + + /* PCI -> ISA bridge */ + pci = pci_create_simple(pci_bus, PCI_DEVFN(1, 0), "i82378"); + cpu_exit_irq = qemu_allocate_irqs(cpu_request_exit, NULL, 1); + qdev_connect_gpio_out(&pci->qdev, 0, + first_cpu->irq_inputs[PPC6xx_INPUT_INT]); + qdev_connect_gpio_out(&pci->qdev, 1, *cpu_exit_irq); + sysbus_connect_irq(&pcihost->busdev, 0, qdev_get_gpio_in(&pci->qdev, 9)); + sysbus_connect_irq(&pcihost->busdev, 1, qdev_get_gpio_in(&pci->qdev, 11)); + sysbus_connect_irq(&pcihost->busdev, 2, qdev_get_gpio_in(&pci->qdev, 9)); + sysbus_connect_irq(&pcihost->busdev, 3, qdev_get_gpio_in(&pci->qdev, 11)); + isa_bus = DO_UPCAST(ISABus, qbus, qdev_get_child_bus(&pci->qdev, "isa.0")); + + /* Super I/O (parallel + serial ports) */ + isa = isa_create(isa_bus, TYPE_PC87312); + qdev_prop_set_uint8(&isa->qdev, "config", 13); /* fdc, ser0, ser1, par0 */ + qdev_init_nofail(&isa->qdev); + + /* Register 8 MB of ISA IO space (needed for non-contiguous map) */ + memory_region_init_io(PPC_io_memory, &PPC_prep_io_ops, sysctrl, + "ppc-io", 0x00800000); + memory_region_add_subregion(sysmem, 0x80000000, PPC_io_memory); + + /* init basic PC hardware */ + pci_vga_init(pci_bus); + + nb_nics1 = nb_nics; + if (nb_nics1 > NE2000_NB_MAX) + nb_nics1 = NE2000_NB_MAX; + for(i = 0; i < nb_nics1; i++) { + if (nd_table[i].model == NULL) { + nd_table[i].model = g_strdup("ne2k_isa"); + } + if (strcmp(nd_table[i].model, "ne2k_isa") == 0) { + isa_ne2000_init(isa_bus, ne2000_io[i], ne2000_irq[i], + &nd_table[i]); + } else { + pci_nic_init_nofail(&nd_table[i], "ne2k_pci", NULL); + } + } + + ide_drive_get(hd, MAX_IDE_BUS); + for(i = 0; i < MAX_IDE_BUS; i++) { + isa_ide_init(isa_bus, ide_iobase[i], ide_iobase2[i], ide_irq[i], + hd[2 * i], + hd[2 * i + 1]); + } + isa_create_simple(isa_bus, "i8042"); + + sysctrl->reset_irq = first_cpu->irq_inputs[PPC6xx_INPUT_HRESET]; + /* System control ports */ + register_ioport_read(0x0092, 0x01, 1, &PREP_io_800_readb, sysctrl); + register_ioport_write(0x0092, 0x01, 1, &PREP_io_800_writeb, sysctrl); + register_ioport_read(0x0800, 0x52, 1, &PREP_io_800_readb, sysctrl); + register_ioport_write(0x0800, 0x52, 1, &PREP_io_800_writeb, sysctrl); + /* PowerPC control and status register group */ +#if 0 + memory_region_init_io(xcsr, &PPC_XCSR_ops, NULL, "ppc-xcsr", 0x1000); + memory_region_add_subregion(sysmem, 0xFEFF0000, xcsr); +#endif + + if (usb_enabled(false)) { + pci_create_simple(pci_bus, -1, "pci-ohci"); + } + + m48t59 = m48t59_init_isa(isa_bus, 0x0074, NVRAM_SIZE, 59); + if (m48t59 == NULL) + return; + sysctrl->nvram = m48t59; + + /* Initialise NVRAM */ + nvram.opaque = m48t59; + nvram.read_fn = &m48t59_read; + nvram.write_fn = &m48t59_write; + PPC_NVRAM_set_params(&nvram, NVRAM_SIZE, "PREP", ram_size, ppc_boot_device, + kernel_base, kernel_size, + kernel_cmdline, + initrd_base, initrd_size, + /* XXX: need an option to load a NVRAM image */ + 0, + graphic_width, graphic_height, graphic_depth); + + /* Special port to get debug messages from Open-Firmware */ + register_ioport_write(0x0F00, 4, 1, &PPC_debug_write, NULL); + + /* Initialize audio subsystem */ + audio_init(isa_bus, pci_bus); +} + +static QEMUMachine prep_machine = { + .name = "prep", + .desc = "PowerPC PREP platform", + .init = ppc_prep_init, + .max_cpus = MAX_CPUS, + DEFAULT_MACHINE_OPTIONS, +}; + +static void prep_machine_init(void) +{ + qemu_register_machine(&prep_machine); +} + +machine_init(prep_machine_init); |