// SPDX-License-Identifier: GPL-2.01#include <linux/err.h>2#include <linux/slab.h>3#include <linux/mm_types.h>4#include <linux/sched/task.h>56#include <asm/branch.h>7#include <asm/cacheflush.h>8#include <asm/fpu_emulator.h>9#include <asm/inst.h>10#include <asm/mipsregs.h>11#include <linux/uaccess.h>1213/**14* struct emuframe - The 'emulation' frame structure15* @emul: The instruction to 'emulate'.16* @badinst: A break instruction to cause a return to the kernel.17*18* This structure defines the frames placed within the delay slot emulation19* page in response to a call to mips_dsemul(). Each thread may be allocated20* only one frame at any given time. The kernel stores within it the21* instruction to be 'emulated' followed by a break instruction, then22* executes the frame in user mode. The break causes a trap to the kernel23* which leads to do_dsemulret() being called unless the instruction in24* @emul causes a trap itself, is a branch, or a signal is delivered to25* the thread. In these cases the allocated frame will either be reused by26* a subsequent delay slot 'emulation', or be freed during signal delivery or27* upon thread exit.28*29* This approach is used because:30*31* - Actually emulating all instructions isn't feasible. We would need to32* be able to handle instructions from all revisions of the MIPS ISA,33* all ASEs & all vendor instruction set extensions. This would be a34* whole lot of work & continual maintenance burden as new instructions35* are introduced, and in the case of some vendor extensions may not36* even be possible. Thus we need to take the approach of actually37* executing the instruction.38*39* - We must execute the instruction within user context. If we were to40* execute the instruction in kernel mode then it would have access to41* kernel resources without very careful checks, leaving us with a42* high potential for security or stability issues to arise.43*44* - We used to place the frame on the users stack, but this requires45* that the stack be executable. This is bad for security so the46* per-process page is now used instead.47*48* - The instruction in @emul may be something entirely invalid for a49* delay slot. The user may (intentionally or otherwise) place a branch50* in a delay slot, or a kernel mode instruction, or something else51* which generates an exception. Thus we can't rely upon the break in52* @badinst always being hit. For this reason we track the index of the53* frame allocated to each thread, allowing us to clean it up at later54* points such as signal delivery or thread exit.55*56* - The user may generate a fake struct emuframe if they wish, invoking57* the BRK_MEMU break instruction themselves. We must therefore not58* trust that BRK_MEMU means there's actually a valid frame allocated59* to the thread, and must not allow the user to do anything they60* couldn't already.61*/62struct emuframe {63mips_instruction emul;64mips_instruction badinst;65};6667static const int emupage_frame_count = PAGE_SIZE / sizeof(struct emuframe);6869static inline __user struct emuframe *dsemul_page(void)70{71return (__user struct emuframe *)STACK_TOP;72}7374static int alloc_emuframe(void)75{76mm_context_t *mm_ctx = ¤t->mm->context;77int idx;7879retry:80spin_lock(&mm_ctx->bd_emupage_lock);8182/* Ensure we have an allocation bitmap */83if (!mm_ctx->bd_emupage_allocmap) {84mm_ctx->bd_emupage_allocmap = bitmap_zalloc(emupage_frame_count,85GFP_ATOMIC);86if (!mm_ctx->bd_emupage_allocmap) {87idx = BD_EMUFRAME_NONE;88goto out_unlock;89}90}9192/* Attempt to allocate a single bit/frame */93idx = bitmap_find_free_region(mm_ctx->bd_emupage_allocmap,94emupage_frame_count, 0);95if (idx < 0) {96/*97* Failed to allocate a frame. We'll wait until one becomes98* available. We unlock the page so that other threads actually99* get the opportunity to free their frames, which means100* technically the result of bitmap_full may be incorrect.101* However the worst case is that we repeat all this and end up102* back here again.103*/104spin_unlock(&mm_ctx->bd_emupage_lock);105if (!wait_event_killable(mm_ctx->bd_emupage_queue,106!bitmap_full(mm_ctx->bd_emupage_allocmap,107emupage_frame_count)))108goto retry;109110/* Received a fatal signal - just give in */111return BD_EMUFRAME_NONE;112}113114/* Success! */115pr_debug("allocate emuframe %d to %d\n", idx, current->pid);116out_unlock:117spin_unlock(&mm_ctx->bd_emupage_lock);118return idx;119}120121static void free_emuframe(int idx, struct mm_struct *mm)122{123mm_context_t *mm_ctx = &mm->context;124125spin_lock(&mm_ctx->bd_emupage_lock);126127pr_debug("free emuframe %d from %d\n", idx, current->pid);128bitmap_clear(mm_ctx->bd_emupage_allocmap, idx, 1);129130/* If some thread is waiting for a frame, now's its chance */131wake_up(&mm_ctx->bd_emupage_queue);132133spin_unlock(&mm_ctx->bd_emupage_lock);134}135136static bool within_emuframe(struct pt_regs *regs)137{138unsigned long base = (unsigned long)dsemul_page();139140if (regs->cp0_epc < base)141return false;142if (regs->cp0_epc >= (base + PAGE_SIZE))143return false;144145return true;146}147148bool dsemul_thread_cleanup(struct task_struct *tsk)149{150int fr_idx;151152/* Clear any allocated frame, retrieving its index */153fr_idx = atomic_xchg(&tsk->thread.bd_emu_frame, BD_EMUFRAME_NONE);154155/* If no frame was allocated, we're done */156if (fr_idx == BD_EMUFRAME_NONE)157return false;158159task_lock(tsk);160161/* Free the frame that this thread had allocated */162if (tsk->mm)163free_emuframe(fr_idx, tsk->mm);164165task_unlock(tsk);166return true;167}168169bool dsemul_thread_rollback(struct pt_regs *regs)170{171struct emuframe __user *fr;172int fr_idx;173174/* Do nothing if we're not executing from a frame */175if (!within_emuframe(regs))176return false;177178/* Find the frame being executed */179fr_idx = atomic_read(¤t->thread.bd_emu_frame);180if (fr_idx == BD_EMUFRAME_NONE)181return false;182fr = &dsemul_page()[fr_idx];183184/*185* If the PC is at the emul instruction, roll back to the branch. If186* PC is at the badinst (break) instruction, we've already emulated the187* instruction so progress to the continue PC. If it's anything else188* then something is amiss & the user has branched into some other area189* of the emupage - we'll free the allocated frame anyway.190*/191if (msk_isa16_mode(regs->cp0_epc) == (unsigned long)&fr->emul)192regs->cp0_epc = current->thread.bd_emu_branch_pc;193else if (msk_isa16_mode(regs->cp0_epc) == (unsigned long)&fr->badinst)194regs->cp0_epc = current->thread.bd_emu_cont_pc;195196atomic_set(¤t->thread.bd_emu_frame, BD_EMUFRAME_NONE);197free_emuframe(fr_idx, current->mm);198return true;199}200201void dsemul_mm_cleanup(struct mm_struct *mm)202{203mm_context_t *mm_ctx = &mm->context;204205bitmap_free(mm_ctx->bd_emupage_allocmap);206}207208int mips_dsemul(struct pt_regs *regs, mips_instruction ir,209unsigned long branch_pc, unsigned long cont_pc)210{211int isa16 = get_isa16_mode(regs->cp0_epc);212mips_instruction break_math;213unsigned long fr_uaddr;214struct emuframe fr;215int fr_idx, ret;216217/* NOP is easy */218if (ir == 0)219return -1;220221/* microMIPS instructions */222if (isa16) {223union mips_instruction insn = { .word = ir };224225/* NOP16 aka MOVE16 $0, $0 */226if ((ir >> 16) == MM_NOP16)227return -1;228229/* ADDIUPC */230if (insn.mm_a_format.opcode == mm_addiupc_op) {231unsigned int rs;232s32 v;233234rs = (((insn.mm_a_format.rs + 0xe) & 0xf) + 2);235v = regs->cp0_epc & ~3;236v += insn.mm_a_format.simmediate << 2;237regs->regs[rs] = (long)v;238return -1;239}240}241242pr_debug("dsemul 0x%08lx cont at 0x%08lx\n", regs->cp0_epc, cont_pc);243244/* Allocate a frame if we don't already have one */245fr_idx = atomic_read(¤t->thread.bd_emu_frame);246if (fr_idx == BD_EMUFRAME_NONE)247fr_idx = alloc_emuframe();248if (fr_idx == BD_EMUFRAME_NONE)249return SIGBUS;250251/* Retrieve the appropriately encoded break instruction */252break_math = BREAK_MATH(isa16);253254/* Write the instructions to the frame */255if (isa16) {256union mips_instruction _emul = {257.halfword = { ir >> 16, ir }258};259union mips_instruction _badinst = {260.halfword = { break_math >> 16, break_math }261};262263fr.emul = _emul.word;264fr.badinst = _badinst.word;265} else {266fr.emul = ir;267fr.badinst = break_math;268}269270/* Write the frame to user memory */271fr_uaddr = (unsigned long)&dsemul_page()[fr_idx];272ret = access_process_vm(current, fr_uaddr, &fr, sizeof(fr),273FOLL_FORCE | FOLL_WRITE);274if (unlikely(ret != sizeof(fr))) {275MIPS_FPU_EMU_INC_STATS(errors);276free_emuframe(fr_idx, current->mm);277return SIGBUS;278}279280/* Record the PC of the branch, PC to continue from & frame index */281current->thread.bd_emu_branch_pc = branch_pc;282current->thread.bd_emu_cont_pc = cont_pc;283atomic_set(¤t->thread.bd_emu_frame, fr_idx);284285/* Change user register context to execute the frame */286regs->cp0_epc = fr_uaddr | isa16;287288return 0;289}290291bool do_dsemulret(struct pt_regs *xcp)292{293/* Cleanup the allocated frame, returning if there wasn't one */294if (!dsemul_thread_cleanup(current)) {295MIPS_FPU_EMU_INC_STATS(errors);296return false;297}298299/* Set EPC to return to post-branch instruction */300xcp->cp0_epc = current->thread.bd_emu_cont_pc;301pr_debug("dsemulret to 0x%08lx\n", xcp->cp0_epc);302MIPS_FPU_EMU_INC_STATS(ds_emul);303return true;304}305306307