From patchwork Thu Feb 16 20:18:10 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Uwe_Kleine-K=C3=B6nig?= X-Patchwork-Id: 141682 Return-Path: X-Original-To: incoming-imx@patchwork.ozlabs.org Delivered-To: patchwork-incoming-imx@bilbo.ozlabs.org Received: from merlin.infradead.org (unknown [IPv6:2001:4978:20e::2]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id B12521007D5 for ; Fri, 17 Feb 2012 07:22:31 +1100 (EST) Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.76 #1 (Red Hat Linux)) id 1Ry7ns-0007Cp-BU; Thu, 16 Feb 2012 20:19:44 +0000 Received: from casper.infradead.org ([2001:770:15f::2]) by merlin.infradead.org with esmtps (Exim 4.76 #1 (Red Hat Linux)) id 1Ry7nP-00074d-7Z for linux-arm-kernel@merlin.infradead.org; Thu, 16 Feb 2012 20:19:15 +0000 Received: from metis.ext.pengutronix.de ([2001:6f8:1178:4:290:27ff:fe1d:cc33]) by casper.infradead.org with esmtps (Exim 4.76 #1 (Red Hat Linux)) id 1Ry7nH-000293-Jm for linux-arm-kernel@lists.infradead.org; Thu, 16 Feb 2012 20:19:13 +0000 Received: from dude.hi.pengutronix.de ([2001:6f8:1178:2:21e:67ff:fe11:9c5c]) by metis.ext.pengutronix.de with esmtp (Exim 4.72) (envelope-from ) id 1Ry7mV-00063p-2z; Thu, 16 Feb 2012 21:18:19 +0100 Received: from ukl by dude.hi.pengutronix.de with local (Exim 4.77) (envelope-from ) id 1Ry7mU-00044Q-W7; Thu, 16 Feb 2012 21:18:19 +0100 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= To: linux-arm-kernel@lists.infradead.org, Catalin Marinas Subject: [PATCH 5/5] ARM: Cortex-M3: Add support for exception handling Date: Thu, 16 Feb 2012 21:18:10 +0100 Message-Id: <1329423490-15580-5-git-send-email-u.kleine-koenig@pengutronix.de> X-Mailer: git-send-email 1.7.9 In-Reply-To: <1329423490-15580-1-git-send-email-u.kleine-koenig@pengutronix.de> References: <20120216200143.GS14173@pengutronix.de> <1329423490-15580-1-git-send-email-u.kleine-koenig@pengutronix.de> MIME-Version: 1.0 X-SA-Exim-Connect-IP: 2001:6f8:1178:2:21e:67ff:fe11:9c5c X-SA-Exim-Mail-From: ukl@pengutronix.de X-SA-Exim-Scanned: No (on metis.ext.pengutronix.de); SAEximRunCond expanded to false X-PTX-Original-Recipient: linux-arm-kernel@lists.infradead.org X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20120216_201908_126400_3AFE6AD6 X-CRM114-Status: GOOD ( 28.59 ) X-Spam-Score: -1.9 (-) X-Spam-Report: SpamAssassin version 3.3.2 on casper.infradead.org summary: Content analysis details: (-1.9 points, 5.0 required) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 T_RP_MATCHES_RCVD Envelope sender domain matches handover relay domain -1.9 BAYES_00 BODY: Bayes spam probability is 0 to 1% [score: 0.0000] Cc: kernel@pengutronix.de X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.14 Precedence: list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: linux-arm-kernel-bounces@lists.infradead.org Errors-To: linux-arm-kernel-bounces+incoming-imx=patchwork.ozlabs.org@lists.infradead.org List-Id: linux-imx-kernel.lists.patchwork.ozlabs.org From: Catalin Marinas This patch implements the exception handling for the ARMv7-M architecture (pretty different from the A or R profiles). [ukleinek: small adaptions, e.g. use show_regs instead of __show_regs in invalid_entry, fix stack corruption in v7m_exception_fast_exit, check for bit 3 in exception lr only instead of explicit values] Signed-off-by: Catalin Marinas Signed-off-by: Uwe Kleine-König --- arch/arm/kernel/entry-common.S | 18 +++++- arch/arm/kernel/entry-header.S | 94 ++++++++++++++++++++++++++++- arch/arm/kernel/entry-v7m.S | 131 ++++++++++++++++++++++++++++++++++++++++ arch/arm/kernel/process.c | 8 +++ 4 files changed, 249 insertions(+), 2 deletions(-) create mode 100644 arch/arm/kernel/entry-v7m.S diff --git a/arch/arm/kernel/entry-common.S b/arch/arm/kernel/entry-common.S index 9fd0ba9..4a12bc3 100644 --- a/arch/arm/kernel/entry-common.S +++ b/arch/arm/kernel/entry-common.S @@ -43,7 +43,13 @@ ret_fast_syscall: * Ok, we need to do extra processing, enter the slow path. */ fast_work_pending: +#ifdef CONFIG_CPU_V7M + @ S_R0 not at the beginning of struct pt_regs + add sp, #S_OFF + str r0, [sp, #S_R0] @ returned r0 +#else str r0, [sp, #S_R0+S_OFF]! @ returned r0 +#endif work_pending: tst r1, #_TIF_NEED_RESCHED bne work_resched @@ -345,6 +351,9 @@ ENDPROC(ftrace_stub) .align 5 ENTRY(vector_swi) +#ifdef CONFIG_CPU_V7M + v7m_exception_entry +#else sub sp, sp, #S_FRAME_SIZE stmia sp, {r0 - r12} @ Calling r0 - r12 ARM( add r8, sp, #S_PC ) @@ -354,6 +363,7 @@ ENTRY(vector_swi) mrs r8, spsr @ called from non-FIQ mode, so ok. str lr, [sp, #S_PC] @ Save calling PC str r8, [sp, #S_PSR] @ Save CPSR +#endif str r0, [sp, #S_OLD_R0] @ Save OLD_R0 zero_fp @@ -473,14 +483,20 @@ __sys_trace: adr lr, BSYM(__sys_trace_return) @ return address mov scno, r0 @ syscall number (possibly new) - add r1, sp, #S_R0 + S_OFF @ pointer to regs + add r1, sp, #S_OFF @ pointer to regs cmp scno, #NR_syscalls @ check upper syscall limit ldmccia r1, {r0 - r3} @ have to reload r0 - r3 ldrcc pc, [tbl, scno, lsl #2] @ call sys_* routine b 2b __sys_trace_return: +#ifdef CONFIG_CPU_V7M + @ S_R0 not at the beginning of struct pt_regs + add sp, #S_OFF + str r0, [sp, #S_R0] @ save returned r0 +#else str r0, [sp, #S_R0 + S_OFF]! @ save returned r0 +#endif mov r2, scno mov r1, sp mov r0, #1 @ trace exit [IP = 1] diff --git a/arch/arm/kernel/entry-header.S b/arch/arm/kernel/entry-header.S index 9a8531e..80055b4 100644 --- a/arch/arm/kernel/entry-header.S +++ b/arch/arm/kernel/entry-header.S @@ -26,7 +26,7 @@ * The SWI code relies on the fact that R0 is at the bottom of the stack * (due to slow/fast restore user regs). */ -#if S_R0 != 0 +#if S_R0 != 0 && !defined(CONFIG_CPU_V7M) #error "Please fix" #endif @@ -44,6 +44,89 @@ #endif .endm +#ifdef CONFIG_CPU_V7M +/* + * ARMv7-M exception entry/exit macros. + * + * xPSR, ReturnAddress(), LR (R14), R12, R3, R2, R1, and R0 are + * automatically saved on the current stack (32 words) before + * switching to the exception stack (SP_main). The order of struct + * pt_regs members was changed to take advantage of the automatic + * state saving. + * + * If exception is taken while in user mode, SP_main is + * empty. Otherwise, SP_main is aligned to 64 bit automatically + * (CCR.STKALIGN set). + * + * Linux assumes that the interrupts are disabled when entering an + * exception handler and it may BUG if this is not the case. Interrupts + * are disabled during entry and reenabled in the exit macro. + * + * The v7m_exception_entry macro preserves the original r0-r5, r7 for + * the system call arguments. + * + * v7_exception_fast_exit is used when returning from interrupts. + * + * v7_exception_slow_exit is used when returning from SVC or PendSV. + * When returning to kernel mode, we don't return from exception. + */ + .macro v7m_exception_entry + cpsid i + tst lr, #0x8 @ check the return stack + bne 1f @ exception on process stack + add r12, sp, #32 @ MSP before exception + stmdb sp!, {r4-r12, lr} @ push unsaved registers + b 2f +1: + mrs r12, psp @ get the process stack + sub sp, #S_FRAME_SIZE + stmia sp, {r4-r12, lr} @ push unsaved registers + ldmia r12, {r0-r3, r6, r8-r10} @ load automatically saved registers + add r12, sp, #S_R0 + stmia r12, {r0-r3, r6, r8-r10} @ fill in the rest of struct pt_regs +2: + .endm + + .macro v7m_exception_fast_exit + ldmia sp!, {r4-r12, lr} @ restore previously saved state + tst lr, #0x8 @ check the return stack + addne sp, #S_FRAME_SIZE-S_R0 @ returning to PSP, just restore MSP + cpsie i + bx lr + .endm + + .macro v7m_exception_slow_exit ret_r0 + cpsid i + ldr lr, [sp, #S_EXC_LR] @ read exception LR + tst lr, #0x8 + bne 1f @ returning to PSP + @ Prepare the MSP stack + ldmia sp, {r4-r11} @ restore previously saved state + ldr lr, [sp, #S_PC] + add sp, #S_R0 + ldmia sp, {r0-r3, r12} @ restore the rest of registers + add sp, #S_FRAME_SIZE-S_R0 @ restore the stack pointer + cpsie i + bx lr +1: + @ Prepare the PSP stack + ldr r12, [sp, #S_SP] @ read original PSP + .if \ret_r0 + add r11, sp, #S_R1 + ldmia r11, {r1-r7} @ read state saved on MSP (r0 preserved) + .else + add r11, sp, #S_R0 + ldmia r11, {r0-r7} @ read state saved on MSP + .endif + msr psp, r12 @ restore PSP + stmia r12, {r0-r7} @ restore saved state to PSP + ldmia sp, {r4-r11} @ restore previously saved state + add sp, #S_FRAME_SIZE @ restore the original MSP + cpsie i + bx lr + .endm +#endif /* CONFIG_CPU_V7M */ + @ @ Store/load the USER SP and LR registers by switching to the SYS @ mode. Useful in Thumb-2 mode where "stm/ldm rd, {sp, lr}^" is not @@ -131,6 +214,14 @@ rfeia sp! .endm +#ifdef CONFIG_CPU_V7M + .macro restore_user_regs, fast = 0, offset = 0 + .if \offset + add sp, #\offset + .endif + v7m_exception_slow_exit ret_r0 = \fast + .endm +#else /* !CONFIG_CPU_V7M */ .macro restore_user_regs, fast = 0, offset = 0 clrex @ clear the exclusive monitor mov r2, sp @@ -147,6 +238,7 @@ add sp, sp, #S_FRAME_SIZE - S_SP movs pc, lr @ return & move spsr_svc into cpsr .endm +#endif /* CONFIG_CPU_V7M */ .macro get_thread_info, rd mov \rd, sp diff --git a/arch/arm/kernel/entry-v7m.S b/arch/arm/kernel/entry-v7m.S new file mode 100644 index 0000000..74a1b7e --- /dev/null +++ b/arch/arm/kernel/entry-v7m.S @@ -0,0 +1,131 @@ +/* + * linux/arch/arm/kernel/entry-v7m.S + * + * Copyright (C) 2008 ARM Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Low-level vector interface routines for the ARMv7-M architecture + */ +#include +#include +#include + +#include + +#include "entry-header.S" + +#ifdef CONFIG_PREEMPT +#error "CONFIG_PREEMPT not supported on the current ARMv7M implementation" +#endif +#ifdef CONFIG_TRACE_IRQFLAGS +#error "CONFIG_TRACE_IRQFLAGS not supported on the current ARMv7M implementation" +#endif + +__invalid_entry: + v7m_exception_entry + adr r0, strerr + mrs r1, ipsr + mov r2, lr + bl printk + mov r0, sp + bl show_regs +1: b 1b +ENDPROC(__invalid_entry) + +strerr: .asciz "\nUnhandled exception: IPSR = %08lx LR = %08lx\n" + + .align 2 +__irq_entry: + v7m_exception_entry + + @ + @ Invoke the IRQ handler + @ + mrs r0, ipsr + and r0, #0xff + sub r0, #16 @ IRQ number + mov r1, sp + @ routine called with r0 = irq number, r1 = struct pt_regs * + bl asm_do_IRQ + + @ + @ Check for any pending work if returning to user + @ + ldr lr, [sp, #S_EXC_LR] + tst lr, #0x8 @ check the return stack + beq 2f @ returning to kernel + get_thread_info tsk + ldr r1, [tsk, #TI_FLAGS] + tst r1, #_TIF_WORK_MASK + beq 2f @ no work pending + ldr r1, =0xe000ed04 @ ICSR + mov r0, #1 << 28 @ ICSR.PENDSVSET + str r0, [r1] @ raise PendSV + +2: + v7m_exception_fast_exit +ENDPROC(__irq_entry) + +__pendsv_entry: + v7m_exception_entry + + ldr r1, =0xe000ed04 @ ICSR + mov r0, #1 << 27 @ ICSR.PENDSVCLR + str r0, [r1] @ clear PendSV + + @ execute the pending work, including reschedule + get_thread_info tsk + mov why, #0 + b ret_to_user +ENDPROC(__pendsv_entry) + +/* + * Register switch for ARMv7-M processors. + * r0 = previous task_struct, r1 = previous thread_info, r2 = next thread_info + * previous and next are guaranteed not to be the same. + */ +ENTRY(__switch_to) + add ip, r1, #TI_CPU_SAVE + stmia ip!, {r4 - sl, fp} @ Store most regs on stack + str sp, [ip], #4 + str lr, [ip], #4 + mov r5, r0 + add r4, r2, #TI_CPU_SAVE + ldr r0, =thread_notify_head + mov r1, #THREAD_NOTIFY_SWITCH + bl atomic_notifier_call_chain + mov ip, r4 + mov r0, r5 + ldmia ip!, {r4 - sl, fp} @ Load all regs saved previously + ldr sp, [ip], #4 + ldr pc, [ip] +ENDPROC(__switch_to) + + .data + .align 8 +/* + * Vector table (64 words => 256 bytes natural alignment) + */ +ENTRY(vector_table) + .long 0 @ 0 - Reset stack pointer + .long __invalid_entry @ 1 - Reset + .long __invalid_entry @ 2 - NMI + .long __invalid_entry @ 3 - HardFault + .long __invalid_entry @ 4 - MemManage + .long __invalid_entry @ 5 - BusFault + .long __invalid_entry @ 6 - UsageFault + .long __invalid_entry @ 7 - Reserved + .long __invalid_entry @ 8 - Reserved + .long __invalid_entry @ 9 - Reserved + .long __invalid_entry @ 10 - Reserved + .long vector_swi @ 11 - SVCall + .long __invalid_entry @ 12 - Debug Monitor + .long __invalid_entry @ 13 - Reserved + .long __pendsv_entry @ 14 - PendSV + .long __invalid_entry @ 15 - SysTick + .rept 64 - 16 + .long __irq_entry @ 16..64 - External Interrupts + .endr diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c index 971d65c..ee604dd 100644 --- a/arch/arm/kernel/process.c +++ b/arch/arm/kernel/process.c @@ -452,7 +452,11 @@ asm( ".pushsection .text\n" #ifdef CONFIG_TRACE_IRQFLAGS " bl trace_hardirqs_on\n" #endif +#ifdef CONFIG_CPU_V7M +" msr primask, r7\n" +#else " msr cpsr_c, r7\n" +#endif " mov r0, r4\n" " mov lr, r6\n" " mov pc, r5\n" @@ -491,6 +495,10 @@ pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags) regs.ARM_r7 = SVC_MODE | PSR_ENDSTATE | PSR_ISETSTATE; regs.ARM_pc = (unsigned long)kernel_thread_helper; regs.ARM_cpsr = regs.ARM_r7 | PSR_I_BIT; +#ifdef CONFIG_CPU_V7M + /* Return to Handler mode */ + regs.ARM_EXC_lr = 0xfffffff1L; +#endif return do_fork(flags|CLONE_VM|CLONE_UNTRACED, 0, ®s, 0, NULL, NULL); }