diff options
author | Ley Foon Tan <lftan@altera.com> | 2014-11-06 02:19:47 -0500 |
---|---|---|
committer | Ley Foon Tan <lftan@altera.com> | 2014-12-07 23:55:53 -0500 |
commit | 71995e4d004f6afbc86cc4a80c4a281f6c00b07f (patch) | |
tree | 68139e7bb9dfee02e23384224e2fa20082e55653 /arch/nios2/kernel | |
parent | cbd15b3fadc27e81a6e8a3d38ce13cde5ce7cb71 (diff) |
nios2: Process management
This patch adds support for thread creation and context switching.
Signed-off-by: Ley Foon Tan <lftan@altera.com>
Diffstat (limited to 'arch/nios2/kernel')
-rw-r--r-- | arch/nios2/kernel/process.c | 258 |
1 files changed, 258 insertions, 0 deletions
diff --git a/arch/nios2/kernel/process.c b/arch/nios2/kernel/process.c new file mode 100644 index 000000000000..0e075b5ad2a5 --- /dev/null +++ b/arch/nios2/kernel/process.c | |||
@@ -0,0 +1,258 @@ | |||
1 | /* | ||
2 | * Architecture-dependent parts of process handling. | ||
3 | * | ||
4 | * Copyright (C) 2013 Altera Corporation | ||
5 | * Copyright (C) 2010 Tobias Klauser <tklauser@distanz.ch> | ||
6 | * Copyright (C) 2009 Wind River Systems Inc | ||
7 | * Implemented by fredrik.markstrom@gmail.com and ivarholmqvist@gmail.com | ||
8 | * Copyright (C) 2004 Microtronix Datacom Ltd | ||
9 | * | ||
10 | * This file is subject to the terms and conditions of the GNU General Public | ||
11 | * License. See the file "COPYING" in the main directory of this archive | ||
12 | * for more details. | ||
13 | */ | ||
14 | |||
15 | #include <linux/export.h> | ||
16 | #include <linux/sched.h> | ||
17 | #include <linux/tick.h> | ||
18 | #include <linux/uaccess.h> | ||
19 | |||
20 | #include <asm/unistd.h> | ||
21 | #include <asm/traps.h> | ||
22 | #include <asm/cpuinfo.h> | ||
23 | |||
24 | asmlinkage void ret_from_fork(void); | ||
25 | asmlinkage void ret_from_kernel_thread(void); | ||
26 | |||
27 | void (*pm_power_off)(void) = NULL; | ||
28 | EXPORT_SYMBOL(pm_power_off); | ||
29 | |||
30 | void arch_cpu_idle(void) | ||
31 | { | ||
32 | local_irq_enable(); | ||
33 | } | ||
34 | |||
35 | /* | ||
36 | * The development boards have no way to pull a board reset. Just jump to the | ||
37 | * cpu reset address and let the boot loader or the code in head.S take care of | ||
38 | * resetting peripherals. | ||
39 | */ | ||
40 | void machine_restart(char *__unused) | ||
41 | { | ||
42 | pr_notice("Machine restart (%08x)...\n", cpuinfo.reset_addr); | ||
43 | local_irq_disable(); | ||
44 | __asm__ __volatile__ ( | ||
45 | "jmp %0\n\t" | ||
46 | : | ||
47 | : "r" (cpuinfo.reset_addr) | ||
48 | : "r4"); | ||
49 | } | ||
50 | |||
51 | void machine_halt(void) | ||
52 | { | ||
53 | pr_notice("Machine halt...\n"); | ||
54 | local_irq_disable(); | ||
55 | for (;;) | ||
56 | ; | ||
57 | } | ||
58 | |||
59 | /* | ||
60 | * There is no way to power off the development boards. So just spin for now. If | ||
61 | * we ever have a way of resetting a board using a GPIO we should add that here. | ||
62 | */ | ||
63 | void machine_power_off(void) | ||
64 | { | ||
65 | pr_notice("Machine power off...\n"); | ||
66 | local_irq_disable(); | ||
67 | for (;;) | ||
68 | ; | ||
69 | } | ||
70 | |||
71 | void show_regs(struct pt_regs *regs) | ||
72 | { | ||
73 | pr_notice("\n"); | ||
74 | show_regs_print_info(KERN_DEFAULT); | ||
75 | |||
76 | pr_notice("r1: %08lx r2: %08lx r3: %08lx r4: %08lx\n", | ||
77 | regs->r1, regs->r2, regs->r3, regs->r4); | ||
78 | |||
79 | pr_notice("r5: %08lx r6: %08lx r7: %08lx r8: %08lx\n", | ||
80 | regs->r5, regs->r6, regs->r7, regs->r8); | ||
81 | |||
82 | pr_notice("r9: %08lx r10: %08lx r11: %08lx r12: %08lx\n", | ||
83 | regs->r9, regs->r10, regs->r11, regs->r12); | ||
84 | |||
85 | pr_notice("r13: %08lx r14: %08lx r15: %08lx\n", | ||
86 | regs->r13, regs->r14, regs->r15); | ||
87 | |||
88 | pr_notice("ra: %08lx fp: %08lx sp: %08lx gp: %08lx\n", | ||
89 | regs->ra, regs->fp, regs->sp, regs->gp); | ||
90 | |||
91 | pr_notice("ea: %08lx estatus: %08lx\n", | ||
92 | regs->ea, regs->estatus); | ||
93 | } | ||
94 | |||
95 | void flush_thread(void) | ||
96 | { | ||
97 | set_fs(USER_DS); | ||
98 | } | ||
99 | |||
100 | int copy_thread(unsigned long clone_flags, | ||
101 | unsigned long usp, unsigned long arg, struct task_struct *p) | ||
102 | { | ||
103 | struct pt_regs *childregs = task_pt_regs(p); | ||
104 | struct pt_regs *regs; | ||
105 | struct switch_stack *stack; | ||
106 | struct switch_stack *childstack = | ||
107 | ((struct switch_stack *)childregs) - 1; | ||
108 | |||
109 | if (unlikely(p->flags & PF_KTHREAD)) { | ||
110 | memset(childstack, 0, | ||
111 | sizeof(struct switch_stack) + sizeof(struct pt_regs)); | ||
112 | |||
113 | childstack->r16 = usp; /* fn */ | ||
114 | childstack->r17 = arg; | ||
115 | childstack->ra = (unsigned long) ret_from_kernel_thread; | ||
116 | childregs->estatus = STATUS_PIE; | ||
117 | childregs->sp = (unsigned long) childstack; | ||
118 | |||
119 | p->thread.ksp = (unsigned long) childstack; | ||
120 | p->thread.kregs = childregs; | ||
121 | return 0; | ||
122 | } | ||
123 | |||
124 | regs = current_pt_regs(); | ||
125 | *childregs = *regs; | ||
126 | childregs->r2 = 0; /* Set the return value for the child. */ | ||
127 | childregs->r7 = 0; | ||
128 | |||
129 | stack = ((struct switch_stack *) regs) - 1; | ||
130 | *childstack = *stack; | ||
131 | childstack->ra = (unsigned long)ret_from_fork; | ||
132 | p->thread.kregs = childregs; | ||
133 | p->thread.ksp = (unsigned long) childstack; | ||
134 | |||
135 | if (usp) | ||
136 | childregs->sp = usp; | ||
137 | |||
138 | /* Initialize tls register. */ | ||
139 | if (clone_flags & CLONE_SETTLS) | ||
140 | childstack->r23 = regs->r8; | ||
141 | |||
142 | return 0; | ||
143 | } | ||
144 | |||
145 | /* | ||
146 | * Generic dumping code. Used for panic and debug. | ||
147 | */ | ||
148 | void dump(struct pt_regs *fp) | ||
149 | { | ||
150 | unsigned long *sp; | ||
151 | unsigned char *tp; | ||
152 | int i; | ||
153 | |||
154 | pr_emerg("\nCURRENT PROCESS:\n\n"); | ||
155 | pr_emerg("COMM=%s PID=%d\n", current->comm, current->pid); | ||
156 | |||
157 | if (current->mm) { | ||
158 | pr_emerg("TEXT=%08x-%08x DATA=%08x-%08x BSS=%08x-%08x\n", | ||
159 | (int) current->mm->start_code, | ||
160 | (int) current->mm->end_code, | ||
161 | (int) current->mm->start_data, | ||
162 | (int) current->mm->end_data, | ||
163 | (int) current->mm->end_data, | ||
164 | (int) current->mm->brk); | ||
165 | pr_emerg("USER-STACK=%08x KERNEL-STACK=%08x\n\n", | ||
166 | (int) current->mm->start_stack, | ||
167 | (int)(((unsigned long) current) + THREAD_SIZE)); | ||
168 | } | ||
169 | |||
170 | pr_emerg("PC: %08lx\n", fp->ea); | ||
171 | pr_emerg("SR: %08lx SP: %08lx\n", | ||
172 | (long) fp->estatus, (long) fp); | ||
173 | |||
174 | pr_emerg("r1: %08lx r2: %08lx r3: %08lx\n", | ||
175 | fp->r1, fp->r2, fp->r3); | ||
176 | |||
177 | pr_emerg("r4: %08lx r5: %08lx r6: %08lx r7: %08lx\n", | ||
178 | fp->r4, fp->r5, fp->r6, fp->r7); | ||
179 | pr_emerg("r8: %08lx r9: %08lx r10: %08lx r11: %08lx\n", | ||
180 | fp->r8, fp->r9, fp->r10, fp->r11); | ||
181 | pr_emerg("r12: %08lx r13: %08lx r14: %08lx r15: %08lx\n", | ||
182 | fp->r12, fp->r13, fp->r14, fp->r15); | ||
183 | pr_emerg("or2: %08lx ra: %08lx fp: %08lx sp: %08lx\n", | ||
184 | fp->orig_r2, fp->ra, fp->fp, fp->sp); | ||
185 | pr_emerg("\nUSP: %08x TRAPFRAME: %08x\n", | ||
186 | (unsigned int) fp->sp, (unsigned int) fp); | ||
187 | |||
188 | pr_emerg("\nCODE:"); | ||
189 | tp = ((unsigned char *) fp->ea) - 0x20; | ||
190 | for (sp = (unsigned long *) tp, i = 0; (i < 0x40); i += 4) { | ||
191 | if ((i % 0x10) == 0) | ||
192 | pr_emerg("\n%08x: ", (int) (tp + i)); | ||
193 | pr_emerg("%08x ", (int) *sp++); | ||
194 | } | ||
195 | pr_emerg("\n"); | ||
196 | |||
197 | pr_emerg("\nKERNEL STACK:"); | ||
198 | tp = ((unsigned char *) fp) - 0x40; | ||
199 | for (sp = (unsigned long *) tp, i = 0; (i < 0xc0); i += 4) { | ||
200 | if ((i % 0x10) == 0) | ||
201 | pr_emerg("\n%08x: ", (int) (tp + i)); | ||
202 | pr_emerg("%08x ", (int) *sp++); | ||
203 | } | ||
204 | pr_emerg("\n"); | ||
205 | pr_emerg("\n"); | ||
206 | |||
207 | pr_emerg("\nUSER STACK:"); | ||
208 | tp = (unsigned char *) (fp->sp - 0x10); | ||
209 | for (sp = (unsigned long *) tp, i = 0; (i < 0x80); i += 4) { | ||
210 | if ((i % 0x10) == 0) | ||
211 | pr_emerg("\n%08x: ", (int) (tp + i)); | ||
212 | pr_emerg("%08x ", (int) *sp++); | ||
213 | } | ||
214 | pr_emerg("\n\n"); | ||
215 | } | ||
216 | |||
217 | unsigned long get_wchan(struct task_struct *p) | ||
218 | { | ||
219 | unsigned long fp, pc; | ||
220 | unsigned long stack_page; | ||
221 | int count = 0; | ||
222 | |||
223 | if (!p || p == current || p->state == TASK_RUNNING) | ||
224 | return 0; | ||
225 | |||
226 | stack_page = (unsigned long)p; | ||
227 | fp = ((struct switch_stack *)p->thread.ksp)->fp; /* ;dgt2 */ | ||
228 | do { | ||
229 | if (fp < stack_page+sizeof(struct task_struct) || | ||
230 | fp >= 8184+stack_page) /* ;dgt2;tmp */ | ||
231 | return 0; | ||
232 | pc = ((unsigned long *)fp)[1]; | ||
233 | if (!in_sched_functions(pc)) | ||
234 | return pc; | ||
235 | fp = *(unsigned long *) fp; | ||
236 | } while (count++ < 16); /* ;dgt2;tmp */ | ||
237 | return 0; | ||
238 | } | ||
239 | |||
240 | /* | ||
241 | * Do necessary setup to start up a newly executed thread. | ||
242 | * Will startup in user mode (status_extension = 0). | ||
243 | */ | ||
244 | void start_thread(struct pt_regs *regs, unsigned long pc, unsigned long sp) | ||
245 | { | ||
246 | memset((void *) regs, 0, sizeof(struct pt_regs)); | ||
247 | regs->estatus = ESTATUS_EPIE | ESTATUS_EU; | ||
248 | regs->ea = pc; | ||
249 | regs->sp = sp; | ||
250 | } | ||
251 | |||
252 | #include <linux/elfcore.h> | ||
253 | |||
254 | /* Fill in the FPU structure for a core dump. */ | ||
255 | int dump_fpu(struct pt_regs *regs, elf_fpregset_t *r) | ||
256 | { | ||
257 | return 0; /* Nios2 has no FPU and thus no FPU registers */ | ||
258 | } | ||