static inline void callinfo_new(struct a2_vm* vm_p, struct a2_closure* cls, int retbegin, int retnumber){ assert(vm_p); struct vm_callinfo* ci = (struct vm_callinfo*)malloc(sizeof(*ci)); ci->cls = cls; // set closure stack frame ci->reg_stack.len = (cls)?(a2_closure_regscount(cls)):(0); ci->reg_stack.sf_idx = up_stack_frame(vm_p, ci->reg_stack.len); ci->pc=0; ci->front = NULL; ci->retbegin = retbegin; ci->retnumber = retnumber; ci->next = vm_p->call_chain; if(vm_p->call_chain) vm_p->call_chain->front = ci; vm_p->call_chain = ci; }
static inline int task_stackargsetup(FAR struct task_tcb_s *tcb, FAR char * const argv[]) { FAR char **stackargv; FAR const char *name; FAR char *str; size_t strtablen; size_t argvlen; int nbytes; int argc; int i; /* Get the name string that we will use as the first argument */ #if CONFIG_TASK_NAME_SIZE > 0 name = tcb->cmn.name; #else name = (FAR const char *)g_noname; #endif /* CONFIG_TASK_NAME_SIZE */ /* Get the size of the task name (including the NUL terminator) */ strtablen = (strlen(name) + 1); /* Count the number of arguments and get the accumulated size of the * argument strings (including the null terminators). The argument count * does not include the task name in that will be in argv[0]. */ argc = 0; if (argv) { /* A NULL argument terminates the list */ while (argv[argc]) { /* Add the size of this argument (with NUL terminator). * Check each time if the accumulated size exceeds the * size of the allocated stack. */ strtablen += (strlen(argv[argc]) + 1); if (strtablen >= tcb->cmn.adj_stack_size) { return -ENAMETOOLONG; } /* Increment the number of args. Here is a sanity check to * prevent running away with an unterminated argv[] list. * MAX_STACK_ARGS should be sufficiently large that this never * happens in normal usage. */ if (++argc > MAX_STACK_ARGS) { return -E2BIG; } } } /* Allocate a stack frame to hold argv[] array and the strings. NOTE * that argc + 2 entries are needed: The number of arguments plus the * task name plus a NULL argv[] entry to terminate the list. */ argvlen = (argc + 2)*sizeof(FAR char*); stackargv = (FAR char **)up_stack_frame(&tcb->cmn, argvlen + strtablen); DEBUGASSERT(stackargv != NULL); if (stackargv == NULL) { return -ENOMEM; } /* Get the address of the string table that will lie immediately after * the argv[] array and mark it as a null string. */ str = (FAR char *)stackargv + argvlen; /* Copy the task name. Increment str to skip over the task name and its * NUL terminator in the string buffer. */ stackargv[0] = str; nbytes = strlen(name) + 1; strcpy(str, name); str += nbytes; /* Copy each argument */ for (i = 0; i < argc; i++) { /* Save the pointer to the location in the string buffer and copy * the argument into the buffer. Increment str to skip over the * argument and its NUL terminator in the string buffer. */ stackargv[i+1] = str; nbytes = strlen(argv[i]) + 1; strcpy(str, argv[i]); str += nbytes; } /* Put a terminator entry at the end of the argv[] array. Then save the * argv[] arry pointer in the TCB where it will be recovered later by * task_start(). */ stackargv[argc + 1] = NULL; tcb->argv = stackargv; return OK; }
pid_t up_vfork(const struct vfork_s *context) { struct tcb_s *parent = this_task(); struct task_tcb_s *child; size_t stacksize; uint32_t newsp; #ifdef CONFIG_MIPS32_FRAMEPOINTER uint32_t newfp; #endif uint32_t stackutil; size_t argsize; void *argv; int ret; sinfo("s0:%08x s1:%08x s2:%08x s3:%08x s4:%08x\n", context->s0, context->s1, context->s2, context->s3, context->s4); #ifdef CONFIG_MIPS32_FRAMEPOINTER sinfo("s5:%08x s6:%08x s7:%08x\n", context->s5, context->s6, context->s7); #ifdef MIPS32_SAVE_GP sinfo("fp:%08x sp:%08x ra:%08x gp:%08x\n", context->fp, context->sp, context->ra, context->gp); #else sinfo("fp:%08x sp:%08x ra:%08x\n", context->fp context->sp, context->ra); #endif #else sinfo("s5:%08x s6:%08x s7:%08x s8:%08x\n", context->s5, context->s6, context->s7, context->s8); #ifdef MIPS32_SAVE_GP sinfo("sp:%08x ra:%08x gp:%08x\n", context->sp, context->ra, context->gp); #else sinfo("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, &argsize); if (!child) { sinfo("task_vforksetup failed\n"); return (pid_t)ERROR; } sinfo("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 + argsize, parent->flags & TCB_FLAG_TTYPE_MASK); if (ret != OK) { serr("ERROR: up_create_stack failed: %d\n", ret); task_vforkabort(child, -ret); return (pid_t)ERROR; } /* Allocate the memory and copy argument from parent task */ argv = up_stack_frame((FAR struct tcb_s *)child, argsize); memcpy(argv, parent->adj_stack_ptr, argsize); /* 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; sinfo("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? */ #ifdef 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; } sinfo("Old stack base:%08x SP:%08x FP:%08x\n", parent->adj_stack_ptr, context->sp, context->fp); sinfo("New stack base:%08x SP:%08x FP:%08x\n", child->cmn.adj_stack_ptr, newsp, newfp); #else sinfo("Old stack base:%08x SP:%08x\n", parent->adj_stack_ptr, context->sp); sinfo("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 */ #ifdef 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 */ #ifdef 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); }