Skip to content

Main thread stack checking alt impl #2402

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Aug 11, 2016
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions hal/common/retarget.cpp
Original file line number Diff line number Diff line change
@@ -23,6 +23,8 @@
#include "mbed_interface.h"
#include "SingletonPtr.h"
#include "PlatformMutex.h"
#include "mbed_error.h"
#include <stdlib.h>
#if DEVICE_STDIO_MESSAGES
#include <stdio.h>
#endif
@@ -68,6 +70,10 @@ extern const char __stdout_name[] = "/stdout";
extern const char __stderr_name[] = "/stderr";
#endif

// Heap limits - only used if set
unsigned char *mbed_heap_start = 0;
uint32_t mbed_heap_size = 0;

/* newlib has the filehandle field in the FILE struct as a short, so
* we can't just return a Filehandle* from _open and instead have to
* put it in a filehandles array and return the index into that array
@@ -596,6 +602,12 @@ extern "C" caddr_t _sbrk(int incr) {
return (caddr_t)-1;
}

// Additional heap checking if set
if (mbed_heap_size && (new_heap >= mbed_heap_start + mbed_heap_size)) {
errno = ENOMEM;
return (caddr_t)-1;
}

heap = new_heap;
return (caddr_t) prev_heap;
}
@@ -714,3 +726,34 @@ extern "C" void __env_unlock( struct _reent *_r )
#endif

} // namespace mbed

void *operator new(std::size_t count)
{
void *buffer = malloc(count);
if (NULL == buffer) {
error("Operator new out of memory\r\n");
}
return buffer;
}

void *operator new[](std::size_t count)
{
void *buffer = malloc(count);
if (NULL == buffer) {
error("Operator new[] out of memory\r\n");
}
return buffer;
}

void operator delete(void *ptr)
{
if (ptr != NULL) {
free(ptr);
}
}
void operator delete[](void *ptr)
{
if (ptr != NULL) {
free(ptr);
}
}
Original file line number Diff line number Diff line change
@@ -25,7 +25,7 @@
; POSSIBILITY OF SUCH DAMAGE.
; ---------------------------------------------------------------------------*/

__initial_sp EQU 0x20008000
__initial_sp EQU 0x20010000

PRESERVE8
THUMB
Original file line number Diff line number Diff line change
@@ -11,7 +11,7 @@ define symbol __ICFEDIT_region_IRAM_end__ = 0x20010000;
define symbol __ICFEDIT_region_XRAM_start__ = 0x60000000;
define symbol __ICFEDIT_region_XRAM_end__ = 0x60100000;
/*-Sizes-*/
define symbol __ICFEDIT_size_cstack__ = 0x3000;
define symbol __ICFEDIT_size_cstack__ = 0x2000;
define symbol __ICFEDIT_size_heap__ = 0xC0000;
/**** End of ICF editor section. ###ICF###*/

