SMP needs a new context switch primitive (to disentangle _swap() from the scheduler) and new interrupt entry behavior (to be able to take a global spinlock on behalf of legacy drivers). The existing code is very obtuse, and working with it led me down a long path of "this would be so much better if..." So this is a new context and entry framework, intended to replace the code that exists now, at least on SMP platforms. New features: * The new context switch primitive is xtensa_switch(), which takes a "new" context handle as an argument instead of getting it from the scheduler, returns an "old" context handle through a pointer (e.g. to save it to the old thread context), and restores the lock state(PS register) exactly as it is at entry instead of taking it as an argument. * The register spill code understands wrap-around register windows and can avoid spilling A4-A15 registers when they are unused by the interrupted function, saving as much as 48 bytes of stack space on the interrupted stacks. * The "spill register windows" routine is entirely different, using a different mechanism, and is MUCH FASTER (to the tune of almost 200 cycles). See notes in comments. * Even better, interrupt entry can be done via a clever "cross stack call" I worked up, meaning that the interrupted thread's registers do not need to be spilled at all until they are naturally pushed out by the interrupt handler or until we return from the interrupt into a different thread. This is a big efficiency win for tiny interrupts (e.g. timers), and a big latency win for all interrupts. * Interrupt entry is 100% symmetric with respect to medium/high interrupts, avoiding the problems seen with hooking high priority interrupts with the current code (e.g. ESP-32's watchdog driver). * Much smaller code size. No cut and paste assembly. No use of HAL calls. * Assumes "XEA2" interrupt architecture, the register window extension (i.e. no CALL0 ABI), and the "high priority interrupts" extension. Does not support the legacy processor variants for which we have no targets. The old code has some stuff in there to support this, but it seems bitrotten, untestable, and I'm all but certain it doesn't work. Note that this simply adds the primitives to the existing tree in a form where they can be unit tested. It does not replace the existing interrupt/exception handling or _Swap() implementation. Signed-off-by: Andy Ross <andrew.j.ross@intel.com>
50 lines
1.4 KiB
C
50 lines
1.4 KiB
C
/*
|
|
* Copyright (c) 2017, Intel Corporation
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
#include <string.h>
|
|
#include <xtensa-asm2.h>
|
|
#include <kernel.h>
|
|
#include <kernel_structs.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);
|
|
|
|
memset(bsa, 0, bsasz);
|
|
|
|
bsa[BSA_PC_OFF/4] = entry;
|
|
bsa[BSA_PS_OFF/4] = (void *)(PS_WOE | PS_UM | PS_CALLINC(1));
|
|
|
|
/* Arguments. Remember these start at A6, which will be
|
|
* rotated into A2 by the ENTRY instruction that begins the
|
|
* entry function. And A4-A7 and A8-A11 are optional quads
|
|
* that live below the BSA!
|
|
*/
|
|
bsa[-1] = arg2; /* a7 */
|
|
bsa[-2] = arg1; /* a6 */
|
|
bsa[-3] = 0; /* a5 */
|
|
bsa[-4] = 0; /* a4 */
|
|
|
|
bsa[-5] = 0; /* a11 */
|
|
bsa[-6] = 0; /* a10 */
|
|
bsa[-7] = 0; /* a9 */
|
|
bsa[-8] = arg3; /* a8 */
|
|
|
|
/* Finally push the BSA pointer and return the stack pointer
|
|
* as the handle
|
|
*/
|
|
bsa[-9] = bsa;
|
|
return &bsa[-9];
|
|
}
|