FILE
ptrace.c
File preview with syntax highlighting.
linux-6.19.14/arch/riscv/kernel/ptrace.c
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * Copyright 2010 Tilera Corporation. All Rights Reserved. 4 * Copyright 2015 Regents of the University of California 5 * Copyright 2017 SiFive 6 * 7 * Copied from arch/tile/kernel/ptrace.c 8 */ 9 10#include <asm/vector.h> 11#include <asm/ptrace.h> 12#include <asm/syscall.h> 13#include <asm/thread_info.h> 14#include <asm/switch_to.h> 15#include <linux/audit.h> 16#include <linux/compat.h> 17#include <linux/ptrace.h> 18#include <linux/elf.h> 19#include <linux/regset.h> 20#include <linux/sched.h> 21#include <linux/sched/task_stack.h> 22 23enum riscv_regset { 24 REGSET_X, 25#ifdef CONFIG_FPU 26 REGSET_F, 27#endif 28#ifdef CONFIG_RISCV_ISA_V 29 REGSET_V, 30#endif 31#ifdef CONFIG_RISCV_ISA_SUPM 32 REGSET_TAGGED_ADDR_CTRL, 33#endif 34}; 35 36static int riscv_gpr_get(struct task_struct *target, 37 const struct user_regset *regset, 38 struct membuf to) 39{ 40 return membuf_write(&to, task_pt_regs(target), 41 sizeof(struct user_regs_struct)); 42} 43 44static int riscv_gpr_set(struct task_struct *target, 45 const struct user_regset *regset, 46 unsigned int pos, unsigned int count, 47 const void *kbuf, const void __user *ubuf) 48{ 49 struct pt_regs *regs; 50 51 regs = task_pt_regs(target); 52 return user_regset_copyin(&pos, &count, &kbuf, &ubuf, regs, 0, -1); 53} 54 55#ifdef CONFIG_FPU 56static int riscv_fpr_get(struct task_struct *target, 57 const struct user_regset *regset, 58 struct membuf to) 59{ 60 struct __riscv_d_ext_state *fstate = &target->thread.fstate; 61 62 if (target == current) 63 fstate_save(current, task_pt_regs(current)); 64 65 membuf_write(&to, fstate, offsetof(struct __riscv_d_ext_state, fcsr)); 66 membuf_store(&to, fstate->fcsr); 67 return membuf_zero(&to, 4); // explicitly pad 68} 69 70static int riscv_fpr_set(struct task_struct *target, 71 const struct user_regset *regset, 72 unsigned int pos, unsigned int count, 73 const void *kbuf, const void __user *ubuf) 74{ 75 int ret; 76 struct __riscv_d_ext_state *fstate = &target->thread.fstate; 77 78 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, fstate, 0, 79 offsetof(struct __riscv_d_ext_state, fcsr)); 80 if (!ret) { 81 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, fstate, 0, 82 offsetof(struct __riscv_d_ext_state, fcsr) + 83 sizeof(fstate->fcsr)); 84 } 85 86 return ret; 87} 88#endif 89 90#ifdef CONFIG_RISCV_ISA_V 91static int riscv_vr_get(struct task_struct *target, 92 const struct user_regset *regset, 93 struct membuf to) 94{ 95 struct __riscv_v_ext_state *vstate = &target->thread.vstate; 96 struct __riscv_v_regset_state ptrace_vstate; 97 98 if (!riscv_v_vstate_query(task_pt_regs(target))) 99 return -EINVAL; 100 101 /* 102 * Ensure the vector registers have been saved to the memory before 103 * copying them to membuf. 104 */ 105 if (target == current) { 106 get_cpu_vector_context(); 107 riscv_v_vstate_save(¤t->thread.vstate, task_pt_regs(current)); 108 put_cpu_vector_context(); 109 } 110 111 ptrace_vstate.vstart = vstate->vstart; 112 ptrace_vstate.vl = vstate->vl; 113 ptrace_vstate.vtype = vstate->vtype; 114 ptrace_vstate.vcsr = vstate->vcsr; 115 ptrace_vstate.vlenb = vstate->vlenb; 116 117 /* Copy vector header from vstate. */ 118 membuf_write(&to, &ptrace_vstate, sizeof(struct __riscv_v_regset_state)); 119 120 /* Copy all the vector registers from vstate. */ 121 return membuf_write(&to, vstate->datap, riscv_v_vsize); 122} 123 124static int riscv_vr_set(struct task_struct *target, 125 const struct user_regset *regset, 126 unsigned int pos, unsigned int count, 127 const void *kbuf, const void __user *ubuf) 128{ 129 int ret; 130 struct __riscv_v_ext_state *vstate = &target->thread.vstate; 131 struct __riscv_v_regset_state ptrace_vstate; 132 133 if (!riscv_v_vstate_query(task_pt_regs(target))) 134 return -EINVAL; 135 136 /* Copy rest of the vstate except datap */ 137 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &ptrace_vstate, 0, 138 sizeof(struct __riscv_v_regset_state)); 139 if (unlikely(ret)) 140 return ret; 141 142 if (vstate->vlenb != ptrace_vstate.vlenb) 143 return -EINVAL; 144 145 vstate->vstart = ptrace_vstate.vstart; 146 vstate->vl = ptrace_vstate.vl; 147 vstate->vtype = ptrace_vstate.vtype; 148 vstate->vcsr = ptrace_vstate.vcsr; 149 150 /* Copy all the vector registers. */ 151 pos = 0; 152 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, vstate->datap, 153 0, riscv_v_vsize); 154 return ret; 155} 156 157static int riscv_vr_active(struct task_struct *target, const struct user_regset *regset) 158{ 159 if (!(has_vector() || has_xtheadvector())) 160 return -ENODEV; 161 162 if (!riscv_v_vstate_query(task_pt_regs(target))) 163 return 0; 164 165 return regset->n; 166} 167#endif 168 169#ifdef CONFIG_RISCV_ISA_SUPM 170static int tagged_addr_ctrl_get(struct task_struct *target, 171 const struct user_regset *regset, 172 struct membuf to) 173{ 174 long ctrl = get_tagged_addr_ctrl(target); 175 176 if (IS_ERR_VALUE(ctrl)) 177 return ctrl; 178 179 return membuf_write(&to, &ctrl, sizeof(ctrl)); 180} 181 182static int tagged_addr_ctrl_set(struct task_struct *target, 183 const struct user_regset *regset, 184 unsigned int pos, unsigned int count, 185 const void *kbuf, const void __user *ubuf) 186{ 187 int ret; 188 long ctrl; 189 190 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &ctrl, 0, -1); 191 if (ret) 192 return ret; 193 194 return set_tagged_addr_ctrl(target, ctrl); 195} 196#endif 197 198static struct user_regset riscv_user_regset[] __ro_after_init = { 199 [REGSET_X] = { 200 USER_REGSET_NOTE_TYPE(PRSTATUS), 201 .n = ELF_NGREG, 202 .size = sizeof(elf_greg_t), 203 .align = sizeof(elf_greg_t), 204 .regset_get = riscv_gpr_get, 205 .set = riscv_gpr_set, 206 }, 207#ifdef CONFIG_FPU 208 [REGSET_F] = { 209 USER_REGSET_NOTE_TYPE(PRFPREG), 210 .n = ELF_NFPREG, 211 .size = sizeof(elf_fpreg_t), 212 .align = sizeof(elf_fpreg_t), 213 .regset_get = riscv_fpr_get, 214 .set = riscv_fpr_set, 215 }, 216#endif 217#ifdef CONFIG_RISCV_ISA_V 218 [REGSET_V] = { 219 USER_REGSET_NOTE_TYPE(RISCV_VECTOR), 220 .align = 16, 221 .size = sizeof(__u32), 222 .regset_get = riscv_vr_get, 223 .set = riscv_vr_set, 224 .active = riscv_vr_active, 225 }, 226#endif 227#ifdef CONFIG_RISCV_ISA_SUPM 228 [REGSET_TAGGED_ADDR_CTRL] = { 229 USER_REGSET_NOTE_TYPE(RISCV_TAGGED_ADDR_CTRL), 230 .n = 1, 231 .size = sizeof(long), 232 .align = sizeof(long), 233 .regset_get = tagged_addr_ctrl_get, 234 .set = tagged_addr_ctrl_set, 235 }, 236#endif 237}; 238 239static const struct user_regset_view riscv_user_native_view = { 240 .name = "riscv", 241 .e_machine = EM_RISCV, 242 .regsets = riscv_user_regset, 243 .n = ARRAY_SIZE(riscv_user_regset), 244}; 245 246#ifdef CONFIG_RISCV_ISA_V 247void __init update_regset_vector_info(unsigned long size) 248{ 249 riscv_user_regset[REGSET_V].n = (size + sizeof(struct __riscv_v_regset_state)) / 250 sizeof(__u32); 251} 252#endif 253 254struct pt_regs_offset { 255 const char *name; 256 int offset; 257}; 258 259#define REG_OFFSET_NAME(r) {.name = #r, .offset = offsetof(struct pt_regs, r)} 260#define REG_OFFSET_END {.name = NULL, .offset = 0} 261 262static const struct pt_regs_offset regoffset_table[] = { 263 REG_OFFSET_NAME(epc), 264 REG_OFFSET_NAME(ra), 265 REG_OFFSET_NAME(sp), 266 REG_OFFSET_NAME(gp), 267 REG_OFFSET_NAME(tp), 268 REG_OFFSET_NAME(t0), 269 REG_OFFSET_NAME(t1), 270 REG_OFFSET_NAME(t2), 271 REG_OFFSET_NAME(s0), 272 REG_OFFSET_NAME(s1), 273 REG_OFFSET_NAME(a0), 274 REG_OFFSET_NAME(a1), 275 REG_OFFSET_NAME(a2), 276 REG_OFFSET_NAME(a3), 277 REG_OFFSET_NAME(a4), 278 REG_OFFSET_NAME(a5), 279 REG_OFFSET_NAME(a6), 280 REG_OFFSET_NAME(a7), 281 REG_OFFSET_NAME(s2), 282 REG_OFFSET_NAME(s3), 283 REG_OFFSET_NAME(s4), 284 REG_OFFSET_NAME(s5), 285 REG_OFFSET_NAME(s6), 286 REG_OFFSET_NAME(s7), 287 REG_OFFSET_NAME(s8), 288 REG_OFFSET_NAME(s9), 289 REG_OFFSET_NAME(s10), 290 REG_OFFSET_NAME(s11), 291 REG_OFFSET_NAME(t3), 292 REG_OFFSET_NAME(t4), 293 REG_OFFSET_NAME(t5), 294 REG_OFFSET_NAME(t6), 295 REG_OFFSET_NAME(status), 296 REG_OFFSET_NAME(badaddr), 297 REG_OFFSET_NAME(cause), 298 REG_OFFSET_NAME(orig_a0), 299 REG_OFFSET_END, 300}; 301 302/** 303 * regs_query_register_offset() - query register offset from its name 304 * @name: the name of a register 305 * 306 * regs_query_register_offset() returns the offset of a register in struct 307 * pt_regs from its name. If the name is invalid, this returns -EINVAL; 308 */ 309int regs_query_register_offset(const char *name) 310{ 311 const struct pt_regs_offset *roff; 312 313 for (roff = regoffset_table; roff->name != NULL; roff++) 314 if (!strcmp(roff->name, name)) 315 return roff->offset; 316 return -EINVAL; 317} 318 319/** 320 * regs_within_kernel_stack() - check the address in the stack 321 * @regs: pt_regs which contains kernel stack pointer. 322 * @addr: address which is checked. 323 * 324 * regs_within_kernel_stack() checks @addr is within the kernel stack page(s). 325 * If @addr is within the kernel stack, it returns true. If not, returns false. 326 */ 327static bool regs_within_kernel_stack(struct pt_regs *regs, unsigned long addr) 328{ 329 return (addr & ~(THREAD_SIZE - 1)) == 330 (kernel_stack_pointer(regs) & ~(THREAD_SIZE - 1)); 331} 332 333/** 334 * regs_get_kernel_stack_nth() - get Nth entry of the stack 335 * @regs: pt_regs which contains kernel stack pointer. 336 * @n: stack entry number. 337 * 338 * regs_get_kernel_stack_nth() returns @n th entry of the kernel stack which 339 * is specified by @regs. If the @n th entry is NOT in the kernel stack, 340 * this returns 0. 341 */ 342unsigned long regs_get_kernel_stack_nth(struct pt_regs *regs, unsigned int n) 343{ 344 unsigned long *addr = (unsigned long *)kernel_stack_pointer(regs); 345 346 addr += n; 347 if (regs_within_kernel_stack(regs, (unsigned long)addr)) 348 return *addr; 349 else 350 return 0; 351} 352 353void ptrace_disable(struct task_struct *child) 354{ 355} 356 357long arch_ptrace(struct task_struct *child, long request, 358 unsigned long addr, unsigned long data) 359{ 360 long ret = -EIO; 361 362 switch (request) { 363 default: 364 ret = ptrace_request(child, request, addr, data); 365 break; 366 } 367 368 return ret; 369} 370 371#ifdef CONFIG_COMPAT 372static int compat_riscv_gpr_get(struct task_struct *target, 373 const struct user_regset *regset, 374 struct membuf to) 375{ 376 struct compat_user_regs_struct cregs; 377 378 regs_to_cregs(&cregs, task_pt_regs(target)); 379 380 return membuf_write(&to, &cregs, 381 sizeof(struct compat_user_regs_struct)); 382} 383 384static int compat_riscv_gpr_set(struct task_struct *target, 385 const struct user_regset *regset, 386 unsigned int pos, unsigned int count, 387 const void *kbuf, const void __user *ubuf) 388{ 389 int ret; 390 struct compat_user_regs_struct cregs; 391 392 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &cregs, 0, -1); 393 394 cregs_to_regs(&cregs, task_pt_regs(target)); 395 396 return ret; 397} 398 399static const struct user_regset compat_riscv_user_regset[] = { 400 [REGSET_X] = { 401 USER_REGSET_NOTE_TYPE(PRSTATUS), 402 .n = ELF_NGREG, 403 .size = sizeof(compat_elf_greg_t), 404 .align = sizeof(compat_elf_greg_t), 405 .regset_get = compat_riscv_gpr_get, 406 .set = compat_riscv_gpr_set, 407 }, 408#ifdef CONFIG_FPU 409 [REGSET_F] = { 410 USER_REGSET_NOTE_TYPE(PRFPREG), 411 .n = ELF_NFPREG, 412 .size = sizeof(elf_fpreg_t), 413 .align = sizeof(elf_fpreg_t), 414 .regset_get = riscv_fpr_get, 415 .set = riscv_fpr_set, 416 }, 417#endif 418}; 419 420static const struct user_regset_view compat_riscv_user_native_view = { 421 .name = "riscv", 422 .e_machine = EM_RISCV, 423 .regsets = compat_riscv_user_regset, 424 .n = ARRAY_SIZE(compat_riscv_user_regset), 425}; 426 427long compat_arch_ptrace(struct task_struct *child, compat_long_t request, 428 compat_ulong_t caddr, compat_ulong_t cdata) 429{ 430 long ret = -EIO; 431 432 switch (request) { 433 default: 434 ret = compat_ptrace_request(child, request, caddr, cdata); 435 break; 436 } 437 438 return ret; 439} 440#else 441static const struct user_regset_view compat_riscv_user_native_view = {}; 442#endif /* CONFIG_COMPAT */ 443 444const struct user_regset_view *task_user_regset_view(struct task_struct *task) 445{ 446 if (is_compat_thread(&task->thread_info)) 447 return &compat_riscv_user_native_view; 448 else 449 return &riscv_user_native_view; 450}
~/
Read-only file preview.