27 changes: 0 additions & 27 deletions rtos/rtx/TARGET_CORTEX_M/HAL_CM.c
Original file line number Diff line number Diff line change
@@ -90,32 +90,6 @@ void rt_init_stack (P_TCB p_TCB, FUNCP task_body) {
/* Task entry point. */
p_TCB->ptask = task_body;


#ifdef __MBED_CMSIS_RTOS_CM
/* Set a magic word for checking of stack overflow.
For the main thread (ID: MAIN_THREAD_ID) the stack is in a memory area shared with the
heap, therefore the last word of the stack is a moving target.
We want to do stack/heap collision detection instead.
Similar applies to stack filling for the magic pattern.
*/
if (p_TCB->task_id != MAIN_THREAD_ID) {
p_TCB->stack[0] = MAGIC_WORD;

/* Initialize stack with magic pattern. */
if (os_stackinfo & 0x10000000U) {
if (size > (16U+1U)) {
for (i = ((size - 16U)/2U) - 1U; i; i--) {
stk -= 2U;
stk[1] = MAGIC_PATTERN;
stk[0] = MAGIC_PATTERN;
}
if (--stk > p_TCB->stack) {
*stk = MAGIC_PATTERN;
}
}
}
}
#else
/* Initialize stack with magic pattern. */
if (os_stackinfo & 0x10000000U) {
if (size > (16U+1U)) {
@@ -132,7 +106,6 @@ void rt_init_stack (P_TCB p_TCB, FUNCP task_body) {

/* Set a magic word for checking of stack overflow. */
p_TCB->stack[0] = MAGIC_WORD;
#endif
}


212 changes: 148 additions & 64 deletions rtos/rtx/TARGET_CORTEX_M/RTX_CM_lib.h
100755 → 100644
Original file line number Diff line number Diff line change
@@ -350,7 +350,46 @@ __attribute__((used)) void _mutex_release (OS_ID *mutex) {

/* Main Thread definition */
extern void pre_main (void);
osThreadDef_t os_thread_def_main = {(os_pthread)pre_main, osPriorityNormal, 1U, 0U, NULL};

#if defined(TARGET_MCU_NRF51822) || defined(TARGET_MCU_NRF52832)
static uint32_t thread_stack_main[DEFAULT_STACK_SIZE / sizeof(uint32_t)];
#else
static uint32_t thread_stack_main[DEFAULT_STACK_SIZE * 2 / sizeof(uint32_t)];
#endif
osThreadDef_t os_thread_def_main = {(os_pthread)pre_main, osPriorityNormal, 1U, sizeof(thread_stack_main), thread_stack_main};

/*
* IAR Default Memory layout notes:
* -Heap defined by "HEAP" region in .icf file
* -Interrupt stack defined by "CSTACK" region in .icf file
* -Value INITIAL_SP is ignored
*
* IAR Custom Memory layout notes:
* -There is no custom layout available for IAR - everything must be defined in
* the .icf file and use the default layout
*
*
* GCC Default Memory layout notes:
* -Block of memory from symbol __end__ to define INITIAL_SP used to setup interrupt
* stack and heap in the function set_stack_heap()
* -ISR_STACK_SIZE can be overridden to be larger or smaller
*
* GCC Custom Memory layout notes:
* -Heap can be explicitly placed by defining both HEAP_START and HEAP_SIZE
* -Interrupt stack can be explicitly placed by defining both ISR_STACK_START and ISR_STACK_SIZE
*
*
* ARM Memory layout
* -Block of memory from end of region "RW_IRAM1" to define INITIAL_SP used to setup interrupt
* stack and heap in the function set_stack_heap()
* -ISR_STACK_SIZE can be overridden to be larger or smaller
*
* ARM Custom Memory layout notes:
* -Heap can be explicitly placed by defining both HEAP_START and HEAP_SIZE
* -Interrupt stack can be explicitly placed by defining both ISR_STACK_START and ISR_STACK_SIZE
*
*/


// This define should be probably moved to the CMSIS layer
#if defined(TARGET_LPC1768)
@@ -381,12 +420,15 @@ osThreadDef_t os_thread_def_main = {(os_pthread)pre_main, osPriorityNormal, 1U,
#define INITIAL_SP (0x20003000UL)

#elif defined(TARGET_K64F)
#if defined(FEATURE_UVISOR) && defined(TARGET_UVISOR_SUPPORTED)
#if defined(__GNUC__) && !defined(__CC_ARM) /* GCC */
extern uint32_t __StackTop[];
#define INITIAL_SP (__StackTop)
#else
#define INITIAL_SP (0x20030000UL)
#endif
#if defined(__CC_ARM) || defined(__GNUC__)
#define ISR_STACK_SIZE (0x1000)
#endif

#elif defined(TARGET_K22F)
#define INITIAL_SP (0x20010000UL)
@@ -534,19 +576,25 @@ extern uint32_t __StackTop[];

#elif defined(TARGET_NUMAKER_PFM_NUC472)
# if defined(__CC_ARM)
extern uint32_t Image$$ARM_LIB_STACK$$ZI$$Limit[];
extern uint32_t Image$$ARM_LIB_STACK$$ZI$$Base[];
#define INITIAL_SP ((uint32_t) Image$$ARM_LIB_STACK$$ZI$$Limit)
#define FINAL_SP ((uint32_t) Image$$ARM_LIB_STACK$$ZI$$Base)
extern uint32_t Image$$ARM_LIB_HEAP$$Base[];
extern uint32_t Image$$ARM_LIB_HEAP$$Length[];
extern uint32_t Image$$ARM_LIB_STACK$$ZI$$Base[];
extern uint32_t Image$$ARM_LIB_STACK$$ZI$$Length[];
#define HEAP_START ((unsigned char*) Image$$ARM_LIB_HEAP$$Base)
#define HEAP_SIZE ((uint32_t) Image$$ARM_LIB_HEAP$$Length)
#define ISR_STACK_START ((unsigned char*)Image$$ARM_LIB_STACK$$ZI$$Base)
#define ISR_STACK_SIZE ((uint32_t)Image$$ARM_LIB_STACK$$ZI$$Length)
# elif defined(__GNUC__)
extern uint32_t __StackTop[];
extern uint32_t __StackLimit[];
#define INITIAL_SP ((uint32_t) __StackTop)
#define FINAL_SP ((uint32_t) __StackLimit)
extern uint32_t __end__[];
extern uint32_t __HeapLimit[];
#define HEAP_START ((unsigned char*)__end__)
#define HEAP_SIZE ((uint32_t)((uint32_t)__HeapLimit - (uint32_t)HEAP_START))
#define ISR_STACK_START ((unsigned char*)__StackLimit)
#define ISR_STACK_SIZE ((uint32_t)((uint32_t)__StackTop - (uint32_t)__StackLimit))
# elif defined(__ICCARM__)
#pragma section="CSTACK"
#define INITIAL_SP ((uint32_t) __section_end("CSTACK"))
#define FINAL_SP ((uint32_t) __section_begin("CSTACK"))
/* No region declarations needed */
# else
#error "no toolchain defined"
# endif
@@ -556,48 +604,90 @@ extern uint32_t __StackLimit[];

#endif

#ifdef __CC_ARM
#if defined(TARGET_NUMAKER_PFM_NUC472)
extern uint32_t Image$$ARM_LIB_HEAP$$Base[];
#define HEAP_START ((uint32_t) Image$$ARM_LIB_HEAP$$Base)
#else
extern uint32_t Image$$RW_IRAM1$$ZI$$Limit[];
#define HEAP_START (Image$$RW_IRAM1$$ZI$$Limit)
#endif
#elif defined(__GNUC__)
extern uint32_t __end__[];
#define HEAP_START (__end__)
#elif defined(__ICCARM__)
#pragma section="HEAP"
#define HEAP_END (void *)__section_end("HEAP")
#endif

void set_main_stack(void) {
#if defined(TARGET_NUMAKER_PFM_NUC472)
// Scheduler stack: OS_MAINSTKSIZE words
// Main thread stack: Reserved stack size - OS_MAINSTKSIZE words
os_thread_def_main.stack_pointer = (uint32_t *) FINAL_SP;
os_thread_def_main.stacksize = (uint32_t) INITIAL_SP - (uint32_t) FINAL_SP - OS_MAINSTKSIZE * 4;
#else
uint32_t interrupt_stack_size = ((uint32_t)OS_MAINSTKSIZE * 4);
#if defined(__ICCARM__)
/* For IAR heap is defined .icf file */
uint32_t main_stack_size = ((uint32_t)INITIAL_SP - (uint32_t)HEAP_END) - interrupt_stack_size;
#else
/* For ARM , uARM, or GCC_ARM , heap can grow and reach main stack */
uint32_t heap_plus_stack_size = ((uint32_t)INITIAL_SP - (uint32_t)HEAP_START) - interrupt_stack_size;
// Main thread's stack is 1/4 of the heap
uint32_t main_stack_size = heap_plus_stack_size/4;
extern unsigned char *mbed_heap_start;
extern uint32_t mbed_heap_size;

unsigned char *mbed_stack_isr_start = 0;
uint32_t mbed_stack_isr_size = 0;

/*
* Sanity check values
*/
#if defined(__ICCARM__) && \
(defined(HEAP_START) || defined(HEAP_SIZE) || \
defined(ISR_STACK_START) && defined(ISR_STACK_SIZE))
#error "No custom layout allowed for IAR. Use .icf file instead"
#endif
#if defined(HEAP_START) && !defined(HEAP_SIZE)
#error "HEAP_SIZE must be defined if HEAP_START is defined"
#endif
#if defined(ISR_STACK_START) && !defined(ISR_STACK_SIZE)
#error "ISR_STACK_SIZE must be defined if ISR_STACK_START is defined"
#endif
#if defined(HEAP_SIZE) && !defined(HEAP_START)
#error "HEAP_START must be defined if HEAP_SIZE is defined"
#endif
// The main thread must be 4 byte aligned
uint32_t main_stack_start = ((uint32_t)INITIAL_SP - interrupt_stack_size - main_stack_size) & ~0x7;

// That is the bottom of the main stack block: no collision detection
os_thread_def_main.stack_pointer = (uint32_t*)main_stack_start;
/* Interrupt stack and heap always defined for IAR
* Main thread defined here
*/
#if defined(__ICCARM__)
#pragma section="CSTACK"
#pragma section="HEAP"
#define HEAP_START ((unsigned char*)__section_begin("HEAP"))
#define HEAP_SIZE ((uint32_t)__section_size("HEAP"))
#define ISR_STACK_START ((unsigned char*)__section_begin("CSTACK"))
#define ISR_STACK_SIZE ((uint32_t)__section_size("CSTACK"))
#endif

/* Define heap region if it has not been defined already */
#if !defined(HEAP_START)
#if defined(__ICCARM__)
#error "Heap should already be defined for IAR"
#elif defined(__CC_ARM)
extern uint32_t Image$$RW_IRAM1$$ZI$$Limit[];
#define HEAP_START ((unsigned char*)Image$$RW_IRAM1$$ZI$$Limit)
#define HEAP_SIZE ((uint32_t)((uint32_t)INITIAL_SP - (uint32_t)HEAP_START))
#elif defined(__GNUC__)
extern uint32_t __end__[];
#define HEAP_START ((unsigned char*)__end__)
#define HEAP_SIZE ((uint32_t)((uint32_t)INITIAL_SP - (uint32_t)HEAP_START))
#endif
#endif

/* Define stack sizes if they haven't been set already */
#if !defined(ISR_STACK_SIZE)
#define ISR_STACK_SIZE ((uint32_t)OS_MAINSTKSIZE * 4)
#endif

/*
* set_stack_heap purpose is to set the following variables:
* -mbed_heap_start
* -mbed_heap_size
* -mbed_stack_isr_start
* -mbed_stack_isr_size
*
* Along with setting up os_thread_def_main
*/
void set_stack_heap(void) {

// Leave OS_MAINSTKSIZE words for the scheduler and interrupts
os_thread_def_main.stacksize = main_stack_size;
unsigned char *free_start = HEAP_START;
uint32_t free_size = HEAP_SIZE;

#ifdef ISR_STACK_START
/* Interrupt stack explicitly specified */
mbed_stack_isr_size = ISR_STACK_SIZE;
mbed_stack_isr_start = ISR_STACK_START;
#else
/* Interrupt stack - reserve space at the end of the free block */
mbed_stack_isr_size = ISR_STACK_SIZE;
mbed_stack_isr_start = free_start + free_size - mbed_stack_isr_size;
free_size -= mbed_stack_isr_size;
#endif

/* Heap - everything else */
mbed_heap_size = free_size;
mbed_heap_start = free_start;
}

#if defined (__CC_ARM)
@@ -611,7 +701,7 @@ void $Super$$__cpp_initialize__aeabi_(void);
void _main_init (void) {
osKernelInitialize();
#ifdef __MBED_CMSIS_RTOS_CM
set_main_stack();
set_stack_heap();
#endif
osThreadCreate(&os_thread_def_main, NULL);
osKernelStart();
@@ -633,15 +723,12 @@ void pre_main()

#else

void * armcc_heap_base;
void * armcc_heap_top;

int main(void);

void pre_main (void)
{
singleton_mutex_id = osMutexCreate(osMutex(singleton_mutex));
__rt_lib_init((unsigned)armcc_heap_base, (unsigned)armcc_heap_top);
__rt_lib_init((unsigned)mbed_heap_start, (unsigned)(mbed_heap_start + mbed_heap_size));
main();
}

@@ -656,12 +743,10 @@ void pre_main (void)
__asm void __rt_entry (void) {

IMPORT __user_setup_stackheap
IMPORT armcc_heap_base
IMPORT armcc_heap_top
IMPORT os_thread_def_main
IMPORT osKernelInitialize
#ifdef __MBED_CMSIS_RTOS_CM
IMPORT set_main_stack
IMPORT set_stack_heap
#endif
IMPORT osKernelStart
IMPORT osThreadCreate
@@ -675,13 +760,12 @@ __asm void __rt_entry (void) {
* ARM Compiler ARM C and C++ Libraries and Floating-Point Support User Guide
*/
BL __user_setup_stackheap
LDR R3,=armcc_heap_base
LDR R4,=armcc_heap_top
STR R0,[R3]
STR R2,[R4]
/* Ignore return value of __user_setup_stackheap since
* this will be setup by set_stack_heap
*/
BL osKernelInitialize
#ifdef __MBED_CMSIS_RTOS_CM
BL set_main_stack
BL set_stack_heap
#endif
LDR R0,=os_thread_def_main
MOVS R1,#0
@@ -719,7 +803,7 @@ __attribute__((naked)) void software_init_hook_rtos (void) {
__asm (
"bl osKernelInitialize\n"
#ifdef __MBED_CMSIS_RTOS_CM
"bl set_main_stack\n"
"bl set_stack_heap\n"
#endif
"ldr r0,=os_thread_def_main\n"
"movs r1,#0\n"
@@ -796,7 +880,7 @@ void __iar_program_start( void )
#endif
osKernelInitialize();
#ifdef __MBED_CMSIS_RTOS_CM
set_main_stack();
set_stack_heap();
#endif
osThreadCreate(&os_thread_def_main, NULL);
osKernelStart();
179 changes: 179 additions & 0 deletions rtos/rtx/TARGET_CORTEX_M/TESTS/memory/heap_and_stack/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
/*
* Copyright (c) 2016-2016, ARM Limited, All Rights Reserved
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "greentea-client/test_env.h"
#include "cmsis.h"
#include "mbed.h"
#include "rtos.h"
#include "mbed_assert.h"

// Amount to malloc for each iteration
#define MALLOC_TEST_SIZE 256
// Malloc fill pattern
#define MALLOC_FILL 0x55

extern uint32_t mbed_heap_start;
extern uint32_t mbed_heap_size;
extern uint32_t mbed_stack_isr_start;
extern uint32_t mbed_stack_isr_size;

static uint32_t max_allocation_size = 0;

static bool inrange(uint32_t addr, uint32_t start, uint32_t size);
static bool rangeinrange(uint32_t addr, uint32_t size, uint32_t start, uint32_t len);
static bool valid_fill(uint8_t * data, uint32_t size, uint8_t fill);
static bool allocate_and_fill_heap(void);
static bool check_and_free_heap(void);

int main (void) {
GREENTEA_SETUP(30, "default_auto");

char c;
char * initial_stack = &c;
char *initial_heap;

// Sanity check malloc
initial_heap = (char*)malloc(1);
if (initial_heap == NULL) {
printf("Unable to malloc a single byte\n");
GREENTEA_TESTSUITE_RESULT(false);
}

if (!inrange((uint32_t)initial_heap, mbed_heap_start, mbed_heap_size)) {
printf("Heap in wrong location\n");
GREENTEA_TESTSUITE_RESULT(false);
}
// MSP stack should be very near end (test using within 128 bytes)
uint32_t msp = __get_MSP();
if (!inrange(msp, mbed_stack_isr_start + mbed_stack_isr_size - 128, 128)) {
printf("Interrupt stack in wrong location\n");
GREENTEA_TESTSUITE_RESULT(false);
}

// Fully allocate the heap and stack
bool ret = true;
ret = ret && allocate_and_fill_heap();
ret = ret && check_and_free_heap();

// Force a task switch so a stack check is performed
Thread::wait(10);

printf("Total size dynamically allocated: %lu\n", max_allocation_size);

GREENTEA_TESTSUITE_RESULT(ret);
}

/*
* Return true if addr is in range [start:start+size)
*/
static bool inrange(uint32_t addr, uint32_t start, uint32_t size)
{
return (addr >= start) && (addr < start + size) ? true : false;
}

/*
* Return true if [addr:addr+size] is inside [start:start+len]
*/
static bool rangeinrange(uint32_t addr, uint32_t size, uint32_t start, uint32_t len)
{
if (addr + size > start + len) {
return false;
}
if (addr < start) {
return false;
}
return true;
}

/*
* Return true of the region is filled only the the specified fill value
*/
static bool valid_fill(uint8_t * data, uint32_t size, uint8_t fill)
{
for (uint32_t i = 0; i < size; i++) {
if (data[i] != fill) {
return false;
}
}
return true;
}

struct linked_list {
linked_list * next;
uint8_t data[MALLOC_TEST_SIZE];
};

static linked_list *head = NULL;
static bool allocate_and_fill_heap()
{

linked_list *current;

current = (linked_list*)malloc(sizeof(linked_list));
if (0 == current) {
return false;
}
current->next = NULL;
memset((void*)current->data, MALLOC_FILL, sizeof(current->data));

// Allocate until malloc returns NULL
bool pass = true;
head = current;
while (true) {

// Allocate
linked_list *temp = (linked_list*)malloc(sizeof(linked_list));
if (NULL == temp) {
break;
}
if (!rangeinrange((uint32_t)temp, sizeof(linked_list), mbed_heap_start, mbed_heap_size)) {
printf("Memory allocation out of range\n");
pass = false;
break;
}

// Init
temp->next = NULL;
memset((void*)temp->data, MALLOC_FILL, sizeof(current->data));

// Add to list
current->next = temp;
current = temp;
}
return pass;
}

static bool check_and_free_heap()
{
uint32_t total_size = 0;
linked_list * current = head;
bool pass = true;
while (current != NULL) {
total_size += sizeof(linked_list);
if (!valid_fill(current->data, sizeof(current->data), MALLOC_FILL)) {
pass = false;
}
linked_list * next = current->next;
free(current);
current = next;
}

max_allocation_size = total_size;
return pass;
}
8 changes: 0 additions & 8 deletions rtos/rtx/TARGET_CORTEX_M/cmsis_os.h
Original file line number Diff line number Diff line change
@@ -80,14 +80,6 @@
#define OS_TIMERS 0
#endif

/* If os timers macro is set to 0, there's no timer thread created, therefore
* main thread has tid 0x01
*/
#if defined(OS_TIMERS) && (OS_TIMERS == 0)
#define MAIN_THREAD_ID 0x01
#else
#define MAIN_THREAD_ID 0x02
#endif
#endif

#define DEFAULT_STACK_SIZE (WORDS_STACK_SIZE*4)
12 changes: 0 additions & 12 deletions rtos/rtx/TARGET_CORTEX_M/rt_System.c
Original file line number Diff line number Diff line change
@@ -313,22 +313,10 @@ void rt_systick (void) {
/*--------------------------- rt_stk_check ----------------------------------*/

__weak void rt_stk_check (void) {
#ifdef __MBED_CMSIS_RTOS_CM
/* Check for stack overflow. */
if (os_tsk.run->task_id == MAIN_THREAD_ID) {
// TODO: For the main thread the check should be done against the main heap pointer
} else {
if ((os_tsk.run->tsk_stack < (U32)os_tsk.run->stack) ||
(os_tsk.run->stack[0] != MAGIC_WORD)) {
os_error (OS_ERR_STK_OVF);
}
}
#else
if ((os_tsk.run->tsk_stack < (U32)os_tsk.run->stack) ||
(os_tsk.run->stack[0] != MAGIC_WORD)) {
os_error (OS_ERR_STK_OVF);
}
#endif
}

/*----------------------------------------------------------------------------