ppc patch queue 2019-02-26

Next set of patches for ppc and spapr.  There's a lot in this one:
  * Support "STOP light" states on POWER9
  * Add support for HVI interrupts on POWER9 (powernv machine)
  * CVE-2019-8934: Don't leak host model and serial information to the guest
  * Tests and cleanups for various hot unplug options
  * Hash and radix MMU implementation on POWER9 for powernv machine
  * PCI Host Bridge hotplug support for pseries machine
  * Allow larger kernels and initrds for powernv machine
 
 Plus a handful of miscellaneous fixes and cleanups.
 
 The cpu hotplug tests and cleanups from David Hildenbrand aren't
 solely power related.  However the consensus amongst Michael Tsirkin,
 David Hildenbrand, Cornelia Huck and myself was that it made most
 sense to come in via my tree.
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEEdfRlhq5hpmzETofcbDjKyiDZs5IFAlx0tIoACgkQbDjKyiDZ
 s5I+ZhAA1LHyTGh2xXYcE1BFUFeYWqpDmFEghKSGvnCjhqkPoACBUyR3GKcNF+vf
 sgX/OgoPPgXE8QpB/4hzcMxiNxFTPNOX+1p5oGv3zzokjyF7qtlVvVub3BI0cvjg
 37eGLW9iLmc/PhiS7kDVSDwQpyhombsU4jb73cp6RqYTKT0wHHl/As3WmzIWW4bk
 BguUE6zPROEuVyQSxiL2pTWv4UBSsMrqqwCBkbAohXkDCjntaSdHCxmaHyf+VXqe
 ac50BSIAkAEIiJiPOGEJkuIOm1goE823RGwuPQWvkfM3flozmTYWh/Y+Y2t9NMBR
 sC8Ly9Wo3Lz/sDr3cfL5HZ3NXCayDZwJEllbHqzDyjSJzU3gY3XMyWnIM0NTckTr
 n5wX1OLghTYkgYkDLRyi9Nj1Gd0B11OfMsw17/Bj9hyz3k1KdgyJ98UZkwUBqvbC
 kwrwkSutMrs8qqAZM6xtn++ABYgxhLOlY83U8rfAXEebUixAj/6WOmxgyYiV+m/n
 9qQfPD8301lxpmmowBVuGyBKcdFUJ+QYNXD3a1S/vphvA2+G1y1SccMrlz2WEYol
 gXVVe1tpA0ohmwflFX87zDOeyvO1gezhtXdaDlVjyeXOaGYUV3Srjei9w1p3PTs0
 FsKwC/bL+cbTmi43qj5et0HG5Fx48fjIOjEqCcVBaz0ZQqjkdus=
 =Z4Z6
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/dgibson/tags/ppc-for-4.0-20190226' into staging

ppc patch queue 2019-02-26

Next set of patches for ppc and spapr.  There's a lot in this one:
 * Support "STOP light" states on POWER9
 * Add support for HVI interrupts on POWER9 (powernv machine)
 * CVE-2019-8934: Don't leak host model and serial information to the guest
 * Tests and cleanups for various hot unplug options
 * Hash and radix MMU implementation on POWER9 for powernv machine
 * PCI Host Bridge hotplug support for pseries machine
 * Allow larger kernels and initrds for powernv machine

Plus a handful of miscellaneous fixes and cleanups.

The cpu hotplug tests and cleanups from David Hildenbrand aren't
solely power related.  However the consensus amongst Michael Tsirkin,
David Hildenbrand, Cornelia Huck and myself was that it made most
sense to come in via my tree.

# gpg: Signature made Tue 26 Feb 2019 03:37:46 GMT
# gpg:                using RSA key 75F46586AE61A66CC44E87DC6C38CACA20D9B392
# gpg: Good signature from "David Gibson <david@gibson.dropbear.id.au>" [full]
# gpg:                 aka "David Gibson (Red Hat) <dgibson@redhat.com>" [full]
# gpg:                 aka "David Gibson (ozlabs.org) <dgibson@ozlabs.org>" [full]
# gpg:                 aka "David Gibson (kernel.org) <dwg@kernel.org>" [unknown]
# Primary key fingerprint: 75F4 6586 AE61 A66C C44E  87DC 6C38 CACA 20D9 B392

* remotes/dgibson/tags/ppc-for-4.0-20190226: (50 commits)
  ppc/pnv: use IEC binary prefixes to represent sizes
  ppc/pnv: add INITRD_MAX_SIZE constant
  ppc/pnv: increase kernel size limit to 256MiB
  hw/ppc: Use object_initialize_child for correct reference counting
  ppc/xive: xive does not have a POWER7 interrupt model
  tests/device-plug: Add PHB unplug request test for spapr
  spapr: enable PHB hotplug for default pseries machine type
  spapr: add hotplug hooks for PHB hotplug
  spapr_pci: add ibm, my-drc-index property for PHB hotplug
  spapr_pci: provide node start offset via spapr_populate_pci_dt()
  spapr_events: add support for phb hotplug events
  spapr: populate PHB DRC entries for root DT node
  spapr: create DR connectors for PHBs
  spapr_pci: add PHB unrealize
  spapr_irq: Expose the phandle of the interrupt controller
  spapr: Expose the name of the interrupt controller node
  xics: Write source state to KVM at claim time
  spapr/drc: Drop spapr_drc_attach() fdt argument
  spapr/pci: Generate FDT fragment at configure connector time
  spapr: Generate FDT fragment for CPUs at configure connector time
  ...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2019-02-28 10:28:00 +00:00
commit 1387294169
43 changed files with 1363 additions and 363 deletions

1
cpus.c
View File

@ -1333,6 +1333,7 @@ static void *qemu_dummy_cpu_thread_fn(void *arg)
qemu_wait_io_event(cpu);
} while (!cpu->unplug);
qemu_mutex_unlock_iothread();
rcu_unregister_thread();
return NULL;
#endif

View File

@ -244,13 +244,12 @@ static void spapr_xive_instance_init(Object *obj)
{
sPAPRXive *xive = SPAPR_XIVE(obj);
object_initialize(&xive->source, sizeof(xive->source), TYPE_XIVE_SOURCE);
object_property_add_child(obj, "source", OBJECT(&xive->source), NULL);
object_initialize_child(obj, "source", &xive->source, sizeof(xive->source),
TYPE_XIVE_SOURCE, &error_abort, NULL);
object_initialize(&xive->end_source, sizeof(xive->end_source),
TYPE_XIVE_END_SOURCE);
object_property_add_child(obj, "end_source", OBJECT(&xive->end_source),
NULL);
object_initialize_child(obj, "end_source", &xive->end_source,
sizeof(xive->end_source), TYPE_XIVE_END_SOURCE,
&error_abort, NULL);
}
static void spapr_xive_realize(DeviceState *dev, Error **errp)
@ -317,6 +316,9 @@ static void spapr_xive_realize(DeviceState *dev, Error **errp)
/* Map all regions */
spapr_xive_map_mmio(xive);
xive->nodename = g_strdup_printf("interrupt-controller@%" PRIx64,
xive->tm_base + XIVE_TM_USER_PAGE * (1 << TM_SHIFT));
qemu_register_reset(spapr_xive_reset, dev);
}
@ -1448,7 +1450,6 @@ void spapr_dt_xive(sPAPRMachineState *spapr, uint32_t nr_servers, void *fdt,
cpu_to_be32(7), /* start */
cpu_to_be32(0xf8), /* count */
};
gchar *nodename;
/* Thread Interrupt Management Area : User (ring 3) and OS (ring 2) */
timas[0] = cpu_to_be64(xive->tm_base +
@ -1458,10 +1459,7 @@ void spapr_dt_xive(sPAPRMachineState *spapr, uint32_t nr_servers, void *fdt,
XIVE_TM_OS_PAGE * (1ull << TM_SHIFT));
timas[3] = cpu_to_be64(1ull << TM_SHIFT);
nodename = g_strdup_printf("interrupt-controller@%" PRIx64,
xive->tm_base + XIVE_TM_USER_PAGE * (1 << TM_SHIFT));
_FDT(node = fdt_add_subnode(fdt, 0, nodename));
g_free(nodename);
_FDT(node = fdt_add_subnode(fdt, 0, xive->nodename));
_FDT(fdt_setprop_string(fdt, node, "device_type", "power-ivpe"));
_FDT(fdt_setprop(fdt, node, "reg", timas, sizeof(timas)));

View File

@ -338,6 +338,9 @@ static void icp_realize(DeviceState *dev, Error **errp)
case PPC_FLAGS_INPUT_POWER7:
icp->output = env->irq_inputs[POWER7_INPUT_INT];
break;
case PPC_FLAGS_INPUT_POWER9: /* For SPAPR xics emulation */
icp->output = env->irq_inputs[POWER9_INPUT_INT];
break;
case PPC_FLAGS_INPUT_970:
icp->output = env->irq_inputs[PPC970_INPUT_INT];
@ -755,6 +758,10 @@ void ics_set_irq_type(ICSState *ics, int srcno, bool lsi)
ics->irqs[srcno].flags |=
lsi ? XICS_FLAGS_IRQ_LSI : XICS_FLAGS_IRQ_MSI;
if (kvm_irqchip_in_kernel()) {
ics_set_kvm_state_one(ics, srcno);
}
}
static void xics_register_types(void)

View File

