aboutsummaryrefslogtreecommitdiff
path: root/target
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2021-05-05 20:29:14 +0100
committerPeter Maydell <peter.maydell@linaro.org>2021-05-05 20:29:14 +0100
commitd90f154867ec0ec22fd719164b88716e8fd48672 (patch)
tree507b11b0bd6884cb0fb41c3e1612cff300a5cdbc /target
parentd45a5270d075ea589f0b0ddcf963a5fea1f500ac (diff)
parent4bb32cd7b1e42c46d274b727c8be8e45b4df3814 (diff)
Merge remote-tracking branch 'remotes/dg-gitlab/tags/ppc-for-6.1-20210504' into staging
ppc patch queue 2021-05-04 Here's the first ppc pull request for qemu-6.1. It has a wide variety of stuff accumulated during the 6.0 freeze. Highlights are: * Multi-phase reset cleanups for PAPR * Preliminary cleanups towards allowing !CONFIG_TCG for the ppc target * Cleanup of AIL logic and extension to POWER10 * Further improvements to handling of hot unplug failures on PAPR * Allow much larger numbers of CPU on pseries * Support for the H_SCM_HEALTH hypercall * Add support for the Pegasos II board * Substantial cleanup to hflag handling * Assorted minor fixes and cleanups # gpg: Signature made Tue 04 May 2021 06:52:39 BST # gpg: using RSA key 75F46586AE61A66CC44E87DC6C38CACA20D9B392 # gpg: Good signature from "David Gibson <david@gibson.dropbear.id.au>" [full] # gpg: aka "David Gibson (Red Hat) <dgibson@redhat.com>" [full] # gpg: aka "David Gibson (ozlabs.org) <dgibson@ozlabs.org>" [full] # gpg: aka "David Gibson (kernel.org) <dwg@kernel.org>" [unknown] # Primary key fingerprint: 75F4 6586 AE61 A66C C44E 87DC 6C38 CACA 20D9 B392 * remotes/dg-gitlab/tags/ppc-for-6.1-20210504: (46 commits) hw/ppc/pnv_psi: Use device_cold_reset() instead of device_legacy_reset() hw/ppc/spapr_vio: Reset TCE table object with device_cold_reset() hw/intc/spapr_xive: Use device_cold_reset() instead of device_legacy_reset() target/ppc: removed VSCR from SPR registration target/ppc: Reduce the size of ppc_spr_t target/ppc: Clean up _spr_register et al target/ppc: Add POWER10 exception model target/ppc: rework AIL logic in interrupt delivery target/ppc: move opcode table logic to translate.c target/ppc: code motion from translate_init.c.inc to gdbstub.c spapr_drc.c: handle hotunplug errors in drc_unisolate_logical() spapr.h: increase FDT_MAX_SIZE spapr.c: do not use MachineClass::max_cpus to limit CPUs ppc: Rename current DAWR macros and variables target/ppc: POWER10 supports scv target/ppc: Fix POWER9 radix guest HV interrupt AIL behaviour docs/system: ppc: Add documentation for ppce500 machine roms/u-boot: Bump ppce500 u-boot to v2021.04 to fix broken pci support roms/Makefile: Update ppce500 u-boot build directory name ppc/spapr: Add support for implement support for H_SCM_HEALTH ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'target')
-rw-r--r--target/ppc/cpu-qom.h2
-rw-r--r--target/ppc/cpu.h80
-rw-r--r--target/ppc/excp_helper.c217
-rw-r--r--target/ppc/gdbstub.c258
-rw-r--r--target/ppc/helper.h1
-rw-r--r--target/ppc/helper_regs.c280
-rw-r--r--target/ppc/helper_regs.h183
-rw-r--r--target/ppc/int_helper.c1
-rw-r--r--target/ppc/internal.h13
-rw-r--r--target/ppc/machine.c39
-rw-r--r--target/ppc/mem_helper.c2
-rw-r--r--target/ppc/meson.build1
-rw-r--r--target/ppc/misc_helper.c13
-rw-r--r--target/ppc/mmu-hash64.c3
-rw-r--r--target/ppc/translate.c493
-rw-r--r--target/ppc/translate_init.c.inc833
16 files changed, 1336 insertions, 1083 deletions
diff --git a/target/ppc/cpu-qom.h b/target/ppc/cpu-qom.h
index 118baf8d41..06b6571bc9 100644
--- a/target/ppc/cpu-qom.h
+++ b/target/ppc/cpu-qom.h
@@ -116,6 +116,8 @@ enum powerpc_excp_t {
POWERPC_EXCP_POWER8,
/* POWER9 exception model */
POWERPC_EXCP_POWER9,
+ /* POWER10 exception model */
+ POWERPC_EXCP_POWER10,
};
/*****************************************************************************/
diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h
index e73416da68..733a2168c4 100644
--- a/target/ppc/cpu.h
+++ b/target/ppc/cpu.h
@@ -192,17 +192,21 @@ typedef struct ppc_hash_pte64 ppc_hash_pte64_t;
/* SPR access micro-ops generations callbacks */
struct ppc_spr_t {
+ const char *name;
+ target_ulong default_value;
+#ifndef CONFIG_USER_ONLY
+ unsigned int gdb_id;
+#endif
+#ifdef CONFIG_TCG
void (*uea_read)(DisasContext *ctx, int gpr_num, int spr_num);
void (*uea_write)(DisasContext *ctx, int spr_num, int gpr_num);
-#if !defined(CONFIG_USER_ONLY)
+# ifndef CONFIG_USER_ONLY
void (*oea_read)(DisasContext *ctx, int gpr_num, int spr_num);
void (*oea_write)(DisasContext *ctx, int spr_num, int gpr_num);
void (*hea_read)(DisasContext *ctx, int gpr_num, int spr_num);
void (*hea_write)(DisasContext *ctx, int spr_num, int gpr_num);
- unsigned int gdb_id;
+# endif
#endif
- const char *name;
- target_ulong default_value;
#ifdef CONFIG_KVM
/*
* We (ab)use the fact that all the SPRs will have ids for the
@@ -322,13 +326,13 @@ typedef struct ppc_v3_pate_t {
#define MSR_PR 14 /* Problem state hflags */
#define MSR_FP 13 /* Floating point available hflags */
#define MSR_ME 12 /* Machine check interrupt enable */
-#define MSR_FE0 11 /* Floating point exception mode 0 hflags */
+#define MSR_FE0 11 /* Floating point exception mode 0 */
#define MSR_SE 10 /* Single-step trace enable x hflags */
#define MSR_DWE 10 /* Debug wait enable on 405 x */
#define MSR_UBLE 10 /* User BTB lock enable on e500 x */
#define MSR_BE 9 /* Branch trace enable x hflags */
#define MSR_DE 9 /* Debug interrupts enable on embedded PowerPC x */
-#define MSR_FE1 8 /* Floating point exception mode 1 hflags */
+#define MSR_FE1 8 /* Floating point exception mode 1 */
#define MSR_AL 7 /* AL bit on POWER */
#define MSR_EP 6 /* Exception prefix on 601 */
#define MSR_IR 5 /* Instruction relocate */
@@ -354,10 +358,11 @@ typedef struct ppc_v3_pate_t {
#define LPCR_PECE_U_SHIFT (63 - 19)
#define LPCR_PECE_U_MASK (0x7ull << LPCR_PECE_U_SHIFT)
#define LPCR_HVEE PPC_BIT(17) /* Hypervisor Virt Exit Enable */
-#define LPCR_RMLS_SHIFT (63 - 37)
+#define LPCR_RMLS_SHIFT (63 - 37) /* RMLS (removed in ISA v3.0) */
#define LPCR_RMLS (0xfull << LPCR_RMLS_SHIFT)
+#define LPCR_HAIL PPC_BIT(37) /* ISA v3.1 HV AIL=3 equivalent */
#define LPCR_ILE PPC_BIT(38)
-#define LPCR_AIL_SHIFT (63 - 40) /* Alternate interrupt location */
+#define LPCR_AIL_SHIFT (63 - 40) /* Alternate interrupt location */
#define LPCR_AIL (3ull << LPCR_AIL_SHIFT)
#define LPCR_UPRT PPC_BIT(41) /* Use Process Table */
#define LPCR_EVIRT PPC_BIT(42) /* Enhanced Virtualisation */
@@ -581,6 +586,34 @@ enum {
POWERPC_FLAG_TM = 0x00100000,
/* Has SCV (ISA 3.00) */
POWERPC_FLAG_SCV = 0x00200000,
+ /* Has HID0 for LE bit (601) */
+ POWERPC_FLAG_HID0_LE = 0x00400000,
+};
+
+/*
+ * Bits for env->hflags.
+ *
+ * Most of these bits overlap with corresponding bits in MSR,
+ * but some come from other sources. Those that do come from
+ * the MSR are validated in hreg_compute_hflags.
+ */
+enum {
+ HFLAGS_LE = 0, /* MSR_LE -- comes from elsewhere on 601 */
+ HFLAGS_HV = 1, /* computed from MSR_HV and other state */
+ HFLAGS_64 = 2, /* computed from MSR_CE and MSR_SF */
+ HFLAGS_GTSE = 3, /* computed from SPR_LPCR[GTSE] */
+ HFLAGS_DR = 4, /* MSR_DR */
+ HFLAGS_SPE = 6, /* from MSR_SPE if cpu has SPE; avoid overlap w/ MSR_VR */
+ HFLAGS_TM = 8, /* computed from MSR_TM */
+ HFLAGS_BE = 9, /* MSR_BE -- from elsewhere on embedded ppc */
+ HFLAGS_SE = 10, /* MSR_SE -- from elsewhere on embedded ppc */
+ HFLAGS_FP = 13, /* MSR_FP */
+ HFLAGS_PR = 14, /* MSR_PR */
+ HFLAGS_VSX = 23, /* MSR_VSX if cpu has VSX */
+ HFLAGS_VR = 25, /* MSR_VR if cpu has VRE */
+
+ HFLAGS_IMMU_IDX = 26, /* 26..28 -- the composite immu_idx */
+ HFLAGS_DMMU_IDX = 29, /* 29..31 -- the composite dmmu_idx */
};
/*****************************************************************************/
@@ -1102,11 +1135,9 @@ struct CPUPPCState {
bool resume_as_sreset;
#endif
- /* These resources are used only in QEMU core */
- target_ulong hflags; /* hflags is MSR & HFLAGS_MASK */
- target_ulong hflags_nmsr; /* specific hflags, not coming from MSR */
- int immu_idx; /* precomputed MMU index to speed up insn accesses */
- int dmmu_idx; /* precomputed MMU index to speed up data accesses */
+ /* These resources are used only in TCG */
+ uint32_t hflags;
+ target_ulong hflags_compat_nmsr; /* for migration compatibility */
/* Power management */
int (*check_pow)(CPUPPCState *env);
@@ -1342,7 +1373,11 @@ int ppc_dcr_write(ppc_dcr_t *dcr_env, int dcrn, uint32_t val);
#define MMU_USER_IDX 0
static inline int cpu_mmu_index(CPUPPCState *env, bool ifetch)
{
- return ifetch ? env->immu_idx : env->dmmu_idx;
+#ifdef CONFIG_USER_ONLY
+ return MMU_USER_IDX;
+#else
+ return (env->hflags >> (ifetch ? HFLAGS_IMMU_IDX : HFLAGS_DMMU_IDX)) & 7;
+#endif
}
/* Compatibility modes */
@@ -1459,10 +1494,10 @@ typedef PowerPCCPU ArchCPU;
#define SPR_MPC_BAR (0x09F)
#define SPR_PSPB (0x09F)
#define SPR_DPDES (0x0B0)
-#define SPR_DAWR (0x0B4)
+#define SPR_DAWR0 (0x0B4)
#define SPR_RPR (0x0BA)
#define SPR_CIABR (0x0BB)
-#define SPR_DAWRX (0x0BC)
+#define SPR_DAWRX0 (0x0BC)
#define SPR_HFSCR (0x0BE)
#define SPR_VRSAVE (0x100)
#define SPR_USPRG0 (0x100)
@@ -2375,14 +2410,6 @@ enum {
HMER_XSCOM_STATUS_MASK = PPC_BITMASK(21, 23),
};
-/* Alternate Interrupt Location (AIL) */
-enum {
- AIL_NONE = 0,
- AIL_RESERVED = 1,
- AIL_0001_8000 = 2,
- AIL_C000_0000_0000_4000 = 3,
-};
-
/*****************************************************************************/
#define is_isa300(ctx) (!!(ctx->insns_flags2 & PPC2_ISA300))
@@ -2395,6 +2422,10 @@ void cpu_write_xer(CPUPPCState *env, target_ulong xer);
*/
#define is_book3s_arch2x(ctx) (!!((ctx)->insns_flags & PPC_SEGMENT_64B))
+#ifdef CONFIG_DEBUG_TCG
+void cpu_get_tb_cpu_state(CPUPPCState *env, target_ulong *pc,
+ target_ulong *cs_base, uint32_t *flags);
+#else
static inline void cpu_get_tb_cpu_state(CPUPPCState *env, target_ulong *pc,
target_ulong *cs_base, uint32_t *flags)
{
@@ -2402,6 +2433,7 @@ static inline void cpu_get_tb_cpu_state(CPUPPCState *env, target_ulong *pc,
*cs_base = 0;
*flags = env->hflags;
}
+#endif
void QEMU_NORETURN raise_exception(CPUPPCState *env, uint32_t exception);
void QEMU_NORETURN raise_exception_ra(CPUPPCState *env, uint32_t exception,
diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c
index 85de7e6c90..f4f15279eb 100644
--- a/target/ppc/excp_helper.c
+++ b/target/ppc/excp_helper.c
@@ -136,25 +136,157 @@ static int powerpc_reset_wakeup(CPUState *cs, CPUPPCState *env, int excp,
return POWERPC_EXCP_RESET;
}
-static uint64_t ppc_excp_vector_offset(CPUState *cs, int ail)
+/*
+ * AIL - Alternate Interrupt Location, a mode that allows interrupts to be
+ * taken with the MMU on, and which uses an alternate location (e.g., so the
+ * kernel/hv can map the vectors there with an effective address).
+ *
+ * An interrupt is considered to be taken "with AIL" or "AIL applies" if they
+ * are delivered in this way. AIL requires the LPCR to be set to enable this
+ * mode, and then a number of conditions have to be true for AIL to apply.
+ *
+ * First of all, SRESET, MCE, and HMI are always delivered without AIL, because
+ * they specifically want to be in real mode (e.g., the MCE might be signaling
+ * a SLB multi-hit which requires SLB flush before the MMU can be enabled).
+ *
+ * After that, behaviour depends on the current MSR[IR], MSR[DR], MSR[HV],
+ * whether or not the interrupt changes MSR[HV] from 0 to 1, and the current
+ * radix mode (LPCR[HR]).
+ *
+ * POWER8, POWER9 with LPCR[HR]=0
+ * | LPCR[AIL] | MSR[IR||DR] | MSR[HV] | new MSR[HV] | AIL |
+ * +-----------+-------------+---------+-------------+-----+
+ * | a | 00/01/10 | x | x | 0 |
+ * | a | 11 | 0 | 1 | 0 |
+ * | a | 11 | 1 | 1 | a |
+ * | a | 11 | 0 | 0 | a |
+ * +-------------------------------------------------------+
+ *
+ * POWER9 with LPCR[HR]=1
+ * | LPCR[AIL] | MSR[IR||DR] | MSR[HV] | new MSR[HV] | AIL |
+ * +-----------+-------------+---------+-------------+-----+
+ * | a | 00/01/10 | x | x | 0 |
+ * | a | 11 | x | x | a |
+ * +-------------------------------------------------------+
+ *
+ * The difference with POWER9 being that MSR[HV] 0->1 interrupts can be sent to
+ * the hypervisor in AIL mode if the guest is radix. This is good for
+ * performance but allows the guest to influence the AIL of hypervisor
+ * interrupts using its MSR, and also the hypervisor must disallow guest
+ * interrupts (MSR[HV] 0->0) from using AIL if the hypervisor does not want to
+ * use AIL for its MSR[HV] 0->1 interrupts.
+ *
+ * POWER10 addresses those issues with a new LPCR[HAIL] bit that is applied to
+ * interrupts that begin execution with MSR[HV]=1 (so both MSR[HV] 0->1 and
+ * MSR[HV] 1->1).
+ *
+ * HAIL=1 is equivalent to AIL=3, for interrupts delivered with MSR[HV]=1.
+ *
+ * POWER10 behaviour is
+ * | LPCR[AIL] | LPCR[HAIL] | MSR[IR||DR] | MSR[HV] | new MSR[HV] | AIL |
+ * +-----------+------------+-------------+---------+-------------+-----+
+ * | a | h | 00/01/10 | 0 | 0 | 0 |
+ * | a | h | 11 | 0 | 0 | a |
+ * | a | h | x | 0 | 1 | h |
+ * | a | h | 00/01/10 | 1 | 1 | 0 |
+ * | a | h | 11 | 1 | 1 | h |
+ * +--------------------------------------------------------------------+
+ */
+static inline void ppc_excp_apply_ail(PowerPCCPU *cpu, int excp_model, int excp,
+ target_ulong msr,
+ target_ulong *new_msr,
+ target_ulong *vector)
{
- uint64_t offset = 0;
+#if defined(TARGET_PPC64)
+ CPUPPCState *env = &cpu->env;
+ bool mmu_all_on = ((msr >> MSR_IR) & 1) && ((msr >> MSR_DR) & 1);
+ bool hv_escalation = !(msr & MSR_HVB) && (*new_msr & MSR_HVB);
+ int ail = 0;
+
+ if (excp == POWERPC_EXCP_MCHECK ||
+ excp == POWERPC_EXCP_RESET ||
+ excp == POWERPC_EXCP_HV_MAINT) {
+ /* SRESET, MCE, HMI never apply AIL */
+ return;
+ }
- switch (ail) {
- case AIL_NONE:
- break;
- case AIL_0001_8000:
- offset = 0x18000;
- break;
- case AIL_C000_0000_0000_4000:
- offset = 0xc000000000004000ull;
- break;
- default:
- cpu_abort(cs, "Invalid AIL combination %d\n", ail);
- break;
+ if (excp_model == POWERPC_EXCP_POWER8 ||
+ excp_model == POWERPC_EXCP_POWER9) {
+ if (!mmu_all_on) {
+ /* AIL only works if MSR[IR] and MSR[DR] are both enabled. */
+ return;
+ }
+ if (hv_escalation && !(env->spr[SPR_LPCR] & LPCR_HR)) {
+ /*
+ * AIL does not work if there is a MSR[HV] 0->1 transition and the
+ * partition is in HPT mode. For radix guests, such interrupts are
+ * allowed to be delivered to the hypervisor in ail mode.
+ */
+ return;
+ }
+
+ ail = (env->spr[SPR_LPCR] & LPCR_AIL) >> LPCR_AIL_SHIFT;
+ if (ail == 0) {
+ return;
+ }
+ if (ail == 1) {
+ /* AIL=1 is reserved, treat it like AIL=0 */
+ return;
+ }
+
+ } else if (excp_model == POWERPC_EXCP_POWER10) {
+ if (!mmu_all_on && !hv_escalation) {
+ /*
+ * AIL works for HV interrupts even with guest MSR[IR/DR] disabled.
+ * Guest->guest and HV->HV interrupts do require MMU on.
+ */
+ return;
+ }
+
+ if (*new_msr & MSR_HVB) {
+ if (!(env->spr[SPR_LPCR] & LPCR_HAIL)) {
+ /* HV interrupts depend on LPCR[HAIL] */
+ return;
+ }
+ ail = 3; /* HAIL=1 gives AIL=3 behaviour for HV interrupts */
+ } else {
+ ail = (env->spr[SPR_LPCR] & LPCR_AIL) >> LPCR_AIL_SHIFT;
+ }
+ if (ail == 0) {
+ return;
+ }
+ if (ail == 1 || ail == 2) {
+ /* AIL=1 and AIL=2 are reserved, treat them like AIL=0 */
+ return;
+ }
+ } else {
+ /* Other processors do not support AIL */
+ return;
}
- return offset;
+ /*
+ * AIL applies, so the new MSR gets IR and DR set, and an offset applied
+ * to the new IP.
+ */
+ *new_msr |= (1 << MSR_IR) | (1 << MSR_DR);
+
+ if (excp != POWERPC_EXCP_SYSCALL_VECTORED) {
+ if (ail == 2) {
+ *vector |= 0x0000000000018000ull;
+ } else if (ail == 3) {
+ *vector |= 0xc000000000004000ull;
+ }
+ } else {
+ /*
+ * scv AIL is a little different. AIL=2 does not change the address,
+ * only the MSR. AIL=3 replaces the 0x17000 base with 0xc...3000.
+ */
+ if (ail == 3) {
+ *vector &= ~0x0000000000017000ull; /* Un-apply the base offset */
+ *vector |= 0xc000000000003000ull; /* Apply scv's AIL=3 offset */
+ }
+ }
+#endif
}
static inline void powerpc_set_excp_state(PowerPCCPU *cpu,
@@ -197,7 +329,7 @@ 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, lev = -1, ail;
+ int srr0, srr1, asrr0, asrr1, lev = -1;
bool lpes0;
qemu_log_mask(CPU_LOG_INT, "Raise exception at " TARGET_FMT_lx
@@ -238,25 +370,17 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
*
* 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 ||
- excp_model == POWERPC_EXCP_POWER9) {
+ excp_model == POWERPC_EXCP_POWER9 ||
+ excp_model == POWERPC_EXCP_POWER10) {
lpes0 = !!(env->spr[SPR_LPCR] & LPCR_LPES0);
- if (excp_model != POWERPC_EXCP_POWER7) {
- ail = (env->spr[SPR_LPCR] & LPCR_AIL) >> LPCR_AIL_SHIFT;
- } else {
- ail = 0;
- }
} else
#endif /* defined(TARGET_PPC64) */
{
lpes0 = true;
- ail = 0;
}
/*
@@ -315,7 +439,6 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
*/
new_msr |= (target_ulong)MSR_HVB;
}
- ail = 0;
/* machine check exceptions don't have ME set */
new_msr &= ~((target_ulong)1 << MSR_ME);
@@ -519,7 +642,6 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
"exception %d with no HV support\n", excp);
}
}
- ail = 0;
break;
case POWERPC_EXCP_DSEG: /* Data segment exception */
case POWERPC_EXCP_ISEG: /* Instruction segment exception */
@@ -773,7 +895,8 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
} else if (env->spr[SPR_LPCR] & LPCR_ILE) {
new_msr |= (target_ulong)1 << MSR_LE;
}
- } else if (excp_model == POWERPC_EXCP_POWER9) {
+ } else if (excp_model == POWERPC_EXCP_POWER9 ||
+ excp_model == POWERPC_EXCP_POWER10) {
if (new_msr & MSR_HVB) {
if (env->spr[SPR_HID0] & HID0_POWER9_HILE) {
new_msr |= (target_ulong)1 << MSR_LE;
@@ -790,15 +913,6 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
}
#endif
- /*
- * 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) ||
- ((new_msr & MSR_HVB) && !(msr & MSR_HVB))) {
- ail = 0;
- }
-
vector = env->excp_vectors[excp];
if (vector == (target_ulong)-1ULL) {
cpu_abort(cs, "Raised an exception without defined vector %d\n",
@@ -839,23 +953,8 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
/* Save MSR */
env->spr[srr1] = msr;
- /* Handle AIL */
- if (ail) {
- new_msr |= (1 << MSR_IR) | (1 << MSR_DR);
- vector |= ppc_excp_vector_offset(cs, ail);
- }
-
#if defined(TARGET_PPC64)
} else {
- /* scv AIL is a little different */
- if (ail) {
- new_msr |= (1 << MSR_IR) | (1 << MSR_DR);
- }
- if (ail == AIL_C000_0000_0000_4000) {
- vector |= 0xc000000000003000ull;
- } else {
- vector |= 0x0000000000017000ull;
- }
vector += lev * 0x20;
env->lr = env->nip;
@@ -863,6 +962,9 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
#endif
}
+ /* This can update new_msr and vector if AIL applies */
+ ppc_excp_apply_ail(cpu, excp_model, excp, msr, &new_msr, &vector);
+
powerpc_set_excp_state(cpu, vector, new_msr);
}
@@ -1130,6 +1232,15 @@ void helper_store_msr(CPUPPCState *env, target_ulong val)
}
#if defined(TARGET_PPC64)
+void helper_scv(CPUPPCState *env, uint32_t lev)
+{
+ if (env->spr[SPR_FSCR] & (1ull << FSCR_SCV)) {
+ raise_exception_err(env, POWERPC_EXCP_SYSCALL_VECTORED, lev);
+ } else {
+ raise_exception_err(env, POWERPC_EXCP_FU, FSCR_IC_SCV);
+ }
+}
+
void helper_pminsn(CPUPPCState *env, powerpc_pm_insn_t insn)
{
CPUState *cs;
diff --git a/target/ppc/gdbstub.c b/target/ppc/gdbstub.c
index c28319fb97..94a7273ee0 100644
--- a/target/ppc/gdbstub.c
+++ b/target/ppc/gdbstub.c
@@ -20,6 +20,8 @@
#include "qemu/osdep.h"
#include "cpu.h"
#include "exec/gdbstub.h"
+#include "exec/helper-proto.h"
+#include "internal.h"
static int ppc_gdb_register_len_apple(int n)
{
@@ -387,3 +389,259 @@ const char *ppc_gdb_get_dynamic_xml(CPUState *cs, const char *xml_name)
return NULL;
}
#endif
+
+static bool avr_need_swap(CPUPPCState *env)
+{
+#ifdef HOST_WORDS_BIGENDIAN
+ return msr_le;
+#else
+ return !msr_le;
+#endif
+}
+
+#if !defined(CONFIG_USER_ONLY)
+static int gdb_find_spr_idx(CPUPPCState *env, int n)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(env->spr_cb); i++) {
+ ppc_spr_t *spr = &env->spr_cb[i];
+
+ if (spr->name && spr->gdb_id == n) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+static int gdb_get_spr_reg(CPUPPCState *env, GByteArray *buf, int n)
+{
+ int reg;
+ int len;
+
+ reg = gdb_find_spr_idx(env, n);
+ if (reg < 0) {
+ return 0;
+ }
+
+ len = TARGET_LONG_SIZE;
+ gdb_get_regl(buf, env->spr[reg]);
+ ppc_maybe_bswap_register(env, gdb_get_reg_ptr(buf, len), len);
+ return len;
+}
+
+static int gdb_set_spr_reg(CPUPPCState *env, uint8_t *mem_buf, int n)
+{
+ int reg;
+ int len;
+
+ reg = gdb_find_spr_idx(env, n);
+ if (reg < 0) {
+ return 0;
+ }
+
+ len = TARGET_LONG_SIZE;
+ ppc_maybe_bswap_register(env, mem_buf, len);
+ env->spr[reg] = ldn_p(mem_buf, len);
+
+ return len;
+}
+#endif
+
+static int gdb_get_float_reg(CPUPPCState *env, GByteArray *buf, int n)
+{
+ uint8_t *mem_buf;
+ if (n < 32) {
+ gdb_get_reg64(buf, *cpu_fpr_ptr(env, n));
+ mem_buf = gdb_get_reg_ptr(buf, 8);
+ ppc_maybe_bswap_register(env, mem_buf, 8);
+ return 8;
+ }
+ if (n == 32) {
+ gdb_get_reg32(buf, env->fpscr);
+ mem_buf = gdb_get_reg_ptr(buf, 4);
+ ppc_maybe_bswap_register(env, mem_buf, 4);
+ return 4;
+ }
+ return 0;
+}
+
+static int gdb_set_float_reg(CPUPPCState *env, uint8_t *mem_buf, int n)
+{
+ if (n < 32) {
+ ppc_maybe_bswap_register(env, mem_buf, 8);
+ *cpu_fpr_ptr(env, n) = ldq_p(mem_buf);
+ return 8;
+ }
+ if (n == 32) {
+ ppc_maybe_bswap_register(env, mem_buf, 4);
+ store_fpscr(env, ldl_p(mem_buf), 0xffffffff);
+ return 4;
+ }
+ return 0;
+}
+
+static int gdb_get_avr_reg(CPUPPCState *env, GByteArray *buf, int n)
+{
+ uint8_t *mem_buf;
+
+ if (n < 32) {
+ ppc_avr_t *avr = cpu_avr_ptr(env, n);
+ if (!avr_need_swap(env)) {
+ gdb_get_reg128(buf, avr->u64[0] , avr->u64[1]);
+ } else {
+ gdb_get_reg128(buf, avr->u64[1] , avr->u64[0]);
+ }
+ mem_buf = gdb_get_reg_ptr(buf, 16);
+ ppc_maybe_bswap_register(env, mem_buf, 8);
+ ppc_maybe_bswap_register(env, mem_buf + 8, 8);
+ return 16;
+ }
+ if (n == 32) {
+ gdb_get_reg32(buf, helper_mfvscr(env));
+ mem_buf = gdb_get_reg_ptr(buf, 4);
+ ppc_maybe_bswap_register(env, mem_buf, 4);
+ return 4;
+ }
+ if (n == 33) {
+ gdb_get_reg32(buf, (uint32_t)env->spr[SPR_VRSAVE]);
+ mem_buf = gdb_get_reg_ptr(buf, 4);
+ ppc_maybe_bswap_register(env, mem_buf, 4);
+ return 4;
+ }
+ return 0;
+}
+
+static int gdb_set_avr_reg(CPUPPCState *env, uint8_t *mem_buf, int n)
+{
+ if (n < 32) {
+ ppc_avr_t *avr = cpu_avr_ptr(env, n);
+ ppc_maybe_bswap_register(env, mem_buf, 8);
+ ppc_maybe_bswap_register(env, mem_buf + 8, 8);
+ if (!avr_need_swap(env)) {
+ avr->u64[0] = ldq_p(mem_buf);
+ avr->u64[1] = ldq_p(mem_buf + 8);
+ } else {
+ avr->u64[1] = ldq_p(mem_buf);
+ avr->u64[0] = ldq_p(mem_buf + 8);
+ }
+ return 16;
+ }
+ if (n == 32) {
+ ppc_maybe_bswap_register(env, mem_buf, 4);
+ helper_mtvscr(env, ldl_p(mem_buf));
+ return 4;
+ }
+ if (n == 33) {
+ ppc_maybe_bswap_register(env, mem_buf, 4);
+ env->spr[SPR_VRSAVE] = (target_ulong)ldl_p(mem_buf);
+ return 4;
+ }
+ return 0;
+}
+
+static int gdb_get_spe_reg(CPUPPCState *env, GByteArray *buf, int n)
+{
+ if (n < 32) {
+#if defined(TARGET_PPC64)
+ gdb_get_reg32(buf, env->gpr[n] >> 32);
+ ppc_maybe_bswap_register(env, gdb_get_reg_ptr(buf, 4), 4);
+#else
+ gdb_get_reg32(buf, env->gprh[n]);
+#endif
+ return 4;
+ }
+ if (n == 32) {
+ gdb_get_reg64(buf, env->spe_acc);
+ ppc_maybe_bswap_register(env, gdb_get_reg_ptr(buf, 8), 8);
+ return 8;
+ }
+ if (n == 33) {
+ gdb_get_reg32(buf, env->spe_fscr);
+ ppc_maybe_bswap_register(env, gdb_get_reg_ptr(buf, 4), 4);
+ return 4;
+ }
+ return 0;
+}
+
+static int gdb_set_spe_reg(CPUPPCState *env, uint8_t *mem_buf, int n)
+{
+ if (n < 32) {
+#if defined(TARGET_PPC64)
+ target_ulong lo = (uint32_t)env->gpr[n];
+ target_ulong hi;
+
+ ppc_maybe_bswap_register(env, mem_buf, 4);
+
+ hi = (target_ulong)ldl_p(mem_buf) << 32;
+ env->gpr[n] = lo | hi;
+#else
+ env->gprh[n] = ldl_p(mem_buf);
+#endif
+ return 4;
+ }
+ if (n == 32) {
+ ppc_maybe_bswap_register(env, mem_buf, 8);
+ env->spe_acc = ldq_p(mem_buf);
+ return 8;
+ }
+ if (n == 33) {
+ ppc_maybe_bswap_register(env, mem_buf, 4);
+ env->spe_fscr = ldl_p(mem_buf);
+ return 4;
+ }
+ return 0;
+}
+
+static int gdb_get_vsx_reg(CPUPPCState *env, GByteArray *buf, int n)
+{
+ if (n < 32) {
+ gdb_get_reg64(buf, *cpu_vsrl_ptr(env, n));
+ ppc_maybe_bswap_register(env, gdb_get_reg_ptr(buf, 8), 8);
+ return 8;
+ }
+ return 0;
+}
+
+static int gdb_set_vsx_reg(CPUPPCState *env, uint8_t *mem_buf, int n)
+{
+ if (n < 32) {
+ ppc_maybe_bswap_register(env, mem_buf, 8);
+ *cpu_vsrl_ptr(env, n) = ldq_p(mem_buf);
+ return 8;
+ }
+ return 0;
+}
+
+gchar *ppc_gdb_arch_name(CPUState *cs)
+{
+#if defined(TARGET_PPC64)
+ return g_strdup("powerpc:common64");
+#else
+ return g_strdup("powerpc:common");
+#endif
+}
+
+void ppc_gdb_init(CPUState *cs, PowerPCCPUClass *pcc)
+{
+ if (pcc->insns_flags & PPC_FLOAT) {
+ gdb_register_coprocessor(cs, gdb_get_float_reg, gdb_set_float_reg,
+ 33, "power-fpu.xml", 0);
+ }
+ if (pcc->insns_flags & PPC_ALTIVEC) {
+ gdb_register_coprocessor(cs, gdb_get_avr_reg, gdb_set_avr_reg,
+ 34, "power-altivec.xml", 0);
+ }
+ if (pcc->insns_flags & PPC_SPE) {
+ gdb_register_coprocessor(cs, gdb_get_spe_reg, gdb_set_spe_reg,
+ 34, "power-spe.xml", 0);
+ }
+ if (pcc->insns_flags2 & PPC2_VSX) {
+ gdb_register_coprocessor(cs, gdb_get_vsx_reg, gdb_set_vsx_reg,
+ 32, "power-vsx.xml", 0);
+ }
+#ifndef CONFIG_USER_ONLY
+ gdb_register_coprocessor(cs, gdb_get_spr_reg, gdb_set_spr_reg,
+ pcc->gdb_num_sprs, "power-spr.xml", 0);
+#endif
+}
diff --git a/target/ppc/helper.h b/target/ppc/helper.h
index 6a4dccf70c..513066d54d 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(scv, noreturn, env, i32)
DEF_HELPER_2(pminsn, void, env, i32)
DEF_HELPER_1(rfid, void, env)
DEF_HELPER_1(rfscv, void, env)
diff --git a/target/ppc/helper_regs.c b/target/ppc/helper_regs.c
new file mode 100644
index 0000000000..3723872aa6
--- /dev/null
+++ b/target/ppc/helper_regs.c
@@ -0,0 +1,280 @@
+/*
+ * PowerPC emulation special registers manipulation helpers for qemu.
+ *
+ * Copyright (c) 2003-2007 Jocelyn Mayer
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "cpu.h"
+#include "qemu/main-loop.h"
+#include "exec/exec-all.h"
+#include "sysemu/kvm.h"
+#include "helper_regs.h"
+
+/* Swap temporary saved registers with GPRs */
+void hreg_swap_gpr_tgpr(CPUPPCState *env)
+{
+ target_ulong tmp;
+
+ tmp = env->gpr[0];
+ env->gpr[0] = env->tgpr[0];
+ env->tgpr[0] = tmp;
+ tmp = env->gpr[1];
+ env->gpr[1] = env->tgpr[1];
+ env->tgpr[1] = tmp;
+ tmp = env->gpr[2];
+ env->gpr[2] = env->tgpr[2];
+ env->tgpr[2] = tmp;
+ tmp = env->gpr[3];
+ env->gpr[3] = env->tgpr[3];
+ env->tgpr[3] = tmp;
+}
+
+static uint32_t hreg_compute_hflags_value(CPUPPCState *env)
+{
+ target_ulong msr = env->msr;
+ uint32_t ppc_flags = env->flags;
+ uint32_t hflags = 0;
+ uint32_t msr_mask;
+
+ /* Some bits come straight across from MSR. */
+ QEMU_BUILD_BUG_ON(MSR_LE != HFLAGS_LE);
+ QEMU_BUILD_BUG_ON(MSR_PR != HFLAGS_PR);
+ QEMU_BUILD_BUG_ON(MSR_DR != HFLAGS_DR);
+ QEMU_BUILD_BUG_ON(MSR_FP != HFLAGS_FP);
+ msr_mask = ((1 << MSR_LE) | (1 << MSR_PR) |
+ (1 << MSR_DR) | (1 << MSR_FP));
+
+ if (ppc_flags & POWERPC_FLAG_HID0_LE) {
+ /*
+ * Note that MSR_LE is not set in env->msr_mask for this cpu,
+ * and so will never be set in msr.
+ */
+ uint32_t le = extract32(env->spr[SPR_HID0], 3, 1);
+ hflags |= le << MSR_LE;
+ }
+
+ if (ppc_flags & POWERPC_FLAG_DE) {
+ target_ulong dbcr0 = env->spr[SPR_BOOKE_DBCR0];
+ if (dbcr0 & DBCR0_ICMP) {
+ hflags |= 1 << HFLAGS_SE;
+ }
+ if (dbcr0 & DBCR0_BRT) {
+ hflags |= 1 << HFLAGS_BE;
+ }
+ } else {
+ if (ppc_flags & POWERPC_FLAG_BE) {
+ QEMU_BUILD_BUG_ON(MSR_BE != HFLAGS_BE);
+ msr_mask |= 1 << MSR_BE;
+ }
+ if (ppc_flags & POWERPC_FLAG_SE) {
+ QEMU_BUILD_BUG_ON(MSR_SE != HFLAGS_SE);
+ msr_mask |= 1 << MSR_SE;
+ }
+ }
+
+ if (msr_is_64bit(env, msr)) {
+ hflags |= 1 << HFLAGS_64;
+ }
+ if ((ppc_flags & POWERPC_FLAG_SPE) && (msr & (1 << MSR_SPE))) {
+ hflags |= 1 << HFLAGS_SPE;
+ }
+ if (ppc_flags & POWERPC_FLAG_VRE) {
+ QEMU_BUILD_BUG_ON(MSR_VR != HFLAGS_VR);
+ msr_mask |= 1 << MSR_VR;
+ }
+ if (ppc_flags & POWERPC_FLAG_VSX) {
+ QEMU_BUILD_BUG_ON(MSR_VSX != HFLAGS_VSX);
+ msr_mask |= 1 << MSR_VSX;
+ }
+ if ((ppc_flags & POWERPC_FLAG_TM) && (msr & (1ull << MSR_TM))) {
+ hflags |= 1 << HFLAGS_TM;
+ }
+ if (env->spr[SPR_LPCR] & LPCR_GTSE) {
+ hflags |= 1 << HFLAGS_GTSE;
+ }
+
+#ifndef CONFIG_USER_ONLY
+ if (!env->has_hv_mode || (msr & (1ull << MSR_HV))) {
+ hflags |= 1 << HFLAGS_HV;
+ }
+
+ /*
+ * This is our encoding for server processors. The architecture
+ * specifies that there is no such thing as userspace with
+ * translation off, however it appears that MacOS does it and some
+ * 32-bit CPUs support it. Weird...
+ *
+ * 0 = Guest User space virtual mode
+ * 1 = Guest Kernel space virtual mode
+ * 2 = Guest User space real mode
+ * 3 = Guest Kernel space real mode
+ * 4 = HV User space virtual mode
+ * 5 = HV Kernel space virtual mode
+ * 6 = HV User space real mode
+ * 7 = HV Kernel space real mode
+ *
+ * For BookE, we need 8 MMU modes as follow:
+ *
+ * 0 = AS 0 HV User space
+ * 1 = AS 0 HV Kernel space
+ * 2 = AS 1 HV User space
+ * 3 = AS 1 HV Kernel space
+ * 4 = AS 0 Guest User space
+ * 5 = AS 0 Guest Kernel space
+ * 6 = AS 1 Guest User space
+ * 7 = AS 1 Guest Kernel space
+ */
+ unsigned immu_idx, dmmu_idx;
+ dmmu_idx = msr & (1 << MSR_PR) ? 0 : 1;
+ if (env->mmu_model & POWERPC_MMU_BOOKE) {
+ dmmu_idx |= msr & (1 << MSR_GS) ? 4 : 0;
+ immu_idx = dmmu_idx;
+ immu_idx |= msr & (1 << MSR_IS) ? 2 : 0;
+ dmmu_idx |= msr & (1 << MSR_DS) ? 2 : 0;
+ } else {
+ dmmu_idx |= msr & (1ull << MSR_HV) ? 4 : 0;
+ immu_idx = dmmu_idx;
+ immu_idx |= msr & (1 << MSR_IR) ? 0 : 2;
+ dmmu_idx |= msr & (1 << MSR_DR) ? 0 : 2;
+ }
+ hflags |= immu_idx << HFLAGS_IMMU_IDX;
+ hflags |= dmmu_idx << HFLAGS_DMMU_IDX;
+#endif
+
+ return hflags | (msr & msr_mask);
+}
+
+void hreg_compute_hflags(CPUPPCState *env)
+{
+ env->hflags = hreg_compute_hflags_value(env);
+}
+
+#ifdef CONFIG_DEBUG_TCG
+void cpu_get_tb_cpu_state(CPUPPCState *env, target_ulong *pc,
+ target_ulong *cs_base, uint32_t *flags)
+{
+ uint32_t hflags_current = env->hflags;
+ uint32_t hflags_rebuilt;
+
+ *pc = env->nip;
+ *cs_base = 0;
+ *flags = hflags_current;
+
+ hflags_rebuilt = hreg_compute_hflags_value(env);
+ if (unlikely(hflags_current != hflags_rebuilt)) {
+ cpu_abort(env_cpu(env),
+ "TCG hflags mismatch (current:0x%08x rebuilt:0x%08x)\n",
+ hflags_current, hflags_rebuilt);
+ }
+}
+#endif
+
+void cpu_interrupt_exittb(CPUState *cs)
+{
+ if (!kvm_enabled()) {
+ return;
+ }
+
+ if (!qemu_mutex_iothread_locked()) {
+ qemu_mutex_lock_iothread();
+ cpu_interrupt(cs, CPU_INTERRUPT_EXITTB);
+ qemu_mutex_unlock_iothread();
+ } else {
+ cpu_interrupt(cs, CPU_INTERRUPT_EXITTB);
+ }
+}
+
+int hreg_store_msr(CPUPPCState *env, target_ulong value, int alter_hv)
+{
+ int excp;
+#if !defined(CONFIG_USER_ONLY)
+ CPUState *cs = env_cpu(env);
+#endif
+
+ excp = 0;
+ value &= env->msr_mask;
+#if !defined(CONFIG_USER_ONLY)
+ /* Neither mtmsr nor guest state can alter HV */
+ if (!alter_hv || !(env->msr & MSR_HVB)) {
+ value &= ~MSR_HVB;
+ value |= env->msr & MSR_HVB;
+ }
+ if (((value >> MSR_IR) & 1) != msr_ir ||
+ ((value >> MSR_DR) & 1) != msr_dr) {
+ cpu_interrupt_exittb(cs);
+ }
+ if ((env->mmu_model & POWERPC_MMU_BOOKE) &&
+ ((value >> MSR_GS) & 1) != msr_gs) {
+ cpu_interrupt_exittb(cs);
+ }
+ if (unlikely((env->flags & POWERPC_FLAG_TGPR) &&
+ ((value ^ env->msr) & (1 << MSR_TGPR)))) {
+ /* Swap temporary saved registers with GPRs */
+ hreg_swap_gpr_tgpr(env);
+ }
+ if (unlikely((value >> MSR_EP) & 1) != msr_ep) {
+ /* Change the exception prefix on PowerPC 601 */
+ env->excp_prefix = ((value >> MSR_EP) & 1) * 0xFFF00000;
+ }
+ /*
+ * If PR=1 then EE, IR and DR must be 1
+ *
+ * Note: We only enforce this on 64-bit server processors.
+ * It appears that:
+ * - 32-bit implementations supports PR=1 and EE/DR/IR=0 and MacOS
+ * exploits it.
+ * - 64-bit embedded implementations do not need any operation to be
+ * performed when PR is set.
+ */
+ if (is_book3s_arch2x(env) && ((value >> MSR_PR) & 1)) {
+ value |= (1 << MSR_EE) | (1 << MSR_DR) | (1 << MSR_IR);
+ }
+#endif
+ env->msr = value;
+ hreg_compute_hflags(env);
+#if !defined(CONFIG_USER_ONLY)
+ if (unlikely(msr_pow == 1)) {
+ if (!env->pending_interrupts && (*env->check_pow)(env)) {
+ cs->halted = 1;
+ excp = EXCP_HALTED;
+ }
+ }
+#endif
+
+ return excp;
+}
+
+#ifndef CONFIG_USER_ONLY
+void check_tlb_flush(CPUPPCState *env, bool global)
+{
+ CPUState *cs = env_cpu(env);
+
+ /* Handle global flushes first */
+ if (global && (env->tlb_need_flush & TLB_NEED_GLOBAL_FLUSH)) {
+ env->tlb_need_flush &= ~TLB_NEED_GLOBAL_FLUSH;
+ env->tlb_need_flush &= ~TLB_NEED_LOCAL_FLUSH;
+ tlb_flush_all_cpus_synced(cs);
+ return;
+ }
+
+ /* Then handle local ones */
+ if (env->tlb_need_flush & TLB_NEED_LOCAL_FLUSH) {
+ env->tlb_need_flush &= ~TLB_NEED_LOCAL_FLUSH;
+ tlb_flush(cs);
+ }
+}
+#endif
diff --git a/target/ppc/helper_regs.h b/target/ppc/helper_regs.h
index efcc903427..42f26870b9 100644
--- a/target/ppc/helper_regs.h
+++ b/target/ppc/helper_regs.h
@@ -20,184 +20,15 @@
#ifndef HELPER_REGS_H
#define HELPER_REGS_H
-#include "qemu/main-loop.h"
-#include "exec/exec-all.h"
-#include "sysemu/kvm.h"
+void hreg_swap_gpr_tgpr(CPUPPCState *env);
+void hreg_compute_hflags(CPUPPCState *env);
+void cpu_interrupt_exittb(CPUState *cs);
+int hreg_store_msr(CPUPPCState *env, target_ulong value, int alter_hv);
-/* Swap temporary saved registers with GPRs */
-static inline void hreg_swap_gpr_tgpr(CPUPPCState *env)
-{
- target_ulong tmp;
-
- tmp = env->gpr[0];
- env->gpr[0] = env->tgpr[0];
- env->tgpr[0] = tmp;
- tmp = env->gpr[1];
- env->gpr[1] = env->tgpr[1];
- env->tgpr[1] = tmp;
- tmp = env->gpr[2];
- env->gpr[2] = env->tgpr[2];
- env->tgpr[2] = tmp;
- tmp = env->gpr[3];
- env->gpr[3] = env->tgpr[3];
- env->tgpr[3] = tmp;
-}
-
-static inline void hreg_compute_mem_idx(CPUPPCState *env)
-{
- /*
- * This is our encoding for server processors. The architecture
- * specifies that there is no such thing as userspace with
- * translation off, however it appears that MacOS does it and some
- * 32-bit CPUs support it. Weird...
- *
- * 0 = Guest User space virtual mode
- * 1 = Guest Kernel space virtual mode
- * 2 = Guest User space real mode
- * 3 = Guest Kernel space real mode
- * 4 = HV User space virtual mode
- * 5 = HV Kernel space virtual mode
- * 6 = HV User space real mode
- * 7 = HV Kernel space real mode
- *
- * For BookE, we need 8 MMU modes as follow:
- *
- * 0 = AS 0 HV User space
- * 1 = AS 0 HV Kernel space
- * 2 = AS 1 HV User space
- * 3 = AS 1 HV Kernel space
- * 4 = AS 0 Guest User space
- * 5 = AS 0 Guest Kernel space
- * 6 = AS 1 Guest User space
- * 7 = AS 1 Guest Kernel space
- */
- if (env->mmu_model & POWERPC_MMU_BOOKE) {
- env->immu_idx = env->dmmu_idx = msr_pr ? 0 : 1;
- env->immu_idx += msr_is ? 2 : 0;
- env->dmmu_idx += msr_ds ? 2 : 0;
- env->immu_idx += msr_gs ? 4 : 0;
- env->dmmu_idx += msr_gs ? 4 : 0;
- } else {
- env->immu_idx = env->dmmu_idx = msr_pr ? 0 : 1;
- env->immu_idx += msr_ir ? 0 : 2;
- env->dmmu_idx += msr_dr ? 0 : 2;
- env->immu_idx += msr_hv ? 4 : 0;
- env->dmmu_idx += msr_hv ? 4 : 0;
- }
-}
-
-static inline void hreg_compute_hflags(CPUPPCState *env)
-{
- target_ulong hflags_mask;
-
- /* We 'forget' FE0 & FE1: we'll never generate imprecise exceptions */
- hflags_mask = (1 << MSR_VR) | (1 << MSR_AP) | (1 << MSR_SA) |
- (1 << MSR_PR) | (1 << MSR_FP) | (1 << MSR_SE) | (1 << MSR_BE) |
- (1 << MSR_LE) | (1 << MSR_VSX) | (1 << MSR_IR) | (1 << MSR_DR);
- hflags_mask |= (1ULL << MSR_CM) | (1ULL << MSR_SF) | MSR_HVB;
- hreg_compute_mem_idx(env);
- env->hflags = env->msr & hflags_mask;
- /* Merge with hflags coming from other registers */
- env->hflags |= env->hflags_nmsr;
-}
-
-static inline void cpu_interrupt_exittb(CPUState *cs)
-{
- if (!kvm_enabled()) {
- return;
- }
-
- if (!qemu_mutex_iothread_locked()) {
- qemu_mutex_lock_iothread();
- cpu_interrupt(cs, CPU_INTERRUPT_EXITTB);
- qemu_mutex_unlock_iothread();
- } else {
- cpu_interrupt(cs, CPU_INTERRUPT_EXITTB);
- }
-}
-
-static inline int hreg_store_msr(CPUPPCState *env, target_ulong value,
- int alter_hv)
-{
- int excp;
-#if !defined(CONFIG_USER_ONLY)
- CPUState *cs = env_cpu(env);
-#endif
-
- excp = 0;
- value &= env->msr_mask;
-#if !defined(CONFIG_USER_ONLY)
- /* Neither mtmsr nor guest state can alter HV */
- if (!alter_hv || !(env->msr & MSR_HVB)) {
- value &= ~MSR_HVB;
- value |= env->msr & MSR_HVB;
- }
- if (((value >> MSR_IR) & 1) != msr_ir ||
- ((value >> MSR_DR) & 1) != msr_dr) {
- cpu_interrupt_exittb(cs);
- }
- if ((env->mmu_model & POWERPC_MMU_BOOKE) &&
- ((value >> MSR_GS) & 1) != msr_gs) {
- cpu_interrupt_exittb(cs);
- }
- if (unlikely((env->flags & POWERPC_FLAG_TGPR) &&
- ((value ^ env->msr) & (1 << MSR_TGPR)))) {
- /* Swap temporary saved registers with GPRs */
- hreg_swap_gpr_tgpr(env);
- }
- if (unlikely((value >> MSR_EP) & 1) != msr_ep) {
- /* Change the exception prefix on PowerPC 601 */
- env->excp_prefix = ((value >> MSR_EP) & 1) * 0xFFF00000;
- }
- /*
- * If PR=1 then EE, IR and DR must be 1
- *
- * Note: We only enforce this on 64-bit server processors.
- * It appears that:
- * - 32-bit implementations supports PR=1 and EE/DR/IR=0 and MacOS
- * exploits it.
- * - 64-bit embedded implementations do not need any operation to be
- * performed when PR is set.
- */
- if (is_book3s_arch2x(env) && ((value >> MSR_PR) & 1)) {
- value |= (1 << MSR_EE) | (1 << MSR_DR) | (1 << MSR_IR);
- }
-#endif
- env->msr = value;
- hreg_compute_hflags(env);
-#if !defined(CONFIG_USER_ONLY)
- if (unlikely(msr_pow == 1)) {
- if (!env->pending_interrupts && (*env->check_pow)(env)) {
- cs->halted = 1;
- excp = EXCP_HALTED;
- }
- }
-#endif
-
- return excp;
-}
-
-#if !defined(CONFIG_USER_ONLY)
-static inline void check_tlb_flush(CPUPPCState *env, bool global)
-{
- CPUState *cs = env_cpu(env);
-
- /* Handle global flushes first */
- if (global && (env->tlb_need_flush & TLB_NEED_GLOBAL_FLUSH)) {
- env->tlb_need_flush &= ~TLB_NEED_GLOBAL_FLUSH;
- env->tlb_need_flush &= ~TLB_NEED_LOCAL_FLUSH;
- tlb_flush_all_cpus_synced(cs);
- return;
- }
-
- /* Then handle local ones */
- if (env->tlb_need_flush & TLB_NEED_LOCAL_FLUSH) {
- env->tlb_need_flush &= ~TLB_NEED_LOCAL_FLUSH;
- tlb_flush(cs);
- }
-}
-#else
+#ifdef CONFIG_USER_ONLY
static inline void check_tlb_flush(CPUPPCState *env, bool global) { }
+#else
+void check_tlb_flush(CPUPPCState *env, bool global);
#endif
#endif /* HELPER_REGS_H */
diff --git a/target/ppc/int_helper.c b/target/ppc/int_helper.c
index 429de28494..a44c2d90ea 100644
--- a/target/ppc/int_helper.c
+++ b/target/ppc/int_helper.c
@@ -22,6 +22,7 @@
#include "internal.h"
#include "qemu/host-utils.h"
#include "qemu/main-loop.h"
+#include "qemu/log.h"
#include "exec/helper-proto.h"
#include "crypto/aes.h"
#include "fpu/softfloat.h"
diff --git a/target/ppc/internal.h b/target/ppc/internal.h
index d547448065..184ba6d6b3 100644
--- a/target/ppc/internal.h
+++ b/target/ppc/internal.h
@@ -215,4 +215,17 @@ void helper_compute_fprf_float128(CPUPPCState *env, float128 arg);
void ppc_cpu_do_unaligned_access(CPUState *cs, vaddr addr,
MMUAccessType access_type,
int mmu_idx, uintptr_t retaddr);
+
+/* translate.c */
+
+/* #define PPC_DUMP_CPU */
+
+int ppc_fixup_cpu(PowerPCCPU *cpu);
+void create_ppc_opcodes(PowerPCCPU *cpu, Error **errp);
+void destroy_ppc_opcodes(PowerPCCPU *cpu);
+
+/* gdbstub.c */
+void ppc_gdb_init(CPUState *cs, PowerPCCPUClass *ppc);
+gchar *ppc_gdb_arch_name(CPUState *cs);
+
#endif /* PPC_INTERNAL_H */
diff --git a/target/ppc/machine.c b/target/ppc/machine.c
index 283db1d28a..e5bffbe365 100644
--- a/target/ppc/machine.c
+++ b/target/ppc/machine.c
@@ -10,6 +10,18 @@
#include "kvm_ppc.h"
#include "exec/helper-proto.h"
+static void post_load_update_msr(CPUPPCState *env)
+{
+ target_ulong msr = env->msr;
+
+ /*
+ * Invalidate all supported msr bits except MSR_TGPR/MSR_HVB
+ * before restoring. Note that this recomputes hflags.
+ */
+ env->msr ^= env->msr_mask & ~((1ULL << MSR_TGPR) | MSR_HVB);
+ ppc_store_msr(env, msr);
+}
+
static int cpu_load_old(QEMUFile *f, void *opaque, int version_id)
{
PowerPCCPU *cpu = opaque;
@@ -111,13 +123,12 @@ static int cpu_load_old(QEMUFile *f, void *opaque, int version_id)
qemu_get_betls(f, &env->ivpr_mask);
qemu_get_betls(f, &env->hreset_vector);
qemu_get_betls(f, &env->nip);
- qemu_get_betls(f, &env->hflags);
- qemu_get_betls(f, &env->hflags_nmsr);
+ qemu_get_sbetl(f); /* Discard unused hflags */
+ qemu_get_sbetl(f); /* Discard unused hflags_nmsr */
qemu_get_sbe32(f); /* Discard unused mmu_idx */
qemu_get_sbe32(f); /* Discard unused power_mode */
- /* Recompute mmu indices */
- hreg_compute_mem_idx(env);
+ post_load_update_msr(env);
return 0;
}
@@ -304,6 +315,10 @@ static int cpu_pre_save(void *opaque)
}
}
+ /* Retain migration compatibility for pre 6.0 for 601 machines. */
+ env->hflags_compat_nmsr = (env->flags & POWERPC_FLAG_HID0_LE
+ ? env->hflags & MSR_LE : 0);
+
return 0;
}
@@ -333,7 +348,6 @@ static int cpu_post_load(void *opaque, int version_id)
PowerPCCPU *cpu = opaque;
CPUPPCState *env = &cpu->env;
int i;
- target_ulong msr;
/*
* If we're operating in compat mode, we should be ok as long as
@@ -407,15 +421,7 @@ static int cpu_post_load(void *opaque, int version_id)
ppc_store_sdr1(env, env->spr[SPR_SDR1]);
}
- /*
- * Invalidate all supported msr bits except MSR_TGPR/MSR_HVB
- * before restoring
- */
- msr = env->msr;
- env->msr ^= env->msr_mask & ~((1ULL << MSR_TGPR) | MSR_HVB);
- ppc_store_msr(env, msr);
-
- hreg_compute_mem_idx(env);
+ post_load_update_msr(env);
return 0;
}
@@ -825,9 +831,8 @@ const VMStateDescription vmstate_ppc_cpu = {
/* Supervisor mode architected state */
VMSTATE_UINTTL(env.msr, PowerPCCPU),
- /* Internal state */
- VMSTATE_UINTTL(env.hflags_nmsr, PowerPCCPU),
- /* FIXME: access_type? */
+ /* Backward compatible internal state */
+ VMSTATE_UINTTL(env.hflags_compat_nmsr, PowerPCCPU),
/* Sanity checking */
VMSTATE_UINTTL_TEST(mig_msr_mask, PowerPCCPU, cpu_pre_2_8_migration),
diff --git a/target/ppc/mem_helper.c b/target/ppc/mem_helper.c
index f4f7e730de..444b2a30ef 100644
--- a/target/ppc/mem_helper.c
+++ b/target/ppc/mem_helper.c
@@ -278,7 +278,7 @@ static void dcbz_common(CPUPPCState *env, target_ulong addr,
target_ulong mask, dcbz_size = env->dcache_line_size;
uint32_t i;
void *haddr;
- int mmu_idx = epid ? PPC_TLB_EPID_STORE : env->dmmu_idx;
+ int mmu_idx = epid ? PPC_TLB_EPID_STORE : cpu_mmu_index(env, false);
#if defined(TARGET_PPC64)
/* Check for dcbz vs dcbzl on 970 */
diff --git a/target/ppc/meson.build b/target/ppc/meson.build
index bbfef90e08..4079d01ee3 100644
--- a/target/ppc/meson.build
+++ b/target/ppc/meson.build
@@ -6,6 +6,7 @@ ppc_ss.add(files(
'excp_helper.c',
'fpu_helper.c',
'gdbstub.c',
+ 'helper_regs.c',
'int_helper.c',
'mem_helper.c',
'misc_helper.c',
diff --git a/target/ppc/misc_helper.c b/target/ppc/misc_helper.c
index 5d6e0de396..002958be26 100644
--- a/target/ppc/misc_helper.c
+++ b/target/ppc/misc_helper.c
@@ -194,16 +194,14 @@ void helper_store_hid0_601(CPUPPCState *env, target_ulong val)
target_ulong hid0;
hid0 = env->spr[SPR_HID0];
+ env->spr[SPR_HID0] = (uint32_t)val;
+
if ((val ^ hid0) & 0x00000008) {
/* Change current endianness */
- env->hflags &= ~(1 << MSR_LE);
- env->hflags_nmsr &= ~(1 << MSR_LE);
- env->hflags_nmsr |= (1 << MSR_LE) & (((val >> 3) & 1) << MSR_LE);
- env->hflags |= env->hflags_nmsr;
- qemu_log("%s: set endianness to %c => " TARGET_FMT_lx "\n", __func__,
+ hreg_compute_hflags(env);
+ qemu_log("%s: set endianness to %c => %08x\n", __func__,
val & 0x8 ? 'l' : 'b', env->hflags);
}
- env->spr[SPR_HID0] = (uint32_t)val;
}
void helper_store_403_pbr(CPUPPCState *env, uint32_t num, target_ulong value)
@@ -217,6 +215,9 @@ void helper_store_403_pbr(CPUPPCState *env, uint32_t num, target_ulong value)
void helper_store_40x_dbcr0(CPUPPCState *env, target_ulong val)
{
+ /* Bits 26 & 27 affect single-stepping. */
+ hreg_compute_hflags(env);
+ /* Bits 28 & 29 affect reset or shutdown. */
store_40x_dbcr0(env, val);
}
diff --git a/target/ppc/mmu-hash64.c b/target/ppc/mmu-hash64.c
index 0fabc10302..d517a99832 100644
--- a/target/ppc/mmu-hash64.c
+++ b/target/ppc/mmu-hash64.c
@@ -30,6 +30,7 @@
#include "exec/log.h"
#include "hw/hw.h"
#include "mmu-book3s-v3.h"
+#include "helper_regs.h"
/* #define DEBUG_SLB */
@@ -1125,6 +1126,8 @@ void ppc_store_lpcr(PowerPCCPU *cpu, target_ulong val)
CPUPPCState *env = &cpu->env;
env->spr[SPR_LPCR] = val & pcc->lpcr_mask;
+ /* The gtse bit affects hflags */
+ hreg_compute_hflags(env);
}
void helper_store_lpcr(CPUPPCState *env, target_ulong val)
diff --git a/target/ppc/translate.c b/target/ppc/translate.c
index 0984ce637b..a6381208a5 100644
--- a/target/ppc/translate.c
+++ b/target/ppc/translate.c
@@ -173,7 +173,6 @@ struct DisasContext {
bool vsx_enabled;
bool spe_enabled;
bool tm_enabled;
- bool scv_enabled;
bool gtse;
ppc_spr_t *spr_cb; /* Needed to check rights for mfspr/mtspr */
int singlestep_enabled;
@@ -4081,15 +4080,16 @@ static void gen_sc(DisasContext *ctx)
#if !defined(CONFIG_USER_ONLY)
static void gen_scv(DisasContext *ctx)
{
- uint32_t lev;
+ uint32_t lev = (ctx->opcode >> 5) & 0x7F;
- if (unlikely(!ctx->scv_enabled)) {
- gen_exception_err(ctx, POWERPC_EXCP_FU, FSCR_IC_SCV);
- return;
+ /* Set the PC back to the faulting instruction. */
+ if (ctx->exception == POWERPC_EXCP_NONE) {
+ gen_update_nip(ctx, ctx->base.pc_next - 4);
}
+ gen_helper_scv(cpu_env, tcg_constant_i32(lev));
- lev = (ctx->opcode >> 5) & 0x7F;
- gen_exception_err(ctx, POWERPC_SYSCALL_VECTORED, lev);
+ /* This need not be exact, just not POWERPC_EXCP_NONE */
+ ctx->exception = POWERPC_SYSCALL_VECTORED;
}
#endif
#endif
@@ -7657,9 +7657,9 @@ void ppc_cpu_dump_state(CPUState *cs, FILE *f, int flags)
env->nip, env->lr, env->ctr, cpu_read_xer(env),
cs->cpu_index);
qemu_fprintf(f, "MSR " TARGET_FMT_lx " HID0 " TARGET_FMT_lx " HF "
- TARGET_FMT_lx " iidx %d didx %d\n",
- env->msr, env->spr[SPR_HID0],
- env->hflags, env->immu_idx, env->dmmu_idx);
+ "%08x iidx %d didx %d\n",
+ env->msr, env->spr[SPR_HID0], env->hflags,
+ cpu_mmu_index(env, true), cpu_mmu_index(env, false));
#if !defined(NO_TIMER_DUMP)
qemu_fprintf(f, "TB %08" PRIu32 " %08" PRIu64
#if !defined(CONFIG_USER_ONLY)
@@ -7731,7 +7731,8 @@ void ppc_cpu_dump_state(CPUState *cs, FILE *f, int flags)
#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_POWER9 ||
+ env->excp_model == POWERPC_EXCP_POWER10) {
qemu_fprintf(f, "HSRR0 " TARGET_FMT_lx " HSRR1 " TARGET_FMT_lx "\n",
env->spr[SPR_HSRR0], env->spr[SPR_HSRR1]);
}
@@ -7825,6 +7826,400 @@ void ppc_cpu_dump_state(CPUState *cs, FILE *f, int flags)
#undef RFPL
}
+/*****************************************************************************/
+/* Opcode types */
+enum {
+ PPC_DIRECT = 0, /* Opcode routine */
+ PPC_INDIRECT = 1, /* Indirect opcode table */
+};
+
+#define PPC_OPCODE_MASK 0x3
+
+static inline int is_indirect_opcode(void *handler)
+{
+ return ((uintptr_t)handler & PPC_OPCODE_MASK) == PPC_INDIRECT;
+}
+
+static inline opc_handler_t **ind_table(void *handler)
+{
+ return (opc_handler_t **)((uintptr_t)handler & ~PPC_OPCODE_MASK);
+}
+
+/* Instruction table creation */
+/* Opcodes tables creation */
+static void fill_new_table(opc_handler_t **table, int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++) {
+ table[i] = &invalid_handler;
+ }
+}
+
+static int create_new_table(opc_handler_t **table, unsigned char idx)
+{
+ opc_handler_t **tmp;
+
+ tmp = g_new(opc_handler_t *, PPC_CPU_INDIRECT_OPCODES_LEN);
+ fill_new_table(tmp, PPC_CPU_INDIRECT_OPCODES_LEN);
+ table[idx] = (opc_handler_t *)((uintptr_t)tmp | PPC_INDIRECT);
+
+ return 0;
+}
+
+static int insert_in_table(opc_handler_t **table, unsigned char idx,
+ opc_handler_t *handler)
+{
+ if (table[idx] != &invalid_handler) {
+ return -1;
+ }
+ table[idx] = handler;
+
+ return 0;
+}
+
+static int register_direct_insn(opc_handler_t **ppc_opcodes,
+ unsigned char idx, opc_handler_t *handler)
+{
+ if (insert_in_table(ppc_opcodes, idx, handler) < 0) {
+ printf("*** ERROR: opcode %02x already assigned in main "
+ "opcode table\n", idx);
+#if defined(DO_PPC_STATISTICS) || defined(PPC_DUMP_CPU)
+ printf(" Registered handler '%s' - new handler '%s'\n",
+ ppc_opcodes[idx]->oname, handler->oname);
+#endif
+ return -1;
+ }
+
+ return 0;
+}
+
+static int register_ind_in_table(opc_handler_t **table,
+ unsigned char idx1, unsigned char idx2,
+ opc_handler_t *handler)
+{
+ if (table[idx1] == &invalid_handler) {
+ if (create_new_table(table, idx1) < 0) {
+ printf("*** ERROR: unable to create indirect table "
+ "idx=%02x\n", idx1);
+ return -1;
+ }
+ } else {
+ if (!is_indirect_opcode(table[idx1])) {
+ printf("*** ERROR: idx %02x already assigned to a direct "
+ "opcode\n", idx1);
+#if defined(DO_PPC_STATISTICS) || defined(PPC_DUMP_CPU)
+ printf(" Registered handler '%s' - new handler '%s'\n",
+ ind_table(table[idx1])[idx2]->oname, handler->oname);
+#endif
+ return -1;
+ }
+ }
+ if (handler != NULL &&
+ insert_in_table(ind_table(table[idx1]), idx2, handler) < 0) {
+ printf("*** ERROR: opcode %02x already assigned in "
+ "opcode table %02x\n", idx2, idx1);
+#if defined(DO_PPC_STATISTICS) || defined(PPC_DUMP_CPU)
+ printf(" Registered handler '%s' - new handler '%s'\n",
+ ind_table(table[idx1])[idx2]->oname, handler->oname);
+#endif
+ return -1;
+ }
+
+ return 0;
+}
+
+static int register_ind_insn(opc_handler_t **ppc_opcodes,
+ unsigned char idx1, unsigned char idx2,
+ opc_handler_t *handler)
+{
+ return register_ind_in_table(ppc_opcodes, idx1, idx2, handler);
+}
+
+static int register_dblind_insn(opc_handler_t **ppc_opcodes,
+ unsigned char idx1, unsigned char idx2,
+ unsigned char idx3, opc_handler_t *handler)
+{
+ if (register_ind_in_table(ppc_opcodes, idx1, idx2, NULL) < 0) {
+ printf("*** ERROR: unable to join indirect table idx "
+ "[%02x-%02x]\n", idx1, idx2);
+ return -1;
+ }
+ if (register_ind_in_table(ind_table(ppc_opcodes[idx1]), idx2, idx3,
+ handler) < 0) {
+ printf("*** ERROR: unable to insert opcode "
+ "[%02x-%02x-%02x]\n", idx1, idx2, idx3);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int register_trplind_insn(opc_handler_t **ppc_opcodes,
+ unsigned char idx1, unsigned char idx2,
+ unsigned char idx3, unsigned char idx4,
+ opc_handler_t *handler)
+{
+ opc_handler_t **table;
+
+ if (register_ind_in_table(ppc_opcodes, idx1, idx2, NULL) < 0) {
+ printf("*** ERROR: unable to join indirect table idx "
+ "[%02x-%02x]\n", idx1, idx2);
+ return -1;
+ }
+ table = ind_table(ppc_opcodes[idx1]);
+ if (register_ind_in_table(table, idx2, idx3, NULL) < 0) {
+ printf("*** ERROR: unable to join 2nd-level indirect table idx "
+ "[%02x-%02x-%02x]\n", idx1, idx2, idx3);
+ return -1;
+ }
+ table = ind_table(table[idx2]);
+ if (register_ind_in_table(table, idx3, idx4, handler) < 0) {
+ printf("*** ERROR: unable to insert opcode "
+ "[%02x-%02x-%02x-%02x]\n", idx1, idx2, idx3, idx4);
+ return -1;
+ }
+ return 0;
+}
+static int register_insn(opc_handler_t **ppc_opcodes, opcode_t *insn)
+{
+ if (insn->opc2 != 0xFF) {
+ if (insn->opc3 != 0xFF) {
+ if (insn->opc4 != 0xFF) {
+ if (register_trplind_insn(ppc_opcodes, insn->opc1, insn->opc2,
+ insn->opc3, insn->opc4,
+ &insn->handler) < 0) {
+ return -1;
+ }
+ } else {
+ if (register_dblind_insn(ppc_opcodes, insn->opc1, insn->opc2,
+ insn->opc3, &insn->handler) < 0) {
+ return -1;
+ }
+ }
+ } else {
+ if (register_ind_insn(ppc_opcodes, insn->opc1,
+ insn->opc2, &insn->handler) < 0) {
+ return -1;
+ }
+ }
+ } else {
+ if (register_direct_insn(ppc_opcodes, insn->opc1, &insn->handler) < 0) {
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int test_opcode_table(opc_handler_t **table, int len)
+{
+ int i, count, tmp;
+
+ for (i = 0, count = 0; i < len; i++) {
+ /* Consistency fixup */
+ if (table[i] == NULL) {
+ table[i] = &invalid_handler;
+ }
+ if (table[i] != &invalid_handler) {
+ if (is_indirect_opcode(table[i])) {
+ tmp = test_opcode_table(ind_table(table[i]),
+ PPC_CPU_INDIRECT_OPCODES_LEN);
+ if (tmp == 0) {
+ free(table[i]);
+ table[i] = &invalid_handler;
+ } else {
+ count++;
+ }
+ } else {
+ count++;
+ }
+ }
+ }
+
+ return count;
+}
+
+static void fix_opcode_tables(opc_handler_t **ppc_opcodes)
+{
+ if (test_opcode_table(ppc_opcodes, PPC_CPU_OPCODES_LEN) == 0) {
+ printf("*** WARNING: no opcode defined !\n");
+ }
+}
+
+/*****************************************************************************/
+void create_ppc_opcodes(PowerPCCPU *cpu, Error **errp)
+{
+ PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu);
+ opcode_t *opc;
+
+ fill_new_table(cpu->opcodes, PPC_CPU_OPCODES_LEN);
+ for (opc = opcodes; opc < &opcodes[ARRAY_SIZE(opcodes)]; opc++) {
+ if (((opc->handler.type & pcc->insns_flags) != 0) ||
+ ((opc->handler.type2 & pcc->insns_flags2) != 0)) {
+ if (register_insn(cpu->opcodes, opc) < 0) {
+ error_setg(errp, "ERROR initializing PowerPC instruction "
+ "0x%02x 0x%02x 0x%02x", opc->opc1, opc->opc2,
+ opc->opc3);
+ return;
+ }
+ }
+ }
+ fix_opcode_tables(cpu->opcodes);
+ fflush(stdout);
+ fflush(stderr);
+}
+
+void destroy_ppc_opcodes(PowerPCCPU *cpu)
+{
+ opc_handler_t **table, **table_2;
+ int i, j, k;
+
+ for (i = 0; i < PPC_CPU_OPCODES_LEN; i++) {
+ if (cpu->opcodes[i] == &invalid_handler) {
+ continue;
+ }
+ if (is_indirect_opcode(cpu->opcodes[i])) {
+ table = ind_table(cpu->opcodes[i]);
+ for (j = 0; j < PPC_CPU_INDIRECT_OPCODES_LEN; j++) {
+ if (table[j] == &invalid_handler) {
+ continue;
+ }
+ if (is_indirect_opcode(table[j])) {
+ table_2 = ind_table(table[j]);
+ for (k = 0; k < PPC_CPU_INDIRECT_OPCODES_LEN; k++) {
+ if (table_2[k] != &invalid_handler &&
+ is_indirect_opcode(table_2[k])) {
+ g_free((opc_handler_t *)((uintptr_t)table_2[k] &
+ ~PPC_INDIRECT));
+ }
+ }
+ g_free((opc_handler_t *)((uintptr_t)table[j] &
+ ~PPC_INDIRECT));
+ }
+ }
+ g_free((opc_handler_t *)((uintptr_t)cpu->opcodes[i] &
+ ~PPC_INDIRECT));
+ }
+ }
+}
+
+#if defined(PPC_DUMP_CPU)
+static void dump_ppc_insns(CPUPPCState *env)
+{
+ opc_handler_t **table, *handler;
+ const char *p, *q;
+ uint8_t opc1, opc2, opc3, opc4;
+
+ printf("Instructions set:\n");
+ /* opc1 is 6 bits long */
+ for (opc1 = 0x00; opc1 < PPC_CPU_OPCODES_LEN; opc1++) {
+ table = env->opcodes;
+ handler = table[opc1];
+ if (is_indirect_opcode(handler)) {
+ /* opc2 is 5 bits long */
+ for (opc2 = 0; opc2 < PPC_CPU_INDIRECT_OPCODES_LEN; opc2++) {
+ table = env->opcodes;
+ handler = env->opcodes[opc1];
+ table = ind_table(handler);
+ handler = table[opc2];
+ if (is_indirect_opcode(handler)) {
+ table = ind_table(handler);
+ /* opc3 is 5 bits long */
+ for (opc3 = 0; opc3 < PPC_CPU_INDIRECT_OPCODES_LEN;
+ opc3++) {
+ handler = table[opc3];
+ if (is_indirect_opcode(handler)) {
+ table = ind_table(handler);
+ /* opc4 is 5 bits long */
+ for (opc4 = 0; opc4 < PPC_CPU_INDIRECT_OPCODES_LEN;
+ opc4++) {
+ handler = table[opc4];
+ if (handler->handler != &gen_invalid) {
+ printf("INSN: %02x %02x %02x %02x -- "
+ "(%02d %04d %02d) : %s\n",
+ opc1, opc2, opc3, opc4,
+ opc1, (opc3 << 5) | opc2, opc4,
+ handler->oname);
+ }
+ }
+ } else {
+ if (handler->handler != &gen_invalid) {
+ /* Special hack to properly dump SPE insns */
+ p = strchr(handler->oname, '_');
+ if (p == NULL) {
+ printf("INSN: %02x %02x %02x (%02d %04d) : "
+ "%s\n",
+ opc1, opc2, opc3, opc1,
+ (opc3 << 5) | opc2,
+ handler->oname);
+ } else {
+ q = "speundef";
+ if ((p - handler->oname) != strlen(q)
+ || (memcmp(handler->oname, q, strlen(q))
+ != 0)) {
+ /* First instruction */
+ printf("INSN: %02x %02x %02x"
+ "(%02d %04d) : %.*s\n",
+ opc1, opc2 << 1, opc3, opc1,
+ (opc3 << 6) | (opc2 << 1),
+ (int)(p - handler->oname),
+ handler->oname);
+ }
+ if (strcmp(p + 1, q) != 0) {
+ /* Second instruction */
+ printf("INSN: %02x %02x %02x "
+ "(%02d %04d) : %s\n", opc1,
+ (opc2 << 1) | 1, opc3, opc1,
+ (opc3 << 6) | (opc2 << 1) | 1,
+ p + 1);
+ }
+ }
+ }
+ }
+ }
+ } else {
+ if (handler->handler != &gen_invalid) {
+ printf("INSN: %02x %02x -- (%02d %04d) : %s\n",
+ opc1, opc2, opc1, opc2, handler->oname);
+ }
+ }
+ }
+ } else {
+ if (handler->handler != &gen_invalid) {
+ printf("INSN: %02x -- -- (%02d ----) : %s\n",
+ opc1, opc1, handler->oname);
+ }
+ }
+ }
+}
+#endif
+int ppc_fixup_cpu(PowerPCCPU *cpu)
+{
+ CPUPPCState *env = &cpu->env;
+
+ /*
+ * TCG doesn't (yet) emulate some groups of instructions that are
+ * implemented on some otherwise supported CPUs (e.g. VSX and
+ * decimal floating point instructions on POWER7). We remove
+ * unsupported instruction groups from the cpu state's instruction
+ * masks and hope the guest can cope. For at least the pseries
+ * machine, the unavailability of these instructions can be
+ * advertised to the guest via the device tree.
+ */
+ if ((env->insns_flags & ~PPC_TCG_INSNS)
+ || (env->insns_flags2 & ~PPC_TCG_INSNS2)) {
+ warn_report("Disabling some instructions which are not "
+ "emulated by TCG (0x%" PRIx64 ", 0x%" PRIx64 ")",
+ env->insns_flags & ~PPC_TCG_INSNS,
+ env->insns_flags2 & ~PPC_TCG_INSNS2);
+ }
+ env->insns_flags &= PPC_TCG_INSNS;
+ env->insns_flags2 &= PPC_TCG_INSNS2;
+ return 0;
+}
+
+
void ppc_cpu_dump_statistics(CPUState *cs, int flags)
{
#if defined(DO_PPC_STATISTICS)
@@ -7879,87 +8274,47 @@ static void ppc_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs)
{
DisasContext *ctx = container_of(dcbase, DisasContext, base);
CPUPPCState *env = cs->env_ptr;
+ uint32_t hflags = ctx->base.tb->flags;
int bound;
ctx->exception = POWERPC_EXCP_NONE;
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->pr = (hflags >> HFLAGS_PR) & 1;
+ ctx->mem_idx = (hflags >> HFLAGS_DMMU_IDX) & 7;
+ ctx->dr = (hflags >> HFLAGS_DR) & 1;
+ ctx->hv = (hflags >> HFLAGS_HV) & 1;
ctx->insns_flags = env->insns_flags;
ctx->insns_flags2 = env->insns_flags2;
ctx->access_type = -1;
ctx->need_access_type = !mmu_is_64bit(env->mmu_model);
- ctx->le_mode = !!(env->hflags & (1 << MSR_LE));
+ ctx->le_mode = (hflags >> HFLAGS_LE) & 1;
ctx->default_tcg_memop_mask = ctx->le_mode ? MO_LE : MO_BE;
ctx->flags = env->flags;
#if defined(TARGET_PPC64)
- ctx->sf_mode = msr_is_64bit(env, env->msr);
+ ctx->sf_mode = (hflags >> HFLAGS_64) & 1;
ctx->has_cfar = !!(env->flags & POWERPC_FLAG_CFAR);
#endif
ctx->lazy_tlb_flush = env->mmu_model == POWERPC_MMU_32B
|| env->mmu_model == POWERPC_MMU_601
|| env->mmu_model & POWERPC_MMU_64;
- ctx->fpu_enabled = !!msr_fp;
- if ((env->flags & POWERPC_FLAG_SPE) && msr_spe) {
- ctx->spe_enabled = !!msr_spe;
- } else {
- ctx->spe_enabled = false;
- }
- if ((env->flags & POWERPC_FLAG_VRE) && msr_vr) {
- ctx->altivec_enabled = !!msr_vr;
- } else {
- ctx->altivec_enabled = false;
- }
- if ((env->flags & POWERPC_FLAG_VSX) && msr_vsx) {
- ctx->vsx_enabled = !!msr_vsx;
- } else {
- ctx->vsx_enabled = false;
- }
- if ((env->flags & POWERPC_FLAG_SCV)
- && (env->spr[SPR_FSCR] & (1ull << FSCR_SCV))) {
- ctx->scv_enabled = true;
- } else {
- ctx->scv_enabled = false;
- }
-#if defined(TARGET_PPC64)
- if ((env->flags & POWERPC_FLAG_TM) && msr_tm) {
- ctx->tm_enabled = !!msr_tm;
- } else {
- ctx->tm_enabled = false;
- }
-#endif
- ctx->gtse = !!(env->spr[SPR_LPCR] & LPCR_GTSE);
- if ((env->flags & POWERPC_FLAG_SE) && msr_se) {
- ctx->singlestep_enabled = CPU_SINGLE_STEP;
- } else {
- ctx->singlestep_enabled = 0;
+ ctx->fpu_enabled = (hflags >> HFLAGS_FP) & 1;
+ ctx->spe_enabled = (hflags >> HFLAGS_SPE) & 1;
+ ctx->altivec_enabled = (hflags >> HFLAGS_VR) & 1;
+ ctx->vsx_enabled = (hflags >> HFLAGS_VSX) & 1;
+ ctx->tm_enabled = (hflags >> HFLAGS_TM) & 1;
+ ctx->gtse = (hflags >> HFLAGS_GTSE) & 1;
+
+ ctx->singlestep_enabled = 0;
+ if ((hflags >> HFLAGS_SE) & 1) {
+ ctx->singlestep_enabled |= CPU_SINGLE_STEP;
}
- if ((env->flags & POWERPC_FLAG_BE) && msr_be) {
+ if ((hflags >> HFLAGS_BE) & 1) {
ctx->singlestep_enabled |= CPU_BRANCH_STEP;
}
- if ((env->flags & POWERPC_FLAG_DE) && msr_de) {
- ctx->singlestep_enabled = 0;
- target_ulong dbcr0 = env->spr[SPR_BOOKE_DBCR0];
- if (dbcr0 & DBCR0_ICMP) {
- ctx->singlestep_enabled |= CPU_SINGLE_STEP;
- }
- if (dbcr0 & DBCR0_BRT) {
- ctx->singlestep_enabled |= CPU_BRANCH_STEP;
- }
-
- }
if (unlikely(ctx->base.singlestep_enabled)) {
ctx->singlestep_enabled |= GDBSTUB_SINGLE_STEP;
}
-#if defined(DO_SINGLE_STEP) && 0
- /* Single step trace mode */
- msr_se = 1;
-#endif
bound = -(ctx->base.pc_first | TARGET_PAGE_MASK) / 4;
ctx->base.max_insns = MIN(ctx->base.max_insns, bound);
diff --git a/target/ppc/translate_init.c.inc b/target/ppc/translate_init.c.inc
index c03a7c4f52..66e6a4a746 100644
--- a/target/ppc/translate_init.c.inc
+++ b/target/ppc/translate_init.c.inc
@@ -42,7 +42,6 @@
#include "fpu/softfloat.h"
#include "qapi/qapi-commands-machine-target.h"
-/* #define PPC_DUMP_CPU */
/* #define PPC_DEBUG_SPR */
/* #define PPC_DUMP_SPR_ACCESSES */
/* #define USE_APPLE_GDB */
@@ -721,104 +720,98 @@ static inline void vscr_init(CPUPPCState *env, uint32_t val)
helper_mtvscr(env, val);
}
-#ifdef CONFIG_USER_ONLY
-#define spr_register_kvm(env, num, name, uea_read, uea_write, \
- oea_read, oea_write, one_reg_id, initial_value) \
- _spr_register(env, num, name, uea_read, uea_write, initial_value)
-#define spr_register_kvm_hv(env, num, name, uea_read, uea_write, \
- oea_read, oea_write, hea_read, hea_write, \
- one_reg_id, initial_value) \
- _spr_register(env, num, name, uea_read, uea_write, initial_value)
-#else
-#if !defined(CONFIG_KVM)
-#define spr_register_kvm(env, num, name, uea_read, uea_write, \
- oea_read, oea_write, one_reg_id, initial_value) \
- _spr_register(env, num, name, uea_read, uea_write, \
- oea_read, oea_write, oea_read, oea_write, initial_value)
-#define spr_register_kvm_hv(env, num, name, uea_read, uea_write, \
- oea_read, oea_write, hea_read, hea_write, \
- one_reg_id, initial_value) \
- _spr_register(env, num, name, uea_read, uea_write, \
- oea_read, oea_write, hea_read, hea_write, initial_value)
+/**
+ * _spr_register
+ *
+ * Register an SPR with all the callbacks required for tcg,
+ * and the ID number for KVM.
+ *
+ * The reason for the conditional compilation is that the tcg functions
+ * may be compiled out, and the system kvm header may not be available
+ * for supplying the ID numbers. This is ugly, but the best we can do.
+ */
+
+#ifdef CONFIG_TCG
+# define USR_ARG(X) X,
+# ifdef CONFIG_USER_ONLY
+# define SYS_ARG(X)
+# else
+# define SYS_ARG(X) X,
+# endif
#else
-#define spr_register_kvm(env, num, name, uea_read, uea_write, \
- oea_read, oea_write, one_reg_id, initial_value) \
- _spr_register(env, num, name, uea_read, uea_write, \
- oea_read, oea_write, oea_read, oea_write, \
- one_reg_id, initial_value)
-#define spr_register_kvm_hv(env, num, name, uea_read, uea_write, \
- oea_read, oea_write, hea_read, hea_write, \
- one_reg_id, initial_value) \
- _spr_register(env, num, name, uea_read, uea_write, \
- oea_read, oea_write, hea_read, hea_write, \
- one_reg_id, initial_value)
+# define USR_ARG(X)
+# define SYS_ARG(X)
#endif
+#ifdef CONFIG_KVM
+# define KVM_ARG(X) X,
+#else
+# define KVM_ARG(X)
#endif
-#define spr_register(env, num, name, uea_read, uea_write, \
- oea_read, oea_write, initial_value) \
- spr_register_kvm(env, num, name, uea_read, uea_write, \
- oea_read, oea_write, 0, initial_value)
-
-#define spr_register_hv(env, num, name, uea_read, uea_write, \
- oea_read, oea_write, hea_read, hea_write, \
- initial_value) \
- spr_register_kvm_hv(env, num, name, uea_read, uea_write, \
- oea_read, oea_write, hea_read, hea_write, \
- 0, initial_value)
-
-static inline void _spr_register(CPUPPCState *env, int num,
- const char *name,
- void (*uea_read)(DisasContext *ctx,
- int gprn, int sprn),
- void (*uea_write)(DisasContext *ctx,
- int sprn, int gprn),
-#if !defined(CONFIG_USER_ONLY)
+typedef void spr_callback(DisasContext *, int, int);
- void (*oea_read)(DisasContext *ctx,
- int gprn, int sprn),
- void (*oea_write)(DisasContext *ctx,
- int sprn, int gprn),
- void (*hea_read)(DisasContext *opaque,
- int gprn, int sprn),
- void (*hea_write)(DisasContext *opaque,
- int sprn, int gprn),
-#endif
-#if defined(CONFIG_KVM)
- uint64_t one_reg_id,
-#endif
- target_ulong initial_value)
+static void _spr_register(CPUPPCState *env, int num, const char *name,
+ USR_ARG(spr_callback *uea_read)
+ USR_ARG(spr_callback *uea_write)
+ SYS_ARG(spr_callback *oea_read)
+ SYS_ARG(spr_callback *oea_write)
+ SYS_ARG(spr_callback *hea_read)
+ SYS_ARG(spr_callback *hea_write)
+ KVM_ARG(uint64_t one_reg_id)
+ target_ulong initial_value)
{
- ppc_spr_t *spr;
+ ppc_spr_t *spr = &env->spr_cb[num];
+
+ /* No SPR should be registered twice. */
+ assert(spr->name == NULL);
+ assert(name != NULL);
- spr = &env->spr_cb[num];
- if (spr->name != NULL || env->spr[num] != 0x00000000 ||
-#if !defined(CONFIG_USER_ONLY)
- spr->oea_read != NULL || spr->oea_write != NULL ||
-#endif
- spr->uea_read != NULL || spr->uea_write != NULL) {
- printf("Error: Trying to register SPR %d (%03x) twice !\n", num, num);
- exit(1);
- }
-#if defined(PPC_DEBUG_SPR)
- printf("*** register spr %d (%03x) %s val " TARGET_FMT_lx "\n", num, num,
- name, initial_value);
-#endif
spr->name = name;
+ spr->default_value = initial_value;
+ env->spr[num] = initial_value;
+
+#ifdef CONFIG_TCG
spr->uea_read = uea_read;
spr->uea_write = uea_write;
-#if !defined(CONFIG_USER_ONLY)
+# ifndef CONFIG_USER_ONLY
spr->oea_read = oea_read;
spr->oea_write = oea_write;
spr->hea_read = hea_read;
spr->hea_write = hea_write;
+# endif
#endif
-#if defined(CONFIG_KVM)
- spr->one_reg_id = one_reg_id,
+#ifdef CONFIG_KVM
+ spr->one_reg_id = one_reg_id;
#endif
- env->spr[num] = spr->default_value = initial_value;
}
+/* spr_register_kvm_hv passes all required arguments. */
+#define spr_register_kvm_hv(env, num, name, uea_read, uea_write, \
+ oea_read, oea_write, hea_read, hea_write, \
+ one_reg_id, initial_value) \
+ _spr_register(env, num, name, \
+ USR_ARG(uea_read) USR_ARG(uea_write) \
+ SYS_ARG(oea_read) SYS_ARG(oea_write) \
+ SYS_ARG(hea_read) SYS_ARG(hea_write) \
+ KVM_ARG(one_reg_id) initial_value)
+
+/* spr_register_kvm duplicates the oea callbacks to the hea callbacks. */
+#define spr_register_kvm(env, num, name, uea_read, uea_write, \
+ oea_read, oea_write, one_reg_id, ival) \
+ spr_register_kvm_hv(env, num, name, uea_read, uea_write, oea_read, \
+ oea_write, oea_read, oea_write, one_reg_id, ival)
+
+/* spr_register_hv and spr_register are similar, except there is no kvm id. */
+#define spr_register_hv(env, num, name, uea_read, uea_write, \
+ oea_read, oea_write, hea_read, hea_write, ival) \
+ spr_register_kvm_hv(env, num, name, uea_read, uea_write, oea_read, \
+ oea_write, hea_read, hea_write, 0, ival)
+
+#define spr_register(env, num, name, uea_read, uea_write, \
+ oea_read, oea_write, ival) \
+ spr_register_kvm(env, num, name, uea_read, uea_write, \
+ oea_read, oea_write, 0, ival)
+
/* Generic PowerPC SPRs */
static void gen_spr_generic(CPUPPCState *env)
{
@@ -1700,8 +1693,6 @@ static void gen_spr_74xx(CPUPPCState *env)
SPR_NOACCESS, SPR_NOACCESS,
&spr_read_generic, spr_access_nop,
0x00000000);
- /* Not strictly an SPR */
- vscr_init(env, 0x00010000);
}
static void gen_l3_ctrl(CPUPPCState *env)
@@ -3457,7 +3448,7 @@ static void init_excp_POWER9(CPUPPCState *env)
#if !defined(CONFIG_USER_ONLY)
env->excp_vectors[POWERPC_EXCP_HVIRT] = 0x00000EA0;
- env->excp_vectors[POWERPC_EXCP_SYSCALL_VECTORED] = 0x00000000;
+ env->excp_vectors[POWERPC_EXCP_SYSCALL_VECTORED] = 0x00017000;
#endif
}
@@ -5441,7 +5432,7 @@ POWERPC_FAMILY(601)(ObjectClass *oc, void *data)
pcc->excp_model = POWERPC_EXCP_601;
pcc->bus_model = PPC_FLAGS_INPUT_6xx;
pcc->bfd_mach = bfd_mach_ppc_601;
- pcc->flags = POWERPC_FLAG_SE | POWERPC_FLAG_RTC_CLK;
+ pcc->flags = POWERPC_FLAG_SE | POWERPC_FLAG_RTC_CLK | POWERPC_FLAG_HID0_LE;
}
#define POWERPC_MSRR_601v (0x0000000000001040ULL)
@@ -5485,7 +5476,7 @@ POWERPC_FAMILY(601v)(ObjectClass *oc, void *data)
#endif
pcc->bus_model = PPC_FLAGS_INPUT_6xx;
pcc->bfd_mach = bfd_mach_ppc_601;
- pcc->flags = POWERPC_FLAG_SE | POWERPC_FLAG_RTC_CLK;
+ pcc->flags = POWERPC_FLAG_SE | POWERPC_FLAG_RTC_CLK | POWERPC_FLAG_HID0_LE;
}
static void init_proc_602(CPUPPCState *env)
@@ -6625,6 +6616,7 @@ static void init_proc_7400(CPUPPCState *env)
gen_tbl(env);
/* 74xx specific SPR */
gen_spr_74xx(env);
+ vscr_init(env, 0x00010000);
/* XXX : not implemented */
spr_register(env, SPR_UBAMR, "UBAMR",
&spr_read_ureg, SPR_NOACCESS,
@@ -6704,6 +6696,7 @@ static void init_proc_7410(CPUPPCState *env)
gen_tbl(env);
/* 74xx specific SPR */
gen_spr_74xx(env);
+ vscr_init(env, 0x00010000);
/* XXX : not implemented */
spr_register(env, SPR_UBAMR, "UBAMR",
&spr_read_ureg, SPR_NOACCESS,
@@ -6789,6 +6782,7 @@ static void init_proc_7440(CPUPPCState *env)
gen_tbl(env);
/* 74xx specific SPR */
gen_spr_74xx(env);
+ vscr_init(env, 0x00010000);
/* XXX : not implemented */
spr_register(env, SPR_UBAMR, "UBAMR",
&spr_read_ureg, SPR_NOACCESS,
@@ -6897,6 +6891,7 @@ static void init_proc_7450(CPUPPCState *env)
gen_tbl(env);
/* 74xx specific SPR */
gen_spr_74xx(env);
+ vscr_init(env, 0x00010000);
/* Level 3 cache control */
gen_l3_ctrl(env);
/* L3ITCR1 */
@@ -7031,6 +7026,7 @@ static void init_proc_7445(CPUPPCState *env)
gen_tbl(env);
/* 74xx specific SPR */
gen_spr_74xx(env);
+ vscr_init(env, 0x00010000);
/* LDSTCR */
/* XXX : not implemented */
spr_register(env, SPR_LDSTCR, "LDSTCR",
@@ -7168,6 +7164,7 @@ static void init_proc_7455(CPUPPCState *env)
gen_tbl(env);
/* 74xx specific SPR */
gen_spr_74xx(env);
+ vscr_init(env, 0x00010000);
/* Level 3 cache control */
gen_l3_ctrl(env);
/* LDSTCR */
@@ -7307,6 +7304,7 @@ static void init_proc_7457(CPUPPCState *env)
gen_tbl(env);
/* 74xx specific SPR */
gen_spr_74xx(env);
+ vscr_init(env, 0x00010000);
/* Level 3 cache control */
gen_l3_ctrl(env);
/* L3ITCR1 */
@@ -7470,6 +7468,7 @@ static void init_proc_e600(CPUPPCState *env)
gen_tbl(env);
/* 74xx specific SPR */
gen_spr_74xx(env);
+ vscr_init(env, 0x00010000);
/* XXX : not implemented */
spr_register(env, SPR_UBAMR, "UBAMR",
&spr_read_ureg, SPR_NOACCESS,
@@ -7720,11 +7719,6 @@ static void gen_spr_book3s_altivec(CPUPPCState *env)
&spr_read_generic, &spr_write_generic,
KVM_REG_PPC_VRSAVE, 0x00000000);
- /*
- * Can't find information on what this should be on reset. This
- * value is the one used by 74xx processors.
- */
- vscr_init(env, 0x00010000);
}
static void gen_spr_book3s_dbg(CPUPPCState *env)
@@ -7748,12 +7742,12 @@ static void gen_spr_book3s_dbg(CPUPPCState *env)
static void gen_spr_book3s_207_dbg(CPUPPCState *env)
{
- spr_register_kvm_hv(env, SPR_DAWR, "DAWR",
+ spr_register_kvm_hv(env, SPR_DAWR0, "DAWR0",
SPR_NOACCESS, SPR_NOACCESS,
SPR_NOACCESS, SPR_NOACCESS,
&spr_read_generic, &spr_write_generic,
KVM_REG_PPC_DAWR, 0x00000000);
- spr_register_kvm_hv(env, SPR_DAWRX, "DAWRX",
+ spr_register_kvm_hv(env, SPR_DAWRX0, "DAWRX0",
SPR_NOACCESS, SPR_NOACCESS,
SPR_NOACCESS, SPR_NOACCESS,
&spr_read_generic, &spr_write_generic,
@@ -8422,6 +8416,11 @@ static void init_proc_book3s_common(CPUPPCState *env)
gen_spr_book3s_pmu_sup(env);
gen_spr_book3s_pmu_user(env);
gen_spr_book3s_ctrl(env);
+ /*
+ * Can't find information on what this should be on reset. This
+ * value is the one used by 74xx processors.
+ */
+ vscr_init(env, 0x00010000);
}
static void init_proc_970(CPUPPCState *env)
@@ -9317,13 +9316,13 @@ POWERPC_FAMILY(POWER10)(ObjectClass *oc, void *data)
pcc->radix_page_info = &POWER10_radix_page_info;
pcc->lrg_decr_bits = 56;
#endif
- pcc->excp_model = POWERPC_EXCP_POWER9;
+ pcc->excp_model = POWERPC_EXCP_POWER10;
pcc->bus_model = PPC_FLAGS_INPUT_POWER9;
pcc->bfd_mach = bfd_mach_ppc64;
pcc->flags = POWERPC_FLAG_VRE | POWERPC_FLAG_SE |
POWERPC_FLAG_BE | POWERPC_FLAG_PMM |
POWERPC_FLAG_BUS_CLK | POWERPC_FLAG_CFAR |
- POWERPC_FLAG_VSX | POWERPC_FLAG_TM;
+ POWERPC_FLAG_VSX | POWERPC_FLAG_TM | POWERPC_FLAG_SCV;
pcc->l1_dcache_size = 0x8000;
pcc->l1_icache_size = 0x8000;
pcc->interrupts_big_endian = ppc_cpu_interrupts_big_endian_lpcr;
@@ -9560,590 +9559,6 @@ static void dump_ppc_sprs(CPUPPCState *env)
}
#endif
-/*****************************************************************************/
-
-/* Opcode types */
-enum {
- PPC_DIRECT = 0, /* Opcode routine */
- PPC_INDIRECT = 1, /* Indirect opcode table */
-};
-
-#define PPC_OPCODE_MASK 0x3
-
-static inline int is_indirect_opcode(void *handler)
-{
- return ((uintptr_t)handler & PPC_OPCODE_MASK) == PPC_INDIRECT;
-}
-
-static inline opc_handler_t **ind_table(void *handler)
-{
- return (opc_handler_t **)((uintptr_t)handler & ~PPC_OPCODE_MASK);
-}
-
-/* Instruction table creation */
-/* Opcodes tables creation */
-static void fill_new_table(opc_handler_t **table, int len)
-{
- int i;
-
- for (i = 0; i < len; i++) {
- table[i] = &invalid_handler;
- }
-}
-
-static int create_new_table(opc_handler_t **table, unsigned char idx)
-{
- opc_handler_t **tmp;
-
- tmp = g_new(opc_handler_t *, PPC_CPU_INDIRECT_OPCODES_LEN);
- fill_new_table(tmp, PPC_CPU_INDIRECT_OPCODES_LEN);
- table[idx] = (opc_handler_t *)((uintptr_t)tmp | PPC_INDIRECT);
-
- return 0;
-}
-
-static int insert_in_table(opc_handler_t **table, unsigned char idx,
- opc_handler_t *handler)
-{
- if (table[idx] != &invalid_handler) {
- return -1;
- }
- table[idx] = handler;
-
- return 0;
-}
-
-static int register_direct_insn(opc_handler_t **ppc_opcodes,
- unsigned char idx, opc_handler_t *handler)
-{
- if (insert_in_table(ppc_opcodes, idx, handler) < 0) {
- printf("*** ERROR: opcode %02x already assigned in main "
- "opcode table\n", idx);
-#if defined(DO_PPC_STATISTICS) || defined(PPC_DUMP_CPU)
- printf(" Registered handler '%s' - new handler '%s'\n",
- ppc_opcodes[idx]->oname, handler->oname);
-#endif
- return -1;
- }
-
- return 0;
-}
-
-static int register_ind_in_table(opc_handler_t **table,
- unsigned char idx1, unsigned char idx2,
- opc_handler_t *handler)
-{
- if (table[idx1] == &invalid_handler) {
- if (create_new_table(table, idx1) < 0) {
- printf("*** ERROR: unable to create indirect table "
- "idx=%02x\n", idx1);
- return -1;
- }
- } else {
- if (!is_indirect_opcode(table[idx1])) {
- printf("*** ERROR: idx %02x already assigned to a direct "
- "opcode\n", idx1);
-#if defined(DO_PPC_STATISTICS) || defined(PPC_DUMP_CPU)
- printf(" Registered handler '%s' - new handler '%s'\n",
- ind_table(table[idx1])[idx2]->oname, handler->oname);
-#endif
- return -1;
- }
- }
- if (handler != NULL &&
- insert_in_table(ind_table(table[idx1]), idx2, handler) < 0) {
- printf("*** ERROR: opcode %02x already assigned in "
- "opcode table %02x\n", idx2, idx1);
-#if defined(DO_PPC_STATISTICS) || defined(PPC_DUMP_CPU)
- printf(" Registered handler '%s' - new handler '%s'\n",
- ind_table(table[idx1])[idx2]->oname, handler->oname);
-#endif
- return -1;
- }
-
- return 0;
-}
-
-static int register_ind_insn(opc_handler_t **ppc_opcodes,
- unsigned char idx1, unsigned char idx2,
- opc_handler_t *handler)
-{
- return register_ind_in_table(ppc_opcodes, idx1, idx2, handler);
-}
-
-static int register_dblind_insn(opc_handler_t **ppc_opcodes,
- unsigned char idx1, unsigned char idx2,
- unsigned char idx3, opc_handler_t *handler)
-{
- if (register_ind_in_table(ppc_opcodes, idx1, idx2, NULL) < 0) {
- printf("*** ERROR: unable to join indirect table idx "
- "[%02x-%02x]\n", idx1, idx2);
- return -1;
- }
- if (register_ind_in_table(ind_table(ppc_opcodes[idx1]), idx2, idx3,
- handler) < 0) {
- printf("*** ERROR: unable to insert opcode "
- "[%02x-%02x-%02x]\n", idx1, idx2, idx3);
- return -1;
- }
-
- return 0;
-}
-
-static int register_trplind_insn(opc_handler_t **ppc_opcodes,
- unsigned char idx1, unsigned char idx2,
- unsigned char idx3, unsigned char idx4,
- opc_handler_t *handler)
-{
- opc_handler_t **table;
-
- if (register_ind_in_table(ppc_opcodes, idx1, idx2, NULL) < 0) {
- printf("*** ERROR: unable to join indirect table idx "
- "[%02x-%02x]\n", idx1, idx2);
- return -1;
- }
- table = ind_table(ppc_opcodes[idx1]);
- if (register_ind_in_table(table, idx2, idx3, NULL) < 0) {
- printf("*** ERROR: unable to join 2nd-level indirect table idx "
- "[%02x-%02x-%02x]\n", idx1, idx2, idx3);
- return -1;
- }
- table = ind_table(table[idx2]);
- if (register_ind_in_table(table, idx3, idx4, handler) < 0) {
- printf("*** ERROR: unable to insert opcode "
- "[%02x-%02x-%02x-%02x]\n", idx1, idx2, idx3, idx4);
- return -1;
- }
- return 0;
-}
-static int register_insn(opc_handler_t **ppc_opcodes, opcode_t *insn)
-{
- if (insn->opc2 != 0xFF) {
- if (insn->opc3 != 0xFF) {
- if (insn->opc4 != 0xFF) {
- if (register_trplind_insn(ppc_opcodes, insn->opc1, insn->opc2,
- insn->opc3, insn->opc4,
- &insn->handler) < 0) {
- return -1;
- }
- } else {
- if (register_dblind_insn(ppc_opcodes, insn->opc1, insn->opc2,
- insn->opc3, &insn->handler) < 0) {
- return -1;
- }
- }
- } else {
- if (register_ind_insn(ppc_opcodes, insn->opc1,
- insn->opc2, &insn->handler) < 0) {
- return -1;
- }
- }
- } else {
- if (register_direct_insn(ppc_opcodes, insn->opc1, &insn->handler) < 0) {
- return -1;
- }
- }
-
- return 0;
-}
-
-static int test_opcode_table(opc_handler_t **table, int len)
-{
- int i, count, tmp;
-
- for (i = 0, count = 0; i < len; i++) {
- /* Consistency fixup */
- if (table[i] == NULL) {
- table[i] = &invalid_handler;
- }
- if (table[i] != &invalid_handler) {
- if (is_indirect_opcode(table[i])) {
- tmp = test_opcode_table(ind_table(table[i]),
- PPC_CPU_INDIRECT_OPCODES_LEN);
- if (tmp == 0) {
- free(table[i]);
- table[i] = &invalid_handler;
- } else {
- count++;
- }
- } else {
- count++;
- }
- }
- }
-
- return count;
-}
-
-static void fix_opcode_tables(opc_handler_t **ppc_opcodes)
-{
- if (test_opcode_table(ppc_opcodes, PPC_CPU_OPCODES_LEN) == 0) {
- printf("*** WARNING: no opcode defined !\n");
- }
-}
-
-/*****************************************************************************/
-static void create_ppc_opcodes(PowerPCCPU *cpu, Error **errp)
-{
- PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu);
- opcode_t *opc;
-
- fill_new_table(cpu->opcodes, PPC_CPU_OPCODES_LEN);
- for (opc = opcodes; opc < &opcodes[ARRAY_SIZE(opcodes)]; opc++) {
- if (((opc->handler.type & pcc->insns_flags) != 0) ||
- ((opc->handler.type2 & pcc->insns_flags2) != 0)) {
- if (register_insn(cpu->opcodes, opc) < 0) {
- error_setg(errp, "ERROR initializing PowerPC instruction "
- "0x%02x 0x%02x 0x%02x", opc->opc1, opc->opc2,
- opc->opc3);
- return;
- }
- }
- }
- fix_opcode_tables(cpu->opcodes);
- fflush(stdout);
- fflush(stderr);
-}
-
-#if defined(PPC_DUMP_CPU)
-static void dump_ppc_insns(CPUPPCState *env)
-{
- opc_handler_t **table, *handler;
- const char *p, *q;
- uint8_t opc1, opc2, opc3, opc4;
-
- printf("Instructions set:\n");
- /* opc1 is 6 bits long */
- for (opc1 = 0x00; opc1 < PPC_CPU_OPCODES_LEN; opc1++) {
- table = env->opcodes;
- handler = table[opc1];
- if (is_indirect_opcode(handler)) {
- /* opc2 is 5 bits long */
- for (opc2 = 0; opc2 < PPC_CPU_INDIRECT_OPCODES_LEN; opc2++) {
- table = env->opcodes;
- handler = env->opcodes[opc1];
- table = ind_table(handler);
- handler = table[opc2];
- if (is_indirect_opcode(handler)) {
- table = ind_table(handler);
- /* opc3 is 5 bits long */
- for (opc3 = 0; opc3 < PPC_CPU_INDIRECT_OPCODES_LEN;
- opc3++) {
- handler = table[opc3];
- if (is_indirect_opcode(handler)) {
- table = ind_table(handler);
- /* opc4 is 5 bits long */
- for (opc4 = 0; opc4 < PPC_CPU_INDIRECT_OPCODES_LEN;
- opc4++) {
- handler = table[opc4];
- if (handler->handler != &gen_invalid) {
- printf("INSN: %02x %02x %02x %02x -- "
- "(%02d %04d %02d) : %s\n",
- opc1, opc2, opc3, opc4,
- opc1, (opc3 << 5) | opc2, opc4,
- handler->oname);
- }
- }
- } else {
- if (handler->handler != &gen_invalid) {
- /* Special hack to properly dump SPE insns */
- p = strchr(handler->oname, '_');
- if (p == NULL) {
- printf("INSN: %02x %02x %02x (%02d %04d) : "
- "%s\n",
- opc1, opc2, opc3, opc1,
- (opc3 << 5) | opc2,
- handler->oname);
- } else {
- q = "speundef";
- if ((p - handler->oname) != strlen(q)
- || (memcmp(handler->oname, q, strlen(q))
- != 0)) {
- /* First instruction */
- printf("INSN: %02x %02x %02x"
- "(%02d %04d) : %.*s\n",
- opc1, opc2 << 1, opc3, opc1,
- (opc3 << 6) | (opc2 << 1),
- (int)(p - handler->oname),
- handler->oname);
- }
- if (strcmp(p + 1, q) != 0) {
- /* Second instruction */
- printf("INSN: %02x %02x %02x "
- "(%02d %04d) : %s\n", opc1,
- (opc2 << 1) | 1, opc3, opc1,
- (opc3 << 6) | (opc2 << 1) | 1,
- p + 1);
- }
- }
- }
- }
- }
- } else {
- if (handler->handler != &gen_invalid) {
- printf("INSN: %02x %02x -- (%02d %04d) : %s\n",
- opc1, opc2, opc1, opc2, handler->oname);
- }
- }
- }
- } else {
- if (handler->handler != &gen_invalid) {
- printf("INSN: %02x -- -- (%02d ----) : %s\n",
- opc1, opc1, handler->oname);
- }
- }
- }
-}
-#endif
-
-static bool avr_need_swap(CPUPPCState *env)
-{
-#ifdef HOST_WORDS_BIGENDIAN
- return msr_le;
-#else
- return !msr_le;
-#endif
-}
-
-#if !defined(CONFIG_USER_ONLY)
-static int gdb_find_spr_idx(CPUPPCState *env, int n)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(env->spr_cb); i++) {
- ppc_spr_t *spr = &env->spr_cb[i];
-
- if (spr->name && spr->gdb_id == n) {
- return i;
- }
- }
- return -1;
-}
-
-static int gdb_get_spr_reg(CPUPPCState *env, GByteArray *buf, int n)
-{
- int reg;
- int len;
-
- reg = gdb_find_spr_idx(env, n);
- if (reg < 0) {
- return 0;
- }
-
- len = TARGET_LONG_SIZE;
- gdb_get_regl(buf, env->spr[reg]);
- ppc_maybe_bswap_register(env, gdb_get_reg_ptr(buf, len), len);
- return len;
-}
-
-static int gdb_set_spr_reg(CPUPPCState *env, uint8_t *mem_buf, int n)
-{
- int reg;
- int len;
-
- reg = gdb_find_spr_idx(env, n);
- if (reg < 0) {
- return 0;
- }
-
- len = TARGET_LONG_SIZE;
- ppc_maybe_bswap_register(env, mem_buf, len);
- env->spr[reg] = ldn_p(mem_buf, len);
-
- return len;
-}
-#endif
-
-static int gdb_get_float_reg(CPUPPCState *env, GByteArray *buf, int n)
-{
- uint8_t *mem_buf;
- if (n < 32) {
- gdb_get_reg64(buf, *cpu_fpr_ptr(env, n));
- mem_buf = gdb_get_reg_ptr(buf, 8);
- ppc_maybe_bswap_register(env, mem_buf, 8);
- return 8;
- }
- if (n == 32) {
- gdb_get_reg32(buf, env->fpscr);
- mem_buf = gdb_get_reg_ptr(buf, 4);
- ppc_maybe_bswap_register(env, mem_buf, 4);
- return 4;
- }
- return 0;
-}
-
-static int gdb_set_float_reg(CPUPPCState *env, uint8_t *mem_buf, int n)
-{
- if (n < 32) {
- ppc_maybe_bswap_register(env, mem_buf, 8);
- *cpu_fpr_ptr(env, n) = ldq_p(mem_buf);
- return 8;
- }
- if (n == 32) {
- ppc_maybe_bswap_register(env, mem_buf, 4);
- helper_store_fpscr(env, ldl_p(mem_buf), 0xffffffff);
- return 4;
- }
- return 0;
-}
-
-static int gdb_get_avr_reg(CPUPPCState *env, GByteArray *buf, int n)
-{
- uint8_t *mem_buf;
-
- if (n < 32) {
- ppc_avr_t *avr = cpu_avr_ptr(env, n);
- if (!avr_need_swap(env)) {
- gdb_get_reg128(buf, avr->u64[0] , avr->u64[1]);
- } else {
- gdb_get_reg128(buf, avr->u64[1] , avr->u64[0]);
- }
- mem_buf = gdb_get_reg_ptr(buf, 16);
- ppc_maybe_bswap_register(env, mem_buf, 8);
- ppc_maybe_bswap_register(env, mem_buf + 8, 8);
- return 16;
- }
- if (n == 32) {
- gdb_get_reg32(buf, helper_mfvscr(env));
- mem_buf = gdb_get_reg_ptr(buf, 4);
- ppc_maybe_bswap_register(env, mem_buf, 4);
- return 4;
- }
- if (n == 33) {
- gdb_get_reg32(buf, (uint32_t)env->spr[SPR_VRSAVE]);
- mem_buf = gdb_get_reg_ptr(buf, 4);
- ppc_maybe_bswap_register(env, mem_buf, 4);
- return 4;
- }
- return 0;
-}
-
-static int gdb_set_avr_reg(CPUPPCState *env, uint8_t *mem_buf, int n)
-{
- if (n < 32) {
- ppc_avr_t *avr = cpu_avr_ptr(env, n);
- ppc_maybe_bswap_register(env, mem_buf, 8);
- ppc_maybe_bswap_register(env, mem_buf + 8, 8);
- if (!avr_need_swap(env)) {
- avr->u64[0] = ldq_p(mem_buf);
- avr->u64[1] = ldq_p(mem_buf + 8);
- } else {
- avr->u64[1] = ldq_p(mem_buf);
- avr->u64[0] = ldq_p(mem_buf + 8);
- }
- return 16;
- }
- if (n == 32) {
- ppc_maybe_bswap_register(env, mem_buf, 4);
- helper_mtvscr(env, ldl_p(mem_buf));
- return 4;
- }
- if (n == 33) {
- ppc_maybe_bswap_register(env, mem_buf, 4);
- env->spr[SPR_VRSAVE] = (target_ulong)ldl_p(mem_buf);
- return 4;
- }
- return 0;
-}
-
-static int gdb_get_spe_reg(CPUPPCState *env, GByteArray *buf, int n)
-{
- if (n < 32) {
-#if defined(TARGET_PPC64)
- gdb_get_reg32(buf, env->gpr[n] >> 32);
- ppc_maybe_bswap_register(env, gdb_get_reg_ptr(buf, 4), 4);
-#else
- gdb_get_reg32(buf, env->gprh[n]);
-#endif
- return 4;
- }
- if (n == 32) {
- gdb_get_reg64(buf, env->spe_acc);
- ppc_maybe_bswap_register(env, gdb_get_reg_ptr(buf, 8), 8);
- return 8;
- }
- if (n == 33) {
- gdb_get_reg32(buf, env->spe_fscr);
- ppc_maybe_bswap_register(env, gdb_get_reg_ptr(buf, 4), 4);
- return 4;
- }
- return 0;
-}
-
-static int gdb_set_spe_reg(CPUPPCState *env, uint8_t *mem_buf, int n)
-{
- if (n < 32) {
-#if defined(TARGET_PPC64)
- target_ulong lo = (uint32_t)env->gpr[n];
- target_ulong hi;
-
- ppc_maybe_bswap_register(env, mem_buf, 4);
-
- hi = (target_ulong)ldl_p(mem_buf) << 32;
- env->gpr[n] = lo | hi;
-#else
- env->gprh[n] = ldl_p(mem_buf);
-#endif
- return 4;
- }
- if (n == 32) {
- ppc_maybe_bswap_register(env, mem_buf, 8);
- env->spe_acc = ldq_p(mem_buf);
- return 8;
- }
- if (n == 33) {
- ppc_maybe_bswap_register(env, mem_buf, 4);
- env->spe_fscr = ldl_p(mem_buf);
- return 4;
- }
- return 0;
-}
-
-static int gdb_get_vsx_reg(CPUPPCState *env, GByteArray *buf, int n)
-{
- if (n < 32) {
- gdb_get_reg64(buf, *cpu_vsrl_ptr(env, n));
- ppc_maybe_bswap_register(env, gdb_get_reg_ptr(buf, 8), 8);
- return 8;
- }
- return 0;
-}
-
-static int gdb_set_vsx_reg(CPUPPCState *env, uint8_t *mem_buf, int n)
-{
- if (n < 32) {
- ppc_maybe_bswap_register(env, mem_buf, 8);
- *cpu_vsrl_ptr(env, n) = ldq_p(mem_buf);
- return 8;
- }
- return 0;
-}
-
-static int ppc_fixup_cpu(PowerPCCPU *cpu)
-{
- CPUPPCState *env = &cpu->env;
-
- /*
- * TCG doesn't (yet) emulate some groups of instructions that are
- * implemented on some otherwise supported CPUs (e.g. VSX and
- * decimal floating point instructions on POWER7). We remove
- * unsupported instruction groups from the cpu state's instruction
- * masks and hope the guest can cope. For at least the pseries
- * machine, the unavailability of these instructions can be
- * advertised to the guest via the device tree.
- */
- if ((env->insns_flags & ~PPC_TCG_INSNS)
- || (env->insns_flags2 & ~PPC_TCG_INSNS2)) {
- warn_report("Disabling some instructions which are not "
- "emulated by TCG (0x%" PRIx64 ", 0x%" PRIx64 ")",
- env->insns_flags & ~PPC_TCG_INSNS,
- env->insns_flags2 & ~PPC_TCG_INSNS2);
- }
- env->insns_flags &= PPC_TCG_INSNS;
- env->insns_flags2 &= PPC_TCG_INSNS2;
- return 0;
-}
-
static void ppc_cpu_realize(DeviceState *dev, Error **errp)
{
CPUState *cs = CPU(dev);
@@ -10174,26 +9589,7 @@ static void ppc_cpu_realize(DeviceState *dev, Error **errp)
}
init_ppc_proc(cpu);
- if (pcc->insns_flags & PPC_FLOAT) {
- gdb_register_coprocessor(cs, gdb_get_float_reg, gdb_set_float_reg,
- 33, "power-fpu.xml", 0);
- }
- if (pcc->insns_flags & PPC_ALTIVEC) {
- gdb_register_coprocessor(cs, gdb_get_avr_reg, gdb_set_avr_reg,
- 34, "power-altivec.xml", 0);
- }
- if (pcc->insns_flags & PPC_SPE) {
- gdb_register_coprocessor(cs, gdb_get_spe_reg, gdb_set_spe_reg,
- 34, "power-spe.xml", 0);
- }
- if (pcc->insns_flags2 & PPC2_VSX) {
- gdb_register_coprocessor(cs, gdb_get_vsx_reg, gdb_set_vsx_reg,
- 32, "power-vsx.xml", 0);
- }
-#ifndef CONFIG_USER_ONLY
- gdb_register_coprocessor(cs, gdb_get_spr_reg, gdb_set_spr_reg,
- pcc->gdb_num_sprs, "power-spr.xml", 0);
-#endif
+ ppc_gdb_init(cs, pcc);
qemu_init_vcpu(cs);
pcc->parent_realize(dev, errp);
@@ -10374,40 +9770,12 @@ static void ppc_cpu_unrealize(DeviceState *dev)
{
PowerPCCPU *cpu = POWERPC_CPU(dev);
PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu);
- opc_handler_t **table, **table_2;
- int i, j, k;
pcc->parent_unrealize(dev);
cpu_remove_sync(CPU(cpu));
- for (i = 0; i < PPC_CPU_OPCODES_LEN; i++) {
- if (cpu->opcodes[i] == &invalid_handler) {
- continue;
- }
- if (is_indirect_opcode(cpu->opcodes[i])) {
- table = ind_table(cpu->opcodes[i]);
- for (j = 0; j < PPC_CPU_INDIRECT_OPCODES_LEN; j++) {
- if (table[j] == &invalid_handler) {
- continue;
- }
- if (is_indirect_opcode(table[j])) {
- table_2 = ind_table(table[j]);
- for (k = 0; k < PPC_CPU_INDIRECT_OPCODES_LEN; k++) {
- if (table_2[k] != &invalid_handler &&
- is_indirect_opcode(table_2[k])) {
- g_free((opc_handler_t *)((uintptr_t)table_2[k] &
- ~PPC_INDIRECT));
- }
- }
- g_free((opc_handler_t *)((uintptr_t)table[j] &
- ~PPC_INDIRECT));
- }
- }
- g_free((opc_handler_t *)((uintptr_t)cpu->opcodes[i] &
- ~PPC_INDIRECT));
- }
- }
+ destroy_ppc_opcodes(cpu);
}
static gint ppc_cpu_compare_class_pvr(gconstpointer a, gconstpointer b)
@@ -10835,15 +10203,6 @@ static bool ppc_pvr_match_default(PowerPCCPUClass *pcc, uint32_t pvr)
return pcc->pvr == pvr;
}
-static gchar *ppc_gdb_arch_name(CPUState *cs)
-{
-#if defined(TARGET_PPC64)
- return g_strdup("powerpc:common64");
-#else
- return g_strdup("powerpc:common");
-#endif
-}
-
static void ppc_disas_set_info(CPUState *cs, disassemble_info *info)
{
PowerPCCPU *cpu = POWERPC_CPU(cs);