]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
sparc64: Set valid bytes of misaligned no-fault loads
authorRob Gardner <rob.gardner@oracle.com>
Fri, 9 Jun 2017 04:36:24 +0000 (00:36 -0400)
committerAllen Pais <allen.pais@oracle.com>
Thu, 29 Jun 2017 08:09:44 +0000 (13:39 +0530)
If a misaligned no-fault load (ldm* from ASI 0x82, primary no fault)
crosses a page boundary, and one of the pages causes an MMU miss
that cannot be resolved, then the kernel must load bytes from the
valid page into the high (or low) bytes of the destination register,
and must load zeros into the low (or high) bytes of the register.

Orabug: 25766652

Signed-off-by: Rob Gardner <rob.gardner@oracle.com>
Reviewed-by: Steve Sistare steven.sistare@oracle.com
Reviewed-by: Anthony Yznaga <anthony.yznaga@oracle.com>
Signed-off-by: Allen Pais <allen.pais@oracle.com>
arch/sparc/include/asm/adi_64.h
arch/sparc/include/asm/setup.h
arch/sparc/kernel/unaligned_64.c
arch/sparc/mm/fault_64.c

index cd2ce353de9e910789cdd81f48b54bde71a0c950..7fbe92a04495956276e49a80e06de5ab41358a31 100644 (file)
@@ -43,6 +43,35 @@ static inline unsigned long adi_nbits(void)
        return adi_state.caps.nbits;
 }
 
+static inline unsigned long adi_normalize(long addr)
+{
+       return addr << adi_nbits() >> adi_nbits();
+}
+
+static inline unsigned long adi_pstate_disable(void)
+{
+       unsigned long saved_pstate;
+
+       __asm__ __volatile__(
+               "rdpr   %%pstate, %0    \n\t"
+               "andn   %0, %1, %%g1    \n\t"
+               "wrpr   %%g1, %%pstate  \n\t"
+               : "=&r" (saved_pstate)
+               : "i"   (PSTATE_MCDE)
+               : "g1");
+
+       return saved_pstate;
+}
+
+static inline void adi_pstate_restore(unsigned long saved_pstate)
+{
+       __asm__ __volatile__(
+               "wrpr   %0, %%pstate  \n\t"
+               :
+               : "r" (saved_pstate)
+               : );
+}
+
 #endif /* __ASSEMBLY__ */
 
 #endif /* !(__ASM_SPARC64_ADI_H) */
index 7cf712fa37a6ce3a75d86af7bf434a970c235772..54f5f94800a68237679be0b74b85ddadc6ac3400 100644 (file)
@@ -53,6 +53,7 @@ void __init start_early_boot(void);
 /* unaligned_64.c */
 int handle_ldf_stq(u32 insn, struct pt_regs *regs);
 void handle_ld_nf(u32 insn, struct pt_regs *regs);
+int handle_ldm_nf(u32 insn, struct pt_regs *regs);
 
 /* init_64.c */
 extern atomic_t dcpage_flushes;
index c89371481ae229fafd70218d458565ff1766cdfa..e301ba5dd444072dd1877e2ceca812115b7e837d 100644 (file)
@@ -592,6 +592,91 @@ void handle_ld_nf(u32 insn, struct pt_regs *regs)
        advance(regs);
 }
 
+int handle_ldm_nf(u32 insn, struct pt_regs *regs)
+{
+       int i, sign_extend_bits = 0, bytes_needed;
+       unsigned long *uregp, saved_pstate = 0;
+       int rd = ((insn >> 25) & 0x1f);
+       int opm = ((insn >> 10) & 0x7);
+       long val, addr;
+       bool is_adi;
+
+       if (regs->tstate & TSTATE_PRIV)
+               return 0;
+
+       perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, regs, 0);
+
+       switch (opm) {
+       case 0:
+               sign_extend_bits = 48;
+               /* fall through */
+       case 1:
+               bytes_needed = 2;
+               break;
+       case 2:
+               sign_extend_bits = 32;
+               /* fall through */
+       case 3:
+               bytes_needed = 4;
+               break;
+       case 5:
+               sign_extend_bits = 0;
+               bytes_needed = 8;
+               break;
+       default:
+               return 0;
+       }
+
+       /*
+        * The LDM* instructions only have a 10 bit immediate so
+        * it might look like a bug to use compute_effective_address()
+        * here. But it's ok because LDM*A cannot have any immediate
+        * offset, so the effective address is always correctly
+        * calculated using only the registers.
+        */
+       addr = compute_effective_address(regs, insn, rd);
+
+       /*
+        * ADI logic here: Since we are emulating a no-fault load,
+        * we also want to emulate the h/w behavior, which is to actually
+        * suppress ADI-related faults. So if the application has
+        * ADI enabled, then let's disable MCD here before attempting
+        * the access, and use a normalized address as well.
+        * This is all to ensure that the only potential faults
+        * are MMU related.
+        */
+       if ((is_adi = (current->mm && current->mm->context.adi))) {
+               saved_pstate = adi_pstate_disable();
+               addr = adi_normalize(addr);
+       }
+
+       for (i = 0, val = 0; i < bytes_needed; i++) {
+               unsigned char v;
+
+               if (get_user(v, (unsigned char __user *) (addr+i)))
+                       v = 0;
+               val = (val << 8) | v;
+       }
+
+       if (is_adi)
+               adi_pstate_restore(saved_pstate);
+
+       if (sign_extend_bits)
+               val = val << sign_extend_bits >> sign_extend_bits;
+
+       maybe_flush_windows(0, 0, rd, 0);
+       uregp = fetch_reg_addr(rd, regs);
+       if (rd < 16)
+               uregp[0] = val;
+       else if (test_thread_64bit_stack(regs->u_regs[UREG_FP]))
+               put_user(val, (long __user *) uregp);
+       else
+               put_user(val, (int __user *) uregp);
+
+       advance(regs);
+       return 1;
+}
+
 void handle_lddfmna(struct pt_regs *regs, unsigned long sfar, unsigned long sfsr)
 {
        enum ctx_state prev_state = exception_enter();
index 02cbe9512131babcff6dd08e10672c3bb9c3677a..f9a7b843a4e23a00ce5b7c14ca9f6bbce09922e6 100644 (file)
@@ -248,6 +248,14 @@ static void __kprobes do_kernel_fault(struct pt_regs *regs, int si_code,
                        }
                        return;
                }
+               /* M8 misaligned load alternate space; OSA 2017, sec 7.89 */
+               if ((insn &
+                       ((3<<30) | (0x3f<<19) | (1<<13) | (1<<9))) ==
+                       ((3<<30) | (0x31<<19) |           (1<<9))) {
+                       asi = (regs->tstate >> 24);
+                       if ((asi & 0xf2) == 0x82 && handle_ldm_nf(insn, regs))
+                                       return;
+               }
        }
                
        /* Is this in ex_table? */