aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--hw/ppc/spapr_hcall.c6
-rw-r--r--hw/ppc/spapr_iommu.c8
-rw-r--r--hw/vfio/common.c9
-rw-r--r--include/exec/memory.h19
-rw-r--r--linux-user/main.c1
-rw-r--r--memory.c16
-rw-r--r--target-ppc/cpu-qom.h9
-rw-r--r--target-ppc/cpu.h32
-rw-r--r--target-ppc/excp_helper.c263
-rw-r--r--target-ppc/helper.h2
-rw-r--r--target-ppc/kvm.c17
-rw-r--r--target-ppc/misc_helper.c41
-rw-r--r--target-ppc/mmu-hash64.c69
-rw-r--r--target-ppc/translate.c870
-rw-r--r--target-ppc/translate_init.c154
-rw-r--r--tests/Makefile.include8
16 files changed, 912 insertions, 612 deletions
diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c
index 2ba5cbd..e011ed4 100644
--- a/hw/ppc/spapr_hcall.c
+++ b/hw/ppc/spapr_hcall.c
@@ -102,11 +102,15 @@ static target_ulong h_enter(PowerPCCPU *cpu, sPAPRMachineState *spapr,
return H_PARAMETER;
}
} else {
+ target_ulong wimg_flags;
/* Looks like an IO address */
/* FIXME: What WIMG combinations could be sensible for IO?
* For now we allow WIMG=010x, but are there others? */
/* FIXME: Should we check against registered IO addresses? */
- if ((ptel & (HPTE64_R_W | HPTE64_R_I | HPTE64_R_M)) != HPTE64_R_I) {
+ wimg_flags = (ptel & (HPTE64_R_W | HPTE64_R_I | HPTE64_R_M));
+
+ if (wimg_flags != HPTE64_R_I &&
+ wimg_flags != (HPTE64_R_I | HPTE64_R_M)) {
return H_PARAMETER;
}
}
diff --git a/hw/ppc/spapr_iommu.c b/hw/ppc/spapr_iommu.c
index a3cc572..e230bac 100644
--- a/hw/ppc/spapr_iommu.c
+++ b/hw/ppc/spapr_iommu.c
@@ -149,6 +149,13 @@ static void spapr_tce_table_pre_save(void *opaque)
tcet->bus_offset, tcet->page_shift);
}
+static uint64_t spapr_tce_get_min_page_size(MemoryRegion *iommu)
+{
+ sPAPRTCETable *tcet = container_of(iommu, sPAPRTCETable, iommu);
+
+ return 1ULL << tcet->page_shift;
+}
+
static int spapr_tce_table_post_load(void *opaque, int version_id)
{
sPAPRTCETable *tcet = SPAPR_TCE_TABLE(opaque);
@@ -228,6 +235,7 @@ static const VMStateDescription vmstate_spapr_tce_table = {
static MemoryRegionIOMMUOps spapr_iommu_ops = {
.translate = spapr_tce_translate_iommu,
+ .get_min_page_size = spapr_tce_get_min_page_size,
};
static int spapr_tce_table_realize(DeviceState *dev)
diff --git a/hw/vfio/common.c b/hw/vfio/common.c
index 1898f1f..27cc159 100644
--- a/hw/vfio/common.c
+++ b/hw/vfio/common.c
@@ -321,11 +321,6 @@ out:
rcu_read_unlock();
}
-static hwaddr vfio_container_granularity(VFIOContainer *container)
-{
- return (hwaddr)1 << ctz64(container->iova_pgsizes);
-}
-
static void vfio_listener_region_add(MemoryListener *listener,
MemoryRegionSection *section)
{
@@ -392,9 +387,7 @@ static void vfio_listener_region_add(MemoryListener *listener,
QLIST_INSERT_HEAD(&container->giommu_list, giommu, giommu_next);
memory_region_register_iommu_notifier(giommu->iommu, &giommu->n);
- memory_region_iommu_replay(giommu->iommu, &giommu->n,
- vfio_container_granularity(container),
- false);
+ memory_region_iommu_replay(giommu->iommu, &giommu->n, false);
return;
}
diff --git a/include/exec/memory.h b/include/exec/memory.h
index 4ab6800..e3829f7 100644
--- a/include/exec/memory.h
+++ b/include/exec/memory.h
@@ -151,6 +151,8 @@ typedef struct MemoryRegionIOMMUOps MemoryRegionIOMMUOps;
struct MemoryRegionIOMMUOps {
/* Return a TLB entry that contains a given address. */
IOMMUTLBEntry (*translate)(MemoryRegion *iommu, hwaddr addr, bool is_write);
+ /* Returns minimum supported page size */
+ uint64_t (*get_min_page_size)(MemoryRegion *iommu);
};
typedef struct CoalescedMemoryRange CoalescedMemoryRange;
@@ -573,6 +575,16 @@ static inline bool memory_region_is_iommu(MemoryRegion *mr)
/**
+ * memory_region_iommu_get_min_page_size: get minimum supported page size
+ * for an iommu
+ *
+ * Returns minimum supported page size for an iommu.
+ *
+ * @mr: the memory region being queried
+ */
+uint64_t memory_region_iommu_get_min_page_size(MemoryRegion *mr);
+
+/**
* memory_region_notify_iommu: notify a change in an IOMMU translation entry.
*
* @mr: the memory region that was changed
@@ -596,16 +608,15 @@ void memory_region_register_iommu_notifier(MemoryRegion *mr, Notifier *n);
/**
* memory_region_iommu_replay: replay existing IOMMU translations to
- * a notifier
+ * a notifier with the minimum page granularity returned by
+ * mr->iommu_ops->get_page_size().
*
* @mr: the memory region to observe
* @n: the notifier to which to replay iommu mappings
- * @granularity: Minimum page granularity to replay notifications for
* @is_write: Whether to treat the replay as a translate "write"
* through the iommu
*/
-void memory_region_iommu_replay(MemoryRegion *mr, Notifier *n,
- hwaddr granularity, bool is_write);
+void memory_region_iommu_replay(MemoryRegion *mr, Notifier *n, bool is_write);
/**
* memory_region_unregister_iommu_notifier: unregister a notifier for
diff --git a/linux-user/main.c b/linux-user/main.c
index 358ed01..fd88e22 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -1721,6 +1721,7 @@ void cpu_loop(CPUPPCState *env)
queue_signal(env, info.si_signo, &info);
break;
case POWERPC_EXCP_PROGRAM: /* Program exception */
+ case POWERPC_EXCP_HV_EMU: /* HV emulation */
/* XXX: check this */
switch (env->error_code & ~0xF) {
case POWERPC_EXCP_FP:
diff --git a/memory.c b/memory.c
index 8ba496d..8549c79 100644
--- a/memory.c
+++ b/memory.c
@@ -1502,12 +1502,22 @@ void memory_region_register_iommu_notifier(MemoryRegion *mr, Notifier *n)
notifier_list_add(&mr->iommu_notify, n);
}
-void memory_region_iommu_replay(MemoryRegion *mr, Notifier *n,
- hwaddr granularity, bool is_write)
+uint64_t memory_region_iommu_get_min_page_size(MemoryRegion *mr)
{
- hwaddr addr;
+ assert(memory_region_is_iommu(mr));
+ if (mr->iommu_ops && mr->iommu_ops->get_min_page_size) {
+ return mr->iommu_ops->get_min_page_size(mr);
+ }
+ return TARGET_PAGE_SIZE;
+}
+
+void memory_region_iommu_replay(MemoryRegion *mr, Notifier *n, bool is_write)
+{
+ hwaddr addr, granularity;
IOMMUTLBEntry iotlb;
+ granularity = memory_region_iommu_get_min_page_size(mr);
+
for (addr = 0; addr < memory_region_size(mr); addr += granularity) {
iotlb = mr->iommu_ops->translate(mr, addr, is_write);
if (iotlb.perm != IOMMU_NONE) {
diff --git a/target-ppc/cpu-qom.h b/target-ppc/cpu-qom.h
index 969ecdf..0fad2de 100644
--- a/target-ppc/cpu-qom.h
+++ b/target-ppc/cpu-qom.h
@@ -126,6 +126,15 @@ enum powerpc_excp_t {
};
/*****************************************************************************/
+/* PM instructions */
+typedef enum {
+ PPC_PM_DOZE,
+ PPC_PM_NAP,
+ PPC_PM_SLEEP,
+ PPC_PM_RVWINKLE,
+} powerpc_pm_insn_t;
+
+/*****************************************************************************/
/* Input pins model */
typedef enum powerpc_input_t powerpc_input_t;
enum powerpc_input_t {
diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h
index 93c2dd5..b1354a4 100644
--- a/target-ppc/cpu.h
+++ b/target-ppc/cpu.h
@@ -116,6 +116,9 @@ enum {
POWERPC_EXCP_HYPPRIV = 41, /* Embedded hypervisor priv instruction */
/* Vectors 42 to 63 are reserved */
/* Exceptions defined in the PowerPC server specification */
+ /* Server doorbell variants */
+#define POWERPC_EXCP_SDOOR POWERPC_EXCP_GDOORI
+#define POWERPC_EXCP_SDOOR_HV POWERPC_EXCP_DOORI
POWERPC_EXCP_RESET = 64, /* System reset exception */
POWERPC_EXCP_DSEG = 65, /* Data segment exception */
POWERPC_EXCP_ISEG = 66, /* Instruction segment exception */
@@ -158,8 +161,12 @@ enum {
/* VSX Unavailable (Power ISA 2.06 and later) */
POWERPC_EXCP_VSXU = 94, /* VSX Unavailable */
POWERPC_EXCP_FU = 95, /* Facility Unavailable */
+ /* Additional ISA 2.06 and later server exceptions */
+ POWERPC_EXCP_HV_EMU = 96, /* HV emulation assistance */
+ POWERPC_EXCP_HV_MAINT = 97, /* HMI */
+ POWERPC_EXCP_HV_FU = 98, /* Hypervisor Facility unavailable */
/* EOL */
- POWERPC_EXCP_NB = 96,
+ POWERPC_EXCP_NB = 99,
/* QEMU exceptions: used internally during code translation */
POWERPC_EXCP_STOP = 0x200, /* stop translation */
POWERPC_EXCP_BRANCH = 0x201, /* branch instruction */
@@ -376,6 +383,14 @@ struct ppc_slb_t {
#define LPCR_LPES1 (1ull << (63 - 61))
#define LPCR_AIL_SHIFT (63 - 40) /* Alternate interrupt location */
#define LPCR_AIL (3ull << LPCR_AIL_SHIFT)
+#define LPCR_P7_PECE0 (1ull << (63 - 49))
+#define LPCR_P7_PECE1 (1ull << (63 - 50))
+#define LPCR_P7_PECE2 (1ull << (63 - 51))
+#define LPCR_P8_PECE0 (1ull << (63 - 47))
+#define LPCR_P8_PECE1 (1ull << (63 - 48))
+#define LPCR_P8_PECE2 (1ull << (63 - 49))
+#define LPCR_P8_PECE3 (1ull << (63 - 50))
+#define LPCR_P8_PECE4 (1ull << (63 - 51))
#define msr_sf ((env->msr >> MSR_SF) & 1)
#define msr_isf ((env->msr >> MSR_ISF) & 1)
@@ -1052,6 +1067,11 @@ struct CPUPPCState {
* instructions and SPRs are diallowed if MSR:HV is 0
*/
bool has_hv_mode;
+ /* On P7/P8, set when in PM state, we need to handle resume
+ * in a special way (such as routing some resume causes to
+ * 0x100), so flag this here.
+ */
+ bool in_pm_state;
#endif
/* Those resources are used only during code translation */
@@ -1905,6 +1925,8 @@ enum {
PPC_POPCNTB = 0x0000000000001000ULL,
/* string load / store */
PPC_STRING = 0x0000000000002000ULL,
+ /* real mode cache inhibited load / store */
+ PPC_CILDST = 0x0000000000004000ULL,
/* Floating-point unit extensions */
/* Optional floating point instructions */
@@ -2019,7 +2041,7 @@ enum {
| PPC_MFAPIDI | PPC_TLBIVA | PPC_TLBIVAX \
| PPC_4xx_COMMON | PPC_40x_ICBT | PPC_RFMCI \
| PPC_RFDI | PPC_DCR | PPC_DCRX | PPC_DCRUX \
- | PPC_POPCNTWD)
+ | PPC_POPCNTWD | PPC_CILDST)
/* extended type values */
@@ -2059,6 +2081,8 @@ enum {
PPC2_FP_CVT_S64 = 0x0000000000010000ULL,
/* Transactional Memory (ISA 2.07, Book II) */
PPC2_TM = 0x0000000000020000ULL,
+ /* Server PM instructgions (ISA 2.06, Book III) */
+ PPC2_PM_ISA206 = 0x0000000000040000ULL,
#define PPC_TCG_INSNS2 (PPC2_BOOKE206 | PPC2_VSX | PPC2_PRCNTL | PPC2_DBRX | \
PPC2_ISA205 | PPC2_VSX207 | PPC2_PERM_ISA206 | \
@@ -2066,7 +2090,7 @@ enum {
PPC2_FP_CVT_ISA206 | PPC2_FP_TST_ISA206 | \
PPC2_BCTAR_ISA207 | PPC2_LSQ_ISA207 | \
PPC2_ALTIVEC_207 | PPC2_ISA207S | PPC2_DFP | \
- PPC2_FP_CVT_S64 | PPC2_TM)
+ PPC2_FP_CVT_S64 | PPC2_TM | PPC2_PM_ISA206)
};
/*****************************************************************************/
@@ -2196,6 +2220,8 @@ enum {
PPC_INTERRUPT_CDOORBELL, /* Critical doorbell interrupt */
PPC_INTERRUPT_DOORBELL, /* Doorbell interrupt */
PPC_INTERRUPT_PERFM, /* Performance monitor interrupt */
+ PPC_INTERRUPT_HMI, /* Hypervisor Maintainance interrupt */
+ PPC_INTERRUPT_HDOORBELL, /* Hypervisor Doorbell interrupt */
};
/* Processor Compatibility mask (PCR) */
diff --git a/target-ppc/excp_helper.c b/target-ppc/excp_helper.c
index 30e960e..533866b 100644
--- a/target-ppc/excp_helper.c
+++ b/target-ppc/excp_helper.c
@@ -77,18 +77,8 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
CPUState *cs = CPU(cpu);
CPUPPCState *env = &cpu->env;
target_ulong msr, new_msr, vector;
- int srr0, srr1, asrr0, asrr1;
- int lpes0, lpes1, lev, ail;
-
- if (0) {
- /* XXX: find a suitable condition to enable the hypervisor mode */
- lpes0 = (env->spr[SPR_LPCR] >> 1) & 1;
- lpes1 = (env->spr[SPR_LPCR] >> 2) & 1;
- } else {
- /* Those values ensure we won't enter the hypervisor mode */
- lpes0 = 0;
- lpes1 = 1;
- }
+ int srr0, srr1, asrr0, asrr1, lev, ail;
+ bool lpes0;
qemu_log_mask(CPU_LOG_INT, "Raise exception at " TARGET_FMT_lx
" => %08x (%02x)\n", env->nip, excp, env->error_code);
@@ -100,8 +90,10 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
msr = env->msr & ~0x783f0000ULL;
}
- /* new interrupt handler msr */
- new_msr = env->msr & ((target_ulong)1 << MSR_ME);
+ /* new interrupt handler msr preserves existing HV and ME unless
+ * explicitly overriden
+ */
+ new_msr = env->msr & (((target_ulong)1 << MSR_ME) | MSR_HVB);
/* target registers */
srr0 = SPR_SRR0;
@@ -109,14 +101,59 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
asrr0 = -1;
asrr1 = -1;
+ /* check for special resume at 0x100 from doze/nap/sleep/winkle on P7/P8 */
+ if (env->in_pm_state) {
+ env->in_pm_state = false;
+
+ /* Pretend to be returning from doze always as we don't lose state */
+ msr |= (0x1ull << (63 - 47));
+
+ /* Non-machine check are routed to 0x100 with a wakeup cause
+ * encoded in SRR1
+ */
+ if (excp != POWERPC_EXCP_MCHECK) {
+ switch (excp) {
+ case POWERPC_EXCP_RESET:
+ msr |= 0x4ull << (63 - 45);
+ break;
+ case POWERPC_EXCP_EXTERNAL:
+ msr |= 0x8ull << (63 - 45);
+ break;
+ case POWERPC_EXCP_DECR:
+ msr |= 0x6ull << (63 - 45);
+ break;
+ case POWERPC_EXCP_SDOOR:
+ msr |= 0x5ull << (63 - 45);
+ break;
+ case POWERPC_EXCP_SDOOR_HV:
+ msr |= 0x3ull << (63 - 45);
+ break;
+ case POWERPC_EXCP_HV_MAINT:
+ msr |= 0xaull << (63 - 45);
+ break;
+ default:
+ cpu_abort(cs, "Unsupported exception %d in Power Save mode\n",
+ excp);
+ }
+ excp = POWERPC_EXCP_RESET;
+ }
+ }
+
/* Exception targetting modifiers
*
+ * LPES0 is supported on POWER7/8
+ * LPES1 is not supported (old iSeries mode)
+ *
+ * On anything else, we behave as if LPES0 is 1
+ * (externals don't alter MSR:HV)
+ *
* AIL is initialized here but can be cleared by
* selected exceptions
*/
#if defined(TARGET_PPC64)
if (excp_model == POWERPC_EXCP_POWER7 ||
excp_model == POWERPC_EXCP_POWER8) {
+ lpes0 = !!(env->spr[SPR_LPCR] & LPCR_LPES0);
if (excp_model == POWERPC_EXCP_POWER8) {
ail = (env->spr[SPR_LPCR] & LPCR_AIL) >> LPCR_AIL_SHIFT;
} else {
@@ -125,9 +162,23 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
} else
#endif /* defined(TARGET_PPC64) */
{
+ lpes0 = true;
ail = 0;
}
+ /* Hypervisor emulation assistance interrupt only exists on server
+ * arch 2.05 server or later. We also don't want to generate it if
+ * we don't have HVB in msr_mask (PAPR mode).
+ */
+ if (excp == POWERPC_EXCP_HV_EMU
+#if defined(TARGET_PPC64)
+ && !((env->mmu_model & POWERPC_MMU_64) && (env->msr_mask & MSR_HVB))
+#endif /* defined(TARGET_PPC64) */
+
+ ) {
+ excp = POWERPC_EXCP_PROGRAM;
+ }
+
switch (excp) {
case POWERPC_EXCP_NONE:
/* Should never happen */
@@ -162,10 +213,7 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
cs->halted = 1;
cs->interrupt_request |= CPU_INTERRUPT_EXITTB;
}
- if (0) {
- /* XXX: find a suitable condition to enable the hypervisor mode */
- new_msr |= (target_ulong)MSR_HVB;
- }
+ new_msr |= (target_ulong)MSR_HVB;
ail = 0;
/* machine check exceptions don't have ME set */
@@ -191,23 +239,20 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
case POWERPC_EXCP_DSI: /* Data storage exception */
LOG_EXCP("DSI exception: DSISR=" TARGET_FMT_lx" DAR=" TARGET_FMT_lx
"\n", env->spr[SPR_DSISR], env->spr[SPR_DAR]);
- if (lpes1 == 0) {
- new_msr |= (target_ulong)MSR_HVB;
- }
goto store_next;
case POWERPC_EXCP_ISI: /* Instruction storage exception */
LOG_EXCP("ISI exception: msr=" TARGET_FMT_lx ", nip=" TARGET_FMT_lx
"\n", msr, env->nip);
- if (lpes1 == 0) {
- new_msr |= (target_ulong)MSR_HVB;
- }
msr |= env->error_code;
goto store_next;
case POWERPC_EXCP_EXTERNAL: /* External input */
cs = CPU(cpu);
- if (lpes0 == 1) {
+ if (!lpes0) {
new_msr |= (target_ulong)MSR_HVB;
+ new_msr |= env->msr & ((target_ulong)1 << MSR_RI);
+ srr0 = SPR_HSRR0;
+ srr1 = SPR_HSRR1;
}
if (env->mpic_proxy) {
/* IACK the IRQ on delivery */
@@ -215,9 +260,6 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
}
goto store_next;
case POWERPC_EXCP_ALIGN: /* Alignment exception */
- if (lpes1 == 0) {
- new_msr |= (target_ulong)MSR_HVB;
- }
/* XXX: this is false */
/* Get rS/rD and rA from faulting opcode */
env->spr[SPR_DSISR] |= (cpu_ldl_code(env, (env->nip - 4))
@@ -232,9 +274,6 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
env->error_code = 0;
return;
}
- if (lpes1 == 0) {
- new_msr |= (target_ulong)MSR_HVB;
- }
msr |= 0x00100000;
if (msr_fe0 == msr_fe1) {
goto store_next;
@@ -243,23 +282,14 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
break;
case POWERPC_EXCP_INVAL:
LOG_EXCP("Invalid instruction at " TARGET_FMT_lx "\n", env->nip);
- if (lpes1 == 0) {
- new_msr |= (target_ulong)MSR_HVB;
- }
msr |= 0x00080000;
env->spr[SPR_BOOKE_ESR] = ESR_PIL;
break;
case POWERPC_EXCP_PRIV:
- if (lpes1 == 0) {
- new_msr |= (target_ulong)MSR_HVB;
- }
msr |= 0x00040000;
env->spr[SPR_BOOKE_ESR] = ESR_PPR;
break;
case POWERPC_EXCP_TRAP:
- if (lpes1 == 0) {
- new_msr |= (target_ulong)MSR_HVB;
- }
msr |= 0x00020000;
env->spr[SPR_BOOKE_ESR] = ESR_PTR;
break;
@@ -270,28 +300,30 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
break;
}
goto store_current;
+ case POWERPC_EXCP_HV_EMU:
+ srr0 = SPR_HSRR0;
+ srr1 = SPR_HSRR1;
+ new_msr |= (target_ulong)MSR_HVB;
+ new_msr |= env->msr & ((target_ulong)1 << MSR_RI);
+ goto store_current;
case POWERPC_EXCP_FPU: /* Floating-point unavailable exception */
- if (lpes1 == 0) {
- new_msr |= (target_ulong)MSR_HVB;
- }
goto store_current;
case POWERPC_EXCP_SYSCALL: /* System call exception */
dump_syscall(env);
lev = env->error_code;
+
+ /* "PAPR mode" built-in hypercall emulation */
if ((lev == 1) && cpu_ppc_hypercall) {
cpu_ppc_hypercall(cpu);
return;
}
- if (lev == 1 || (lpes0 == 0 && lpes1 == 0)) {
+ if (lev == 1) {
new_msr |= (target_ulong)MSR_HVB;
}
goto store_next;
case POWERPC_EXCP_APU: /* Auxiliary processor unavailable */
goto store_current;
case POWERPC_EXCP_DECR: /* Decrementer exception */
- if (lpes1 == 0) {
- new_msr |= (target_ulong)MSR_HVB;
- }
goto store_next;
case POWERPC_EXCP_FIT: /* Fixed-interval timer interrupt */
/* FIT on 4xx */
@@ -361,21 +393,12 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
new_msr &= ~((target_ulong)1 << MSR_ME);
}
- if (0) {
- /* XXX: find a suitable condition to enable the hypervisor mode */
- new_msr |= (target_ulong)MSR_HVB;
- }
+ new_msr |= (target_ulong)MSR_HVB;
ail = 0;
goto store_next;
case POWERPC_EXCP_DSEG: /* Data segment exception */
- if (lpes1 == 0) {
- new_msr |= (target_ulong)MSR_HVB;
- }
goto store_next;
case POWERPC_EXCP_ISEG: /* Instruction segment exception */
- if (lpes1 == 0) {
- new_msr |= (target_ulong)MSR_HVB;
- }
goto store_next;
case POWERPC_EXCP_HDECR: /* Hypervisor decrementer exception */
srr0 = SPR_HSRR0;
@@ -384,9 +407,6 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
new_msr |= env->msr & ((target_ulong)1 << MSR_RI);
goto store_next;
case POWERPC_EXCP_TRACE: /* Trace exception */
- if (lpes1 == 0) {
- new_msr |= (target_ulong)MSR_HVB;
- }
goto store_next;
case POWERPC_EXCP_HDSI: /* Hypervisor data storage exception */
srr0 = SPR_HSRR0;
@@ -413,19 +433,10 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
new_msr |= env->msr & ((target_ulong)1 << MSR_RI);
goto store_next;
case POWERPC_EXCP_VPU: /* Vector unavailable exception */
- if (lpes1 == 0) {
- new_msr |= (target_ulong)MSR_HVB;
- }
goto store_current;
case POWERPC_EXCP_VSXU: /* VSX unavailable exception */
- if (lpes1 == 0) {
- new_msr |= (target_ulong)MSR_HVB;
- }
goto store_current;
case POWERPC_EXCP_FU: /* Facility unavailable exception */
- if (lpes1 == 0) {
- new_msr |= (target_ulong)MSR_HVB;
- }
goto store_current;
case POWERPC_EXCP_PIT: /* Programmable interval timer interrupt */
LOG_EXCP("PIT exception\n");
@@ -444,9 +455,6 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
"is not implemented yet !\n");
goto store_next;
case POWERPC_EXCP_IFTLB: /* Instruction fetch TLB error */
- if (lpes1 == 0) { /* XXX: check this */
- new_msr |= (target_ulong)MSR_HVB;
- }
switch (excp_model) {
case POWERPC_EXCP_602:
case POWERPC_EXCP_603:
@@ -463,9 +471,6 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
}
break;
case POWERPC_EXCP_DLTLB: /* Data load TLB miss */
- if (lpes1 == 0) { /* XXX: check this */
- new_msr |= (target_ulong)MSR_HVB;
- }
switch (excp_model) {
case POWERPC_EXCP_602:
case POWERPC_EXCP_603:
@@ -482,9 +487,6 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
}
break;
case POWERPC_EXCP_DSTLB: /* Data store TLB miss */
- if (lpes1 == 0) { /* XXX: check this */
- new_msr |= (target_ulong)MSR_HVB;
- }
switch (excp_model) {
case POWERPC_EXCP_602:
case POWERPC_EXCP_603:
@@ -590,9 +592,6 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
"is not implemented yet !\n");
goto store_next;
case POWERPC_EXCP_PERFM: /* Embedded performance monitor interrupt */
- if (lpes1 == 0) {
- new_msr |= (target_ulong)MSR_HVB;
- }
/* XXX: TODO */
cpu_abort(cs,
"Performance counter exception is not implemented yet !\n");
@@ -636,6 +635,13 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
}
/* Save MSR */
env->spr[srr1] = msr;
+
+ /* Sanity check */
+ if (!(env->msr_mask & MSR_HVB) && (srr0 == SPR_HSRR0)) {
+ cpu_abort(cs, "Trying to deliver HV exception %d with "
+ "no HV support\n", excp);
+ }
+
/* If any alternate SRR register are defined, duplicate saved values */
if (asrr0 != -1) {
env->spr[asrr0] = env->spr[srr0];
@@ -644,14 +650,20 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
env->spr[asrr1] = env->spr[srr1];
}
- if (env->spr[SPR_LPCR] & LPCR_AIL) {
- new_msr |= (1 << MSR_IR) | (1 << MSR_DR);
- }
-
+ /* Sort out endianness of interrupt, this differs depending on the
+ * CPU, the HV mode, etc...
+ */
#ifdef TARGET_PPC64
- if (excp_model == POWERPC_EXCP_POWER7 ||
- excp_model == POWERPC_EXCP_POWER8) {
- if (env->spr[SPR_LPCR] & LPCR_ILE) {
+ if (excp_model == POWERPC_EXCP_POWER7) {
+ if (!(new_msr & MSR_HVB) && (env->spr[SPR_LPCR] & LPCR_ILE)) {
+ new_msr |= (target_ulong)1 << MSR_LE;
+ }
+ } else if (excp_model == POWERPC_EXCP_POWER8) {
+ if (new_msr & MSR_HVB) {
+ if (env->spr[SPR_HID0] & HID0_HILE) {
+ new_msr |= (target_ulong)1 << MSR_LE;
+ }
+ } else if (env->spr[SPR_LPCR] & LPCR_ILE) {
new_msr |= (target_ulong)1 << MSR_LE;
}
} else if (msr_ile) {
@@ -674,7 +686,8 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
/* AIL only works if there is no HV transition and we are running with
* translations enabled
*/
- if (!((msr >> MSR_IR) & 1) || !((msr >> MSR_DR) & 1)) {
+ if (!((msr >> MSR_IR) & 1) || !((msr >> MSR_DR) & 1) ||
+ ((new_msr & MSR_HVB) && !(msr & MSR_HVB))) {
ail = 0;
}
/* Handle AIL */
@@ -922,25 +935,41 @@ void helper_store_msr(CPUPPCState *env, target_ulong val)
}
}
-static inline void do_rfi(CPUPPCState *env, target_ulong nip, target_ulong msr,
- target_ulong msrm, int keep_msrh)
+#if defined(TARGET_PPC64)
+void helper_pminsn(CPUPPCState *env, powerpc_pm_insn_t insn)
+{
+ CPUState *cs;
+
+ cs = CPU(ppc_env_get_cpu(env));
+ cs->halted = 1;
+ env->in_pm_state = true;
+
+ /* Technically, nap doesn't set EE, but if we don't set it
+ * then ppc_hw_interrupt() won't deliver. We could add some
+ * other tests there based on LPCR but it's simpler to just
+ * whack EE in. It will be cleared by the 0x100 at wakeup
+ * anyway. It will still be observable by the guest in SRR1
+ * but this doesn't seem to be a problem.
+ */
+ env->msr |= (1ull << MSR_EE);
+ helper_raise_exception(env, EXCP_HLT);
+}
+#endif /* defined(TARGET_PPC64) */
+
+static inline void do_rfi(CPUPPCState *env, target_ulong nip, target_ulong msr)
{
CPUState *cs = CPU(ppc_env_get_cpu(env));
+ /* MSR:POW cannot be set by any form of rfi */
+ msr &= ~(1ULL << MSR_POW);
+
#if defined(TARGET_PPC64)
- if (msr_is_64bit(env, msr)) {
- nip = (uint64_t)nip;
- msr &= (uint64_t)msrm;
- } else {
+ /* Switching to 32-bit ? Crop the nip */
+ if (!msr_is_64bit(env, msr)) {
nip = (uint32_t)nip;
- msr = (uint32_t)(msr & msrm);
- if (keep_msrh) {
- msr |= env->msr & ~((uint64_t)0xFFFFFFFF);
- }
}
#else
nip = (uint32_t)nip;
- msr &= (uint32_t)msrm;
#endif
/* XXX: beware: this is false if VLE is supported */
env->nip = nip & ~((target_ulong)0x00000003);
@@ -959,26 +988,24 @@ static inline void do_rfi(CPUPPCState *env, target_ulong nip, target_ulong msr,
void helper_rfi(CPUPPCState *env)
{
- if (env->excp_model == POWERPC_EXCP_BOOKE) {
- do_rfi(env, env->spr[SPR_SRR0], env->spr[SPR_SRR1],
- ~((target_ulong)0), 0);
- } else {
- do_rfi(env, env->spr[SPR_SRR0], env->spr[SPR_SRR1],
- ~((target_ulong)0x783F0000), 1);
- }
+ do_rfi(env, env->spr[SPR_SRR0], env->spr[SPR_SRR1] & 0xfffffffful);
}
+#define MSR_BOOK3S_MASK
#if defined(TARGET_PPC64)
void helper_rfid(CPUPPCState *env)
{
- do_rfi(env, env->spr[SPR_SRR0], env->spr[SPR_SRR1],
- ~((target_ulong)0x783F0000), 0);
+ /* The architeture defines a number of rules for which bits
+ * can change but in practice, we handle this in hreg_store_msr()
+ * which will be called by do_rfi(), so there is no need to filter
+ * here
+ */
+ do_rfi(env, env->spr[SPR_SRR0], env->spr[SPR_SRR1]);
}
void helper_hrfid(CPUPPCState *env)
{
- do_rfi(env, env->spr[SPR_HSRR0], env->spr[SPR_HSRR1],
- ~((target_ulong)0x783F0000), 0);
+ do_rfi(env, env->spr[SPR_HSRR0], env->spr[SPR_HSRR1]);
}
#endif
@@ -986,28 +1013,24 @@ void helper_hrfid(CPUPPCState *env)
/* Embedded PowerPC specific helpers */
void helper_40x_rfci(CPUPPCState *env)
{
- do_rfi(env, env->spr[SPR_40x_SRR2], env->spr[SPR_40x_SRR3],
- ~((target_ulong)0xFFFF0000), 0);
+ do_rfi(env, env->spr[SPR_40x_SRR2], env->spr[SPR_40x_SRR3]);
}
void helper_rfci(CPUPPCState *env)
{
- do_rfi(env, env->spr[SPR_BOOKE_CSRR0], env->spr[SPR_BOOKE_CSRR1],
- ~((target_ulong)0), 0);
+ do_rfi(env, env->spr[SPR_BOOKE_CSRR0], env->spr[SPR_BOOKE_CSRR1]);
}
void helper_rfdi(CPUPPCState *env)
{
/* FIXME: choose CSRR1 or DSRR1 based on cpu type */
- do_rfi(env, env->spr[SPR_BOOKE_DSRR0], env->spr[SPR_BOOKE_DSRR1],
- ~((target_ulong)0), 0);
+ do_rfi(env, env->spr[SPR_BOOKE_DSRR0], env->spr[SPR_BOOKE_DSRR1]);
}
void helper_rfmci(CPUPPCState *env)
{
/* FIXME: choose CSRR1 or MCSRR1 based on cpu type */
- do_rfi(env, env->spr[SPR_BOOKE_MCSRR0], env->spr[SPR_BOOKE_MCSRR1],
- ~((target_ulong)0), 0);
+ do_rfi(env, env->spr[SPR_BOOKE_MCSRR0], env->spr[SPR_BOOKE_MCSRR1]);
}
#endif
@@ -1045,7 +1068,7 @@ void helper_td(CPUPPCState *env, target_ulong arg1, target_ulong arg2,
void helper_rfsvc(CPUPPCState *env)
{
- do_rfi(env, env->lr, env->ctr, 0x0000FFFF, 0);
+ do_rfi(env, env->lr, env->ctr & 0x0000FFFF);
}
/* Embedded.Processor Control */
diff --git a/target-ppc/helper.h b/target-ppc/helper.h
index f4410a8..5056ac2 100644
--- a/target-ppc/helper.h
+++ b/target-ppc/helper.h
@@ -13,6 +13,7 @@ DEF_HELPER_1(rfci, void, env)
DEF_HELPER_1(rfdi, void, env)
DEF_HELPER_1(rfmci, void, env)
#if defined(TARGET_PPC64)
+DEF_HELPER_2(pminsn, void, env, i32)
DEF_HELPER_1(rfid, void, env)
DEF_HELPER_1(hrfid, void, env)
#endif
@@ -670,3 +671,4 @@ DEF_HELPER_4(dscli, void, env, fprp, fprp, i32)
DEF_HELPER_4(dscliq, void, env, fprp, fprp, i32)
DEF_HELPER_1(tbegin, void, env)
+DEF_HELPER_1(fixup_thrm, void, env)
diff --git a/target-ppc/kvm.c b/target-ppc/kvm.c
index e14da60..884d564 100644
--- a/target-ppc/kvm.c
+++ b/target-ppc/kvm.c
@@ -27,6 +27,7 @@
#include "qemu/timer.h"
#include "sysemu/sysemu.h"
#include "sysemu/kvm.h"
+#include "sysemu/numa.h"
#include "kvm_ppc.h"
#include "sysemu/cpus.h"
#include "sysemu/device_tree.h"
@@ -388,7 +389,21 @@ static long getrampagesize(void)
object_child_foreach(memdev_root, find_max_supported_pagesize, &hpsize);
- return (hpsize == LONG_MAX) ? getpagesize() : hpsize;
+ if (hpsize == LONG_MAX) {
+ return getpagesize();
+ }
+
+ if (nb_numa_nodes == 0 && hpsize > getpagesize()) {
+ /* No NUMA nodes and normal RAM without -mem-path ==> no huge pages! */
+ static bool warned;
+ if (!warned) {
+ error_report("Huge page support disabled (n/a for main memory).");
+ warned = true;
+ }
+ return getpagesize();
+ }
+
+ return hpsize;
}
static bool kvm_valid_page_size(uint32_t flags, long rampgsize, uint32_t shift)
diff --git a/target-ppc/misc_helper.c b/target-ppc/misc_helper.c
index 7d41b01..cb5ebf5 100644
--- a/target-ppc/misc_helper.c
+++ b/target-ppc/misc_helper.c
@@ -166,3 +166,44 @@ void ppc_store_msr(CPUPPCState *env, target_ulong value)
{
hreg_store_msr(env, value, 0);
}
+
+/* This code is lifted from MacOnLinux. It is called whenever
+ * THRM1,2 or 3 is read an fixes up the values in such a way
+ * that will make MacOS not hang. These registers exist on some
+ * 75x and 74xx processors.
+ */
+void helper_fixup_thrm(CPUPPCState *env)
+{
+ target_ulong v, t;
+ int i;
+
+#define THRM1_TIN (1 << 31)
+#define THRM1_TIV (1 << 30)
+#define THRM1_THRES(x) (((x) & 0x7f) << 23)
+#define THRM1_TID (1 << 2)
+#define THRM1_TIE (1 << 1)
+#define THRM1_V (1 << 0)
+#define THRM3_E (1 << 0)
+
+ if (!(env->spr[SPR_THRM3] & THRM3_E)) {
+ return;
+ }
+
+ /* Note: Thermal interrupts are unimplemented */
+ for (i = SPR_THRM1; i <= SPR_THRM2; i++) {
+ v = env->spr[i];
+ if (!(v & THRM1_V)) {
+ continue;
+ }
+ v |= THRM1_TIV;
+ v &= ~THRM1_TIN;
+ t = v & THRM1_THRES(127);
+ if ((v & THRM1_TID) && t < THRM1_THRES(24)) {
+ v |= THRM1_TIN;
+ }
+ if (!(v & THRM1_TID) && t > THRM1_THRES(24)) {
+ v |= THRM1_TIN;
+ }
+ env->spr[i] = v;
+ }
+}
diff --git a/target-ppc/mmu-hash64.c b/target-ppc/mmu-hash64.c
index 668da5e..5b7b5e9 100644
--- a/target-ppc/mmu-hash64.c
+++ b/target-ppc/mmu-hash64.c
@@ -613,6 +613,47 @@ unsigned ppc_hash64_hpte_page_shift_noslb(PowerPCCPU *cpu,
return 0;
}
+static void ppc_hash64_set_isi(CPUState *cs, CPUPPCState *env,
+ uint64_t error_code)
+{
+ bool vpm;
+
+ if (msr_ir) {
+ vpm = !!(env->spr[SPR_LPCR] & LPCR_VPM1);
+ } else {
+ vpm = !!(env->spr[SPR_LPCR] & LPCR_VPM0);
+ }
+ if (vpm && !msr_hv) {
+ cs->exception_index = POWERPC_EXCP_HISI;
+ } else {
+ cs->exception_index = POWERPC_EXCP_ISI;
+ }
+ env->error_code = error_code;
+}
+
+static void ppc_hash64_set_dsi(CPUState *cs, CPUPPCState *env, uint64_t dar,
+ uint64_t dsisr)
+{
+ bool vpm;
+
+ if (msr_dr) {
+ vpm = !!(env->spr[SPR_LPCR] & LPCR_VPM1);
+ } else {
+ vpm = !!(env->spr[SPR_LPCR] & LPCR_VPM0);
+ }
+ if (vpm && !msr_hv) {
+ cs->exception_index = POWERPC_EXCP_HDSI;
+ env->spr[SPR_HDAR] = dar;
+ env->spr[SPR_HDSISR] = dsisr;
+ } else {
+ cs->exception_index = POWERPC_EXCP_DSI;
+ env->spr[SPR_DAR] = dar;
+ env->spr[SPR_DSISR] = dsisr;
+ }
+ env->error_code = 0;
+}
+
+
int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr,
int rwx, int mmu_idx)
{
@@ -623,7 +664,7 @@ int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr,
hwaddr pte_offset;
ppc_hash_pte64_t pte;
int pp_prot, amr_prot, prot;
- uint64_t new_pte1;
+ uint64_t new_pte1, dsisr;
const int need_prot[] = {PAGE_READ, PAGE_WRITE, PAGE_EXEC};
hwaddr raddr;
@@ -657,26 +698,21 @@ int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr,
/* 3. Check for segment level no-execute violation */
if ((rwx == 2) && (slb->vsid & SLB_VSID_N)) {
- cs->exception_index = POWERPC_EXCP_ISI;
- env->error_code = 0x10000000;
+ ppc_hash64_set_isi(cs, env, 0x10000000);
return 1;
}
/* 4. Locate the PTE in the hash table */
pte_offset = ppc_hash64_htab_lookup(cpu, slb, eaddr, &pte);
if (pte_offset == -1) {
+ dsisr = 0x40000000;
if (rwx == 2) {
- cs->exception_index = POWERPC_EXCP_ISI;
- env->error_code = 0x40000000;
+ ppc_hash64_set_isi(cs, env, dsisr);
} else {
- cs->exception_index = POWERPC_EXCP_DSI;
- env->error_code = 0;
- env->spr[SPR_DAR] = eaddr;
if (rwx == 1) {
- env->spr[SPR_DSISR] = 0x42000000;
- } else {
- env->spr[SPR_DSISR] = 0x40000000;
+ dsisr |= 0x02000000;
}
+ ppc_hash64_set_dsi(cs, env, eaddr, dsisr);
}
return 1;
}
@@ -705,14 +741,9 @@ int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr,
/* Access right violation */
qemu_log_mask(CPU_LOG_MMU, "PTE access rejected\n");
if (rwx == 2) {
- cs->exception_index = POWERPC_EXCP_ISI;
- env->error_code = 0x08000000;
+ ppc_hash64_set_isi(cs, env, 0x08000000);
} else {
- target_ulong dsisr = 0;
-
- cs->exception_index = POWERPC_EXCP_DSI;
- env->error_code = 0;
- env->spr[SPR_DAR] = eaddr;
+ dsisr = 0;
if (need_prot[rwx] & ~pp_prot) {
dsisr |= 0x08000000;
}
@@ -722,7 +753,7 @@ int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr,
if (need_prot[rwx] & ~amr_prot) {
dsisr |= 0x00200000;
}
- env->spr[SPR_DSISR] = dsisr;
+ ppc_hash64_set_dsi(cs, env, eaddr, dsisr);
}
return 1;
}
diff --git a/target-ppc/translate.c b/target-ppc/translate.c
index df4e0a3..2f1c591 100644
--- a/target-ppc/translate.c
+++ b/target-ppc/translate.c
@@ -193,22 +193,21 @@ struct DisasContext {
uint32_t opcode;
uint32_t exception;
/* Routine used to access memory */
- bool pr, hv;
+ bool pr, hv, dr, le_mode;
bool lazy_tlb_flush;
int mem_idx;
int access_type;
/* Translation flags */
- int le_mode;
TCGMemOp default_tcg_memop_mask;
#if defined(TARGET_PPC64)
- int sf_mode;
- int has_cfar;
-#endif
- int fpu_enabled;
- int altivec_enabled;
- int vsx_enabled;
- int spe_enabled;
- int tm_enabled;
+ bool sf_mode;
+ bool has_cfar;
+#endif
+ bool fpu_enabled;
+ bool altivec_enabled;
+ bool vsx_enabled;
+ bool spe_enabled;
+ bool tm_enabled;
ppc_spr_t *spr_cb; /* Needed to check rights for mfspr/mtspr */
int singlestep_enabled;
uint64_t insns_flags;
@@ -285,7 +284,7 @@ void gen_update_current_nip(void *opaque)
tcg_gen_movi_tl(cpu_nip, ctx->nip);
}
-static inline void gen_exception_err(DisasContext *ctx, uint32_t excp, uint32_t error)
+static void gen_exception_err(DisasContext *ctx, uint32_t excp, uint32_t error)
{
TCGv_i32 t0, t1;
if (ctx->exception == POWERPC_EXCP_NONE) {
@@ -299,7 +298,7 @@ static inline void gen_exception_err(DisasContext *ctx, uint32_t excp, uint32_t
ctx->exception = (excp);
}
-static inline void gen_exception(DisasContext *ctx, uint32_t excp)
+static void gen_exception(DisasContext *ctx, uint32_t excp)
{
TCGv_i32 t0;
if (ctx->exception == POWERPC_EXCP_NONE) {
@@ -311,7 +310,7 @@ static inline void gen_exception(DisasContext *ctx, uint32_t excp)
ctx->exception = (excp);
}
-static inline void gen_debug_exception(DisasContext *ctx)
+static void gen_debug_exception(DisasContext *ctx)
{
TCGv_i32 t0;
@@ -326,7 +325,19 @@ static inline void gen_debug_exception(DisasContext *ctx)
static inline void gen_inval_exception(DisasContext *ctx, uint32_t error)
{
- gen_exception_err(ctx, POWERPC_EXCP_PROGRAM, POWERPC_EXCP_INVAL | error);
+ /* Will be converted to program check if needed */
+ gen_exception_err(ctx, POWERPC_EXCP_HV_EMU, POWERPC_EXCP_INVAL | error);
+}
+
+static inline void gen_priv_exception(DisasContext *ctx, uint32_t error)
+{
+ gen_exception_err(ctx, POWERPC_EXCP_PROGRAM, POWERPC_EXCP_PRIV | error);
+}
+
+static inline void gen_hvpriv_exception(DisasContext *ctx, uint32_t error)
+{
+ /* Will be converted to program check if needed */
+ gen_exception_err(ctx, POWERPC_EXCP_HV_EMU, POWERPC_EXCP_PRIV | error);
}
/* Stop translation */
@@ -367,6 +378,40 @@ typedef struct opcode_t {
const char *oname;
} opcode_t;
+/* Helpers for priv. check */
+#define GEN_PRIV \
+ do { \
+ gen_priv_exception(ctx, POWERPC_EXCP_PRIV_OPC); return; \
+ } while (0)
+
+#if defined(CONFIG_USER_ONLY)
+#define CHK_HV GEN_PRIV
+#define CHK_SV GEN_PRIV
+#define CHK_HVRM GEN_PRIV
+#else
+#define CHK_HV \
+ do { \
+ if (unlikely(ctx->pr || !ctx->hv)) { \
+ GEN_PRIV; \
+ } \
+ } while (0)
+#define CHK_SV \
+ do { \
+ if (unlikely(ctx->pr)) { \
+ GEN_PRIV; \
+ } \
+ } while (0)
+#define CHK_HVRM \
+ do { \
+ if (unlikely(ctx->pr || !ctx->hv || ctx->dr)) { \
+ GEN_PRIV; \
+ } \
+ } while (0)
+#endif
+
+#define CHK_NONE
+
+
/*****************************************************************************/
/*** Instruction decoding ***/
#define EXTRACT_HELPER(name, shift, nb) \
@@ -1394,7 +1439,7 @@ GEN_LOGICAL2(nand, tcg_gen_nand_tl, 0x0E, PPC_INTEGER);
/* nor & nor. */
GEN_LOGICAL2(nor, tcg_gen_nor_tl, 0x03, PPC_INTEGER);
-#if defined(TARGET_PPC64)
+#if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY)
static void gen_pause(DisasContext *ctx)
{
TCGv_i32 t0 = tcg_const_i32(0);
@@ -1482,7 +1527,9 @@ static void gen_or(DisasContext *ctx)
/* Pause us out of TCG otherwise spin loops with smt_low
* eat too much CPU and the kernel hangs
*/
+#if !defined(CONFIG_USER_ONLY)
gen_pause(ctx);
+#endif
}
#endif
}
@@ -1694,9 +1741,7 @@ static void gen_rlwinm(DisasContext *ctx)
#endif
mask = MASK(mb, me);
- if (sh == 0) {
- tcg_gen_andi_tl(t_ra, t_rs, mask);
- } else if (mask <= 0xffffffffu) {
+ if (mask <= 0xffffffffu) {
TCGv_i32 t0 = tcg_temp_new_i32();
tcg_gen_trunc_tl_i32(t0, t_rs);
tcg_gen_rotli_i32(t0, t0, sh);
@@ -2888,18 +2933,23 @@ static void glue(gen_, name##ux)(DisasContext *ctx)
tcg_temp_free(EA); \
}
-#define GEN_LDX_E(name, ldop, opc2, opc3, type, type2) \
+#define GEN_LDX_E(name, ldop, opc2, opc3, type, type2, chk) \
static void glue(gen_, name##x)(DisasContext *ctx) \
{ \
TCGv EA; \
+ chk; \
gen_set_access_type(ctx, ACCESS_INT); \
EA = tcg_temp_new(); \
gen_addr_reg_index(ctx, EA); \
gen_qemu_##ldop(ctx, cpu_gpr[rD(ctx->opcode)], EA); \
tcg_temp_free(EA); \
}
+
#define GEN_LDX(name, ldop, opc2, opc3, type) \
- GEN_LDX_E(name, ldop, opc2, opc3, type, PPC_NONE)
+ GEN_LDX_E(name, ldop, opc2, opc3, type, PPC_NONE, CHK_NONE)
+
+#define GEN_LDX_HVRM(name, ldop, opc2, opc3, type) \
+ GEN_LDX_E(name, ldop, opc2, opc3, type, PPC_NONE, CHK_HVRM)
#define GEN_LDS(name, ldop, op, type) \
GEN_LD(name, ldop, op | 0x20, type); \
@@ -2925,6 +2975,12 @@ GEN_LDUX(ld, ld64, 0x15, 0x01, PPC_64B);
/* ldx */
GEN_LDX(ld, ld64, 0x15, 0x00, PPC_64B);
+/* CI load/store variants */
+GEN_LDX_HVRM(ldcix, ld64, 0x15, 0x1b, PPC_CILDST)
+GEN_LDX_HVRM(lwzcix, ld32u, 0x15, 0x15, PPC_CILDST)
+GEN_LDX_HVRM(lhzcix, ld16u, 0x15, 0x19, PPC_CILDST)
+GEN_LDX_HVRM(lbzcix, ld8u, 0x15, 0x1a, PPC_CILDST)
+
static void gen_ld(DisasContext *ctx)
{
TCGv EA;
@@ -2961,7 +3017,7 @@ static void gen_lq(DisasContext *ctx)
bool le_is_supported = (ctx->insns_flags2 & PPC2_LSQ_ISA207) != 0;
if (!legal_in_user_mode && ctx->pr) {
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ gen_priv_exception(ctx, POWERPC_EXCP_PRIV_OPC);
return;
}
@@ -3043,10 +3099,11 @@ static void glue(gen_, name##ux)(DisasContext *ctx)
tcg_temp_free(EA); \
}
-#define GEN_STX_E(name, stop, opc2, opc3, type, type2) \
+#define GEN_STX_E(name, stop, opc2, opc3, type, type2, chk) \
static void glue(gen_, name##x)(DisasContext *ctx) \
{ \
TCGv EA; \
+ chk; \
gen_set_access_type(ctx, ACCESS_INT); \
EA = tcg_temp_new(); \
gen_addr_reg_index(ctx, EA); \
@@ -3054,7 +3111,10 @@ static void glue(gen_, name##x)(DisasContext *ctx) \
tcg_temp_free(EA); \
}
#define GEN_STX(name, stop, opc2, opc3, type) \
- GEN_STX_E(name, stop, opc2, opc3, type, PPC_NONE)
+ GEN_STX_E(name, stop, opc2, opc3, type, PPC_NONE, CHK_NONE)
+
+#define GEN_STX_HVRM(name, stop, opc2, opc3, type) \
+ GEN_STX_E(name, stop, opc2, opc3, type, PPC_NONE, CHK_HVRM)
#define GEN_STS(name, stop, op, type) \
GEN_ST(name, stop, op | 0x20, type); \
@@ -3071,6 +3131,10 @@ GEN_STS(stw, st32, 0x04, PPC_INTEGER);
#if defined(TARGET_PPC64)
GEN_STUX(std, st64, 0x15, 0x05, PPC_64B);
GEN_STX(std, st64, 0x15, 0x04, PPC_64B);
+GEN_STX_HVRM(stdcix, st64, 0x15, 0x1f, PPC_CILDST)
+GEN_STX_HVRM(stwcix, st32, 0x15, 0x1c, PPC_CILDST)
+GEN_STX_HVRM(sthcix, st16, 0x15, 0x1d, PPC_CILDST)
+GEN_STX_HVRM(stbcix, st8, 0x15, 0x1e, PPC_CILDST)
static void gen_std(DisasContext *ctx)
{
@@ -3087,7 +3151,7 @@ static void gen_std(DisasContext *ctx)
}
if (!legal_in_user_mode && ctx->pr) {
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ gen_priv_exception(ctx, POWERPC_EXCP_PRIV_OPC);
return;
}
@@ -3159,7 +3223,7 @@ static inline void gen_qemu_ld64ur(DisasContext *ctx, TCGv arg1, TCGv arg2)
TCGMemOp op = MO_Q | (ctx->default_tcg_memop_mask ^ MO_BSWAP);
tcg_gen_qemu_ld_i64(arg1, arg2, ctx->mem_idx, op);
}
-GEN_LDX_E(ldbr, ld64ur, 0x14, 0x10, PPC_NONE, PPC2_DBRX);
+GEN_LDX_E(ldbr, ld64ur, 0x14, 0x10, PPC_NONE, PPC2_DBRX, CHK_NONE);
#endif /* TARGET_PPC64 */
/* sthbrx */
@@ -3185,7 +3249,7 @@ static inline void gen_qemu_st64r(DisasContext *ctx, TCGv arg1, TCGv arg2)
TCGMemOp op = MO_Q | (ctx->default_tcg_memop_mask ^ MO_BSWAP);
tcg_gen_qemu_st_i64(arg1, arg2, ctx->mem_idx, op);
}
-GEN_STX_E(stdbr, st64r, 0x14, 0x14, PPC_NONE, PPC2_DBRX);
+GEN_STX_E(stdbr, st64r, 0x14, 0x14, PPC_NONE, PPC2_DBRX, CHK_NONE);
#endif /* TARGET_PPC64 */
/*** Integer load and store multiple ***/
@@ -3539,6 +3603,68 @@ static void gen_wait(DisasContext *ctx)
gen_exception_err(ctx, EXCP_HLT, 1);
}
+#if defined(TARGET_PPC64)
+static void gen_doze(DisasContext *ctx)
+{
+#if defined(CONFIG_USER_ONLY)
+ GEN_PRIV;
+#else
+ TCGv_i32 t;
+
+ CHK_HV;
+ t = tcg_const_i32(PPC_PM_DOZE);
+ gen_helper_pminsn(cpu_env, t);
+ tcg_temp_free_i32(t);
+ gen_stop_exception(ctx);
+#endif /* defined(CONFIG_USER_ONLY) */
+}
+
+static void gen_nap(DisasContext *ctx)
+{
+#if defined(CONFIG_USER_ONLY)
+ GEN_PRIV;
+#else
+ TCGv_i32 t;
+
+ CHK_HV;
+ t = tcg_const_i32(PPC_PM_NAP);
+ gen_helper_pminsn(cpu_env, t);
+ tcg_temp_free_i32(t);
+ gen_stop_exception(ctx);
+#endif /* defined(CONFIG_USER_ONLY) */
+}
+
+static void gen_sleep(DisasContext *ctx)
+{
+#if defined(CONFIG_USER_ONLY)
+ GEN_PRIV;
+#else
+ TCGv_i32 t;
+
+ CHK_HV;
+ t = tcg_const_i32(PPC_PM_SLEEP);
+ gen_helper_pminsn(cpu_env, t);
+ tcg_temp_free_i32(t);
+ gen_stop_exception(ctx);
+#endif /* defined(CONFIG_USER_ONLY) */
+}
+
+static void gen_rvwinkle(DisasContext *ctx)
+{
+#if defined(CONFIG_USER_ONLY)
+ GEN_PRIV;
+#else
+ TCGv_i32 t;
+
+ CHK_HV;
+ t = tcg_const_i32(PPC_PM_RVWINKLE);
+ gen_helper_pminsn(cpu_env, t);
+ tcg_temp_free_i32(t);
+ gen_stop_exception(ctx);
+#endif /* defined(CONFIG_USER_ONLY) */
+}
+#endif /* #if defined(TARGET_PPC64) */
+
/*** Floating-point load ***/
#define GEN_LDF(name, ldop, opc, type) \
static void glue(gen_, name)(DisasContext *ctx) \
@@ -4117,13 +4243,14 @@ static void gen_mcrf(DisasContext *ctx)
static void gen_rfi(DisasContext *ctx)
{
#if defined(CONFIG_USER_ONLY)
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ GEN_PRIV;
#else
+ /* FIXME: This instruction doesn't exist anymore on 64-bit server
+ * processors compliant with arch 2.x, we should remove it there,
+ * but we need to fix OpenBIOS not to use it on 970 first
+ */
/* Restore CPU state */
- if (unlikely(ctx->pr)) {
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
- return;
- }
+ CHK_SV;
gen_update_cfar(ctx, ctx->nip);
gen_helper_rfi(cpu_env);
gen_sync_exception(ctx);
@@ -4134,13 +4261,10 @@ static void gen_rfi(DisasContext *ctx)
static void gen_rfid(DisasContext *ctx)
{
#if defined(CONFIG_USER_ONLY)
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ GEN_PRIV;
#else
/* Restore CPU state */
- if (unlikely(ctx->pr)) {
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
- return;
- }
+ CHK_SV;
gen_update_cfar(ctx, ctx->nip);
gen_helper_rfid(cpu_env);
gen_sync_exception(ctx);
@@ -4150,13 +4274,10 @@ static void gen_rfid(DisasContext *ctx)
static void gen_hrfid(DisasContext *ctx)
{
#if defined(CONFIG_USER_ONLY)
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ GEN_PRIV;
#else
/* Restore CPU state */
- if (unlikely(ctx->pr || !ctx->hv)) {
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
- return;
- }
+ CHK_HV;
gen_helper_hrfid(cpu_env);
gen_sync_exception(ctx);
#endif
@@ -4319,15 +4440,8 @@ static void gen_mfcr(DisasContext *ctx)
/* mfmsr */
static void gen_mfmsr(DisasContext *ctx)
{
-#if defined(CONFIG_USER_ONLY)
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
-#else
- if (unlikely(ctx->pr)) {
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
- return;
- }
+ CHK_SV;
tcg_gen_mov_tl(cpu_gpr[rD(ctx->opcode)], cpu_msr);
-#endif
}
static void spr_noaccess(DisasContext *ctx, int gprn, int sprn)
@@ -4373,9 +4487,15 @@ static inline void gen_op_mfspr(DisasContext *ctx)
TARGET_FMT_lx "\n", sprn, sprn, ctx->nip - 4);
}
}
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
+ gen_priv_exception(ctx, POWERPC_EXCP_PRIV_REG);
}
} else {
+ /* ISA 2.07 defines these as no-ops */
+ if ((ctx->insns_flags2 & PPC2_ISA207S) &&
+ (sprn >= 808 && sprn <= 811)) {
+ /* This is a nop */
+ return;
+ }
/* Not defined */
fprintf(stderr, "Trying to read invalid spr %d (0x%03x) at "
TARGET_FMT_lx "\n", sprn, sprn, ctx->nip - 4);
@@ -4383,9 +4503,18 @@ static inline void gen_op_mfspr(DisasContext *ctx)
qemu_log("Trying to read invalid spr %d (0x%03x) at "
TARGET_FMT_lx "\n", sprn, sprn, ctx->nip - 4);
}
- /* Only generate an exception in user space, otherwise this is a nop */
- if (ctx->pr) {
- gen_inval_exception(ctx, POWERPC_EXCP_INVAL_SPR);
+
+ /* The behaviour depends on MSR:PR and SPR# bit 0x10,
+ * it can generate a priv, a hv emu or a no-op
+ */
+ if (sprn & 0x10) {
+ if (ctx->pr) {
+ gen_priv_exception(ctx, POWERPC_EXCP_INVAL_SPR);
+ }
+ } else {
+ if (ctx->pr || sprn == 0 || sprn == 4 || sprn == 5 || sprn == 6) {
+ gen_hvpriv_exception(ctx, POWERPC_EXCP_INVAL_SPR);
+ }
}
}
}
@@ -4433,13 +4562,9 @@ static void gen_mtcrf(DisasContext *ctx)
#if defined(TARGET_PPC64)
static void gen_mtmsrd(DisasContext *ctx)
{
-#if defined(CONFIG_USER_ONLY)
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
-#else
- if (unlikely(ctx->pr)) {
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
- return;
- }
+ CHK_SV;
+
+#if !defined(CONFIG_USER_ONLY)
if (ctx->opcode & 0x00010000) {
/* Special form that does not need any synchronisation */
TCGv t0 = tcg_temp_new();
@@ -4458,20 +4583,16 @@ static void gen_mtmsrd(DisasContext *ctx)
/* Note that mtmsr is not always defined as context-synchronizing */
gen_stop_exception(ctx);
}
-#endif
+#endif /* !defined(CONFIG_USER_ONLY) */
}
-#endif
+#endif /* defined(TARGET_PPC64) */
static void gen_mtmsr(DisasContext *ctx)
{
-#if defined(CONFIG_USER_ONLY)
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
-#else
- if (unlikely(ctx->pr)) {
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
- return;
- }
- if (ctx->opcode & 0x00010000) {
+ CHK_SV;
+
+#if !defined(CONFIG_USER_ONLY)
+ if (ctx->opcode & 0x00010000) {
/* Special form that does not need any synchronisation */
TCGv t0 = tcg_temp_new();
tcg_gen_andi_tl(t0, cpu_gpr[rS(ctx->opcode)], (1 << MSR_RI) | (1 << MSR_EE));
@@ -4528,9 +4649,16 @@ static void gen_mtspr(DisasContext *ctx)
qemu_log("Trying to write privileged spr %d (0x%03x) at "
TARGET_FMT_lx "\n", sprn, sprn, ctx->nip - 4);
}
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
+ gen_priv_exception(ctx, POWERPC_EXCP_PRIV_REG);
}
} else {
+ /* ISA 2.07 defines these as no-ops */
+ if ((ctx->insns_flags2 & PPC2_ISA207S) &&
+ (sprn >= 808 && sprn <= 811)) {
+ /* This is a nop */
+ return;
+ }
+
/* Not defined */
if (qemu_log_separate()) {
qemu_log("Trying to write invalid spr %d (0x%03x) at "
@@ -4539,9 +4667,18 @@ static void gen_mtspr(DisasContext *ctx)
fprintf(stderr, "Trying to write invalid spr %d (0x%03x) at "
TARGET_FMT_lx "\n", sprn, sprn, ctx->nip - 4);
- /* Only generate an exception in user space, otherwise this is a nop */
- if (ctx->pr) {
- gen_inval_exception(ctx, POWERPC_EXCP_INVAL_SPR);
+
+ /* The behaviour depends on MSR:PR and SPR# bit 0x10,
+ * it can generate a priv, a hv emu or a no-op
+ */
+ if (sprn & 0x10) {
+ if (ctx->pr) {
+ gen_priv_exception(ctx, POWERPC_EXCP_INVAL_SPR);
+ }
+ } else {
+ if (ctx->pr || sprn == 0) {
+ gen_hvpriv_exception(ctx, POWERPC_EXCP_INVAL_SPR);
+ }
}
}
}
@@ -4564,13 +4701,11 @@ static void gen_dcbf(DisasContext *ctx)
static void gen_dcbi(DisasContext *ctx)
{
#if defined(CONFIG_USER_ONLY)
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ GEN_PRIV;
#else
TCGv EA, val;
- if (unlikely(ctx->pr)) {
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
- return;
- }
+
+ CHK_SV;
EA = tcg_temp_new();
gen_set_access_type(ctx, ACCESS_CACHE);
gen_addr_reg_index(ctx, EA);
@@ -4580,7 +4715,7 @@ static void gen_dcbi(DisasContext *ctx)
gen_qemu_st8(ctx, val, EA);
tcg_temp_free(val);
tcg_temp_free(EA);
-#endif
+#endif /* defined(CONFIG_USER_ONLY) */
}
/* dcdst */
@@ -4701,72 +4836,64 @@ static void gen_dcba(DisasContext *ctx)
static void gen_mfsr(DisasContext *ctx)
{
#if defined(CONFIG_USER_ONLY)
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
+ GEN_PRIV;
#else
TCGv t0;
- if (unlikely(ctx->pr)) {
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
- return;
- }
+
+ CHK_SV;
t0 = tcg_const_tl(SR(ctx->opcode));
gen_helper_load_sr(cpu_gpr[rD(ctx->opcode)], cpu_env, t0);
tcg_temp_free(t0);
-#endif
+#endif /* defined(CONFIG_USER_ONLY) */
}
/* mfsrin */
static void gen_mfsrin(DisasContext *ctx)
{
#if defined(CONFIG_USER_ONLY)
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
+ GEN_PRIV;
#else
TCGv t0;
- if (unlikely(ctx->pr)) {
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
- return;
- }
+
+ CHK_SV;
t0 = tcg_temp_new();
tcg_gen_shri_tl(t0, cpu_gpr[rB(ctx->opcode)], 28);
tcg_gen_andi_tl(t0, t0, 0xF);
gen_helper_load_sr(cpu_gpr[rD(ctx->opcode)], cpu_env, t0);
tcg_temp_free(t0);
-#endif
+#endif /* defined(CONFIG_USER_ONLY) */
}
/* mtsr */
static void gen_mtsr(DisasContext *ctx)
{
#if defined(CONFIG_USER_ONLY)
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
+ GEN_PRIV;
#else
TCGv t0;
- if (unlikely(ctx->pr)) {
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
- return;
- }
+
+ CHK_SV;
t0 = tcg_const_tl(SR(ctx->opcode));
gen_helper_store_sr(cpu_env, t0, cpu_gpr[rS(ctx->opcode)]);
tcg_temp_free(t0);
-#endif
+#endif /* defined(CONFIG_USER_ONLY) */
}
/* mtsrin */
static void gen_mtsrin(DisasContext *ctx)
{
#if defined(CONFIG_USER_ONLY)
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
+ GEN_PRIV;
#else
TCGv t0;
- if (unlikely(ctx->pr)) {
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
- return;
- }
+ CHK_SV;
+
t0 = tcg_temp_new();
tcg_gen_shri_tl(t0, cpu_gpr[rB(ctx->opcode)], 28);
tcg_gen_andi_tl(t0, t0, 0xF);
gen_helper_store_sr(cpu_env, t0, cpu_gpr[rD(ctx->opcode)]);
tcg_temp_free(t0);
-#endif
+#endif /* defined(CONFIG_USER_ONLY) */
}
#if defined(TARGET_PPC64)
@@ -4776,115 +4903,101 @@ static void gen_mtsrin(DisasContext *ctx)
static void gen_mfsr_64b(DisasContext *ctx)
{
#if defined(CONFIG_USER_ONLY)
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
+ GEN_PRIV;
#else
TCGv t0;
- if (unlikely(ctx->pr)) {
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
- return;
- }
+
+ CHK_SV;
t0 = tcg_const_tl(SR(ctx->opcode));
gen_helper_load_sr(cpu_gpr[rD(ctx->opcode)], cpu_env, t0);
tcg_temp_free(t0);
-#endif
+#endif /* defined(CONFIG_USER_ONLY) */
}
/* mfsrin */
static void gen_mfsrin_64b(DisasContext *ctx)
{
#if defined(CONFIG_USER_ONLY)
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
+ GEN_PRIV;
#else
TCGv t0;
- if (unlikely(ctx->pr)) {
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
- return;
- }
+
+ CHK_SV;
t0 = tcg_temp_new();
tcg_gen_shri_tl(t0, cpu_gpr[rB(ctx->opcode)], 28);
tcg_gen_andi_tl(t0, t0, 0xF);
gen_helper_load_sr(cpu_gpr[rD(ctx->opcode)], cpu_env, t0);
tcg_temp_free(t0);
-#endif
+#endif /* defined(CONFIG_USER_ONLY) */
}
/* mtsr */
static void gen_mtsr_64b(DisasContext *ctx)
{
#if defined(CONFIG_USER_ONLY)
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
+ GEN_PRIV;
#else
TCGv t0;
- if (unlikely(ctx->pr)) {
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
- return;
- }
+
+ CHK_SV;
t0 = tcg_const_tl(SR(ctx->opcode));
gen_helper_store_sr(cpu_env, t0, cpu_gpr[rS(ctx->opcode)]);
tcg_temp_free(t0);
-#endif
+#endif /* defined(CONFIG_USER_ONLY) */
}
/* mtsrin */
static void gen_mtsrin_64b(DisasContext *ctx)
{
#if defined(CONFIG_USER_ONLY)
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
+ GEN_PRIV;
#else
TCGv t0;
- if (unlikely(ctx->pr)) {
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
- return;
- }
+
+ CHK_SV;
t0 = tcg_temp_new();
tcg_gen_shri_tl(t0, cpu_gpr[rB(ctx->opcode)], 28);
tcg_gen_andi_tl(t0, t0, 0xF);
gen_helper_store_sr(cpu_env, t0, cpu_gpr[rS(ctx->opcode)]);
tcg_temp_free(t0);
-#endif
+#endif /* defined(CONFIG_USER_ONLY) */
}
/* slbmte */
static void gen_slbmte(DisasContext *ctx)
{
#if defined(CONFIG_USER_ONLY)
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
+ GEN_PRIV;
#else
- if (unlikely(ctx->pr)) {
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
- return;
- }
+ CHK_SV;
+
gen_helper_store_slb(cpu_env, cpu_gpr[rB(ctx->opcode)],
cpu_gpr[rS(ctx->opcode)]);
-#endif
+#endif /* defined(CONFIG_USER_ONLY) */
}
static void gen_slbmfee(DisasContext *ctx)
{
#if defined(CONFIG_USER_ONLY)
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
+ GEN_PRIV;
#else
- if (unlikely(ctx->pr)) {
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
- return;
- }
+ CHK_SV;
+
gen_helper_load_slb_esid(cpu_gpr[rS(ctx->opcode)], cpu_env,
cpu_gpr[rB(ctx->opcode)]);
-#endif
+#endif /* defined(CONFIG_USER_ONLY) */
}
static void gen_slbmfev(DisasContext *ctx)
{
#if defined(CONFIG_USER_ONLY)
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
+ GEN_PRIV;
#else
- if (unlikely(ctx->pr)) {
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
- return;
- }
+ CHK_SV;
+
gen_helper_load_slb_vsid(cpu_gpr[rS(ctx->opcode)], cpu_env,
cpu_gpr[rB(ctx->opcode)]);
-#endif
+#endif /* defined(CONFIG_USER_ONLY) */
}
static void gen_slbfee_(DisasContext *ctx)
@@ -4920,40 +5033,34 @@ static void gen_slbfee_(DisasContext *ctx)
static void gen_tlbia(DisasContext *ctx)
{
#if defined(CONFIG_USER_ONLY)
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ GEN_PRIV;
#else
- if (unlikely(ctx->pr || !ctx->hv)) {
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
- return;
- }
+ CHK_HV;
+
gen_helper_tlbia(cpu_env);
-#endif
+#endif /* defined(CONFIG_USER_ONLY) */
}
/* tlbiel */
static void gen_tlbiel(DisasContext *ctx)
{
#if defined(CONFIG_USER_ONLY)
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ GEN_PRIV;
#else
- if (unlikely(ctx->pr)) {
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
- return;
- }
+ CHK_SV;
+
gen_helper_tlbie(cpu_env, cpu_gpr[rB(ctx->opcode)]);
-#endif
+#endif /* defined(CONFIG_USER_ONLY) */
}
/* tlbie */
static void gen_tlbie(DisasContext *ctx)
{
#if defined(CONFIG_USER_ONLY)
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ GEN_PRIV;
#else
- if (unlikely(ctx->pr || !ctx->hv)) {
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
- return;
- }
+ CHK_HV;
+
if (NARROW_MODE(ctx)) {
TCGv t0 = tcg_temp_new();
tcg_gen_ext32u_tl(t0, cpu_gpr[rB(ctx->opcode)]);
@@ -4962,25 +5069,23 @@ static void gen_tlbie(DisasContext *ctx)
} else {
gen_helper_tlbie(cpu_env, cpu_gpr[rB(ctx->opcode)]);
}
-#endif
+#endif /* defined(CONFIG_USER_ONLY) */
}
/* tlbsync */
static void gen_tlbsync(DisasContext *ctx)
{
#if defined(CONFIG_USER_ONLY)
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ GEN_PRIV;
#else
- if (unlikely(ctx->pr || !ctx->hv)) {
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
- return;
- }
+ CHK_HV;
+
/* tlbsync is a nop for server, ptesync handles delayed tlb flush,
* embedded however needs to deal with tlbsync. We don't try to be
* fancy and swallow the overhead of checking for both.
*/
gen_check_tlb_flush(ctx);
-#endif
+#endif /* defined(CONFIG_USER_ONLY) */
}
#if defined(TARGET_PPC64)
@@ -4988,30 +5093,26 @@ static void gen_tlbsync(DisasContext *ctx)
static void gen_slbia(DisasContext *ctx)
{
#if defined(CONFIG_USER_ONLY)
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ GEN_PRIV;
#else
- if (unlikely(ctx->pr)) {
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
- return;
- }
+ CHK_SV;
+
gen_helper_slbia(cpu_env);
-#endif
+#endif /* defined(CONFIG_USER_ONLY) */
}
/* slbie */
static void gen_slbie(DisasContext *ctx)
{
#if defined(CONFIG_USER_ONLY)
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ GEN_PRIV;
#else
- if (unlikely(ctx->pr)) {
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
- return;
- }
+ CHK_SV;
+
gen_helper_slbie(cpu_env, cpu_gpr[rB(ctx->opcode)]);
-#endif
+#endif /* defined(CONFIG_USER_ONLY) */
}
-#endif
+#endif /* defined(TARGET_PPC64) */
/*** External control ***/
/* Optional: */
@@ -5710,14 +5811,11 @@ static void gen_esa(DisasContext *ctx)
static void gen_mfrom(DisasContext *ctx)
{
#if defined(CONFIG_USER_ONLY)
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ GEN_PRIV;
#else
- if (unlikely(ctx->pr)) {
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
- return;
- }
+ CHK_SV;
gen_helper_602_mfrom(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]);
-#endif
+#endif /* defined(CONFIG_USER_ONLY) */
}
/* 602 - 603 - G2 TLB management */
@@ -5726,28 +5824,22 @@ static void gen_mfrom(DisasContext *ctx)
static void gen_tlbld_6xx(DisasContext *ctx)
{
#if defined(CONFIG_USER_ONLY)
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ GEN_PRIV;
#else
- if (unlikely(ctx->pr)) {
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
- return;
- }
+ CHK_SV;
gen_helper_6xx_tlbd(cpu_env, cpu_gpr[rB(ctx->opcode)]);
-#endif
+#endif /* defined(CONFIG_USER_ONLY) */
}
/* tlbli */
static void gen_tlbli_6xx(DisasContext *ctx)
{
#if defined(CONFIG_USER_ONLY)
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ GEN_PRIV;
#else
- if (unlikely(ctx->pr)) {
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
- return;
- }
+ CHK_SV;
gen_helper_6xx_tlbi(cpu_env, cpu_gpr[rB(ctx->opcode)]);
-#endif
+#endif /* defined(CONFIG_USER_ONLY) */
}
/* 74xx TLB management */
@@ -5756,28 +5848,22 @@ static void gen_tlbli_6xx(DisasContext *ctx)
static void gen_tlbld_74xx(DisasContext *ctx)
{
#if defined(CONFIG_USER_ONLY)
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ GEN_PRIV;
#else
- if (unlikely(ctx->pr)) {
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
- return;
- }
+ CHK_SV;
gen_helper_74xx_tlbd(cpu_env, cpu_gpr[rB(ctx->opcode)]);
-#endif
+#endif /* defined(CONFIG_USER_ONLY) */
}
/* tlbli */
static void gen_tlbli_74xx(DisasContext *ctx)
{
#if defined(CONFIG_USER_ONLY)
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ GEN_PRIV;
#else
- if (unlikely(ctx->pr)) {
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
- return;
- }
+ CHK_SV;
gen_helper_74xx_tlbi(cpu_env, cpu_gpr[rB(ctx->opcode)]);
-#endif
+#endif /* defined(CONFIG_USER_ONLY) */
}
/* POWER instructions not in PowerPC 601 */
@@ -5791,15 +5877,12 @@ static void gen_clf(DisasContext *ctx)
/* cli */
static void gen_cli(DisasContext *ctx)
{
- /* Cache line invalidate: privileged and treated as no-op */
#if defined(CONFIG_USER_ONLY)
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ GEN_PRIV;
#else
- if (unlikely(ctx->pr)) {
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
- return;
- }
-#endif
+ /* Cache line invalidate: privileged and treated as no-op */
+ CHK_SV;
+#endif /* defined(CONFIG_USER_ONLY) */
}
/* dclst */
@@ -5811,15 +5894,13 @@ static void gen_dclst(DisasContext *ctx)
static void gen_mfsri(DisasContext *ctx)
{
#if defined(CONFIG_USER_ONLY)
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ GEN_PRIV;
#else
int ra = rA(ctx->opcode);
int rd = rD(ctx->opcode);
TCGv t0;
- if (unlikely(ctx->pr)) {
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
- return;
- }
+
+ CHK_SV;
t0 = tcg_temp_new();
gen_addr_reg_index(ctx, t0);
tcg_gen_shri_tl(t0, t0, 28);
@@ -5828,38 +5909,34 @@ static void gen_mfsri(DisasContext *ctx)
tcg_temp_free(t0);
if (ra != 0 && ra != rd)
tcg_gen_mov_tl(cpu_gpr[ra], cpu_gpr[rd]);
-#endif
+#endif /* defined(CONFIG_USER_ONLY) */
}
static void gen_rac(DisasContext *ctx)
{
#if defined(CONFIG_USER_ONLY)
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ GEN_PRIV;
#else
TCGv t0;
- if (unlikely(ctx->pr)) {
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
- return;
- }
+
+ CHK_SV;
t0 = tcg_temp_new();
gen_addr_reg_index(ctx, t0);
gen_helper_rac(cpu_gpr[rD(ctx->opcode)], cpu_env, t0);
tcg_temp_free(t0);
-#endif
+#endif /* defined(CONFIG_USER_ONLY) */
}
static void gen_rfsvc(DisasContext *ctx)
{
#if defined(CONFIG_USER_ONLY)
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ GEN_PRIV;
#else
- if (unlikely(ctx->pr)) {
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
- return;
- }
+ CHK_SV;
+
gen_helper_rfsvc(cpu_env);
gen_sync_exception(ctx);
-#endif
+#endif /* defined(CONFIG_USER_ONLY) */
}
/* svc is not implemented for now */
@@ -6012,18 +6089,16 @@ static void gen_mfapidi(DisasContext *ctx)
static void gen_tlbiva(DisasContext *ctx)
{
#if defined(CONFIG_USER_ONLY)
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ GEN_PRIV;
#else
TCGv t0;
- if (unlikely(ctx->pr)) {
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
- return;
- }
+
+ CHK_SV;
t0 = tcg_temp_new();
gen_addr_reg_index(ctx, t0);
gen_helper_tlbiva(cpu_env, cpu_gpr[rB(ctx->opcode)]);
tcg_temp_free(t0);
-#endif
+#endif /* defined(CONFIG_USER_ONLY) */
}
/* All 405 MAC instructions are translated here */
@@ -6245,38 +6320,34 @@ GEN_MAC_HANDLER(mullhwu, 0x08, 0x0C);
static void gen_mfdcr(DisasContext *ctx)
{
#if defined(CONFIG_USER_ONLY)
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
+ GEN_PRIV;
#else
TCGv dcrn;
- if (unlikely(ctx->pr)) {
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
- return;
- }
+
+ CHK_SV;
/* NIP cannot be restored if the memory exception comes from an helper */
gen_update_nip(ctx, ctx->nip - 4);
dcrn = tcg_const_tl(SPR(ctx->opcode));
gen_helper_load_dcr(cpu_gpr[rD(ctx->opcode)], cpu_env, dcrn);
tcg_temp_free(dcrn);
-#endif
+#endif /* defined(CONFIG_USER_ONLY) */
}
/* mtdcr */
static void gen_mtdcr(DisasContext *ctx)
{
#if defined(CONFIG_USER_ONLY)
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
+ GEN_PRIV;
#else
TCGv dcrn;
- if (unlikely(ctx->pr)) {
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
- return;
- }
+
+ CHK_SV;
/* NIP cannot be restored if the memory exception comes from an helper */
gen_update_nip(ctx, ctx->nip - 4);
dcrn = tcg_const_tl(SPR(ctx->opcode));
gen_helper_store_dcr(cpu_env, dcrn, cpu_gpr[rS(ctx->opcode)]);
tcg_temp_free(dcrn);
-#endif
+#endif /* defined(CONFIG_USER_ONLY) */
}
/* mfdcrx */
@@ -6284,18 +6355,15 @@ static void gen_mtdcr(DisasContext *ctx)
static void gen_mfdcrx(DisasContext *ctx)
{
#if defined(CONFIG_USER_ONLY)
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
+ GEN_PRIV;
#else
- if (unlikely(ctx->pr)) {
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
- return;
- }
+ CHK_SV;
/* NIP cannot be restored if the memory exception comes from an helper */
gen_update_nip(ctx, ctx->nip - 4);
gen_helper_load_dcr(cpu_gpr[rD(ctx->opcode)], cpu_env,
cpu_gpr[rA(ctx->opcode)]);
/* Note: Rc update flag set leads to undefined state of Rc0 */
-#endif
+#endif /* defined(CONFIG_USER_ONLY) */
}
/* mtdcrx */
@@ -6303,18 +6371,15 @@ static void gen_mfdcrx(DisasContext *ctx)
static void gen_mtdcrx(DisasContext *ctx)
{
#if defined(CONFIG_USER_ONLY)
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
+ GEN_PRIV;
#else
- if (unlikely(ctx->pr)) {
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
- return;
- }
+ CHK_SV;
/* NIP cannot be restored if the memory exception comes from an helper */
gen_update_nip(ctx, ctx->nip - 4);
gen_helper_store_dcr(cpu_env, cpu_gpr[rA(ctx->opcode)],
cpu_gpr[rS(ctx->opcode)]);
/* Note: Rc update flag set leads to undefined state of Rc0 */
-#endif
+#endif /* defined(CONFIG_USER_ONLY) */
}
/* mfdcrux (PPC 460) : user-mode access to DCR */
@@ -6340,28 +6405,19 @@ static void gen_mtdcrux(DisasContext *ctx)
/* dccci */
static void gen_dccci(DisasContext *ctx)
{
-#if defined(CONFIG_USER_ONLY)
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
-#else
- if (unlikely(ctx->pr)) {
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
- return;
- }
+ CHK_SV;
/* interpreted as no-op */
-#endif
}
/* dcread */
static void gen_dcread(DisasContext *ctx)
{
#if defined(CONFIG_USER_ONLY)
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ GEN_PRIV;
#else
TCGv EA, val;
- if (unlikely(ctx->pr)) {
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
- return;
- }
+
+ CHK_SV;
gen_set_access_type(ctx, ACCESS_CACHE);
EA = tcg_temp_new();
gen_addr_reg_index(ctx, EA);
@@ -6370,7 +6426,7 @@ static void gen_dcread(DisasContext *ctx)
tcg_temp_free(val);
tcg_gen_mov_tl(cpu_gpr[rD(ctx->opcode)], EA);
tcg_temp_free(EA);
-#endif
+#endif /* defined(CONFIG_USER_ONLY) */
}
/* icbt */
@@ -6385,60 +6441,40 @@ static void gen_icbt_40x(DisasContext *ctx)
/* iccci */
static void gen_iccci(DisasContext *ctx)
{
-#if defined(CONFIG_USER_ONLY)
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
-#else
- if (unlikely(ctx->pr)) {
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
- return;
- }
+ CHK_SV;
/* interpreted as no-op */
-#endif
}
/* icread */
static void gen_icread(DisasContext *ctx)
{
-#if defined(CONFIG_USER_ONLY)
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
-#else
- if (unlikely(ctx->pr)) {
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
- return;
- }
+ CHK_SV;
/* interpreted as no-op */
-#endif
}
/* rfci (supervisor only) */
static void gen_rfci_40x(DisasContext *ctx)
{
#if defined(CONFIG_USER_ONLY)
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ GEN_PRIV;
#else
- if (unlikely(ctx->pr)) {
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
- return;
- }
+ CHK_SV;
/* Restore CPU state */
gen_helper_40x_rfci(cpu_env);
gen_sync_exception(ctx);
-#endif
+#endif /* defined(CONFIG_USER_ONLY) */
}
static void gen_rfci(DisasContext *ctx)
{
#if defined(CONFIG_USER_ONLY)
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ GEN_PRIV;
#else
- if (unlikely(ctx->pr)) {
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
- return;
- }
+ CHK_SV;
/* Restore CPU state */
gen_helper_rfci(cpu_env);
gen_sync_exception(ctx);
-#endif
+#endif /* defined(CONFIG_USER_ONLY) */
}
/* BookE specific */
@@ -6447,32 +6483,26 @@ static void gen_rfci(DisasContext *ctx)
static void gen_rfdi(DisasContext *ctx)
{
#if defined(CONFIG_USER_ONLY)
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ GEN_PRIV;
#else
- if (unlikely(ctx->pr)) {
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
- return;
- }
+ CHK_SV;
/* Restore CPU state */
gen_helper_rfdi(cpu_env);
gen_sync_exception(ctx);
-#endif
+#endif /* defined(CONFIG_USER_ONLY) */
}
/* XXX: not implemented on 440 ? */
static void gen_rfmci(DisasContext *ctx)
{
#if defined(CONFIG_USER_ONLY)
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ GEN_PRIV;
#else
- if (unlikely(ctx->pr)) {
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
- return;
- }
+ CHK_SV;
/* Restore CPU state */
gen_helper_rfmci(cpu_env);
gen_sync_exception(ctx);
-#endif
+#endif /* defined(CONFIG_USER_ONLY) */
}
/* TLB management - PowerPC 405 implementation */
@@ -6481,12 +6511,9 @@ static void gen_rfmci(DisasContext *ctx)
static void gen_tlbre_40x(DisasContext *ctx)
{
#if defined(CONFIG_USER_ONLY)
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ GEN_PRIV;
#else
- if (unlikely(ctx->pr)) {
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
- return;
- }
+ CHK_SV;
switch (rB(ctx->opcode)) {
case 0:
gen_helper_4xx_tlbre_hi(cpu_gpr[rD(ctx->opcode)], cpu_env,
@@ -6500,20 +6527,18 @@ static void gen_tlbre_40x(DisasContext *ctx)
gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL);
break;
}
-#endif
+#endif /* defined(CONFIG_USER_ONLY) */
}
/* tlbsx - tlbsx. */
static void gen_tlbsx_40x(DisasContext *ctx)
{
#if defined(CONFIG_USER_ONLY)
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ GEN_PRIV;
#else
TCGv t0;
- if (unlikely(ctx->pr)) {
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
- return;
- }
+
+ CHK_SV;
t0 = tcg_temp_new();
gen_addr_reg_index(ctx, t0);
gen_helper_4xx_tlbsx(cpu_gpr[rD(ctx->opcode)], cpu_env, t0);
@@ -6525,19 +6550,17 @@ static void gen_tlbsx_40x(DisasContext *ctx)
tcg_gen_ori_i32(cpu_crf[0], cpu_crf[0], 0x02);
gen_set_label(l1);
}
-#endif
+#endif /* defined(CONFIG_USER_ONLY) */
}
/* tlbwe */
static void gen_tlbwe_40x(DisasContext *ctx)
{
#if defined(CONFIG_USER_ONLY)
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ GEN_PRIV;
#else
- if (unlikely(ctx->pr)) {
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
- return;
- }
+ CHK_SV;
+
switch (rB(ctx->opcode)) {
case 0:
gen_helper_4xx_tlbwe_hi(cpu_env, cpu_gpr[rA(ctx->opcode)],
@@ -6551,7 +6574,7 @@ static void gen_tlbwe_40x(DisasContext *ctx)
gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL);
break;
}
-#endif
+#endif /* defined(CONFIG_USER_ONLY) */
}
/* TLB management - PowerPC 440 implementation */
@@ -6560,12 +6583,10 @@ static void gen_tlbwe_40x(DisasContext *ctx)
static void gen_tlbre_440(DisasContext *ctx)
{
#if defined(CONFIG_USER_ONLY)
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ GEN_PRIV;
#else
- if (unlikely(ctx->pr)) {
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
- return;
- }
+ CHK_SV;
+
switch (rB(ctx->opcode)) {
case 0:
case 1:
@@ -6581,20 +6602,18 @@ static void gen_tlbre_440(DisasContext *ctx)
gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL);
break;
}
-#endif
+#endif /* defined(CONFIG_USER_ONLY) */
}
/* tlbsx - tlbsx. */
static void gen_tlbsx_440(DisasContext *ctx)
{
#if defined(CONFIG_USER_ONLY)
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ GEN_PRIV;
#else
TCGv t0;
- if (unlikely(ctx->pr)) {
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
- return;
- }
+
+ CHK_SV;
t0 = tcg_temp_new();
gen_addr_reg_index(ctx, t0);
gen_helper_440_tlbsx(cpu_gpr[rD(ctx->opcode)], cpu_env, t0);
@@ -6606,19 +6625,16 @@ static void gen_tlbsx_440(DisasContext *ctx)
tcg_gen_ori_i32(cpu_crf[0], cpu_crf[0], 0x02);
gen_set_label(l1);
}
-#endif
+#endif /* defined(CONFIG_USER_ONLY) */
}
/* tlbwe */
static void gen_tlbwe_440(DisasContext *ctx)
{
#if defined(CONFIG_USER_ONLY)
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ GEN_PRIV;
#else
- if (unlikely(ctx->pr)) {
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
- return;
- }
+ CHK_SV;
switch (rB(ctx->opcode)) {
case 0:
case 1:
@@ -6634,7 +6650,7 @@ static void gen_tlbwe_440(DisasContext *ctx)
gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL);
break;
}
-#endif
+#endif /* defined(CONFIG_USER_ONLY) */
}
/* TLB management - PowerPC BookE 2.06 implementation */
@@ -6642,30 +6658,23 @@ static void gen_tlbwe_440(DisasContext *ctx)
/* tlbre */
static void gen_tlbre_booke206(DisasContext *ctx)
{
-#if defined(CONFIG_USER_ONLY)
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ #if defined(CONFIG_USER_ONLY)
+ GEN_PRIV;
#else
- if (unlikely(ctx->pr)) {
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
- return;
- }
-
+ CHK_SV;
gen_helper_booke206_tlbre(cpu_env);
-#endif
+#endif /* defined(CONFIG_USER_ONLY) */
}
/* tlbsx - tlbsx. */
static void gen_tlbsx_booke206(DisasContext *ctx)
{
#if defined(CONFIG_USER_ONLY)
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ GEN_PRIV;
#else
TCGv t0;
- if (unlikely(ctx->pr)) {
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
- return;
- }
+ CHK_SV;
if (rA(ctx->opcode)) {
t0 = tcg_temp_new();
tcg_gen_mov_tl(t0, cpu_gpr[rD(ctx->opcode)]);
@@ -6676,54 +6685,44 @@ static void gen_tlbsx_booke206(DisasContext *ctx)
tcg_gen_add_tl(t0, t0, cpu_gpr[rB(ctx->opcode)]);
gen_helper_booke206_tlbsx(cpu_env, t0);
tcg_temp_free(t0);
-#endif
+#endif /* defined(CONFIG_USER_ONLY) */
}
/* tlbwe */
static void gen_tlbwe_booke206(DisasContext *ctx)
{
#if defined(CONFIG_USER_ONLY)
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ GEN_PRIV;
#else
- if (unlikely(ctx->pr)) {
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
- return;
- }
+ CHK_SV;
gen_update_nip(ctx, ctx->nip - 4);
gen_helper_booke206_tlbwe(cpu_env);
-#endif
+#endif /* defined(CONFIG_USER_ONLY) */
}
static void gen_tlbivax_booke206(DisasContext *ctx)
{
#if defined(CONFIG_USER_ONLY)
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ GEN_PRIV;
#else
TCGv t0;
- if (unlikely(ctx->pr)) {
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
- return;
- }
+ CHK_SV;
t0 = tcg_temp_new();
gen_addr_reg_index(ctx, t0);
-
gen_helper_booke206_tlbivax(cpu_env, t0);
tcg_temp_free(t0);
-#endif
+#endif /* defined(CONFIG_USER_ONLY) */
}
static void gen_tlbilx_booke206(DisasContext *ctx)
{
#if defined(CONFIG_USER_ONLY)
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ GEN_PRIV;
#else
TCGv t0;
- if (unlikely(ctx->pr)) {
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
- return;
- }
+ CHK_SV;
t0 = tcg_temp_new();
gen_addr_reg_index(ctx, t0);
@@ -6743,7 +6742,7 @@ static void gen_tlbilx_booke206(DisasContext *ctx)
}
tcg_temp_free(t0);
-#endif
+#endif /* defined(CONFIG_USER_ONLY) */
}
@@ -6751,13 +6750,11 @@ static void gen_tlbilx_booke206(DisasContext *ctx)
static void gen_wrtee(DisasContext *ctx)
{
#if defined(CONFIG_USER_ONLY)
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ GEN_PRIV;
#else
TCGv t0;
- if (unlikely(ctx->pr)) {
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
- return;
- }
+
+ CHK_SV;
t0 = tcg_temp_new();
tcg_gen_andi_tl(t0, cpu_gpr[rD(ctx->opcode)], (1 << MSR_EE));
tcg_gen_andi_tl(cpu_msr, cpu_msr, ~(1 << MSR_EE));
@@ -6767,19 +6764,16 @@ static void gen_wrtee(DisasContext *ctx)
* if we just set msr_ee to 1
*/
gen_stop_exception(ctx);
-#endif
+#endif /* defined(CONFIG_USER_ONLY) */
}
/* wrteei */
static void gen_wrteei(DisasContext *ctx)
{
#if defined(CONFIG_USER_ONLY)
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ GEN_PRIV;
#else
- if (unlikely(ctx->pr)) {
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
- return;
- }
+ CHK_SV;
if (ctx->opcode & 0x00008000) {
tcg_gen_ori_tl(cpu_msr, cpu_msr, (1 << MSR_EE));
/* Stop translation to have a chance to raise an exception */
@@ -6787,7 +6781,7 @@ static void gen_wrteei(DisasContext *ctx)
} else {
tcg_gen_andi_tl(cpu_msr, cpu_msr, ~(1 << MSR_EE));
}
-#endif
+#endif /* defined(CONFIG_USER_ONLY) */
}
/* PowerPC 440 specific instructions */
@@ -6827,29 +6821,21 @@ static void gen_icbt_440(DisasContext *ctx)
static void gen_msgclr(DisasContext *ctx)
{
#if defined(CONFIG_USER_ONLY)
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ GEN_PRIV;
#else
- if (unlikely(ctx->pr)) {
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
- return;
- }
-
+ CHK_SV;
gen_helper_msgclr(cpu_env, cpu_gpr[rB(ctx->opcode)]);
-#endif
+#endif /* defined(CONFIG_USER_ONLY) */
}
static void gen_msgsnd(DisasContext *ctx)
{
#if defined(CONFIG_USER_ONLY)
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ GEN_PRIV;
#else
- if (unlikely(ctx->pr)) {
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
- return;
- }
-
+ CHK_SV;
gen_helper_msgsnd(cpu_gpr[rB(ctx->opcode)]);
-#endif
+#endif /* defined(CONFIG_USER_ONLY) */
}
/*** Altivec vector extension ***/
@@ -9851,7 +9837,7 @@ static void gen_tcheck(DisasContext *ctx)
#define GEN_TM_PRIV_NOOP(name) \
static inline void gen_##name(DisasContext *ctx) \
{ \
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); \
+ gen_priv_exception(ctx, POWERPC_EXCP_PRIV_OPC); \
}
#else
@@ -9859,10 +9845,7 @@ static inline void gen_##name(DisasContext *ctx) \
#define GEN_TM_PRIV_NOOP(name) \
static inline void gen_##name(DisasContext *ctx) \
{ \
- if (unlikely(ctx->pr)) { \
- gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); \
- return; \
- } \
+ CHK_SV; \
if (unlikely(!ctx->tm_enabled)) { \
gen_exception_err(ctx, POWERPC_EXCP_FU, FSCR_IC_TM); \
return; \
@@ -9990,6 +9973,10 @@ GEN_HANDLER(mcrf, 0x13, 0x00, 0xFF, 0x00000001, PPC_INTEGER),
GEN_HANDLER(rfi, 0x13, 0x12, 0x01, 0x03FF8001, PPC_FLOW),
#if defined(TARGET_PPC64)
GEN_HANDLER(rfid, 0x13, 0x12, 0x00, 0x03FF8001, PPC_64B),
+GEN_HANDLER_E(doze, 0x13, 0x12, 0x0c, 0x03FFF801, PPC_NONE, PPC2_PM_ISA206),
+GEN_HANDLER_E(nap, 0x13, 0x12, 0x0d, 0x03FFF801, PPC_NONE, PPC2_PM_ISA206),
+GEN_HANDLER_E(sleep, 0x13, 0x12, 0x0e, 0x03FFF801, PPC_NONE, PPC2_PM_ISA206),
+GEN_HANDLER_E(rvwinkle, 0x13, 0x12, 0x0f, 0x03FFF801, PPC_NONE, PPC2_PM_ISA206),
GEN_HANDLER(hrfid, 0x13, 0x12, 0x08, 0x03FF8001, PPC_64H),
#endif
GEN_HANDLER(sc, 0x11, 0xFF, 0xFF, 0x03FFF01D, PPC_FLOW),
@@ -10342,7 +10329,7 @@ GEN_HANDLER(name, opc, 0xFF, 0xFF, 0x00000000, type),
GEN_HANDLER(name##u, opc, 0xFF, 0xFF, 0x00000000, type),
#define GEN_LDUX(name, ldop, opc2, opc3, type) \
GEN_HANDLER(name##ux, 0x1F, opc2, opc3, 0x00000001, type),
-#define GEN_LDX_E(name, ldop, opc2, opc3, type, type2) \
+#define GEN_LDX_E(name, ldop, opc2, opc3, type, type2, chk) \
GEN_HANDLER_E(name##x, 0x1F, opc2, opc3, 0x00000001, type, type2),
#define GEN_LDS(name, ldop, op, type) \
GEN_LD(name, ldop, op | 0x20, type) \
@@ -10359,7 +10346,13 @@ GEN_LDUX(lwa, ld32s, 0x15, 0x0B, PPC_64B)
GEN_LDX(lwa, ld32s, 0x15, 0x0A, PPC_64B)
GEN_LDUX(ld, ld64, 0x15, 0x01, PPC_64B)
GEN_LDX(ld, ld64, 0x15, 0x00, PPC_64B)
-GEN_LDX_E(ldbr, ld64ur, 0x14, 0x10, PPC_NONE, PPC2_DBRX)
+GEN_LDX_E(ldbr, ld64ur, 0x14, 0x10, PPC_NONE, PPC2_DBRX, CHK_NONE)
+
+/* HV/P7 and later only */
+GEN_LDX_HVRM(ldcix, ld64, 0x15, 0x1b, PPC_CILDST)
+GEN_LDX_HVRM(lwzcix, ld32u, 0x15, 0x18, PPC_CILDST)
+GEN_LDX_HVRM(lhzcix, ld16u, 0x15, 0x19, PPC_CILDST)
+GEN_LDX_HVRM(lbzcix, ld8u, 0x15, 0x1a, PPC_CILDST)
#endif
GEN_LDX(lhbr, ld16ur, 0x16, 0x18, PPC_INTEGER)
GEN_LDX(lwbr, ld32ur, 0x16, 0x10, PPC_INTEGER)
@@ -10375,7 +10368,7 @@ GEN_HANDLER(name, opc, 0xFF, 0xFF, 0x00000000, type),
GEN_HANDLER(stop##u, opc, 0xFF, 0xFF, 0x00000000, type),
#define GEN_STUX(name, stop, opc2, opc3, type) \
GEN_HANDLER(name##ux, 0x1F, opc2, opc3, 0x00000001, type),
-#define GEN_STX_E(name, stop, opc2, opc3, type, type2) \
+#define GEN_STX_E(name, stop, opc2, opc3, type, type2, chk) \
GEN_HANDLER_E(name##x, 0x1F, opc2, opc3, 0x00000001, type, type2),
#define GEN_STS(name, stop, op, type) \
GEN_ST(name, stop, op | 0x20, type) \
@@ -10389,7 +10382,11 @@ GEN_STS(stw, st32, 0x04, PPC_INTEGER)
#if defined(TARGET_PPC64)
GEN_STUX(std, st64, 0x15, 0x05, PPC_64B)
GEN_STX(std, st64, 0x15, 0x04, PPC_64B)
-GEN_STX_E(stdbr, st64r, 0x14, 0x14, PPC_NONE, PPC2_DBRX)
+GEN_STX_E(stdbr, st64r, 0x14, 0x14, PPC_NONE, PPC2_DBRX, CHK_NONE)
+GEN_STX_HVRM(stdcix, st64, 0x15, 0x1f, PPC_CILDST)
+GEN_STX_HVRM(stwcix, st32, 0x15, 0x1c, PPC_CILDST)
+GEN_STX_HVRM(sthcix, st16, 0x15, 0x1d, PPC_CILDST)
+GEN_STX_HVRM(stbcix, st8, 0x15, 0x1e, PPC_CILDST)
#endif
GEN_STX(sthbr, st16r, 0x16, 0x1C, PPC_INTEGER)
GEN_STX(stwbr, st32r, 0x16, 0x14, PPC_INTEGER)
@@ -11557,13 +11554,14 @@ void gen_intermediate_code(CPUPPCState *env, struct TranslationBlock *tb)
ctx.spr_cb = env->spr_cb;
ctx.pr = msr_pr;
ctx.mem_idx = env->dmmu_idx;
+ ctx.dr = msr_dr;
#if !defined(CONFIG_USER_ONLY)
ctx.hv = msr_hv || !env->has_hv_mode;
#endif
ctx.insns_flags = env->insns_flags;
ctx.insns_flags2 = env->insns_flags2;
ctx.access_type = -1;
- ctx.le_mode = env->hflags & (1 << MSR_LE) ? 1 : 0;
+ ctx.le_mode = !!(env->hflags & (1 << MSR_LE));
ctx.default_tcg_memop_mask = ctx.le_mode ? MO_LE : MO_BE;
#if defined(TARGET_PPC64)
ctx.sf_mode = msr_is_64bit(env, env->msr);
@@ -11574,25 +11572,25 @@ void gen_intermediate_code(CPUPPCState *env, struct TranslationBlock *tb)
(env->mmu_model & POWERPC_MMU_64B))
ctx.lazy_tlb_flush = true;
- ctx.fpu_enabled = msr_fp;
+ ctx.fpu_enabled = !!msr_fp;
if ((env->flags & POWERPC_FLAG_SPE) && msr_spe)
- ctx.spe_enabled = msr_spe;
+ ctx.spe_enabled = !!msr_spe;
else
- ctx.spe_enabled = 0;
+ ctx.spe_enabled = false;
if ((env->flags & POWERPC_FLAG_VRE) && msr_vr)
- ctx.altivec_enabled = msr_vr;
+ ctx.altivec_enabled = !!msr_vr;
else
- ctx.altivec_enabled = 0;
+ ctx.altivec_enabled = false;
if ((env->flags & POWERPC_FLAG_VSX) && msr_vsx) {
- ctx.vsx_enabled = msr_vsx;
+ ctx.vsx_enabled = !!msr_vsx;
} else {
- ctx.vsx_enabled = 0;
+ ctx.vsx_enabled = false;
}
#if defined(TARGET_PPC64)
if ((env->flags & POWERPC_FLAG_TM) && msr_tm) {
- ctx.tm_enabled = msr_tm;
+ ctx.tm_enabled = !!msr_tm;
} else {
- ctx.tm_enabled = 0;
+ ctx.tm_enabled = false;
}
#endif
if ((env->flags & POWERPC_FLAG_SE) && msr_se)
diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c
index ca894ff..08bdd07 100644
--- a/target-ppc/translate_init.c
+++ b/target-ppc/translate_init.c
@@ -1179,23 +1179,32 @@ static void gen_spr_amr(CPUPPCState *env, bool has_iamr)
}
#endif /* TARGET_PPC64 */
+#ifndef CONFIG_USER_ONLY
+static void spr_read_thrm(DisasContext *ctx, int gprn, int sprn)
+{
+ gen_helper_fixup_thrm(cpu_env);
+ gen_load_spr(cpu_gpr[gprn], sprn);
+ spr_load_dump_spr(sprn);
+}
+#endif /* !CONFIG_USER_ONLY */
+
static void gen_spr_thrm (CPUPPCState *env)
{
/* Thermal management */
/* XXX : not implemented */
spr_register(env, SPR_THRM1, "THRM1",
SPR_NOACCESS, SPR_NOACCESS,
- &spr_read_generic, &spr_write_generic,
+ &spr_read_thrm, &spr_write_generic,
0x00000000);
/* XXX : not implemented */
spr_register(env, SPR_THRM2, "THRM2",
SPR_NOACCESS, SPR_NOACCESS,
- &spr_read_generic, &spr_write_generic,
+ &spr_read_thrm, &spr_write_generic,
0x00000000);
/* XXX : not implemented */
spr_register(env, SPR_THRM3, "THRM3",
SPR_NOACCESS, SPR_NOACCESS,
- &spr_read_generic, &spr_write_generic,
+ &spr_read_thrm, &spr_write_generic,
0x00000000);
}
@@ -3171,18 +3180,30 @@ static void init_excp_POWER7 (CPUPPCState *env)
env->excp_vectors[POWERPC_EXCP_HDECR] = 0x00000980;
env->excp_vectors[POWERPC_EXCP_SYSCALL] = 0x00000C00;
env->excp_vectors[POWERPC_EXCP_TRACE] = 0x00000D00;
+ env->excp_vectors[POWERPC_EXCP_HDSI] = 0x00000E00;
+ env->excp_vectors[POWERPC_EXCP_HISI] = 0x00000E20;
+ env->excp_vectors[POWERPC_EXCP_HV_EMU] = 0x00000E40;
+ env->excp_vectors[POWERPC_EXCP_HV_MAINT] = 0x00000E60;
env->excp_vectors[POWERPC_EXCP_PERFM] = 0x00000F00;
env->excp_vectors[POWERPC_EXCP_VPU] = 0x00000F20;
env->excp_vectors[POWERPC_EXCP_VSXU] = 0x00000F40;
- env->excp_vectors[POWERPC_EXCP_FU] = 0x00000F60;
- env->excp_vectors[POWERPC_EXCP_IABR] = 0x00001300;
- env->excp_vectors[POWERPC_EXCP_MAINT] = 0x00001600;
- env->excp_vectors[POWERPC_EXCP_VPUA] = 0x00001700;
- env->excp_vectors[POWERPC_EXCP_THERM] = 0x00001800;
/* Hardware reset vector */
env->hreset_vector = 0x0000000000000100ULL;
#endif
}
+
+static void init_excp_POWER8(CPUPPCState *env)
+{
+ init_excp_POWER7(env);
+
+#if !defined(CONFIG_USER_ONLY)
+ env->excp_vectors[POWERPC_EXCP_SDOOR] = 0x00000A00;
+ env->excp_vectors[POWERPC_EXCP_FU] = 0x00000F60;
+ env->excp_vectors[POWERPC_EXCP_HV_FU] = 0x00000F80;
+ env->excp_vectors[POWERPC_EXCP_SDOOR_HV] = 0x00000E80;
+#endif
+}
+
#endif
/*****************************************************************************/
@@ -8123,10 +8144,13 @@ static void init_proc_book3s_64(CPUPPCState *env, int version)
ppc970_irq_init(ppc_env_get_cpu(env));
break;
case BOOK3S_CPU_POWER7:
- case BOOK3S_CPU_POWER8:
init_excp_POWER7(env);
ppcPOWER7_irq_init(ppc_env_get_cpu(env));
break;
+ case BOOK3S_CPU_POWER8:
+ init_excp_POWER8(env);
+ ppcPOWER7_irq_init(ppc_env_get_cpu(env));
+ break;
default:
g_assert_not_reached();
}
@@ -8356,10 +8380,45 @@ static bool ppc_pvr_match_power7(PowerPCCPUClass *pcc, uint32_t pvr)
return false;
}
+static bool cpu_has_work_POWER7(CPUState *cs)
+{
+ PowerPCCPU *cpu = POWERPC_CPU(cs);
+ CPUPPCState *env = &cpu->env;
+
+ if (cs->halted) {
+ if (!(cs->interrupt_request & CPU_INTERRUPT_HARD)) {
+ return false;
+ }
+ if ((env->pending_interrupts & (1u << PPC_INTERRUPT_EXT)) &&
+ (env->spr[SPR_LPCR] & LPCR_P7_PECE0)) {
+ return true;
+ }
+ if ((env->pending_interrupts & (1u << PPC_INTERRUPT_DECR)) &&
+ (env->spr[SPR_LPCR] & LPCR_P7_PECE1)) {
+ return true;
+ }
+ if ((env->pending_interrupts & (1u << PPC_INTERRUPT_MCK)) &&
+ (env->spr[SPR_LPCR] & LPCR_P7_PECE2)) {
+ return true;
+ }
+ if ((env->pending_interrupts & (1u << PPC_INTERRUPT_HMI)) &&
+ (env->spr[SPR_LPCR] & LPCR_P7_PECE2)) {
+ return true;
+ }
+ if (env->pending_interrupts & (1u << PPC_INTERRUPT_RESET)) {
+ return true;
+ }
+ return false;
+ } else {
+ return msr_ee && (cs->interrupt_request & CPU_INTERRUPT_HARD);
+ }
+}
+
POWERPC_FAMILY(POWER7)(ObjectClass *oc, void *data)
{
DeviceClass *dc = DEVICE_CLASS(oc);
PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc);
+ CPUClass *cc = CPU_CLASS(oc);
dc->fw_name = "PowerPC,POWER7";
dc->desc = "POWER7";
@@ -8369,6 +8428,7 @@ POWERPC_FAMILY(POWER7)(ObjectClass *oc, void *data)
pcc->pcr_supported = PCR_COMPAT_2_06 | PCR_COMPAT_2_05;
pcc->init_proc = init_proc_POWER7;
pcc->check_pow = check_pow_nocheck;
+ cc->has_work = cpu_has_work_POWER7;
pcc->insns_flags = PPC_INSNS_BASE | PPC_ISEL | PPC_STRING | PPC_MFTB |
PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES |
PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE |
@@ -8380,11 +8440,13 @@ POWERPC_FAMILY(POWER7)(ObjectClass *oc, void *data)
PPC_MEM_TLBIE | PPC_MEM_TLBSYNC |
PPC_64B | PPC_64H | PPC_64BX | PPC_ALTIVEC |
PPC_SEGMENT_64B | PPC_SLBI |
- PPC_POPCNTB | PPC_POPCNTWD;
+ PPC_POPCNTB | PPC_POPCNTWD |
+ PPC_CILDST;
pcc->insns_flags2 = PPC2_VSX | PPC2_DFP | PPC2_DBRX | PPC2_ISA205 |
PPC2_PERM_ISA206 | PPC2_DIVE_ISA206 |
PPC2_ATOMIC_ISA206 | PPC2_FP_CVT_ISA206 |
- PPC2_FP_TST_ISA206 | PPC2_FP_CVT_S64;
+ PPC2_FP_TST_ISA206 | PPC2_FP_CVT_S64 |
+ PPC2_PM_ISA206;
pcc->msr_mask = (1ull << MSR_SF) |
(1ull << MSR_VR) |
(1ull << MSR_VSX) |
@@ -8437,10 +8499,53 @@ static bool ppc_pvr_match_power8(PowerPCCPUClass *pcc, uint32_t pvr)
return false;
}
+static bool cpu_has_work_POWER8(CPUState *cs)
+{
+ PowerPCCPU *cpu = POWERPC_CPU(cs);
+ CPUPPCState *env = &cpu->env;
+
+ if (cs->halted) {
+ if (!(cs->interrupt_request & CPU_INTERRUPT_HARD)) {
+ return false;
+ }
+ if ((env->pending_interrupts & (1u << PPC_INTERRUPT_EXT)) &&
+ (env->spr[SPR_LPCR] & LPCR_P8_PECE2)) {
+ return true;
+ }
+ if ((env->pending_interrupts & (1u << PPC_INTERRUPT_DECR)) &&
+ (env->spr[SPR_LPCR] & LPCR_P8_PECE3)) {
+ return true;
+ }
+ if ((env->pending_interrupts & (1u << PPC_INTERRUPT_MCK)) &&
+ (env->spr[SPR_LPCR] & LPCR_P8_PECE4)) {
+ return true;
+ }
+ if ((env->pending_interrupts & (1u << PPC_INTERRUPT_HMI)) &&
+ (env->spr[SPR_LPCR] & LPCR_P8_PECE4)) {
+ return true;
+ }
+ if ((env->pending_interrupts & (1u << PPC_INTERRUPT_DOORBELL)) &&
+ (env->spr[SPR_LPCR] & LPCR_P8_PECE0)) {
+ return true;
+ }
+ if ((env->pending_interrupts & (1u << PPC_INTERRUPT_HDOORBELL)) &&
+ (env->spr[SPR_LPCR] & LPCR_P8_PECE1)) {
+ return true;
+ }
+ if (env->pending_interrupts & (1u << PPC_INTERRUPT_RESET)) {
+ return true;
+ }
+ return false;
+ } else {
+ return msr_ee && (cs->interrupt_request & CPU_INTERRUPT_HARD);
+ }
+}
+
POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data)
{
DeviceClass *dc = DEVICE_CLASS(oc);
PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc);
+ CPUClass *cc = CPU_CLASS(oc);
dc->fw_name = "PowerPC,POWER8";
dc->desc = "POWER8";
@@ -8450,6 +8555,7 @@ POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data)
pcc->pcr_supported = PCR_COMPAT_2_07 | PCR_COMPAT_2_06 | PCR_COMPAT_2_05;
pcc->init_proc = init_proc_POWER8;
pcc->check_pow = check_pow_nocheck;
+ cc->has_work = cpu_has_work_POWER8;
pcc->insns_flags = PPC_INSNS_BASE | PPC_ISEL | PPC_STRING | PPC_MFTB |
PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES |
PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE |
@@ -8461,14 +8567,15 @@ POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data)
PPC_MEM_TLBIE | PPC_MEM_TLBSYNC |
PPC_64B | PPC_64H | PPC_64BX | PPC_ALTIVEC |
PPC_SEGMENT_64B | PPC_SLBI |
- PPC_POPCNTB | PPC_POPCNTWD;
+ PPC_POPCNTB | PPC_POPCNTWD |
+ PPC_CILDST;
pcc->insns_flags2 = PPC2_VSX | PPC2_VSX207 | PPC2_DFP | PPC2_DBRX |
PPC2_PERM_ISA206 | PPC2_DIVE_ISA206 |
PPC2_ATOMIC_ISA206 | PPC2_FP_CVT_ISA206 |
PPC2_FP_TST_ISA206 | PPC2_BCTAR_ISA207 |
PPC2_LSQ_ISA207 | PPC2_ALTIVEC_207 |
PPC2_ISA205 | PPC2_ISA207S | PPC2_FP_CVT_S64 |
- PPC2_TM;
+ PPC2_TM | PPC2_PM_ISA206;
pcc->msr_mask = (1ull << MSR_SF) |
(1ull << MSR_SHV) |
(1ull << MSR_TM) |
@@ -8509,6 +8616,7 @@ POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data)
void cpu_ppc_set_papr(PowerPCCPU *cpu)
{
CPUPPCState *env = &cpu->env;
+ ppc_spr_t *lpcr = &env->spr_cb[SPR_LPCR];
ppc_spr_t *amor = &env->spr_cb[SPR_AMOR];
/* PAPR always has exception vectors in RAM not ROM. To ensure this,
@@ -8518,6 +8626,26 @@ void cpu_ppc_set_papr(PowerPCCPU *cpu)
*/
env->msr_mask &= ~((1ull << MSR_EP) | MSR_HVB);
+ /* Set emulated LPCR to not send interrupts to hypervisor. Note that
+ * under KVM, the actual HW LPCR will be set differently by KVM itself,
+ * the settings below ensure proper operations with TCG in absence of
+ * a real hypervisor
+ */
+ lpcr->default_value &= ~(LPCR_VPM0 | LPCR_VPM1 | LPCR_ISL | LPCR_KBV);
+ lpcr->default_value |= LPCR_LPES0 | LPCR_LPES1;
+
+ /* P7 and P8 has slightly different PECE bits, mostly because P8 adds
+ * bit 47 and 48 which are reserved on P7. Here we set them all, which
+ * will work as expected for both implementations
+ */
+ lpcr->default_value |= LPCR_P8_PECE0 | LPCR_P8_PECE1 | LPCR_P8_PECE2 |
+ LPCR_P8_PECE3 | LPCR_P8_PECE4;
+
+ /* We should be followed by a CPU reset but update the active value
+ * just in case...
+ */
+ env->spr[SPR_LPCR] = lpcr->default_value;
+
/* Set a full AMOR so guest can use the AMR as it sees fit */
env->spr[SPR_AMOR] = amor->default_value = 0xffffffffffffffffull;
diff --git a/tests/Makefile.include b/tests/Makefile.include
index a2ed83b..fd2dba4 100644
--- a/tests/Makefile.include
+++ b/tests/Makefile.include
@@ -259,11 +259,11 @@ check-qtest-ppc-y += tests/boot-order-test$(EXESUF)
check-qtest-ppc64-y += tests/boot-order-test$(EXESUF)
check-qtest-ppc64-y += tests/spapr-phb-test$(EXESUF)
gcov-files-ppc64-y += ppc64-softmmu/hw/ppc/spapr_pci.c
-check-qtest-ppc-y = tests/prom-env-test$(EXESUF)
-check-qtest-ppc64-y = tests/prom-env-test$(EXESUF)
-check-qtest-sparc-y = tests/prom-env-test$(EXESUF)
+check-qtest-ppc-y += tests/prom-env-test$(EXESUF)
+check-qtest-ppc64-y += tests/prom-env-test$(EXESUF)
+check-qtest-sparc-y += tests/prom-env-test$(EXESUF)
#Disabled for now, triggers a TCG bug on 32-bit hosts
-#check-qtest-sparc64-y = tests/prom-env-test$(EXESUF)
+#check-qtest-sparc64-y += tests/prom-env-test$(EXESUF)
check-qtest-microblazeel-y = $(check-qtest-microblaze-y)
check-qtest-xtensaeb-y = $(check-qtest-xtensa-y)