From 231903bf27c18ba6d4d905601eab368f324159eb Mon Sep 17 00:00:00 2001 From: Alexey Kardashevskiy Date: Wed, 13 Jun 2012 18:38:50 +0000 Subject: pseries pci: removed redundant busdev The PCIHostState struct already contains SysBusDevice so the one in sPAPRPHBState has to go. Signed-off-by: Alexey Kardashevskiy Signed-off-by: Alexander Graf --- hw/spapr_pci.c | 4 ++-- hw/spapr_pci.h | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) (limited to 'hw') diff --git a/hw/spapr_pci.c b/hw/spapr_pci.c index b2e4f78..5bee180 100644 --- a/hw/spapr_pci.c +++ b/hw/spapr_pci.c @@ -276,7 +276,7 @@ static DMAContext *spapr_pci_dma_context_fn(PCIBus *bus, void *opaque, static int spapr_phb_init(SysBusDevice *s) { - sPAPRPHBState *phb = FROM_SYSBUS(sPAPRPHBState, s); + sPAPRPHBState *phb = DO_UPCAST(sPAPRPHBState, host_state.busdev, s); char *namebuf; int i; PCIBus *bus; @@ -314,7 +314,7 @@ static int spapr_phb_init(SysBusDevice *s) memory_region_add_subregion(get_system_memory(), phb->io_win_addr, &phb->iowindow); - bus = pci_register_bus(&phb->busdev.qdev, + bus = pci_register_bus(&phb->host_state.busdev.qdev, phb->busname ? phb->busname : phb->dtbusname, pci_spapr_set_irq, pci_spapr_map_irq, phb, &phb->memspace, &phb->iospace, diff --git a/hw/spapr_pci.h b/hw/spapr_pci.h index d9e46e2..a141764 100644 --- a/hw/spapr_pci.h +++ b/hw/spapr_pci.h @@ -28,7 +28,6 @@ #include "hw/xics.h" typedef struct sPAPRPHBState { - SysBusDevice busdev; PCIHostState host_state; uint64_t buid; -- cgit v1.1 From e0fdbd7c1b45c1af034d4070a65067f6635f77b0 Mon Sep 17 00:00:00 2001 From: Alexey Kardashevskiy Date: Wed, 13 Jun 2012 18:40:06 +0000 Subject: pseries pci: spapr_populate_pci_devices renamed to spapr_populate_pci_dt spapr_populate_pci_devices() populates the device tree only with bus properties and has nothing to do with the devices on it as PCI BAR allocation is done by the system firmware (SLOF). New name - spapr_populate_pci_dt() - describes the functionality better. Signed-off-by: Alexey Kardashevskiy Signed-off-by: Alexander Graf --- hw/spapr.c | 2 +- hw/spapr_pci.c | 6 +++--- hw/spapr_pci.h | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) (limited to 'hw') diff --git a/hw/spapr.c b/hw/spapr.c index 81c9343..ab5a0c2 100644 --- a/hw/spapr.c +++ b/hw/spapr.c @@ -481,7 +481,7 @@ static void spapr_finalize_fdt(sPAPREnvironment *spapr, } QLIST_FOREACH(phb, &spapr->phbs, list) { - ret = spapr_populate_pci_devices(phb, PHANDLE_XICP, fdt); + ret = spapr_populate_pci_dt(phb, PHANDLE_XICP, fdt); } if (ret < 0) { diff --git a/hw/spapr_pci.c b/hw/spapr_pci.c index 5bee180..2913540 100644 --- a/hw/spapr_pci.c +++ b/hw/spapr_pci.c @@ -406,9 +406,9 @@ void spapr_create_phb(sPAPREnvironment *spapr, #define b_fff(x) b_x((x), 8, 3) /* function number */ #define b_rrrrrrrr(x) b_x((x), 0, 8) /* register number */ -int spapr_populate_pci_devices(sPAPRPHBState *phb, - uint32_t xics_phandle, - void *fdt) +int spapr_populate_pci_dt(sPAPRPHBState *phb, + uint32_t xics_phandle, + void *fdt) { int bus_off, i, j; char nodename[256]; diff --git a/hw/spapr_pci.h b/hw/spapr_pci.h index a141764..dd66f4b 100644 --- a/hw/spapr_pci.h +++ b/hw/spapr_pci.h @@ -55,8 +55,8 @@ void spapr_create_phb(sPAPREnvironment *spapr, uint64_t mem_win_addr, uint64_t mem_win_size, uint64_t io_win_addr); -int spapr_populate_pci_devices(sPAPRPHBState *phb, - uint32_t xics_phandle, - void *fdt); +int spapr_populate_pci_dt(sPAPRPHBState *phb, + uint32_t xics_phandle, + void *fdt); #endif /* __HW_SPAPR_PCI_H__ */ -- cgit v1.1 From 4a18e7c92a3c23ee742eda1e4442299b56198517 Mon Sep 17 00:00:00 2001 From: Scott Wood Date: Mon, 2 Jul 2012 13:03:18 +0000 Subject: PPC: e500: rename mpc8544ds into generic file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename the file (with no changes other than fixing up the header paths) in preparation for refactoring into a generic e500 platform. Also move it into the newly created ppc/ directory. Signed-off-by: Scott Wood Reviewed-by: Andreas Färber [agraf: conditionalize on CONFIG_FDT] Signed-off-by: Alexander Graf --- hw/ppc/Makefile.objs | 4 +- hw/ppc/e500.c | 610 +++++++++++++++++++++++++++++++++++++++++++++++++ hw/ppce500_mpc8544ds.c | 610 ------------------------------------------------- 3 files changed, 613 insertions(+), 611 deletions(-) create mode 100644 hw/ppc/e500.c delete mode 100644 hw/ppce500_mpc8544ds.c (limited to 'hw') diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs index aa4bbeb..e86c524 100644 --- a/hw/ppc/Makefile.objs +++ b/hw/ppc/Makefile.objs @@ -15,7 +15,7 @@ obj-$(CONFIG_PSERIES) += spapr_pci.o pci-hotplug.o spapr_iommu.o obj-y += ppc4xx_devs.o ppc4xx_pci.o ppc405_uc.o ppc405_boards.o obj-y += ppc440_bamboo.o # PowerPC E500 boards -obj-$(CONFIG_FDT) += ppce500_mpc8544ds.o mpc8544_guts.o ppce500_spin.o +obj-$(CONFIG_FDT) += mpc8544_guts.o ppce500_spin.o # PowerPC 440 Xilinx ML507 reference board. obj-y += virtex_ml507.o # PowerPC OpenPIC @@ -26,3 +26,5 @@ obj-$(CONFIG_FDT) += ../device_tree.o obj-y += xilinx_ethlite.o obj-y := $(addprefix ../,$(obj-y)) + +obj-$(CONFIG_FDT) += e500.o diff --git a/hw/ppc/e500.c b/hw/ppc/e500.c new file mode 100644 index 0000000..0b383e6 --- /dev/null +++ b/hw/ppc/e500.c @@ -0,0 +1,610 @@ +/* + * QEMU PowerPC MPC8544DS board emulation + * + * Copyright (C) 2009 Freescale Semiconductor, Inc. All rights reserved. + * + * Author: Yu Liu, + * + * This file is derived from hw/ppc440_bamboo.c, + * the copyright for that material belongs to the original owners. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include "config.h" +#include "qemu-common.h" +#include "net.h" +#include "hw/hw.h" +#include "hw/pc.h" +#include "hw/pci.h" +#include "hw/boards.h" +#include "sysemu.h" +#include "kvm.h" +#include "kvm_ppc.h" +#include "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" + +#define BINARY_DEVICE_TREE_FILE "mpc8544ds.dtb" +#define UIMAGE_LOAD_BASE 0 +#define DTC_LOAD_PAD 0x500000 +#define DTC_PAD_MASK 0xFFFFF +#define INITRD_LOAD_PAD 0x2000000 +#define INITRD_PAD_MASK 0xFFFFFF + +#define RAM_SIZES_ALIGN (64UL << 20) + +#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_PCI_REGS_SIZE 0x1000ULL +#define MPC8544_PCI_IO 0xE1000000ULL +#define MPC8544_PCI_IOLEN 0x10000ULL +#define MPC8544_UTIL_BASE (MPC8544_CCSRBAR_BASE + 0xe0000ULL) +#define MPC8544_SPIN_BASE 0xEF000000ULL + +struct boot_info +{ + uint32_t dt_base; + uint32_t dt_size; + uint32_t entry; +}; + +static void pci_map_create(void *fdt, uint32_t *pci_map, uint32_t mpic) +{ + int i; + const uint32_t tmp[] = { + /* IDSEL 0x11 J17 Slot 1 */ + 0x8800, 0x0, 0x0, 0x1, mpic, 0x2, 0x1, 0x0, 0x0, + 0x8800, 0x0, 0x0, 0x2, mpic, 0x3, 0x1, 0x0, 0x0, + 0x8800, 0x0, 0x0, 0x3, mpic, 0x4, 0x1, 0x0, 0x0, + 0x8800, 0x0, 0x0, 0x4, mpic, 0x1, 0x1, 0x0, 0x0, + + /* IDSEL 0x12 J16 Slot 2 */ + 0x9000, 0x0, 0x0, 0x1, mpic, 0x3, 0x1, 0x0, 0x0, + 0x9000, 0x0, 0x0, 0x2, mpic, 0x4, 0x1, 0x0, 0x0, + 0x9000, 0x0, 0x0, 0x3, mpic, 0x2, 0x1, 0x0, 0x0, + 0x9000, 0x0, 0x0, 0x4, mpic, 0x1, 0x1, 0x0, 0x0, + }; + for (i = 0; i < ARRAY_SIZE(tmp); i++) { + pci_map[i] = cpu_to_be32(tmp[i]); + } +} + +static void dt_serial_create(void *fdt, unsigned long long offset, + const char *soc, const char *mpic, + const char *alias, int idx, bool defcon) +{ + char ser[128]; + + snprintf(ser, sizeof(ser), "%s/serial@%llx", soc, offset); + qemu_devtree_add_subnode(fdt, ser); + qemu_devtree_setprop_string(fdt, ser, "device_type", "serial"); + qemu_devtree_setprop_string(fdt, ser, "compatible", "ns16550"); + qemu_devtree_setprop_cells(fdt, ser, "reg", offset, 0x100); + qemu_devtree_setprop_cell(fdt, ser, "cell-index", idx); + qemu_devtree_setprop_cell(fdt, ser, "clock-frequency", 0); + qemu_devtree_setprop_cells(fdt, ser, "interrupts", 42, 2, 0, 0); + qemu_devtree_setprop_phandle(fdt, ser, "interrupt-parent", mpic); + qemu_devtree_setprop_string(fdt, "/aliases", alias, ser); + + if (defcon) { + qemu_devtree_setprop_string(fdt, "/chosen", "linux,stdout-path", ser); + } +} + +static int mpc8544_load_device_tree(CPUPPCState *env, + target_phys_addr_t addr, + target_phys_addr_t ramsize, + target_phys_addr_t initrd_base, + target_phys_addr_t initrd_size, + const char *kernel_cmdline) +{ + int ret = -1; + uint64_t mem_reg_property[] = { 0, cpu_to_be64(ramsize) }; + int fdt_size; + void *fdt; + uint8_t hypercall[16]; + uint32_t clock_freq = 400000000; + uint32_t tb_freq = 400000000; + int i; + const char *compatible = "MPC8544DS\0MPC85xxDS"; + int compatible_len = sizeof("MPC8544DS\0MPC85xxDS"); + char compatible_sb[] = "fsl,mpc8544-immr\0simple-bus"; + char model[] = "MPC8544DS"; + char soc[128]; + char mpic[128]; + uint32_t mpic_ph; + char gutil[128]; + char pci[128]; + uint32_t pci_map[9 * 8]; + uint32_t pci_ranges[14] = + { + 0x2000000, 0x0, 0xc0000000, + 0x0, 0xc0000000, + 0x0, 0x20000000, + + 0x1000000, 0x0, 0x0, + 0x0, 0xe1000000, + 0x0, 0x10000, + }; + QemuOpts *machine_opts; + const char *dumpdtb = NULL; + const char *dtb_file = NULL; + + machine_opts = qemu_opts_find(qemu_find_opts("machine"), 0); + if (machine_opts) { + const char *tmp; + dumpdtb = qemu_opt_get(machine_opts, "dumpdtb"); + dtb_file = qemu_opt_get(machine_opts, "dtb"); + tmp = qemu_opt_get(machine_opts, "dt_compatible"); + if (tmp) { + compatible = tmp; + compatible_len = strlen(compatible) + 1; + } + } + + if (dtb_file) { + char *filename; + filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, dtb_file); + if (!filename) { + goto out; + } + + fdt = load_device_tree(filename, &fdt_size); + if (!fdt) { + goto out; + } + goto done; + } + + fdt = create_device_tree(&fdt_size); + if (fdt == NULL) { + goto out; + } + + /* Manipulate device tree in memory. */ + qemu_devtree_setprop_string(fdt, "/", "model", model); + qemu_devtree_setprop(fdt, "/", "compatible", compatible, compatible_len); + qemu_devtree_setprop_cell(fdt, "/", "#address-cells", 2); + qemu_devtree_setprop_cell(fdt, "/", "#size-cells", 2); + + qemu_devtree_add_subnode(fdt, "/memory"); + qemu_devtree_setprop_string(fdt, "/memory", "device_type", "memory"); + qemu_devtree_setprop(fdt, "/memory", "reg", mem_reg_property, + sizeof(mem_reg_property)); + + qemu_devtree_add_subnode(fdt, "/chosen"); + if (initrd_size) { + ret = qemu_devtree_setprop_cell(fdt, "/chosen", "linux,initrd-start", + initrd_base); + if (ret < 0) { + fprintf(stderr, "couldn't set /chosen/linux,initrd-start\n"); + } + + ret = qemu_devtree_setprop_cell(fdt, "/chosen", "linux,initrd-end", + (initrd_base + initrd_size)); + if (ret < 0) { + fprintf(stderr, "couldn't set /chosen/linux,initrd-end\n"); + } + } + + ret = qemu_devtree_setprop_string(fdt, "/chosen", "bootargs", + kernel_cmdline); + if (ret < 0) + fprintf(stderr, "couldn't set /chosen/bootargs\n"); + + if (kvm_enabled()) { + /* Read out host's frequencies */ + clock_freq = kvmppc_get_clockfreq(); + tb_freq = kvmppc_get_tbfreq(); + + /* indicate KVM hypercall interface */ + qemu_devtree_add_subnode(fdt, "/hypervisor"); + qemu_devtree_setprop_string(fdt, "/hypervisor", "compatible", + "linux,kvm"); + kvmppc_get_hypercall(env, hypercall, sizeof(hypercall)); + qemu_devtree_setprop(fdt, "/hypervisor", "hcall-instructions", + hypercall, sizeof(hypercall)); + } + + /* Create CPU nodes */ + qemu_devtree_add_subnode(fdt, "/cpus"); + qemu_devtree_setprop_cell(fdt, "/cpus", "#address-cells", 1); + qemu_devtree_setprop_cell(fdt, "/cpus", "#size-cells", 0); + + /* 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--) { + 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) { + break; + } + } + + if (!env) { + continue; + } + + snprintf(cpu_name, sizeof(cpu_name), "/cpus/PowerPC,8544@%x", env->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, "d-cache-line-size", + env->dcache_line_size); + qemu_devtree_setprop_cell(fdt, cpu_name, "i-cache-line-size", + env->icache_line_size); + 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) { + 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", + cpu_release_addr); + } else { + qemu_devtree_setprop_string(fdt, cpu_name, "status", "okay"); + } + } + + qemu_devtree_add_subnode(fdt, "/aliases"); + /* XXX These should go into their respective devices' code */ + snprintf(soc, sizeof(soc), "/soc@%llx", MPC8544_CCSRBAR_BASE); + qemu_devtree_add_subnode(fdt, soc); + qemu_devtree_setprop_string(fdt, soc, "device_type", "soc"); + qemu_devtree_setprop(fdt, soc, "compatible", compatible_sb, + sizeof(compatible_sb)); + qemu_devtree_setprop_cell(fdt, soc, "#address-cells", 1); + qemu_devtree_setprop_cell(fdt, soc, "#size-cells", 1); + qemu_devtree_setprop_cells(fdt, soc, "ranges", 0x0, + MPC8544_CCSRBAR_BASE >> 32, MPC8544_CCSRBAR_BASE, + MPC8544_CCSRBAR_SIZE); + /* 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); + qemu_devtree_add_subnode(fdt, mpic); + qemu_devtree_setprop_string(fdt, mpic, "device_type", "open-pic"); + qemu_devtree_setprop_string(fdt, mpic, "compatible", "fsl,mpic"); + qemu_devtree_setprop_cells(fdt, mpic, "reg", MPC8544_MPIC_REGS_BASE - + MPC8544_CCSRBAR_BASE, 0x40000); + qemu_devtree_setprop_cell(fdt, mpic, "#address-cells", 0); + qemu_devtree_setprop_cell(fdt, mpic, "#interrupt-cells", 4); + mpic_ph = qemu_devtree_alloc_phandle(fdt); + qemu_devtree_setprop_cell(fdt, mpic, "phandle", mpic_ph); + qemu_devtree_setprop_cell(fdt, mpic, "linux,phandle", mpic_ph); + qemu_devtree_setprop(fdt, mpic, "interrupt-controller", NULL, 0); + qemu_devtree_setprop(fdt, mpic, "big-endian", NULL, 0); + qemu_devtree_setprop(fdt, mpic, "single-cpu-affinity", NULL, 0); + qemu_devtree_setprop_cell(fdt, mpic, "last-interrupt-source", 255); + + /* + * We have to generate ser1 first, because Linux takes the first + * 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, + soc, mpic, "serial1", 1, false); + dt_serial_create(fdt, MPC8544_SERIAL0_REGS_BASE - MPC8544_CCSRBAR_BASE, + soc, mpic, "serial0", 0, true); + + snprintf(gutil, sizeof(gutil), "%s/global-utilities@%llx", soc, + MPC8544_UTIL_BASE - MPC8544_CCSRBAR_BASE); + 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(fdt, gutil, "fsl,has-rstcr", NULL, 0); + + 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); + qemu_devtree_setprop_string(fdt, pci, "compatible", "fsl,mpc8540-pci"); + 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)); + qemu_devtree_setprop_phandle(fdt, pci, "interrupt-parent", mpic); + qemu_devtree_setprop_cells(fdt, pci, "interrupts", 24, 2, 0, 0); + 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(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); + qemu_devtree_setprop_cell(fdt, pci, "clock-frequency", 66666666); + qemu_devtree_setprop_cell(fdt, pci, "#interrupt-cells", 1); + qemu_devtree_setprop_cell(fdt, pci, "#size-cells", 2); + qemu_devtree_setprop_cell(fdt, pci, "#address-cells", 3); + qemu_devtree_setprop_string(fdt, "/aliases", "pci0", pci); + +done: + if (dumpdtb) { + /* Dump the dtb to a file and quit */ + FILE *f = fopen(dumpdtb, "wb"); + size_t len; + len = fwrite(fdt, fdt_size, 1, f); + fclose(f); + if (len != fdt_size) { + exit(1); + } + exit(0); + } + + ret = rom_add_blob_fixed(BINARY_DEVICE_TREE_FILE, fdt, fdt_size, addr); + if (ret < 0) { + goto out; + } + g_free(fdt); + ret = fdt_size; + +out: + + return ret; +} + +/* Create -kernel TLB entries for BookE. */ +static inline target_phys_addr_t booke206_page_size_to_tlb(uint64_t size) +{ + return 63 - clz64(size >> 10); +} + +static void mmubooke_create_initial_mapping(CPUPPCState *env) +{ + struct boot_info *bi = env->load_info; + ppcmas_tlb_t *tlb = booke206_get_tlbm(env, 1, 0, 0); + target_phys_addr_t size, dt_end; + int ps; + + /* Our initial TLB entry needs to cover everything from 0 to + the device tree top */ + dt_end = bi->dt_base + bi->dt_size; + ps = booke206_page_size_to_tlb(dt_end) + 1; + size = (ps << MAS1_TSIZE_SHIFT); + tlb->mas1 = MAS1_VALID | size; + tlb->mas2 = 0; + tlb->mas7_3 = 0; + tlb->mas7_3 |= MAS3_UR | MAS3_UW | MAS3_UX | MAS3_SR | MAS3_SW | MAS3_SX; + + env->tlb_dirty = true; +} + +static void mpc8544ds_cpu_reset_sec(void *opaque) +{ + PowerPCCPU *cpu = opaque; + CPUPPCState *env = &cpu->env; + + cpu_reset(CPU(cpu)); + + /* Secondary CPU starts in halted state for now. Needs to change when + implementing non-kernel boot. */ + env->halted = 1; + env->exception_index = EXCP_HLT; +} + +static void mpc8544ds_cpu_reset(void *opaque) +{ + PowerPCCPU *cpu = opaque; + CPUPPCState *env = &cpu->env; + struct boot_info *bi = env->load_info; + + cpu_reset(CPU(cpu)); + + /* Set initial guest state. */ + env->halted = 0; + env->gpr[1] = (16<<20) - 8; + env->gpr[3] = bi->dt_base; + env->nip = bi->entry; + mmubooke_create_initial_mapping(env); +} + +static void mpc8544ds_init(ram_addr_t ram_size, + const char *boot_device, + const char *kernel_filename, + const char *kernel_cmdline, + const char *initrd_filename, + const char *cpu_model) +{ + MemoryRegion *address_space_mem = get_system_memory(); + MemoryRegion *ram = g_new(MemoryRegion, 1); + PCIBus *pci_bus; + CPUPPCState *env = NULL; + uint64_t elf_entry; + uint64_t elf_lowaddr; + target_phys_addr_t entry=0; + target_phys_addr_t loadaddr=UIMAGE_LOAD_BASE; + target_long kernel_size=0; + target_ulong dt_base = 0; + target_ulong initrd_base = 0; + target_long initrd_size=0; + int i=0; + unsigned int pci_irq_nrs[4] = {1, 2, 3, 4}; + qemu_irq **irqs, *mpic; + DeviceState *dev; + CPUPPCState *firstenv = NULL; + + /* Setup CPUs */ + if (cpu_model == NULL) { + cpu_model = "e500v2_v30"; + } + + irqs = g_malloc0(smp_cpus * sizeof(qemu_irq *)); + irqs[0] = g_malloc0(smp_cpus * sizeof(qemu_irq) * OPENPIC_OUTPUT_NB); + for (i = 0; i < smp_cpus; i++) { + PowerPCCPU *cpu; + qemu_irq *input; + + cpu = cpu_ppc_init(cpu_model); + if (cpu == NULL) { + fprintf(stderr, "Unable to initialize CPU!\n"); + exit(1); + } + env = &cpu->env; + + if (!firstenv) { + firstenv = env; + } + + irqs[i] = irqs[0] + (i * OPENPIC_OUTPUT_NB); + 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; + + ppc_booke_timers_init(env, 400000000, PPC_TIMER_E500); + + /* Register reset handler */ + if (!i) { + /* Primary CPU */ + struct boot_info *boot_info; + boot_info = g_malloc0(sizeof(struct boot_info)); + qemu_register_reset(mpc8544ds_cpu_reset, cpu); + env->load_info = boot_info; + } else { + /* Secondary CPUs */ + qemu_register_reset(mpc8544ds_cpu_reset_sec, cpu); + } + } + + env = firstenv; + + /* Fixup Memory size on a alignment boundary */ + ram_size &= ~(RAM_SIZES_ALIGN - 1); + + /* Register Memory */ + memory_region_init_ram(ram, "mpc8544ds.ram", ram_size); + vmstate_register_ram_global(ram); + memory_region_add_subregion(address_space_mem, 0, ram); + + /* MPIC */ + mpic = mpic_init(address_space_mem, MPC8544_MPIC_REGS_BASE, + smp_cpus, irqs, NULL); + + if (!mpic) { + cpu_abort(env, "MPIC failed to initialize\n"); + } + + /* Serial */ + if (serial_hds[0]) { + serial_mm_init(address_space_mem, MPC8544_SERIAL0_REGS_BASE, + 0, mpic[12+26], 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_hds[0], DEVICE_BIG_ENDIAN); + } + + /* General Utility device */ + sysbus_create_simple("mpc8544-guts", MPC8544_UTIL_BASE, NULL); + + /* 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); + pci_bus = (PCIBus *)qdev_get_child_bus(dev, "pci.0"); + if (!pci_bus) + printf("couldn't create PCI controller!\n"); + + isa_mmio_init(MPC8544_PCI_IO, MPC8544_PCI_IOLEN); + + if (pci_bus) { + /* Register network interfaces. */ + for (i = 0; i < nb_nics; i++) { + pci_nic_init_nofail(&nd_table[i], "virtio", NULL); + } + } + + /* Register spinning region */ + sysbus_create_simple("e500-spin", MPC8544_SPIN_BASE, NULL); + + /* Load kernel. */ + if (kernel_filename) { + kernel_size = load_uimage(kernel_filename, &entry, &loadaddr, NULL); + if (kernel_size < 0) { + kernel_size = load_elf(kernel_filename, NULL, NULL, &elf_entry, + &elf_lowaddr, NULL, 1, ELF_MACHINE, 0); + entry = elf_entry; + loadaddr = elf_lowaddr; + } + /* XXX try again as binary */ + if (kernel_size < 0) { + fprintf(stderr, "qemu: could not load kernel '%s'\n", + kernel_filename); + exit(1); + } + } + + /* Load initrd. */ + if (initrd_filename) { + initrd_base = (kernel_size + INITRD_LOAD_PAD) & ~INITRD_PAD_MASK; + initrd_size = load_image_targphys(initrd_filename, initrd_base, + ram_size - initrd_base); + + if (initrd_size < 0) { + fprintf(stderr, "qemu: could not load initial ram disk '%s'\n", + initrd_filename); + exit(1); + } + } + + /* If we're loading a kernel directly, we must load the device tree too. */ + if (kernel_filename) { + struct boot_info *boot_info; + int dt_size; + + dt_base = (loadaddr + kernel_size + DTC_LOAD_PAD) & ~DTC_PAD_MASK; + dt_size = mpc8544_load_device_tree(env, dt_base, ram_size, initrd_base, + initrd_size, kernel_cmdline); + if (dt_size < 0) { + fprintf(stderr, "couldn't load device tree\n"); + exit(1); + } + + boot_info = env->load_info; + boot_info->entry = entry; + boot_info->dt_base = dt_base; + boot_info->dt_size = dt_size; + } + + if (kvm_enabled()) { + kvmppc_init(); + } +} + +static QEMUMachine mpc8544ds_machine = { + .name = "mpc8544ds", + .desc = "mpc8544ds", + .init = mpc8544ds_init, + .max_cpus = 15, +}; + +static void mpc8544ds_machine_init(void) +{ + qemu_register_machine(&mpc8544ds_machine); +} + +machine_init(mpc8544ds_machine_init); diff --git a/hw/ppce500_mpc8544ds.c b/hw/ppce500_mpc8544ds.c deleted file mode 100644 index 8b9fd83..0000000 --- a/hw/ppce500_mpc8544ds.c +++ /dev/null @@ -1,610 +0,0 @@ -/* - * QEMU PowerPC MPC8544DS board emulation - * - * Copyright (C) 2009 Freescale Semiconductor, Inc. All rights reserved. - * - * Author: Yu Liu, - * - * This file is derived from hw/ppc440_bamboo.c, - * the copyright for that material belongs to the original owners. - * - * This is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - */ - -#include "config.h" -#include "qemu-common.h" -#include "net.h" -#include "hw.h" -#include "pc.h" -#include "pci.h" -#include "boards.h" -#include "sysemu.h" -#include "kvm.h" -#include "kvm_ppc.h" -#include "device_tree.h" -#include "openpic.h" -#include "ppc.h" -#include "loader.h" -#include "elf.h" -#include "sysbus.h" -#include "exec-memory.h" -#include "host-utils.h" - -#define BINARY_DEVICE_TREE_FILE "mpc8544ds.dtb" -#define UIMAGE_LOAD_BASE 0 -#define DTC_LOAD_PAD 0x500000 -#define DTC_PAD_MASK 0xFFFFF -#define INITRD_LOAD_PAD 0x2000000 -#define INITRD_PAD_MASK 0xFFFFFF - -#define RAM_SIZES_ALIGN (64UL << 20) - -#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_PCI_REGS_SIZE 0x1000ULL -#define MPC8544_PCI_IO 0xE1000000ULL -#define MPC8544_PCI_IOLEN 0x10000ULL -#define MPC8544_UTIL_BASE (MPC8544_CCSRBAR_BASE + 0xe0000ULL) -#define MPC8544_SPIN_BASE 0xEF000000ULL - -struct boot_info -{ - uint32_t dt_base; - uint32_t dt_size; - uint32_t entry; -}; - -static void pci_map_create(void *fdt, uint32_t *pci_map, uint32_t mpic) -{ - int i; - const uint32_t tmp[] = { - /* IDSEL 0x11 J17 Slot 1 */ - 0x8800, 0x0, 0x0, 0x1, mpic, 0x2, 0x1, 0x0, 0x0, - 0x8800, 0x0, 0x0, 0x2, mpic, 0x3, 0x1, 0x0, 0x0, - 0x8800, 0x0, 0x0, 0x3, mpic, 0x4, 0x1, 0x0, 0x0, - 0x8800, 0x0, 0x0, 0x4, mpic, 0x1, 0x1, 0x0, 0x0, - - /* IDSEL 0x12 J16 Slot 2 */ - 0x9000, 0x0, 0x0, 0x1, mpic, 0x3, 0x1, 0x0, 0x0, - 0x9000, 0x0, 0x0, 0x2, mpic, 0x4, 0x1, 0x0, 0x0, - 0x9000, 0x0, 0x0, 0x3, mpic, 0x2, 0x1, 0x0, 0x0, - 0x9000, 0x0, 0x0, 0x4, mpic, 0x1, 0x1, 0x0, 0x0, - }; - for (i = 0; i < ARRAY_SIZE(tmp); i++) { - pci_map[i] = cpu_to_be32(tmp[i]); - } -} - -static void dt_serial_create(void *fdt, unsigned long long offset, - const char *soc, const char *mpic, - const char *alias, int idx, bool defcon) -{ - char ser[128]; - - snprintf(ser, sizeof(ser), "%s/serial@%llx", soc, offset); - qemu_devtree_add_subnode(fdt, ser); - qemu_devtree_setprop_string(fdt, ser, "device_type", "serial"); - qemu_devtree_setprop_string(fdt, ser, "compatible", "ns16550"); - qemu_devtree_setprop_cells(fdt, ser, "reg", offset, 0x100); - qemu_devtree_setprop_cell(fdt, ser, "cell-index", idx); - qemu_devtree_setprop_cell(fdt, ser, "clock-frequency", 0); - qemu_devtree_setprop_cells(fdt, ser, "interrupts", 42, 2, 0, 0); - qemu_devtree_setprop_phandle(fdt, ser, "interrupt-parent", mpic); - qemu_devtree_setprop_string(fdt, "/aliases", alias, ser); - - if (defcon) { - qemu_devtree_setprop_string(fdt, "/chosen", "linux,stdout-path", ser); - } -} - -static int mpc8544_load_device_tree(CPUPPCState *env, - target_phys_addr_t addr, - target_phys_addr_t ramsize, - target_phys_addr_t initrd_base, - target_phys_addr_t initrd_size, - const char *kernel_cmdline) -{ - int ret = -1; - uint64_t mem_reg_property[] = { 0, cpu_to_be64(ramsize) }; - int fdt_size; - void *fdt; - uint8_t hypercall[16]; - uint32_t clock_freq = 400000000; - uint32_t tb_freq = 400000000; - int i; - const char *compatible = "MPC8544DS\0MPC85xxDS"; - int compatible_len = sizeof("MPC8544DS\0MPC85xxDS"); - char compatible_sb[] = "fsl,mpc8544-immr\0simple-bus"; - char model[] = "MPC8544DS"; - char soc[128]; - char mpic[128]; - uint32_t mpic_ph; - char gutil[128]; - char pci[128]; - uint32_t pci_map[9 * 8]; - uint32_t pci_ranges[14] = - { - 0x2000000, 0x0, 0xc0000000, - 0x0, 0xc0000000, - 0x0, 0x20000000, - - 0x1000000, 0x0, 0x0, - 0x0, 0xe1000000, - 0x0, 0x10000, - }; - QemuOpts *machine_opts; - const char *dumpdtb = NULL; - const char *dtb_file = NULL; - - machine_opts = qemu_opts_find(qemu_find_opts("machine"), 0); - if (machine_opts) { - const char *tmp; - dumpdtb = qemu_opt_get(machine_opts, "dumpdtb"); - dtb_file = qemu_opt_get(machine_opts, "dtb"); - tmp = qemu_opt_get(machine_opts, "dt_compatible"); - if (tmp) { - compatible = tmp; - compatible_len = strlen(compatible) + 1; - } - } - - if (dtb_file) { - char *filename; - filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, dtb_file); - if (!filename) { - goto out; - } - - fdt = load_device_tree(filename, &fdt_size); - if (!fdt) { - goto out; - } - goto done; - } - - fdt = create_device_tree(&fdt_size); - if (fdt == NULL) { - goto out; - } - - /* Manipulate device tree in memory. */ - qemu_devtree_setprop_string(fdt, "/", "model", model); - qemu_devtree_setprop(fdt, "/", "compatible", compatible, compatible_len); - qemu_devtree_setprop_cell(fdt, "/", "#address-cells", 2); - qemu_devtree_setprop_cell(fdt, "/", "#size-cells", 2); - - qemu_devtree_add_subnode(fdt, "/memory"); - qemu_devtree_setprop_string(fdt, "/memory", "device_type", "memory"); - qemu_devtree_setprop(fdt, "/memory", "reg", mem_reg_property, - sizeof(mem_reg_property)); - - qemu_devtree_add_subnode(fdt, "/chosen"); - if (initrd_size) { - ret = qemu_devtree_setprop_cell(fdt, "/chosen", "linux,initrd-start", - initrd_base); - if (ret < 0) { - fprintf(stderr, "couldn't set /chosen/linux,initrd-start\n"); - } - - ret = qemu_devtree_setprop_cell(fdt, "/chosen", "linux,initrd-end", - (initrd_base + initrd_size)); - if (ret < 0) { - fprintf(stderr, "couldn't set /chosen/linux,initrd-end\n"); - } - } - - ret = qemu_devtree_setprop_string(fdt, "/chosen", "bootargs", - kernel_cmdline); - if (ret < 0) - fprintf(stderr, "couldn't set /chosen/bootargs\n"); - - if (kvm_enabled()) { - /* Read out host's frequencies */ - clock_freq = kvmppc_get_clockfreq(); - tb_freq = kvmppc_get_tbfreq(); - - /* indicate KVM hypercall interface */ - qemu_devtree_add_subnode(fdt, "/hypervisor"); - qemu_devtree_setprop_string(fdt, "/hypervisor", "compatible", - "linux,kvm"); - kvmppc_get_hypercall(env, hypercall, sizeof(hypercall)); - qemu_devtree_setprop(fdt, "/hypervisor", "hcall-instructions", - hypercall, sizeof(hypercall)); - } - - /* Create CPU nodes */ - qemu_devtree_add_subnode(fdt, "/cpus"); - qemu_devtree_setprop_cell(fdt, "/cpus", "#address-cells", 1); - qemu_devtree_setprop_cell(fdt, "/cpus", "#size-cells", 0); - - /* 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--) { - 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) { - break; - } - } - - if (!env) { - continue; - } - - snprintf(cpu_name, sizeof(cpu_name), "/cpus/PowerPC,8544@%x", env->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, "d-cache-line-size", - env->dcache_line_size); - qemu_devtree_setprop_cell(fdt, cpu_name, "i-cache-line-size", - env->icache_line_size); - 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) { - 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", - cpu_release_addr); - } else { - qemu_devtree_setprop_string(fdt, cpu_name, "status", "okay"); - } - } - - qemu_devtree_add_subnode(fdt, "/aliases"); - /* XXX These should go into their respective devices' code */ - snprintf(soc, sizeof(soc), "/soc@%llx", MPC8544_CCSRBAR_BASE); - qemu_devtree_add_subnode(fdt, soc); - qemu_devtree_setprop_string(fdt, soc, "device_type", "soc"); - qemu_devtree_setprop(fdt, soc, "compatible", compatible_sb, - sizeof(compatible_sb)); - qemu_devtree_setprop_cell(fdt, soc, "#address-cells", 1); - qemu_devtree_setprop_cell(fdt, soc, "#size-cells", 1); - qemu_devtree_setprop_cells(fdt, soc, "ranges", 0x0, - MPC8544_CCSRBAR_BASE >> 32, MPC8544_CCSRBAR_BASE, - MPC8544_CCSRBAR_SIZE); - /* 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); - qemu_devtree_add_subnode(fdt, mpic); - qemu_devtree_setprop_string(fdt, mpic, "device_type", "open-pic"); - qemu_devtree_setprop_string(fdt, mpic, "compatible", "fsl,mpic"); - qemu_devtree_setprop_cells(fdt, mpic, "reg", MPC8544_MPIC_REGS_BASE - - MPC8544_CCSRBAR_BASE, 0x40000); - qemu_devtree_setprop_cell(fdt, mpic, "#address-cells", 0); - qemu_devtree_setprop_cell(fdt, mpic, "#interrupt-cells", 4); - mpic_ph = qemu_devtree_alloc_phandle(fdt); - qemu_devtree_setprop_cell(fdt, mpic, "phandle", mpic_ph); - qemu_devtree_setprop_cell(fdt, mpic, "linux,phandle", mpic_ph); - qemu_devtree_setprop(fdt, mpic, "interrupt-controller", NULL, 0); - qemu_devtree_setprop(fdt, mpic, "big-endian", NULL, 0); - qemu_devtree_setprop(fdt, mpic, "single-cpu-affinity", NULL, 0); - qemu_devtree_setprop_cell(fdt, mpic, "last-interrupt-source", 255); - - /* - * We have to generate ser1 first, because Linux takes the first - * 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, - soc, mpic, "serial1", 1, false); - dt_serial_create(fdt, MPC8544_SERIAL0_REGS_BASE - MPC8544_CCSRBAR_BASE, - soc, mpic, "serial0", 0, true); - - snprintf(gutil, sizeof(gutil), "%s/global-utilities@%llx", soc, - MPC8544_UTIL_BASE - MPC8544_CCSRBAR_BASE); - 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(fdt, gutil, "fsl,has-rstcr", NULL, 0); - - 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); - qemu_devtree_setprop_string(fdt, pci, "compatible", "fsl,mpc8540-pci"); - 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)); - qemu_devtree_setprop_phandle(fdt, pci, "interrupt-parent", mpic); - qemu_devtree_setprop_cells(fdt, pci, "interrupts", 24, 2, 0, 0); - 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(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); - qemu_devtree_setprop_cell(fdt, pci, "clock-frequency", 66666666); - qemu_devtree_setprop_cell(fdt, pci, "#interrupt-cells", 1); - qemu_devtree_setprop_cell(fdt, pci, "#size-cells", 2); - qemu_devtree_setprop_cell(fdt, pci, "#address-cells", 3); - qemu_devtree_setprop_string(fdt, "/aliases", "pci0", pci); - -done: - if (dumpdtb) { - /* Dump the dtb to a file and quit */ - FILE *f = fopen(dumpdtb, "wb"); - size_t len; - len = fwrite(fdt, fdt_size, 1, f); - fclose(f); - if (len != fdt_size) { - exit(1); - } - exit(0); - } - - ret = rom_add_blob_fixed(BINARY_DEVICE_TREE_FILE, fdt, fdt_size, addr); - if (ret < 0) { - goto out; - } - g_free(fdt); - ret = fdt_size; - -out: - - return ret; -} - -/* Create -kernel TLB entries for BookE. */ -static inline target_phys_addr_t booke206_page_size_to_tlb(uint64_t size) -{ - return 63 - clz64(size >> 10); -} - -static void mmubooke_create_initial_mapping(CPUPPCState *env) -{ - struct boot_info *bi = env->load_info; - ppcmas_tlb_t *tlb = booke206_get_tlbm(env, 1, 0, 0); - target_phys_addr_t size, dt_end; - int ps; - - /* Our initial TLB entry needs to cover everything from 0 to - the device tree top */ - dt_end = bi->dt_base + bi->dt_size; - ps = booke206_page_size_to_tlb(dt_end) + 1; - size = (ps << MAS1_TSIZE_SHIFT); - tlb->mas1 = MAS1_VALID | size; - tlb->mas2 = 0; - tlb->mas7_3 = 0; - tlb->mas7_3 |= MAS3_UR | MAS3_UW | MAS3_UX | MAS3_SR | MAS3_SW | MAS3_SX; - - env->tlb_dirty = true; -} - -static void mpc8544ds_cpu_reset_sec(void *opaque) -{ - PowerPCCPU *cpu = opaque; - CPUPPCState *env = &cpu->env; - - cpu_reset(CPU(cpu)); - - /* Secondary CPU starts in halted state for now. Needs to change when - implementing non-kernel boot. */ - env->halted = 1; - env->exception_index = EXCP_HLT; -} - -static void mpc8544ds_cpu_reset(void *opaque) -{ - PowerPCCPU *cpu = opaque; - CPUPPCState *env = &cpu->env; - struct boot_info *bi = env->load_info; - - cpu_reset(CPU(cpu)); - - /* Set initial guest state. */ - env->halted = 0; - env->gpr[1] = (16<<20) - 8; - env->gpr[3] = bi->dt_base; - env->nip = bi->entry; - mmubooke_create_initial_mapping(env); -} - -static void mpc8544ds_init(ram_addr_t ram_size, - const char *boot_device, - const char *kernel_filename, - const char *kernel_cmdline, - const char *initrd_filename, - const char *cpu_model) -{ - MemoryRegion *address_space_mem = get_system_memory(); - MemoryRegion *ram = g_new(MemoryRegion, 1); - PCIBus *pci_bus; - CPUPPCState *env = NULL; - uint64_t elf_entry; - uint64_t elf_lowaddr; - target_phys_addr_t entry=0; - target_phys_addr_t loadaddr=UIMAGE_LOAD_BASE; - target_long kernel_size=0; - target_ulong dt_base = 0; - target_ulong initrd_base = 0; - target_long initrd_size=0; - int i=0; - unsigned int pci_irq_nrs[4] = {1, 2, 3, 4}; - qemu_irq **irqs, *mpic; - DeviceState *dev; - CPUPPCState *firstenv = NULL; - - /* Setup CPUs */ - if (cpu_model == NULL) { - cpu_model = "e500v2_v30"; - } - - irqs = g_malloc0(smp_cpus * sizeof(qemu_irq *)); - irqs[0] = g_malloc0(smp_cpus * sizeof(qemu_irq) * OPENPIC_OUTPUT_NB); - for (i = 0; i < smp_cpus; i++) { - PowerPCCPU *cpu; - qemu_irq *input; - - cpu = cpu_ppc_init(cpu_model); - if (cpu == NULL) { - fprintf(stderr, "Unable to initialize CPU!\n"); - exit(1); - } - env = &cpu->env; - - if (!firstenv) { - firstenv = env; - } - - irqs[i] = irqs[0] + (i * OPENPIC_OUTPUT_NB); - 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; - - ppc_booke_timers_init(env, 400000000, PPC_TIMER_E500); - - /* Register reset handler */ - if (!i) { - /* Primary CPU */ - struct boot_info *boot_info; - boot_info = g_malloc0(sizeof(struct boot_info)); - qemu_register_reset(mpc8544ds_cpu_reset, cpu); - env->load_info = boot_info; - } else { - /* Secondary CPUs */ - qemu_register_reset(mpc8544ds_cpu_reset_sec, cpu); - } - } - - env = firstenv; - - /* Fixup Memory size on a alignment boundary */ - ram_size &= ~(RAM_SIZES_ALIGN - 1); - - /* Register Memory */ - memory_region_init_ram(ram, "mpc8544ds.ram", ram_size); - vmstate_register_ram_global(ram); - memory_region_add_subregion(address_space_mem, 0, ram); - - /* MPIC */ - mpic = mpic_init(address_space_mem, MPC8544_MPIC_REGS_BASE, - smp_cpus, irqs, NULL); - - if (!mpic) { - cpu_abort(env, "MPIC failed to initialize\n"); - } - - /* Serial */ - if (serial_hds[0]) { - serial_mm_init(address_space_mem, MPC8544_SERIAL0_REGS_BASE, - 0, mpic[12+26], 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_hds[0], DEVICE_BIG_ENDIAN); - } - - /* General Utility device */ - sysbus_create_simple("mpc8544-guts", MPC8544_UTIL_BASE, NULL); - - /* 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); - pci_bus = (PCIBus *)qdev_get_child_bus(dev, "pci.0"); - if (!pci_bus) - printf("couldn't create PCI controller!\n"); - - isa_mmio_init(MPC8544_PCI_IO, MPC8544_PCI_IOLEN); - - if (pci_bus) { - /* Register network interfaces. */ - for (i = 0; i < nb_nics; i++) { - pci_nic_init_nofail(&nd_table[i], "virtio", NULL); - } - } - - /* Register spinning region */ - sysbus_create_simple("e500-spin", MPC8544_SPIN_BASE, NULL); - - /* Load kernel. */ - if (kernel_filename) { - kernel_size = load_uimage(kernel_filename, &entry, &loadaddr, NULL); - if (kernel_size < 0) { - kernel_size = load_elf(kernel_filename, NULL, NULL, &elf_entry, - &elf_lowaddr, NULL, 1, ELF_MACHINE, 0); - entry = elf_entry; - loadaddr = elf_lowaddr; - } - /* XXX try again as binary */ - if (kernel_size < 0) { - fprintf(stderr, "qemu: could not load kernel '%s'\n", - kernel_filename); - exit(1); - } - } - - /* Load initrd. */ - if (initrd_filename) { - initrd_base = (kernel_size + INITRD_LOAD_PAD) & ~INITRD_PAD_MASK; - initrd_size = load_image_targphys(initrd_filename, initrd_base, - ram_size - initrd_base); - - if (initrd_size < 0) { - fprintf(stderr, "qemu: could not load initial ram disk '%s'\n", - initrd_filename); - exit(1); - } - } - - /* If we're loading a kernel directly, we must load the device tree too. */ - if (kernel_filename) { - struct boot_info *boot_info; - int dt_size; - - dt_base = (loadaddr + kernel_size + DTC_LOAD_PAD) & ~DTC_PAD_MASK; - dt_size = mpc8544_load_device_tree(env, dt_base, ram_size, initrd_base, - initrd_size, kernel_cmdline); - if (dt_size < 0) { - fprintf(stderr, "couldn't load device tree\n"); - exit(1); - } - - boot_info = env->load_info; - boot_info->entry = entry; - boot_info->dt_base = dt_base; - boot_info->dt_size = dt_size; - } - - if (kvm_enabled()) { - kvmppc_init(); - } -} - -static QEMUMachine mpc8544ds_machine = { - .name = "mpc8544ds", - .desc = "mpc8544ds", - .init = mpc8544ds_init, - .max_cpus = 15, -}; - -static void mpc8544ds_machine_init(void) -{ - qemu_register_machine(&mpc8544ds_machine); -} - -machine_init(mpc8544ds_machine_init); -- cgit v1.1 From b330598137cad915f65562a524b30e32498e44be Mon Sep 17 00:00:00 2001 From: Scott Wood Date: Mon, 2 Jul 2012 13:03:19 +0000 Subject: PPC: e500: change internal references away from mpc8544ds No functional changes -- machine is still outwardly mpc8544ds. The references that are not changed contain mpc8544 hardware details that need to be parameterized if/when a different e500 platform wants to change them. Signed-off-by: Scott Wood Signed-off-by: Alexander Graf --- hw/ppc/e500.c | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) (limited to 'hw') diff --git a/hw/ppc/e500.c b/hw/ppc/e500.c index 0b383e6..db5ca6e 100644 --- a/hw/ppc/e500.c +++ b/hw/ppc/e500.c @@ -1,5 +1,5 @@ /* - * QEMU PowerPC MPC8544DS board emulation + * QEMU PowerPC e500-based platforms * * Copyright (C) 2009 Freescale Semiconductor, Inc. All rights reserved. * @@ -42,6 +42,7 @@ #define RAM_SIZES_ALIGN (64UL << 20) +/* TODO: parameterize */ #define MPC8544_CCSRBAR_BASE 0xE0000000ULL #define MPC8544_CCSRBAR_SIZE 0x00100000ULL #define MPC8544_MPIC_REGS_BASE (MPC8544_CCSRBAR_BASE + 0x40000ULL) @@ -104,7 +105,7 @@ static void dt_serial_create(void *fdt, unsigned long long offset, } } -static int mpc8544_load_device_tree(CPUPPCState *env, +static int ppce500_load_device_tree(CPUPPCState *env, target_phys_addr_t addr, target_phys_addr_t ramsize, target_phys_addr_t initrd_base, @@ -388,7 +389,7 @@ static void mmubooke_create_initial_mapping(CPUPPCState *env) env->tlb_dirty = true; } -static void mpc8544ds_cpu_reset_sec(void *opaque) +static void ppce500_cpu_reset_sec(void *opaque) { PowerPCCPU *cpu = opaque; CPUPPCState *env = &cpu->env; @@ -401,7 +402,7 @@ static void mpc8544ds_cpu_reset_sec(void *opaque) env->exception_index = EXCP_HLT; } -static void mpc8544ds_cpu_reset(void *opaque) +static void ppce500_cpu_reset(void *opaque) { PowerPCCPU *cpu = opaque; CPUPPCState *env = &cpu->env; @@ -417,7 +418,7 @@ static void mpc8544ds_cpu_reset(void *opaque) mmubooke_create_initial_mapping(env); } -static void mpc8544ds_init(ram_addr_t ram_size, +static void ppce500_init(ram_addr_t ram_size, const char *boot_device, const char *kernel_filename, const char *kernel_cmdline, @@ -478,11 +479,11 @@ static void mpc8544ds_init(ram_addr_t ram_size, /* Primary CPU */ struct boot_info *boot_info; boot_info = g_malloc0(sizeof(struct boot_info)); - qemu_register_reset(mpc8544ds_cpu_reset, cpu); + qemu_register_reset(ppce500_cpu_reset, cpu); env->load_info = boot_info; } else { /* Secondary CPUs */ - qemu_register_reset(mpc8544ds_cpu_reset_sec, cpu); + qemu_register_reset(ppce500_cpu_reset_sec, cpu); } } @@ -577,7 +578,7 @@ static void mpc8544ds_init(ram_addr_t ram_size, int dt_size; dt_base = (loadaddr + kernel_size + DTC_LOAD_PAD) & ~DTC_PAD_MASK; - dt_size = mpc8544_load_device_tree(env, dt_base, ram_size, initrd_base, + dt_size = ppce500_load_device_tree(env, dt_base, ram_size, initrd_base, initrd_size, kernel_cmdline); if (dt_size < 0) { fprintf(stderr, "couldn't load device tree\n"); @@ -595,16 +596,16 @@ static void mpc8544ds_init(ram_addr_t ram_size, } } -static QEMUMachine mpc8544ds_machine = { +static QEMUMachine ppce500_machine = { .name = "mpc8544ds", .desc = "mpc8544ds", - .init = mpc8544ds_init, + .init = ppce500_init, .max_cpus = 15, }; -static void mpc8544ds_machine_init(void) +static void ppce500_machine_init(void) { - qemu_register_machine(&mpc8544ds_machine); + qemu_register_machine(&ppce500_machine); } -machine_init(mpc8544ds_machine_init); +machine_init(ppce500_machine_init); -- cgit v1.1 From e6eaabeb8dfb026da51d178974bddf56f1f06ffe Mon Sep 17 00:00:00 2001 From: Scott Wood Date: Mon, 2 Jul 2012 13:03:20 +0000 Subject: PPC: e500: split mpc8544ds machine from generic e500 code Currently the only mpc8544ds-ism that is factored out is toplevel compatible and model. In the future the generic e500 code is expected to become more generic. Signed-off-by: Scott Wood [agraf: conditionalize on CONFIG_FDT] Signed-off-by: Alexander Graf --- hw/ppc/Makefile.objs | 2 +- hw/ppc/e500.c | 81 ++++++++++++++++++++-------------------------------- hw/ppc/e500.h | 21 ++++++++++++++ hw/ppc/mpc8544ds.c | 61 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 114 insertions(+), 51 deletions(-) create mode 100644 hw/ppc/e500.h create mode 100644 hw/ppc/mpc8544ds.c (limited to 'hw') diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs index e86c524..99fe837 100644 --- a/hw/ppc/Makefile.objs +++ b/hw/ppc/Makefile.objs @@ -27,4 +27,4 @@ obj-y += xilinx_ethlite.o obj-y := $(addprefix ../,$(obj-y)) -obj-$(CONFIG_FDT) += e500.o +obj-$(CONFIG_FDT) += e500.o mpc8544ds.o diff --git a/hw/ppc/e500.c b/hw/ppc/e500.c index db5ca6e..f07be08 100644 --- a/hw/ppc/e500.c +++ b/hw/ppc/e500.c @@ -16,6 +16,7 @@ #include "config.h" #include "qemu-common.h" +#include "e500.h" #include "net.h" #include "hw/hw.h" #include "hw/pc.h" @@ -106,24 +107,21 @@ static void dt_serial_create(void *fdt, unsigned long long offset, } static int ppce500_load_device_tree(CPUPPCState *env, + PPCE500Params *params, target_phys_addr_t addr, - target_phys_addr_t ramsize, target_phys_addr_t initrd_base, - target_phys_addr_t initrd_size, - const char *kernel_cmdline) + target_phys_addr_t initrd_size) { int ret = -1; - uint64_t mem_reg_property[] = { 0, cpu_to_be64(ramsize) }; + uint64_t mem_reg_property[] = { 0, cpu_to_be64(params->ram_size) }; int fdt_size; void *fdt; uint8_t hypercall[16]; uint32_t clock_freq = 400000000; uint32_t tb_freq = 400000000; int i; - const char *compatible = "MPC8544DS\0MPC85xxDS"; - int compatible_len = sizeof("MPC8544DS\0MPC85xxDS"); + const char *toplevel_compat = NULL; /* user override */ char compatible_sb[] = "fsl,mpc8544-immr\0simple-bus"; - char model[] = "MPC8544DS"; char soc[128]; char mpic[128]; uint32_t mpic_ph; @@ -146,14 +144,9 @@ static int ppce500_load_device_tree(CPUPPCState *env, machine_opts = qemu_opts_find(qemu_find_opts("machine"), 0); if (machine_opts) { - const char *tmp; dumpdtb = qemu_opt_get(machine_opts, "dumpdtb"); dtb_file = qemu_opt_get(machine_opts, "dtb"); - tmp = qemu_opt_get(machine_opts, "dt_compatible"); - if (tmp) { - compatible = tmp; - compatible_len = strlen(compatible) + 1; - } + toplevel_compat = qemu_opt_get(machine_opts, "dt_compatible"); } if (dtb_file) { @@ -176,8 +169,6 @@ static int ppce500_load_device_tree(CPUPPCState *env, } /* Manipulate device tree in memory. */ - qemu_devtree_setprop_string(fdt, "/", "model", model); - qemu_devtree_setprop(fdt, "/", "compatible", compatible, compatible_len); qemu_devtree_setprop_cell(fdt, "/", "#address-cells", 2); qemu_devtree_setprop_cell(fdt, "/", "#size-cells", 2); @@ -202,7 +193,7 @@ static int ppce500_load_device_tree(CPUPPCState *env, } ret = qemu_devtree_setprop_string(fdt, "/chosen", "bootargs", - kernel_cmdline); + params->kernel_cmdline); if (ret < 0) fprintf(stderr, "couldn't set /chosen/bootargs\n"); @@ -338,6 +329,13 @@ static int ppce500_load_device_tree(CPUPPCState *env, qemu_devtree_setprop_cell(fdt, pci, "#address-cells", 3); qemu_devtree_setprop_string(fdt, "/aliases", "pci0", pci); + params->fixup_devtree(params, fdt); + + if (toplevel_compat) { + qemu_devtree_setprop(fdt, "/", "compatible", toplevel_compat, + strlen(toplevel_compat) + 1); + } + done: if (dumpdtb) { /* Dump the dtb to a file and quit */ @@ -418,12 +416,7 @@ static void ppce500_cpu_reset(void *opaque) mmubooke_create_initial_mapping(env); } -static void ppce500_init(ram_addr_t ram_size, - const char *boot_device, - const char *kernel_filename, - const char *kernel_cmdline, - const char *initrd_filename, - const char *cpu_model) +void ppce500_init(PPCE500Params *params) { MemoryRegion *address_space_mem = get_system_memory(); MemoryRegion *ram = g_new(MemoryRegion, 1); @@ -444,8 +437,8 @@ static void ppce500_init(ram_addr_t ram_size, CPUPPCState *firstenv = NULL; /* Setup CPUs */ - if (cpu_model == NULL) { - cpu_model = "e500v2_v30"; + if (params->cpu_model == NULL) { + params->cpu_model = "e500v2_v30"; } irqs = g_malloc0(smp_cpus * sizeof(qemu_irq *)); @@ -454,7 +447,7 @@ static void ppce500_init(ram_addr_t ram_size, PowerPCCPU *cpu; qemu_irq *input; - cpu = cpu_ppc_init(cpu_model); + cpu = cpu_ppc_init(params->cpu_model); if (cpu == NULL) { fprintf(stderr, "Unable to initialize CPU!\n"); exit(1); @@ -543,43 +536,45 @@ static void ppce500_init(ram_addr_t ram_size, sysbus_create_simple("e500-spin", MPC8544_SPIN_BASE, NULL); /* Load kernel. */ - if (kernel_filename) { - kernel_size = load_uimage(kernel_filename, &entry, &loadaddr, NULL); + if (params->kernel_filename) { + kernel_size = load_uimage(params->kernel_filename, &entry, + &loadaddr, NULL); if (kernel_size < 0) { - kernel_size = load_elf(kernel_filename, NULL, NULL, &elf_entry, - &elf_lowaddr, NULL, 1, ELF_MACHINE, 0); + kernel_size = load_elf(params->kernel_filename, NULL, NULL, + &elf_entry, &elf_lowaddr, NULL, 1, + ELF_MACHINE, 0); entry = elf_entry; loadaddr = elf_lowaddr; } /* XXX try again as binary */ if (kernel_size < 0) { fprintf(stderr, "qemu: could not load kernel '%s'\n", - kernel_filename); + params->kernel_filename); exit(1); } } /* Load initrd. */ - if (initrd_filename) { + if (params->initrd_filename) { initrd_base = (kernel_size + INITRD_LOAD_PAD) & ~INITRD_PAD_MASK; - initrd_size = load_image_targphys(initrd_filename, initrd_base, + initrd_size = load_image_targphys(params->initrd_filename, initrd_base, ram_size - initrd_base); if (initrd_size < 0) { fprintf(stderr, "qemu: could not load initial ram disk '%s'\n", - initrd_filename); + params->initrd_filename); exit(1); } } /* If we're loading a kernel directly, we must load the device tree too. */ - if (kernel_filename) { + if (params->kernel_filename) { 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, dt_base, ram_size, initrd_base, - initrd_size, kernel_cmdline); + 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); @@ -595,17 +590,3 @@ static void ppce500_init(ram_addr_t ram_size, kvmppc_init(); } } - -static QEMUMachine ppce500_machine = { - .name = "mpc8544ds", - .desc = "mpc8544ds", - .init = ppce500_init, - .max_cpus = 15, -}; - -static void ppce500_machine_init(void) -{ - qemu_register_machine(&ppce500_machine); -} - -machine_init(ppce500_machine_init); diff --git a/hw/ppc/e500.h b/hw/ppc/e500.h new file mode 100644 index 0000000..7ae87f4 --- /dev/null +++ b/hw/ppc/e500.h @@ -0,0 +1,21 @@ +#ifndef PPCE500_H +#define PPCE500_H + +typedef struct PPCE500Params { + /* Standard QEMU machine init params */ + ram_addr_t ram_size; + const char *boot_device; + const char *kernel_filename; + const char *kernel_cmdline; + const char *initrd_filename; + const char *cpu_model; + + /* e500-specific params */ + + /* required -- must at least add toplevel board compatible */ + void (*fixup_devtree)(struct PPCE500Params *params, void *fdt); +} PPCE500Params; + +void ppce500_init(PPCE500Params *params); + +#endif diff --git a/hw/ppc/mpc8544ds.c b/hw/ppc/mpc8544ds.c new file mode 100644 index 0000000..984d21c --- /dev/null +++ b/hw/ppc/mpc8544ds.c @@ -0,0 +1,61 @@ +/* + * Support for the PPC e500-based mpc8544ds board + * + * Copyright 2012 Freescale Semiconductor, Inc. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include "config.h" +#include "qemu-common.h" +#include "e500.h" +#include "../boards.h" +#include "device_tree.h" + +static void mpc8544ds_fixup_devtree(PPCE500Params *params, void *fdt) +{ + const char model[] = "MPC8544DS"; + const char compatible[] = "MPC8544DS\0MPC85xxDS"; + + qemu_devtree_setprop(fdt, "/", "model", model, sizeof(model)); + qemu_devtree_setprop(fdt, "/", "compatible", compatible, + sizeof(compatible)); +} + +static void mpc8544ds_init(ram_addr_t ram_size, + const char *boot_device, + const char *kernel_filename, + const char *kernel_cmdline, + const char *initrd_filename, + const char *cpu_model) +{ + PPCE500Params params = { + .ram_size = ram_size, + .boot_device = boot_device, + .kernel_filename = kernel_filename, + .kernel_cmdline = kernel_cmdline, + .initrd_filename = initrd_filename, + .cpu_model = cpu_model, + .fixup_devtree = mpc8544ds_fixup_devtree, + }; + + ppce500_init(¶ms); +} + + +static QEMUMachine ppce500_machine = { + .name = "mpc8544ds", + .desc = "mpc8544ds", + .init = mpc8544ds_init, + .max_cpus = 15, +}; + +static void ppce500_machine_init(void) +{ + qemu_register_machine(&ppce500_machine); +} + +machine_init(ppce500_machine_init); -- cgit v1.1 From 4d5c29ca455ed6adb1fb9f8394e4d7badfd0c532 Mon Sep 17 00:00:00 2001 From: Scott Wood Date: Mon, 2 Jul 2012 13:03:21 +0000 Subject: PPC: e500: add generic e500 platform This gives the kernel a paravirtualized machine to target, without requiring both sides to pretend to be targeting a specific board that likely has little to do with the host in KVM scenarios. This avoids the need to add new boards to QEMU, just to be able to run KVM on new CPUs. Signed-off-by: Scott Wood [agraf: conditionalize on CONFIG_FDT] Signed-off-by: Alexander Graf --- hw/ppc/Makefile.objs | 2 +- hw/ppc/e500plat.c | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 hw/ppc/e500plat.c (limited to 'hw') diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs index 99fe837..951e407 100644 --- a/hw/ppc/Makefile.objs +++ b/hw/ppc/Makefile.objs @@ -27,4 +27,4 @@ obj-y += xilinx_ethlite.o obj-y := $(addprefix ../,$(obj-y)) -obj-$(CONFIG_FDT) += e500.o mpc8544ds.o +obj-$(CONFIG_FDT) += e500.o mpc8544ds.o e500plat.o diff --git a/hw/ppc/e500plat.c b/hw/ppc/e500plat.c new file mode 100644 index 0000000..60a5cb3 --- /dev/null +++ b/hw/ppc/e500plat.c @@ -0,0 +1,60 @@ +/* + * Generic device-tree-driven paravirt PPC e500 platform + * + * Copyright 2012 Freescale Semiconductor, Inc. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include "config.h" +#include "qemu-common.h" +#include "e500.h" +#include "../boards.h" +#include "device_tree.h" + +static void e500plat_fixup_devtree(PPCE500Params *params, void *fdt) +{ + const char model[] = "QEMU ppce500"; + const char compatible[] = "fsl,qemu-e500"; + + qemu_devtree_setprop(fdt, "/", "model", model, sizeof(model)); + qemu_devtree_setprop(fdt, "/", "compatible", compatible, + sizeof(compatible)); +} + +static void e500plat_init(ram_addr_t ram_size, + const char *boot_device, + const char *kernel_filename, + const char *kernel_cmdline, + const char *initrd_filename, + const char *cpu_model) +{ + PPCE500Params params = { + .ram_size = ram_size, + .boot_device = boot_device, + .kernel_filename = kernel_filename, + .kernel_cmdline = kernel_cmdline, + .initrd_filename = initrd_filename, + .cpu_model = cpu_model, + .fixup_devtree = e500plat_fixup_devtree, + }; + + ppce500_init(¶ms); +} + +static QEMUMachine e500plat_machine = { + .name = "ppce500", + .desc = "generic paravirt e500 platform", + .init = e500plat_init, + .max_cpus = 15, +}; + +static void e500plat_machine_init(void) +{ + qemu_register_machine(&e500plat_machine); +} + +machine_init(e500plat_machine_init); -- cgit v1.1 From 7e99826c350f21cb7d162e802efaa5d1b8689ad0 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Thu, 9 Aug 2012 22:57:14 +0200 Subject: Revert "PPC: e500: Use new MPIC dt format" This reverts commit 518c7fb44f2182cde943dc64f88cb2fd4e4ff6b5. It breaks new Linux guests with SMP, because IPIs get mapped to large vectors which our MPIC emulation does not implement. Conflicts: hw/ppc/e500.c --- hw/ppc/e500.c | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) (limited to 'hw') diff --git a/hw/ppc/e500.c b/hw/ppc/e500.c index f07be08..6f0de6d 100644 --- a/hw/ppc/e500.c +++ b/hw/ppc/e500.c @@ -68,18 +68,18 @@ static void pci_map_create(void *fdt, uint32_t *pci_map, uint32_t mpic) int i; const uint32_t tmp[] = { /* IDSEL 0x11 J17 Slot 1 */ - 0x8800, 0x0, 0x0, 0x1, mpic, 0x2, 0x1, 0x0, 0x0, - 0x8800, 0x0, 0x0, 0x2, mpic, 0x3, 0x1, 0x0, 0x0, - 0x8800, 0x0, 0x0, 0x3, mpic, 0x4, 0x1, 0x0, 0x0, - 0x8800, 0x0, 0x0, 0x4, mpic, 0x1, 0x1, 0x0, 0x0, + 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, 0x0, 0x0, - 0x9000, 0x0, 0x0, 0x2, mpic, 0x4, 0x1, 0x0, 0x0, - 0x9000, 0x0, 0x0, 0x3, mpic, 0x2, 0x1, 0x0, 0x0, - 0x9000, 0x0, 0x0, 0x4, mpic, 0x1, 0x1, 0x0, 0x0, + 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 < ARRAY_SIZE(tmp); i++) { + for (i = 0; i < (7 * 8); i++) { pci_map[i] = cpu_to_be32(tmp[i]); } } @@ -97,7 +97,7 @@ static void dt_serial_create(void *fdt, unsigned long long offset, qemu_devtree_setprop_cells(fdt, ser, "reg", offset, 0x100); qemu_devtree_setprop_cell(fdt, ser, "cell-index", idx); qemu_devtree_setprop_cell(fdt, ser, "clock-frequency", 0); - qemu_devtree_setprop_cells(fdt, ser, "interrupts", 42, 2, 0, 0); + qemu_devtree_setprop_cells(fdt, ser, "interrupts", 42, 2); qemu_devtree_setprop_phandle(fdt, ser, "interrupt-parent", mpic); qemu_devtree_setprop_string(fdt, "/aliases", alias, ser); @@ -127,7 +127,7 @@ static int ppce500_load_device_tree(CPUPPCState *env, uint32_t mpic_ph; char gutil[128]; char pci[128]; - uint32_t pci_map[9 * 8]; + uint32_t pci_map[7 * 8]; uint32_t pci_ranges[14] = { 0x2000000, 0x0, 0xc0000000, @@ -274,18 +274,15 @@ static int ppce500_load_device_tree(CPUPPCState *env, MPC8544_MPIC_REGS_BASE - MPC8544_CCSRBAR_BASE); qemu_devtree_add_subnode(fdt, mpic); qemu_devtree_setprop_string(fdt, mpic, "device_type", "open-pic"); - qemu_devtree_setprop_string(fdt, mpic, "compatible", "fsl,mpic"); + 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_cell(fdt, mpic, "#address-cells", 0); - qemu_devtree_setprop_cell(fdt, mpic, "#interrupt-cells", 4); + qemu_devtree_setprop_cell(fdt, mpic, "#interrupt-cells", 2); mpic_ph = qemu_devtree_alloc_phandle(fdt); qemu_devtree_setprop_cell(fdt, mpic, "phandle", mpic_ph); qemu_devtree_setprop_cell(fdt, mpic, "linux,phandle", mpic_ph); qemu_devtree_setprop(fdt, mpic, "interrupt-controller", NULL, 0); - qemu_devtree_setprop(fdt, mpic, "big-endian", NULL, 0); - qemu_devtree_setprop(fdt, mpic, "single-cpu-affinity", NULL, 0); - qemu_devtree_setprop_cell(fdt, mpic, "last-interrupt-source", 255); /* * We have to generate ser1 first, because Linux takes the first @@ -315,7 +312,7 @@ static int ppce500_load_device_tree(CPUPPCState *env, pci_map_create(fdt, pci_map, qemu_devtree_get_phandle(fdt, mpic)); qemu_devtree_setprop(fdt, pci, "interrupt-map", pci_map, sizeof(pci_map)); qemu_devtree_setprop_phandle(fdt, pci, "interrupt-parent", mpic); - qemu_devtree_setprop_cells(fdt, pci, "interrupts", 24, 2, 0, 0); + 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]); -- cgit v1.1 From c1195d1677a41f42be712620674d42b39d83bdfe Mon Sep 17 00:00:00 2001 From: "zhlcindy@gmail.com" Date: Mon, 6 Aug 2012 16:41:59 +0000 Subject: Add one new file vga-pci.h and cleanup on all platforms Functions pci_vga_init() and pci_cirrus_vga_init() are declared in pc.h. That prevents other platforms (e.g. sPAPR) to use them. This patch is to create one new file vga-pci.h and move the declarations to vga-pci.h, so that they can be shared by all platforms. This patch also cleans up on all platforms. Signed-off-by: Li Zhang Signed-off-by: Alexander Graf --- hw/alpha_pci.c | 1 + hw/cirrus_vga.c | 2 +- hw/mips_malta.c | 1 + hw/pc.c | 1 + hw/pc.h | 4 ---- hw/ppc_newworld.c | 2 +- hw/ppc_oldworld.c | 2 +- hw/ppc_prep.c | 1 + hw/sun4u.c | 1 + hw/vga-pci.c | 2 +- hw/vga-pci.h | 12 ++++++++++++ 11 files changed, 21 insertions(+), 8 deletions(-) create mode 100644 hw/vga-pci.h (limited to 'hw') diff --git a/hw/alpha_pci.c b/hw/alpha_pci.c index 6735577..ea546f8 100644 --- a/hw/alpha_pci.c +++ b/hw/alpha_pci.c @@ -11,6 +11,7 @@ #include "qemu-log.h" #include "sysemu.h" #include "vmware_vga.h" +#include "vga-pci.h" /* PCI IO reads/writes, to byte-word addressable memory. */ diff --git a/hw/cirrus_vga.c b/hw/cirrus_vga.c index 623dd68..e8dcc6b 100644 --- a/hw/cirrus_vga.c +++ b/hw/cirrus_vga.c @@ -27,8 +27,8 @@ * available at http://home.worldonline.dk/~finth/ */ #include "hw.h" -#include "pc.h" #include "pci.h" +#include "vga-pci.h" #include "console.h" #include "vga_int.h" #include "loader.h" diff --git a/hw/mips_malta.c b/hw/mips_malta.c index 351c88e..ad23f26 100644 --- a/hw/mips_malta.c +++ b/hw/mips_malta.c @@ -48,6 +48,7 @@ #include "blockdev.h" #include "exec-memory.h" #include "sysbus.h" /* SysBusDevice */ +#include "vga-pci.h" //#define DEBUG_BOARD_INIT diff --git a/hw/pc.c b/hw/pc.c index e8bcfc0..3ed1a3c 100644 --- a/hw/pc.c +++ b/hw/pc.c @@ -51,6 +51,7 @@ #include "exec-memory.h" #include "arch_init.h" #include "bitmap.h" +#include "vga-pci.h" /* output Bochs bios info messages */ //#define DEBUG_BIOS diff --git a/hw/pc.h b/hw/pc.h index 31ccb6f..e4db071 100644 --- a/hw/pc.h +++ b/hw/pc.h @@ -189,14 +189,10 @@ static inline DeviceState *isa_vga_init(ISABus *bus) return &dev->qdev; } -DeviceState *pci_vga_init(PCIBus *bus); int isa_vga_mm_init(target_phys_addr_t vram_base, target_phys_addr_t ctrl_base, int it_shift, MemoryRegion *address_space); -/* cirrus_vga.c */ -DeviceState *pci_cirrus_vga_init(PCIBus *bus); - /* ne2000.c */ static inline bool isa_ne2000_init(ISABus *bus, int base, int irq, NICInfo *nd) { diff --git a/hw/ppc_newworld.c b/hw/ppc_newworld.c index 4e2a6e6..e95cfe8 100644 --- a/hw/ppc_newworld.c +++ b/hw/ppc_newworld.c @@ -52,7 +52,6 @@ #include "adb.h" #include "mac_dbdma.h" #include "nvram.h" -#include "pc.h" #include "pci.h" #include "net.h" #include "sysemu.h" @@ -68,6 +67,7 @@ #include "hw/usb.h" #include "blockdev.h" #include "exec-memory.h" +#include "vga-pci.h" #define MAX_IDE_BUS 2 #define CFG_ADDR 0xf0000510 diff --git a/hw/ppc_oldworld.c b/hw/ppc_oldworld.c index f2c6908..1dcd8a6 100644 --- a/hw/ppc_oldworld.c +++ b/hw/ppc_oldworld.c @@ -29,7 +29,6 @@ #include "adb.h" #include "mac_dbdma.h" #include "nvram.h" -#include "pc.h" #include "sysemu.h" #include "net.h" #include "isa.h" @@ -44,6 +43,7 @@ #include "kvm_ppc.h" #include "blockdev.h" #include "exec-memory.h" +#include "vga-pci.h" #define MAX_IDE_BUS 2 #define CFG_ADDR 0xf0000510 diff --git a/hw/ppc_prep.c b/hw/ppc_prep.c index be2b268..7a87616 100644 --- a/hw/ppc_prep.c +++ b/hw/ppc_prep.c @@ -39,6 +39,7 @@ #include "blockdev.h" #include "arch_init.h" #include "exec-memory.h" +#include "vga-pci.h" //#define HARD_DEBUG_PPC_IO //#define DEBUG_PPC_IO diff --git a/hw/sun4u.c b/hw/sun4u.c index 137a7c6..07cd042 100644 --- a/hw/sun4u.c +++ b/hw/sun4u.c @@ -39,6 +39,7 @@ #include "elf.h" #include "blockdev.h" #include "exec-memory.h" +#include "vga-pci.h" //#define DEBUG_IRQ //#define DEBUG_EBUS diff --git a/hw/vga-pci.c b/hw/vga-pci.c index 37dc019..9abbada 100644 --- a/hw/vga-pci.c +++ b/hw/vga-pci.c @@ -23,8 +23,8 @@ */ #include "hw.h" #include "console.h" -#include "pc.h" #include "pci.h" +#include "vga-pci.h" #include "vga_int.h" #include "pixel_ops.h" #include "qemu-timer.h" diff --git a/hw/vga-pci.h b/hw/vga-pci.h new file mode 100644 index 0000000..49abf13 --- /dev/null +++ b/hw/vga-pci.h @@ -0,0 +1,12 @@ +#ifndef VGA_PCI_H +#define VGA_PCI_H + +#include "qemu-common.h" + +/* vga-pci.c */ +DeviceState *pci_vga_init(PCIBus *bus); + +/* cirrus_vga.c */ +DeviceState *pci_cirrus_vga_init(PCIBus *bus); + +#endif -- cgit v1.1 From f28359d8e8fc64192450a72164b89d06cc3f781d Mon Sep 17 00:00:00 2001 From: "zhlcindy@gmail.com" Date: Mon, 6 Aug 2012 16:42:00 +0000 Subject: spapr: Add support for -vga option Also instanciate the USB keyboard and mouse when that option is used (you can still use -device to create individual devices without all the defaults) Signed-off-by: Benjamin Herrenschmidt Signed-off-by: Li Zhang [agraf: remove USB bits] Signed-off-by: Alexander Graf --- hw/spapr.c | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) (limited to 'hw') diff --git a/hw/spapr.c b/hw/spapr.c index ab5a0c2..494c412 100644 --- a/hw/spapr.c +++ b/hw/spapr.c @@ -45,6 +45,7 @@ #include "kvm.h" #include "kvm_ppc.h" #include "pci.h" +#include "vga-pci.h" #include "exec-memory.h" @@ -82,6 +83,7 @@ #define PHANDLE_XICP 0x00001111 sPAPREnvironment *spapr; +bool spapr_has_graphics; qemu_irq spapr_allocate_irq(uint32_t hint, uint32_t *irq_num, enum xics_irq_type type) @@ -257,6 +259,9 @@ static void *spapr_create_fdt_skel(const char *cpu_model, _FDT((fdt_property(fdt, "qemu,boot-kernel", &kprop, sizeof(kprop)))); } _FDT((fdt_property_string(fdt, "qemu,boot-device", boot_device))); + _FDT((fdt_property_cell(fdt, "qemu,graphic-width", graphic_width))); + _FDT((fdt_property_cell(fdt, "qemu,graphic-height", graphic_height))); + _FDT((fdt_property_cell(fdt, "qemu,graphic-depth", graphic_depth))); _FDT((fdt_end_node(fdt))); @@ -503,7 +508,9 @@ static void spapr_finalize_fdt(sPAPREnvironment *spapr, } } - spapr_populate_chosen_stdout(fdt, spapr->vio_bus); + if (!spapr_has_graphics) { + spapr_populate_chosen_stdout(fdt, spapr->vio_bus); + } _FDT((fdt_pack(fdt))); @@ -556,6 +563,18 @@ static void spapr_cpu_reset(void *opaque) cpu_reset(CPU(cpu)); } +static int spapr_vga_init(PCIBus *pci_bus) +{ + if (std_vga_enabled) { + pci_vga_init(pci_bus); + } else { + fprintf(stderr, "This vga model is not supported," + "currently it only supports -vga std\n"); + return 0; + } + return 1; +} + /* pSeries LPAR / sPAPR hardware init */ static void ppc_spapr_init(ram_addr_t ram_size, const char *boot_device, @@ -710,6 +729,11 @@ static void ppc_spapr_init(ram_addr_t ram_size, spapr_vscsi_create(spapr->vio_bus); } + /* Graphics */ + if (spapr_vga_init(QLIST_FIRST(&spapr->phbs)->host_state.bus)) { + spapr_has_graphics = true; + } + if (rma_size < (MIN_RMA_SLOF << 20)) { fprintf(stderr, "qemu: pSeries SLOF firmware requires >= " "%ldM guest RMA (Real Mode Area memory)\n", MIN_RMA_SLOF); -- cgit v1.1 From 8c57b867b5d18b0d916797dfbac465b8aaa5bf5a Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Tue, 14 Aug 2012 13:11:49 +0200 Subject: PPC: spapr: Rework VGA select logic When selecting our VGA adapter, we want to: * fail completely when we can't satisfy the user's request * support -nographic where no VGA adapter should be spawned This patch reworks the logic so we fulfill the two conditions above. Signed-off-by: Alexander Graf --- hw/spapr.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'hw') diff --git a/hw/spapr.c b/hw/spapr.c index 494c412..709673e 100644 --- a/hw/spapr.c +++ b/hw/spapr.c @@ -563,16 +563,21 @@ static void spapr_cpu_reset(void *opaque) cpu_reset(CPU(cpu)); } +/* Returns whether we want to use VGA or not */ static int spapr_vga_init(PCIBus *pci_bus) { - if (std_vga_enabled) { + switch (vga_interface_type) { + case VGA_STD: pci_vga_init(pci_bus); - } else { + return 1; + case VGA_NONE: + return 0; + default: fprintf(stderr, "This vga model is not supported," "currently it only supports -vga std\n"); - return 0; + exit(0); + break; } - return 1; } /* pSeries LPAR / sPAPR hardware init */ -- cgit v1.1 From 3fc5acdeedfcea7c7d86040fa427ae785920b399 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Tue, 14 Aug 2012 13:22:13 +0200 Subject: PPC: spapr: Remove global variable Global variables are bad. Let's move spapr_has_graphics into the machine state struct. Signed-off-by: Alexander Graf --- hw/spapr.c | 5 ++--- hw/spapr.h | 1 + 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'hw') diff --git a/hw/spapr.c b/hw/spapr.c index 709673e..aa39d2d 100644 --- a/hw/spapr.c +++ b/hw/spapr.c @@ -83,7 +83,6 @@ #define PHANDLE_XICP 0x00001111 sPAPREnvironment *spapr; -bool spapr_has_graphics; qemu_irq spapr_allocate_irq(uint32_t hint, uint32_t *irq_num, enum xics_irq_type type) @@ -508,7 +507,7 @@ static void spapr_finalize_fdt(sPAPREnvironment *spapr, } } - if (!spapr_has_graphics) { + if (!spapr->has_graphics) { spapr_populate_chosen_stdout(fdt, spapr->vio_bus); } @@ -736,7 +735,7 @@ static void ppc_spapr_init(ram_addr_t ram_size, /* Graphics */ if (spapr_vga_init(QLIST_FIRST(&spapr->phbs)->host_state.bus)) { - spapr_has_graphics = true; + spapr->has_graphics = true; } if (rma_size < (MIN_RMA_SLOF << 20)) { diff --git a/hw/spapr.h b/hw/spapr.h index 9153f29..fe40e7d 100644 --- a/hw/spapr.h +++ b/hw/spapr.h @@ -23,6 +23,7 @@ typedef struct sPAPREnvironment { int next_irq; int rtc_offset; char *cpu_model; + bool has_graphics; } sPAPREnvironment; #define H_SUCCESS 0 -- cgit v1.1 From c9f709d2e7d9dcc6e6ecbee8d499b4b14b11320b Mon Sep 17 00:00:00 2001 From: David Gibson Date: Tue, 7 Aug 2012 16:10:31 +0000 Subject: pseries: Remove extraneous prints The pseries machine prints several messages to stderr whenever it starts up and another whenever the vm is reset. It's not normal for qemu machines to do this though, so this patch removes them. We can put them back conditional on a DEBUG symbol if we really need them in future. Signed-off-by: David Gibson Signed-off-by: Alexander Graf --- hw/spapr.c | 18 ------------------ 1 file changed, 18 deletions(-) (limited to 'hw') diff --git a/hw/spapr.c b/hw/spapr.c index aa39d2d..032d259 100644 --- a/hw/spapr.c +++ b/hw/spapr.c @@ -538,8 +538,6 @@ static void spapr_reset(void *opaque) { sPAPREnvironment *spapr = (sPAPREnvironment *)opaque; - fprintf(stderr, "sPAPR reset\n"); - /* flush out the hash table */ memset(spapr->htab, 0, spapr->htab_size); @@ -744,14 +742,6 @@ static void ppc_spapr_init(ram_addr_t ram_size, exit(1); } - fprintf(stderr, "sPAPR memory map:\n"); - fprintf(stderr, "RTAS : 0x%08lx..%08lx\n", - (unsigned long)spapr->rtas_addr, - (unsigned long)(spapr->rtas_addr + spapr->rtas_size - 1)); - fprintf(stderr, "FDT : 0x%08lx..%08lx\n", - (unsigned long)spapr->fdt_addr, - (unsigned long)(spapr->fdt_addr + FDT_MAX_SIZE - 1)); - if (kernel_filename) { uint64_t lowaddr = 0; @@ -767,8 +757,6 @@ static void ppc_spapr_init(ram_addr_t ram_size, kernel_filename); exit(1); } - fprintf(stderr, "Kernel : 0x%08x..%08lx\n", - KERNEL_LOAD_ADDR, KERNEL_LOAD_ADDR + kernel_size - 1); /* load initrd */ if (initrd_filename) { @@ -783,8 +771,6 @@ static void ppc_spapr_init(ram_addr_t ram_size, initrd_filename); exit(1); } - fprintf(stderr, "Ramdisk : 0x%08lx..%08lx\n", - (long)initrd_base, (long)(initrd_base + initrd_size - 1)); } else { initrd_base = 0; initrd_size = 0; @@ -798,10 +784,6 @@ static void ppc_spapr_init(ram_addr_t ram_size, exit(1); } g_free(filename); - fprintf(stderr, "Firmware load : 0x%08x..%08lx\n", - 0, fw_size); - fprintf(stderr, "Firmware runtime : 0x%08lx..%08lx\n", - load_limit, (unsigned long)spapr->fdt_addr); spapr->entry_point = 0x100; -- cgit v1.1 From a307d59434ba78b97544b42b8cfd24a1b62e39a6 Mon Sep 17 00:00:00 2001 From: Alexey Kardashevskiy Date: Tue, 7 Aug 2012 16:10:32 +0000 Subject: pseries: Rework irq assignment to avoid carrying qemu_irqs around Currently, the interfaces in the pseries machine code for assignment and setup of interrupts pass around qemu_irq objects. That was done in an attempt not to be too closely linked to the specific XICS interrupt controller. However interactions with the device tree setup made that attempt rather futile, and XICS is part of the PAPR spec anyway, so this really just meant we had to carry both the qemu_irq pointers and the XICS irq numbers around. This mess will just get worse when we add upcoming PCI MSI support, since that will require tracking a bunch more interrupt. Therefore, this patch reworks the spapr code to just use XICS irq numbers (roughly equivalent to GSIs on x86) and only retrieve the qemu_irq pointers from the XICS code when we need them (a trivial lookup). This is a reworked and generalized version of an earlier spapr_pci specific patch from Alexey Kardashevskiy. Signed-off-by: Alexey Kardashevskiy Signed-off-by: David Gibson [agraf: fix checkpath warning] Signed-off-by: Alexander Graf --- hw/spapr.c | 18 +++++++----------- hw/spapr.h | 11 +++++------ hw/spapr_llan.c | 2 +- hw/spapr_pci.c | 14 ++++++-------- hw/spapr_pci.h | 8 ++++++-- hw/spapr_vio.c | 12 ++++++------ hw/spapr_vio.h | 8 ++++++-- hw/spapr_vty.c | 2 +- hw/xics.c | 12 +++++++++--- hw/xics.h | 5 +++-- 10 files changed, 50 insertions(+), 42 deletions(-) (limited to 'hw') diff --git a/hw/spapr.c b/hw/spapr.c index 032d259..40bb6d3 100644 --- a/hw/spapr.c +++ b/hw/spapr.c @@ -84,11 +84,9 @@ sPAPREnvironment *spapr; -qemu_irq spapr_allocate_irq(uint32_t hint, uint32_t *irq_num, - enum xics_irq_type type) +int spapr_allocate_irq(int hint, enum xics_irq_type type) { - uint32_t irq; - qemu_irq qirq; + int irq; if (hint) { irq = hint; @@ -97,16 +95,14 @@ qemu_irq spapr_allocate_irq(uint32_t hint, uint32_t *irq_num, irq = spapr->next_irq++; } - qirq = xics_assign_irq(spapr->icp, irq, type); - if (!qirq) { - return NULL; + /* Configure irq type */ + if (!xics_get_qirq(spapr->icp, irq)) { + return 0; } - if (irq_num) { - *irq_num = irq; - } + xics_set_irq_type(spapr->icp, irq, type); - return qirq; + return irq; } static int spapr_set_associativity(void *fdt, sPAPREnvironment *spapr) diff --git a/hw/spapr.h b/hw/spapr.h index fe40e7d..b5cf6af 100644 --- a/hw/spapr.h +++ b/hw/spapr.h @@ -289,17 +289,16 @@ void spapr_register_hypercall(target_ulong opcode, spapr_hcall_fn fn); target_ulong spapr_hypercall(CPUPPCState *env, target_ulong opcode, target_ulong *args); -qemu_irq spapr_allocate_irq(uint32_t hint, uint32_t *irq_num, - enum xics_irq_type type); +int spapr_allocate_irq(int hint, enum xics_irq_type type); -static inline qemu_irq spapr_allocate_msi(uint32_t hint, uint32_t *irq_num) +static inline int spapr_allocate_msi(int hint) { - return spapr_allocate_irq(hint, irq_num, XICS_MSI); + return spapr_allocate_irq(hint, XICS_MSI); } -static inline qemu_irq spapr_allocate_lsi(uint32_t hint, uint32_t *irq_num) +static inline int spapr_allocate_lsi(int hint) { - return spapr_allocate_irq(hint, irq_num, XICS_LSI); + return spapr_allocate_irq(hint, XICS_LSI); } static inline uint32_t rtas_ld(target_ulong phys, int n) diff --git a/hw/spapr_llan.c b/hw/spapr_llan.c index 01e54f3..bd3f131 100644 --- a/hw/spapr_llan.c +++ b/hw/spapr_llan.c @@ -169,7 +169,7 @@ static ssize_t spapr_vlan_receive(NetClientState *nc, const uint8_t *buf, } if (sdev->signal_state & 1) { - qemu_irq_pulse(sdev->qirq); + qemu_irq_pulse(spapr_vio_qirq(sdev)); } return size; diff --git a/hw/spapr_pci.c b/hw/spapr_pci.c index 2913540..65ae8c4 100644 --- a/hw/spapr_pci.c +++ b/hw/spapr_pci.c @@ -223,7 +223,7 @@ static void pci_spapr_set_irq(void *opaque, int irq_num, int level) */ sPAPRPHBState *phb = opaque; - qemu_set_irq(phb->lsi_table[irq_num].qirq, level); + qemu_set_irq(spapr_phb_lsi_qirq(phb, irq_num), level); } static uint64_t spapr_io_read(void *opaque, target_phys_addr_t addr, @@ -329,16 +329,14 @@ static int spapr_phb_init(SysBusDevice *s) /* Initialize the LSI table */ for (i = 0; i < PCI_NUM_PINS; i++) { - qemu_irq qirq; - uint32_t num; + uint32_t irq; - qirq = spapr_allocate_lsi(0, &num); - if (!qirq) { + irq = spapr_allocate_lsi(0); + if (!irq) { return -1; } - phb->lsi_table[i].dt_irq = num; - phb->lsi_table[i].qirq = qirq; + phb->lsi_table[i].irq = irq; } return 0; @@ -477,7 +475,7 @@ int spapr_populate_pci_dt(sPAPRPHBState *phb, irqmap[2] = 0; irqmap[3] = cpu_to_be32(j+1); irqmap[4] = cpu_to_be32(xics_phandle); - irqmap[5] = cpu_to_be32(phb->lsi_table[lsi_num].dt_irq); + irqmap[5] = cpu_to_be32(phb->lsi_table[lsi_num].irq); irqmap[6] = cpu_to_be32(0x8); } } diff --git a/hw/spapr_pci.h b/hw/spapr_pci.h index dd66f4b..6bba885 100644 --- a/hw/spapr_pci.h +++ b/hw/spapr_pci.h @@ -40,13 +40,17 @@ typedef struct sPAPRPHBState { DMAContext *dma; struct { - uint32_t dt_irq; - qemu_irq qirq; + uint32_t irq; } lsi_table[PCI_NUM_PINS]; QLIST_ENTRY(sPAPRPHBState) list; } sPAPRPHBState; +static inline qemu_irq spapr_phb_lsi_qirq(struct sPAPRPHBState *phb, int pin) +{ + return xics_get_qirq(spapr->icp, phb->lsi_table[pin].irq); +} + #define SPAPR_PCI_MEM_WIN_BUS_OFFSET 0x80000000ULL #define SPAPR_PCI_IO_WIN_SIZE 0x10000 diff --git a/hw/spapr_vio.c b/hw/spapr_vio.c index 05b5503..3abe853 100644 --- a/hw/spapr_vio.c +++ b/hw/spapr_vio.c @@ -49,7 +49,7 @@ #endif static Property spapr_vio_props[] = { - DEFINE_PROP_UINT32("irq", VIOsPAPRDevice, vio_irq_num, 0), \ + DEFINE_PROP_UINT32("irq", VIOsPAPRDevice, irq, 0), \ DEFINE_PROP_END_OF_LIST(), }; @@ -132,8 +132,8 @@ static int vio_make_devnode(VIOsPAPRDevice *dev, } } - if (dev->qirq) { - uint32_t ints_prop[] = {cpu_to_be32(dev->vio_irq_num), 0}; + if (dev->irq) { + uint32_t ints_prop[] = {cpu_to_be32(dev->irq), 0}; ret = fdt_setprop(fdt, node_off, "interrupts", ints_prop, sizeof(ints_prop)); @@ -306,7 +306,7 @@ int spapr_vio_send_crq(VIOsPAPRDevice *dev, uint8_t *crq) dev->crq.qnext = (dev->crq.qnext + 16) % dev->crq.qsize; if (dev->signal_state & 1) { - qemu_irq_pulse(dev->qirq); + qemu_irq_pulse(spapr_vio_qirq(dev)); } return 0; @@ -459,8 +459,8 @@ static int spapr_vio_busdev_init(DeviceState *qdev) dev->qdev.id = id; } - dev->qirq = spapr_allocate_msi(dev->vio_irq_num, &dev->vio_irq_num); - if (!dev->qirq) { + dev->irq = spapr_allocate_msi(dev->irq); + if (!dev->irq) { return -1; } diff --git a/hw/spapr_vio.h b/hw/spapr_vio.h index 6f9a498..ea6aa43 100644 --- a/hw/spapr_vio.h +++ b/hw/spapr_vio.h @@ -61,8 +61,7 @@ struct VIOsPAPRDevice { DeviceState qdev; uint32_t reg; uint32_t flags; - qemu_irq qirq; - uint32_t vio_irq_num; + uint32_t irq; target_ulong signal_state; VIOsPAPR_CRQ crq; DMAContext *dma; @@ -85,6 +84,11 @@ extern int spapr_populate_chosen_stdout(void *fdt, VIOsPAPRBus *bus); extern int spapr_vio_signal(VIOsPAPRDevice *dev, target_ulong mode); +static inline qemu_irq spapr_vio_qirq(VIOsPAPRDevice *dev) +{ + return xics_get_qirq(spapr->icp, dev->irq); +} + static inline bool spapr_vio_dma_valid(VIOsPAPRDevice *dev, uint64_t taddr, uint32_t size, DMADirection dir) { diff --git a/hw/spapr_vty.c b/hw/spapr_vty.c index 99e52cc..5da17a3 100644 --- a/hw/spapr_vty.c +++ b/hw/spapr_vty.c @@ -26,7 +26,7 @@ static void vty_receive(void *opaque, const uint8_t *buf, int size) if ((dev->in == dev->out) && size) { /* toggle line to simulate edge interrupt */ - qemu_irq_pulse(dev->sdev.qirq); + qemu_irq_pulse(spapr_vio_qirq(&dev->sdev)); } for (i = 0; i < size; i++) { assert((dev->in - dev->out) < VTERM_BUFSIZE); diff --git a/hw/xics.c b/hw/xics.c index 668a0d6..b674771 100644 --- a/hw/xics.c +++ b/hw/xics.c @@ -315,18 +315,24 @@ static void ics_eoi(struct ics_state *ics, int nr) * Exported functions */ -qemu_irq xics_assign_irq(struct icp_state *icp, int irq, - enum xics_irq_type type) +qemu_irq xics_get_qirq(struct icp_state *icp, int irq) { if ((irq < icp->ics->offset) || (irq >= (icp->ics->offset + icp->ics->nr_irqs))) { return NULL; } + return icp->ics->qirqs[irq - icp->ics->offset]; +} + +void xics_set_irq_type(struct icp_state *icp, int irq, + enum xics_irq_type type) +{ + assert((irq >= icp->ics->offset) + && (irq < (icp->ics->offset + icp->ics->nr_irqs))); assert((type == XICS_MSI) || (type == XICS_LSI)); icp->ics->irqs[irq - icp->ics->offset].type = type; - return icp->ics->qirqs[irq - icp->ics->offset]; } static target_ulong h_cppr(CPUPPCState *env, sPAPREnvironment *spapr, diff --git a/hw/xics.h b/hw/xics.h index 2080159..99b96ac 100644 --- a/hw/xics.h +++ b/hw/xics.h @@ -36,8 +36,9 @@ enum xics_irq_type { XICS_LSI, /* Level-signalled interrupt */ }; -qemu_irq xics_assign_irq(struct icp_state *icp, int irq, - enum xics_irq_type type); +qemu_irq xics_get_qirq(struct icp_state *icp, int irq); +void xics_set_irq_type(struct icp_state *icp, int irq, + enum xics_irq_type type); struct icp_state *xics_system_init(int nr_irqs); -- cgit v1.1 From fa28f71b4a88cdb796f1e0a308205c6be604d3f3 Mon Sep 17 00:00:00 2001 From: Alexey Kardashevskiy Date: Tue, 7 Aug 2012 16:10:33 +0000 Subject: pseries: Separate PCI RTAS setup from common from emulation specific PCI setup Currently the RTAS functions for handling PCI are registered from the class init code for the PCI host bridge. That sort of makes sense now, but will break in the future when vfio gives us multiple types of host bridge for pseries (emulated and pass-through, at least). The RTAS functions will be common across all host bridge types (and will call out to different places internally depending on the type). So, this patch moves the RTAS registration into its own function called direct from the machine setup code. Signed-off-by: Alexey Kardashevskiy Signed-off-by: David Gibson Signed-off-by: Alexander Graf --- hw/spapr.c | 2 ++ hw/spapr_pci.c | 13 ++++++++----- hw/spapr_pci.h | 2 ++ 3 files changed, 12 insertions(+), 5 deletions(-) (limited to 'hw') diff --git a/hw/spapr.c b/hw/spapr.c index 40bb6d3..8153c05 100644 --- a/hw/spapr.c +++ b/hw/spapr.c @@ -704,6 +704,8 @@ static void ppc_spapr_init(ram_addr_t ram_size, } /* Set up PCI */ + spapr_pci_rtas_init(); + spapr_create_phb(spapr, "pci", SPAPR_PCI_BUID, SPAPR_PCI_MEM_WIN_ADDR, SPAPR_PCI_MEM_WIN_SIZE, diff --git a/hw/spapr_pci.c b/hw/spapr_pci.c index 65ae8c4..fcc358e 100644 --- a/hw/spapr_pci.c +++ b/hw/spapr_pci.c @@ -359,11 +359,6 @@ static void spapr_phb_class_init(ObjectClass *klass, void *data) sdc->init = spapr_phb_init; dc->props = spapr_phb_properties; - - spapr_rtas_register("read-pci-config", rtas_read_pci_config); - spapr_rtas_register("write-pci-config", rtas_write_pci_config); - spapr_rtas_register("ibm,read-pci-config", rtas_ibm_read_pci_config); - spapr_rtas_register("ibm,write-pci-config", rtas_ibm_write_pci_config); } static TypeInfo spapr_phb_info = { @@ -488,6 +483,14 @@ int spapr_populate_pci_dt(sPAPRPHBState *phb, return 0; } +void spapr_pci_rtas_init(void) +{ + spapr_rtas_register("read-pci-config", rtas_read_pci_config); + spapr_rtas_register("write-pci-config", rtas_write_pci_config); + spapr_rtas_register("ibm,read-pci-config", rtas_ibm_read_pci_config); + spapr_rtas_register("ibm,write-pci-config", rtas_ibm_write_pci_config); +} + static void register_types(void) { type_register_static(&spapr_phb_info); diff --git a/hw/spapr_pci.h b/hw/spapr_pci.h index 6bba885..2aee67f 100644 --- a/hw/spapr_pci.h +++ b/hw/spapr_pci.h @@ -63,4 +63,6 @@ int spapr_populate_pci_dt(sPAPRPHBState *phb, uint32_t xics_phandle, void *fdt); +void spapr_pci_rtas_init(void); + #endif /* __HW_SPAPR_PCI_H__ */ -- cgit v1.1 From f4b9523ba6388f6f951933de3f9a76e2e9ea2ede Mon Sep 17 00:00:00 2001 From: Alexey Kardashevskiy Date: Tue, 7 Aug 2012 16:10:34 +0000 Subject: pseries: added allocator for a block of IRQs The patch adds a simple helper which allocates a consecutive sequence of IRQs calling spapr_allocate_irq for each and checks that allocated IRQs go consequently. The patch is required for upcoming support of MSI/MSIX on POWER. Signed-off-by: Alexey Kardashevskiy Signed-off-by: David Gibson Signed-off-by: Alexander Graf --- hw/spapr.c | 26 ++++++++++++++++++++++++++ hw/spapr.h | 1 + 2 files changed, 27 insertions(+) (limited to 'hw') diff --git a/hw/spapr.c b/hw/spapr.c index 8153c05..afbdbc5 100644 --- a/hw/spapr.c +++ b/hw/spapr.c @@ -105,6 +105,32 @@ int spapr_allocate_irq(int hint, enum xics_irq_type type) return irq; } +/* Allocate block of consequtive IRQs, returns a number of the first */ +int spapr_allocate_irq_block(int num, enum xics_irq_type type) +{ + int first = -1; + int i; + + for (i = 0; i < num; ++i) { + int irq; + + irq = spapr_allocate_irq(0, type); + if (!irq) { + return -1; + } + + if (0 == i) { + first = irq; + } + + /* If the above doesn't create a consecutive block then that's + * an internal bug */ + assert(irq == (first + i)); + } + + return first; +} + static int spapr_set_associativity(void *fdt, sPAPREnvironment *spapr) { int ret = 0, offset; diff --git a/hw/spapr.h b/hw/spapr.h index b5cf6af..6229769 100644 --- a/hw/spapr.h +++ b/hw/spapr.h @@ -290,6 +290,7 @@ target_ulong spapr_hypercall(CPUPPCState *env, target_ulong opcode, target_ulong *args); int spapr_allocate_irq(int hint, enum xics_irq_type type); +int spapr_allocate_irq_block(int num, enum xics_irq_type type); static inline int spapr_allocate_msi(int hint) { -- cgit v1.1 From 9894c5d4b467d24e281c22f2f5e24822c9b55fb3 Mon Sep 17 00:00:00 2001 From: Alexey Kardashevskiy Date: Tue, 7 Aug 2012 16:10:35 +0000 Subject: pseries: Export find_phb() utility function for PCI code The pseries PCI code makes use of an internal find_dev() function which locates a PCIDevice * given a (platform specific) bus ID and device address. Internally this needs to first locate the host bridge on which the device resides based on the bus ID. This patch exposes that host bridge lookup as a separate function, which we will need later in the MSI and VFIO code. Signed-off-by: Alexey Kardashevskiy Signed-off-by: David Gibson [agraf: drop trace.h inclusion] Signed-off-by: Alexander Graf --- hw/spapr_pci.c | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) (limited to 'hw') diff --git a/hw/spapr_pci.c b/hw/spapr_pci.c index fcc358e..2e38fee 100644 --- a/hw/spapr_pci.c +++ b/hw/spapr_pci.c @@ -32,24 +32,35 @@ #include "hw/pci_internals.h" -static PCIDevice *find_dev(sPAPREnvironment *spapr, - uint64_t buid, uint32_t config_addr) +static sPAPRPHBState *find_phb(sPAPREnvironment *spapr, uint64_t buid) { - int devfn = (config_addr >> 8) & 0xFF; sPAPRPHBState *phb; QLIST_FOREACH(phb, &spapr->phbs, list) { - BusChild *kid; - if (phb->buid != buid) { continue; } + return phb; + } + + return NULL; +} + +static PCIDevice *find_dev(sPAPREnvironment *spapr, uint64_t buid, + uint32_t config_addr) +{ + sPAPRPHBState *phb = find_phb(spapr, buid); + BusChild *kid; + int devfn = (config_addr >> 8) & 0xFF; + + if (!phb) { + return NULL; + } - QTAILQ_FOREACH(kid, &phb->host_state.bus->qbus.children, sibling) { - PCIDevice *dev = (PCIDevice *)kid->child; - if (dev->devfn == devfn) { - return dev; - } + QTAILQ_FOREACH(kid, &phb->host_state.bus->qbus.children, sibling) { + PCIDevice *dev = (PCIDevice *)kid->child; + if (dev->devfn == devfn) { + return dev; } } -- cgit v1.1 From a2950fb6e795e3e10fed35d347a7aa28a44be2ff Mon Sep 17 00:00:00 2001 From: Alexey Kardashevskiy Date: Tue, 7 Aug 2012 16:10:36 +0000 Subject: pseries: Add trace event for PCI irqs This adds a trace event in the pseries PCI specific set_irq() function to assist in debugging. Signed-off-by: Alexey Kardashevskiy Signed-off-by: David Gibson [agraf: add trace.h include] Signed-off-by: Alexander Graf --- hw/spapr_pci.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'hw') diff --git a/hw/spapr_pci.c b/hw/spapr_pci.c index 2e38fee..1eb1a7e 100644 --- a/hw/spapr_pci.c +++ b/hw/spapr_pci.c @@ -29,6 +29,7 @@ #include "hw/spapr_pci.h" #include "exec-memory.h" #include +#include "trace.h" #include "hw/pci_internals.h" @@ -234,6 +235,7 @@ static void pci_spapr_set_irq(void *opaque, int irq_num, int level) */ sPAPRPHBState *phb = opaque; + trace_spapr_pci_lsi_set(phb->busname, irq_num, phb->lsi_table[irq_num].irq); qemu_set_irq(spapr_phb_lsi_qirq(phb, irq_num), level); } -- cgit v1.1 From 0ee2c058a3fe485b8901186179102e251a33d082 Mon Sep 17 00:00:00 2001 From: Alexey Kardashevskiy Date: Tue, 7 Aug 2012 16:10:37 +0000 Subject: pseries: Add PCI MSI/MSI-X support This patch implements MSI and MSI-X support for the pseries PCI host bridge. To do this it adds: * A "config_space_address to msi_table" map, since the MSI RTAS calls take a PCI config space address as an identifier. * A MSIX memory region to catch msi_notify()/msix_notiry() from virtio-pci and pass them to the guest via qemu_irq_pulse(). * RTAS call "ibm,change-msi" which sets up MSI vectors for a device. Note that this call may configure and return lesser number of vectors than requested. * RTAS call "ibm,query-interrupt-source-number" which translates MSI vector to interrupt controller (XICS) IRQ number. Signed-off-by: Alexey Kardashevskiy Signed-off-by: David Gibson [agraf: fix error case ndev < 0] Signed-off-by: Alexander Graf --- hw/spapr.c | 7 +- hw/spapr_pci.c | 245 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- hw/spapr_pci.h | 15 +++- 3 files changed, 263 insertions(+), 4 deletions(-) (limited to 'hw') diff --git a/hw/spapr.c b/hw/spapr.c index afbdbc5..5178721 100644 --- a/hw/spapr.c +++ b/hw/spapr.c @@ -41,6 +41,7 @@ #include "hw/spapr_vio.h" #include "hw/spapr_pci.h" #include "hw/xics.h" +#include "hw/msi.h" #include "kvm.h" #include "kvm_ppc.h" @@ -79,6 +80,7 @@ #define SPAPR_PCI_MEM_WIN_ADDR (0x10000000000ULL + 0xA0000000) #define SPAPR_PCI_MEM_WIN_SIZE 0x20000000 #define SPAPR_PCI_IO_WIN_ADDR (0x10000000000ULL + 0x80000000) +#define SPAPR_PCI_MSI_WIN_ADDR (0x10000000000ULL + 0x90000000) #define PHANDLE_XICP 0x00001111 @@ -619,6 +621,8 @@ static void ppc_spapr_init(ram_addr_t ram_size, long pteg_shift = 17; char *filename; + msi_supported = true; + spapr = g_malloc0(sizeof(*spapr)); QLIST_INIT(&spapr->phbs); @@ -735,7 +739,8 @@ static void ppc_spapr_init(ram_addr_t ram_size, spapr_create_phb(spapr, "pci", SPAPR_PCI_BUID, SPAPR_PCI_MEM_WIN_ADDR, SPAPR_PCI_MEM_WIN_SIZE, - SPAPR_PCI_IO_WIN_ADDR); + SPAPR_PCI_IO_WIN_ADDR, + SPAPR_PCI_MSI_WIN_ADDR); for (i = 0; i < nb_nics; i++) { NICInfo *nd = &nd_table[i]; diff --git a/hw/spapr_pci.c b/hw/spapr_pci.c index 1eb1a7e..780a4d6 100644 --- a/hw/spapr_pci.c +++ b/hw/spapr_pci.c @@ -24,6 +24,8 @@ */ #include "hw.h" #include "pci.h" +#include "msi.h" +#include "msix.h" #include "pci_host.h" #include "hw/spapr.h" #include "hw/spapr_pci.h" @@ -33,6 +35,17 @@ #include "hw/pci_internals.h" +/* Copied from the kernel arch/powerpc/platforms/pseries/msi.c */ +#define RTAS_QUERY_FN 0 +#define RTAS_CHANGE_FN 1 +#define RTAS_RESET_FN 2 +#define RTAS_CHANGE_MSI_FN 3 +#define RTAS_CHANGE_MSIX_FN 4 + +/* Interrupt types to return on RTAS_CHANGE_* */ +#define RTAS_TYPE_MSI 1 +#define RTAS_TYPE_MSIX 2 + static sPAPRPHBState *find_phb(sPAPREnvironment *spapr, uint64_t buid) { sPAPRPHBState *phb; @@ -211,6 +224,191 @@ static void rtas_write_pci_config(sPAPREnvironment *spapr, finish_write_pci_config(spapr, 0, addr, size, val, rets); } +/* + * Find an entry with config_addr or returns the empty one if not found AND + * alloc_new is set. + * At the moment the msi_table entries are never released so there is + * no point to look till the end of the list if we need to find the free entry. + */ +static int spapr_msicfg_find(sPAPRPHBState *phb, uint32_t config_addr, + bool alloc_new) +{ + int i; + + for (i = 0; i < SPAPR_MSIX_MAX_DEVS; ++i) { + if (!phb->msi_table[i].nvec) { + break; + } + if (phb->msi_table[i].config_addr == config_addr) { + return i; + } + } + if ((i < SPAPR_MSIX_MAX_DEVS) && alloc_new) { + trace_spapr_pci_msi("Allocating new MSI config", i, config_addr); + return i; + } + + return -1; +} + +/* + * Set MSI/MSIX message data. + * This is required for msi_notify()/msix_notify() which + * will write at the addresses via spapr_msi_write(). + */ +static void spapr_msi_setmsg(PCIDevice *pdev, target_phys_addr_t addr, + bool msix, unsigned req_num) +{ + unsigned i; + MSIMessage msg = { .address = addr, .data = 0 }; + + if (!msix) { + msi_set_message(pdev, msg); + trace_spapr_pci_msi_setup(pdev->name, 0, msg.address); + return; + } + + for (i = 0; i < req_num; ++i) { + msg.address = addr | (i << 2); + msix_set_message(pdev, i, msg); + trace_spapr_pci_msi_setup(pdev->name, i, msg.address); + } +} + +static void rtas_ibm_change_msi(sPAPREnvironment *spapr, + uint32_t token, uint32_t nargs, + target_ulong args, uint32_t nret, + target_ulong rets) +{ + uint32_t config_addr = rtas_ld(args, 0); + uint64_t buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2); + unsigned int func = rtas_ld(args, 3); + unsigned int req_num = rtas_ld(args, 4); /* 0 == remove all */ + unsigned int seq_num = rtas_ld(args, 5); + unsigned int ret_intr_type; + int ndev, irq; + sPAPRPHBState *phb = NULL; + PCIDevice *pdev = NULL; + + switch (func) { + case RTAS_CHANGE_MSI_FN: + case RTAS_CHANGE_FN: + ret_intr_type = RTAS_TYPE_MSI; + break; + case RTAS_CHANGE_MSIX_FN: + ret_intr_type = RTAS_TYPE_MSIX; + break; + default: + fprintf(stderr, "rtas_ibm_change_msi(%u) is not implemented\n", func); + rtas_st(rets, 0, -3); /* Parameter error */ + return; + } + + /* Fins sPAPRPHBState */ + phb = find_phb(spapr, buid); + if (phb) { + pdev = find_dev(spapr, buid, config_addr); + } + if (!phb || !pdev) { + rtas_st(rets, 0, -3); /* Parameter error */ + return; + } + + /* Releasing MSIs */ + if (!req_num) { + ndev = spapr_msicfg_find(phb, config_addr, false); + if (ndev < 0) { + trace_spapr_pci_msi("MSI has not been enabled", -1, config_addr); + rtas_st(rets, 0, -1); /* Hardware error */ + return; + } + trace_spapr_pci_msi("Released MSIs", ndev, config_addr); + rtas_st(rets, 0, 0); + rtas_st(rets, 1, 0); + return; + } + + /* Enabling MSI */ + + /* Find a device number in the map to add or reuse the existing one */ + ndev = spapr_msicfg_find(phb, config_addr, true); + if (ndev >= SPAPR_MSIX_MAX_DEVS || ndev < 0) { + fprintf(stderr, "No free entry for a new MSI device\n"); + rtas_st(rets, 0, -1); /* Hardware error */ + return; + } + trace_spapr_pci_msi("Configuring MSI", ndev, config_addr); + + /* Check if there is an old config and MSI number has not changed */ + if (phb->msi_table[ndev].nvec && (req_num != phb->msi_table[ndev].nvec)) { + /* Unexpected behaviour */ + fprintf(stderr, "Cannot reuse MSI config for device#%d", ndev); + rtas_st(rets, 0, -1); /* Hardware error */ + return; + } + + /* There is no cached config, allocate MSIs */ + if (!phb->msi_table[ndev].nvec) { + irq = spapr_allocate_irq_block(req_num, XICS_MSI); + if (irq < 0) { + fprintf(stderr, "Cannot allocate MSIs for device#%d", ndev); + rtas_st(rets, 0, -1); /* Hardware error */ + return; + } + phb->msi_table[ndev].irq = irq; + phb->msi_table[ndev].nvec = req_num; + phb->msi_table[ndev].config_addr = config_addr; + } + + /* Setup MSI/MSIX vectors in the device (via cfgspace or MSIX BAR) */ + spapr_msi_setmsg(pdev, phb->msi_win_addr | (ndev << 16), + ret_intr_type == RTAS_TYPE_MSIX, req_num); + + rtas_st(rets, 0, 0); + rtas_st(rets, 1, req_num); + rtas_st(rets, 2, ++seq_num); + rtas_st(rets, 3, ret_intr_type); + + trace_spapr_pci_rtas_ibm_change_msi(func, req_num); +} + +static void rtas_ibm_query_interrupt_source_number(sPAPREnvironment *spapr, + uint32_t token, + uint32_t nargs, + target_ulong args, + uint32_t nret, + target_ulong rets) +{ + uint32_t config_addr = rtas_ld(args, 0); + uint64_t buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2); + unsigned int intr_src_num = -1, ioa_intr_num = rtas_ld(args, 3); + int ndev; + sPAPRPHBState *phb = NULL; + + /* Fins sPAPRPHBState */ + phb = find_phb(spapr, buid); + if (!phb) { + rtas_st(rets, 0, -3); /* Parameter error */ + return; + } + + /* Find device descriptor and start IRQ */ + ndev = spapr_msicfg_find(phb, config_addr, false); + if (ndev < 0) { + trace_spapr_pci_msi("MSI has not been enabled", -1, config_addr); + rtas_st(rets, 0, -1); /* Hardware error */ + return; + } + + intr_src_num = phb->msi_table[ndev].irq + ioa_intr_num; + trace_spapr_pci_rtas_ibm_query_interrupt_source_number(ioa_intr_num, + intr_src_num); + + rtas_st(rets, 0, 0); + rtas_st(rets, 1, intr_src_num); + rtas_st(rets, 2, 1);/* 0 == level; 1 == edge */ +} + static int pci_spapr_swizzle(int slot, int pin) { return (slot + pin) % PCI_NUM_PINS; @@ -277,6 +475,33 @@ static const MemoryRegionOps spapr_io_ops = { }; /* + * MSI/MSIX memory region implementation. + * The handler handles both MSI and MSIX. + * For MSI-X, the vector number is encoded as a part of the address, + * data is set to 0. + * For MSI, the vector number is encoded in least bits in data. + */ +static void spapr_msi_write(void *opaque, target_phys_addr_t addr, + uint64_t data, unsigned size) +{ + sPAPRPHBState *phb = opaque; + int ndev = addr >> 16; + int vec = ((addr & 0xFFFF) >> 2) | data; + uint32_t irq = phb->msi_table[ndev].irq + vec; + + trace_spapr_pci_msi_write(addr, data, irq); + + qemu_irq_pulse(xics_get_qirq(spapr->icp, irq)); +} + +static const MemoryRegionOps spapr_msi_ops = { + /* There is no .read as the read result is undefined by PCI spec */ + .read = NULL, + .write = spapr_msi_write, + .endianness = DEVICE_LITTLE_ENDIAN +}; + +/* * PHB PCI device */ static DMAContext *spapr_pci_dma_context_fn(PCIBus *bus, void *opaque, @@ -327,6 +552,17 @@ static int spapr_phb_init(SysBusDevice *s) memory_region_add_subregion(get_system_memory(), phb->io_win_addr, &phb->iowindow); + /* As MSI/MSIX interrupts trigger by writing at MSI/MSIX vectors, + * we need to allocate some memory to catch those writes coming + * from msi_notify()/msix_notify() */ + if (msi_supported) { + sprintf(namebuf, "%s.msi", phb->dtbusname); + memory_region_init_io(&phb->msiwindow, &spapr_msi_ops, phb, + namebuf, SPAPR_MSIX_MAX_DEVS * 0x10000); + memory_region_add_subregion(get_system_memory(), phb->msi_win_addr, + &phb->msiwindow); + } + bus = pci_register_bus(&phb->host_state.busdev.qdev, phb->busname ? phb->busname : phb->dtbusname, pci_spapr_set_irq, pci_spapr_map_irq, phb, @@ -362,6 +598,7 @@ static Property spapr_phb_properties[] = { DEFINE_PROP_HEX64("mem_win_size", sPAPRPHBState, mem_win_size, 0x20000000), DEFINE_PROP_HEX64("io_win_addr", sPAPRPHBState, io_win_addr, 0), DEFINE_PROP_HEX64("io_win_size", sPAPRPHBState, io_win_size, 0x10000), + DEFINE_PROP_HEX64("msi_win_addr", sPAPRPHBState, msi_win_addr, 0), DEFINE_PROP_END_OF_LIST(), }; @@ -384,7 +621,7 @@ static TypeInfo spapr_phb_info = { void spapr_create_phb(sPAPREnvironment *spapr, const char *busname, uint64_t buid, uint64_t mem_win_addr, uint64_t mem_win_size, - uint64_t io_win_addr) + uint64_t io_win_addr, uint64_t msi_win_addr) { DeviceState *dev; @@ -397,6 +634,7 @@ void spapr_create_phb(sPAPREnvironment *spapr, qdev_prop_set_uint64(dev, "mem_win_addr", mem_win_addr); qdev_prop_set_uint64(dev, "mem_win_size", mem_win_size); qdev_prop_set_uint64(dev, "io_win_addr", io_win_addr); + qdev_prop_set_uint64(dev, "msi_win_addr", msi_win_addr); qdev_init_nofail(dev); } @@ -502,6 +740,11 @@ void spapr_pci_rtas_init(void) spapr_rtas_register("write-pci-config", rtas_write_pci_config); spapr_rtas_register("ibm,read-pci-config", rtas_ibm_read_pci_config); spapr_rtas_register("ibm,write-pci-config", rtas_ibm_write_pci_config); + if (msi_supported) { + spapr_rtas_register("ibm,query-interrupt-source-number", + rtas_ibm_query_interrupt_source_number); + spapr_rtas_register("ibm,change-msi", rtas_ibm_change_msi); + } } static void register_types(void) diff --git a/hw/spapr_pci.h b/hw/spapr_pci.h index 2aee67f..6892e4f 100644 --- a/hw/spapr_pci.h +++ b/hw/spapr_pci.h @@ -27,6 +27,8 @@ #include "hw/pci_host.h" #include "hw/xics.h" +#define SPAPR_MSIX_MAX_DEVS 32 + typedef struct sPAPRPHBState { PCIHostState host_state; @@ -36,13 +38,22 @@ typedef struct sPAPRPHBState { MemoryRegion memspace, iospace; target_phys_addr_t mem_win_addr, mem_win_size, io_win_addr, io_win_size; - MemoryRegion memwindow, iowindow; + target_phys_addr_t msi_win_addr; + MemoryRegion memwindow, iowindow, msiwindow; + + DMAContext *dma; struct { uint32_t irq; } lsi_table[PCI_NUM_PINS]; + struct { + uint32_t config_addr; + uint32_t irq; + int nvec; + } msi_table[SPAPR_MSIX_MAX_DEVS]; + QLIST_ENTRY(sPAPRPHBState) list; } sPAPRPHBState; @@ -57,7 +68,7 @@ static inline qemu_irq spapr_phb_lsi_qirq(struct sPAPRPHBState *phb, int pin) void spapr_create_phb(sPAPREnvironment *spapr, const char *busname, uint64_t buid, uint64_t mem_win_addr, uint64_t mem_win_size, - uint64_t io_win_addr); + uint64_t io_win_addr, uint64_t msi_win_addr); int spapr_populate_pci_dt(sPAPRPHBState *phb, uint32_t xics_phandle, -- cgit v1.1 From 5c4cbcf26ca9465a36fc0661b4103d55897e1a19 Mon Sep 17 00:00:00 2001 From: Alexey Kardashevskiy Date: Tue, 7 Aug 2012 16:10:38 +0000 Subject: pseries dma: DMA window params added to PHB and DT population changed Previously the only PCI bus supported was the emulated PCI bus with fixed DMA window with start at 0 and size 1GB. As we are going to support PCI pass through which DMA window properties are set by the host kernel, we have to support DMA windows with parameters other than default. This patch adds: 1. DMA window properties to sPAPRPHBState: LIOBN (bus id), start, size of the window. 2. An additional function spapr_dma_dt() to populate DMA window properties in the device tree which simply accepts all the parameters and does not try to guess what kind of IOMMU is given to it. The original spapr_dma_dt() is renamed to spapr_tcet_dma_dt(). Signed-off-by: Alexey Kardashevskiy Signed-off-by: David Gibson Signed-off-by: Alexander Graf --- hw/spapr.h | 4 +++- hw/spapr_iommu.c | 58 ++++++++++++++++++++++++++++++++++++-------------------- hw/spapr_pci.c | 11 +++++++---- hw/spapr_pci.h | 4 +++- hw/spapr_vio.c | 2 +- 5 files changed, 51 insertions(+), 28 deletions(-) (limited to 'hw') diff --git a/hw/spapr.h b/hw/spapr.h index 6229769..ac34a17 100644 --- a/hw/spapr.h +++ b/hw/spapr.h @@ -337,6 +337,8 @@ void spapr_iommu_init(void); DMAContext *spapr_tce_new_dma_context(uint32_t liobn, size_t window_size); void spapr_tce_free(DMAContext *dma); int spapr_dma_dt(void *fdt, int node_off, const char *propname, - DMAContext *dma); + uint32_t liobn, uint64_t window, uint32_t size); +int spapr_tcet_dma_dt(void *fdt, int node_off, const char *propname, + DMAContext *dma); #endif /* !defined (__HW_SPAPR_H__) */ diff --git a/hw/spapr_iommu.c b/hw/spapr_iommu.c index 388ffa4..53b7317 100644 --- a/hw/spapr_iommu.c +++ b/hw/spapr_iommu.c @@ -216,31 +216,47 @@ void spapr_iommu_init(void) } int spapr_dma_dt(void *fdt, int node_off, const char *propname, - DMAContext *dma) + uint32_t liobn, uint64_t window, uint32_t size) { - if (dma) { - sPAPRTCETable *tcet = DO_UPCAST(sPAPRTCETable, dma, dma); - uint32_t dma_prop[] = {cpu_to_be32(tcet->liobn), - 0, 0, - 0, cpu_to_be32(tcet->window_size)}; - int ret; - - ret = fdt_setprop_cell(fdt, node_off, "ibm,#dma-address-cells", 2); - if (ret < 0) { - return ret; - } + uint32_t dma_prop[5]; + int ret; + + dma_prop[0] = cpu_to_be32(liobn); + dma_prop[1] = cpu_to_be32(window >> 32); + dma_prop[2] = cpu_to_be32(window & 0xFFFFFFFF); + dma_prop[3] = 0; /* window size is 32 bits */ + dma_prop[4] = cpu_to_be32(size); + + ret = fdt_setprop_cell(fdt, node_off, "ibm,#dma-address-cells", 2); + if (ret < 0) { + return ret; + } - ret = fdt_setprop_cell(fdt, node_off, "ibm,#dma-size-cells", 2); - if (ret < 0) { - return ret; - } + ret = fdt_setprop_cell(fdt, node_off, "ibm,#dma-size-cells", 2); + if (ret < 0) { + return ret; + } - ret = fdt_setprop(fdt, node_off, propname, dma_prop, - sizeof(dma_prop)); - if (ret < 0) { - return ret; - } + ret = fdt_setprop(fdt, node_off, propname, dma_prop, sizeof(dma_prop)); + if (ret < 0) { + return ret; } return 0; } + +int spapr_tcet_dma_dt(void *fdt, int node_off, const char *propname, + DMAContext *iommu) +{ + if (!iommu) { + return 0; + } + + if (iommu->translate == spapr_tce_translate) { + sPAPRTCETable *tcet = DO_UPCAST(sPAPRTCETable, dma, iommu); + return spapr_dma_dt(fdt, node_off, propname, + tcet->liobn, 0, tcet->window_size); + } + + return -1; +} diff --git a/hw/spapr_pci.c b/hw/spapr_pci.c index 780a4d6..b92583a 100644 --- a/hw/spapr_pci.c +++ b/hw/spapr_pci.c @@ -518,7 +518,6 @@ static int spapr_phb_init(SysBusDevice *s) char *namebuf; int i; PCIBus *bus; - uint32_t liobn; phb->dtbusname = g_strdup_printf("pci@%" PRIx64, phb->buid); namebuf = alloca(strlen(phb->dtbusname) + 32); @@ -570,8 +569,10 @@ static int spapr_phb_init(SysBusDevice *s) PCI_DEVFN(0, 0), PCI_NUM_PINS); phb->host_state.bus = bus; - liobn = SPAPR_PCI_BASE_LIOBN | (pci_find_domain(bus) << 16); - phb->dma = spapr_tce_new_dma_context(liobn, 0x40000000); + phb->dma_liobn = SPAPR_PCI_BASE_LIOBN | (pci_find_domain(bus) << 16); + phb->dma_window_start = 0; + phb->dma_window_size = 0x40000000; + phb->dma = spapr_tce_new_dma_context(phb->dma_liobn, phb->dma_window_size); pci_setup_iommu(bus, spapr_pci_dma_context_fn, phb); QLIST_INSERT_HEAD(&spapr->phbs, phb, list); @@ -729,7 +730,9 @@ int spapr_populate_pci_dt(sPAPRPHBState *phb, _FDT(fdt_setprop(fdt, bus_off, "interrupt-map", &interrupt_map, sizeof(interrupt_map))); - spapr_dma_dt(fdt, bus_off, "ibm,dma-window", phb->dma); + spapr_dma_dt(fdt, bus_off, "ibm,dma-window", + phb->dma_liobn, phb->dma_window_start, + phb->dma_window_size); return 0; } diff --git a/hw/spapr_pci.h b/hw/spapr_pci.h index 6892e4f..7518899 100644 --- a/hw/spapr_pci.h +++ b/hw/spapr_pci.h @@ -41,7 +41,9 @@ typedef struct sPAPRPHBState { target_phys_addr_t msi_win_addr; MemoryRegion memwindow, iowindow, msiwindow; - + uint32_t dma_liobn; + uint64_t dma_window_start; + uint64_t dma_window_size; DMAContext *dma; struct { diff --git a/hw/spapr_vio.c b/hw/spapr_vio.c index 3abe853..7ca4452 100644 --- a/hw/spapr_vio.c +++ b/hw/spapr_vio.c @@ -142,7 +142,7 @@ static int vio_make_devnode(VIOsPAPRDevice *dev, } } - ret = spapr_dma_dt(fdt, node_off, "ibm,my-dma-window", dev->dma); + ret = spapr_tcet_dma_dt(fdt, node_off, "ibm,my-dma-window", dev->dma); if (ret < 0) { return ret; } -- cgit v1.1 From 3e77223293bcb9277f7b760b88f694f097f29c33 Mon Sep 17 00:00:00 2001 From: Bharat Bhushan Date: Tue, 14 Aug 2012 04:30:55 +0000 Subject: openpic: Added BRR1 register Linux mpic driver uses (changes may be in pipeline to get upstreamed soon) BRR1. This patch adds the support to emulate readonly FSL BRR1 register. Currently QEMU does not fully emulate any version on MPIC, so the MPIC Major number and Minor number are set to 0. Signed-off-by: Bharat Bhushan Signed-off-by: Alexander Graf --- hw/openpic.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'hw') diff --git a/hw/openpic.c b/hw/openpic.c index 58ef871..b9d8568 100644 --- a/hw/openpic.c +++ b/hw/openpic.c @@ -130,6 +130,17 @@ enum { #define MPIC_CPU_REG_START 0x20000 #define MPIC_CPU_REG_SIZE 0x100 + ((MAX_CPU - 1) * 0x1000) +/* + * Block Revision Register1 (BRR1): QEMU does not fully emulate + * any version on MPIC. So to start with, set the IP version to 0. + * + * NOTE: This is Freescale MPIC specific register. Keep it here till + * this code is refactored for different variants of OPENPIC and MPIC. + */ +#define FSL_BRR1_IPID (0x0040 << 16) /* 16 bit IP-block ID */ +#define FSL_BRR1_IPMJ (0x00 << 8) /* 8 bit IP major number */ +#define FSL_BRR1_IPMN 0x00 /* 8 bit IP minor number */ + enum mpic_ide_bits { IDR_EP = 31, IDR_CI0 = 30, @@ -595,6 +606,8 @@ static void openpic_gbl_write (void *opaque, target_phys_addr_t addr, uint32_t v if (addr & 0xF) return; switch (addr) { + case 0x00: /* Block Revision Register1 (BRR1) is Readonly */ + break; case 0x40: case 0x50: case 0x60: @@ -671,6 +684,7 @@ static uint32_t openpic_gbl_read (void *opaque, target_phys_addr_t addr) case 0x1090: /* PINT */ retval = 0x00000000; break; + case 0x00: /* Block Revision Register1 (BRR1) */ case 0x40: case 0x50: case 0x60: @@ -893,6 +907,9 @@ static uint32_t openpic_cpu_read_internal(void *opaque, target_phys_addr_t addr, dst = &opp->dst[idx]; addr &= 0xFF0; switch (addr) { + case 0x00: /* Block Revision Register1 (BRR1) */ + retval = FSL_BRR1_IPID | FSL_BRR1_IPMJ | FSL_BRR1_IPMN; + break; case 0x80: /* PCTP */ retval = dst->pctp; break; -- cgit v1.1