From 36b46a6a40c6d98962d45645806de2d471192aab Mon Sep 17 00:00:00 2001 From: Xiang Xiao Date: Fri, 24 Aug 2018 07:43:00 -0600 Subject: [PATCH] arch/ and task/sched: vfork operation needs to allocate and copy the task argument too. Also correction of the address correction cannot depend on the stack pointer since it is not available in all architectures. Rather callculate the offset from the stack allocation pointer --- arch/arm/src/common/up_vfork.c | 11 +++++-- arch/mips/src/mips32/up_vfork.c | 11 +++++-- arch/risc-v/src/rv32im/up_vfork.c | 11 +++++-- include/nuttx/sched.h | 2 +- sched/task/task_vfork.c | 53 ++++++++++++++++++++++++++++--- 5 files changed, 76 insertions(+), 12 deletions(-) diff --git a/arch/arm/src/common/up_vfork.c b/arch/arm/src/common/up_vfork.c index ff43c9cc97..1c9e1d8287 100644 --- a/arch/arm/src/common/up_vfork.c +++ b/arch/arm/src/common/up_vfork.c @@ -117,6 +117,8 @@ pid_t up_vfork(const struct vfork_s *context) uint32_t newsp; uint32_t newfp; uint32_t stackutil; + size_t argsize; + void *argv; int ret; sinfo("vfork context [%p]:\n", context); @@ -129,7 +131,7 @@ pid_t up_vfork(const struct vfork_s *context) /* Allocate and initialize a TCB for the child task. */ - child = task_vforksetup((start_t)(context->lr & ~1)); + child = task_vforksetup((start_t)(context->lr & ~1), &argsize); if (!child) { serr("ERROR: task_vforksetup failed\n"); @@ -147,7 +149,7 @@ pid_t up_vfork(const struct vfork_s *context) /* Allocate the stack for the TCB */ - ret = up_create_stack((FAR struct tcb_s *)child, stacksize, + ret = up_create_stack((FAR struct tcb_s *)child, stacksize + argsize, parent->flags & TCB_FLAG_TTYPE_MASK); if (ret != OK) { @@ -156,6 +158,11 @@ pid_t up_vfork(const struct vfork_s *context) 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 ARM uses * a push-down stack so that the current stack pointer should * be lower than the initial, adjusted stack pointer. The diff --git a/arch/mips/src/mips32/up_vfork.c b/arch/mips/src/mips32/up_vfork.c index d62c2dd623..b84707ebbe 100644 --- a/arch/mips/src/mips32/up_vfork.c +++ b/arch/mips/src/mips32/up_vfork.c @@ -121,6 +121,8 @@ pid_t up_vfork(const struct vfork_s *context) 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", @@ -149,7 +151,7 @@ pid_t up_vfork(const struct vfork_s *context) /* Allocate and initialize a TCB for the child task. */ - child = task_vforksetup((start_t)context->ra); + child = task_vforksetup((start_t)context->ra, &argsize); if (!child) { sinfo("task_vforksetup failed\n"); @@ -167,7 +169,7 @@ pid_t up_vfork(const struct vfork_s *context) /* Allocate the stack for the TCB */ - ret = up_create_stack((FAR struct tcb_s *)child, stacksize, + ret = up_create_stack((FAR struct tcb_s *)child, stacksize + argsize, parent->flags & TCB_FLAG_TTYPE_MASK); if (ret != OK) { @@ -176,6 +178,11 @@ pid_t up_vfork(const struct vfork_s *context) 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 diff --git a/arch/risc-v/src/rv32im/up_vfork.c b/arch/risc-v/src/rv32im/up_vfork.c index d38d29c158..51e3dcae20 100644 --- a/arch/risc-v/src/rv32im/up_vfork.c +++ b/arch/risc-v/src/rv32im/up_vfork.c @@ -125,6 +125,8 @@ pid_t up_vfork(const struct vfork_s *context) 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", @@ -153,7 +155,7 @@ pid_t up_vfork(const struct vfork_s *context) /* Allocate and initialize a TCB for the child task. */ - child = task_vforksetup((start_t)context->ra); + child = task_vforksetup((start_t)context->ra, &argsize); if (!child) { sinfo("task_vforksetup failed\n"); @@ -171,7 +173,7 @@ pid_t up_vfork(const struct vfork_s *context) /* Allocate the stack for the TCB */ - ret = up_create_stack((FAR struct tcb_s *)child, stacksize, + ret = up_create_stack((FAR struct tcb_s *)child, stacksize + argsize, parent->flags & TCB_FLAG_TTYPE_MASK); if (ret != OK) { @@ -180,6 +182,11 @@ pid_t up_vfork(const struct vfork_s *context) 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 diff --git a/include/nuttx/sched.h b/include/nuttx/sched.h index 2d6b9ac11c..050d26b63f 100644 --- a/include/nuttx/sched.h +++ b/include/nuttx/sched.h @@ -872,7 +872,7 @@ void task_starthook(FAR struct task_tcb_s *tcb, starthook_t starthook, * ********************************************************************************/ -FAR struct task_tcb_s *task_vforksetup(start_t retaddr); +FAR struct task_tcb_s *task_vforksetup(start_t retaddr, size_t *argsize); pid_t task_vforkstart(FAR struct task_tcb_s *child); void task_vforkabort(FAR struct task_tcb_s *child, int errcode); diff --git a/sched/task/task_vfork.c b/sched/task/task_vfork.c index 124a7fd077..b5d7345d8a 100644 --- a/sched/task/task_vfork.c +++ b/sched/task/task_vfork.c @@ -131,7 +131,7 @@ static inline int vfork_stackargsetup(FAR struct tcb_s *parent, /* Get the address correction */ - offset = child->cmn.xcp.regs[REG_SP] - parent->xcp.regs[REG_SP]; + offset = child->cmn.adj_stack_ptr - parent->adj_stack_ptr; /* Change the child argv[] to point into its stack (instead of its * parent's stack). @@ -194,6 +194,45 @@ static inline int vfork_argsetup(FAR struct tcb_s *parent, return vfork_stackargsetup(parent, child); } +/**************************************************************************** + * Name: vfork_argsize + * + * Description: + * Get the parent's argument size. + * + * Input Parameters: + * parent - Address of the parent task's TCB + * + * Return Value: + * The parent's argument size. + * + ****************************************************************************/ + +static inline size_t vfork_argsize(FAR struct tcb_s *parent) +{ + if ((parent->flags & TCB_FLAG_TTYPE_MASK) != TCB_FLAG_TTYPE_PTHREAD) + { + FAR struct task_tcb_s *ptcb = (FAR struct task_tcb_s *)parent; + size_t strtablen = 0; + int argc = 0; + + while (ptcb->argv[argc]) + { + /* Add the size of this argument (with NUL terminator) */ + + strtablen += strlen(ptcb->argv[argc++]) + 1; + } + + /* Return the size to hold argv[] array and the strings. */ + + return (argc + 1) * sizeof(FAR char *) + strtablen; + } + else + { + return 0; + } +} + /**************************************************************************** * Public Functions ****************************************************************************/ @@ -230,8 +269,8 @@ static inline int vfork_argsetup(FAR struct tcb_s *parent, * 6) task_vforkstart() then executes the child thread. * * Input Parameters: - * parent - Address of the parent task's TCB - * child - Address of the child task's TCB + * retaddr - Return address + * argsize - Location to return the argument size * * Returned Value: * Upon successful completion, task_vforksetup() returns a pointer to @@ -240,7 +279,7 @@ static inline int vfork_argsetup(FAR struct tcb_s *parent, * ****************************************************************************/ -FAR struct task_tcb_s *task_vforksetup(start_t retaddr) +FAR struct task_tcb_s *task_vforksetup(start_t retaddr, size_t *argsize) { struct tcb_s *parent = this_task(); struct task_tcb_s *child; @@ -248,7 +287,7 @@ FAR struct task_tcb_s *task_vforksetup(start_t retaddr) int priority; int ret; - DEBUGASSERT(retaddr); + DEBUGASSERT(retaddr != NULL && argsize != NULL); /* Get the type of the fork'ed task (kernel or user) */ @@ -312,6 +351,10 @@ FAR struct task_tcb_s *task_vforksetup(start_t retaddr) goto errout_with_tcb; } + /* Return the argument size */ + + *argsize = vfork_argsize(parent); + sinfo("parent=%p, returning child=%p\n", parent, child); return child;