aboutsummaryrefslogtreecommitdiff
path: root/hw
diff options
context:
space:
mode:
authorAnthony Liguori <aliguori@us.ibm.com>2012-08-15 14:59:21 -0500
committerAnthony Liguori <aliguori@us.ibm.com>2012-08-15 14:59:21 -0500
commit0d16fdd732d1b211842fa96b7c90ddf9e6bde0e4 (patch)
treedc65a3de88d05fe00976f3193167d0d18ca27fcc /hw
parent76b64a7aa84965ee9c7594058a43b00c2a14c72e (diff)
parent3e77223293bcb9277f7b760b88f694f097f29c33 (diff)
Merge remote-tracking branch 'agraf/ppc-for-upstream' into staging
* agraf/ppc-for-upstream: (24 commits) openpic: Added BRR1 register pseries: Update SLOF firmware image pseries dma: DMA window params added to PHB and DT population changed pseries: Add PCI MSI/MSI-X support pseries: Add trace event for PCI irqs pseries: Export find_phb() utility function for PCI code pseries: added allocator for a block of IRQs pseries: Separate PCI RTAS setup from common from emulation specific PCI setup pseries: Rework irq assignment to avoid carrying qemu_irqs around pseries: Remove extraneous prints pseries: Update SLOF PPC: spapr: Remove global variable PPC: spapr: Rework VGA select logic xbzrle: fix compilation on ppc32 spapr: Add support for -vga option Add one new file vga-pci.h and cleanup on all platforms Revert "PPC: e500: Use new MPIC dt format" ppc: Fix bug in handling of PAPR hypercall exits PPC: e500: add generic e500 platform PPC: e500: split mpc8544ds machine from generic e500 code ...
Diffstat (limited to 'hw')
-rw-r--r--hw/alpha_pci.c1
-rw-r--r--hw/cirrus_vga.c2
-rw-r--r--hw/mips_malta.c1
-rw-r--r--hw/openpic.c17
-rw-r--r--hw/pc.c1
-rw-r--r--hw/pc.h4
-rw-r--r--hw/ppc/Makefile.objs4
-rw-r--r--hw/ppc/e500.c (renamed from hw/ppce500_mpc8544ds.c)141
-rw-r--r--hw/ppc/e500.h21
-rw-r--r--hw/ppc/e500plat.c60
-rw-r--r--hw/ppc/mpc8544ds.c61
-rw-r--r--hw/ppc_newworld.c2
-rw-r--r--hw/ppc_oldworld.c2
-rw-r--r--hw/ppc_prep.c1
-rw-r--r--hw/spapr.c101
-rw-r--r--hw/spapr.h17
-rw-r--r--hw/spapr_iommu.c58
-rw-r--r--hw/spapr_llan.c2
-rw-r--r--hw/spapr_pci.c326
-rw-r--r--hw/spapr_pci.h34
-rw-r--r--hw/spapr_vio.c14
-rw-r--r--hw/spapr_vio.h8
-rw-r--r--hw/spapr_vty.c2
-rw-r--r--hw/sun4u.c1
-rw-r--r--hw/vga-pci.c2
-rw-r--r--hw/vga-pci.h12
-rw-r--r--hw/xics.c12
-rw-r--r--hw/xics.h5
28 files changed, 706 insertions, 206 deletions
diff --git a/hw/alpha_pci.c b/hw/alpha_pci.c
index 673557781e..ea546f83f1 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 623dd688d9..e8dcc6b883 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 351c88ebca..ad23f26e59 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/openpic.c b/hw/openpic.c
index 58ef871f68..b9d856830a 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;
diff --git a/hw/pc.c b/hw/pc.c
index e8bcfc0b4b..3ed1a3caa2 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 31ccb6f495..e4db0715b8 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/Makefile.objs b/hw/ppc/Makefile.objs
index aa4bbeb664..951e407f14 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 mpc8544ds.o e500plat.o
diff --git a/hw/ppce500_mpc8544ds.c b/hw/ppc/e500.c
index 8b9fd83ce1..6f0de6d959 100644
--- a/hw/ppce500_mpc8544ds.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.
*
@@ -16,20 +16,21 @@
#include "config.h"
#include "qemu-common.h"
+#include "e500.h"
#include "net.h"
-#include "hw.h"
-#include "pc.h"
-#include "pci.h"
-#include "boards.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 "openpic.h"
-#include "ppc.h"
-#include "loader.h"
+#include "hw/openpic.h"
+#include "hw/ppc.h"
+#include "hw/loader.h"
#include "elf.h"
-#include "sysbus.h"
+#include "hw/sysbus.h"
#include "exec-memory.h"
#include "host-utils.h"
@@ -42,6 +43,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)
@@ -66,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]);
}
}
@@ -95,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);
@@ -104,31 +106,28 @@ 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,
+ 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;
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,
@@ -145,14 +144,9 @@ static int mpc8544_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) {
@@ -175,8 +169,6 @@ static int mpc8544_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);
@@ -201,7 +193,7 @@ static int mpc8544_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");
@@ -282,18 +274,15 @@ static int mpc8544_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
@@ -323,7 +312,7 @@ static int mpc8544_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]);
@@ -337,6 +326,13 @@ static int mpc8544_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 */
@@ -388,7 +384,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 +397,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,12 +413,7 @@ static void mpc8544ds_cpu_reset(void *opaque)
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)
+void ppce500_init(PPCE500Params *params)
{
MemoryRegion *address_space_mem = get_system_memory();
MemoryRegion *ram = g_new(MemoryRegion, 1);
@@ -443,8 +434,8 @@ static void mpc8544ds_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 *));
@@ -453,7 +444,7 @@ static void mpc8544ds_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);
@@ -478,11 +469,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);
}
}
@@ -542,43 +533,45 @@ static void mpc8544ds_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 = mpc8544_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);
@@ -594,17 +587,3 @@ static void mpc8544ds_init(ram_addr_t ram_size,
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/ppc/e500.h b/hw/ppc/e500.h
new file mode 100644
index 0000000000..7ae87f4e21
--- /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/e500plat.c b/hw/ppc/e500plat.c
new file mode 100644
index 0000000000..60a5cb3bd0
--- /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(&params);
+}
+
+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);
diff --git a/hw/ppc/mpc8544ds.c b/hw/ppc/mpc8544ds.c
new file mode 100644
index 0000000000..984d21cbf5
--- /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(&params);
+}
+
+
+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);
diff --git a/hw/ppc_newworld.c b/hw/ppc_newworld.c
index 4e2a6e691b..e95cfe831e 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 f2c6908534..1dcd8a6c36 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 be2b26830d..7a876164c9 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/spapr.c b/hw/spapr.c
index 81c9343ca5..5178721d49 100644
--- a/hw/spapr.c
+++ b/hw/spapr.c
@@ -41,10 +41,12 @@
#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"
#include "pci.h"
+#include "vga-pci.h"
#include "exec-memory.h"
@@ -78,16 +80,15 @@
#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
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;
@@ -96,16 +97,40 @@ 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 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 qirq;
+ return first;
}
static int spapr_set_associativity(void *fdt, sPAPREnvironment *spapr)
@@ -257,6 +282,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)));
@@ -481,7 +509,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) {
@@ -503,7 +531,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)));
@@ -532,8 +562,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);
@@ -556,6 +584,23 @@ 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)
+{
+ switch (vga_interface_type) {
+ case VGA_STD:
+ pci_vga_init(pci_bus);
+ return 1;
+ case VGA_NONE:
+ return 0;
+ default:
+ fprintf(stderr, "This vga model is not supported,"
+ "currently it only supports -vga std\n");
+ exit(0);
+ break;
+ }
+}
+
/* pSeries LPAR / sPAPR hardware init */
static void ppc_spapr_init(ram_addr_t ram_size,
const char *boot_device,
@@ -576,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);
@@ -687,10 +734,13 @@ 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,
- 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];
@@ -710,20 +760,17 @@ 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);
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;
@@ -739,8 +786,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) {
@@ -755,8 +800,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;
@@ -770,10 +813,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;
diff --git a/hw/spapr.h b/hw/spapr.h
index 9153f29a60..ac34a171e3 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
@@ -288,17 +289,17 @@ 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);
+int spapr_allocate_irq_block(int num, 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)
@@ -336,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 388ffa4b22..53b731773a 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_llan.c b/hw/spapr_llan.c
index 01e54f3675..bd3f131d7e 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 b2e4f785ea..b92583a991 100644
--- a/hw/spapr_pci.c
+++ b/hw/spapr_pci.c
@@ -24,32 +24,57 @@
*/
#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"
#include "exec-memory.h"
#include <libfdt.h>
+#include "trace.h"
#include "hw/pci_internals.h"
-static PCIDevice *find_dev(sPAPREnvironment *spapr,
- uint64_t buid, uint32_t config_addr)
+/* 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)
{
- 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;
}
}
@@ -199,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;
@@ -223,7 +433,8 @@ 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);
+ 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);
}
static uint64_t spapr_io_read(void *opaque, target_phys_addr_t addr,
@@ -264,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,
@@ -276,11 +514,10 @@ 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;
- uint32_t liobn;
phb->dtbusname = g_strdup_printf("pci@%" PRIx64, phb->buid);
namebuf = alloca(strlen(phb->dtbusname) + 32);
@@ -314,31 +551,42 @@ 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,
+ /* 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,
&phb->memspace, &phb->iospace,
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);
/* 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;
@@ -351,6 +599,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(),
};
@@ -361,11 +610,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 = {
@@ -378,7 +622,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;
@@ -391,6 +635,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);
}
@@ -406,9 +651,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];
@@ -477,7 +722,7 @@ int spapr_populate_pci_devices(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);
}
}
@@ -485,11 +730,26 @@ int spapr_populate_pci_devices(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;
}
+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);
+ 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)
{
type_register_static(&spapr_phb_info);
diff --git a/hw/spapr_pci.h b/hw/spapr_pci.h
index d9e46e22e3..7518899b85 100644
--- a/hw/spapr_pci.h
+++ b/hw/spapr_pci.h
@@ -27,8 +27,9 @@
#include "hw/pci_host.h"
#include "hw/xics.h"
+#define SPAPR_MSIX_MAX_DEVS 32
+
typedef struct sPAPRPHBState {
- SysBusDevice busdev;
PCIHostState host_state;
uint64_t buid;
@@ -37,27 +38,44 @@ 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;
+
+ uint32_t dma_liobn;
+ uint64_t dma_window_start;
+ uint64_t dma_window_size;
DMAContext *dma;
struct {
- uint32_t dt_irq;
- qemu_irq qirq;
+ 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;
+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
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,
+ void *fdt);
-int spapr_populate_pci_devices(sPAPRPHBState *phb,
- uint32_t xics_phandle,
- void *fdt);
+void spapr_pci_rtas_init(void);
#endif /* __HW_SPAPR_PCI_H__ */
diff --git a/hw/spapr_vio.c b/hw/spapr_vio.c
index 05b55032a9..7ca445216d 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));
@@ -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;
}
@@ -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 6f9a498ccd..ea6aa43e26 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 99e52cc6b7..5da17a3ff4 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/sun4u.c b/hw/sun4u.c
index 137a7c6666..07cd04273a 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 37dc019a61..9abbada8f1 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 0000000000..49abf1309d
--- /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
diff --git a/hw/xics.c b/hw/xics.c
index 668a0d6484..b674771dc4 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 208015939c..99b96ac85a 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);