|
@@ -657,3 +657,138 @@ got_exception:
|
|
|
una_reg(3), una_reg(4), una_reg(5));
|
|
|
printk("r6 = %016lx r7 = %016lx r8 = %016lx\n",
|
|
|
una_reg(6), una_reg(7), una_reg(8));
|
|
|
+ printk("r9 = %016lx r10= %016lx r11= %016lx\n",
|
|
|
+ una_reg(9), una_reg(10), una_reg(11));
|
|
|
+ printk("r12= %016lx r13= %016lx r14= %016lx\n",
|
|
|
+ una_reg(12), una_reg(13), una_reg(14));
|
|
|
+ printk("r15= %016lx\n", una_reg(15));
|
|
|
+ printk("r16= %016lx r17= %016lx r18= %016lx\n",
|
|
|
+ una_reg(16), una_reg(17), una_reg(18));
|
|
|
+ printk("r19= %016lx r20= %016lx r21= %016lx\n",
|
|
|
+ una_reg(19), una_reg(20), una_reg(21));
|
|
|
+ printk("r22= %016lx r23= %016lx r24= %016lx\n",
|
|
|
+ una_reg(22), una_reg(23), una_reg(24));
|
|
|
+ printk("r25= %016lx r27= %016lx r28= %016lx\n",
|
|
|
+ una_reg(25), una_reg(27), una_reg(28));
|
|
|
+ printk("gp = %016lx sp = %p\n", regs->gp, regs+1);
|
|
|
+
|
|
|
+ dik_show_code((unsigned int *)pc);
|
|
|
+ dik_show_trace((unsigned long *)(regs+1));
|
|
|
+
|
|
|
+ if (test_and_set_thread_flag (TIF_DIE_IF_KERNEL)) {
|
|
|
+ printk("die_if_kernel recursion detected.\n");
|
|
|
+ local_irq_enable();
|
|
|
+ while (1);
|
|
|
+ }
|
|
|
+ do_exit(SIGSEGV);
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * Convert an s-floating point value in memory format to the
|
|
|
+ * corresponding value in register format. The exponent
|
|
|
+ * needs to be remapped to preserve non-finite values
|
|
|
+ * (infinities, not-a-numbers, denormals).
|
|
|
+ */
|
|
|
+static inline unsigned long
|
|
|
+s_mem_to_reg (unsigned long s_mem)
|
|
|
+{
|
|
|
+ unsigned long frac = (s_mem >> 0) & 0x7fffff;
|
|
|
+ unsigned long sign = (s_mem >> 31) & 0x1;
|
|
|
+ unsigned long exp_msb = (s_mem >> 30) & 0x1;
|
|
|
+ unsigned long exp_low = (s_mem >> 23) & 0x7f;
|
|
|
+ unsigned long exp;
|
|
|
+
|
|
|
+ exp = (exp_msb << 10) | exp_low; /* common case */
|
|
|
+ if (exp_msb) {
|
|
|
+ if (exp_low == 0x7f) {
|
|
|
+ exp = 0x7ff;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if (exp_low == 0x00) {
|
|
|
+ exp = 0x000;
|
|
|
+ } else {
|
|
|
+ exp |= (0x7 << 7);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return (sign << 63) | (exp << 52) | (frac << 29);
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * Convert an s-floating point value in register format to the
|
|
|
+ * corresponding value in memory format.
|
|
|
+ */
|
|
|
+static inline unsigned long
|
|
|
+s_reg_to_mem (unsigned long s_reg)
|
|
|
+{
|
|
|
+ return ((s_reg >> 62) << 30) | ((s_reg << 5) >> 34);
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * Handle user-level unaligned fault. Handling user-level unaligned
|
|
|
+ * faults is *extremely* slow and produces nasty messages. A user
|
|
|
+ * program *should* fix unaligned faults ASAP.
|
|
|
+ *
|
|
|
+ * Notice that we have (almost) the regular kernel stack layout here,
|
|
|
+ * so finding the appropriate registers is a little more difficult
|
|
|
+ * than in the kernel case.
|
|
|
+ *
|
|
|
+ * Finally, we handle regular integer load/stores only. In
|
|
|
+ * particular, load-linked/store-conditionally and floating point
|
|
|
+ * load/stores are not supported. The former make no sense with
|
|
|
+ * unaligned faults (they are guaranteed to fail) and I don't think
|
|
|
+ * the latter will occur in any decent program.
|
|
|
+ *
|
|
|
+ * Sigh. We *do* have to handle some FP operations, because GCC will
|
|
|
+ * uses them as temporary storage for integer memory to memory copies.
|
|
|
+ * However, we need to deal with stt/ldt and sts/lds only.
|
|
|
+ */
|
|
|
+
|
|
|
+#define OP_INT_MASK ( 1L << 0x28 | 1L << 0x2c /* ldl stl */ \
|
|
|
+ | 1L << 0x29 | 1L << 0x2d /* ldq stq */ \
|
|
|
+ | 1L << 0x0c | 1L << 0x0d /* ldwu stw */ \
|
|
|
+ | 1L << 0x0a | 1L << 0x0e ) /* ldbu stb */
|
|
|
+
|
|
|
+#define OP_WRITE_MASK ( 1L << 0x26 | 1L << 0x27 /* sts stt */ \
|
|
|
+ | 1L << 0x2c | 1L << 0x2d /* stl stq */ \
|
|
|
+ | 1L << 0x0d | 1L << 0x0e ) /* stw stb */
|
|
|
+
|
|
|
+#define R(x) ((size_t) &((struct pt_regs *)0)->x)
|
|
|
+
|
|
|
+static int unauser_reg_offsets[32] = {
|
|
|
+ R(r0), R(r1), R(r2), R(r3), R(r4), R(r5), R(r6), R(r7), R(r8),
|
|
|
+ /* r9 ... r15 are stored in front of regs. */
|
|
|
+ -56, -48, -40, -32, -24, -16, -8,
|
|
|
+ R(r16), R(r17), R(r18),
|
|
|
+ R(r19), R(r20), R(r21), R(r22), R(r23), R(r24), R(r25), R(r26),
|
|
|
+ R(r27), R(r28), R(gp),
|
|
|
+ 0, 0
|
|
|
+};
|
|
|
+
|
|
|
+#undef R
|
|
|
+
|
|
|
+asmlinkage void
|
|
|
+do_entUnaUser(void __user * va, unsigned long opcode,
|
|
|
+ unsigned long reg, struct pt_regs *regs)
|
|
|
+{
|
|
|
+ static DEFINE_RATELIMIT_STATE(ratelimit, 5 * HZ, 5);
|
|
|
+
|
|
|
+ unsigned long tmp1, tmp2, tmp3, tmp4;
|
|
|
+ unsigned long fake_reg, *reg_addr = &fake_reg;
|
|
|
+ siginfo_t info;
|
|
|
+ long error;
|
|
|
+
|
|
|
+ /* Check the UAC bits to decide what the user wants us to do
|
|
|
+ with the unaliged access. */
|
|
|
+
|
|
|
+ if (!(current_thread_info()->status & TS_UAC_NOPRINT)) {
|
|
|
+ if (__ratelimit(&ratelimit)) {
|
|
|
+ printk("%s(%d): unaligned trap at %016lx: %p %lx %ld\n",
|
|
|
+ current->comm, task_pid_nr(current),
|
|
|
+ regs->pc - 4, va, opcode, reg);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if ((current_thread_info()->status & TS_UAC_SIGBUS))
|
|
|
+ goto give_sigbus;
|
|
|
+ /* Not sure why you'd want to use this, but... */
|
|
|
+ if ((current_thread_info()->status & TS_UAC_NOFIX))
|
|
|
+ return;
|