* arch/arm64/src/common/arm64_syscall.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <inttypes.h>
#include <stdint.h>
#include <string.h>
#include <assert.h>
#include <debug.h>
#include <syscall.h>
#include <nuttx/arch.h>
#include <nuttx/sched.h>
#include <nuttx/addrenv.h>
#include "arch/irq.h"
#include "arm64_internal.h"
#include "arm64_fatal.h"
#include "sched/sched.h"
#include "signal/signal.h"
* Private Types
****************************************************************************/
typedef uintptr_t (*syscall_t)(unsigned int, ...);
* Private Functions
****************************************************************************/
static void arm64_dump_syscall(const char *tag, uint64_t cmd,
const uint64_t *regs)
{
svcinfo("SYSCALL %s: regs: %p cmd: %" PRId64 "\n", tag, regs, cmd);
svcinfo("x0: 0x%-16lx x1: 0x%lx\n",
regs[REG_X0], regs[REG_X1]);
svcinfo("x2: 0x%-16lx x3: 0x%lx\n",
regs[REG_X2], regs[REG_X3]);
svcinfo("x4: 0x%-16lx x5: 0x%lx\n",
regs[REG_X4], regs[REG_X5]);
svcinfo("x6: 0x%-16lx x7: 0x%lx\n",
regs[REG_X6], regs[REG_X7]);
}
#ifdef CONFIG_LIB_SYSCALL
* Public Functions
****************************************************************************/
* Name: dispatch_syscall
*
* Description:
* Call the stub function corresponding to the system call. NOTE the non-
* standard parameter passing:
*
* R0 = SYS_ call number
* R1 = parm0
* R2 = parm1
* R3 = parm2
* R4 = parm3
* R5 = parm4
* R6 = parm5
* R7 = context (aka SP)
*
****************************************************************************/
uintptr_t dispatch_syscall(unsigned int nbr, uintptr_t parm1,
uintptr_t parm2, uintptr_t parm3,
uintptr_t parm4, uintptr_t parm5,
uintptr_t parm6)
{
struct tcb_s *rtcb = this_task();
atomic_or(&rtcb->flags, TCB_FLAG_SYSCALL);
register long x0 asm("x0") = (long)(nbr);
register long x1 asm("x1") = (long)(parm1);
register long x2 asm("x2") = (long)(parm2);
register long x3 asm("x3") = (long)(parm3);
register long x4 asm("x4") = (long)(parm4);
register long x5 asm("x5") = (long)(parm5);
register long x6 asm("x6") = (long)(parm6);
syscall_t do_syscall;
uintptr_t ret;
if (x0 > SYS_maxsyscall)
{
return -ENOSYS;
}
x0 -= CONFIG_SYS_RESERVED;
do_syscall = (syscall_t)g_stublookup[x0];
ret = do_syscall(x0, x1, x2, x3, x4, x5, x6);
atomic_and(&rtcb->flags, ~TCB_FLAG_SYSCALL);
nxsig_unmask_pendingsignal();
return ret;
}
#endif
* Name: arm64_syscall
*
* Description:
* task switch syscall
*
****************************************************************************/
uint64_t *arm64_syscall(uint64_t *regs)
{
struct tcb_s **running_task = &g_running_task;
struct tcb_s *tcb = this_task();
uint64_t cmd;
#if defined(CONFIG_BUILD_KERNEL) || defined(CONFIG_BUILD_PROTECTED)
uint64_t spsr;
#endif
g_interrupt_context = true;
if (*running_task != NULL)
{
nxsched_suspend_scheduler(*running_task);
}
DEBUGASSERT(regs);
cmd = regs[REG_X0];
* should not be overwriten
*/
if (cmd != SYS_restore_context)
{
(*running_task)->xcp.regs = regs;
}
arm64_dump_syscall(__func__, cmd, regs);
switch (cmd)
{
case SYS_restore_context:
case SYS_switch_context:
#ifdef CONFIG_ARCH_ADDRENV
addrenv_switch(tcb);
tcb = this_task();
#endif
break_critical_section();
*running_task = tcb;
break;
#if (defined(CONFIG_BUILD_KERNEL) || defined(CONFIG_BUILD_PROTECTED)) && \
!defined(CONFIG_DISABLE_SIGNALS)
*
* void signal_handler(_sa_sigaction_t sighand, int signo,
* siginfo_t *info, void *ucontext);
*
* At this point, the following values are saved in context:
*
* R0 = SYS_signal_handler
* R1 = sighand
* R2 = signo
* R3 = info
* ucontext (on the stack)
*/
case SYS_signal_handler:
{
struct tcb_s *rtcb = this_task();
DEBUGASSERT(rtcb->xcp.sigreturn == 0);
rtcb->xcp.sigreturn = regs[REG_ELR];
* unprivileged mode.
*/
#if defined(CONFIG_BUILD_KERNEL)
regs[REG_ELR] = (uint64_t)ARCH_DATA_RESERVE->ar_sigtramp;
#elif defined(CONFIG_BUILD_PROTECTED)
regs[REG_ELR] = (uint64_t)(USERSPACE->signal_handler);
#endif
spsr = regs[REG_SPSR] & ~SPSR_MODE_MASK;
regs[REG_SPSR] = spsr | SPSR_MODE_EL0T;
* userpace_s signal_handler.
*/
regs[REG_X0] = regs[REG_X1];
regs[REG_X1] = regs[REG_X2];
regs[REG_X2] = regs[REG_X3];
regs[REG_X3] = regs[REG_X4];
#ifdef CONFIG_ARCH_KERNEL_STACK
* on the kernel stack now. We need to switch back to the user
* stack before dispatching the signal handler to the user code.
* The existence of an allocated kernel stack is sufficient
* information to make this decision.
*/
if (rtcb->xcp.kstack != NULL)
{
uint64_t usp;
rtcb->xcp.ustkptr = (uintptr_t *)regs[REG_SP_EL0];
usp = (uintptr_t)rtcb->xcp.ustkptr - sizeof(siginfo_t);
memcpy((void *)usp, (void *)regs[REG_X2], sizeof(siginfo_t));
regs[REG_SP_EL0] = usp;
regs[REG_X2] = usp;
}
#endif
}
break;
#endif
#if (defined(CONFIG_BUILD_KERNEL) || defined(CONFIG_BUILD_PROTECTED)) && \
!defined(CONFIG_DISABLE_SIGNALS)
*
* void signal_handler_return(void);
*
* At this point, the following values are saved in context:
*
* R0 = SYS_signal_handler_return
*/
case SYS_signal_handler_return:
{
struct tcb_s *rtcb = this_task();
DEBUGASSERT(rtcb->xcp.sigreturn != 0);
regs[REG_ELR] = rtcb->xcp.sigreturn;
spsr = regs[REG_SPSR] & ~SPSR_MODE_MASK;
regs[REG_SPSR] = spsr | SPSR_MODE_EL1H;
rtcb->xcp.sigreturn = 0;
#ifdef CONFIG_ARCH_KERNEL_STACK
write_sysreg(rtcb->xcp.ustkptr, sp_el0);
#endif
}
break;
#endif
case SYS_assert_handler:
{
_assert((const char *)regs[REG_X1], (int)regs[REG_X2],
(const char *)regs[REG_X3], (void *)running_regs(), false);
}
break;
default:
{
svcerr("ERROR: Bad SYS call: 0x%" PRIx64 "\n", cmd);
g_interrupt_context = false;
return 0;
}
break;
}
regs = tcb->xcp.regs;
nxsched_resume_scheduler(tcb);
* and will be marked as NULL to avoid misusage.
*/
(*running_task)->xcp.regs = NULL;
g_interrupt_context = false;
write_sysreg(tcb->stack_alloc_ptr, tpidr_el0);
return regs;
}