Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

MIPS: Use per-mm page to execute branch delay slot instructions

In some cases the kernel needs to execute an instruction from the delay
slot of an emulated branch instruction. These cases include:

- Emulated floating point branch instructions (bc1[ft]l?) for systems
which don't include an FPU, or upon which the kernel is run with the
"nofpu" parameter.

- MIPSr6 systems running binaries targeting older revisions of the
architecture, which may include branch instructions whose encodings
are no longer valid in MIPSr6.

Executing instructions from such delay slots is done by writing the
instruction to memory followed by a trap, as part of an "emuframe", and
executing it. This avoids the requirement of an emulator for the entire
MIPS instruction set. Prior to this patch such emuframes are written to
the user stack and executed from there.

This patch moves FP branch delay emuframes off of the user stack and
into a per-mm page. Allocating a page per-mm leaves userland with access
to only what it had access to previously, and compared to other
solutions is relatively simple.

When a thread requires a delay slot emulation, it is allocated a frame.
A thread may only have one frame allocated at any one time, since it may
only ever be executing one instruction at any one time. In order to
ensure that we can free up allocated frame later, its index is recorded
in struct thread_struct. In the typical case, after executing the delay
slot instruction we'll execute a break instruction with the BRK_MEMU
code. This traps back to the kernel & leads to a call to do_dsemulret
which frees the allocated frame & moves the user PC back to the
instruction that would have executed following the emulated branch.
In some cases the delay slot instruction may be invalid, such as a
branch, or may trigger an exception. In these cases the BRK_MEMU break
instruction will not be hit. In order to ensure that frames are freed
this patch introduces dsemul_thread_cleanup() and calls it to free any
allocated frame upon thread exit. If the instruction generated an
exception & leads to a signal being delivered to the thread, or indeed
if a signal simply happens to be delivered to the thread whilst it is
executing from the struct emuframe, then we need to take care to exit
the frame appropriately. This is done by either rolling back the user PC
to the branch or advancing it to the continuation PC prior to signal
delivery, using dsemul_thread_rollback(). If this were not done then a
sigreturn would return to the struct emuframe, and if that frame had
meanwhile been used in response to an emulated branch instruction within
the signal handler then we would execute the wrong user code.

Whilst a user could theoretically place something like a compact branch
to self in a delay slot and cause their thread to become stuck in an
infinite loop with the frame never being deallocated, this would:

- Only affect the users single process.

- Be architecturally invalid since there would be a branch in the
delay slot, which is forbidden.

- Be extremely unlikely to happen by mistake, and provide a program
with no more ability to harm the system than a simple infinite loop
would.

If a thread requires a delay slot emulation & no frame is available to
it (ie. the process has enough other threads that all frames are
currently in use) then the thread joins a waitqueue. It will sleep until
a frame is freed by another thread in the process.

Since we now know whether a thread has an allocated frame due to our
tracking of its index, the cookie field of struct emuframe is removed as
we can be more certain whether we have a valid frame. Since a thread may
only ever have a single frame at any given time, the epc field of struct
emuframe is also removed & the PC to continue from is instead stored in
struct thread_struct. Together these changes simplify & shrink struct
emuframe somewhat, allowing twice as many frames to fit into the page
allocated for them.

The primary benefit of this patch is that we are now free to mark the
user stack non-executable where that is possible.

Signed-off-by: Paul Burton <paul.burton@imgtec.com>
Cc: Leonid Yegoshin <leonid.yegoshin@imgtec.com>
Cc: Maciej Rozycki <maciej.rozycki@imgtec.com>
Cc: Faraz Shahbazker <faraz.shahbazker@imgtec.com>
Cc: Raghu Gandham <raghu.gandham@imgtec.com>
Cc: Matthew Fortune <matthew.fortune@imgtec.com>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/13764/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>

authored by

Paul Burton and committed by
Ralf Baechle
432c6bac 33799a6d

