arch/armv7-[a|r]: correct the handing of group env switch

This PR resolved 2 issues:
1. CURRENT_REGS is not set correctly on swint handling
2. group env is not changed properly

Signed-off-by: chao.an <anchao@xiaomi.com>
This commit is contained in:
chao.an 2022-04-19 12:33:27 +08:00 committed by Petro Karashchenko
parent 96fa8be5f5
commit b110c984b1
3 changed files with 103 additions and 16 deletions

View File

@ -32,6 +32,7 @@
#include <nuttx/arch.h>
#include "arm_internal.h"
#include "group/group.h"
/****************************************************************************
* Public Functions
@ -54,10 +55,22 @@ uint32_t *arm_syscall(uint32_t *regs)
{
uint32_t cmd;
DEBUGASSERT(regs);
/* Nested interrupts are not supported */
DEBUGASSERT(CURRENT_REGS == NULL);
/* Current regs non-zero indicates that we are processing an interrupt;
* CURRENT_REGS is also used to manage interrupt level context switches.
*/
CURRENT_REGS = regs;
/* The SYSCALL command is in R0 on entry. Parameters follow in R1..R7 */
cmd = regs[REG_R0];
/* Handle the SVCall according to the command in R0 */
switch (cmd)
{
/* R0=SYS_restore_context: Restore task context
@ -78,8 +91,8 @@ uint32_t *arm_syscall(uint32_t *regs)
* set will determine the restored context.
*/
regs = (uint32_t *)regs[REG_R1];
DEBUGASSERT(regs);
CURRENT_REGS = (uint32_t *)regs[REG_R1];
DEBUGASSERT(CURRENT_REGS);
}
break;
@ -104,7 +117,7 @@ uint32_t *arm_syscall(uint32_t *regs)
{
DEBUGASSERT(regs[REG_R1] != 0 && regs[REG_R2] != 0);
*(uint32_t **)regs[REG_R1] = regs;
regs = (uint32_t *)regs[REG_R2];
CURRENT_REGS = (uint32_t *)regs[REG_R2];
}
break;
@ -112,12 +125,37 @@ uint32_t *arm_syscall(uint32_t *regs)
{
svcerr("ERROR: Bad SYS call: 0x%" PRIx32 "\n", regs[REG_R0]);
_alert("Syscall from 0x%" PRIx32 "\n", regs[REG_PC]);
CURRENT_REGS = regs;
PANIC();
}
break;
}
#ifdef CONFIG_ARCH_ADDRENV
/* Check for a context switch. If a context switch occurred, then
* CURRENT_REGS will have a different value than it did on entry. If an
* interrupt level context switch has occurred, then establish the correct
* address environment before returning from the interrupt.
*/
if (regs != CURRENT_REGS)
{
/* Make sure that the address environment for the previously
* running task is closed down gracefully (data caches dump,
* MMU flushed) and set up the address environment for the new
* thread at the head of the ready-to-run list.
*/
group_addrenv(NULL);
}
#endif
/* Set CURRENT_REGS to NULL to indicate that we are no longer in an
* interrupt handler.
*/
regs = (uint32_t *)CURRENT_REGS;
CURRENT_REGS = NULL;
/* Return the last value of curent_regs. This supports context switches
* on return from the exception. That capability is only used with the
* SYS_context_switch system call.

View File

@ -35,10 +35,11 @@
#include <nuttx/sched.h>
#include <nuttx/addrenv.h>
#include "signal/signal.h"
#include "arm.h"
#include "addrenv.h"
#include "arm.h"
#include "arm_internal.h"
#include "group/group.h"
#include "signal/signal.h"
/****************************************************************************
* Private Functions
@ -165,7 +166,13 @@ uint32_t *arm_syscall(uint32_t *regs)
/* Nested interrupts are not supported */
DEBUGASSERT(regs);
DEBUGASSERT(CURRENT_REGS == NULL);
/* Current regs non-zero indicates that we are processing an interrupt;
* CURRENT_REGS is also used to manage interrupt level context switches.
*/
CURRENT_REGS = regs;
/* The SYSCALL command is in R0 on entry. Parameters follow in R1..R7 */
@ -262,8 +269,8 @@ uint32_t *arm_syscall(uint32_t *regs)
* set will determine the restored context.
*/
regs = (uint32_t *)regs[REG_R1];
DEBUGASSERT(regs);
CURRENT_REGS = (uint32_t *)regs[REG_R1];
DEBUGASSERT(CURRENT_REGS);
}
break;
@ -288,7 +295,7 @@ uint32_t *arm_syscall(uint32_t *regs)
{
DEBUGASSERT(regs[REG_R1] != 0 && regs[REG_R2] != 0);
*(uint32_t **)regs[REG_R1] = regs;
regs = (uint32_t *)regs[REG_R2];
CURRENT_REGS = (uint32_t *)regs[REG_R2];
}
break;
@ -529,10 +536,37 @@ uint32_t *arm_syscall(uint32_t *regs)
break;
}
#ifdef CONFIG_ARCH_ADDRENV
/* Check for a context switch. If a context switch occurred, then
* CURRENT_REGS will have a different value than it did on entry. If an
* interrupt level context switch has occurred, then establish the correct
* address environment before returning from the interrupt.
*/
if (regs != CURRENT_REGS)
{
/* Make sure that the address environment for the previously
* running task is closed down gracefully (data caches dump,
* MMU flushed) and set up the address environment for the new
* thread at the head of the ready-to-run list.
*/
group_addrenv(NULL);
}
#endif
regs = (uint32_t *)CURRENT_REGS;
/* Report what happened */
dump_syscall("Exit", cmd, regs);
/* Set CURRENT_REGS to NULL to indicate that we are no longer in an
* interrupt handler.
*/
CURRENT_REGS = NULL;
/* Return the last value of curent_regs. This supports context switches
* on return from the exception. That capability is only used with the
* SYS_context_switch system call.

View File

@ -33,9 +33,10 @@
#include <nuttx/arch.h>
#include <nuttx/sched.h>
#include "signal/signal.h"
#include "addrenv.h"
#include "arm.h"
#include "arm_internal.h"
#include "signal/signal.h"
/****************************************************************************
* Private Functions
@ -162,7 +163,13 @@ uint32_t *arm_syscall(uint32_t *regs)
/* Nested interrupts are not supported */
DEBUGASSERT(regs);
DEBUGASSERT(CURRENT_REGS == NULL);
/* Current regs non-zero indicates that we are processing an interrupt;
* CURRENT_REGS is also used to manage interrupt level context switches.
*/
CURRENT_REGS = regs;
/* The SYSCALL command is in R0 on entry. Parameters follow in R1..R7 */
@ -259,8 +266,8 @@ uint32_t *arm_syscall(uint32_t *regs)
* set will determine the restored context.
*/
regs = (uint32_t *)regs[REG_R1];
DEBUGASSERT(regs);
CURRENT_REGS = (uint32_t *)regs[REG_R1];
DEBUGASSERT(CURRENT_REGS);
}
break;
@ -285,7 +292,7 @@ uint32_t *arm_syscall(uint32_t *regs)
{
DEBUGASSERT(regs[REG_R1] != 0 && regs[REG_R2] != 0);
*(uint32_t **)regs[REG_R1] = regs;
regs = (uint32_t *)regs[REG_R2];
CURRENT_REGS = (uint32_t *)regs[REG_R2];
}
break;
@ -526,10 +533,18 @@ uint32_t *arm_syscall(uint32_t *regs)
break;
}
regs = (uint32_t *)CURRENT_REGS;
/* Report what happened */
dump_syscall("Exit", cmd, regs);
/* Set CURRENT_REGS to NULL to indicate that we are no longer in an
* interrupt handler.
*/
CURRENT_REGS = NULL;
/* Return the last value of curent_regs. This supports context switches
* on return from the exception. That capability is only used with the
* SYS_context_switch system call.