This patch implements process/thread related codes. Backtrace and stacktrace are here.
Unified · Split
| @@ -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__ */ | |
| @@ -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__ */ | |
| @@ -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); | |
| @@ -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(®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 | +} | |
| @@ -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 | |
| @@ -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 | |