Commit f73670e8a55c11d47c28dca35dc4bc7dfbd4e6eb
1 parent
141c943fd4
Exists in
master
and in
7 other branches
unicore32 core architecture: process/thread related codes
This patch implements process/thread related codes. Backtrace and stacktrace are here. Signed-off-by: Guan Xuetao <gxt@mprc.pku.edu.cn> Reviewed-by: Arnd Bergmann <arnd@arndb.de>
Showing 6 changed files with 912 additions and 0 deletions Side-by-side Diff
arch/unicore32/include/asm/stacktrace.h
1 | +/* | |
2 | + * linux/arch/unicore32/include/asm/stacktrace.h | |
3 | + * | |
4 | + * Code specific to PKUnity SoC and UniCore ISA | |
5 | + * | |
6 | + * Copyright (C) 2001-2010 GUAN Xue-tao | |
7 | + * | |
8 | + * This program is free software; you can redistribute it and/or modify | |
9 | + * it under the terms of the GNU General Public License version 2 as | |
10 | + * published by the Free Software Foundation. | |
11 | + */ | |
12 | + | |
13 | +#ifndef __UNICORE_STACKTRACE_H__ | |
14 | +#define __UNICORE_STACKTRACE_H__ | |
15 | + | |
16 | +struct stackframe { | |
17 | + unsigned long fp; | |
18 | + unsigned long sp; | |
19 | + unsigned long lr; | |
20 | + unsigned long pc; | |
21 | +}; | |
22 | + | |
23 | +#ifdef CONFIG_FRAME_POINTER | |
24 | +extern int unwind_frame(struct stackframe *frame); | |
25 | +#else | |
26 | +#define unwind_frame(f) (-EINVAL) | |
27 | +#endif | |
28 | +extern void walk_stackframe(struct stackframe *frame, | |
29 | + int (*fn)(struct stackframe *, void *), void *data); | |
30 | + | |
31 | +#endif /* __UNICORE_STACKTRACE_H__ */ |
arch/unicore32/include/asm/thread_info.h
1 | +/* | |
2 | + * linux/arch/unicore32/include/asm/thread_info.h | |
3 | + * | |
4 | + * Code specific to PKUnity SoC and UniCore ISA | |
5 | + * | |
6 | + * Copyright (C) 2001-2010 GUAN Xue-tao | |
7 | + * | |
8 | + * This program is free software; you can redistribute it and/or modify | |
9 | + * it under the terms of the GNU General Public License version 2 as | |
10 | + * published by the Free Software Foundation. | |
11 | + */ | |
12 | +#ifndef __UNICORE_THREAD_INFO_H__ | |
13 | +#define __UNICORE_THREAD_INFO_H__ | |
14 | + | |
15 | +#ifdef __KERNEL__ | |
16 | + | |
17 | +#include <linux/compiler.h> | |
18 | +#include <asm/fpstate.h> | |
19 | + | |
20 | +#define THREAD_SIZE_ORDER 1 | |
21 | +#define THREAD_SIZE 8192 | |
22 | +#define THREAD_START_SP (THREAD_SIZE - 8) | |
23 | + | |
24 | +#ifndef __ASSEMBLY__ | |
25 | + | |
26 | +struct task_struct; | |
27 | +struct exec_domain; | |
28 | + | |
29 | +#include <asm/types.h> | |
30 | + | |
31 | +typedef struct { | |
32 | + unsigned long seg; | |
33 | +} mm_segment_t; | |
34 | + | |
35 | +struct cpu_context_save { | |
36 | + __u32 r4; | |
37 | + __u32 r5; | |
38 | + __u32 r6; | |
39 | + __u32 r7; | |
40 | + __u32 r8; | |
41 | + __u32 r9; | |
42 | + __u32 r10; | |
43 | + __u32 r11; | |
44 | + __u32 r12; | |
45 | + __u32 r13; | |
46 | + __u32 r14; | |
47 | + __u32 r15; | |
48 | + __u32 r16; | |
49 | + __u32 r17; | |
50 | + __u32 r18; | |
51 | + __u32 r19; | |
52 | + __u32 r20; | |
53 | + __u32 r21; | |
54 | + __u32 r22; | |
55 | + __u32 r23; | |
56 | + __u32 r24; | |
57 | + __u32 r25; | |
58 | + __u32 r26; | |
59 | + __u32 fp; | |
60 | + __u32 sp; | |
61 | + __u32 pc; | |
62 | +}; | |
63 | + | |
64 | +/* | |
65 | + * low level task data that entry.S needs immediate access to. | |
66 | + * __switch_to() assumes cpu_context follows immediately after cpu_domain. | |
67 | + */ | |
68 | +struct thread_info { | |
69 | + unsigned long flags; /* low level flags */ | |
70 | + int preempt_count; /* 0 => preemptable */ | |
71 | + /* <0 => bug */ | |
72 | + mm_segment_t addr_limit; /* address limit */ | |
73 | + struct task_struct *task; /* main task structure */ | |
74 | + struct exec_domain *exec_domain; /* execution domain */ | |
75 | + __u32 cpu; /* cpu */ | |
76 | + struct cpu_context_save cpu_context; /* cpu context */ | |
77 | + __u32 syscall; /* syscall number */ | |
78 | + __u8 used_cp[16]; /* thread used copro */ | |
79 | +#ifdef CONFIG_UNICORE_FPU_F64 | |
80 | + struct fp_state fpstate __attribute__((aligned(8))); | |
81 | +#endif | |
82 | + struct restart_block restart_block; | |
83 | +}; | |
84 | + | |
85 | +#define INIT_THREAD_INFO(tsk) \ | |
86 | +{ \ | |
87 | + .task = &tsk, \ | |
88 | + .exec_domain = &default_exec_domain, \ | |
89 | + .flags = 0, \ | |
90 | + .preempt_count = INIT_PREEMPT_COUNT, \ | |
91 | + .addr_limit = KERNEL_DS, \ | |
92 | + .restart_block = { \ | |
93 | + .fn = do_no_restart_syscall, \ | |
94 | + }, \ | |
95 | +} | |
96 | + | |
97 | +#define init_thread_info (init_thread_union.thread_info) | |
98 | +#define init_stack (init_thread_union.stack) | |
99 | + | |
100 | +/* | |
101 | + * how to get the thread information struct from C | |
102 | + */ | |
103 | +static inline struct thread_info *current_thread_info(void) __attribute_const__; | |
104 | + | |
105 | +static inline struct thread_info *current_thread_info(void) | |
106 | +{ | |
107 | + register unsigned long sp asm ("sp"); | |
108 | + return (struct thread_info *)(sp & ~(THREAD_SIZE - 1)); | |
109 | +} | |
110 | + | |
111 | +#define thread_saved_pc(tsk) \ | |
112 | + ((unsigned long)(task_thread_info(tsk)->cpu_context.pc)) | |
113 | +#define thread_saved_sp(tsk) \ | |
114 | + ((unsigned long)(task_thread_info(tsk)->cpu_context.sp)) | |
115 | +#define thread_saved_fp(tsk) \ | |
116 | + ((unsigned long)(task_thread_info(tsk)->cpu_context.fp)) | |
117 | + | |
118 | +#endif | |
119 | + | |
120 | +/* | |
121 | + * We use bit 30 of the preempt_count to indicate that kernel | |
122 | + * preemption is occurring. See <asm/hardirq.h>. | |
123 | + */ | |
124 | +#define PREEMPT_ACTIVE 0x40000000 | |
125 | + | |
126 | +/* | |
127 | + * thread information flags: | |
128 | + * TIF_SYSCALL_TRACE - syscall trace active | |
129 | + * TIF_SIGPENDING - signal pending | |
130 | + * TIF_NEED_RESCHED - rescheduling necessary | |
131 | + * TIF_NOTIFY_RESUME - callback before returning to user | |
132 | + */ | |
133 | +#define TIF_SIGPENDING 0 | |
134 | +#define TIF_NEED_RESCHED 1 | |
135 | +#define TIF_NOTIFY_RESUME 2 /* callback before returning to user */ | |
136 | +#define TIF_SYSCALL_TRACE 8 | |
137 | +#define TIF_MEMDIE 18 | |
138 | +#define TIF_FREEZE 19 | |
139 | +#define TIF_RESTORE_SIGMASK 20 | |
140 | + | |
141 | +#define _TIF_SIGPENDING (1 << TIF_SIGPENDING) | |
142 | +#define _TIF_NEED_RESCHED (1 << TIF_NEED_RESCHED) | |
143 | +#define _TIF_NOTIFY_RESUME (1 << TIF_NOTIFY_RESUME) | |
144 | +#define _TIF_SYSCALL_TRACE (1 << TIF_SYSCALL_TRACE) | |
145 | +#define _TIF_FREEZE (1 << TIF_FREEZE) | |
146 | +#define _TIF_RESTORE_SIGMASK (1 << TIF_RESTORE_SIGMASK) | |
147 | + | |
148 | +/* | |
149 | + * Change these and you break ASM code in entry-common.S | |
150 | + */ | |
151 | +#define _TIF_WORK_MASK 0x000000ff | |
152 | + | |
153 | +#endif /* __KERNEL__ */ | |
154 | +#endif /* __UNICORE_THREAD_INFO_H__ */ |
arch/unicore32/kernel/init_task.c
1 | +/* | |
2 | + * linux/arch/unicore32/kernel/init_task.c | |
3 | + * | |
4 | + * Code specific to PKUnity SoC and UniCore ISA | |
5 | + * | |
6 | + * Copyright (C) 2001-2010 GUAN Xue-tao | |
7 | + * | |
8 | + * This program is free software; you can redistribute it and/or modify | |
9 | + * it under the terms of the GNU General Public License version 2 as | |
10 | + * published by the Free Software Foundation. | |
11 | + */ | |
12 | +#include <linux/mm.h> | |
13 | +#include <linux/module.h> | |
14 | +#include <linux/fs.h> | |
15 | +#include <linux/sched.h> | |
16 | +#include <linux/init.h> | |
17 | +#include <linux/init_task.h> | |
18 | +#include <linux/mqueue.h> | |
19 | +#include <linux/uaccess.h> | |
20 | + | |
21 | +#include <asm/pgtable.h> | |
22 | + | |
23 | +static struct signal_struct init_signals = INIT_SIGNALS(init_signals); | |
24 | +static struct sighand_struct init_sighand = INIT_SIGHAND(init_sighand); | |
25 | +/* | |
26 | + * Initial thread structure. | |
27 | + * | |
28 | + * We need to make sure that this is 8192-byte aligned due to the | |
29 | + * way process stacks are handled. This is done by making sure | |
30 | + * the linker maps this in the .text segment right after head.S, | |
31 | + * and making head.S ensure the proper alignment. | |
32 | + * | |
33 | + * The things we do for performance.. | |
34 | + */ | |
35 | +union thread_union init_thread_union __init_task_data = { | |
36 | + INIT_THREAD_INFO(init_task) }; | |
37 | + | |
38 | +/* | |
39 | + * Initial task structure. | |
40 | + * | |
41 | + * All other task structs will be allocated on slabs in fork.c | |
42 | + */ | |
43 | +struct task_struct init_task = INIT_TASK(init_task); | |
44 | +EXPORT_SYMBOL(init_task); |
arch/unicore32/kernel/process.c
1 | +/* | |
2 | + * linux/arch/unicore32/kernel/process.c | |
3 | + * | |
4 | + * Code specific to PKUnity SoC and UniCore ISA | |
5 | + * | |
6 | + * Copyright (C) 2001-2010 GUAN Xue-tao | |
7 | + * | |
8 | + * This program is free software; you can redistribute it and/or modify | |
9 | + * it under the terms of the GNU General Public License version 2 as | |
10 | + * published by the Free Software Foundation. | |
11 | + */ | |
12 | +#include <stdarg.h> | |
13 | + | |
14 | +#include <linux/module.h> | |
15 | +#include <linux/sched.h> | |
16 | +#include <linux/kernel.h> | |
17 | +#include <linux/mm.h> | |
18 | +#include <linux/stddef.h> | |
19 | +#include <linux/unistd.h> | |
20 | +#include <linux/delay.h> | |
21 | +#include <linux/reboot.h> | |
22 | +#include <linux/interrupt.h> | |
23 | +#include <linux/kallsyms.h> | |
24 | +#include <linux/init.h> | |
25 | +#include <linux/cpu.h> | |
26 | +#include <linux/elfcore.h> | |
27 | +#include <linux/pm.h> | |
28 | +#include <linux/tick.h> | |
29 | +#include <linux/utsname.h> | |
30 | +#include <linux/uaccess.h> | |
31 | +#include <linux/random.h> | |
32 | +#include <linux/gpio.h> | |
33 | +#include <linux/stacktrace.h> | |
34 | + | |
35 | +#include <asm/cacheflush.h> | |
36 | +#include <asm/processor.h> | |
37 | +#include <asm/system.h> | |
38 | +#include <asm/stacktrace.h> | |
39 | + | |
40 | +#include "setup.h" | |
41 | + | |
42 | +static const char * const processor_modes[] = { | |
43 | + "UK00", "UK01", "UK02", "UK03", "UK04", "UK05", "UK06", "UK07", | |
44 | + "UK08", "UK09", "UK0A", "UK0B", "UK0C", "UK0D", "UK0E", "UK0F", | |
45 | + "USER", "REAL", "INTR", "PRIV", "UK14", "UK15", "UK16", "ABRT", | |
46 | + "UK18", "UK19", "UK1A", "EXTN", "UK1C", "UK1D", "UK1E", "SUSR" | |
47 | +}; | |
48 | + | |
49 | +/* | |
50 | + * The idle thread, has rather strange semantics for calling pm_idle, | |
51 | + * but this is what x86 does and we need to do the same, so that | |
52 | + * things like cpuidle get called in the same way. | |
53 | + */ | |
54 | +void cpu_idle(void) | |
55 | +{ | |
56 | + /* endless idle loop with no priority at all */ | |
57 | + while (1) { | |
58 | + tick_nohz_stop_sched_tick(1); | |
59 | + while (!need_resched()) { | |
60 | + local_irq_disable(); | |
61 | + stop_critical_timings(); | |
62 | + cpu_do_idle(); | |
63 | + local_irq_enable(); | |
64 | + start_critical_timings(); | |
65 | + } | |
66 | + tick_nohz_restart_sched_tick(); | |
67 | + preempt_enable_no_resched(); | |
68 | + schedule(); | |
69 | + preempt_disable(); | |
70 | + } | |
71 | +} | |
72 | + | |
73 | +static char reboot_mode = 'h'; | |
74 | + | |
75 | +int __init reboot_setup(char *str) | |
76 | +{ | |
77 | + reboot_mode = str[0]; | |
78 | + return 1; | |
79 | +} | |
80 | + | |
81 | +__setup("reboot=", reboot_setup); | |
82 | + | |
83 | +void machine_halt(void) | |
84 | +{ | |
85 | + gpio_set_value(GPO_SOFT_OFF, 0); | |
86 | +} | |
87 | + | |
88 | +/* | |
89 | + * Function pointers to optional machine specific functions | |
90 | + */ | |
91 | +void (*pm_power_off)(void) = NULL; | |
92 | + | |
93 | +void machine_power_off(void) | |
94 | +{ | |
95 | + if (pm_power_off) | |
96 | + pm_power_off(); | |
97 | + machine_halt(); | |
98 | +} | |
99 | + | |
100 | +void machine_restart(char *cmd) | |
101 | +{ | |
102 | + /* Disable interrupts first */ | |
103 | + local_irq_disable(); | |
104 | + | |
105 | + /* | |
106 | + * Tell the mm system that we are going to reboot - | |
107 | + * we may need it to insert some 1:1 mappings so that | |
108 | + * soft boot works. | |
109 | + */ | |
110 | + setup_mm_for_reboot(reboot_mode); | |
111 | + | |
112 | + /* Clean and invalidate caches */ | |
113 | + flush_cache_all(); | |
114 | + | |
115 | + /* Turn off caching */ | |
116 | + cpu_proc_fin(); | |
117 | + | |
118 | + /* Push out any further dirty data, and ensure cache is empty */ | |
119 | + flush_cache_all(); | |
120 | + | |
121 | + /* | |
122 | + * Now handle reboot code. | |
123 | + */ | |
124 | + if (reboot_mode == 's') { | |
125 | + /* Jump into ROM at address 0xffff0000 */ | |
126 | + cpu_reset(VECTORS_BASE); | |
127 | + } else { | |
128 | + PM_PLLSYSCFG = 0x00002001; /* cpu clk = 250M */ | |
129 | + PM_PLLDDRCFG = 0x00100800; /* ddr clk = 44M */ | |
130 | + PM_PLLVGACFG = 0x00002001; /* vga clk = 250M */ | |
131 | + | |
132 | + /* Use on-chip reset capability */ | |
133 | + /* following instructions must be in one icache line */ | |
134 | + __asm__ __volatile__( | |
135 | + " .align 5\n\t" | |
136 | + " stw %1, [%0]\n\t" | |
137 | + "201: ldw r0, [%0]\n\t" | |
138 | + " cmpsub.a r0, #0\n\t" | |
139 | + " bne 201b\n\t" | |
140 | + " stw %3, [%2]\n\t" | |
141 | + " nop; nop; nop\n\t" | |
142 | + /* prefetch 3 instructions at most */ | |
143 | + : | |
144 | + : "r" ((unsigned long)&PM_PMCR), | |
145 | + "r" (PM_PMCR_CFBSYS | PM_PMCR_CFBDDR | |
146 | + | PM_PMCR_CFBVGA), | |
147 | + "r" ((unsigned long)&RESETC_SWRR), | |
148 | + "r" (RESETC_SWRR_SRB) | |
149 | + : "r0", "memory"); | |
150 | + } | |
151 | + | |
152 | + /* | |
153 | + * Whoops - the architecture was unable to reboot. | |
154 | + * Tell the user! | |
155 | + */ | |
156 | + mdelay(1000); | |
157 | + printk(KERN_EMERG "Reboot failed -- System halted\n"); | |
158 | + do { } while (1); | |
159 | +} | |
160 | + | |
161 | +void __show_regs(struct pt_regs *regs) | |
162 | +{ | |
163 | + unsigned long flags; | |
164 | + char buf[64]; | |
165 | + | |
166 | + printk(KERN_DEFAULT "CPU: %d %s (%s %.*s)\n", | |
167 | + raw_smp_processor_id(), print_tainted(), | |
168 | + init_utsname()->release, | |
169 | + (int)strcspn(init_utsname()->version, " "), | |
170 | + init_utsname()->version); | |
171 | + print_symbol("PC is at %s\n", instruction_pointer(regs)); | |
172 | + print_symbol("LR is at %s\n", regs->UCreg_lr); | |
173 | + printk(KERN_DEFAULT "pc : [<%08lx>] lr : [<%08lx>] psr: %08lx\n" | |
174 | + "sp : %08lx ip : %08lx fp : %08lx\n", | |
175 | + regs->UCreg_pc, regs->UCreg_lr, regs->UCreg_asr, | |
176 | + regs->UCreg_sp, regs->UCreg_ip, regs->UCreg_fp); | |
177 | + printk(KERN_DEFAULT "r26: %08lx r25: %08lx r24: %08lx\n", | |
178 | + regs->UCreg_26, regs->UCreg_25, | |
179 | + regs->UCreg_24); | |
180 | + printk(KERN_DEFAULT "r23: %08lx r22: %08lx r21: %08lx r20: %08lx\n", | |
181 | + regs->UCreg_23, regs->UCreg_22, | |
182 | + regs->UCreg_21, regs->UCreg_20); | |
183 | + printk(KERN_DEFAULT "r19: %08lx r18: %08lx r17: %08lx r16: %08lx\n", | |
184 | + regs->UCreg_19, regs->UCreg_18, | |
185 | + regs->UCreg_17, regs->UCreg_16); | |
186 | + printk(KERN_DEFAULT "r15: %08lx r14: %08lx r13: %08lx r12: %08lx\n", | |
187 | + regs->UCreg_15, regs->UCreg_14, | |
188 | + regs->UCreg_13, regs->UCreg_12); | |
189 | + printk(KERN_DEFAULT "r11: %08lx r10: %08lx r9 : %08lx r8 : %08lx\n", | |
190 | + regs->UCreg_11, regs->UCreg_10, | |
191 | + regs->UCreg_09, regs->UCreg_08); | |
192 | + printk(KERN_DEFAULT "r7 : %08lx r6 : %08lx r5 : %08lx r4 : %08lx\n", | |
193 | + regs->UCreg_07, regs->UCreg_06, | |
194 | + regs->UCreg_05, regs->UCreg_04); | |
195 | + printk(KERN_DEFAULT "r3 : %08lx r2 : %08lx r1 : %08lx r0 : %08lx\n", | |
196 | + regs->UCreg_03, regs->UCreg_02, | |
197 | + regs->UCreg_01, regs->UCreg_00); | |
198 | + | |
199 | + flags = regs->UCreg_asr; | |
200 | + buf[0] = flags & PSR_S_BIT ? 'S' : 's'; | |
201 | + buf[1] = flags & PSR_Z_BIT ? 'Z' : 'z'; | |
202 | + buf[2] = flags & PSR_C_BIT ? 'C' : 'c'; | |
203 | + buf[3] = flags & PSR_V_BIT ? 'V' : 'v'; | |
204 | + buf[4] = '\0'; | |
205 | + | |
206 | + printk(KERN_DEFAULT "Flags: %s INTR o%s REAL o%s Mode %s Segment %s\n", | |
207 | + buf, interrupts_enabled(regs) ? "n" : "ff", | |
208 | + fast_interrupts_enabled(regs) ? "n" : "ff", | |
209 | + processor_modes[processor_mode(regs)], | |
210 | + segment_eq(get_fs(), get_ds()) ? "kernel" : "user"); | |
211 | + { | |
212 | + unsigned int ctrl; | |
213 | + | |
214 | + buf[0] = '\0'; | |
215 | + { | |
216 | + unsigned int transbase; | |
217 | + asm("movc %0, p0.c2, #0\n" | |
218 | + : "=r" (transbase)); | |
219 | + snprintf(buf, sizeof(buf), " Table: %08x", transbase); | |
220 | + } | |
221 | + asm("movc %0, p0.c1, #0\n" : "=r" (ctrl)); | |
222 | + | |
223 | + printk(KERN_DEFAULT "Control: %08x%s\n", ctrl, buf); | |
224 | + } | |
225 | +} | |
226 | + | |
227 | +void show_regs(struct pt_regs *regs) | |
228 | +{ | |
229 | + printk(KERN_DEFAULT "\n"); | |
230 | + printk(KERN_DEFAULT "Pid: %d, comm: %20s\n", | |
231 | + task_pid_nr(current), current->comm); | |
232 | + __show_regs(regs); | |
233 | + __backtrace(); | |
234 | +} | |
235 | + | |
236 | +/* | |
237 | + * Free current thread data structures etc.. | |
238 | + */ | |
239 | +void exit_thread(void) | |
240 | +{ | |
241 | +} | |
242 | + | |
243 | +void flush_thread(void) | |
244 | +{ | |
245 | + struct thread_info *thread = current_thread_info(); | |
246 | + struct task_struct *tsk = current; | |
247 | + | |
248 | + memset(thread->used_cp, 0, sizeof(thread->used_cp)); | |
249 | + memset(&tsk->thread.debug, 0, sizeof(struct debug_info)); | |
250 | +#ifdef CONFIG_UNICORE_FPU_F64 | |
251 | + memset(&thread->fpstate, 0, sizeof(struct fp_state)); | |
252 | +#endif | |
253 | +} | |
254 | + | |
255 | +void release_thread(struct task_struct *dead_task) | |
256 | +{ | |
257 | +} | |
258 | + | |
259 | +asmlinkage void ret_from_fork(void) __asm__("ret_from_fork"); | |
260 | + | |
261 | +int | |
262 | +copy_thread(unsigned long clone_flags, unsigned long stack_start, | |
263 | + unsigned long stk_sz, struct task_struct *p, struct pt_regs *regs) | |
264 | +{ | |
265 | + struct thread_info *thread = task_thread_info(p); | |
266 | + struct pt_regs *childregs = task_pt_regs(p); | |
267 | + | |
268 | + *childregs = *regs; | |
269 | + childregs->UCreg_00 = 0; | |
270 | + childregs->UCreg_sp = stack_start; | |
271 | + | |
272 | + memset(&thread->cpu_context, 0, sizeof(struct cpu_context_save)); | |
273 | + thread->cpu_context.sp = (unsigned long)childregs; | |
274 | + thread->cpu_context.pc = (unsigned long)ret_from_fork; | |
275 | + | |
276 | + if (clone_flags & CLONE_SETTLS) | |
277 | + childregs->UCreg_16 = regs->UCreg_03; | |
278 | + | |
279 | + return 0; | |
280 | +} | |
281 | + | |
282 | +/* | |
283 | + * Fill in the task's elfregs structure for a core dump. | |
284 | + */ | |
285 | +int dump_task_regs(struct task_struct *t, elf_gregset_t *elfregs) | |
286 | +{ | |
287 | + elf_core_copy_regs(elfregs, task_pt_regs(t)); | |
288 | + return 1; | |
289 | +} | |
290 | + | |
291 | +/* | |
292 | + * fill in the fpe structure for a core dump... | |
293 | + */ | |
294 | +int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fp) | |
295 | +{ | |
296 | + struct thread_info *thread = current_thread_info(); | |
297 | + int used_math = thread->used_cp[1] | thread->used_cp[2]; | |
298 | + | |
299 | +#ifdef CONFIG_UNICORE_FPU_F64 | |
300 | + if (used_math) | |
301 | + memcpy(fp, &thread->fpstate, sizeof(*fp)); | |
302 | +#endif | |
303 | + return used_math != 0; | |
304 | +} | |
305 | +EXPORT_SYMBOL(dump_fpu); | |
306 | + | |
307 | +/* | |
308 | + * Shuffle the argument into the correct register before calling the | |
309 | + * thread function. r1 is the thread argument, r2 is the pointer to | |
310 | + * the thread function, and r3 points to the exit function. | |
311 | + */ | |
312 | +asm(".pushsection .text\n" | |
313 | +" .align\n" | |
314 | +" .type kernel_thread_helper, #function\n" | |
315 | +"kernel_thread_helper:\n" | |
316 | +" mov.a asr, r7\n" | |
317 | +" mov r0, r4\n" | |
318 | +" mov lr, r6\n" | |
319 | +" mov pc, r5\n" | |
320 | +" .size kernel_thread_helper, . - kernel_thread_helper\n" | |
321 | +" .popsection"); | |
322 | + | |
323 | +/* | |
324 | + * Create a kernel thread. | |
325 | + */ | |
326 | +pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags) | |
327 | +{ | |
328 | + struct pt_regs regs; | |
329 | + | |
330 | + memset(®s, 0, sizeof(regs)); | |
331 | + | |
332 | + regs.UCreg_04 = (unsigned long)arg; | |
333 | + regs.UCreg_05 = (unsigned long)fn; | |
334 | + regs.UCreg_06 = (unsigned long)do_exit; | |
335 | + regs.UCreg_07 = PRIV_MODE; | |
336 | + regs.UCreg_pc = (unsigned long)kernel_thread_helper; | |
337 | + regs.UCreg_asr = regs.UCreg_07 | PSR_I_BIT; | |
338 | + | |
339 | + return do_fork(flags|CLONE_VM|CLONE_UNTRACED, 0, ®s, 0, NULL, NULL); | |
340 | +} | |
341 | +EXPORT_SYMBOL(kernel_thread); | |
342 | + | |
343 | +unsigned long get_wchan(struct task_struct *p) | |
344 | +{ | |
345 | + struct stackframe frame; | |
346 | + int count = 0; | |
347 | + if (!p || p == current || p->state == TASK_RUNNING) | |
348 | + return 0; | |
349 | + | |
350 | + frame.fp = thread_saved_fp(p); | |
351 | + frame.sp = thread_saved_sp(p); | |
352 | + frame.lr = 0; /* recovered from the stack */ | |
353 | + frame.pc = thread_saved_pc(p); | |
354 | + do { | |
355 | + int ret = unwind_frame(&frame); | |
356 | + if (ret < 0) | |
357 | + return 0; | |
358 | + if (!in_sched_functions(frame.pc)) | |
359 | + return frame.pc; | |
360 | + } while ((count++) < 16); | |
361 | + return 0; | |
362 | +} | |
363 | + | |
364 | +unsigned long arch_randomize_brk(struct mm_struct *mm) | |
365 | +{ | |
366 | + unsigned long range_end = mm->brk + 0x02000000; | |
367 | + return randomize_range(mm->brk, range_end, 0) ? : mm->brk; | |
368 | +} | |
369 | + | |
370 | +/* | |
371 | + * The vectors page is always readable from user space for the | |
372 | + * atomic helpers and the signal restart code. Let's declare a mapping | |
373 | + * for it so it is visible through ptrace and /proc/<pid>/mem. | |
374 | + */ | |
375 | + | |
376 | +int vectors_user_mapping(void) | |
377 | +{ | |
378 | + struct mm_struct *mm = current->mm; | |
379 | + return install_special_mapping(mm, 0xffff0000, PAGE_SIZE, | |
380 | + VM_READ | VM_EXEC | | |
381 | + VM_MAYREAD | VM_MAYEXEC | | |
382 | + VM_ALWAYSDUMP | VM_RESERVED, | |
383 | + NULL); | |
384 | +} | |
385 | + | |
386 | +const char *arch_vma_name(struct vm_area_struct *vma) | |
387 | +{ | |
388 | + return (vma->vm_start == 0xffff0000) ? "[vectors]" : NULL; | |
389 | +} |
arch/unicore32/kernel/stacktrace.c
1 | +/* | |
2 | + * linux/arch/unicore32/kernel/stacktrace.c | |
3 | + * | |
4 | + * Code specific to PKUnity SoC and UniCore ISA | |
5 | + * | |
6 | + * Copyright (C) 2001-2010 GUAN Xue-tao | |
7 | + * | |
8 | + * This program is free software; you can redistribute it and/or modify | |
9 | + * it under the terms of the GNU General Public License version 2 as | |
10 | + * published by the Free Software Foundation. | |
11 | + */ | |
12 | +#include <linux/module.h> | |
13 | +#include <linux/sched.h> | |
14 | +#include <linux/stacktrace.h> | |
15 | + | |
16 | +#include <asm/stacktrace.h> | |
17 | + | |
18 | +#if defined(CONFIG_FRAME_POINTER) | |
19 | +/* | |
20 | + * Unwind the current stack frame and store the new register values in the | |
21 | + * structure passed as argument. Unwinding is equivalent to a function return, | |
22 | + * hence the new PC value rather than LR should be used for backtrace. | |
23 | + * | |
24 | + * With framepointer enabled, a simple function prologue looks like this: | |
25 | + * mov ip, sp | |
26 | + * stmdb sp!, {fp, ip, lr, pc} | |
27 | + * sub fp, ip, #4 | |
28 | + * | |
29 | + * A simple function epilogue looks like this: | |
30 | + * ldm sp, {fp, sp, pc} | |
31 | + * | |
32 | + * Note that with framepointer enabled, even the leaf functions have the same | |
33 | + * prologue and epilogue, therefore we can ignore the LR value in this case. | |
34 | + */ | |
35 | +int notrace unwind_frame(struct stackframe *frame) | |
36 | +{ | |
37 | + unsigned long high, low; | |
38 | + unsigned long fp = frame->fp; | |
39 | + | |
40 | + /* only go to a higher address on the stack */ | |
41 | + low = frame->sp; | |
42 | + high = ALIGN(low, THREAD_SIZE); | |
43 | + | |
44 | + /* check current frame pointer is within bounds */ | |
45 | + if (fp < (low + 12) || fp + 4 >= high) | |
46 | + return -EINVAL; | |
47 | + | |
48 | + /* restore the registers from the stack frame */ | |
49 | + frame->fp = *(unsigned long *)(fp - 12); | |
50 | + frame->sp = *(unsigned long *)(fp - 8); | |
51 | + frame->pc = *(unsigned long *)(fp - 4); | |
52 | + | |
53 | + return 0; | |
54 | +} | |
55 | +#endif | |
56 | + | |
57 | +void notrace walk_stackframe(struct stackframe *frame, | |
58 | + int (*fn)(struct stackframe *, void *), void *data) | |
59 | +{ | |
60 | + while (1) { | |
61 | + int ret; | |
62 | + | |
63 | + if (fn(frame, data)) | |
64 | + break; | |
65 | + ret = unwind_frame(frame); | |
66 | + if (ret < 0) | |
67 | + break; | |
68 | + } | |
69 | +} | |
70 | +EXPORT_SYMBOL(walk_stackframe); | |
71 | + | |
72 | +#ifdef CONFIG_STACKTRACE | |
73 | +struct stack_trace_data { | |
74 | + struct stack_trace *trace; | |
75 | + unsigned int no_sched_functions; | |
76 | + unsigned int skip; | |
77 | +}; | |
78 | + | |
79 | +static int save_trace(struct stackframe *frame, void *d) | |
80 | +{ | |
81 | + struct stack_trace_data *data = d; | |
82 | + struct stack_trace *trace = data->trace; | |
83 | + unsigned long addr = frame->pc; | |
84 | + | |
85 | + if (data->no_sched_functions && in_sched_functions(addr)) | |
86 | + return 0; | |
87 | + if (data->skip) { | |
88 | + data->skip--; | |
89 | + return 0; | |
90 | + } | |
91 | + | |
92 | + trace->entries[trace->nr_entries++] = addr; | |
93 | + | |
94 | + return trace->nr_entries >= trace->max_entries; | |
95 | +} | |
96 | + | |
97 | +void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) | |
98 | +{ | |
99 | + struct stack_trace_data data; | |
100 | + struct stackframe frame; | |
101 | + | |
102 | + data.trace = trace; | |
103 | + data.skip = trace->skip; | |
104 | + | |
105 | + if (tsk != current) { | |
106 | + data.no_sched_functions = 1; | |
107 | + frame.fp = thread_saved_fp(tsk); | |
108 | + frame.sp = thread_saved_sp(tsk); | |
109 | + frame.lr = 0; /* recovered from the stack */ | |
110 | + frame.pc = thread_saved_pc(tsk); | |
111 | + } else { | |
112 | + register unsigned long current_sp asm("sp"); | |
113 | + | |
114 | + data.no_sched_functions = 0; | |
115 | + frame.fp = (unsigned long)__builtin_frame_address(0); | |
116 | + frame.sp = current_sp; | |
117 | + frame.lr = (unsigned long)__builtin_return_address(0); | |
118 | + frame.pc = (unsigned long)save_stack_trace_tsk; | |
119 | + } | |
120 | + | |
121 | + walk_stackframe(&frame, save_trace, &data); | |
122 | + if (trace->nr_entries < trace->max_entries) | |
123 | + trace->entries[trace->nr_entries++] = ULONG_MAX; | |
124 | +} | |
125 | + | |
126 | +void save_stack_trace(struct stack_trace *trace) | |
127 | +{ | |
128 | + save_stack_trace_tsk(current, trace); | |
129 | +} | |
130 | +EXPORT_SYMBOL_GPL(save_stack_trace); | |
131 | +#endif |
arch/unicore32/lib/backtrace.S
1 | +/* | |
2 | + * linux/arch/unicore32/lib/backtrace.S | |
3 | + * | |
4 | + * Code specific to PKUnity SoC and UniCore ISA | |
5 | + * | |
6 | + * Copyright (C) 2001-2010 GUAN Xue-tao | |
7 | + * | |
8 | + * This program is free software; you can redistribute it and/or modify | |
9 | + * it under the terms of the GNU General Public License version 2 as | |
10 | + * published by the Free Software Foundation. | |
11 | + */ | |
12 | +#include <linux/linkage.h> | |
13 | +#include <asm/assembler.h> | |
14 | + .text | |
15 | + | |
16 | +@ fp is 0 or stack frame | |
17 | + | |
18 | +#define frame v4 | |
19 | +#define sv_fp v5 | |
20 | +#define sv_pc v6 | |
21 | +#define offset v8 | |
22 | + | |
23 | +ENTRY(__backtrace) | |
24 | + mov r0, fp | |
25 | + | |
26 | +ENTRY(c_backtrace) | |
27 | + | |
28 | +#if !defined(CONFIG_FRAME_POINTER) || !defined(CONFIG_PRINTK) | |
29 | + mov pc, lr | |
30 | +ENDPROC(__backtrace) | |
31 | +ENDPROC(c_backtrace) | |
32 | +#else | |
33 | + stm.w (v4 - v8, lr), [sp-] @ Save an extra register | |
34 | + @ so we have a location... | |
35 | + mov.a frame, r0 @ if frame pointer is zero | |
36 | + beq no_frame @ we have no stack frames | |
37 | + | |
38 | +1: stm.w (pc), [sp-] @ calculate offset of PC stored | |
39 | + ldw.w r0, [sp]+, #4 @ by stmfd for this CPU | |
40 | + adr r1, 1b | |
41 | + sub offset, r0, r1 | |
42 | + | |
43 | +/* | |
44 | + * Stack frame layout: | |
45 | + * optionally saved caller registers (r4 - r10) | |
46 | + * saved fp | |
47 | + * saved sp | |
48 | + * saved lr | |
49 | + * frame => saved pc | |
50 | + * optionally saved arguments (r0 - r3) | |
51 | + * saved sp => <next word> | |
52 | + * | |
53 | + * Functions start with the following code sequence: | |
54 | + * mov ip, sp | |
55 | + * stm.w (r0 - r3), [sp-] (optional) | |
56 | + * corrected pc => stm.w sp, (..., fp, ip, lr, pc) | |
57 | + */ | |
58 | +for_each_frame: | |
59 | + | |
60 | +1001: ldw sv_pc, [frame+], #0 @ get saved pc | |
61 | +1002: ldw sv_fp, [frame+], #-12 @ get saved fp | |
62 | + | |
63 | + sub sv_pc, sv_pc, offset @ Correct PC for prefetching | |
64 | + | |
65 | +1003: ldw r2, [sv_pc+], #-4 @ if stmfd sp, {args} exists, | |
66 | + ldw r3, .Ldsi+4 @ adjust saved 'pc' back one | |
67 | + cxor.a r3, r2 >> #14 @ instruction | |
68 | + beq 201f | |
69 | + sub r0, sv_pc, #4 @ allow for mov | |
70 | + b 202f | |
71 | +201: | |
72 | + sub r0, sv_pc, #8 @ allow for mov + stmia | |
73 | +202: | |
74 | + ldw r1, [frame+], #-4 @ get saved lr | |
75 | + mov r2, frame | |
76 | + b.l dump_backtrace_entry | |
77 | + | |
78 | + ldw r1, [sv_pc+], #-4 @ if stmfd sp, {args} exists, | |
79 | + ldw r3, .Ldsi+4 | |
80 | + cxor.a r3, r1 >> #14 | |
81 | + bne 1004f | |
82 | + ldw r0, [frame+], #-8 @ get sp | |
83 | + sub r0, r0, #4 @ point at the last arg | |
84 | + b.l .Ldumpstm @ dump saved registers | |
85 | + | |
86 | +1004: ldw r1, [sv_pc+], #0 @ if stmfd {, fp, ip, lr, pc} | |
87 | + ldw r3, .Ldsi @ instruction exists, | |
88 | + cxor.a r3, r1 >> #14 | |
89 | + bne 201f | |
90 | + sub r0, frame, #16 | |
91 | + b.l .Ldumpstm @ dump saved registers | |
92 | +201: | |
93 | + cxor.a sv_fp, #0 @ zero saved fp means | |
94 | + beq no_frame @ no further frames | |
95 | + | |
96 | + csub.a sv_fp, frame @ next frame must be | |
97 | + mov frame, sv_fp @ above the current frame | |
98 | + bua for_each_frame | |
99 | + | |
100 | +1006: adr r0, .Lbad | |
101 | + mov r1, frame | |
102 | + b.l printk | |
103 | +no_frame: ldm.w (v4 - v8, pc), [sp]+ | |
104 | +ENDPROC(__backtrace) | |
105 | +ENDPROC(c_backtrace) | |
106 | + | |
107 | + .pushsection __ex_table,"a" | |
108 | + .align 3 | |
109 | + .long 1001b, 1006b | |
110 | + .long 1002b, 1006b | |
111 | + .long 1003b, 1006b | |
112 | + .long 1004b, 1006b | |
113 | + .popsection | |
114 | + | |
115 | +#define instr v4 | |
116 | +#define reg v5 | |
117 | +#define stack v6 | |
118 | + | |
119 | +.Ldumpstm: stm.w (instr, reg, stack, v7, lr), [sp-] | |
120 | + mov stack, r0 | |
121 | + mov instr, r1 | |
122 | + mov reg, #14 | |
123 | + mov v7, #0 | |
124 | +1: mov r3, #1 | |
125 | + csub.a reg, #8 | |
126 | + bne 201f | |
127 | + sub reg, reg, #3 | |
128 | +201: | |
129 | + cand.a instr, r3 << reg | |
130 | + beq 2f | |
131 | + add v7, v7, #1 | |
132 | + cxor.a v7, #6 | |
133 | + cmoveq v7, #1 | |
134 | + cmoveq r1, #'\n' | |
135 | + cmovne r1, #' ' | |
136 | + ldw.w r3, [stack]+, #-4 | |
137 | + mov r2, reg | |
138 | + csub.a r2, #8 | |
139 | + bsl 201f | |
140 | + sub r2, r2, #3 | |
141 | +201: | |
142 | + cand.a instr, #0x40 @ if H is 1, high 16 regs | |
143 | + beq 201f | |
144 | + add r2, r2, #0x10 @ so r2 need add 16 | |
145 | +201: | |
146 | + adr r0, .Lfp | |
147 | + b.l printk | |
148 | +2: sub.a reg, reg, #1 | |
149 | + bns 1b | |
150 | + cxor.a v7, #0 | |
151 | + beq 201f | |
152 | + adr r0, .Lcr | |
153 | + b.l printk | |
154 | +201: ldm.w (instr, reg, stack, v7, pc), [sp]+ | |
155 | + | |
156 | +.Lfp: .asciz "%cr%d:%08x" | |
157 | +.Lcr: .asciz "\n" | |
158 | +.Lbad: .asciz "Backtrace aborted due to bad frame pointer <%p>\n" | |
159 | + .align | |
160 | +.Ldsi: .word 0x92eec000 >> 14 @ stm.w sp, (... fp, ip, lr, pc) | |
161 | + .word 0x92e10000 >> 14 @ stm.w sp, () | |
162 | + | |
163 | +#endif |