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:
commit
1387294169
1
cpus.c
1
cpus.c
@ -1333,6 +1333,7 @@ static void *qemu_dummy_cpu_thread_fn(void *arg)
|
|||||||
qemu_wait_io_event(cpu);
|
qemu_wait_io_event(cpu);
|
||||||
} while (!cpu->unplug);
|
} while (!cpu->unplug);
|
||||||
|
|
||||||
|
qemu_mutex_unlock_iothread();
|
||||||
rcu_unregister_thread();
|
rcu_unregister_thread();
|
||||||
return NULL;
|
return NULL;
|
||||||
#endif
|
#endif
|
||||||
|
@ -244,13 +244,12 @@ static void spapr_xive_instance_init(Object *obj)
|
|||||||
{
|
{
|
||||||
sPAPRXive *xive = SPAPR_XIVE(obj);
|
sPAPRXive *xive = SPAPR_XIVE(obj);
|
||||||
|
|
||||||
object_initialize(&xive->source, sizeof(xive->source), TYPE_XIVE_SOURCE);
|
object_initialize_child(obj, "source", &xive->source, sizeof(xive->source),
|
||||||
object_property_add_child(obj, "source", OBJECT(&xive->source), NULL);
|
TYPE_XIVE_SOURCE, &error_abort, NULL);
|
||||||
|
|
||||||
object_initialize(&xive->end_source, sizeof(xive->end_source),
|
object_initialize_child(obj, "end_source", &xive->end_source,
|
||||||
TYPE_XIVE_END_SOURCE);
|
sizeof(xive->end_source), TYPE_XIVE_END_SOURCE,
|
||||||
object_property_add_child(obj, "end_source", OBJECT(&xive->end_source),
|
&error_abort, NULL);
|
||||||
NULL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void spapr_xive_realize(DeviceState *dev, Error **errp)
|
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 */
|
/* Map all regions */
|
||||||
spapr_xive_map_mmio(xive);
|
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);
|
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(7), /* start */
|
||||||
cpu_to_be32(0xf8), /* count */
|
cpu_to_be32(0xf8), /* count */
|
||||||
};
|
};
|
||||||
gchar *nodename;
|
|
||||||
|
|
||||||
/* Thread Interrupt Management Area : User (ring 3) and OS (ring 2) */
|
/* Thread Interrupt Management Area : User (ring 3) and OS (ring 2) */
|
||||||
timas[0] = cpu_to_be64(xive->tm_base +
|
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));
|
XIVE_TM_OS_PAGE * (1ull << TM_SHIFT));
|
||||||
timas[3] = cpu_to_be64(1ull << TM_SHIFT);
|
timas[3] = cpu_to_be64(1ull << TM_SHIFT);
|
||||||
|
|
||||||
nodename = g_strdup_printf("interrupt-controller@%" PRIx64,
|
_FDT(node = fdt_add_subnode(fdt, 0, xive->nodename));
|
||||||
xive->tm_base + XIVE_TM_USER_PAGE * (1 << TM_SHIFT));
|
|
||||||
_FDT(node = fdt_add_subnode(fdt, 0, nodename));
|
|
||||||
g_free(nodename);
|
|
||||||
|
|
||||||
_FDT(fdt_setprop_string(fdt, node, "device_type", "power-ivpe"));
|
_FDT(fdt_setprop_string(fdt, node, "device_type", "power-ivpe"));
|
||||||
_FDT(fdt_setprop(fdt, node, "reg", timas, sizeof(timas)));
|
_FDT(fdt_setprop(fdt, node, "reg", timas, sizeof(timas)));
|
||||||
|
@ -338,6 +338,9 @@ static void icp_realize(DeviceState *dev, Error **errp)
|
|||||||
case PPC_FLAGS_INPUT_POWER7:
|
case PPC_FLAGS_INPUT_POWER7:
|
||||||
icp->output = env->irq_inputs[POWER7_INPUT_INT];
|
icp->output = env->irq_inputs[POWER7_INPUT_INT];
|
||||||
break;
|
break;
|
||||||
|
case PPC_FLAGS_INPUT_POWER9: /* For SPAPR xics emulation */
|
||||||
|
icp->output = env->irq_inputs[POWER9_INPUT_INT];
|
||||||
|
break;
|
||||||
|
|
||||||
case PPC_FLAGS_INPUT_970:
|
case PPC_FLAGS_INPUT_970:
|
||||||
icp->output = env->irq_inputs[PPC970_INPUT_INT];
|
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 |=
|
ics->irqs[srcno].flags |=
|
||||||
lsi ? XICS_FLAGS_IRQ_LSI : XICS_FLAGS_IRQ_MSI;
|
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)
|
static void xics_register_types(void)
|
||||||
|
@ -213,45 +213,57 @@ void ics_synchronize_state(ICSState *ics)
|
|||||||
ics_get_kvm_state(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;
|
uint64_t state;
|
||||||
int i;
|
|
||||||
Error *local_err = NULL;
|
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++) {
|
for (i = 0; i < ics->nr_irqs; i++) {
|
||||||
ICSIRQState *irq = &ics->irqs[i];
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
state = irq->server;
|
ret = ics_set_kvm_state_one(ics, i);
|
||||||
state |= (uint64_t)(irq->saved_priority & KVM_XICS_PRIORITY_MASK)
|
if (ret) {
|
||||||
<< 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);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -254,7 +254,7 @@ void spapr_dt_xics(sPAPRMachineState *spapr, uint32_t nr_servers, void *fdt,
|
|||||||
};
|
};
|
||||||
int node;
|
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",
|
_FDT(fdt_setprop_string(fdt, node, "device_type",
|
||||||
"PowerPC-External-Interrupt-Presentation"));
|
"PowerPC-External-Interrupt-Presentation"));
|
||||||
|
@ -481,8 +481,8 @@ static void xive_tctx_realize(DeviceState *dev, Error **errp)
|
|||||||
|
|
||||||
env = &cpu->env;
|
env = &cpu->env;
|
||||||
switch (PPC_INPUT(env)) {
|
switch (PPC_INPUT(env)) {
|
||||||
case PPC_FLAGS_INPUT_POWER7:
|
case PPC_FLAGS_INPUT_POWER9:
|
||||||
tctx->output = env->irq_inputs[POWER7_INPUT_INT];
|
tctx->output = env->irq_inputs[POWER9_INPUT_INT];
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
22
hw/ppc/pnv.c
22
hw/ppc/pnv.c
@ -47,14 +47,16 @@
|
|||||||
|
|
||||||
#include <libfdt.h>
|
#include <libfdt.h>
|
||||||
|
|
||||||
#define FDT_MAX_SIZE 0x00100000
|
#define FDT_MAX_SIZE (1 * MiB)
|
||||||
|
|
||||||
#define FW_FILE_NAME "skiboot.lid"
|
#define FW_FILE_NAME "skiboot.lid"
|
||||||
#define FW_LOAD_ADDR 0x0
|
#define FW_LOAD_ADDR 0x0
|
||||||
#define FW_MAX_SIZE 0x00400000
|
#define FW_MAX_SIZE (4 * MiB)
|
||||||
|
|
||||||
#define KERNEL_LOAD_ADDR 0x20000000
|
#define KERNEL_LOAD_ADDR 0x20000000
|
||||||
|
#define KERNEL_MAX_SIZE (256 * MiB)
|
||||||
#define INITRD_LOAD_ADDR 0x60000000
|
#define INITRD_LOAD_ADDR 0x60000000
|
||||||
|
#define INITRD_MAX_SIZE (256 * MiB)
|
||||||
|
|
||||||
static const char *pnv_chip_core_typename(const PnvChip *o)
|
static const char *pnv_chip_core_typename(const PnvChip *o)
|
||||||
{
|
{
|
||||||
@ -588,7 +590,7 @@ static void pnv_init(MachineState *machine)
|
|||||||
long kernel_size;
|
long kernel_size;
|
||||||
|
|
||||||
kernel_size = load_image_targphys(machine->kernel_filename,
|
kernel_size = load_image_targphys(machine->kernel_filename,
|
||||||
KERNEL_LOAD_ADDR, 0x2000000);
|
KERNEL_LOAD_ADDR, KERNEL_MAX_SIZE);
|
||||||
if (kernel_size < 0) {
|
if (kernel_size < 0) {
|
||||||
error_report("Could not load kernel '%s'",
|
error_report("Could not load kernel '%s'",
|
||||||
machine->kernel_filename);
|
machine->kernel_filename);
|
||||||
@ -600,7 +602,7 @@ static void pnv_init(MachineState *machine)
|
|||||||
if (machine->initrd_filename) {
|
if (machine->initrd_filename) {
|
||||||
pnv->initrd_base = INITRD_LOAD_ADDR;
|
pnv->initrd_base = INITRD_LOAD_ADDR;
|
||||||
pnv->initrd_size = load_image_targphys(machine->initrd_filename,
|
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) {
|
if (pnv->initrd_size < 0) {
|
||||||
error_report("Could not load initial ram disk '%s'",
|
error_report("Could not load initial ram disk '%s'",
|
||||||
machine->initrd_filename);
|
machine->initrd_filename);
|
||||||
@ -736,18 +738,18 @@ static void pnv_chip_power8_instance_init(Object *obj)
|
|||||||
{
|
{
|
||||||
Pnv8Chip *chip8 = PNV8_CHIP(obj);
|
Pnv8Chip *chip8 = PNV8_CHIP(obj);
|
||||||
|
|
||||||
object_initialize(&chip8->psi, sizeof(chip8->psi), TYPE_PNV_PSI);
|
object_initialize_child(obj, "psi", &chip8->psi, sizeof(chip8->psi),
|
||||||
object_property_add_child(obj, "psi", OBJECT(&chip8->psi), NULL);
|
TYPE_PNV_PSI, &error_abort, NULL);
|
||||||
object_property_add_const_link(OBJECT(&chip8->psi), "xics",
|
object_property_add_const_link(OBJECT(&chip8->psi), "xics",
|
||||||
OBJECT(qdev_get_machine()), &error_abort);
|
OBJECT(qdev_get_machine()), &error_abort);
|
||||||
|
|
||||||
object_initialize(&chip8->lpc, sizeof(chip8->lpc), TYPE_PNV_LPC);
|
object_initialize_child(obj, "lpc", &chip8->lpc, sizeof(chip8->lpc),
|
||||||
object_property_add_child(obj, "lpc", OBJECT(&chip8->lpc), NULL);
|
TYPE_PNV_LPC, &error_abort, NULL);
|
||||||
object_property_add_const_link(OBJECT(&chip8->lpc), "psi",
|
object_property_add_const_link(OBJECT(&chip8->lpc), "psi",
|
||||||
OBJECT(&chip8->psi), &error_abort);
|
OBJECT(&chip8->psi), &error_abort);
|
||||||
|
|
||||||
object_initialize(&chip8->occ, sizeof(chip8->occ), TYPE_PNV_OCC);
|
object_initialize_child(obj, "occ", &chip8->occ, sizeof(chip8->occ),
|
||||||
object_property_add_child(obj, "occ", OBJECT(&chip8->occ), NULL);
|
TYPE_PNV_OCC, &error_abort, NULL);
|
||||||
object_property_add_const_link(OBJECT(&chip8->occ), "psi",
|
object_property_add_const_link(OBJECT(&chip8->occ), "psi",
|
||||||
OBJECT(&chip8->psi), &error_abort);
|
OBJECT(&chip8->psi), &error_abort);
|
||||||
}
|
}
|
||||||
|
@ -444,8 +444,8 @@ static void pnv_psi_init(Object *obj)
|
|||||||
{
|
{
|
||||||
PnvPsi *psi = PNV_PSI(obj);
|
PnvPsi *psi = PNV_PSI(obj);
|
||||||
|
|
||||||
object_initialize(&psi->ics, sizeof(psi->ics), TYPE_ICS_SIMPLE);
|
object_initialize_child(obj, "ics-psi", &psi->ics, sizeof(psi->ics),
|
||||||
object_property_add_child(obj, "ics-psi", OBJECT(&psi->ics), NULL);
|
TYPE_ICS_SIMPLE, &error_abort, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const uint8_t irq_to_xivr[] = {
|
static const uint8_t irq_to_xivr[] = {
|
||||||
|
44
hw/ppc/ppc.c
44
hw/ppc/ppc.c
@ -306,6 +306,48 @@ void ppcPOWER7_irq_init(PowerPCCPU *cpu)
|
|||||||
env->irq_inputs = (void **)qemu_allocate_irqs(&power7_set_irq, cpu,
|
env->irq_inputs = (void **)qemu_allocate_irqs(&power7_set_irq, cpu,
|
||||||
POWER7_INPUT_NB);
|
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) */
|
#endif /* defined(TARGET_PPC64) */
|
||||||
|
|
||||||
void ppc40x_core_reset(PowerPCCPU *cpu)
|
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
|
* interrupts in a PM state. Not only they don't cause a
|
||||||
* wakeup but they also get effectively discarded.
|
* 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);
|
ppc_set_irq(cpu, PPC_INTERRUPT_HDECR, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
406
hw/ppc/spapr.c
406
hw/ppc/spapr.c
@ -1247,13 +1247,30 @@ static void *spapr_build_fdt(sPAPRMachineState *spapr)
|
|||||||
* Add info to guest to indentify which host is it being run on
|
* Add info to guest to indentify which host is it being run on
|
||||||
* and what is the uuid of the guest
|
* and what is the uuid of the guest
|
||||||
*/
|
*/
|
||||||
if (kvmppc_get_host_model(&buf)) {
|
if (spapr->host_model && !g_str_equal(spapr->host_model, "none")) {
|
||||||
_FDT(fdt_setprop_string(fdt, 0, "host-model", buf));
|
if (g_str_equal(spapr->host_model, "passthrough")) {
|
||||||
g_free(buf);
|
/* -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));
|
if (spapr->host_serial && !g_str_equal(spapr->host_serial, "none")) {
|
||||||
g_free(buf);
|
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);
|
buf = qemu_uuid_unparse_strdup(&qemu_uuid);
|
||||||
@ -1295,7 +1312,7 @@ static void *spapr_build_fdt(sPAPRMachineState *spapr)
|
|||||||
|
|
||||||
QLIST_FOREACH(phb, &spapr->phbs, list) {
|
QLIST_FOREACH(phb, &spapr->phbs, list) {
|
||||||
ret = spapr_populate_pci_dt(phb, PHANDLE_INTC, fdt,
|
ret = spapr_populate_pci_dt(phb, PHANDLE_INTC, fdt,
|
||||||
spapr->irq->nr_msis);
|
spapr->irq->nr_msis, NULL);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_report("couldn't setup PCI devices in fdt");
|
error_report("couldn't setup PCI devices in fdt");
|
||||||
exit(1);
|
exit(1);
|
||||||
@ -1348,6 +1365,14 @@ static void *spapr_build_fdt(sPAPRMachineState *spapr)
|
|||||||
exit(1);
|
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;
|
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);
|
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))
|
#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) {
|
if (!spapr->htab) {
|
||||||
kvmppc_write_hpte(ptex, pte0, pte1);
|
kvmppc_write_hpte(ptex, pte0, pte1);
|
||||||
} else {
|
} else {
|
||||||
stq_p(spapr->htab + offset, pte0);
|
if (pte0 & HPTE64_V_VALID) {
|
||||||
stq_p(spapr->htab + offset + HASH_PTE_SIZE_64 / 2, pte1);
|
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 */
|
/* 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)
|
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() &&
|
if (kvm_enabled() && kvmppc_has_cap_mmu_radix() &&
|
||||||
ppc_type_check_compat(machine->cpu_type, CPU_POWERPC_LOGICAL_3_00, 0,
|
ppc_type_check_compat(machine->cpu_type, CPU_POWERPC_LOGICAL_3_00, 0,
|
||||||
spapr->max_compat_pvr)) {
|
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.
|
* 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. */
|
* Set the GR bit in PATE so that we know there is no HPT.
|
||||||
spapr->patb_entry = PATBE1_GR;
|
*/
|
||||||
|
spapr->patb_entry = PATE1_GR;
|
||||||
|
spapr_set_all_lpcrs(LPCR_HR | LPCR_UPRT, LPCR_HR | LPCR_UPRT);
|
||||||
} else {
|
} else {
|
||||||
spapr_setup_hpt_and_vrma(spapr);
|
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) {
|
if (!spapr->cas_reboot) {
|
||||||
spapr_ovec_cleanup(spapr->ov5_cas);
|
spapr_ovec_cleanup(spapr->ov5_cas);
|
||||||
spapr->ov5_cas = spapr_ovec_new();
|
spapr->ov5_cas = spapr_ovec_new();
|
||||||
@ -1696,9 +1776,9 @@ static void spapr_create_nvram(sPAPRMachineState *spapr)
|
|||||||
|
|
||||||
static void spapr_rtc_create(sPAPRMachineState *spapr)
|
static void spapr_rtc_create(sPAPRMachineState *spapr)
|
||||||
{
|
{
|
||||||
object_initialize(&spapr->rtc, sizeof(spapr->rtc), TYPE_SPAPR_RTC);
|
object_initialize_child(OBJECT(spapr), "rtc",
|
||||||
object_property_add_child(OBJECT(spapr), "rtc", OBJECT(&spapr->rtc),
|
&spapr->rtc, sizeof(spapr->rtc), TYPE_SPAPR_RTC,
|
||||||
&error_fatal);
|
&error_fatal, NULL);
|
||||||
object_property_set_bool(OBJECT(&spapr->rtc), true, "realized",
|
object_property_set_bool(OBJECT(&spapr->rtc), true, "realized",
|
||||||
&error_fatal);
|
&error_fatal);
|
||||||
object_property_add_alias(OBJECT(spapr), "rtc-time", OBJECT(&spapr->rtc),
|
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) {
|
if (kvm_enabled() && spapr->patb_entry) {
|
||||||
PowerPCCPU *cpu = POWERPC_CPU(first_cpu);
|
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);
|
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);
|
err = kvmppc_configure_v3_mmu(cpu, radix, gtse, spapr->patb_entry);
|
||||||
if (err) {
|
if (err) {
|
||||||
error_report("Process table config unsupported by the host");
|
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 */
|
/* We always have at least the nvram device on VIO */
|
||||||
spapr_create_nvram(spapr);
|
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 */
|
/* Set up PCI */
|
||||||
spapr_pci_rtas_init();
|
spapr_pci_rtas_init();
|
||||||
|
|
||||||
@ -2909,6 +3009,9 @@ static void spapr_machine_init(MachineState *machine)
|
|||||||
register_savevm_live(NULL, "spapr/htab", -1, 1,
|
register_savevm_live(NULL, "spapr/htab", -1, 1,
|
||||||
&savevm_htab_handlers, spapr);
|
&savevm_htab_handlers, spapr);
|
||||||
|
|
||||||
|
qbus_set_hotplug_handler(sysbus_get_default(), OBJECT(machine),
|
||||||
|
&error_fatal);
|
||||||
|
|
||||||
qemu_register_boot_set(spapr_boot_set, spapr);
|
qemu_register_boot_set(spapr_boot_set, spapr);
|
||||||
|
|
||||||
if (kvm_enabled()) {
|
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)
|
static void spapr_instance_init(Object *obj)
|
||||||
{
|
{
|
||||||
sPAPRMachineState *spapr = SPAPR_MACHINE(obj);
|
sPAPRMachineState *spapr = SPAPR_MACHINE(obj);
|
||||||
@ -3189,6 +3322,17 @@ static void spapr_instance_init(Object *obj)
|
|||||||
object_property_set_description(obj, "ic-mode",
|
object_property_set_description(obj, "ic-mode",
|
||||||
"Specifies the interrupt controller mode (xics, xive, dual)",
|
"Specifies the interrupt controller mode (xics, xive, dual)",
|
||||||
NULL);
|
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)
|
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,
|
static void spapr_add_lmbs(DeviceState *dev, uint64_t addr_start, uint64_t size,
|
||||||
uint32_t node, bool dedicated_hp_event_source,
|
bool dedicated_hp_event_source, Error **errp)
|
||||||
Error **errp)
|
|
||||||
{
|
{
|
||||||
sPAPRDRConnector *drc;
|
sPAPRDRConnector *drc;
|
||||||
uint32_t nr_lmbs = size/SPAPR_MEMORY_BLOCK_SIZE;
|
uint32_t nr_lmbs = size/SPAPR_MEMORY_BLOCK_SIZE;
|
||||||
int i, fdt_offset, fdt_size;
|
int i;
|
||||||
void *fdt;
|
|
||||||
uint64_t addr = addr_start;
|
uint64_t addr = addr_start;
|
||||||
bool hotplugged = spapr_drc_hotplugged(dev);
|
bool hotplugged = spapr_drc_hotplugged(dev);
|
||||||
Error *local_err = NULL;
|
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);
|
addr / SPAPR_MEMORY_BLOCK_SIZE);
|
||||||
g_assert(drc);
|
g_assert(drc);
|
||||||
|
|
||||||
fdt = create_device_tree(&fdt_size);
|
spapr_drc_attach(drc, dev, &local_err);
|
||||||
fdt_offset = spapr_populate_memory_node(fdt, node, addr,
|
|
||||||
SPAPR_MEMORY_BLOCK_SIZE);
|
|
||||||
|
|
||||||
spapr_drc_attach(drc, dev, fdt, fdt_offset, &local_err);
|
|
||||||
if (local_err) {
|
if (local_err) {
|
||||||
while (addr > addr_start) {
|
while (addr > addr_start) {
|
||||||
addr -= SPAPR_MEMORY_BLOCK_SIZE;
|
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);
|
addr / SPAPR_MEMORY_BLOCK_SIZE);
|
||||||
spapr_drc_detach(drc);
|
spapr_drc_detach(drc);
|
||||||
}
|
}
|
||||||
g_free(fdt);
|
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -3275,7 +3426,6 @@ static void spapr_memory_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
|
|||||||
sPAPRMachineState *ms = SPAPR_MACHINE(hotplug_dev);
|
sPAPRMachineState *ms = SPAPR_MACHINE(hotplug_dev);
|
||||||
PCDIMMDevice *dimm = PC_DIMM(dev);
|
PCDIMMDevice *dimm = PC_DIMM(dev);
|
||||||
uint64_t size, addr;
|
uint64_t size, addr;
|
||||||
uint32_t node;
|
|
||||||
|
|
||||||
size = memory_device_get_region_size(MEMORY_DEVICE(dev), &error_abort);
|
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;
|
goto out_unplug;
|
||||||
}
|
}
|
||||||
|
|
||||||
node = object_property_get_uint(OBJECT(dev), PC_DIMM_NODE_PROP,
|
spapr_add_lmbs(dev, addr, size, spapr_ovec_test(ms->ov5_cas, OV5_HP_EVT),
|
||||||
&error_abort);
|
|
||||||
spapr_add_lmbs(dev, addr, size, node,
|
|
||||||
spapr_ovec_test(ms->ov5_cas, OV5_HP_EVT),
|
|
||||||
&local_err);
|
&local_err);
|
||||||
if (local_err) {
|
if (local_err) {
|
||||||
goto out_unplug;
|
goto out_unplug;
|
||||||
@ -3513,27 +3660,6 @@ out:
|
|||||||
error_propagate(errp, local_err);
|
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. */
|
/* Callback to be called during DRC release. */
|
||||||
void spapr_core_release(DeviceState *dev)
|
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);
|
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,
|
static void spapr_core_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
@ -3602,7 +3749,7 @@ static void spapr_core_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
|
|||||||
sPAPRMachineClass *smc = SPAPR_MACHINE_CLASS(mc);
|
sPAPRMachineClass *smc = SPAPR_MACHINE_CLASS(mc);
|
||||||
sPAPRCPUCore *core = SPAPR_CPU_CORE(OBJECT(dev));
|
sPAPRCPUCore *core = SPAPR_CPU_CORE(OBJECT(dev));
|
||||||
CPUCore *cc = CPU_CORE(dev);
|
CPUCore *cc = CPU_CORE(dev);
|
||||||
CPUState *cs = CPU(core->threads[0]);
|
CPUState *cs;
|
||||||
sPAPRDRConnector *drc;
|
sPAPRDRConnector *drc;
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
CPUArchId *core_slot;
|
CPUArchId *core_slot;
|
||||||
@ -3621,14 +3768,8 @@ static void spapr_core_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
|
|||||||
g_assert(drc || !mc->has_hotpluggable_cpus);
|
g_assert(drc || !mc->has_hotpluggable_cpus);
|
||||||
|
|
||||||
if (drc) {
|
if (drc) {
|
||||||
void *fdt;
|
spapr_drc_attach(drc, dev, &local_err);
|
||||||
int fdt_offset;
|
|
||||||
|
|
||||||
fdt = spapr_populate_hotplug_cpu_dt(cs, &fdt_offset, spapr);
|
|
||||||
|
|
||||||
spapr_drc_attach(drc, dev, fdt, fdt_offset, &local_err);
|
|
||||||
if (local_err) {
|
if (local_err) {
|
||||||
g_free(fdt);
|
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -3712,6 +3853,115 @@ out:
|
|||||||
error_propagate(errp, local_err);
|
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,
|
static void spapr_machine_device_plug(HotplugHandler *hotplug_dev,
|
||||||
DeviceState *dev, Error **errp)
|
DeviceState *dev, Error **errp)
|
||||||
{
|
{
|
||||||
@ -3719,6 +3969,8 @@ static void spapr_machine_device_plug(HotplugHandler *hotplug_dev,
|
|||||||
spapr_memory_plug(hotplug_dev, dev, errp);
|
spapr_memory_plug(hotplug_dev, dev, errp);
|
||||||
} else if (object_dynamic_cast(OBJECT(dev), TYPE_SPAPR_CPU_CORE)) {
|
} else if (object_dynamic_cast(OBJECT(dev), TYPE_SPAPR_CPU_CORE)) {
|
||||||
spapr_core_plug(hotplug_dev, dev, errp);
|
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);
|
spapr_memory_unplug(hotplug_dev, dev);
|
||||||
} else if (object_dynamic_cast(OBJECT(dev), TYPE_SPAPR_CPU_CORE)) {
|
} else if (object_dynamic_cast(OBJECT(dev), TYPE_SPAPR_CPU_CORE)) {
|
||||||
spapr_core_unplug(hotplug_dev, dev);
|
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));
|
sPAPRMachineState *sms = SPAPR_MACHINE(OBJECT(hotplug_dev));
|
||||||
MachineClass *mc = MACHINE_GET_CLASS(sms);
|
MachineClass *mc = MACHINE_GET_CLASS(sms);
|
||||||
|
sPAPRMachineClass *smc = SPAPR_MACHINE_CLASS(mc);
|
||||||
|
|
||||||
if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
|
if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
|
||||||
if (spapr_ovec_test(sms->ov5_cas, OV5_HP_EVT)) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
spapr_core_unplug_request(hotplug_dev, dev, errp);
|
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);
|
spapr_memory_pre_plug(hotplug_dev, dev, errp);
|
||||||
} else if (object_dynamic_cast(OBJECT(dev), TYPE_SPAPR_CPU_CORE)) {
|
} else if (object_dynamic_cast(OBJECT(dev), TYPE_SPAPR_CPU_CORE)) {
|
||||||
spapr_core_pre_plug(hotplug_dev, dev, errp);
|
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)
|
DeviceState *dev)
|
||||||
{
|
{
|
||||||
if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM) ||
|
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 HOTPLUG_HANDLER(machine);
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -4004,7 +4268,7 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data)
|
|||||||
vhc->map_hptes = spapr_map_hptes;
|
vhc->map_hptes = spapr_map_hptes;
|
||||||
vhc->unmap_hptes = spapr_unmap_hptes;
|
vhc->unmap_hptes = spapr_unmap_hptes;
|
||||||
vhc->store_hpte = spapr_store_hpte;
|
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;
|
vhc->encode_hpt_for_kvm_pr = spapr_encode_hpt_for_kvm_pr;
|
||||||
xic->ics_get = spapr_ics_get;
|
xic->ics_get = spapr_ics_get;
|
||||||
xic->ics_resend = spapr_ics_resend;
|
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;
|
smc->default_caps.caps[SPAPR_CAP_NESTED_KVM_HV] = SPAPR_CAP_OFF;
|
||||||
spapr_caps_add_properties(smc, &error_abort);
|
spapr_caps_add_properties(smc, &error_abort);
|
||||||
smc->irq = &spapr_irq_xics;
|
smc->irq = &spapr_irq_xics;
|
||||||
|
smc->dr_phb_enabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const TypeInfo spapr_machine_info = {
|
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)
|
static void spapr_machine_3_1_class_options(MachineClass *mc)
|
||||||
{
|
{
|
||||||
sPAPRMachineClass *smc = SPAPR_MACHINE_CLASS(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);
|
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, 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");
|
mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("power8_v2.0");
|
||||||
smc->update_dt_enabled = false;
|
smc->update_dt_enabled = false;
|
||||||
|
smc->dr_phb_enabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
DEFINE_SPAPR_MACHINE(3_1, "3.1", false);
|
DEFINE_SPAPR_MACHINE(3_1, "3.1", false);
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
#include "qemu/error-report.h"
|
#include "qemu/error-report.h"
|
||||||
#include "hw/ppc/spapr.h" /* for RTAS return codes */
|
#include "hw/ppc/spapr.h" /* for RTAS return codes */
|
||||||
#include "hw/pci-host/spapr.h" /* spapr_phb_remove_pci_device_cb callback */
|
#include "hw/pci-host/spapr.h" /* spapr_phb_remove_pci_device_cb callback */
|
||||||
|
#include "sysemu/device_tree.h"
|
||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
|
|
||||||
#define DRC_CONTAINER_PATH "/dr-connector"
|
#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);
|
} while (fdt_depth != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void spapr_drc_attach(sPAPRDRConnector *drc, DeviceState *d, void *fdt,
|
void spapr_drc_attach(sPAPRDRConnector *drc, DeviceState *d, Error **errp)
|
||||||
int fdt_start_offset, Error **errp)
|
|
||||||
{
|
{
|
||||||
trace_spapr_drc_attach(spapr_drc_index(drc));
|
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)
|
g_assert((drc->state == SPAPR_DRC_STATE_LOGICAL_UNUSABLE)
|
||||||
|| (drc->state == SPAPR_DRC_STATE_PHYSICAL_POWERON));
|
|| (drc->state == SPAPR_DRC_STATE_PHYSICAL_POWERON));
|
||||||
g_assert(fdt);
|
|
||||||
|
|
||||||
drc->dev = d;
|
drc->dev = d;
|
||||||
drc->fdt = fdt;
|
|
||||||
drc->fdt_start_offset = fdt_start_offset;
|
|
||||||
|
|
||||||
object_property_add_link(OBJECT(drc), "device",
|
object_property_add_link(OBJECT(drc), "device",
|
||||||
object_get_typename(OBJECT(drc->dev)),
|
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->typename = "CPU";
|
||||||
drck->drc_name_prefix = "CPU ";
|
drck->drc_name_prefix = "CPU ";
|
||||||
drck->release = spapr_core_release;
|
drck->release = spapr_core_release;
|
||||||
|
drck->dt_populate = spapr_core_dt_populate;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void spapr_drc_pci_class_init(ObjectClass *k, void *data)
|
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->typename = "28";
|
||||||
drck->drc_name_prefix = "C";
|
drck->drc_name_prefix = "C";
|
||||||
drck->release = spapr_phb_remove_pci_device_cb;
|
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)
|
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->typename = "MEM";
|
||||||
drck->drc_name_prefix = "LMB ";
|
drck->drc_name_prefix = "LMB ";
|
||||||
drck->release = spapr_lmb_release;
|
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 = {
|
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,
|
.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 */
|
/* helper functions for external users */
|
||||||
|
|
||||||
sPAPRDRConnector *spapr_drc_by_index(uint32_t index)
|
sPAPRDRConnector *spapr_drc_by_index(uint32_t index)
|
||||||
@ -1102,10 +1120,28 @@ static void rtas_ibm_configure_connector(PowerPCCPU *cpu,
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
g_assert(drc->fdt);
|
|
||||||
|
|
||||||
drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
|
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 {
|
do {
|
||||||
uint32_t tag;
|
uint32_t tag;
|
||||||
const char *name;
|
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_cpu_info);
|
||||||
type_register_static(&spapr_drc_pci_info);
|
type_register_static(&spapr_drc_pci_info);
|
||||||
type_register_static(&spapr_drc_lmb_info);
|
type_register_static(&spapr_drc_lmb_info);
|
||||||
|
type_register_static(&spapr_drc_phb_info);
|
||||||
|
|
||||||
spapr_rtas_register(RTAS_SET_INDICATOR, "set-indicator",
|
spapr_rtas_register(RTAS_SET_INDICATOR, "set-indicator",
|
||||||
rtas_set_indicator);
|
rtas_set_indicator);
|
||||||
|
@ -526,6 +526,9 @@ static void spapr_hotplug_req_event(uint8_t hp_id, uint8_t hp_action,
|
|||||||
case SPAPR_DR_CONNECTOR_TYPE_CPU:
|
case SPAPR_DR_CONNECTOR_TYPE_CPU:
|
||||||
hp->hotplug_type = RTAS_LOG_V6_HP_TYPE_CPU;
|
hp->hotplug_type = RTAS_LOG_V6_HP_TYPE_CPU;
|
||||||
break;
|
break;
|
||||||
|
case SPAPR_DR_CONNECTOR_TYPE_PHB:
|
||||||
|
hp->hotplug_type = RTAS_LOG_V6_HP_TYPE_PHB;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
/* we shouldn't be signaling hotplug events for resources
|
/* we shouldn't be signaling hotplug events for resources
|
||||||
* that don't support them
|
* that don't support them
|
||||||
|
@ -17,37 +17,6 @@
|
|||||||
#include "mmu-book3s-v3.h"
|
#include "mmu-book3s-v3.h"
|
||||||
#include "hw/mem/memory-device.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)
|
static bool has_spr(PowerPCCPU *cpu, int spr)
|
||||||
{
|
{
|
||||||
/* We can test whether the SPR is defined by checking for a valid name */
|
/* 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) {
|
switch (mflags) {
|
||||||
case H_SET_MODE_ENDIAN_BIG:
|
case H_SET_MODE_ENDIAN_BIG:
|
||||||
set_all_lpcrs(0, LPCR_ILE);
|
spapr_set_all_lpcrs(0, LPCR_ILE);
|
||||||
spapr_pci_switch_vga(true);
|
spapr_pci_switch_vga(true);
|
||||||
return H_SUCCESS;
|
return H_SUCCESS;
|
||||||
|
|
||||||
case H_SET_MODE_ENDIAN_LITTLE:
|
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);
|
spapr_pci_switch_vga(false);
|
||||||
return H_SUCCESS;
|
return H_SUCCESS;
|
||||||
}
|
}
|
||||||
@ -1289,7 +1258,7 @@ static target_ulong h_set_mode_resource_addr_trans_mode(PowerPCCPU *cpu,
|
|||||||
return H_UNSUPPORTED_FLAG;
|
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;
|
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
|
* 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 */
|
/* 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 */
|
/* HASH->RADIX : Free HPT */
|
||||||
spapr_free_hpt(spapr);
|
spapr_free_hpt(spapr);
|
||||||
} else if (!(patbe_new & PATBE1_GR)) {
|
} else if (!(patbe_new & PATE1_GR)) {
|
||||||
/* RADIX->HASH || NOTHING->HASH : Allocate HPT */
|
/* RADIX->HASH || NOTHING->HASH : Allocate HPT */
|
||||||
spapr_setup_hpt_and_vrma(spapr);
|
spapr_setup_hpt_and_vrma(spapr);
|
||||||
}
|
}
|
||||||
@ -1385,7 +1354,7 @@ static target_ulong h_register_process_table(PowerPCCPU *cpu,
|
|||||||
} else if (table_size > 24) {
|
} else if (table_size > 24) {
|
||||||
return H_P4;
|
return H_P4;
|
||||||
}
|
}
|
||||||
cproc = PATBE1_GR | proc_tbl | table_size;
|
cproc = PATE1_GR | proc_tbl | table_size;
|
||||||
} else { /* Register new HPT process table */
|
} else { /* Register new HPT process table */
|
||||||
if (flags & FLAG_HASH_PROC_TBL) { /* Hash with Segment Tables */
|
if (flags & FLAG_HASH_PROC_TBL) { /* Hash with Segment Tables */
|
||||||
/* TODO - Not Supported */
|
/* TODO - Not Supported */
|
||||||
@ -1404,13 +1373,15 @@ static target_ulong h_register_process_table(PowerPCCPU *cpu,
|
|||||||
}
|
}
|
||||||
|
|
||||||
} else { /* Deregister current process table */
|
} 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
|
* Set to benign value: (current GR) | 0. This allows
|
||||||
* doesn't match the radix bit in the old PATB. */
|
* deregistration in KVM to succeed even if the radix bit
|
||||||
cproc = spapr->patb_entry & PATBE1_GR;
|
* in flags doesn't match the radix bit in the old PATE.
|
||||||
|
*/
|
||||||
|
cproc = spapr->patb_entry & PATE1_GR;
|
||||||
}
|
}
|
||||||
} else { /* Maintain current registration */
|
} 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 */
|
/* Technically caused by flag bits => H_PARAMETER */
|
||||||
return H_PARAMETER; /* Existing Process Table Mismatch */
|
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 */
|
spapr->patb_entry = cproc; /* Save new process table */
|
||||||
|
|
||||||
/* Update the UPRT and GTSE bits in the LPCR for all cpus */
|
/* Update the UPRT, HR and GTSE bits in the LPCR for all cpus */
|
||||||
set_all_lpcrs(((flags & (FLAG_RADIX | FLAG_HASH_PROC_TBL)) ? LPCR_UPRT : 0) |
|
spapr_set_all_lpcrs(((flags & (FLAG_RADIX | FLAG_HASH_PROC_TBL)) ?
|
||||||
((flags & FLAG_GTSE) ? LPCR_GTSE : 0),
|
(LPCR_UPRT | LPCR_HR) : 0) |
|
||||||
LPCR_UPRT | LPCR_GTSE);
|
((flags & FLAG_GTSE) ? LPCR_GTSE : 0),
|
||||||
|
LPCR_UPRT | LPCR_HR | LPCR_GTSE);
|
||||||
|
|
||||||
if (kvm_enabled()) {
|
if (kvm_enabled()) {
|
||||||
return kvmppc_configure_v3_mmu(cpu, flags & FLAG_RADIX,
|
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->cas_reboot) {
|
||||||
/* If spapr_machine_reset() did not set up a HPT but one is necessary
|
/* 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. */
|
* (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: */
|
/* legacy hash or new hash: */
|
||||||
spapr_setup_hpt_and_vrma(spapr);
|
spapr_setup_hpt_and_vrma(spapr);
|
||||||
}
|
}
|
||||||
|
@ -230,6 +230,11 @@ static void spapr_irq_reset_xics(sPAPRMachineState *spapr, Error **errp)
|
|||||||
/* TODO: create the KVM XICS device */
|
/* 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_IRQS 0x1000
|
||||||
#define SPAPR_IRQ_XICS_NR_MSIS \
|
#define SPAPR_IRQ_XICS_NR_MSIS \
|
||||||
(XICS_IRQ_BASE + SPAPR_IRQ_XICS_NR_IRQS - SPAPR_IRQ_MSI)
|
(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,
|
.post_load = spapr_irq_post_load_xics,
|
||||||
.reset = spapr_irq_reset_xics,
|
.reset = spapr_irq_reset_xics,
|
||||||
.set_irq = spapr_irq_set_irq_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);
|
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
|
* XIVE uses the full IRQ number space. Set it to 8K to be compatible
|
||||||
* with XICS.
|
* with XICS.
|
||||||
@ -407,6 +418,7 @@ sPAPRIrq spapr_irq_xive = {
|
|||||||
.post_load = spapr_irq_post_load_xive,
|
.post_load = spapr_irq_post_load_xive,
|
||||||
.reset = spapr_irq_reset_xive,
|
.reset = spapr_irq_reset_xive,
|
||||||
.set_irq = spapr_irq_set_irq_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);
|
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
|
* 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,
|
.cpu_intc_create = spapr_irq_cpu_intc_create_dual,
|
||||||
.post_load = spapr_irq_post_load_dual,
|
.post_load = spapr_irq_post_load_dual,
|
||||||
.reset = spapr_irq_reset_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
|
* 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,
|
.cpu_intc_create = spapr_irq_cpu_intc_create_xics,
|
||||||
.post_load = spapr_irq_post_load_xics,
|
.post_load = spapr_irq_post_load_xics,
|
||||||
.set_irq = spapr_irq_set_irq_xics,
|
.set_irq = spapr_irq_set_irq_xics,
|
||||||
|
.get_nodename = spapr_irq_get_nodename_xics,
|
||||||
};
|
};
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
#include "qemu/bitmap.h"
|
#include "qemu/bitmap.h"
|
||||||
#include "exec/address-spaces.h"
|
#include "exec/address-spaces.h"
|
||||||
#include "qemu/error-report.h"
|
#include "qemu/error-report.h"
|
||||||
|
#include "sysemu/qtest.h"
|
||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
#include <libfdt.h>
|
#include <libfdt.h>
|
||||||
|
|
||||||
@ -131,6 +132,11 @@ bool spapr_ovec_test(sPAPROptionVector *ov, long bitnr)
|
|||||||
g_assert(ov);
|
g_assert(ov);
|
||||||
g_assert(bitnr < OV_MAXBITS);
|
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;
|
return test_bit(bitnr, ov->bitmap) ? true : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1408,6 +1408,17 @@ static uint32_t spapr_phb_get_pci_drc_index(sPAPRPHBState *phb,
|
|||||||
return spapr_drc_index(drc);
|
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,
|
static void spapr_pci_plug(HotplugHandler *plug_handler,
|
||||||
DeviceState *plugged_dev, Error **errp)
|
DeviceState *plugged_dev, Error **errp)
|
||||||
{
|
{
|
||||||
@ -1417,8 +1428,6 @@ static void spapr_pci_plug(HotplugHandler *plug_handler,
|
|||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
PCIBus *bus = PCI_BUS(qdev_get_parent_bus(DEVICE(pdev)));
|
PCIBus *bus = PCI_BUS(qdev_get_parent_bus(DEVICE(pdev)));
|
||||||
uint32_t slotnr = PCI_SLOT(pdev->devfn);
|
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
|
/* if DR is disabled we don't need to do anything in the case of
|
||||||
* hotplug or coldplug callbacks
|
* hotplug or coldplug callbacks
|
||||||
@ -1448,10 +1457,7 @@ static void spapr_pci_plug(HotplugHandler *plug_handler,
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
fdt = create_device_tree(&fdt_size);
|
spapr_drc_attach(drc, DEVICE(pdev), &local_err);
|
||||||
fdt_start_offset = spapr_create_pci_child_dt(phb, pdev, fdt, 0);
|
|
||||||
|
|
||||||
spapr_drc_attach(drc, DEVICE(pdev), fdt, fdt_start_offset, &local_err);
|
|
||||||
if (local_err) {
|
if (local_err) {
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
@ -1483,7 +1489,6 @@ static void spapr_pci_plug(HotplugHandler *plug_handler,
|
|||||||
out:
|
out:
|
||||||
if (local_err) {
|
if (local_err) {
|
||||||
error_propagate(errp, 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)
|
static void spapr_phb_realize(DeviceState *dev, Error **errp)
|
||||||
{
|
{
|
||||||
/* We don't use SPAPR_MACHINE() in order to exit gracefully if the user
|
/* 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;
|
PCIBus *bus;
|
||||||
uint64_t msi_window_size = 4096;
|
uint64_t msi_window_size = 4096;
|
||||||
sPAPRTCETable *tcet;
|
sPAPRTCETable *tcet;
|
||||||
const unsigned windows_supported =
|
const unsigned windows_supported = spapr_phb_windows_supported(sphb);
|
||||||
sphb->ddw_enabled ? SPAPR_PCI_DMA_MAX_WINDOWS : 1;
|
|
||||||
|
|
||||||
if (!spapr) {
|
if (!spapr) {
|
||||||
error_setg(errp, TYPE_SPAPR_PCI_HOST_BRIDGE " needs a pseries machine");
|
error_setg(errp, TYPE_SPAPR_PCI_HOST_BRIDGE " needs a pseries machine");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sphb->index != (uint32_t)-1) {
|
assert(sphb->index != (uint32_t)-1); /* checked in spapr_phb_pre_plug() */
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sphb->mem64_win_size != 0) {
|
if (sphb->mem64_win_size != 0) {
|
||||||
if (sphb->mem_win_size > SPAPR_PCI_MEM32_WIN_SIZE) {
|
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) {
|
if (local_err) {
|
||||||
error_propagate_prepend(errp, local_err,
|
error_propagate_prepend(errp, local_err,
|
||||||
"can't allocate LSIs: ");
|
"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;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1747,7 +1810,7 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp)
|
|||||||
spapr_irq_claim(spapr, irq, true, &local_err);
|
spapr_irq_claim(spapr, irq, true, &local_err);
|
||||||
if (local_err) {
|
if (local_err) {
|
||||||
error_propagate_prepend(errp, local_err, "can't allocate LSIs: ");
|
error_propagate_prepend(errp, local_err, "can't allocate LSIs: ");
|
||||||
return;
|
goto unrealize;
|
||||||
}
|
}
|
||||||
|
|
||||||
sphb->lsi_table[i].irq = irq;
|
sphb->lsi_table[i].irq = irq;
|
||||||
@ -1767,13 +1830,17 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp)
|
|||||||
if (!tcet) {
|
if (!tcet) {
|
||||||
error_setg(errp, "Creating window#%d failed for %s",
|
error_setg(errp, "Creating window#%d failed for %s",
|
||||||
i, sphb->dtbusname);
|
i, sphb->dtbusname);
|
||||||
return;
|
goto unrealize;
|
||||||
}
|
}
|
||||||
memory_region_add_subregion(&sphb->iommu_root, 0,
|
memory_region_add_subregion(&sphb->iommu_root, 0,
|
||||||
spapr_tce_get_iommu(tcet));
|
spapr_tce_get_iommu(tcet));
|
||||||
}
|
}
|
||||||
|
|
||||||
sphb->msi = g_hash_table_new_full(g_int_hash, g_int_equal, g_free, g_free);
|
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)
|
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;
|
hc->root_bus_path = spapr_phb_root_bus_path;
|
||||||
dc->realize = spapr_phb_realize;
|
dc->realize = spapr_phb_realize;
|
||||||
|
dc->unrealize = spapr_phb_unrealize;
|
||||||
dc->props = spapr_phb_properties;
|
dc->props = spapr_phb_properties;
|
||||||
dc->reset = spapr_phb_reset;
|
dc->reset = spapr_phb_reset;
|
||||||
dc->vmsd = &vmstate_spapr_pci;
|
dc->vmsd = &vmstate_spapr_pci;
|
||||||
@ -1987,6 +2055,7 @@ static const TypeInfo spapr_phb_info = {
|
|||||||
.name = TYPE_SPAPR_PCI_HOST_BRIDGE,
|
.name = TYPE_SPAPR_PCI_HOST_BRIDGE,
|
||||||
.parent = TYPE_PCI_HOST_BRIDGE,
|
.parent = TYPE_PCI_HOST_BRIDGE,
|
||||||
.instance_size = sizeof(sPAPRPHBState),
|
.instance_size = sizeof(sPAPRPHBState),
|
||||||
|
.instance_finalize = spapr_phb_finalizefn,
|
||||||
.class_init = spapr_phb_class_init,
|
.class_init = spapr_phb_class_init,
|
||||||
.interfaces = (InterfaceInfo[]) {
|
.interfaces = (InterfaceInfo[]) {
|
||||||
{ TYPE_HOTPLUG_HANDLER },
|
{ 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,
|
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;
|
int bus_off, i, j, ret;
|
||||||
gchar *nodename;
|
gchar *nodename;
|
||||||
@ -2120,11 +2189,15 @@ int spapr_populate_pci_dt(sPAPRPHBState *phb, uint32_t intc_phandle, void *fdt,
|
|||||||
sPAPRTCETable *tcet;
|
sPAPRTCETable *tcet;
|
||||||
PCIBus *bus = PCI_HOST_BRIDGE(phb)->bus;
|
PCIBus *bus = PCI_HOST_BRIDGE(phb)->bus;
|
||||||
sPAPRFDT s_fdt;
|
sPAPRFDT s_fdt;
|
||||||
|
sPAPRDRConnector *drc;
|
||||||
|
|
||||||
/* Start populating the FDT */
|
/* Start populating the FDT */
|
||||||
nodename = g_strdup_printf("pci@%" PRIx64, phb->buid);
|
nodename = g_strdup_printf("pci@%" PRIx64, phb->buid);
|
||||||
_FDT(bus_off = fdt_add_subnode(fdt, 0, nodename));
|
_FDT(bus_off = fdt_add_subnode(fdt, 0, nodename));
|
||||||
g_free(nodename);
|
g_free(nodename);
|
||||||
|
if (node_offset) {
|
||||||
|
*node_offset = bus_off;
|
||||||
|
}
|
||||||
|
|
||||||
/* Write PHB properties */
|
/* Write PHB properties */
|
||||||
_FDT(fdt_setprop_string(fdt, bus_off, "device_type", "pci"));
|
_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->liobn, tcet->bus_offset,
|
||||||
tcet->nb_table << tcet->page_shift);
|
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*/
|
/* Walk the bridges and program the bus numbers*/
|
||||||
spapr_phb_pci_enumerate(phb);
|
spapr_phb_pci_enumerate(phb);
|
||||||
_FDT(fdt_setprop_cell(fdt, bus_off, "qemu,phb-enumerated", 0x1));
|
_FDT(fdt_setprop_cell(fdt, bus_off, "qemu,phb-enumerated", 0x1));
|
||||||
|
@ -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
|
* New cpus are expected to start in the same radix/hash mode
|
||||||
* as the existing CPUs
|
* as the existing CPUs
|
||||||
*/
|
*/
|
||||||
if (ppc64_radix_guest(callcpu)) {
|
if (ppc64_v3_radix(callcpu)) {
|
||||||
lpcr |= LPCR_UPRT | LPCR_GTSE;
|
lpcr |= LPCR_UPRT | LPCR_GTSE | LPCR_HR;
|
||||||
} else {
|
} else {
|
||||||
lpcr &= ~(LPCR_UPRT | LPCR_GTSE);
|
lpcr &= ~(LPCR_UPRT | LPCR_GTSE | LPCR_HR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ppc_store_lpcr(newcpu, lpcr);
|
ppc_store_lpcr(newcpu, lpcr);
|
||||||
|
@ -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,
|
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);
|
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,
|
PCIDevice *spapr_pci_find_dev(sPAPRMachineState *spapr, uint64_t buid,
|
||||||
uint32_t config_addr);
|
uint32_t config_addr);
|
||||||
|
|
||||||
/* PCI release callback. */
|
/* DRC callbacks */
|
||||||
void spapr_phb_remove_pci_device_cb(DeviceState *dev);
|
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 */
|
/* VFIO EEH hooks */
|
||||||
#ifdef CONFIG_LINUX
|
#ifdef CONFIG_LINUX
|
||||||
@ -163,4 +165,9 @@ static inline void spapr_phb_vfio_reset(DeviceState *qdev)
|
|||||||
|
|
||||||
void spapr_phb_dma_reset(sPAPRPHBState *sphb);
|
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 */
|
#endif /* PCI_HOST_SPAPR_H */
|
||||||
|
@ -73,6 +73,7 @@ static inline void ppc40x_irq_init(PowerPCCPU *cpu) {}
|
|||||||
static inline void ppc6xx_irq_init(PowerPCCPU *cpu) {}
|
static inline void ppc6xx_irq_init(PowerPCCPU *cpu) {}
|
||||||
static inline void ppc970_irq_init(PowerPCCPU *cpu) {}
|
static inline void ppc970_irq_init(PowerPCCPU *cpu) {}
|
||||||
static inline void ppcPOWER7_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) {}
|
static inline void ppce500_irq_init(PowerPCCPU *cpu) {}
|
||||||
#else
|
#else
|
||||||
void ppc40x_irq_init(PowerPCCPU *cpu);
|
void ppc40x_irq_init(PowerPCCPU *cpu);
|
||||||
@ -80,6 +81,7 @@ void ppce500_irq_init(PowerPCCPU *cpu);
|
|||||||
void ppc6xx_irq_init(PowerPCCPU *cpu);
|
void ppc6xx_irq_init(PowerPCCPU *cpu);
|
||||||
void ppc970_irq_init(PowerPCCPU *cpu);
|
void ppc970_irq_init(PowerPCCPU *cpu);
|
||||||
void ppcPOWER7_irq_init(PowerPCCPU *cpu);
|
void ppcPOWER7_irq_init(PowerPCCPU *cpu);
|
||||||
|
void ppcPOWER9_irq_init(PowerPCCPU *cpu);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* PPC machines for OpenBIOS */
|
/* PPC machines for OpenBIOS */
|
||||||
|
@ -104,6 +104,7 @@ struct sPAPRMachineClass {
|
|||||||
|
|
||||||
/*< public >*/
|
/*< public >*/
|
||||||
bool dr_lmb_enabled; /* enable dynamic-reconfig/hotplug of LMBs */
|
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 update_dt_enabled; /* enable KVMPPC_H_UPDATE_DT */
|
||||||
bool use_ohci_by_default; /* use USB-OHCI instead of XHCI */
|
bool use_ohci_by_default; /* use USB-OHCI instead of XHCI */
|
||||||
bool pre_2_10_has_unused_icps;
|
bool pre_2_10_has_unused_icps;
|
||||||
@ -177,6 +178,8 @@ struct sPAPRMachineState {
|
|||||||
|
|
||||||
/*< public >*/
|
/*< public >*/
|
||||||
char *kvm_type;
|
char *kvm_type;
|
||||||
|
char *host_model;
|
||||||
|
char *host_serial;
|
||||||
|
|
||||||
int32_t irq_map_nr;
|
int32_t irq_map_nr;
|
||||||
unsigned long *irq_map;
|
unsigned long *irq_map;
|
||||||
@ -762,9 +765,16 @@ void spapr_reallocate_hpt(sPAPRMachineState *spapr, int shift,
|
|||||||
void spapr_clear_pending_events(sPAPRMachineState *spapr);
|
void spapr_clear_pending_events(sPAPRMachineState *spapr);
|
||||||
int spapr_max_server_number(sPAPRMachineState *spapr);
|
int spapr_max_server_number(sPAPRMachineState *spapr);
|
||||||
|
|
||||||
/* CPU and LMB DRC release callbacks. */
|
/* DRC callbacks. */
|
||||||
void spapr_core_release(DeviceState *dev);
|
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);
|
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);
|
void spapr_rtc_read(sPAPRRTCState *rtc, struct tm *tm, uint32_t *ns);
|
||||||
int spapr_rtc_import_offset(sPAPRRTCState *rtc, int64_t legacy_offset);
|
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_EXPLOIT 0x40
|
||||||
#define SPAPR_OV5_XIVE_BOTH 0x80 /* Only to advertise on the platform */
|
#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 */
|
#endif /* HW_SPAPR_H */
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
#include "qom/object.h"
|
#include "qom/object.h"
|
||||||
#include "sysemu/sysemu.h"
|
#include "sysemu/sysemu.h"
|
||||||
#include "hw/qdev.h"
|
#include "hw/qdev.h"
|
||||||
|
#include "qapi/error.h"
|
||||||
|
|
||||||
#define TYPE_SPAPR_DR_CONNECTOR "spapr-dr-connector"
|
#define TYPE_SPAPR_DR_CONNECTOR "spapr-dr-connector"
|
||||||
#define SPAPR_DR_CONNECTOR_GET_CLASS(obj) \
|
#define SPAPR_DR_CONNECTOR_GET_CLASS(obj) \
|
||||||
@ -70,6 +71,14 @@
|
|||||||
#define SPAPR_DRC_LMB(obj) OBJECT_CHECK(sPAPRDRConnector, (obj), \
|
#define SPAPR_DRC_LMB(obj) OBJECT_CHECK(sPAPRDRConnector, (obj), \
|
||||||
TYPE_SPAPR_DRC_LMB)
|
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
|
* Various hotplug types managed by sPAPRDRConnector
|
||||||
*
|
*
|
||||||
@ -213,6 +222,8 @@ typedef struct sPAPRDRConnector {
|
|||||||
int fdt_start_offset;
|
int fdt_start_offset;
|
||||||
} sPAPRDRConnector;
|
} sPAPRDRConnector;
|
||||||
|
|
||||||
|
struct sPAPRMachineState;
|
||||||
|
|
||||||
typedef struct sPAPRDRConnectorClass {
|
typedef struct sPAPRDRConnectorClass {
|
||||||
/*< private >*/
|
/*< private >*/
|
||||||
DeviceClass parent;
|
DeviceClass parent;
|
||||||
@ -228,6 +239,9 @@ typedef struct sPAPRDRConnectorClass {
|
|||||||
uint32_t (*isolate)(sPAPRDRConnector *drc);
|
uint32_t (*isolate)(sPAPRDRConnector *drc);
|
||||||
uint32_t (*unisolate)(sPAPRDRConnector *drc);
|
uint32_t (*unisolate)(sPAPRDRConnector *drc);
|
||||||
void (*release)(DeviceState *dev);
|
void (*release)(DeviceState *dev);
|
||||||
|
|
||||||
|
int (*dt_populate)(sPAPRDRConnector *drc, struct sPAPRMachineState *spapr,
|
||||||
|
void *fdt, int *fdt_start_offset, Error **errp);
|
||||||
} sPAPRDRConnectorClass;
|
} sPAPRDRConnectorClass;
|
||||||
|
|
||||||
typedef struct sPAPRDRCPhysical {
|
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,
|
int spapr_drc_populate_dt(void *fdt, int fdt_offset, Object *owner,
|
||||||
uint32_t drc_type_mask);
|
uint32_t drc_type_mask);
|
||||||
|
|
||||||
void spapr_drc_attach(sPAPRDRConnector *drc, DeviceState *d, void *fdt,
|
void spapr_drc_attach(sPAPRDRConnector *drc, DeviceState *d, Error **errp);
|
||||||
int fdt_start_offset, Error **errp);
|
|
||||||
void spapr_drc_detach(sPAPRDRConnector *drc);
|
void spapr_drc_detach(sPAPRDRConnector *drc);
|
||||||
bool spapr_drc_needed(void *opaque);
|
bool spapr_drc_needed(void *opaque);
|
||||||
|
|
||||||
|
@ -47,6 +47,7 @@ typedef struct sPAPRIrq {
|
|||||||
int (*post_load)(sPAPRMachineState *spapr, int version_id);
|
int (*post_load)(sPAPRMachineState *spapr, int version_id);
|
||||||
void (*reset)(sPAPRMachineState *spapr, Error **errp);
|
void (*reset)(sPAPRMachineState *spapr, Error **errp);
|
||||||
void (*set_irq)(void *opaque, int srcno, int val);
|
void (*set_irq)(void *opaque, int srcno, int val);
|
||||||
|
const char *(*get_nodename)(sPAPRMachineState *spapr);
|
||||||
} sPAPRIrq;
|
} sPAPRIrq;
|
||||||
|
|
||||||
extern sPAPRIrq spapr_irq_xics;
|
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);
|
qemu_irq spapr_qirq(sPAPRMachineState *spapr, int irq);
|
||||||
int spapr_irq_post_load(sPAPRMachineState *spapr, int version_id);
|
int spapr_irq_post_load(sPAPRMachineState *spapr, int version_id);
|
||||||
void spapr_irq_reset(sPAPRMachineState *spapr, Error **errp);
|
void spapr_irq_reset(sPAPRMachineState *spapr, Error **errp);
|
||||||
|
int spapr_irq_get_phandle(sPAPRMachineState *spapr, void *fdt, Error **errp);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* XICS legacy routines
|
* XICS legacy routines
|
||||||
|
@ -26,6 +26,9 @@ typedef struct sPAPRXive {
|
|||||||
XiveENDSource end_source;
|
XiveENDSource end_source;
|
||||||
hwaddr end_base;
|
hwaddr end_base;
|
||||||
|
|
||||||
|
/* DT */
|
||||||
|
gchar *nodename;
|
||||||
|
|
||||||
/* Routing table */
|
/* Routing table */
|
||||||
XiveEAS *eat;
|
XiveEAS *eat;
|
||||||
uint32_t nr_irqs;
|
uint32_t nr_irqs;
|
||||||
|
@ -195,6 +195,7 @@ void icp_synchronize_state(ICPState *icp);
|
|||||||
void icp_kvm_realize(DeviceState *dev, Error **errp);
|
void icp_kvm_realize(DeviceState *dev, Error **errp);
|
||||||
|
|
||||||
void ics_get_kvm_state(ICSState *ics);
|
void ics_get_kvm_state(ICSState *ics);
|
||||||
|
int ics_set_kvm_state_one(ICSState *ics, int srcno);
|
||||||
int ics_set_kvm_state(ICSState *ics);
|
int ics_set_kvm_state(ICSState *ics);
|
||||||
void ics_synchronize_state(ICSState *ics);
|
void ics_synchronize_state(ICSState *ics);
|
||||||
void ics_kvm_set_irq(ICSState *ics, int srcno, int val);
|
void ics_kvm_set_irq(ICSState *ics, int srcno, int val);
|
||||||
|
@ -29,6 +29,8 @@
|
|||||||
|
|
||||||
#include "hw/ppc/spapr.h"
|
#include "hw/ppc/spapr.h"
|
||||||
|
|
||||||
|
#define XICS_NODENAME "interrupt-controller"
|
||||||
|
|
||||||
void spapr_dt_xics(sPAPRMachineState *spapr, uint32_t nr_servers, void *fdt,
|
void spapr_dt_xics(sPAPRMachineState *spapr, uint32_t nr_servers, void *fdt,
|
||||||
uint32_t phandle);
|
uint32_t phandle);
|
||||||
int xics_kvm_init(sPAPRMachineState *spapr, Error **errp);
|
int xics_kvm_init(sPAPRMachineState *spapr, Error **errp);
|
||||||
|
@ -113,6 +113,8 @@ enum powerpc_excp_t {
|
|||||||
POWERPC_EXCP_POWER7,
|
POWERPC_EXCP_POWER7,
|
||||||
/* POWER8 exception model */
|
/* POWER8 exception model */
|
||||||
POWERPC_EXCP_POWER8,
|
POWERPC_EXCP_POWER8,
|
||||||
|
/* POWER9 exception model */
|
||||||
|
POWERPC_EXCP_POWER9,
|
||||||
};
|
};
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
@ -122,6 +124,7 @@ typedef enum {
|
|||||||
PPC_PM_NAP,
|
PPC_PM_NAP,
|
||||||
PPC_PM_SLEEP,
|
PPC_PM_SLEEP,
|
||||||
PPC_PM_RVWINKLE,
|
PPC_PM_RVWINKLE,
|
||||||
|
PPC_PM_STOP,
|
||||||
} powerpc_pm_insn_t;
|
} powerpc_pm_insn_t;
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
@ -139,6 +142,8 @@ enum powerpc_input_t {
|
|||||||
PPC_FLAGS_INPUT_970,
|
PPC_FLAGS_INPUT_970,
|
||||||
/* PowerPC POWER7 bus */
|
/* PowerPC POWER7 bus */
|
||||||
PPC_FLAGS_INPUT_POWER7,
|
PPC_FLAGS_INPUT_POWER7,
|
||||||
|
/* PowerPC POWER9 bus */
|
||||||
|
PPC_FLAGS_INPUT_POWER9,
|
||||||
/* PowerPC 401 bus */
|
/* PowerPC 401 bus */
|
||||||
PPC_FLAGS_INPUT_401,
|
PPC_FLAGS_INPUT_401,
|
||||||
/* Freescale RCPU bus */
|
/* Freescale RCPU bus */
|
||||||
|
@ -160,8 +160,10 @@ enum {
|
|||||||
/* Server doorbell variants */
|
/* Server doorbell variants */
|
||||||
POWERPC_EXCP_SDOOR = 99,
|
POWERPC_EXCP_SDOOR = 99,
|
||||||
POWERPC_EXCP_SDOOR_HV = 100,
|
POWERPC_EXCP_SDOOR_HV = 100,
|
||||||
|
/* ISA 3.00 additions */
|
||||||
|
POWERPC_EXCP_HVIRT = 101,
|
||||||
/* EOL */
|
/* EOL */
|
||||||
POWERPC_EXCP_NB = 101,
|
POWERPC_EXCP_NB = 102,
|
||||||
/* QEMU exceptions: used internally during code translation */
|
/* QEMU exceptions: used internally during code translation */
|
||||||
POWERPC_EXCP_STOP = 0x200, /* stop translation */
|
POWERPC_EXCP_STOP = 0x200, /* stop translation */
|
||||||
POWERPC_EXCP_BRANCH = 0x201, /* branch instruction */
|
POWERPC_EXCP_BRANCH = 0x201, /* branch instruction */
|
||||||
@ -318,6 +320,10 @@ struct ppc_slb_t {
|
|||||||
#define SEGMENT_SHIFT_1T 40
|
#define SEGMENT_SHIFT_1T 40
|
||||||
#define SEGMENT_MASK_1T (~((1ULL << SEGMENT_SHIFT_1T) - 1))
|
#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 */
|
/* Machine state register bits definition */
|
||||||
@ -386,6 +392,7 @@ struct ppc_slb_t {
|
|||||||
#define LPCR_AIL (3ull << LPCR_AIL_SHIFT)
|
#define LPCR_AIL (3ull << LPCR_AIL_SHIFT)
|
||||||
#define LPCR_UPRT PPC_BIT(41) /* Use Process Table */
|
#define LPCR_UPRT PPC_BIT(41) /* Use Process Table */
|
||||||
#define LPCR_EVIRT PPC_BIT(42) /* Enhanced Virtualisation */
|
#define LPCR_EVIRT PPC_BIT(42) /* Enhanced Virtualisation */
|
||||||
|
#define LPCR_HR PPC_BIT(43) /* Host Radix */
|
||||||
#define LPCR_ONL PPC_BIT(45)
|
#define LPCR_ONL PPC_BIT(45)
|
||||||
#define LPCR_LD PPC_BIT(46) /* Large Decrementer */
|
#define LPCR_LD PPC_BIT(46) /* Large Decrementer */
|
||||||
#define LPCR_P7_PECE0 PPC_BIT(49)
|
#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_HVICE PPC_BIT(62) /* HV Virtualisation Int Enable */
|
||||||
#define LPCR_HDICE PPC_BIT(63)
|
#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_sf ((env->msr >> MSR_SF) & 1)
|
||||||
#define msr_isf ((env->msr >> MSR_ISF) & 1)
|
#define msr_isf ((env->msr >> MSR_ISF) & 1)
|
||||||
#define msr_shv ((env->msr >> MSR_SHV) & 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
|
* instructions and SPRs are diallowed if MSR:HV is 0
|
||||||
*/
|
*/
|
||||||
bool has_hv_mode;
|
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
|
#endif
|
||||||
|
|
||||||
/* Those resources are used only during code translation */
|
/* Those resources are used only during code translation */
|
||||||
@ -1239,7 +1252,7 @@ struct PPCVirtualHypervisorClass {
|
|||||||
hwaddr ptex, int n);
|
hwaddr ptex, int n);
|
||||||
void (*store_hpte)(PPCVirtualHypervisor *vhyp, hwaddr ptex,
|
void (*store_hpte)(PPCVirtualHypervisor *vhyp, hwaddr ptex,
|
||||||
uint64_t pte0, uint64_t pte1);
|
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);
|
target_ulong (*encode_hpt_for_kvm_pr)(PPCVirtualHypervisor *vhyp);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -2319,6 +2332,13 @@ enum {
|
|||||||
* them */
|
* them */
|
||||||
POWER7_INPUT_NB,
|
POWER7_INPUT_NB,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
/* POWER9 input pins */
|
||||||
|
POWER9_INPUT_INT = 0,
|
||||||
|
POWER9_INPUT_HINT = 1,
|
||||||
|
POWER9_INPUT_NB,
|
||||||
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Hardware exceptions definitions */
|
/* Hardware exceptions definitions */
|
||||||
@ -2343,6 +2363,7 @@ enum {
|
|||||||
PPC_INTERRUPT_PERFM, /* Performance monitor interrupt */
|
PPC_INTERRUPT_PERFM, /* Performance monitor interrupt */
|
||||||
PPC_INTERRUPT_HMI, /* Hypervisor Maintainance interrupt */
|
PPC_INTERRUPT_HMI, /* Hypervisor Maintainance interrupt */
|
||||||
PPC_INTERRUPT_HDOORBELL, /* Hypervisor Doorbell interrupt */
|
PPC_INTERRUPT_HDOORBELL, /* Hypervisor Doorbell interrupt */
|
||||||
|
PPC_INTERRUPT_HVIRT, /* Hypervisor virtualization interrupt */
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Processor Compatibility mask (PCR) */
|
/* Processor Compatibility mask (PCR) */
|
||||||
|
@ -65,6 +65,49 @@ static inline void dump_syscall(CPUPPCState *env)
|
|||||||
ppc_dump_gpr(env, 6), env->nip);
|
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
|
/* Note that this function should be greatly optimized
|
||||||
* when called with a constant excp, from ppc_hw_interrupt
|
* 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;
|
asrr0 = -1;
|
||||||
asrr1 = -1;
|
asrr1 = -1;
|
||||||
|
|
||||||
/* check for special resume at 0x100 from doze/nap/sleep/winkle on P7/P8 */
|
/*
|
||||||
if (env->in_pm_state) {
|
* check for special resume at 0x100 from doze/nap/sleep/winkle on
|
||||||
env->in_pm_state = false;
|
* P7/P8/P9
|
||||||
|
*/
|
||||||
/* Pretend to be returning from doze always as we don't lose state */
|
if (env->resume_as_sreset) {
|
||||||
msr |= (0x1ull << (63 - 47));
|
excp = powerpc_reset_wakeup(cs, env, excp, &msr);
|
||||||
|
|
||||||
/* 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Exception targetting modifiers
|
/* Exception targetting modifiers
|
||||||
*
|
*
|
||||||
* LPES0 is supported on POWER7/8
|
* LPES0 is supported on POWER7/8/9
|
||||||
* LPES1 is not supported (old iSeries mode)
|
* LPES1 is not supported (old iSeries mode)
|
||||||
*
|
*
|
||||||
* On anything else, we behave as if LPES0 is 1
|
* 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 defined(TARGET_PPC64)
|
||||||
if (excp_model == POWERPC_EXCP_POWER7 ||
|
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);
|
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;
|
ail = (env->spr[SPR_LPCR] & LPCR_AIL) >> LPCR_AIL_SHIFT;
|
||||||
} else {
|
} else {
|
||||||
ail = 0;
|
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_HISEG: /* Hypervisor instruction segment exception */
|
||||||
case POWERPC_EXCP_SDOOR_HV: /* Hypervisor Doorbell interrupt */
|
case POWERPC_EXCP_SDOOR_HV: /* Hypervisor Doorbell interrupt */
|
||||||
case POWERPC_EXCP_HV_EMU:
|
case POWERPC_EXCP_HV_EMU:
|
||||||
|
case POWERPC_EXCP_HVIRT: /* Hypervisor virtualization */
|
||||||
srr0 = SPR_HSRR0;
|
srr0 = SPR_HSRR0;
|
||||||
srr1 = SPR_HSRR1;
|
srr1 = SPR_HSRR1;
|
||||||
new_msr |= (target_ulong)MSR_HVB;
|
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) {
|
} else if (excp_model == POWERPC_EXCP_POWER8) {
|
||||||
if (new_msr & MSR_HVB) {
|
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;
|
new_msr |= (target_ulong)1 << MSR_LE;
|
||||||
}
|
}
|
||||||
} else if (env->spr[SPR_LPCR] & LPCR_ILE) {
|
} 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)
|
static void ppc_hw_interrupt(CPUPPCState *env)
|
||||||
{
|
{
|
||||||
PowerPCCPU *cpu = ppc_env_get_cpu(env);
|
PowerPCCPU *cpu = ppc_env_get_cpu(env);
|
||||||
|
bool async_deliver;
|
||||||
|
|
||||||
/* External reset */
|
/* External reset */
|
||||||
if (env->pending_interrupts & (1 << PPC_INTERRUPT_RESET)) {
|
if (env->pending_interrupts & (1 << PPC_INTERRUPT_RESET)) {
|
||||||
@ -769,21 +793,44 @@ static void ppc_hw_interrupt(CPUPPCState *env)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#endif
|
#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 */
|
/* Hypervisor decrementer exception */
|
||||||
if (env->pending_interrupts & (1 << PPC_INTERRUPT_HDECR)) {
|
if (env->pending_interrupts & (1 << PPC_INTERRUPT_HDECR)) {
|
||||||
/* LPCR will be clear when not supported so this will work */
|
/* LPCR will be clear when not supported so this will work */
|
||||||
bool hdice = !!(env->spr[SPR_LPCR] & LPCR_HDICE);
|
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 */
|
/* HDEC clears on delivery */
|
||||||
env->pending_interrupts &= ~(1 << PPC_INTERRUPT_HDECR);
|
env->pending_interrupts &= ~(1 << PPC_INTERRUPT_HDECR);
|
||||||
powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_HDECR);
|
powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_HDECR);
|
||||||
return;
|
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)) {
|
if (env->pending_interrupts & (1 << PPC_INTERRUPT_EXT)) {
|
||||||
bool lpes0 = !!(env->spr[SPR_LPCR] & LPCR_LPES0);
|
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);
|
powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_EXTERNAL);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -795,7 +842,7 @@ static void ppc_hw_interrupt(CPUPPCState *env)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (msr_ee != 0) {
|
if (async_deliver != 0) {
|
||||||
/* Watchdog timer on embedded PowerPC */
|
/* Watchdog timer on embedded PowerPC */
|
||||||
if (env->pending_interrupts & (1 << PPC_INTERRUPT_WDT)) {
|
if (env->pending_interrupts & (1 << PPC_INTERRUPT_WDT)) {
|
||||||
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;
|
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)
|
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 = CPU(ppc_env_get_cpu(env));
|
||||||
cs->halted = 1;
|
cs->halted = 1;
|
||||||
env->in_pm_state = true;
|
|
||||||
|
|
||||||
/* The architecture specifies that HDEC interrupts are
|
/* The architecture specifies that HDEC interrupts are
|
||||||
* discarded in PM states
|
* discarded in PM states
|
||||||
*/
|
*/
|
||||||
env->pending_interrupts &= ~(1 << PPC_INTERRUPT_HDECR);
|
env->pending_interrupts &= ~(1 << PPC_INTERRUPT_HDECR);
|
||||||
|
|
||||||
/* Technically, nap doesn't set EE, but if we don't set it
|
/* Condition for waking up at 0x100 */
|
||||||
* then ppc_hw_interrupt() won't deliver. We could add some
|
env->resume_as_sreset = (insn != PPC_PM_STOP) ||
|
||||||
* other tests there based on LPCR but it's simpler to just
|
(env->spr[SPR_PSSCR] & PSSCR_EC);
|
||||||
* 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);
|
|
||||||
}
|
}
|
||||||
#endif /* defined(TARGET_PPC64) */
|
#endif /* defined(TARGET_PPC64) */
|
||||||
|
|
||||||
|
@ -689,6 +689,7 @@ DEF_HELPER_2(store_ptcr, void, env, tl)
|
|||||||
#endif
|
#endif
|
||||||
DEF_HELPER_2(store_sdr1, void, env, tl)
|
DEF_HELPER_2(store_sdr1, void, env, tl)
|
||||||
DEF_HELPER_2(store_pidr, 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_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_tbu, TCG_CALL_NO_RWG, void, env, tl)
|
||||||
DEF_HELPER_FLAGS_2(store_atbl, TCG_CALL_NO_RWG, void, env, tl)
|
DEF_HELPER_FLAGS_2(store_atbl, TCG_CALL_NO_RWG, void, env, tl)
|
||||||
|
@ -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)
|
static inline void check_tlb_flush(CPUPPCState *env, bool global)
|
||||||
{
|
{
|
||||||
CPUState *cs = CPU(ppc_env_get_cpu(env));
|
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;
|
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
|
/* Then handle local ones */
|
||||||
* TLB invalidation instructions.
|
if (env->tlb_need_flush & TLB_NEED_LOCAL_FLUSH) {
|
||||||
*/
|
env->tlb_need_flush &= ~TLB_NEED_LOCAL_FLUSH;
|
||||||
if (global && (env->tlb_need_flush & TLB_NEED_GLOBAL_FLUSH)) {
|
tlb_flush(cs);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
|
@ -117,6 +117,21 @@ void helper_store_pidr(CPUPPCState *env, target_ulong val)
|
|||||||
tlb_flush(CPU(cpu));
|
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)
|
void helper_store_hid0_601(CPUPPCState *env, target_ulong val)
|
||||||
{
|
{
|
||||||
target_ulong hid0;
|
target_ulong hid0;
|
||||||
|
@ -26,9 +26,36 @@
|
|||||||
int ppc64_v3_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, int rwx,
|
int ppc64_v3_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, int rwx,
|
||||||
int mmu_idx)
|
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);
|
return ppc_radix64_handle_mmu_fault(cpu, eaddr, rwx, mmu_idx);
|
||||||
} else { /* Guest uses hash */
|
} else { /* Guest uses hash */
|
||||||
return ppc_hash64_handle_mmu_fault(cpu, eaddr, rwx, mmu_idx);
|
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;
|
||||||
|
}
|
||||||
|
@ -17,8 +17,10 @@
|
|||||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef MMU_H
|
#ifndef MMU_BOOOK3S_V3_H
|
||||||
#define MMU_H
|
#define MMU_BOOOK3S_V3_H
|
||||||
|
|
||||||
|
#include "mmu-hash64.h"
|
||||||
|
|
||||||
#ifndef CONFIG_USER_ONLY
|
#ifndef CONFIG_USER_ONLY
|
||||||
|
|
||||||
@ -29,7 +31,16 @@
|
|||||||
#define PTCR_PATS 0x000000000000001FULL /* Partition Table Size */
|
#define PTCR_PATS 0x000000000000001FULL /* Partition Table Size */
|
||||||
|
|
||||||
/* Partition Table Entry Fields */
|
/* 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 */
|
/* Process Table Entry */
|
||||||
struct prtb_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);
|
return !!(cpu->env.spr[SPR_LPCR] & LPCR_UPRT);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool ppc64_radix_guest(PowerPCCPU *cpu)
|
bool ppc64_v3_get_pate(PowerPCCPU *cpu, target_ulong lpid,
|
||||||
{
|
ppc_v3_pate_t *entry);
|
||||||
PPCVirtualHypervisorClass *vhc =
|
|
||||||
PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp);
|
|
||||||
|
|
||||||
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 ppc64_v3_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, int rwx,
|
||||||
int mmu_idx);
|
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 /* TARGET_PPC64 */
|
||||||
|
|
||||||
#endif /* CONFIG_USER_ONLY */
|
#endif /* CONFIG_USER_ONLY */
|
||||||
|
|
||||||
#endif /* MMU_H */
|
#endif /* MMU_BOOOK3S_V3_H */
|
||||||
|
@ -319,6 +319,12 @@ static hwaddr ppc_hash32_pteg_search(PowerPCCPU *cpu, hwaddr pteg_off,
|
|||||||
|
|
||||||
for (i = 0; i < HPTES_PER_GROUP; i++) {
|
for (i = 0; i < HPTES_PER_GROUP; i++) {
|
||||||
pte0 = ppc_hash32_load_hpte0(cpu, pte_offset);
|
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);
|
pte1 = ppc_hash32_load_hpte1(cpu, pte_offset);
|
||||||
|
|
||||||
if ((pte0 & HPTE32_V_VALID)
|
if ((pte0 & HPTE32_V_VALID)
|
||||||
|
@ -417,7 +417,7 @@ const ppc_hash_pte64_t *ppc_hash64_map_hptes(PowerPCCPU *cpu,
|
|||||||
hwaddr ptex, int n)
|
hwaddr ptex, int n)
|
||||||
{
|
{
|
||||||
hwaddr pte_offset = ptex * HASH_PTE_SIZE_64;
|
hwaddr pte_offset = ptex * HASH_PTE_SIZE_64;
|
||||||
hwaddr base = ppc_hash64_hpt_base(cpu);
|
hwaddr base;
|
||||||
hwaddr plen = n * HASH_PTE_SIZE_64;
|
hwaddr plen = n * HASH_PTE_SIZE_64;
|
||||||
const ppc_hash_pte64_t *hptes;
|
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);
|
PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp);
|
||||||
return vhc->map_hptes(cpu->vhyp, ptex, n);
|
return vhc->map_hptes(cpu->vhyp, ptex, n);
|
||||||
}
|
}
|
||||||
|
base = ppc_hash64_hpt_base(cpu);
|
||||||
|
|
||||||
if (!base) {
|
if (!base) {
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -490,6 +491,18 @@ static unsigned hpte_page_shift(const PPCHash64SegmentPageSizes *sps,
|
|||||||
return 0; /* Bad page size encoding */
|
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,
|
static hwaddr ppc_hash64_pteg_search(PowerPCCPU *cpu, hwaddr hash,
|
||||||
const PPCHash64SegmentPageSizes *sps,
|
const PPCHash64SegmentPageSizes *sps,
|
||||||
target_ulong ptem,
|
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++) {
|
for (i = 0; i < HPTES_PER_GROUP; i++) {
|
||||||
pte0 = ppc_hash64_hpte0(cpu, pteg, 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);
|
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 */
|
/* This compares V, B, H (secondary) and the AVPN */
|
||||||
if (HPTE64_V_COMPARE(pte0, ptem)) {
|
if (HPTE64_V_COMPARE(pte0, ptem)) {
|
||||||
*pshift = hpte_page_shift(sps, pte0, pte1);
|
*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,
|
void ppc_hash64_store_hpte(PowerPCCPU *cpu, hwaddr ptex,
|
||||||
uint64_t pte0, uint64_t pte1)
|
uint64_t pte0, uint64_t pte1)
|
||||||
{
|
{
|
||||||
hwaddr base = ppc_hash64_hpt_base(cpu);
|
hwaddr base;
|
||||||
hwaddr offset = ptex * HASH_PTE_SIZE_64;
|
hwaddr offset = ptex * HASH_PTE_SIZE_64;
|
||||||
|
|
||||||
if (cpu->vhyp) {
|
if (cpu->vhyp) {
|
||||||
@ -927,6 +951,7 @@ void ppc_hash64_store_hpte(PowerPCCPU *cpu, hwaddr ptex,
|
|||||||
vhc->store_hpte(cpu->vhyp, ptex, pte0, pte1);
|
vhc->store_hpte(cpu->vhyp, ptex, pte0, pte1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
base = ppc_hash64_hpt_base(cpu);
|
||||||
|
|
||||||
stq_phys(CPU(cpu)->as, base + offset, pte0);
|
stq_phys(CPU(cpu)->as, base + offset, pte0);
|
||||||
stq_phys(CPU(cpu)->as, base + offset + HASH_PTE_SIZE_64 / 2, pte1);
|
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 */
|
case POWERPC_MMU_3_00: /* P9 */
|
||||||
lpcr = val & (LPCR_VPM1 | LPCR_ISL | LPCR_KBV | LPCR_DPFD |
|
lpcr = val & (LPCR_VPM1 | LPCR_ISL | LPCR_KBV | LPCR_DPFD |
|
||||||
(LPCR_PECE_U_MASK & LPCR_HVEE) | LPCR_ILE | LPCR_AIL |
|
(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_PECE_L_MASK & (LPCR_PDEE | LPCR_HDEE | LPCR_EEE |
|
||||||
LPCR_DEE | LPCR_OEE)) | LPCR_MER | LPCR_GTSE | LPCR_TC |
|
LPCR_DEE | LPCR_OEE)) | LPCR_MER | LPCR_GTSE | LPCR_TC |
|
||||||
LPCR_HEIC | LPCR_LPES0 | LPCR_HVICE | LPCR_HDICE);
|
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;
|
break;
|
||||||
default:
|
default:
|
||||||
;
|
;
|
||||||
|
@ -63,6 +63,7 @@ void ppc_hash64_filter_pagesizes(PowerPCCPU *cpu,
|
|||||||
#define SDR_64_HTABORG 0x0FFFFFFFFFFC0000ULL
|
#define SDR_64_HTABORG 0x0FFFFFFFFFFC0000ULL
|
||||||
#define SDR_64_HTABSIZE 0x000000000000001FULL
|
#define SDR_64_HTABSIZE 0x000000000000001FULL
|
||||||
|
|
||||||
|
#define PATE0_HTABORG 0x0FFFFFFFFFFC0000ULL
|
||||||
#define HPTES_PER_GROUP 8
|
#define HPTES_PER_GROUP 8
|
||||||
#define HASH_PTE_SIZE_64 16
|
#define HASH_PTE_SIZE_64 16
|
||||||
#define HASH_PTEG_SIZE_64 (HASH_PTE_SIZE_64 * HPTES_PER_GROUP)
|
#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_1TB_SEG 0x4000000000000000ULL
|
||||||
#define HPTE64_V_VRMA_MASK 0x4001ffffff000000ULL
|
#define HPTE64_V_VRMA_MASK 0x4001ffffff000000ULL
|
||||||
|
|
||||||
static inline hwaddr ppc_hash64_hpt_base(PowerPCCPU *cpu)
|
/* Format changes for ARCH v3 */
|
||||||
{
|
#define HPTE64_V_COMMON_BITS 0x000fffffffffffffULL
|
||||||
if (cpu->vhyp) {
|
#define HPTE64_R_3_0_SSIZE_SHIFT 58
|
||||||
return 0;
|
#define HPTE64_R_3_0_SSIZE_MASK (3ULL << HPTE64_R_3_0_SSIZE_SHIFT)
|
||||||
}
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ppc_hash_pte64 {
|
struct ppc_hash_pte64 {
|
||||||
uint64_t pte0, pte1;
|
uint64_t pte0, pte1;
|
||||||
|
@ -31,10 +31,26 @@
|
|||||||
static bool ppc_radix64_get_fully_qualified_addr(CPUPPCState *env, vaddr eaddr,
|
static bool ppc_radix64_get_fully_qualified_addr(CPUPPCState *env, vaddr eaddr,
|
||||||
uint64_t *lpid, uint64_t *pid)
|
uint64_t *lpid, uint64_t *pid)
|
||||||
{
|
{
|
||||||
/* We don't have HV support yet and shouldn't get here with it set anyway */
|
if (msr_hv) { /* MSR[HV] -> Hypervisor/bare metal */
|
||||||
assert(!msr_hv);
|
switch (eaddr & R_EADDR_QUADRANT) {
|
||||||
|
case R_EADDR_QUADRANT0:
|
||||||
if (!msr_hv) { /* !MSR[HV] -> Guest */
|
*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) {
|
switch (eaddr & R_EADDR_QUADRANT) {
|
||||||
case R_EADDR_QUADRANT0: /* Guest application */
|
case R_EADDR_QUADRANT0: /* Guest application */
|
||||||
*lpid = env->spr[SPR_LPIDR];
|
*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);
|
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 ppc_radix64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, int rwx,
|
||||||
int mmu_idx)
|
int mmu_idx)
|
||||||
{
|
{
|
||||||
CPUState *cs = CPU(cpu);
|
CPUState *cs = CPU(cpu);
|
||||||
CPUPPCState *env = &cpu->env;
|
CPUPPCState *env = &cpu->env;
|
||||||
PPCVirtualHypervisorClass *vhc =
|
PPCVirtualHypervisorClass *vhc;
|
||||||
PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp);
|
|
||||||
hwaddr raddr, pte_addr;
|
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;
|
int page_size, prot, fault_cause = 0;
|
||||||
|
ppc_v3_pate_t pate;
|
||||||
|
|
||||||
assert((rwx == 0) || (rwx == 1) || (rwx == 2));
|
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));
|
assert(ppc64_use_proc_tbl(cpu));
|
||||||
|
|
||||||
/* Real Mode Access */
|
/* Real Mode Access */
|
||||||
@ -220,17 +248,33 @@ int ppc_radix64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, int rwx,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Get Process Table */
|
/* 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 */
|
/* Index Process Table by PID to Find Corresponding Process Table Entry */
|
||||||
offset = pid * sizeof(struct prtb_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) {
|
if (offset >= size) {
|
||||||
/* offset exceeds size of the process table */
|
/* offset exceeds size of the process table */
|
||||||
ppc_radix64_raise_si(cpu, rwx, eaddr, DSISR_NOPTE);
|
ppc_radix64_raise_si(cpu, rwx, eaddr, DSISR_NOPTE);
|
||||||
return 1;
|
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 */
|
/* Walk Radix Tree from Process Table Entry to Convert EA to RA */
|
||||||
page_size = PRTBE_R_GET_RTS(prtbe0);
|
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);
|
CPUState *cs = CPU(cpu);
|
||||||
CPUPPCState *env = &cpu->env;
|
CPUPPCState *env = &cpu->env;
|
||||||
PPCVirtualHypervisorClass *vhc =
|
PPCVirtualHypervisorClass *vhc;
|
||||||
PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp);
|
|
||||||
hwaddr raddr, pte_addr;
|
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;
|
int page_size, fault_cause = 0;
|
||||||
|
ppc_v3_pate_t pate;
|
||||||
|
|
||||||
/* Handle Real Mode */
|
/* Handle Real Mode */
|
||||||
if (msr_dr == 0) {
|
if (msr_dr == 0) {
|
||||||
@ -273,16 +317,31 @@ hwaddr ppc_radix64_get_phys_page_debug(PowerPCCPU *cpu, target_ulong eaddr)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Get Process Table */
|
/* 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 */
|
/* Index Process Table by PID to Find Corresponding Process Table Entry */
|
||||||
offset = pid * sizeof(struct prtb_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) {
|
if (offset >= size) {
|
||||||
/* offset exceeds size of the process table */
|
/* offset exceeds size of the process table */
|
||||||
return -1;
|
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 */
|
/* Walk Radix Tree from Process Table Entry to Convert EA to RA */
|
||||||
page_size = PRTBE_R_GET_RTS(prtbe0);
|
page_size = PRTBE_R_GET_RTS(prtbe0);
|
||||||
|
@ -12,8 +12,8 @@
|
|||||||
#define R_EADDR_QUADRANT3 0xC000000000000000
|
#define R_EADDR_QUADRANT3 0xC000000000000000
|
||||||
|
|
||||||
/* Radix Partition Table Entry Fields */
|
/* Radix Partition Table Entry Fields */
|
||||||
#define PATBE1_R_PRTB 0x0FFFFFFFFFFFF000
|
#define PATE1_R_PRTB 0x0FFFFFFFFFFFF000
|
||||||
#define PATBE1_R_PRTS 0x000000000000001F
|
#define PATE1_R_PRTS 0x000000000000001F
|
||||||
|
|
||||||
/* Radix Process Table Entry Fields */
|
/* Radix Process Table Entry Fields */
|
||||||
#define PRTBE_R_GET_RTS(rts) \
|
#define PRTBE_R_GET_RTS(rts) \
|
||||||
|
@ -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));
|
dump_slb(f, cpu_fprintf, ppc_env_get_cpu(env));
|
||||||
break;
|
break;
|
||||||
case POWERPC_MMU_3_00:
|
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 */
|
/* TODO - Unsupported */
|
||||||
} else {
|
} else {
|
||||||
dump_slb(f, cpu_fprintf, ppc_env_get_cpu(env));
|
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:
|
case POWERPC_MMU_2_07:
|
||||||
return ppc_hash64_get_phys_page_debug(cpu, addr);
|
return ppc_hash64_get_phys_page_debug(cpu, addr);
|
||||||
case POWERPC_MMU_3_00:
|
case POWERPC_MMU_3_00:
|
||||||
if (ppc64_radix_guest(ppc_env_get_cpu(env))) {
|
return ppc64_v3_get_phys_page_debug(cpu, addr);
|
||||||
return ppc_radix64_get_phys_page_debug(cpu, addr);
|
|
||||||
} else {
|
|
||||||
return ppc_hash64_get_phys_page_debug(cpu, addr);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
case POWERPC_MMU_32B:
|
case POWERPC_MMU_32B:
|
||||||
|
@ -3566,7 +3566,8 @@ static void gen_doze(DisasContext *ctx)
|
|||||||
t = tcg_const_i32(PPC_PM_DOZE);
|
t = tcg_const_i32(PPC_PM_DOZE);
|
||||||
gen_helper_pminsn(cpu_env, t);
|
gen_helper_pminsn(cpu_env, t);
|
||||||
tcg_temp_free_i32(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 /* defined(CONFIG_USER_ONLY) */
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3581,13 +3582,25 @@ static void gen_nap(DisasContext *ctx)
|
|||||||
t = tcg_const_i32(PPC_PM_NAP);
|
t = tcg_const_i32(PPC_PM_NAP);
|
||||||
gen_helper_pminsn(cpu_env, t);
|
gen_helper_pminsn(cpu_env, t);
|
||||||
tcg_temp_free_i32(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 /* defined(CONFIG_USER_ONLY) */
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gen_stop(DisasContext *ctx)
|
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)
|
static void gen_sleep(DisasContext *ctx)
|
||||||
@ -3601,7 +3614,8 @@ static void gen_sleep(DisasContext *ctx)
|
|||||||
t = tcg_const_i32(PPC_PM_SLEEP);
|
t = tcg_const_i32(PPC_PM_SLEEP);
|
||||||
gen_helper_pminsn(cpu_env, t);
|
gen_helper_pminsn(cpu_env, t);
|
||||||
tcg_temp_free_i32(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 /* defined(CONFIG_USER_ONLY) */
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3616,7 +3630,8 @@ static void gen_rvwinkle(DisasContext *ctx)
|
|||||||
t = tcg_const_i32(PPC_PM_RVWINKLE);
|
t = tcg_const_i32(PPC_PM_RVWINKLE);
|
||||||
gen_helper_pminsn(cpu_env, t);
|
gen_helper_pminsn(cpu_env, t);
|
||||||
tcg_temp_free_i32(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 /* defined(CONFIG_USER_ONLY) */
|
||||||
}
|
}
|
||||||
#endif /* #if defined(TARGET_PPC64) */
|
#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 defined(TARGET_PPC64)
|
||||||
if (env->excp_model == POWERPC_EXCP_POWER7 ||
|
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",
|
cpu_fprintf(f, "HSRR0 " TARGET_FMT_lx " HSRR1 " TARGET_FMT_lx "\n",
|
||||||
env->spr[SPR_HSRR0], env->spr[SPR_HSRR1]);
|
env->spr[SPR_HSRR0], env->spr[SPR_HSRR1]);
|
||||||
}
|
}
|
||||||
|
@ -408,6 +408,11 @@ static void spr_write_pidr(DisasContext *ctx, int sprn, int gprn)
|
|||||||
gen_helper_store_pidr(cpu_env, cpu_gpr[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)
|
static void spr_read_hior(DisasContext *ctx, int gprn, int sprn)
|
||||||
{
|
{
|
||||||
tcg_gen_ld_tl(cpu_gpr[gprn], cpu_env, offsetof(CPUPPCState, excp_prefix));
|
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
|
#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
|
#endif
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
@ -7876,7 +7890,7 @@ static void gen_spr_book3s_ids(CPUPPCState *env)
|
|||||||
spr_register_hv(env, SPR_LPIDR, "LPIDR",
|
spr_register_hv(env, SPR_LPIDR, "LPIDR",
|
||||||
SPR_NOACCESS, SPR_NOACCESS,
|
SPR_NOACCESS, SPR_NOACCESS,
|
||||||
SPR_NOACCESS, SPR_NOACCESS,
|
SPR_NOACCESS, SPR_NOACCESS,
|
||||||
&spr_read_generic, &spr_write_generic,
|
&spr_read_generic, &spr_write_lpidr,
|
||||||
0x00000000);
|
0x00000000);
|
||||||
spr_register_hv(env, SPR_HFSCR, "HFSCR",
|
spr_register_hv(env, SPR_HFSCR, "HFSCR",
|
||||||
SPR_NOACCESS, SPR_NOACCESS,
|
SPR_NOACCESS, SPR_NOACCESS,
|
||||||
@ -8783,8 +8797,8 @@ static void init_proc_POWER9(CPUPPCState *env)
|
|||||||
env->icache_line_size = 128;
|
env->icache_line_size = 128;
|
||||||
|
|
||||||
/* Allocate hardware IRQ controller */
|
/* Allocate hardware IRQ controller */
|
||||||
init_excp_POWER8(env);
|
init_excp_POWER9(env);
|
||||||
ppcPOWER7_irq_init(ppc_env_get_cpu(env));
|
ppcPOWER9_irq_init(ppc_env_get_cpu(env));
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool ppc_pvr_match_power9(PowerPCCPUClass *pcc, uint32_t pvr)
|
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;
|
CPUPPCState *env = &cpu->env;
|
||||||
|
|
||||||
if (cs->halted) {
|
if (cs->halted) {
|
||||||
|
uint64_t psscr = env->spr[SPR_PSSCR];
|
||||||
|
|
||||||
if (!(cs->interrupt_request & CPU_INTERRUPT_HARD)) {
|
if (!(cs->interrupt_request & CPU_INTERRUPT_HARD)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* If EC is clear, just return true on any pending interrupt */
|
||||||
|
if (!(psscr & PSSCR_EC)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
/* External Exception */
|
/* External Exception */
|
||||||
if ((env->pending_interrupts & (1u << PPC_INTERRUPT_EXT)) &&
|
if ((env->pending_interrupts & (1u << PPC_INTERRUPT_EXT)) &&
|
||||||
(env->spr[SPR_LPCR] & LPCR_EEE)) {
|
(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 */
|
/* Decrementer Exception */
|
||||||
if ((env->pending_interrupts & (1u << PPC_INTERRUPT_DECR)) &&
|
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)) {
|
(env->spr[SPR_LPCR] & LPCR_HDEE)) {
|
||||||
return true;
|
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)) {
|
if (env->pending_interrupts & (1u << PPC_INTERRUPT_RESET)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -8898,8 +8927,8 @@ POWERPC_FAMILY(POWER9)(ObjectClass *oc, void *data)
|
|||||||
pcc->hash64_opts = &ppc_hash64_opts_POWER7;
|
pcc->hash64_opts = &ppc_hash64_opts_POWER7;
|
||||||
pcc->radix_page_info = &POWER9_radix_page_info;
|
pcc->radix_page_info = &POWER9_radix_page_info;
|
||||||
#endif
|
#endif
|
||||||
pcc->excp_model = POWERPC_EXCP_POWER8;
|
pcc->excp_model = POWERPC_EXCP_POWER9;
|
||||||
pcc->bus_model = PPC_FLAGS_INPUT_POWER7;
|
pcc->bus_model = PPC_FLAGS_INPUT_POWER9;
|
||||||
pcc->bfd_mach = bfd_mach_ppc64;
|
pcc->bfd_mach = bfd_mach_ppc64;
|
||||||
pcc->flags = POWERPC_FLAG_VRE | POWERPC_FLAG_SE |
|
pcc->flags = POWERPC_FLAG_VRE | POWERPC_FLAG_SE |
|
||||||
POWERPC_FLAG_BE | POWERPC_FLAG_PMM |
|
POWERPC_FLAG_BE | POWERPC_FLAG_PMM |
|
||||||
|
@ -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-$(CONFIG_ISA_IPMI_BT) += tests/ipmi-bt-test$(EXESUF)
|
||||||
check-qtest-i386-y += tests/i440fx-test$(EXESUF)
|
check-qtest-i386-y += tests/i440fx-test$(EXESUF)
|
||||||
check-qtest-i386-y += tests/fw_cfg-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-y += tests/drive_del-test$(EXESUF)
|
||||||
check-qtest-i386-$(CONFIG_WDT_IB700) += tests/wdt_ib700-test$(EXESUF)
|
check-qtest-i386-$(CONFIG_WDT_IB700) += tests/wdt_ib700-test$(EXESUF)
|
||||||
check-qtest-i386-y += tests/tco-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-y += $(check-qtest-ppc-y)
|
||||||
check-qtest-ppc64-$(CONFIG_PSERIES) += tests/spapr-phb-test$(EXESUF)
|
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-$(CONFIG_POWERNV) += tests/pnv-xscom-test$(EXESUF)
|
||||||
check-qtest-ppc64-y += tests/migration-test$(EXESUF)
|
check-qtest-ppc64-y += tests/migration-test$(EXESUF)
|
||||||
check-qtest-ppc64-$(CONFIG_PSERIES) += tests/rtas-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-mirror$(EXESUF)
|
||||||
check-qtest-s390x-$(CONFIG_POSIX) += tests/test-filter-redirector$(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/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/virtio-ccw-test$(EXESUF)
|
||||||
check-qtest-s390x-y += tests/cpu-plug-test$(EXESUF)
|
check-qtest-s390x-y += tests/cpu-plug-test$(EXESUF)
|
||||||
check-qtest-s390x-y += tests/migration-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/qom-test$(EXESUF): tests/qom-test.o
|
||||||
tests/test-hmp$(EXESUF): tests/test-hmp.o
|
tests/test-hmp$(EXESUF): tests/test-hmp.o
|
||||||
tests/machine-none-test$(EXESUF): tests/machine-none-test.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/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/nvme-test$(EXESUF): tests/nvme-test.o $(libqos-pc-obj-y)
|
||||||
tests/pvpanic-test$(EXESUF): tests/pvpanic-test.o
|
tests/pvpanic-test$(EXESUF): tests/pvpanic-test.o
|
||||||
|
178
tests/device-plug-test.c
Normal file
178
tests/device-plug-test.c
Normal 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();
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user