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

View File

@ -37,8 +37,10 @@
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <nuttx/irq.h>
#include "up_internal.h"
#include "svcall.h"
.file "arm_fullcontextrestore.S"
@ -112,6 +114,28 @@ up_fullcontextrestore:
vmsr fpscr, r2 /* Restore the FPCSR */
#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 */
add r1, r0, #(4*REG_R2) /* Offset to REG_R2 storage */
@ -148,4 +172,7 @@ up_fullcontextrestore:
*/
ldr pc, [sp], #4
#endif
.size up_fullcontextrestore, . - up_fullcontextrestore

View File

@ -58,16 +58,16 @@
****************************************************************************/
/* Debug ********************************************************************/
/* Output debug info if stack dump is selected -- even if
* debug is not selected.
*/
#if defined(CONFIG_DEBUG_SYSCALL) || defined(CONFIG_DEBUG_SVCALL)
#if defined(CONFIG_DEBUG_SYSCALL)
# define svcdbg(format, ...) lldbg(format, ##__VA_ARGS__)
#else
# define svcdbg(x...)
#endif
/* Output debug info if stack dump is selected -- even if debug is not
* selected.
*/
#ifdef CONFIG_ARCH_STACKDUMP
# undef lldbg
# define lldbg lowsyslog
@ -229,6 +229,30 @@ uint32_t *arm_syscall(uint32_t *regs)
}
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
*
* 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]);
#endif
/* Return the last value of curent_regs. This supports context switchs
* on return from the exception. That capability is not used here,
* however.
/* 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.
*/
return regs;

View File

@ -101,7 +101,7 @@ static void sig_trampoline(void)
" blx ip\n" /* Call the signal handler */
" pop {r2}\n" /* Recover LR in R2 */
" 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 */
);
}

View File

@ -60,9 +60,9 @@
#ifdef CONFIG_BUILD_KERNEL
# ifndef CONFIG_SYS_RESERVED
# error "CONFIG_SYS_RESERVED must be defined to have the value 5"
# elif CONFIG_SYS_RESERVED != 5
# error "CONFIG_SYS_RESERVED must have the value 5"
# error "CONFIG_SYS_RESERVED must be defined to have the value 6"
# elif CONFIG_SYS_RESERVED != 6
# error "CONFIG_SYS_RESERVED must have the value 6"
# endif
#else
# ifndef CONFIG_SYS_RESERVED
@ -83,35 +83,42 @@
#ifdef CONFIG_BUILD_KERNEL
/* 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[])
* 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)
* 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,
* FAR void *ucontext);
*/
#define SYS_signal_handler (3)
#define SYS_signal_handler (4)
/* SYS call 4:
/* SYS call 5:
*
* void signal_handler_return(void);
*/
#define SYS_signal_handler_return (4)
#define SYS_signal_handler_return (5)
#endif /* CONFIG_BUILD_KERNEL */

View File

@ -455,7 +455,7 @@ CONFIG_USERMAIN_STACKSIZE=2048
CONFIG_PTHREAD_STACK_MIN=256
CONFIG_PTHREAD_STACK_DEFAULT=2048
CONFIG_LIB_SYSCALL=y
CONFIG_SYS_RESERVED=5
CONFIG_SYS_RESERVED=6
CONFIG_SYS_NNEST=2
#

View File

@ -438,7 +438,7 @@ CONFIG_USERMAIN_STACKSIZE=2048
CONFIG_PTHREAD_STACK_MIN=256
CONFIG_PTHREAD_STACK_DEFAULT=2048
CONFIG_LIB_SYSCALL=y
CONFIG_SYS_RESERVED=5
CONFIG_SYS_RESERVED=6
CONFIG_SYS_NNEST=2
#