int pthread_getschedparam(pthread_t thread, FAR int *policy, FAR struct sched_param *param) { int ret; svdbg("Thread ID=%d policy=0x%p param=0x%p\n", thread, policy, param); if (!policy || !param) { ret = EINVAL; } else { /* Get the schedparams of the thread. */ ret = sched_getparam((pid_t)thread, param); if (ret != OK) { ret = EINVAL; } /* Return the policy. */ *policy = sched_getscheduler((pid_t)thread); if (*policy == ERROR) { ret = get_errno(); } } svdbg("Returning %d\n", ret); return ret; }
int spawn_execattrs(pid_t pid, FAR const posix_spawnattr_t *attr) { struct sched_param param; DEBUGASSERT(attr); /* Now set the attributes. Note that we ignore all of the return values * here because we have already successfully started the task. If we * return an error value, then we would also have to stop the task. */ /* If we are only setting the priority, then call sched_setparm() * to set the priority of the of the new task. */ if ((attr->flags & POSIX_SPAWN_SETSCHEDPARAM) != 0) { /* Get the priority from the attrributes */ param.sched_priority = attr->priority; /* If we are setting *both* the priority and the scheduler, * then we will call sched_setscheduler() below. */ if ((attr->flags & POSIX_SPAWN_SETSCHEDULER) == 0) { svdbg("Setting priority=%d for pid=%d\n", param.sched_priority, pid); (void)sched_setparam(pid, ¶m); } } /* If we are only changing the scheduling policy, then reset * the priority to the default value (the same as this thread) in * preparation for the sched_setscheduler() call below. */ else if ((attr->flags & POSIX_SPAWN_SETSCHEDULER) != 0) { (void)sched_getparam(0, ¶m); } /* Are we setting the scheduling policy? If so, use the priority * setting determined above. */ if ((attr->flags & POSIX_SPAWN_SETSCHEDULER) != 0) { svdbg("Setting policy=%d priority=%d for pid=%d\n", attr->policy, param.sched_priority, pid); (void)sched_setscheduler(pid, attr->policy, ¶m); } return OK; }
void up_sigdeliver(void) { struct tcb_s *rtcb = this_task(); uint32_t regs[XCPTCONTEXT_REGS]; sig_deliver_t sigdeliver; /* Save the errno. This must be preserved throughout the signal handling * so that the user code final gets the correct errno value (probably * EINTR). */ int saved_errno = rtcb->pterrno; board_autoled_on(LED_SIGNAL); svdbg("rtcb=%p sigdeliver=%p sigpendactionq.head=%p\n", rtcb, rtcb->xcp.sigdeliver, rtcb->sigpendactionq.head); ASSERT(rtcb->xcp.sigdeliver != NULL); /* Save the real return state on the stack. */ up_copyfullstate(regs, rtcb->xcp.regs); regs[REG_PC] = rtcb->xcp.saved_pc; regs[REG_CPSR] = rtcb->xcp.saved_cpsr; /* Get a local copy of the sigdeliver function pointer. we do this so that * we can nullify the sigdeliver function pointer in the TCB and accept * more signal deliveries while processing the current pending signals. */ sigdeliver = rtcb->xcp.sigdeliver; rtcb->xcp.sigdeliver = NULL; /* Then restore the task interrupt state */ irqrestore(regs[REG_CPSR]); /* Deliver the signals */ sigdeliver(rtcb); /* Output any debug messages BEFORE restoring errno (because they may * alter errno), then disable interrupts again and restore the original * errno that is needed by the user logic (it is probably EINTR). */ svdbg("Resuming\n"); (void)irqsave(); rtcb->pterrno = saved_errno; /* Then restore the correct state for this thread of execution. */ board_autoled_off(LED_SIGNAL); #ifdef CONFIG_TASK_SCHED_HISTORY /* Save the task name which will be scheduled */ save_task_scheduling_status(rtcb); #endif up_fullcontextrestore(regs); }
int pthread_condattr_destroy(FAR pthread_condattr_t *attr) { int ret = OK; svdbg("attr=0x%p\n", attr); if (!attr) { ret = EINVAL; } svdbg("Returning %d\n", ret); return ret; }
int pthread_cond_broadcast(FAR pthread_cond_t *cond) { int ret = OK; int sval; svdbg("cond=0x%p\n", cond); if (!cond) { ret = EINVAL; } else { /* Disable pre-emption until all of the waiting threads have been * restarted. This is necessary to assure that the sval behaves as * expected in the following while loop */ sched_lock(); /* Get the current value of the semaphore */ if (sem_getvalue((sem_t *)&cond->sem, &sval) != OK) { ret = EINVAL; } else { /* Loop until all of the waiting threads have been restarted. */ while (sval < 0) { /* If the value is less than zero (meaning that one or more * thread is waiting), then post the condition semaphore. * Only the highest priority waiting thread will get to execute */ ret = pthread_sem_give((sem_t *)&cond->sem); /* Increment the semaphore count (as was done by the * above post). */ sval++; } } /* Now we can let the restarted threads run */ sched_unlock(); } svdbg("Returning %d\n", ret); return ret; }
int pthread_attr_getinheritsched(FAR const pthread_attr_t *attr, FAR int *inheritsched) { int ret; svdbg("attr=0x%p inheritsched=0x%p\n", attr, inheritsched); if (!attr || !inheritsched) { ret = EINVAL; } else { *inheritsched = (int)attr->inheritsched; ret = OK; } svdbg("Returning %d\n", ret); return ret; }
static inline int spawn_close(FAR struct spawn_close_file_action_s *action) { /* The return value from close() is ignored */ svdbg("Closing fd=%d\n", action->fd); (void)close(action->fd); return OK; }
int pthread_attr_init(FAR pthread_attr_t *attr) { int ret = OK; svdbg("attr=0x%p\n", attr); if (!attr) { ret = ENOMEM; } else { /* Set the child thread priority to be the default * priority. Set the child stack size to some arbitrary * default value. */ memcpy(attr, &g_default_pthread_attr, sizeof(pthread_attr_t)); } svdbg("Returning %d\n", ret); return ret; }
int pthread_cond_destroy(FAR pthread_cond_t *cond) { int ret = OK; svdbg("cond=0x%p\n", cond); if (!cond) { ret = EINVAL; } /* Destroy the semaphore contained in the structure */ else if (sem_destroy((sem_t *)&cond->sem) != OK) { ret = EINVAL; } svdbg("Returning %d\n", ret); return ret; }
int pthread_attr_setschedpolicy(FAR pthread_attr_t *attr, int policy) { int ret; svdbg("attr=0x%p policy=%d\n", attr, policy); #if CONFIG_RR_INTERVAL > 0 if (!attr || (policy != SCHED_FIFO && policy != SCHED_RR)) #else if (!attr || policy != SCHED_FIFO) #endif { ret = EINVAL; } else { attr->policy = policy; ret = OK; } svdbg("Returning %d\n", ret); return ret; }
static inline int spawn_open(FAR struct spawn_open_file_action_s *action) { int fd; int ret = OK; /* Open the file */ svdbg("Open'ing path=%s oflags=%04x mode=%04x\n", action->path, action->oflags, action->mode); fd = open(action->path, action->oflags, action->mode); if (fd < 0) { ret = get_errno(); sdbg("ERROR: open failed: %d\n", ret); } /* Does the return file descriptor happen to match the required file * desciptor number? */ else if (fd != action->fd) { /* No.. dup2 to get the correct file number */ svdbg("Dup'ing %d->%d\n", fd, action->fd); ret = dup2(fd, action->fd); if (ret < 0) { ret = get_errno(); sdbg("ERROR: dup2 failed: %d\n", ret); } svdbg("Closing fd=%d\n", fd); close(fd); } return ret; }
int mod_load(FAR struct mod_loadinfo_s *loadinfo) { int ret; svdbg("loadinfo: %p\n", loadinfo); DEBUGASSERT(loadinfo && loadinfo->filfd >= 0); /* Load section headers into memory */ ret = mod_loadshdrs(loadinfo); if (ret < 0) { sdbg("ERROR: mod_loadshdrs failed: %d\n", ret); goto errout_with_buffers; } /* Determine total size to allocate */ mod_elfsize(loadinfo); /* Allocate (and zero) memory for the ELF file. */ /* Allocate memory to hold the ELF image */ loadinfo->textalloc = (uintptr_t)kmm_zalloc(loadinfo->textsize + loadinfo->datasize); if (!loadinfo->textalloc) { sdbg("ERROR: Failed to allocate memory for the module\n"); ret = -ENOMEM; goto errout_with_buffers; } loadinfo->datastart = loadinfo->textalloc + loadinfo->textsize; /* Load ELF section data into memory */ ret = mod_loadfile(loadinfo); if (ret < 0) { sdbg("ERROR: mod_loadfile failed: %d\n", ret); goto errout_with_buffers; } return OK; /* Error exits */ errout_with_buffers: mod_unload(loadinfo); return ret; }
int pthread_completejoin(pid_t pid, FAR void *exit_value) { FAR join_t *pjoin; svdbg("pid=%d exit_value=%p\n", pid, exit_value); /* First, find thread's structure in the private data set. */ (void)pthread_takesemaphore(&g_join_semaphore); pjoin = pthread_findjoininfo(pid); if (!pjoin) { sdbg("Could not find join info, pid=%d\n", pid); (void)pthread_givesemaphore(&g_join_semaphore); return ERROR; } else { bool waiters; /* Save the return exit value in the thread structure. */ pjoin->terminated = true; pjoin->exit_value = exit_value; /* Notify waiters of the availability of the exit value */ waiters = pthread_notifywaiters(pjoin); /* If there are no waiters and if the thread is marked as detached. * then discard the join information now. Otherwise, the pthread * join logic will call pthread_destroyjoin() when all of the threads * have sampled the exit value. */ if (!waiters && pjoin->detached) { pthread_destroyjoin(pjoin); } /* Giving the following semaphore will allow the waiters * to call pthread_destroyjoin. */ (void)pthread_givesemaphore(&g_join_semaphore); } return OK; }
int work_lpstart(void) { pid_t pid; int wndx; /* Initialize work queue data structures */ memset(&g_lpwork, 0, sizeof(struct kwork_wqueue_s)); g_lpwork.delay = CONFIG_SCHED_LPWORKPERIOD / USEC_PER_TICK; dq_init(&g_lpwork.q); /* Don't permit any of the threads to run until we have fully initialized * g_lpwork. */ sched_lock(); /* Start the low-priority, kernel mode worker thread(s) */ svdbg("Starting low-priority kernel worker thread(s)\n"); for (wndx = 0; wndx < CONFIG_SCHED_LPNTHREADS; wndx++) { pid = kernel_thread(LPWORKNAME, CONFIG_SCHED_LPWORKPRIORITY, CONFIG_SCHED_LPWORKSTACKSIZE, (main_t)work_lpthread, (FAR char * const *)NULL); DEBUGASSERT(pid > 0); if (pid < 0) { int errcode = errno; DEBUGASSERT(errcode > 0); slldbg("kernel_thread %d failed: %d\n", wndx, errcode); sched_unlock(); return -errcode; } g_lpwork.worker[wndx].pid = pid; g_lpwork.worker[wndx].busy = true; } sched_unlock(); return g_lpwork.worker[0].pid; }
int pthread_setschedparam(pthread_t thread, int policy, FAR const struct sched_param *param) { int ret; svdbg("thread ID=%d policy=%d param=0x%p\n", thread, policy, param); /* Let sched_setscheduler do all of the work */ ret = sched_setscheduler((pid_t)thread, policy, param); if (ret != OK) { /* If sched_setscheduler() fails, return the errno */ ret = get_errno(); } return ret; }
static inline int spawn_dup2(FAR struct spawn_dup2_file_action_s *action) { int ret; /* Perform the dup */ svdbg("Dup'ing %d->%d\n", action->fd1, action->fd2); ret = dup2(action->fd1, action->fd2); if (ret < 0) { int errcode = get_errno(); sdbg("ERROR: dup2 failed: %d\n", errcode); return -errcode; } return OK; }
static bool pthread_notifywaiters(FAR join_t *pjoin) { int ntasks_waiting; int status; svdbg("pjoin=0x%p\n", pjoin); /* Are any tasks waiting for our exit value? */ status = sem_getvalue(&pjoin->exit_sem, &ntasks_waiting); if (status == OK && ntasks_waiting < 0) { /* Set the data semaphore so that this thread will be * awakened when all waiting tasks receive the data */ (void)sem_init(&pjoin->data_sem, 0, (ntasks_waiting+1)); /* Post the semaphore to restart each thread that is waiting * on the semaphore */ do { status = pthread_givesemaphore(&pjoin->exit_sem); if (status == OK) { status = sem_getvalue(&pjoin->exit_sem, &ntasks_waiting); } } while (ntasks_waiting < 0 && status == OK); /* Now wait for all these restarted tasks to obtain the return * value. */ (void)pthread_takesemaphore(&pjoin->data_sem); return true; } return false; }
int work_usrstart(void) { /* Start a user-mode worker thread for use by applications. */ svdbg("Starting user-mode worker thread\n"); g_usrwork[USRWORK].pid = task_create("usrwork", CONFIG_SCHED_USRWORKPRIORITY, CONFIG_SCHED_USRWORKSTACKSIZE, (main_t)work_usrthread, (FAR char * const *)NULL); DEBUGASSERT(g_usrwork[USRWORK].pid > 0); if (g_usrwork[USRWORK].pid < 0) { int errcode = errno; DEBUGASSERT(errcode > 0); sdbg("task_create failed: %d\n", errcode); return -errcode; } return g_usrwork[USRWORK].pid; }
int pthread_mutex_destroy(FAR pthread_mutex_t *mutex) { struct tcb_s *ptcb; struct join_s *pjoin = NULL; int ret = EINVAL; int status; svdbg("mutex=0x%p\n", mutex); DEBUGASSERT(mutex != NULL); if (mutex != NULL) { /* Make sure the semaphore is stable while we make the following checks */ sched_lock(); /* Is the semaphore available? */ if (mutex->pid >= 0) { DEBUGASSERT(mutex->pid != 0); /* < 0: available, >0 owned, ==0 error */ /* No.. Verify that the PID still exists. We may be destroying * the mutex after cancelling a pthread and the mutex may have * been in a bad state owned by the dead pthread. NOTE: The * following behavior is unspecified for pthread_mutex_destroy() * (see pthread_mutex_consistent()). * * If the holding thread is still valid, then we should be able to * map its PID to the underlying TCB. That is what sched_gettcb() * does. */ ptcb = sched_gettcb(mutex->pid); if (ptcb && ((ptcb->flags & TCB_FLAG_TTYPE_MASK) == TCB_FLAG_TTYPE_PTHREAD)) { pjoin = pthread_findjoininfo(ptcb->group, ptcb->pid); } /* In some cases, pthread_mutex_destroy can be executed on middle of * exiting pthread which holds mutex. The sched_releasepid() updates * g_pidhash list to NULL by calling _exit() from pthread_exit(). * But, before calling it, pthread_exit() calls pthread_completejoin(). * It gives semaphore of pthread_join before executing _exit(). * It might cause user call pthread_mutex_destroy before updating scheduler. * Checking pjoin structure and terminated element helps to check * whether pthread which holds mutex is exiting or not. */ if (ptcb == NULL || \ (((ptcb->flags & TCB_FLAG_TTYPE_MASK) == TCB_FLAG_TTYPE_PTHREAD) && (pjoin == NULL || pjoin->terminated == true))) { /* The thread associated with the PID no longer exists */ mutex->pid = -1; /* Reset the semaphore. If threads are were on this * semaphore, then this will awakened them and make * destruction of the semaphore impossible here. */ status = sem_reset((FAR sem_t *)&mutex->sem, 1); if (status < 0) { ret = -status; } /* Check if the reset caused some other thread to lock the * mutex. */ else if (mutex->pid != -1) { /* Yes.. then we cannot destroy the mutex now. */ ret = EBUSY; } /* Destroy the underlying semaphore */ else { status = sem_destroy((FAR sem_t *)&mutex->sem); ret = (status != OK) ? get_errno() : OK; } } else { ret = EBUSY; } } else { /* Destroy the semaphore * * REVISIT: What if there are threads waiting on the semaphore? * Perhaps this logic should all sem_reset() first? */ status = sem_destroy((sem_t *)&mutex->sem); ret = ((status != OK) ? get_errno() : OK); } sched_unlock(); } svdbg("Returning %d\n", ret); return ret; }
pid_t up_vfork(const struct vfork_s *context) { struct tcb_s *parent = (FAR struct tcb_s *)g_readytorun.head; struct task_tcb_s *child; size_t stacksize; uint32_t newsp; uint32_t newfp; uint32_t stackutil; int ret; svdbg("vfork context [%p]:\n", context); svdbg(" r4:%08x r5:%08x r6:%08x r7:%08x\n", context->r4, context->r5, context->r6, context->r7); svdbg(" r8:%08x r9:%08x r10:%08x\n", context->r8, context->r9, context->r10); svdbg(" fp:%08x sp:%08x lr:%08x\n", context->fp, context->sp, context->lr); /* Allocate and initialize a TCB for the child task. */ child = task_vforksetup((start_t)(context->lr & ~1)); if (!child) { sdbg("ERROR: task_vforksetup failed\n"); return (pid_t)ERROR; } svdbg("TCBs: Parent=%p Child=%p\n", parent, child); /* Get the size of the parent task's stack. Due to alignment operations, * the adjusted stack size may be smaller than the stack size originally * requested. */ stacksize = parent->adj_stack_size + CONFIG_STACK_ALIGNMENT - 1; /* Allocate the stack for the TCB */ ret = up_create_stack((FAR struct tcb_s *)child, stacksize, parent->flags & TCB_FLAG_TTYPE_MASK); if (ret != OK) { sdbg("ERROR: up_create_stack failed: %d\n", ret); task_vforkabort(child, -ret); return (pid_t)ERROR; } /* How much of the parent's stack was utilized? The ARM uses * a push-down stack so that the current stack pointer should * be lower than the initial, adjusted stack pointer. The * stack usage should be the difference between those two. */ DEBUGASSERT((uint32_t)parent->adj_stack_ptr > context->sp); stackutil = (uint32_t)parent->adj_stack_ptr - context->sp; svdbg("Parent: stacksize:%d stackutil:%d\n", stacksize, stackutil); /* Make some feeble effort to preserve the stack contents. This is * feeble because the stack surely contains invalid pointers and other * content that will not work in the child context. However, if the * user follows all of the caveats of vfork() usage, even this feeble * effort is overkill. */ newsp = (uint32_t)child->cmn.adj_stack_ptr - stackutil; memcpy((void *)newsp, (const void *)context->sp, stackutil); /* Was there a frame pointer in place before? */ if (context->fp <= (uint32_t)parent->adj_stack_ptr && context->fp >= (uint32_t)parent->adj_stack_ptr - stacksize) { uint32_t frameutil = (uint32_t)parent->adj_stack_ptr - context->fp; newfp = (uint32_t)child->cmn.adj_stack_ptr - frameutil; } else { newfp = context->fp; } svdbg("Parent: stack base:%08x SP:%08x FP:%08x\n", parent->adj_stack_ptr, context->sp, context->fp); svdbg("Child: stack base:%08x SP:%08x FP:%08x\n", child->cmn.adj_stack_ptr, newsp, newfp); /* Update the stack pointer, frame pointer, and volatile registers. When * the child TCB was initialized, all of the values were set to zero. * up_initial_state() altered a few values, but the return value in R0 * should be cleared to zero, providing the indication to the newly started * child thread. */ child->cmn.xcp.regs[REG_R4] = context->r4; /* Volatile register r4 */ child->cmn.xcp.regs[REG_R5] = context->r5; /* Volatile register r5 */ child->cmn.xcp.regs[REG_R6] = context->r6; /* Volatile register r6 */ child->cmn.xcp.regs[REG_R7] = context->r7; /* Volatile register r7 */ child->cmn.xcp.regs[REG_R8] = context->r8; /* Volatile register r8 */ child->cmn.xcp.regs[REG_R9] = context->r9; /* Volatile register r9 */ child->cmn.xcp.regs[REG_R10] = context->r10; /* Volatile register r10 */ child->cmn.xcp.regs[REG_FP] = newfp; /* Frame pointer */ child->cmn.xcp.regs[REG_SP] = newsp; /* Stack pointer */ #ifdef CONFIG_LIB_SYSCALL /* If we got here via a syscall, then we are going to have to setup some * syscall return information as well. */ if (parent->xcp.nsyscalls > 0) { int index; for (index = 0; index < parent->xcp.nsyscalls; index++) { child->cmn.xcp.syscall[index].sysreturn = parent->xcp.syscall[index].sysreturn; /* REVISIT: This logic is *not* common. */ #if defined(CONFIG_ARCH_CORTEXA5) || defined(CONFIG_ARCH_CORTEXA8) # ifdef CONFIG_BUILD_KERNEL child->cmn.xcp.syscall[index].cpsr = parent->xcp.syscall[index].cpsr; # endif #elif defined(CONFIG_ARCH_CORTEXM3) || defined(CONFIG_ARCH_CORTEXM4) || \ defined(CONFIG_ARCH_CORTEXM0) || defined(CONFIG_ARCH_CORTEXM7) child->cmn.xcp.syscall[index].excreturn = parent->xcp.syscall[index].excreturn; #else # error Missing logic #endif } child->cmn.xcp.nsyscalls = parent->xcp.nsyscalls; } #endif /* And, finally, start the child task. On a failure, task_vforkstart() * will discard the TCB by calling task_vforkabort(). */ return task_vforkstart(child); }
void up_sigdeliver(void) { struct tcb_s *rtcb = (struct tcb_s*)g_readytorun.head; uint32_t regs[XCPTCONTEXT_REGS]; sig_deliver_t sigdeliver; /* Save the errno. This must be preserved throughout the signal handling * so that the user code final gets the correct errno value (probably * EINTR). */ int saved_errno = rtcb->pterrno; up_ledon(LED_SIGNAL); sdbg("rtcb=%p sigdeliver=%p sigpendactionq.head=%p\n", rtcb, rtcb->xcp.sigdeliver, rtcb->sigpendactionq.head); ASSERT(rtcb->xcp.sigdeliver != NULL); /* Save the real return state on the stack. */ up_copystate(regs, rtcb->xcp.regs); regs[REG_EPC] = rtcb->xcp.saved_epc; regs[REG_STATUS] = rtcb->xcp.saved_status; /* Get a local copy of the sigdeliver function pointer. We do this so that * we can nullify the sigdeliver function pointer in the TCB and accept * more signal deliveries while processing the current pending signals. */ sigdeliver = rtcb->xcp.sigdeliver; rtcb->xcp.sigdeliver = NULL; /* Then restore the task interrupt state */ irqrestore((irqstate_t)regs[REG_STATUS]); /* Deliver the signals */ sigdeliver(rtcb); /* Output any debug messages BEFORE restoring errno (because they may * alter errno), then disable interrupts again and restore the original * errno that is needed by the user logic (it is probably EINTR). */ svdbg("Resuming EPC: %08x STATUS: %08x\n", regs[REG_EPC], regs[REG_STATUS]); (void)irqsave(); rtcb->pterrno = saved_errno; /* Then restore the correct state for this thread of * execution. */ up_ledoff(LED_SIGNAL); up_fullcontextrestore(regs); /* up_fullcontextrestore() should not return but could if the software * interrupts are disabled. */ PANIC(); }
int posix_spawn(FAR pid_t *pid, FAR const char *path, FAR const posix_spawn_file_actions_t *file_actions, FAR const posix_spawnattr_t *attr, FAR char *const argv[], FAR char *const envp[]) #endif { struct sched_param param; pid_t proxy; #ifdef CONFIG_SCHED_WAITPID int status; #endif int ret; DEBUGASSERT(path); svdbg("pid=%p path=%s file_actions=%p attr=%p argv=%p\n", pid, path, file_actions, attr, argv); /* If there are no file actions to be performed and there is no change to * the signal mask, then start the new child task directly from the parent task. */ #ifndef CONFIG_DISABLE_SIGNALS if ((file_actions == NULL || *file_actions == NULL) && (attr == NULL || (attr->flags & POSIX_SPAWN_SETSIGMASK) == 0)) #else if (file_actions == NULL || *file_actions == NULL) #endif { return posix_spawn_exec(pid, path, attr, argv); } /* Otherwise, we will have to go through an intermediary/proxy task in order * to perform the I/O redirection. This would be a natural place to fork(). * However, true fork() behavior requires an MMU and most implementations * of vfork() are not capable of these operations. * * Even without fork(), we can still do the job, but parameter passing is * messier. Unfortunately, there is no (clean) way to pass binary values * as a task parameter, so we will use a semaphore-protected global * structure. */ /* Get exclusive access to the global parameter structure */ spawn_semtake(&g_spawn_parmsem); /* Populate the parameter structure */ g_spawn_parms.result = ENOSYS; g_spawn_parms.pid = pid; g_spawn_parms.file_actions = file_actions ? *file_actions : NULL; g_spawn_parms.attr = attr; g_spawn_parms.argv = argv; g_spawn_parms.u.posix.path = path; /* Get the priority of this (parent) task */ ret = sched_getparam(0, ¶m); if (ret < 0) { int errcode = get_errno(); sdbg("ERROR: sched_getparam failed: %d\n", errcode); spawn_semgive(&g_spawn_parmsem); return errcode; } /* Disable pre-emption so that the proxy does not run until waitpid * is called. This is probably unnecessary since the posix_spawn_proxy has * the same priority as this thread; it should be schedule behind this * task in the ready-to-run list. */ #ifdef CONFIG_SCHED_WAITPID sched_lock(); #endif /* Start the intermediary/proxy task at the same priority as the parent * task. */ proxy = kernel_thread("posix_spawn_proxy", param.sched_priority, CONFIG_POSIX_SPAWN_PROXY_STACKSIZE, (main_t)posix_spawn_proxy, (FAR char * const *)NULL); if (proxy < 0) { ret = get_errno(); sdbg("ERROR: Failed to start posix_spawn_proxy: %d\n", ret); goto errout_with_lock; } /* Wait for the proxy to complete its job */ #ifdef CONFIG_SCHED_WAITPID ret = waitpid(proxy, &status, 0); if (ret < 0) { sdbg("ERROR: waitpid() failed: %d\n", errno); goto errout_with_lock; } #else spawn_semtake(&g_spawn_execsem); #endif /* Get the result and relinquish our access to the parameter structure */ ret = g_spawn_parms.result; errout_with_lock: #ifdef CONFIG_SCHED_WAITPID sched_unlock(); #endif spawn_semgive(&g_spawn_parmsem); return ret; }
static int spawn_exec(FAR pid_t *pidp, FAR const char *path, FAR const posix_spawnattr_t *attr, FAR char *const argv[]) { struct sched_param param; FAR const struct symtab_s *symtab; int nsymbols; int pid; int ret = OK; DEBUGASSERT(path); /* Get the current symbol table selection */ exec_getsymtab(&symtab, &nsymbols); /* Disable pre-emption so that we can modify the task parameters after * we start the new task; the new task will not actually begin execution * until we re-enable pre-emption. */ sched_lock(); /* Start the task */ pid = exec(path, (FAR const char **)argv, symtab, nsymbols); if (pid < 0) { ret = errno; sdbg("ERROR: exec failed: %d\n", ret); goto errout; } /* Return the task ID to the caller */ if (pid) { *pidp = pid; } /* Now set the attributes. Note that we ignore all of the return values * here because we have already successfully started the task. If we * return an error value, then we would also have to stop the task. */ if (attr) { /* If we are only setting the priority, then call sched_setparm() * to set the priority of the of the new task. */ if ((attr->flags & POSIX_SPAWN_SETSCHEDPARAM) != 0) { /* Get the priority from the attrributes */ param.sched_priority = attr->priority; /* If we are setting *both* the priority and the scheduler, * then we will call sched_setscheduler() below. */ if ((attr->flags & POSIX_SPAWN_SETSCHEDULER) == 0) { svdbg("Setting priority=%d for pid=%d\n", param.sched_priority, pid); (void)sched_setparam(pid, ¶m); } } /* If we are only changing the scheduling policy, then reset * the priority to the default value (the same as this thread) in * preparation for the sched_setscheduler() call below. */ else if ((attr->flags & POSIX_SPAWN_SETSCHEDULER) != 0) { (void)sched_getparam(0, ¶m); } /* Are we setting the scheduling policy? If so, use the priority * setting determined above. */ if ((attr->flags & POSIX_SPAWN_SETSCHEDULER) != 0) { svdbg("Setting policy=%d priority=%d for pid=%d\n", attr->policy, param.sched_priority, pid); (void)sched_setscheduler(pid, attr->policy, ¶m); } } /* Re-enable pre-emption and return */ errout: sched_unlock(); return ret; }
int spawn_execattrs(pid_t pid, FAR const posix_spawnattr_t *attr) { struct sched_param param; DEBUGASSERT(attr); /* Now set the attributes. Note that we ignore all of the return values * here because we have already successfully started the task. If we * return an error value, then we would also have to stop the task. */ /* If we are only setting the priority, then call sched_setparm() * to set the priority of the of the new task. */ if ((attr->flags & POSIX_SPAWN_SETSCHEDPARAM) != 0) { #ifdef CONFIG_SCHED_SPORADIC int ret; /* Get the current sporadic scheduling parameters. Those will not be * modified. */ ret = sched_getparam(pid, ¶m); if (ret < 0) { int errcode = get_errno(); return -errcode; } #endif /* Get the priority from the attributes */ param.sched_priority = attr->priority; /* If we are setting *both* the priority and the scheduler, * then we will call sched_setscheduler() below. */ if ((attr->flags & POSIX_SPAWN_SETSCHEDULER) == 0) { svdbg("Setting priority=%d for pid=%d\n", param.sched_priority, pid); (void)sched_setparam(pid, ¶m); } } /* If we are only changing the scheduling policy, then reset * the priority to the default value (the same as this thread) in * preparation for the sched_setscheduler() call below. */ else if ((attr->flags & POSIX_SPAWN_SETSCHEDULER) != 0) { (void)sched_getparam(0, ¶m); } /* Are we setting the scheduling policy? If so, use the priority * setting determined above. */ if ((attr->flags & POSIX_SPAWN_SETSCHEDULER) != 0) { svdbg("Setting policy=%d priority=%d for pid=%d\n", attr->policy, param.sched_priority, pid); #ifdef CONFIG_SCHED_SPORADIC /* But take the sporadic scheduler parameters from the attributes */ param.sched_ss_low_priority = (int)attr->low_priority; param.sched_ss_max_repl = (int)attr->max_repl; param.sched_ss_repl_period.tv_sec = attr->repl_period.tv_sec; param.sched_ss_repl_period.tv_nsec = attr->repl_period.tv_nsec; param.sched_ss_init_budget.tv_sec = attr->budget.tv_sec; param.sched_ss_init_budget.tv_nsec = attr->budget.tv_nsec; #endif (void)sched_setscheduler(pid, attr->policy, ¶m); } return OK; }
pid_t up_vfork(const struct vfork_s *context) { struct tcb_s *parent = (FAR struct tcb_s *)g_readytorun.head; struct task_tcb_s *child; size_t stacksize; uint32_t newsp; #if CONFIG_MIPS32_FRAMEPOINTER uint32_t newfp; #endif uint32_t stackutil; int ret; svdbg("s0:%08x s1:%08x s2:%08x s3:%08x s4:%08x\n", context->s0, context->s1, context->s2, context->s3, context->s4); #if CONFIG_MIPS32_FRAMEPOINTER svdbg("s5:%08x s6:%08x s7:%08x\n", context->s5, context->s6, context->s7); #ifdef MIPS32_SAVE_GP svdbg("fp:%08x sp:%08x ra:%08x gp:%08x\n", context->fp, context->sp, context->ra, context->gp); #else svdbg("fp:%08x sp:%08x ra:%08x\n", context->fp context->sp, context->ra); #endif #else svdbg("s5:%08x s6:%08x s7:%08x s8:%08x\n", context->s5, context->s6, context->s7, context->s8); #ifdef MIPS32_SAVE_GP svdbg("sp:%08x ra:%08x gp:%08x\n", context->sp, context->ra, context->gp); #else svdbg("sp:%08x ra:%08x\n", context->sp, context->ra); #endif #endif /* Allocate and initialize a TCB for the child task. */ child = task_vforksetup((start_t)context->ra); if (!child) { sdbg("task_vforksetup failed\n"); return (pid_t)ERROR; } svdbg("Parent=%p Child=%p\n", parent, child); /* Get the size of the parent task's stack. Due to alignment operations, * the adjusted stack size may be smaller than the stack size originally * requrested. */ stacksize = parent->adj_stack_size + CONFIG_STACK_ALIGNMENT - 1; /* Allocate the stack for the TCB */ ret = up_create_stack((FAR struct tcb_s *)child, stacksize, parent->flags & TCB_FLAG_TTYPE_MASK); if (ret != OK) { sdbg("up_create_stack failed: %d\n", ret); task_vforkabort(child, -ret); return (pid_t)ERROR; } /* How much of the parent's stack was utilized? The MIPS uses * a push-down stack so that the current stack pointer should * be lower than the initial, adjusted stack pointer. The * stack usage should be the difference between those two. */ DEBUGASSERT((uint32_t)parent->adj_stack_ptr > context->sp); stackutil = (uint32_t)parent->adj_stack_ptr - context->sp; svdbg("stacksize:%d stackutil:%d\n", stacksize, stackutil); /* Make some feeble effort to perserve the stack contents. This is * feeble because the stack surely contains invalid pointers and other * content that will not work in the child context. However, if the * user follows all of the caveats of vfork() usage, even this feeble * effort is overkill. */ newsp = (uint32_t)child->cmn.adj_stack_ptr - stackutil; memcpy((void *)newsp, (const void *)context->sp, stackutil); /* Was there a frame pointer in place before? */ #if CONFIG_MIPS32_FRAMEPOINTER if (context->fp <= (uint32_t)parent->adj_stack_ptr && context->fp >= (uint32_t)parent->adj_stack_ptr - stacksize) { uint32_t frameutil = (uint32_t)parent->adj_stack_ptr - context->fp; newfp = (uint32_t)child->cmn.adj_stack_ptr - frameutil; } else { newfp = context->fp; } svdbg("Old stack base:%08x SP:%08x FP:%08x\n", parent->adj_stack_ptr, context->sp, context->fp); svdbg("New stack base:%08x SP:%08x FP:%08x\n", child->cmn.adj_stack_ptr, newsp, newfp); #else svdbg("Old stack base:%08x SP:%08x\n", parent->adj_stack_ptr, context->sp); svdbg("New stack base:%08x SP:%08x\n", child->cmn.adj_stack_ptr, newsp); #endif /* Update the stack pointer, frame pointer, global pointer and saved * registers. When the child TCB was initialized, all of the values * were set to zero. up_initial_state() altered a few values, but the * return value in v0 should be cleared to zero, providing the * indication to the newly started child thread. */ child->cmn.xcp.regs[REG_S0] = context->s0; /* Saved register s0 */ child->cmn.xcp.regs[REG_S1] = context->s1; /* Saved register s1 */ child->cmn.xcp.regs[REG_S2] = context->s2; /* Saved register s2 */ child->cmn.xcp.regs[REG_S3] = context->s3; /* Volatile register s3 */ child->cmn.xcp.regs[REG_S4] = context->s4; /* Volatile register s4 */ child->cmn.xcp.regs[REG_S5] = context->s5; /* Volatile register s5 */ child->cmn.xcp.regs[REG_S6] = context->s6; /* Volatile register s6 */ child->cmn.xcp.regs[REG_S7] = context->s7; /* Volatile register s7 */ #if CONFIG_MIPS32_FRAMEPOINTER child->cmn.xcp.regs[REG_FP] = newfp; /* Frame pointer */ #else child->cmn.xcp.regs[REG_S8] = context->s8; /* Volatile register s8 */ #endif child->cmn.xcp.regs[REG_SP] = newsp; /* Stack pointer */ #if MIPS32_SAVE_GP child->cmn.xcp.regs[REG_GP] = newsp; /* Global pointer */ #endif /* And, finally, start the child task. On a failure, task_vforkstart() * will discard the TCB by calling task_vforkabort(). */ return task_vforkstart(child); }
int os_bringup(void) { int taskid; /* Setup up the initial environment for the idle task. At present, this * may consist of only the initial PATH variable. The PATH variable is * (probably) not used by the IDLE task. However, the environment * containing the PATH variable will be inherited by all of the threads * created by the IDLE task. */ #if !defined(CONFIG_DISABLE_ENVIRON) && defined(CONFIG_PATH_INITIAL) (void)setenv("PATH", CONFIG_PATH_INITIAL, 1); #endif /* Start the page fill worker kernel thread that will resolve page faults. * This should always be the first thread started because it may have to * resolve page faults in other threads */ #ifdef CONFIG_PAGING svdbg("Starting paging thread\n"); g_pgworker = KERNEL_THREAD("pgfill", CONFIG_PAGING_DEFPRIO, CONFIG_PAGING_STACKSIZE, (main_t)pg_worker, (FAR char * const *)NULL); DEBUGASSERT(g_pgworker > 0); #endif /* Start the worker thread that will serve as the device driver "bottom- * half" and will perform misc garbage clean-up. */ #ifdef CONFIG_SCHED_WORKQUEUE #ifdef CONFIG_SCHED_HPWORK #ifdef CONFIG_SCHED_LPWORK svdbg("Starting high-priority kernel worker thread\n"); #else svdbg("Starting kernel worker thread\n"); #endif g_work[HPWORK].pid = KERNEL_THREAD(HPWORKNAME, CONFIG_SCHED_WORKPRIORITY, CONFIG_SCHED_WORKSTACKSIZE, (main_t)work_hpthread, (FAR char * const *)NULL); DEBUGASSERT(g_work[HPWORK].pid > 0); /* Start a lower priority worker thread for other, non-critical continuation * tasks */ #ifdef CONFIG_SCHED_LPWORK svdbg("Starting low-priority kernel worker thread\n"); g_work[LPWORK].pid = KERNEL_THREAD(LPWORKNAME, CONFIG_SCHED_LPWORKPRIORITY, CONFIG_SCHED_LPWORKSTACKSIZE, (main_t)work_lpthread, (FAR char * const *)NULL); DEBUGASSERT(g_work[LPWORK].pid > 0); #endif /* CONFIG_SCHED_LPWORK */ #endif /* CONFIG_SCHED_HPWORK */ #if defined(CONFIG_NUTTX_KERNEL) && defined(CONFIG_SCHED_USRWORK) /* Start the user-space work queue */ DEBUGASSERT(USERSPACE->work_usrstart != NULL); taskid = USERSPACE->work_usrstart(); DEBUGASSERT(taskid > 0); #endif #endif /* CONFIG_SCHED_WORKQUEUE */ /* Once the operating system has been initialized, the system must be * started by spawning the user init thread of execution. This is the * first user-mode thead. */ svdbg("Starting init thread\n"); /* Perform any last-minute, board-specific initialization, if so * configured. */ #ifdef CONFIG_BOARD_INITIALIZE board_initialize(); #endif /* Start the default application. In a flat build, this is entrypoint * is given by the definitions, CONFIG_USER_ENTRYPOINT. In the kernel * build, however, we must get the address of the entrypoint from the * header at the beginning of the user-space blob. */ #ifdef CONFIG_NUTTX_KERNEL DEBUGASSERT(USERSPACE->us_entrypoint != NULL); taskid = TASK_CREATE("init", SCHED_PRIORITY_DEFAULT, CONFIG_USERMAIN_STACKSIZE, USERSPACE->us_entrypoint, (FAR char * const *)NULL); #else taskid = TASK_CREATE("init", SCHED_PRIORITY_DEFAULT, CONFIG_USERMAIN_STACKSIZE, (main_t)CONFIG_USER_ENTRYPOINT, (FAR char * const *)NULL); #endif ASSERT(taskid > 0); /* We an save a few bytes by discarding the IDLE thread's environment. */ #if !defined(CONFIG_DISABLE_ENVIRON) && defined(CONFIG_PATH_INITIAL) (void)clearenv(); #endif return OK; }
pid_t up_vfork(const struct vfork_s *context) { _TCB *parent = (FAR _TCB *)g_readytorun.head; _TCB *child; size_t stacksize; uint32_t newsp; uint32_t newfp; uint32_t stackutil; int ret; svdbg("r4:%08x r5:%08x r6:%08x r7:%08x\n", context->r4, context->r5, context->r6, context->r7); svdbg("r8:%08x r9:%08x r10:%08x\n", context->r8, context->r9, context->r10); svdbg("fp:%08x sp:%08x lr:%08x\n", context->fp, context->sp, context->lr); /* Allocate and initialize a TCB for the child task. */ child = task_vforksetup((start_t)(context->lr & ~1)); if (!child) { sdbg("task_vforksetup failed\n"); return (pid_t)ERROR; } svdbg("Parent=%p Child=%p\n", parent, child); /* Get the size of the parent task's stack. Due to alignment operations, * the adjusted stack size may be smaller than the stack size originally * requrested. */ stacksize = parent->adj_stack_size + CONFIG_STACK_ALIGNMENT - 1; /* Allocate the stack for the TCB */ ret = up_create_stack(child, stacksize); if (ret != OK) { sdbg("up_create_stack failed: %d\n", ret); task_vforkabort(child, -ret); return (pid_t)ERROR; } /* How much of the parent's stack was utilized? The ARM uses * a push-down stack so that the current stack pointer should * be lower than the initial, adjusted stack pointer. The * stack usage should be the difference between those two. */ DEBUGASSERT((uint32_t)parent->adj_stack_ptr > context->sp); stackutil = (uint32_t)parent->adj_stack_ptr - context->sp; svdbg("stacksize:%d stackutil:%d\n", stacksize, stackutil); /* Make some feeble effort to perserve the stack contents. This is * feeble because the stack surely contains invalid pointers and other * content that will not work in the child context. However, if the * user follows all of the caveats of vfor() usage, even this feeble * effort is overkill. */ newsp = (uint32_t)child->adj_stack_ptr - stackutil; memcpy((void *)newsp, (const void *)context->sp, stackutil); /* Was there a frame pointer in place before? */ if (context->fp <= (uint32_t)parent->adj_stack_ptr && context->fp >= (uint32_t)parent->adj_stack_ptr - stacksize) { uint32_t frameutil = (uint32_t)parent->adj_stack_ptr - context->fp; newfp = (uint32_t)child->adj_stack_ptr - frameutil; } else { newfp = context->fp; } svdbg("Old stack base:%08x SP:%08x FP:%08x\n", parent->adj_stack_ptr, context->sp, context->fp); svdbg("New stack base:%08x SP:%08x FP:%08x\n", child->adj_stack_ptr, newsp, newfp); /* Update the stack pointer, frame pointer, and volatile registers. When * the child TCB was initialized, all of the values were set to zero. * up_initial_state() altered a few values, but the return value in R0 * should be cleared to zero, providing the indication to the newly started * child thread. */ child->xcp.regs[REG_R4] = context->r4; /* Volatile register r4 */ child->xcp.regs[REG_R5] = context->r5; /* Volatile register r5 */ child->xcp.regs[REG_R6] = context->r6; /* Volatile register r6 */ child->xcp.regs[REG_R7] = context->r7; /* Volatile register r7 */ child->xcp.regs[REG_R8] = context->r8; /* Volatile register r8 */ child->xcp.regs[REG_R9] = context->r9; /* Volatile register r9 */ child->xcp.regs[REG_R10] = context->r10; /* Volatile register r10 */ child->xcp.regs[REG_FP] = newfp; /* Frame pointer */ child->xcp.regs[REG_SP] = newsp; /* Stack pointer */ /* And, finally, start the child task. On a failure, task_vforkstart() * will discard the TCB by calling task_vforkabort(). */ return task_vforkstart(child); }
int pthread_mutex_trylock(FAR pthread_mutex_t *mutex) { int status; int ret = EINVAL; svdbg("mutex=0x%p\n", mutex); DEBUGASSERT(mutex != NULL); if (mutex != NULL) { int mypid = (int)getpid(); /* Make sure the semaphore is stable while we make the following * checks. This all needs to be one atomic action. */ sched_lock(); /* Try to get the semaphore. */ status = pthread_mutex_trytake(mutex); if (status == OK) { /* If we successfully obtained the semaphore, then indicate * that we own it. */ mutex->pid = mypid; #ifdef CONFIG_PTHREAD_MUTEX_TYPES if (mutex->type == PTHREAD_MUTEX_RECURSIVE) { mutex->nlocks = 1; } #endif ret = OK; } /* pthread_mutex_trytake failed. Did it fail because the semaphore * was not avaialable? */ else if (status == EAGAIN) { #ifdef CONFIG_PTHREAD_MUTEX_TYPES /* Check if recursive mutex was locked by the calling thread. */ if (mutex->type == PTHREAD_MUTEX_RECURSIVE && mutex->pid == mypid) { /* Increment the number of locks held and return successfully. */ if (mutex->nlocks < INT16_MAX) { mutex->nlocks++; ret = OK; } else { ret = EOVERFLOW; } } else #endif #ifndef CONFIG_PTHREAD_MUTEX_UNSAFE /* The calling thread does not hold the semaphore. The correct * behavior for the 'robust' mutex is to verify that the holder of * the mutex is still valid. This is protection from the case * where the holder of the mutex has exitted without unlocking it. */ #ifdef CONFIG_PTHREAD_MUTEX_BOTH #ifdef CONFIG_PTHREAD_MUTEX_TYPES /* Check if this NORMAL mutex is robust */ if (mutex->pid > 0 && ((mutex->flags & _PTHREAD_MFLAGS_ROBUST) != 0 || mutex->type != PTHREAD_MUTEX_NORMAL) && sched_gettcb(mutex->pid) == NULL) #else /* CONFIG_PTHREAD_MUTEX_TYPES */ /* Check if this NORMAL mutex is robust */ if (mutex->pid > 0 && (mutex->flags & _PTHREAD_MFLAGS_ROBUST) != 0 && sched_gettcb(mutex->pid) == NULL) #endif /* CONFIG_PTHREAD_MUTEX_TYPES */ #else /* CONFIG_PTHREAD_MUTEX_ROBUST */ /* This mutex is always robust, whatever type it is. */ if (mutex->pid > 0 && sched_gettcb(mutex->pid) == NULL) #endif { DEBUGASSERT(mutex->pid != 0); /* < 0: available, >0 owned, ==0 error */ DEBUGASSERT((mutex->flags & _PTHREAD_MFLAGS_INCONSISTENT) != 0); /* A thread holds the mutex, but there is no such thread. * POSIX requires that the 'robust' mutex return EOWNERDEAD * in this case. It is then the caller's responsibility to * call pthread_mutx_consistent() fo fix the mutex. */ mutex->flags |= _PTHREAD_MFLAGS_INCONSISTENT; ret = EOWNERDEAD; } /* The mutex is locked by another, active thread */ else #endif /* CONFIG_PTHREAD_MUTEX_UNSAFE */ { ret = EBUSY; } } /* Some other, unhandled error occurred */ else { ret = status; } sched_unlock(); } svdbg("Returning %d\n", ret); return ret; }
int os_bringup(void) { int init_taskid; /* Setup up the initial environment for the idle task. At present, this * may consist of only the initial PATH variable. The PATH variable is * (probably) not used by the IDLE task. However, the environment * containing the PATH variable will be inherited by all of the threads * created by the IDLE task. */ #if !defined(CONFIG_DISABLE_ENVIRON) && defined(CONFIG_PATH_INITIAL) (void)setenv("PATH", CONFIG_PATH_INITIAL, 1); #endif /* Start the page fill worker kernel thread that will resolve page faults. * This should always be the first thread started because it may have to * resolve page faults in other threads */ #ifdef CONFIG_PAGING svdbg("Starting paging thread\n"); g_pgworker = KERNEL_THREAD("pgfill", CONFIG_PAGING_DEFPRIO, CONFIG_PAGING_STACKSIZE, (main_t)pg_worker, (const char **)NULL); ASSERT(g_pgworker != ERROR); #endif /* Start the worker thread that will serve as the device driver "bottom- * half" and will perform misc garbage clean-up. */ #ifdef CONFIG_SCHED_WORKQUEUE svdbg("Starting worker thread\n"); g_work[HPWORK].pid = KERNEL_THREAD("work0", CONFIG_SCHED_WORKPRIORITY, CONFIG_SCHED_WORKSTACKSIZE, (main_t)work_hpthread, (const char **)NULL); ASSERT(g_work[HPWORK].pid != ERROR); /* Start a lower priority worker thread for other, non-critical continuation * tasks */ #ifdef CONFIG_SCHED_LPWORK svdbg("Starting worker thread\n"); g_work[LPWORK].pid = KERNEL_THREAD("work1", CONFIG_SCHED_LPWORKPRIORITY, CONFIG_SCHED_LPWORKSTACKSIZE, (main_t)work_lpthread, (const char **)NULL); ASSERT(g_work[LPWORK].pid != ERROR); #endif #endif /* Once the operating system has been initialized, the system must be * started by spawning the user init thread of execution. This is the * first user-mode thead. */ svdbg("Starting init thread\n"); /* Start the default application at CONFIG_USER_ENTRYPOINT() */ init_taskid = TASK_CREATE("init", SCHED_PRIORITY_DEFAULT, CONFIG_USERMAIN_STACKSIZE, (main_t)CONFIG_USER_ENTRYPOINT, (const char **)NULL); ASSERT(init_taskid != ERROR); /* We an save a few bytes by discarding the IDLE thread's environment. */ #if !defined(CONFIG_DISABLE_ENVIRON) && defined(CONFIG_PATH_INITIAL) (void)clearenv(); #endif return OK; }
static inline int mod_loadfile(FAR struct mod_loadinfo_s *loadinfo) { FAR uint8_t *text; FAR uint8_t *data; FAR uint8_t **pptr; int ret; int i; /* Read each section into memory that is marked SHF_ALLOC + SHT_NOBITS */ svdbg("Loaded sections:\n"); text = (FAR uint8_t *)loadinfo->textalloc; data = (FAR uint8_t *)loadinfo->datastart; for (i = 0; i < loadinfo->ehdr.e_shnum; i++) { FAR Elf32_Shdr *shdr = &loadinfo->shdr[i]; /* SHF_ALLOC indicates that the section requires memory during * execution */ if ((shdr->sh_flags & SHF_ALLOC) == 0) { continue; } /* SHF_WRITE indicates that the section address space is write- * able */ if ((shdr->sh_flags & SHF_WRITE) != 0) { pptr = &data; } else { pptr = &text; } /* SHT_NOBITS indicates that there is no data in the file for the * section. */ if (shdr->sh_type != SHT_NOBITS) { /* Read the section data from sh_offset to the memory region */ ret = mod_read(loadinfo, *pptr, shdr->sh_size, shdr->sh_offset); if (ret < 0) { sdbg("ERROR: Failed to read section %d: %d\n", i, ret); return ret; } } /* If there is no data in an allocated section, then the allocated * section must be cleared. */ else { memset(*pptr, 0, shdr->sh_size); } /* Update sh_addr to point to copy in memory */ svdbg("%d. %08lx->%08lx\n", i, (unsigned long)shdr->sh_addr, (unsigned long)*pptr); shdr->sh_addr = (uintptr_t)*pptr; /* Setup the memory pointer for the next time through the loop */ *pptr += ELF_ALIGNUP(shdr->sh_size); } return OK; }