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:
Richard Henderson 2022-01-04 07:23:27 -08:00
commit 67e41fe0cf
30 changed files with 604 additions and 446 deletions

View File

@ -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);

View File

@ -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;
}

View File

@ -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)) {

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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);

View File

@ -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;

View File

@ -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;
}

View File

@ -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

View File

@ -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;
};

View File

@ -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);

View File

@ -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)

View File

@ -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);

View File

@ -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);
}
/*

View File

@ -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)

View File

@ -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

View File

@ -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)

View File

@ -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;
}

View File

@ -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

View File

@ -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:

View File

@ -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];

View File

@ -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)

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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

View 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;
}