@@ -55,6 +55,7 @@ extern struct ppc_emulated {
struct ppc_emulated_entry mfdscr;
struct ppc_emulated_entry mtdscr;
struct ppc_emulated_entry lq_stq;
+ struct ppc_emulated_entry paste;
#endif
} ppc_emulated;
@@ -229,6 +229,7 @@
#define PPC_INST_MTTMR 0x7c0003dc
#define PPC_INST_NOP 0x60000000
#define PPC_INST_PASTE 0x7c20070d
+#define PPC_INST_PASTE_MASK 0xfc2007ff
#define PPC_INST_POPCNTB 0x7c0000f4
#define PPC_INST_POPCNTB_MASK 0xfc0007fe
#define PPC_INST_POPCNTD 0x7c0003f4
@@ -468,6 +468,8 @@
#define SPRN_DBAT7U 0x23E /* Data BAT 7 Upper Register */
#define SPRN_PPR 0x380 /* SMT Thread status Register */
#define SPRN_TSCR 0x399 /* Thread Switch Control Register */
+#define SPRN_TRIG1 0x371 /* WAT Trigger 1 */
+#define SPRN_TRIG2 0x372 /* WAT Trigger 2 */
#define SPRN_DEC 0x016 /* Decrement Register */
#define SPRN_DER 0x095 /* Debug Enable Register */
@@ -956,6 +956,65 @@ static inline bool tm_abort_check(struct pt_regs *regs, int reason)
}
#endif
+static DEFINE_SPINLOCK(paste_emulation_lock);
+
+static inline int paste(void *i)
+{
+ int cr;
+ long retval = 0;
+
+ /* Need per core lock to ensure trig1/2 writes don't race */
+ spin_lock(&paste_emulation_lock);
+ mtspr(SPRN_TRIG1, 0); /* data doesn't matter */
+ mtspr(SPRN_TRIG1, 0); /* HW says do this twice */
+ asm volatile(
+ "1: " PPC_PASTE(0, %2) "\n"
+ "2: mfcr %1\n"
+ ".section .fixup,\"ax\"\n"
+ "3: li %0,%3\n"
+ " li %2,0\n"
+ " b 2b\n"
+ ".previous\n"
+ EX_TABLE(1b, 3b)
+ : "=r" (retval), "=r" (cr)
+ : "b" (i), "i" (-EFAULT), "0" (retval));
+ mtspr(SPRN_TRIG2, 0);
+ spin_unlock(&paste_emulation_lock);
+ return cr;
+}
+
+static int emulate_paste(struct pt_regs *regs, u32 instword)
+{
+ const void __user *addr;
+ unsigned long ea;
+ u8 ra, rb;
+
+ if (!cpu_has_feature(CPU_FTR_ARCH_300))
+ return -EINVAL;
+
+ ra = (instword >> 16) & 0x1f;
+ rb = (instword >> 11) & 0x1f;
+
+ ea = regs->gpr[rb] + (ra ? regs->gpr[ra] : 0ul);
+ if (is_32bit_task())
+ ea &= 0xfffffffful;
+ addr = (__force const void __user *)ea;
+
+ if (!access_ok(VERIFY_WRITE, addr, 128)) // cacheline size == 128
+ return -EFAULT;
+
+ hard_irq_disable(); /* FIXME: could we just soft disable ?? */
+ pagefault_disable();
+
+ PPC_WARN_EMULATED(paste, regs);
+ regs->ccr = paste((void *)addr);
+
+ pagefault_enable();
+ may_hard_irq_enable();
+
+ return 0;
+}
+
static int emulate_instruction(struct pt_regs *regs)
{
u32 instword;
@@ -968,6 +1027,10 @@ static int emulate_instruction(struct pt_regs *regs)
if (get_user(instword, (u32 __user *)(regs->nip)))
return -EFAULT;
+ /* Emulate the paste RA, RB. */
+ if ((instword & PPC_INST_PASTE_MASK) == PPC_INST_PASTE)
+ return emulate_paste(regs, instword);
+
/* Emulate the mfspr rD, PVR. */
if ((instword & PPC_INST_MFSPR_PVR_MASK) == PPC_INST_MFSPR_PVR) {
PPC_WARN_EMULATED(mfpvr, regs);
@@ -1924,6 +1987,7 @@ struct ppc_emulated ppc_emulated = {
WARN_EMULATED_SETUP(mfdscr),
WARN_EMULATED_SETUP(mtdscr),
WARN_EMULATED_SETUP(lq_stq),
+ WARN_EMULATED_SETUP(paste),
#endif
};