History

unicore32 core architecture: process/thread related codes

This patch implements process/thread related codes. Backtrace and stacktrace are here.

GuanXuetao committed 15 years ago

Commit f73670e1 parent 141c9436 files · +912 −0Browse files

Unified · Split

6 files
  1. arch/unicore32/include/asm/stacktrace.h +31 −0
  2. arch/unicore32/include/asm/thread_info.h +154 −0
  3. arch/unicore32/kernel/init_task.c +44 −0
  4. arch/unicore32/kernel/process.c +389 −0
  5. arch/unicore32/kernel/stacktrace.c +131 −0
  6. arch/unicore32/lib/backtrace.S +163 −0
arch/unicore32/include/asm/stacktrace.h+31−0
@@ -0,0 +1,31 @@
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+154−0
@@ -0,0 +1,154 @@
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+44−0
@@ -0,0 +1,44 @@
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+389−0
@@ -0,0 +1,389 @@
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(&regs, 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, &regs, 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+131−0
@@ -0,0 +1,131 @@
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+163−0
@@ -0,0 +1,163 @@
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