diff options
Diffstat (limited to 'arch/microblaze/kernel')
-rw-r--r-- | arch/microblaze/kernel/Makefile | 7 | ||||
-rw-r--r-- | arch/microblaze/kernel/cpu/mb.c | 1 | ||||
-rw-r--r-- | arch/microblaze/kernel/entry-nommu.S | 28 | ||||
-rw-r--r-- | arch/microblaze/kernel/entry.S | 607 | ||||
-rw-r--r-- | arch/microblaze/kernel/exceptions.c | 9 | ||||
-rw-r--r-- | arch/microblaze/kernel/head.S | 4 | ||||
-rw-r--r-- | arch/microblaze/kernel/hw_exception_handler.S | 4 | ||||
-rw-r--r-- | arch/microblaze/kernel/irq.c | 16 | ||||
-rw-r--r-- | arch/microblaze/kernel/kgdb.c | 147 | ||||
-rw-r--r-- | arch/microblaze/kernel/misc.S | 2 | ||||
-rw-r--r-- | arch/microblaze/kernel/of_device.c | 112 | ||||
-rw-r--r-- | arch/microblaze/kernel/of_platform.c | 200 | ||||
-rw-r--r-- | arch/microblaze/kernel/process.c | 7 | ||||
-rw-r--r-- | arch/microblaze/kernel/prom_parse.c | 877 | ||||
-rw-r--r-- | arch/microblaze/kernel/ptrace.c | 17 | ||||
-rw-r--r-- | arch/microblaze/kernel/reset.c | 12 | ||||
-rw-r--r-- | arch/microblaze/kernel/setup.c | 6 | ||||
-rw-r--r-- | arch/microblaze/kernel/stacktrace.c | 44 | ||||
-rw-r--r-- | arch/microblaze/kernel/timer.c | 24 | ||||
-rw-r--r-- | arch/microblaze/kernel/traps.c | 91 | ||||
-rw-r--r-- | arch/microblaze/kernel/unwind.c | 318 | ||||
-rw-r--r-- | arch/microblaze/kernel/vmlinux.lds.S | 16 |
22 files changed, 857 insertions, 1692 deletions
diff --git a/arch/microblaze/kernel/Makefile b/arch/microblaze/kernel/Makefile index e51bc152082..f0cb5c26c81 100644 --- a/arch/microblaze/kernel/Makefile +++ b/arch/microblaze/kernel/Makefile @@ -15,9 +15,9 @@ endif extra-y := head.o vmlinux.lds obj-y += dma.o exceptions.o \ - hw_exception_handler.o init_task.o intc.o irq.o of_device.o \ - of_platform.o process.o prom.o prom_parse.o ptrace.o \ - setup.o signal.o sys_microblaze.o timer.o traps.o reset.o + hw_exception_handler.o init_task.o intc.o irq.o \ + process.o prom.o prom_parse.o ptrace.o \ + reset.o setup.o signal.o sys_microblaze.o timer.o traps.o unwind.o obj-y += cpu/ @@ -28,5 +28,6 @@ obj-$(CONFIG_MODULES) += microblaze_ksyms.o module.o obj-$(CONFIG_MMU) += misc.o obj-$(CONFIG_STACKTRACE) += stacktrace.o obj-$(CONFIG_FUNCTION_TRACER) += ftrace.o mcount.o +obj-$(CONFIG_KGDB) += kgdb.o obj-y += entry$(MMU).o diff --git a/arch/microblaze/kernel/cpu/mb.c b/arch/microblaze/kernel/cpu/mb.c index 4216eb1eaa3..7086e356428 100644 --- a/arch/microblaze/kernel/cpu/mb.c +++ b/arch/microblaze/kernel/cpu/mb.c @@ -126,6 +126,7 @@ static int show_cpuinfo(struct seq_file *m, void *v) cpuinfo.pvr_user1, cpuinfo.pvr_user2); + count += seq_printf(m, "Page size:\t%lu\n", PAGE_SIZE); return 0; } diff --git a/arch/microblaze/kernel/entry-nommu.S b/arch/microblaze/kernel/entry-nommu.S index 8cc18cd2cce..ca84368570b 100644 --- a/arch/microblaze/kernel/entry-nommu.S +++ b/arch/microblaze/kernel/entry-nommu.S @@ -588,3 +588,31 @@ sys_rt_sigsuspend_wrapper: #include "syscall_table.S" syscall_table_size=(.-sys_call_table) + +type_SYSCALL: + .ascii "SYSCALL\0" +type_IRQ: + .ascii "IRQ\0" +type_IRQ_PREEMPT: + .ascii "IRQ (PREEMPTED)\0" +type_SYSCALL_PREEMPT: + .ascii " SYSCALL (PREEMPTED)\0" + + /* + * Trap decoding for stack unwinder + * Tuples are (start addr, end addr, string) + * If return address lies on [start addr, end addr], + * unwinder displays 'string' + */ + + .align 4 +.global microblaze_trap_handlers +microblaze_trap_handlers: + /* Exact matches come first */ + .word ret_to_user ; .word ret_to_user ; .word type_SYSCALL + .word ret_from_intr; .word ret_from_intr ; .word type_IRQ + /* Fuzzy matches go here */ + .word ret_from_intr; .word no_intr_resched; .word type_IRQ_PREEMPT + .word work_pending ; .word no_work_pending; .word type_SYSCALL_PREEMPT + /* End of table */ + .word 0 ; .word 0 ; .word 0 diff --git a/arch/microblaze/kernel/entry.S b/arch/microblaze/kernel/entry.S index c0ede25c5b9..304882e5645 100644 --- a/arch/microblaze/kernel/entry.S +++ b/arch/microblaze/kernel/entry.S @@ -48,128 +48,107 @@ */ #if CONFIG_XILINX_MICROBLAZE0_USE_MSR_INSTR .macro clear_bip - msrclr r11, MSR_BIP - nop + msrclr r0, MSR_BIP .endm .macro set_bip - msrset r11, MSR_BIP - nop + msrset r0, MSR_BIP .endm .macro clear_eip - msrclr r11, MSR_EIP - nop + msrclr r0, MSR_EIP .endm .macro set_ee - msrset r11, MSR_EE - nop + msrset r0, MSR_EE .endm .macro disable_irq - msrclr r11, MSR_IE - nop + msrclr r0, MSR_IE .endm .macro enable_irq - msrset r11, MSR_IE - nop + msrset r0, MSR_IE .endm .macro set_ums - msrset r11, MSR_UMS - nop - msrclr r11, MSR_VMS - nop + msrset r0, MSR_UMS + msrclr r0, MSR_VMS .endm .macro set_vms - msrclr r11, MSR_UMS - nop - msrset r11, MSR_VMS - nop + msrclr r0, MSR_UMS + msrset r0, MSR_VMS + .endm + + .macro clear_ums + msrclr r0, MSR_UMS .endm .macro clear_vms_ums - msrclr r11, MSR_VMS - nop - msrclr r11, MSR_UMS - nop + msrclr r0, MSR_VMS | MSR_UMS .endm #else .macro clear_bip mfs r11, rmsr - nop andi r11, r11, ~MSR_BIP mts rmsr, r11 - nop .endm .macro set_bip mfs r11, rmsr - nop ori r11, r11, MSR_BIP mts rmsr, r11 - nop .endm .macro clear_eip mfs r11, rmsr - nop andi r11, r11, ~MSR_EIP mts rmsr, r11 - nop .endm .macro set_ee mfs r11, rmsr - nop ori r11, r11, MSR_EE mts rmsr, r11 - nop .endm .macro disable_irq mfs r11, rmsr - nop andi r11, r11, ~MSR_IE mts rmsr, r11 - nop .endm .macro enable_irq mfs r11, rmsr - nop ori r11, r11, MSR_IE mts rmsr, r11 - nop .endm .macro set_ums mfs r11, rmsr - nop ori r11, r11, MSR_VMS andni r11, r11, MSR_UMS mts rmsr, r11 - nop .endm .macro set_vms mfs r11, rmsr - nop ori r11, r11, MSR_VMS andni r11, r11, MSR_UMS mts rmsr, r11 - nop + .endm + + .macro clear_ums + mfs r11, rmsr + andni r11, r11, MSR_UMS + mts rmsr,r11 .endm .macro clear_vms_ums mfs r11, rmsr - nop andni r11, r11, (MSR_VMS|MSR_UMS) mts rmsr,r11 - nop .endm #endif @@ -180,18 +159,22 @@ /* turn on virtual protected mode save */ #define VM_ON \ - set_ums; \ + set_ums; \ rted r0, 2f; \ -2: nop; + nop; \ +2: /* turn off virtual protected mode save and user mode save*/ #define VM_OFF \ - clear_vms_ums; \ + clear_vms_ums; \ rted r0, TOPHYS(1f); \ -1: nop; + nop; \ +1: #define SAVE_REGS \ swi r2, r1, PTO+PT_R2; /* Save SDA */ \ + swi r3, r1, PTO+PT_R3; \ + swi r4, r1, PTO+PT_R4; \ swi r5, r1, PTO+PT_R5; \ swi r6, r1, PTO+PT_R6; \ swi r7, r1, PTO+PT_R7; \ @@ -218,14 +201,14 @@ swi r30, r1, PTO+PT_R30; \ swi r31, r1, PTO+PT_R31; /* Save current task reg */ \ mfs r11, rmsr; /* save MSR */ \ - nop; \ swi r11, r1, PTO+PT_MSR; #define RESTORE_REGS \ lwi r11, r1, PTO+PT_MSR; \ mts rmsr , r11; \ - nop; \ lwi r2, r1, PTO+PT_R2; /* restore SDA */ \ + lwi r3, r1, PTO+PT_R3; \ + lwi r4, r1, PTO+PT_R4; \ lwi r5, r1, PTO+PT_R5; \ lwi r6, r1, PTO+PT_R6; \ lwi r7, r1, PTO+PT_R7; \ @@ -252,6 +235,39 @@ lwi r30, r1, PTO+PT_R30; \ lwi r31, r1, PTO+PT_R31; /* Restore cur task reg */ +#define SAVE_STATE \ + swi r1, r0, TOPHYS(PER_CPU(ENTRY_SP)); /* save stack */ \ + /* See if already in kernel mode.*/ \ + mfs r1, rmsr; \ + andi r1, r1, MSR_UMS; \ + bnei r1, 1f; \ + /* Kernel-mode state save. */ \ + /* Reload kernel stack-ptr. */ \ + lwi r1, r0, TOPHYS(PER_CPU(ENTRY_SP)); \ + /* FIXME: I can add these two lines to one */ \ + /* tophys(r1,r1); */ \ + /* addik r1, r1, -STATE_SAVE_SIZE; */ \ + addik r1, r1, CONFIG_KERNEL_BASE_ADDR - CONFIG_KERNEL_START - STATE_SAVE_SIZE; \ + SAVE_REGS \ + brid 2f; \ + swi r1, r1, PTO+PT_MODE; \ +1: /* User-mode state save. */ \ + lwi r1, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); /* get saved current */\ + tophys(r1,r1); \ + lwi r1, r1, TS_THREAD_INFO; /* get the thread info */ \ + /* MS these three instructions can be added to one */ \ + /* addik r1, r1, THREAD_SIZE; */ \ + /* tophys(r1,r1); */ \ + /* addik r1, r1, -STATE_SAVE_SIZE; */ \ + addik r1, r1, THREAD_SIZE + CONFIG_KERNEL_BASE_ADDR - CONFIG_KERNEL_START - STATE_SAVE_SIZE; \ + SAVE_REGS \ + lwi r11, r0, TOPHYS(PER_CPU(ENTRY_SP)); \ + swi r11, r1, PTO+PT_R1; /* Store user SP. */ \ + swi r0, r1, PTO + PT_MODE; /* Was in user-mode. */ \ + /* MS: I am clearing UMS even in case when I come from kernel space */ \ + clear_ums; \ +2: lwi CURRENT_TASK, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); + .text /* @@ -267,45 +283,23 @@ * are masked. This is nice, means we don't have to CLI before state save */ C_ENTRY(_user_exception): - swi r1, r0, TOPHYS(PER_CPU(ENTRY_SP)) /* save stack */ addi r14, r14, 4 /* return address is 4 byte after call */ - swi r11, r0, TOPHYS(r0_ram + PTO + PT_R11); /* Save r11 */ - - lwi r11, r0, TOPHYS(PER_CPU(KM));/* See if already in kernel mode.*/ - beqi r11, 1f; /* Jump ahead if coming from user */ -/* Kernel-mode state save. */ - lwi r11, r0, TOPHYS(PER_CPU(ENTRY_SP)); /* Reload kernel stack-ptr*/ - tophys(r1,r11); - swi r11, r1, (PT_R1-PT_SIZE); /* Save original SP. */ - lwi r11, r0, TOPHYS(r0_ram + PTO + PT_R11); /* restore r11 */ - - addik r1, r1, -STATE_SAVE_SIZE; /* Make room on the stack. */ - SAVE_REGS - - addi r11, r0, 1; /* Was in kernel-mode. */ - swi r11, r1, PTO+PT_MODE; /* pt_regs -> kernel mode */ - brid 2f; - nop; /* Fill delay slot */ + swi r1, r0, TOPHYS(PER_CPU(ENTRY_SP)) /* save stack */ -/* User-mode state save. */ -1: - lwi r11, r0, TOPHYS(r0_ram + PTO + PT_R11); /* restore r11 */ lwi r1, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); /* get saved current */ tophys(r1,r1); lwi r1, r1, TS_THREAD_INFO; /* get stack from task_struct */ -/* calculate kernel stack pointer from task struct 8k */ - addik r1, r1, THREAD_SIZE; - tophys(r1,r1); - - addik r1, r1, -STATE_SAVE_SIZE; /* Make room on the stack. */ + /* MS these three instructions can be added to one */ + /* addik r1, r1, THREAD_SIZE; */ + /* tophys(r1,r1); */ + /* addik r1, r1, -STATE_SAVE_SIZE; */ + addik r1, r1, THREAD_SIZE + CONFIG_KERNEL_BASE_ADDR - CONFIG_KERNEL_START - STATE_SAVE_SIZE; SAVE_REGS - swi r0, r1, PTO+PT_MODE; /* Was in user-mode. */ lwi r11, r0, TOPHYS(PER_CPU(ENTRY_SP)); swi r11, r1, PTO+PT_R1; /* Store user SP. */ - addi r11, r0, 1; - swi r11, r0, TOPHYS(PER_CPU(KM)); /* Now we're in kernel-mode. */ -2: lwi CURRENT_TASK, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); + clear_ums; + lwi CURRENT_TASK, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); /* Save away the syscall number. */ swi r12, r1, PTO+PT_R0; tovirt(r1,r1) @@ -316,10 +310,8 @@ C_ENTRY(_user_exception): * register should point to the location where * the called function should return. [note that MAKE_SYS_CALL uses label 1] */ - # Step into virtual mode. - set_vms; - addik r11, r0, 3f - rtid r11, 0 + /* Step into virtual mode */ + rtbd r0, 3f nop 3: lwi r11, CURRENT_TASK, TS_THREAD_INFO /* get thread info */ @@ -363,24 +355,17 @@ C_ENTRY(_user_exception): # Find and jump into the syscall handler. lwi r12, r12, sys_call_table /* where the trap should return need -8 to adjust for rtsd r15, 8 */ - la r15, r0, ret_from_trap-8 + addi r15, r0, ret_from_trap-8 bra r12 /* The syscall number is invalid, return an error. */ 5: + rtsd r15, 8; /* looks like a normal subroutine return */ addi r3, r0, -ENOSYS; - rtsd r15,8; /* looks like a normal subroutine return */ - or r0, r0, r0 - /* Entry point used to return from a syscall/trap */ /* We re-enable BIP bit before state restore */ C_ENTRY(ret_from_trap): - set_bip; /* Ints masked for state restore*/ - lwi r11, r1, PTO+PT_MODE; -/* See if returning to kernel mode, if so, skip resched &c. */ - bnei r11, 2f; - swi r3, r1, PTO + PT_R3 swi r4, r1, PTO + PT_R4 @@ -413,32 +398,19 @@ C_ENTRY(ret_from_trap): andi r11, r11, _TIF_SIGPENDING; beqi r11, 1f; /* Signals to handle, handle them */ - la r5, r1, PTO; /* Arg 1: struct pt_regs *regs */ + addik r5, r1, PTO; /* Arg 1: struct pt_regs *regs */ addi r7, r0, 1; /* Arg 3: int in_syscall */ bralid r15, do_signal; /* Handle any signals */ add r6, r0, r0; /* Arg 2: sigset_t *oldset */ /* Finally, return to user state. */ -1: - lwi r3, r1, PTO + PT_R3; /* restore syscall result */ - lwi r4, r1, PTO + PT_R4; - - swi r0, r0, PER_CPU(KM); /* Now officially in user state. */ +1: set_bip; /* Ints masked for state restore */ swi CURRENT_TASK, r0, PER_CPU(CURRENT_SAVE); /* save current */ VM_OFF; tophys(r1,r1); RESTORE_REGS; addik r1, r1, STATE_SAVE_SIZE /* Clean up stack space. */ lwi r1, r1, PT_R1 - PT_SIZE;/* Restore user stack pointer. */ - bri 6f; - -/* Return to kernel state. */ -2: VM_OFF; - tophys(r1,r1); - RESTORE_REGS; - addik r1, r1, STATE_SAVE_SIZE /* Clean up stack space. */ - tovirt(r1,r1); -6: TRAP_return: /* Make global symbol for debugging */ rtbd r14, 0; /* Instructions to return from an IRQ */ nop; @@ -450,12 +422,11 @@ TRAP_return: /* Make global symbol for debugging */ C_ENTRY(sys_fork_wrapper): addi r5, r0, SIGCHLD /* Arg 0: flags */ lwi r6, r1, PTO+PT_R1 /* Arg 1: child SP (use parent's) */ - la r7, r1, PTO /* Arg 2: parent context */ + addik r7, r1, PTO /* Arg 2: parent context */ add r8. r0, r0 /* Arg 3: (unused) */ add r9, r0, r0; /* Arg 4: (unused) */ - add r10, r0, r0; /* Arg 5: (unused) */ brid do_fork /* Do real work (tail-call) */ - nop; + add r10, r0, r0; /* Arg 5: (unused) */ /* This the initial entry point for a new child thread, with an appropriate stack in place that makes it look the the child is in the middle of an @@ -466,35 +437,31 @@ C_ENTRY(ret_from_fork): bralid r15, schedule_tail; /* ...which is schedule_tail's arg */ add r3, r5, r0; /* switch_thread returns the prev task */ /* ( in the delay slot ) */ - add r3, r0, r0; /* Child's fork call should return 0. */ brid ret_from_trap; /* Do normal trap return */ - nop; + add r3, r0, r0; /* Child's fork call should return 0. */ C_ENTRY(sys_vfork): brid microblaze_vfork /* Do real work (tail-call) */ - la r5, r1, PTO + addik r5, r1, PTO C_ENTRY(sys_clone): bnei r6, 1f; /* See if child SP arg (arg 1) is 0. */ - lwi r6, r1, PTO+PT_R1; /* If so, use paret's stack ptr */ -1: la r7, r1, PTO; /* Arg 2: parent context */ + lwi r6, r1, PTO + PT_R1; /* If so, use paret's stack ptr */ +1: addik r7, r1, PTO; /* Arg 2: parent context */ add r8, r0, r0; /* Arg 3: (unused) */ add r9, r0, r0; /* Arg 4: (unused) */ - add r10, r0, r0; /* Arg 5: (unused) */ brid do_fork /* Do real work (tail-call) */ - nop; + add r10, r0, r0; /* Arg 5: (unused) */ C_ENTRY(sys_execve): - la r8, r1, PTO; /* add user context as 4th arg */ brid microblaze_execve; /* Do real work (tail-call).*/ - nop; + addik r8, r1, PTO; /* add user context as 4th arg */ C_ENTRY(sys_rt_sigreturn_wrapper): swi r3, r1, PTO+PT_R3; /* restore saved r3, r4 registers */ swi r4, r1, PTO+PT_R4; - la r5, r1, PTO; /* add user context as 1st arg */ brlid r15, sys_rt_sigreturn /* Do real work */ - nop; + addik r5, r1, PTO; /* add user context as 1st arg */ lwi r3, r1, PTO+PT_R3; /* restore saved r3, r4 registers */ lwi r4, r1, PTO+PT_R4; bri ret_from_trap /* fall through will not work here due to align */ @@ -503,83 +470,23 @@ C_ENTRY(sys_rt_sigreturn_wrapper): /* * HW EXCEPTION rutine start */ - -#define SAVE_STATE \ - swi r11, r0, TOPHYS(r0_ram + PTO + PT_R11); /* Save r11 */ \ - set_bip; /*equalize initial state for all possible entries*/\ - clear_eip; \ - enable_irq; \ - set_ee; \ - /* See if already in kernel mode.*/ \ - lwi r11, r0, TOPHYS(PER_CPU(KM)); \ - beqi r11, 1f; /* Jump ahead if coming from user */\ - /* Kernel-mode state save. */ \ - /* Reload kernel stack-ptr. */ \ - lwi r11, r0, TOPHYS(PER_CPU(ENTRY_SP)); \ - tophys(r1,r11); \ - swi r11, r1, (PT_R1-PT_SIZE); /* Save original SP. */ \ - lwi r11, r0, TOPHYS(r0_ram + PTO + PT_R11); /* restore r11 */\ - addik r1, r1, -STATE_SAVE_SIZE; /* Make room on the stack. */\ - /* store return registers separately because \ - * this macros is use for others exceptions */ \ - swi r3, r1, PTO + PT_R3; \ - swi r4, r1, PTO + PT_R4; \ - SAVE_REGS \ - /* PC, before IRQ/trap - this is one instruction above */ \ - swi r17, r1, PTO+PT_PC; \ - \ - addi r11, r0, 1; /* Was in kernel-mode. */ \ - swi r11, r1, PTO+PT_MODE; \ - brid 2f; \ - nop; /* Fill delay slot */ \ -1: /* User-mode state save. */ \ - lwi r11, r0, TOPHYS(r0_ram + PTO + PT_R11); /* restore r11 */\ - lwi r1, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); /* get saved current */\ - tophys(r1,r1); \ - lwi r1, r1, TS_THREAD_INFO; /* get the thread info */ \ - addik r1, r1, THREAD_SIZE; /* calculate kernel stack pointer */\ - tophys(r1,r1); \ - \ - addik r1, r1, -STATE_SAVE_SIZE; /* Make room on the stack. */\ - /* store return registers separately because this macros \ - * is use for others exceptions */ \ - swi r3, r1, PTO + PT_R3; \ - swi r4, r1, PTO + PT_R4; \ - SAVE_REGS \ - /* PC, before IRQ/trap - this is one instruction above FIXME*/ \ - swi r17, r1, PTO+PT_PC; \ - \ - swi r0, r1, PTO+PT_MODE; /* Was in user-mode. */ \ - lwi r11, r0, TOPHYS(PER_CPU(ENTRY_SP)); \ - swi r11, r1, PTO+PT_R1; /* Store user SP. */ \ - addi r11, r0, 1; \ - swi r11, r0, TOPHYS(PER_CPU(KM)); /* Now we're in kernel-mode.*/\ -2: lwi CURRENT_TASK, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); \ - /* Save away the syscall number. */ \ - swi r0, r1, PTO+PT_R0; \ - tovirt(r1,r1) - C_ENTRY(full_exception_trap): - swi r1, r0, TOPHYS(PER_CPU(ENTRY_SP)) /* save stack */ /* adjust exception address for privileged instruction * for finding where is it */ addik r17, r17, -4 SAVE_STATE /* Save registers */ + /* PC, before IRQ/trap - this is one instruction above */ + swi r17, r1, PTO+PT_PC; + tovirt(r1,r1) /* FIXME this can be store directly in PT_ESR reg. * I tested it but there is a fault */ /* where the trap should return need -8 to adjust for rtsd r15, 8 */ - la r15, r0, ret_from_exc - 8 - la r5, r1, PTO /* parameter struct pt_regs * regs */ + addik r15, r0, ret_from_exc - 8 mfs r6, resr - nop mfs r7, rfsr; /* save FSR */ - nop mts rfsr, r0; /* Clear sticky fsr */ - nop - la r12, r0, full_exception - set_vms; - rtbd r12, 0; - nop; + rted r0, full_exception + addik r5, r1, PTO /* parameter struct pt_regs * regs */ /* * Unaligned data trap. @@ -592,19 +499,27 @@ C_ENTRY(full_exception_trap): * The assembler routine is in "arch/microblaze/kernel/hw_exception_handler.S" */ C_ENTRY(unaligned_data_trap): - swi r1, r0, TOPHYS(PER_CPU(ENTRY_SP)) /* save stack */ + /* MS: I have to save r11 value and then restore it because + * set_bit, clear_eip, set_ee use r11 as temp register if MSR + * instructions are not used. We don't need to do if MSR instructions + * are used and they use r0 instead of r11. + * I am using ENTRY_SP which should be primary used only for stack + * pointer saving. */ + swi r11, r0, TOPHYS(PER_CPU(ENTRY_SP)); + set_bip; /* equalize initial state for all possible entries */ + clear_eip; + set_ee; + lwi r11, r0, TOPHYS(PER_CPU(ENTRY_SP)); SAVE_STATE /* Save registers.*/ + /* PC, before IRQ/trap - this is one instruction above */ + swi r17, r1, PTO+PT_PC; + tovirt(r1,r1) /* where the trap should return need -8 to adjust for rtsd r15, 8 */ - la r15, r0, ret_from_exc-8 + addik r15, r0, ret_from_exc-8 mfs r3, resr /* ESR */ - nop mfs r4, rear /* EAR */ - nop - la r7, r1, PTO /* parameter struct pt_regs * regs */ - la r12, r0, _unaligned_data_exception - set_vms; - rtbd r12, 0; /* interrupts enabled */ - nop; + rtbd r0, _unaligned_data_exception + addik r7, r1, PTO /* parameter struct pt_regs * regs */ /* * Page fault traps. @@ -625,38 +540,32 @@ C_ENTRY(unaligned_data_trap): */ /* data and intruction trap - which is choose is resolved int fault.c */ C_ENTRY(page_fault_data_trap): - swi r1, r0, TOPHYS(PER_CPU(ENTRY_SP)) /* save stack */ SAVE_STATE /* Save registers.*/ + /* PC, before IRQ/trap - this is one instruction above */ + swi r17, r1, PTO+PT_PC; + tovirt(r1,r1) /* where the trap should return need -8 to adjust for rtsd r15, 8 */ - la r15, r0, ret_from_exc-8 - la r5, r1, PTO /* parameter struct pt_regs * regs */ + addik r15, r0, ret_from_exc-8 mfs r6, rear /* parameter unsigned long address */ - nop mfs r7, resr /* parameter unsigned long error_code */ - nop - la r12, r0, do_page_fault - set_vms; - rtbd r12, 0; /* interrupts enabled */ - nop; + rted r0, do_page_fault + addik r5, r1, PTO /* parameter struct pt_regs * regs */ C_ENTRY(page_fault_instr_trap): - swi r1, r0, TOPHYS(PER_CPU(ENTRY_SP)) /* save stack */ SAVE_STATE /* Save registers.*/ + /* PC, before IRQ/trap - this is one instruction above */ + swi r17, r1, PTO+PT_PC; + tovirt(r1,r1) /* where the trap should return need -8 to adjust for rtsd r15, 8 */ - la r15, r0, ret_from_exc-8 - la r5, r1, PTO /* parameter struct pt_regs * regs */ + addik r15, r0, ret_from_exc-8 mfs r6, rear /* parameter unsigned long address */ - nop ori r7, r0, 0 /* parameter unsigned long error_code */ - la r12, r0, do_page_fault - set_vms; - rtbd r12, 0; /* interrupts enabled */ - nop; + rted r0, do_page_fault + addik r5, r1, PTO /* parameter struct pt_regs * regs */ /* Entry point used to return from an exception. */ C_ENTRY(ret_from_exc): - set_bip; /* Ints masked for state restore*/ - lwi r11, r1, PTO+PT_MODE; + lwi r11, r1, PTO + PT_MODE; bnei r11, 2f; /* See if returning to kernel mode, */ /* ... if so, skip resched &c. */ @@ -687,32 +596,27 @@ C_ENTRY(ret_from_exc): * traps), but signal handlers may want to examine or change the * complete register state. Here we save anything not saved by * the normal entry sequence, so that it may be safely restored - * (in a possibly modified form) after do_signal returns. - * store return registers separately because this macros is use - * for others exceptions */ - la r5, r1, PTO; /* Arg 1: struct pt_regs *regs */ + * (in a possibly modified form) after do_signal returns. */ + addik r5, r1, PTO; /* Arg 1: struct pt_regs *regs */ addi r7, r0, 0; /* Arg 3: int in_syscall */ bralid r15, do_signal; /* Handle any signals */ add r6, r0, r0; /* Arg 2: sigset_t *oldset */ /* Finally, return to user state. */ -1: swi r0, r0, PER_CPU(KM); /* Now officially in user state. */ +1: set_bip; /* Ints masked for state restore */ swi CURRENT_TASK, r0, PER_CPU(CURRENT_SAVE); /* save current */ VM_OFF; tophys(r1,r1); - lwi r3, r1, PTO+PT_R3; /* restore saved r3, r4 registers */ - lwi r4, r1, PTO+PT_R4; RESTORE_REGS; addik r1, r1, STATE_SAVE_SIZE /* Clean up stack space. */ lwi r1, r1, PT_R1 - PT_SIZE; /* Restore user stack pointer. */ bri 6f; /* Return to kernel state. */ -2: VM_OFF; +2: set_bip; /* Ints masked for state restore */ + VM_OFF; tophys(r1,r1); - lwi r3, r1, PTO+PT_R3; /* restore saved r3, r4 registers */ - lwi r4, r1, PTO+PT_R4; RESTORE_REGS; addik r1, r1, STATE_SAVE_SIZE /* Clean up stack space. */ @@ -736,36 +640,23 @@ C_ENTRY(_interrupt): /* MS: we are in physical address */ /* Save registers, switch to proper stack, convert SP to virtual.*/ swi r1, r0, TOPHYS(PER_CPU(ENTRY_SP)) - swi r11, r0, TOPHYS(PER_CPU(R11_SAVE)); /* MS: See if already in kernel mode. */ - lwi r11, r0, TOPHYS(PER_CPU(KM)); - beqi r11, 1f; /* MS: Jump ahead if coming from user */ + mfs r1, rmsr + nop + andi r1, r1, MSR_UMS + bnei r1, 1f /* Kernel-mode state save. */ - or r11, r1, r0 - tophys(r1,r11); /* MS: I have in r1 physical address where stack is */ -/* MS: Save original SP - position PT_R1 to next stack frame 4 *1 - 152*/ - swi r11, r1, (PT_R1 - PT_SIZE); -/* MS: restore r11 because of saving in SAVE_REGS */ - lwi r11, r0, TOPHYS(PER_CPU(R11_SAVE)); + lwi r1, r0, TOPHYS(PER_CPU(ENTRY_SP)) + tophys(r1,r1); /* MS: I have in r1 physical address where stack is */ /* save registers */ /* MS: Make room on the stack -> activation record */ addik r1, r1, -STATE_SAVE_SIZE; -/* MS: store return registers separately because - * this macros is use for others exceptions */ - swi r3, r1, PTO + PT_R3; - swi r4, r1, PTO + PT_R4; SAVE_REGS - /* MS: store mode */ - addi r11, r0, 1; /* MS: Was in kernel-mode. */ - swi r11, r1, PTO + PT_MODE; /* MS: and save it */ brid 2f; - nop; /* MS: Fill delay slot */ - + swi r1, r1, PTO + PT_MODE; /* 0 - user mode, 1 - kernel mode */ 1: /* User-mode state save. */ -/* MS: restore r11 -> FIXME move before SAVE_REG */ - lwi r11, r0, TOPHYS(PER_CPU(R11_SAVE)); /* MS: get the saved current */ lwi r1, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); tophys(r1,r1); @@ -774,27 +665,18 @@ C_ENTRY(_interrupt): tophys(r1,r1); /* save registers */ addik r1, r1, -STATE_SAVE_SIZE; - swi r3, r1, PTO+PT_R3; - swi r4, r1, PTO+PT_R4; SAVE_REGS /* calculate mode */ swi r0, r1, PTO + PT_MODE; lwi r11, r0, TOPHYS(PER_CPU(ENTRY_SP)); swi r11, r1, PTO+PT_R1; - /* setup kernel mode to KM */ - addi r11, r0, 1; - swi r11, r0, TOPHYS(PER_CPU(KM)); - + clear_ums; 2: lwi CURRENT_TASK, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); - swi r0, r1, PTO + PT_R0; tovirt(r1,r1) - la r5, r1, PTO; - set_vms; - la r11, r0, do_IRQ; - la r15, r0, irq_call; -irq_call:rtbd r11, 0; - nop; + addik r15, r0, irq_call; +irq_call:rtbd r0, do_IRQ; + addik r5, r1, PTO; /* MS: we are in virtual mode */ ret_from_irq: @@ -815,7 +697,7 @@ ret_from_irq: beqid r11, no_intr_resched /* Handle a signal return; Pending signals should be in r18. */ addi r7, r0, 0; /* Arg 3: int in_syscall */ - la r5, r1, PTO; /* Arg 1: struct pt_regs *regs */ + addik r5, r1, PTO; /* Arg 1: struct pt_regs *regs */ bralid r15, do_signal; /* Handle any signals */ add r6, r0, r0; /* Arg 2: sigset_t *oldset */ @@ -823,12 +705,9 @@ ret_from_irq: no_intr_resched: /* Disable interrupts, we are now committed to the state restore */ disable_irq - swi r0, r0, PER_CPU(KM); /* MS: Now officially in user state. */ swi CURRENT_TASK, r0, PER_CPU(CURRENT_SAVE); VM_OFF; tophys(r1,r1); - lwi r3, r1, PTO + PT_R3; /* MS: restore saved r3, r4 registers */ - lwi r4, r1, PTO + PT_R4; RESTORE_REGS addik r1, r1, STATE_SAVE_SIZE /* MS: Clean up stack space. */ lwi r1, r1, PT_R1 - PT_SIZE; @@ -857,8 +736,6 @@ restore: #endif VM_OFF /* MS: turn off MMU */ tophys(r1,r1) - lwi r3, r1, PTO + PT_R3; /* MS: restore saved r3, r4 registers */ - lwi r4, r1, PTO + PT_R4; RESTORE_REGS addik r1, r1, STATE_SAVE_SIZE /* MS: Clean up stack space. */ tovirt(r1,r1); @@ -868,86 +745,91 @@ IRQ_return: /* MS: Make global symbol for debugging */ nop /* - * `Debug' trap - * We enter dbtrap in "BIP" (breakpoint) mode. - * So we exit the breakpoint mode with an 'rtbd' and proceed with the - * original dbtrap. - * however, wait to save state first + * Debug trap for KGDB. Enter to _debug_exception by brki r16, 0x18 + * and call handling function with saved pt_regs */ C_ENTRY(_debug_exception): /* BIP bit is set on entry, no interrupts can occur */ swi r1, r0, TOPHYS(PER_CPU(ENTRY_SP)) - swi r11, r0, TOPHYS(r0_ram + PTO + PT_R11); /* Save r11 */ - set_bip; /*equalize initial state for all possible entries*/ - clear_eip; - enable_irq; - lwi r11, r0, TOPHYS(PER_CPU(KM));/* See if already in kernel mode.*/ - beqi r11, 1f; /* Jump ahead if coming from user */ - /* Kernel-mode state save. */ - lwi r11, r0, TOPHYS(PER_CPU(ENTRY_SP)); /* Reload kernel stack-ptr*/ - tophys(r1,r11); - swi r11, r1, (PT_R1-PT_SIZE); /* Save original SP. */ - lwi r11, r0, TOPHYS(r0_ram + PTO + PT_R11); /* restore r11 */ + mfs r1, rmsr + nop + andi r1, r1, MSR_UMS + bnei r1, 1f +/* MS: Kernel-mode state save - kgdb */ + lwi r1, r0, TOPHYS(PER_CPU(ENTRY_SP)); /* Reload kernel stack-ptr*/ - addik r1, r1, -STATE_SAVE_SIZE; /* Make room on the stack. */ - swi r3, r1, PTO + PT_R3; - swi r4, r1, PTO + PT_R4; + /* BIP bit is set on entry, no interrupts can occur */ + addik r1, r1, CONFIG_KERNEL_BASE_ADDR - CONFIG_KERNEL_START - STATE_SAVE_SIZE; SAVE_REGS; + /* save all regs to pt_reg structure */ + swi r0, r1, PTO+PT_R0; /* R0 must be saved too */ + swi r14, r1, PTO+PT_R14 /* rewrite saved R14 value */ + swi r16, r1, PTO+PT_R16 + swi r16, r1, PTO+PT_PC; /* PC and r16 are the same */ + swi r17, r1, PTO+PT_R17 + /* save special purpose registers to pt_regs */ + mfs r11, rear; + swi r11, r1, PTO+PT_EAR; + mfs r11, resr; + swi r11, r1, PTO+PT_ESR; + mfs r11, rfsr; + swi r11, r1, PTO+PT_FSR; + + /* stack pointer is in physical address at it is decrease + * by STATE_SAVE_SIZE but we need to get correct R1 value */ + addik r11, r1, CONFIG_KERNEL_START - CONFIG_KERNEL_BASE_ADDR + STATE_SAVE_SIZE; + swi r11, r1, PTO+PT_R1 + /* MS: r31 - current pointer isn't changed */ + tovirt(r1,r1) +#ifdef CONFIG_KGDB + addi r5, r1, PTO /* pass pt_reg address as the first arg */ + la r15, r0, dbtrap_call; /* return address */ + rtbd r0, microblaze_kgdb_break + nop; +#endif + /* MS: Place handler for brki from kernel space if KGDB is OFF. + * It is very unlikely that another brki instruction is called. */ + bri 0 - addi r11, r0, 1; /* Was in kernel-mode. */ - swi r11, r1, PTO + PT_MODE; - brid 2f; - nop; /* Fill delay slot */ -1: /* User-mode state save. */ - lwi r11, r0, TOPHYS(r0_ram + PTO + PT_R11); /* restore r11 */ - lwi r1, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); /* get saved current */ +/* MS: User-mode state save - gdb */ +1: lwi r1, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); /* get saved current */ tophys(r1,r1); lwi r1, r1, TS_THREAD_INFO; /* get the thread info */ addik r1, r1, THREAD_SIZE; /* calculate kernel stack pointer */ tophys(r1,r1); addik r1, r1, -STATE_SAVE_SIZE; /* Make room on the stack. */ - swi r3, r1, PTO + PT_R3; - swi r4, r1, PTO + PT_R4; SAVE_REGS; - - swi r0, r1, PTO+PT_MODE; /* Was in user-mode. */ + swi r17, r1, PTO+PT_R17; + swi r16, r1, PTO+PT_R16; + swi r16, r1, PTO+PT_PC; /* Save LP */ + swi r0, r1, PTO + PT_MODE; /* Was in user-mode. */ lwi r11, r0, TOPHYS(PER_CPU(ENTRY_SP)); swi r11, r1, PTO+PT_R1; /* Store user SP. */ - addi r11, r0, 1; - swi r11, r0, TOPHYS(PER_CPU(KM)); /* Now we're in kernel-mode. */ -2: lwi CURRENT_TASK, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); - /* Save away the syscall number. */ - swi r0, r1, PTO+PT_R0; + lwi CURRENT_TASK, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); tovirt(r1,r1) - - addi r5, r0, SIGTRAP /* send the trap signal */ - add r6, r0, CURRENT_TASK; /* Get current task ptr into r11 */ - addk r7, r0, r0 /* 3rd param zero */ - set_vms; - la r11, r0, send_sig; - la r15, r0, dbtrap_call; -dbtrap_call: rtbd r11, 0; - nop; + addik r5, r1, PTO; + addik r15, r0, dbtrap_call; +dbtrap_call: /* Return point for kernel/user entry + 8 because of rtsd r15, 8 */ + rtbd r0, sw_exception + nop - set_bip; /* Ints masked for state restore*/ - lwi r11, r1, PTO+PT_MODE; + /* MS: The first instruction for the second part of the gdb/kgdb */ + set_bip; /* Ints masked for state restore */ + lwi r11, r1, PTO + PT_MODE; bnei r11, 2f; - +/* MS: Return to user space - gdb */ /* Get current task ptr into r11 */ lwi r11, CURRENT_TASK, TS_THREAD_INFO; /* get thread info */ lwi r11, r11, TI_FLAGS; /* get flags in thread info */ andi r11, r11, _TIF_NEED_RESCHED; beqi r11, 5f; -/* Call the scheduler before returning from a syscall/trap. */ - + /* Call the scheduler before returning from a syscall/trap. */ bralid r15, schedule; /* Call scheduler */ nop; /* delay slot */ - /* XXX Is PT_DTRACE handling needed here? */ - /* XXX m68knommu also checks TASK_STATE & TASK_COUNTER here. */ /* Maybe handle a signal */ 5: lwi r11, CURRENT_TASK, TS_THREAD_INFO; /* get thread info */ @@ -955,54 +837,40 @@ dbtrap_call: rtbd r11, 0; andi r11, r11, _TIF_SIGPENDING; beqi r11, 1f; /* Signals to handle, handle them */ -/* Handle a signal return; Pending signals should be in r18. */ - /* Not all registers are saved by the normal trap/interrupt entry - points (for instance, call-saved registers (because the normal - C-compiler calling sequence in the kernel makes sure they're - preserved), and call-clobbered registers in the case of - traps), but signal handlers may want to examine or change the - complete register state. Here we save anything not saved by - the normal entry sequence, so that it may be safely restored - (in a possibly modified form) after do_signal returns. */ - - la r5, r1, PTO; /* Arg 1: struct pt_regs *regs */ + addik r5, r1, PTO; /* Arg 1: struct pt_regs *regs */ addi r7, r0, 0; /* Arg 3: int in_syscall */ bralid r15, do_signal; /* Handle any signals */ add r6, r0, r0; /* Arg 2: sigset_t *oldset */ - /* Finally, return to user state. */ -1: swi r0, r0, PER_CPU(KM); /* Now officially in user state. */ - swi CURRENT_TASK, r0, PER_CPU(CURRENT_SAVE); /* save current */ +1: swi CURRENT_TASK, r0, PER_CPU(CURRENT_SAVE); /* save current */ VM_OFF; tophys(r1,r1); - - lwi r3, r1, PTO+PT_R3; /* restore saved r3, r4 registers */ - lwi r4, r1, PTO+PT_R4; + /* MS: Restore all regs */ RESTORE_REGS - addik r1, r1, STATE_SAVE_SIZE /* Clean up stack space. */ - - - lwi r1, r1, PT_R1 - PT_SIZE; - /* Restore user stack pointer. */ - bri 6f; + lwi r17, r1, PTO+PT_R17; + lwi r16, r1, PTO+PT_R16; + addik r1, r1, STATE_SAVE_SIZE /* Clean up stack space */ + lwi r1, r1, PT_R1 - PT_SIZE; /* Restore user stack pointer */ +DBTRAP_return_user: /* MS: Make global symbol for debugging */ + rtbd r16, 0; /* MS: Instructions to return from a debug trap */ + nop; -/* Return to kernel state. */ +/* MS: Return to kernel state - kgdb */ 2: VM_OFF; tophys(r1,r1); - lwi r3, r1, PTO+PT_R3; /* restore saved r3, r4 registers */ - lwi r4, r1, PTO+PT_R4; + /* MS: Restore all regs */ RESTORE_REGS - addik r1, r1, STATE_SAVE_SIZE /* Clean up stack space. */ - + lwi r14, r1, PTO+PT_R14; + lwi r16, r1, PTO+PT_PC; + lwi r17, r1, PTO+PT_R17; + addik r1, r1, STATE_SAVE_SIZE; /* MS: Clean up stack space */ tovirt(r1,r1); -6: -DBTRAP_return: /* Make global symbol for debugging */ - rtbd r14, 0; /* Instructions to return from an IRQ */ +DBTRAP_return_kernel: /* MS: Make global symbol for debugging */ + rtbd r16, 0; /* MS: Instructions to return from a debug trap */ nop; - ENTRY(_switch_to) /* prepare return value */ addk r3, r0, CURRENT_TASK @@ -1037,16 +905,12 @@ ENTRY(_switch_to) swi r30, r11, CC_R30 /* special purpose registers */ mfs r12, rmsr - nop swi r12, r11, CC_MSR mfs r12, rear - nop swi r12, r11, CC_EAR mfs r12, resr - nop swi r12, r11, CC_ESR mfs r12, rfsr - nop swi r12, r11, CC_FSR /* update r31, the current-give me pointer to task which will be next */ @@ -1085,10 +949,8 @@ ENTRY(_switch_to) /* special purpose registers */ lwi r12, r11, CC_FSR mts rfsr, r12 - nop lwi r12, r11, CC_MSR mts rmsr, r12 - nop rtsd r15, 8 nop @@ -1096,15 +958,6 @@ ENTRY(_switch_to) ENTRY(_reset) brai 0x70; /* Jump back to FS-boot */ -ENTRY(_break) - mfs r5, rmsr - nop - swi r5, r0, 0x250 + TOPHYS(r0_ram) - mfs r5, resr - nop - swi r5, r0, 0x254 + TOPHYS(r0_ram) - bri 0 - /* These are compiled and loaded into high memory, then * copied into place in mach_early_setup */ .section .init.ivt, "ax" @@ -1116,14 +969,38 @@ ENTRY(_break) nop brai TOPHYS(_user_exception); /* syscall handler */ brai TOPHYS(_interrupt); /* Interrupt handler */ - brai TOPHYS(_break); /* nmi trap handler */ + brai TOPHYS(_debug_exception); /* debug trap handler */ brai TOPHYS(_hw_exception_handler); /* HW exception handler */ - .org 0x60 - brai TOPHYS(_debug_exception); /* debug trap handler*/ - .section .rodata,"a" #include "syscall_table.S" syscall_table_size=(.-sys_call_table) +type_SYSCALL: + .ascii "SYSCALL\0" +type_IRQ: + .ascii "IRQ\0" +type_IRQ_PREEMPT: + .ascii "IRQ (PREEMPTED)\0" +type_SYSCALL_PREEMPT: + .ascii " SYSCALL (PREEMPTED)\0" + + /* + * Trap decoding for stack unwinder + * Tuples are (start addr, end addr, string) + * If return address lies on [start addr, end addr], + * unwinder displays 'string' + */ + + .align 4 +.global microblaze_trap_handlers +microblaze_trap_handlers: + /* Exact matches come first */ + .word ret_from_trap; .word ret_from_trap ; .word type_SYSCALL + .word ret_from_irq ; .word ret_from_irq ; .word type_IRQ + /* Fuzzy matches go here */ + .word ret_from_irq ; .word no_intr_resched ; .word type_IRQ_PREEMPT + .word ret_from_trap; .word TRAP_return ; .word type_SYSCALL_PREEMPT + /* End of table */ + .word 0 ; .word 0 ; .word 0 diff --git a/arch/microblaze/kernel/exceptions.c b/arch/microblaze/kernel/exceptions.c index 02cbdfe5aa8..b98ee8d0c1c 100644 --- a/arch/microblaze/kernel/exceptions.c +++ b/arch/microblaze/kernel/exceptions.c @@ -48,12 +48,17 @@ void die(const char *str, struct pt_regs *fp, long err) do_exit(err); } +/* for user application debugging */ +void sw_exception(struct pt_regs *regs) +{ + _exception(SIGTRAP, regs, TRAP_BRKPT, regs->r16); +} + void _exception(int signr, struct pt_regs *regs, int code, unsigned long addr) { siginfo_t info; if (kernel_mode(regs)) { - debugger(regs); die("Exception in kernel mode", regs, signr); } info.si_signo = signr; @@ -143,7 +148,7 @@ asmlinkage void full_exception(struct pt_regs *regs, unsigned int type, #ifdef CONFIG_MMU case MICROBLAZE_PRIVILEGED_EXCEPTION: pr_debug(KERN_WARNING "Privileged exception\n"); - /* "brk r0,r0" - used as debug breakpoint */ + /* "brk r0,r0" - used as debug breakpoint - old toolchain */ if (get_user(code, (unsigned long *)regs->pc) == 0 && code == 0x980c0000) { _exception(SIGTRAP, regs, TRAP_BRKPT, addr); diff --git a/arch/microblaze/kernel/head.S b/arch/microblaze/kernel/head.S index 1bf73988826..42434008209 100644 --- a/arch/microblaze/kernel/head.S +++ b/arch/microblaze/kernel/head.S @@ -43,10 +43,10 @@ .global empty_zero_page .align 12 empty_zero_page: - .space 4096 + .space PAGE_SIZE .global swapper_pg_dir swapper_pg_dir: - .space 4096 + .space PAGE_SIZE #endif /* CONFIG_MMU */ diff --git a/arch/microblaze/kernel/hw_exception_handler.S b/arch/microblaze/kernel/hw_exception_handler.S index 995a2123635..781195438ee 100644 --- a/arch/microblaze/kernel/hw_exception_handler.S +++ b/arch/microblaze/kernel/hw_exception_handler.S @@ -78,9 +78,6 @@ #include <asm/asm-offsets.h> /* Helpful Macros */ -#ifndef CONFIG_MMU -#define EX_HANDLER_STACK_SIZ (4*19) -#endif #define NUM_TO_REG(num) r ## num #ifdef CONFIG_MMU @@ -988,6 +985,7 @@ ex_unaligned_fixup: .end _unaligned_data_exception #endif /* CONFIG_MMU */ +.global ex_handler_unhandled ex_handler_unhandled: /* FIXME add handle function for unhandled exception - dump register */ bri 0 diff --git a/arch/microblaze/kernel/irq.c b/arch/microblaze/kernel/irq.c index 8f120aca123..a9345fb4906 100644 --- a/arch/microblaze/kernel/irq.c +++ b/arch/microblaze/kernel/irq.c @@ -17,26 +17,17 @@ #include <linux/seq_file.h> #include <linux/kernel_stat.h> #include <linux/irq.h> +#include <linux/of_irq.h> #include <asm/prom.h> -unsigned int irq_of_parse_and_map(struct device_node *dev, int index) -{ - struct of_irq oirq; - - if (of_irq_map_one(dev, index, &oirq)) - return NO_IRQ; - - return oirq.specifier[0]; -} -EXPORT_SYMBOL_GPL(irq_of_parse_and_map); - static u32 concurrent_irq; void __irq_entry do_IRQ(struct pt_regs *regs) { unsigned int irq; struct pt_regs *old_regs = set_irq_regs(regs); + trace_hardirqs_off(); irq_enter(); irq = get_irq(regs); @@ -53,6 +44,7 @@ next_irq: irq_exit(); set_irq_regs(old_regs); + trace_hardirqs_on(); } int show_interrupts(struct seq_file *p, void *v) @@ -104,7 +96,7 @@ unsigned int irq_create_mapping(struct irq_host *host, irq_hw_number_t hwirq) EXPORT_SYMBOL_GPL(irq_create_mapping); unsigned int irq_create_of_mapping(struct device_node *controller, - u32 *intspec, unsigned int intsize) + const u32 *intspec, unsigned int intsize) { return intspec[0]; } diff --git a/arch/microblaze/kernel/kgdb.c b/arch/microblaze/kernel/kgdb.c new file mode 100644 index 00000000000..bfc006b7f2d --- /dev/null +++ b/arch/microblaze/kernel/kgdb.c @@ -0,0 +1,147 @@ +/* + * Microblaze KGDB support + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ + +#include <linux/kgdb.h> +#include <linux/kdebug.h> +#include <linux/irq.h> +#include <linux/io.h> +#include <asm/cacheflush.h> +#include <asm/asm-offsets.h> +#include <asm/pvr.h> + +#define GDB_REG 0 +#define GDB_PC 32 +#define GDB_MSR 33 +#define GDB_EAR 34 +#define GDB_ESR 35 +#define GDB_FSR 36 +#define GDB_BTR 37 +#define GDB_PVR 38 +#define GDB_REDR 50 +#define GDB_RPID 51 +#define GDB_RZPR 52 +#define GDB_RTLBX 53 +#define GDB_RTLBSX 54 /* mfs can't read it */ +#define GDB_RTLBLO 55 +#define GDB_RTLBHI 56 + +/* keep pvr separately because it is unchangeble */ +struct pvr_s pvr; + +void pt_regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *regs) +{ + int i; + unsigned long *pt_regb = (unsigned long *)regs; + int temp; + /* registers r0 - r31, pc, msr, ear, esr, fsr + do not save pt_mode */ + for (i = 0; i < (sizeof(struct pt_regs) / 4) - 1; i++) + gdb_regs[i] = pt_regb[i]; + + /* Branch target register can't be changed */ + __asm__ __volatile__ ("mfs %0, rbtr;" : "=r"(temp) : ); + gdb_regs[GDB_BTR] = temp; + + /* pvr part - we have 11 pvr regs */ + for (i = 0; i < sizeof(struct pvr_s)/4; i++) + gdb_regs[GDB_PVR + i] = pvr.pvr[i]; + + /* read special registers - can't be changed */ + __asm__ __volatile__ ("mfs %0, redr;" : "=r"(temp) : ); + gdb_regs[GDB_REDR] = temp; + __asm__ __volatile__ ("mfs %0, rpid;" : "=r"(temp) : ); + gdb_regs[GDB_RPID] = temp; + __asm__ __volatile__ ("mfs %0, rzpr;" : "=r"(temp) : ); + gdb_regs[GDB_RZPR] = temp; + __asm__ __volatile__ ("mfs %0, rtlbx;" : "=r"(temp) : ); + gdb_regs[GDB_RTLBX] = temp; + __asm__ __volatile__ ("mfs %0, rtlblo;" : "=r"(temp) : ); + gdb_regs[GDB_RTLBLO] = temp; + __asm__ __volatile__ ("mfs %0, rtlbhi;" : "=r"(temp) : ); + gdb_regs[GDB_RTLBHI] = temp; +} + +void gdb_regs_to_pt_regs(unsigned long *gdb_regs, struct pt_regs *regs) +{ + int i; + unsigned long *pt_regb = (unsigned long *)regs; + + /* pt_regs and gdb_regs have the same 37 values. + * The rest of gdb_regs are unused and can't be changed. + * r0 register value can't be changed too. */ + for (i = 1; i < (sizeof(struct pt_regs) / 4) - 1; i++) + pt_regb[i] = gdb_regs[i]; +} + +void microblaze_kgdb_break(struct pt_regs *regs) +{ + if (kgdb_handle_exception(1, SIGTRAP, 0, regs) != 0) + return 0; + + /* Jump over the first arch_kgdb_breakpoint which is barrier to + * get kgdb work. The same solution is used for powerpc */ + if (*(u32 *) (regs->pc) == *(u32 *) (&arch_kgdb_ops.gdb_bpt_instr)) + regs->pc += BREAK_INSTR_SIZE; +} + +/* untested */ +void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p) +{ + int i; + unsigned long *pt_regb = (unsigned long *)(p->thread.regs); + + /* registers r0 - r31, pc, msr, ear, esr, fsr + do not save pt_mode */ + for (i = 0; i < (sizeof(struct pt_regs) / 4) - 1; i++) + gdb_regs[i] = pt_regb[i]; + + /* pvr part - we have 11 pvr regs */ + for (i = 0; i < sizeof(struct pvr_s)/4; i++) + gdb_regs[GDB_PVR + i] = pvr.pvr[i]; +} + +void kgdb_arch_set_pc(struct pt_regs *regs, unsigned long ip) +{ + regs->pc = ip; +} + +int kgdb_arch_handle_exception(int vector, int signo, int err_code, + char *remcom_in_buffer, char *remcom_out_buffer, + struct pt_regs *regs) +{ + char *ptr; + unsigned long address; + int cpu = smp_processor_id(); + + switch (remcom_in_buffer[0]) { + case 'c': + /* handle the optional parameter */ + ptr = &remcom_in_buffer[1]; + if (kgdb_hex2long(&ptr, &address)) + regs->pc = address; + + return 0; + } + return -1; /* this means that we do not want to exit from the handler */ +} + +int kgdb_arch_init(void) +{ + get_pvr(&pvr); /* Fill PVR structure */ + return 0; +} + +void kgdb_arch_exit(void) +{ + /* Nothing to do */ +} + +/* + * Global data + */ +struct kgdb_arch arch_kgdb_ops = { + .gdb_bpt_instr = {0xba, 0x0c, 0x00, 0x18}, /* brki r16, 0x18 */ +}; diff --git a/arch/microblaze/kernel/misc.S b/arch/microblaze/kernel/misc.S index 0fb5fc6c1fc..206da3da361 100644 --- a/arch/microblaze/kernel/misc.S +++ b/arch/microblaze/kernel/misc.S @@ -76,7 +76,7 @@ early_console_reg_tlb_alloc: * the UARTs nice and early. We use a 4k real==virtual mapping. */ ori r4, r0, MICROBLAZE_TLB_SIZE - 1 - mts rtlbx, r4 /* TLB slot 2 */ + mts rtlbx, r4 /* TLB slot 63 */ or r4,r5,r0 andi r4,r4,0xfffff000 diff --git a/arch/microblaze/kernel/of_device.c b/arch/microblaze/kernel/of_device.c deleted file mode 100644 index b372787886e..00000000000 --- a/arch/microblaze/kernel/of_device.c +++ /dev/null @@ -1,112 +0,0 @@ -#include <linux/string.h> -#include <linux/kernel.h> -#include <linux/of.h> -#include <linux/init.h> -#include <linux/module.h> -#include <linux/mod_devicetable.h> -#include <linux/slab.h> -#include <linux/of_device.h> - -#include <linux/errno.h> - -void of_device_make_bus_id(struct of_device *dev) -{ - static atomic_t bus_no_reg_magic; - struct device_node *node = dev->dev.of_node; - const u32 *reg; - u64 addr; - int magic; - - /* - * For MMIO, get the physical address - */ - reg = of_get_property(node, "reg", NULL); - if (reg) { - addr = of_translate_address(node, reg); - if (addr != OF_BAD_ADDR) { - dev_set_name(&dev->dev, "%llx.%s", - (unsigned long long)addr, node->name); - return; - } - } - - /* - * No BusID, use the node name and add a globally incremented - * counter (and pray...) - */ - magic = atomic_add_return(1, &bus_no_reg_magic); - dev_set_name(&dev->dev, "%s.%d", node->name, magic - 1); -} -EXPORT_SYMBOL(of_device_make_bus_id); - -struct of_device *of_device_alloc(struct device_node *np, - const char *bus_id, - struct device *parent) -{ - struct of_device *dev; - - dev = kzalloc(sizeof(*dev), GFP_KERNEL); - if (!dev) - return NULL; - - dev->dev.of_node = of_node_get(np); - dev->dev.dma_mask = &dev->archdata.dma_mask; - dev->dev.parent = parent; - dev->dev.release = of_release_dev; - - if (bus_id) - dev_set_name(&dev->dev, bus_id); - else - of_device_make_bus_id(dev); - - return dev; -} -EXPORT_SYMBOL(of_device_alloc); - -int of_device_uevent(struct device *dev, struct kobj_uevent_env *env) -{ - struct of_device *ofdev; - const char *compat; - int seen = 0, cplen, sl; - - if (!dev) - return -ENODEV; - - ofdev = to_of_device(dev); - - if (add_uevent_var(env, "OF_NAME=%s", ofdev->dev.of_node->name)) - return -ENOMEM; - - if (add_uevent_var(env, "OF_TYPE=%s", ofdev->dev.of_node->type)) - return -ENOMEM; - - /* Since the compatible field can contain pretty much anything - * it's not really legal to split it out with commas. We split it - * up using a number of environment variables instead. */ - - compat = of_get_property(ofdev->dev.of_node, "compatible", &cplen); - while (compat && *compat && cplen > 0) { - if (add_uevent_var(env, "OF_COMPATIBLE_%d=%s", seen, compat)) - return -ENOMEM; - - sl = strlen(compat) + 1; - compat += sl; - cplen -= sl; - seen++; - } - - if (add_uevent_var(env, "OF_COMPATIBLE_N=%d", seen)) - return -ENOMEM; - - /* modalias is trickier, we add it in 2 steps */ - if (add_uevent_var(env, "MODALIAS=")) - return -ENOMEM; - sl = of_device_get_modalias(ofdev, &env->buf[env->buflen-1], - sizeof(env->buf) - env->buflen); - if (sl >= (sizeof(env->buf) - env->buflen)) - return -ENOMEM; - env->buflen += sl; - - return 0; -} -EXPORT_SYMBOL(of_device_uevent); diff --git a/arch/microblaze/kernel/of_platform.c b/arch/microblaze/kernel/of_platform.c deleted file mode 100644 index ccf6f4257f4..00000000000 --- a/arch/microblaze/kernel/of_platform.c +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Copyright (C) 2006 Benjamin Herrenschmidt, IBM Corp. - * <benh@kernel.crashing.org> - * and Arnd Bergmann, IBM Corp. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - * - */ - -#undef DEBUG - -#include <linux/string.h> -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/module.h> -#include <linux/mod_devicetable.h> -#include <linux/pci.h> -#include <linux/of.h> -#include <linux/of_device.h> -#include <linux/of_platform.h> - -#include <linux/errno.h> -#include <linux/topology.h> -#include <asm/atomic.h> - -struct bus_type of_platform_bus_type = { - .uevent = of_device_uevent, -}; -EXPORT_SYMBOL(of_platform_bus_type); - -static int __init of_bus_driver_init(void) -{ - return of_bus_type_init(&of_platform_bus_type, "of_platform"); -} -postcore_initcall(of_bus_driver_init); - -struct of_device *of_platform_device_create(struct device_node *np, - const char *bus_id, - struct device *parent) -{ - struct of_device *dev; - - dev = of_device_alloc(np, bus_id, parent); - if (!dev) - return NULL; - - dev->archdata.dma_mask = 0xffffffffUL; - dev->dev.bus = &of_platform_bus_type; - - /* We do not fill the DMA ops for platform devices by default. - * This is currently the responsibility of the platform code - * to do such, possibly using a device notifier - */ - - if (of_device_register(dev) != 0) { - of_device_free(dev); - return NULL; - } - - return dev; -} -EXPORT_SYMBOL(of_platform_device_create); - -/** - * of_platform_bus_create - Create an OF device for a bus node and all its - * children. Optionally recursively instanciate matching busses. - * @bus: device node of the bus to instanciate - * @matches: match table, NULL to use the default, OF_NO_DEEP_PROBE to - * disallow recursive creation of child busses - */ -static int of_platform_bus_create(const struct device_node *bus, - const struct of_device_id *matches, - struct device *parent) -{ - struct device_node *child; - struct of_device *dev; - int rc = 0; - - for_each_child_of_node(bus, child) { - pr_debug(" create child: %s\n", child->full_name); - dev = of_platform_device_create(child, NULL, parent); - if (dev == NULL) - rc = -ENOMEM; - else if (!of_match_node(matches, child)) - continue; - if (rc == 0) { - pr_debug(" and sub busses\n"); - rc = of_platform_bus_create(child, matches, &dev->dev); - } - if (rc) { - of_node_put(child); - break; - } - } - return rc; -} - - -/** - * of_platform_bus_probe - Probe the device-tree for platform busses - * @root: parent of the first level to probe or NULL for the root of the tree - * @matches: match table, NULL to use the default - * @parent: parent to hook devices from, NULL for toplevel - * - * Note that children of the provided root are not instanciated as devices - * unless the specified root itself matches the bus list and is not NULL. - */ - -int of_platform_bus_probe(struct device_node *root, - const struct of_device_id *matches, - struct device *parent) -{ - struct device_node *child; - struct of_device *dev; - int rc = 0; - - if (matches == NULL) - matches = of_default_bus_ids; - if (matches == OF_NO_DEEP_PROBE) - return -EINVAL; - if (root == NULL) - root = of_find_node_by_path("/"); - else - of_node_get(root); - - pr_debug("of_platform_bus_probe()\n"); - pr_debug(" starting at: %s\n", root->full_name); - - /* Do a self check of bus type, if there's a match, create - * children - */ - if (of_match_node(matches, root)) { - pr_debug(" root match, create all sub devices\n"); - dev = of_platform_device_create(root, NULL, parent); - if (dev == NULL) { - rc = -ENOMEM; - goto bail; - } - pr_debug(" create all sub busses\n"); - rc = of_platform_bus_create(root, matches, &dev->dev); - goto bail; - } - for_each_child_of_node(root, child) { - if (!of_match_node(matches, child)) - continue; - - pr_debug(" match: %s\n", child->full_name); - dev = of_platform_device_create(child, NULL, parent); - if (dev == NULL) - rc = -ENOMEM; - else - rc = of_platform_bus_create(child, matches, &dev->dev); - if (rc) { - of_node_put(child); - break; - } - } - bail: - of_node_put(root); - return rc; -} -EXPORT_SYMBOL(of_platform_bus_probe); - -static int of_dev_node_match(struct device *dev, void *data) -{ - return to_of_device(dev)->dev.of_node == data; -} - -struct of_device *of_find_device_by_node(struct device_node *np) -{ - struct device *dev; - - dev = bus_find_device(&of_platform_bus_type, - NULL, np, of_dev_node_match); - if (dev) - return to_of_device(dev); - return NULL; -} -EXPORT_SYMBOL(of_find_device_by_node); - -static int of_dev_phandle_match(struct device *dev, void *data) -{ - phandle *ph = data; - return to_of_device(dev)->dev.of_node->phandle == *ph; -} - -struct of_device *of_find_device_by_phandle(phandle ph) -{ - struct device *dev; - - dev = bus_find_device(&of_platform_bus_type, - NULL, &ph, of_dev_phandle_match); - if (dev) - return to_of_device(dev); - return NULL; -} -EXPORT_SYMBOL(of_find_device_by_phandle); diff --git a/arch/microblaze/kernel/process.c b/arch/microblaze/kernel/process.c index 09bed44dfcd..ba7c4b16ed3 100644 --- a/arch/microblaze/kernel/process.c +++ b/arch/microblaze/kernel/process.c @@ -76,8 +76,11 @@ __setup("hlt", hlt_setup); void default_idle(void) { if (likely(hlt_counter)) { - while (!need_resched()) - cpu_relax(); + local_irq_disable(); + stop_critical_timings(); + cpu_relax(); + start_critical_timings(); + local_irq_enable(); } else { clear_thread_flag(TIF_POLLING_NRFLAG); smp_mb__after_clear_bit(); diff --git a/arch/microblaze/kernel/prom_parse.c b/arch/microblaze/kernel/prom_parse.c index bf7e6c27e31..d33ba17601f 100644 --- a/arch/microblaze/kernel/prom_parse.c +++ b/arch/microblaze/kernel/prom_parse.c @@ -6,219 +6,11 @@ #include <linux/module.h> #include <linux/ioport.h> #include <linux/etherdevice.h> +#include <linux/of_address.h> #include <asm/prom.h> #include <asm/pci-bridge.h> -#define PRu64 "%llx" - -/* Max address size we deal with */ -#define OF_MAX_ADDR_CELLS 4 -#define OF_CHECK_COUNTS(na, ns) ((na) > 0 && (na) <= OF_MAX_ADDR_CELLS && \ - (ns) > 0) - -static struct of_bus *of_match_bus(struct device_node *np); -static int __of_address_to_resource(struct device_node *dev, - const u32 *addrp, u64 size, unsigned int flags, - struct resource *r); - -/* Debug utility */ -#ifdef DEBUG -static void of_dump_addr(const char *s, const u32 *addr, int na) -{ - printk(KERN_INFO "%s", s); - while (na--) - printk(KERN_INFO " %08x", *(addr++)); - printk(KERN_INFO "\n"); -} -#else -static void of_dump_addr(const char *s, const u32 *addr, int na) { } -#endif - -/* Callbacks for bus specific translators */ -struct of_bus { - const char *name; - const char *addresses; - int (*match)(struct device_node *parent); - void (*count_cells)(struct device_node *child, - int *addrc, int *sizec); - u64 (*map)(u32 *addr, const u32 *range, - int na, int ns, int pna); - int (*translate)(u32 *addr, u64 offset, int na); - unsigned int (*get_flags)(const u32 *addr); -}; - -/* - * Default translator (generic bus) - */ - -static void of_bus_default_count_cells(struct device_node *dev, - int *addrc, int *sizec) -{ - if (addrc) - *addrc = of_n_addr_cells(dev); - if (sizec) - *sizec = of_n_size_cells(dev); -} - -static u64 of_bus_default_map(u32 *addr, const u32 *range, - int na, int ns, int pna) -{ - u64 cp, s, da; - - cp = of_read_number(range, na); - s = of_read_number(range + na + pna, ns); - da = of_read_number(addr, na); - - pr_debug("OF: default map, cp="PRu64", s="PRu64", da="PRu64"\n", - cp, s, da); - - if (da < cp || da >= (cp + s)) - return OF_BAD_ADDR; - return da - cp; -} - -static int of_bus_default_translate(u32 *addr, u64 offset, int na) -{ - u64 a = of_read_number(addr, na); - memset(addr, 0, na * 4); - a += offset; - if (na > 1) - addr[na - 2] = a >> 32; - addr[na - 1] = a & 0xffffffffu; - - return 0; -} - -static unsigned int of_bus_default_get_flags(const u32 *addr) -{ - return IORESOURCE_MEM; -} - #ifdef CONFIG_PCI -/* - * PCI bus specific translator - */ - -static int of_bus_pci_match(struct device_node *np) -{ - /* "vci" is for the /chaos bridge on 1st-gen PCI powermacs */ - return !strcmp(np->type, "pci") || !strcmp(np->type, "vci"); -} - -static void of_bus_pci_count_cells(struct device_node *np, - int *addrc, int *sizec) -{ - if (addrc) - *addrc = 3; - if (sizec) - *sizec = 2; -} - -static u64 of_bus_pci_map(u32 *addr, const u32 *range, int na, int ns, int pna) -{ - u64 cp, s, da; - - /* Check address type match */ - if ((addr[0] ^ range[0]) & 0x03000000) - return OF_BAD_ADDR; - - /* Read address values, skipping high cell */ - cp = of_read_number(range + 1, na - 1); - s = of_read_number(range + na + pna, ns); - da = of_read_number(addr + 1, na - 1); - - pr_debug("OF: PCI map, cp="PRu64", s="PRu64", da="PRu64"\n", cp, s, da); - - if (da < cp || da >= (cp + s)) - return OF_BAD_ADDR; - return da - cp; -} - -static int of_bus_pci_translate(u32 *addr, u64 offset, int na) -{ - return of_bus_default_translate(addr + 1, offset, na - 1); -} - -static unsigned int of_bus_pci_get_flags(const u32 *addr) -{ - unsigned int flags = 0; - u32 w = addr[0]; - - switch ((w >> 24) & 0x03) { - case 0x01: - flags |= IORESOURCE_IO; - break; - case 0x02: /* 32 bits */ - case 0x03: /* 64 bits */ - flags |= IORESOURCE_MEM; - break; - } - if (w & 0x40000000) - flags |= IORESOURCE_PREFETCH; - return flags; -} - -const u32 *of_get_pci_address(struct device_node *dev, int bar_no, u64 *size, - unsigned int *flags) -{ - const u32 *prop; - unsigned int psize; - struct device_node *parent; - struct of_bus *bus; - int onesize, i, na, ns; - - /* Get parent & match bus type */ - parent = of_get_parent(dev); - if (parent == NULL) - return NULL; - bus = of_match_bus(parent); - if (strcmp(bus->name, "pci")) { - of_node_put(parent); - return NULL; - } - bus->count_cells(dev, &na, &ns); - of_node_put(parent); - if (!OF_CHECK_COUNTS(na, ns)) - return NULL; - - /* Get "reg" or "assigned-addresses" property */ - prop = of_get_property(dev, bus->addresses, &psize); - if (prop == NULL) - return NULL; - psize /= 4; - - onesize = na + ns; - for (i = 0; psize >= onesize; psize -= onesize, prop += onesize, i++) - if ((prop[0] & 0xff) == ((bar_no * 4) + PCI_BASE_ADDRESS_0)) { - if (size) - *size = of_read_number(prop + na, ns); - if (flags) - *flags = bus->get_flags(prop); - return prop; - } - return NULL; -} -EXPORT_SYMBOL(of_get_pci_address); - -int of_pci_address_to_resource(struct device_node *dev, int bar, - struct resource *r) -{ - const u32 *addrp; - u64 size; - unsigned int flags; - - addrp = of_get_pci_address(dev, bar, &size, &flags); - if (addrp == NULL) - return -EINVAL; - return __of_address_to_resource(dev, addrp, size, flags, r); -} -EXPORT_SYMBOL_GPL(of_pci_address_to_resource); - -static u8 of_irq_pci_swizzle(u8 slot, u8 pin) -{ - return (((pin - 1) + slot) % 4) + 1; -} - int of_irq_map_pci(struct pci_dev *pdev, struct of_irq *out_irq) { struct device_node *dn, *ppnode; @@ -293,331 +85,6 @@ int of_irq_map_pci(struct pci_dev *pdev, struct of_irq *out_irq) EXPORT_SYMBOL_GPL(of_irq_map_pci); #endif /* CONFIG_PCI */ -/* - * ISA bus specific translator - */ - -static int of_bus_isa_match(struct device_node *np) -{ - return !strcmp(np->name, "isa"); -} - -static void of_bus_isa_count_cells(struct device_node *child, - int *addrc, int *sizec) -{ - if (addrc) - *addrc = 2; - if (sizec) - *sizec = 1; -} - -static u64 of_bus_isa_map(u32 *addr, const u32 *range, int na, int ns, int pna) -{ - u64 cp, s, da; - - /* Check address type match */ - if ((addr[0] ^ range[0]) & 0x00000001) - return OF_BAD_ADDR; - - /* Read address values, skipping high cell */ - cp = of_read_number(range + 1, na - 1); - s = of_read_number(range + na + pna, ns); - da = of_read_number(addr + 1, na - 1); - - pr_debug("OF: ISA map, cp="PRu64", s="PRu64", da="PRu64"\n", cp, s, da); - - if (da < cp || da >= (cp + s)) - return OF_BAD_ADDR; - return da - cp; -} - -static int of_bus_isa_translate(u32 *addr, u64 offset, int na) -{ - return of_bus_default_translate(addr + 1, offset, na - 1); -} - -static unsigned int of_bus_isa_get_flags(const u32 *addr) -{ - unsigned int flags = 0; - u32 w = addr[0]; - - if (w & 1) - flags |= IORESOURCE_IO; - else - flags |= IORESOURCE_MEM; - return flags; -} - -/* - * Array of bus specific translators - */ - -static struct of_bus of_busses[] = { -#ifdef CONFIG_PCI - /* PCI */ - { - .name = "pci", - .addresses = "assigned-addresses", - .match = of_bus_pci_match, - .count_cells = of_bus_pci_count_cells, - .map = of_bus_pci_map, - .translate = of_bus_pci_translate, - .get_flags = of_bus_pci_get_flags, - }, -#endif /* CONFIG_PCI */ - /* ISA */ - { - .name = "isa", - .addresses = "reg", - .match = of_bus_isa_match, - .count_cells = of_bus_isa_count_cells, - .map = of_bus_isa_map, - .translate = of_bus_isa_translate, - .get_flags = of_bus_isa_get_flags, - }, - /* Default */ - { - .name = "default", - .addresses = "reg", - .match = NULL, - .count_cells = of_bus_default_count_cells, - .map = of_bus_default_map, - .translate = of_bus_default_translate, - .get_flags = of_bus_default_get_flags, - }, -}; - -static struct of_bus *of_match_bus(struct device_node *np) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(of_busses); i++) - if (!of_busses[i].match || of_busses[i].match(np)) - return &of_busses[i]; - BUG(); - return NULL; -} - -static int of_translate_one(struct device_node *parent, struct of_bus *bus, - struct of_bus *pbus, u32 *addr, - int na, int ns, int pna) -{ - const u32 *ranges; - unsigned int rlen; - int rone; - u64 offset = OF_BAD_ADDR; - - /* Normally, an absence of a "ranges" property means we are - * crossing a non-translatable boundary, and thus the addresses - * below the current not cannot be converted to CPU physical ones. - * Unfortunately, while this is very clear in the spec, it's not - * what Apple understood, and they do have things like /uni-n or - * /ht nodes with no "ranges" property and a lot of perfectly - * useable mapped devices below them. Thus we treat the absence of - * "ranges" as equivalent to an empty "ranges" property which means - * a 1:1 translation at that level. It's up to the caller not to try - * to translate addresses that aren't supposed to be translated in - * the first place. --BenH. - */ - ranges = of_get_property(parent, "ranges", (int *) &rlen); - if (ranges == NULL || rlen == 0) { - offset = of_read_number(addr, na); - memset(addr, 0, pna * 4); - pr_debug("OF: no ranges, 1:1 translation\n"); - goto finish; - } - - pr_debug("OF: walking ranges...\n"); - - /* Now walk through the ranges */ - rlen /= 4; - rone = na + pna + ns; - for (; rlen >= rone; rlen -= rone, ranges += rone) { - offset = bus->map(addr, ranges, na, ns, pna); - if (offset != OF_BAD_ADDR) - break; - } - if (offset == OF_BAD_ADDR) { - pr_debug("OF: not found !\n"); - return 1; - } - memcpy(addr, ranges + na, 4 * pna); - - finish: - of_dump_addr("OF: parent translation for:", addr, pna); - pr_debug("OF: with offset: "PRu64"\n", offset); - - /* Translate it into parent bus space */ - return pbus->translate(addr, offset, pna); -} - -/* - * Translate an address from the device-tree into a CPU physical address, - * this walks up the tree and applies the various bus mappings on the - * way. - * - * Note: We consider that crossing any level with #size-cells == 0 to mean - * that translation is impossible (that is we are not dealing with a value - * that can be mapped to a cpu physical address). This is not really specified - * that way, but this is traditionally the way IBM at least do things - */ -u64 of_translate_address(struct device_node *dev, const u32 *in_addr) -{ - struct device_node *parent = NULL; - struct of_bus *bus, *pbus; - u32 addr[OF_MAX_ADDR_CELLS]; - int na, ns, pna, pns; - u64 result = OF_BAD_ADDR; - - pr_debug("OF: ** translation for device %s **\n", dev->full_name); - - /* Increase refcount at current level */ - of_node_get(dev); - - /* Get parent & match bus type */ - parent = of_get_parent(dev); - if (parent == NULL) - goto bail; - bus = of_match_bus(parent); - - /* Cound address cells & copy address locally */ - bus->count_cells(dev, &na, &ns); - if (!OF_CHECK_COUNTS(na, ns)) { - printk(KERN_ERR "prom_parse: Bad cell count for %s\n", - dev->full_name); - goto bail; - } - memcpy(addr, in_addr, na * 4); - - pr_debug("OF: bus is %s (na=%d, ns=%d) on %s\n", - bus->name, na, ns, parent->full_name); - of_dump_addr("OF: translating address:", addr, na); - - /* Translate */ - for (;;) { - /* Switch to parent bus */ - of_node_put(dev); - dev = parent; - parent = of_get_parent(dev); - - /* If root, we have finished */ - if (parent == NULL) { - pr_debug("OF: reached root node\n"); - result = of_read_number(addr, na); - break; - } - - /* Get new parent bus and counts */ - pbus = of_match_bus(parent); - pbus->count_cells(dev, &pna, &pns); - if (!OF_CHECK_COUNTS(pna, pns)) { - printk(KERN_ERR "prom_parse: Bad cell count for %s\n", - dev->full_name); - break; - } - - pr_debug("OF: parent bus is %s (na=%d, ns=%d) on %s\n", - pbus->name, pna, pns, parent->full_name); - - /* Apply bus translation */ - if (of_translate_one(dev, bus, pbus, addr, na, ns, pna)) - break; - - /* Complete the move up one level */ - na = pna; - ns = pns; - bus = pbus; - - of_dump_addr("OF: one level translation:", addr, na); - } - bail: - of_node_put(parent); - of_node_put(dev); - - return result; -} -EXPORT_SYMBOL(of_translate_address); - -const u32 *of_get_address(struct device_node *dev, int index, u64 *size, - unsigned int *flags) -{ - const u32 *prop; - unsigned int psize; - struct device_node *parent; - struct of_bus *bus; - int onesize, i, na, ns; - - /* Get parent & match bus type */ - parent = of_get_parent(dev); - if (parent == NULL) - return NULL; - bus = of_match_bus(parent); - bus->count_cells(dev, &na, &ns); - of_node_put(parent); - if (!OF_CHECK_COUNTS(na, ns)) - return NULL; - - /* Get "reg" or "assigned-addresses" property */ - prop = of_get_property(dev, bus->addresses, (int *) &psize); - if (prop == NULL) - return NULL; - psize /= 4; - - onesize = na + ns; - for (i = 0; psize >= onesize; psize -= onesize, prop += onesize, i++) - if (i == index) { - if (size) - *size = of_read_number(prop + na, ns); - if (flags) - *flags = bus->get_flags(prop); - return prop; - } - return NULL; -} -EXPORT_SYMBOL(of_get_address); - -static int __of_address_to_resource(struct device_node *dev, const u32 *addrp, - u64 size, unsigned int flags, - struct resource *r) -{ - u64 taddr; - - if ((flags & (IORESOURCE_IO | IORESOURCE_MEM)) == 0) - return -EINVAL; - taddr = of_translate_address(dev, addrp); - if (taddr == OF_BAD_ADDR) - return -EINVAL; - memset(r, 0, sizeof(struct resource)); - if (flags & IORESOURCE_IO) { - unsigned long port; - port = -1; /* pci_address_to_pio(taddr); */ - if (port == (unsigned long)-1) - return -EINVAL; - r->start = port; - r->end = port + size - 1; - } else { - r->start = taddr; - r->end = taddr + size - 1; - } - r->flags = flags; - r->name = dev->name; - return 0; -} - -int of_address_to_resource(struct device_node *dev, int index, - struct resource *r) -{ - const u32 *addrp; - u64 size; - unsigned int flags; - - addrp = of_get_address(dev, index, &size, &flags); - if (addrp == NULL) - return -EINVAL; - return __of_address_to_resource(dev, addrp, size, flags, r); -} -EXPORT_SYMBOL_GPL(of_address_to_resource); - void of_parse_dma_window(struct device_node *dn, const void *dma_window_prop, unsigned long *busno, unsigned long *phys, unsigned long *size) { @@ -644,308 +111,6 @@ void of_parse_dma_window(struct device_node *dn, const void *dma_window_prop, *size = of_read_number(dma_window, cells); } -/* - * Interrupt remapper - */ - -static unsigned int of_irq_workarounds; -static struct device_node *of_irq_dflt_pic; - -static struct device_node *of_irq_find_parent(struct device_node *child) -{ - struct device_node *p; - const phandle *parp; - - if (!of_node_get(child)) - return NULL; - - do { - parp = of_get_property(child, "interrupt-parent", NULL); - if (parp == NULL) - p = of_get_parent(child); - else { - if (of_irq_workarounds & OF_IMAP_NO_PHANDLE) - p = of_node_get(of_irq_dflt_pic); - else - p = of_find_node_by_phandle(*parp); - } - of_node_put(child); - child = p; - } while (p && of_get_property(p, "#interrupt-cells", NULL) == NULL); - - return p; -} - -/* This doesn't need to be called if you don't have any special workaround - * flags to pass - */ -void of_irq_map_init(unsigned int flags) -{ - of_irq_workarounds = flags; - - /* OldWorld, don't bother looking at other things */ - if (flags & OF_IMAP_OLDWORLD_MAC) - return; - - /* If we don't have phandles, let's try to locate a default interrupt - * controller (happens when booting with BootX). We do a first match - * here, hopefully, that only ever happens on machines with one - * controller. - */ - if (flags & OF_IMAP_NO_PHANDLE) { - struct device_node *np; - - for (np = NULL; (np = of_find_all_nodes(np)) != NULL;) { - if (of_get_property(np, "interrupt-controller", NULL) - == NULL) - continue; - /* Skip /chosen/interrupt-controller */ - if (strcmp(np->name, "chosen") == 0) - continue; - /* It seems like at least one person on this planet - * wants to use BootX on a machine with an AppleKiwi - * controller which happens to pretend to be an - * interrupt controller too. - */ - if (strcmp(np->name, "AppleKiwi") == 0) - continue; - /* I think we found one ! */ - of_irq_dflt_pic = np; - break; - } - } - -} - -int of_irq_map_raw(struct device_node *parent, const u32 *intspec, u32 ointsize, - const u32 *addr, struct of_irq *out_irq) -{ - struct device_node *ipar, *tnode, *old = NULL, *newpar = NULL; - const u32 *tmp, *imap, *imask; - u32 intsize = 1, addrsize, newintsize = 0, newaddrsize = 0; - int imaplen, match, i; - - pr_debug("of_irq_map_raw: par=%s,intspec=[0x%08x 0x%08x...]," - "ointsize=%d\n", - parent->full_name, intspec[0], intspec[1], ointsize); - - ipar = of_node_get(parent); - - /* First get the #interrupt-cells property of the current cursor - * that tells us how to interpret the passed-in intspec. If there - * is none, we are nice and just walk up the tree - */ - do { - tmp = of_get_property(ipar, "#interrupt-cells", NULL); - if (tmp != NULL) { - intsize = *tmp; - break; - } - tnode = ipar; - ipar = of_irq_find_parent(ipar); - of_node_put(tnode); - } while (ipar); - if (ipar == NULL) { - pr_debug(" -> no parent found !\n"); - goto fail; - } - - pr_debug("of_irq_map_raw: ipar=%s, size=%d\n", - ipar->full_name, intsize); - - if (ointsize != intsize) - return -EINVAL; - - /* Look for this #address-cells. We have to implement the old linux - * trick of looking for the parent here as some device-trees rely on it - */ - old = of_node_get(ipar); - do { - tmp = of_get_property(old, "#address-cells", NULL); - tnode = of_get_parent(old); - of_node_put(old); - old = tnode; - } while (old && tmp == NULL); - of_node_put(old); - old = NULL; - addrsize = (tmp == NULL) ? 2 : *tmp; - - pr_debug(" -> addrsize=%d\n", addrsize); - - /* Now start the actual "proper" walk of the interrupt tree */ - while (ipar != NULL) { - /* Now check if cursor is an interrupt-controller and if it is - * then we are done - */ - if (of_get_property(ipar, "interrupt-controller", NULL) != - NULL) { - pr_debug(" -> got it !\n"); - memcpy(out_irq->specifier, intspec, - intsize * sizeof(u32)); - out_irq->size = intsize; - out_irq->controller = ipar; - of_node_put(old); - return 0; - } - - /* Now look for an interrupt-map */ - imap = of_get_property(ipar, "interrupt-map", &imaplen); - /* No interrupt map, check for an interrupt parent */ - if (imap == NULL) { - pr_debug(" -> no map, getting parent\n"); - newpar = of_irq_find_parent(ipar); - goto skiplevel; - } - imaplen /= sizeof(u32); - - /* Look for a mask */ - imask = of_get_property(ipar, "interrupt-map-mask", NULL); - - /* If we were passed no "reg" property and we attempt to parse - * an interrupt-map, then #address-cells must be 0. - * Fail if it's not. - */ - if (addr == NULL && addrsize != 0) { - pr_debug(" -> no reg passed in when needed !\n"); - goto fail; - } - - /* Parse interrupt-map */ - match = 0; - while (imaplen > (addrsize + intsize + 1) && !match) { - /* Compare specifiers */ - match = 1; - for (i = 0; i < addrsize && match; ++i) { - u32 mask = imask ? imask[i] : 0xffffffffu; - match = ((addr[i] ^ imap[i]) & mask) == 0; - } - for (; i < (addrsize + intsize) && match; ++i) { - u32 mask = imask ? imask[i] : 0xffffffffu; - match = - ((intspec[i-addrsize] ^ imap[i]) - & mask) == 0; - } - imap += addrsize + intsize; - imaplen -= addrsize + intsize; - - pr_debug(" -> match=%d (imaplen=%d)\n", match, imaplen); - - /* Get the interrupt parent */ - if (of_irq_workarounds & OF_IMAP_NO_PHANDLE) - newpar = of_node_get(of_irq_dflt_pic); - else - newpar = - of_find_node_by_phandle((phandle)*imap); - imap++; - --imaplen; - - /* Check if not found */ - if (newpar == NULL) { - pr_debug(" -> imap parent not found !\n"); - goto fail; - } - - /* Get #interrupt-cells and #address-cells of new - * parent - */ - tmp = of_get_property(newpar, "#interrupt-cells", NULL); - if (tmp == NULL) { - pr_debug(" -> parent lacks " - "#interrupt-cells!\n"); - goto fail; - } - newintsize = *tmp; - tmp = of_get_property(newpar, "#address-cells", NULL); - newaddrsize = (tmp == NULL) ? 0 : *tmp; - - pr_debug(" -> newintsize=%d, newaddrsize=%d\n", - newintsize, newaddrsize); - - /* Check for malformed properties */ - if (imaplen < (newaddrsize + newintsize)) - goto fail; - - imap += newaddrsize + newintsize; - imaplen -= newaddrsize + newintsize; - - pr_debug(" -> imaplen=%d\n", imaplen); - } - if (!match) - goto fail; - - of_node_put(old); - old = of_node_get(newpar); - addrsize = newaddrsize; - intsize = newintsize; - intspec = imap - intsize; - addr = intspec - addrsize; - -skiplevel: - /* Iterate again with new parent */ - pr_debug(" -> new parent: %s\n", - newpar ? newpar->full_name : "<>"); - of_node_put(ipar); - ipar = newpar; - newpar = NULL; - } -fail: - of_node_put(ipar); - of_node_put(old); - of_node_put(newpar); - - return -EINVAL; -} -EXPORT_SYMBOL_GPL(of_irq_map_raw); - -int of_irq_map_one(struct device_node *device, - int index, struct of_irq *out_irq) -{ - struct device_node *p; - const u32 *intspec, *tmp, *addr; - u32 intsize, intlen; - int res; - - pr_debug("of_irq_map_one: dev=%s, index=%d\n", - device->full_name, index); - - /* Get the interrupts property */ - intspec = of_get_property(device, "interrupts", (int *) &intlen); - if (intspec == NULL) - return -EINVAL; - intlen /= sizeof(u32); - - pr_debug(" intspec=%d intlen=%d\n", *intspec, intlen); - - /* Get the reg property (if any) */ - addr = of_get_property(device, "reg", NULL); - - /* Look for the interrupt parent. */ - p = of_irq_find_parent(device); - if (p == NULL) - return -EINVAL; - - /* Get size of interrupt specifier */ - tmp = of_get_property(p, "#interrupt-cells", NULL); - if (tmp == NULL) { - of_node_put(p); - return -EINVAL; - } - intsize = *tmp; - - pr_debug(" intsize=%d intlen=%d\n", intsize, intlen); - - /* Check index */ - if ((index + 1) * intsize > intlen) - return -EINVAL; - - /* Get new specifier and map it */ - res = of_irq_map_raw(p, intspec + index * intsize, intsize, - addr, out_irq); - of_node_put(p); - return res; -} -EXPORT_SYMBOL_GPL(of_irq_map_one); - /** * Search the device tree for the best MAC address to use. 'mac-address' is * checked first, because that is supposed to contain to "most recent" MAC @@ -983,43 +148,3 @@ const void *of_get_mac_address(struct device_node *np) return NULL; } EXPORT_SYMBOL(of_get_mac_address); - -int of_irq_to_resource(struct device_node *dev, int index, struct resource *r) -{ - struct of_irq out_irq; - int irq; - int res; - - res = of_irq_map_one(dev, index, &out_irq); - - /* Get irq for the device */ - if (res) { - pr_debug("IRQ not found... code = %d", res); - return NO_IRQ; - } - /* Assuming single interrupt controller... */ - irq = out_irq.specifier[0]; - - pr_debug("IRQ found = %d", irq); - - /* Only dereference the resource if both the - * resource and the irq are valid. */ - if (r && irq != NO_IRQ) { - r->start = r->end = irq; - r->flags = IORESOURCE_IRQ; - } - - return irq; -} -EXPORT_SYMBOL_GPL(of_irq_to_resource); - -void __iomem *of_iomap(struct device_node *np, int index) -{ - struct resource res; - - if (of_address_to_resource(np, index, &res)) - return NULL; - - return ioremap(res.start, 1 + res.end - res.start); -} -EXPORT_SYMBOL(of_iomap); diff --git a/arch/microblaze/kernel/ptrace.c b/arch/microblaze/kernel/ptrace.c index a4a7770c614..dc03ffc8174 100644 --- a/arch/microblaze/kernel/ptrace.c +++ b/arch/microblaze/kernel/ptrace.c @@ -38,6 +38,8 @@ #include <asm/processor.h> #include <linux/uaccess.h> #include <asm/asm-offsets.h> +#include <asm/cacheflush.h> +#include <asm/io.h> /* Returns the address where the register at REG_OFFS in P is stashed away. */ static microblaze_reg_t *reg_save_addr(unsigned reg_offs, @@ -101,8 +103,21 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) microblaze_reg_t *reg_addr = reg_save_addr(addr, child); if (request == PTRACE_PEEKUSR) val = *reg_addr; - else + else { +#if 1 *reg_addr = data; +#else + /* MS potential problem on WB system + * Be aware that reg_addr is virtual address + * virt_to_phys conversion is necessary. + * This could be sensible solution. + */ + u32 paddr = virt_to_phys((u32)reg_addr); + invalidate_icache_range(paddr, paddr + 4); + *reg_addr = data; + flush_dcache_range(paddr, paddr + 4); +#endif + } } else rval = -EIO; diff --git a/arch/microblaze/kernel/reset.c b/arch/microblaze/kernel/reset.c index a1721a33042..bd8ccab5cef 100644 --- a/arch/microblaze/kernel/reset.c +++ b/arch/microblaze/kernel/reset.c @@ -24,8 +24,8 @@ static int of_reset_gpio_handle(void) int ret; /* variable which stored handle reset gpio pin */ struct device_node *root; /* root node */ struct device_node *gpio; /* gpio node */ - struct of_gpio_chip *of_gc = NULL; - enum of_gpio_flags flags ; + struct gpio_chip *gc; + u32 flags; const void *gpio_spec; /* find out root node */ @@ -39,19 +39,19 @@ static int of_reset_gpio_handle(void) goto err0; } - of_gc = gpio->data; - if (!of_gc) { + gc = of_node_to_gpiochip(gpio); + if (!gc) { pr_debug("%s: gpio controller %s isn't registered\n", root->full_name, gpio->full_name); ret = -ENODEV; goto err1; } - ret = of_gc->xlate(of_gc, root, gpio_spec, &flags); + ret = gc->of_xlate(gc, root, gpio_spec, &flags); if (ret < 0) goto err1; - ret += of_gc->gc.base; + ret += gc->base; err1: of_node_put(gpio); err0: diff --git a/arch/microblaze/kernel/setup.c b/arch/microblaze/kernel/setup.c index 17c98dbcec8..f5f76884235 100644 --- a/arch/microblaze/kernel/setup.c +++ b/arch/microblaze/kernel/setup.c @@ -213,15 +213,9 @@ static struct notifier_block dflt_plat_bus_notifier = { .priority = INT_MAX, }; -static struct notifier_block dflt_of_bus_notifier = { - .notifier_call = dflt_bus_notify, - .priority = INT_MAX, -}; - static int __init setup_bus_notifier(void) { bus_register_notifier(&platform_bus_type, &dflt_plat_bus_notifier); - bus_register_notifier(&of_platform_bus_type, &dflt_of_bus_notifier); return 0; } diff --git a/arch/microblaze/kernel/stacktrace.c b/arch/microblaze/kernel/stacktrace.c index 123692f2264..84bc6686102 100644 --- a/arch/microblaze/kernel/stacktrace.c +++ b/arch/microblaze/kernel/stacktrace.c @@ -14,52 +14,18 @@ #include <linux/thread_info.h> #include <linux/ptrace.h> #include <linux/module.h> +#include <asm/unwind.h> -/* FIXME initial support */ void save_stack_trace(struct stack_trace *trace) { - unsigned long *sp; - unsigned long addr; - asm("addik %0, r1, 0" : "=r" (sp)); - - while (!kstack_end(sp)) { - addr = *sp++; - if (__kernel_text_address(addr)) { - if (trace->skip > 0) - trace->skip--; - else - trace->entries[trace->nr_entries++] = addr; - - if (trace->nr_entries >= trace->max_entries) - break; - } - } + /* Exclude our helper functions from the trace*/ + trace->skip += 2; + microblaze_unwind(NULL, trace); } EXPORT_SYMBOL_GPL(save_stack_trace); void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) { - unsigned int *sp; - unsigned long addr; - - struct thread_info *ti = task_thread_info(tsk); - - if (tsk == current) - asm("addik %0, r1, 0" : "=r" (sp)); - else - sp = (unsigned int *)ti->cpu_context.r1; - - while (!kstack_end(sp)) { - addr = *sp++; - if (__kernel_text_address(addr)) { - if (trace->skip > 0) - trace->skip--; - else - trace->entries[trace->nr_entries++] = addr; - - if (trace->nr_entries >= trace->max_entries) - break; - } - } + microblaze_unwind(tsk, trace); } EXPORT_SYMBOL_GPL(save_stack_trace_tsk); diff --git a/arch/microblaze/kernel/timer.c b/arch/microblaze/kernel/timer.c index ed61b2f1771..b1380ae93ae 100644 --- a/arch/microblaze/kernel/timer.c +++ b/arch/microblaze/kernel/timer.c @@ -28,6 +28,7 @@ #include <asm/prom.h> #include <asm/irq.h> #include <asm/system.h> +#include <linux/cnt32_to_63.h> #ifdef CONFIG_SELFMOD_TIMER #include <asm/selfmod.h> @@ -135,7 +136,7 @@ static void microblaze_timer_set_mode(enum clock_event_mode mode, static struct clock_event_device clockevent_microblaze_timer = { .name = "microblaze_clockevent", .features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC, - .shift = 24, + .shift = 8, .rating = 300, .set_next_event = microblaze_timer_set_next_event, .set_mode = microblaze_timer_set_mode, @@ -195,7 +196,7 @@ static cycle_t microblaze_cc_read(const struct cyclecounter *cc) static struct cyclecounter microblaze_cc = { .read = microblaze_cc_read, .mask = CLOCKSOURCE_MASK(32), - .shift = 24, + .shift = 8, }; int __init init_microblaze_timecounter(void) @@ -213,7 +214,7 @@ static struct clocksource clocksource_microblaze = { .rating = 300, .read = microblaze_read, .mask = CLOCKSOURCE_MASK(32), - .shift = 24, /* I can shift it */ + .shift = 8, /* I can shift it */ .flags = CLOCK_SOURCE_IS_CONTINUOUS, }; @@ -235,6 +236,12 @@ static int __init microblaze_clocksource_init(void) return 0; } +/* + * We have to protect accesses before timer initialization + * and return 0 for sched_clock function below. + */ +static int timer_initialized; + void __init time_init(void) { u32 irq, i = 0; @@ -289,4 +296,15 @@ void __init time_init(void) #endif microblaze_clocksource_init(); microblaze_clockevent_init(); + timer_initialized = 1; +} + +unsigned long long notrace sched_clock(void) +{ + if (timer_initialized) { + struct clocksource *cs = &clocksource_microblaze; + cycle_t cyc = cnt32_to_63(cs->read(NULL)); + return clocksource_cyc2ns(cyc, cs->mult, cs->shift); + } + return 0; } diff --git a/arch/microblaze/kernel/traps.c b/arch/microblaze/kernel/traps.c index 75e49202a5e..ba034d421ec 100644 --- a/arch/microblaze/kernel/traps.c +++ b/arch/microblaze/kernel/traps.c @@ -16,13 +16,14 @@ #include <asm/exceptions.h> #include <asm/system.h> +#include <asm/unwind.h> void trap_init(void) { __enable_hw_exceptions(); } -static unsigned long kstack_depth_to_print = 24; +static unsigned long kstack_depth_to_print; /* 0 == entire stack */ static int __init kstack_setup(char *s) { @@ -30,31 +31,47 @@ static int __init kstack_setup(char *s) } __setup("kstack=", kstack_setup); -void show_trace(struct task_struct *task, unsigned long *stack) +void show_stack(struct task_struct *task, unsigned long *sp) { - unsigned long addr; - - if (!stack) - stack = (unsigned long *)&stack; + unsigned long words_to_show; + u32 fp = (u32) sp; + + if (fp == 0) { + if (task) { + fp = ((struct thread_info *) + (task->stack))->cpu_context.r1; + } else { + /* Pick up caller of dump_stack() */ + fp = (u32)&sp - 8; + } + } - printk(KERN_NOTICE "Call Trace: "); -#ifdef CONFIG_KALLSYMS - printk(KERN_NOTICE "\n"); -#endif - while (!kstack_end(stack)) { - addr = *stack++; - /* - * If the address is either in the text segment of the - * kernel, or in the region which contains vmalloc'ed - * memory, it *may* be the address of a calling - * routine; if so, print it so that someone tracing - * down the cause of the crash will be able to figure - * out the call path that was taken. - */ - if (kernel_text_address(addr)) - print_ip_sym(addr); + words_to_show = (THREAD_SIZE - (fp & (THREAD_SIZE - 1))) >> 2; + if (kstack_depth_to_print && (words_to_show > kstack_depth_to_print)) + words_to_show = kstack_depth_to_print; + + pr_info("Kernel Stack:\n"); + + /* + * Make the first line an 'odd' size if necessary to get + * remaining lines to start at an address multiple of 0x10 + */ + if (fp & 0xF) { + unsigned long line1_words = (0x10 - (fp & 0xF)) >> 2; + if (line1_words < words_to_show) { + print_hex_dump(KERN_INFO, "", DUMP_PREFIX_ADDRESS, 32, + 4, (void *)fp, line1_words << 2, 0); + fp += line1_words << 2; + words_to_show -= line1_words; + } } - printk(KERN_NOTICE "\n"); + print_hex_dump(KERN_INFO, "", DUMP_PREFIX_ADDRESS, 32, 4, (void *)fp, + words_to_show << 2, 0); + printk(KERN_INFO "\n\n"); + + pr_info("Call Trace:\n"); + microblaze_unwind(task, NULL); + pr_info("\n"); if (!task) task = current; @@ -62,34 +79,6 @@ void show_trace(struct task_struct *task, unsigned long *stack) debug_show_held_locks(task); } -void show_stack(struct task_struct *task, unsigned long *sp) -{ - unsigned long *stack; - int i; - - if (sp == NULL) { - if (task) - sp = (unsigned long *) ((struct thread_info *) - (task->stack))->cpu_context.r1; - else - sp = (unsigned long *)&sp; - } - - stack = sp; - - printk(KERN_INFO "\nStack:\n "); - - for (i = 0; i < kstack_depth_to_print; i++) { - if (kstack_end(sp)) - break; - if (i && ((i % 8) == 0)) - printk("\n "); - printk("%08lx ", *sp++); - } - printk("\n"); - show_trace(task, stack); -} - void dump_stack(void) { show_stack(NULL, NULL); diff --git a/arch/microblaze/kernel/unwind.c b/arch/microblaze/kernel/unwind.c new file mode 100644 index 00000000000..fefac5c3358 --- /dev/null +++ b/arch/microblaze/kernel/unwind.c @@ -0,0 +1,318 @@ +/* + * Backtrace support for Microblaze + * + * Copyright (C) 2010 Digital Design Corporation + * + * Based on arch/sh/kernel/cpu/sh5/unwind.c code which is: + * Copyright (C) 2004 Paul Mundt + * Copyright (C) 2004 Richard Curnow + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ + +/* #define DEBUG 1 */ +#include <linux/kallsyms.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/stacktrace.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/module.h> +#include <linux/io.h> +#include <asm/sections.h> +#include <asm/exceptions.h> +#include <asm/unwind.h> + +struct stack_trace; + +/* + * On Microblaze, finding the previous stack frame is a little tricky. + * At this writing (3/2010), Microblaze does not support CONFIG_FRAME_POINTERS, + * and even if it did, gcc (4.1.2) does not store the frame pointer at + * a consistent offset within each frame. To determine frame size, it is + * necessary to search for the assembly instruction that creates or reclaims + * the frame and extract the size from it. + * + * Microblaze stores the stack pointer in r1, and creates a frame via + * + * addik r1, r1, -FRAME_SIZE + * + * The frame is reclaimed via + * + * addik r1, r1, FRAME_SIZE + * + * Frame creation occurs at or near the top of a function. + * Depending on the compiler, reclaim may occur at the end, or before + * a mid-function return. + * + * A stack frame is usually not created in a leaf function. + * + */ + +/** + * get_frame_size - Extract the stack adjustment from an + * "addik r1, r1, adjust" instruction + * @instr : Microblaze instruction + * + * Return - Number of stack bytes the instruction reserves or reclaims + */ +inline long get_frame_size(unsigned long instr) +{ + return abs((s16)(instr & 0xFFFF)); +} + +/** + * find_frame_creation - Search backward to find the instruction that creates + * the stack frame (hopefully, for the same function the + * initial PC is in). + * @pc : Program counter at which to begin the search + * + * Return - PC at which stack frame creation occurs + * NULL if this cannot be found, i.e. a leaf function + */ +static unsigned long *find_frame_creation(unsigned long *pc) +{ + int i; + + /* NOTE: Distance to search is arbitrary + * 250 works well for most things, + * 750 picks up things like tcp_recvmsg(), + * 1000 needed for fat_fill_super() + */ + for (i = 0; i < 1000; i++, pc--) { + unsigned long instr; + s16 frame_size; + + if (!kernel_text_address((unsigned long) pc)) + return NULL; + + instr = *pc; + + /* addik r1, r1, foo ? */ + if ((instr & 0xFFFF0000) != 0x30210000) + continue; /* No */ + + frame_size = get_frame_size(instr); + if ((frame_size < 8) || (frame_size & 3)) { + pr_debug(" Invalid frame size %d at 0x%p\n", + frame_size, pc); + return NULL; + } + + pr_debug(" Found frame creation at 0x%p, size %d\n", pc, + frame_size); + return pc; + } + + return NULL; +} + +/** + * lookup_prev_stack_frame - Find the stack frame of the previous function. + * @fp : Frame (stack) pointer for current function + * @pc : Program counter within current function + * @leaf_return : r15 value within current function. If the current function + * is a leaf, this is the caller's return address. + * @pprev_fp : On exit, set to frame (stack) pointer for previous function + * @pprev_pc : On exit, set to current function caller's return address + * + * Return - 0 on success, -EINVAL if the previous frame cannot be found + */ +static int lookup_prev_stack_frame(unsigned long fp, unsigned long pc, + unsigned long leaf_return, + unsigned long *pprev_fp, + unsigned long *pprev_pc) +{ + unsigned long *prologue = NULL; + + /* _switch_to is a special leaf function */ + if (pc != (unsigned long) &_switch_to) + prologue = find_frame_creation((unsigned long *)pc); + + if (prologue) { + long frame_size = get_frame_size(*prologue); + + *pprev_fp = fp + frame_size; + *pprev_pc = *(unsigned long *)fp; + } else { + if (!leaf_return) + return -EINVAL; + *pprev_pc = leaf_return; + *pprev_fp = fp; + } + + /* NOTE: don't check kernel_text_address here, to allow display + * of userland return address + */ + return (!*pprev_pc || (*pprev_pc & 3)) ? -EINVAL : 0; +} + +static void microblaze_unwind_inner(struct task_struct *task, + unsigned long pc, unsigned long fp, + unsigned long leaf_return, + struct stack_trace *trace); + +/** + * unwind_trap - Unwind through a system trap, that stored previous state + * on the stack. + */ +#ifdef CONFIG_MMU +static inline void unwind_trap(struct task_struct *task, unsigned long pc, + unsigned long fp, struct stack_trace *trace) +{ + /* To be implemented */ +} +#else +static inline void unwind_trap(struct task_struct *task, unsigned long pc, + unsigned long fp, struct stack_trace *trace) +{ + const struct pt_regs *regs = (const struct pt_regs *) fp; + microblaze_unwind_inner(task, regs->pc, regs->r1, regs->r15, trace); +} +#endif + +/** + * microblaze_unwind_inner - Unwind the stack from the specified point + * @task : Task whose stack we are to unwind (may be NULL) + * @pc : Program counter from which we start unwinding + * @fp : Frame (stack) pointer from which we start unwinding + * @leaf_return : Value of r15 at pc. If the function is a leaf, this is + * the caller's return address. + * @trace : Where to store stack backtrace (PC values). + * NULL == print backtrace to kernel log + */ +void microblaze_unwind_inner(struct task_struct *task, + unsigned long pc, unsigned long fp, + unsigned long leaf_return, + struct stack_trace *trace) +{ + int ofs = 0; + + pr_debug(" Unwinding with PC=%p, FP=%p\n", (void *)pc, (void *)fp); + if (!pc || !fp || (pc & 3) || (fp & 3)) { + pr_debug(" Invalid state for unwind, aborting\n"); + return; + } + for (; pc != 0;) { + unsigned long next_fp, next_pc = 0; + unsigned long return_to = pc + 2 * sizeof(unsigned long); + const struct trap_handler_info *handler = + µblaze_trap_handlers; + + /* Is previous function the HW exception handler? */ + if ((return_to >= (unsigned long)&_hw_exception_handler) + &&(return_to < (unsigned long)&ex_handler_unhandled)) { + /* + * HW exception handler doesn't save all registers, + * so we open-code a special case of unwind_trap() + */ +#ifndef CONFIG_MMU + const struct pt_regs *regs = + (const struct pt_regs *) fp; +#endif + pr_info("HW EXCEPTION\n"); +#ifndef CONFIG_MMU + microblaze_unwind_inner(task, regs->r17 - 4, + fp + EX_HANDLER_STACK_SIZ, + regs->r15, trace); +#endif + return; + } + + /* Is previous function a trap handler? */ + for (; handler->start_addr; ++handler) { + if ((return_to >= handler->start_addr) + && (return_to <= handler->end_addr)) { + if (!trace) + pr_info("%s\n", handler->trap_name); + unwind_trap(task, pc, fp, trace); + return; + } + } + pc -= ofs; + + if (trace) { +#ifdef CONFIG_STACKTRACE + if (trace->skip > 0) + trace->skip--; + else + trace->entries[trace->nr_entries++] = pc; + + if (trace->nr_entries >= trace->max_entries) + break; +#endif + } else { + /* Have we reached userland? */ + if (unlikely(pc == task_pt_regs(task)->pc)) { + pr_info("[<%p>] PID %lu [%s]\n", + (void *) pc, + (unsigned long) task->pid, + task->comm); + break; + } else + print_ip_sym(pc); + } + + /* Stop when we reach anything not part of the kernel */ + if (!kernel_text_address(pc)) + break; + + if (lookup_prev_stack_frame(fp, pc, leaf_return, &next_fp, + &next_pc) == 0) { + ofs = sizeof(unsigned long); + pc = next_pc & ~3; + fp = next_fp; + leaf_return = 0; + } else { + pr_debug(" Failed to find previous stack frame\n"); + break; + } + + pr_debug(" Next PC=%p, next FP=%p\n", + (void *)next_pc, (void *)next_fp); + } +} + +/** + * microblaze_unwind - Stack unwinder for Microblaze (external entry point) + * @task : Task whose stack we are to unwind (NULL == current) + * @trace : Where to store stack backtrace (PC values). + * NULL == print backtrace to kernel log + */ +void microblaze_unwind(struct task_struct *task, struct stack_trace *trace) +{ + if (task) { + if (task == current) { + const struct pt_regs *regs = task_pt_regs(task); + microblaze_unwind_inner(task, regs->pc, regs->r1, + regs->r15, trace); + } else { + struct thread_info *thread_info = + (struct thread_info *)(task->stack); + const struct cpu_context *cpu_context = + &thread_info->cpu_context; + + microblaze_unwind_inner(task, + (unsigned long) &_switch_to, + cpu_context->r1, + cpu_context->r15, trace); + } + } else { + unsigned long pc, fp; + + __asm__ __volatile__ ("or %0, r1, r0" : "=r" (fp)); + + __asm__ __volatile__ ( + "brlid %0, 0f;" + "nop;" + "0:" + : "=r" (pc) + ); + + /* Since we are not a leaf function, use leaf_return = 0 */ + microblaze_unwind_inner(current, pc, fp, 0, trace); + } +} + diff --git a/arch/microblaze/kernel/vmlinux.lds.S b/arch/microblaze/kernel/vmlinux.lds.S index db72d712460..a09f2962fbe 100644 --- a/arch/microblaze/kernel/vmlinux.lds.S +++ b/arch/microblaze/kernel/vmlinux.lds.S @@ -10,7 +10,7 @@ OUTPUT_FORMAT("elf32-microblaze", "elf32-microblaze", "elf32-microblaze") OUTPUT_ARCH(microblaze) -ENTRY(_start) +ENTRY(microblaze_start) #include <asm/page.h> #include <asm-generic/vmlinux.lds.h> @@ -20,7 +20,7 @@ jiffies = jiffies_64 + 4; SECTIONS { . = CONFIG_KERNEL_START; - _start = CONFIG_KERNEL_BASE_ADDR; + microblaze_start = CONFIG_KERNEL_BASE_ADDR; .text : AT(ADDR(.text) - LOAD_OFFSET) { _text = . ; _stext = . ; @@ -55,7 +55,7 @@ SECTIONS { */ .sdata2 : AT(ADDR(.sdata2) - LOAD_OFFSET) { _ssrw = .; - . = ALIGN(4096); /* page aligned when MMU used - origin 0x8 */ + . = ALIGN(PAGE_SIZE); /* page aligned when MMU used */ *(.sdata2) . = ALIGN(8); _essrw = .; @@ -70,7 +70,7 @@ SECTIONS { /* Reserve some low RAM for r0 based memory references */ . = ALIGN(0x4) ; r0_ram = . ; - . = . + 4096; /* a page should be enough */ + . = . + PAGE_SIZE; /* a page should be enough */ /* Under the microblaze ABI, .sdata and .sbss must be contiguous */ . = ALIGN(8); @@ -120,7 +120,7 @@ SECTIONS { __init_end_before_initramfs = .; - .init.ramfs ALIGN(4096) : AT(ADDR(.init.ramfs) - LOAD_OFFSET) { + .init.ramfs ALIGN(PAGE_SIZE) : AT(ADDR(.init.ramfs) - LOAD_OFFSET) { __initramfs_start = .; *(.init.ramfs) __initramfs_end = .; @@ -132,11 +132,11 @@ SECTIONS { * so that __init_end == __bss_start. This will make image.elf * consistent with the image.bin */ - /* . = ALIGN(4096); */ + /* . = ALIGN(PAGE_SIZE); */ } __init_end = .; - .bss ALIGN (4096) : AT(ADDR(.bss) - LOAD_OFFSET) { + .bss ALIGN (PAGE_SIZE) : AT(ADDR(.bss) - LOAD_OFFSET) { /* page aligned when MMU used */ __bss_start = . ; *(.bss*) @@ -145,7 +145,7 @@ SECTIONS { __bss_stop = . ; _ebss = . ; } - . = ALIGN(4096); + . = ALIGN(PAGE_SIZE); _end = .; DISCARDS |