2026-01-29 22:25:33 +08:00

859 lines
24 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/audit.h>
#include <linux/regset.h>
#include <linux/elf.h>
#include <linux/sched/task_stack.h>
#include <asm/asm-offsets.h>
#include "proto.h"
#include <asm/csr.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
* +================================+
*/
/*
* 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).
*/
#define PCB_OFF(var) offsetof(struct pcb_struct, var)
static int pcboff[] = {
[PT_TP] = PCB_OFF(tp),
[PT_DA_MATCH] = PCB_OFF(da_match),
[PT_DA_MASK] = PCB_OFF(da_mask),
[PT_DV_MATCH] = PCB_OFF(dv_match),
[PT_DV_MASK] = PCB_OFF(dv_mask),
[PT_DC_CTL] = PCB_OFF(dc_ctl),
[PT_MATCH_CTL] = PCB_OFF(match_ctl),
[PT_IA_MATCH] = PCB_OFF(ia_match),
[PT_IA_MASK] = PCB_OFF(ia_mask),
[PT_IV_MATCH] = PCB_OFF(iv_match),
[PT_IDA_MATCH] = PCB_OFF(ida_match),
[PT_IDA_MASK] = PCB_OFF(ida_mask)
};
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)
{
void *addr;
int fno, vno;
switch (regno) {
case PT_UNIQUE:
case PT_DA_MATCH:
case PT_DA_MASK:
case PT_DV_MATCH:
case PT_DV_MASK:
case PT_MATCH_CTL:
case PT_IA_MATCH:
case PT_IA_MASK:
case PT_IV_MATCH:
case PT_IDA_MATCH:
case PT_IDA_MASK:
addr = (void *)task_thread_info(task) + pcboff[regno];
break;
case PT_REG_BASE ... PT_REG_END:
addr = &task_pt_regs(task)->regs[regno];
break;
case PT_FPREG_BASE ... PT_FPREG_END:
fno = regno - PT_FPREG_BASE;
addr = &task->thread.fpstate.fp[fno].v[0];
break;
case PT_VECREG_BASE ... PT_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 == PT_F31_V1 || regno == PT_F31_V2) {
addr = &zero;
break;
}
fno = (regno - PT_VECREG_BASE) & 0x1f;
vno = 1 + ((regno - PT_VECREG_BASE) >> 5);
addr = &task->thread.fpstate.fp[fno].v[vno];
break;
case PT_FPCR:
addr = &task->thread.fpstate.fpcr;
break;
case PT_PC:
addr = (void *)task_pt_regs(task) + PT_REGS_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, PT_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) {
pr_info("%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);
}
static int gpr_get(struct task_struct *target,
const struct user_regset *regset,
struct membuf to)
{
return membuf_write(&to, task_pt_regs(target), sizeof(struct user_pt_regs));
}
static int gpr_set(struct task_struct *target,
const struct user_regset *regset,
unsigned int pos, unsigned int count,
const void *kbuf, const void __user *ubuf)
{
return user_regset_copyin(&pos, &count, &kbuf, &ubuf,
task_pt_regs(target), 0, sizeof(struct user_pt_regs));
}
static int fpr_get(struct task_struct *target,
const struct user_regset *regset,
struct membuf to)
{
return membuf_write(&to, &target->thread.fpstate,
sizeof(struct user_fpsimd_state));
}
static int fpr_set(struct task_struct *target,
const struct user_regset *regset,
unsigned int pos, unsigned int count,
const void *kbuf, const void __user *ubuf)
{
return user_regset_copyin(&pos, &count, &kbuf, &ubuf,
&target->thread.fpstate, 0,
sizeof(struct user_fpsimd_state));
}
enum sw64_regset {
REGSET_GPR,
REGSET_FPR,
};
static const struct user_regset sw64_regsets[] = {
[REGSET_GPR] = {
.core_note_type = NT_PRSTATUS,
.n = ELF_NGREG,
.size = sizeof(elf_greg_t),
.align = sizeof(elf_greg_t),
.regset_get = gpr_get,
.set = gpr_set
},
[REGSET_FPR] = {
.core_note_type = NT_PRFPREG,
.n = sizeof(struct user_fpsimd_state) / sizeof(u64),
.size = sizeof(u64),
.align = sizeof(u64),
.regset_get = fpr_get,
.set = fpr_set
},
};
static const struct user_regset_view user_sw64_view = {
.name = "sw64", .e_machine = EM_SW64,
.regsets = sw64_regsets, .n = ARRAY_SIZE(sw64_regsets)
};
const struct user_regset_view *task_user_regset_view(struct task_struct *task)
{
return &user_sw64_view;
}
long arch_ptrace(struct task_struct *child, long request,
unsigned long addr, unsigned long data)
{
unsigned long tmp;
size_t copied;
long ret;
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;
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) &&
ptrace_report_syscall_entry(regs))
return NO_SYSCALL;
#ifdef CONFIG_SECCOMP
/* Do seccomp after ptrace, to catch any tracer changes. */
if (secure_computing() == -1)
return NO_SYSCALL;
#endif
if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
trace_sys_enter(regs, regs->regs[0]);
audit_syscall_entry(regs->regs[0], regs->regs[16], regs->regs[17], regs->regs[18], regs->regs[19]);
return ret ?: regs->regs[0];
}
asmlinkage void
syscall_trace_leave(void)
{
struct pt_regs *regs = current_pt_regs();
audit_syscall_exit(regs);
if (test_thread_flag(TIF_SYSCALL_TRACE))
ptrace_report_syscall_exit(regs, 0);
if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
trace_sys_exit(regs, regs_return_value(regs));
}
#ifdef CONFIG_SUBARCH_C3B
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;
pr_info("%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:
show_regs(regs);
if (!(current->ptrace & PT_PTRACED)) {
pr_notice(" 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);
pr_notice("value is %#lx\n", value);
value = rwcsr(RCSR, CSR_DV_MASK, 0);
pr_notice("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;
pr_notice("do_page_fault: want to send SIGTRAP, pid = %d\n", current->pid);
force_sig_fault(SIGTRAP, TRAP_HWBKPT, (void *) address);
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;
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);
}
}
#elif defined(CONFIG_SUBARCH_C4)
int do_match(unsigned long address, unsigned long mmcsr, long cause, struct pt_regs *regs)
{
kernel_siginfo_t info;
unsigned long match_ctl, ia_match;
sigval_t sw64_value;
pr_info("%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:
case MMCSR__IA_MATCH:
case MMCSR__IDA_MATCH:
case MMCSR__IV_MATCH:
show_regs(regs);
if (!(current->ptrace & PT_PTRACED)) {
pr_notice(" pid %d %s not be ptraced, return\n", current->pid, current->comm);
if (mmcsr == MMCSR__DA_MATCH) {
match_ctl = read_csr(CSR_DC_CTLP);
match_ctl &= ~(0x3UL << DA_MATCH_EN_S);
write_csr(match_ctl, CSR_DC_CTLP);
write_csr(0, CSR_DA_MATCH); // clear da_match
task_thread_info(current)->pcb.match_ctl &= ~0x1;
task_thread_info(current)->pcb.da_match = 0;
}
if (mmcsr == MMCSR__DV_MATCH) {
match_ctl = read_csr(CSR_DC_CTLP);
match_ctl &= ~(0x1UL << DV_MATCH_EN_S);
write_csr(match_ctl, CSR_DC_CTLP);
write_csr(0, CSR_DV_MATCH); // clear dv_match
task_thread_info(current)->pcb.match_ctl &= ~(0x1 << 1);
task_thread_info(current)->pcb.dv_match = 0;
}
if (mmcsr == MMCSR__DAV_MATCH) {
match_ctl = read_csr(CSR_DC_CTLP);
match_ctl &= ~((0x3UL << DA_MATCH_EN_S) | (0x1UL << DV_MATCH_EN_S) | (0x1UL << DAV_MATCH_EN_S));
write_csr(match_ctl, CSR_DC_CTLP);
write_csr(0, CSR_DA_MATCH); // clear da_match
write_csr(0, CSR_DV_MATCH); // clear dv_match
task_thread_info(current)->pcb.match_ctl &= ~(0x1 | (0x1 << 1) | (0x1 << 2));
task_thread_info(current)->pcb.da_match = 0;
task_thread_info(current)->pcb.dv_match = 0;
}
if (mmcsr == MMCSR__IA_MATCH) {
ia_match = read_csr(CSR_IA_MATCH);
ia_match &= ~((0x1UL << IA_MATCH_EN_S) | (0x7ffffffffffffUL << 2));
write_csr(ia_match, CSR_IA_MATCH); // clear ia_match
task_thread_info(current)->pcb.match_ctl &= ~(0x1 << 3);
task_thread_info(current)->pcb.ia_match = 0;
}
if (mmcsr == MMCSR__IV_MATCH) {
ia_match = read_csr(CSR_IA_MATCH);
ia_match &= ~((0x1UL << IV_MATCH_EN_S) | (0x1UL << IV_PM_EN_S));
write_csr(ia_match, CSR_IA_MATCH); // clear ia_match
write_csr(0, CSR_IV_MATCH); // clear iv_match
task_thread_info(current)->pcb.match_ctl &= ~(0x1 << 4);
task_thread_info(current)->pcb.ia_match &= ~((0x1UL << IV_MATCH_EN_S) | (0x1UL << IV_PM_EN_S));
task_thread_info(current)->pcb.iv_match = 0;
}
if (mmcsr == MMCSR__IDA_MATCH) {
write_csr(0, CSR_IDA_MATCH); // clear ida_match
task_thread_info(current)->pcb.match_ctl &= ~(0x1 << 5);
task_thread_info(current)->pcb.ida_match = 0;
}
return 1;
}
info.si_signo = SIGTRAP;
info.si_addr = (void *) address;
sw64_value.sival_ptr = (void *)(regs->pc);
info.si_value = sw64_value;
info.si_code = TRAP_HWBKPT;
if (mmcsr == MMCSR__DA_MATCH) {
info.si_errno = 1;
match_ctl = read_csr(CSR_DC_CTLP);
match_ctl &= ~(0x3UL << DA_MATCH_EN_S);
write_csr(match_ctl, CSR_DC_CTLP);
write_csr(0, CSR_DA_MATCH); // clear da_match
task_thread_info(current)->pcb.match_ctl &= ~0x1;
task_thread_info(current)->pcb.da_match = 0;
}
if (mmcsr == MMCSR__DV_MATCH) {
info.si_errno = 2;
match_ctl = read_csr(CSR_DC_CTLP);
match_ctl &= ~(0x1UL << DV_MATCH_EN_S);
write_csr(match_ctl, CSR_DC_CTLP);
write_csr(0, CSR_DV_MATCH); // clear dv_match
task_thread_info(current)->pcb.match_ctl &= ~(0x1 << 1);
task_thread_info(current)->pcb.dv_match = 0;
}
if (mmcsr == MMCSR__DAV_MATCH) {
info.si_errno = 3;
match_ctl = read_csr(CSR_DC_CTLP);
match_ctl &= ~((0x3UL << DA_MATCH_EN_S) | (0x1UL << DV_MATCH_EN_S) | (0x1UL << DAV_MATCH_EN_S));
write_csr(match_ctl, CSR_DC_CTLP);
write_csr(0, CSR_DA_MATCH); // clear da_match
write_csr(0, CSR_DV_MATCH); // clear dv_match
task_thread_info(current)->pcb.match_ctl &= ~(0x1 | (0x1 << 1) | (0x1 << 2));
task_thread_info(current)->pcb.da_match = 0;
task_thread_info(current)->pcb.dv_match = 0;
}
if (mmcsr == MMCSR__IA_MATCH) {
info.si_errno = 4;
ia_match = read_csr(CSR_IA_MATCH);
ia_match &= ~((0x1UL << IA_MATCH_EN_S) | (0x7ffffffffffffUL << 2));
write_csr(ia_match, CSR_IA_MATCH); // clear ia_match
task_thread_info(current)->pcb.match_ctl &= ~(0x1 << 3);
task_thread_info(current)->pcb.ia_match = 0;
}
if (mmcsr == MMCSR__IV_MATCH) {
info.si_errno = 5;
ia_match = read_csr(CSR_IA_MATCH);
ia_match &= ~((0x1UL << IV_MATCH_EN_S) | (0x1UL << IV_PM_EN_S));
write_csr(ia_match, CSR_IA_MATCH); // clear ia_match
write_csr(0, CSR_IV_MATCH); // clear iv_match
task_thread_info(current)->pcb.match_ctl &= ~(0x1 << 4);
task_thread_info(current)->pcb.ia_match &= ~((0x1UL << IV_MATCH_EN_S) | (0x1UL << IV_PM_EN_S));
task_thread_info(current)->pcb.iv_match = 0;
}
if (mmcsr == MMCSR__IDA_MATCH) {
info.si_errno = 6;
write_csr(0, CSR_IDA_MATCH); // clear ida_match
task_thread_info(current)->pcb.match_ctl &= ~(0x1 << 5);
task_thread_info(current)->pcb.ida_match = 0;
}
pr_notice("do_page_fault: want to send SIGTRAP, pid = %d\n", current->pid);
force_sig_info(&info);
return 1;
}
return 0;
}
/*
*pcb->match_ctl:
* [0] DA_MATCH
* [1] DV_MATCH
* [2] DAV_MATCH
* [3] IA_MATCH
* [4] IV_MATCH
* [5] IDA_MATCH
* [8:9] match_ctl_mode
*
*/
#define DA_MATCH 0x1
#define DV_MATCH 0x2
#define DAV_MATCH 0x4
#define IA_MATCH 0x8
#define IV_MATCH 0x10
#define IDA_MATCH 0x20
void restore_da_match_after_sched(void)
{
unsigned long match_ctl_mode;
unsigned long match_ctl;
struct pcb_struct *pcb = &task_thread_info(current)->pcb;
unsigned long vpn, upn;
if (!pcb->match_ctl)
return;
pr_info("Restroe MATCH status, pid: %d\n", current->pid);
if (pcb->match_ctl & DA_MATCH) {
write_csr(pcb->da_match, CSR_DA_MATCH);
write_csr(pcb->da_mask, CSR_DA_MASK);
match_ctl_mode = (pcb->match_ctl >> 8) & 0x3;
match_ctl = read_csr(CSR_DC_CTLP);
match_ctl &= ~((0x1UL << 3) | (0x3UL << DA_MATCH_EN_S) | (0x1UL << DV_MATCH_EN_S) | (0x1UL << DAV_MATCH_EN_S));
match_ctl |= (match_ctl_mode << DA_MATCH_EN_S) | (0x1UL << DPM_MATCH_EN_S) | (0x3UL << DPM_MATCH);
write_csr(match_ctl, CSR_DC_CTLP);
pr_info("da_match:%#lx da_mask:%#lx match_ctl:%#lx\n", pcb->da_match, pcb->da_mask, match_ctl);
}
if (pcb->match_ctl & DV_MATCH) {
write_csr(pcb->dv_match, CSR_DV_MATCH);
write_csr(pcb->dv_mask, CSR_DV_MASK);
match_ctl = read_csr(CSR_DC_CTLP);
match_ctl &= ~((0x1UL << 3) | (0x3UL << DA_MATCH_EN_S) | (0x1UL << DV_MATCH_EN_S) | (0x1UL << DAV_MATCH_EN_S));
match_ctl |= (0x1UL << DV_MATCH_EN_S) | (0x1UL << DPM_MATCH_EN_S) | (0x3UL << DPM_MATCH);
write_csr(match_ctl, CSR_DC_CTLP);
pr_info("dv_match:%#lx dv_mask:%#lx match_ctl:%#lx\n", pcb->dv_match, pcb->dv_mask, match_ctl);
}
if (pcb->match_ctl & DAV_MATCH) {
write_csr(pcb->da_match, CSR_DA_MATCH);
write_csr(pcb->da_mask, CSR_DA_MASK);
write_csr(pcb->dv_match, CSR_DV_MATCH);
write_csr(pcb->dv_mask, CSR_DV_MASK);
write_csr(0xfffffffff, CSR_DA_MATCH_MODE);
match_ctl_mode = (pcb->match_ctl >> 8) & 0x3;
match_ctl = read_csr(CSR_DC_CTLP);
match_ctl &= ~((0x3UL << DA_MATCH_EN_S) | (0x1UL << DV_MATCH_EN_S) | (0x1UL << DAV_MATCH_EN_S));
match_ctl |= (match_ctl_mode << DA_MATCH_EN_S) | (0x1UL << DV_MATCH_EN_S)
| (0x1UL << DAV_MATCH_EN_S) | (0x1UL << DPM_MATCH_EN_S)
| (0x3UL << DPM_MATCH);
write_csr(match_ctl, CSR_DC_CTLP);
pr_info("da_match:%#lx da_mask:%#lx dv_match:%#lx dv_mask:%#lx match_ctl:%#lx\n",
pcb->da_match, pcb->da_mask, pcb->dv_match, pcb->dv_mask, match_ctl);
}
if (pcb->match_ctl & IA_MATCH) {
pcb->ia_match |= (0x1UL << IA_MATCH_EN_S) | 0x3;
pcb->ia_mask |= 0x3;
write_csr(pcb->ia_match, CSR_IA_MATCH);
write_csr(pcb->ia_mask, CSR_IA_MASK);
vpn = read_csr(CSR_VPCR) >> 44;
vpn &= 0x3ff;
upn = read_csr(CSR_UPCR);
upn &= 0x3ff;
write_csr(((0x3ff << 18) | vpn), CSR_IA_VPNMATCH);
write_csr(((0x3ff << 18) | upn), CSR_IA_UPNMATCH);
pr_info("ia_match:%#lx ia_mask:%#lx\n", pcb->ia_match, pcb->ia_mask);
}
if (pcb->match_ctl & IV_MATCH) {
pcb->ia_match |= (0x1UL << IV_MATCH_EN_S) | (0x1UL << IV_PM_EN_S) | 0x3;
write_csr(pcb->ia_match, CSR_IA_MATCH);
write_csr(pcb->iv_match, CSR_IV_MATCH);
pr_info("ia_match:%#lx iv_match:%#lx\n", pcb->ia_match, pcb->iv_match);
}
if (pcb->match_ctl & IDA_MATCH) {
pcb->ida_match |= (0x1UL << IDA_MATCH_EN_S) | 0x3;
pcb->ida_mask |= 0x3;
write_csr(pcb->ida_match, CSR_IDA_MATCH);
write_csr(pcb->ida_mask, CSR_IDA_MASK);
pr_info("ida_match:%#lx ida_mask:%#lx\n", pcb->ida_match, pcb->ida_mask);
}
}
#endif
struct pt_regs_offset {
const char *name;
int offset;
};
#define GPR_OFFSET_NAME(r) { \
.name = "r" #r, \
.offset = offsetof(struct pt_regs, regs[r]) \
}
#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[] = {
GPR_OFFSET_NAME(0),
GPR_OFFSET_NAME(1),
GPR_OFFSET_NAME(2),
GPR_OFFSET_NAME(3),
GPR_OFFSET_NAME(4),
GPR_OFFSET_NAME(5),
GPR_OFFSET_NAME(6),
GPR_OFFSET_NAME(7),
GPR_OFFSET_NAME(8),
GPR_OFFSET_NAME(9),
GPR_OFFSET_NAME(10),
GPR_OFFSET_NAME(11),
GPR_OFFSET_NAME(12),
GPR_OFFSET_NAME(13),
GPR_OFFSET_NAME(14),
GPR_OFFSET_NAME(15),
GPR_OFFSET_NAME(16),
GPR_OFFSET_NAME(17),
GPR_OFFSET_NAME(18),
GPR_OFFSET_NAME(19),
GPR_OFFSET_NAME(20),
GPR_OFFSET_NAME(21),
GPR_OFFSET_NAME(22),
GPR_OFFSET_NAME(23),
GPR_OFFSET_NAME(24),
GPR_OFFSET_NAME(25),
GPR_OFFSET_NAME(26),
GPR_OFFSET_NAME(27),
GPR_OFFSET_NAME(28),
GPR_OFFSET_NAME(29),
GPR_OFFSET_NAME(30),
REG_OFFSET_NAME(pc),
REG_OFFSET_NAME(ps),
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;
}