armv6/7/8m: use pendsv to handle context switch

This PR support Nested interrupt in armv6/7/8m:

There are two types of nested interrupt model:

Zero latency nested interrupt
Interrupt           Priority            Note
Data abort          Highest
SVC                 0x50
High irq1           0x60             ISR can't access system API
irq_save()          0x70
High irq2           0x80             ISR can't access system API
normal irq3         0xB0
We have already support this mode before this PR

Nested interrupt which interrupt level lower than up_irq_save()
Interrupt           Priority            Note
Data abort          Highest
SVC                 0x70
irq_save()          0x80
High irq1           0x90              ISR can access system API
High irq2           0xA0              ISR can access system API
normal irq3         0xB0
Now, this PR can support this mode

Signed-off-by: ligd <liguiding1@xiaomi.com>
This commit is contained in:
ligd 2024-07-18 21:22:27 +08:00 committed by Xiang Xiao
parent 064415a765
commit 8b8a2610ab
9 changed files with 126 additions and 148 deletions

View File

@ -35,11 +35,25 @@
#include "arm_internal.h"
#include "exc_return.h"
#include "nvic.h"
/****************************************************************************
* Public Functions
****************************************************************************/
void exception_direct(void)
{
int irq = getipsr();
arm_ack_irq(irq);
irq_dispatch(irq, NULL);
if (g_running_tasks[this_cpu()] != this_task())
{
up_trigger_irq(NVIC_IRQ_PENDSV, 0);
}
}
uint32_t *arm_doirq(int irq, uint32_t *regs)
{
struct tcb_s *tcb = this_task();
@ -49,19 +63,20 @@ uint32_t *arm_doirq(int irq, uint32_t *regs)
PANIC();
#else
if (regs[REG_EXC_RETURN] & EXC_RETURN_THREAD_MODE)
{
tcb->xcp.regs = regs;
up_set_current_regs(regs);
}
/* Acknowledge the interrupt */
arm_ack_irq(irq);
/* Deliver the IRQ */
irq_dispatch(irq, regs);
if (irq == NVIC_IRQ_PENDSV)
{
up_irq_save();
g_running_tasks[this_cpu()]->xcp.regs = regs;
}
else
{
tcb->xcp.regs = regs;
irq_dispatch(irq, regs);
}
/* If a context switch occurred while processing the interrupt then
* current_regs may have change value. If we return any value different
@ -69,30 +84,20 @@ uint32_t *arm_doirq(int irq, uint32_t *regs)
* switch occurred during interrupt processing.
*/
if (regs[REG_EXC_RETURN] & EXC_RETURN_THREAD_MODE)
{
tcb = this_task();
tcb = this_task();
if (regs != tcb->xcp.regs)
{
/* Update scheduler parameters */
/* Update scheduler parameters */
nxsched_suspend_scheduler(g_running_tasks[this_cpu()]);
nxsched_resume_scheduler(tcb);
nxsched_suspend_scheduler(g_running_tasks[this_cpu()]);
nxsched_resume_scheduler(tcb);
/* Record the new "running" task when context switch occurred.
* g_running_tasks[] is only used by assertion logic for reporting
* crashes.
*/
/* Record the new "running" task when context switch occurred.
* g_running_tasks[] is only used by assertion logic for reporting
* crashes.
*/
g_running_tasks[this_cpu()] = tcb;
regs = tcb->xcp.regs;
}
/* Update the current_regs to NULL. */
up_set_current_regs(NULL);
}
g_running_tasks[this_cpu()] = tcb;
regs = tcb->xcp.regs;
#endif
board_autoled_off(LED_INIRQ);

View File

@ -121,7 +121,6 @@ int arm_svcall(int irq, void *context, void *arg)
uint32_t *regs = (uint32_t *)context;
uint32_t cmd;
DEBUGASSERT(regs && regs == up_current_regs());
cmd = regs[REG_R0];
/* The SVCall software interrupt is called with R0 = system call command

View File

@ -45,6 +45,8 @@
#include "chip.h"
#include "arm_internal.h"
#include "ram_vectors.h"
#include "nvic.h"
/****************************************************************************
* Pre-processor Definitions
@ -81,6 +83,7 @@ static void start(void)
/* Common exception entrypoint */
extern void exception_common(void);
extern void exception_direct(void);
/****************************************************************************
* Public data
@ -92,7 +95,7 @@ extern void exception_common(void);
* As all exceptions (interrupts) are routed via exception_common, we just
* need to fill this array with pointers to it.
*
* Note that the [ ... ] designated initialiser is a GCC extension.
* Note that the [ ... ] designated initializer is a GCC extension.
*/
const void * const _vectors[] locate_data(".vectors") =
@ -107,5 +110,7 @@ const void * const _vectors[] locate_data(".vectors") =
/* Vectors 2 - n point directly at the generic handler */
[2 ... (15 + ARMV6M_PERIPHERAL_INTERRUPTS)] = exception_common
[2 ... NVIC_IRQ_PENDSV] = &exception_common,
[(NVIC_IRQ_PENDSV + 1) ... (15 + ARMV6M_PERIPHERAL_INTERRUPTS)]
= &exception_direct
};

