mirror of
https://github.com/zephyrproject-rtos/zephyr
synced 2025-08-26 02:35:58 +00:00
This macro is slated for complete removal, as it's not possible on arches with an MPU stack guard to know the true buffer bounds without also knowing the runtime state of its associated thread. As removing this completely would be invasive to where we are in the 1.14 release, demote to a private kernel Z_ API instead. The current way that the macro is being used internally will not cause any undue harm, we just don't want any external code depending on it. The final work to remove this (and overhaul stack specification in general) will take place in 1.15 in the context of #14269 Fixes: #14766 Signed-off-by: Andrew Boie <andrew.p.boie@intel.com>
210 lines
5.9 KiB
C
210 lines
5.9 KiB
C
/*
|
|
* Copyright (c) 2017, Intel Corporation
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
#include <misc/printk.h>
|
|
#include <string.h>
|
|
#include <xtensa-asm2.h>
|
|
#include <kernel.h>
|
|
#include <ksched.h>
|
|
#include <kernel_structs.h>
|
|
#include <kernel_internal.h>
|
|
#include <kswap.h>
|
|
#include <_soc_inthandlers.h>
|
|
|
|
void *xtensa_init_stack(int *stack_top,
|
|
void (*entry)(void *, void *, void *),
|
|
void *arg1, void *arg2, void *arg3)
|
|
{
|
|
/* We cheat and shave 16 bytes off, the top four words are the
|
|
* A0-A3 spill area for the caller of the entry function,
|
|
* which doesn't exist. It will never be touched, so we
|
|
* arrange to enter the function with a CALLINC of 1 and a
|
|
* stack pointer 16 bytes above the top, so its ENTRY at the
|
|
* start will decrement the stack pointer by 16.
|
|
*/
|
|
const int bsasz = BASE_SAVE_AREA_SIZE - 16;
|
|
void **bsa = (void **) (((char *) stack_top) - bsasz);
|
|
|
|
(void)memset(bsa, 0, bsasz);
|
|
|
|
bsa[BSA_PC_OFF/4] = z_thread_entry;
|
|
bsa[BSA_PS_OFF/4] = (void *)(PS_WOE | PS_UM | PS_CALLINC(1));
|
|
|
|
/* Arguments to z_thread_entry(). Remember these start at A6,
|
|
* which will be rotated into A2 by the ENTRY instruction that
|
|
* begins the C function. And A4-A7 and A8-A11 are optional
|
|
* quads that live below the BSA!
|
|
*/
|
|
bsa[-1] = arg1; /* a7 */
|
|
bsa[-2] = entry; /* a6 */
|
|
bsa[-3] = 0; /* a5 */
|
|
bsa[-4] = 0; /* a4 */
|
|
|
|
bsa[-5] = 0; /* a11 */
|
|
bsa[-6] = 0; /* a10 */
|
|
bsa[-7] = arg3; /* a9 */
|
|
bsa[-8] = arg2; /* a8 */
|
|
|
|
/* Finally push the BSA pointer and return the stack pointer
|
|
* as the handle
|
|
*/
|
|
bsa[-9] = bsa;
|
|
return &bsa[-9];
|
|
}
|
|
|
|
/* This is a kernel hook, just a wrapper around other APIs. Build
|
|
* only if we're using asm2 as the core OS interface and not just as
|
|
* utilities/testables.
|
|
*/
|
|
#ifdef CONFIG_XTENSA_ASM2
|
|
void z_new_thread(struct k_thread *thread, k_thread_stack_t *stack, size_t sz,
|
|
k_thread_entry_t entry, void *p1, void *p2, void *p3,
|
|
int prio, unsigned int opts)
|
|
{
|
|
char *base = Z_THREAD_STACK_BUFFER(stack);
|
|
char *top = base + sz;
|
|
|
|
/* Align downward. The API as specified requires a runtime check. */
|
|
top = (char *)(((unsigned int)top) & ~3);
|
|
|
|
z_new_thread_init(thread, base, sz, prio, opts);
|
|
|
|
thread->switch_handle = xtensa_init_stack((void *)top, entry,
|
|
p1, p2, p3);
|
|
}
|
|
#endif
|
|
|
|
#ifdef CONFIG_XTENSA_ASM2
|
|
void z_irq_spurious(void *arg)
|
|
{
|
|
int irqs, ie;
|
|
|
|
ARG_UNUSED(arg);
|
|
|
|
__asm__ volatile("rsr.interrupt %0" : "=r"(irqs));
|
|
__asm__ volatile("rsr.intenable %0" : "=r"(ie));
|
|
printk(" ** Spurious INTERRUPT(s) %p, INTENABLE = %p\n",
|
|
(void *)irqs, (void *)ie);
|
|
z_NanoFatalErrorHandler(_NANO_ERR_RESERVED_IRQ, &_default_esf);
|
|
}
|
|
#endif
|
|
|
|
static void dump_stack(int *stack)
|
|
{
|
|
int *bsa = *(int **)stack;
|
|
|
|
printk(" ** A0 %p SP %p A2 %p A3 %p\n",
|
|
(void *)bsa[BSA_A0_OFF/4], ((char *)bsa) + BASE_SAVE_AREA_SIZE,
|
|
(void *)bsa[BSA_A2_OFF/4], (void *)bsa[BSA_A3_OFF/4]);
|
|
|
|
if (bsa - stack > 4) {
|
|
printk(" ** A4 %p A5 %p A6 %p A7 %p\n",
|
|
(void *)bsa[-4], (void *)bsa[-3],
|
|
(void *)bsa[-2], (void *)bsa[-1]);
|
|
}
|
|
|
|
if (bsa - stack > 8) {
|
|
printk(" ** A8 %p A9 %p A10 %p A11 %p\n",
|
|
(void *)bsa[-8], (void *)bsa[-7],
|
|
(void *)bsa[-6], (void *)bsa[-5]);
|
|
}
|
|
|
|
if (bsa - stack > 12) {
|
|
printk(" ** A12 %p A13 %p A14 %p A15 %p\n",
|
|
(void *)bsa[-12], (void *)bsa[-11],
|
|
(void *)bsa[-10], (void *)bsa[-9]);
|
|
}
|
|
|
|
#if XCHAL_HAVE_LOOPS
|
|
printk(" ** LBEG %p LEND %p LCOUNT %p\n",
|
|
(void *)bsa[BSA_LBEG_OFF/4],
|
|
(void *)bsa[BSA_LEND_OFF/4],
|
|
(void *)bsa[BSA_LCOUNT_OFF/4]);
|
|
|
|
#endif
|
|
|
|
printk(" ** SAR %p\n", (void *)bsa[BSA_SAR_OFF/4]);
|
|
}
|
|
|
|
/* The wrapper code lives here instead of in the python script that
|
|
* generates _xtensa_handle_one_int*(). Seems cleaner, still kind of
|
|
* ugly.
|
|
*/
|
|
#define DEF_INT_C_HANDLER(l) \
|
|
void *xtensa_int##l##_c(void *interrupted_stack) \
|
|
{ \
|
|
u32_t irqs, intenable, m; \
|
|
__asm__ volatile("rsr.interrupt %0" : "=r"(irqs)); \
|
|
__asm__ volatile("rsr.intenable %0" : "=r"(intenable)); \
|
|
irqs &= intenable; \
|
|
while ((m = _xtensa_handle_one_int##l(irqs))) { \
|
|
irqs ^= m; \
|
|
__asm__ volatile("wsr.intclear %0" : : "r"(m)); \
|
|
} \
|
|
return z_get_next_switch_handle(interrupted_stack); \
|
|
}
|
|
|
|
DEF_INT_C_HANDLER(2)
|
|
DEF_INT_C_HANDLER(3)
|
|
DEF_INT_C_HANDLER(4)
|
|
DEF_INT_C_HANDLER(5)
|
|
DEF_INT_C_HANDLER(6)
|
|
DEF_INT_C_HANDLER(7)
|
|
|
|
static inline DEF_INT_C_HANDLER(1)
|
|
|
|
/* C handler for level 1 exceptions/interrupts. Hooked from the
|
|
* DEF_EXCINT 1 vector declaration in assembly code. This one looks
|
|
* different because exceptions and interrupts land at the same
|
|
* vector; other interrupt levels have their own vectors.
|
|
*/
|
|
void *xtensa_excint1_c(int *interrupted_stack)
|
|
{
|
|
int cause, vaddr, *bsa = *(int **)interrupted_stack;
|
|
|
|
__asm__ volatile("rsr.exccause %0" : "=r"(cause));
|
|
|
|
if (cause == EXCCAUSE_LEVEL1_INTERRUPT) {
|
|
|
|
return xtensa_int1_c(interrupted_stack);
|
|
|
|
} else if (cause == EXCCAUSE_SYSCALL) {
|
|
|
|
/* Just report it to the console for now */
|
|
printk(" ** SYSCALL PS %p PC %p\n",
|
|
(void *)bsa[BSA_PS_OFF/4], (void *)bsa[BSA_PC_OFF/4]);
|
|
dump_stack(interrupted_stack);
|
|
|
|
/* Xtensa exceptions don't automatically advance PC,
|
|
* have to skip the SYSCALL instruction manually or
|
|
* else it will just loop forever
|
|
*/
|
|
bsa[BSA_PC_OFF/4] += 3;
|
|
|
|
} else {
|
|
__asm__ volatile("rsr.excvaddr %0" : "=r"(vaddr));
|
|
|
|
/* Wouldn't hurt to translate EXCCAUSE to a string for
|
|
* the user...
|
|
*/
|
|
printk(" ** FATAL EXCEPTION\n");
|
|
printk(" ** CPU %d EXCCAUSE %d PS %p PC %p VADDR %p\n",
|
|
z_arch_curr_cpu()->id, cause, (void *)bsa[BSA_PS_OFF/4],
|
|
(void *)bsa[BSA_PC_OFF/4], (void *)vaddr);
|
|
|
|
dump_stack(interrupted_stack);
|
|
|
|
/* FIXME: legacy xtensa port reported "HW" exception
|
|
* for all unhandled exceptions, which seems incorrect
|
|
* as these are software errors. Should clean this
|
|
* up.
|
|
*/
|
|
z_NanoFatalErrorHandler(_NANO_ERR_HW_EXCEPTION, &_default_esf);
|
|
}
|
|
|
|
return z_get_next_switch_handle(interrupted_stack);
|
|
}
|
|
|