| /* SPDX-License-Identifier: GPL-2.0 */ |
| /* |
| * Non-emulated single-stepping support (currently limited to basic integer |
| * computations) used to validate the instruction emulation infrastructure. |
| * |
| * Copyright (C) 2019 IBM Corporation |
| */ |
| |
| #include <asm/asm-offsets.h> |
| #include <asm/ppc_asm.h> |
| #include <asm/code-patching-asm.h> |
| #include <linux/errno.h> |
| |
| /* int exec_instr(struct pt_regs *regs) */ |
| _GLOBAL(exec_instr) |
| |
| /* |
| * Stack frame layout (INT_FRAME_SIZE bytes) |
| * In-memory pt_regs (SP + STACK_INT_FRAME_REGS) |
| * Scratch space (SP + 8) |
| * Back chain (SP + 0) |
| */ |
| |
| /* |
| * Allocate a new stack frame with enough space to hold the register |
| * states in an in-memory pt_regs and also create the back chain to |
| * the caller's stack frame. |
| */ |
| stdu r1, -INT_FRAME_SIZE(r1) |
| |
| /* |
| * Save non-volatile GPRs on stack. This includes TOC pointer (GPR2) |
| * and local variables (GPR14 to GPR31). The register for the pt_regs |
| * parameter (GPR3) is saved additionally to ensure that the resulting |
| * register state can still be saved even if GPR3 gets overwritten |
| * when loading the initial register state for the test instruction. |
| * The stack pointer (GPR1) and the thread pointer (GPR13) are not |
| * saved as these should not be modified anyway. |
| */ |
| SAVE_GPRS(2, 3, r1) |
| SAVE_NVGPRS(r1) |
| |
| /* |
| * Save LR on stack to ensure that the return address is available |
| * even if it gets overwritten by the test instruction. |
| */ |
| mflr r0 |
| std r0, _LINK(r1) |
| |
| /* |
| * Save CR on stack. For simplicity, the entire register is saved |
| * even though only fields 2 to 4 are non-volatile. |
| */ |
| mfcr r0 |
| std r0, _CCR(r1) |
| |
| /* |
| * Load register state for the test instruction without touching the |
| * critical non-volatile registers. The register state is passed as a |
| * pointer to a pt_regs instance. |
| */ |
| subi r31, r3, GPR0 |
| |
| /* Load LR from pt_regs */ |
| ld r0, _LINK(r31) |
| mtlr r0 |
| |
| /* Load CR from pt_regs */ |
| ld r0, _CCR(r31) |
| mtcr r0 |
| |
| /* Load XER from pt_regs */ |
| ld r0, _XER(r31) |
| mtxer r0 |
| |
| /* Load GPRs from pt_regs */ |
| REST_GPR(0, r31) |
| REST_GPRS(2, 12, r31) |
| REST_NVGPRS(r31) |
| |
| /* Placeholder for the test instruction */ |
| .balign 64 |
| 1: nop |
| nop |
| patch_site 1b patch__exec_instr |
| |
| /* |
| * Since GPR3 is overwritten, temporarily restore it back to its |
| * original state, i.e. the pointer to pt_regs, to ensure that the |
| * resulting register state can be saved. Before doing this, a copy |
| * of it is created in the scratch space which is used later on to |
| * save it to pt_regs. |
| */ |
| std r3, 8(r1) |
| REST_GPR(3, r1) |
| |
| /* Save resulting GPR state to pt_regs */ |
| subi r3, r3, GPR0 |
| SAVE_GPR(0, r3) |
| SAVE_GPR(2, r3) |
| SAVE_GPRS(4, 12, r3) |
| SAVE_NVGPRS(r3) |
| |
| /* Save resulting LR to pt_regs */ |
| mflr r0 |
| std r0, _LINK(r3) |
| |
| /* Save resulting CR to pt_regs */ |
| mfcr r0 |
| std r0, _CCR(r3) |
| |
| /* Save resulting XER to pt_regs */ |
| mfxer r0 |
| std r0, _XER(r3) |
| |
| /* Restore resulting GPR3 from scratch space and save it to pt_regs */ |
| ld r0, 8(r1) |
| std r0, GPR3(r3) |
| |
| /* Set return value to denote execution success */ |
| li r3, 0 |
| |
| /* Continue */ |
| b 3f |
| |
| /* Set return value to denote execution failure */ |
| 2: li r3, -EFAULT |
| |
| /* Restore the non-volatile GPRs from stack */ |
| 3: REST_GPR(2, r1) |
| REST_NVGPRS(r1) |
| |
| /* Restore LR from stack to be able to return */ |
| ld r0, _LINK(r1) |
| mtlr r0 |
| |
| /* Restore CR from stack */ |
| ld r0, _CCR(r1) |
| mtcr r0 |
| |
| /* Tear down stack frame */ |
| addi r1, r1, INT_FRAME_SIZE |
| |
| /* Return */ |
| blr |
| |
| /* Setup exception table */ |
| EX_TABLE(1b, 2b) |
| |
| _ASM_NOKPROBE_SYMBOL(exec_instr) |