From f91e55b79851ff511bee33de33c246fefcce9fb4 Mon Sep 17 00:00:00 2001 From: Alexandre d'Alton Date: Fri, 11 Mar 2016 18:29:14 +0100 Subject: [PATCH] arc: implement stack checking ARC CPU has stack checking feature that allows to trigger an exception whenever the stack is incorrectly accessed. This patch implements the stack_top and stack_base register updates on context switches, and activates the Stack Checking bit of STATUS32 register when the CPU is in the context of a fiber or task. As GCC accesses the non-yet allocated stack with frame pointer enabled, this patch also add the omit-frame-pointer gcc flag in order to work properly. Change-Id: Ia9e224085a03bd29d682fb8f51f8e712f2ccb556 Signed-off-by: Alexandre d'Alton --- arch/arc/Kconfig | 8 ++++++++ arch/arc/Makefile | 1 + arch/arc/core/fast_irq.S | 18 ++++++++++++++++-- arch/arc/core/offsets/offsets.c | 3 +++ arch/arc/core/regular_irq.S | 13 +++++++++++++ arch/arc/core/swap.S | 25 ++++++++++++++++++++++++- arch/arc/core/thread.c | 6 +++++- arch/arc/include/nano_private.h | 3 +++ include/arch/arc/v2/aux_regs.h | 5 ++++- 9 files changed, 77 insertions(+), 5 deletions(-) diff --git a/arch/arc/Kconfig b/arch/arc/Kconfig index b2fa03a306a..23bb049a5dd 100644 --- a/arch/arc/Kconfig +++ b/arch/arc/Kconfig @@ -130,6 +130,14 @@ config FIRQ_STACK_SIZE FIRQs and regular IRQs have different stacks so that a FIRQ can start running without doing stack switching in software. +config ARC_STACK_CHECKING + bool "Enable Stack Checking" + depends on CPU_ARCV2 + default n + help + ARCV2 has a special feature allowing to check stack overflows. This + enables code that allows using this debug feature + config FAULT_DUMP int prompt "Fault dump level" diff --git a/arch/arc/Makefile b/arch/arc/Makefile index 09ae0460cd5..a31e5ba8619 100644 --- a/arch/arc/Makefile +++ b/arch/arc/Makefile @@ -1,4 +1,5 @@ cflags-y += $(call cc-option,-ffunction-sections,) $(call cc-option,-fdata-sections,) +cflags-$(CONFIG_ARC_STACK_CHECKING) = $(call cc-option,-fomit-frame-pointer) cflags-$(CONFIG_LTO) = $(call cc-option,-flto,) include $(srctree)/arch/$(ARCH)/soc/$(SOC_NAME)/Makefile diff --git a/arch/arc/core/fast_irq.S b/arch/arc/core/fast_irq.S index f41d5f0e01d..1ded85b35a7 100644 --- a/arch/arc/core/fast_irq.S +++ b/arch/arc/core/fast_irq.S @@ -48,8 +48,8 @@ SECTION_VAR(NOINIT, _firq_stack) * LP_COUNT/LP_END registers, which are not banked. * * If all FIRQ ISRs are programmed such that there are no use of the LP - * registers (ie. no LPcc instruction), then the kernel can be configured to - * remove the use of _firq_enter(). + * registers (ie. no LPcc instruction), and CONFIG_ARC_STACK_CHECKING is + * not set, then the kernel can be configured to remove the use of _firq_enter(). * * When entering a FIRQ, interrupts might as well be locked: the processor is * running at its highest priority, and cannot be preempted by anything. @@ -61,6 +61,13 @@ SECTION_VAR(NOINIT, _firq_stack) SECTION_FUNC(TEXT, _firq_enter) +#ifdef CONFIG_ARC_STACK_CHECKING + /* disable stack checking */ + lr r2, [_ARC_V2_STATUS32] + bclr r2, r2, _ARC_V2_STATUS32_SC_BIT + kflag r2 +#endif + #ifndef CONFIG_FIRQ_NO_LPCC /* * Unlike the rest of context switching code, r2 is loaded with something @@ -184,6 +191,13 @@ _firq_reschedule: ld r3, [r2, __tTCS_link_OFFSET] st r3, [r1, __tNANO_fiber_OFFSET] +#ifdef CONFIG_ARC_STACK_CHECKING + /* Use stack top and down registers from restored context */ + add r3, r2, __tTCS_NOFLOAT_SIZEOF + sr r3, [_ARC_V2_KSTACK_TOP] + ld r3, [r2, __tTCS_stack_top_OFFSET] + sr r3, [_ARC_V2_KSTACK_BASE] +#endif /* * _load_callee_saved_regs expects incoming thread in r2. * _load_callee_saved_regs restores the stack pointer. diff --git a/arch/arc/core/offsets/offsets.c b/arch/arc/core/offsets/offsets.c index 3eda9209205..1e95ae8b485 100644 --- a/arch/arc/core/offsets/offsets.c +++ b/arch/arc/core/offsets/offsets.c @@ -48,6 +48,9 @@ GEN_OFFSET_SYM(tNANO, idle); GEN_OFFSET_SYM(tTCS, intlock_key); GEN_OFFSET_SYM(tTCS, relinquish_cause); GEN_OFFSET_SYM(tTCS, return_value); +#ifdef CONFIG_ARC_STACK_CHECKING +GEN_OFFSET_SYM(tTCS, stack_top); +#endif #ifdef CONFIG_THREAD_CUSTOM_DATA GEN_OFFSET_SYM(tTCS, custom_data); #endif diff --git a/arch/arc/core/regular_irq.S b/arch/arc/core/regular_irq.S index d814f106c1c..1acb5a2e0bb 100644 --- a/arch/arc/core/regular_irq.S +++ b/arch/arc/core/regular_irq.S @@ -51,6 +51,12 @@ GTEXT(_rirq_exit) SECTION_FUNC(TEXT, _rirq_enter) mov r1, _nanokernel +#ifdef CONFIG_ARC_STACK_CHECKING + /* disable stack checking */ + lr r2, [_ARC_V2_STATUS32] + bclr r2, r2, _ARC_V2_STATUS32_SC_BIT + kflag r2 +#endif ld r2, [r1, __tNANO_current_OFFSET] #if CONFIG_NUM_REGULAR_IRQ_PRIO_LEVELS == 1 st sp, [r2, __tTCS_preempReg_OFFSET + __tPreempt_sp_OFFSET] @@ -126,6 +132,13 @@ _rirq_reschedule: ld r3, [r2, __tTCS_link_OFFSET] st r3, [r1, __tNANO_fiber_OFFSET] +#ifdef CONFIG_ARC_STACK_CHECKING + /* Use stack top and down registers from restored context */ + add r3, r2, __tTCS_NOFLOAT_SIZEOF + sr r3, [_ARC_V2_KSTACK_TOP] + ld r3, [r2, __tTCS_stack_top_OFFSET] + sr r3, [_ARC_V2_KSTACK_BASE] +#endif /* * _load_callee_saved_regs expects incoming thread in r2. * _load_callee_saved_regs restores the stack pointer. diff --git a/arch/arc/core/swap.S b/arch/arc/core/swap.S index 7111de3e747..2fe264d42c4 100644 --- a/arch/arc/core/swap.S +++ b/arch/arc/core/swap.S @@ -86,6 +86,11 @@ SECTION_FUNC(TEXT, _Swap) */ lr r3, [_ARC_V2_STATUS32] push_s r3 +#ifdef CONFIG_ARC_STACK_CHECKING + /* disable stack checking during swap */ + bclr r3, r3, _ARC_V2_STATUS32_SC_BIT + kflag r3 +#endif push_s blink _save_callee_saved_regs @@ -118,7 +123,13 @@ _finish_swapping_to_thread: ld r3, [r2, __tTCS_flags_OFFSET] st r3, [r1, __tNANO_flags_OFFSET] #endif - +#ifdef CONFIG_ARC_STACK_CHECKING + /* Use stack top and down registers from restored context */ + add r3, r2, __tTCS_NOFLOAT_SIZEOF + sr r3, [_ARC_V2_KSTACK_TOP] + ld r3, [r2, __tTCS_stack_top_OFFSET] + sr r3, [_ARC_V2_KSTACK_BASE] +#endif /* XXX - can be moved to delay slot of _CAUSE_RIRQ ? */ st r2, [r1, __tNANO_current_OFFSET] @@ -168,11 +179,23 @@ _swap_return_from_firq: bbit1 ilink, _ARC_V2_STATUS32_AE_BIT, _return_from_exc ld ilink, [sp, -4] /* status32 into ilink */ +#ifdef CONFIG_ARC_STACK_CHECKING + and ilink, ilink, 0x7fffbffe // keep SC off till we stop using under SP +#else and ilink, ilink, 0x7ffffffe // keep interrupts disabled until seti +#endif kflag ilink ld ilink, [sp, -8] /* pc into ilink */ +#ifdef CONFIG_ARC_STACK_CHECKING + /* Enable stack checking now it is safe */ + push_s r3 + lr r3, [_ARC_V2_STATUS32] + bset r3, r3, _ARC_V2_STATUS32_SC_BIT + flag r3 + pop_s r3 +#endif j.d [ilink] seti (_ARC_V2_DEF_IRQ_LEVEL | (1 << 4)) diff --git a/arch/arc/core/thread.c b/arch/arc/core/thread.c index 809a6499d7b..ba0a8a2dba9 100644 --- a/arch/arc/core/thread.c +++ b/arch/arc/core/thread.c @@ -131,8 +131,12 @@ void _new_thread(char *pStackMem, unsigned stackSize, _thread_entry_t pEntry, * enable the interrupts based on intlock_key * value. */ +#ifdef CONFIG_ARC_STACK_CHECKING + pInitCtx->status32 = _ARC_V2_STATUS32_SC | _ARC_V2_STATUS32_E(_ARC_V2_DEF_IRQ_LEVEL); + tcs->stack_top = (uint32_t) stackEnd; +#else pInitCtx->status32 = _ARC_V2_STATUS32_E(_ARC_V2_DEF_IRQ_LEVEL); - +#endif tcs->link = NULL; tcs->flags = priority == -1 ? TASK | PREEMPTIBLE : FIBER; tcs->prio = priority; diff --git a/arch/arc/include/nano_private.h b/arch/arc/include/nano_private.h index 7adca7ad18e..6430fc8797c 100644 --- a/arch/arc/include/nano_private.h +++ b/arch/arc/include/nano_private.h @@ -194,6 +194,9 @@ struct tcs { #ifdef CONFIG_ERRNO int errno_var; #endif +#ifdef CONFIG_ARC_STACK_CHECKING + uint32_t stack_top; +#endif }; struct s_NANO { diff --git a/include/arch/arc/v2/aux_regs.h b/include/arch/arc/v2/aux_regs.h index 71738b7f84b..77ccd87ad6a 100644 --- a/include/arch/arc/v2/aux_regs.h +++ b/include/arch/arc/v2/aux_regs.h @@ -46,6 +46,8 @@ extern "C" { #define _ARC_V2_IRQ_PRIO_PEND 0x200 #define _ARC_V2_AUX_IRQ_HINT 0x201 #define _ARC_V2_IRQ_PRIORITY 0x206 +#define _ARC_V2_KSTACK_TOP 0x264 +#define _ARC_V2_KSTACK_BASE 0x265 #define _ARC_V2_ERET 0x400 #define _ARC_V2_ERSTATUS 0x402 #define _ARC_V2_ECR 0x403 @@ -71,7 +73,8 @@ extern "C" { #define _ARC_V2_STATUS32_Z (1 << 11) #define _ARC_V2_STATUS32_L (1 << 12) #define _ARC_V2_STATUS32_DZ (1 << 13) -#define _ARC_V2_STATUS32_SC (1 << 14) +#define _ARC_V2_STATUS32_SC_BIT 14 +#define _ARC_V2_STATUS32_SC (1 << _ARC_V2_STATUS32_SC_BIT) #define _ARC_V2_STATUS32_ES (1 << 15) #define _ARC_V2_STATUS32_RB(x) ((x) << 16) #define _ARC_V2_STATUS32_IE (1 << 31)