aboutsummaryrefslogtreecommitdiff
path: root/hw/ppc
diff options
context:
space:
mode:
Diffstat (limited to 'hw/ppc')
-rw-r--r--hw/ppc/Makefile.objs1
-rw-r--r--hw/ppc/e500.c2
-rw-r--r--hw/ppc/e500.h2
-rw-r--r--hw/ppc/ppc.c17
-rw-r--r--hw/ppc/ppce500_spin.c9
-rw-r--r--hw/ppc/spapr.c40
-rw-r--r--hw/ppc/spapr_cpu_core.c75
-rw-r--r--hw/ppc/spapr_events.c8
-rw-r--r--hw/ppc/spapr_hcall.c4
-rw-r--r--hw/ppc/spapr_iommu.c12
-rw-r--r--hw/ppc/spapr_pci.c94
-rw-r--r--hw/ppc/spapr_rtas_ddw.c295
-rw-r--r--hw/ppc/spapr_vio.c2
-rw-r--r--hw/ppc/trace-events4
14 files changed, 479 insertions, 86 deletions
diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs
index 5cc6608e50..91a3420f47 100644
--- a/hw/ppc/Makefile.objs
+++ b/hw/ppc/Makefile.objs
@@ -8,6 +8,7 @@ obj-$(CONFIG_PSERIES) += spapr_cpu_core.o
ifeq ($(CONFIG_PCI)$(CONFIG_PSERIES)$(CONFIG_LINUX), yyy)
obj-y += spapr_pci_vfio.o
endif
+obj-$(CONFIG_PSERIES) += spapr_rtas_ddw.o
# PowerPC 4xx boards
obj-y += ppc405_boards.o ppc4xx_devs.o ppc405_uc.o ppc440_bamboo.o
obj-y += ppc4xx_pci.o
diff --git a/hw/ppc/e500.c b/hw/ppc/e500.c
index ee1c60b820..0cd534df55 100644
--- a/hw/ppc/e500.c
+++ b/hw/ppc/e500.c
@@ -601,7 +601,7 @@ static int ppce500_prep_device_tree(MachineState *machine,
}
/* Create -kernel TLB entries for BookE. */
-static inline hwaddr booke206_page_size_to_tlb(uint64_t size)
+hwaddr booke206_page_size_to_tlb(uint64_t size)
{
return 63 - clz64(size >> 10);
}
diff --git a/hw/ppc/e500.h b/hw/ppc/e500.h
index ef224ea5e6..70ba1d8f4f 100644
--- a/hw/ppc/e500.h
+++ b/hw/ppc/e500.h
@@ -26,4 +26,6 @@ typedef struct PPCE500Params {
void ppce500_init(MachineState *machine, PPCE500Params *params);
+hwaddr booke206_page_size_to_tlb(uint64_t size);
+
#endif
diff --git a/hw/ppc/ppc.c b/hw/ppc/ppc.c
index 1bcf740f0e..e4252528a6 100644
--- a/hw/ppc/ppc.c
+++ b/hw/ppc/ppc.c
@@ -699,9 +699,18 @@ static inline void cpu_ppc_decr_lower(PowerPCCPU *cpu)
static inline void cpu_ppc_hdecr_excp(PowerPCCPU *cpu)
{
+ CPUPPCState *env = &cpu->env;
+
/* Raise it */
- LOG_TB("raise decrementer exception\n");
- ppc_set_irq(cpu, PPC_INTERRUPT_HDECR, 1);
+ LOG_TB("raise hv decrementer exception\n");
+
+ /* The architecture specifies that we don't deliver HDEC
+ * interrupts in a PM state. Not only they don't cause a
+ * wakeup but they also get effectively discarded.
+ */
+ if (!env->in_pm_state) {
+ ppc_set_irq(cpu, PPC_INTERRUPT_HDECR, 1);
+ }
}
static inline void cpu_ppc_hdecr_lower(PowerPCCPU *cpu)
@@ -928,9 +937,7 @@ clk_setup_cb cpu_ppc_tb_init (CPUPPCState *env, uint32_t freq)
}
/* Create new timer */
tb_env->decr_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &cpu_ppc_decr_cb, cpu);
- if (0) {
- /* XXX: find a suitable condition to enable the hypervisor decrementer
- */
+ if (env->has_hv_mode) {
tb_env->hdecr_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &cpu_ppc_hdecr_cb,
cpu);
} else {
diff --git a/hw/ppc/ppce500_spin.c b/hw/ppc/ppce500_spin.c
index 76bd78bfd7..22c584eb8d 100644
--- a/hw/ppc/ppce500_spin.c
+++ b/hw/ppc/ppce500_spin.c
@@ -32,6 +32,7 @@
#include "sysemu/sysemu.h"
#include "hw/sysbus.h"
#include "sysemu/kvm.h"
+#include "e500.h"
#define MAX_CPUS 32
@@ -72,12 +73,6 @@ static void spin_reset(void *opaque)
}
}
-/* Create -kernel TLB entries for BookE, linearly spanning 256MB. */
-static inline hwaddr booke206_page_size_to_tlb(uint64_t size)
-{
- return ctz32(size >> 10) >> 1;
-}
-
static void mmubooke_create_initial_mapping(CPUPPCState *env,
target_ulong va,
hwaddr pa,
@@ -104,7 +99,7 @@ static void spin_kick(void *data)
hwaddr map_start;
cpu_synchronize_state(cpu);
- stl_p(&curspin->pir, env->spr[SPR_PIR]);
+ stl_p(&curspin->pir, env->spr[SPR_BOOKE_PIR]);
env->nip = ldq_p(&curspin->addr) & (map_size - 1);
env->gpr[3] = ldq_p(&curspin->r3);
env->gpr[4] = 0;
diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c
index 778fa255a9..7f33a1b2b5 100644
--- a/hw/ppc/spapr.c
+++ b/hw/ppc/spapr.c
@@ -116,15 +116,16 @@ static XICSState *try_create_xics(const char *type, int nr_servers,
static XICSState *xics_system_init(MachineState *machine,
int nr_servers, int nr_irqs, Error **errp)
{
- XICSState *icp = NULL;
+ XICSState *xics = NULL;
if (kvm_enabled()) {
Error *err = NULL;
if (machine_kernel_irqchip_allowed(machine)) {
- icp = try_create_xics(TYPE_KVM_XICS, nr_servers, nr_irqs, &err);
+ xics = try_create_xics(TYPE_XICS_SPAPR_KVM, nr_servers, nr_irqs,
+ &err);
}
- if (machine_kernel_irqchip_required(machine) && !icp) {
+ if (machine_kernel_irqchip_required(machine) && !xics) {
error_reportf_err(err,
"kernel_irqchip requested but unavailable: ");
} else {
@@ -132,11 +133,11 @@ static XICSState *xics_system_init(MachineState *machine,
}
}
- if (!icp) {
- icp = try_create_xics(TYPE_XICS, nr_servers, nr_irqs, errp);
+ if (!xics) {
+ xics = try_create_xics(TYPE_XICS_SPAPR, nr_servers, nr_irqs, errp);
}
- return icp;
+ return xics;
}
static int spapr_fixup_cpu_smt_dt(void *fdt, int offset, PowerPCCPU *cpu,
@@ -339,6 +340,9 @@ static void *spapr_create_fdt_skel(hwaddr initrd_base,
add_str(hypertas, "hcall-splpar");
add_str(hypertas, "hcall-bulk");
add_str(hypertas, "hcall-set-mode");
+ add_str(hypertas, "hcall-sprg0");
+ add_str(hypertas, "hcall-copy");
+ add_str(hypertas, "hcall-debug");
add_str(qemu_hypertas, "hcall-memop1");
fdt = g_malloc0(FDT_MAX_SIZE);
@@ -1767,6 +1771,13 @@ static void ppc_spapr_init(MachineState *machine)
spapr->vrma_adjust = 1;
spapr->rma_size = MIN(spapr->rma_size, 0x10000000);
}
+
+ /* Actually we don't support unbounded RMA anymore since we
+ * added proper emulation of HV mode. The max we can get is
+ * 16G which also happens to be what we configure for PAPR
+ * mode so make sure we don't do anything bigger than that
+ */
+ spapr->rma_size = MIN(spapr->rma_size, 0x400000000ull);
}
if (spapr->rma_size > node0_size) {
@@ -1779,9 +1790,9 @@ static void ppc_spapr_init(MachineState *machine)
load_limit = MIN(spapr->rma_size, RTAS_MAX_ADDR) - FW_OVERHEAD;
/* Set up Interrupt Controller before we create the VCPUs */
- spapr->icp = xics_system_init(machine,
- DIV_ROUND_UP(max_cpus * smt, smp_threads),
- XICS_IRQS, &error_fatal);
+ spapr->xics = xics_system_init(machine,
+ DIV_ROUND_UP(max_cpus * smt, smp_threads),
+ XICS_IRQS_SPAPR, &error_fatal);
if (smc->dr_lmb_enabled) {
spapr_validate_node_memory(machine, &error_fatal);
@@ -2367,8 +2378,8 @@ static HotpluggableCPUList *spapr_query_hotpluggable_cpus(MachineState *machine)
cpu_item->type = spapr_get_cpu_core_type(machine->cpu_model);
cpu_item->vcpus_count = smp_threads;
- cpu_props->has_core = true;
- cpu_props->core = i * smt;
+ cpu_props->has_core_id = true;
+ cpu_props->core_id = i * smt;
/* TODO: add 'has_node/node' here to describe
to which node core belongs */
@@ -2485,7 +2496,12 @@ DEFINE_SPAPR_MACHINE(2_7, "2.7", true);
* pseries-2.6
*/
#define SPAPR_COMPAT_2_6 \
- HW_COMPAT_2_6
+ HW_COMPAT_2_6 \
+ { \
+ .driver = TYPE_SPAPR_PCI_HOST_BRIDGE,\
+ .property = "ddw",\
+ .value = stringify(off),\
+ },
static void spapr_machine_2_6_instance_options(MachineState *machine)
{
diff --git a/hw/ppc/spapr_cpu_core.c b/hw/ppc/spapr_cpu_core.c
index 3a5da09b99..70b6b0b5ee 100644
--- a/hw/ppc/spapr_cpu_core.c
+++ b/hw/ppc/spapr_cpu_core.c
@@ -42,7 +42,7 @@ static void spapr_cpu_destroy(PowerPCCPU *cpu)
{
sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine());
- xics_cpu_destroy(spapr->icp, cpu);
+ xics_cpu_destroy(spapr->xics, cpu);
qemu_unregister_reset(spapr_cpu_reset, cpu);
}
@@ -76,7 +76,7 @@ void spapr_cpu_init(sPAPRMachineState *spapr, PowerPCCPU *cpu, Error **errp)
}
}
- xics_cpu_setup(spapr->icp, cpu);
+ xics_cpu_setup(spapr->xics, cpu);
qemu_register_reset(spapr_cpu_reset, cpu);
spapr_cpu_reset(cpu);
@@ -102,7 +102,6 @@ static void spapr_core_release(DeviceState *dev, void *opaque)
const char *typename = object_class_get_name(sc->cpu_class);
size_t size = object_type_get_instance_size(typename);
sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine());
- sPAPRCPUCore *core = SPAPR_CPU_CORE(OBJECT(dev));
CPUCore *cc = CPU_CORE(dev);
int smt = kvmppc_smt_threads();
int i;
@@ -120,7 +119,7 @@ static void spapr_core_release(DeviceState *dev, void *opaque)
spapr->cores[cc->core_id / smt] = NULL;
- g_free(core->threads);
+ g_free(sc->threads);
object_unparent(OBJECT(dev));
}
@@ -260,23 +259,24 @@ out:
error_propagate(errp, local_err);
}
-static int spapr_cpu_core_realize_child(Object *child, void *opaque)
+static void spapr_cpu_core_realize_child(Object *child, Error **errp)
{
- Error **errp = opaque;
+ Error *local_err = NULL;
sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine());
CPUState *cs = CPU(child);
PowerPCCPU *cpu = POWERPC_CPU(cs);
- object_property_set_bool(child, true, "realized", errp);
- if (*errp) {
- return 1;
+ object_property_set_bool(child, true, "realized", &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
}
- spapr_cpu_init(spapr, cpu, errp);
- if (*errp) {
- return 1;
+ spapr_cpu_init(spapr, cpu, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
}
- return 0;
}
static void spapr_cpu_core_realize(DeviceState *dev, Error **errp)
@@ -286,13 +286,13 @@ static void spapr_cpu_core_realize(DeviceState *dev, Error **errp)
const char *typename = object_class_get_name(sc->cpu_class);
size_t size = object_type_get_instance_size(typename);
Error *local_err = NULL;
- Object *obj;
- int i;
+ void *obj;
+ int i, j;
sc->threads = g_malloc0(size * cc->nr_threads);
for (i = 0; i < cc->nr_threads; i++) {
char id[32];
- void *obj = sc->threads + i * size;
+ obj = sc->threads + i * size;
object_initialize(obj, size, typename);
snprintf(id, sizeof(id), "thread[%d]", i);
@@ -300,19 +300,23 @@ static void spapr_cpu_core_realize(DeviceState *dev, Error **errp)
if (local_err) {
goto err;
}
+ object_unref(obj);
}
- object_child_foreach(OBJECT(dev), spapr_cpu_core_realize_child, &local_err);
- if (local_err) {
- goto err;
- } else {
- return;
+
+ for (j = 0; j < cc->nr_threads; j++) {
+ obj = sc->threads + j * size;
+
+ spapr_cpu_core_realize_child(obj, &local_err);
+ if (local_err) {
+ goto err;
+ }
}
+ return;
err:
- while (i >= 0) {
+ while (--i >= 0) {
obj = sc->threads + i * size;
object_unparent(obj);
- i--;
}
g_free(sc->threads);
error_propagate(errp, local_err);
@@ -326,7 +330,6 @@ static void spapr_cpu_core_class_init(ObjectClass *oc, void *data)
/*
* instance_init routines from different flavours of sPAPR CPU cores.
- * TODO: Add support for 'host' core type.
*/
#define SPAPR_CPU_CORE_INITFN(_type, _fname) \
static void glue(glue(spapr_cpu_core_, _fname), _initfn(Object *obj)) \
@@ -339,10 +342,15 @@ static void glue(glue(spapr_cpu_core_, _fname), _initfn(Object *obj)) \
core->cpu_class = oc; \
}
+SPAPR_CPU_CORE_INITFN(970mp_v1.0, 970MP_v10);
+SPAPR_CPU_CORE_INITFN(970mp_v1.1, 970MP_v11);
+SPAPR_CPU_CORE_INITFN(970_v2.2, 970);
+SPAPR_CPU_CORE_INITFN(POWER5+_v2.1, POWER5plus);
SPAPR_CPU_CORE_INITFN(POWER7_v2.3, POWER7);
SPAPR_CPU_CORE_INITFN(POWER7+_v2.1, POWER7plus);
SPAPR_CPU_CORE_INITFN(POWER8_v2.0, POWER8);
SPAPR_CPU_CORE_INITFN(POWER8E_v2.1, POWER8E);
+SPAPR_CPU_CORE_INITFN(POWER8NVL_v1.0, POWER8NVL);
typedef struct SPAPRCoreInfo {
const char *name;
@@ -350,6 +358,21 @@ typedef struct SPAPRCoreInfo {
} SPAPRCoreInfo;
static const SPAPRCoreInfo spapr_cores[] = {
+ /* 970 and aliaes */
+ { .name = "970_v2.2", .initfn = spapr_cpu_core_970_initfn },
+ { .name = "970", .initfn = spapr_cpu_core_970_initfn },
+
+ /* 970MP variants and aliases */
+ { .name = "970MP_v1.0", .initfn = spapr_cpu_core_970MP_v10_initfn },
+ { .name = "970mp_v1.0", .initfn = spapr_cpu_core_970MP_v10_initfn },
+ { .name = "970MP_v1.1", .initfn = spapr_cpu_core_970MP_v11_initfn },
+ { .name = "970mp_v1.1", .initfn = spapr_cpu_core_970MP_v11_initfn },
+ { .name = "970mp", .initfn = spapr_cpu_core_970MP_v11_initfn },
+
+ /* POWER5 and aliases */
+ { .name = "POWER5+_v2.1", .initfn = spapr_cpu_core_POWER5plus_initfn },
+ { .name = "POWER5+", .initfn = spapr_cpu_core_POWER5plus_initfn },
+
/* POWER7 and aliases */
{ .name = "POWER7_v2.3", .initfn = spapr_cpu_core_POWER7_initfn },
{ .name = "POWER7", .initfn = spapr_cpu_core_POWER7_initfn },
@@ -367,6 +390,10 @@ static const SPAPRCoreInfo spapr_cores[] = {
{ .name = "POWER8E_v2.1", .initfn = spapr_cpu_core_POWER8E_initfn },
{ .name = "POWER8E", .initfn = spapr_cpu_core_POWER8E_initfn },
+ /* POWER8NVL and aliases */
+ { .name = "POWER8NVL_v1.0", .initfn = spapr_cpu_core_POWER8NVL_initfn },
+ { .name = "POWER8NVL", .initfn = spapr_cpu_core_POWER8NVL_initfn },
+
{ .name = NULL }
};
diff --git a/hw/ppc/spapr_events.c b/hw/ppc/spapr_events.c
index af8099220e..b0668b34a9 100644
--- a/hw/ppc/spapr_events.c
+++ b/hw/ppc/spapr_events.c
@@ -386,7 +386,7 @@ static void spapr_powerdown_req(Notifier *n, void *opaque)
rtas_event_log_queue(RTAS_LOG_TYPE_EPOW, new_epow, true);
- qemu_irq_pulse(xics_get_qirq(spapr->icp, spapr->check_exception_irq));
+ qemu_irq_pulse(xics_get_qirq(spapr->xics, spapr->check_exception_irq));
}
static void spapr_hotplug_set_signalled(uint32_t drc_index)
@@ -468,7 +468,7 @@ static void spapr_hotplug_req_event(uint8_t hp_id, uint8_t hp_action,
rtas_event_log_queue(RTAS_LOG_TYPE_HOTPLUG, new_hp, true);
- qemu_irq_pulse(xics_get_qirq(spapr->icp, spapr->check_exception_irq));
+ qemu_irq_pulse(xics_get_qirq(spapr->xics, spapr->check_exception_irq));
}
void spapr_hotplug_req_add_by_index(sPAPRDRConnector *drc)
@@ -551,7 +551,7 @@ static void check_exception(PowerPCCPU *cpu, sPAPRMachineState *spapr,
* interrupts.
*/
if (rtas_event_log_contains(mask, true)) {
- qemu_irq_pulse(xics_get_qirq(spapr->icp, spapr->check_exception_irq));
+ qemu_irq_pulse(xics_get_qirq(spapr->xics, spapr->check_exception_irq));
}
return;
@@ -603,7 +603,7 @@ out_no_events:
void spapr_events_init(sPAPRMachineState *spapr)
{
QTAILQ_INIT(&spapr->pending_events);
- spapr->check_exception_irq = xics_alloc(spapr->icp, 0, 0, false,
+ spapr->check_exception_irq = xics_spapr_alloc(spapr->xics, 0, 0, false,
&error_fatal);
spapr->epow_notifier.notify = spapr_powerdown_req;
qemu_register_powerdown_notifier(&spapr->epow_notifier);
diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c
index e011ed4b66..73af112e1d 100644
--- a/hw/ppc/spapr_hcall.c
+++ b/hw/ppc/spapr_hcall.c
@@ -83,12 +83,12 @@ static target_ulong h_enter(PowerPCCPU *cpu, sPAPRMachineState *spapr,
target_ulong pte_index = args[1];
target_ulong pteh = args[2];
target_ulong ptel = args[3];
- unsigned apshift, spshift;
+ unsigned apshift;
target_ulong raddr;
target_ulong index;
uint64_t token;
- apshift = ppc_hash64_hpte_page_shift_noslb(cpu, pteh, ptel, &spshift);
+ apshift = ppc_hash64_hpte_page_shift_noslb(cpu, pteh, ptel);
if (!apshift) {
/* Bad page size encoding */
return H_PARAMETER;
diff --git a/hw/ppc/spapr_iommu.c b/hw/ppc/spapr_iommu.c
index e230bacae1..d57b05d5c0 100644
--- a/hw/ppc/spapr_iommu.c
+++ b/hw/ppc/spapr_iommu.c
@@ -156,6 +156,16 @@ static uint64_t spapr_tce_get_min_page_size(MemoryRegion *iommu)
return 1ULL << tcet->page_shift;
}
+static void spapr_tce_notify_started(MemoryRegion *iommu)
+{
+ spapr_tce_set_need_vfio(container_of(iommu, sPAPRTCETable, iommu), true);
+}
+
+static void spapr_tce_notify_stopped(MemoryRegion *iommu)
+{
+ spapr_tce_set_need_vfio(container_of(iommu, sPAPRTCETable, iommu), false);
+}
+
static int spapr_tce_table_post_load(void *opaque, int version_id)
{
sPAPRTCETable *tcet = SPAPR_TCE_TABLE(opaque);
@@ -236,6 +246,8 @@ static const VMStateDescription vmstate_spapr_tce_table = {
static MemoryRegionIOMMUOps spapr_iommu_ops = {
.translate = spapr_tce_translate_iommu,
.get_min_page_size = spapr_tce_get_min_page_size,
+ .notify_started = spapr_tce_notify_started,
+ .notify_stopped = spapr_tce_notify_stopped,
};
static int spapr_tce_table_realize(DeviceState *dev)
diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c
index 9f28fb3829..949c44fec8 100644
--- a/hw/ppc/spapr_pci.c
+++ b/hw/ppc/spapr_pci.c
@@ -35,6 +35,7 @@
#include "hw/ppc/spapr.h"
#include "hw/pci-host/spapr.h"
#include "exec/address-spaces.h"
+#include "exec/ram_addr.h"
#include <libfdt.h>
#include "trace.h"
#include "qemu/error-report.h"
@@ -45,6 +46,7 @@
#include "hw/ppc/spapr_drc.h"
#include "sysemu/device_tree.h"
#include "sysemu/kvm.h"
+#include "sysemu/hostmem.h"
#include "hw/vfio/vfio.h"
@@ -322,7 +324,7 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPRMachineState *spapr,
return;
}
- xics_free(spapr->icp, msi->first_irq, msi->num);
+ xics_spapr_free(spapr->xics, msi->first_irq, msi->num);
if (msi_present(pdev)) {
spapr_msi_setmsg(pdev, 0, false, 0, 0);
}
@@ -360,7 +362,7 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPRMachineState *spapr,
}
/* Allocate MSIs */
- irq = xics_alloc_block(spapr->icp, 0, req_num, false,
+ irq = xics_spapr_alloc_block(spapr->xics, 0, req_num, false,
ret_intr_type == RTAS_TYPE_MSI, &err);
if (err) {
error_reportf_err(err, "Can't allocate MSIs for device %x: ",
@@ -371,7 +373,7 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPRMachineState *spapr,
/* Release previous MSIs */
if (msi) {
- xics_free(spapr->icp, msi->first_irq, msi->num);
+ xics_spapr_free(spapr->xics, msi->first_irq, msi->num);
g_hash_table_remove(phb->msi, &config_addr);
}
@@ -733,7 +735,7 @@ static void spapr_msi_write(void *opaque, hwaddr addr,
trace_spapr_pci_msi_write(addr, data, irq);
- qemu_irq_pulse(xics_get_qirq(spapr->icp, irq));
+ qemu_irq_pulse(xics_get_qirq(spapr->xics, irq));
}
static const MemoryRegionOps spapr_msi_ops = {
@@ -1087,12 +1089,6 @@ static void spapr_phb_add_pci_device(sPAPRDRConnector *drc,
void *fdt = NULL;
int fdt_start_offset = 0, fdt_size;
- if (object_dynamic_cast(OBJECT(pdev), "vfio-pci")) {
- sPAPRTCETable *tcet = spapr_tce_find_by_liobn(phb->dma_liobn);
-
- spapr_tce_set_need_vfio(tcet, true);
- }
-
fdt = create_device_tree(&fdt_size);
fdt_start_offset = spapr_create_pci_child_dt(phb, pdev, fdt, 0);
if (!fdt_start_offset) {
@@ -1310,11 +1306,14 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp)
PCIBus *bus;
uint64_t msi_window_size = 4096;
sPAPRTCETable *tcet;
+ const unsigned windows_supported =
+ sphb->ddw_enabled ? SPAPR_PCI_DMA_MAX_WINDOWS : 1;
if (sphb->index != (uint32_t)-1) {
hwaddr windows_base;
- if ((sphb->buid != (uint64_t)-1) || (sphb->dma_liobn != (uint32_t)-1)
+ if ((sphb->buid != (uint64_t)-1) || (sphb->dma_liobn[0] != (uint32_t)-1)
+ || (sphb->dma_liobn[1] != (uint32_t)-1 && windows_supported == 2)
|| (sphb->mem_win_addr != (hwaddr)-1)
|| (sphb->io_win_addr != (hwaddr)-1)) {
error_setg(errp, "Either \"index\" or other parameters must"
@@ -1329,7 +1328,9 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp)
}
sphb->buid = SPAPR_PCI_BASE_BUID + sphb->index;
- sphb->dma_liobn = SPAPR_PCI_LIOBN(sphb->index, 0);
+ for (i = 0; i < windows_supported; ++i) {
+ sphb->dma_liobn[i] = SPAPR_PCI_LIOBN(sphb->index, i);
+ }
windows_base = SPAPR_PCI_WINDOW_BASE
+ sphb->index * SPAPR_PCI_WINDOW_SPACING;
@@ -1342,8 +1343,9 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp)
return;
}
- if (sphb->dma_liobn == (uint32_t)-1) {
- error_setg(errp, "LIOBN not specified for PHB");
+ if ((sphb->dma_liobn[0] == (uint32_t)-1) ||
+ ((sphb->dma_liobn[1] == (uint32_t)-1) && (windows_supported > 1))) {
+ error_setg(errp, "LIOBN(s) not specified for PHB");
return;
}
@@ -1442,7 +1444,8 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp)
uint32_t irq;
Error *local_err = NULL;
- irq = xics_alloc_block(spapr->icp, 0, 1, true, false, &local_err);
+ irq = xics_spapr_alloc_block(spapr->xics, 0, 1, true, false,
+ &local_err);
if (local_err) {
error_propagate(errp, local_err);
error_prepend(errp, "can't allocate LSIs: ");
@@ -1461,16 +1464,18 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp)
}
}
- tcet = spapr_tce_new_table(DEVICE(sphb), sphb->dma_liobn);
- if (!tcet) {
- error_setg(errp, "Unable to create TCE table for %s",
- sphb->dtbusname);
- return;
+ /* DMA setup */
+ for (i = 0; i < windows_supported; ++i) {
+ tcet = spapr_tce_new_table(DEVICE(sphb), sphb->dma_liobn[i]);
+ if (!tcet) {
+ error_setg(errp, "Creating window#%d failed for %s",
+ i, sphb->dtbusname);
+ return;
+ }
+ memory_region_add_subregion_overlap(&sphb->iommu_root, 0,
+ spapr_tce_get_iommu(tcet), 0);
}
- memory_region_add_subregion_overlap(&sphb->iommu_root, 0,
- spapr_tce_get_iommu(tcet), 0);
-
sphb->msi = g_hash_table_new_full(g_int_hash, g_int_equal, g_free, g_free);
}
@@ -1487,13 +1492,19 @@ static int spapr_phb_children_reset(Object *child, void *opaque)
void spapr_phb_dma_reset(sPAPRPHBState *sphb)
{
- sPAPRTCETable *tcet = spapr_tce_find_by_liobn(sphb->dma_liobn);
+ int i;
+ sPAPRTCETable *tcet;
- if (tcet && tcet->nb_table) {
- spapr_tce_table_disable(tcet);
+ for (i = 0; i < SPAPR_PCI_DMA_MAX_WINDOWS; ++i) {
+ tcet = spapr_tce_find_by_liobn(sphb->dma_liobn[i]);
+
+ if (tcet && tcet->nb_table) {
+ spapr_tce_table_disable(tcet);
+ }
}
/* Register default 32bit DMA window */
+ tcet = spapr_tce_find_by_liobn(sphb->dma_liobn[0]);
spapr_tce_table_enable(tcet, SPAPR_TCE_PAGE_SHIFT, sphb->dma_win_addr,
sphb->dma_win_size >> SPAPR_TCE_PAGE_SHIFT);
}
@@ -1515,7 +1526,8 @@ static void spapr_phb_reset(DeviceState *qdev)
static Property spapr_phb_properties[] = {
DEFINE_PROP_UINT32("index", sPAPRPHBState, index, -1),
DEFINE_PROP_UINT64("buid", sPAPRPHBState, buid, -1),
- DEFINE_PROP_UINT32("liobn", sPAPRPHBState, dma_liobn, -1),
+ DEFINE_PROP_UINT32("liobn", sPAPRPHBState, dma_liobn[0], -1),
+ DEFINE_PROP_UINT32("liobn64", sPAPRPHBState, dma_liobn[1], -1),
DEFINE_PROP_UINT64("mem_win_addr", sPAPRPHBState, mem_win_addr, -1),
DEFINE_PROP_UINT64("mem_win_size", sPAPRPHBState, mem_win_size,
SPAPR_PCI_MMIO_WIN_SIZE),
@@ -1527,6 +1539,11 @@ static Property spapr_phb_properties[] = {
/* Default DMA window is 0..1GB */
DEFINE_PROP_UINT64("dma_win_addr", sPAPRPHBState, dma_win_addr, 0),
DEFINE_PROP_UINT64("dma_win_size", sPAPRPHBState, dma_win_size, 0x40000000),
+ DEFINE_PROP_UINT64("dma64_win_addr", sPAPRPHBState, dma64_win_addr,
+ 0x800000000000000ULL),
+ DEFINE_PROP_BOOL("ddw", sPAPRPHBState, ddw_enabled, true),
+ DEFINE_PROP_UINT64("pgsz", sPAPRPHBState, page_size_mask,
+ (1ULL << 12) | (1ULL << 16)),
DEFINE_PROP_END_OF_LIST(),
};
@@ -1603,7 +1620,7 @@ static const VMStateDescription vmstate_spapr_pci = {
.post_load = spapr_pci_post_load,
.fields = (VMStateField[]) {
VMSTATE_UINT64_EQUAL(buid, sPAPRPHBState),
- VMSTATE_UINT32_EQUAL(dma_liobn, sPAPRPHBState),
+ VMSTATE_UINT32_EQUAL(dma_liobn[0], sPAPRPHBState),
VMSTATE_UINT64_EQUAL(mem_win_addr, sPAPRPHBState),
VMSTATE_UINT64_EQUAL(mem_win_size, sPAPRPHBState),
VMSTATE_UINT64_EQUAL(io_win_addr, sPAPRPHBState),
@@ -1779,6 +1796,15 @@ int spapr_populate_pci_dt(sPAPRPHBState *phb,
uint32_t interrupt_map_mask[] = {
cpu_to_be32(b_ddddd(-1)|b_fff(0)), 0x0, 0x0, cpu_to_be32(-1)};
uint32_t interrupt_map[PCI_SLOT_MAX * PCI_NUM_PINS][7];
+ uint32_t ddw_applicable[] = {
+ cpu_to_be32(RTAS_IBM_QUERY_PE_DMA_WINDOW),
+ cpu_to_be32(RTAS_IBM_CREATE_PE_DMA_WINDOW),
+ cpu_to_be32(RTAS_IBM_REMOVE_PE_DMA_WINDOW)
+ };
+ uint32_t ddw_extensions[] = {
+ cpu_to_be32(1),
+ cpu_to_be32(RTAS_IBM_RESET_PE_DMA_WINDOW)
+ };
sPAPRTCETable *tcet;
PCIBus *bus = PCI_HOST_BRIDGE(phb)->bus;
sPAPRFDT s_fdt;
@@ -1801,7 +1827,15 @@ int spapr_populate_pci_dt(sPAPRPHBState *phb,
_FDT(fdt_setprop(fdt, bus_off, "ranges", &ranges, sizeof_ranges));
_FDT(fdt_setprop(fdt, bus_off, "reg", &bus_reg, sizeof(bus_reg)));
_FDT(fdt_setprop_cell(fdt, bus_off, "ibm,pci-config-space-type", 0x1));
- _FDT(fdt_setprop_cell(fdt, bus_off, "ibm,pe-total-#msi", XICS_IRQS));
+ _FDT(fdt_setprop_cell(fdt, bus_off, "ibm,pe-total-#msi", XICS_IRQS_SPAPR));
+
+ /* Dynamic DMA window */
+ if (phb->ddw_enabled) {
+ _FDT(fdt_setprop(fdt, bus_off, "ibm,ddw-applicable", &ddw_applicable,
+ sizeof(ddw_applicable)));
+ _FDT(fdt_setprop(fdt, bus_off, "ibm,ddw-extensions",
+ &ddw_extensions, sizeof(ddw_extensions)));
+ }
/* Build the interrupt-map, this must matches what is done
* in pci_spapr_map_irq
@@ -1826,7 +1860,7 @@ int spapr_populate_pci_dt(sPAPRPHBState *phb,
_FDT(fdt_setprop(fdt, bus_off, "interrupt-map", &interrupt_map,
sizeof(interrupt_map)));
- tcet = spapr_tce_find_by_liobn(phb->dma_liobn);
+ tcet = spapr_tce_find_by_liobn(phb->dma_liobn[0]);
if (!tcet) {
return -1;
}
diff --git a/hw/ppc/spapr_rtas_ddw.c b/hw/ppc/spapr_rtas_ddw.c
new file mode 100644
index 0000000000..177dcffc9b
--- /dev/null
+++ b/hw/ppc/spapr_rtas_ddw.c
@@ -0,0 +1,295 @@
+/*
+ * QEMU sPAPR Dynamic DMA windows support
+ *
+ * Copyright (c) 2015 Alexey Kardashevskiy, IBM Corporation.
+ *
+ * This program 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "cpu.h"
+#include "qemu/error-report.h"
+#include "hw/ppc/spapr.h"
+#include "hw/pci-host/spapr.h"
+#include "trace.h"
+
+static int spapr_phb_get_active_win_num_cb(Object *child, void *opaque)
+{
+ sPAPRTCETable *tcet;
+
+ tcet = (sPAPRTCETable *) object_dynamic_cast(child, TYPE_SPAPR_TCE_TABLE);
+ if (tcet && tcet->nb_table) {
+ ++*(unsigned *)opaque;
+ }
+ return 0;
+}
+
+static unsigned spapr_phb_get_active_win_num(sPAPRPHBState *sphb)
+{
+ unsigned ret = 0;
+
+ object_child_foreach(OBJECT(sphb), spapr_phb_get_active_win_num_cb, &ret);
+
+ return ret;
+}
+
+static int spapr_phb_get_free_liobn_cb(Object *child, void *opaque)
+{
+ sPAPRTCETable *tcet;
+
+ tcet = (sPAPRTCETable *) object_dynamic_cast(child, TYPE_SPAPR_TCE_TABLE);
+ if (tcet && !tcet->nb_table) {
+ *(uint32_t *)opaque = tcet->liobn;
+ return 1;
+ }
+ return 0;
+}
+
+static unsigned spapr_phb_get_free_liobn(sPAPRPHBState *sphb)
+{
+ uint32_t liobn = 0;
+
+ object_child_foreach(OBJECT(sphb), spapr_phb_get_free_liobn_cb, &liobn);
+
+ return liobn;
+}
+
+static uint32_t spapr_page_mask_to_query_mask(uint64_t page_mask)
+{
+ int i;
+ uint32_t mask = 0;
+ const struct { int shift; uint32_t mask; } masks[] = {
+ { 12, RTAS_DDW_PGSIZE_4K },
+ { 16, RTAS_DDW_PGSIZE_64K },
+ { 24, RTAS_DDW_PGSIZE_16M },
+ { 25, RTAS_DDW_PGSIZE_32M },
+ { 26, RTAS_DDW_PGSIZE_64M },
+ { 27, RTAS_DDW_PGSIZE_128M },
+ { 28, RTAS_DDW_PGSIZE_256M },
+ { 34, RTAS_DDW_PGSIZE_16G },
+ };
+
+ for (i = 0; i < ARRAY_SIZE(masks); ++i) {
+ if (page_mask & (1ULL << masks[i].shift)) {
+ mask |= masks[i].mask;
+ }
+ }
+
+ return mask;
+}
+
+static void rtas_ibm_query_pe_dma_window(PowerPCCPU *cpu,
+ sPAPRMachineState *spapr,
+ uint32_t token, uint32_t nargs,
+ target_ulong args,
+ uint32_t nret, target_ulong rets)
+{
+ sPAPRPHBState *sphb;
+ uint64_t buid, max_window_size;
+ uint32_t avail, addr, pgmask = 0;
+ MachineState *machine = MACHINE(spapr);
+
+ if ((nargs != 3) || (nret != 5)) {
+ goto param_error_exit;
+ }
+
+ buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2);
+ addr = rtas_ld(args, 0);
+ sphb = spapr_pci_find_phb(spapr, buid);
+ if (!sphb || !sphb->ddw_enabled) {
+ goto param_error_exit;
+ }
+
+ /* Translate page mask to LoPAPR format */
+ pgmask = spapr_page_mask_to_query_mask(sphb->page_size_mask);
+
+ /*
+ * This is "Largest contiguous block of TCEs allocated specifically
+ * for (that is, are reserved for) this PE".
+ * Return the maximum number as maximum supported RAM size was in 4K pages.
+ */
+ if (machine->ram_size == machine->maxram_size) {
+ max_window_size = machine->ram_size;
+ } else {
+ MemoryHotplugState *hpms = &spapr->hotplug_memory;
+
+ max_window_size = hpms->base + memory_region_size(&hpms->mr);
+ }
+
+ avail = SPAPR_PCI_DMA_MAX_WINDOWS - spapr_phb_get_active_win_num(sphb);
+
+ rtas_st(rets, 0, RTAS_OUT_SUCCESS);
+ rtas_st(rets, 1, avail);
+ rtas_st(rets, 2, max_window_size >> SPAPR_TCE_PAGE_SHIFT);
+ rtas_st(rets, 3, pgmask);
+ rtas_st(rets, 4, 0); /* DMA migration mask, not supported */
+
+ trace_spapr_iommu_ddw_query(buid, addr, avail, max_window_size, pgmask);
+ return;
+
+param_error_exit:
+ rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
+}
+
+static void rtas_ibm_create_pe_dma_window(PowerPCCPU *cpu,
+ sPAPRMachineState *spapr,
+ uint32_t token, uint32_t nargs,
+ target_ulong args,
+ uint32_t nret, target_ulong rets)
+{
+ sPAPRPHBState *sphb;
+ sPAPRTCETable *tcet = NULL;
+ uint32_t addr, page_shift, window_shift, liobn;
+ uint64_t buid, win_addr;
+ int windows;
+
+ if ((nargs != 5) || (nret != 4)) {
+ goto param_error_exit;
+ }
+
+ buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2);
+ addr = rtas_ld(args, 0);
+ sphb = spapr_pci_find_phb(spapr, buid);
+ if (!sphb || !sphb->ddw_enabled) {
+ goto param_error_exit;
+ }
+
+ page_shift = rtas_ld(args, 3);
+ window_shift = rtas_ld(args, 4);
+ liobn = spapr_phb_get_free_liobn(sphb);
+ windows = spapr_phb_get_active_win_num(sphb);
+
+ if (!(sphb->page_size_mask & (1ULL << page_shift)) ||
+ (window_shift < page_shift)) {
+ goto param_error_exit;
+ }
+
+ if (!liobn || !sphb->ddw_enabled || windows == SPAPR_PCI_DMA_MAX_WINDOWS) {
+ goto hw_error_exit;
+ }
+
+ tcet = spapr_tce_find_by_liobn(liobn);
+ if (!tcet) {
+ goto hw_error_exit;
+ }
+
+ win_addr = (windows == 0) ? sphb->dma_win_addr : sphb->dma64_win_addr;
+ spapr_tce_table_enable(tcet, page_shift, win_addr,
+ 1ULL << (window_shift - page_shift));
+ if (!tcet->nb_table) {
+ goto hw_error_exit;
+ }
+
+ trace_spapr_iommu_ddw_create(buid, addr, 1ULL << page_shift,
+ 1ULL << window_shift, tcet->bus_offset, liobn);
+
+ rtas_st(rets, 0, RTAS_OUT_SUCCESS);
+ rtas_st(rets, 1, liobn);
+ rtas_st(rets, 2, tcet->bus_offset >> 32);
+ rtas_st(rets, 3, tcet->bus_offset & ((uint32_t) -1));
+
+ return;
+
+hw_error_exit:
+ rtas_st(rets, 0, RTAS_OUT_HW_ERROR);
+ return;
+
+param_error_exit:
+ rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
+}
+
+static void rtas_ibm_remove_pe_dma_window(PowerPCCPU *cpu,
+ sPAPRMachineState *spapr,
+ uint32_t token, uint32_t nargs,
+ target_ulong args,
+ uint32_t nret, target_ulong rets)
+{
+ sPAPRPHBState *sphb;
+ sPAPRTCETable *tcet;
+ uint32_t liobn;
+
+ if ((nargs != 1) || (nret != 1)) {
+ goto param_error_exit;
+ }
+
+ liobn = rtas_ld(args, 0);
+ tcet = spapr_tce_find_by_liobn(liobn);
+ if (!tcet) {
+ goto param_error_exit;
+ }
+
+ sphb = SPAPR_PCI_HOST_BRIDGE(OBJECT(tcet)->parent);
+ if (!sphb || !sphb->ddw_enabled || !tcet->nb_table) {
+ goto param_error_exit;
+ }
+
+ spapr_tce_table_disable(tcet);
+ trace_spapr_iommu_ddw_remove(liobn);
+
+ rtas_st(rets, 0, RTAS_OUT_SUCCESS);
+ return;
+
+param_error_exit:
+ rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
+}
+
+static void rtas_ibm_reset_pe_dma_window(PowerPCCPU *cpu,
+ sPAPRMachineState *spapr,
+ uint32_t token, uint32_t nargs,
+ target_ulong args,
+ uint32_t nret, target_ulong rets)
+{
+ sPAPRPHBState *sphb;
+ uint64_t buid;
+ uint32_t addr;
+
+ if ((nargs != 3) || (nret != 1)) {
+ goto param_error_exit;
+ }
+
+ buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2);
+ addr = rtas_ld(args, 0);
+ sphb = spapr_pci_find_phb(spapr, buid);
+ if (!sphb || !sphb->ddw_enabled) {
+ goto param_error_exit;
+ }
+
+ spapr_phb_dma_reset(sphb);
+ trace_spapr_iommu_ddw_reset(buid, addr);
+
+ rtas_st(rets, 0, RTAS_OUT_SUCCESS);
+
+ return;
+
+param_error_exit:
+ rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
+}
+
+static void spapr_rtas_ddw_init(void)
+{
+ spapr_rtas_register(RTAS_IBM_QUERY_PE_DMA_WINDOW,
+ "ibm,query-pe-dma-window",
+ rtas_ibm_query_pe_dma_window);
+ spapr_rtas_register(RTAS_IBM_CREATE_PE_DMA_WINDOW,
+ "ibm,create-pe-dma-window",
+ rtas_ibm_create_pe_dma_window);
+ spapr_rtas_register(RTAS_IBM_REMOVE_PE_DMA_WINDOW,
+ "ibm,remove-pe-dma-window",
+ rtas_ibm_remove_pe_dma_window);
+ spapr_rtas_register(RTAS_IBM_RESET_PE_DMA_WINDOW,
+ "ibm,reset-pe-dma-window",
+ rtas_ibm_reset_pe_dma_window);
+}
+
+type_init(spapr_rtas_ddw_init)
diff --git a/hw/ppc/spapr_vio.c b/hw/ppc/spapr_vio.c
index ae40db8fd2..f93244d7c1 100644
--- a/hw/ppc/spapr_vio.c
+++ b/hw/ppc/spapr_vio.c
@@ -463,7 +463,7 @@ static void spapr_vio_busdev_realize(DeviceState *qdev, Error **errp)
dev->qdev.id = id;
}
- dev->irq = xics_alloc(spapr->icp, 0, dev->irq, false, &local_err);
+ dev->irq = xics_spapr_alloc(spapr->xics, 0, dev->irq, false, &local_err);
if (local_err) {
error_propagate(errp, local_err);
return;
diff --git a/hw/ppc/trace-events b/hw/ppc/trace-events
index 6da713547f..900679bc9d 100644
--- a/hw/ppc/trace-events
+++ b/hw/ppc/trace-events
@@ -30,6 +30,10 @@ spapr_iommu_xlate(uint64_t liobn, uint64_t ioba, uint64_t tce, unsigned perm, un
spapr_iommu_new_table(uint64_t liobn, void *table, int fd) "liobn=%"PRIx64" table=%p fd=%d"
spapr_iommu_pre_save(uint64_t liobn, uint32_t nb, uint64_t offs, uint32_t ps) "liobn=%"PRIx64" %"PRIx32" bus_offset=%"PRIx64" ps=%"PRIu32
spapr_iommu_post_load(uint64_t liobn, uint32_t pre_nb, uint32_t post_nb, uint64_t offs, uint32_t ps) "liobn=%"PRIx64" %"PRIx32" => %"PRIx32" bus_offset=%"PRIx64" ps=%"PRIu32
+spapr_iommu_ddw_query(uint64_t buid, uint32_t cfgaddr, unsigned wa, uint64_t win_size, uint32_t pgmask) "buid=%"PRIx64" addr=%"PRIx32", %u windows available, max window size=%"PRIx64", mask=%"PRIx32
+spapr_iommu_ddw_create(uint64_t buid, uint32_t cfgaddr, uint64_t pg_size, uint64_t req_size, uint64_t start, uint32_t liobn) "buid=%"PRIx64" addr=%"PRIx32", page size=0x%"PRIx64", requested=0x%"PRIx64", start addr=%"PRIx64", liobn=%"PRIx32
+spapr_iommu_ddw_remove(uint32_t liobn) "liobn=%"PRIx32
+spapr_iommu_ddw_reset(uint64_t buid, uint32_t cfgaddr) "buid=%"PRIx64" addr=%"PRIx32
# hw/ppc/ppc.c
ppc_tb_adjust(uint64_t offs1, uint64_t offs2, int64_t diff, int64_t seconds) "adjusted from 0x%"PRIx64" to 0x%"PRIx64", diff %"PRId64" (%"PRId64"s)"