View File

@ -35,11 +35,25 @@
#include "arm_internal.h"
#include "exc_return.h"
#include "nvic.h"
/****************************************************************************
* Public Functions
****************************************************************************/
void exception_direct(void)
{
int irq = getipsr();
arm_ack_irq(irq);
irq_dispatch(irq, NULL);
if (g_running_tasks[this_cpu()] != this_task())
{
up_trigger_irq(NVIC_IRQ_PENDSV, 0);
}
}
uint32_t *arm_doirq(int irq, uint32_t *regs)
{
struct tcb_s *tcb = this_task();
@ -49,19 +63,20 @@ uint32_t *arm_doirq(int irq, uint32_t *regs)
PANIC();
#else
if (regs[REG_EXC_RETURN] & EXC_RETURN_THREAD_MODE)
{
tcb->xcp.regs = regs;
up_set_current_regs(regs);
}
/* Acknowledge the interrupt */
arm_ack_irq(irq);
/* Deliver the IRQ */
irq_dispatch(irq, regs);
if (irq == NVIC_IRQ_PENDSV)
{
up_irq_save();
g_running_tasks[this_cpu()]->xcp.regs = regs;
}
else
{
tcb->xcp.regs = regs;
irq_dispatch(irq, regs);
}
/* If a context switch occurred while processing the interrupt then
* current_regs may have change value. If we return any value different
@ -69,30 +84,20 @@ uint32_t *arm_doirq(int irq, uint32_t *regs)
* switch occurred during interrupt processing.
*/
if (regs[REG_EXC_RETURN] & EXC_RETURN_THREAD_MODE)
{
tcb = this_task();
tcb = this_task();
if (regs != tcb->xcp.regs)
{
/* Update scheduler parameters */
/* Update scheduler parameters */
nxsched_suspend_scheduler(g_running_tasks[this_cpu()]);
nxsched_resume_scheduler(tcb);
nxsched_suspend_scheduler(g_running_tasks[this_cpu()]);
nxsched_resume_scheduler(tcb);
/* Record the new "running" task when context switch occurred.
* g_running_tasks[] is only used by assertion logic for reporting
* crashes.
*/
/* Record the new "running" task when context switch occurred.
* g_running_tasks[] is only used by assertion logic for reporting
* crashes.
*/
g_running_tasks[this_cpu()] = tcb;
regs = tcb->xcp.regs;
}
/* Update the current_regs to NULL. */
up_set_current_regs(NULL);
}
g_running_tasks[this_cpu()] = tcb;
regs = tcb->xcp.regs;
#endif
board_autoled_off(LED_INIRQ);

View File

@ -129,7 +129,6 @@ int arm_svcall(int irq, void *context, void *arg)
uint32_t *regs = (uint32_t *)context;
uint32_t cmd;
DEBUGASSERT(regs && regs == up_current_regs());
cmd = regs[REG_R0];
/* The SVCall software interrupt is called with R0 = system call command
@ -299,8 +298,9 @@ int arm_svcall(int irq, void *context, void *arg)
* At this point, the following values are saved in context:
*
* R0 = SYS_pthread_start
* R1 = entrypt
* R2 = arg
* R1 = startup (trampoline)
* R2 = entrypt
* R3 = arg
*/
#if !defined(CONFIG_BUILD_FLAT) && !defined(CONFIG_DISABLE_PTHREAD)

View File

@ -41,6 +41,7 @@
#include "chip.h"
#include "arm_internal.h"
#include "ram_vectors.h"
#include "nvic.h"
/****************************************************************************
* Pre-processor Definitions
@ -71,6 +72,7 @@ static void start(void)
/* Common exception entrypoint */
extern void exception_common(void);
extern void exception_direct(void);
/****************************************************************************
* Public data
@ -98,5 +100,7 @@ const void * const _vectors[] locate_data(".vectors")
/* Vectors 2 - n point directly at the generic handler */
[2 ... (15 + ARMV7M_PERIPHERAL_INTERRUPTS)] = exception_common
[2 ... NVIC_IRQ_PENDSV] = &exception_common,
[(NVIC_IRQ_PENDSV + 1) ... (15 + ARMV7M_PERIPHERAL_INTERRUPTS)]
= &exception_direct
};

