ARMv7-A: Modify up_fullcontextrestore() for CONFIG_BUILD_KERNEL. It changed CPSR while in kernel. That will crash is the new CPSR is user mode while executing in kernel space. Fixed by adding a SYS_context_restore system call. There is an alternative, simpler modification to up_fullcontextrestore() that could have been done: It might have been possible to use the SPSR instead of the CPRSR and then do an exception return from up_fullcontextrestore(). That would be more efficient, but I never tried it.

This commit is contained in:
Gregory Nutt
2014-09-12 08:04:27 -06:00
parent 5a96bc37e4
commit f8170550e1
6 changed files with 79 additions and 21 deletions
@@ -37,8 +37,10 @@
* Included Files * Included Files
****************************************************************************/ ****************************************************************************/
#include <nuttx/config.h>
#include <nuttx/irq.h> #include <nuttx/irq.h>
#include "up_internal.h" #include "up_internal.h"
#include "svcall.h"
.file "arm_fullcontextrestore.S" .file "arm_fullcontextrestore.S"
@@ -112,6 +114,28 @@ up_fullcontextrestore:
vmsr fpscr, r2 /* Restore the FPCSR */ vmsr fpscr, r2 /* Restore the FPCSR */
#endif #endif
#ifdef CONFIG_BUILD_KERNEL
/* For the kernel build, we need to be able to transition gracefully
* between kernel- and user-mode tasks. We have to do that with a system
* call; the system call will execute in kernel mode and but can return
* to either user or kernel mode.
*/
/* Perform the System call with R0=SYS_context_restore, R1=restoreregs */
mov r1, r0 /* R1: restoreregs */
mov r0, #SYS_context_restore /* R0: SYS_context_restore syscall */
svc #0x900001 /* Perform the system call */
/* This call should not return */
bx lr /* Unnecessary ... will not return */
#else
/* For a flat build, we can do all of this here... Just think of this as
* a longjmp() all on steriods.
*/
/* Recover all registers except for r0, r1, R15, and CPSR */ /* Recover all registers except for r0, r1, R15, and CPSR */
add r1, r0, #(4*REG_R2) /* Offset to REG_R2 storage */ add r1, r0, #(4*REG_R2) /* Offset to REG_R2 storage */
@@ -148,4 +172,7 @@ up_fullcontextrestore:
*/ */
ldr pc, [sp], #4 ldr pc, [sp], #4
#endif
.size up_fullcontextrestore, . - up_fullcontextrestore .size up_fullcontextrestore, . - up_fullcontextrestore
+32 -8
View File
@@ -58,16 +58,16 @@
****************************************************************************/ ****************************************************************************/
/* Debug ********************************************************************/ /* Debug ********************************************************************/
/* Output debug info if stack dump is selected -- even if #if defined(CONFIG_DEBUG_SYSCALL)
* debug is not selected.
*/
#if defined(CONFIG_DEBUG_SYSCALL) || defined(CONFIG_DEBUG_SVCALL)
# define svcdbg(format, ...) lldbg(format, ##__VA_ARGS__) # define svcdbg(format, ...) lldbg(format, ##__VA_ARGS__)
#else #else
# define svcdbg(x...) # define svcdbg(x...)
#endif #endif
/* Output debug info if stack dump is selected -- even if debug is not
* selected.
*/
#ifdef CONFIG_ARCH_STACKDUMP #ifdef CONFIG_ARCH_STACKDUMP
# undef lldbg # undef lldbg
# define lldbg lowsyslog # define lldbg lowsyslog
@@ -229,6 +229,30 @@ uint32_t *arm_syscall(uint32_t *regs)
} }
break; break;
/* R0=SYS_context_restore: Restore task context
*
* void up_fullcontextrestore(uint32_t *restoreregs) noreturn_function;
*
* At this point, the following values are saved in context:
*
* R0 = SYS_context_restore
* R1 = restoreregs
*/
#ifdef CONFIG_BUILD_KERNEL
case SYS_context_restore:
{
/* Replace 'regs' with the pointer to the register set in
* regs[REG_R1]. On return from the system call, that register
* set will determine the restored context.
*/
regs = (uint32_t *)regs[REG_R1];
DEBUGASSERT(regs);
}
break;
#endif
/* R0=SYS_task_start: This a user task start /* R0=SYS_task_start: This a user task start
* *
* void up_task_start(main_t taskentry, int argc, FAR char *argv[]) noreturn_function; * void up_task_start(main_t taskentry, int argc, FAR char *argv[]) noreturn_function;
@@ -427,9 +451,9 @@ uint32_t *arm_syscall(uint32_t *regs)
svcdbg("CPSR: %08x\n", regs[REG_CPSR]); svcdbg("CPSR: %08x\n", regs[REG_CPSR]);
#endif #endif
/* Return the last value of curent_regs. This supports context switchs /* Return the last value of curent_regs. This supports context switches
* on return from the exception. That capability is not used here, * on return from the exception. That capability is only used with the
* however. * SYS_context_switch system call.
*/ */
return regs; return regs;
+1 -1
View File
@@ -101,7 +101,7 @@ static void sig_trampoline(void)
" blx ip\n" /* Call the signal handler */ " blx ip\n" /* Call the signal handler */
" pop {r2}\n" /* Recover LR in R2 */ " pop {r2}\n" /* Recover LR in R2 */
" mov lr, r2\n" /* Restore LR */ " mov lr, r2\n" /* Restore LR */
" mov r0, #4\n" /* SYS_signal_handler_return */ " mov r0, #5\n" /* SYS_signal_handler_return */
" svc #0x900001\n" /* Return from the signal handler */ " svc #0x900001\n" /* Return from the signal handler */
); );
} }
+17 -10
View File
@@ -60,9 +60,9 @@
#ifdef CONFIG_BUILD_KERNEL #ifdef CONFIG_BUILD_KERNEL
# ifndef CONFIG_SYS_RESERVED # ifndef CONFIG_SYS_RESERVED
# error "CONFIG_SYS_RESERVED must be defined to have the value 5" # error "CONFIG_SYS_RESERVED must be defined to have the value 6"
# elif CONFIG_SYS_RESERVED != 5 # elif CONFIG_SYS_RESERVED != 6
# error "CONFIG_SYS_RESERVED must have the value 5" # error "CONFIG_SYS_RESERVED must have the value 6"
# endif # endif
#else #else
# ifndef CONFIG_SYS_RESERVED # ifndef CONFIG_SYS_RESERVED
@@ -83,35 +83,42 @@
#ifdef CONFIG_BUILD_KERNEL #ifdef CONFIG_BUILD_KERNEL
/* SYS call 1: /* SYS call 1:
*
* void up_fullcontextrestore(uint32_t *restoreregs) noreturn_function;
*/
#define SYS_context_restore (1)
/* SYS call 2:
* *
* void up_task_start(main_t taskentry, int argc, FAR char *argv[]) * void up_task_start(main_t taskentry, int argc, FAR char *argv[])
* noreturn_function; * noreturn_function;
*/ */
#define SYS_task_start (1) #define SYS_task_start (2)
/* SYS call 2: /* SYS call 3:
* *
* void up_pthread_start(pthread_startroutine_t entrypt, pthread_addr_t arg) * void up_pthread_start(pthread_startroutine_t entrypt, pthread_addr_t arg)
* noreturn_function * noreturn_function
*/ */
#define SYS_pthread_start (2) #define SYS_pthread_start (3)
/* SYS call 3: /* SYS call 4:
* *
* void signal_handler(_sa_sigaction_t sighand, int signo, FAR siginfo_t *info, * void signal_handler(_sa_sigaction_t sighand, int signo, FAR siginfo_t *info,
* FAR void *ucontext); * FAR void *ucontext);
*/ */
#define SYS_signal_handler (3) #define SYS_signal_handler (4)
/* SYS call 4: /* SYS call 5:
* *
* void signal_handler_return(void); * void signal_handler_return(void);
*/ */
#define SYS_signal_handler_return (4) #define SYS_signal_handler_return (5)
#endif /* CONFIG_BUILD_KERNEL */ #endif /* CONFIG_BUILD_KERNEL */
+1 -1
View File
@@ -455,7 +455,7 @@ CONFIG_USERMAIN_STACKSIZE=2048
CONFIG_PTHREAD_STACK_MIN=256 CONFIG_PTHREAD_STACK_MIN=256
CONFIG_PTHREAD_STACK_DEFAULT=2048 CONFIG_PTHREAD_STACK_DEFAULT=2048
CONFIG_LIB_SYSCALL=y CONFIG_LIB_SYSCALL=y
CONFIG_SYS_RESERVED=5 CONFIG_SYS_RESERVED=6
CONFIG_SYS_NNEST=2 CONFIG_SYS_NNEST=2
# #
+1 -1
View File
@@ -438,7 +438,7 @@ CONFIG_USERMAIN_STACKSIZE=2048
CONFIG_PTHREAD_STACK_MIN=256 CONFIG_PTHREAD_STACK_MIN=256
CONFIG_PTHREAD_STACK_DEFAULT=2048 CONFIG_PTHREAD_STACK_DEFAULT=2048
CONFIG_LIB_SYSCALL=y CONFIG_LIB_SYSCALL=y
CONFIG_SYS_RESERVED=5 CONFIG_SYS_RESERVED=6
CONFIG_SYS_NNEST=2 CONFIG_SYS_NNEST=2
# #