dsemul.c (498495dba268b20e8eadd7fe93c140c68b6cc9d2) dsemul.c (adcc81f148d733b7e8e641300c5590a2cdc13bf3)
1// SPDX-License-Identifier: GPL-2.0
2#include <linux/err.h>
3#include <linux/slab.h>
4#include <linux/mm_types.h>
5#include <linux/sched/task.h>
6
7#include <asm/branch.h>
8#include <asm/cacheflush.h>

--- 200 unchanged lines hidden (view full) ---

209 kfree(mm_ctx->bd_emupage_allocmap);
210}
211
212int mips_dsemul(struct pt_regs *regs, mips_instruction ir,
213 unsigned long branch_pc, unsigned long cont_pc)
214{
215 int isa16 = get_isa16_mode(regs->cp0_epc);
216 mips_instruction break_math;
1// SPDX-License-Identifier: GPL-2.0
2#include <linux/err.h>
3#include <linux/slab.h>
4#include <linux/mm_types.h>
5#include <linux/sched/task.h>
6
7#include <asm/branch.h>
8#include <asm/cacheflush.h>

--- 200 unchanged lines hidden (view full) ---

209 kfree(mm_ctx->bd_emupage_allocmap);
210}
211
212int mips_dsemul(struct pt_regs *regs, mips_instruction ir,
213 unsigned long branch_pc, unsigned long cont_pc)
214{
215 int isa16 = get_isa16_mode(regs->cp0_epc);
216 mips_instruction break_math;
217 struct emuframe __user *fr;
218 int err, fr_idx;
217 unsigned long fr_uaddr;
218 struct emuframe fr;
219 int fr_idx, ret;
219
220 /* NOP is easy */
221 if (ir == 0)
222 return -1;
223
224 /* microMIPS instructions */
225 if (isa16) {
226 union mips_instruction insn = { .word = ir };

--- 18 unchanged lines hidden (view full) ---

245 pr_debug("dsemul 0x%08lx cont at 0x%08lx\n", regs->cp0_epc, cont_pc);
246
247 /* Allocate a frame if we don't already have one */
248 fr_idx = atomic_read(&current->thread.bd_emu_frame);
249 if (fr_idx == BD_EMUFRAME_NONE)
250 fr_idx = alloc_emuframe();
251 if (fr_idx == BD_EMUFRAME_NONE)
252 return SIGBUS;
220
221 /* NOP is easy */
222 if (ir == 0)
223 return -1;
224
225 /* microMIPS instructions */
226 if (isa16) {
227 union mips_instruction insn = { .word = ir };

--- 18 unchanged lines hidden (view full) ---

246 pr_debug("dsemul 0x%08lx cont at 0x%08lx\n", regs->cp0_epc, cont_pc);
247
248 /* Allocate a frame if we don't already have one */
249 fr_idx = atomic_read(&current->thread.bd_emu_frame);
250 if (fr_idx == BD_EMUFRAME_NONE)
251 fr_idx = alloc_emuframe();
252 if (fr_idx == BD_EMUFRAME_NONE)
253 return SIGBUS;
253 fr = &dsemul_page()[fr_idx];
254
255 /* Retrieve the appropriately encoded break instruction */
256 break_math = BREAK_MATH(isa16);
257
258 /* Write the instructions to the frame */
259 if (isa16) {
254
255 /* Retrieve the appropriately encoded break instruction */
256 break_math = BREAK_MATH(isa16);
257
258 /* Write the instructions to the frame */
259 if (isa16) {
260 err = __put_user(ir >> 16,
261 (u16 __user *)(&fr->emul));
262 err |= __put_user(ir & 0xffff,
263 (u16 __user *)((long)(&fr->emul) + 2));
264 err |= __put_user(break_math >> 16,
265 (u16 __user *)(&fr->badinst));
266 err |= __put_user(break_math & 0xffff,
267 (u16 __user *)((long)(&fr->badinst) + 2));
260 union mips_instruction _emul = {
261 .halfword = { ir >> 16, ir }
262 };
263 union mips_instruction _badinst = {
264 .halfword = { break_math >> 16, break_math }
265 };
266
267 fr.emul = _emul.word;
268 fr.badinst = _badinst.word;
268 } else {
269 } else {
269 err = __put_user(ir, &fr->emul);
270 err |= __put_user(break_math, &fr->badinst);
270 fr.emul = ir;
271 fr.badinst = break_math;
271 }
272
272 }
273
273 if (unlikely(err)) {
274 /* Write the frame to user memory */
275 fr_uaddr = (unsigned long)&dsemul_page()[fr_idx];
276 ret = access_process_vm(current, fr_uaddr, &fr, sizeof(fr),
277 FOLL_FORCE | FOLL_WRITE);
278 if (unlikely(ret != sizeof(fr))) {
274 MIPS_FPU_EMU_INC_STATS(errors);
275 free_emuframe(fr_idx, current->mm);
276 return SIGBUS;
277 }
278
279 /* Record the PC of the branch, PC to continue from & frame index */
280 current->thread.bd_emu_branch_pc = branch_pc;
281 current->thread.bd_emu_cont_pc = cont_pc;
282 atomic_set(&current->thread.bd_emu_frame, fr_idx);
283
284 /* Change user register context to execute the frame */
279 MIPS_FPU_EMU_INC_STATS(errors);
280 free_emuframe(fr_idx, current->mm);
281 return SIGBUS;
282 }
283
284 /* Record the PC of the branch, PC to continue from & frame index */
285 current->thread.bd_emu_branch_pc = branch_pc;
286 current->thread.bd_emu_cont_pc = cont_pc;
287 atomic_set(&current->thread.bd_emu_frame, fr_idx);
288
289 /* Change user register context to execute the frame */
285 regs->cp0_epc = (unsigned long)&fr->emul | isa16;
290 regs->cp0_epc = fr_uaddr | isa16;
286
291
287 /* Ensure the icache observes our newly written frame */
288 flush_cache_sigtramp((unsigned long)&fr->emul);
289
290 return 0;
291}
292
293bool do_dsemulret(struct pt_regs *xcp)
294{
295 /* Cleanup the allocated frame, returning if there wasn't one */
296 if (!dsemul_thread_cleanup(current)) {
297 MIPS_FPU_EMU_INC_STATS(errors);
298 return false;
299 }
300
301 /* Set EPC to return to post-branch instruction */
302 xcp->cp0_epc = current->thread.bd_emu_cont_pc;
303 pr_debug("dsemulret to 0x%08lx\n", xcp->cp0_epc);
304 MIPS_FPU_EMU_INC_STATS(ds_emul);
305 return true;
306}
292 return 0;
293}
294
295bool do_dsemulret(struct pt_regs *xcp)
296{
297 /* Cleanup the allocated frame, returning if there wasn't one */
298 if (!dsemul_thread_cleanup(current)) {
299 MIPS_FPU_EMU_INC_STATS(errors);
300 return false;
301 }
302
303 /* Set EPC to return to post-branch instruction */
304 xcp->cp0_epc = current->thread.bd_emu_cont_pc;
305 pr_debug("dsemulret to 0x%08lx\n", xcp->cp0_epc);
306 MIPS_FPU_EMU_INC_STATS(ds_emul);
307 return true;
308}