696 lines
18 KiB
C
696 lines
18 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/* ptrace.c */
|
|
/* By Ross Biro 1/23/92 */
|
|
/* edited by Linus Torvalds */
|
|
/* mangled further by Bob Manson (manson@santafe.edu) */
|
|
/* more mutilation by David Mosberger (davidm@azstarnet.com) */
|
|
|
|
#include <linux/tracehook.h>
|
|
#include <linux/audit.h>
|
|
|
|
#include <asm/reg.h>
|
|
|
|
#include "proto.h"
|
|
|
|
#define CREATE_TRACE_POINTS
|
|
#include <trace/events/syscalls.h>
|
|
|
|
#define BREAKINST 0x00000080 /* sys_call bpt */
|
|
|
|
/*
|
|
* does not yet catch signals sent when the child dies.
|
|
* in exit.c or in signal.c.
|
|
*/
|
|
|
|
/*
|
|
* Processes always block with the following stack-layout:
|
|
*
|
|
* +================================+ <---- task + 2*PAGE_SIZE
|
|
* | HMcode saved frame (ps, pc, | ^
|
|
* | gp, a0, a1, a2) | |
|
|
* +================================+ | struct pt_regs
|
|
* | | |
|
|
* | frame generated by SAVE_ALL | |
|
|
* | | v
|
|
* +================================+
|
|
* | | ^
|
|
* | frame saved by do_switch_stack | | struct switch_stack
|
|
* | | v
|
|
* +================================+
|
|
*/
|
|
|
|
/*
|
|
* The following table maps a register index into the stack offset at
|
|
* which the register is saved. Register indices are 0-31 for integer
|
|
* regs, 32-63 for fp regs, and 64 for the pc. Notice that sp and
|
|
* zero have no stack-slot and need to be treated specially (see
|
|
* get_reg/put_reg below).
|
|
*/
|
|
enum {
|
|
REG_R0 = 0,
|
|
REG_F0 = 32,
|
|
REG_FPCR = 63,
|
|
REG_PC = 64,
|
|
REG_SP = 30,
|
|
REG_PS = 31,
|
|
REG_GP = 29
|
|
};
|
|
|
|
#define PT_REG(reg) \
|
|
(PAGE_SIZE * 2 - sizeof(struct pt_regs) + offsetof(struct pt_regs, reg))
|
|
|
|
#define SW_REG(reg) \
|
|
(PAGE_SIZE * 2 - sizeof(struct pt_regs) - sizeof(struct switch_stack) \
|
|
+ offsetof(struct switch_stack, reg))
|
|
|
|
#define FP_REG(fp_regno, vector_regno) \
|
|
(fp_regno * 32 + vector_regno * 8)
|
|
|
|
static int regoff[] = {
|
|
PT_REG(r0), PT_REG(r1), PT_REG(r2), PT_REG(r3),
|
|
PT_REG(r4), PT_REG(r5), PT_REG(r6), PT_REG(r7),
|
|
PT_REG(r8), SW_REG(r9), SW_REG(r10), SW_REG(r11),
|
|
SW_REG(r12), SW_REG(r13), SW_REG(r14), SW_REG(r15),
|
|
PT_REG(r16), PT_REG(r17), PT_REG(r18), PT_REG(r19),
|
|
PT_REG(r20), PT_REG(r21), PT_REG(r22), PT_REG(r23),
|
|
PT_REG(r24), PT_REG(r25), PT_REG(r26), PT_REG(r27),
|
|
PT_REG(r28), PT_REG(gp), -1, -1
|
|
};
|
|
|
|
#define PCB_OFF(var) offsetof(struct pcb_struct, var)
|
|
|
|
static int pcboff[] = {
|
|
[USP] = PCB_OFF(usp),
|
|
[UNIQUE] = PCB_OFF(unique),
|
|
[DA_MATCH] = PCB_OFF(da_match),
|
|
[DA_MASK] = PCB_OFF(da_mask),
|
|
[DV_MATCH] = PCB_OFF(dv_match),
|
|
[DV_MASK] = PCB_OFF(dv_mask),
|
|
[DC_CTL] = PCB_OFF(dc_ctl)
|
|
};
|
|
|
|
static unsigned long zero;
|
|
|
|
/*
|
|
* Get address of register REGNO in task TASK.
|
|
*/
|
|
|
|
static unsigned long *
|
|
get_reg_addr(struct task_struct *task, unsigned long regno)
|
|
{
|
|
unsigned long *addr;
|
|
int fp_regno, vector_regno;
|
|
|
|
switch (regno) {
|
|
case USP:
|
|
case UNIQUE:
|
|
case DA_MATCH:
|
|
case DA_MASK:
|
|
case DV_MATCH:
|
|
case DV_MASK:
|
|
case DC_CTL:
|
|
addr = (void *)task_thread_info(task) + pcboff[regno];
|
|
break;
|
|
case REG_BASE ... REG_END:
|
|
addr = (void *)task_thread_info(task) + regoff[regno];
|
|
break;
|
|
case FPREG_BASE ... FPREG_END:
|
|
fp_regno = regno - FPREG_BASE;
|
|
vector_regno = 0;
|
|
addr = (void *)((unsigned long)&task->thread.ctx_fp + FP_REG(fp_regno, vector_regno));
|
|
break;
|
|
case VECREG_BASE ... VECREG_END:
|
|
/*
|
|
* return addr for zero value if we catch vectors of f31
|
|
* v0 and v3 of f31 are not in this range so ignore them
|
|
*/
|
|
if (regno == F31_V1 || regno == F31_V2) {
|
|
addr = &zero;
|
|
break;
|
|
}
|
|
fp_regno = (regno - VECREG_BASE) & 0x1f;
|
|
vector_regno = 1 + ((regno - VECREG_BASE) >> 5);
|
|
addr = (void *)((unsigned long)&task->thread.ctx_fp + FP_REG(fp_regno, vector_regno));
|
|
break;
|
|
case FPCR:
|
|
addr = (void *)&task->thread.fpcr;
|
|
break;
|
|
case PC:
|
|
addr = (void *)task_thread_info(task) + PT_REG(pc);
|
|
break;
|
|
default:
|
|
addr = &zero;
|
|
}
|
|
|
|
return addr;
|
|
}
|
|
|
|
/*
|
|
* Get contents of register REGNO in task TASK.
|
|
*/
|
|
unsigned long
|
|
get_reg(struct task_struct *task, unsigned long regno)
|
|
{
|
|
return *get_reg_addr(task, regno);
|
|
}
|
|
|
|
/*
|
|
* Write contents of register REGNO in task TASK.
|
|
*/
|
|
static int
|
|
put_reg(struct task_struct *task, unsigned long regno, unsigned long data)
|
|
{
|
|
*get_reg_addr(task, regno) = data;
|
|
return 0;
|
|
}
|
|
|
|
static inline int
|
|
read_int(struct task_struct *task, unsigned long addr, int *data)
|
|
{
|
|
int copied = access_process_vm(task, addr, data, sizeof(int), FOLL_FORCE);
|
|
|
|
return (copied == sizeof(int)) ? 0 : -EIO;
|
|
}
|
|
|
|
static inline int
|
|
write_int(struct task_struct *task, unsigned long addr, int data)
|
|
{
|
|
int copied = access_process_vm(task, addr, &data, sizeof(int),
|
|
FOLL_FORCE | FOLL_WRITE);
|
|
return (copied == sizeof(int)) ? 0 : -EIO;
|
|
}
|
|
|
|
/*
|
|
* Set breakpoint.
|
|
*/
|
|
int
|
|
ptrace_set_bpt(struct task_struct *child)
|
|
{
|
|
int displ, i, res, reg_b, nsaved = 0;
|
|
unsigned int insn, op_code;
|
|
unsigned long pc;
|
|
|
|
pc = get_reg(child, REG_PC);
|
|
res = read_int(child, pc, (int *)&insn);
|
|
if (res < 0)
|
|
return res;
|
|
|
|
op_code = insn >> 26;
|
|
/* br bsr beq bne blt ble bgt bge blbc blbs fbeq fbne fblt fble fbgt fbge */
|
|
if ((1UL << op_code) & 0x3fff000000000030UL) {
|
|
/*
|
|
* It's a branch: instead of trying to figure out
|
|
* whether the branch will be taken or not, we'll put
|
|
* a breakpoint at either location. This is simpler,
|
|
* more reliable, and probably not a whole lot slower
|
|
* than the alternative approach of emulating the
|
|
* branch (emulation can be tricky for fp branches).
|
|
*/
|
|
displ = ((s32)(insn << 11)) >> 9;
|
|
task_thread_info(child)->bpt_addr[nsaved++] = pc + 4;
|
|
if (displ) /* guard against unoptimized code */
|
|
task_thread_info(child)->bpt_addr[nsaved++]
|
|
= pc + 4 + displ;
|
|
/*call ret jmp*/
|
|
} else if (op_code >= 0x1 && op_code <= 0x3) {
|
|
reg_b = (insn >> 16) & 0x1f;
|
|
task_thread_info(child)->bpt_addr[nsaved++] = get_reg(child, reg_b);
|
|
} else {
|
|
task_thread_info(child)->bpt_addr[nsaved++] = pc + 4;
|
|
}
|
|
|
|
/* install breakpoints: */
|
|
for (i = 0; i < nsaved; ++i) {
|
|
res = read_int(child, task_thread_info(child)->bpt_addr[i],
|
|
(int *)&insn);
|
|
if (res < 0)
|
|
return res;
|
|
task_thread_info(child)->bpt_insn[i] = insn;
|
|
res = write_int(child, task_thread_info(child)->bpt_addr[i],
|
|
BREAKINST);
|
|
if (res < 0)
|
|
return res;
|
|
}
|
|
task_thread_info(child)->bpt_nsaved = nsaved;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Ensure no single-step breakpoint is pending. Returns non-zero
|
|
* value if child was being single-stepped.
|
|
*/
|
|
int
|
|
ptrace_cancel_bpt(struct task_struct *child)
|
|
{
|
|
int i, nsaved = task_thread_info(child)->bpt_nsaved;
|
|
|
|
task_thread_info(child)->bpt_nsaved = 0;
|
|
|
|
if (nsaved > 2) {
|
|
printk("%s: bogus nsaved: %d!\n", __func__, nsaved);
|
|
nsaved = 2;
|
|
}
|
|
|
|
for (i = 0; i < nsaved; ++i) {
|
|
write_int(child, task_thread_info(child)->bpt_addr[i],
|
|
task_thread_info(child)->bpt_insn[i]);
|
|
}
|
|
return (nsaved != 0);
|
|
}
|
|
|
|
void user_enable_single_step(struct task_struct *child)
|
|
{
|
|
/* Mark single stepping. */
|
|
task_thread_info(child)->bpt_nsaved = -1;
|
|
}
|
|
|
|
void user_disable_single_step(struct task_struct *child)
|
|
{
|
|
ptrace_cancel_bpt(child);
|
|
}
|
|
|
|
/*
|
|
* Called by kernel/ptrace.c when detaching..
|
|
*
|
|
* Make sure the single step bit is not set.
|
|
*/
|
|
void ptrace_disable(struct task_struct *child)
|
|
{
|
|
user_disable_single_step(child);
|
|
}
|
|
|
|
int ptrace_getregs(struct task_struct *child, __s64 __user *data)
|
|
{
|
|
int ret, retval = 0;
|
|
int i;
|
|
unsigned long regval;
|
|
|
|
if (!access_ok(data, sizeof(long) * 33))
|
|
return -EIO;
|
|
|
|
/* r0-r15 */
|
|
for (i = 0; i < 16; i++) {
|
|
regval = get_reg(child, i);
|
|
retval |= __put_user((long)regval, data + i);
|
|
}
|
|
/* r19-r28 */
|
|
for (i = 19; i < 29; i++) {
|
|
regval = get_reg(child, i);
|
|
retval |= __put_user((long)regval, data + i - 3);
|
|
}
|
|
/*SP, PS ,PC,GP*/
|
|
retval |= __put_user((long)(get_reg(child, REG_SP)), data + EF_SP);
|
|
retval |= __put_user((long)(get_reg(child, REG_PS)), data + EF_PS);
|
|
retval |= __put_user((long)(get_reg(child, REG_PC)), data + EF_PC);
|
|
retval |= __put_user((long)(get_reg(child, REG_GP)), data + EF_GP);
|
|
/* r16-r18 */
|
|
retval |= __put_user((long)(get_reg(child, 16)), data + EF_A0);
|
|
retval |= __put_user((long)(get_reg(child, 17)), data + EF_A1);
|
|
retval |= __put_user((long)(get_reg(child, 18)), data + EF_A2);
|
|
|
|
ret = retval ? -EIO : 0;
|
|
return ret;
|
|
}
|
|
|
|
int ptrace_setregs(struct task_struct *child, __s64 __user *data)
|
|
{
|
|
int ret, retval = 0;
|
|
int i;
|
|
unsigned long regval;
|
|
|
|
if (!access_ok(data, sizeof(long) * 33))
|
|
return -EIO;
|
|
|
|
/* r0-r15 */
|
|
for (i = 0; i < 16; i++) {
|
|
retval |= __get_user(regval, data + i);
|
|
ret = put_reg(child, i, regval);
|
|
}
|
|
/* r19-r28 */
|
|
for (i = 19; i < 29; i++) {
|
|
retval |= __get_user(regval, data + i - 3);
|
|
ret = put_reg(child, i, regval);
|
|
}
|
|
/*SP, PS ,PC,GP*/
|
|
retval |= __get_user(regval, data + EF_SP);
|
|
ret = put_reg(child, REG_SP, regval);
|
|
retval |= __get_user(regval, data + EF_PS);
|
|
ret = put_reg(child, REG_PS, regval);
|
|
retval |= __get_user(regval, data + EF_PC);
|
|
ret = put_reg(child, REG_PC, regval);
|
|
retval |= __get_user(regval, data + EF_GP);
|
|
ret = put_reg(child, REG_GP, regval);
|
|
/* r16-r18 */
|
|
retval |= __get_user(regval, data + EF_A0);
|
|
ret = put_reg(child, 16, regval);
|
|
retval |= __get_user(regval, data + EF_A1);
|
|
ret = put_reg(child, 17, regval);
|
|
retval |= __get_user(regval, data + EF_A2);
|
|
ret = put_reg(child, 18, regval);
|
|
|
|
ret = retval ? -EIO : 0;
|
|
return 0;
|
|
}
|
|
|
|
int ptrace_getfpregs(struct task_struct *child, __s64 __user *data)
|
|
{
|
|
int ret, retval = 0;
|
|
int i;
|
|
unsigned long regval;
|
|
|
|
if (!access_ok(data, sizeof(long) * 32))
|
|
return -EIO;
|
|
|
|
/* fp0-fp31 */
|
|
for (i = 0; i < 32; i++) {
|
|
regval = get_reg(child, REG_F0 + i);
|
|
retval |= __put_user((long)regval, data + i);
|
|
}
|
|
|
|
ret = retval ? -EIO : 0;
|
|
return 0;
|
|
}
|
|
|
|
int ptrace_setfpregs(struct task_struct *child, __s64 __user *data)
|
|
{
|
|
int ret, retval = 0;
|
|
int i;
|
|
unsigned long regval;
|
|
|
|
if (!access_ok(data, sizeof(long) * 32))
|
|
return -EIO;
|
|
|
|
/* fp0-fp31 */
|
|
for (i = 0; i < 32; i++) {
|
|
retval |= __get_user(regval, data + i);
|
|
ret = put_reg(child, REG_F0 + i, regval);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
long arch_ptrace(struct task_struct *child, long request,
|
|
unsigned long addr, unsigned long data)
|
|
{
|
|
unsigned long tmp;
|
|
size_t copied;
|
|
long ret;
|
|
void __user *datavp = (void __user *) data;
|
|
|
|
switch (request) {
|
|
/* When I and D space are separate, these will need to be fixed. */
|
|
case PTRACE_PEEKTEXT: /* read word at location addr. */
|
|
case PTRACE_PEEKDATA:
|
|
copied = access_process_vm(child, addr, &tmp, sizeof(tmp), FOLL_FORCE);
|
|
ret = -EIO;
|
|
if (copied != sizeof(tmp))
|
|
break;
|
|
|
|
force_successful_syscall_return();
|
|
ret = tmp;
|
|
break;
|
|
|
|
/* Read register number ADDR. */
|
|
case PTRACE_PEEKUSR:
|
|
force_successful_syscall_return();
|
|
ret = get_reg(child, addr);
|
|
break;
|
|
|
|
/* When I and D space are separate, this will have to be fixed. */
|
|
case PTRACE_POKETEXT: /* write the word at location addr. */
|
|
case PTRACE_POKEDATA:
|
|
ret = generic_ptrace_pokedata(child, addr, data);
|
|
break;
|
|
|
|
case PTRACE_POKEUSR: /* write the specified register */
|
|
ret = put_reg(child, addr, data);
|
|
break;
|
|
case PTRACE_GETREGS:
|
|
ret = ptrace_getregs(child, datavp);
|
|
break;
|
|
case PTRACE_SETREGS:
|
|
ret = ptrace_setregs(child, datavp);
|
|
break;
|
|
case PTRACE_GETFPREGS:
|
|
ret = ptrace_getfpregs(child, datavp);
|
|
break;
|
|
case PTRACE_SETFPREGS:
|
|
ret = ptrace_setfpregs(child, datavp);
|
|
break;
|
|
default:
|
|
ret = ptrace_request(child, request, addr, data);
|
|
break;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
asmlinkage unsigned long syscall_trace_enter(void)
|
|
{
|
|
unsigned long ret = 0;
|
|
struct pt_regs *regs = current_pt_regs();
|
|
|
|
if (test_thread_flag(TIF_SYSCALL_TRACE) &&
|
|
tracehook_report_syscall_entry(current_pt_regs()))
|
|
ret = -1UL;
|
|
|
|
#ifdef CONFIG_SECCOMP
|
|
/* Do seccomp after ptrace, to catch any tracer changes. */
|
|
if (secure_computing() == -1)
|
|
return -1;
|
|
#endif
|
|
|
|
if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
|
|
trace_sys_enter(regs, regs->r0);
|
|
audit_syscall_entry(regs->r0, regs->r16, regs->r17, regs->r18, regs->r19);
|
|
return ret ?: current_pt_regs()->r0;
|
|
}
|
|
|
|
asmlinkage void
|
|
syscall_trace_leave(void)
|
|
{
|
|
struct pt_regs *regs = current_pt_regs();
|
|
|
|
audit_syscall_exit(current_pt_regs());
|
|
if (test_thread_flag(TIF_SYSCALL_TRACE))
|
|
tracehook_report_syscall_exit(current_pt_regs(), 0);
|
|
if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
|
|
trace_sys_exit(regs, regs_return_value(regs));
|
|
}
|
|
|
|
static long rwcsr(int rw, unsigned long csr, unsigned long value)
|
|
{
|
|
register unsigned long __r0 __asm__("$0");
|
|
register unsigned long __r16 __asm__("$16") = rw;
|
|
register unsigned long __r17 __asm__("$17") = csr;
|
|
register unsigned long __r18 __asm__("$18") = value;
|
|
|
|
__asm__ __volatile__(
|
|
"sys_call %4"
|
|
: "=r"(__r0), "=r"(__r16), "=r"(__r17), "=r"(__r18)
|
|
: "i"(HMC_rwreg), "1"(__r16), "2"(__r17), "3"(__r18)
|
|
: "$1", "$22", "$23", "$24", "$25");
|
|
|
|
return __r0;
|
|
}
|
|
|
|
#define RCSR 0
|
|
#define WCSR 1
|
|
|
|
#define CSR_DA_MATCH 0
|
|
#define CSR_DA_MASK 1
|
|
#define CSR_IA_MATCH 2
|
|
#define CSR_IA_MASK 3
|
|
#define CSR_IDA_MATCH 6
|
|
#define CSR_IDA_MASK 7
|
|
#define CSR_DC_CTL 11
|
|
#define CSR_DV_MATCH 15
|
|
#define CSR_DV_MASK 16
|
|
|
|
#define DV_MATCH_EN_S 19
|
|
#define DAV_MATCH_EN_S 20
|
|
|
|
int do_match(unsigned long address, unsigned long mmcsr, long cause, struct pt_regs *regs)
|
|
{
|
|
unsigned long dc_ctl;
|
|
unsigned long value;
|
|
|
|
printk("%s: pid %d, name = %s,cause = %#lx, mmcsr = %#lx, address = %#lx, pc %#lx\n",
|
|
__func__, current->pid, current->comm, cause, mmcsr, address, regs->pc);
|
|
|
|
switch (mmcsr) {
|
|
case MMCSR__DA_MATCH:
|
|
case MMCSR__DV_MATCH:
|
|
case MMCSR__DAV_MATCH:
|
|
dik_show_regs(regs, (unsigned long *)regs-15);
|
|
|
|
if (!(current->ptrace & PT_PTRACED)) {
|
|
printk(" pid %d %s not be ptraced, return\n", current->pid, current->comm);
|
|
if (mmcsr == MMCSR__DA_MATCH)
|
|
rwcsr(WCSR, CSR_DA_MATCH, 0); //clear da_match
|
|
if (mmcsr == MMCSR__DV_MATCH) {
|
|
value = rwcsr(RCSR, CSR_DV_MATCH, 0);
|
|
printk("value is %#lx\n", value);
|
|
value = rwcsr(RCSR, CSR_DV_MASK, 0);
|
|
printk("value is %#lx\n", value);
|
|
dc_ctl = rwcsr(RCSR, CSR_DC_CTL, 0);
|
|
dc_ctl &= ~(0x1UL << DV_MATCH_EN_S);
|
|
rwcsr(WCSR, CSR_DC_CTL, dc_ctl);
|
|
}
|
|
if (mmcsr == MMCSR__DAV_MATCH) {
|
|
dc_ctl = rwcsr(RCSR, CSR_DC_CTL, 0);
|
|
dc_ctl &= ~((0x1UL << DV_MATCH_EN_S) | (0x1UL << DAV_MATCH_EN_S));
|
|
rwcsr(WCSR, CSR_DC_CTL, dc_ctl);
|
|
rwcsr(WCSR, CSR_DA_MATCH, 0); //clear da_match
|
|
}
|
|
task_thread_info(current)->pcb.da_match = 0;
|
|
task_thread_info(current)->pcb.dv_match = 0;
|
|
task_thread_info(current)->pcb.dc_ctl = 0;
|
|
return 1;
|
|
}
|
|
|
|
if (mmcsr == MMCSR__DA_MATCH) {
|
|
rwcsr(WCSR, CSR_DA_MATCH, 0); //clear da_match
|
|
task_thread_info(current)->pcb.da_match = 0;
|
|
}
|
|
if (mmcsr == MMCSR__DV_MATCH) {
|
|
dc_ctl = rwcsr(RCSR, CSR_DC_CTL, 0);
|
|
dc_ctl &= ~(0x1UL << DV_MATCH_EN_S);
|
|
rwcsr(WCSR, CSR_DC_CTL, dc_ctl);
|
|
}
|
|
if (mmcsr == MMCSR__DAV_MATCH) {
|
|
dc_ctl = rwcsr(RCSR, CSR_DC_CTL, 0);
|
|
dc_ctl &= ~((0x1UL << DV_MATCH_EN_S) | (0x1UL << DAV_MATCH_EN_S));
|
|
rwcsr(WCSR, CSR_DC_CTL, dc_ctl);
|
|
rwcsr(WCSR, CSR_DA_MATCH, 0); //clear da_match
|
|
}
|
|
task_thread_info(current)->pcb.dv_match = 0;
|
|
task_thread_info(current)->pcb.dc_ctl = 0;
|
|
printk("do_page_fault: want to send SIGTRAP, pid = %d\n", current->pid);
|
|
force_sig_fault(SIGTRAP, TRAP_HWBKPT, (void *) address, 0);
|
|
return 1;
|
|
|
|
case MMCSR__IA_MATCH:
|
|
rwcsr(WCSR, CSR_IA_MATCH, 0); //clear ia_match
|
|
return 1;
|
|
case MMCSR__IDA_MATCH:
|
|
rwcsr(WCSR, CSR_IDA_MATCH, 0); //clear ida_match
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void restore_da_match_after_sched(void)
|
|
{
|
|
unsigned long dc_ctl_mode;
|
|
unsigned long dc_ctl;
|
|
struct pcb_struct *pcb = &task_thread_info(current)->pcb;
|
|
|
|
if (!(pcb->da_match || pcb->da_mask || pcb->dv_match || pcb->dv_mask || pcb->dc_ctl))
|
|
return;
|
|
printk("Restroe MATCH status, pid: %d\n", current->pid);
|
|
rwcsr(WCSR, CSR_DA_MATCH, 0);
|
|
rwcsr(WCSR, CSR_DA_MASK, pcb->da_mask);
|
|
rwcsr(WCSR, CSR_DA_MATCH, pcb->da_match);
|
|
dc_ctl_mode = pcb->dc_ctl;
|
|
dc_ctl = rwcsr(RCSR, CSR_DC_CTL, 0);
|
|
dc_ctl &= ~((0x1UL << DV_MATCH_EN_S) | (0x1UL << DAV_MATCH_EN_S));
|
|
dc_ctl |= ((dc_ctl_mode << DV_MATCH_EN_S) & ((0x1UL << DV_MATCH_EN_S) | (0x1UL << DAV_MATCH_EN_S)));
|
|
if (dc_ctl_mode & 0x1) {
|
|
rwcsr(WCSR, CSR_DV_MATCH, pcb->dv_match);
|
|
rwcsr(WCSR, CSR_DV_MASK, pcb->dv_mask);
|
|
rwcsr(WCSR, CSR_DC_CTL, dc_ctl);
|
|
}
|
|
}
|
|
|
|
struct pt_regs_offset {
|
|
const char *name;
|
|
int offset;
|
|
};
|
|
|
|
#define REG_OFFSET_NAME(r) { \
|
|
.name = #r, \
|
|
.offset = offsetof(struct pt_regs, r) \
|
|
}
|
|
|
|
#define REG_OFFSET_END { \
|
|
.name = NULL, \
|
|
.offset = 0 \
|
|
}
|
|
|
|
static const struct pt_regs_offset regoffset_table[] = {
|
|
REG_OFFSET_NAME(r0),
|
|
REG_OFFSET_NAME(r1),
|
|
REG_OFFSET_NAME(r2),
|
|
REG_OFFSET_NAME(r3),
|
|
REG_OFFSET_NAME(r4),
|
|
REG_OFFSET_NAME(r5),
|
|
REG_OFFSET_NAME(r6),
|
|
REG_OFFSET_NAME(r7),
|
|
REG_OFFSET_NAME(r8),
|
|
REG_OFFSET_NAME(r19),
|
|
REG_OFFSET_NAME(r20),
|
|
REG_OFFSET_NAME(r21),
|
|
REG_OFFSET_NAME(r22),
|
|
REG_OFFSET_NAME(r23),
|
|
REG_OFFSET_NAME(r24),
|
|
REG_OFFSET_NAME(r25),
|
|
REG_OFFSET_NAME(r26),
|
|
REG_OFFSET_NAME(r27),
|
|
REG_OFFSET_NAME(r28),
|
|
REG_OFFSET_NAME(hae),
|
|
REG_OFFSET_NAME(trap_a0),
|
|
REG_OFFSET_NAME(trap_a1),
|
|
REG_OFFSET_NAME(trap_a2),
|
|
REG_OFFSET_NAME(ps),
|
|
REG_OFFSET_NAME(pc),
|
|
REG_OFFSET_NAME(gp),
|
|
REG_OFFSET_NAME(r16),
|
|
REG_OFFSET_NAME(r17),
|
|
REG_OFFSET_NAME(r18),
|
|
REG_OFFSET_END,
|
|
};
|
|
|
|
/**
|
|
* regs_query_register_offset() - query register offset from its name
|
|
* @name: the name of a register
|
|
*
|
|
* regs_query_register_offset() returns the offset of a register in struct
|
|
* pt_regs from its name. If the name is invalid, this returns -EINVAL;
|
|
*/
|
|
int regs_query_register_offset(const char *name)
|
|
{
|
|
const struct pt_regs_offset *roff;
|
|
|
|
for (roff = regoffset_table; roff->name != NULL; roff++)
|
|
if (!strcmp(roff->name, name))
|
|
return roff->offset;
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int regs_within_kernel_stack(struct pt_regs *regs, unsigned long addr)
|
|
{
|
|
unsigned long ksp = kernel_stack_pointer(regs);
|
|
|
|
return (addr & ~(THREAD_SIZE - 1)) == (ksp & ~(THREAD_SIZE - 1));
|
|
}
|
|
|
|
/**
|
|
* regs_get_kernel_stack_nth() - get Nth entry of the stack
|
|
* @regs:pt_regs which contains kernel stack pointer.
|
|
* @n:stack entry number.
|
|
*
|
|
* regs_get_kernel_stack_nth() returns @n th entry of the kernel stack which
|
|
* is specifined by @regs. If the @n th entry is NOT in the kernel stack,
|
|
* this returns 0.
|
|
*/
|
|
unsigned long regs_get_kernel_stack_nth(struct pt_regs *regs, unsigned int n)
|
|
{
|
|
unsigned long addr;
|
|
|
|
addr = kernel_stack_pointer(regs) + n * sizeof(long);
|
|
if (!regs_within_kernel_stack(regs, addr))
|
|
return 0;
|
|
return *(unsigned long *)addr;
|
|
}
|