@ -213,45 +213,57 @@ void ics_synchronize_state(ICSState *ics)
ics_get_kvm_state(ics);
}
int ics_set_kvm_state(ICSState *ics)
int ics_set_kvm_state_one(ICSState *ics, int srcno)
{
uint64_t state;
int i;
Error *local_err = NULL;
ICSIRQState *irq = &ics->irqs[srcno];
int ret;
state = irq->server;
state |= (uint64_t)(irq->saved_priority & KVM_XICS_PRIORITY_MASK)
<< KVM_XICS_PRIORITY_SHIFT;
if (irq->priority != irq->saved_priority) {
assert(irq->priority == 0xff);
state |= KVM_XICS_MASKED;
}
if (irq->flags & XICS_FLAGS_IRQ_LSI) {
state |= KVM_XICS_LEVEL_SENSITIVE;
if (irq->status & XICS_STATUS_ASSERTED) {
state |= KVM_XICS_PENDING;
}
} else {
if (irq->status & XICS_STATUS_MASKED_PENDING) {
state |= KVM_XICS_PENDING;
}
}
if (irq->status & XICS_STATUS_PRESENTED) {
state |= KVM_XICS_PRESENTED;
}
if (irq->status & XICS_STATUS_QUEUED) {
state |= KVM_XICS_QUEUED;
}
ret = kvm_device_access(kernel_xics_fd, KVM_DEV_XICS_GRP_SOURCES,
srcno + ics->offset, &state, true, &local_err);
if (local_err) {
error_report_err(local_err);
return ret;
}
return 0;
}
int ics_set_kvm_state(ICSState *ics)
{
int i;
for (i = 0; i < ics->nr_irqs; i++) {
ICSIRQState *irq = &ics->irqs[i];
int ret;
state = irq->server;
state |= (uint64_t)(irq->saved_priority & KVM_XICS_PRIORITY_MASK)
<< KVM_XICS_PRIORITY_SHIFT;
if (irq->priority != irq->saved_priority) {
assert(irq->priority == 0xff);
state |= KVM_XICS_MASKED;
}
if (ics->irqs[i].flags & XICS_FLAGS_IRQ_LSI) {
state |= KVM_XICS_LEVEL_SENSITIVE;
if (irq->status & XICS_STATUS_ASSERTED) {
state |= KVM_XICS_PENDING;
}
} else {
if (irq->status & XICS_STATUS_MASKED_PENDING) {
state |= KVM_XICS_PENDING;
}
}
if (irq->status & XICS_STATUS_PRESENTED) {
state |= KVM_XICS_PRESENTED;
}
if (irq->status & XICS_STATUS_QUEUED) {
state |= KVM_XICS_QUEUED;
}
ret = kvm_device_access(kernel_xics_fd, KVM_DEV_XICS_GRP_SOURCES,
i + ics->offset, &state, true, &local_err);
if (local_err) {
error_report_err(local_err);
ret = ics_set_kvm_state_one(ics, i);
if (ret) {
return ret;
}
}

View File

@ -254,7 +254,7 @@ void spapr_dt_xics(sPAPRMachineState *spapr, uint32_t nr_servers, void *fdt,
};
int node;
_FDT(node = fdt_add_subnode(fdt, 0, "interrupt-controller"));
_FDT(node = fdt_add_subnode(fdt, 0, XICS_NODENAME));
_FDT(fdt_setprop_string(fdt, node, "device_type",
"PowerPC-External-Interrupt-Presentation"));

View File

@ -481,8 +481,8 @@ static void xive_tctx_realize(DeviceState *dev, Error **errp)
env = &cpu->env;
switch (PPC_INPUT(env)) {
case PPC_FLAGS_INPUT_POWER7:
tctx->output = env->irq_inputs[POWER7_INPUT_INT];
case PPC_FLAGS_INPUT_POWER9:
tctx->output = env->irq_inputs[POWER9_INPUT_INT];
break;
default:

View File

@ -47,14 +47,16 @@
#include <libfdt.h>
#define FDT_MAX_SIZE 0x00100000
#define FDT_MAX_SIZE (1 * MiB)
#define FW_FILE_NAME "skiboot.lid"
#define FW_LOAD_ADDR 0x0
#define FW_MAX_SIZE 0x00400000
#define FW_MAX_SIZE (4 * MiB)
#define KERNEL_LOAD_ADDR 0x20000000
#define KERNEL_MAX_SIZE (256 * MiB)
#define INITRD_LOAD_ADDR 0x60000000
#define INITRD_MAX_SIZE (256 * MiB)
static const char *pnv_chip_core_typename(const PnvChip *o)
{
@ -588,7 +590,7 @@ static void pnv_init(MachineState *machine)
long kernel_size;
kernel_size = load_image_targphys(machine->kernel_filename,
KERNEL_LOAD_ADDR, 0x2000000);
KERNEL_LOAD_ADDR, KERNEL_MAX_SIZE);
if (kernel_size < 0) {
error_report("Could not load kernel '%s'",
machine->kernel_filename);
@ -600,7 +602,7 @@ static void pnv_init(MachineState *machine)
if (machine->initrd_filename) {
pnv->initrd_base = INITRD_LOAD_ADDR;
pnv->initrd_size = load_image_targphys(machine->initrd_filename,
pnv->initrd_base, 0x10000000); /* 128MB max */
pnv->initrd_base, INITRD_MAX_SIZE);
if (pnv->initrd_size < 0) {
error_report("Could not load initial ram disk '%s'",
machine->initrd_filename);
@ -736,18 +738,18 @@ static void pnv_chip_power8_instance_init(Object *obj)
{
Pnv8Chip *chip8 = PNV8_CHIP(obj);
object_initialize(&chip8->psi, sizeof(chip8->psi), TYPE_PNV_PSI);
object_property_add_child(obj, "psi", OBJECT(&chip8->psi), NULL);
object_initialize_child(obj, "psi", &chip8->psi, sizeof(chip8->psi),
TYPE_PNV_PSI, &error_abort, NULL);
object_property_add_const_link(OBJECT(&chip8->psi), "xics",
OBJECT(qdev_get_machine()), &error_abort);
object_initialize(&chip8->lpc, sizeof(chip8->lpc), TYPE_PNV_LPC);
object_property_add_child(obj, "lpc", OBJECT(&chip8->lpc), NULL);
object_initialize_child(obj, "lpc", &chip8->lpc, sizeof(chip8->lpc),
TYPE_PNV_LPC, &error_abort, NULL);
object_property_add_const_link(OBJECT(&chip8->lpc), "psi",
OBJECT(&chip8->psi), &error_abort);
object_initialize(&chip8->occ, sizeof(chip8->occ), TYPE_PNV_OCC);
object_property_add_child(obj, "occ", OBJECT(&chip8->occ), NULL);
object_initialize_child(obj, "occ", &chip8->occ, sizeof(chip8->occ),
TYPE_PNV_OCC, &error_abort, NULL);
object_property_add_const_link(OBJECT(&chip8->occ), "psi",
OBJECT(&chip8->psi), &error_abort);
}

View File

@ -444,8 +444,8 @@ static void pnv_psi_init(Object *obj)
{
PnvPsi *psi = PNV_PSI(obj);
object_initialize(&psi->ics, sizeof(psi->ics), TYPE_ICS_SIMPLE);
object_property_add_child(obj, "ics-psi", OBJECT(&psi->ics), NULL);
object_initialize_child(obj, "ics-psi", &psi->ics, sizeof(psi->ics),
TYPE_ICS_SIMPLE, &error_abort, NULL);
}
static const uint8_t irq_to_xivr[] = {

View File

@ -306,6 +306,48 @@ void ppcPOWER7_irq_init(PowerPCCPU *cpu)
env->irq_inputs = (void **)qemu_allocate_irqs(&power7_set_irq, cpu,
POWER7_INPUT_NB);
}
/* POWER9 internal IRQ controller */
static void power9_set_irq(void *opaque, int pin, int level)
{
PowerPCCPU *cpu = opaque;
CPUPPCState *env = &cpu->env;
LOG_IRQ("%s: env %p pin %d level %d\n", __func__,
env, pin, level);
switch (pin) {
case POWER9_INPUT_INT:
/* Level sensitive - active high */
LOG_IRQ("%s: set the external IRQ state to %d\n",
__func__, level);
ppc_set_irq(cpu, PPC_INTERRUPT_EXT, level);
break;
case POWER9_INPUT_HINT:
/* Level sensitive - active high */
LOG_IRQ("%s: set the external IRQ state to %d\n",
__func__, level);
ppc_set_irq(cpu, PPC_INTERRUPT_HVIRT, level);
break;
default:
/* Unknown pin - do nothing */
LOG_IRQ("%s: unknown IRQ pin %d\n", __func__, pin);
return;
}
if (level) {
env->irq_input_state |= 1 << pin;
} else {
env->irq_input_state &= ~(1 << pin);
}
}
void ppcPOWER9_irq_init(PowerPCCPU *cpu)
{
CPUPPCState *env = &cpu->env;
env->irq_inputs = (void **)qemu_allocate_irqs(&power9_set_irq, cpu,
POWER9_INPUT_NB);
}
#endif /* defined(TARGET_PPC64) */
void ppc40x_core_reset(PowerPCCPU *cpu)
@ -776,7 +818,7 @@ static inline void cpu_ppc_hdecr_excp(PowerPCCPU *cpu)
* interrupts in a PM state. Not only they don't cause a
* wakeup but they also get effectively discarded.
*/
if (!env->in_pm_state) {
if (!env->resume_as_sreset) {
ppc_set_irq(cpu, PPC_INTERRUPT_HDECR, 1);
}
}

View File

@ -1247,13 +1247,30 @@ static void *spapr_build_fdt(sPAPRMachineState *spapr)
* Add info to guest to indentify which host is it being run on
* and what is the uuid of the guest
*/
if (kvmppc_get_host_model(&buf)) {
_FDT(fdt_setprop_string(fdt, 0, "host-model", buf));
g_free(buf);
if (spapr->host_model && !g_str_equal(spapr->host_model, "none")) {
if (g_str_equal(spapr->host_model, "passthrough")) {
/* -M host-model=passthrough */
if (kvmppc_get_host_model(&buf)) {
_FDT(fdt_setprop_string(fdt, 0, "host-model", buf));
g_free(buf);
}
} else {
/* -M host-model=<user-string> */
_FDT(fdt_setprop_string(fdt, 0, "host-model", spapr->host_model));
}
}
if (kvmppc_get_host_serial(&buf)) {
_FDT(fdt_setprop_string(fdt, 0, "host-serial", buf));
g_free(buf);
if (spapr->host_serial && !g_str_equal(spapr->host_serial, "none")) {
if (g_str_equal(spapr->host_serial, "passthrough")) {
/* -M host-serial=passthrough */
if (kvmppc_get_host_serial(&buf)) {
_FDT(fdt_setprop_string(fdt, 0, "host-serial", buf));
g_free(buf);
}
} else {
/* -M host-serial=<user-string> */
_FDT(fdt_setprop_string(fdt, 0, "host-serial", spapr->host_serial));
}
}
buf = qemu_uuid_unparse_strdup(&qemu_uuid);
@ -1295,7 +1312,7 @@ static void *spapr_build_fdt(sPAPRMachineState *spapr)
QLIST_FOREACH(phb, &spapr->phbs, list) {
ret = spapr_populate_pci_dt(phb, PHANDLE_INTC, fdt,
spapr->irq->nr_msis);
spapr->irq->nr_msis, NULL);
if (ret < 0) {
error_report("couldn't setup PCI devices in fdt");
exit(1);
@ -1348,6 +1365,14 @@ static void *spapr_build_fdt(sPAPRMachineState *spapr)
exit(1);
}
if (smc->dr_phb_enabled) {
ret = spapr_drc_populate_dt(fdt, 0, NULL, SPAPR_DR_CONNECTOR_TYPE_PHB);
if (ret < 0) {
error_report("Couldn't set up PHB DR device tree properties");
exit(1);
}
}
return fdt;
}
@ -1372,11 +1397,44 @@ static void emulate_spapr_hypercall(PPCVirtualHypervisor *vhyp,
}
}
static uint64_t spapr_get_patbe(PPCVirtualHypervisor *vhyp)
struct LPCRSyncState {
target_ulong value;
target_ulong mask;
};
static void do_lpcr_sync(CPUState *cs, run_on_cpu_data arg)
{
struct LPCRSyncState *s = arg.host_ptr;
PowerPCCPU *cpu = POWERPC_CPU(cs);
CPUPPCState *env = &cpu->env;
target_ulong lpcr;
cpu_synchronize_state(cs);
lpcr = env->spr[SPR_LPCR];
lpcr &= ~s->mask;
lpcr |= s->value;
ppc_store_lpcr(cpu, lpcr);
}
void spapr_set_all_lpcrs(target_ulong value, target_ulong mask)
{
CPUState *cs;
struct LPCRSyncState s = {
.value = value,
.mask = mask
};
CPU_FOREACH(cs) {
run_on_cpu(cs, do_lpcr_sync, RUN_ON_CPU_HOST_PTR(&s));
}
}
static void spapr_get_pate(PPCVirtualHypervisor *vhyp, ppc_v3_pate_t *entry)
{
sPAPRMachineState *spapr = SPAPR_MACHINE(vhyp);
return spapr->patb_entry;
/* Copy PATE1:GR into PATE0:HR */
entry->dw0 = spapr->patb_entry & PATE0_HR;
entry->dw1 = spapr->patb_entry;
}
#define HPTE(_table, _i) (void *)(((uint64_t *)(_table)) + ((_i) * 2))
@ -1476,8 +1534,25 @@ static void spapr_store_hpte(PPCVirtualHypervisor *vhyp, hwaddr ptex,
if (!spapr->htab) {
kvmppc_write_hpte(ptex, pte0, pte1);
} else {
stq_p(spapr->htab + offset, pte0);
stq_p(spapr->htab + offset + HASH_PTE_SIZE_64 / 2, pte1);
if (pte0 & HPTE64_V_VALID) {
stq_p(spapr->htab + offset + HASH_PTE_SIZE_64 / 2, pte1);
/*
* When setting valid, we write PTE1 first. This ensures
* proper synchronization with the reading code in
* ppc_hash64_pteg_search()
*/
smp_wmb();
stq_p(spapr->htab + offset, pte0);
} else {
stq_p(spapr->htab + offset, pte0);
/*
* When clearing it we set PTE0 first. This ensures proper
* synchronization with the reading code in
* ppc_hash64_pteg_search()
*/
smp_wmb();
stq_p(spapr->htab + offset + HASH_PTE_SIZE_64 / 2, pte1);
}
}
}
@ -1548,7 +1623,7 @@ void spapr_reallocate_hpt(sPAPRMachineState *spapr, int shift,
}
}
/* We're setting up a hash table, so that means we're not radix */
spapr->patb_entry = 0;
spapr_set_all_lpcrs(0, LPCR_HR | LPCR_UPRT);
}
void spapr_setup_hpt_and_vrma(sPAPRMachineState *spapr)
@ -1602,16 +1677,21 @@ static void spapr_machine_reset(void)
if (kvm_enabled() && kvmppc_has_cap_mmu_radix() &&
ppc_type_check_compat(machine->cpu_type, CPU_POWERPC_LOGICAL_3_00, 0,
spapr->max_compat_pvr)) {
/* If using KVM with radix mode available, VCPUs can be started
/*
* If using KVM with radix mode available, VCPUs can be started
* without a HPT because KVM will start them in radix mode.
* Set the GR bit in PATB so that we know there is no HPT. */
spapr->patb_entry = PATBE1_GR;
* Set the GR bit in PATE so that we know there is no HPT.
*/
spapr->patb_entry = PATE1_GR;
spapr_set_all_lpcrs(LPCR_HR | LPCR_UPRT, LPCR_HR | LPCR_UPRT);
} else {
spapr_setup_hpt_and_vrma(spapr);
}
/* if this reset wasn't generated by CAS, we should reset our
* negotiated options and start from scratch */
/*
* If this reset wasn't generated by CAS, we should reset our
* negotiated options and start from scratch
*/
if (!spapr->cas_reboot) {
spapr_ovec_cleanup(spapr->ov5_cas);
spapr->ov5_cas = spapr_ovec_new();
@ -1696,9 +1776,9 @@ static void spapr_create_nvram(sPAPRMachineState *spapr)
static void spapr_rtc_create(sPAPRMachineState *spapr)
{
object_initialize(&spapr->rtc, sizeof(spapr->rtc), TYPE_SPAPR_RTC);
object_property_add_child(OBJECT(spapr), "rtc", OBJECT(&spapr->rtc),
&error_fatal);
object_initialize_child(OBJECT(spapr), "rtc",
&spapr->rtc, sizeof(spapr->rtc), TYPE_SPAPR_RTC,
&error_fatal, NULL);
object_property_set_bool(OBJECT(&spapr->rtc), true, "realized",
&error_fatal);
object_property_add_alias(OBJECT(spapr), "rtc-time", OBJECT(&spapr->rtc),
@ -1761,9 +1841,16 @@ static int spapr_post_load(void *opaque, int version_id)
if (kvm_enabled() && spapr->patb_entry) {
PowerPCCPU *cpu = POWERPC_CPU(first_cpu);
bool radix = !!(spapr->patb_entry & PATBE1_GR);
bool radix = !!(spapr->patb_entry & PATE1_GR);
bool gtse = !!(cpu->env.spr[SPR_LPCR] & LPCR_GTSE);
/*
* Update LPCR:HR and UPRT as they may not be set properly in
* the stream
*/
spapr_set_all_lpcrs(radix ? (LPCR_HR | LPCR_UPRT) : 0,
LPCR_HR | LPCR_UPRT);
err = kvmppc_configure_v3_mmu(cpu, radix, gtse, spapr->patb_entry);
if (err) {
error_report("Process table config unsupported by the host");
@ -2796,6 +2883,19 @@ static void spapr_machine_init(MachineState *machine)
/* We always have at least the nvram device on VIO */
spapr_create_nvram(spapr);
/*
* Setup hotplug / dynamic-reconfiguration connectors. top-level
* connectors (described in root DT node's "ibm,drc-types" property)
* are pre-initialized here. additional child connectors (such as
* connectors for a PHBs PCI slots) are added as needed during their
* parent's realization.
*/
if (smc->dr_phb_enabled) {
for (i = 0; i < SPAPR_MAX_PHBS; i++) {
spapr_dr_connector_new(OBJECT(machine), TYPE_SPAPR_DRC_PHB, i);
}
}
/* Set up PCI */
spapr_pci_rtas_init();
@ -2909,6 +3009,9 @@ static void spapr_machine_init(MachineState *machine)
register_savevm_live(NULL, "spapr/htab", -1, 1,
&savevm_htab_handlers, spapr);
qbus_set_hotplug_handler(sysbus_get_default(), OBJECT(machine),
&error_fatal);
qemu_register_boot_set(spapr_boot_set, spapr);
if (kvm_enabled()) {
@ -3144,6 +3247,36 @@ static void spapr_set_ic_mode(Object *obj, const char *value, Error **errp)
}
}
static char *spapr_get_host_model(Object *obj, Error **errp)
{
sPAPRMachineState *spapr = SPAPR_MACHINE(obj);
return g_strdup(spapr->host_model);
}
static void spapr_set_host_model(Object *obj, const char *value, Error **errp)
{
sPAPRMachineState *spapr = SPAPR_MACHINE(obj);
g_free(spapr->host_model);
spapr->host_model = g_strdup(value);
}
static char *spapr_get_host_serial(Object *obj, Error **errp)
{
sPAPRMachineState *spapr = SPAPR_MACHINE(obj);
return g_strdup(spapr->host_serial);
}
static void spapr_set_host_serial(Object *obj, const char *value, Error **errp)
{
sPAPRMachineState *spapr = SPAPR_MACHINE(obj);
g_free(spapr->host_serial);
spapr->host_serial = g_strdup(value);
}
static void spapr_instance_init(Object *obj)
{
sPAPRMachineState *spapr = SPAPR_MACHINE(obj);
@ -3189,6 +3322,17 @@ static void spapr_instance_init(Object *obj)
object_property_set_description(obj, "ic-mode",
"Specifies the interrupt controller mode (xics, xive, dual)",
NULL);
object_property_add_str(obj, "host-model",
spapr_get_host_model, spapr_set_host_model,
&error_abort);
object_property_set_description(obj, "host-model",
"Set host's model-id to use - none|passthrough|string", &error_abort);
object_property_add_str(obj, "host-serial",
spapr_get_host_serial, spapr_set_host_serial,
&error_abort);
object_property_set_description(obj, "host-serial",
"Set host's system-id to use - none|passthrough|string", &error_abort);
}
static void spapr_machine_finalizefn(Object *obj)
@ -3213,14 +3357,26 @@ static void spapr_nmi(NMIState *n, int cpu_index, Error **errp)
}
}
int spapr_lmb_dt_populate(sPAPRDRConnector *drc, sPAPRMachineState *spapr,
void *fdt, int *fdt_start_offset, Error **errp)
{
uint64_t addr;
uint32_t node;
addr = spapr_drc_index(drc) * SPAPR_MEMORY_BLOCK_SIZE;
node = object_property_get_uint(OBJECT(drc->dev), PC_DIMM_NODE_PROP,
&error_abort);
*fdt_start_offset = spapr_populate_memory_node(fdt, node, addr,
SPAPR_MEMORY_BLOCK_SIZE);
return 0;
}
static void spapr_add_lmbs(DeviceState *dev, uint64_t addr_start, uint64_t size,
uint32_t node, bool dedicated_hp_event_source,
Error **errp)
bool dedicated_hp_event_source, Error **errp)
{
sPAPRDRConnector *drc;
uint32_t nr_lmbs = size/SPAPR_MEMORY_BLOCK_SIZE;
int i, fdt_offset, fdt_size;
void *fdt;
int i;
uint64_t addr = addr_start;
bool hotplugged = spapr_drc_hotplugged(dev);
Error *local_err = NULL;
@ -3230,11 +3386,7 @@ static void spapr_add_lmbs(DeviceState *dev, uint64_t addr_start, uint64_t size,
addr / SPAPR_MEMORY_BLOCK_SIZE);
g_assert(drc);
fdt = create_device_tree(&fdt_size);
fdt_offset = spapr_populate_memory_node(fdt, node, addr,
SPAPR_MEMORY_BLOCK_SIZE);
spapr_drc_attach(drc, dev, fdt, fdt_offset, &local_err);
spapr_drc_attach(drc, dev, &local_err);
if (local_err) {
while (addr > addr_start) {
addr -= SPAPR_MEMORY_BLOCK_SIZE;
@ -3242,7 +3394,6 @@ static void spapr_add_lmbs(DeviceState *dev, uint64_t addr_start, uint64_t size,
addr / SPAPR_MEMORY_BLOCK_SIZE);
spapr_drc_detach(drc);
}
g_free(fdt);
error_propagate(errp, local_err);
return;
}
@ -3275,7 +3426,6 @@ static void spapr_memory_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
sPAPRMachineState *ms = SPAPR_MACHINE(hotplug_dev);
PCDIMMDevice *dimm = PC_DIMM(dev);
uint64_t size, addr;
uint32_t node;
size = memory_device_get_region_size(MEMORY_DEVICE(dev), &error_abort);
@ -3290,10 +3440,7 @@ static void spapr_memory_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
goto out_unplug;
}
node = object_property_get_uint(OBJECT(dev), PC_DIMM_NODE_PROP,
&error_abort);
spapr_add_lmbs(dev, addr, size, node,
spapr_ovec_test(ms->ov5_cas, OV5_HP_EVT),
spapr_add_lmbs(dev, addr, size, spapr_ovec_test(ms->ov5_cas, OV5_HP_EVT),
&local_err);
if (local_err) {
goto out_unplug;
@ -3513,27 +3660,6 @@ out:
error_propagate(errp, local_err);
}
static void *spapr_populate_hotplug_cpu_dt(CPUState *cs, int *fdt_offset,
sPAPRMachineState *spapr)
{
PowerPCCPU *cpu = POWERPC_CPU(cs);
DeviceClass *dc = DEVICE_GET_CLASS(cs);
int id = spapr_get_vcpu_id(cpu);
void *fdt;
int offset, fdt_size;
char *nodename;
fdt = create_device_tree(&fdt_size);
nodename = g_strdup_printf("%s@%x", dc->fw_name, id);
offset = fdt_add_subnode(fdt, 0, nodename);
spapr_populate_cpu_dt(cs, fdt, offset, spapr);
g_free(nodename);
*fdt_offset = offset;
return fdt;
}
/* Callback to be called during DRC release. */
void spapr_core_release(DeviceState *dev)
{
@ -3594,6 +3720,27 @@ void spapr_core_unplug_request(HotplugHandler *hotplug_dev, DeviceState *dev,
spapr_hotplug_req_remove_by_index(drc);
}
int spapr_core_dt_populate(sPAPRDRConnector *drc, sPAPRMachineState *spapr,
void *fdt, int *fdt_start_offset, Error **errp)
{
sPAPRCPUCore *core = SPAPR_CPU_CORE(drc->dev);
CPUState *cs = CPU(core->threads[0]);
PowerPCCPU *cpu = POWERPC_CPU(cs);
DeviceClass *dc = DEVICE_GET_CLASS(cs);
int id = spapr_get_vcpu_id(cpu);
char *nodename;
int offset;
nodename = g_strdup_printf("%s@%x", dc->fw_name, id);
offset = fdt_add_subnode(fdt, 0, nodename);
g_free(nodename);
spapr_populate_cpu_dt(cs, fdt, offset, spapr);
*fdt_start_offset = offset;
return 0;
}
static void spapr_core_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
Error **errp)
{
@ -3602,7 +3749,7 @@ static void spapr_core_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
sPAPRMachineClass *smc = SPAPR_MACHINE_CLASS(mc);
sPAPRCPUCore *core = SPAPR_CPU_CORE(OBJECT(dev));
CPUCore *cc = CPU_CORE(dev);
CPUState *cs = CPU(core->threads[0]);
CPUState *cs;
sPAPRDRConnector *drc;
Error *local_err = NULL;
CPUArchId *core_slot;
@ -3621,14 +3768,8 @@ static void spapr_core_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
g_assert(drc || !mc->has_hotpluggable_cpus);
if (drc) {
void *fdt;
int fdt_offset;
fdt = spapr_populate_hotplug_cpu_dt(cs, &fdt_offset, spapr);
spapr_drc_attach(drc, dev, fdt, fdt_offset, &local_err);
spapr_drc_attach(drc, dev, &local_err);
if (local_err) {
g_free(fdt);
error_propagate(errp, local_err);
return;
}
@ -3712,6 +3853,115 @@ out:
error_propagate(errp, local_err);
}
int spapr_phb_dt_populate(sPAPRDRConnector *drc, sPAPRMachineState *spapr,
void *fdt, int *fdt_start_offset, Error **errp)
{
sPAPRPHBState *sphb = SPAPR_PCI_HOST_BRIDGE(drc->dev);
int intc_phandle;
intc_phandle = spapr_irq_get_phandle(spapr, spapr->fdt_blob, errp);
if (intc_phandle <= 0) {
return -1;
}
if (spapr_populate_pci_dt(sphb, intc_phandle, fdt, spapr->irq->nr_msis,
fdt_start_offset)) {
error_setg(errp, "unable to create FDT node for PHB %d", sphb->index);
return -1;
}
/* generally SLOF creates these, for hotplug it's up to QEMU */
_FDT(fdt_setprop_string(fdt, *fdt_start_offset, "name", "pci"));
return 0;
}
static void spapr_phb_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
Error **errp)
{
sPAPRMachineState *spapr = SPAPR_MACHINE(OBJECT(hotplug_dev));
sPAPRPHBState *sphb = SPAPR_PCI_HOST_BRIDGE(dev);
sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr);
const unsigned windows_supported = spapr_phb_windows_supported(sphb);
if (dev->hotplugged && !smc->dr_phb_enabled) {
error_setg(errp, "PHB hotplug not supported for this machine");
return;
}
if (sphb->index == (uint32_t)-1) {
error_setg(errp, "\"index\" for PAPR PHB is mandatory");
return;
}
/*
* This will check that sphb->index doesn't exceed the maximum number of
* PHBs for the current machine type.
*/
smc->phb_placement(spapr, sphb->index,
&sphb->buid, &sphb->io_win_addr,
&sphb->mem_win_addr, &sphb->mem64_win_addr,
windows_supported, sphb->dma_liobn, errp);
}
static void spapr_phb_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
Error **errp)
{
sPAPRMachineState *spapr = SPAPR_MACHINE(OBJECT(hotplug_dev));
sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr);
sPAPRPHBState *sphb = SPAPR_PCI_HOST_BRIDGE(dev);
sPAPRDRConnector *drc;
bool hotplugged = spapr_drc_hotplugged(dev);
Error *local_err = NULL;
if (!smc->dr_phb_enabled) {
return;
}
drc = spapr_drc_by_id(TYPE_SPAPR_DRC_PHB, sphb->index);
/* hotplug hooks should check it's enabled before getting this far */
assert(drc);
spapr_drc_attach(drc, DEVICE(dev), &local_err);
if (local_err) {
error_propagate(errp, local_err);
return;
}
if (hotplugged) {
spapr_hotplug_req_add_by_index(drc);
} else {
spapr_drc_reset(drc);
}
}
void spapr_phb_release(DeviceState *dev)
{
HotplugHandler *hotplug_ctrl = qdev_get_hotplug_handler(dev);
hotplug_handler_unplug(hotplug_ctrl, dev, &error_abort);
}
static void spapr_phb_unplug(HotplugHandler *hotplug_dev, DeviceState *dev)
{
object_unparent(OBJECT(dev));
}
static void spapr_phb_unplug_request(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp)
{
sPAPRPHBState *sphb = SPAPR_PCI_HOST_BRIDGE(dev);
sPAPRDRConnector *drc;
drc = spapr_drc_by_id(TYPE_SPAPR_DRC_PHB, sphb->index);
assert(drc);
if (!spapr_drc_unplug_requested(drc)) {
spapr_drc_detach(drc);
spapr_hotplug_req_remove_by_index(drc);
}
}
static void spapr_machine_device_plug(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp)
{
@ -3719,6 +3969,8 @@ static void spapr_machine_device_plug(HotplugHandler *hotplug_dev,
spapr_memory_plug(hotplug_dev, dev, errp);
} else if (object_dynamic_cast(OBJECT(dev), TYPE_SPAPR_CPU_CORE)) {
spapr_core_plug(hotplug_dev, dev, errp);
} else if (object_dynamic_cast(OBJECT(dev), TYPE_SPAPR_PCI_HOST_BRIDGE)) {
spapr_phb_plug(hotplug_dev, dev, errp);
}
}
@ -3729,6 +3981,8 @@ static void spapr_machine_device_unplug(HotplugHandler *hotplug_dev,
spapr_memory_unplug(hotplug_dev, dev);
} else if (object_dynamic_cast(OBJECT(dev), TYPE_SPAPR_CPU_CORE)) {
spapr_core_unplug(hotplug_dev, dev);
} else if (object_dynamic_cast(OBJECT(dev), TYPE_SPAPR_PCI_HOST_BRIDGE)) {
spapr_phb_unplug(hotplug_dev, dev);
}
}
@ -3737,6 +3991,7 @@ static void spapr_machine_device_unplug_request(HotplugHandler *hotplug_dev,
{
sPAPRMachineState *sms = SPAPR_MACHINE(OBJECT(hotplug_dev));
MachineClass *mc = MACHINE_GET_CLASS(sms);
sPAPRMachineClass *smc = SPAPR_MACHINE_CLASS(mc);
if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
if (spapr_ovec_test(sms->ov5_cas, OV5_HP_EVT)) {
@ -3756,6 +4011,12 @@ static void spapr_machine_device_unplug_request(HotplugHandler *hotplug_dev,
return;
}
spapr_core_unplug_request(hotplug_dev, dev, errp);
} else if (object_dynamic_cast(OBJECT(dev), TYPE_SPAPR_PCI_HOST_BRIDGE)) {
if (!smc->dr_phb_enabled) {
error_setg(errp, "PHB hot unplug not supported on this machine");
return;
}
spapr_phb_unplug_request(hotplug_dev, dev, errp);
}
}
@ -3766,6 +4027,8 @@ static void spapr_machine_device_pre_plug(HotplugHandler *hotplug_dev,
spapr_memory_pre_plug(hotplug_dev, dev, errp);
} else if (object_dynamic_cast(OBJECT(dev), TYPE_SPAPR_CPU_CORE)) {
spapr_core_pre_plug(hotplug_dev, dev, errp);
} else if (object_dynamic_cast(OBJECT(dev), TYPE_SPAPR_PCI_HOST_BRIDGE)) {
spapr_phb_pre_plug(hotplug_dev, dev, errp);
}
}
@ -3773,7 +4036,8 @@ static HotplugHandler *spapr_get_hotplug_handler(MachineState *machine,
DeviceState *dev)
{
if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM) ||
object_dynamic_cast(OBJECT(dev), TYPE_SPAPR_CPU_CORE)) {
object_dynamic_cast(OBJECT(dev), TYPE_SPAPR_CPU_CORE) ||
object_dynamic_cast(OBJECT(dev), TYPE_SPAPR_PCI_HOST_BRIDGE)) {
return HOTPLUG_HANDLER(machine);
}
return NULL;
@ -4004,7 +4268,7 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data)
vhc->map_hptes = spapr_map_hptes;
vhc->unmap_hptes = spapr_unmap_hptes;
vhc->store_hpte = spapr_store_hpte;
vhc->get_patbe = spapr_get_patbe;
vhc->get_pate = spapr_get_pate;
vhc->encode_hpt_for_kvm_pr = spapr_encode_hpt_for_kvm_pr;
xic->ics_get = spapr_ics_get;
xic->ics_resend = spapr_ics_resend;
@ -4026,6 +4290,7 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data)
smc->default_caps.caps[SPAPR_CAP_NESTED_KVM_HV] = SPAPR_CAP_OFF;
spapr_caps_add_properties(smc, &error_abort);
smc->irq = &spapr_irq_xics;
smc->dr_phb_enabled = true;
}
static const TypeInfo spapr_machine_info = {
@ -4086,11 +4351,18 @@ DEFINE_SPAPR_MACHINE(4_0, "4.0", true);
static void spapr_machine_3_1_class_options(MachineClass *mc)
{
sPAPRMachineClass *smc = SPAPR_MACHINE_CLASS(mc);
static GlobalProperty compat[] = {
{ TYPE_SPAPR_MACHINE, "host-model", "passthrough" },
{ TYPE_SPAPR_MACHINE, "host-serial", "passthrough" },
};
spapr_machine_4_0_class_options(mc);
compat_props_add(mc->compat_props, hw_compat_3_1, hw_compat_3_1_len);
compat_props_add(mc->compat_props, compat, G_N_ELEMENTS(compat));
mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("power8_v2.0");
smc->update_dt_enabled = false;
smc->dr_phb_enabled = false;
}
DEFINE_SPAPR_MACHINE(3_1, "3.1", false);

View File

@ -22,6 +22,7 @@
#include "qemu/error-report.h"
#include "hw/ppc/spapr.h" /* for RTAS return codes */
#include "hw/pci-host/spapr.h" /* spapr_phb_remove_pci_device_cb callback */
#include "sysemu/device_tree.h"
#include "trace.h"
#define DRC_CONTAINER_PATH "/dr-connector"
@ -373,8 +374,7 @@ static void prop_get_fdt(Object *obj, Visitor *v, const char *name,
} while (fdt_depth != 0);
}
void spapr_drc_attach(sPAPRDRConnector *drc, DeviceState *d, void *fdt,
int fdt_start_offset, Error **errp)
void spapr_drc_attach(sPAPRDRConnector *drc, DeviceState *d, Error **errp)
{
trace_spapr_drc_attach(spapr_drc_index(drc));
@ -384,11 +384,8 @@ void spapr_drc_attach(sPAPRDRConnector *drc, DeviceState *d, void *fdt,
}
g_assert((drc->state == SPAPR_DRC_STATE_LOGICAL_UNUSABLE)
|| (drc->state == SPAPR_DRC_STATE_PHYSICAL_POWERON));
g_assert(fdt);
drc->dev = d;
drc->fdt = fdt;
drc->fdt_start_offset = fdt_start_offset;
object_property_add_link(OBJECT(drc), "device",
object_get_typename(OBJECT(drc->dev)),
@ -674,6 +671,7 @@ static void spapr_drc_cpu_class_init(ObjectClass *k, void *data)
drck->typename = "CPU";
drck->drc_name_prefix = "CPU ";
drck->release = spapr_core_release;
drck->dt_populate = spapr_core_dt_populate;
}
static void spapr_drc_pci_class_init(ObjectClass *k, void *data)
@ -684,6 +682,7 @@ static void spapr_drc_pci_class_init(ObjectClass *k, void *data)
drck->typename = "28";
drck->drc_name_prefix = "C";
drck->release = spapr_phb_remove_pci_device_cb;
drck->dt_populate = spapr_pci_dt_populate;
}
static void spapr_drc_lmb_class_init(ObjectClass *k, void *data)
@ -694,6 +693,18 @@ static void spapr_drc_lmb_class_init(ObjectClass *k, void *data)
drck->typename = "MEM";
drck->drc_name_prefix = "LMB ";
drck->release = spapr_lmb_release;
drck->dt_populate = spapr_lmb_dt_populate;
}
static void spapr_drc_phb_class_init(ObjectClass *k, void *data)
{
sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_CLASS(k);
drck->typeshift = SPAPR_DR_CONNECTOR_TYPE_SHIFT_PHB;
drck->typename = "PHB";
drck->drc_name_prefix = "PHB ";
drck->release = spapr_phb_release;
drck->dt_populate = spapr_phb_dt_populate;
}
static const TypeInfo spapr_dr_connector_info = {
@ -739,6 +750,13 @@ static const TypeInfo spapr_drc_lmb_info = {
.class_init = spapr_drc_lmb_class_init,
};
static const TypeInfo spapr_drc_phb_info = {
.name = TYPE_SPAPR_DRC_PHB,
.parent = TYPE_SPAPR_DRC_LOGICAL,
.instance_size = sizeof(sPAPRDRConnector),
.class_init = spapr_drc_phb_class_init,
};
/* helper functions for external users */
sPAPRDRConnector *spapr_drc_by_index(uint32_t index)
@ -1102,10 +1120,28 @@ static void rtas_ibm_configure_connector(PowerPCCPU *cpu,
goto out;
}
g_assert(drc->fdt);
drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
if (!drc->fdt) {
Error *local_err = NULL;
void *fdt;
int fdt_size;
fdt = create_device_tree(&fdt_size);
if (drck->dt_populate(drc, spapr, fdt, &drc->fdt_start_offset,
&local_err)) {
g_free(fdt);
error_free(local_err);
rc = SPAPR_DR_CC_RESPONSE_ERROR;
goto out;
}
drc->fdt = fdt;
drc->ccs_offset = drc->fdt_start_offset;
drc->ccs_depth = 0;
}
do {
uint32_t tag;
const char *name;
@ -1189,6 +1225,7 @@ static void spapr_drc_register_types(void)
type_register_static(&spapr_drc_cpu_info);
type_register_static(&spapr_drc_pci_info);
type_register_static(&spapr_drc_lmb_info);
type_register_static(&spapr_drc_phb_info);
spapr_rtas_register(RTAS_SET_INDICATOR, "set-indicator",
rtas_set_indicator);

View File

@ -526,6 +526,9 @@ static void spapr_hotplug_req_event(uint8_t hp_id, uint8_t hp_action,
case SPAPR_DR_CONNECTOR_TYPE_CPU:
hp->hotplug_type = RTAS_LOG_V6_HP_TYPE_CPU;
break;
case SPAPR_DR_CONNECTOR_TYPE_PHB:
hp->hotplug_type = RTAS_LOG_V6_HP_TYPE_PHB;
break;
default:
/* we shouldn't be signaling hotplug events for resources
* that don't support them

View File

@ -17,37 +17,6 @@
#include "mmu-book3s-v3.h"
#include "hw/mem/memory-device.h"
struct LPCRSyncState {
target_ulong value;
target_ulong mask;
};
static void do_lpcr_sync(CPUState *cs, run_on_cpu_data arg)
{
struct LPCRSyncState *s = arg.host_ptr;
PowerPCCPU *cpu = POWERPC_CPU(cs);
CPUPPCState *env = &cpu->env;
target_ulong lpcr;
cpu_synchronize_state(cs);
lpcr = env->spr[SPR_LPCR];
lpcr &= ~s->mask;
lpcr |= s->value;
ppc_store_lpcr(cpu, lpcr);
}
static void set_all_lpcrs(target_ulong value, target_ulong mask)
{
CPUState *cs;
struct LPCRSyncState s = {
.value = value,
.mask = mask
};
CPU_FOREACH(cs) {
run_on_cpu(cs, do_lpcr_sync, RUN_ON_CPU_HOST_PTR(&s));
}
}
static bool has_spr(PowerPCCPU *cpu, int spr)
{
/* We can test whether the SPR is defined by checking for a valid name */
@ -1255,12 +1224,12 @@ static target_ulong h_set_mode_resource_le(PowerPCCPU *cpu,
switch (mflags) {
case H_SET_MODE_ENDIAN_BIG:
set_all_lpcrs(0, LPCR_ILE);
spapr_set_all_lpcrs(0, LPCR_ILE);
spapr_pci_switch_vga(true);
return H_SUCCESS;
case H_SET_MODE_ENDIAN_LITTLE:
set_all_lpcrs(LPCR_ILE, LPCR_ILE);
spapr_set_all_lpcrs(LPCR_ILE, LPCR_ILE);
spapr_pci_switch_vga(false);
return H_SUCCESS;
}
@ -1289,7 +1258,7 @@ static target_ulong h_set_mode_resource_addr_trans_mode(PowerPCCPU *cpu,
return H_UNSUPPORTED_FLAG;
}
set_all_lpcrs(mflags << LPCR_AIL_SHIFT, LPCR_AIL);
spapr_set_all_lpcrs(mflags << LPCR_AIL_SHIFT, LPCR_AIL);
return H_SUCCESS;
}
@ -1342,12 +1311,12 @@ static void spapr_check_setup_free_hpt(sPAPRMachineState *spapr,
* later and so assumed radix and now it's called H_REG_PROC_TBL
*/
if ((patbe_old & PATBE1_GR) == (patbe_new & PATBE1_GR)) {
if ((patbe_old & PATE1_GR) == (patbe_new & PATE1_GR)) {
/* We assume RADIX, so this catches all the "Do Nothing" cases */
} else if (!(patbe_old & PATBE1_GR)) {
} else if (!(patbe_old & PATE1_GR)) {
/* HASH->RADIX : Free HPT */
spapr_free_hpt(spapr);
} else if (!(patbe_new & PATBE1_GR)) {
} else if (!(patbe_new & PATE1_GR)) {
/* RADIX->HASH || NOTHING->HASH : Allocate HPT */
spapr_setup_hpt_and_vrma(spapr);
}
@ -1385,7 +1354,7 @@ static target_ulong h_register_process_table(PowerPCCPU *cpu,
} else if (table_size > 24) {
return H_P4;
}
cproc = PATBE1_GR | proc_tbl | table_size;
cproc = PATE1_GR | proc_tbl | table_size;
} else { /* Register new HPT process table */
if (flags & FLAG_HASH_PROC_TBL) { /* Hash with Segment Tables */
/* TODO - Not Supported */
@ -1404,13 +1373,15 @@ static target_ulong h_register_process_table(PowerPCCPU *cpu,
}
} else { /* Deregister current process table */
/* Set to benign value: (current GR) | 0. This allows
* deregistration in KVM to succeed even if the radix bit in flags
* doesn't match the radix bit in the old PATB. */
cproc = spapr->patb_entry & PATBE1_GR;
/*
* Set to benign value: (current GR) | 0. This allows
* deregistration in KVM to succeed even if the radix bit
* in flags doesn't match the radix bit in the old PATE.
*/
cproc = spapr->patb_entry & PATE1_GR;
}
} else { /* Maintain current registration */
if (!(flags & FLAG_RADIX) != !(spapr->patb_entry & PATBE1_GR)) {
if (!(flags & FLAG_RADIX) != !(spapr->patb_entry & PATE1_GR)) {
/* Technically caused by flag bits => H_PARAMETER */
return H_PARAMETER; /* Existing Process Table Mismatch */
}
@ -1422,10 +1393,11 @@ static target_ulong h_register_process_table(PowerPCCPU *cpu,
spapr->patb_entry = cproc; /* Save new process table */
/* Update the UPRT and GTSE bits in the LPCR for all cpus */
set_all_lpcrs(((flags & (FLAG_RADIX | FLAG_HASH_PROC_TBL)) ? LPCR_UPRT : 0) |
((flags & FLAG_GTSE) ? LPCR_GTSE : 0),
LPCR_UPRT | LPCR_GTSE);
/* Update the UPRT, HR and GTSE bits in the LPCR for all cpus */
spapr_set_all_lpcrs(((flags & (FLAG_RADIX | FLAG_HASH_PROC_TBL)) ?
(LPCR_UPRT | LPCR_HR) : 0) |
((flags & FLAG_GTSE) ? LPCR_GTSE : 0),
LPCR_UPRT | LPCR_HR | LPCR_GTSE);
if (kvm_enabled()) {
return kvmppc_configure_v3_mmu(cpu, flags & FLAG_RADIX,
@ -1646,7 +1618,7 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu,
if (!spapr->cas_reboot) {
/* If spapr_machine_reset() did not set up a HPT but one is necessary
* (because the guest isn't going to use radix) then set it up here. */
if ((spapr->patb_entry & PATBE1_GR) && !guest_radix) {
if ((spapr->patb_entry & PATE1_GR) && !guest_radix) {
/* legacy hash or new hash: */
spapr_setup_hpt_and_vrma(spapr);
}

View File

@ -230,6 +230,11 @@ static void spapr_irq_reset_xics(sPAPRMachineState *spapr, Error **errp)
/* TODO: create the KVM XICS device */
}
static const char *spapr_irq_get_nodename_xics(sPAPRMachineState *spapr)
{
return XICS_NODENAME;
}
#define SPAPR_IRQ_XICS_NR_IRQS 0x1000
#define SPAPR_IRQ_XICS_NR_MSIS \
(XICS_IRQ_BASE + SPAPR_IRQ_XICS_NR_IRQS - SPAPR_IRQ_MSI)
@ -249,6 +254,7 @@ sPAPRIrq spapr_irq_xics = {
.post_load = spapr_irq_post_load_xics,
.reset = spapr_irq_reset_xics,
.set_irq = spapr_irq_set_irq_xics,
.get_nodename = spapr_irq_get_nodename_xics,
};
/*
@ -384,6 +390,11 @@ static void spapr_irq_set_irq_xive(void *opaque, int srcno, int val)
xive_source_set_irq(&spapr->xive->source, srcno, val);
}
static const char *spapr_irq_get_nodename_xive(sPAPRMachineState *spapr)
{
return spapr->xive->nodename;
}
/*
* XIVE uses the full IRQ number space. Set it to 8K to be compatible
* with XICS.
@ -407,6 +418,7 @@ sPAPRIrq spapr_irq_xive = {
.post_load = spapr_irq_post_load_xive,
.reset = spapr_irq_reset_xive,
.set_irq = spapr_irq_set_irq_xive,
.get_nodename = spapr_irq_get_nodename_xive,
};
/*
@ -541,6 +553,11 @@ static void spapr_irq_set_irq_dual(void *opaque, int srcno, int val)
spapr_irq_current(spapr)->set_irq(spapr, srcno, val);
}
static const char *spapr_irq_get_nodename_dual(sPAPRMachineState *spapr)
{
return spapr_irq_current(spapr)->get_nodename(spapr);
}
/*
* Define values in sync with the XIVE and XICS backend
*/
@ -561,7 +578,8 @@ sPAPRIrq spapr_irq_dual = {
.cpu_intc_create = spapr_irq_cpu_intc_create_dual,
.post_load = spapr_irq_post_load_dual,
.reset = spapr_irq_reset_dual,
.set_irq = spapr_irq_set_irq_dual
.set_irq = spapr_irq_set_irq_dual,
.get_nodename = spapr_irq_get_nodename_dual,
};
/*
@ -620,6 +638,27 @@ void spapr_irq_reset(sPAPRMachineState *spapr, Error **errp)
}
}
int spapr_irq_get_phandle(sPAPRMachineState *spapr, void *fdt, Error **errp)
{
const char *nodename = spapr->irq->get_nodename(spapr);
int offset, phandle;
offset = fdt_subnode_offset(fdt, 0, nodename);
if (offset < 0) {
error_setg(errp, "Can't find node \"%s\": %s", nodename,
fdt_strerror(offset));
return -1;
}
phandle = fdt_get_phandle(fdt, offset);
if (!phandle) {
error_setg(errp, "Can't get phandle of node \"%s\"", nodename);
return -1;
}
return phandle;
}
/*
* XICS legacy routines - to deprecate one day
*/
@ -691,4 +730,5 @@ sPAPRIrq spapr_irq_xics_legacy = {
.cpu_intc_create = spapr_irq_cpu_intc_create_xics,
.post_load = spapr_irq_post_load_xics,
.set_irq = spapr_irq_set_irq_xics,
.get_nodename = spapr_irq_get_nodename_xics,
};

View File

@ -16,6 +16,7 @@
#include "qemu/bitmap.h"
#include "exec/address-spaces.h"
#include "qemu/error-report.h"
#include "sysemu/qtest.h"
#include "trace.h"
#include <libfdt.h>
@ -131,6 +132,11 @@ bool spapr_ovec_test(sPAPROptionVector *ov, long bitnr)
g_assert(ov);
g_assert(bitnr < OV_MAXBITS);
/* support memory unplug for qtest */
if (qtest_enabled() && bitnr == OV5_HP_EVT) {
return true;
}
return test_bit(bitnr, ov->bitmap) ? true : false;
}

View File

@ -1408,6 +1408,17 @@ static uint32_t spapr_phb_get_pci_drc_index(sPAPRPHBState *phb,
return spapr_drc_index(drc);
}
int spapr_pci_dt_populate(sPAPRDRConnector *drc, sPAPRMachineState *spapr,
void *fdt, int *fdt_start_offset, Error **errp)
{
HotplugHandler *plug_handler = qdev_get_hotplug_handler(drc->dev);
sPAPRPHBState *sphb = SPAPR_PCI_HOST_BRIDGE(plug_handler);
PCIDevice *pdev = PCI_DEVICE(drc->dev);
*fdt_start_offset = spapr_create_pci_child_dt(sphb, pdev, fdt, 0);
return 0;
}
static void spapr_pci_plug(HotplugHandler *plug_handler,
DeviceState *plugged_dev, Error **errp)
{
@ -1417,8 +1428,6 @@ static void spapr_pci_plug(HotplugHandler *plug_handler,
Error *local_err = NULL;
PCIBus *bus = PCI_BUS(qdev_get_parent_bus(DEVICE(pdev)));
uint32_t slotnr = PCI_SLOT(pdev->devfn);
void *fdt = NULL;
int fdt_start_offset, fdt_size;
/* if DR is disabled we don't need to do anything in the case of
* hotplug or coldplug callbacks
@ -1448,10 +1457,7 @@ static void spapr_pci_plug(HotplugHandler *plug_handler,
goto out;
}
fdt = create_device_tree(&fdt_size);
fdt_start_offset = spapr_create_pci_child_dt(phb, pdev, fdt, 0);
spapr_drc_attach(drc, DEVICE(pdev), fdt, fdt_start_offset, &local_err);
spapr_drc_attach(drc, DEVICE(pdev), &local_err);
if (local_err) {
goto out;
}
@ -1483,7 +1489,6 @@ static void spapr_pci_plug(HotplugHandler *plug_handler,
out:
if (local_err) {
error_propagate(errp, local_err);
g_free(fdt);
}
}
@ -1565,6 +1570,75 @@ static void spapr_pci_unplug_request(HotplugHandler *plug_handler,
}
}
static void spapr_phb_finalizefn(Object *obj)
{
sPAPRPHBState *sphb = SPAPR_PCI_HOST_BRIDGE(obj);
g_free(sphb->dtbusname);
sphb->dtbusname = NULL;
}
static void spapr_phb_unrealize(DeviceState *dev, Error **errp)
{
sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine());
SysBusDevice *s = SYS_BUS_DEVICE(dev);
PCIHostState *phb = PCI_HOST_BRIDGE(s);
sPAPRPHBState *sphb = SPAPR_PCI_HOST_BRIDGE(phb);
sPAPRTCETable *tcet;
int i;
const unsigned windows_supported = spapr_phb_windows_supported(sphb);
if (sphb->msi) {
g_hash_table_unref(sphb->msi);
sphb->msi = NULL;
}
/*
* Remove IO/MMIO subregions and aliases, rest should get cleaned
* via PHB's unrealize->object_finalize
*/
for (i = windows_supported - 1; i >= 0; i--) {
tcet = spapr_tce_find_by_liobn(sphb->dma_liobn[i]);
if (tcet) {
memory_region_del_subregion(&sphb->iommu_root,
spapr_tce_get_iommu(tcet));
}
}
if (sphb->dr_enabled) {
for (i = PCI_SLOT_MAX * 8 - 1; i >= 0; i--) {
sPAPRDRConnector *drc = spapr_drc_by_id(TYPE_SPAPR_DRC_PCI,
(sphb->index << 16) | i);
if (drc) {
object_unparent(OBJECT(drc));
}
}
}
for (i = PCI_NUM_PINS - 1; i >= 0; i--) {
if (sphb->lsi_table[i].irq) {
spapr_irq_free(spapr, sphb->lsi_table[i].irq, 1);
sphb->lsi_table[i].irq = 0;
}
}
QLIST_REMOVE(sphb, list);
memory_region_del_subregion(&sphb->iommu_root, &sphb->msiwindow);
address_space_destroy(&sphb->iommu_as);
qbus_set_hotplug_handler(BUS(phb->bus), NULL, &error_abort);
pci_unregister_root_bus(phb->bus);
memory_region_del_subregion(get_system_memory(), &sphb->iowindow);
if (sphb->mem64_win_pciaddr != (hwaddr)-1) {
memory_region_del_subregion(get_system_memory(), &sphb->mem64window);
}
memory_region_del_subregion(get_system_memory(), &sphb->mem32window);
}
static void spapr_phb_realize(DeviceState *dev, Error **errp)
{
/* We don't use SPAPR_MACHINE() in order to exit gracefully if the user
@ -1582,29 +1656,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;
const unsigned windows_supported = spapr_phb_windows_supported(sphb);
if (!spapr) {
error_setg(errp, TYPE_SPAPR_PCI_HOST_BRIDGE " needs a pseries machine");
return;
}
if (sphb->index != (uint32_t)-1) {
Error *local_err = NULL;
smc->phb_placement(spapr, sphb->index,
&sphb->buid, &sphb->io_win_addr,
&sphb->mem_win_addr, &sphb->mem64_win_addr,
windows_supported, sphb->dma_liobn, &local_err);
if (local_err) {
error_propagate(errp, local_err);
return;
}
} else {
error_setg(errp, "\"index\" for PAPR PHB is mandatory");
return;
}
assert(sphb->index != (uint32_t)-1); /* checked in spapr_phb_pre_plug() */
if (sphb->mem64_win_size != 0) {
if (sphb->mem_win_size > SPAPR_PCI_MEM32_WIN_SIZE) {
@ -1740,6 +1799,10 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp)
if (local_err) {
error_propagate_prepend(errp, local_err,
"can't allocate LSIs: ");
/*
* Older machines will never support PHB hotplug, ie, this is an
* init only path and QEMU will terminate. No need to rollback.
*/
return;
}
}
@ -1747,7 +1810,7 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp)
spapr_irq_claim(spapr, irq, true, &local_err);
if (local_err) {
error_propagate_prepend(errp, local_err, "can't allocate LSIs: ");
return;
goto unrealize;
}
sphb->lsi_table[i].irq = irq;
@ -1767,13 +1830,17 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp)
if (!tcet) {
error_setg(errp, "Creating window#%d failed for %s",
i, sphb->dtbusname);
return;
goto unrealize;
}
memory_region_add_subregion(&sphb->iommu_root, 0,
spapr_tce_get_iommu(tcet));
}
sphb->msi = g_hash_table_new_full(g_int_hash, g_int_equal, g_free, g_free);
return;
unrealize:
spapr_phb_unrealize(dev, NULL);
}
static int spapr_phb_children_reset(Object *child, void *opaque)
@ -1972,6 +2039,7 @@ static void spapr_phb_class_init(ObjectClass *klass, void *data)
hc->root_bus_path = spapr_phb_root_bus_path;
dc->realize = spapr_phb_realize;
dc->unrealize = spapr_phb_unrealize;
dc->props = spapr_phb_properties;
dc->reset = spapr_phb_reset;
dc->vmsd = &vmstate_spapr_pci;
@ -1987,6 +2055,7 @@ static const TypeInfo spapr_phb_info = {
.name = TYPE_SPAPR_PCI_HOST_BRIDGE,
.parent = TYPE_PCI_HOST_BRIDGE,
.instance_size = sizeof(sPAPRPHBState),
.instance_finalize = spapr_phb_finalizefn,
.class_init = spapr_phb_class_init,
.interfaces = (InterfaceInfo[]) {
{ TYPE_HOTPLUG_HANDLER },
@ -2070,7 +2139,7 @@ static void spapr_phb_pci_enumerate(sPAPRPHBState *phb)
}
int spapr_populate_pci_dt(sPAPRPHBState *phb, uint32_t intc_phandle, void *fdt,
uint32_t nr_msis)
uint32_t nr_msis, int *node_offset)
{
int bus_off, i, j, ret;
gchar *nodename;
@ -2120,11 +2189,15 @@ int spapr_populate_pci_dt(sPAPRPHBState *phb, uint32_t intc_phandle, void *fdt,
sPAPRTCETable *tcet;
PCIBus *bus = PCI_HOST_BRIDGE(phb)->bus;
sPAPRFDT s_fdt;
sPAPRDRConnector *drc;
/* Start populating the FDT */
nodename = g_strdup_printf("pci@%" PRIx64, phb->buid);
_FDT(bus_off = fdt_add_subnode(fdt, 0, nodename));
g_free(nodename);
if (node_offset) {
*node_offset = bus_off;
}
/* Write PHB properties */
_FDT(fdt_setprop_string(fdt, bus_off, "device_type", "pci"));
@ -2183,6 +2256,14 @@ int spapr_populate_pci_dt(sPAPRPHBState *phb, uint32_t intc_phandle, void *fdt,
tcet->liobn, tcet->bus_offset,
tcet->nb_table << tcet->page_shift);
drc = spapr_drc_by_id(TYPE_SPAPR_DRC_PHB, phb->index);
if (drc) {
uint32_t drc_index = cpu_to_be32(spapr_drc_index(drc));
_FDT(fdt_setprop(fdt, bus_off, "ibm,my-drc-index", &drc_index,
sizeof(drc_index)));
}
/* Walk the bridges and program the bus numbers*/
spapr_phb_pci_enumerate(phb);
_FDT(fdt_setprop_cell(fdt, bus_off, "qemu,phb-enumerated", 0x1));

View File

@ -172,10 +172,10 @@ static void rtas_start_cpu(PowerPCCPU *callcpu, sPAPRMachineState *spapr,
* New cpus are expected to start in the same radix/hash mode
* as the existing CPUs
*/
if (ppc64_radix_guest(callcpu)) {
lpcr |= LPCR_UPRT | LPCR_GTSE;
if (ppc64_v3_radix(callcpu)) {
lpcr |= LPCR_UPRT | LPCR_GTSE | LPCR_HR;
} else {
lpcr &= ~(LPCR_UPRT | LPCR_GTSE);
lpcr &= ~(LPCR_UPRT | LPCR_GTSE | LPCR_HR);
}
}
ppc_store_lpcr(newcpu, lpcr);

View File

@ -113,7 +113,7 @@ static inline qemu_irq spapr_phb_lsi_qirq(struct sPAPRPHBState *phb, int pin)
}
int spapr_populate_pci_dt(sPAPRPHBState *phb, uint32_t intc_phandle, void *fdt,
uint32_t nr_msis);
uint32_t nr_msis, int *node_offset);
void spapr_pci_rtas_init(void);
@ -121,8 +121,10 @@ sPAPRPHBState *spapr_pci_find_phb(sPAPRMachineState *spapr, uint64_t buid);
PCIDevice *spapr_pci_find_dev(sPAPRMachineState *spapr, uint64_t buid,
uint32_t config_addr);
/* PCI release callback. */
/* DRC callbacks */
void spapr_phb_remove_pci_device_cb(DeviceState *dev);
int spapr_pci_dt_populate(sPAPRDRConnector *drc, sPAPRMachineState *spapr,
void *fdt, int *fdt_start_offset, Error **errp);
/* VFIO EEH hooks */
#ifdef CONFIG_LINUX
@ -163,4 +165,9 @@ static inline void spapr_phb_vfio_reset(DeviceState *qdev)
void spapr_phb_dma_reset(sPAPRPHBState *sphb);
static inline unsigned spapr_phb_windows_supported(sPAPRPHBState *sphb)
{
return sphb->ddw_enabled ? SPAPR_PCI_DMA_MAX_WINDOWS : 1;
}
#endif /* PCI_HOST_SPAPR_H */

View File

@ -73,6 +73,7 @@ static inline void ppc40x_irq_init(PowerPCCPU *cpu) {}
static inline void ppc6xx_irq_init(PowerPCCPU *cpu) {}
static inline void ppc970_irq_init(PowerPCCPU *cpu) {}
static inline void ppcPOWER7_irq_init(PowerPCCPU *cpu) {}
static inline void ppcPOWER9_irq_init(PowerPCCPU *cpu) {}
static inline void ppce500_irq_init(PowerPCCPU *cpu) {}
#else
void ppc40x_irq_init(PowerPCCPU *cpu);
@ -80,6 +81,7 @@ void ppce500_irq_init(PowerPCCPU *cpu);
void ppc6xx_irq_init(PowerPCCPU *cpu);
void ppc970_irq_init(PowerPCCPU *cpu);
void ppcPOWER7_irq_init(PowerPCCPU *cpu);
void ppcPOWER9_irq_init(PowerPCCPU *cpu);
#endif
/* PPC machines for OpenBIOS */

View File

@ -104,6 +104,7 @@ struct sPAPRMachineClass {
/*< public >*/
bool dr_lmb_enabled; /* enable dynamic-reconfig/hotplug of LMBs */
bool dr_phb_enabled; /* enable dynamic-reconfig/hotplug of PHBs */
bool update_dt_enabled; /* enable KVMPPC_H_UPDATE_DT */
bool use_ohci_by_default; /* use USB-OHCI instead of XHCI */
bool pre_2_10_has_unused_icps;
@ -177,6 +178,8 @@ struct sPAPRMachineState {
/*< public >*/
char *kvm_type;
char *host_model;
char *host_serial;
int32_t irq_map_nr;
unsigned long *irq_map;
@ -762,9 +765,16 @@ void spapr_reallocate_hpt(sPAPRMachineState *spapr, int shift,
void spapr_clear_pending_events(sPAPRMachineState *spapr);
int spapr_max_server_number(sPAPRMachineState *spapr);
/* CPU and LMB DRC release callbacks. */
/* DRC callbacks. */
void spapr_core_release(DeviceState *dev);
int spapr_core_dt_populate(sPAPRDRConnector *drc, sPAPRMachineState *spapr,
void *fdt, int *fdt_start_offset, Error **errp);
void spapr_lmb_release(DeviceState *dev);
int spapr_lmb_dt_populate(sPAPRDRConnector *drc, sPAPRMachineState *spapr,
void *fdt, int *fdt_start_offset, Error **errp);
void spapr_phb_release(DeviceState *dev);
int spapr_phb_dt_populate(sPAPRDRConnector *drc, sPAPRMachineState *spapr,
void *fdt, int *fdt_start_offset, Error **errp);
void spapr_rtc_read(sPAPRRTCState *rtc, struct tm *tm, uint32_t *ns);
int spapr_rtc_import_offset(sPAPRRTCState *rtc, int64_t legacy_offset);
@ -839,4 +849,5 @@ void spapr_check_pagesize(sPAPRMachineState *spapr, hwaddr pagesize,
#define SPAPR_OV5_XIVE_EXPLOIT 0x40
#define SPAPR_OV5_XIVE_BOTH 0x80 /* Only to advertise on the platform */
void spapr_set_all_lpcrs(target_ulong value, target_ulong mask);
#endif /* HW_SPAPR_H */

View File

@ -18,6 +18,7 @@
#include "qom/object.h"
#include "sysemu/sysemu.h"
#include "hw/qdev.h"
#include "qapi/error.h"
#define TYPE_SPAPR_DR_CONNECTOR "spapr-dr-connector"
#define SPAPR_DR_CONNECTOR_GET_CLASS(obj) \
@ -70,6 +71,14 @@
#define SPAPR_DRC_LMB(obj) OBJECT_CHECK(sPAPRDRConnector, (obj), \
TYPE_SPAPR_DRC_LMB)
#define TYPE_SPAPR_DRC_PHB "spapr-drc-phb"
#define SPAPR_DRC_PHB_GET_CLASS(obj) \
OBJECT_GET_CLASS(sPAPRDRConnectorClass, obj, TYPE_SPAPR_DRC_PHB)
#define SPAPR_DRC_PHB_CLASS(klass) \
OBJECT_CLASS_CHECK(sPAPRDRConnectorClass, klass, TYPE_SPAPR_DRC_PHB)
#define SPAPR_DRC_PHB(obj) OBJECT_CHECK(sPAPRDRConnector, (obj), \
TYPE_SPAPR_DRC_PHB)
/*
* Various hotplug types managed by sPAPRDRConnector
*
@ -213,6 +222,8 @@ typedef struct sPAPRDRConnector {
int fdt_start_offset;
} sPAPRDRConnector;
struct sPAPRMachineState;
typedef struct sPAPRDRConnectorClass {
/*< private >*/
DeviceClass parent;
@ -228,6 +239,9 @@ typedef struct sPAPRDRConnectorClass {
uint32_t (*isolate)(sPAPRDRConnector *drc);
uint32_t (*unisolate)(sPAPRDRConnector *drc);
void (*release)(DeviceState *dev);
int (*dt_populate)(sPAPRDRConnector *drc, struct sPAPRMachineState *spapr,
void *fdt, int *fdt_start_offset, Error **errp);
} sPAPRDRConnectorClass;
typedef struct sPAPRDRCPhysical {
@ -255,8 +269,7 @@ sPAPRDRConnector *spapr_drc_by_id(const char *type, uint32_t id);
int spapr_drc_populate_dt(void *fdt, int fdt_offset, Object *owner,
uint32_t drc_type_mask);
void spapr_drc_attach(sPAPRDRConnector *drc, DeviceState *d, void *fdt,
int fdt_start_offset, Error **errp);
void spapr_drc_attach(sPAPRDRConnector *drc, DeviceState *d, Error **errp);
void spapr_drc_detach(sPAPRDRConnector *drc);
bool spapr_drc_needed(void *opaque);

View File

@ -47,6 +47,7 @@ typedef struct sPAPRIrq {
int (*post_load)(sPAPRMachineState *spapr, int version_id);
void (*reset)(sPAPRMachineState *spapr, Error **errp);
void (*set_irq)(void *opaque, int srcno, int val);
const char *(*get_nodename)(sPAPRMachineState *spapr);
} sPAPRIrq;
extern sPAPRIrq spapr_irq_xics;
@ -60,6 +61,7 @@ void spapr_irq_free(sPAPRMachineState *spapr, int irq, int num);
qemu_irq spapr_qirq(sPAPRMachineState *spapr, int irq);
int spapr_irq_post_load(sPAPRMachineState *spapr, int version_id);
void spapr_irq_reset(sPAPRMachineState *spapr, Error **errp);
int spapr_irq_get_phandle(sPAPRMachineState *spapr, void *fdt, Error **errp);
/*
* XICS legacy routines

View File

@ -26,6 +26,9 @@ typedef struct sPAPRXive {
XiveENDSource end_source;
hwaddr end_base;
/* DT */
gchar *nodename;
/* Routing table */
XiveEAS *eat;
uint32_t nr_irqs;

View File

@ -195,6 +195,7 @@ void icp_synchronize_state(ICPState *icp);
void icp_kvm_realize(DeviceState *dev, Error **errp);
void ics_get_kvm_state(ICSState *ics);
int ics_set_kvm_state_one(ICSState *ics, int srcno);
int ics_set_kvm_state(ICSState *ics);
void ics_synchronize_state(ICSState *ics);
void ics_kvm_set_irq(ICSState *ics, int srcno, int val);

View File

@ -29,6 +29,8 @@
#include "hw/ppc/spapr.h"
#define XICS_NODENAME "interrupt-controller"
void spapr_dt_xics(sPAPRMachineState *spapr, uint32_t nr_servers, void *fdt,
uint32_t phandle);
int xics_kvm_init(sPAPRMachineState *spapr, Error **errp);

View File

@ -113,6 +113,8 @@ enum powerpc_excp_t {
POWERPC_EXCP_POWER7,
/* POWER8 exception model */
POWERPC_EXCP_POWER8,
/* POWER9 exception model */
POWERPC_EXCP_POWER9,
};
/*****************************************************************************/
@ -122,6 +124,7 @@ typedef enum {
PPC_PM_NAP,
PPC_PM_SLEEP,
PPC_PM_RVWINKLE,
PPC_PM_STOP,
} powerpc_pm_insn_t;
/*****************************************************************************/
@ -139,6 +142,8 @@ enum powerpc_input_t {
PPC_FLAGS_INPUT_970,
/* PowerPC POWER7 bus */
PPC_FLAGS_INPUT_POWER7,
/* PowerPC POWER9 bus */
PPC_FLAGS_INPUT_POWER9,
/* PowerPC 401 bus */
PPC_FLAGS_INPUT_401,
/* Freescale RCPU bus */

View File

@ -160,8 +160,10 @@ enum {
/* Server doorbell variants */
POWERPC_EXCP_SDOOR = 99,
POWERPC_EXCP_SDOOR_HV = 100,
/* ISA 3.00 additions */
POWERPC_EXCP_HVIRT = 101,
/* EOL */
POWERPC_EXCP_NB = 101,
POWERPC_EXCP_NB = 102,
/* QEMU exceptions: used internally during code translation */
POWERPC_EXCP_STOP = 0x200, /* stop translation */
POWERPC_EXCP_BRANCH = 0x201, /* branch instruction */
@ -318,6 +320,10 @@ struct ppc_slb_t {
#define SEGMENT_SHIFT_1T 40
#define SEGMENT_MASK_1T (~((1ULL << SEGMENT_SHIFT_1T) - 1))
typedef struct ppc_v3_pate_t {
uint64_t dw0;
uint64_t dw1;
} ppc_v3_pate_t;
/*****************************************************************************/
/* Machine state register bits definition */
@ -386,6 +392,7 @@ struct ppc_slb_t {
#define LPCR_AIL (3ull << LPCR_AIL_SHIFT)
#define LPCR_UPRT PPC_BIT(41) /* Use Process Table */
#define LPCR_EVIRT PPC_BIT(42) /* Enhanced Virtualisation */
#define LPCR_HR PPC_BIT(43) /* Host Radix */
#define LPCR_ONL PPC_BIT(45)
#define LPCR_LD PPC_BIT(46) /* Large Decrementer */
#define LPCR_P7_PECE0 PPC_BIT(49)
@ -414,6 +421,10 @@ struct ppc_slb_t {
#define LPCR_HVICE PPC_BIT(62) /* HV Virtualisation Int Enable */
#define LPCR_HDICE PPC_BIT(63)
/* PSSCR bits */
#define PSSCR_ESL PPC_BIT(42) /* Enable State Loss */
#define PSSCR_EC PPC_BIT(43) /* Exit Criterion */
#define msr_sf ((env->msr >> MSR_SF) & 1)
#define msr_isf ((env->msr >> MSR_ISF) & 1)
#define msr_shv ((env->msr >> MSR_SHV) & 1)
@ -1110,11 +1121,13 @@ struct CPUPPCState {
* instructions and SPRs are diallowed if MSR:HV is 0
*/
bool has_hv_mode;
/* On P7/P8, set when in PM state, we need to handle resume
* in a special way (such as routing some resume causes to
* 0x100), so flag this here.
/*
* On P7/P8/P9, set when in PM state, we need to handle resume in
* a special way (such as routing some resume causes to 0x100, ie,
* sreset), so flag this here.
*/
bool in_pm_state;
bool resume_as_sreset;
#endif
/* Those resources are used only during code translation */
@ -1239,7 +1252,7 @@ struct PPCVirtualHypervisorClass {
hwaddr ptex, int n);
void (*store_hpte)(PPCVirtualHypervisor *vhyp, hwaddr ptex,
uint64_t pte0, uint64_t pte1);
uint64_t (*get_patbe)(PPCVirtualHypervisor *vhyp);
void (*get_pate)(PPCVirtualHypervisor *vhyp, ppc_v3_pate_t *entry);
target_ulong (*encode_hpt_for_kvm_pr)(PPCVirtualHypervisor *vhyp);
};
@ -2319,6 +2332,13 @@ enum {
* them */
POWER7_INPUT_NB,
};
enum {
/* POWER9 input pins */
POWER9_INPUT_INT = 0,
POWER9_INPUT_HINT = 1,
POWER9_INPUT_NB,
};
#endif
/* Hardware exceptions definitions */
@ -2343,6 +2363,7 @@ enum {
PPC_INTERRUPT_PERFM, /* Performance monitor interrupt */
PPC_INTERRUPT_HMI, /* Hypervisor Maintainance interrupt */
PPC_INTERRUPT_HDOORBELL, /* Hypervisor Doorbell interrupt */
PPC_INTERRUPT_HVIRT, /* Hypervisor virtualization interrupt */
};
/* Processor Compatibility mask (PCR) */

View File

@ -65,6 +65,49 @@ static inline void dump_syscall(CPUPPCState *env)
ppc_dump_gpr(env, 6), env->nip);
}
static int powerpc_reset_wakeup(CPUState *cs, CPUPPCState *env, int excp,
target_ulong *msr)
{
/* We no longer are in a PM state */
env->resume_as_sreset = false;
/* Pretend to be returning from doze always as we don't lose state */
*msr |= (0x1ull << (63 - 47));
/* Machine checks are sent normally */
if (excp == POWERPC_EXCP_MCHECK) {
return excp;
}
switch (excp) {
case POWERPC_EXCP_RESET:
*msr |= 0x4ull << (63 - 45);
break;
case POWERPC_EXCP_EXTERNAL:
*msr |= 0x8ull << (63 - 45);
break;
case POWERPC_EXCP_DECR:
*msr |= 0x6ull << (63 - 45);
break;
case POWERPC_EXCP_SDOOR:
*msr |= 0x5ull << (63 - 45);
break;
case POWERPC_EXCP_SDOOR_HV:
*msr |= 0x3ull << (63 - 45);
break;
case POWERPC_EXCP_HV_MAINT:
*msr |= 0xaull << (63 - 45);
break;
case POWERPC_EXCP_HVIRT:
*msr |= 0x9ull << (63 - 45);
break;
default:
cpu_abort(cs, "Unsupported exception %d in Power Save mode\n",
excp);
}
return POWERPC_EXCP_RESET;
}
/* Note that this function should be greatly optimized
* when called with a constant excp, from ppc_hw_interrupt
*/
@ -97,47 +140,17 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
asrr0 = -1;
asrr1 = -1;
/* check for special resume at 0x100 from doze/nap/sleep/winkle on P7/P8 */
if (env->in_pm_state) {
env->in_pm_state = false;
/* Pretend to be returning from doze always as we don't lose state */
msr |= (0x1ull << (63 - 47));
/* Non-machine check are routed to 0x100 with a wakeup cause
* encoded in SRR1
*/
if (excp != POWERPC_EXCP_MCHECK) {
switch (excp) {
case POWERPC_EXCP_RESET:
msr |= 0x4ull << (63 - 45);
break;
case POWERPC_EXCP_EXTERNAL:
msr |= 0x8ull << (63 - 45);
break;
case POWERPC_EXCP_DECR:
msr |= 0x6ull << (63 - 45);
break;
case POWERPC_EXCP_SDOOR:
msr |= 0x5ull << (63 - 45);
break;
case POWERPC_EXCP_SDOOR_HV:
msr |= 0x3ull << (63 - 45);
break;
case POWERPC_EXCP_HV_MAINT:
msr |= 0xaull << (63 - 45);
break;
default:
cpu_abort(cs, "Unsupported exception %d in Power Save mode\n",
excp);
}
excp = POWERPC_EXCP_RESET;
}
/*
* check for special resume at 0x100 from doze/nap/sleep/winkle on
* P7/P8/P9
*/
if (env->resume_as_sreset) {
excp = powerpc_reset_wakeup(cs, env, excp, &msr);
}
/* Exception targetting modifiers
*
* LPES0 is supported on POWER7/8
* LPES0 is supported on POWER7/8/9
* LPES1 is not supported (old iSeries mode)
*
* On anything else, we behave as if LPES0 is 1
@ -148,9 +161,10 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
*/
#if defined(TARGET_PPC64)
if (excp_model == POWERPC_EXCP_POWER7 ||
excp_model == POWERPC_EXCP_POWER8) {
excp_model == POWERPC_EXCP_POWER8 ||
excp_model == POWERPC_EXCP_POWER9) {
lpes0 = !!(env->spr[SPR_LPCR] & LPCR_LPES0);
if (excp_model == POWERPC_EXCP_POWER8) {
if (excp_model != POWERPC_EXCP_POWER7) {
ail = (env->spr[SPR_LPCR] & LPCR_AIL) >> LPCR_AIL_SHIFT;
} else {
ail = 0;
@ -416,6 +430,7 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
case POWERPC_EXCP_HISEG: /* Hypervisor instruction segment exception */
case POWERPC_EXCP_SDOOR_HV: /* Hypervisor Doorbell interrupt */
case POWERPC_EXCP_HV_EMU:
case POWERPC_EXCP_HVIRT: /* Hypervisor virtualization */
srr0 = SPR_HSRR0;
srr1 = SPR_HSRR1;
new_msr |= (target_ulong)MSR_HVB;
@ -652,7 +667,15 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
}
} else if (excp_model == POWERPC_EXCP_POWER8) {
if (new_msr & MSR_HVB) {
if (env->spr[SPR_HID0] & (HID0_HILE | HID0_POWER9_HILE)) {
if (env->spr[SPR_HID0] & HID0_HILE) {
new_msr |= (target_ulong)1 << MSR_LE;
}
} else if (env->spr[SPR_LPCR] & LPCR_ILE) {
new_msr |= (target_ulong)1 << MSR_LE;
}
} else if (excp_model == POWERPC_EXCP_POWER9) {
if (new_msr & MSR_HVB) {
if (env->spr[SPR_HID0] & HID0_POWER9_HILE) {
new_msr |= (target_ulong)1 << MSR_LE;
}
} else if (env->spr[SPR_LPCR] & LPCR_ILE) {
@ -748,6 +771,7 @@ void ppc_cpu_do_interrupt(CPUState *cs)
static void ppc_hw_interrupt(CPUPPCState *env)
{
PowerPCCPU *cpu = ppc_env_get_cpu(env);
bool async_deliver;
/* External reset */
if (env->pending_interrupts & (1 << PPC_INTERRUPT_RESET)) {
@ -769,21 +793,44 @@ static void ppc_hw_interrupt(CPUPPCState *env)
return;
}
#endif
/*
* For interrupts that gate on MSR:EE, we need to do something a
* bit more subtle, as we need to let them through even when EE is
* clear when coming out of some power management states (in order
* for them to become a 0x100).
*/
async_deliver = (msr_ee != 0) || env->resume_as_sreset;
/* Hypervisor decrementer exception */
if (env->pending_interrupts & (1 << PPC_INTERRUPT_HDECR)) {
/* LPCR will be clear when not supported so this will work */
bool hdice = !!(env->spr[SPR_LPCR] & LPCR_HDICE);
if ((msr_ee != 0 || msr_hv == 0) && hdice) {
if ((async_deliver || msr_hv == 0) && hdice) {
/* HDEC clears on delivery */
env->pending_interrupts &= ~(1 << PPC_INTERRUPT_HDECR);
powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_HDECR);
return;
}
}
/* Extermal interrupt can ignore MSR:EE under some circumstances */
/* Hypervisor virtualization interrupt */
if (env->pending_interrupts & (1 << PPC_INTERRUPT_HVIRT)) {
/* LPCR will be clear when not supported so this will work */
bool hvice = !!(env->spr[SPR_LPCR] & LPCR_HVICE);
if ((async_deliver || msr_hv == 0) && hvice) {
powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_HVIRT);
return;
}
}
/* External interrupt can ignore MSR:EE under some circumstances */
if (env->pending_interrupts & (1 << PPC_INTERRUPT_EXT)) {
bool lpes0 = !!(env->spr[SPR_LPCR] & LPCR_LPES0);
if (msr_ee != 0 || (env->has_hv_mode && msr_hv == 0 && !lpes0)) {
bool heic = !!(env->spr[SPR_LPCR] & LPCR_HEIC);
/* HEIC blocks delivery to the hypervisor */
if ((async_deliver && !(heic && msr_hv && !msr_pr)) ||
(env->has_hv_mode && msr_hv == 0 && !lpes0)) {
powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_EXTERNAL);
return;
}
@ -795,7 +842,7 @@ static void ppc_hw_interrupt(CPUPPCState *env)
return;
}
}
if (msr_ee != 0) {
if (async_deliver != 0) {
/* Watchdog timer on embedded PowerPC */
if (env->pending_interrupts & (1 << PPC_INTERRUPT_WDT)) {
env->pending_interrupts &= ~(1 << PPC_INTERRUPT_WDT);
@ -849,6 +896,22 @@ static void ppc_hw_interrupt(CPUPPCState *env)
return;
}
}
if (env->resume_as_sreset) {
/*
* This is a bug ! It means that has_work took us out of halt without
* anything to deliver while in a PM state that requires getting
* out via a 0x100
*
* This means we will incorrectly execute past the power management
* instruction instead of triggering a reset.
*
* It generally means a discrepancy between the wakup conditions in the
* processor has_work implementation and the logic in this function.
*/
cpu_abort(CPU(ppc_env_get_cpu(env)),
"Wakeup from PM state but interrupt Undelivered");
}
}
void ppc_cpu_do_system_reset(CPUState *cs)
@ -943,22 +1006,15 @@ void helper_pminsn(CPUPPCState *env, powerpc_pm_insn_t insn)
cs = CPU(ppc_env_get_cpu(env));
cs->halted = 1;
env->in_pm_state = true;
/* The architecture specifies that HDEC interrupts are
* discarded in PM states
*/
env->pending_interrupts &= ~(1 << PPC_INTERRUPT_HDECR);
/* Technically, nap doesn't set EE, but if we don't set it
* then ppc_hw_interrupt() won't deliver. We could add some
* other tests there based on LPCR but it's simpler to just
* whack EE in. It will be cleared by the 0x100 at wakeup
* anyway. It will still be observable by the guest in SRR1
* but this doesn't seem to be a problem.
*/
env->msr |= (1ull << MSR_EE);
raise_exception(env, EXCP_HLT);
/* Condition for waking up at 0x100 */
env->resume_as_sreset = (insn != PPC_PM_STOP) ||
(env->spr[SPR_PSSCR] & PSSCR_EC);
}
#endif /* defined(TARGET_PPC64) */

View File

@ -689,6 +689,7 @@ DEF_HELPER_2(store_ptcr, void, env, tl)
#endif
DEF_HELPER_2(store_sdr1, void, env, tl)
DEF_HELPER_2(store_pidr, void, env, tl)
DEF_HELPER_2(store_lpidr, void, env, tl)
DEF_HELPER_FLAGS_2(store_tbl, TCG_CALL_NO_RWG, void, env, tl)
DEF_HELPER_FLAGS_2(store_tbu, TCG_CALL_NO_RWG, void, env, tl)
DEF_HELPER_FLAGS_2(store_atbl, TCG_CALL_NO_RWG, void, env, tl)

View File

@ -174,26 +174,19 @@ static inline int hreg_store_msr(CPUPPCState *env, target_ulong value,
static inline void check_tlb_flush(CPUPPCState *env, bool global)
{
CPUState *cs = CPU(ppc_env_get_cpu(env));
if (env->tlb_need_flush & TLB_NEED_LOCAL_FLUSH) {
tlb_flush(cs);
/* Handle global flushes first */
if (global && (env->tlb_need_flush & TLB_NEED_GLOBAL_FLUSH)) {
env->tlb_need_flush &= ~TLB_NEED_GLOBAL_FLUSH;
env->tlb_need_flush &= ~TLB_NEED_LOCAL_FLUSH;
tlb_flush_all_cpus_synced(cs);
return;
}
/* Propagate TLB invalidations to other CPUs when the guest uses broadcast
* TLB invalidation instructions.
*/
if (global && (env->tlb_need_flush & TLB_NEED_GLOBAL_FLUSH)) {
CPUState *other_cs;
CPU_FOREACH(other_cs) {
if (other_cs != cs) {
PowerPCCPU *cpu = POWERPC_CPU(other_cs);
CPUPPCState *other_env = &cpu->env;
other_env->tlb_need_flush &= ~TLB_NEED_LOCAL_FLUSH;
tlb_flush(other_cs);
}
}
env->tlb_need_flush &= ~TLB_NEED_GLOBAL_FLUSH;
/* Then handle local ones */
if (env->tlb_need_flush & TLB_NEED_LOCAL_FLUSH) {
env->tlb_need_flush &= ~TLB_NEED_LOCAL_FLUSH;
tlb_flush(cs);
}
}
#else

View File

@ -117,6 +117,21 @@ void helper_store_pidr(CPUPPCState *env, target_ulong val)
tlb_flush(CPU(cpu));
}
void helper_store_lpidr(CPUPPCState *env, target_ulong val)
{
PowerPCCPU *cpu = ppc_env_get_cpu(env);
env->spr[SPR_LPIDR] = val;
/*
* We need to flush the TLB on LPID changes as we only tag HV vs
* guest in TCG TLB. Also the quadrants means the HV will
* potentially access and cache entries for the current LPID as
* well.
*/
tlb_flush(CPU(cpu));
}
void helper_store_hid0_601(CPUPPCState *env, target_ulong val)
{
target_ulong hid0;

View File

@ -26,9 +26,36 @@
int ppc64_v3_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, int rwx,
int mmu_idx)
{
if (ppc64_radix_guest(cpu)) { /* Guest uses radix */
if (ppc64_v3_radix(cpu)) { /* Guest uses radix */
return ppc_radix64_handle_mmu_fault(cpu, eaddr, rwx, mmu_idx);
} else { /* Guest uses hash */
return ppc_hash64_handle_mmu_fault(cpu, eaddr, rwx, mmu_idx);
}
}
hwaddr ppc64_v3_get_phys_page_debug(PowerPCCPU *cpu, vaddr eaddr)
{
if (ppc64_v3_radix(cpu)) {
return ppc_radix64_get_phys_page_debug(cpu, eaddr);
} else {
return ppc_hash64_get_phys_page_debug(cpu, eaddr);
}
}
bool ppc64_v3_get_pate(PowerPCCPU *cpu, target_ulong lpid, ppc_v3_pate_t *entry)
{
uint64_t patb = cpu->env.spr[SPR_PTCR] & PTCR_PATB;
uint64_t pats = cpu->env.spr[SPR_PTCR] & PTCR_PATS;
/* Calculate number of entries */
pats = 1ull << (pats + 12 - 4);
if (pats <= lpid) {
return false;
}
/* Grab entry */
patb += 16 * lpid;
entry->dw0 = ldq_phys(CPU(cpu)->as, patb);
entry->dw1 = ldq_phys(CPU(cpu)->as, patb + 8);
return true;
}

View File

@ -17,8 +17,10 @@
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MMU_H
#define MMU_H
#ifndef MMU_BOOOK3S_V3_H
#define MMU_BOOOK3S_V3_H
#include "mmu-hash64.h"
#ifndef CONFIG_USER_ONLY
@ -29,7 +31,16 @@
#define PTCR_PATS 0x000000000000001FULL /* Partition Table Size */
/* Partition Table Entry Fields */
#define PATBE1_GR 0x8000000000000000
#define PATE0_HR 0x8000000000000000
/*
* WARNING: This field doesn't actually exist in the final version of
* the architecture and is unused by hardware. However, qemu uses it
* as an indication of a radix guest in the pseudo-PATB entry that it
* maintains for SPAPR guests and in the migration stream, so we need
* to keep it around
*/
#define PATE1_GR 0x8000000000000000
/* Process Table Entry */
struct prtb_entry {
@ -43,19 +54,68 @@ static inline bool ppc64_use_proc_tbl(PowerPCCPU *cpu)
return !!(cpu->env.spr[SPR_LPCR] & LPCR_UPRT);
}
static inline bool ppc64_radix_guest(PowerPCCPU *cpu)
{
PPCVirtualHypervisorClass *vhc =
PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp);
bool ppc64_v3_get_pate(PowerPCCPU *cpu, target_ulong lpid,
ppc_v3_pate_t *entry);
return !!(vhc->get_patbe(cpu->vhyp) & PATBE1_GR);
/*
* The LPCR:HR bit is a shortcut that avoids having to
* dig out the partition table in the fast path. This is
* also how the HW uses it.
*/
static inline bool ppc64_v3_radix(PowerPCCPU *cpu)
{
return !!(cpu->env.spr[SPR_LPCR] & LPCR_HR);
}
hwaddr ppc64_v3_get_phys_page_debug(PowerPCCPU *cpu, vaddr eaddr);
int ppc64_v3_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, int rwx,
int mmu_idx);
static inline hwaddr ppc_hash64_hpt_base(PowerPCCPU *cpu)
{
uint64_t base;
if (cpu->vhyp) {
return 0;
}
if (cpu->env.mmu_model == POWERPC_MMU_3_00) {
ppc_v3_pate_t pate;
if (!ppc64_v3_get_pate(cpu, cpu->env.spr[SPR_LPIDR], &pate)) {
return 0;
}
base = pate.dw0;
} else {
base = cpu->env.spr[SPR_SDR1];
}
return base & SDR_64_HTABORG;
}
static inline hwaddr ppc_hash64_hpt_mask(PowerPCCPU *cpu)
{
uint64_t base;
if (cpu->vhyp) {
PPCVirtualHypervisorClass *vhc =
PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp);
return vhc->hpt_mask(cpu->vhyp);
}
if (cpu->env.mmu_model == POWERPC_MMU_3_00) {
ppc_v3_pate_t pate;
if (!ppc64_v3_get_pate(cpu, cpu->env.spr[SPR_LPIDR], &pate)) {
return 0;
}
base = pate.dw0;
} else {
base = cpu->env.spr[SPR_SDR1];
}
return (1ULL << ((base & SDR_64_HTABSIZE) + 18 - 7)) - 1;
}
#endif /* TARGET_PPC64 */
#endif /* CONFIG_USER_ONLY */
#endif /* MMU_H */
#endif /* MMU_BOOOK3S_V3_H */

View File

@ -319,6 +319,12 @@ static hwaddr ppc_hash32_pteg_search(PowerPCCPU *cpu, hwaddr pteg_off,
for (i = 0; i < HPTES_PER_GROUP; i++) {
pte0 = ppc_hash32_load_hpte0(cpu, pte_offset);
/*
* pte0 contains the valid bit and must be read before pte1,
* otherwise we might see an old pte1 with a new valid bit and
* thus an inconsistent hpte value
*/
smp_rmb();
pte1 = ppc_hash32_load_hpte1(cpu, pte_offset);
if ((pte0 & HPTE32_V_VALID)

View File

@ -417,7 +417,7 @@ const ppc_hash_pte64_t *ppc_hash64_map_hptes(PowerPCCPU *cpu,
hwaddr ptex, int n)
{
hwaddr pte_offset = ptex * HASH_PTE_SIZE_64;
hwaddr base = ppc_hash64_hpt_base(cpu);
hwaddr base;
hwaddr plen = n * HASH_PTE_SIZE_64;
const ppc_hash_pte64_t *hptes;
@ -426,6 +426,7 @@ const ppc_hash_pte64_t *ppc_hash64_map_hptes(PowerPCCPU *cpu,
PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp);
return vhc->map_hptes(cpu->vhyp, ptex, n);
}
base = ppc_hash64_hpt_base(cpu);
if (!base) {
return NULL;
@ -490,6 +491,18 @@ static unsigned hpte_page_shift(const PPCHash64SegmentPageSizes *sps,
return 0; /* Bad page size encoding */
}
static void ppc64_v3_new_to_old_hpte(target_ulong *pte0, target_ulong *pte1)
{
/* Insert B into pte0 */
*pte0 = (*pte0 & HPTE64_V_COMMON_BITS) |
((*pte1 & HPTE64_R_3_0_SSIZE_MASK) <<
(HPTE64_V_SSIZE_SHIFT - HPTE64_R_3_0_SSIZE_SHIFT));
/* Remove B from pte1 */
*pte1 = *pte1 & ~HPTE64_R_3_0_SSIZE_MASK;
}
static hwaddr ppc_hash64_pteg_search(PowerPCCPU *cpu, hwaddr hash,
const PPCHash64SegmentPageSizes *sps,
target_ulong ptem,
@ -507,8 +520,19 @@ static hwaddr ppc_hash64_pteg_search(PowerPCCPU *cpu, hwaddr hash,
}
for (i = 0; i < HPTES_PER_GROUP; i++) {
pte0 = ppc_hash64_hpte0(cpu, pteg, i);
/*
* pte0 contains the valid bit and must be read before pte1,
* otherwise we might see an old pte1 with a new valid bit and
* thus an inconsistent hpte value
*/
smp_rmb();
pte1 = ppc_hash64_hpte1(cpu, pteg, i);
/* Convert format if necessary */
if (cpu->env.mmu_model == POWERPC_MMU_3_00 && !cpu->vhyp) {
ppc64_v3_new_to_old_hpte(&pte0, &pte1);
}
/* This compares V, B, H (secondary) and the AVPN */
if (HPTE64_V_COMPARE(pte0, ptem)) {
*pshift = hpte_page_shift(sps, pte0, pte1);
@ -918,7 +942,7 @@ hwaddr ppc_hash64_get_phys_page_debug(PowerPCCPU *cpu, target_ulong addr)
void ppc_hash64_store_hpte(PowerPCCPU *cpu, hwaddr ptex,
uint64_t pte0, uint64_t pte1)
{
hwaddr base = ppc_hash64_hpt_base(cpu);
hwaddr base;
hwaddr offset = ptex * HASH_PTE_SIZE_64;
if (cpu->vhyp) {
@ -927,6 +951,7 @@ void ppc_hash64_store_hpte(PowerPCCPU *cpu, hwaddr ptex,
vhc->store_hpte(cpu->vhyp, ptex, pte0, pte1);
return;
}
base = ppc_hash64_hpt_base(cpu);
stq_phys(CPU(cpu)->as, base + offset, pte0);
stq_phys(CPU(cpu)->as, base + offset + HASH_PTE_SIZE_64 / 2, pte1);
@ -1084,10 +1109,18 @@ void ppc_store_lpcr(PowerPCCPU *cpu, target_ulong val)
case POWERPC_MMU_3_00: /* P9 */
lpcr = val & (LPCR_VPM1 | LPCR_ISL | LPCR_KBV | LPCR_DPFD |
(LPCR_PECE_U_MASK & LPCR_HVEE) | LPCR_ILE | LPCR_AIL |
LPCR_UPRT | LPCR_EVIRT | LPCR_ONL |
LPCR_UPRT | LPCR_EVIRT | LPCR_ONL | LPCR_HR |
(LPCR_PECE_L_MASK & (LPCR_PDEE | LPCR_HDEE | LPCR_EEE |
LPCR_DEE | LPCR_OEE)) | LPCR_MER | LPCR_GTSE | LPCR_TC |
LPCR_HEIC | LPCR_LPES0 | LPCR_HVICE | LPCR_HDICE);
/*
* If we have a virtual hypervisor, we need to bring back RMLS. It
* doesn't exist on an actual P9 but that's all we know how to
* configure with softmmu at the moment
*/
if (cpu->vhyp) {
lpcr |= (val & LPCR_RMLS);
}
break;
default:
;

View File

@ -63,6 +63,7 @@ void ppc_hash64_filter_pagesizes(PowerPCCPU *cpu,
#define SDR_64_HTABORG 0x0FFFFFFFFFFC0000ULL
#define SDR_64_HTABSIZE 0x000000000000001FULL
#define PATE0_HTABORG 0x0FFFFFFFFFFC0000ULL
#define HPTES_PER_GROUP 8
#define HASH_PTE_SIZE_64 16
#define HASH_PTEG_SIZE_64 (HASH_PTE_SIZE_64 * HPTES_PER_GROUP)
@ -102,23 +103,10 @@ void ppc_hash64_filter_pagesizes(PowerPCCPU *cpu,
#define HPTE64_V_1TB_SEG 0x4000000000000000ULL
#define HPTE64_V_VRMA_MASK 0x4001ffffff000000ULL
static inline hwaddr ppc_hash64_hpt_base(PowerPCCPU *cpu)
{
if (cpu->vhyp) {
return 0;
}
return cpu->env.spr[SPR_SDR1] & SDR_64_HTABORG;
}
static inline hwaddr ppc_hash64_hpt_mask(PowerPCCPU *cpu)
{
if (cpu->vhyp) {
PPCVirtualHypervisorClass *vhc =
PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp);
return vhc->hpt_mask(cpu->vhyp);
}
return (1ULL << ((cpu->env.spr[SPR_SDR1] & SDR_64_HTABSIZE) + 18 - 7)) - 1;
}
/* Format changes for ARCH v3 */
#define HPTE64_V_COMMON_BITS 0x000fffffffffffffULL
#define HPTE64_R_3_0_SSIZE_SHIFT 58
#define HPTE64_R_3_0_SSIZE_MASK (3ULL << HPTE64_R_3_0_SSIZE_SHIFT)
struct ppc_hash_pte64 {
uint64_t pte0, pte1;

View File

@ -31,10 +31,26 @@
static bool ppc_radix64_get_fully_qualified_addr(CPUPPCState *env, vaddr eaddr,
uint64_t *lpid, uint64_t *pid)
{
/* We don't have HV support yet and shouldn't get here with it set anyway */
assert(!msr_hv);
if (!msr_hv) { /* !MSR[HV] -> Guest */
if (msr_hv) { /* MSR[HV] -> Hypervisor/bare metal */
switch (eaddr & R_EADDR_QUADRANT) {
case R_EADDR_QUADRANT0:
*lpid = 0;
*pid = env->spr[SPR_BOOKS_PID];
break;
case R_EADDR_QUADRANT1:
*lpid = env->spr[SPR_LPIDR];
*pid = env->spr[SPR_BOOKS_PID];
break;
case R_EADDR_QUADRANT2:
*lpid = env->spr[SPR_LPIDR];
*pid = 0;
break;
case R_EADDR_QUADRANT3:
*lpid = 0;
*pid = 0;
break;
}
} else { /* !MSR[HV] -> Guest */
switch (eaddr & R_EADDR_QUADRANT) {
case R_EADDR_QUADRANT0: /* Guest application */
*lpid = env->spr[SPR_LPIDR];
@ -186,20 +202,32 @@ static uint64_t ppc_radix64_walk_tree(PowerPCCPU *cpu, vaddr eaddr,
raddr, psize, fault_cause, pte_addr);
}
static bool validate_pate(PowerPCCPU *cpu, uint64_t lpid, ppc_v3_pate_t *pate)
{
CPUPPCState *env = &cpu->env;
if (!(pate->dw0 & PATE0_HR)) {
return false;
}
if (lpid == 0 && !msr_hv) {
return false;
}
/* More checks ... */
return true;
}
int ppc_radix64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, int rwx,
int mmu_idx)
{
CPUState *cs = CPU(cpu);
CPUPPCState *env = &cpu->env;
PPCVirtualHypervisorClass *vhc =
PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp);
PPCVirtualHypervisorClass *vhc;
hwaddr raddr, pte_addr;
uint64_t lpid = 0, pid = 0, offset, size, patbe, prtbe0, pte;
uint64_t lpid = 0, pid = 0, offset, size, prtbe0, pte;
int page_size, prot, fault_cause = 0;
ppc_v3_pate_t pate;
assert((rwx == 0) || (rwx == 1) || (rwx == 2));
assert(!msr_hv); /* For now there is no Radix PowerNV Support */
assert(cpu->vhyp);
assert(ppc64_use_proc_tbl(cpu));
/* Real Mode Access */
@ -220,17 +248,33 @@ int ppc_radix64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, int rwx,
}
/* Get Process Table */
patbe = vhc->get_patbe(cpu->vhyp);
if (cpu->vhyp) {
vhc = PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp);
vhc->get_pate(cpu->vhyp, &pate);
} else {
if (!ppc64_v3_get_pate(cpu, lpid, &pate)) {
ppc_radix64_raise_si(cpu, rwx, eaddr, DSISR_NOPTE);
return 1;
}
if (!validate_pate(cpu, lpid, &pate)) {
ppc_radix64_raise_si(cpu, rwx, eaddr, DSISR_R_BADCONFIG);
}
/* We don't support guest mode yet */
if (lpid != 0) {
error_report("PowerNV guest support Unimplemented");
exit(1);
}
}
/* Index Process Table by PID to Find Corresponding Process Table Entry */
offset = pid * sizeof(struct prtb_entry);
size = 1ULL << ((patbe & PATBE1_R_PRTS) + 12);
size = 1ULL << ((pate.dw1 & PATE1_R_PRTS) + 12);
if (offset >= size) {
/* offset exceeds size of the process table */
ppc_radix64_raise_si(cpu, rwx, eaddr, DSISR_NOPTE);
return 1;
}
prtbe0 = ldq_phys(cs->as, (patbe & PATBE1_R_PRTB) + offset);
prtbe0 = ldq_phys(cs->as, (pate.dw1 & PATE1_R_PRTB) + offset);
/* Walk Radix Tree from Process Table Entry to Convert EA to RA */
page_size = PRTBE_R_GET_RTS(prtbe0);
@ -255,11 +299,11 @@ hwaddr ppc_radix64_get_phys_page_debug(PowerPCCPU *cpu, target_ulong eaddr)
{
CPUState *cs = CPU(cpu);
CPUPPCState *env = &cpu->env;
PPCVirtualHypervisorClass *vhc =
PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp);
PPCVirtualHypervisorClass *vhc;
hwaddr raddr, pte_addr;
uint64_t lpid = 0, pid = 0, offset, size, patbe, prtbe0, pte;
uint64_t lpid = 0, pid = 0, offset, size, prtbe0, pte;
int page_size, fault_cause = 0;
ppc_v3_pate_t pate;
/* Handle Real Mode */
if (msr_dr == 0) {
@ -273,16 +317,31 @@ hwaddr ppc_radix64_get_phys_page_debug(PowerPCCPU *cpu, target_ulong eaddr)
}
/* Get Process Table */
patbe = vhc->get_patbe(cpu->vhyp);
if (cpu->vhyp) {
vhc = PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp);
vhc->get_pate(cpu->vhyp, &pate);
} else {
if (!ppc64_v3_get_pate(cpu, lpid, &pate)) {
return -1;
}
if (!validate_pate(cpu, lpid, &pate)) {
return -1;
}
/* We don't support guest mode yet */
if (lpid != 0) {
error_report("PowerNV guest support Unimplemented");
exit(1);
}
}
/* Index Process Table by PID to Find Corresponding Process Table Entry */
offset = pid * sizeof(struct prtb_entry);
size = 1ULL << ((patbe & PATBE1_R_PRTS) + 12);
size = 1ULL << ((pate.dw1 & PATE1_R_PRTS) + 12);
if (offset >= size) {
/* offset exceeds size of the process table */
return -1;
}
prtbe0 = ldq_phys(cs->as, (patbe & PATBE1_R_PRTB) + offset);
prtbe0 = ldq_phys(cs->as, (pate.dw1 & PATE1_R_PRTB) + offset);
/* Walk Radix Tree from Process Table Entry to Convert EA to RA */
page_size = PRTBE_R_GET_RTS(prtbe0);

View File

@ -12,8 +12,8 @@
#define R_EADDR_QUADRANT3 0xC000000000000000
/* Radix Partition Table Entry Fields */
#define PATBE1_R_PRTB 0x0FFFFFFFFFFFF000
#define PATBE1_R_PRTS 0x000000000000001F
#define PATE1_R_PRTB 0x0FFFFFFFFFFFF000
#define PATE1_R_PRTS 0x000000000000001F
/* Radix Process Table Entry Fields */
#define PRTBE_R_GET_RTS(rts) \

View File

@ -1342,7 +1342,7 @@ void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUPPCState *env)
dump_slb(f, cpu_fprintf, ppc_env_get_cpu(env));
break;
case POWERPC_MMU_3_00:
if (ppc64_radix_guest(ppc_env_get_cpu(env))) {
if (ppc64_v3_radix(ppc_env_get_cpu(env))) {
/* TODO - Unsupported */
} else {
dump_slb(f, cpu_fprintf, ppc_env_get_cpu(env));
@ -1489,12 +1489,7 @@ hwaddr ppc_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
case POWERPC_MMU_2_07:
return ppc_hash64_get_phys_page_debug(cpu, addr);
case POWERPC_MMU_3_00:
if (ppc64_radix_guest(ppc_env_get_cpu(env))) {
return ppc_radix64_get_phys_page_debug(cpu, addr);
} else {
return ppc_hash64_get_phys_page_debug(cpu, addr);
}
break;
return ppc64_v3_get_phys_page_debug(cpu, addr);
#endif
case POWERPC_MMU_32B:

View File

@ -3566,7 +3566,8 @@ static void gen_doze(DisasContext *ctx)
t = tcg_const_i32(PPC_PM_DOZE);
gen_helper_pminsn(cpu_env, t);
tcg_temp_free_i32(t);
gen_stop_exception(ctx);
/* Stop translation, as the CPU is supposed to sleep from now */
gen_exception_nip(ctx, EXCP_HLT, ctx->base.pc_next);
#endif /* defined(CONFIG_USER_ONLY) */
}
@ -3581,13 +3582,25 @@ static void gen_nap(DisasContext *ctx)
t = tcg_const_i32(PPC_PM_NAP);
gen_helper_pminsn(cpu_env, t);
tcg_temp_free_i32(t);
gen_stop_exception(ctx);
/* Stop translation, as the CPU is supposed to sleep from now */
gen_exception_nip(ctx, EXCP_HLT, ctx->base.pc_next);
#endif /* defined(CONFIG_USER_ONLY) */
}
static void gen_stop(DisasContext *ctx)
{
gen_nap(ctx);
#if defined(CONFIG_USER_ONLY)
GEN_PRIV;
#else
TCGv_i32 t;
CHK_HV;
t = tcg_const_i32(PPC_PM_STOP);
gen_helper_pminsn(cpu_env, t);
tcg_temp_free_i32(t);
/* Stop translation, as the CPU is supposed to sleep from now */
gen_exception_nip(ctx, EXCP_HLT, ctx->base.pc_next);
#endif /* defined(CONFIG_USER_ONLY) */
}
static void gen_sleep(DisasContext *ctx)
@ -3601,7 +3614,8 @@ static void gen_sleep(DisasContext *ctx)
t = tcg_const_i32(PPC_PM_SLEEP);
gen_helper_pminsn(cpu_env, t);
tcg_temp_free_i32(t);
gen_stop_exception(ctx);
/* Stop translation, as the CPU is supposed to sleep from now */
gen_exception_nip(ctx, EXCP_HLT, ctx->base.pc_next);
#endif /* defined(CONFIG_USER_ONLY) */
}
@ -3616,7 +3630,8 @@ static void gen_rvwinkle(DisasContext *ctx)
t = tcg_const_i32(PPC_PM_RVWINKLE);
gen_helper_pminsn(cpu_env, t);
tcg_temp_free_i32(t);
gen_stop_exception(ctx);
/* Stop translation, as the CPU is supposed to sleep from now */
gen_exception_nip(ctx, EXCP_HLT, ctx->base.pc_next);
#endif /* defined(CONFIG_USER_ONLY) */
}
#endif /* #if defined(TARGET_PPC64) */
@ -7466,7 +7481,8 @@ void ppc_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf,
#if defined(TARGET_PPC64)
if (env->excp_model == POWERPC_EXCP_POWER7 ||
env->excp_model == POWERPC_EXCP_POWER8) {
env->excp_model == POWERPC_EXCP_POWER8 ||
env->excp_model == POWERPC_EXCP_POWER9) {
cpu_fprintf(f, "HSRR0 " TARGET_FMT_lx " HSRR1 " TARGET_FMT_lx "\n",
env->spr[SPR_HSRR0], env->spr[SPR_HSRR1]);
}

View File

@ -408,6 +408,11 @@ static void spr_write_pidr(DisasContext *ctx, int sprn, int gprn)
gen_helper_store_pidr(cpu_env, cpu_gpr[gprn]);
}
static void spr_write_lpidr(DisasContext *ctx, int sprn, int gprn)
{
gen_helper_store_lpidr(cpu_env, cpu_gpr[gprn]);
}
static void spr_read_hior(DisasContext *ctx, int gprn, int sprn)
{
tcg_gen_ld_tl(cpu_gpr[gprn], cpu_env, offsetof(CPUPPCState, excp_prefix));
@ -3313,6 +3318,15 @@ static void init_excp_POWER8(CPUPPCState *env)
#endif
}
static void init_excp_POWER9(CPUPPCState *env)
{
init_excp_POWER8(env);
#if !defined(CONFIG_USER_ONLY)
env->excp_vectors[POWERPC_EXCP_HVIRT] = 0x00000EA0;
#endif
}
#endif
/*****************************************************************************/
@ -7876,7 +7890,7 @@ static void gen_spr_book3s_ids(CPUPPCState *env)
spr_register_hv(env, SPR_LPIDR, "LPIDR",
SPR_NOACCESS, SPR_NOACCESS,
SPR_NOACCESS, SPR_NOACCESS,
&spr_read_generic, &spr_write_generic,
&spr_read_generic, &spr_write_lpidr,
0x00000000);
spr_register_hv(env, SPR_HFSCR, "HFSCR",
SPR_NOACCESS, SPR_NOACCESS,
@ -8783,8 +8797,8 @@ static void init_proc_POWER9(CPUPPCState *env)
env->icache_line_size = 128;
/* Allocate hardware IRQ controller */
init_excp_POWER8(env);
ppcPOWER7_irq_init(ppc_env_get_cpu(env));
init_excp_POWER9(env);
ppcPOWER9_irq_init(ppc_env_get_cpu(env));
}
static bool ppc_pvr_match_power9(PowerPCCPUClass *pcc, uint32_t pvr)
@ -8801,13 +8815,23 @@ static bool cpu_has_work_POWER9(CPUState *cs)
CPUPPCState *env = &cpu->env;
if (cs->halted) {
uint64_t psscr = env->spr[SPR_PSSCR];
if (!(cs->interrupt_request & CPU_INTERRUPT_HARD)) {
return false;
}
/* If EC is clear, just return true on any pending interrupt */
if (!(psscr & PSSCR_EC)) {
return true;
}
/* External Exception */
if ((env->pending_interrupts & (1u << PPC_INTERRUPT_EXT)) &&
(env->spr[SPR_LPCR] & LPCR_EEE)) {
return true;
bool heic = !!(env->spr[SPR_LPCR] & LPCR_HEIC);
if (heic == 0 || !msr_hv || msr_pr) {
return true;
}
}
/* Decrementer Exception */
if ((env->pending_interrupts & (1u << PPC_INTERRUPT_DECR)) &&
@ -8829,6 +8853,11 @@ static bool cpu_has_work_POWER9(CPUState *cs)
(env->spr[SPR_LPCR] & LPCR_HDEE)) {
return true;
}
/* Hypervisor virtualization exception */
if ((env->pending_interrupts & (1u << PPC_INTERRUPT_HVIRT)) &&
(env->spr[SPR_LPCR] & LPCR_HVEE)) {
return true;
}
if (env->pending_interrupts & (1u << PPC_INTERRUPT_RESET)) {
return true;
}
@ -8898,8 +8927,8 @@ POWERPC_FAMILY(POWER9)(ObjectClass *oc, void *data)
pcc->hash64_opts = &ppc_hash64_opts_POWER7;
pcc->radix_page_info = &POWER9_radix_page_info;
#endif
pcc->excp_model = POWERPC_EXCP_POWER8;
pcc->bus_model = PPC_FLAGS_INPUT_POWER7;
pcc->excp_model = POWERPC_EXCP_POWER9;
pcc->bus_model = PPC_FLAGS_INPUT_POWER9;
pcc->bfd_mach = bfd_mach_ppc64;
pcc->flags = POWERPC_FLAG_VRE | POWERPC_FLAG_SE |
POWERPC_FLAG_BE | POWERPC_FLAG_PMM |

View File

@ -198,6 +198,7 @@ check-qtest-i386-$(CONFIG_ISA_IPMI_KCS) += tests/ipmi-kcs-test$(EXESUF)
# check-qtest-i386-$(CONFIG_ISA_IPMI_BT) += tests/ipmi-bt-test$(EXESUF)
check-qtest-i386-y += tests/i440fx-test$(EXESUF)
check-qtest-i386-y += tests/fw_cfg-test$(EXESUF)
check-qtest-i386-y += tests/device-plug-test$(EXESUF)
check-qtest-i386-y += tests/drive_del-test$(EXESUF)
check-qtest-i386-$(CONFIG_WDT_IB700) += tests/wdt_ib700-test$(EXESUF)
check-qtest-i386-y += tests/tco-test$(EXESUF)
@ -262,6 +263,7 @@ check-qtest-ppc-$(CONFIG_M48T59) += tests/m48t59-test$(EXESUF)
check-qtest-ppc64-y += $(check-qtest-ppc-y)
check-qtest-ppc64-$(CONFIG_PSERIES) += tests/spapr-phb-test$(EXESUF)
check-qtest-ppc64-$(CONFIG_PSERIES) += tests/device-plug-test$(EXESUF)
check-qtest-ppc64-$(CONFIG_POWERNV) += tests/pnv-xscom-test$(EXESUF)
check-qtest-ppc64-y += tests/migration-test$(EXESUF)
check-qtest-ppc64-$(CONFIG_PSERIES) += tests/rtas-test$(EXESUF)
@ -316,6 +318,7 @@ check-qtest-s390x-$(CONFIG_SLIRP) += tests/test-netfilter$(EXESUF)
check-qtest-s390x-$(CONFIG_POSIX) += tests/test-filter-mirror$(EXESUF)
check-qtest-s390x-$(CONFIG_POSIX) += tests/test-filter-redirector$(EXESUF)
check-qtest-s390x-y += tests/drive_del-test$(EXESUF)
check-qtest-s390x-y += tests/device-plug-test$(EXESUF)
check-qtest-s390x-y += tests/virtio-ccw-test$(EXESUF)
check-qtest-s390x-y += tests/cpu-plug-test$(EXESUF)
check-qtest-s390x-y += tests/migration-test$(EXESUF)
@ -764,6 +767,7 @@ tests/ipoctal232-test$(EXESUF): tests/ipoctal232-test.o
tests/qom-test$(EXESUF): tests/qom-test.o
tests/test-hmp$(EXESUF): tests/test-hmp.o
tests/machine-none-test$(EXESUF): tests/machine-none-test.o
tests/device-plug-test$(EXESUF): tests/device-plug-test.o
tests/drive_del-test$(EXESUF): tests/drive_del-test.o $(libqos-virtio-obj-y)
tests/nvme-test$(EXESUF): tests/nvme-test.o $(libqos-pc-obj-y)
tests/pvpanic-test$(EXESUF): tests/pvpanic-test.o

178
tests/device-plug-test.c Normal file
View File

@ -0,0 +1,178 @@
/*
* QEMU device plug/unplug handling
*
* Copyright (C) 2019 Red Hat Inc.
*
* Authors:
* David Hildenbrand <david@redhat.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/
#include "qemu/osdep.h"
#include "libqtest.h"
#include "qapi/qmp/qdict.h"
#include "qapi/qmp/qstring.h"
static void device_del_start(QTestState *qtest, const char *id)
{
qtest_qmp_send(qtest,
"{'execute': 'device_del', 'arguments': { 'id': %s } }", id);
}
static void device_del_finish(QTestState *qtest)
{
QDict *resp = qtest_qmp_receive(qtest);
g_assert(qdict_haskey(resp, "return"));
qobject_unref(resp);
}
static void device_del_request(QTestState *qtest, const char *id)
{
device_del_start(qtest, id);
device_del_finish(qtest);
}
static void system_reset(QTestState *qtest)
{
QDict *resp;
resp = qtest_qmp(qtest, "{'execute': 'system_reset'}");
g_assert(qdict_haskey(resp, "return"));
qobject_unref(resp);
}
static void wait_device_deleted_event(QTestState *qtest, const char *id)
{
QDict *resp, *data;
QString *qstr;
/*
* Other devices might get removed along with the removed device. Skip
* these. The device of interest will be the last one.
*/
for (;;) {
resp = qtest_qmp_eventwait_ref(qtest, "DEVICE_DELETED");
data = qdict_get_qdict(resp, "data");
if (!data || !qdict_get(data, "device")) {
qobject_unref(resp);
continue;
}
qstr = qobject_to(QString, qdict_get(data, "device"));
g_assert(qstr);
if (!strcmp(qstring_get_str(qstr), id)) {
qobject_unref(resp);
break;
}
qobject_unref(resp);
}
}
static void test_pci_unplug_request(void)
{
QTestState *qtest = qtest_initf("-device virtio-mouse-pci,id=dev0");
/*
* Request device removal. As the guest is not running, the request won't
* be processed. However during system reset, the removal will be
* handled, removing the device.
*/
device_del_request(qtest, "dev0");
system_reset(qtest);
wait_device_deleted_event(qtest, "dev0");
qtest_quit(qtest);
}
static void test_ccw_unplug(void)
{
QTestState *qtest = qtest_initf("-device virtio-balloon-ccw,id=dev0");
/*
* The DEVICE_DELETED events will be sent before the command
* completes.
*/
device_del_start(qtest, "dev0");
wait_device_deleted_event(qtest, "dev0");
device_del_finish(qtest);
qtest_quit(qtest);
}
static void test_spapr_cpu_unplug_request(void)
{
QTestState *qtest;
qtest = qtest_initf("-cpu power9_v2.0 -smp 1,maxcpus=2 "
"-device power9_v2.0-spapr-cpu-core,core-id=1,id=dev0");
/* similar to test_pci_unplug_request */
device_del_request(qtest, "dev0");
system_reset(qtest);
wait_device_deleted_event(qtest, "dev0");
qtest_quit(qtest);
}
static void test_spapr_memory_unplug_request(void)
{
QTestState *qtest;
qtest = qtest_initf("-m 256M,slots=1,maxmem=768M "
"-object memory-backend-ram,id=mem0,size=512M "
"-device pc-dimm,id=dev0,memdev=mem0");
/* similar to test_pci_unplug_request */
device_del_request(qtest, "dev0");
system_reset(qtest);
wait_device_deleted_event(qtest, "dev0");
qtest_quit(qtest);
}
static void test_spapr_phb_unplug_request(void)
{
QTestState *qtest;
qtest = qtest_initf("-device spapr-pci-host-bridge,index=1,id=dev0");
/* similar to test_pci_unplug_request */
device_del_request(qtest, "dev0");
system_reset(qtest);
wait_device_deleted_event(qtest, "dev0");
qtest_quit(qtest);
}
int main(int argc, char **argv)
{
const char *arch = qtest_get_arch();
g_test_init(&argc, &argv, NULL);
/*
* We need a system that will process unplug requests during system resets
* and does not do PCI surprise removal. This holds for x86 ACPI,
* s390x and spapr.
*/
qtest_add_func("/device-plug/pci-unplug-request",
test_pci_unplug_request);
if (!strcmp(arch, "s390x")) {
qtest_add_func("/device-plug/ccw-unplug",
test_ccw_unplug);
}
if (!strcmp(arch, "ppc64")) {
qtest_add_func("/device-plug/spapr-cpu-unplug-request",
test_spapr_cpu_unplug_request);
qtest_add_func("/device-plug/spapr-memory-unplug-request",
test_spapr_memory_unplug_request);
qtest_add_func("/device-plug/spapr-phb-unplug-request",
test_spapr_phb_unplug_request);
}
return g_test_run();
}