1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * arch/sh/kernel/process.c |
4 | * |
5 | * This file handles the architecture-dependent parts of process handling.. |
6 | * |
7 | * Copyright (C) 1995 Linus Torvalds |
8 | * |
9 | * SuperH version: Copyright (C) 1999, 2000 Niibe Yutaka & Kaz Kojima |
10 | * Copyright (C) 2006 Lineo Solutions Inc. support SH4A UBC |
11 | * Copyright (C) 2002 - 2008 Paul Mundt |
12 | */ |
13 | #include <linux/module.h> |
14 | #include <linux/mm.h> |
15 | #include <linux/sched/debug.h> |
16 | #include <linux/sched/task.h> |
17 | #include <linux/sched/task_stack.h> |
18 | #include <linux/slab.h> |
19 | #include <linux/elfcore.h> |
20 | #include <linux/fs.h> |
21 | #include <linux/ftrace.h> |
22 | #include <linux/hw_breakpoint.h> |
23 | #include <linux/prefetch.h> |
24 | #include <linux/stackprotector.h> |
25 | #include <linux/uaccess.h> |
26 | #include <asm/mmu_context.h> |
27 | #include <asm/fpu.h> |
28 | #include <asm/syscalls.h> |
29 | #include <asm/switch_to.h> |
30 | |
31 | void show_regs(struct pt_regs * regs) |
32 | { |
33 | pr_info("\n" ); |
34 | show_regs_print_info(KERN_DEFAULT); |
35 | |
36 | pr_info("PC is at %pS\n" , (void *)instruction_pointer(regs)); |
37 | pr_info("PR is at %pS\n" , (void *)regs->pr); |
38 | |
39 | pr_info("PC : %08lx SP : %08lx SR : %08lx " , regs->pc, |
40 | regs->regs[15], regs->sr); |
41 | #ifdef CONFIG_MMU |
42 | pr_cont("TEA : %08x\n" , __raw_readl(MMU_TEA)); |
43 | #else |
44 | pr_cont("\n" ); |
45 | #endif |
46 | |
47 | pr_info("R0 : %08lx R1 : %08lx R2 : %08lx R3 : %08lx\n" , |
48 | regs->regs[0], regs->regs[1], regs->regs[2], regs->regs[3]); |
49 | pr_info("R4 : %08lx R5 : %08lx R6 : %08lx R7 : %08lx\n" , |
50 | regs->regs[4], regs->regs[5], regs->regs[6], regs->regs[7]); |
51 | pr_info("R8 : %08lx R9 : %08lx R10 : %08lx R11 : %08lx\n" , |
52 | regs->regs[8], regs->regs[9], regs->regs[10], regs->regs[11]); |
53 | pr_info("R12 : %08lx R13 : %08lx R14 : %08lx\n" , |
54 | regs->regs[12], regs->regs[13], regs->regs[14]); |
55 | pr_info("MACH: %08lx MACL: %08lx GBR : %08lx PR : %08lx\n" , |
56 | regs->mach, regs->macl, regs->gbr, regs->pr); |
57 | |
58 | show_trace(NULL, (unsigned long *)regs->regs[15], regs, KERN_DEFAULT); |
59 | show_code(regs); |
60 | } |
61 | |
62 | void start_thread(struct pt_regs *regs, unsigned long new_pc, |
63 | unsigned long new_sp) |
64 | { |
65 | regs->pr = 0; |
66 | regs->sr = SR_FD; |
67 | regs->pc = new_pc; |
68 | regs->regs[15] = new_sp; |
69 | |
70 | free_thread_xstate(current); |
71 | } |
72 | EXPORT_SYMBOL(start_thread); |
73 | |
74 | void flush_thread(void) |
75 | { |
76 | struct task_struct *tsk = current; |
77 | |
78 | flush_ptrace_hw_breakpoint(tsk); |
79 | |
80 | #if defined(CONFIG_SH_FPU) |
81 | /* Forget lazy FPU state */ |
82 | clear_fpu(tsk, task_pt_regs(tsk)); |
83 | clear_used_math(); |
84 | #endif |
85 | } |
86 | |
87 | asmlinkage void ret_from_fork(void); |
88 | asmlinkage void ret_from_kernel_thread(void); |
89 | |
90 | int copy_thread(struct task_struct *p, const struct kernel_clone_args *args) |
91 | { |
92 | unsigned long clone_flags = args->flags; |
93 | unsigned long usp = args->stack; |
94 | unsigned long tls = args->tls; |
95 | struct thread_info *ti = task_thread_info(p); |
96 | struct pt_regs *childregs; |
97 | |
98 | #if defined(CONFIG_SH_DSP) |
99 | struct task_struct *tsk = current; |
100 | |
101 | if (is_dsp_enabled(tsk)) { |
102 | /* We can use the __save_dsp or just copy the struct: |
103 | * __save_dsp(p); |
104 | * p->thread.dsp_status.status |= SR_DSP |
105 | */ |
106 | p->thread.dsp_status = tsk->thread.dsp_status; |
107 | } |
108 | #endif |
109 | |
110 | memset(p->thread.ptrace_bps, 0, sizeof(p->thread.ptrace_bps)); |
111 | |
112 | childregs = task_pt_regs(p); |
113 | p->thread.sp = (unsigned long) childregs; |
114 | if (unlikely(args->fn)) { |
115 | memset(childregs, 0, sizeof(struct pt_regs)); |
116 | p->thread.pc = (unsigned long) ret_from_kernel_thread; |
117 | childregs->regs[4] = (unsigned long) args->fn_arg; |
118 | childregs->regs[5] = (unsigned long) args->fn; |
119 | childregs->sr = SR_MD; |
120 | #if defined(CONFIG_SH_FPU) |
121 | childregs->sr |= SR_FD; |
122 | #endif |
123 | ti->status &= ~TS_USEDFPU; |
124 | p->thread.fpu_counter = 0; |
125 | return 0; |
126 | } |
127 | *childregs = *current_pt_regs(); |
128 | |
129 | if (usp) |
130 | childregs->regs[15] = usp; |
131 | |
132 | if (clone_flags & CLONE_SETTLS) |
133 | childregs->gbr = tls; |
134 | |
135 | childregs->regs[0] = 0; /* Set return value for child */ |
136 | p->thread.pc = (unsigned long) ret_from_fork; |
137 | return 0; |
138 | } |
139 | |
140 | /* |
141 | * switch_to(x,y) should switch tasks from x to y. |
142 | * |
143 | */ |
144 | __notrace_funcgraph struct task_struct * |
145 | __switch_to(struct task_struct *prev, struct task_struct *next) |
146 | { |
147 | struct thread_struct *next_t = &next->thread; |
148 | |
149 | #if defined(CONFIG_STACKPROTECTOR) && !defined(CONFIG_SMP) |
150 | __stack_chk_guard = next->stack_canary; |
151 | #endif |
152 | |
153 | unlazy_fpu(prev, task_pt_regs(prev)); |
154 | |
155 | /* we're going to use this soon, after a few expensive things */ |
156 | if (next->thread.fpu_counter > 5) |
157 | prefetch(next_t->xstate); |
158 | |
159 | #ifdef CONFIG_MMU |
160 | /* |
161 | * Restore the kernel mode register |
162 | * k7 (r7_bank1) |
163 | */ |
164 | asm volatile("ldc %0, r7_bank" |
165 | : /* no output */ |
166 | : "r" (task_thread_info(next))); |
167 | #endif |
168 | |
169 | /* |
170 | * If the task has used fpu the last 5 timeslices, just do a full |
171 | * restore of the math state immediately to avoid the trap; the |
172 | * chances of needing FPU soon are obviously high now |
173 | */ |
174 | if (next->thread.fpu_counter > 5) |
175 | __fpu_state_restore(); |
176 | |
177 | return prev; |
178 | } |
179 | |
180 | unsigned long __get_wchan(struct task_struct *p) |
181 | { |
182 | unsigned long pc; |
183 | |
184 | /* |
185 | * The same comment as on the Alpha applies here, too ... |
186 | */ |
187 | pc = thread_saved_pc(p); |
188 | |
189 | #ifdef CONFIG_FRAME_POINTER |
190 | if (in_sched_functions(pc)) { |
191 | unsigned long schedule_frame = (unsigned long)p->thread.sp; |
192 | return ((unsigned long *)schedule_frame)[21]; |
193 | } |
194 | #endif |
195 | |
196 | return pc; |
197 | } |
198 | |