View File

@ -35,60 +35,25 @@
#include "arm_internal.h"
#include "exc_return.h"
/****************************************************************************
* Inline Functions
****************************************************************************/
/****************************************************************************
* Name: arm_from_thread
*
* Description:
* If not defined CONFIG_ARCH_HAVE_TRUSTZONE
* Return true if interrupt return to thread mode, false otherwise.
*
* If defined CONFIG_ARCH_HAVE_TRUSTZONE
* Return true if interrupt return to thread mode, or if it is the first
* interrupt from TEE to REE, or REE to TEE, false otherwise.
*
* Interrupt nesting between TEE and REE can be determined based
* on the S and ES bits of EXC_RETURN
* If TEE interrupts REE, then EXC_RETURN.S=0, EXC_RETURN.ES=1;
* Conversely, EXC_RETURN.S=1, EXC_RETURN.ES=0.
*
* But only one level nesting between TEE and REE is supported, and
* recursive nesting between TEE and REE is not supported.
*
****************************************************************************/
static inline bool arm_from_thread(uint32_t excret)
{
if (excret & EXC_RETURN_THREAD_MODE)
{
return true;
}
#if defined(CONFIG_ARCH_TRUSTZONE_SECURE)
if (!(excret & EXC_RETURN_SECURE_STACK) &&
(excret & EXC_RETURN_EXC_SECURE))
{
return true;
}
if (!(excret & EXC_RETURN_EXC_SECURE) &&
(excret & EXC_RETURN_SECURE_STACK))
{
return true;
}
#endif
return false;
}
#include "nvic.h"
/****************************************************************************
* Public Functions
****************************************************************************/
void exception_direct(void)
{
int irq = getipsr();
arm_ack_irq(irq);
irq_dispatch(irq, NULL);
if (g_running_tasks[this_cpu()] != this_task())
{
up_trigger_irq(NVIC_IRQ_PENDSV, 0);
}
}
uint32_t *arm_doirq(int irq, uint32_t *regs)
{
struct tcb_s *tcb = this_task();
@ -98,19 +63,20 @@ uint32_t *arm_doirq(int irq, uint32_t *regs)
PANIC();
#else
if (arm_from_thread(regs[REG_EXC_RETURN]))
{
tcb->xcp.regs = regs;
up_set_current_regs(regs);
}
/* Acknowledge the interrupt */
arm_ack_irq(irq);
/* Deliver the IRQ */
irq_dispatch(irq, regs);
if (irq == NVIC_IRQ_PENDSV)
{
up_irq_save();
g_running_tasks[this_cpu()]->xcp.regs = regs;
}
else
{
tcb->xcp.regs = regs;
irq_dispatch(irq, regs);
}
/* If a context switch occurred while processing the interrupt then
* current_regs may have change value. If we return any value different
@ -118,30 +84,20 @@ uint32_t *arm_doirq(int irq, uint32_t *regs)
* switch occurred during interrupt processing.
*/
if (arm_from_thread(regs[REG_EXC_RETURN]))
{
tcb = this_task();
tcb = this_task();
if (regs != tcb->xcp.regs)
{
/* Update scheduler parameters */
/* Update scheduler parameters */
nxsched_suspend_scheduler(g_running_tasks[this_cpu()]);
nxsched_resume_scheduler(tcb);
nxsched_suspend_scheduler(g_running_tasks[this_cpu()]);
nxsched_resume_scheduler(tcb);
/* Record the new "running" task when context switch occurred.
* g_running_tasks[] is only used by assertion logic for reporting
* crashes.
*/
/* Record the new "running" task when context switch occurred.
* g_running_tasks[] is only used by assertion logic for reporting
* crashes.
*/
g_running_tasks[this_cpu()] = tcb;
regs = tcb->xcp.regs;
}
/* Update the current_regs to NULL. */
up_set_current_regs(NULL);
}
g_running_tasks[this_cpu()] = tcb;
regs = tcb->xcp.regs;
#endif
board_autoled_off(LED_INIRQ);

View File

@ -24,6 +24,7 @@
#include <nuttx/config.h>
#include <inttypes.h>
#include <stdint.h>
#include <string.h>
#include <assert.h>
@ -128,7 +129,6 @@ int arm_svcall(int irq, void *context, void *arg)
uint32_t *regs = (uint32_t *)context;
uint32_t cmd;
DEBUGASSERT(regs && regs == up_current_regs());
cmd = regs[REG_R0];
/* The SVCall software interrupt is called with R0 = system call command
@ -398,7 +398,6 @@ int arm_svcall(int irq, void *context, void *arg)
/* Return privileged mode */
regs[REG_CONTROL] = getcontrol() & ~CONTROL_NPRIV;
rtcb->xcp.sigreturn = 0;
}
break;
@ -447,7 +446,7 @@ int arm_svcall(int irq, void *context, void *arg)
rtcb->flags |= TCB_FLAG_SYSCALL;
#else
svcerr("ERROR: Bad SYS call: %d\n", (int)regs[REG_R0]);
svcerr("ERROR: Bad SYS call: %" PRId32 "\n", regs[REG_R0]);
#endif
}
break;

View File

@ -40,6 +40,8 @@
#include "chip.h"
#include "arm_internal.h"
#include "ram_vectors.h"
#include "nvic.h"
/****************************************************************************
* Pre-processor Definitions
@ -74,12 +76,13 @@ static void start(void)
/* Common exception entrypoint */
extern void exception_common(void);
extern void exception_direct(void);
/****************************************************************************
* Public data
****************************************************************************/
/* The v7m vector table consists of an array of function pointers, with the
/* The v8m vector table consists of an array of function pointers, with the
* first slot (vector zero) used to hold the initial stack pointer.
*
* As all exceptions (interrupts) are routed via exception_common, we just
@ -100,5 +103,7 @@ const void * const _vectors[] locate_data(".vectors") =
/* Vectors 2 - n point directly at the generic handler */
[2 ... (15 + ARMV8M_PERIPHERAL_INTERRUPTS)] = &exception_common
[2 ... NVIC_IRQ_PENDSV] = &exception_common,
[(NVIC_IRQ_PENDSV + 1) ... (15 + ARMV8M_PERIPHERAL_INTERRUPTS)]
= &exception_direct
};