int sched_setupidlefiles(FAR _TCB *tcb) { #if CONFIG_NFILE_DESCRIPTORS > 0 && defined(CONFIG_DEV_CONSOLE) int fd; #endif #if CONFIG_NFILE_DESCRIPTORS > 0 /* Allocate file descriptors for the TCB */ tcb->filelist = files_alloclist(); if (!tcb->filelist) { return -ENOMEM; } #endif /* CONFIG_NFILE_DESCRIPTORS */ #if CONFIG_NSOCKET_DESCRIPTORS > 0 /* Allocate socket descriptors for the TCB */ tcb->sockets = net_alloclist(); if (!tcb->sockets) { return -ENOMEM; } #endif /* CONFIG_NSOCKET_DESCRIPTORS */ #if CONFIG_NFILE_DESCRIPTORS > 0 && defined(CONFIG_DEV_CONSOLE) /* Open stdin, dup to get stdout and stderr. This should always * be the first file opened and, hence, should always get file * descriptor 0. */ fd = open("/dev/console", O_RDWR); if (fd == 0) { /* Successfully opened /dev/console as stdin (fd == 0) */ (void)file_dup2(0, 1); (void)file_dup2(0, 2); } else { /* We failed to open /dev/console OR for some reason, we opened * it and got some file descriptor other than 0. */ if (fd >- 0) { slldbg("Open /dev/console fd: %d\n", fd); (void)close(fd); } else { slldbg("Failed to open /dev/console: %d\n", errno); } return -ENFILE; } #if CONFIG_NFILE_STREAMS > 0 /* Allocate file strems for the TCB */ return sched_setupstreams(tcb); #else return OK; #endif /* CONFIG_NFILE_STREAMS */ #else return OK; #endif /* CONFIG_NFILE_DESCRIPTORS && CONFIG_DEV_CONSOLE */ }
void up_reprioritize_rtr(FAR struct tcb_s *tcb, uint8_t priority) { /* Verify that the caller is sane */ if (tcb->task_state < FIRST_READY_TO_RUN_STATE || tcb->task_state > LAST_READY_TO_RUN_STATE #if SCHED_PRIORITY_MIN > 0 || priority < SCHED_PRIORITY_MIN #endif #if SCHED_PRIORITY_MAX < UINT8_MAX || priority > SCHED_PRIORITY_MAX #endif ) { PANIC(); } else { FAR struct tcb_s *rtcb = (FAR struct tcb_s*)g_readytorun.head; bool switch_needed; slldbg("TCB=%p PRI=%d\n", tcb, priority); /* Remove the tcb task from the ready-to-run list. * sched_removereadytorun will return true if we just * remove the head of the ready to run list. */ switch_needed = sched_removereadytorun(tcb); /* Setup up the new task priority */ tcb->sched_priority = (uint8_t)priority; /* Return the task to the specified blocked task list. * sched_addreadytorun will return true if the task was * added to the new list. We will need to perform a context * switch only if the EXCLUSIVE or of the two calls is non-zero * (i.e., one and only one the calls changes the head of the * ready-to-run list). */ switch_needed ^= sched_addreadytorun(tcb); /* Now, perform the context switch if one is needed */ if (switch_needed) { /* If we are going to do a context switch, then now is the right * time to add any pending tasks back into the ready-to-run list. * task list now */ if (g_pendingtasks.head) { sched_mergepending(); } /* Are we in an interrupt handler? */ if (IN_INTERRUPT()) { /* Yes, then we have to do things differently. * Just copy the current context into the OLD rtcb. */ SAVE_IRQCONTEXT(rtcb); /* Restore the exception context of the rtcb at the (new) head * of the g_readytorun task list. */ rtcb = (FAR struct tcb_s*)g_readytorun.head; slldbg("New Active Task TCB=%p\n", rtcb); /* Then setup so that the context will be performed on exit * from the interrupt. */ SET_IRQCONTEXT(rtcb); } /* Copy the exception context into the TCB at the (old) head of the * g_readytorun Task list. if SAVE_USERCONTEXT returns a non-zero * value, then this is really the previously running task restarting! */ else if (!SAVE_USERCONTEXT(rtcb)) { /* Restore the exception context of the rtcb at the (new) head * of the g_readytorun task list. */ rtcb = (FAR struct tcb_s*)g_readytorun.head; slldbg("New Active Task TCB=%p\n", rtcb); /* Then switch contexts */ RESTORE_USERCONTEXT(rtcb); } } } }
void up_release_pending(void) { struct tcb_s *rtcb = (struct tcb_s*)g_readytorun.head; slldbg("From TCB=%p\n", rtcb); /* Merge the g_pendingtasks list into the g_readytorun task list */ /* sched_lock(); */ if (sched_mergepending()) { /* The currently active task has changed! We will need to * switch contexts. First check if we are operating in * interrupt context: */ if (current_regs) { /* Yes, then we have to do things differently. * Just copy the current_regs into the OLD rtcb. */ up_savestate(rtcb->xcp.regs); /* Restore the exception context of the rtcb at the (new) head * of the g_readytorun task list. */ rtcb = (struct tcb_s*)g_readytorun.head; slldbg("New Active Task TCB=%p\n", rtcb); /* Then switch contexts. Any necessary address environment * changes will be made when the interrupt returns. */ up_restorestate(rtcb->xcp.regs); } /* Copy the exception context into the TCB of the task that * was currently active. if up_saveusercontext returns a non-zero * value, then this is really the previously running task * restarting! */ else if (!up_saveusercontext(rtcb->xcp.regs)) { /* Restore the exception context of the rtcb at the (new) head * of the g_readytorun task list. */ rtcb = (struct tcb_s*)g_readytorun.head; slldbg("New Active Task TCB=%p\n", rtcb); #ifdef CONFIG_ARCH_ADDRENV /* 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. */ (void)group_addrenv(rtcb); #endif /* Then switch contexts */ up_fullcontextrestore(rtcb->xcp.regs); } } }
void os_start(void) { int i; slldbg("Entry\n"); /* Initialize all task lists */ dq_init(&g_readytorun); dq_init(&g_pendingtasks); dq_init(&g_waitingforsemaphore); #ifndef CONFIG_DISABLE_SIGNALS dq_init(&g_waitingforsignal); #endif #ifndef CONFIG_DISABLE_MQUEUE dq_init(&g_waitingformqnotfull); dq_init(&g_waitingformqnotempty); #endif #ifdef CONFIG_PAGING dq_init(&g_waitingforfill); #endif dq_init(&g_inactivetasks); sq_init(&g_delayeddeallocations); /* Initialize the logic that determine unique process IDs. */ g_lastpid = 0; for (i = 0; i < CONFIG_MAX_TASKS; i++) { g_pidhash[i].tcb = NULL; g_pidhash[i].pid = INVALID_PROCESS_ID; } /* Assign the process ID of ZERO to the idle task */ g_pidhash[ PIDHASH(0)].tcb = &g_idletcb; g_pidhash[ PIDHASH(0)].pid = 0; /* Initialize a TCB for this thread of execution. NOTE: The default * value for most components of the g_idletcb are zero. The entire * structure is set to zero. Then only the (potentially) non-zero * elements are initialized. NOTE: The idle task is the only task in * that has pid == 0 and sched_priority == 0. */ bzero((void*)&g_idletcb, sizeof(_TCB)); g_idletcb.task_state = TSTATE_TASK_RUNNING; g_idletcb.entry.main = (main_t)os_start; #if CONFIG_TASK_NAME_SIZE > 0 strncpy(g_idletcb.name, g_idlename, CONFIG_TASK_NAME_SIZE-1); g_idletcb.argv[0] = g_idletcb.name; #else g_idletcb.argv[0] = g_idlename; #endif /* CONFIG_TASK_NAME_SIZE */ /* Then add the idle task's TCB to the head of the ready to run list */ dq_addfirst((FAR dq_entry_t*)&g_idletcb, (FAR dq_queue_t*)&g_readytorun); /* Initialize the processor-specific portion of the TCB */ g_idletcb.flags = TCB_FLAG_TTYPE_KERNEL; up_initial_state(&g_idletcb); /* Initialize the memory manager */ #ifndef CONFIG_HEAP_BASE { FAR void *heap_start; size_t heap_size; up_allocate_heap(&heap_start, &heap_size); kmm_initialize(heap_start, heap_size); } #else kmm_initialize((void*)CONFIG_HEAP_BASE, CONFIG_HEAP_SIZE); #endif /* Initialize the interrupt handling subsystem (if included) */ #ifdef CONFIG_HAVE_WEAKFUNCTIONS if (irq_initialize != NULL) #endif { irq_initialize(); } /* Initialize the watchdog facility (if included in the link) */ #ifdef CONFIG_HAVE_WEAKFUNCTIONS if (wd_initialize != NULL) #endif { wd_initialize(); } /* Initialize the POSIX timer facility (if included in the link) */ #ifndef CONFIG_DISABLE_CLOCK #ifdef CONFIG_HAVE_WEAKFUNCTIONS if (clock_initialize != NULL) #endif { clock_initialize(); } #endif #ifndef CONFIG_DISABLE_POSIX_TIMERS #ifdef CONFIG_HAVE_WEAKFUNCTIONS if (timer_initialize != NULL) #endif { timer_initialize(); } #endif /* Initialize the signal facility (if in link) */ #ifndef CONFIG_DISABLE_SIGNALS #ifdef CONFIG_HAVE_WEAKFUNCTIONS if (sig_initialize != NULL) #endif { sig_initialize(); } #endif /* Initialize the semaphore facility. (if in link) */ #ifdef CONFIG_HAVE_WEAKFUNCTIONS if (sem_initialize != NULL) #endif { sem_initialize(); } /* Initialize the named message queue facility (if in link) */ #ifndef CONFIG_DISABLE_MQUEUE #ifdef CONFIG_HAVE_WEAKFUNCTIONS if (mq_initialize != NULL) #endif { mq_initialize(); } #endif /* Initialize the thread-specific data facility (if in link) */ #ifndef CONFIG_DISABLE_PTHREAD #ifdef CONFIG_HAVE_WEAKFUNCTIONS if (pthread_initialize != NULL) #endif { pthread_initialize(); } #endif /* Initialize the file system (needed to support device drivers) */ #if CONFIG_NFILE_DESCRIPTORS > 0 #ifdef CONFIG_HAVE_WEAKFUNCTIONS if (fs_initialize != NULL) #endif { fs_initialize(); } #endif /* Initialize the network system */ #ifdef CONFIG_NET #if 0 if (net_initialize != NULL) #endif { net_initialize(); } #endif /* The processor specific details of running the operating system * will be handled here. Such things as setting up interrupt * service routines and starting the clock are some of the things * that are different for each processor and hardware platform. */ up_initialize(); /* Initialize the C libraries (if included in the link). This * is done last because the libraries may depend on the above. */ #ifdef CONFIG_HAVE_WEAKFUNCTIONS if (lib_initialize != NULL) #endif { lib_initialize(); } /* Create stdout, stderr, stdin */ (void)sched_setupidlefiles(&g_idletcb); /* Create initial tasks and bring-up the system */ (void)os_bringup(); /* When control is return to this point, the system is idle. */ sdbg("Beginning Idle Loop\n"); for (;;) { /* Perform garbage collection (if it is not being done by the worker * thread). This cleans-up memory de-allocations that were queued * because they could not be freed in that execution context (for * example, if the memory was freed from an interrupt handler). */ #ifndef CONFIG_SCHED_WORKQUEUE /* We must have exclusive access to the memory manager to do this * BUT the idle task cannot wait on a semaphore. So we only do * the cleanup now if we can get the semaphore -- this should be * possible because if the IDLE thread is running, no other task is! */ if (kmm_trysemaphore() == 0) { sched_garbagecollection(); kmm_givesemaphore(); } #endif /* Perform any processor-specific idle state operations */ up_idle(); } }
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 regiser 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-specify 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; }
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 vaue. 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; }
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 CONFIG_NUTTX_KERNEL svcdbg(" PSR: %08x PRIMASK: %08x EXC_RETURN: %08x\n", regs[REG_XPSR], regs[REG_PRIMASK], regs[REG_EXC_RETURN]); # else svcdbg(" PSR: %08x PRIMASK: %08x\n", regs[REG_XPSR], regs[REG_PRIMASK]); # 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); } 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); 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 CONFIG_NUTTX_KERNEL svcdbg(" PSR: %08x PRIMASK: %08x EXC_RETURN: %08x\n", current_regs[REG_XPSR], current_regs[REG_PRIMASK], current_regs[REG_EXC_RETURN]); #else svcdbg(" PSR: %08x PRIMASK: %08x\n", current_regs[REG_XPSR], current_regs[REG_PRIMASK]); #endif } # ifdef CONFIG_DEBUG_SVCALL else { svcdbg("SVCall Return: %d\n", regs[REG_R0]); } # endif #endif return OK; }
int up_swint0(int irq, FAR void *context) { uint32_t *regs = (uint32_t*)context; DEBUGASSERT(regs && regs == current_regs); /* Software interrupt 0 is invoked with REG_R4 = system call command and * REG_R5..R10 = variable number of arguments depending on the system call. */ #ifdef DEBUG_SWINT0 swidbg("Entry: regs: %p cmd: %d\n", regs, regs[REG_R4]); up_registerdump(regs); #endif /* Handle the SWInt according to the command in $4 */ switch (regs[REG_R4]) { /* R4=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: * * R4 = SYS_restore_context * R5 = 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_R5] != 0); current_regs = (uint32_t*)regs[REG_R5]; } break; /* R4=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: * * R4 = SYS_switch_context * R5 = saveregs * R6 = restoreregs * * In this case, we save the context registers to the save register * area reference by the saved contents of R5 and then set * current_regs to to the save register area referenced by the saved * contents of R6. */ case SYS_switch_context: { DEBUGASSERT(regs[REG_R5] != 0 && regs[REG_R6] != 0); memcpy((uint32_t*)regs[REG_R5], regs, XCPTCONTEXT_SIZE); current_regs = (uint32_t*)regs[REG_R6]; } break; /* This is not an architecture-specify 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 */ #ifdef DEBUG_SWINT0 if (regs != current_regs) { swidbg("SWInt Return: Context switch!\n"); up_registerdump(current_regs); } else { swidbg("SWInt Return: %d\n", regs[REG_R2]); } #endif return OK; }