mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
Merge remote-tracking branch 'remotes/dgibson/tags/ppc-for-6.2-20211021' into staging
ppc patch queue 2021-10-21 Here's the next batch of ppc target related patches for qemu-6.2. Highlights are: * Some fixes and minimal tests for old embedded ppc platforms * The beginnings of PMU emulation in TCG from Daniel Barboza * Some improvements to the pegasos2 platform * A number of TCG bugfixes from the folks at the El Dorado Institute * A few other assorted bugfixes and cleanups # gpg: Signature made Wed 20 Oct 2021 09:19:04 PM PDT # gpg: using RSA key 75F46586AE61A66CC44E87DC6C38CACA20D9B392 # gpg: Good signature from "David Gibson <david@gibson.dropbear.id.au>" [full] # gpg: aka "David Gibson (kernel.org) <dwg@kernel.org>" [unknown] # gpg: aka "David Gibson (Red Hat) <dgibson@redhat.com>" [full] # gpg: aka "David Gibson (ozlabs.org) <dgibson@ozlabs.org>" [full] * remotes/dgibson/tags/ppc-for-6.2-20211021: (25 commits) hw/ppc/ppc4xx_pci: Fix ppc4xx_pci_map_irq() for recent Linux kernels target/ppc: adding user read/write functions for PMCs target/ppc: add user read/write functions for MMCR2 target/ppc: add user read/write functions for MMCR0 target/ppc: add MMCR0 PMCC bits to hflags target/ppc: Filter mtmsr[d] input before setting MSR tests/acceptance: Add a test for the bamboo ppc board ppc/pegasos2: Implement power-off RTAS function with VOF ppc/pegasos2: Add constants for PCI config addresses ppc/pegasos2: Access MV64361 registers via their memory region ppc/pegasos2: Implement get-time-of-day RTAS function with VOF ppc/pegasos2: Warn when using VOF but no kernel is specified ppc/pegasos2: Restrict memory to 2 gigabytes target/ppc: Fix XER access in monitor linux-user: Fix XER access in ppc version of elf_core_copy_regs target/ppc: Fix XER access in gdbstub linux-user/ppc: Fix XER access in save/restore_user_regs tests/acceptance: Add tests for the ppc405 boards hw/ppc: Fix iothread locking in the 405 code spapr/xive: Use xive_esb_rw() to trigger interrupts ... Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
This commit is contained in:
@@ -1245,6 +1245,7 @@ Bamboo
|
||||
L: qemu-ppc@nongnu.org
|
||||
S: Orphan
|
||||
F: hw/ppc/ppc440_bamboo.c
|
||||
F: tests/acceptance/ppc_bamboo.py
|
||||
|
||||
e500
|
||||
L: qemu-ppc@nongnu.org
|
||||
|
||||
@@ -185,7 +185,7 @@ static void spapr_xive_pic_print_info(SpaprXive *xive, Monitor *mon)
|
||||
xive_source_irq_is_lsi(xsrc, i) ? "LSI" : "MSI",
|
||||
pq & XIVE_ESB_VAL_P ? 'P' : '-',
|
||||
pq & XIVE_ESB_VAL_Q ? 'Q' : '-',
|
||||
xsrc->status[i] & XIVE_STATUS_ASSERTED ? 'A' : ' ',
|
||||
xive_source_is_asserted(xsrc, i) ? 'A' : ' ',
|
||||
xive_eas_is_masked(eas) ? "M" : " ",
|
||||
(int) xive_get_field64(EAS_END_DATA, eas->w));
|
||||
|
||||
|
||||
@@ -242,7 +242,7 @@ int kvmppc_xive_source_reset_one(XiveSource *xsrc, int srcno, Error **errp)
|
||||
|
||||
if (xive_source_irq_is_lsi(xsrc, srcno)) {
|
||||
state |= KVM_XIVE_LEVEL_SENSITIVE;
|
||||
if (xsrc->status[srcno] & XIVE_STATUS_ASSERTED) {
|
||||
if (xive_source_is_asserted(xsrc, srcno)) {
|
||||
state |= KVM_XIVE_LEVEL_ASSERTED;
|
||||
}
|
||||
}
|
||||
@@ -301,9 +301,7 @@ static uint8_t xive_esb_read(XiveSource *xsrc, int srcno, uint32_t offset)
|
||||
|
||||
static void kvmppc_xive_esb_trigger(XiveSource *xsrc, int srcno)
|
||||
{
|
||||
uint64_t *addr = xsrc->esb_mmap + xive_source_esb_page(xsrc, srcno);
|
||||
|
||||
*addr = 0x0;
|
||||
xive_esb_rw(xsrc, srcno, 0, 0, true);
|
||||
}
|
||||
|
||||
uint64_t kvmppc_xive_esb_rw(XiveSource *xsrc, int srcno, uint32_t offset,
|
||||
@@ -321,7 +319,7 @@ uint64_t kvmppc_xive_esb_rw(XiveSource *xsrc, int srcno, uint32_t offset,
|
||||
if (xive_source_irq_is_lsi(xsrc, srcno) &&
|
||||
offset == XIVE_ESB_LOAD_EOI) {
|
||||
xive_esb_read(xsrc, srcno, XIVE_ESB_SET_PQ_00);
|
||||
if (xsrc->status[srcno] & XIVE_STATUS_ASSERTED) {
|
||||
if (xive_source_is_asserted(xsrc, srcno)) {
|
||||
kvmppc_xive_esb_trigger(xsrc, srcno);
|
||||
}
|
||||
return 0;
|
||||
@@ -359,11 +357,7 @@ void kvmppc_xive_source_set_irq(void *opaque, int srcno, int val)
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (val) {
|
||||
xsrc->status[srcno] |= XIVE_STATUS_ASSERTED;
|
||||
} else {
|
||||
xsrc->status[srcno] &= ~XIVE_STATUS_ASSERTED;
|
||||
}
|
||||
xive_source_set_asserted(xsrc, srcno, val);
|
||||
}
|
||||
|
||||
kvmppc_xive_esb_trigger(xsrc, srcno);
|
||||
|
||||
+4
-4
@@ -875,7 +875,7 @@ static bool xive_source_lsi_trigger(XiveSource *xsrc, uint32_t srcno)
|
||||
{
|
||||
uint8_t old_pq = xive_source_esb_get(xsrc, srcno);
|
||||
|
||||
xsrc->status[srcno] |= XIVE_STATUS_ASSERTED;
|
||||
xive_source_set_asserted(xsrc, srcno, true);
|
||||
|
||||
switch (old_pq) {
|
||||
case XIVE_ESB_RESET:
|
||||
@@ -923,7 +923,7 @@ static bool xive_source_esb_eoi(XiveSource *xsrc, uint32_t srcno)
|
||||
* notification
|
||||
*/
|
||||
if (xive_source_irq_is_lsi(xsrc, srcno) &&
|
||||
xsrc->status[srcno] & XIVE_STATUS_ASSERTED) {
|
||||
xive_source_is_asserted(xsrc, srcno)) {
|
||||
ret = xive_source_lsi_trigger(xsrc, srcno);
|
||||
}
|
||||
|
||||
@@ -1104,7 +1104,7 @@ void xive_source_set_irq(void *opaque, int srcno, int val)
|
||||
if (val) {
|
||||
notify = xive_source_lsi_trigger(xsrc, srcno);
|
||||
} else {
|
||||
xsrc->status[srcno] &= ~XIVE_STATUS_ASSERTED;
|
||||
xive_source_set_asserted(xsrc, srcno, false);
|
||||
}
|
||||
} else {
|
||||
if (val) {
|
||||
@@ -1133,7 +1133,7 @@ void xive_source_pic_print_info(XiveSource *xsrc, uint32_t offset, Monitor *mon)
|
||||
xive_source_irq_is_lsi(xsrc, i) ? "LSI" : "MSI",
|
||||
pq & XIVE_ESB_VAL_P ? 'P' : '-',
|
||||
pq & XIVE_ESB_VAL_Q ? 'Q' : '-',
|
||||
xsrc->status[i] & XIVE_STATUS_ASSERTED ? 'A' : ' ');
|
||||
xive_source_is_asserted(xsrc, i) ? 'A' : ' ');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -869,6 +869,7 @@ static void mv64361_realize(DeviceState *dev, Error **errp)
|
||||
s->base_addr_enable = 0x1fffff;
|
||||
memory_region_init_io(&s->regs, OBJECT(s), &mv64361_ops, s,
|
||||
TYPE_MV64361, 0x10000);
|
||||
sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->regs);
|
||||
for (i = 0; i < 2; i++) {
|
||||
g_autofree char *name = g_strdup_printf("pcihost%d", i);
|
||||
object_initialize_child(OBJECT(dev), name, &s->pci[i],
|
||||
|
||||
+98
-60
@@ -22,6 +22,7 @@
|
||||
#include "hw/i2c/smbus_eeprom.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "sysemu/reset.h"
|
||||
#include "sysemu/runstate.h"
|
||||
#include "hw/boards.h"
|
||||
#include "hw/loader.h"
|
||||
#include "hw/fw-path-provider.h"
|
||||
@@ -31,6 +32,8 @@
|
||||
#include "sysemu/kvm.h"
|
||||
#include "kvm_ppc.h"
|
||||
#include "exec/address-spaces.h"
|
||||
#include "qom/qom-qobject.h"
|
||||
#include "qapi/qmp/qdict.h"
|
||||
#include "trace.h"
|
||||
#include "qemu/datadir.h"
|
||||
#include "sysemu/device_tree.h"
|
||||
@@ -52,11 +55,13 @@
|
||||
|
||||
#define BUS_FREQ_HZ 133333333
|
||||
|
||||
#define PCI0_CFG_ADDR 0xcf8
|
||||
#define PCI0_MEM_BASE 0xc0000000
|
||||
#define PCI0_MEM_SIZE 0x20000000
|
||||
#define PCI0_IO_BASE 0xf8000000
|
||||
#define PCI0_IO_SIZE 0x10000
|
||||
|
||||
#define PCI1_CFG_ADDR 0xc78
|
||||
#define PCI1_MEM_BASE 0x80000000
|
||||
#define PCI1_MEM_SIZE 0x40000000
|
||||
#define PCI1_IO_BASE 0xfe000000
|
||||
@@ -117,6 +122,10 @@ static void pegasos2_init(MachineState *machine)
|
||||
qemu_register_reset(pegasos2_cpu_reset, pm->cpu);
|
||||
|
||||
/* RAM */
|
||||
if (machine->ram_size > 2 * GiB) {
|
||||
error_report("RAM size more than 2 GiB is not supported");
|
||||
exit(1);
|
||||
}
|
||||
memory_region_add_subregion(get_system_memory(), 0, machine->ram);
|
||||
|
||||
/* allocate and load firmware */
|
||||
@@ -190,62 +199,58 @@ static void pegasos2_init(MachineState *machine)
|
||||
if (!pm->vof) {
|
||||
warn_report("Option -kernel may be ineffective with -bios.");
|
||||
}
|
||||
} else if (pm->vof) {
|
||||
warn_report("Using Virtual OpenFirmware but no -kernel option.");
|
||||
}
|
||||
|
||||
if (!pm->vof && machine->kernel_cmdline && machine->kernel_cmdline[0]) {
|
||||
warn_report("Option -append may be ineffective with -bios.");
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t pegasos2_pci_config_read(AddressSpace *as, int bus,
|
||||
static uint32_t pegasos2_mv_reg_read(Pegasos2MachineState *pm,
|
||||
uint32_t addr, uint32_t len)
|
||||
{
|
||||
MemoryRegion *r = sysbus_mmio_get_region(SYS_BUS_DEVICE(pm->mv), 0);
|
||||
uint64_t val = 0xffffffffULL;
|
||||
memory_region_dispatch_read(r, addr, &val, size_memop(len) | MO_LE,
|
||||
MEMTXATTRS_UNSPECIFIED);
|
||||
return val;
|
||||
}
|
||||
|
||||
static void pegasos2_mv_reg_write(Pegasos2MachineState *pm, uint32_t addr,
|
||||
uint32_t len, uint32_t val)
|
||||
{
|
||||
MemoryRegion *r = sysbus_mmio_get_region(SYS_BUS_DEVICE(pm->mv), 0);
|
||||
memory_region_dispatch_write(r, addr, val, size_memop(len) | MO_LE,
|
||||
MEMTXATTRS_UNSPECIFIED);
|
||||
}
|
||||
|
||||
static uint32_t pegasos2_pci_config_read(Pegasos2MachineState *pm, int bus,
|
||||
uint32_t addr, uint32_t len)
|
||||
{
|
||||
hwaddr pcicfg = (bus ? 0xf1000c78 : 0xf1000cf8);
|
||||
uint32_t val = 0xffffffff;
|
||||
hwaddr pcicfg = bus ? PCI1_CFG_ADDR : PCI0_CFG_ADDR;
|
||||
uint64_t val = 0xffffffffULL;
|
||||
|
||||
stl_le_phys(as, pcicfg, addr | BIT(31));
|
||||
switch (len) {
|
||||
case 4:
|
||||
val = ldl_le_phys(as, pcicfg + 4);
|
||||
break;
|
||||
case 2:
|
||||
val = lduw_le_phys(as, pcicfg + 4);
|
||||
break;
|
||||
case 1:
|
||||
val = ldub_phys(as, pcicfg + 4);
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid length\n", __func__);
|
||||
break;
|
||||
if (len <= 4) {
|
||||
pegasos2_mv_reg_write(pm, pcicfg, 4, addr | BIT(31));
|
||||
val = pegasos2_mv_reg_read(pm, pcicfg + 4, len);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
static void pegasos2_pci_config_write(AddressSpace *as, int bus, uint32_t addr,
|
||||
uint32_t len, uint32_t val)
|
||||
static void pegasos2_pci_config_write(Pegasos2MachineState *pm, int bus,
|
||||
uint32_t addr, uint32_t len, uint32_t val)
|
||||
{
|
||||
hwaddr pcicfg = (bus ? 0xf1000c78 : 0xf1000cf8);
|
||||
hwaddr pcicfg = bus ? PCI1_CFG_ADDR : PCI0_CFG_ADDR;
|
||||
|
||||
stl_le_phys(as, pcicfg, addr | BIT(31));
|
||||
switch (len) {
|
||||
case 4:
|
||||
stl_le_phys(as, pcicfg + 4, val);
|
||||
break;
|
||||
case 2:
|
||||
stw_le_phys(as, pcicfg + 4, val);
|
||||
break;
|
||||
case 1:
|
||||
stb_phys(as, pcicfg + 4, val);
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid length\n", __func__);
|
||||
break;
|
||||
}
|
||||
pegasos2_mv_reg_write(pm, pcicfg, 4, addr | BIT(31));
|
||||
pegasos2_mv_reg_write(pm, pcicfg + 4, len, val);
|
||||
}
|
||||
|
||||
static void pegasos2_machine_reset(MachineState *machine)
|
||||
{
|
||||
Pegasos2MachineState *pm = PEGASOS2_MACHINE(machine);
|
||||
AddressSpace *as = CPU(pm->cpu)->as;
|
||||
void *fdt;
|
||||
uint64_t d[2];
|
||||
int sz;
|
||||
@@ -256,51 +261,51 @@ static void pegasos2_machine_reset(MachineState *machine)
|
||||
}
|
||||
|
||||
/* Otherwise, set up devices that board firmware would normally do */
|
||||
stl_le_phys(as, 0xf1000000, 0x28020ff);
|
||||
stl_le_phys(as, 0xf1000278, 0xa31fc);
|
||||
stl_le_phys(as, 0xf100f300, 0x11ff0400);
|
||||
stl_le_phys(as, 0xf100f10c, 0x80000000);
|
||||
stl_le_phys(as, 0xf100001c, 0x8000000);
|
||||
pegasos2_pci_config_write(as, 0, PCI_COMMAND, 2, PCI_COMMAND_IO |
|
||||
pegasos2_mv_reg_write(pm, 0, 4, 0x28020ff);
|
||||
pegasos2_mv_reg_write(pm, 0x278, 4, 0xa31fc);
|
||||
pegasos2_mv_reg_write(pm, 0xf300, 4, 0x11ff0400);
|
||||
pegasos2_mv_reg_write(pm, 0xf10c, 4, 0x80000000);
|
||||
pegasos2_mv_reg_write(pm, 0x1c, 4, 0x8000000);
|
||||
pegasos2_pci_config_write(pm, 0, PCI_COMMAND, 2, PCI_COMMAND_IO |
|
||||
PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
|
||||
pegasos2_pci_config_write(as, 1, PCI_COMMAND, 2, PCI_COMMAND_IO |
|
||||
pegasos2_pci_config_write(pm, 1, PCI_COMMAND, 2, PCI_COMMAND_IO |
|
||||
PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
|
||||
|
||||
pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 0) << 8) |
|
||||
pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 0) << 8) |
|
||||
PCI_INTERRUPT_LINE, 2, 0x9);
|
||||
pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 0) << 8) |
|
||||
pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 0) << 8) |
|
||||
0x50, 1, 0x2);
|
||||
|
||||
pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 1) << 8) |
|
||||
pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 1) << 8) |
|
||||
PCI_INTERRUPT_LINE, 2, 0x109);
|
||||
pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 1) << 8) |
|
||||
pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 1) << 8) |
|
||||
PCI_CLASS_PROG, 1, 0xf);
|
||||
pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 1) << 8) |
|
||||
pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 1) << 8) |
|
||||
0x40, 1, 0xb);
|
||||
pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 1) << 8) |
|
||||
pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 1) << 8) |
|
||||
0x50, 4, 0x17171717);
|
||||
pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 1) << 8) |
|
||||
pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 1) << 8) |
|
||||
PCI_COMMAND, 2, 0x87);
|
||||
|
||||
pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 2) << 8) |
|
||||
pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 2) << 8) |
|
||||
PCI_INTERRUPT_LINE, 2, 0x409);
|
||||
|
||||
pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 3) << 8) |
|
||||
pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 3) << 8) |
|
||||
PCI_INTERRUPT_LINE, 2, 0x409);
|
||||
|
||||
pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 4) << 8) |
|
||||
pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 4) << 8) |
|
||||
PCI_INTERRUPT_LINE, 2, 0x9);
|
||||
pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 4) << 8) |
|
||||
pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 4) << 8) |
|
||||
0x48, 4, 0xf00);
|
||||
pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 4) << 8) |
|
||||
pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 4) << 8) |
|
||||
0x40, 4, 0x558020);
|
||||
pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 4) << 8) |
|
||||
pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 4) << 8) |
|
||||
0x90, 4, 0xd00);
|
||||
|
||||
pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 5) << 8) |
|
||||
pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 5) << 8) |
|
||||
PCI_INTERRUPT_LINE, 2, 0x309);
|
||||
|
||||
pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 6) << 8) |
|
||||
pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 6) << 8) |
|
||||
PCI_INTERRUPT_LINE, 2, 0x309);
|
||||
|
||||
/* Device tree and VOF set up */
|
||||
@@ -362,6 +367,29 @@ static target_ulong pegasos2_rtas(PowerPCCPU *cpu, Pegasos2MachineState *pm,
|
||||
return H_PARAMETER;
|
||||
}
|
||||
switch (token) {
|
||||
case RTAS_GET_TIME_OF_DAY:
|
||||
{
|
||||
QObject *qo = object_property_get_qobject(qdev_get_machine(),
|
||||
"rtc-time", &error_fatal);
|
||||
QDict *qd = qobject_to(QDict, qo);
|
||||
|
||||
if (nargs != 0 || nrets != 8 || !qd) {
|
||||
stl_be_phys(as, rets, -1);
|
||||
qobject_unref(qo);
|
||||
return H_PARAMETER;
|
||||
}
|
||||
|
||||
stl_be_phys(as, rets, 0);
|
||||
stl_be_phys(as, rets + 4, qdict_get_int(qd, "tm_year") + 1900);
|
||||
stl_be_phys(as, rets + 8, qdict_get_int(qd, "tm_mon") + 1);
|
||||
stl_be_phys(as, rets + 12, qdict_get_int(qd, "tm_mday"));
|
||||
stl_be_phys(as, rets + 16, qdict_get_int(qd, "tm_hour"));
|
||||
stl_be_phys(as, rets + 20, qdict_get_int(qd, "tm_min"));
|
||||
stl_be_phys(as, rets + 24, qdict_get_int(qd, "tm_sec"));
|
||||
stl_be_phys(as, rets + 28, 0);
|
||||
qobject_unref(qo);
|
||||
return H_SUCCESS;
|
||||
}
|
||||
case RTAS_READ_PCI_CONFIG:
|
||||
{
|
||||
uint32_t addr, len, val;
|
||||
@@ -372,7 +400,7 @@ static target_ulong pegasos2_rtas(PowerPCCPU *cpu, Pegasos2MachineState *pm,
|
||||
}
|
||||
addr = ldl_be_phys(as, args);
|
||||
len = ldl_be_phys(as, args + 4);
|
||||
val = pegasos2_pci_config_read(as, !(addr >> 24),
|
||||
val = pegasos2_pci_config_read(pm, !(addr >> 24),
|
||||
addr & 0x0fffffff, len);
|
||||
stl_be_phys(as, rets, 0);
|
||||
stl_be_phys(as, rets + 4, val);
|
||||
@@ -389,7 +417,7 @@ static target_ulong pegasos2_rtas(PowerPCCPU *cpu, Pegasos2MachineState *pm,
|
||||
addr = ldl_be_phys(as, args);
|
||||
len = ldl_be_phys(as, args + 4);
|
||||
val = ldl_be_phys(as, args + 8);
|
||||
pegasos2_pci_config_write(as, !(addr >> 24),
|
||||
pegasos2_pci_config_write(pm, !(addr >> 24),
|
||||
addr & 0x0fffffff, len, val);
|
||||
stl_be_phys(as, rets, 0);
|
||||
return H_SUCCESS;
|
||||
@@ -402,6 +430,16 @@ static target_ulong pegasos2_rtas(PowerPCCPU *cpu, Pegasos2MachineState *pm,
|
||||
qemu_log_mask(LOG_UNIMP, "%c", ldl_be_phys(as, args));
|
||||
stl_be_phys(as, rets, 0);
|
||||
return H_SUCCESS;
|
||||
case RTAS_POWER_OFF:
|
||||
{
|
||||
if (nargs != 2 || nrets != 1) {
|
||||
stl_be_phys(as, rets, -1);
|
||||
return H_PARAMETER;
|
||||
}
|
||||
qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN);
|
||||
stl_be_phys(as, rets, 0);
|
||||
return H_SUCCESS;
|
||||
}
|
||||
default:
|
||||
qemu_log_mask(LOG_UNIMP, "Unknown RTAS token %u (args=%u, rets=%u)\n",
|
||||
token, nargs, nrets);
|
||||
|
||||
+5
-1
@@ -336,6 +336,8 @@ void store_40x_dbcr0(CPUPPCState *env, uint32_t val)
|
||||
{
|
||||
PowerPCCPU *cpu = env_archcpu(env);
|
||||
|
||||
qemu_mutex_lock_iothread();
|
||||
|
||||
switch ((val >> 28) & 0x3) {
|
||||
case 0x0:
|
||||
/* No action */
|
||||
@@ -353,6 +355,8 @@ void store_40x_dbcr0(CPUPPCState *env, uint32_t val)
|
||||
ppc40x_system_reset(cpu);
|
||||
break;
|
||||
}
|
||||
|
||||
qemu_mutex_unlock_iothread();
|
||||
}
|
||||
|
||||
/* PowerPC 40x internal IRQ controller */
|
||||
@@ -848,7 +852,7 @@ static void __cpu_ppc_store_decr(PowerPCCPU *cpu, uint64_t *nextp,
|
||||
* On MSB edge based DEC implementations the MSB going from 0 -> 1 triggers
|
||||
* an edge interrupt, so raise it here too.
|
||||
*/
|
||||
if ((signed_value < 3) ||
|
||||
if ((value < 3) ||
|
||||
((tb_env->flags & PPC_DECR_UNDERFLOW_LEVEL) && signed_value < 0) ||
|
||||
((tb_env->flags & PPC_DECR_UNDERFLOW_TRIGGERED) && signed_value < 0
|
||||
&& signed_decr >= 0)) {
|
||||
|
||||
+5
-3
@@ -48,12 +48,14 @@ OBJECT_DECLARE_SIMPLE_TYPE(PPC4xxPCIState, PPC4xx_PCI_HOST_BRIDGE)
|
||||
#define PPC4xx_PCI_NR_PMMS 3
|
||||
#define PPC4xx_PCI_NR_PTMS 2
|
||||
|
||||
#define PPC4xx_PCI_NUM_DEVS 5
|
||||
|
||||
struct PPC4xxPCIState {
|
||||
PCIHostState parent_obj;
|
||||
|
||||
struct PCIMasterMap pmm[PPC4xx_PCI_NR_PMMS];
|
||||
struct PCITargetMap ptm[PPC4xx_PCI_NR_PTMS];
|
||||
qemu_irq irq[PCI_NUM_PINS];
|
||||
qemu_irq irq[PPC4xx_PCI_NUM_DEVS];
|
||||
|
||||
MemoryRegion container;
|
||||
MemoryRegion iomem;
|
||||
@@ -246,7 +248,7 @@ static int ppc4xx_pci_map_irq(PCIDevice *pci_dev, int irq_num)
|
||||
|
||||
trace_ppc4xx_pci_map_irq(pci_dev->devfn, irq_num, slot);
|
||||
|
||||
return slot - 1;
|
||||
return slot > 0 ? slot - 1 : PPC4xx_PCI_NUM_DEVS - 1;
|
||||
}
|
||||
|
||||
static void ppc4xx_pci_set_irq(void *opaque, int irq_num, int level)
|
||||
@@ -254,7 +256,7 @@ static void ppc4xx_pci_set_irq(void *opaque, int irq_num, int level)
|
||||
qemu_irq *pci_irqs = opaque;
|
||||
|
||||
trace_ppc4xx_pci_set_irq(irq_num);
|
||||
assert(irq_num >= 0);
|
||||
assert(irq_num >= 0 && irq_num < PPC4xx_PCI_NUM_DEVS);
|
||||
qemu_set_irq(pci_irqs[irq_num], level);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,25 +1,10 @@
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/cutils.h"
|
||||
#include "qapi/error.h"
|
||||
#include "sysemu/hw_accel.h"
|
||||
#include "sysemu/runstate.h"
|
||||
#include "qemu/log.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "cpu.h"
|
||||
#include "exec/exec-all.h"
|
||||
#include "helper_regs.h"
|
||||
#include "hw/ppc/spapr.h"
|
||||
#include "hw/ppc/spapr_cpu_core.h"
|
||||
#include "mmu-hash64.h"
|
||||
#include "cpu-models.h"
|
||||
#include "trace.h"
|
||||
#include "kvm_ppc.h"
|
||||
#include "hw/ppc/fdt.h"
|
||||
#include "hw/ppc/spapr_ovec.h"
|
||||
#include "mmu-book3s-v3.h"
|
||||
#include "hw/mem/memory-device.h"
|
||||
|
||||
static inline bool valid_ptex(PowerPCCPU *cpu, target_ulong ptex)
|
||||
{
|
||||
|
||||
@@ -286,6 +286,30 @@ uint8_t xive_esb_set(uint8_t *pq, uint8_t value);
|
||||
uint8_t xive_source_esb_get(XiveSource *xsrc, uint32_t srcno);
|
||||
uint8_t xive_source_esb_set(XiveSource *xsrc, uint32_t srcno, uint8_t pq);
|
||||
|
||||
/*
|
||||
* Source status helpers
|
||||
*/
|
||||
static inline void xive_source_set_status(XiveSource *xsrc, uint32_t srcno,
|
||||
uint8_t status, bool enable)
|
||||
{
|
||||
if (enable) {
|
||||
xsrc->status[srcno] |= status;
|
||||
} else {
|
||||
xsrc->status[srcno] &= ~status;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void xive_source_set_asserted(XiveSource *xsrc, uint32_t srcno,
|
||||
bool enable)
|
||||
{
|
||||
xive_source_set_status(xsrc, srcno, XIVE_STATUS_ASSERTED, enable);
|
||||
}
|
||||
|
||||
static inline bool xive_source_is_asserted(XiveSource *xsrc, uint32_t srcno)
|
||||
{
|
||||
return xsrc->status[srcno] & XIVE_STATUS_ASSERTED;
|
||||
}
|
||||
|
||||
void xive_source_pic_print_info(XiveSource *xsrc, uint32_t offset,
|
||||
Monitor *mon);
|
||||
|
||||
|
||||
@@ -901,7 +901,7 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUPPCState *en
|
||||
(*regs)[33] = tswapreg(env->msr);
|
||||
(*regs)[35] = tswapreg(env->ctr);
|
||||
(*regs)[36] = tswapreg(env->lr);
|
||||
(*regs)[37] = tswapreg(env->xer);
|
||||
(*regs)[37] = tswapreg(cpu_read_xer(env));
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(env->crf); i++) {
|
||||
ccr |= env->crf[i] << (32 - ((i + 1) * 4));
|
||||
|
||||
@@ -242,7 +242,7 @@ static void save_user_regs(CPUPPCState *env, struct target_mcontext *frame)
|
||||
__put_user(env->nip, &frame->mc_gregs[TARGET_PT_NIP]);
|
||||
__put_user(env->ctr, &frame->mc_gregs[TARGET_PT_CTR]);
|
||||
__put_user(env->lr, &frame->mc_gregs[TARGET_PT_LNK]);
|
||||
__put_user(env->xer, &frame->mc_gregs[TARGET_PT_XER]);
|
||||
__put_user(cpu_read_xer(env), &frame->mc_gregs[TARGET_PT_XER]);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(env->crf); i++) {
|
||||
ccr |= env->crf[i] << (32 - ((i + 1) * 4));
|
||||
@@ -315,6 +315,7 @@ static void restore_user_regs(CPUPPCState *env,
|
||||
{
|
||||
target_ulong save_r2 = 0;
|
||||
target_ulong msr;
|
||||
target_ulong xer;
|
||||
target_ulong ccr;
|
||||
|
||||
int i;
|
||||
@@ -330,9 +331,11 @@ static void restore_user_regs(CPUPPCState *env,
|
||||
__get_user(env->nip, &frame->mc_gregs[TARGET_PT_NIP]);
|
||||
__get_user(env->ctr, &frame->mc_gregs[TARGET_PT_CTR]);
|
||||
__get_user(env->lr, &frame->mc_gregs[TARGET_PT_LNK]);
|
||||
__get_user(env->xer, &frame->mc_gregs[TARGET_PT_XER]);
|
||||
__get_user(ccr, &frame->mc_gregs[TARGET_PT_CCR]);
|
||||
|
||||
__get_user(xer, &frame->mc_gregs[TARGET_PT_XER]);
|
||||
cpu_write_xer(env, xer);
|
||||
|
||||
__get_user(ccr, &frame->mc_gregs[TARGET_PT_CCR]);
|
||||
for (i = 0; i < ARRAY_SIZE(env->crf); i++) {
|
||||
env->crf[i] = (ccr >> (32 - ((i + 1) * 4))) & 0xf;
|
||||
}
|
||||
|
||||
+1
-1
@@ -27,7 +27,7 @@
|
||||
#include "helper_regs.h"
|
||||
#include "sysemu/tcg.h"
|
||||
|
||||
target_ulong cpu_read_xer(CPUPPCState *env)
|
||||
target_ulong cpu_read_xer(const CPUPPCState *env)
|
||||
{
|
||||
if (is_isa300(env)) {
|
||||
return env->xer | (env->so << XER_SO) |
|
||||
|
||||
+24
-1
@@ -314,6 +314,7 @@ typedef struct ppc_v3_pate_t {
|
||||
#define MSR_AP 23 /* Access privilege state on 602 hflags */
|
||||
#define MSR_VSX 23 /* Vector Scalar Extension (ISA 2.06 and later) x hflags */
|
||||
#define MSR_SA 22 /* Supervisor access mode on 602 hflags */
|
||||
#define MSR_S 22 /* Secure state */
|
||||
#define MSR_KEY 19 /* key bit on 603e */
|
||||
#define MSR_POW 18 /* Power management */
|
||||
#define MSR_TGPR 17 /* TGPR usage on 602/603 x */
|
||||
@@ -342,6 +343,26 @@ typedef struct ppc_v3_pate_t {
|
||||
#define MSR_RI 1 /* Recoverable interrupt 1 */
|
||||
#define MSR_LE 0 /* Little-endian mode 1 hflags */
|
||||
|
||||
/* PMU bits */
|
||||
#define MMCR0_FC PPC_BIT(32) /* Freeze Counters */
|
||||
#define MMCR0_PMAO PPC_BIT(56) /* Perf Monitor Alert Ocurred */
|
||||
#define MMCR0_PMAE PPC_BIT(37) /* Perf Monitor Alert Enable */
|
||||
#define MMCR0_EBE PPC_BIT(43) /* Perf Monitor EBB Enable */
|
||||
#define MMCR0_FCECE PPC_BIT(38) /* FC on Enabled Cond or Event */
|
||||
#define MMCR0_PMCC0 PPC_BIT(44) /* PMC Control bit 0 */
|
||||
#define MMCR0_PMCC1 PPC_BIT(45) /* PMC Control bit 1 */
|
||||
/* MMCR0 userspace r/w mask */
|
||||
#define MMCR0_UREG_MASK (MMCR0_FC | MMCR0_PMAO | MMCR0_PMAE)
|
||||
/* MMCR2 userspace r/w mask */
|
||||
#define MMCR2_FC1P0 PPC_BIT(1) /* MMCR2 FCnP0 for PMC1 */
|
||||
#define MMCR2_FC2P0 PPC_BIT(10) /* MMCR2 FCnP0 for PMC2 */
|
||||
#define MMCR2_FC3P0 PPC_BIT(19) /* MMCR2 FCnP0 for PMC3 */
|
||||
#define MMCR2_FC4P0 PPC_BIT(28) /* MMCR2 FCnP0 for PMC4 */
|
||||
#define MMCR2_FC5P0 PPC_BIT(37) /* MMCR2 FCnP0 for PMC5 */
|
||||
#define MMCR2_FC6P0 PPC_BIT(46) /* MMCR2 FCnP0 for PMC6 */
|
||||
#define MMCR2_UREG_MASK (MMCR2_FC1P0 | MMCR2_FC2P0 | MMCR2_FC3P0 | \
|
||||
MMCR2_FC4P0 | MMCR2_FC5P0 | MMCR2_FC6P0)
|
||||
|
||||
/* LPCR bits */
|
||||
#define LPCR_VPM0 PPC_BIT(0)
|
||||
#define LPCR_VPM1 PPC_BIT(1)
|
||||
@@ -607,6 +628,8 @@ enum {
|
||||
HFLAGS_SE = 10, /* MSR_SE -- from elsewhere on embedded ppc */
|
||||
HFLAGS_FP = 13, /* MSR_FP */
|
||||
HFLAGS_PR = 14, /* MSR_PR */
|
||||
HFLAGS_PMCC0 = 15, /* MMCR0 PMCC bit 0 */
|
||||
HFLAGS_PMCC1 = 16, /* MMCR0 PMCC bit 1 */
|
||||
HFLAGS_VSX = 23, /* MSR_VSX if cpu has VSX */
|
||||
HFLAGS_VR = 25, /* MSR_VR if cpu has VRE */
|
||||
|
||||
@@ -2412,7 +2435,7 @@ enum {
|
||||
/*****************************************************************************/
|
||||
|
||||
#define is_isa300(ctx) (!!(ctx->insns_flags2 & PPC2_ISA300))
|
||||
target_ulong cpu_read_xer(CPUPPCState *env);
|
||||
target_ulong cpu_read_xer(const CPUPPCState *env);
|
||||
void cpu_write_xer(CPUPPCState *env, target_ulong xer);
|
||||
|
||||
/*
|
||||
|
||||
@@ -6867,7 +6867,7 @@ static void register_book3s_pmu_sup_sprs(CPUPPCState *env)
|
||||
static void register_book3s_pmu_user_sprs(CPUPPCState *env)
|
||||
{
|
||||
spr_register(env, SPR_POWER_UMMCR0, "UMMCR0",
|
||||
&spr_read_ureg, SPR_NOACCESS,
|
||||
&spr_read_MMCR0_ureg, &spr_write_MMCR0_ureg,
|
||||
&spr_read_ureg, &spr_write_ureg,
|
||||
0x00000000);
|
||||
spr_register(env, SPR_POWER_UMMCR1, "UMMCR1",
|
||||
@@ -6879,27 +6879,27 @@ static void register_book3s_pmu_user_sprs(CPUPPCState *env)
|
||||
&spr_read_ureg, &spr_write_ureg,
|
||||
0x00000000);
|
||||
spr_register(env, SPR_POWER_UPMC1, "UPMC1",
|
||||
&spr_read_ureg, SPR_NOACCESS,
|
||||
&spr_read_PMC14_ureg, &spr_write_PMC14_ureg,
|
||||
&spr_read_ureg, &spr_write_ureg,
|
||||
0x00000000);
|
||||
spr_register(env, SPR_POWER_UPMC2, "UPMC2",
|
||||
&spr_read_ureg, SPR_NOACCESS,
|
||||
&spr_read_PMC14_ureg, &spr_write_PMC14_ureg,
|
||||
&spr_read_ureg, &spr_write_ureg,
|
||||
0x00000000);
|
||||
spr_register(env, SPR_POWER_UPMC3, "UPMC3",
|
||||
&spr_read_ureg, SPR_NOACCESS,
|
||||
&spr_read_PMC14_ureg, &spr_write_PMC14_ureg,
|
||||
&spr_read_ureg, &spr_write_ureg,
|
||||
0x00000000);
|
||||
spr_register(env, SPR_POWER_UPMC4, "UPMC4",
|
||||
&spr_read_ureg, SPR_NOACCESS,
|
||||
&spr_read_PMC14_ureg, &spr_write_PMC14_ureg,
|
||||
&spr_read_ureg, &spr_write_ureg,
|
||||
0x00000000);
|
||||
spr_register(env, SPR_POWER_UPMC5, "UPMC5",
|
||||
&spr_read_ureg, SPR_NOACCESS,
|
||||
&spr_read_PMC56_ureg, &spr_write_PMC56_ureg,
|
||||
&spr_read_ureg, &spr_write_ureg,
|
||||
0x00000000);
|
||||
spr_register(env, SPR_POWER_UPMC6, "UPMC6",
|
||||
&spr_read_ureg, SPR_NOACCESS,
|
||||
&spr_read_PMC56_ureg, &spr_write_PMC56_ureg,
|
||||
&spr_read_ureg, &spr_write_ureg,
|
||||
0x00000000);
|
||||
spr_register(env, SPR_POWER_USIAR, "USIAR",
|
||||
@@ -6975,7 +6975,7 @@ static void register_power8_pmu_sup_sprs(CPUPPCState *env)
|
||||
static void register_power8_pmu_user_sprs(CPUPPCState *env)
|
||||
{
|
||||
spr_register(env, SPR_POWER_UMMCR2, "UMMCR2",
|
||||
&spr_read_ureg, SPR_NOACCESS,
|
||||
&spr_read_MMCR2_ureg, &spr_write_MMCR2_ureg,
|
||||
&spr_read_ureg, &spr_write_ureg,
|
||||
0x00000000);
|
||||
spr_register(env, SPR_POWER_USIER, "USIER",
|
||||
|
||||
@@ -159,7 +159,7 @@ int ppc_cpu_gdb_read_register(CPUState *cs, GByteArray *buf, int n)
|
||||
gdb_get_regl(buf, env->ctr);
|
||||
break;
|
||||
case 69:
|
||||
gdb_get_reg32(buf, env->xer);
|
||||
gdb_get_reg32(buf, cpu_read_xer(env));
|
||||
break;
|
||||
case 70:
|
||||
gdb_get_reg32(buf, env->fpscr);
|
||||
@@ -217,7 +217,7 @@ int ppc_cpu_gdb_read_register_apple(CPUState *cs, GByteArray *buf, int n)
|
||||
gdb_get_reg64(buf, env->ctr);
|
||||
break;
|
||||
case 69 + 32:
|
||||
gdb_get_reg32(buf, env->xer);
|
||||
gdb_get_reg32(buf, cpu_read_xer(env));
|
||||
break;
|
||||
case 70 + 32:
|
||||
gdb_get_reg64(buf, env->fpscr);
|
||||
@@ -269,7 +269,7 @@ int ppc_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n)
|
||||
env->ctr = ldtul_p(mem_buf);
|
||||
break;
|
||||
case 69:
|
||||
env->xer = ldl_p(mem_buf);
|
||||
cpu_write_xer(env, ldl_p(mem_buf));
|
||||
break;
|
||||
case 70:
|
||||
/* fpscr */
|
||||
@@ -319,7 +319,7 @@ int ppc_cpu_gdb_write_register_apple(CPUState *cs, uint8_t *mem_buf, int n)
|
||||
env->ctr = ldq_p(mem_buf);
|
||||
break;
|
||||
case 69 + 32:
|
||||
env->xer = ldl_p(mem_buf);
|
||||
cpu_write_xer(env, ldl_p(mem_buf));
|
||||
break;
|
||||
case 70 + 32:
|
||||
/* fpscr */
|
||||
|
||||
@@ -109,6 +109,12 @@ static uint32_t hreg_compute_hflags_value(CPUPPCState *env)
|
||||
if (env->spr[SPR_LPCR] & LPCR_HR) {
|
||||
hflags |= 1 << HFLAGS_HR;
|
||||
}
|
||||
if (env->spr[SPR_POWER_MMCR0] & MMCR0_PMCC0) {
|
||||
hflags |= 1 << HFLAGS_PMCC0;
|
||||
}
|
||||
if (env->spr[SPR_POWER_MMCR0] & MMCR0_PMCC1) {
|
||||
hflags |= 1 << HFLAGS_PMCC1;
|
||||
}
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
if (!env->has_hv_mode || (msr & (1ull << MSR_HV))) {
|
||||
|
||||
@@ -44,6 +44,13 @@ static target_long monitor_get_ccr(Monitor *mon, const struct MonitorDef *md,
|
||||
return u;
|
||||
}
|
||||
|
||||
static target_long monitor_get_xer(Monitor *mon, const struct MonitorDef *md,
|
||||
int val)
|
||||
{
|
||||
CPUArchState *env = mon_get_cpu_env(mon);
|
||||
return cpu_read_xer(env);
|
||||
}
|
||||
|
||||
static target_long monitor_get_decr(Monitor *mon, const struct MonitorDef *md,
|
||||
int val)
|
||||
{
|
||||
@@ -85,7 +92,7 @@ const MonitorDef monitor_defs[] = {
|
||||
{ "decr", 0, &monitor_get_decr, },
|
||||
{ "ccr|cr", 0, &monitor_get_ccr, },
|
||||
/* Machine state register */
|
||||
{ "xer", offsetof(CPUPPCState, xer) },
|
||||
{ "xer", 0, &monitor_get_xer },
|
||||
{ "msr", offsetof(CPUPPCState, msr) },
|
||||
{ "tbu", 0, &monitor_get_tbu, },
|
||||
{ "tbl", 0, &monitor_get_tbl, },
|
||||
|
||||
@@ -0,0 +1,262 @@
|
||||
/*
|
||||
* PMU register read/write functions for TCG IBM POWER chips
|
||||
*
|
||||
* Copyright IBM Corp. 2021
|
||||
*
|
||||
* Authors:
|
||||
* Daniel Henrique Barboza <danielhb413@gmail.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.
|
||||
*/
|
||||
|
||||
#if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY)
|
||||
|
||||
/*
|
||||
* Checks whether the Group A SPR (MMCR0, MMCR2, MMCRA, and the
|
||||
* PMCs) has problem state read access.
|
||||
*
|
||||
* Read acccess is granted for all PMCC values but 0b01, where a
|
||||
* Facility Unavailable Interrupt will occur.
|
||||
*/
|
||||
static bool spr_groupA_read_allowed(DisasContext *ctx)
|
||||
{
|
||||
if (!ctx->mmcr0_pmcc0 && ctx->mmcr0_pmcc1) {
|
||||
gen_hvpriv_exception(ctx, POWERPC_EXCP_FU);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks whether the Group A SPR (MMCR0, MMCR2, MMCRA, and the
|
||||
* PMCs) has problem state write access.
|
||||
*
|
||||
* Write acccess is granted for PMCC values 0b10 and 0b11. Userspace
|
||||
* writing with PMCC 0b00 will generate a Hypervisor Emulation
|
||||
* Assistance Interrupt. Userspace writing with PMCC 0b01 will
|
||||
* generate a Facility Unavailable Interrupt.
|
||||
*/
|
||||
static bool spr_groupA_write_allowed(DisasContext *ctx)
|
||||
{
|
||||
if (ctx->mmcr0_pmcc0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ctx->mmcr0_pmcc1) {
|
||||
/* PMCC = 0b01 */
|
||||
gen_hvpriv_exception(ctx, POWERPC_EXCP_FU);
|
||||
} else {
|
||||
/* PMCC = 0b00 */
|
||||
gen_hvpriv_exception(ctx, POWERPC_EXCP_INVAL_SPR);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper function to avoid code repetition between MMCR0 and
|
||||
* MMCR2 problem state write functions.
|
||||
*
|
||||
* 'ret' must be tcg_temp_freed() by the caller.
|
||||
*/
|
||||
static TCGv masked_gprn_for_spr_write(int gprn, int sprn,
|
||||
uint64_t spr_mask)
|
||||
{
|
||||
TCGv ret = tcg_temp_new();
|
||||
TCGv t0 = tcg_temp_new();
|
||||
|
||||
/* 'ret' starts with all mask bits cleared */
|
||||
gen_load_spr(ret, sprn);
|
||||
tcg_gen_andi_tl(ret, ret, ~(spr_mask));
|
||||
|
||||
/* Apply the mask into 'gprn' in a temp var */
|
||||
tcg_gen_andi_tl(t0, cpu_gpr[gprn], spr_mask);
|
||||
|
||||
/* Add the masked gprn bits into 'ret' */
|
||||
tcg_gen_or_tl(ret, ret, t0);
|
||||
|
||||
tcg_temp_free(t0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void spr_read_MMCR0_ureg(DisasContext *ctx, int gprn, int sprn)
|
||||
{
|
||||
TCGv t0;
|
||||
|
||||
if (!spr_groupA_read_allowed(ctx)) {
|
||||
return;
|
||||
}
|
||||
|
||||
t0 = tcg_temp_new();
|
||||
|
||||
/*
|
||||
* Filter out all bits but FC, PMAO, and PMAE, according
|
||||
* to ISA v3.1, in 10.4.4 Monitor Mode Control Register 0,
|
||||
* fourth paragraph.
|
||||
*/
|
||||
gen_load_spr(t0, SPR_POWER_MMCR0);
|
||||
tcg_gen_andi_tl(t0, t0, MMCR0_UREG_MASK);
|
||||
tcg_gen_mov_tl(cpu_gpr[gprn], t0);
|
||||
|
||||
tcg_temp_free(t0);
|
||||
}
|
||||
|
||||
void spr_write_MMCR0_ureg(DisasContext *ctx, int sprn, int gprn)
|
||||
{
|
||||
TCGv masked_gprn;
|
||||
|
||||
if (!spr_groupA_write_allowed(ctx)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Filter out all bits but FC, PMAO, and PMAE, according
|
||||
* to ISA v3.1, in 10.4.4 Monitor Mode Control Register 0,
|
||||
* fourth paragraph.
|
||||
*/
|
||||
masked_gprn = masked_gprn_for_spr_write(gprn, SPR_POWER_MMCR0,
|
||||
MMCR0_UREG_MASK);
|
||||
gen_store_spr(SPR_POWER_MMCR0, masked_gprn);
|
||||
|
||||
tcg_temp_free(masked_gprn);
|
||||
}
|
||||
|
||||
void spr_read_MMCR2_ureg(DisasContext *ctx, int gprn, int sprn)
|
||||
{
|
||||
TCGv t0;
|
||||
|
||||
if (!spr_groupA_read_allowed(ctx)) {
|
||||
return;
|
||||
}
|
||||
|
||||
t0 = tcg_temp_new();
|
||||
|
||||
/*
|
||||
* On read, filter out all bits that are not FCnP0 bits.
|
||||
* When MMCR0[PMCC] is set to 0b10 or 0b11, providing
|
||||
* problem state programs read/write access to MMCR2,
|
||||
* only the FCnP0 bits can be accessed. All other bits are
|
||||
* not changed when mtspr is executed in problem state, and
|
||||
* all other bits return 0s when mfspr is executed in problem
|
||||
* state, according to ISA v3.1, section 10.4.6 Monitor Mode
|
||||
* Control Register 2, p. 1316, third paragraph.
|
||||
*/
|
||||
gen_load_spr(t0, SPR_POWER_MMCR2);
|
||||
tcg_gen_andi_tl(t0, t0, MMCR2_UREG_MASK);
|
||||
tcg_gen_mov_tl(cpu_gpr[gprn], t0);
|
||||
|
||||
tcg_temp_free(t0);
|
||||
}
|
||||
|
||||
void spr_write_MMCR2_ureg(DisasContext *ctx, int sprn, int gprn)
|
||||
{
|
||||
TCGv masked_gprn;
|
||||
|
||||
if (!spr_groupA_write_allowed(ctx)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Filter the bits that can be written using MMCR2_UREG_MASK,
|
||||
* similar to what is done in spr_write_MMCR0_ureg().
|
||||
*/
|
||||
masked_gprn = masked_gprn_for_spr_write(gprn, SPR_POWER_MMCR2,
|
||||
MMCR2_UREG_MASK);
|
||||
gen_store_spr(SPR_POWER_MMCR2, masked_gprn);
|
||||
|
||||
tcg_temp_free(masked_gprn);
|
||||
}
|
||||
|
||||
void spr_read_PMC14_ureg(DisasContext *ctx, int gprn, int sprn)
|
||||
{
|
||||
if (!spr_groupA_read_allowed(ctx)) {
|
||||
return;
|
||||
}
|
||||
|
||||
spr_read_ureg(ctx, gprn, sprn);
|
||||
}
|
||||
|
||||
void spr_read_PMC56_ureg(DisasContext *ctx, int gprn, int sprn)
|
||||
{
|
||||
/*
|
||||
* If PMCC = 0b11, PMC5 and PMC6 aren't included in the Performance
|
||||
* Monitor, and a read attempt results in a Facility Unavailable
|
||||
* Interrupt.
|
||||
*/
|
||||
if (ctx->mmcr0_pmcc0 && ctx->mmcr0_pmcc1) {
|
||||
gen_hvpriv_exception(ctx, POWERPC_EXCP_FU);
|
||||
return;
|
||||
}
|
||||
|
||||
/* The remaining steps are similar to PMCs 1-4 userspace read */
|
||||
spr_read_PMC14_ureg(ctx, gprn, sprn);
|
||||
}
|
||||
|
||||
void spr_write_PMC14_ureg(DisasContext *ctx, int sprn, int gprn)
|
||||
{
|
||||
if (!spr_groupA_write_allowed(ctx)) {
|
||||
return;
|
||||
}
|
||||
|
||||
spr_write_ureg(ctx, sprn, gprn);
|
||||
}
|
||||
|
||||
void spr_write_PMC56_ureg(DisasContext *ctx, int sprn, int gprn)
|
||||
{
|
||||
/*
|
||||
* If PMCC = 0b11, PMC5 and PMC6 aren't included in the Performance
|
||||
* Monitor, and a write attempt results in a Facility Unavailable
|
||||
* Interrupt.
|
||||
*/
|
||||
if (ctx->mmcr0_pmcc0 && ctx->mmcr0_pmcc1) {
|
||||
gen_hvpriv_exception(ctx, POWERPC_EXCP_FU);
|
||||
return;
|
||||
}
|
||||
|
||||
/* The remaining steps are similar to PMCs 1-4 userspace write */
|
||||
spr_write_PMC14_ureg(ctx, sprn, gprn);
|
||||
}
|
||||
#else
|
||||
void spr_read_MMCR0_ureg(DisasContext *ctx, int gprn, int sprn)
|
||||
{
|
||||
spr_read_ureg(ctx, gprn, sprn);
|
||||
}
|
||||
|
||||
void spr_write_MMCR0_ureg(DisasContext *ctx, int sprn, int gprn)
|
||||
{
|
||||
spr_noaccess(ctx, gprn, sprn);
|
||||
}
|
||||
|
||||
void spr_read_MMCR2_ureg(DisasContext *ctx, int gprn, int sprn)
|
||||
{
|
||||
spr_read_ureg(ctx, gprn, sprn);
|
||||
}
|
||||
|
||||
void spr_write_MMCR2_ureg(DisasContext *ctx, int sprn, int gprn)
|
||||
{
|
||||
spr_noaccess(ctx, gprn, sprn);
|
||||
}
|
||||
|
||||
void spr_read_PMC14_ureg(DisasContext *ctx, int gprn, int sprn)
|
||||
{
|
||||
spr_read_ureg(ctx, gprn, sprn);
|
||||
}
|
||||
|
||||
void spr_read_PMC56_ureg(DisasContext *ctx, int gprn, int sprn)
|
||||
{
|
||||
spr_read_ureg(ctx, gprn, sprn);
|
||||
}
|
||||
|
||||
void spr_write_PMC14_ureg(DisasContext *ctx, int sprn, int gprn)
|
||||
{
|
||||
spr_noaccess(ctx, gprn, sprn);
|
||||
}
|
||||
|
||||
void spr_write_PMC56_ureg(DisasContext *ctx, int sprn, int gprn)
|
||||
{
|
||||
spr_noaccess(ctx, gprn, sprn);
|
||||
}
|
||||
#endif /* defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) */
|
||||
@@ -32,6 +32,10 @@ void spr_write_lr(DisasContext *ctx, int sprn, int gprn);
|
||||
void spr_read_ctr(DisasContext *ctx, int gprn, int sprn);
|
||||
void spr_write_ctr(DisasContext *ctx, int sprn, int gprn);
|
||||
void spr_read_ureg(DisasContext *ctx, int gprn, int sprn);
|
||||
void spr_read_MMCR0_ureg(DisasContext *ctx, int gprn, int sprn);
|
||||
void spr_read_MMCR2_ureg(DisasContext *ctx, int gprn, int sprn);
|
||||
void spr_read_PMC14_ureg(DisasContext *ctx, int gprn, int sprn);
|
||||
void spr_read_PMC56_ureg(DisasContext *ctx, int gprn, int sprn);
|
||||
void spr_read_tbl(DisasContext *ctx, int gprn, int sprn);
|
||||
void spr_read_tbu(DisasContext *ctx, int gprn, int sprn);
|
||||
void spr_read_atbl(DisasContext *ctx, int gprn, int sprn);
|
||||
@@ -40,6 +44,10 @@ void spr_read_601_rtcl(DisasContext *ctx, int gprn, int sprn);
|
||||
void spr_read_601_rtcu(DisasContext *ctx, int gprn, int sprn);
|
||||
void spr_read_spefscr(DisasContext *ctx, int gprn, int sprn);
|
||||
void spr_write_spefscr(DisasContext *ctx, int sprn, int gprn);
|
||||
void spr_write_MMCR0_ureg(DisasContext *ctx, int sprn, int gprn);
|
||||
void spr_write_MMCR2_ureg(DisasContext *ctx, int sprn, int gprn);
|
||||
void spr_write_PMC14_ureg(DisasContext *ctx, int sprn, int gprn);
|
||||
void spr_write_PMC56_ureg(DisasContext *ctx, int sprn, int gprn);
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
void spr_write_generic32(DisasContext *ctx, int sprn, int gprn);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user