+391 -133
+1
arch/mips/Kconfig
··· 64 64 select GENERIC_TIME_VSYSCALL 65 65 select ARCH_CLOCKSOURCE_DATA 66 66 select HANDLE_DOMAIN_IRQ 67 + select HAVE_EXIT_THREAD 67 68 68 69 menu "Machine selection" 69 70
+92
arch/mips/include/asm/dsemul.h
··· 1 + /* 2 + * Copyright (C) 2016 Imagination Technologies 3 + * Author: Paul Burton <paul.burton@imgtec.com> 4 + * 5 + * This program is free software; you can redistribute it and/or modify it 6 + * under the terms of the GNU General Public License as published by the 7 + * Free Software Foundation; either version 2 of the License, or (at your 8 + * option) any later version. 9 + */ 10 + 11 + #ifndef __MIPS_ASM_DSEMUL_H__ 12 + #define __MIPS_ASM_DSEMUL_H__ 13 + 14 + #include <asm/break.h> 15 + #include <asm/inst.h> 16 + 17 + /* Break instruction with special math emu break code set */ 18 + #define BREAK_MATH(micromips) (((micromips) ? 0x7 : 0xd) | (BRK_MEMU << 16)) 19 + 20 + /* When used as a frame index, indicates the lack of a frame */ 21 + #define BD_EMUFRAME_NONE ((int)BIT(31)) 22 + 23 + struct mm_struct; 24 + struct pt_regs; 25 + struct task_struct; 26 + 27 + /** 28 + * mips_dsemul() - 'Emulate' an instruction from a branch delay slot 29 + * @regs: User thread register context. 30 + * @ir: The instruction to be 'emulated'. 31 + * @branch_pc: The PC of the branch instruction. 32 + * @cont_pc: The PC to continue at following 'emulation'. 33 + * 34 + * Emulate or execute an arbitrary MIPS instruction within the context of 35 + * the current user thread. This is used primarily to handle instructions 36 + * in the delay slots of emulated branch instructions, for example FP 37 + * branch instructions on systems without an FPU. 38 + * 39 + * Return: Zero on success, negative if ir is a NOP, signal number on failure. 40 + */ 41 + extern int mips_dsemul(struct pt_regs *regs, mips_instruction ir, 42 + unsigned long branch_pc, unsigned long cont_pc); 43 + 44 + /** 45 + * do_dsemulret() - Return from a delay slot 'emulation' frame 46 + * @xcp: User thread register context. 47 + * 48 + * Call in response to the BRK_MEMU break instruction used to return to 49 + * the kernel from branch delay slot 'emulation' frames following a call 50 + * to mips_dsemul(). Restores the user thread PC to the value that was 51 + * passed as the cpc parameter to mips_dsemul(). 52 + * 53 + * Return: True if an emulation frame was returned from, else false. 54 + */ 55 + extern bool do_dsemulret(struct pt_regs *xcp); 56 + 57 + /** 58 + * dsemul_thread_cleanup() - Cleanup thread 'emulation' frame 59 + * @tsk: The task structure associated with the thread 60 + * 61 + * If the thread @tsk has a branch delay slot 'emulation' frame 62 + * allocated to it then free that frame. 63 + * 64 + * Return: True if a frame was freed, else false. 65 + */ 66 + extern bool dsemul_thread_cleanup(struct task_struct *tsk); 67 + 68 + /** 69 + * dsemul_thread_rollback() - Rollback from an 'emulation' frame 70 + * @regs: User thread register context. 71 + * 72 + * If the current thread, whose register context is represented by @regs, 73 + * is executing within a delay slot 'emulation' frame then exit that 74 + * frame. The PC will be rolled back to the branch if the instruction 75 + * that was being 'emulated' has not yet executed, or advanced to the 76 + * continuation PC if it has. 77 + * 78 + * Return: True if a frame was exited, else false. 79 + */ 80 + extern bool dsemul_thread_rollback(struct pt_regs *regs); 81 + 82 + /** 83 + * dsemul_mm_cleanup() - Cleanup per-mm delay slot 'emulation' state 84 + * @mm: The struct mm_struct to cleanup state for. 85 + * 86 + * Cleanup state for the given @mm, ensuring that any memory allocated 87 + * for delay slot 'emulation' book-keeping is freed. This is to be called 88 + * before @mm is freed in order to avoid memory leaks. 89 + */ 90 + extern void dsemul_mm_cleanup(struct mm_struct *mm); 91 + 92 + #endif /* __MIPS_ASM_DSEMUL_H__ */
+3 -14
arch/mips/include/asm/fpu_emulator.h
··· 24 24 #define _ASM_FPU_EMULATOR_H 25 25 26 26 #include <linux/sched.h> 27 - #include <asm/break.h> 27 + #include <asm/dsemul.h> 28 28 #include <asm/thread_info.h> 29 29 #include <asm/inst.h> 30 30 #include <asm/local.h> ··· 60 60 #define MIPS_FPU_EMU_INC_STATS(M) do { } while (0) 61 61 #endif /* CONFIG_DEBUG_FS */ 62 62 63 - extern int mips_dsemul(struct pt_regs *regs, mips_instruction ir, 64 - unsigned long cpc); 65 - extern int do_dsemulret(struct pt_regs *xcp); 66 63 extern int fpu_emulator_cop1Handler(struct pt_regs *xcp, 67 64 struct mips_fpu_struct *ctx, int has_fpu, 68 65 void *__user *fault_addr); 69 66 int process_fpemu_return(int sig, void __user *fault_addr, 70 67 unsigned long fcr31); 68 + int isBranchInstr(struct pt_regs *regs, struct mm_decoded_insn dec_insn, 69 + unsigned long *contpc); 71 70 int mm_isBranchInstr(struct pt_regs *regs, struct mm_decoded_insn dec_insn, 72 71 unsigned long *contpc); 73 - 74 - /* 75 - * Instruction inserted following the badinst to further tag the sequence 76 - */ 77 - #define BD_COOKIE 0x0000bd36 /* tne $0, $0 with baggage */ 78 - 79 - /* 80 - * Break instruction with special math emu break code set 81 - */ 82 - #define BREAK_MATH(micromips) (((micromips) ? 0x7 : 0xd) | (BRK_MEMU << 16)) 83 72 84 73 #define SIGNALLING_NAN 0x7ff800007ff80000LL 85 74
+9
arch/mips/include/asm/mmu.h
··· 2 2 #define __ASM_MMU_H 3 3 4 4 #include <linux/atomic.h> 5 + #include <linux/spinlock.h> 6 + #include <linux/wait.h> 5 7 6 8 typedef struct { 7 9 unsigned long asid[NR_CPUS]; 8 10 void *vdso; 9 11 atomic_t fp_mode_switching; 12 + 13 + /* lock to be held whilst modifying fp_bd_emupage_allocmap */ 14 + spinlock_t bd_emupage_lock; 15 + /* bitmap tracking allocation of fp_bd_emupage */ 16 + unsigned long *bd_emupage_allocmap; 17 + /* wait queue for threads requiring an emuframe */ 18 + wait_queue_head_t bd_emupage_queue; 10 19 } mm_context_t; 11 20 12 21 #endif /* __ASM_MMU_H */
+6
arch/mips/include/asm/mmu_context.h
··· 16 16 #include <linux/smp.h> 17 17 #include <linux/slab.h> 18 18 #include <asm/cacheflush.h> 19 + #include <asm/dsemul.h> 19 20 #include <asm/hazards.h> 20 21 #include <asm/tlbflush.h> 21 22 #include <asm-generic/mm_hooks.h> ··· 129 128 130 129 atomic_set(&mm->context.fp_mode_switching, 0); 131 130 131 + mm->context.bd_emupage_allocmap = NULL; 132 + spin_lock_init(&mm->context.bd_emupage_lock); 133 + init_waitqueue_head(&mm->context.bd_emupage_queue); 134 + 132 135 return 0; 133 136 } 134 137 ··· 167 162 */ 168 163 static inline void destroy_context(struct mm_struct *mm) 169 164 { 165 + dsemul_mm_cleanup(mm); 170 166 } 171 167 172 168 #define deactivate_mm(tsk, mm) do { } while (0)
+17 -1
arch/mips/include/asm/processor.h
··· 11 11 #ifndef _ASM_PROCESSOR_H 12 12 #define _ASM_PROCESSOR_H 13 13 14 + #include <linux/atomic.h> 14 15 #include <linux/cpumask.h> 15 16 #include <linux/threads.h> 16 17 17 18 #include <asm/cachectl.h> 18 19 #include <asm/cpu.h> 19 20 #include <asm/cpu-info.h> 21 + #include <asm/dsemul.h> 20 22 #include <asm/mipsregs.h> 21 23 #include <asm/prefetch.h> 22 24 ··· 80 78 81 79 #endif 82 80 83 - #define STACK_TOP (TASK_SIZE & PAGE_MASK) 81 + /* 82 + * One page above the stack is used for branch delay slot "emulation". 83 + * See dsemul.c for details. 84 + */ 85 + #define STACK_TOP ((TASK_SIZE & PAGE_MASK) - PAGE_SIZE) 84 86 85 87 /* 86 88 * This decides where the kernel will search for a free chunk of vm ··· 262 256 263 257 /* Saved fpu/fpu emulator stuff. */ 264 258 struct mips_fpu_struct fpu FPU_ALIGN; 259 + /* Assigned branch delay slot 'emulation' frame */ 260 + atomic_t bd_emu_frame; 261 + /* PC of the branch from a branch delay slot 'emulation' */ 262 + unsigned long bd_emu_branch_pc; 263 + /* PC to continue from following a branch delay slot 'emulation' */ 264 + unsigned long bd_emu_cont_pc; 265 265 #ifdef CONFIG_MIPS_MT_FPAFF 266 266 /* Emulated instruction count */ 267 267 unsigned long emulated_fp; ··· 335 323 * FPU affinity state (null if not FPAFF) \ 336 324 */ \ 337 325 FPAFF_INIT \ 326 + /* Delay slot emulation */ \ 327 + .bd_emu_frame = ATOMIC_INIT(BD_EMUFRAME_NONE), \ 328 + .bd_emu_branch_pc = 0, \ 329 + .bd_emu_cont_pc = 0, \ 338 330 /* \ 339 331 * Saved DSP stuff \ 340 332 */ \
+4 -4
arch/mips/kernel/mips-r2-to-r6-emul.c
··· 283 283 err = mipsr6_emul(regs, nir); 284 284 if (err > 0) { 285 285 regs->cp0_epc = nepc; 286 - err = mips_dsemul(regs, nir, cepc); 286 + err = mips_dsemul(regs, nir, epc, cepc); 287 287 if (err == SIGILL) 288 288 err = SIGEMT; 289 289 MIPS_R2_STATS(dsemul); ··· 1033 1033 if (nir) { 1034 1034 err = mipsr6_emul(regs, nir); 1035 1035 if (err > 0) { 1036 - err = mips_dsemul(regs, nir, cpc); 1036 + err = mips_dsemul(regs, nir, epc, cpc); 1037 1037 if (err == SIGILL) 1038 1038 err = SIGEMT; 1039 1039 MIPS_R2_STATS(dsemul); ··· 1082 1082 if (nir) { 1083 1083 err = mipsr6_emul(regs, nir); 1084 1084 if (err > 0) { 1085 - err = mips_dsemul(regs, nir, cpc); 1085 + err = mips_dsemul(regs, nir, epc, cpc); 1086 1086 if (err == SIGILL) 1087 1087 err = SIGEMT; 1088 1088 MIPS_R2_STATS(dsemul); ··· 1149 1149 if (nir) { 1150 1150 err = mipsr6_emul(regs, nir); 1151 1151 if (err > 0) { 1152 - err = mips_dsemul(regs, nir, cpc); 1152 + err = mips_dsemul(regs, nir, epc, cpc); 1153 1153 if (err == SIGILL) 1154 1154 err = SIGEMT; 1155 1155 MIPS_R2_STATS(dsemul);
+14
arch/mips/kernel/process.c
··· 30 30 #include <asm/asm.h> 31 31 #include <asm/bootinfo.h> 32 32 #include <asm/cpu.h> 33 + #include <asm/dsemul.h> 33 34 #include <asm/dsp.h> 34 35 #include <asm/fpu.h> 35 36 #include <asm/msa.h> ··· 69 68 lose_fpu(0); 70 69 clear_thread_flag(TIF_MSA_CTX_LIVE); 71 70 clear_used_math(); 71 + atomic_set(&current->thread.bd_emu_frame, BD_EMUFRAME_NONE); 72 72 init_dsp(); 73 73 regs->cp0_epc = pc; 74 74 regs->regs[29] = sp; 75 + } 76 + 77 + void exit_thread(struct task_struct *tsk) 78 + { 79 + /* 80 + * User threads may have allocated a delay slot emulation frame. 81 + * If so, clean up that allocation. 82 + */ 83 + if (!(current->flags & PF_KTHREAD)) 84 + dsemul_thread_cleanup(tsk); 75 85 } 76 86 77 87 int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src) ··· 170 158 #ifdef CONFIG_MIPS_MT_FPAFF 171 159 clear_tsk_thread_flag(p, TIF_FPUBOUND); 172 160 #endif /* CONFIG_MIPS_MT_FPAFF */ 161 + 162 + atomic_set(&p->thread.bd_emu_frame, BD_EMUFRAME_NONE); 173 163 174 164 if (clone_flags & CLONE_SETTLS) 175 165 ti->tp_value = regs->regs[7];
+8
arch/mips/kernel/signal.c
··· 772 772 struct mips_abi *abi = current->thread.abi; 773 773 void *vdso = current->mm->context.vdso; 774 774 775 + /* 776 + * If we were emulating a delay slot instruction, exit that frame such 777 + * that addresses in the sigframe are as expected for userland and we 778 + * don't have a problem if we reuse the thread's frame for an 779 + * instruction within the signal handler. 780 + */ 781 + dsemul_thread_rollback(regs); 782 + 775 783 if (regs->regs[0]) { 776 784 switch(regs->regs[2]) { 777 785 case ERESTART_RESTARTBLOCK:
+10
arch/mips/kernel/vdso.c
··· 107 107 if (down_write_killable(&mm->mmap_sem)) 108 108 return -EINTR; 109 109 110 + /* Map delay slot emulation page */ 111 + base = mmap_region(NULL, STACK_TOP, PAGE_SIZE, 112 + VM_READ|VM_WRITE|VM_EXEC| 113 + VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC, 114 + 0); 115 + if (IS_ERR_VALUE(base)) { 116 + ret = base; 117 + goto out; 118 + } 119 + 110 120 /* 111 121 * Determine total area size. This includes the VDSO data itself, the 112 122 * data page, and the GIC user page if present. Always create a mapping
+4 -4
arch/mips/math-emu/cp1emu.c
··· 434 434 * a single subroutine should be used across both 435 435 * modules. 436 436 */ 437 - static int isBranchInstr(struct pt_regs *regs, struct mm_decoded_insn dec_insn, 438 - unsigned long *contpc) 437 + int isBranchInstr(struct pt_regs *regs, struct mm_decoded_insn dec_insn, 438 + unsigned long *contpc) 439 439 { 440 440 union mips_instruction insn = (union mips_instruction)dec_insn.insn; 441 441 unsigned int fcr31; ··· 1268 1268 * instruction in the dslot. 1269 1269 */ 1270 1270 sig = mips_dsemul(xcp, ir, 1271 - contpc); 1271 + bcpc, contpc); 1272 1272 if (sig < 0) 1273 1273 break; 1274 1274 if (sig) ··· 1323 1323 * Single step the non-cp1 1324 1324 * instruction in the dslot 1325 1325 */ 1326 - sig = mips_dsemul(xcp, ir, contpc); 1326 + sig = mips_dsemul(xcp, ir, bcpc, contpc); 1327 1327 if (sig < 0) 1328 1328 break; 1329 1329 if (sig)
+223 -110
arch/mips/math-emu/dsemul.c
··· 1 + #include <linux/err.h> 2 + #include <linux/slab.h> 3 + 1 4 #include <asm/branch.h> 2 5 #include <asm/cacheflush.h> 3 6 #include <asm/fpu_emulator.h> ··· 8 5 #include <asm/mipsregs.h> 9 6 #include <asm/uaccess.h> 10 7 11 - #include "ieee754.h" 12 - 13 - /* 14 - * Emulate the arbitrary instruction ir at xcp->cp0_epc. Required when 15 - * we have to emulate the instruction in a COP1 branch delay slot. Do 16 - * not change cp0_epc due to the instruction 8 + /** 9 + * struct emuframe - The 'emulation' frame structure 10 + * @emul: The instruction to 'emulate'. 11 + * @badinst: A break instruction to cause a return to the kernel. 17 12 * 18 - * According to the spec: 19 - * 1) it shouldn't be a branch :-) 20 - * 2) it can be a COP instruction :-( 21 - * 3) if we are tring to run a protected memory space we must take 22 - * special care on memory access instructions :-( 13 + * This structure defines the frames placed within the delay slot emulation 14 + * page in response to a call to mips_dsemul(). Each thread may be allocated 15 + * only one frame at any given time. The kernel stores within it the 16 + * instruction to be 'emulated' followed by a break instruction, then 17 + * executes the frame in user mode. The break causes a trap to the kernel 18 + * which leads to do_dsemulret() being called unless the instruction in 19 + * @emul causes a trap itself, is a branch, or a signal is delivered to 20 + * the thread. In these cases the allocated frame will either be reused by 21 + * a subsequent delay slot 'emulation', or be freed during signal delivery or 22 + * upon thread exit. 23 + * 24 + * This approach is used because: 25 + * 26 + * - Actually emulating all instructions isn't feasible. We would need to 27 + * be able to handle instructions from all revisions of the MIPS ISA, 28 + * all ASEs & all vendor instruction set extensions. This would be a 29 + * whole lot of work & continual maintenance burden as new instructions 30 + * are introduced, and in the case of some vendor extensions may not 31 + * even be possible. Thus we need to take the approach of actually 32 + * executing the instruction. 33 + * 34 + * - We must execute the instruction within user context. If we were to 35 + * execute the instruction in kernel mode then it would have access to 36 + * kernel resources without very careful checks, leaving us with a 37 + * high potential for security or stability issues to arise. 38 + * 39 + * - We used to place the frame on the users stack, but this requires 40 + * that the stack be executable. This is bad for security so the 41 + * per-process page is now used instead. 42 + * 43 + * - The instruction in @emul may be something entirely invalid for a 44 + * delay slot. The user may (intentionally or otherwise) place a branch 45 + * in a delay slot, or a kernel mode instruction, or something else 46 + * which generates an exception. Thus we can't rely upon the break in 47 + * @badinst always being hit. For this reason we track the index of the 48 + * frame allocated to each thread, allowing us to clean it up at later 49 + * points such as signal delivery or thread exit. 50 + * 51 + * - The user may generate a fake struct emuframe if they wish, invoking 52 + * the BRK_MEMU break instruction themselves. We must therefore not 53 + * trust that BRK_MEMU means there's actually a valid frame allocated 54 + * to the thread, and must not allow the user to do anything they 55 + * couldn't already. 23 56 */ 24 - 25 - /* 26 - * "Trampoline" return routine to catch exception following 27 - * execution of delay-slot instruction execution. 28 - */ 29 - 30 57 struct emuframe { 31 58 mips_instruction emul; 32 59 mips_instruction badinst; 33 - mips_instruction cookie; 34 - unsigned long epc; 35 60 }; 36 61 37 - /* 38 - * Set up an emulation frame for instruction IR, from a delay slot of 39 - * a branch jumping to CPC. Return 0 if successful, -1 if no emulation 40 - * required, otherwise a signal number causing a frame setup failure. 41 - */ 42 - int mips_dsemul(struct pt_regs *regs, mips_instruction ir, unsigned long cpc) 62 + static const int emupage_frame_count = PAGE_SIZE / sizeof(struct emuframe); 63 + 64 + static inline __user struct emuframe *dsemul_page(void) 65 + { 66 + return (__user struct emuframe *)STACK_TOP; 67 + } 68 + 69 + static int alloc_emuframe(void) 70 + { 71 + mm_context_t *mm_ctx = &current->mm->context; 72 + int idx; 73 + 74 + retry: 75 + spin_lock(&mm_ctx->bd_emupage_lock); 76 + 77 + /* Ensure we have an allocation bitmap */ 78 + if (!mm_ctx->bd_emupage_allocmap) { 79 + mm_ctx->bd_emupage_allocmap = 80 + kcalloc(BITS_TO_LONGS(emupage_frame_count), 81 + sizeof(unsigned long), 82 + GFP_ATOMIC); 83 + 84 + if (!mm_ctx->bd_emupage_allocmap) { 85 + idx = BD_EMUFRAME_NONE; 86 + goto out_unlock; 87 + } 88 + } 89 + 90 + /* Attempt to allocate a single bit/frame */ 91 + idx = bitmap_find_free_region(mm_ctx->bd_emupage_allocmap, 92 + emupage_frame_count, 0); 93 + if (idx < 0) { 94 + /* 95 + * Failed to allocate a frame. We'll wait until one becomes 96 + * available. We unlock the page so that other threads actually 97 + * get the opportunity to free their frames, which means 98 + * technically the result of bitmap_full may be incorrect. 99 + * However the worst case is that we repeat all this and end up 100 + * back here again. 101 + */ 102 + spin_unlock(&mm_ctx->bd_emupage_lock); 103 + if (!wait_event_killable(mm_ctx->bd_emupage_queue, 104 + !bitmap_full(mm_ctx->bd_emupage_allocmap, 105 + emupage_frame_count))) 106 + goto retry; 107 + 108 + /* Received a fatal signal - just give in */ 109 + return BD_EMUFRAME_NONE; 110 + } 111 + 112 + /* Success! */ 113 + pr_debug("allocate emuframe %d to %d\n", idx, current->pid); 114 + out_unlock: 115 + spin_unlock(&mm_ctx->bd_emupage_lock); 116 + return idx; 117 + } 118 + 119 + static void free_emuframe(int idx, struct mm_struct *mm) 120 + { 121 + mm_context_t *mm_ctx = &mm->context; 122 + 123 + spin_lock(&mm_ctx->bd_emupage_lock); 124 + 125 + pr_debug("free emuframe %d from %d\n", idx, current->pid); 126 + bitmap_clear(mm_ctx->bd_emupage_allocmap, idx, 1); 127 + 128 + /* If some thread is waiting for a frame, now's its chance */ 129 + wake_up(&mm_ctx->bd_emupage_queue); 130 + 131 + spin_unlock(&mm_ctx->bd_emupage_lock); 132 + } 133 + 134 + static bool within_emuframe(struct pt_regs *regs) 135 + { 136 + unsigned long base = (unsigned long)dsemul_page(); 137 + 138 + if (regs->cp0_epc < base) 139 + return false; 140 + if (regs->cp0_epc >= (base + PAGE_SIZE)) 141 + return false; 142 + 143 + return true; 144 + } 145 + 146 + bool dsemul_thread_cleanup(struct task_struct *tsk) 147 + { 148 + int fr_idx; 149 + 150 + /* Clear any allocated frame, retrieving its index */ 151 + fr_idx = atomic_xchg(&tsk->thread.bd_emu_frame, BD_EMUFRAME_NONE); 152 + 153 + /* If no frame was allocated, we're done */ 154 + if (fr_idx == BD_EMUFRAME_NONE) 155 + return false; 156 + 157 + task_lock(tsk); 158 + 159 + /* Free the frame that this thread had allocated */ 160 + if (tsk->mm) 161 + free_emuframe(fr_idx, tsk->mm); 162 + 163 + task_unlock(tsk); 164 + return true; 165 + } 166 + 167 + bool dsemul_thread_rollback(struct pt_regs *regs) 168 + { 169 + struct emuframe __user *fr; 170 + int fr_idx; 171 + 172 + /* Do nothing if we're not executing from a frame */ 173 + if (!within_emuframe(regs)) 174 + return false; 175 + 176 + /* Find the frame being executed */ 177 + fr_idx = atomic_read(&current->thread.bd_emu_frame); 178 + if (fr_idx == BD_EMUFRAME_NONE) 179 + return false; 180 + fr = &dsemul_page()[fr_idx]; 181 + 182 + /* 183 + * If the PC is at the emul instruction, roll back to the branch. If 184 + * PC is at the badinst (break) instruction, we've already emulated the 185 + * instruction so progress to the continue PC. If it's anything else 186 + * then something is amiss & the user has branched into some other area 187 + * of the emupage - we'll free the allocated frame anyway. 188 + */ 189 + if (msk_isa16_mode(regs->cp0_epc) == (unsigned long)&fr->emul) 190 + regs->cp0_epc = current->thread.bd_emu_branch_pc; 191 + else if (msk_isa16_mode(regs->cp0_epc) == (unsigned long)&fr->badinst) 192 + regs->cp0_epc = current->thread.bd_emu_cont_pc; 193 + 194 + atomic_set(&current->thread.bd_emu_frame, BD_EMUFRAME_NONE); 195 + free_emuframe(fr_idx, current->mm); 196 + return true; 197 + } 198 + 199 + void dsemul_mm_cleanup(struct mm_struct *mm) 200 + { 201 + mm_context_t *mm_ctx = &mm->context; 202 + 203 + kfree(mm_ctx->bd_emupage_allocmap); 204 + } 205 + 206 + int mips_dsemul(struct pt_regs *regs, mips_instruction ir, 207 + unsigned long branch_pc, unsigned long cont_pc) 43 208 { 44 209 int isa16 = get_isa16_mode(regs->cp0_epc); 45 210 mips_instruction break_math; 46 211 struct emuframe __user *fr; 47 - int err; 212 + int err, fr_idx; 48 213 49 214 /* NOP is easy */ 50 215 if (ir == 0) ··· 239 68 } 240 69 } 241 70 242 - pr_debug("dsemul %lx %lx\n", regs->cp0_epc, cpc); 71 + pr_debug("dsemul 0x%08lx cont at 0x%08lx\n", regs->cp0_epc, cont_pc); 243 72 244 - /* 245 - * The strategy is to push the instruction onto the user stack 246 - * and put a trap after it which we can catch and jump to 247 - * the required address any alternative apart from full 248 - * instruction emulation!!. 249 - * 250 - * Algorithmics used a system call instruction, and 251 - * borrowed that vector. MIPS/Linux version is a bit 252 - * more heavyweight in the interests of portability and 253 - * multiprocessor support. For Linux we use a BREAK 514 254 - * instruction causing a breakpoint exception. 255 - */ 73 + /* Allocate a frame if we don't already have one */ 74 + fr_idx = atomic_read(&current->thread.bd_emu_frame); 75 + if (fr_idx == BD_EMUFRAME_NONE) 76 + fr_idx = alloc_emuframe(); 77 + if (fr_idx == BD_EMUFRAME_NONE) 78 + return SIGBUS; 79 + fr = &dsemul_page()[fr_idx]; 80 + 81 + /* Retrieve the appropriately encoded break instruction */ 256 82 break_math = BREAK_MATH(isa16); 257 83 258 - /* Ensure that the two instructions are in the same cache line */ 259 - fr = (struct emuframe __user *) 260 - ((regs->regs[29] - sizeof(struct emuframe)) & ~0x7); 261 - 262 - /* Verify that the stack pointer is not completely insane */ 263 - if (unlikely(!access_ok(VERIFY_WRITE, fr, sizeof(struct emuframe)))) 264 - return SIGBUS; 265 - 84 + /* Write the instructions to the frame */ 266 85 if (isa16) { 267 86 err = __put_user(ir >> 16, 268 87 (u16 __user *)(&fr->emul)); ··· 267 106 err |= __put_user(break_math, &fr->badinst); 268 107 } 269 108 270 - err |= __put_user((mips_instruction)BD_COOKIE, &fr->cookie); 271 - err |= __put_user(cpc, &fr->epc); 272 - 273 109 if (unlikely(err)) { 274 110 MIPS_FPU_EMU_INC_STATS(errors); 111 + free_emuframe(fr_idx, current->mm); 275 112 return SIGBUS; 276 113 } 277 114 115 + /* Record the PC of the branch, PC to continue from & frame index */ 116 + current->thread.bd_emu_branch_pc = branch_pc; 117 + current->thread.bd_emu_cont_pc = cont_pc; 118 + atomic_set(&current->thread.bd_emu_frame, fr_idx); 119 + 120 + /* Change user register context to execute the frame */ 278 121 regs->cp0_epc = (unsigned long)&fr->emul | isa16; 279 122 123 + /* Ensure the icache observes our newly written frame */ 280 124 flush_cache_sigtramp((unsigned long)&fr->emul); 281 125 282 126 return 0; 283 127 } 284 128 285 - int do_dsemulret(struct pt_regs *xcp) 129 + bool do_dsemulret(struct pt_regs *xcp) 286 130 { 287 - int isa16 = get_isa16_mode(xcp->cp0_epc); 288 - struct emuframe __user *fr; 289 - unsigned long epc; 290 - u32 insn, cookie; 291 - int err = 0; 292 - u16 instr[2]; 293 - 294 - fr = (struct emuframe __user *) 295 - (msk_isa16_mode(xcp->cp0_epc) - sizeof(mips_instruction)); 296 - 297 - /* 298 - * If we can't even access the area, something is very wrong, but we'll 299 - * leave that to the default handling 300 - */ 301 - if (!access_ok(VERIFY_READ, fr, sizeof(struct emuframe))) 302 - return 0; 303 - 304 - /* 305 - * Do some sanity checking on the stackframe: 306 - * 307 - * - Is the instruction pointed to by the EPC an BREAK_MATH? 308 - * - Is the following memory word the BD_COOKIE? 309 - */ 310 - if (isa16) { 311 - err = __get_user(instr[0], 312 - (u16 __user *)(&fr->badinst)); 313 - err |= __get_user(instr[1], 314 - (u16 __user *)((long)(&fr->badinst) + 2)); 315 - insn = (instr[0] << 16) | instr[1]; 316 - } else { 317 - err = __get_user(insn, &fr->badinst); 318 - } 319 - err |= __get_user(cookie, &fr->cookie); 320 - 321 - if (unlikely(err || 322 - insn != BREAK_MATH(isa16) || cookie != BD_COOKIE)) { 131 + /* Cleanup the allocated frame, returning if there wasn't one */ 132 + if (!dsemul_thread_cleanup(current)) { 323 133 MIPS_FPU_EMU_INC_STATS(errors); 324 - return 0; 325 - } 326 - 327 - /* 328 - * At this point, we are satisfied that it's a BD emulation trap. Yes, 329 - * a user might have deliberately put two malformed and useless 330 - * instructions in a row in his program, in which case he's in for a 331 - * nasty surprise - the next instruction will be treated as a 332 - * continuation address! Alas, this seems to be the only way that we 333 - * can handle signals, recursion, and longjmps() in the context of 334 - * emulating the branch delay instruction. 335 - */ 336 - 337 - pr_debug("dsemulret\n"); 338 - 339 - if (__get_user(epc, &fr->epc)) { /* Saved EPC */ 340 - /* This is not a good situation to be in */ 341 - force_sig(SIGBUS, current); 342 - 343 - return 0; 134 + return false; 344 135 } 345 136 346 137 /* Set EPC to return to post-branch instruction */ 347 - xcp->cp0_epc = epc; 348 - MIPS_FPU_EMU_INC_STATS(ds_emul); 349 - return 1; 138 + xcp->cp0_epc = current->thread.bd_emu_cont_pc; 139 + pr_debug("dsemulret to 0x%08lx\n", xcp->cp0_epc); 140 + return true; 350 141 }