vfork: support sim vfork

N/A

Change-Id: I15920bcbacfc5ea519cfe12c39cb64dfe6365838
Signed-off-by: Jiuzhu Dong <dongjiuzhu1@xiaomi.com>
This commit is contained in:
Jiuzhu Dong 2020-12-31 16:27:08 +08:00 committed by Xiang Xiao
parent 6fd80289f9
commit f6cfd1c87b
8 changed files with 543 additions and 2 deletions

View file

@ -73,6 +73,7 @@ config ARCH_SIM
select ARCH_HAVE_TICKLESS
select ARCH_HAVE_POWEROFF
select ARCH_HAVE_TESTSET
select ARCH_HAVE_VFORK
select ALARM_ARCH
select ONESHOT
select SERIAL_CONSOLE

View file

@ -52,12 +52,15 @@
/* Storage order: %rbx, %rsp, %rbp, %r12, %r13, %r14, %r15, %rip */
# define XCPTCONTEXT_REGS 8
# define XCPTCONTEXT_SIZE (8 * XCPTCONTEXT_REGS)
#elif defined(CONFIG_HOST_X86) || defined(CONFIG_SIM_M32)
/* Storage order: %ebx, %esi, %edi, %ebp, sp, and return PC */
# define XCPTCONTEXT_REGS 6
# define XCPTCONTEXT_SIZE (4 * XCPTCONTEXT_REGS)
#elif defined(CONFIG_HOST_ARM)
# define XCPTCONTEXT_REGS 16
# define XCPTCONTEXT_SIZE (4 * XCPTCONTEXT_REGS)
#endif
/****************************************************************************

View file

@ -58,13 +58,17 @@ REQUIREDOBJS = $(LINKOBJS)
ifeq ($(CONFIG_HOST_X86_64),y)
ifeq ($(CONFIG_SIM_M32),y)
ASRCS += up_setjmp32.S
ASRCS += up_vfork32.S
else
ASRCS += up_setjmp64.S
ASRCS += up_vfork64.S
endif
else ifeq ($(CONFIG_HOST_X86),y)
ASRCS += up_setjmp32.S
ASRCS += up_vfork32.S
else ifeq ($(CONFIG_HOST_ARM),y)
ASRCS += up_setjmp_arm.S
ASRCS += up_vfork_arm.S
endif
AOBJS = $(ASRCS:.S=$(OBJEXT))
@ -76,6 +80,12 @@ CSRCS += up_reprioritizertr.c up_exit.c up_schedulesigaction.c
CSRCS += up_allocateheap.c up_uart.c
CSRCS += up_copyfullstate.c
ifeq ($(CONFIG_ARCH_HAVE_VFORK),y)
ifeq ($(CONFIG_SCHED_WAITPID),y)
CSRCS += up_vfork.c
endif
endif
VPATH = sim
DEPPATH = $(patsubst %,--dep-path %,$(subst :, ,$(VPATH)))

View file

@ -94,8 +94,9 @@
/* Compatibility definitions */
# define JB_SP JB_RSP
# define JB_PC JB_RSI
# define JB_FP JB_RBP
# define JB_SP JB_RSP
# define JB_PC JB_RSI
#elif defined(CONFIG_HOST_X86) || defined(CONFIG_SIM_M32)
/* Storage order: %ebx, $esi, %edi, %ebp, sp, and return PC */
@ -117,7 +118,13 @@
# define JB_PC (5)
# endif /* __ASSEMBLY__ */
/* Compatibility definitions */
# define JB_FP JB_EBP
#elif defined(CONFIG_HOST_ARM)
# define JB_FP 7
# define JB_SP 8
# define JB_PC 9
#endif

193
arch/sim/src/sim/up_vfork.c Normal file
View file

