ppc 7.0 queue:
* Cleanup of PowerNV PHBs (Daniel and Cedric) * Cleanup and fixes for PPC405 machine (Cedric) * Fix for xscvspdpn (Matheus) * Rework of powerpc exception handling 1/n (Fabiano) * Optimisation for PMU (Richard and Daniel) -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEEoPZlSPBIlev+awtgUaNDx8/77KEFAmHT8WYACgkQUaNDx8/7 7KFfVw//REr9O6KidKRcGdznUnCiDofwCMCmX1ORB2bzmL5ZqEHV2F0hq6r6VfO3 D3ERCoN2MWOdSBc1nH2gSVLB3UlabeTtVl1h7r/RiW8Hs+kr713GQ2WXvTo7d6oA kPDnkWTjGIh4mr3Nk29trdChxm9NkwOzJyHejOkp/dO4H0XmwzL7ZS04hWi7lNab 7ubT2JfjIHctsRzp792OuyZqkQ/blXI1F1azFlWaHVRT4CZbC+XXln1NSJ4GOleC YYTKUnJTdOz6tD8FmuIhEqkXzWzx/uaD9sMSjJN3xwT7+kBMQv8D1MkTP4Obvnq/ a3ntnjxuV+4DNdvk0Mf6BYl/l+qtyCxUYkQmRai2VetNFbeaPRTBPN0YNrD0Qa8o yvGu26UDnNtp8t7dlw2C23bZ7LToEEc8/g7e6rEVIMS/Bk9vKfMr2BlOMeRfBXMX pvhQ3Q2CDnlooafjxOvjtKI3s3qIaf9xR+drgrl0EK7KqdCgmTDxmowSaxbNGgGs D2R5Y4NbGtFsgIqRrov1lmfTrtc2kZAkW2u6uwaRE5AzvPkv43OSGwkUHM97ay+E RLuN0vCDrhZzZ1gaAIjY90SBXue7oD7JFhdMfAZUumqssGT9yE+mku58vibr6x8c Qeam21JNHwyoKWrjtsI1dmeStM2xhTq8Oj4TpACiGtHdRWRAlqA= =eKtt -----END PGP SIGNATURE----- Merge tag 'pull-ppc-20220104' of https://github.com/legoater/qemu into staging ppc 7.0 queue: * Cleanup of PowerNV PHBs (Daniel and Cedric) * Cleanup and fixes for PPC405 machine (Cedric) * Fix for xscvspdpn (Matheus) * Rework of powerpc exception handling 1/n (Fabiano) * Optimisation for PMU (Richard and Daniel) # gpg: Signature made Mon 03 Jan 2022 11:04:06 PM PST # gpg: using RSA key A0F66548F04895EBFE6B0B6051A343C7CFFBECA1 # gpg: Good signature from "Cédric Le Goater <clg@kaod.org>" [undefined] # gpg: WARNING: This key is not certified with a trusted signature! # gpg: There is no indication that the signature belongs to the owner. # Primary key fingerprint: A0F6 6548 F048 95EB FE6B 0B60 51A3 43C7 CFFB ECA1 * tag 'pull-ppc-20220104' of https://github.com/legoater/qemu: (26 commits) target/ppc: do not call hreg_compute_hflags() in helper_store_mmcr0() target/ppc: Use env->pnc_cyc_cnt target/ppc: Rewrite pmu_increment_insns target/ppc: Cache per-pmc insn and cycle count settings target/ppc: powerpc_excp: Stop passing excp_model around target/ppc: powerpc_excp: Move system call vectored code together target/ppc: powerpc_excp: Set vector earlier target/ppc: powerpc_excp: Add excp_vectors bounds check target/ppc: powerpc_excp: Set alternate SRRs directly target/ppc: do not silence snan in xscvspdpn ppc/ppc405: Dump specific registers ppc/ppc405: Introduce a store helper for SPR_40x_PID ppc/ppc405: Fix timer initialization ppc/ppc405: Rework ppc_40x_timers_init() to use a PowerPCCPU ppc/ppc405: Restore TCR and STR write handlers ppc/ppc405: Activate MMU logs ppc/ppc4xx: Convert printfs() target/ppc: Print out literal exception names in logs target/ppc: Remove static inline target/ppc: Check effective address validity ... Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
This commit is contained in:
commit
67e41fe0cf
@ -1045,7 +1045,8 @@ static void pnv_phb3_realize(DeviceState *dev, Error **errp)
|
||||
memory_region_init(&phb->pci_mmio, OBJECT(phb), "pci-mmio",
|
||||
PCI_MMIO_TOTAL_SIZE);
|
||||
|
||||
pci->bus = pci_register_root_bus(dev, "root-bus",
|
||||
pci->bus = pci_register_root_bus(dev,
|
||||
dev->id ? dev->id : NULL,
|
||||
pnv_phb3_set_irq, pnv_phb3_map_irq, phb,
|
||||
&phb->pci_mmio, &phb->pci_io,
|
||||
0, 4, TYPE_PNV_PHB3_ROOT_BUS);
|
||||
|
@ -1201,7 +1201,7 @@ static void pnv_phb4_realize(DeviceState *dev, Error **errp)
|
||||
memory_region_init(&phb->pci_mmio, OBJECT(phb), name,
|
||||
PCI_MMIO_TOTAL_SIZE);
|
||||
|
||||
pci->bus = pci_register_root_bus(dev, "root-bus",
|
||||
pci->bus = pci_register_root_bus(dev, dev->id,
|
||||
pnv_phb4_set_irq, pnv_phb4_map_irq, phb,
|
||||
&phb->pci_mmio, &phb->pci_io,
|
||||
0, 4, TYPE_PNV_PHB4_ROOT_BUS);
|
||||
@ -1230,18 +1230,6 @@ static void pnv_phb4_realize(DeviceState *dev, Error **errp)
|
||||
phb->qirqs = qemu_allocate_irqs(xive_source_set_irq, xsrc, xsrc->nr_irqs);
|
||||
}
|
||||
|
||||
static void pnv_phb4_reset(DeviceState *dev)
|
||||
{
|
||||
PnvPHB4 *phb = PNV_PHB4(dev);
|
||||
PCIDevice *root_dev = PCI_DEVICE(&phb->root);
|
||||
|
||||
/*
|
||||
* Configure PCI device id at reset using a property.
|
||||
*/
|
||||
pci_config_set_vendor_id(root_dev->config, PCI_VENDOR_ID_IBM);
|
||||
pci_config_set_device_id(root_dev->config, phb->device_id);
|
||||
}
|
||||
|
||||
static const char *pnv_phb4_root_bus_path(PCIHostState *host_bridge,
|
||||
PCIBus *rootbus)
|
||||
{
|
||||
@ -1274,7 +1262,6 @@ static Property pnv_phb4_properties[] = {
|
||||
DEFINE_PROP_UINT32("index", PnvPHB4, phb_id, 0),
|
||||
DEFINE_PROP_UINT32("chip-id", PnvPHB4, chip_id, 0),
|
||||
DEFINE_PROP_UINT64("version", PnvPHB4, version, 0),
|
||||
DEFINE_PROP_UINT16("device-id", PnvPHB4, device_id, 0),
|
||||
DEFINE_PROP_LINK("stack", PnvPHB4, stack, TYPE_PNV_PHB4_PEC_STACK,
|
||||
PnvPhb4PecStack *),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
@ -1291,7 +1278,6 @@ static void pnv_phb4_class_init(ObjectClass *klass, void *data)
|
||||
device_class_set_props(dc, pnv_phb4_properties);
|
||||
set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
|
||||
dc->user_creatable = false;
|
||||
dc->reset = pnv_phb4_reset;
|
||||
|
||||
xfc->notify = pnv_phb4_xive_notify;
|
||||
}
|
||||
|
@ -527,7 +527,6 @@ static void pnv_pec_class_init(ObjectClass *klass, void *data)
|
||||
pecc->stk_compat = stk_compat;
|
||||
pecc->stk_compat_size = sizeof(stk_compat);
|
||||
pecc->version = PNV_PHB4_VERSION;
|
||||
pecc->device_id = PNV_PHB4_DEVICE_ID;
|
||||
pecc->num_stacks = pnv_pec_num_stacks;
|
||||
}
|
||||
|
||||
@ -587,8 +586,6 @@ static void pnv_pec_stk_realize(DeviceState *dev, Error **errp)
|
||||
&error_fatal);
|
||||
object_property_set_int(OBJECT(&stack->phb), "version", pecc->version,
|
||||
&error_fatal);
|
||||
object_property_set_int(OBJECT(&stack->phb), "device-id", pecc->device_id,
|
||||
&error_fatal);
|
||||
object_property_set_link(OBJECT(&stack->phb), "stack", OBJECT(stack),
|
||||
&error_abort);
|
||||
if (!sysbus_realize(SYS_BUS_DEVICE(&stack->phb), errp)) {
|
||||
|
@ -19,6 +19,7 @@
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/log.h"
|
||||
#include "sysemu/runstate.h"
|
||||
#include "cpu.h"
|
||||
#include "hw/sysbus.h"
|
||||
@ -82,7 +83,9 @@ static uint64_t mpc8544_guts_read(void *opaque, hwaddr addr,
|
||||
value = env->spr[SPR_E500_SVR];
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "guts: Unknown register read: %x\n", (int)addr);
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"%s: Unknown register 0x%" HWADDR_PRIx "\n",
|
||||
__func__, addr);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -101,8 +104,8 @@ static void mpc8544_guts_write(void *opaque, hwaddr addr,
|
||||
}
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "guts: Unknown register write: %x = %x\n",
|
||||
(int)addr, (unsigned)value);
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "%s: Unknown register 0x%" HWADDR_PRIx
|
||||
" = 0x%" PRIx64 "\n", __func__, addr, value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -1314,7 +1314,7 @@ static void pnv_chip_power8nvl_class_init(ObjectClass *klass, void *data)
|
||||
|
||||
k->chip_cfam_id = 0x120d304980000000ull; /* P8 Naples DD1.0 */
|
||||
k->cores_mask = POWER8_CORE_MASK;
|
||||
k->num_phbs = 3;
|
||||
k->num_phbs = 4;
|
||||
k->core_pir = pnv_chip_core_pir_p8;
|
||||
k->intc_create = pnv_chip_power8_intc_create;
|
||||
k->intc_reset = pnv_chip_power8_intc_reset;
|
||||
|
67
hw/ppc/ppc.c
67
hw/ppc/ppc.c
@ -1124,14 +1124,12 @@ struct ppc40x_timer_t {
|
||||
/* Fixed interval timer */
|
||||
static void cpu_4xx_fit_cb (void *opaque)
|
||||
{
|
||||
PowerPCCPU *cpu;
|
||||
CPUPPCState *env;
|
||||
PowerPCCPU *cpu = opaque;
|
||||
CPUPPCState *env = &cpu->env;
|
||||
ppc_tb_t *tb_env;
|
||||
ppc40x_timer_t *ppc40x_timer;
|
||||
uint64_t now, next;
|
||||
|
||||
env = opaque;
|
||||
cpu = env_archcpu(env);
|
||||
tb_env = env->tb_env;
|
||||
ppc40x_timer = tb_env->opaque;
|
||||
now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
@ -1193,13 +1191,11 @@ static void start_stop_pit (CPUPPCState *env, ppc_tb_t *tb_env, int is_excp)
|
||||
|
||||
static void cpu_4xx_pit_cb (void *opaque)
|
||||
{
|
||||
PowerPCCPU *cpu;
|
||||
CPUPPCState *env;
|
||||
PowerPCCPU *cpu = opaque;
|
||||
CPUPPCState *env = &cpu->env;
|
||||
ppc_tb_t *tb_env;
|
||||
ppc40x_timer_t *ppc40x_timer;
|
||||
|
||||
env = opaque;
|
||||
cpu = env_archcpu(env);
|
||||
tb_env = env->tb_env;
|
||||
ppc40x_timer = tb_env->opaque;
|
||||
env->spr[SPR_40x_TSR] |= 1 << 27;
|
||||
@ -1216,14 +1212,12 @@ static void cpu_4xx_pit_cb (void *opaque)
|
||||
/* Watchdog timer */
|
||||
static void cpu_4xx_wdt_cb (void *opaque)
|
||||
{
|
||||
PowerPCCPU *cpu;
|
||||
CPUPPCState *env;
|
||||
PowerPCCPU *cpu = opaque;
|
||||
CPUPPCState *env = &cpu->env;
|
||||
ppc_tb_t *tb_env;
|
||||
ppc40x_timer_t *ppc40x_timer;
|
||||
uint64_t now, next;
|
||||
|
||||
env = opaque;
|
||||
cpu = env_archcpu(env);
|
||||
tb_env = env->tb_env;
|
||||
ppc40x_timer = tb_env->opaque;
|
||||
now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
@ -1300,6 +1294,31 @@ target_ulong load_40x_pit (CPUPPCState *env)
|
||||
return cpu_ppc_load_decr(env);
|
||||
}
|
||||
|
||||
void store_40x_tsr(CPUPPCState *env, target_ulong val)
|
||||
{
|
||||
PowerPCCPU *cpu = env_archcpu(env);
|
||||
|
||||
trace_ppc40x_store_tcr(val);
|
||||
|
||||
env->spr[SPR_40x_TSR] &= ~(val & 0xFC000000);
|
||||
if (val & 0x80000000) {
|
||||
ppc_set_irq(cpu, PPC_INTERRUPT_PIT, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void store_40x_tcr(CPUPPCState *env, target_ulong val)
|
||||
{
|
||||
PowerPCCPU *cpu = env_archcpu(env);
|
||||
ppc_tb_t *tb_env;
|
||||
|
||||
trace_ppc40x_store_tsr(val);
|
||||
|
||||
tb_env = env->tb_env;
|
||||
env->spr[SPR_40x_TCR] = val & 0xFFC00000;
|
||||
start_stop_pit(env, tb_env, 1);
|
||||
cpu_4xx_wdt_cb(cpu);
|
||||
}
|
||||
|
||||
static void ppc_40x_set_tb_clk (void *opaque, uint32_t freq)
|
||||
{
|
||||
CPUPPCState *env = opaque;
|
||||
@ -1316,24 +1335,26 @@ clk_setup_cb ppc_40x_timers_init (CPUPPCState *env, uint32_t freq,
|
||||
{
|
||||
ppc_tb_t *tb_env;
|
||||
ppc40x_timer_t *ppc40x_timer;
|
||||
PowerPCCPU *cpu = env_archcpu(env);
|
||||
|
||||
trace_ppc40x_timers_init(freq);
|
||||
|
||||
tb_env = g_malloc0(sizeof(ppc_tb_t));
|
||||
ppc40x_timer = g_malloc0(sizeof(ppc40x_timer_t));
|
||||
|
||||
env->tb_env = tb_env;
|
||||
tb_env->flags = PPC_DECR_UNDERFLOW_TRIGGERED;
|
||||
ppc40x_timer = g_malloc0(sizeof(ppc40x_timer_t));
|
||||
tb_env->tb_freq = freq;
|
||||
tb_env->decr_freq = freq;
|
||||
tb_env->opaque = ppc40x_timer;
|
||||
trace_ppc40x_timers_init(freq);
|
||||
if (ppc40x_timer != NULL) {
|
||||
/* We use decr timer for PIT */
|
||||
tb_env->decr_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &cpu_4xx_pit_cb, env);
|
||||
ppc40x_timer->fit_timer =
|
||||
timer_new_ns(QEMU_CLOCK_VIRTUAL, &cpu_4xx_fit_cb, env);
|
||||
ppc40x_timer->wdt_timer =
|
||||
timer_new_ns(QEMU_CLOCK_VIRTUAL, &cpu_4xx_wdt_cb, env);
|
||||
ppc40x_timer->decr_excp = decr_excp;
|
||||
}
|
||||
|
||||
/* We use decr timer for PIT */
|
||||
tb_env->decr_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &cpu_4xx_pit_cb, cpu);
|
||||
ppc40x_timer->fit_timer =
|
||||
timer_new_ns(QEMU_CLOCK_VIRTUAL, &cpu_4xx_fit_cb, cpu);
|
||||
ppc40x_timer->wdt_timer =
|
||||
timer_new_ns(QEMU_CLOCK_VIRTUAL, &cpu_4xx_wdt_cb, cpu);
|
||||
ppc40x_timer->decr_excp = decr_excp;
|
||||
|
||||
return &ppc_40x_set_tb_clk;
|
||||
}
|
||||
|
@ -1461,8 +1461,6 @@ PowerPCCPU *ppc405ep_init(MemoryRegion *address_space_mem,
|
||||
ppc4xx_pob_init(env);
|
||||
/* OBP arbitrer */
|
||||
ppc4xx_opba_init(0xef600600);
|
||||
/* Initialize timers */
|
||||
ppc_booke_timers_init(cpu, sysclk, 0);
|
||||
/* Universal interrupt controller */
|
||||
uicdev = qdev_new(TYPE_PPC_UIC);
|
||||
uicsbd = SYS_BUS_DEVICE(uicdev);
|
||||
|
@ -35,14 +35,7 @@
|
||||
#include "exec/address-spaces.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qapi/error.h"
|
||||
|
||||
/*#define DEBUG_UIC*/
|
||||
|
||||
#ifdef DEBUG_UIC
|
||||
# define LOG_UIC(...) qemu_log_mask(CPU_LOG_INT, ## __VA_ARGS__)
|
||||
#else
|
||||
# define LOG_UIC(...) do { } while (0)
|
||||
#endif
|
||||
#include "trace.h"
|
||||
|
||||
static void ppc4xx_reset(void *opaque)
|
||||
{
|
||||
@ -137,8 +130,9 @@ static uint32_t sdram_bcr (hwaddr ram_base,
|
||||
bcr = 0x000C0000;
|
||||
break;
|
||||
default:
|
||||
printf("%s: invalid RAM size " TARGET_FMT_plx "\n", __func__,
|
||||
ram_size);
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"%s: invalid RAM size 0x%" HWADDR_PRIx "\n", __func__,
|
||||
ram_size);
|
||||
return 0x00000000;
|
||||
}
|
||||
bcr |= ram_base & 0xFF800000;
|
||||
@ -171,10 +165,8 @@ static void sdram_set_bcr(ppc4xx_sdram_t *sdram, int i,
|
||||
{
|
||||
if (sdram->bcr[i] & 0x00000001) {
|
||||
/* Unmap RAM */
|
||||
#ifdef DEBUG_SDRAM
|
||||
printf("%s: unmap RAM area " TARGET_FMT_plx " " TARGET_FMT_lx "\n",
|
||||
__func__, sdram_base(sdram->bcr[i]), sdram_size(sdram->bcr[i]));
|
||||
#endif
|
||||
trace_ppc4xx_sdram_unmap(sdram_base(sdram->bcr[i]),
|
||||
sdram_size(sdram->bcr[i]));
|
||||
memory_region_del_subregion(get_system_memory(),
|
||||
&sdram->containers[i]);
|
||||
memory_region_del_subregion(&sdram->containers[i],
|
||||
@ -183,10 +175,7 @@ static void sdram_set_bcr(ppc4xx_sdram_t *sdram, int i,
|
||||
}
|
||||
sdram->bcr[i] = bcr & 0xFFDEE001;
|
||||
if (enabled && (bcr & 0x00000001)) {
|
||||
#ifdef DEBUG_SDRAM
|
||||
printf("%s: Map RAM area " TARGET_FMT_plx " " TARGET_FMT_lx "\n",
|
||||
__func__, sdram_base(bcr), sdram_size(bcr));
|
||||
#endif
|
||||
trace_ppc4xx_sdram_unmap(sdram_base(bcr), sdram_size(bcr));
|
||||
memory_region_init(&sdram->containers[i], NULL, "sdram-containers",
|
||||
sdram_size(bcr));
|
||||
memory_region_add_subregion(&sdram->containers[i], 0,
|
||||
@ -216,10 +205,8 @@ static void sdram_unmap_bcr (ppc4xx_sdram_t *sdram)
|
||||
int i;
|
||||
|
||||
for (i = 0; i < sdram->nbanks; i++) {
|
||||
#ifdef DEBUG_SDRAM
|
||||
printf("%s: Unmap RAM area " TARGET_FMT_plx " " TARGET_FMT_lx "\n",
|
||||
__func__, sdram_base(sdram->bcr[i]), sdram_size(sdram->bcr[i]));
|
||||
#endif
|
||||
trace_ppc4xx_sdram_unmap(sdram_base(sdram->bcr[i]),
|
||||
sdram_size(sdram->bcr[i]));
|
||||
memory_region_del_subregion(get_system_memory(),
|
||||
&sdram->ram_memories[i]);
|
||||
}
|
||||
@ -316,16 +303,12 @@ static void dcr_write_sdram (void *opaque, int dcrn, uint32_t val)
|
||||
case 0x20: /* SDRAM_CFG */
|
||||
val &= 0xFFE00000;
|
||||
if (!(sdram->cfg & 0x80000000) && (val & 0x80000000)) {
|
||||
#ifdef DEBUG_SDRAM
|
||||
printf("%s: enable SDRAM controller\n", __func__);
|
||||
#endif
|
||||
trace_ppc4xx_sdram_enable("enable");
|
||||
/* validate all RAM mappings */
|
||||
sdram_map_bcr(sdram);
|
||||
sdram->status &= ~0x80000000;
|
||||
} else if ((sdram->cfg & 0x80000000) && !(val & 0x80000000)) {
|
||||
#ifdef DEBUG_SDRAM
|
||||
printf("%s: disable SDRAM controller\n", __func__);
|
||||
#endif
|
||||
trace_ppc4xx_sdram_enable("disable");
|
||||
/* invalidate all RAM mappings */
|
||||
sdram_unmap_bcr(sdram);
|
||||
sdram->status |= 0x80000000;
|
||||
|
@ -20,6 +20,7 @@
|
||||
* 4xx SoCs, such as the 440EP. */
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/log.h"
|
||||
#include "hw/irq.h"
|
||||
#include "hw/ppc/ppc.h"
|
||||
#include "hw/ppc/ppc4xx.h"
|
||||
@ -152,8 +153,9 @@ static void ppc4xx_pci_reg_write4(void *opaque, hwaddr offset,
|
||||
break;
|
||||
|
||||
default:
|
||||
printf("%s: unhandled PCI internal register 0x%lx\n", __func__,
|
||||
(unsigned long)offset);
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"%s: unhandled PCI internal register 0x%" HWADDR_PRIx "\n",
|
||||
__func__, offset);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -218,8 +220,9 @@ static uint64_t ppc4xx_pci_reg_read4(void *opaque, hwaddr offset,
|
||||
break;
|
||||
|
||||
default:
|
||||
printf("%s: invalid PCI internal register 0x%lx\n", __func__,
|
||||
(unsigned long)offset);
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"%s: invalid PCI internal register 0x%" HWADDR_PRIx "\n",
|
||||
__func__, offset);
|
||||
value = 0;
|
||||
}
|
||||
|
||||
|
@ -110,6 +110,8 @@ ppc4xx_pit_start(uint64_t reload) "PIT 0x%016" PRIx64
|
||||
ppc4xx_pit(uint32_t ar, uint32_t ir, uint64_t tcr, uint64_t tsr, uint64_t reload) "ar %d ir %d TCR 0x%" PRIx64 " TSR 0x%" PRIx64 " PIT 0x%016" PRIx64
|
||||
ppc4xx_wdt(uint64_t tcr, uint64_t tsr) "TCR 0x%" PRIx64 " TSR 0x%" PRIx64
|
||||
ppc40x_store_pit(uint64_t value) "val 0x%" PRIx64
|
||||
ppc40x_store_tcr(uint64_t value) "val 0x%" PRIx64
|
||||
ppc40x_store_tsr(uint64_t value) "val 0x%" PRIx64
|
||||
ppc40x_set_tb_clk(uint32_t value) "new frequency %" PRIu32
|
||||
ppc40x_timers_init(uint32_t value) "frequency %" PRIu32
|
||||
|
||||
@ -164,3 +166,8 @@ ppc4xx_gpt_init(uint64_t addr) "offet 0x%" PRIx64
|
||||
|
||||
ppc405ep_clocks_compute(const char *param, uint32_t param2, uint32_t val) "%s 0x%1" PRIx32 " %d"
|
||||
ppc405ep_clocks_setup(const char *trace) "%s"
|
||||
|
||||
# ppc4xx_devs.c
|
||||
ppc4xx_sdram_enable(const char *trace) "%s SDRAM controller"
|
||||
ppc4xx_sdram_unmap(uint64_t addr, uint64_t size) "Unmap RAM area 0x%" PRIx64 " size 0x%" PRIx64
|
||||
ppc4xx_sdram_map(uint64_t addr, uint64_t size) "Map RAM area 0x%" PRIx64 " size 0x%" PRIx64
|
||||
|
@ -84,7 +84,6 @@ struct PnvPHB4 {
|
||||
uint32_t phb_id;
|
||||
|
||||
uint64_t version;
|
||||
uint16_t device_id;
|
||||
|
||||
char bus_path[8];
|
||||
|
||||
@ -222,7 +221,6 @@ struct PnvPhb4PecClass {
|
||||
const char *stk_compat;
|
||||
int stk_compat_size;
|
||||
uint64_t version;
|
||||
uint64_t device_id;
|
||||
const uint32_t *num_stacks;
|
||||
};
|
||||
|
||||
|
@ -1144,6 +1144,9 @@ struct CPUPPCState {
|
||||
/* Other registers */
|
||||
target_ulong spr[1024]; /* special purpose registers */
|
||||
ppc_spr_t spr_cb[1024];
|
||||
/* Composite status for PMC[1-6] enabled and counting insns or cycles. */
|
||||
uint8_t pmc_ins_cnt;
|
||||
uint8_t pmc_cyc_cnt;
|
||||
/* Vector status and control register, minus VSCR_SAT */
|
||||
uint32_t vscr;
|
||||
/* VSX registers (including FP and AVR) */
|
||||
@ -1399,6 +1402,8 @@ target_ulong load_40x_pit(CPUPPCState *env);
|
||||
void store_40x_pit(CPUPPCState *env, target_ulong val);
|
||||
void store_40x_dbcr0(CPUPPCState *env, uint32_t val);
|
||||
void store_40x_sler(CPUPPCState *env, uint32_t val);
|
||||
void store_40x_tcr(CPUPPCState *env, target_ulong val);
|
||||
void store_40x_tsr(CPUPPCState *env, target_ulong val);
|
||||
void store_booke_tcr(CPUPPCState *env, target_ulong val);
|
||||
void store_booke_tsr(CPUPPCState *env, target_ulong val);
|
||||
void ppc_tlb_invalidate_all(CPUPPCState *env);
|
||||
|
@ -1440,11 +1440,11 @@ static void register_40x_sprs(CPUPPCState *env)
|
||||
0x00000000);
|
||||
spr_register(env, SPR_40x_TCR, "TCR",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_booke_tcr,
|
||||
&spr_read_generic, &spr_write_40x_tcr,
|
||||
0x00000000);
|
||||
spr_register(env, SPR_40x_TSR, "TSR",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_booke_tsr,
|
||||
&spr_read_generic, &spr_write_40x_tsr,
|
||||
0x00000000);
|
||||
}
|
||||
|
||||
@ -1454,7 +1454,7 @@ static void register_405_sprs(CPUPPCState *env)
|
||||
/* MMU */
|
||||
spr_register(env, SPR_40x_PID, "PID",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
&spr_read_generic, &spr_write_40x_pid,
|
||||
0x00000000);
|
||||
spr_register(env, SPR_4xx_CCR0, "CCR0",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
@ -8313,6 +8313,7 @@ static void ppc_cpu_reset(DeviceState *dev)
|
||||
#endif /* CONFIG_TCG */
|
||||
#endif
|
||||
|
||||
pmu_update_summaries(env);
|
||||
hreg_compute_hflags(env);
|
||||
env->reserve_addr = (target_ulong)-1ULL;
|
||||
/* Be sure no exception or interrupt is pending */
|
||||
@ -8648,16 +8649,17 @@ void ppc_cpu_dump_state(CPUState *cs, FILE *f, int flags)
|
||||
env->spr[SPR_SPRG4], env->spr[SPR_SPRG5],
|
||||
env->spr[SPR_SPRG6], env->spr[SPR_SPRG7]);
|
||||
|
||||
switch (env->excp_model) {
|
||||
#if defined(TARGET_PPC64)
|
||||
if (env->excp_model == POWERPC_EXCP_POWER7 ||
|
||||
env->excp_model == POWERPC_EXCP_POWER8 ||
|
||||
env->excp_model == POWERPC_EXCP_POWER9 ||
|
||||
env->excp_model == POWERPC_EXCP_POWER10) {
|
||||
case POWERPC_EXCP_POWER7:
|
||||
case POWERPC_EXCP_POWER8:
|
||||
case POWERPC_EXCP_POWER9:
|
||||
case POWERPC_EXCP_POWER10:
|
||||
qemu_fprintf(f, "HSRR0 " TARGET_FMT_lx " HSRR1 " TARGET_FMT_lx "\n",
|
||||
env->spr[SPR_HSRR0], env->spr[SPR_HSRR1]);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
if (env->excp_model == POWERPC_EXCP_BOOKE) {
|
||||
case POWERPC_EXCP_BOOKE:
|
||||
qemu_fprintf(f, "CSRR0 " TARGET_FMT_lx " CSRR1 " TARGET_FMT_lx
|
||||
" MCSRR0 " TARGET_FMT_lx " MCSRR1 " TARGET_FMT_lx "\n",
|
||||
env->spr[SPR_BOOKE_CSRR0], env->spr[SPR_BOOKE_CSRR1],
|
||||
@ -8688,6 +8690,20 @@ void ppc_cpu_dump_state(CPUState *cs, FILE *f, int flags)
|
||||
* IVORs are left out as they are large and do not change often --
|
||||
* they can be read with "p $ivor0", "p $ivor1", etc.
|
||||
*/
|
||||
break;
|
||||
case POWERPC_EXCP_40x:
|
||||
qemu_fprintf(f, " TCR " TARGET_FMT_lx " TSR " TARGET_FMT_lx
|
||||
" ESR " TARGET_FMT_lx " DEAR " TARGET_FMT_lx "\n",
|
||||
env->spr[SPR_40x_TCR], env->spr[SPR_40x_TSR],
|
||||
env->spr[SPR_40x_ESR], env->spr[SPR_40x_DEAR]);
|
||||
|
||||
qemu_fprintf(f, " EVPR " TARGET_FMT_lx " SRR2 " TARGET_FMT_lx
|
||||
" SRR3 " TARGET_FMT_lx " PID " TARGET_FMT_lx "\n",
|
||||
env->spr[SPR_40x_EVPR], env->spr[SPR_40x_SRR2],
|
||||
env->spr[SPR_40x_SRR3], env->spr[SPR_40x_PID]);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
#if defined(TARGET_PPC64)
|
||||
|
@ -36,7 +36,79 @@
|
||||
/* Exception processing */
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
|
||||
static inline void dump_syscall(CPUPPCState *env)
|
||||
static const char *powerpc_excp_name(int excp)
|
||||
{
|
||||
switch (excp) {
|
||||
case POWERPC_EXCP_CRITICAL: return "CRITICAL";
|
||||
case POWERPC_EXCP_MCHECK: return "MCHECK";
|
||||
case POWERPC_EXCP_DSI: return "DSI";
|
||||
case POWERPC_EXCP_ISI: return "ISI";
|
||||
case POWERPC_EXCP_EXTERNAL: return "EXTERNAL";
|
||||
case POWERPC_EXCP_ALIGN: return "ALIGN";
|
||||
case POWERPC_EXCP_PROGRAM: return "PROGRAM";
|
||||
case POWERPC_EXCP_FPU: return "FPU";
|
||||
case POWERPC_EXCP_SYSCALL: return "SYSCALL";
|
||||
case POWERPC_EXCP_APU: return "APU";
|
||||
case POWERPC_EXCP_DECR: return "DECR";
|
||||
case POWERPC_EXCP_FIT: return "FIT";
|
||||
case POWERPC_EXCP_WDT: return "WDT";
|
||||
case POWERPC_EXCP_DTLB: return "DTLB";
|
||||
case POWERPC_EXCP_ITLB: return "ITLB";
|
||||
case POWERPC_EXCP_DEBUG: return "DEBUG";
|
||||
case POWERPC_EXCP_SPEU: return "SPEU";
|
||||
case POWERPC_EXCP_EFPDI: return "EFPDI";
|
||||
case POWERPC_EXCP_EFPRI: return "EFPRI";
|
||||
case POWERPC_EXCP_EPERFM: return "EPERFM";
|
||||
case POWERPC_EXCP_DOORI: return "DOORI";
|
||||
case POWERPC_EXCP_DOORCI: return "DOORCI";
|
||||
case POWERPC_EXCP_GDOORI: return "GDOORI";
|
||||
case POWERPC_EXCP_GDOORCI: return "GDOORCI";
|
||||
case POWERPC_EXCP_HYPPRIV: return "HYPPRIV";
|
||||
case POWERPC_EXCP_RESET: return "RESET";
|
||||
case POWERPC_EXCP_DSEG: return "DSEG";
|
||||
case POWERPC_EXCP_ISEG: return "ISEG";
|
||||
case POWERPC_EXCP_HDECR: return "HDECR";
|
||||
case POWERPC_EXCP_TRACE: return "TRACE";
|
||||
case POWERPC_EXCP_HDSI: return "HDSI";
|
||||
case POWERPC_EXCP_HISI: return "HISI";
|
||||
case POWERPC_EXCP_HDSEG: return "HDSEG";
|
||||
case POWERPC_EXCP_HISEG: return "HISEG";
|
||||
case POWERPC_EXCP_VPU: return "VPU";
|
||||
case POWERPC_EXCP_PIT: return "PIT";
|
||||
case POWERPC_EXCP_IO: return "IO";
|
||||
case POWERPC_EXCP_RUNM: return "RUNM";
|
||||
case POWERPC_EXCP_EMUL: return "EMUL";
|
||||
case POWERPC_EXCP_IFTLB: return "IFTLB";
|
||||
case POWERPC_EXCP_DLTLB: return "DLTLB";
|
||||
case POWERPC_EXCP_DSTLB: return "DSTLB";
|
||||
case POWERPC_EXCP_FPA: return "FPA";
|
||||
case POWERPC_EXCP_DABR: return "DABR";
|
||||
case POWERPC_EXCP_IABR: return "IABR";
|
||||
case POWERPC_EXCP_SMI: return "SMI";
|
||||
case POWERPC_EXCP_PERFM: return "PERFM";
|
||||
case POWERPC_EXCP_THERM: return "THERM";
|
||||
case POWERPC_EXCP_VPUA: return "VPUA";
|
||||
case POWERPC_EXCP_SOFTP: return "SOFTP";
|
||||
case POWERPC_EXCP_MAINT: return "MAINT";
|
||||
case POWERPC_EXCP_MEXTBR: return "MEXTBR";
|
||||
case POWERPC_EXCP_NMEXTBR: return "NMEXTBR";
|
||||
case POWERPC_EXCP_ITLBE: return "ITLBE";
|
||||
case POWERPC_EXCP_DTLBE: return "DTLBE";
|
||||
case POWERPC_EXCP_VSXU: return "VSXU";
|
||||
case POWERPC_EXCP_FU: return "FU";
|
||||
case POWERPC_EXCP_HV_EMU: return "HV_EMU";
|
||||
case POWERPC_EXCP_HV_MAINT: return "HV_MAINT";
|
||||
case POWERPC_EXCP_HV_FU: return "HV_FU";
|
||||
case POWERPC_EXCP_SDOOR: return "SDOOR";
|
||||
case POWERPC_EXCP_SDOOR_HV: return "SDOOR_HV";
|
||||
case POWERPC_EXCP_HVIRT: return "HVIRT";
|
||||
case POWERPC_EXCP_SYSCALL_VECTORED: return "SYSCALL_VECTORED";
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
}
|
||||
|
||||
static void dump_syscall(CPUPPCState *env)
|
||||
{
|
||||
qemu_log_mask(CPU_LOG_INT, "syscall r0=%016" PRIx64
|
||||
" r3=%016" PRIx64 " r4=%016" PRIx64 " r5=%016" PRIx64
|
||||
@ -48,7 +120,7 @@ static inline void dump_syscall(CPUPPCState *env)
|
||||
ppc_dump_gpr(env, 8), env->nip);
|
||||
}
|
||||
|
||||
static inline void dump_hcall(CPUPPCState *env)
|
||||
static void dump_hcall(CPUPPCState *env)
|
||||
{
|
||||
qemu_log_mask(CPU_LOG_INT, "hypercall r3=%016" PRIx64
|
||||
" r4=%016" PRIx64 " r5=%016" PRIx64 " r6=%016" PRIx64
|
||||
@ -161,7 +233,7 @@ static int powerpc_reset_wakeup(CPUState *cs, CPUPPCState *env, int excp,
|
||||
* | a | h | 11 | 1 | 1 | h |
|
||||
* +--------------------------------------------------------------------+
|
||||
*/
|
||||
static inline void ppc_excp_apply_ail(PowerPCCPU *cpu, int excp_model, int excp,
|
||||
static void ppc_excp_apply_ail(PowerPCCPU *cpu, int excp_model, int excp,
|
||||
target_ulong msr,
|
||||
target_ulong *new_msr,
|
||||
target_ulong *vector)
|
||||
@ -258,7 +330,7 @@ static inline void ppc_excp_apply_ail(PowerPCCPU *cpu, int excp_model, int excp,
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void powerpc_set_excp_state(PowerPCCPU *cpu,
|
||||
static void powerpc_set_excp_state(PowerPCCPU *cpu,
|
||||
target_ulong vector, target_ulong msr)
|
||||
{
|
||||
CPUState *cs = CPU(cpu);
|
||||
@ -293,15 +365,21 @@ static inline void powerpc_set_excp_state(PowerPCCPU *cpu,
|
||||
* Note that this function should be greatly optimized when called
|
||||
* with a constant excp, from ppc_hw_interrupt
|
||||
*/
|
||||
static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
|
||||
static void powerpc_excp(PowerPCCPU *cpu, int excp)
|
||||
{
|
||||
CPUState *cs = CPU(cpu);
|
||||
CPUPPCState *env = &cpu->env;
|
||||
int excp_model = env->excp_model;
|
||||
target_ulong msr, new_msr, vector;
|
||||
int srr0, srr1, asrr0, asrr1, lev = -1;
|
||||
int srr0, srr1, lev = -1;
|
||||
|
||||
if (excp <= POWERPC_EXCP_NONE || excp >= POWERPC_EXCP_NB) {
|
||||
cpu_abort(cs, "Invalid PowerPC exception %d. Aborting\n", excp);
|
||||
}
|
||||
|
||||
qemu_log_mask(CPU_LOG_INT, "Raise exception at " TARGET_FMT_lx
|
||||
" => %08x (%02x)\n", env->nip, excp, env->error_code);
|
||||
" => %s (%d) error=%02x\n", env->nip, powerpc_excp_name(excp),
|
||||
excp, env->error_code);
|
||||
|
||||
/* new srr1 value excluding must-be-zero bits */
|
||||
if (excp_model == POWERPC_EXCP_BOOKE) {
|
||||
@ -319,8 +397,6 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
|
||||
/* target registers */
|
||||
srr0 = SPR_SRR0;
|
||||
srr1 = SPR_SRR1;
|
||||
asrr0 = -1;
|
||||
asrr1 = -1;
|
||||
|
||||
/*
|
||||
* check for special resume at 0x100 from doze/nap/sleep/winkle on
|
||||
@ -354,10 +430,15 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
|
||||
}
|
||||
#endif
|
||||
|
||||
vector = env->excp_vectors[excp];
|
||||
if (vector == (target_ulong)-1ULL) {
|
||||
cpu_abort(cs, "Raised an exception without defined vector %d\n",
|
||||
excp);
|
||||
}
|
||||
|
||||
vector |= env->excp_prefix;
|
||||
|
||||
switch (excp) {
|
||||
case POWERPC_EXCP_NONE:
|
||||
/* Should never happen */
|
||||
return;
|
||||
case POWERPC_EXCP_CRITICAL: /* Critical input */
|
||||
switch (excp_model) {
|
||||
case POWERPC_EXCP_40x:
|
||||
@ -410,8 +491,9 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
|
||||
/* FIXME: choose one or the other based on CPU type */
|
||||
srr0 = SPR_BOOKE_MCSRR0;
|
||||
srr1 = SPR_BOOKE_MCSRR1;
|
||||
asrr0 = SPR_BOOKE_CSRR0;
|
||||
asrr1 = SPR_BOOKE_CSRR1;
|
||||
|
||||
env->spr[SPR_BOOKE_CSRR0] = env->nip;
|
||||
env->spr[SPR_BOOKE_CSRR1] = msr;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@ -542,6 +624,11 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
|
||||
env->nip += 4;
|
||||
new_msr |= env->msr & ((target_ulong)1 << MSR_EE);
|
||||
new_msr |= env->msr & ((target_ulong)1 << MSR_RI);
|
||||
|
||||
vector += lev * 0x20;
|
||||
|
||||
env->lr = env->nip;
|
||||
env->ctr = msr;
|
||||
break;
|
||||
case POWERPC_EXCP_FPU: /* Floating-point unavailable exception */
|
||||
case POWERPC_EXCP_APU: /* Auxiliary processor unavailable */
|
||||
@ -570,8 +657,10 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
|
||||
/* FIXME: choose one or the other based on CPU type */
|
||||
srr0 = SPR_BOOKE_DSRR0;
|
||||
srr1 = SPR_BOOKE_DSRR1;
|
||||
asrr0 = SPR_BOOKE_CSRR0;
|
||||
asrr1 = SPR_BOOKE_CSRR1;
|
||||
|
||||
env->spr[SPR_BOOKE_CSRR0] = env->nip;
|
||||
env->spr[SPR_BOOKE_CSRR1] = msr;
|
||||
|
||||
/* DBSR already modified by caller */
|
||||
} else {
|
||||
cpu_abort(cs, "Debug exception triggered on unsupported model\n");
|
||||
@ -830,22 +919,6 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
|
||||
}
|
||||
#endif
|
||||
|
||||
vector = env->excp_vectors[excp];
|
||||
if (vector == (target_ulong)-1ULL) {
|
||||
cpu_abort(cs, "Raised an exception without defined vector %d\n",
|
||||
excp);
|
||||
}
|
||||
|
||||
vector |= env->excp_prefix;
|
||||
|
||||
/* If any alternate SRR register are defined, duplicate saved values */
|
||||
if (asrr0 != -1) {
|
||||
env->spr[asrr0] = env->nip;
|
||||
}
|
||||
if (asrr1 != -1) {
|
||||
env->spr[asrr1] = msr;
|
||||
}
|
||||
|
||||
#if defined(TARGET_PPC64)
|
||||
if (excp_model == POWERPC_EXCP_BOOKE) {
|
||||
if (env->spr[SPR_BOOKE_EPCR] & EPCR_ICM) {
|
||||
@ -869,14 +942,6 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
|
||||
|
||||
/* Save MSR */
|
||||
env->spr[srr1] = msr;
|
||||
|
||||
#if defined(TARGET_PPC64)
|
||||
} else {
|
||||
vector += lev * 0x20;
|
||||
|
||||
env->lr = env->nip;
|
||||
env->ctr = msr;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* This can update new_msr and vector if AIL applies */
|
||||
@ -888,9 +953,8 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
|
||||
void ppc_cpu_do_interrupt(CPUState *cs)
|
||||
{
|
||||
PowerPCCPU *cpu = POWERPC_CPU(cs);
|
||||
CPUPPCState *env = &cpu->env;
|
||||
|
||||
powerpc_excp(cpu, env->excp_model, cs->exception_index);
|
||||
powerpc_excp(cpu, cs->exception_index);
|
||||
}
|
||||
|
||||
static void ppc_hw_interrupt(CPUPPCState *env)
|
||||
@ -901,20 +965,20 @@ static void ppc_hw_interrupt(CPUPPCState *env)
|
||||
/* External reset */
|
||||
if (env->pending_interrupts & (1 << PPC_INTERRUPT_RESET)) {
|
||||
env->pending_interrupts &= ~(1 << PPC_INTERRUPT_RESET);
|
||||
powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_RESET);
|
||||
powerpc_excp(cpu, POWERPC_EXCP_RESET);
|
||||
return;
|
||||
}
|
||||
/* Machine check exception */
|
||||
if (env->pending_interrupts & (1 << PPC_INTERRUPT_MCK)) {
|
||||
env->pending_interrupts &= ~(1 << PPC_INTERRUPT_MCK);
|
||||
powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_MCHECK);
|
||||
powerpc_excp(cpu, POWERPC_EXCP_MCHECK);
|
||||
return;
|
||||
}
|
||||
#if 0 /* TODO */
|
||||
/* External debug exception */
|
||||
if (env->pending_interrupts & (1 << PPC_INTERRUPT_DEBUG)) {
|
||||
env->pending_interrupts &= ~(1 << PPC_INTERRUPT_DEBUG);
|
||||
powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_DEBUG);
|
||||
powerpc_excp(cpu, POWERPC_EXCP_DEBUG);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
@ -934,7 +998,7 @@ static void ppc_hw_interrupt(CPUPPCState *env)
|
||||
if ((async_deliver || msr_hv == 0) && hdice) {
|
||||
/* HDEC clears on delivery */
|
||||
env->pending_interrupts &= ~(1 << PPC_INTERRUPT_HDECR);
|
||||
powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_HDECR);
|
||||
powerpc_excp(cpu, POWERPC_EXCP_HDECR);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -944,7 +1008,7 @@ static void ppc_hw_interrupt(CPUPPCState *env)
|
||||
/* 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);
|
||||
powerpc_excp(cpu, POWERPC_EXCP_HVIRT);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -956,14 +1020,14 @@ static void ppc_hw_interrupt(CPUPPCState *env)
|
||||
/* 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, POWERPC_EXCP_EXTERNAL);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (msr_ce != 0) {
|
||||
/* External critical interrupt */
|
||||
if (env->pending_interrupts & (1 << PPC_INTERRUPT_CEXT)) {
|
||||
powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_CRITICAL);
|
||||
powerpc_excp(cpu, POWERPC_EXCP_CRITICAL);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -971,24 +1035,24 @@ static void ppc_hw_interrupt(CPUPPCState *env)
|
||||
/* Watchdog timer on embedded PowerPC */
|
||||
if (env->pending_interrupts & (1 << PPC_INTERRUPT_WDT)) {
|
||||
env->pending_interrupts &= ~(1 << PPC_INTERRUPT_WDT);
|
||||
powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_WDT);
|
||||
powerpc_excp(cpu, POWERPC_EXCP_WDT);
|
||||
return;
|
||||
}
|
||||
if (env->pending_interrupts & (1 << PPC_INTERRUPT_CDOORBELL)) {
|
||||
env->pending_interrupts &= ~(1 << PPC_INTERRUPT_CDOORBELL);
|
||||
powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_DOORCI);
|
||||
powerpc_excp(cpu, POWERPC_EXCP_DOORCI);
|
||||
return;
|
||||
}
|
||||
/* Fixed interval timer on embedded PowerPC */
|
||||
if (env->pending_interrupts & (1 << PPC_INTERRUPT_FIT)) {
|
||||
env->pending_interrupts &= ~(1 << PPC_INTERRUPT_FIT);
|
||||
powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_FIT);
|
||||
powerpc_excp(cpu, POWERPC_EXCP_FIT);
|
||||
return;
|
||||
}
|
||||
/* Programmable interval timer on embedded PowerPC */
|
||||
if (env->pending_interrupts & (1 << PPC_INTERRUPT_PIT)) {
|
||||
env->pending_interrupts &= ~(1 << PPC_INTERRUPT_PIT);
|
||||
powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_PIT);
|
||||
powerpc_excp(cpu, POWERPC_EXCP_PIT);
|
||||
return;
|
||||
}
|
||||
/* Decrementer exception */
|
||||
@ -996,32 +1060,32 @@ static void ppc_hw_interrupt(CPUPPCState *env)
|
||||
if (ppc_decr_clear_on_delivery(env)) {
|
||||
env->pending_interrupts &= ~(1 << PPC_INTERRUPT_DECR);
|
||||
}
|
||||
powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_DECR);
|
||||
powerpc_excp(cpu, POWERPC_EXCP_DECR);
|
||||
return;
|
||||
}
|
||||
if (env->pending_interrupts & (1 << PPC_INTERRUPT_DOORBELL)) {
|
||||
env->pending_interrupts &= ~(1 << PPC_INTERRUPT_DOORBELL);
|
||||
if (is_book3s_arch2x(env)) {
|
||||
powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_SDOOR);
|
||||
powerpc_excp(cpu, POWERPC_EXCP_SDOOR);
|
||||
} else {
|
||||
powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_DOORI);
|
||||
powerpc_excp(cpu, POWERPC_EXCP_DOORI);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (env->pending_interrupts & (1 << PPC_INTERRUPT_HDOORBELL)) {
|
||||
env->pending_interrupts &= ~(1 << PPC_INTERRUPT_HDOORBELL);
|
||||
powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_SDOOR_HV);
|
||||
powerpc_excp(cpu, POWERPC_EXCP_SDOOR_HV);
|
||||
return;
|
||||
}
|
||||
if (env->pending_interrupts & (1 << PPC_INTERRUPT_PERFM)) {
|
||||
env->pending_interrupts &= ~(1 << PPC_INTERRUPT_PERFM);
|
||||
powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_PERFM);
|
||||
powerpc_excp(cpu, POWERPC_EXCP_PERFM);
|
||||
return;
|
||||
}
|
||||
/* Thermal interrupt */
|
||||
if (env->pending_interrupts & (1 << PPC_INTERRUPT_THERM)) {
|
||||
env->pending_interrupts &= ~(1 << PPC_INTERRUPT_THERM);
|
||||
powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_THERM);
|
||||
powerpc_excp(cpu, POWERPC_EXCP_THERM);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -1046,9 +1110,8 @@ static void ppc_hw_interrupt(CPUPPCState *env)
|
||||
void ppc_cpu_do_system_reset(CPUState *cs)
|
||||
{
|
||||
PowerPCCPU *cpu = POWERPC_CPU(cs);
|
||||
CPUPPCState *env = &cpu->env;
|
||||
|
||||
powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_RESET);
|
||||
powerpc_excp(cpu, POWERPC_EXCP_RESET);
|
||||
}
|
||||
|
||||
void ppc_cpu_do_fwnmi_machine_check(CPUState *cs, target_ulong vector)
|
||||
@ -1167,7 +1230,7 @@ void helper_pminsn(CPUPPCState *env, powerpc_pm_insn_t insn)
|
||||
#endif /* defined(TARGET_PPC64) */
|
||||
#endif /* CONFIG_TCG */
|
||||
|
||||
static inline void do_rfi(CPUPPCState *env, target_ulong nip, target_ulong msr)
|
||||
static void do_rfi(CPUPPCState *env, target_ulong nip, target_ulong msr)
|
||||
{
|
||||
CPUState *cs = env_cpu(env);
|
||||
|
||||
|
@ -2816,10 +2816,7 @@ uint64_t helper_xscvdpspn(CPUPPCState *env, uint64_t xb)
|
||||
|
||||
uint64_t helper_xscvspdpn(CPUPPCState *env, uint64_t xb)
|
||||
{
|
||||
float_status tstat = env->fp_status;
|
||||
set_float_exception_flags(0, &tstat);
|
||||
|
||||
return float32_to_float64(xb >> 32, &tstat);
|
||||
return helper_todouble(xb >> 32);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -706,6 +706,8 @@ DEF_HELPER_2(store_hid0_601, void, env, tl)
|
||||
DEF_HELPER_3(store_403_pbr, void, env, i32, tl)
|
||||
DEF_HELPER_FLAGS_1(load_40x_pit, TCG_CALL_NO_RWG, tl, env)
|
||||
DEF_HELPER_FLAGS_2(store_40x_pit, TCG_CALL_NO_RWG, void, env, tl)
|
||||
DEF_HELPER_FLAGS_2(store_40x_tcr, TCG_CALL_NO_RWG, void, env, tl)
|
||||
DEF_HELPER_FLAGS_2(store_40x_tsr, TCG_CALL_NO_RWG, void, env, tl)
|
||||
DEF_HELPER_2(store_40x_dbcr0, void, env, tl)
|
||||
DEF_HELPER_2(store_40x_sler, void, env, tl)
|
||||
DEF_HELPER_FLAGS_2(store_booke_tcr, TCG_CALL_NO_RWG, void, env, tl)
|
||||
|
@ -123,7 +123,7 @@ static uint32_t hreg_compute_hflags_value(CPUPPCState *env)
|
||||
}
|
||||
|
||||
#if defined(TARGET_PPC64)
|
||||
if (pmu_insn_cnt_enabled(env)) {
|
||||
if (env->pmc_ins_cnt) {
|
||||
hflags |= 1 << HFLAGS_INSN_CNT;
|
||||
}
|
||||
#endif
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "kvm_ppc.h"
|
||||
#include "power8-pmu.h"
|
||||
|
||||
static void post_load_update_msr(CPUPPCState *env)
|
||||
{
|
||||
@ -19,6 +20,7 @@ static void post_load_update_msr(CPUPPCState *env)
|
||||
*/
|
||||
env->msr ^= env->msr_mask & ~((1ULL << MSR_TGPR) | MSR_HVB);
|
||||
ppc_store_msr(env, msr);
|
||||
pmu_update_summaries(env);
|
||||
}
|
||||
|
||||
static int cpu_load_old(QEMUFile *f, void *opaque, int version_id)
|
||||
|
@ -32,6 +32,11 @@ static bool ppc_radix64_get_fully_qualified_addr(const CPUPPCState *env,
|
||||
vaddr eaddr,
|
||||
uint64_t *lpid, uint64_t *pid)
|
||||
{
|
||||
/* When EA(2:11) are nonzero, raise a segment interrupt */
|
||||
if (eaddr & ~R_EADDR_VALID_MASK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (msr_hv) { /* MSR[HV] -> Hypervisor/bare metal */
|
||||
switch (eaddr & R_EADDR_QUADRANT) {
|
||||
case R_EADDR_QUADRANT0:
|
||||
@ -97,12 +102,22 @@ static void ppc_radix64_raise_segi(PowerPCCPU *cpu, MMUAccessType access_type,
|
||||
env->error_code = 0;
|
||||
}
|
||||
|
||||
static inline const char *access_str(MMUAccessType access_type)
|
||||
{
|
||||
return access_type == MMU_DATA_LOAD ? "reading" :
|
||||
(access_type == MMU_DATA_STORE ? "writing" : "execute");
|
||||
}
|
||||
|
||||
static void ppc_radix64_raise_si(PowerPCCPU *cpu, MMUAccessType access_type,
|
||||
vaddr eaddr, uint32_t cause)
|
||||
{
|
||||
CPUState *cs = CPU(cpu);
|
||||
CPUPPCState *env = &cpu->env;
|
||||
|
||||
qemu_log_mask(CPU_LOG_MMU, "%s for %s @0x%"VADDR_PRIx" cause %08x\n",
|
||||
__func__, access_str(access_type),
|
||||
eaddr, cause);
|
||||
|
||||
switch (access_type) {
|
||||
case MMU_INST_FETCH:
|
||||
/* Instruction Storage Interrupt */
|
||||
@ -130,6 +145,11 @@ static void ppc_radix64_raise_hsi(PowerPCCPU *cpu, MMUAccessType access_type,
|
||||
CPUState *cs = CPU(cpu);
|
||||
CPUPPCState *env = &cpu->env;
|
||||
|
||||
qemu_log_mask(CPU_LOG_MMU, "%s for %s @0x%"VADDR_PRIx" 0x%"
|
||||
HWADDR_PRIx" cause %08x\n",
|
||||
__func__, access_str(access_type),
|
||||
eaddr, g_raddr, cause);
|
||||
|
||||
switch (access_type) {
|
||||
case MMU_INST_FETCH:
|
||||
/* H Instruction Storage Interrupt */
|
||||
@ -306,6 +326,15 @@ static int ppc_radix64_partition_scoped_xlate(PowerPCCPU *cpu,
|
||||
hwaddr pte_addr;
|
||||
uint64_t pte;
|
||||
|
||||
qemu_log_mask(CPU_LOG_MMU, "%s for %s @0x%"VADDR_PRIx
|
||||
" mmu_idx %u (prot %c%c%c) 0x%"HWADDR_PRIx"\n",
|
||||
__func__, access_str(access_type),
|
||||
eaddr, mmu_idx,
|
||||
*h_prot & PAGE_READ ? 'r' : '-',
|
||||
*h_prot & PAGE_WRITE ? 'w' : '-',
|
||||
*h_prot & PAGE_EXEC ? 'x' : '-',
|
||||
g_raddr);
|
||||
|
||||
*h_page_size = PRTBE_R_GET_RTS(pate.dw0);
|
||||
/* No valid pte or access denied due to protection */
|
||||
if (ppc_radix64_walk_tree(CPU(cpu)->as, g_raddr, pate.dw0 & PRTBE_R_RPDB,
|
||||
@ -343,6 +372,11 @@ static int ppc_radix64_process_scoped_xlate(PowerPCCPU *cpu,
|
||||
hwaddr h_raddr, pte_addr;
|
||||
int ret;
|
||||
|
||||
qemu_log_mask(CPU_LOG_MMU, "%s for %s @0x%"VADDR_PRIx
|
||||
" mmu_idx %u pid %"PRIu64"\n",
|
||||
__func__, access_str(access_type),
|
||||
eaddr, mmu_idx, pid);
|
||||
|
||||
/* Index Process Table by PID to Find Corresponding Process Table Entry */
|
||||
offset = pid * sizeof(struct prtb_entry);
|
||||
size = 1ULL << ((pate.dw1 & PATE1_R_PRTS) + 12);
|
||||
@ -468,9 +502,10 @@ static int ppc_radix64_process_scoped_xlate(PowerPCCPU *cpu,
|
||||
* | = On | Process Scoped | Scoped |
|
||||
* +-------------+----------------+---------------+
|
||||
*/
|
||||
bool ppc_radix64_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type,
|
||||
hwaddr *raddr, int *psizep, int *protp, int mmu_idx,
|
||||
bool guest_visible)
|
||||
static bool ppc_radix64_xlate_impl(PowerPCCPU *cpu, vaddr eaddr,
|
||||
MMUAccessType access_type, hwaddr *raddr,
|
||||
int *psizep, int *protp, int mmu_idx,
|
||||
bool guest_visible)
|
||||
{
|
||||
CPUPPCState *env = &cpu->env;
|
||||
uint64_t lpid, pid;
|
||||
@ -588,3 +623,22 @@ bool ppc_radix64_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type,
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ppc_radix64_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type,
|
||||
hwaddr *raddrp, int *psizep, int *protp, int mmu_idx,
|
||||
bool guest_visible)
|
||||
{
|
||||
bool ret = ppc_radix64_xlate_impl(cpu, eaddr, access_type, raddrp,
|
||||
psizep, protp, mmu_idx, guest_visible);
|
||||
|
||||
qemu_log_mask(CPU_LOG_MMU, "%s for %s @0x%"VADDR_PRIx
|
||||
" mmu_idx %u (prot %c%c%c) -> 0x%"HWADDR_PRIx"\n",
|
||||
__func__, access_str(access_type),
|
||||
eaddr, mmu_idx,
|
||||
*protp & PAGE_READ ? 'r' : '-',
|
||||
*protp & PAGE_WRITE ? 'w' : '-',
|
||||
*protp & PAGE_EXEC ? 'x' : '-',
|
||||
*raddrp);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
/* Radix Quadrants */
|
||||
#define R_EADDR_MASK 0x3FFFFFFFFFFFFFFF
|
||||
#define R_EADDR_VALID_MASK 0xC00FFFFFFFFFFFFF
|
||||
#define R_EADDR_QUADRANT 0xC000000000000000
|
||||
#define R_EADDR_QUADRANT0 0x0000000000000000
|
||||
#define R_EADDR_QUADRANT1 0x4000000000000000
|
||||
|
@ -34,29 +34,7 @@
|
||||
#include "mmu-book3s-v3.h"
|
||||
#include "mmu-radix64.h"
|
||||
|
||||
/* #define DEBUG_MMU */
|
||||
/* #define DEBUG_BATS */
|
||||
/* #define DEBUG_SOFTWARE_TLB */
|
||||
/* #define DUMP_PAGE_TABLES */
|
||||
/* #define FLUSH_ALL_TLBS */
|
||||
|
||||
#ifdef DEBUG_MMU
|
||||
# define LOG_MMU_STATE(cpu) log_cpu_state_mask(CPU_LOG_MMU, (cpu), 0)
|
||||
#else
|
||||
# define LOG_MMU_STATE(cpu) do { } while (0)
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG_SOFTWARE_TLB
|
||||
# define LOG_SWTLB(...) qemu_log_mask(CPU_LOG_MMU, __VA_ARGS__)
|
||||
#else
|
||||
# define LOG_SWTLB(...) do { } while (0)
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG_BATS
|
||||
# define LOG_BATS(...) qemu_log_mask(CPU_LOG_MMU, __VA_ARGS__)
|
||||
#else
|
||||
# define LOG_BATS(...) do { } while (0)
|
||||
#endif
|
||||
|
||||
void ppc_store_sdr1(CPUPPCState *env, target_ulong value)
|
||||
{
|
||||
@ -231,18 +209,20 @@ static int ppc6xx_tlb_check(CPUPPCState *env, mmu_ctx_t *ctx,
|
||||
tlb = &env->tlb.tlb6[nr];
|
||||
/* This test "emulates" the PTE index match for hardware TLBs */
|
||||
if ((eaddr & TARGET_PAGE_MASK) != tlb->EPN) {
|
||||
LOG_SWTLB("TLB %d/%d %s [" TARGET_FMT_lx " " TARGET_FMT_lx
|
||||
"] <> " TARGET_FMT_lx "\n", nr, env->nb_tlb,
|
||||
pte_is_valid(tlb->pte0) ? "valid" : "inval",
|
||||
tlb->EPN, tlb->EPN + TARGET_PAGE_SIZE, eaddr);
|
||||
qemu_log_mask(CPU_LOG_MMU, "TLB %d/%d %s [" TARGET_FMT_lx
|
||||
" " TARGET_FMT_lx "] <> " TARGET_FMT_lx "\n",
|
||||
nr, env->nb_tlb,
|
||||
pte_is_valid(tlb->pte0) ? "valid" : "inval",
|
||||
tlb->EPN, tlb->EPN + TARGET_PAGE_SIZE, eaddr);
|
||||
continue;
|
||||
}
|
||||
LOG_SWTLB("TLB %d/%d %s " TARGET_FMT_lx " <> " TARGET_FMT_lx " "
|
||||
TARGET_FMT_lx " %c %c\n", nr, env->nb_tlb,
|
||||
pte_is_valid(tlb->pte0) ? "valid" : "inval",
|
||||
tlb->EPN, eaddr, tlb->pte1,
|
||||
access_type == MMU_DATA_STORE ? 'S' : 'L',
|
||||
access_type == MMU_INST_FETCH ? 'I' : 'D');
|
||||
qemu_log_mask(CPU_LOG_MMU, "TLB %d/%d %s " TARGET_FMT_lx " <> "
|
||||
TARGET_FMT_lx " " TARGET_FMT_lx " %c %c\n",
|
||||
nr, env->nb_tlb,
|
||||
pte_is_valid(tlb->pte0) ? "valid" : "inval",
|
||||
tlb->EPN, eaddr, tlb->pte1,
|
||||
access_type == MMU_DATA_STORE ? 'S' : 'L',
|
||||
access_type == MMU_INST_FETCH ? 'I' : 'D');
|
||||
switch (ppc6xx_tlb_pte_check(ctx, tlb->pte0, tlb->pte1,
|
||||
0, access_type)) {
|
||||
case -3:
|
||||
@ -272,8 +252,9 @@ static int ppc6xx_tlb_check(CPUPPCState *env, mmu_ctx_t *ctx,
|
||||
}
|
||||
if (best != -1) {
|
||||
done:
|
||||
LOG_SWTLB("found TLB at addr " TARGET_FMT_plx " prot=%01x ret=%d\n",
|
||||
ctx->raddr & TARGET_PAGE_MASK, ctx->prot, ret);
|
||||
qemu_log_mask(CPU_LOG_MMU, "found TLB at addr " TARGET_FMT_plx
|
||||
" prot=%01x ret=%d\n",
|
||||
ctx->raddr & TARGET_PAGE_MASK, ctx->prot, ret);
|
||||
/* Update page flags */
|
||||
pte_update_flags(ctx, &env->tlb.tlb6[best].pte1, ret, access_type);
|
||||
}
|
||||
@ -317,7 +298,7 @@ static int get_bat_6xx_tlb(CPUPPCState *env, mmu_ctx_t *ctx,
|
||||
int ret = -1;
|
||||
bool ifetch = access_type == MMU_INST_FETCH;
|
||||
|
||||
LOG_BATS("%s: %cBAT v " TARGET_FMT_lx "\n", __func__,
|
||||
qemu_log_mask(CPU_LOG_MMU, "%s: %cBAT v " TARGET_FMT_lx "\n", __func__,
|
||||
ifetch ? 'I' : 'D', virtual);
|
||||
if (ifetch) {
|
||||
BATlt = env->IBAT[1];
|
||||
@ -332,9 +313,9 @@ static int get_bat_6xx_tlb(CPUPPCState *env, mmu_ctx_t *ctx,
|
||||
BEPIu = *BATu & 0xF0000000;
|
||||
BEPIl = *BATu & 0x0FFE0000;
|
||||
bat_size_prot(env, &bl, &valid, &prot, BATu, BATl);
|
||||
LOG_BATS("%s: %cBAT%d v " TARGET_FMT_lx " BATu " TARGET_FMT_lx
|
||||
" BATl " TARGET_FMT_lx "\n", __func__,
|
||||
ifetch ? 'I' : 'D', i, virtual, *BATu, *BATl);
|
||||
qemu_log_mask(CPU_LOG_MMU, "%s: %cBAT%d v " TARGET_FMT_lx " BATu "
|
||||
TARGET_FMT_lx " BATl " TARGET_FMT_lx "\n", __func__,
|
||||
ifetch ? 'I' : 'D', i, virtual, *BATu, *BATl);
|
||||
if ((virtual & 0xF0000000) == BEPIu &&
|
||||
((virtual & 0x0FFE0000) & ~bl) == BEPIl) {
|
||||
/* BAT matches */
|
||||
@ -347,32 +328,33 @@ static int get_bat_6xx_tlb(CPUPPCState *env, mmu_ctx_t *ctx,
|
||||
ctx->prot = prot;
|
||||
ret = check_prot(ctx->prot, access_type);
|
||||
if (ret == 0) {
|
||||
LOG_BATS("BAT %d match: r " TARGET_FMT_plx " prot=%c%c\n",
|
||||
i, ctx->raddr, ctx->prot & PAGE_READ ? 'R' : '-',
|
||||
ctx->prot & PAGE_WRITE ? 'W' : '-');
|
||||
qemu_log_mask(CPU_LOG_MMU, "BAT %d match: r " TARGET_FMT_plx
|
||||
" prot=%c%c\n", i, ctx->raddr,
|
||||
ctx->prot & PAGE_READ ? 'R' : '-',
|
||||
ctx->prot & PAGE_WRITE ? 'W' : '-');
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ret < 0) {
|
||||
#if defined(DEBUG_BATS)
|
||||
if (qemu_log_enabled()) {
|
||||
LOG_BATS("no BAT match for " TARGET_FMT_lx ":\n", virtual);
|
||||
qemu_log_mask(CPU_LOG_MMU, "no BAT match for "
|
||||
TARGET_FMT_lx ":\n", virtual);
|
||||
for (i = 0; i < 4; i++) {
|
||||
BATu = &BATut[i];
|
||||
BATl = &BATlt[i];
|
||||
BEPIu = *BATu & 0xF0000000;
|
||||
BEPIl = *BATu & 0x0FFE0000;
|
||||
bl = (*BATu & 0x00001FFC) << 15;
|
||||
LOG_BATS("%s: %cBAT%d v " TARGET_FMT_lx " BATu " TARGET_FMT_lx
|
||||
" BATl " TARGET_FMT_lx "\n\t" TARGET_FMT_lx " "
|
||||
TARGET_FMT_lx " " TARGET_FMT_lx "\n",
|
||||
__func__, ifetch ? 'I' : 'D', i, virtual,
|
||||
*BATu, *BATl, BEPIu, BEPIl, bl);
|
||||
qemu_log_mask(CPU_LOG_MMU, "%s: %cBAT%d v "
|
||||
TARGET_FMT_lx " BATu " TARGET_FMT_lx
|
||||
" BATl " TARGET_FMT_lx "\n\t" TARGET_FMT_lx " "
|
||||
TARGET_FMT_lx " " TARGET_FMT_lx "\n",
|
||||
__func__, ifetch ? 'I' : 'D', i, virtual,
|
||||
*BATu, *BATl, BEPIu, BEPIl, bl);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
/* No hit */
|
||||
return ret;
|
||||
@ -401,11 +383,12 @@ static int get_segment_6xx_tlb(CPUPPCState *env, mmu_ctx_t *ctx,
|
||||
vsid = sr & 0x00FFFFFF;
|
||||
target_page_bits = TARGET_PAGE_BITS;
|
||||
qemu_log_mask(CPU_LOG_MMU,
|
||||
"Check segment v=" TARGET_FMT_lx " %d " TARGET_FMT_lx
|
||||
" nip=" TARGET_FMT_lx " lr=" TARGET_FMT_lx
|
||||
" ir=%d dr=%d pr=%d %d t=%d\n",
|
||||
eaddr, (int)(eaddr >> 28), sr, env->nip, env->lr, (int)msr_ir,
|
||||
(int)msr_dr, pr != 0 ? 1 : 0, access_type == MMU_DATA_STORE, type);
|
||||
"Check segment v=" TARGET_FMT_lx " %d " TARGET_FMT_lx
|
||||
" nip=" TARGET_FMT_lx " lr=" TARGET_FMT_lx
|
||||
" ir=%d dr=%d pr=%d %d t=%d\n",
|
||||
eaddr, (int)(eaddr >> 28), sr, env->nip, env->lr, (int)msr_ir,
|
||||
(int)msr_dr, pr != 0 ? 1 : 0,
|
||||
access_type == MMU_DATA_STORE, type);
|
||||
pgidx = (eaddr & ~SEGMENT_MASK_256M) >> target_page_bits;
|
||||
hash = vsid ^ pgidx;
|
||||
ctx->ptem = (vsid << 7) | (pgidx >> 10);
|
||||
@ -536,9 +519,10 @@ int ppcemb_tlb_check(CPUPPCState *env, ppcemb_tlb_t *tlb,
|
||||
return -1;
|
||||
}
|
||||
mask = ~(tlb->size - 1);
|
||||
LOG_SWTLB("%s: TLB %d address " TARGET_FMT_lx " PID %u <=> " TARGET_FMT_lx
|
||||
" " TARGET_FMT_lx " %u %x\n", __func__, i, address, pid, tlb->EPN,
|
||||
mask, (uint32_t)tlb->PID, tlb->prot);
|
||||
qemu_log_mask(CPU_LOG_MMU, "%s: TLB %d address " TARGET_FMT_lx
|
||||
" PID %u <=> " TARGET_FMT_lx " " TARGET_FMT_lx " %u %x\n",
|
||||
__func__, i, address, pid, tlb->EPN,
|
||||
mask, (uint32_t)tlb->PID, tlb->prot);
|
||||
/* Check PID */
|
||||
if (tlb->PID != 0 && tlb->PID != pid) {
|
||||
return -1;
|
||||
@ -575,8 +559,9 @@ static int mmu40x_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx,
|
||||
}
|
||||
zsel = (tlb->attr >> 4) & 0xF;
|
||||
zpr = (env->spr[SPR_40x_ZPR] >> (30 - (2 * zsel))) & 0x3;
|
||||
LOG_SWTLB("%s: TLB %d zsel %d zpr %d ty %d attr %08x\n",
|
||||
__func__, i, zsel, zpr, access_type, tlb->attr);
|
||||
qemu_log_mask(CPU_LOG_MMU,
|
||||
"%s: TLB %d zsel %d zpr %d ty %d attr %08x\n",
|
||||
__func__, i, zsel, zpr, access_type, tlb->attr);
|
||||
/* Check execute enable bit */
|
||||
switch (zpr) {
|
||||
case 0x2:
|
||||
@ -610,14 +595,16 @@ static int mmu40x_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx,
|
||||
}
|
||||
if (ret >= 0) {
|
||||
ctx->raddr = raddr;
|
||||
LOG_SWTLB("%s: access granted " TARGET_FMT_lx " => " TARGET_FMT_plx
|
||||
" %d %d\n", __func__, address, ctx->raddr, ctx->prot,
|
||||
ret);
|
||||
qemu_log_mask(CPU_LOG_MMU, "%s: access granted " TARGET_FMT_lx
|
||||
" => " TARGET_FMT_plx
|
||||
" %d %d\n", __func__, address, ctx->raddr, ctx->prot,
|
||||
ret);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
LOG_SWTLB("%s: access refused " TARGET_FMT_lx " => " TARGET_FMT_plx
|
||||
" %d %d\n", __func__, address, raddr, ctx->prot, ret);
|
||||
qemu_log_mask(CPU_LOG_MMU, "%s: access refused " TARGET_FMT_lx
|
||||
" => " TARGET_FMT_plx
|
||||
" %d %d\n", __func__, address, raddr, ctx->prot, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -646,7 +633,7 @@ static int mmubooke_check_tlb(CPUPPCState *env, ppcemb_tlb_t *tlb,
|
||||
goto found_tlb;
|
||||
}
|
||||
|
||||
LOG_SWTLB("%s: TLB entry not found\n", __func__);
|
||||
qemu_log_mask(CPU_LOG_MMU, "%s: TLB entry not found\n", __func__);
|
||||
return -1;
|
||||
|
||||
found_tlb:
|
||||
@ -659,17 +646,17 @@ found_tlb:
|
||||
|
||||
/* Check the address space */
|
||||
if ((access_type == MMU_INST_FETCH ? msr_ir : msr_dr) != (tlb->attr & 1)) {
|
||||
LOG_SWTLB("%s: AS doesn't match\n", __func__);
|
||||
qemu_log_mask(CPU_LOG_MMU, "%s: AS doesn't match\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
*prot = prot2;
|
||||
if (prot2 & prot_for_access_type(access_type)) {
|
||||
LOG_SWTLB("%s: good TLB!\n", __func__);
|
||||
qemu_log_mask(CPU_LOG_MMU, "%s: good TLB!\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
LOG_SWTLB("%s: no prot match: %x\n", __func__, prot2);
|
||||
qemu_log_mask(CPU_LOG_MMU, "%s: no prot match: %x\n", __func__, prot2);
|
||||
return access_type == MMU_INST_FETCH ? -3 : -2;
|
||||
}
|
||||
|
||||
@ -694,12 +681,13 @@ static int mmubooke_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx,
|
||||
|
||||
if (ret >= 0) {
|
||||
ctx->raddr = raddr;
|
||||
LOG_SWTLB("%s: access granted " TARGET_FMT_lx " => " TARGET_FMT_plx
|
||||
" %d %d\n", __func__, address, ctx->raddr, ctx->prot,
|
||||
ret);
|
||||
qemu_log_mask(CPU_LOG_MMU, "%s: access granted " TARGET_FMT_lx
|
||||
" => " TARGET_FMT_plx " %d %d\n", __func__,
|
||||
address, ctx->raddr, ctx->prot, ret);
|
||||
} else {
|
||||
LOG_SWTLB("%s: access refused " TARGET_FMT_lx " => " TARGET_FMT_plx
|
||||
" %d %d\n", __func__, address, raddr, ctx->prot, ret);
|
||||
qemu_log_mask(CPU_LOG_MMU, "%s: access refused " TARGET_FMT_lx
|
||||
" => " TARGET_FMT_plx " %d %d\n", __func__,
|
||||
address, raddr, ctx->prot, ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
@ -734,10 +722,11 @@ int ppcmas_tlb_check(CPUPPCState *env, ppcmas_tlb_t *tlb,
|
||||
}
|
||||
|
||||
mask = ~(booke206_tlb_to_page_size(env, tlb) - 1);
|
||||
LOG_SWTLB("%s: TLB ADDR=0x" TARGET_FMT_lx " PID=0x%x MAS1=0x%x MAS2=0x%"
|
||||
PRIx64 " mask=0x%" HWADDR_PRIx " MAS7_3=0x%" PRIx64 " MAS8=0x%"
|
||||
PRIx32 "\n", __func__, address, pid, tlb->mas1, tlb->mas2, mask,
|
||||
tlb->mas7_3, tlb->mas8);
|
||||
qemu_log_mask(CPU_LOG_MMU, "%s: TLB ADDR=0x" TARGET_FMT_lx
|
||||
" PID=0x%x MAS1=0x%x MAS2=0x%" PRIx64 " mask=0x%"
|
||||
HWADDR_PRIx " MAS7_3=0x%" PRIx64 " MAS8=0x%" PRIx32 "\n",
|
||||
__func__, address, pid, tlb->mas1, tlb->mas2, mask,
|
||||
tlb->mas7_3, tlb->mas8);
|
||||
|
||||
/* Check PID */
|
||||
tlb_pid = (tlb->mas1 & MAS1_TID_MASK) >> MAS1_TID_SHIFT;
|
||||
@ -838,7 +827,7 @@ static int mmubooke206_check_tlb(CPUPPCState *env, ppcmas_tlb_t *tlb,
|
||||
}
|
||||
}
|
||||
|
||||
LOG_SWTLB("%s: TLB entry not found\n", __func__);
|
||||
qemu_log_mask(CPU_LOG_MMU, "%s: TLB entry not found\n", __func__);
|
||||
return -1;
|
||||
|
||||
found_tlb:
|
||||
@ -873,17 +862,17 @@ found_tlb:
|
||||
}
|
||||
|
||||
if (as != ((tlb->mas1 & MAS1_TS) >> MAS1_TS_SHIFT)) {
|
||||
LOG_SWTLB("%s: AS doesn't match\n", __func__);
|
||||
qemu_log_mask(CPU_LOG_MMU, "%s: AS doesn't match\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
*prot = prot2;
|
||||
if (prot2 & prot_for_access_type(access_type)) {
|
||||
LOG_SWTLB("%s: good TLB!\n", __func__);
|
||||
qemu_log_mask(CPU_LOG_MMU, "%s: good TLB!\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
LOG_SWTLB("%s: no prot match: %x\n", __func__, prot2);
|
||||
qemu_log_mask(CPU_LOG_MMU, "%s: no prot match: %x\n", __func__, prot2);
|
||||
return access_type == MMU_INST_FETCH ? -3 : -2;
|
||||
}
|
||||
|
||||
@ -919,12 +908,13 @@ found_tlb:
|
||||
|
||||
if (ret >= 0) {
|
||||
ctx->raddr = raddr;
|
||||
LOG_SWTLB("%s: access granted " TARGET_FMT_lx " => " TARGET_FMT_plx
|
||||
" %d %d\n", __func__, address, ctx->raddr, ctx->prot,
|
||||
ret);
|
||||
qemu_log_mask(CPU_LOG_MMU, "%s: access granted " TARGET_FMT_lx
|
||||
" => " TARGET_FMT_plx " %d %d\n", __func__, address,
|
||||
ctx->raddr, ctx->prot, ret);
|
||||
} else {
|
||||
LOG_SWTLB("%s: access refused " TARGET_FMT_lx " => " TARGET_FMT_plx
|
||||
" %d %d\n", __func__, address, raddr, ctx->prot, ret);
|
||||
qemu_log_mask(CPU_LOG_MMU, "%s: access refused " TARGET_FMT_lx
|
||||
" => " TARGET_FMT_plx " %d %d\n", __func__, address,
|
||||
raddr, ctx->prot, ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
@ -1338,7 +1328,7 @@ static bool ppc_jumbo_xlate(PowerPCCPU *cpu, vaddr eaddr,
|
||||
}
|
||||
|
||||
if (guest_visible) {
|
||||
LOG_MMU_STATE(cs);
|
||||
log_cpu_state_mask(CPU_LOG_MMU, cs, 0);
|
||||
if (type == ACCESS_CODE) {
|
||||
switch (ret) {
|
||||
case -1:
|
||||
|
@ -36,23 +36,8 @@
|
||||
#include "exec/helper-proto.h"
|
||||
#include "exec/cpu_ldst.h"
|
||||
|
||||
/* #define DEBUG_BATS */
|
||||
/* #define DEBUG_SOFTWARE_TLB */
|
||||
/* #define DUMP_PAGE_TABLES */
|
||||
/* #define FLUSH_ALL_TLBS */
|
||||
|
||||
#ifdef DEBUG_SOFTWARE_TLB
|
||||
# define LOG_SWTLB(...) qemu_log_mask(CPU_LOG_MMU, __VA_ARGS__)
|
||||
#else
|
||||
# define LOG_SWTLB(...) do { } while (0)
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG_BATS
|
||||
# define LOG_BATS(...) qemu_log_mask(CPU_LOG_MMU, __VA_ARGS__)
|
||||
#else
|
||||
# define LOG_BATS(...) do { } while (0)
|
||||
#endif
|
||||
|
||||
/*****************************************************************************/
|
||||
/* PowerPC MMU emulation */
|
||||
|
||||
@ -89,8 +74,8 @@ static inline void ppc6xx_tlb_invalidate_virt2(CPUPPCState *env,
|
||||
nr = ppc6xx_tlb_getnum(env, eaddr, way, is_code);
|
||||
tlb = &env->tlb.tlb6[nr];
|
||||
if (pte_is_valid(tlb->pte0) && (match_epn == 0 || eaddr == tlb->EPN)) {
|
||||
LOG_SWTLB("TLB invalidate %d/%d " TARGET_FMT_lx "\n", nr,
|
||||
env->nb_tlb, eaddr);
|
||||
qemu_log_mask(CPU_LOG_MMU, "TLB invalidate %d/%d "
|
||||
TARGET_FMT_lx "\n", nr, env->nb_tlb, eaddr);
|
||||
pte_invalidate(&tlb->pte0);
|
||||
tlb_flush_page(cs, tlb->EPN);
|
||||
}
|
||||
@ -115,8 +100,9 @@ static void ppc6xx_tlb_store(CPUPPCState *env, target_ulong EPN, int way,
|
||||
|
||||
nr = ppc6xx_tlb_getnum(env, EPN, way, is_code);
|
||||
tlb = &env->tlb.tlb6[nr];
|
||||
LOG_SWTLB("Set TLB %d/%d EPN " TARGET_FMT_lx " PTE0 " TARGET_FMT_lx
|
||||
" PTE1 " TARGET_FMT_lx "\n", nr, env->nb_tlb, EPN, pte0, pte1);
|
||||
qemu_log_mask(CPU_LOG_MMU, "Set TLB %d/%d EPN " TARGET_FMT_lx " PTE0 "
|
||||
TARGET_FMT_lx " PTE1 " TARGET_FMT_lx "\n", nr, env->nb_tlb,
|
||||
EPN, pte0, pte1);
|
||||
/* Invalidate any pending reference in QEMU for this virtual address */
|
||||
ppc6xx_tlb_invalidate_virt2(env, EPN, is_code, 1);
|
||||
tlb->pte0 = pte0;
|
||||
@ -204,25 +190,27 @@ static inline void do_invalidate_BAT(CPUPPCState *env, target_ulong BATu,
|
||||
end = base + mask + 0x00020000;
|
||||
if (((end - base) >> TARGET_PAGE_BITS) > 1024) {
|
||||
/* Flushing 1024 4K pages is slower than a complete flush */
|
||||
LOG_BATS("Flush all BATs\n");
|
||||
qemu_log_mask(CPU_LOG_MMU, "Flush all BATs\n");
|
||||
tlb_flush(cs);
|
||||
LOG_BATS("Flush done\n");
|
||||
qemu_log_mask(CPU_LOG_MMU, "Flush done\n");
|
||||
return;
|
||||
}
|
||||
LOG_BATS("Flush BAT from " TARGET_FMT_lx " to " TARGET_FMT_lx " ("
|
||||
TARGET_FMT_lx ")\n", base, end, mask);
|
||||
qemu_log_mask(CPU_LOG_MMU, "Flush BAT from " TARGET_FMT_lx
|
||||
" to " TARGET_FMT_lx " (" TARGET_FMT_lx ")\n",
|
||||
base, end, mask);
|
||||
for (page = base; page != end; page += TARGET_PAGE_SIZE) {
|
||||
tlb_flush_page(cs, page);
|
||||
}
|
||||
LOG_BATS("Flush done\n");
|
||||
qemu_log_mask(CPU_LOG_MMU, "Flush done\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline void dump_store_bat(CPUPPCState *env, char ID, int ul, int nr,
|
||||
target_ulong value)
|
||||
{
|
||||
LOG_BATS("Set %cBAT%d%c to " TARGET_FMT_lx " (" TARGET_FMT_lx ")\n", ID,
|
||||
nr, ul == 0 ? 'u' : 'l', value, env->nip);
|
||||
qemu_log_mask(CPU_LOG_MMU, "Set %cBAT%d%c to " TARGET_FMT_lx " ("
|
||||
TARGET_FMT_lx ")\n", ID, nr, ul == 0 ? 'u' : 'l',
|
||||
value, env->nip);
|
||||
}
|
||||
|
||||
void helper_store_ibatu(CPUPPCState *env, uint32_t nr, target_ulong value)
|
||||
@ -550,9 +538,9 @@ static void do_6xx_tlb(CPUPPCState *env, target_ulong new_EPN, int is_code)
|
||||
}
|
||||
way = (env->spr[SPR_SRR1] >> 17) & 1;
|
||||
(void)EPN; /* avoid a compiler warning */
|
||||
LOG_SWTLB("%s: EPN " TARGET_FMT_lx " " TARGET_FMT_lx " PTE0 " TARGET_FMT_lx
|
||||
" PTE1 " TARGET_FMT_lx " way %d\n", __func__, new_EPN, EPN, CMP,
|
||||
RPN, way);
|
||||
qemu_log_mask(CPU_LOG_MMU, "%s: EPN " TARGET_FMT_lx " " TARGET_FMT_lx
|
||||
" PTE0 " TARGET_FMT_lx " PTE1 " TARGET_FMT_lx " way %d\n",
|
||||
__func__, new_EPN, EPN, CMP, RPN, way);
|
||||
/* Store this TLB */
|
||||
ppc6xx_tlb_store(env, (uint32_t)(new_EPN & TARGET_PAGE_MASK),
|
||||
way, is_code, CMP, RPN);
|
||||
@ -721,15 +709,17 @@ void helper_4xx_tlbwe_hi(CPUPPCState *env, target_ulong entry,
|
||||
ppcemb_tlb_t *tlb;
|
||||
target_ulong page, end;
|
||||
|
||||
LOG_SWTLB("%s entry %d val " TARGET_FMT_lx "\n", __func__, (int)entry,
|
||||
qemu_log_mask(CPU_LOG_MMU, "%s entry %d val " TARGET_FMT_lx "\n",
|
||||
__func__, (int)entry,
|
||||
val);
|
||||
entry &= PPC4XX_TLB_ENTRY_MASK;
|
||||
tlb = &env->tlb.tlbe[entry];
|
||||
/* Invalidate previous TLB (if it's valid) */
|
||||
if (tlb->prot & PAGE_VALID) {
|
||||
end = tlb->EPN + tlb->size;
|
||||
LOG_SWTLB("%s: invalidate old TLB %d start " TARGET_FMT_lx " end "
|
||||
TARGET_FMT_lx "\n", __func__, (int)entry, tlb->EPN, end);
|
||||
qemu_log_mask(CPU_LOG_MMU, "%s: invalidate old TLB %d start "
|
||||
TARGET_FMT_lx " end " TARGET_FMT_lx "\n", __func__,
|
||||
(int)entry, tlb->EPN, end);
|
||||
for (page = tlb->EPN; page < end; page += TARGET_PAGE_SIZE) {
|
||||
tlb_flush_page(cs, page);
|
||||
}
|
||||
@ -758,18 +748,20 @@ void helper_4xx_tlbwe_hi(CPUPPCState *env, target_ulong entry,
|
||||
tlb->prot &= ~PAGE_VALID;
|
||||
}
|
||||
tlb->PID = env->spr[SPR_40x_PID]; /* PID */
|
||||
LOG_SWTLB("%s: set up TLB %d RPN " TARGET_FMT_plx " EPN " TARGET_FMT_lx
|
||||
" size " TARGET_FMT_lx " prot %c%c%c%c PID %d\n", __func__,
|
||||
(int)entry, tlb->RPN, tlb->EPN, tlb->size,
|
||||
tlb->prot & PAGE_READ ? 'r' : '-',
|
||||
tlb->prot & PAGE_WRITE ? 'w' : '-',
|
||||
tlb->prot & PAGE_EXEC ? 'x' : '-',
|
||||
tlb->prot & PAGE_VALID ? 'v' : '-', (int)tlb->PID);
|
||||
qemu_log_mask(CPU_LOG_MMU, "%s: set up TLB %d RPN " TARGET_FMT_plx
|
||||
" EPN " TARGET_FMT_lx " size " TARGET_FMT_lx
|
||||
" prot %c%c%c%c PID %d\n", __func__,
|
||||
(int)entry, tlb->RPN, tlb->EPN, tlb->size,
|
||||
tlb->prot & PAGE_READ ? 'r' : '-',
|
||||
tlb->prot & PAGE_WRITE ? 'w' : '-',
|
||||
tlb->prot & PAGE_EXEC ? 'x' : '-',
|
||||
tlb->prot & PAGE_VALID ? 'v' : '-', (int)tlb->PID);
|
||||
/* Invalidate new TLB (if valid) */
|
||||
if (tlb->prot & PAGE_VALID) {
|
||||
end = tlb->EPN + tlb->size;
|
||||
LOG_SWTLB("%s: invalidate TLB %d start " TARGET_FMT_lx " end "
|
||||
TARGET_FMT_lx "\n", __func__, (int)entry, tlb->EPN, end);
|
||||
qemu_log_mask(CPU_LOG_MMU, "%s: invalidate TLB %d start "
|
||||
TARGET_FMT_lx " end " TARGET_FMT_lx "\n", __func__,
|
||||
(int)entry, tlb->EPN, end);
|
||||
for (page = tlb->EPN; page < end; page += TARGET_PAGE_SIZE) {
|
||||
tlb_flush_page(cs, page);
|
||||
}
|
||||
@ -781,8 +773,8 @@ void helper_4xx_tlbwe_lo(CPUPPCState *env, target_ulong entry,
|
||||
{
|
||||
ppcemb_tlb_t *tlb;
|
||||
|
||||
LOG_SWTLB("%s entry %i val " TARGET_FMT_lx "\n", __func__, (int)entry,
|
||||
val);
|
||||
qemu_log_mask(CPU_LOG_MMU, "%s entry %i val " TARGET_FMT_lx "\n",
|
||||
__func__, (int)entry, val);
|
||||
entry &= PPC4XX_TLB_ENTRY_MASK;
|
||||
tlb = &env->tlb.tlbe[entry];
|
||||
tlb->attr = val & PPC4XX_TLBLO_ATTR_MASK;
|
||||
@ -794,13 +786,14 @@ void helper_4xx_tlbwe_lo(CPUPPCState *env, target_ulong entry,
|
||||
if (val & PPC4XX_TLBLO_WR) {
|
||||
tlb->prot |= PAGE_WRITE;
|
||||
}
|
||||
LOG_SWTLB("%s: set up TLB %d RPN " TARGET_FMT_plx " EPN " TARGET_FMT_lx
|
||||
" size " TARGET_FMT_lx " prot %c%c%c%c PID %d\n", __func__,
|
||||
(int)entry, tlb->RPN, tlb->EPN, tlb->size,
|
||||
tlb->prot & PAGE_READ ? 'r' : '-',
|
||||
tlb->prot & PAGE_WRITE ? 'w' : '-',
|
||||
tlb->prot & PAGE_EXEC ? 'x' : '-',
|
||||
tlb->prot & PAGE_VALID ? 'v' : '-', (int)tlb->PID);
|
||||
qemu_log_mask(CPU_LOG_MMU, "%s: set up TLB %d RPN " TARGET_FMT_plx
|
||||
" EPN " TARGET_FMT_lx
|
||||
" size " TARGET_FMT_lx " prot %c%c%c%c PID %d\n", __func__,
|
||||
(int)entry, tlb->RPN, tlb->EPN, tlb->size,
|
||||
tlb->prot & PAGE_READ ? 'r' : '-',
|
||||
tlb->prot & PAGE_WRITE ? 'w' : '-',
|
||||
tlb->prot & PAGE_EXEC ? 'x' : '-',
|
||||
tlb->prot & PAGE_VALID ? 'v' : '-', (int)tlb->PID);
|
||||
}
|
||||
|
||||
target_ulong helper_4xx_tlbsx(CPUPPCState *env, target_ulong address)
|
||||
@ -816,8 +809,8 @@ void helper_440_tlbwe(CPUPPCState *env, uint32_t word, target_ulong entry,
|
||||
target_ulong EPN, RPN, size;
|
||||
int do_flush_tlbs;
|
||||
|
||||
LOG_SWTLB("%s word %d entry %d value " TARGET_FMT_lx "\n",
|
||||
__func__, word, (int)entry, value);
|
||||
qemu_log_mask(CPU_LOG_MMU, "%s word %d entry %d value " TARGET_FMT_lx "\n",
|
||||
__func__, word, (int)entry, value);
|
||||
do_flush_tlbs = 0;
|
||||
entry &= 0x3F;
|
||||
tlb = &env->tlb.tlbe[entry];
|
||||
|
@ -11,8 +11,6 @@
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
|
||||
#include "power8-pmu.h"
|
||||
#include "cpu.h"
|
||||
#include "helper_regs.h"
|
||||
#include "exec/exec-all.h"
|
||||
@ -20,24 +18,12 @@
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "hw/ppc/ppc.h"
|
||||
#include "power8-pmu.h"
|
||||
|
||||
#if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY)
|
||||
|
||||
#define PMC_COUNTER_NEGATIVE_VAL 0x80000000UL
|
||||
|
||||
static bool pmc_is_inactive(CPUPPCState *env, int sprn)
|
||||
{
|
||||
if (env->spr[SPR_POWER_MMCR0] & MMCR0_FC) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (sprn < SPR_POWER_PMC5) {
|
||||
return env->spr[SPR_POWER_MMCR0] & MMCR0_FC14;
|
||||
}
|
||||
|
||||
return env->spr[SPR_POWER_MMCR0] & MMCR0_FC56;
|
||||
}
|
||||
|
||||
static bool pmc_has_overflow_enabled(CPUPPCState *env, int sprn)
|
||||
{
|
||||
if (sprn == SPR_POWER_PMC1) {
|
||||
@ -47,135 +33,115 @@ static bool pmc_has_overflow_enabled(CPUPPCState *env, int sprn)
|
||||
return env->spr[SPR_POWER_MMCR0] & MMCR0_PMCjCE;
|
||||
}
|
||||
|
||||
/*
|
||||
* For PMCs 1-4, IBM POWER chips has support for an implementation
|
||||
* dependent event, 0x1E, that enables cycle counting. The Linux kernel
|
||||
* makes extensive use of 0x1E, so let's also support it.
|
||||
*
|
||||
* Likewise, event 0x2 is an implementation-dependent event that IBM
|
||||
* POWER chips implement (at least since POWER8) that is equivalent to
|
||||
* PM_INST_CMPL. Let's support this event on PMCs 1-4 as well.
|
||||
*/
|
||||
static PMUEventType pmc_get_event(CPUPPCState *env, int sprn)
|
||||
void pmu_update_summaries(CPUPPCState *env)
|
||||
{
|
||||
uint8_t mmcr1_evt_extr[] = { MMCR1_PMC1EVT_EXTR, MMCR1_PMC2EVT_EXTR,
|
||||
MMCR1_PMC3EVT_EXTR, MMCR1_PMC4EVT_EXTR };
|
||||
PMUEventType evt_type = PMU_EVENT_INVALID;
|
||||
uint8_t pmcsel;
|
||||
int i;
|
||||
target_ulong mmcr0 = env->spr[SPR_POWER_MMCR0];
|
||||
target_ulong mmcr1 = env->spr[SPR_POWER_MMCR1];
|
||||
int ins_cnt = 0;
|
||||
int cyc_cnt = 0;
|
||||
|
||||
if (pmc_is_inactive(env, sprn)) {
|
||||
return PMU_EVENT_INACTIVE;
|
||||
if (mmcr0 & MMCR0_FC) {
|
||||
goto hflags_calc;
|
||||
}
|
||||
|
||||
if (sprn == SPR_POWER_PMC5) {
|
||||
return PMU_EVENT_INSTRUCTIONS;
|
||||
}
|
||||
if (!(mmcr0 & MMCR0_FC14) && mmcr1 != 0) {
|
||||
target_ulong sel;
|
||||
|
||||
if (sprn == SPR_POWER_PMC6) {
|
||||
return PMU_EVENT_CYCLES;
|
||||
}
|
||||
|
||||
i = sprn - SPR_POWER_PMC1;
|
||||
pmcsel = extract64(env->spr[SPR_POWER_MMCR1], mmcr1_evt_extr[i],
|
||||
MMCR1_EVT_SIZE);
|
||||
|
||||
switch (pmcsel) {
|
||||
case 0x2:
|
||||
evt_type = PMU_EVENT_INSTRUCTIONS;
|
||||
break;
|
||||
case 0x1E:
|
||||
evt_type = PMU_EVENT_CYCLES;
|
||||
break;
|
||||
case 0xF0:
|
||||
/*
|
||||
* PMC1SEL = 0xF0 is the architected PowerISA v3.1
|
||||
* event that counts cycles using PMC1.
|
||||
*/
|
||||
if (sprn == SPR_POWER_PMC1) {
|
||||
evt_type = PMU_EVENT_CYCLES;
|
||||
sel = extract64(mmcr1, MMCR1_PMC1EVT_EXTR, MMCR1_EVT_SIZE);
|
||||
switch (sel) {
|
||||
case 0x02:
|
||||
case 0xfe:
|
||||
ins_cnt |= 1 << 1;
|
||||
break;
|
||||
case 0x1e:
|
||||
case 0xf0:
|
||||
cyc_cnt |= 1 << 1;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 0xFA:
|
||||
/*
|
||||
* PMC4SEL = 0xFA is the "instructions completed
|
||||
* with run latch set" event.
|
||||
*/
|
||||
if (sprn == SPR_POWER_PMC4) {
|
||||
evt_type = PMU_EVENT_INSN_RUN_LATCH;
|
||||
}
|
||||
break;
|
||||
case 0xFE:
|
||||
/*
|
||||
* PMC1SEL = 0xFE is the architected PowerISA v3.1
|
||||
* event to sample instructions using PMC1.
|
||||
*/
|
||||
if (sprn == SPR_POWER_PMC1) {
|
||||
evt_type = PMU_EVENT_INSTRUCTIONS;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
||||
sel = extract64(mmcr1, MMCR1_PMC2EVT_EXTR, MMCR1_EVT_SIZE);
|
||||
ins_cnt |= (sel == 0x02) << 2;
|
||||
cyc_cnt |= (sel == 0x1e) << 2;
|
||||
|
||||
sel = extract64(mmcr1, MMCR1_PMC3EVT_EXTR, MMCR1_EVT_SIZE);
|
||||
ins_cnt |= (sel == 0x02) << 3;
|
||||
cyc_cnt |= (sel == 0x1e) << 3;
|
||||
|
||||
sel = extract64(mmcr1, MMCR1_PMC4EVT_EXTR, MMCR1_EVT_SIZE);
|
||||
ins_cnt |= ((sel == 0xfa) || (sel == 0x2)) << 4;
|
||||
cyc_cnt |= (sel == 0x1e) << 4;
|
||||
}
|
||||
|
||||
return evt_type;
|
||||
}
|
||||
ins_cnt |= !(mmcr0 & MMCR0_FC56) << 5;
|
||||
cyc_cnt |= !(mmcr0 & MMCR0_FC56) << 6;
|
||||
|
||||
bool pmu_insn_cnt_enabled(CPUPPCState *env)
|
||||
{
|
||||
int sprn;
|
||||
|
||||
for (sprn = SPR_POWER_PMC1; sprn <= SPR_POWER_PMC5; sprn++) {
|
||||
if (pmc_get_event(env, sprn) == PMU_EVENT_INSTRUCTIONS ||
|
||||
pmc_get_event(env, sprn) == PMU_EVENT_INSN_RUN_LATCH) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
hflags_calc:
|
||||
env->pmc_ins_cnt = ins_cnt;
|
||||
env->pmc_cyc_cnt = cyc_cnt;
|
||||
env->hflags = deposit32(env->hflags, HFLAGS_INSN_CNT, 1, ins_cnt != 0);
|
||||
}
|
||||
|
||||
static bool pmu_increment_insns(CPUPPCState *env, uint32_t num_insns)
|
||||
{
|
||||
target_ulong mmcr0 = env->spr[SPR_POWER_MMCR0];
|
||||
unsigned ins_cnt = env->pmc_ins_cnt;
|
||||
bool overflow_triggered = false;
|
||||
int sprn;
|
||||
target_ulong tmp;
|
||||
|
||||
/* PMC6 never counts instructions */
|
||||
for (sprn = SPR_POWER_PMC1; sprn <= SPR_POWER_PMC5; sprn++) {
|
||||
PMUEventType evt_type = pmc_get_event(env, sprn);
|
||||
bool insn_event = evt_type == PMU_EVENT_INSTRUCTIONS ||
|
||||
evt_type == PMU_EVENT_INSN_RUN_LATCH;
|
||||
|
||||
if (pmc_is_inactive(env, sprn) || !insn_event) {
|
||||
continue;
|
||||
if (unlikely(ins_cnt & 0x1e)) {
|
||||
if (ins_cnt & (1 << 1)) {
|
||||
tmp = env->spr[SPR_POWER_PMC1];
|
||||
tmp += num_insns;
|
||||
if (tmp >= PMC_COUNTER_NEGATIVE_VAL && (mmcr0 & MMCR0_PMC1CE)) {
|
||||
tmp = PMC_COUNTER_NEGATIVE_VAL;
|
||||
overflow_triggered = true;
|
||||
}
|
||||
env->spr[SPR_POWER_PMC1] = tmp;
|
||||
}
|
||||
|
||||
if (evt_type == PMU_EVENT_INSTRUCTIONS) {
|
||||
env->spr[sprn] += num_insns;
|
||||
if (ins_cnt & (1 << 2)) {
|
||||
tmp = env->spr[SPR_POWER_PMC2];
|
||||
tmp += num_insns;
|
||||
if (tmp >= PMC_COUNTER_NEGATIVE_VAL && (mmcr0 & MMCR0_PMCjCE)) {
|
||||
tmp = PMC_COUNTER_NEGATIVE_VAL;
|
||||
overflow_triggered = true;
|
||||
}
|
||||
env->spr[SPR_POWER_PMC2] = tmp;
|
||||
}
|
||||
|
||||
if (evt_type == PMU_EVENT_INSN_RUN_LATCH &&
|
||||
env->spr[SPR_CTRL] & CTRL_RUN) {
|
||||
env->spr[sprn] += num_insns;
|
||||
if (ins_cnt & (1 << 3)) {
|
||||
tmp = env->spr[SPR_POWER_PMC3];
|
||||
tmp += num_insns;
|
||||
if (tmp >= PMC_COUNTER_NEGATIVE_VAL && (mmcr0 & MMCR0_PMCjCE)) {
|
||||
tmp = PMC_COUNTER_NEGATIVE_VAL;
|
||||
overflow_triggered = true;
|
||||
}
|
||||
env->spr[SPR_POWER_PMC3] = tmp;
|
||||
}
|
||||
|
||||
if (env->spr[sprn] >= PMC_COUNTER_NEGATIVE_VAL &&
|
||||
pmc_has_overflow_enabled(env, sprn)) {
|
||||
if (ins_cnt & (1 << 4)) {
|
||||
target_ulong mmcr1 = env->spr[SPR_POWER_MMCR1];
|
||||
int sel = extract64(mmcr1, MMCR1_PMC4EVT_EXTR, MMCR1_EVT_SIZE);
|
||||
if (sel == 0x02 || (env->spr[SPR_CTRL] & CTRL_RUN)) {
|
||||
tmp = env->spr[SPR_POWER_PMC4];
|
||||
tmp += num_insns;
|
||||
if (tmp >= PMC_COUNTER_NEGATIVE_VAL && (mmcr0 & MMCR0_PMCjCE)) {
|
||||
tmp = PMC_COUNTER_NEGATIVE_VAL;
|
||||
overflow_triggered = true;
|
||||
}
|
||||
env->spr[SPR_POWER_PMC4] = tmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ins_cnt & (1 << 5)) {
|
||||
tmp = env->spr[SPR_POWER_PMC5];
|
||||
tmp += num_insns;
|
||||
if (tmp >= PMC_COUNTER_NEGATIVE_VAL && (mmcr0 & MMCR0_PMCjCE)) {
|
||||
tmp = PMC_COUNTER_NEGATIVE_VAL;
|
||||
overflow_triggered = true;
|
||||
|
||||
/*
|
||||
* The real PMU will always trigger a counter overflow with
|
||||
* PMC_COUNTER_NEGATIVE_VAL. We don't have an easy way to
|
||||
* do that since we're counting block of instructions at
|
||||
* the end of each translation block, and we're probably
|
||||
* passing this value at this point.
|
||||
*
|
||||
* Let's write PMC_COUNTER_NEGATIVE_VAL to the overflowed
|
||||
* counter to simulate what the real hardware would do.
|
||||
*/
|
||||
env->spr[sprn] = PMC_COUNTER_NEGATIVE_VAL;
|
||||
}
|
||||
env->spr[SPR_POWER_PMC5] = tmp;
|
||||
}
|
||||
|
||||
return overflow_triggered;
|
||||
@ -185,18 +151,16 @@ static void pmu_update_cycles(CPUPPCState *env)
|
||||
{
|
||||
uint64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
uint64_t time_delta = now - env->pmu_base_time;
|
||||
int sprn;
|
||||
int sprn, cyc_cnt = env->pmc_cyc_cnt;
|
||||
|
||||
for (sprn = SPR_POWER_PMC1; sprn <= SPR_POWER_PMC6; sprn++) {
|
||||
if (pmc_get_event(env, sprn) != PMU_EVENT_CYCLES) {
|
||||
continue;
|
||||
if (cyc_cnt & (1 << (sprn - SPR_POWER_PMC1 + 1))) {
|
||||
/*
|
||||
* The pseries and powernv clock runs at 1Ghz, meaning
|
||||
* that 1 nanosec equals 1 cycle.
|
||||
*/
|
||||
env->spr[sprn] += time_delta;
|
||||
}
|
||||
|
||||
/*
|
||||
* The pseries and powernv clock runs at 1Ghz, meaning
|
||||
* that 1 nanosec equals 1 cycle.
|
||||
*/
|
||||
env->spr[sprn] += time_delta;
|
||||
}
|
||||
|
||||
/* Update base_time for future calculations */
|
||||
@ -225,7 +189,7 @@ static void pmc_update_overflow_timer(CPUPPCState *env, int sprn)
|
||||
return;
|
||||
}
|
||||
|
||||
if (pmc_get_event(env, sprn) != PMU_EVENT_CYCLES ||
|
||||
if (!(env->pmc_cyc_cnt & (1 << (sprn - SPR_POWER_PMC1 + 1))) ||
|
||||
!pmc_has_overflow_enabled(env, sprn)) {
|
||||
/* Overflow timer is not needed for this counter */
|
||||
timer_del(pmc_overflow_timer);
|
||||
@ -233,7 +197,7 @@ static void pmc_update_overflow_timer(CPUPPCState *env, int sprn)
|
||||
}
|
||||
|
||||
if (env->spr[sprn] >= PMC_COUNTER_NEGATIVE_VAL) {
|
||||
timeout = 0;
|
||||
timeout = 0;
|
||||
} else {
|
||||
timeout = PMC_COUNTER_NEGATIVE_VAL - env->spr[sprn];
|
||||
}
|
||||
@ -260,12 +224,18 @@ static void pmu_update_overflow_timers(CPUPPCState *env)
|
||||
|
||||
void helper_store_mmcr0(CPUPPCState *env, target_ulong value)
|
||||
{
|
||||
bool hflags_pmcc0 = (value & MMCR0_PMCC0) != 0;
|
||||
bool hflags_pmcc1 = (value & MMCR0_PMCC1) != 0;
|
||||
|
||||
pmu_update_cycles(env);
|
||||
|
||||
env->spr[SPR_POWER_MMCR0] = value;
|
||||
|
||||
/* MMCR0 writes can change HFLAGS_PMCCCLEAR and HFLAGS_INSN_CNT */
|
||||
hreg_compute_hflags(env);
|
||||
/* MMCR0 writes can change HFLAGS_PMCC[01] and HFLAGS_INSN_CNT */
|
||||
env->hflags = deposit32(env->hflags, HFLAGS_PMCC0, 1, hflags_pmcc0);
|
||||
env->hflags = deposit32(env->hflags, HFLAGS_PMCC1, 1, hflags_pmcc1);
|
||||
|
||||
pmu_update_summaries(env);
|
||||
|
||||
/* Update cycle overflow timers with the current MMCR0 state */
|
||||
pmu_update_overflow_timers(env);
|
||||
@ -278,7 +248,7 @@ void helper_store_mmcr1(CPUPPCState *env, uint64_t value)
|
||||
env->spr[SPR_POWER_MMCR1] = value;
|
||||
|
||||
/* MMCR1 writes can change HFLAGS_INSN_CNT */
|
||||
hreg_compute_hflags(env);
|
||||
pmu_update_summaries(env);
|
||||
}
|
||||
|
||||
target_ulong helper_read_pmc(CPUPPCState *env, uint32_t sprn)
|
||||
|
@ -13,14 +13,12 @@
|
||||
#ifndef POWER8_PMU
|
||||
#define POWER8_PMU
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "cpu.h"
|
||||
#include "exec/exec-all.h"
|
||||
#include "exec/helper-proto.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/main-loop.h"
|
||||
|
||||
void cpu_ppc_pmu_init(CPUPPCState *env);
|
||||
bool pmu_insn_cnt_enabled(CPUPPCState *env);
|
||||
|
||||
#if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY)
|
||||
void pmu_update_summaries(CPUPPCState *env);
|
||||
#else
|
||||
static inline void pmu_update_summaries(CPUPPCState *env) { }
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@ -87,6 +87,9 @@ void spr_read_40x_pit(DisasContext *ctx, int gprn, int sprn);
|
||||
void spr_write_40x_pit(DisasContext *ctx, int sprn, int gprn);
|
||||
void spr_write_40x_dbcr0(DisasContext *ctx, int sprn, int gprn);
|
||||
void spr_write_40x_sler(DisasContext *ctx, int sprn, int gprn);
|
||||
void spr_write_40x_tcr(DisasContext *ctx, int sprn, int gprn);
|
||||
void spr_write_40x_tsr(DisasContext *ctx, int sprn, int gprn);
|
||||
void spr_write_40x_pid(DisasContext *ctx, int sprn, int gprn);
|
||||
void spr_write_booke_tcr(DisasContext *ctx, int sprn, int gprn);
|
||||
void spr_write_booke_tsr(DisasContext *ctx, int sprn, int gprn);
|
||||
void spr_read_403_pbr(DisasContext *ctx, int gprn, int sprn);
|
||||
|
@ -144,6 +144,16 @@ void helper_store_40x_pit(CPUPPCState *env, target_ulong val)
|
||||
store_40x_pit(env, val);
|
||||
}
|
||||
|
||||
void helper_store_40x_tcr(CPUPPCState *env, target_ulong val)
|
||||
{
|
||||
store_40x_tcr(env, val);
|
||||
}
|
||||
|
||||
void helper_store_40x_tsr(CPUPPCState *env, target_ulong val)
|
||||
{
|
||||
store_40x_tsr(env, val);
|
||||
}
|
||||
|
||||
void helper_store_booke_tcr(CPUPPCState *env, target_ulong val)
|
||||
{
|
||||
store_booke_tcr(env, val);
|
||||
|
@ -878,6 +878,26 @@ void spr_write_40x_sler(DisasContext *ctx, int sprn, int gprn)
|
||||
gen_helper_store_40x_sler(cpu_env, cpu_gpr[gprn]);
|
||||
}
|
||||
|
||||
void spr_write_40x_tcr(DisasContext *ctx, int sprn, int gprn)
|
||||
{
|
||||
gen_icount_io_start(ctx);
|
||||
gen_helper_store_40x_tcr(cpu_env, cpu_gpr[gprn]);
|
||||
}
|
||||
|
||||
void spr_write_40x_tsr(DisasContext *ctx, int sprn, int gprn)
|
||||
{
|
||||
gen_icount_io_start(ctx);
|
||||
gen_helper_store_40x_tsr(cpu_env, cpu_gpr[gprn]);
|
||||
}
|
||||
|
||||
void spr_write_40x_pid(DisasContext *ctx, int sprn, int gprn)
|
||||
{
|
||||
TCGv t0 = tcg_temp_new();
|
||||
tcg_gen_andi_tl(t0, cpu_gpr[gprn], 0xFF);
|
||||
gen_store_spr(SPR_40x_PID, t0);
|
||||
tcg_temp_free(t0);
|
||||
}
|
||||
|
||||
void spr_write_booke_tcr(DisasContext *ctx, int sprn, int gprn)
|
||||
{
|
||||
gen_icount_io_start(ctx);
|
||||
|
@ -6,9 +6,9 @@ VPATH += $(SRC_PATH)/tests/tcg/ppc64
|
||||
VPATH += $(SRC_PATH)/tests/tcg/ppc64le
|
||||
|
||||
ifneq ($(DOCKER_IMAGE)$(CROSS_CC_HAS_POWER8_VECTOR),)
|
||||
PPC64_TESTS=bcdsub
|
||||
PPC64_TESTS=bcdsub non_signalling_xscv
|
||||
endif
|
||||
bcdsub: CFLAGS += -mpower8-vector
|
||||
$(PPC64_TESTS): CFLAGS += -mpower8-vector
|
||||
|
||||
PPC64_TESTS += byte_reverse
|
||||
PPC64_TESTS += mtfsf
|
||||
|
@ -5,9 +5,9 @@
|
||||
VPATH += $(SRC_PATH)/tests/tcg/ppc64le
|
||||
|
||||
ifneq ($(DOCKER_IMAGE)$(CROSS_CC_HAS_POWER8_VECTOR),)
|
||||
PPC64LE_TESTS=bcdsub
|
||||
PPC64LE_TESTS=bcdsub non_signalling_xscv
|
||||
endif
|
||||
bcdsub: CFLAGS += -mpower8-vector
|
||||
$(PPC64LE_TESTS): CFLAGS += -mpower8-vector
|
||||
|
||||
ifneq ($(DOCKER_IMAGE)$(CROSS_CC_HAS_POWER10),)
|
||||
PPC64LE_TESTS += byte_reverse
|
||||
|
37
tests/tcg/ppc64le/non_signalling_xscv.c
Normal file
37
tests/tcg/ppc64le/non_signalling_xscv.c
Normal file
@ -0,0 +1,37 @@
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <inttypes.h>
|
||||
#include <assert.h>
|
||||
|
||||
#define TEST(INSN, B_HI, B_LO, T_HI, T_LO) \
|
||||
do { \
|
||||
uint64_t th, tl, bh = B_HI, bl = B_LO; \
|
||||
asm("mtvsrd 0, %2\n\t" \
|
||||
"mtvsrd 1, %3\n\t" \
|
||||
"xxmrghd 0, 0, 1\n\t" \
|
||||
INSN " 0, 0\n\t" \
|
||||
"mfvsrd %0, 0\n\t" \
|
||||
"xxswapd 0, 0\n\t" \
|
||||
"mfvsrd %1, 0\n\t" \
|
||||
: "=r" (th), "=r" (tl) \
|
||||
: "r" (bh), "r" (bl) \
|
||||
: "vs0", "vs1"); \
|
||||
printf(INSN "(0x%016" PRIx64 "%016" PRIx64 ") = 0x%016" PRIx64 \
|
||||
"%016" PRIx64 "\n", bh, bl, th, tl); \
|
||||
assert(th == T_HI && tl == T_LO); \
|
||||
} while (0)
|
||||
|
||||
int main(void)
|
||||
{
|
||||
/* SNaN shouldn't be silenced */
|
||||
TEST("xscvspdpn", 0x7fbfffff00000000ULL, 0x0, 0x7ff7ffffe0000000ULL, 0x0);
|
||||
TEST("xscvdpspn", 0x7ff7ffffffffffffULL, 0x0, 0x7fbfffff7fbfffffULL, 0x0);
|
||||
|
||||
/*
|
||||
* SNaN inputs having no significant bits in the upper 23 bits of the
|
||||
* signifcand will return Infinity as the result.
|
||||
*/
|
||||
TEST("xscvdpspn", 0x7ff000001fffffffULL, 0x0, 0x7f8000007f800000ULL, 0x0);
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user