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 <alexandre.dalton@intel.com>
This commit is contained in:
Alexandre d'Alton 2016-03-11 18:29:14 +01:00 committed by Anas Nashif
commit f91e55b798
9 changed files with 77 additions and 5 deletions

View file

@ -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"

View file

@ -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

View file

@ -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.

View file

@ -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

View file

@ -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.

View file

@ -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))

View file

@ -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;

View file

@ -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 {

View file

@ -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)