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 <nuttx/arch.h>
#include "arm_internal.h" #include "arm_internal.h"
#include "group/group.h"
/**************************************************************************** /****************************************************************************
* Public Functions * Public Functions
@ -54,10 +55,22 @@ uint32_t *arm_syscall(uint32_t *regs)
{ {
uint32_t cmd; 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]; cmd = regs[REG_R0];
/* Handle the SVCall according to the command in R0 */
switch (cmd) switch (cmd)
{ {
/* R0=SYS_restore_context: Restore task context /* R0=SYS_restore_context: Restore task context
@ -78,8 +91,8 @@ uint32_t *arm_syscall(uint32_t *regs)
* set will determine the restored context. * set will determine the restored context.
*/ */
regs = (uint32_t *)regs[REG_R1]; CURRENT_REGS = (uint32_t *)regs[REG_R1];
DEBUGASSERT(regs); DEBUGASSERT(CURRENT_REGS);
} }
break; break;
@ -104,7 +117,7 @@ uint32_t *arm_syscall(uint32_t *regs)
{ {
DEBUGASSERT(regs[REG_R1] != 0 && regs[REG_R2] != 0); DEBUGASSERT(regs[REG_R1] != 0 && regs[REG_R2] != 0);
*(uint32_t **)regs[REG_R1] = regs; *(uint32_t **)regs[REG_R1] = regs;
regs = (uint32_t *)regs[REG_R2]; CURRENT_REGS = (uint32_t *)regs[REG_R2];
} }
break; break;
@ -112,12 +125,37 @@ uint32_t *arm_syscall(uint32_t *regs)
{ {
svcerr("ERROR: Bad SYS call: 0x%" PRIx32 "\n", regs[REG_R0]); svcerr("ERROR: Bad SYS call: 0x%" PRIx32 "\n", regs[REG_R0]);
_alert("Syscall from 0x%" PRIx32 "\n", regs[REG_PC]); _alert("Syscall from 0x%" PRIx32 "\n", regs[REG_PC]);
CURRENT_REGS = regs;
PANIC(); PANIC();
} }
break; 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 /* Return the last value of curent_regs. This supports context switches
* on return from the exception. That capability is only used with the * on return from the exception. That capability is only used with the
* SYS_context_switch system call. * SYS_context_switch system call.

View File

@ -35,10 +35,11 @@
#include <nuttx/sched.h> #include <nuttx/sched.h>
#include <nuttx/addrenv.h> #include <nuttx/addrenv.h>
#include "signal/signal.h"
#include "arm.h"
#include "addrenv.h" #include "addrenv.h"
#include "arm.h"
#include "arm_internal.h" #include "arm_internal.h"
#include "group/group.h"
#include "signal/signal.h"
/**************************************************************************** /****************************************************************************
* Private Functions * Private Functions
@ -165,7 +166,13 @@ uint32_t *arm_syscall(uint32_t *regs)
/* Nested interrupts are not supported */ /* 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 */ /* 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. * set will determine the restored context.
*/ */
regs = (uint32_t *)regs[REG_R1]; CURRENT_REGS = (uint32_t *)regs[REG_R1];
DEBUGASSERT(regs); DEBUGASSERT(CURRENT_REGS);
} }
break; break;
@ -288,7 +295,7 @@ uint32_t *arm_syscall(uint32_t *regs)
{ {
DEBUGASSERT(regs[REG_R1] != 0 && regs[REG_R2] != 0); DEBUGASSERT(regs[REG_R1] != 0 && regs[REG_R2] != 0);
*(uint32_t **)regs[REG_R1] = regs; *(uint32_t **)regs[REG_R1] = regs;
regs = (uint32_t *)regs[REG_R2]; CURRENT_REGS = (uint32_t *)regs[REG_R2];
} }
break; break;
@ -529,10 +536,37 @@ uint32_t *arm_syscall(uint32_t *regs)
break; 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 */ /* Report what happened */
dump_syscall("Exit", cmd, regs); 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 /* Return the last value of curent_regs. This supports context switches
* on return from the exception. That capability is only used with the * on return from the exception. That capability is only used with the
* SYS_context_switch system call. * SYS_context_switch system call.

View File

@ -33,9 +33,10 @@
#include <nuttx/arch.h> #include <nuttx/arch.h>
#include <nuttx/sched.h> #include <nuttx/sched.h>
#include "signal/signal.h" #include "addrenv.h"
#include "arm.h" #include "arm.h"
#include "arm_internal.h" #include "arm_internal.h"
#include "signal/signal.h"
/**************************************************************************** /****************************************************************************
* Private Functions * Private Functions
@ -162,7 +163,13 @@ uint32_t *arm_syscall(uint32_t *regs)
/* Nested interrupts are not supported */ /* 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 */ /* 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. * set will determine the restored context.
*/ */
regs = (uint32_t *)regs[REG_R1]; CURRENT_REGS = (uint32_t *)regs[REG_R1];
DEBUGASSERT(regs); DEBUGASSERT(CURRENT_REGS);
} }
break; break;
@ -285,7 +292,7 @@ uint32_t *arm_syscall(uint32_t *regs)
{ {
DEBUGASSERT(regs[REG_R1] != 0 && regs[REG_R2] != 0); DEBUGASSERT(regs[REG_R1] != 0 && regs[REG_R2] != 0);
*(uint32_t **)regs[REG_R1] = regs; *(uint32_t **)regs[REG_R1] = regs;
regs = (uint32_t *)regs[REG_R2]; CURRENT_REGS = (uint32_t *)regs[REG_R2];
} }
break; break;
@ -526,10 +533,18 @@ uint32_t *arm_syscall(uint32_t *regs)
break; break;
} }
regs = (uint32_t *)CURRENT_REGS;
/* Report what happened */ /* Report what happened */
dump_syscall("Exit", cmd, regs); 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 /* Return the last value of curent_regs. This supports context switches
* on return from the exception. That capability is only used with the * on return from the exception. That capability is only used with the
* SYS_context_switch system call. * SYS_context_switch system call.