Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 | // SPDX-License-Identifier: GPL-2.0-only /* * Copyright 2010 Tilera Corporation. All Rights Reserved. * Copyright 2015 Regents of the University of California * Copyright 2017 SiFive * * Copied from arch/tile/kernel/ptrace.c */ #include <asm/ptrace.h> #include <asm/syscall.h> #include <asm/thread_info.h> #include <linux/audit.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> #define CREATE_TRACE_POINTS #include <trace/events/syscalls.h> enum riscv_regset { REGSET_X, #ifdef CONFIG_FPU REGSET_F, #endif }; 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, regs, 0, -1); return ret; } #ifdef CONFIG_FPU static int riscv_fpr_get(struct task_struct *target, const struct user_regset *regset, unsigned int pos, unsigned int count, void *kbuf, void __user *ubuf) { int ret; struct __riscv_d_ext_state *fstate = &target->thread.fstate; ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, fstate, 0, offsetof(struct __riscv_d_ext_state, fcsr)); if (!ret) { ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, fstate, 0, offsetof(struct __riscv_d_ext_state, fcsr) + sizeof(fstate->fcsr)); } return ret; } static int riscv_fpr_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 __riscv_d_ext_state *fstate = &target->thread.fstate; ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, fstate, 0, offsetof(struct __riscv_d_ext_state, fcsr)); if (!ret) { ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, fstate, 0, offsetof(struct __riscv_d_ext_state, fcsr) + sizeof(fstate->fcsr)); } return ret; } #endif 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, }, #ifdef CONFIG_FPU [REGSET_F] = { .core_note_type = NT_PRFPREG, .n = ELF_NFPREG, .size = sizeof(elf_fpreg_t), .align = sizeof(elf_fpreg_t), .get = &riscv_fpr_get, .set = &riscv_fpr_set, }, #endif }; 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. */ __visible int do_syscall_trace_enter(struct pt_regs *regs) { if (test_thread_flag(TIF_SYSCALL_TRACE)) if (tracehook_report_syscall_entry(regs)) return -1; /* * Do the secure computing after ptrace; failures should be fast. * If this fails we might have return value in a0 from seccomp * (via SECCOMP_RET_ERRNO/TRACE). */ if (secure_computing() == -1) return -1; #ifdef CONFIG_HAVE_SYSCALL_TRACEPOINTS if (test_thread_flag(TIF_SYSCALL_TRACEPOINT)) trace_sys_enter(regs, syscall_get_nr(current, regs)); #endif audit_syscall_entry(regs->a7, regs->a0, regs->a1, regs->a2, regs->a3); return 0; } __visible void do_syscall_trace_exit(struct pt_regs *regs) { audit_syscall_exit(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_return_value(regs)); #endif } |