@ -0,0 +1,193 @@
/****************************************************************************
* arch/sim/src/sim/up_vfork.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <inttypes.h>
#include <stdint.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <debug.h>
#include <nuttx/sched.h>
#include <nuttx/arch.h>
#include <arch/irq.h>
#include "up_internal.h"
#include "sched/sched.h"
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: up_vfork
*
* Description:
* The vfork() function has the same effect as fork(), except that the
* behavior is undefined if the process created by vfork() either modifies
* any data other than a variable of type pid_t used to store the return
* value from vfork(), or returns from the function in which vfork() was
* called, or calls any other function before successfully calling _exit()
* or one of the exec family of functions.
*
* The overall sequence is:
*
* 1) User code calls vfork(). vfork() collects context information and
* transfers control up up_vfork().
* 2) up_vfork()and calls nxtask_setup_vfork().
* 3) nxtask_setup_vfork() allocates and configures the child task's TCB.
* This consists of:
* - Allocation of the child task's TCB.
* - Initialization of file descriptors and streams
* - Configuration of environment variables
* - Setup the input parameters for the task.
* - Initialization of the TCB (including call to up_initial_state()
* 4) up_vfork() provides any additional operating context. up_vfork must:
* - Allocate and initialize the stack
* - Initialize special values in any CPU registers that were not
* already configured by up_initial_state()
* 5) up_vfork() then calls nxtask_start_vfork()
* 6) nxtask_start_vfork() then executes the child thread.
*
* nxtask_abort_vfork() may be called if an error occurs between steps 3 and
* 6.
*
* Returned Value:
* Upon successful completion, vfork() returns 0 to the child process and
* returns the process ID of the child process to the parent process.
* Otherwise, -1 is returned to the parent, no child process is created,
* and errno is set to indicate the error.
*
****************************************************************************/
pid_t up_vfork(const xcpt_reg_t *context)
{
struct tcb_s *parent = this_task();
struct task_tcb_s *child;
size_t stacksize;
xcpt_reg_t newsp;
xcpt_reg_t newfp;
xcpt_reg_t stackutil;
size_t argsize;
void *argv;
int ret;
sinfo("vfork context [%p]:\n", context);
sinfo(" frame pointer:%08" PRIxPTR " sp:%08" PRIxPTR " pc:%08" PRIxPTR ""
"\n", context[JB_FP], context[JB_SP], context[JB_PC]);
/* Allocate and initialize a TCB for the child task. */
child = nxtask_setup_vfork((start_t)(context[JB_PC]), &argsize);
if (!child)
{
serr("ERROR: nxtask_setup_vfork failed\n");
return (pid_t)ERROR;
}
sinfo("TCBs: Parent=%p Child=%p\n", parent, child);
/* Get the size of the parent task's stack. */
stacksize = parent->adj_stack_size;
/* 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);
nxtask_abort_vfork(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 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((xcpt_reg_t)parent->adj_stack_ptr > context[JB_SP]);
stackutil = (xcpt_reg_t)parent->adj_stack_ptr - context[JB_SP];
sinfo("Parent: stacksize:%zu stackutil:%" PRIdPTR "\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 = (xcpt_reg_t)child->cmn.adj_stack_ptr - stackutil;
memcpy((void *)newsp, (const void *)context[JB_SP], stackutil);
/* Was there a frame pointer in place before? */
if (context[JB_FP] <= (xcpt_reg_t)parent->adj_stack_ptr &&
context[JB_FP] >= (xcpt_reg_t)parent->adj_stack_ptr -
stacksize)
{
xcpt_reg_t frameutil = (xcpt_reg_t)parent->adj_stack_ptr -
context[JB_FP];
newfp = (xcpt_reg_t)child->cmn.adj_stack_ptr - frameutil;
}
else
{
newfp = context[JB_FP];
}
sinfo("Parent: stack base:%p SP:%08" PRIxPTR " FP:%08" PRIxPTR "\n",
parent->adj_stack_ptr, context[JB_SP], context[JB_FP]);
sinfo("Child: stack base:%p SP:%08" PRIxPTR " FP:%08" PRIxPTR "\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.
*/
memcpy(child->cmn.xcp.regs, context, sizeof(xcpt_reg_t) *
XCPTCONTEXT_REGS);
child->cmn.xcp.regs[JB_FP] = newfp; /* Frame pointer */
child->cmn.xcp.regs[JB_SP] = newsp; /* Stack pointer */
/* And, finally, start the child task. On a failure, nxtask_start_vfork()
* will discard the TCB by calling nxtask_abort_vfork().
*/
return nxtask_start_vfork(child);
}

View file

@ -0,0 +1,112 @@
/************************************************************************************
* arch/sim/src/sim/up_vfork32.S
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/************************************************************************************
* Included Files
************************************************************************************/
#include <nuttx/config.h>
#include "up_internal.h"
#include <arch/irq.h>
/************************************************************************************
* Pre-processor Definitions
************************************************************************************/
#ifdef __CYGWIN__
# define SYMBOL(s) _##s
#elif defined(__ELF__)
# define SYMBOL(s) s
#else
# define SYMBOL(s) _##s
#endif
/************************************************************************************
* Public Symbols
************************************************************************************/
.file "vfork.S"
.globl up_vfork
/************************************************************************************
* Public Functions
************************************************************************************/
/************************************************************************************
* Name: vfork
*
* Description:
* The vfork() function has the same effect as fork(), except that the behavior is
* undefined if the process created by vfork() either modifies any data other than
* a variable of type pid_t used to store the return value from vfork(), or returns
* from the function in which vfork() was called, or calls any other function before
* successfully calling _exit() or one of the exec family of functions.
*
* This thin layer implements vfork by simply calling up_vfork() with the vfork()
* context as an argument. The overall sequence is:
*
* 1) User code calls vfork(). vfork() collects context information and
* transfers control up up_vfork().
* 2) up_vfork()and calls nxtask_setup_vfork().
* 3) nxtask_setup_vfork() allocates and configures the child task's TCB. This
* consists of:
* - Allocation of the child task's TCB.
* - Initialization of file descriptors and streams
* - Configuration of environment variables
* - Setup the input parameters for the task.
* - Initialization of the TCB (including call to up_initial_state()
* 4) up_vfork() provides any additional operating context. up_vfork must:
* - Allocate and initialize the stack
* - Initialize special values in any CPU registers that were not
* already configured by up_initial_state()
* 5) up_vfork() then calls nxtask_start_vfork()
* 6) nxtask_start_vfork() then executes the child thread.
*
* Input Parameters:
* None
*
* Returned Value:
* Upon successful completion, vfork() returns 0 to the child process and returns
* the process ID of the child process to the parent process. Otherwise, -1 is
* returned to the parent, no child process is created, and errno is set to
* indicate the error.
*
************************************************************************************/
.text
.globl SYMBOL(vfork)
#ifdef __ELF__
.type SYMBOL(vfork), @function
#endif
SYMBOL(vfork):
sub $XCPTCONTEXT_SIZE, %esp
push %esp
call SYMBOL(up_setjmp)
sub $1, %eax
jz child
call SYMBOL(up_vfork)
child:
add $XCPTCONTEXT_SIZE+4, %esp
ret
#ifdef __ELF__
.size SYMBOL(vfork), . - SYMBOL(vfork)
#endif

View file

@ -0,0 +1,116 @@
/************************************************************************************
* arch/sim/src/sim/up_vfork64.S
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/************************************************************************************
* Included Files
************************************************************************************/
#include <nuttx/config.h>
#include "up_internal.h"
#include <arch/irq.h>
/************************************************************************************
* Pre-processor Definitions
************************************************************************************/
#ifdef __CYGWIN__
# define SYMBOL(s) _##s
#elif defined(__ELF__)
# define SYMBOL(s) s
#else
# define SYMBOL(s) _##s
#endif
/************************************************************************************
* Public Symbols
************************************************************************************/
.file "vfork.S"
.globl up_vfork
/************************************************************************************
* Public Functions
************************************************************************************/
/************************************************************************************
* Name: vfork
*
* Description:
* The vfork() function has the same effect as fork(), except that the behavior is
* undefined if the process created by vfork() either modifies any data other than
* a variable of type pid_t used to store the return value from vfork(), or returns
* from the function in which vfork() was called, or calls any other function before
* successfully calling _exit() or one of the exec family of functions.
*
* This thin layer implements vfork by simply calling up_vfork() with the vfork()
* context as an argument. The overall sequence is:
*
* 1) User code calls vfork(). vfork() collects context information and
* transfers control up up_vfork().
* 2) up_vfork()and calls nxtask_setup_vfork().
* 3) nxtask_setup_vfork() allocates and configures the child task's TCB. This
* consists of:
* - Allocation of the child task's TCB.
* - Initialization of file descriptors and streams
* - Configuration of environment variables
* - Setup the input parameters for the task.
* - Initialization of the TCB (including call to up_initial_state()
* 4) up_vfork() provides any additional operating context. up_vfork must:
* - Allocate and initialize the stack
* - Initialize special values in any CPU registers that were not
* already configured by up_initial_state()
* 5) up_vfork() then calls nxtask_start_vfork()
* 6) nxtask_start_vfork() then executes the child thread.
*
* Input Parameters:
* None
*
* Returned Value:
* Upon successful completion, vfork() returns 0 to the child process and returns
* the process ID of the child process to the parent process. Otherwise, -1 is
* returned to the parent, no child process is created, and errno is set to
* indicate the error.
*
************************************************************************************/
.text
.globl SYMBOL(vfork)
#ifdef __ELF__
.type SYMBOL(vfork), @function
#endif
SYMBOL(vfork):
sub $XCPTCONTEXT_SIZE, %rsp
#ifdef CONFIG_SIM_X8664_MICROSOFT
mov %rsp, %rcx
#else /* if defined(CONFIG_SIM_X8664_SYSTEMV) */
mov %rsp, %rdi
#endif
call SYMBOL(up_setjmp)
sub $1, %eax
jz child
call SYMBOL(up_vfork)
child:
add $XCPTCONTEXT_SIZE, %rsp
ret
#ifdef __ELF__
.size SYMBOL(vfork), . - SYMBOL(vfork)
#endif

View file

@ -0,0 +1,99 @@
/************************************************************************************
* arch/sim/src/sim/up_vfork_arm.S
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/************************************************************************************
* Included Files
************************************************************************************/
#include <nuttx/config.h>
#include "up_internal.h"
/************************************************************************************
* Pre-processor Definitions
************************************************************************************/
/************************************************************************************
* Public Symbols
************************************************************************************/
.file "vfork.S"
.globl up_vfork
/************************************************************************************
* Public Functions
************************************************************************************/
/************************************************************************************
* Name: vfork
*
* Description:
* The vfork() function has the same effect as fork(), except that the behavior is
* undefined if the process created by vfork() either modifies any data other than
* a variable of type pid_t used to store the return value from vfork(), or returns
* from the function in which vfork() was called, or calls any other function before
* successfully calling _exit() or one of the exec family of functions.
*
* This thin layer implements vfork by simply calling up_vfork() with the vfork()
* context as an argument. The overall sequence is:
*
* 1) User code calls vfork(). vfork() collects context information and
* transfers control up up_vfork().
* 2) up_vfork()and calls nxtask_setup_vfork().
* 3) nxtask_setup_vfork() allocates and configures the child task's TCB. This
* consists of:
* - Allocation of the child task's TCB.
* - Initialization of file descriptors and streams
* - Configuration of environment variables
* - Setup the input parameters for the task.
* - Initialization of the TCB (including call to up_initial_state()
* 4) up_vfork() provides any additional operating context. up_vfork must:
* - Allocate and initialize the stack
* - Initialize special values in any CPU registers that were not
* already configured by up_initial_state()
* 5) up_vfork() then calls nxtask_start_vfork()
* 6) nxtask_start_vfork() then executes the child thread.
*
* Input Parameters:
* None
*
* Returned Value:
* Upon successful completion, vfork() returns 0 to the child process and returns
* the process ID of the child process to the parent process. Otherwise, -1 is
* returned to the parent, no child process is created, and errno is set to
* indicate the error.
*
************************************************************************************/
.text
.globl vfork
.type vfork, @function
vfork:
sub sp, sp, #XCPTCONTEXT_SIZE
mov r0, sp
bl up_setjmp
subs r0, #1
jz child
bl up_vfork
child:
add sp, sp, #XCPTCONTEXT_SIZE
ret
.size vfork, . - vfork
.end