vserver 2.0 rc7
[linux-2.6.git] / arch / ppc / kernel / traps.c
index 0d7eb74..c65731e 100644 (file)
@@ -176,7 +176,7 @@ static inline int check_io_access(struct pt_regs *regs)
 #else
 #define get_mc_reason(regs)    (mfspr(SPRN_MCSR))
 #endif
-#define REASON_FP              0
+#define REASON_FP              ESR_FP
 #define REASON_ILLEGAL         ESR_PIL
 #define REASON_PRIVILEGED      ESR_PPR
 #define REASON_TRAP            ESR_PTR
@@ -389,6 +389,82 @@ void RunModeException(struct pt_regs *regs)
 #define INST_MCRXR             0x7c000400
 #define INST_MCRXR_MASK                0x7c0007fe
 
+#define INST_STRING            0x7c00042a
+#define INST_STRING_MASK       0x7c0007fe
+#define INST_STRING_GEN_MASK   0x7c00067e
+#define INST_LSWI              0x7c0004aa
+#define INST_LSWX              0x7c00042a
+#define INST_STSWI             0x7c0005aa
+#define INST_STSWX             0x7c00052a
+
+static int emulate_string_inst(struct pt_regs *regs, u32 instword)
+{
+       u8 rT = (instword >> 21) & 0x1f;
+       u8 rA = (instword >> 16) & 0x1f;
+       u8 NB_RB = (instword >> 11) & 0x1f;
+       u32 num_bytes;
+       unsigned long EA;
+       int pos = 0;
+
+       /* Early out if we are an invalid form of lswx */
+       if ((instword & INST_STRING_MASK) == INST_LSWX)
+               if ((rT == rA) || (rT == NB_RB))
+                       return -EINVAL;
+
+       EA = (rA == 0) ? 0 : regs->gpr[rA];
+
+       switch (instword & INST_STRING_MASK) {
+               case INST_LSWX:
+               case INST_STSWX:
+                       EA += NB_RB;
+                       num_bytes = regs->xer & 0x7f;
+                       break;
+               case INST_LSWI:
+               case INST_STSWI:
+                       num_bytes = (NB_RB == 0) ? 32 : NB_RB;
+                       break;
+               default:
+                       return -EINVAL;
+       }
+
+       while (num_bytes != 0)
+       {
+               u8 val;
+               u32 shift = 8 * (3 - (pos & 0x3));
+
+               switch ((instword & INST_STRING_MASK)) {
+                       case INST_LSWX:
+                       case INST_LSWI:
+                               if (get_user(val, (u8 __user *)EA))
+                                       return -EFAULT;
+                               /* first time updating this reg,
+                                * zero it out */
+                               if (pos == 0)
+                                       regs->gpr[rT] = 0;
+                               regs->gpr[rT] |= val << shift;
+                               break;
+                       case INST_STSWI:
+                       case INST_STSWX:
+                               val = regs->gpr[rT] >> shift;
+                               if (put_user(val, (u8 __user *)EA))
+                                       return -EFAULT;
+                               break;
+               }
+               /* move EA to next address */
+               EA += 1;
+               num_bytes--;
+
+               /* manage our position within the register */
+               if (++pos == 4) {
+                       pos = 0;
+                       if (++rT == 32)
+                               rT = 0;
+               }
+       }
+
+       return 0;
+}
+
 static int emulate_instruction(struct pt_regs *regs)
 {
        u32 instword;
@@ -405,7 +481,7 @@ static int emulate_instruction(struct pt_regs *regs)
         */
        if ((instword & INST_MFSPR_PVR_MASK) == INST_MFSPR_PVR) {
                rd = (instword >> 21) & 0x1f;
-               regs->gpr[rd] = mfspr(PVR);
+               regs->gpr[rd] = mfspr(SPRN_PVR);
                return 0;
        }
 
@@ -423,6 +499,10 @@ static int emulate_instruction(struct pt_regs *regs)
                return 0;
        }
 
+       /* Emulate load/store string insn. */
+       if ((instword & INST_STRING_GEN_MASK) == INST_STRING)
+               return emulate_string_inst(regs, instword);
+
        return -EINVAL;
 }
 
@@ -594,6 +674,7 @@ void AlignmentException(struct pt_regs *regs)
        fixed = fix_alignment(regs);
        if (fixed == 1) {
                regs->nip += 4; /* skip over emulated instruction */
+               emulate_single_step(regs);
                return;
        }
        if (fixed == -EFAULT) {
@@ -720,6 +801,13 @@ void AltivecAssistException(struct pt_regs *regs)
        if (regs->msr & MSR_VEC)
                giveup_altivec(current);
        preempt_enable();
+       if (!user_mode(regs)) {
+               printk(KERN_ERR "altivec assist exception in kernel mode"
+                      " at %lx\n", regs->nip);
+               debugger(regs);
+               die("altivec assist exception", regs, SIGFPE);
+               return;
+       }
 
        err = emulate_altivec(regs);
        if (err == 0) {