From f6cfd1c87bdedd8ae00fec35e97966d2a0b4d1c9 Mon Sep 17 00:00:00 2001 From: Jiuzhu Dong Date: Thu, 31 Dec 2020 16:27:08 +0800 Subject: [PATCH] vfork: support sim vfork N/A Change-Id: I15920bcbacfc5ea519cfe12c39cb64dfe6365838 Signed-off-by: Jiuzhu Dong --- arch/Kconfig | 1 + arch/sim/include/irq.h | 3 + arch/sim/src/Makefile | 10 ++ arch/sim/src/sim/up_internal.h | 11 +- arch/sim/src/sim/up_vfork.c | 193 ++++++++++++++++++++++++++++++++ arch/sim/src/sim/up_vfork32.S | 112 ++++++++++++++++++ arch/sim/src/sim/up_vfork64.S | 116 +++++++++++++++++++ arch/sim/src/sim/up_vfork_arm.S | 99 ++++++++++++++++ 8 files changed, 543 insertions(+), 2 deletions(-) create mode 100644 arch/sim/src/sim/up_vfork.c create mode 100644 arch/sim/src/sim/up_vfork32.S create mode 100644 arch/sim/src/sim/up_vfork64.S create mode 100644 arch/sim/src/sim/up_vfork_arm.S diff --git a/arch/Kconfig b/arch/Kconfig index a7a32e66e97..1a1208a8986 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -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 diff --git a/arch/sim/include/irq.h b/arch/sim/include/irq.h index 604d4635cc7..68cba45700d 100644 --- a/arch/sim/include/irq.h +++ b/arch/sim/include/irq.h @@ -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 /**************************************************************************** diff --git a/arch/sim/src/Makefile b/arch/sim/src/Makefile index 54b83508fcd..51ff3779f1f 100644 --- a/arch/sim/src/Makefile +++ b/arch/sim/src/Makefile @@ -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))) diff --git a/arch/sim/src/sim/up_internal.h b/arch/sim/src/sim/up_internal.h index 55863c648b0..a473b10f828 100644 --- a/arch/sim/src/sim/up_internal.h +++ b/arch/sim/src/sim/up_internal.h @@ -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 diff --git a/arch/sim/src/sim/up_vfork.c b/arch/sim/src/sim/up_vfork.c new file mode 100644 index 00000000000..9ea63056a86 --- /dev/null +++ b/arch/sim/src/sim/up_vfork.c @@ -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 + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#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); +} diff --git a/arch/sim/src/sim/up_vfork32.S b/arch/sim/src/sim/up_vfork32.S new file mode 100644 index 00000000000..6fee7bbe3c5 --- /dev/null +++ b/arch/sim/src/sim/up_vfork32.S @@ -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 +#include "up_internal.h" +#include + +/************************************************************************************ + * 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 diff --git a/arch/sim/src/sim/up_vfork64.S b/arch/sim/src/sim/up_vfork64.S new file mode 100644 index 00000000000..465b1c87035 --- /dev/null +++ b/arch/sim/src/sim/up_vfork64.S @@ -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 +#include "up_internal.h" +#include + +/************************************************************************************ + * 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 diff --git a/arch/sim/src/sim/up_vfork_arm.S b/arch/sim/src/sim/up_vfork_arm.S new file mode 100644 index 00000000000..72a7bdfdf61 --- /dev/null +++ b/arch/sim/src/sim/up_vfork_arm.S @@ -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 +#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