Пример #1
0
int up_svcall(int irq, FAR void *context)
{
    uint32_t *regs = (uint32_t*)context;

    DEBUGASSERT(regs && regs == current_regs);

    /* The SVCall software interrupt is called with R0 = system call command
     * and R1..R7 =  variable number of arguments depending on the system call.
     */

    svcdbg("SVCALL Entry: regs: %p cmd: %d\n", regs, regs[REG_R0]);
    svcdbg("  R0: %08x %08x %08x %08x %08x %08x %08x %08x\n",
           regs[REG_R0],  regs[REG_R1],  regs[REG_R2],  regs[REG_R3],
           regs[REG_R4],  regs[REG_R5],  regs[REG_R6],  regs[REG_R7]);
    svcdbg("  R8: %08x %08x %08x %08x %08x %08x %08x %08x\n",
           regs[REG_R8],  regs[REG_R9],  regs[REG_R10], regs[REG_R11],
           regs[REG_R12], regs[REG_R13], regs[REG_R14], regs[REG_R15]);
    svcdbg("  PSR=%08x\n", regs[REG_XPSR]);

    /* Handle the SVCall according to the command in R0 */

    switch (regs[REG_R0])
    {
    /* R0=SYS_save_context:  This is a save context command:
     *
     *   int up_saveusercontext(uint32_t *saveregs);
     *
     * At this point, the following values are saved in context:
     *
     *   R0 = SYS_save_context
     *   R1 = saveregs
     *
     * In this case, we simply need to copy the current regsters to the
     * save register space references in the saved R1 and return.
     */

    case SYS_save_context:
    {
        DEBUGASSERT(regs[REG_R1] != 0);
        memcpy((uint32_t*)regs[REG_R1], regs, XCPTCONTEXT_SIZE);
    }
    break;

    /* R0=SYS_restore_context: This a restore context command:
     *
     *   void up_fullcontextrestore(uint32_t *restoreregs) __attribute__ ((noreturn));
     *
     * At this point, the following values are saved in context:
     *
     *   R0 = SYS_restore_context
     *   R1 = restoreregs
     *
     * In this case, we simply need to set current_regs to restore register
     * area referenced in the saved R1. context == current_regs is the normal
     * exception return.  By setting current_regs = context[R1], we force
     * the return to the saved context referenced in R1.
     */

    case SYS_restore_context:
    {
        DEBUGASSERT(regs[REG_R1] != 0);
        current_regs = (uint32_t*)regs[REG_R1];
    }
    break;

    /* R0=SYS_switch_context: This a switch context command:
     *
     *   void up_switchcontext(uint32_t *saveregs, uint32_t *restoreregs);
     *
     * At this point, the following values are saved in context:
     *
     *   R0 = 1
     *   R1 = saveregs
     *   R2 = restoreregs
     *
     * In this case, we do both: We save the context registers to the save
     * register area reference by the saved contents of R1 and then set
     * current_regs to to the save register area referenced by the saved
     * contents of R2.
     */

    case SYS_switch_context:
    {
        DEBUGASSERT(regs[REG_R1] != 0 && regs[REG_R2] != 0);
        memcpy((uint32_t*)regs[REG_R1], regs, XCPTCONTEXT_SIZE);
        current_regs = (uint32_t*)regs[REG_R2];
    }
    break;

    /* This is not an architecture-specific system call.  If NuttX is built
     * as a standalone kernel with a system call interface, then all of the
     * additional system calls must be handled as in the default case.
     */

    default:
#ifdef CONFIG_NUTTX_KERNEL
        dispatch_syscall(regs);
#else
        slldbg("ERROR: Bad SYS call: %d\n", regs[REG_R0]);
#endif
        break;
    }

    /* Report what happened.  That might difficult in the case of a context switch */

    if (regs != current_regs)
    {
        svcdbg("SVCall Return: Context switch!\n");
        svcdbg("  R0: %08x %08x %08x %08x %08x %08x %08x %08x\n",
               current_regs[REG_R0],  current_regs[REG_R1],  current_regs[REG_R2],  current_regs[REG_R3],
               current_regs[REG_R4],  current_regs[REG_R5],  current_regs[REG_R6],  current_regs[REG_R7]);
        svcdbg("  R8: %08x %08x %08x %08x %08x %08x %08x %08x\n",
               current_regs[REG_R8],  current_regs[REG_R9],  current_regs[REG_R10], current_regs[REG_R11],
               current_regs[REG_R12], current_regs[REG_R13], current_regs[REG_R14], current_regs[REG_R15]);
        svcdbg("  PSR=%08x\n", current_regs[REG_XPSR]);
    }
    else
    {
        svcdbg("SVCall Return: %d\n", regs[REG_R0]);
    }

    return OK;
}
Пример #2
0
int up_svcall(int irq, FAR void *context)
{
  uint32_t *regs = (uint32_t*)context;
  uint32_t cmd;

  DEBUGASSERT(regs && regs == current_regs);
  cmd = regs[REG_R0];

  /* The SVCall software interrupt is called with R0 = system call command
   * and R1..R7 =  variable number of arguments depending on the system call.
   */

#if defined(CONFIG_DEBUG_SYSCALL) || defined(CONFIG_DEBUG_SVCALL)
# ifndef CONFIG_DEBUG_SVCALL
  if (cmd > SYS_switch_context)
# endif
    {
      svcdbg("SVCALL Entry: regs: %p cmd: %d\n", regs, cmd);
      svcdbg("  R0: %08x %08x %08x %08x %08x %08x %08x %08x\n",
             regs[REG_R0],  regs[REG_R1],  regs[REG_R2],  regs[REG_R3],
             regs[REG_R4],  regs[REG_R5],  regs[REG_R6],  regs[REG_R7]);
      svcdbg("  R8: %08x %08x %08x %08x %08x %08x %08x %08x\n",
             regs[REG_R8],  regs[REG_R9],  regs[REG_R10], regs[REG_R11],
             regs[REG_R12], regs[REG_R13], regs[REG_R14], regs[REG_R15]);
# ifdef REG_EXC_RETURN
      svcdbg(" PSR: %08x EXC_RETURN: %08x\n",
             regs[REG_XPSR], regs[REG_EXC_RETURN]);
# else
      svcdbg(" PSR: %08x\n", regs[REG_XPSR]);
# endif
    }
#endif

  /* Handle the SVCall according to the command in R0 */

  switch (cmd)
    {
      /* R0=SYS_save_context:  This is a save context command:
       *
       *   int up_saveusercontext(uint32_t *saveregs);
       *
       * At this point, the following values are saved in context:
       *
       *   R0 = SYS_save_context
       *   R1 = saveregs
       *
       * In this case, we simply need to copy the current regsters to the
       * save register space references in the saved R1 and return.
       */

      case SYS_save_context:
        {
          DEBUGASSERT(regs[REG_R1] != 0);
          memcpy((uint32_t*)regs[REG_R1], regs, XCPTCONTEXT_SIZE);
#if defined(CONFIG_ARCH_FPU) && !defined(CONFIG_ARMV7M_CMNVECTOR)
          up_savefpu((uint32_t*)regs[REG_R1]);
#endif
        }
        break;

      /* R0=SYS_restore_context:  This a restore context command:
       *
       *   void up_fullcontextrestore(uint32_t *restoreregs) noreturn_function;
       *
       * At this point, the following values are saved in context:
       *
       *   R0 = SYS_restore_context
       *   R1 = restoreregs
       *
       * In this case, we simply need to set current_regs to restore register
       * area referenced in the saved R1. context == current_regs is the normal
       * exception return.  By setting current_regs = context[R1], we force
       * the return to the saved context referenced in R1.
       */

      case SYS_restore_context:
        {
          DEBUGASSERT(regs[REG_R1] != 0);
          current_regs = (uint32_t*)regs[REG_R1];
        }
        break;

      /* R0=SYS_switch_context:  This a switch context command:
       *
       *   void up_switchcontext(uint32_t *saveregs, uint32_t *restoreregs);
       *
       * At this point, the following values are saved in context:
       *
       *   R0 = SYS_switch_context
       *   R1 = saveregs
       *   R2 = restoreregs
       *
       * In this case, we do both: We save the context registers to the save
       * register area reference by the saved contents of R1 and then set
       * current_regs to to the save register area referenced by the saved
       * contents of R2.
       */

      case SYS_switch_context:
        {
          DEBUGASSERT(regs[REG_R1] != 0 && regs[REG_R2] != 0);
          memcpy((uint32_t*)regs[REG_R1], regs, XCPTCONTEXT_SIZE);
#if defined(CONFIG_ARCH_FPU) && !defined(CONFIG_ARMV7M_CMNVECTOR)
          up_savefpu((uint32_t*)regs[REG_R1]);
#endif
          current_regs = (uint32_t*)regs[REG_R2];
        }
        break;

      /* R0=SYS_syscall_return:  This a syscall return command:
       *
       *   void up_syscall_return(void);
       *
       * At this point, the following values are saved in context:
       *
       *   R0 = SYS_syscall_return
       *
       * We need to restore the saved return address and return in
       * unprivileged thread mode.
       */

#ifdef CONFIG_NUTTX_KERNEL
      case SYS_syscall_return:
        {
          struct tcb_s *rtcb = sched_self();
          int index = (int)rtcb->xcp.nsyscalls - 1;

          /* Make sure that there is a saved syscall return address. */

          DEBUGASSERT(index >= 0);

          /* Setup to return to the saved syscall return address in
           * the original mode.
           */

          regs[REG_PC]         = rtcb->xcp.syscall[index].sysreturn;
          regs[REG_EXC_RETURN] = rtcb->xcp.syscall[index].excreturn;
          rtcb->xcp.nsyscalls  = index;

          /* The return value must be in R0-R1.  dispatch_syscall() temporarily
           * moved the value for R0 into R2.
           */

          regs[REG_R0]         = regs[REG_R2];
        }
        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;
       *
       * At this point, the following values are saved in context:
       *
       *   R0 = SYS_task_start
       *   R1 = taskentry
       *   R2 = argc
       *   R3 = argv
       */

#ifdef CONFIG_NUTTX_KERNEL
      case SYS_task_start:
        {
          /* Set up to return to the user-space task start-up function in
           * unprivileged mode.
           */

          regs[REG_PC]         = (uint32_t)USERSPACE->task_startup;
          regs[REG_EXC_RETURN] = EXC_RETURN_UNPRIVTHR;

          /* Change the parameter ordering to match the expectation of struct
           * userpace_s task_startup:
           */

          regs[REG_R0]         = regs[REG_R1]; /* Task entry */
          regs[REG_R1]         = regs[REG_R2]; /* argc */
          regs[REG_R2]         = regs[REG_R3]; /* argv */
        }
        break;
#endif

      /* R0=SYS_pthread_start:  This a user pthread start
       *
       *   void up_pthread_start(pthread_startroutine_t entrypt, pthread_addr_t arg) noreturn_function;
       *
       * At this point, the following values are saved in context:
       *
       *   R0 = SYS_pthread_start
       *   R1 = entrypt
       *   R2 = arg
       */

#if defined(CONFIG_NUTTX_KERNEL) && !defined(CONFIG_DISABLE_PTHREAD)
      case SYS_pthread_start:
        {
          /* Set up to return to the user-space pthread start-up function in
           * unprivileged mode.
           */

          regs[REG_PC]         = (uint32_t)USERSPACE->pthread_startup;
          regs[REG_EXC_RETURN] = EXC_RETURN_UNPRIVTHR;

          /* Change the parameter ordering to match the expectation of struct
           * userpace_s pthread_startup:
           */

          regs[REG_R0]         = regs[REG_R1]; /* pthread entry */
          regs[REG_R1]         = regs[REG_R2]; /* arg */
        }
        break;
#endif

      /* R0=SYS_signal_handler:  This a user signal handler callback
       *
       * void signal_handler(_sa_sigaction_t sighand, int signo,
       *                     FAR siginfo_t *info, FAR 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)
       */

#if defined(CONFIG_NUTTX_KERNEL) && !defined(CONFIG_DISABLE_SIGNALS)
      case SYS_signal_handler:
        {
          struct tcb_s *rtcb   = sched_self();

          /* Remember the caller's return address */

          DEBUGASSERT(rtcb->xcp.sigreturn == 0);
          rtcb->xcp.sigreturn  = regs[REG_PC];

          /* Set up to return to the user-space pthread start-up function in
           * unprivileged mode.
           */

          regs[REG_PC]         = (uint32_t)USERSPACE->signal_handler;
          regs[REG_EXC_RETURN] = EXC_RETURN_UNPRIVTHR;

          /* Change the parameter ordering to match the expectation of struct
           * userpace_s signal_handler.
           */

          regs[REG_R0]         = regs[REG_R1]; /* sighand */
          regs[REG_R1]         = regs[REG_R2]; /* signal */
          regs[REG_R2]         = regs[REG_R3]; /* info */

          /* The last parameter, ucontext, is trickier.  The ucontext
           * parameter will reside at an offset of 4 from the stack pointer.
           */

          regs[REG_R3]         = *(uint32_t*)(regs[REG_SP+4]);
        }
        break;
#endif

      /* R0=SYS_signal_handler_return:  This a user signal handler callback
       *
       *   void signal_handler_return(void);
       *
       * At this point, the following values are saved in context:
       *
       *   R0 = SYS_signal_handler_return
       */

#if defined(CONFIG_NUTTX_KERNEL) && !defined(CONFIG_DISABLE_SIGNALS)
      case SYS_signal_handler_return:
        {
          struct tcb_s *rtcb   = sched_self();

          /* Set up to return to the kernel-mode signal dispatching logic. */

          DEBUGASSERT(rtcb->xcp.sigreturn != 0);

          regs[REG_PC]         = rtcb->xcp.sigreturn;
          regs[REG_EXC_RETURN] = EXC_RETURN_PRIVTHR;
          rtcb->xcp.sigreturn  = 0;
        }
        break;
#endif

      /* This is not an architecture-specific system call.  If NuttX is built
       * as a standalone kernel with a system call interface, then all of the
       * additional system calls must be handled as in the default case.
       */

      default:
        {
#ifdef CONFIG_NUTTX_KERNEL
          FAR struct tcb_s *rtcb = sched_self();
          int index = rtcb->xcp.nsyscalls;

          /* Verify that the SYS call number is within range */

          DEBUGASSERT(cmd >= CONFIG_SYS_RESERVED && cmd < SYS_maxsyscall);

          /* Make sure that there is a no saved syscall return address.  We
           * cannot yet handle nested system calls.
           */

          DEBUGASSERT(index < CONFIG_SYS_NNEST);

          /* Setup to return to dispatch_syscall in privileged mode. */

          rtcb->xcp.syscall[index].sysreturn  = regs[REG_PC];
          rtcb->xcp.syscall[index].excreturn  = regs[REG_EXC_RETURN];
          rtcb->xcp.nsyscalls  = index + 1;

          regs[REG_PC]         = (uint32_t)dispatch_syscall;
          regs[REG_EXC_RETURN] = EXC_RETURN_PRIVTHR;

          /* Offset R0 to account for the reserved values */

          regs[REG_R0] -= CONFIG_SYS_RESERVED;
#else
          slldbg("ERROR: Bad SYS call: %d\n", regs[REG_R0]);
#endif
        }
        break;
    }

  /* Report what happened.  That might difficult in the case of a context switch */

#if defined(CONFIG_DEBUG_SYSCALL) || defined(CONFIG_DEBUG_SVCALL)
# ifndef CONFIG_DEBUG_SVCALL
  if (cmd > SYS_switch_context)
# else
  if (regs != current_regs)
# endif
    {
      svcdbg("SVCall Return:\n");
      svcdbg("  R0: %08x %08x %08x %08x %08x %08x %08x %08x\n",
             current_regs[REG_R0],  current_regs[REG_R1],  current_regs[REG_R2],  current_regs[REG_R3],
             current_regs[REG_R4],  current_regs[REG_R5],  current_regs[REG_R6],  current_regs[REG_R7]);
      svcdbg("  R8: %08x %08x %08x %08x %08x %08x %08x %08x\n",
             current_regs[REG_R8],  current_regs[REG_R9],  current_regs[REG_R10], current_regs[REG_R11],
             current_regs[REG_R12], current_regs[REG_R13], current_regs[REG_R14], current_regs[REG_R15]);
# ifdef REG_EXC_RETURN
      svcdbg(" PSR: %08x EXC_RETURN: %08x\n",
             current_regs[REG_XPSR], current_regs[REG_EXC_RETURN]);
# else
      svcdbg(" PSR: %08x\n", current_regs[REG_XPSR]);
# endif
    }
# ifdef CONFIG_DEBUG_SVCALL
  else
    {
      svcdbg("SVCall Return: %d\n", regs[REG_R0]);
    }
# endif
#endif

  return OK;
}
Пример #3
0
static inline void dispatch_syscall(uint32_t *regs)
{
    uint32_t  cmd  = regs[REG_R0];
    FAR _TCB *rtcb = sched_self();
    uintptr_t ret  = (uintptr_t)ERROR;

    /* Verify the the SYS call number is within range */

    if (cmd < SYS_maxsyscall)
    {
        /* Report error and return ERROR */

        slldbg("ERROR: Bad SYS call: %d\n", cmd);
    }
    else
    {
        /* The index into the syscall table is offset by the number of architecture-
         * specific reserved entries at the beginning of the SYS call number space.
         */

        int index = cmd - CONFIG_SYS_RESERVED;

        /* Enable interrupts while the SYSCALL executes */

#ifdef SYSCALL_INTERRUPTIBLE
        irqenable();
#endif

        /* Call the correct stub for each SYS call, based on the number of parameters */

        svcdbg("Calling stub%d at %p\n", index, g_stubloopkup[index].stub0);

        switch (g_stubnparms[index])
        {
        /* No parameters */

        case 0:
            ret = g_stublookup[index].stub0();
            break;

        /* Number of parameters: 1 */

        case 1:
            ret = g_stublookup[index].stub1(regs[REG_R1]);
            break;

        /* Number of parameters: 2 */

        case 2:
            ret = g_stublookup[index].stub2(regs[REG_R1], regs[REG_R2]);
            break;

        /* Number of parameters: 3 */

        case 3:
            ret = g_stublookup[index].stub3(regs[REG_R1], regs[REG_R2],
                                            regs[REG_R3]);
            break;

        /* Number of parameters: 4 */

        case 4:
            ret = g_stublookup[index].stub4(regs[REG_R1], regs[REG_R2],
                                            regs[REG_R3], regs[REG_R4]);
            break;

        /* Number of parameters: 5 */

        case 5:
            ret = g_stublookup[index].stub5(regs[REG_R1], regs[REG_R2],
                                            regs[REG_R3], regs[REG_R4],
                                            regs[REG_R5]);
            break;

        /* Number of parameters: 6 */

        case 6:
            ret = g_stublookup[index].stub6(regs[REG_R1], regs[REG_R2],
                                            regs[REG_R3], regs[REG_R4],
                                            regs[REG_R5], regs[REG_R6]);
            break;

        /* Unsupported number of paramters. Report error and return ERROR */

        default:
            slldbg("ERROR: Bad SYS call %d number parameters %d\n",
                   cmd, g_stubnparms[index]);
            break;
        }

#ifdef SYSCALL_INTERRUPTIBLE
        irqdisable();
#endif
    }

    /* Set up the return value.  First, check if a context switch occurred.
     * In this case, regs will no longer be the same as current_regs.  In
     * the case of a context switch, we will have to save the return value
     * in the TCB where it can be returned later when the task is restarted.
     */

    if (regs != current_regs)
    {
        regs = rtcb->xcp.regs;
    }

    /* Then return the result in R0 */

    svcdbg("Return value regs: %p value: %d\n", regs, ret);
    regs[REG_R0] = (uint32_t)ret;
}
Пример #4
0
uint32_t *arm_syscall(uint32_t *regs)
{
  uint32_t cmd;
#ifdef CONFIG_BUILD_KERNEL
  uint32_t cpsr;
#endif

  /* Nested interrupts are not supported */

  DEBUGASSERT(regs);

  /* The SYSCALL command is in R0 on entry.  Parameters follow in R1..R7 */

  cmd = regs[REG_R0];

  /* The SVCall software interrupt is called with R0 = system call command
   * and R1..R7 =  variable number of arguments depending on the system call.
   */

#if defined(CONFIG_DEBUG_SYSCALL)
  svcdbg("SYSCALL Entry: regs: %p cmd: %d\n", regs, cmd);
  svcdbg("  R0: %08x %08x %08x %08x %08x %08x %08x %08x\n",
         regs[REG_R0],  regs[REG_R1],  regs[REG_R2],  regs[REG_R3],
         regs[REG_R4],  regs[REG_R5],  regs[REG_R6],  regs[REG_R7]);
  svcdbg("  R8: %08x %08x %08x %08x %08x %08x %08x %08x\n",
         regs[REG_R8],  regs[REG_R9],  regs[REG_R10], regs[REG_R11],
         regs[REG_R12], regs[REG_R13], regs[REG_R14], regs[REG_R15]);
  svcdbg("CPSR: %08x\n", regs[REG_CPSR]);
#endif

  /* Handle the SVCall according to the command in R0 */

  switch (cmd)
    {
      /* R0=SYS_syscall_return:  This a SYSCALL return command:
       *
       *   void up_syscall_return(void);
       *
       * At this point, the following values are saved in context:
       *
       *   R0 = SYS_syscall_return
       *
       * We need to restore the saved return address and return in
       * unprivileged thread mode.
       */

      case SYS_syscall_return:
        {
          FAR struct tcb_s *rtcb = sched_self();
          int index = (int)rtcb->xcp.nsyscalls - 1;

          /* Make sure that there is a saved SYSCALL return address. */

          DEBUGASSERT(index >= 0);

          /* Setup to return to the saved SYSCALL return address in
           * the original mode.
           */

          regs[REG_PC]        = rtcb->xcp.syscall[index].sysreturn;
#ifdef CONFIG_BUILD_KERNEL
          regs[REG_CPSR]      = rtcb->xcp.syscall[index].cpsr;
#endif
          /* The return value must be in R0-R1.  dispatch_syscall() temporarily
           * moved the value for R0 into R2.
           */

          regs[REG_R0]         = regs[REG_R2];

#ifdef CONFIG_ARCH_KERNEL_STACK
          /* If this is the outermost SYSCALL and if there is a saved user stack
           * pointer, then restore the user stack pointer on this final return to
           * user code.
           */

          if (index == 0 && rtcb->xcp.ustkptr != NULL)
            {
              regs[REG_SP]      = (uint32_t)rtcb->xcp.ustkptr;
              rtcb->xcp.ustkptr = NULL;
            }
#endif
          /* Save the new SYSCALL nesting level */

          rtcb->xcp.nsyscalls = index;
        }
        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;
       *
       * At this point, the following values are saved in context:
       *
       *   R0 = SYS_task_start
       *   R1 = taskentry
       *   R2 = argc
       *   R3 = argv
       */

#ifdef CONFIG_BUILD_KERNEL
      case SYS_task_start:
        {
          /* Set up to return to the user-space _start function in
           * unprivileged mode.  We need:
           *
           *   R0   = argc
           *   R1   = argv
           *   PC   = taskentry
           *   CSPR = user mode
           */

          regs[REG_PC]   = regs[REG_R1];
          regs[REG_R0]   = regs[REG_R2];
          regs[REG_R1]   = regs[REG_R3];

          cpsr           = regs[REG_CPSR] & ~PSR_MODE_MASK;
          regs[REG_CPSR] = cpsr | PSR_MODE_USR;
        }
        break;
#endif

      /* R0=SYS_pthread_start:  This a user pthread start
       *
       *   void up_pthread_start(pthread_startroutine_t entrypt, pthread_addr_t arg) noreturn_function;
       *
       * At this point, the following values are saved in context:
       *
       *   R0 = SYS_pthread_start
       *   R1 = entrypt
       *   R2 = arg
       */

#if defined(CONFIG_BUILD_KERNEL) && !defined(CONFIG_DISABLE_PTHREAD)
      case SYS_pthread_start:
        {
          /* Set up to return to the user-space pthread start-up function in
           * unprivileged mode. We need:
           *
           *   R0   = arg
           *   PC   = entrypt
           *   CSPR = user mode
           */


          regs[REG_PC]   = regs[REG_R1];
          regs[REG_R0]   = regs[REG_R2];

          cpsr           = regs[REG_CPSR] & ~PSR_MODE_MASK;
          regs[REG_CPSR] = cpsr | PSR_MODE_USR;
        }
        break;
#endif

#if defined(CONFIG_BUILD_KERNEL) && !defined(CONFIG_DISABLE_SIGNALS)
      /* R0=SYS_signal_handler:  This a user signal handler callback
       *
       * void signal_handler(_sa_sigaction_t sighand, int signo,
       *                     FAR siginfo_t *info, FAR 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:
        {
          FAR struct tcb_s *rtcb = sched_self();
          /* Remember the caller's return address */

          DEBUGASSERT(rtcb->xcp.sigreturn == 0);
          rtcb->xcp.sigreturn  = regs[REG_PC];

          /* Set up to return to the user-space pthread start-up function in
           * unprivileged mode.
           */

          regs[REG_PC]   = (uint32_t)ARCH_DATA_RESERVE->ar_sigtramp;
          cpsr           = regs[REG_CPSR] & ~PSR_MODE_MASK;
          regs[REG_CPSR] = cpsr | PSR_MODE_USR;

          /* Change the parameter ordering to match the expectation of struct
           * userpace_s signal_handler.
           */

          regs[REG_R0]   = regs[REG_R1]; /* sighand */
          regs[REG_R1]   = regs[REG_R2]; /* signal */
          regs[REG_R2]   = regs[REG_R3]; /* info */

          /* The last parameter, ucontext, is trickier.  The ucontext
           * parameter will reside at an offset of 4 from the stack pointer.
           */

          regs[REG_R3]   = *(uint32_t*)(regs[REG_SP]+4);

#ifdef CONFIG_ARCH_KERNEL_STACK
          /* If we are signalling a user process, then we must be operating
           * 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)
            {
              DEBUGASSERT(rtcb->xcp.kstkptr == NULL && rtcb->xcp.ustkptr != NULL);

              rtcb->xcp.kstkptr = (FAR uint32_t *)regs[REG_SP];
              regs[REG_SP]      = (uint32_t)rtcb->xcp.ustkptr;
            }
#endif
        }
        break;
#endif

#if defined(CONFIG_BUILD_KERNEL) && !defined(CONFIG_DISABLE_SIGNALS)
      /* R0=SYS_signal_handler_return:  This a user signal handler callback
       *
       *   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:
        {
          FAR struct tcb_s *rtcb = sched_self();

          /* Set up to return to the kernel-mode signal dispatching logic. */

          DEBUGASSERT(rtcb->xcp.sigreturn != 0);

          regs[REG_PC]         = rtcb->xcp.sigreturn;
          cpsr                 = regs[REG_CPSR] & ~PSR_MODE_MASK;
          regs[REG_CPSR]       = cpsr | PSR_MODE_SVC;
          rtcb->xcp.sigreturn  = 0;

#ifdef CONFIG_ARCH_KERNEL_STACK
          /* We must enter here be using the user stack.  We need to switch
           * to back to the kernel user stack before returning to the kernel
           * mode signal trampoline.
           */

          if (rtcb->xcp.kstack != NULL)
            {
              DEBUGASSERT(rtcb->xcp.kstkptr != NULL &&
                          (uint32_t)rtcb->xcp.ustkptr == regs[REG_SP]);

              regs[REG_SP]      = (uint32_t)rtcb->xcp.kstkptr;
              rtcb->xcp.kstkptr = NULL;
            }
#endif
        }
        break;
#endif

      /* This is not an architecture-specific system call.  If NuttX is built
       * as a standalone kernel with a system call interface, then all of the
       * additional system calls must be handled as in the default case.
       */

      default:
        {
#ifdef CONFIG_LIB_SYSCALL
          FAR struct tcb_s *rtcb = sched_self();
          int index = rtcb->xcp.nsyscalls;

          /* Verify that the SYS call number is within range */

          DEBUGASSERT(cmd >= CONFIG_SYS_RESERVED && cmd < SYS_maxsyscall);

          /* Make sure that there is a no saved SYSCALL return address.  We
           * cannot yet handle nested system calls.
           */

          DEBUGASSERT(index < CONFIG_SYS_NNEST);

          /* Setup to return to dispatch_syscall in privileged mode. */

          rtcb->xcp.syscall[index].sysreturn = regs[REG_PC];
#ifdef CONFIG_BUILD_KERNEL
          rtcb->xcp.syscall[index].cpsr      = regs[REG_CPSR];
#endif

          regs[REG_PC]   = (uint32_t)dispatch_syscall;
#ifdef CONFIG_BUILD_KERNEL
          cpsr           = regs[REG_CPSR] & ~PSR_MODE_MASK;
          regs[REG_CPSR] = cpsr | PSR_MODE_SVC;
#endif
          /* Offset R0 to account for the reserved values */

          regs[REG_R0] -= CONFIG_SYS_RESERVED;
#else
          svcdbg("ERROR: Bad SYS call: %d\n", regs[REG_R0]);
#endif

#ifdef CONFIG_ARCH_KERNEL_STACK
          /* If this is the first SYSCALL and if there is an allocated
           * kernel stack, then switch to the kernel stack.
           */

          if (index == 0 && rtcb->xcp.kstack != NULL)
            {
              rtcb->xcp.ustkptr = (FAR uint32_t *)regs[REG_SP];
              regs[REG_SP]      = (uint32_t)rtcb->xcp.kstack + ARCH_KERNEL_STACKSIZE;
            }
#endif
          /* Save the new SYSCALL nesting level */

          rtcb->xcp.nsyscalls   = index + 1;
        }
        break;
    }

#if defined(CONFIG_DEBUG_SYSCALL)
  /* Report what happened */

  svcdbg("SYSCALL Exit: regs: %p\n", regs);
  svcdbg("  R0: %08x %08x %08x %08x %08x %08x %08x %08x\n",
         regs[REG_R0],  regs[REG_R1],  regs[REG_R2],  regs[REG_R3],
         regs[REG_R4],  regs[REG_R5],  regs[REG_R6],  regs[REG_R7]);
  svcdbg("  R8: %08x %08x %08x %08x %08x %08x %08x %08x\n",
         regs[REG_R8],  regs[REG_R9],  regs[REG_R10], regs[REG_R11],
         regs[REG_R12], regs[REG_R13], regs[REG_R14], regs[REG_R15]);
  svcdbg("CPSR: %08x\n", regs[REG_CPSR]);
#endif

  /* 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;
}