1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Copyright (C) 2009 Sunplus Core Technology Co., Ltd. |
4 | * Chen Liqin <liqin.chen@sunplusct.com> |
5 | * Lennox Wu <lennox.wu@sunplusct.com> |
6 | * Copyright (C) 2012 Regents of the University of California |
7 | * Copyright (C) 2017 SiFive |
8 | */ |
9 | |
10 | #include <linux/cpu.h> |
11 | #include <linux/kernel.h> |
12 | #include <linux/sched.h> |
13 | #include <linux/sched/debug.h> |
14 | #include <linux/sched/task_stack.h> |
15 | #include <linux/tick.h> |
16 | #include <linux/ptrace.h> |
17 | #include <linux/uaccess.h> |
18 | |
19 | #include <asm/unistd.h> |
20 | #include <asm/processor.h> |
21 | #include <asm/csr.h> |
22 | #include <asm/stacktrace.h> |
23 | #include <asm/string.h> |
24 | #include <asm/switch_to.h> |
25 | #include <asm/thread_info.h> |
26 | #include <asm/cpuidle.h> |
27 | #include <asm/vector.h> |
28 | #include <asm/cpufeature.h> |
29 | |
30 | #if defined(CONFIG_STACKPROTECTOR) && !defined(CONFIG_STACKPROTECTOR_PER_TASK) |
31 | #include <linux/stackprotector.h> |
32 | unsigned long __stack_chk_guard __read_mostly; |
33 | EXPORT_SYMBOL(__stack_chk_guard); |
34 | #endif |
35 | |
36 | extern asmlinkage void ret_from_fork(void); |
37 | |
38 | void noinstr arch_cpu_idle(void) |
39 | { |
40 | cpu_do_idle(); |
41 | } |
42 | |
43 | int set_unalign_ctl(struct task_struct *tsk, unsigned int val) |
44 | { |
45 | if (!unaligned_ctl_available()) |
46 | return -EINVAL; |
47 | |
48 | tsk->thread.align_ctl = val; |
49 | return 0; |
50 | } |
51 | |
52 | int get_unalign_ctl(struct task_struct *tsk, unsigned long adr) |
53 | { |
54 | if (!unaligned_ctl_available()) |
55 | return -EINVAL; |
56 | |
57 | return put_user(tsk->thread.align_ctl, (unsigned long __user *)adr); |
58 | } |
59 | |
60 | void __show_regs(struct pt_regs *regs) |
61 | { |
62 | show_regs_print_info(KERN_DEFAULT); |
63 | |
64 | if (!user_mode(regs)) { |
65 | pr_cont("epc : %pS\n" , (void *)regs->epc); |
66 | pr_cont(" ra : %pS\n" , (void *)regs->ra); |
67 | } |
68 | |
69 | pr_cont("epc : " REG_FMT " ra : " REG_FMT " sp : " REG_FMT "\n" , |
70 | regs->epc, regs->ra, regs->sp); |
71 | pr_cont(" gp : " REG_FMT " tp : " REG_FMT " t0 : " REG_FMT "\n" , |
72 | regs->gp, regs->tp, regs->t0); |
73 | pr_cont(" t1 : " REG_FMT " t2 : " REG_FMT " s0 : " REG_FMT "\n" , |
74 | regs->t1, regs->t2, regs->s0); |
75 | pr_cont(" s1 : " REG_FMT " a0 : " REG_FMT " a1 : " REG_FMT "\n" , |
76 | regs->s1, regs->a0, regs->a1); |
77 | pr_cont(" a2 : " REG_FMT " a3 : " REG_FMT " a4 : " REG_FMT "\n" , |
78 | regs->a2, regs->a3, regs->a4); |
79 | pr_cont(" a5 : " REG_FMT " a6 : " REG_FMT " a7 : " REG_FMT "\n" , |
80 | regs->a5, regs->a6, regs->a7); |
81 | pr_cont(" s2 : " REG_FMT " s3 : " REG_FMT " s4 : " REG_FMT "\n" , |
82 | regs->s2, regs->s3, regs->s4); |
83 | pr_cont(" s5 : " REG_FMT " s6 : " REG_FMT " s7 : " REG_FMT "\n" , |
84 | regs->s5, regs->s6, regs->s7); |
85 | pr_cont(" s8 : " REG_FMT " s9 : " REG_FMT " s10: " REG_FMT "\n" , |
86 | regs->s8, regs->s9, regs->s10); |
87 | pr_cont(" s11: " REG_FMT " t3 : " REG_FMT " t4 : " REG_FMT "\n" , |
88 | regs->s11, regs->t3, regs->t4); |
89 | pr_cont(" t5 : " REG_FMT " t6 : " REG_FMT "\n" , |
90 | regs->t5, regs->t6); |
91 | |
92 | pr_cont("status: " REG_FMT " badaddr: " REG_FMT " cause: " REG_FMT "\n" , |
93 | regs->status, regs->badaddr, regs->cause); |
94 | } |
95 | void show_regs(struct pt_regs *regs) |
96 | { |
97 | __show_regs(regs); |
98 | if (!user_mode(regs)) |
99 | dump_backtrace(regs, NULL, KERN_DEFAULT); |
100 | } |
101 | |
102 | #ifdef CONFIG_COMPAT |
103 | static bool compat_mode_supported __read_mostly; |
104 | |
105 | bool compat_elf_check_arch(Elf32_Ehdr *hdr) |
106 | { |
107 | return compat_mode_supported && |
108 | hdr->e_machine == EM_RISCV && |
109 | hdr->e_ident[EI_CLASS] == ELFCLASS32; |
110 | } |
111 | |
112 | static int __init compat_mode_detect(void) |
113 | { |
114 | unsigned long tmp = csr_read(CSR_STATUS); |
115 | |
116 | csr_write(CSR_STATUS, (tmp & ~SR_UXL) | SR_UXL_32); |
117 | compat_mode_supported = |
118 | (csr_read(CSR_STATUS) & SR_UXL) == SR_UXL_32; |
119 | |
120 | csr_write(CSR_STATUS, tmp); |
121 | |
122 | pr_info("riscv: ELF compat mode %s" , |
123 | compat_mode_supported ? "supported" : "unsupported" ); |
124 | |
125 | return 0; |
126 | } |
127 | early_initcall(compat_mode_detect); |
128 | #endif |
129 | |
130 | void start_thread(struct pt_regs *regs, unsigned long pc, |
131 | unsigned long sp) |
132 | { |
133 | regs->status = SR_PIE; |
134 | if (has_fpu()) { |
135 | regs->status |= SR_FS_INITIAL; |
136 | /* |
137 | * Restore the initial value to the FP register |
138 | * before starting the user program. |
139 | */ |
140 | fstate_restore(current, regs); |
141 | } |
142 | regs->epc = pc; |
143 | regs->sp = sp; |
144 | |
145 | #ifdef CONFIG_64BIT |
146 | regs->status &= ~SR_UXL; |
147 | |
148 | if (is_compat_task()) |
149 | regs->status |= SR_UXL_32; |
150 | else |
151 | regs->status |= SR_UXL_64; |
152 | #endif |
153 | } |
154 | |
155 | void flush_thread(void) |
156 | { |
157 | #ifdef CONFIG_FPU |
158 | /* |
159 | * Reset FPU state and context |
160 | * frm: round to nearest, ties to even (IEEE default) |
161 | * fflags: accrued exceptions cleared |
162 | */ |
163 | fstate_off(current, task_pt_regs(current)); |
164 | memset(¤t->thread.fstate, 0, sizeof(current->thread.fstate)); |
165 | #endif |
166 | #ifdef CONFIG_RISCV_ISA_V |
167 | /* Reset vector state */ |
168 | riscv_v_vstate_ctrl_init(current); |
169 | riscv_v_vstate_off(task_pt_regs(current)); |
170 | kfree(current->thread.vstate.datap); |
171 | memset(¤t->thread.vstate, 0, sizeof(struct __riscv_v_ext_state)); |
172 | clear_tsk_thread_flag(current, TIF_RISCV_V_DEFER_RESTORE); |
173 | #endif |
174 | } |
175 | |
176 | void arch_release_task_struct(struct task_struct *tsk) |
177 | { |
178 | /* Free the vector context of datap. */ |
179 | if (has_vector()) |
180 | riscv_v_thread_free(tsk); |
181 | } |
182 | |
183 | int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src) |
184 | { |
185 | fstate_save(src, task_pt_regs(src)); |
186 | *dst = *src; |
187 | /* clear entire V context, including datap for a new task */ |
188 | memset(&dst->thread.vstate, 0, sizeof(struct __riscv_v_ext_state)); |
189 | memset(&dst->thread.kernel_vstate, 0, sizeof(struct __riscv_v_ext_state)); |
190 | clear_tsk_thread_flag(tsk: dst, flag: TIF_RISCV_V_DEFER_RESTORE); |
191 | |
192 | return 0; |
193 | } |
194 | |
195 | int copy_thread(struct task_struct *p, const struct kernel_clone_args *args) |
196 | { |
197 | unsigned long clone_flags = args->flags; |
198 | unsigned long usp = args->stack; |
199 | unsigned long tls = args->tls; |
200 | struct pt_regs *childregs = task_pt_regs(p); |
201 | |
202 | memset(&p->thread.s, 0, sizeof(p->thread.s)); |
203 | |
204 | /* p->thread holds context to be restored by __switch_to() */ |
205 | if (unlikely(args->fn)) { |
206 | /* Kernel thread */ |
207 | memset(childregs, 0, sizeof(struct pt_regs)); |
208 | /* Supervisor/Machine, irqs on: */ |
209 | childregs->status = SR_PP | SR_PIE; |
210 | |
211 | p->thread.s[0] = (unsigned long)args->fn; |
212 | p->thread.s[1] = (unsigned long)args->fn_arg; |
213 | } else { |
214 | *childregs = *(current_pt_regs()); |
215 | /* Turn off status.VS */ |
216 | riscv_v_vstate_off(childregs); |
217 | if (usp) /* User fork */ |
218 | childregs->sp = usp; |
219 | if (clone_flags & CLONE_SETTLS) |
220 | childregs->tp = tls; |
221 | childregs->a0 = 0; /* Return value of fork() */ |
222 | p->thread.s[0] = 0; |
223 | } |
224 | p->thread.riscv_v_flags = 0; |
225 | if (has_vector()) |
226 | riscv_v_thread_alloc(p); |
227 | p->thread.ra = (unsigned long)ret_from_fork; |
228 | p->thread.sp = (unsigned long)childregs; /* kernel sp */ |
229 | return 0; |
230 | } |
231 | |
232 | void __init arch_task_cache_init(void) |
233 | { |
234 | riscv_v_setup_ctx_cache(); |
235 | } |
236 | |