RISC-V: Support MODULE_SECTIONS mechanism on RV32
[sfrench/cifs-2.6.git] / arch / riscv / kernel / ptrace.c
1 /*
2  * Copyright 2010 Tilera Corporation. All Rights Reserved.
3  * Copyright 2015 Regents of the University of California
4  * Copyright 2017 SiFive
5  *
6  *   This program is free software; you can redistribute it and/or
7  *   modify it under the terms of the GNU General Public License
8  *   as published by the Free Software Foundation, version 2.
9  *
10  *   This program is distributed in the hope that it will be useful,
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *   GNU General Public License for more details.
14  *
15  * Copied from arch/tile/kernel/ptrace.c
16  */
17
18 #include <asm/ptrace.h>
19 #include <asm/syscall.h>
20 #include <asm/thread_info.h>
21 #include <linux/ptrace.h>
22 #include <linux/elf.h>
23 #include <linux/regset.h>
24 #include <linux/sched.h>
25 #include <linux/sched/task_stack.h>
26 #include <linux/tracehook.h>
27 #include <trace/events/syscalls.h>
28
29 enum riscv_regset {
30         REGSET_X,
31 #ifdef CONFIG_FPU
32         REGSET_F,
33 #endif
34 };
35
36 static int riscv_gpr_get(struct task_struct *target,
37                          const struct user_regset *regset,
38                          unsigned int pos, unsigned int count,
39                          void *kbuf, void __user *ubuf)
40 {
41         struct pt_regs *regs;
42
43         regs = task_pt_regs(target);
44         return user_regset_copyout(&pos, &count, &kbuf, &ubuf, regs, 0, -1);
45 }
46
47 static int riscv_gpr_set(struct task_struct *target,
48                          const struct user_regset *regset,
49                          unsigned int pos, unsigned int count,
50                          const void *kbuf, const void __user *ubuf)
51 {
52         int ret;
53         struct pt_regs *regs;
54
55         regs = task_pt_regs(target);
56         ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, regs, 0, -1);
57         return ret;
58 }
59
60 #ifdef CONFIG_FPU
61 static int riscv_fpr_get(struct task_struct *target,
62                          const struct user_regset *regset,
63                          unsigned int pos, unsigned int count,
64                          void *kbuf, void __user *ubuf)
65 {
66         int ret;
67         struct __riscv_d_ext_state *fstate = &target->thread.fstate;
68
69         ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, fstate, 0,
70                                   offsetof(struct __riscv_d_ext_state, fcsr));
71         if (!ret) {
72                 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, fstate, 0,
73                                           offsetof(struct __riscv_d_ext_state, fcsr) +
74                                           sizeof(fstate->fcsr));
75         }
76
77         return ret;
78 }
79
80 static int riscv_fpr_set(struct task_struct *target,
81                          const struct user_regset *regset,
82                          unsigned int pos, unsigned int count,
83                          const void *kbuf, const void __user *ubuf)
84 {
85         int ret;
86         struct __riscv_d_ext_state *fstate = &target->thread.fstate;
87
88         ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, fstate, 0,
89                                  offsetof(struct __riscv_d_ext_state, fcsr));
90         if (!ret) {
91                 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, fstate, 0,
92                                          offsetof(struct __riscv_d_ext_state, fcsr) +
93                                          sizeof(fstate->fcsr));
94         }
95
96         return ret;
97 }
98 #endif
99
100 static const struct user_regset riscv_user_regset[] = {
101         [REGSET_X] = {
102                 .core_note_type = NT_PRSTATUS,
103                 .n = ELF_NGREG,
104                 .size = sizeof(elf_greg_t),
105                 .align = sizeof(elf_greg_t),
106                 .get = &riscv_gpr_get,
107                 .set = &riscv_gpr_set,
108         },
109 #ifdef CONFIG_FPU
110         [REGSET_F] = {
111                 .core_note_type = NT_PRFPREG,
112                 .n = ELF_NFPREG,
113                 .size = sizeof(elf_fpreg_t),
114                 .align = sizeof(elf_fpreg_t),
115                 .get = &riscv_fpr_get,
116                 .set = &riscv_fpr_set,
117         },
118 #endif
119 };
120
121 static const struct user_regset_view riscv_user_native_view = {
122         .name = "riscv",
123         .e_machine = EM_RISCV,
124         .regsets = riscv_user_regset,
125         .n = ARRAY_SIZE(riscv_user_regset),
126 };
127
128 const struct user_regset_view *task_user_regset_view(struct task_struct *task)
129 {
130         return &riscv_user_native_view;
131 }
132
133 void ptrace_disable(struct task_struct *child)
134 {
135         clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
136 }
137
138 long arch_ptrace(struct task_struct *child, long request,
139                  unsigned long addr, unsigned long data)
140 {
141         long ret = -EIO;
142
143         switch (request) {
144         default:
145                 ret = ptrace_request(child, request, addr, data);
146                 break;
147         }
148
149         return ret;
150 }
151
152 /*
153  * Allows PTRACE_SYSCALL to work.  These are called from entry.S in
154  * {handle,ret_from}_syscall.
155  */
156 void do_syscall_trace_enter(struct pt_regs *regs)
157 {
158         if (test_thread_flag(TIF_SYSCALL_TRACE))
159                 if (tracehook_report_syscall_entry(regs))
160                         syscall_set_nr(current, regs, -1);
161
162 #ifdef CONFIG_HAVE_SYSCALL_TRACEPOINTS
163         if (test_thread_flag(TIF_SYSCALL_TRACEPOINT))
164                 trace_sys_enter(regs, syscall_get_nr(current, regs));
165 #endif
166 }
167
168 void do_syscall_trace_exit(struct pt_regs *regs)
169 {
170         if (test_thread_flag(TIF_SYSCALL_TRACE))
171                 tracehook_report_syscall_exit(regs, 0);
172
173 #ifdef CONFIG_HAVE_SYSCALL_TRACEPOINTS
174         if (test_thread_flag(TIF_SYSCALL_TRACEPOINT))
175                 trace_sys_exit(regs, regs->regs[0]);
176 #endif
177 }