diff options
author | Palmer Dabbelt <palmer@dabbelt.com> | 2017-07-10 18:07:09 -0700 |
---|---|---|
committer | Palmer Dabbelt <palmer@dabbelt.com> | 2017-09-26 15:26:48 -0700 |
commit | e2c0cdfba7f69925afc92b20cd9835d81e11a4f1 (patch) | |
tree | 2c501638321da451423b385652183d60da02db5c /arch/riscv/kernel/ptrace.c | |
parent | 07037db5d479f90377c998259a4f9a469c404edf (diff) | |
download | linux-e2c0cdfba7f69925afc92b20cd9835d81e11a4f1.tar.gz |
RISC-V: User-facing API
This patch contains code that is in some way visible to the user:
including via system calls, the VDSO, module loading and signal
handling. It also contains some generic code that is ABI visible.
Signed-off-by: Palmer Dabbelt <palmer@dabbelt.com>
Diffstat (limited to 'arch/riscv/kernel/ptrace.c')
-rw-r--r-- | arch/riscv/kernel/ptrace.c | 125 |
1 files changed, 125 insertions, 0 deletions
diff --git a/arch/riscv/kernel/ptrace.c b/arch/riscv/kernel/ptrace.c new file mode 100644 index 000000000000..ba3e80712797 --- /dev/null +++ b/arch/riscv/kernel/ptrace.c @@ -0,0 +1,125 @@ +/* + * Copyright 2010 Tilera Corporation. All Rights Reserved. + * Copyright 2015 Regents of the University of California + * Copyright 2017 SiFive + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, version 2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Copied from arch/tile/kernel/ptrace.c + */ + +#include <asm/ptrace.h> +#include <asm/syscall.h> +#include <asm/thread_info.h> +#include <linux/ptrace.h> +#include <linux/elf.h> +#include <linux/regset.h> +#include <linux/sched.h> +#include <linux/sched/task_stack.h> +#include <linux/tracehook.h> +#include <trace/events/syscalls.h> + +enum riscv_regset { + REGSET_X, +}; + +static int riscv_gpr_get(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + void *kbuf, void __user *ubuf) +{ + struct pt_regs *regs; + + regs = task_pt_regs(target); + return user_regset_copyout(&pos, &count, &kbuf, &ubuf, regs, 0, -1); +} + +static int riscv_gpr_set(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) +{ + int ret; + struct pt_regs *regs; + + regs = task_pt_regs(target); + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, ®s, 0, -1); + return ret; +} + + +static const struct user_regset riscv_user_regset[] = { + [REGSET_X] = { + .core_note_type = NT_PRSTATUS, + .n = ELF_NGREG, + .size = sizeof(elf_greg_t), + .align = sizeof(elf_greg_t), + .get = &riscv_gpr_get, + .set = &riscv_gpr_set, + }, +}; + +static const struct user_regset_view riscv_user_native_view = { + .name = "riscv", + .e_machine = EM_RISCV, + .regsets = riscv_user_regset, + .n = ARRAY_SIZE(riscv_user_regset), +}; + +const struct user_regset_view *task_user_regset_view(struct task_struct *task) +{ + return &riscv_user_native_view; +} + +void ptrace_disable(struct task_struct *child) +{ + clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); +} + +long arch_ptrace(struct task_struct *child, long request, + unsigned long addr, unsigned long data) +{ + long ret = -EIO; + + switch (request) { + default: + ret = ptrace_request(child, request, addr, data); + break; + } + + return ret; +} + +/* + * Allows PTRACE_SYSCALL to work. These are called from entry.S in + * {handle,ret_from}_syscall. + */ +void do_syscall_trace_enter(struct pt_regs *regs) +{ + if (test_thread_flag(TIF_SYSCALL_TRACE)) + if (tracehook_report_syscall_entry(regs)) + syscall_set_nr(current, regs, -1); + +#ifdef CONFIG_HAVE_SYSCALL_TRACEPOINTS + if (test_thread_flag(TIF_SYSCALL_TRACEPOINT)) + trace_sys_enter(regs, syscall_get_nr(current, regs)); +#endif +} + +void do_syscall_trace_exit(struct pt_regs *regs) +{ + if (test_thread_flag(TIF_SYSCALL_TRACE)) + tracehook_report_syscall_exit(regs, 0); + +#ifdef CONFIG_HAVE_SYSCALL_TRACEPOINTS + if (test_thread_flag(TIF_SYSCALL_TRACEPOINT)) + trace_sys_exit(regs, regs->regs[0]); +#endif +} |