-
-
Save thehajime/fd11fbcea45db9fff6d0358a9fdf9e85 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| diff --git a/arch/um/kernel/Makefile b/arch/um/kernel/Makefile | |
| index b087b4278173..9aac5101af7e 100644 | |
| --- a/arch/um/kernel/Makefile | |
| +++ b/arch/um/kernel/Makefile | |
| @@ -31,6 +31,8 @@ obj-$(CONFIG_EARLY_PRINTK) += early_printk.o | |
| obj-$(CONFIG_STACKTRACE) += stacktrace.o | |
| obj-$(CONFIG_SMP) += smp.o | |
| +CFLAGS_process.o += -fomit-frame-pointer | |
| + | |
| USER_OBJS := config.o | |
| include $(srctree)/arch/um/scripts/Makefile.rules | |
| diff --git a/arch/um/kernel/process.c b/arch/um/kernel/process.c | |
| index e117a1e27c7e..d03b82e13bb4 100644 | |
| --- a/arch/um/kernel/process.c | |
| +++ b/arch/um/kernel/process.c | |
| @@ -160,6 +160,110 @@ static void fork_handler(void) | |
| userspace(¤t->thread.regs.regs); | |
| } | |
| +static void *uml_nommu_user_stack_alloc(unsigned long size) | |
| +{ | |
| + return (void *)alloc_stack(0, __uml_cant_sleep()); | |
| +} | |
| + | |
| +static void uml_nommu_user_stack_free(void *address, | |
| + unsigned long size) | |
| +{ | |
| + free_stack((unsigned long)address, 0); | |
| +} | |
| + | |
| +static int uml_nommu_get_user_stack_range(struct mm_struct *mm, | |
| + unsigned long sp, | |
| + unsigned long *low, | |
| + unsigned long *high) | |
| +{ | |
| + unsigned long stack_low; | |
| + unsigned long stack_high; | |
| + | |
| + if (!mm || !low || !high) | |
| + return -EINVAL; | |
| + | |
| + stack_low = mm->start_brk; | |
| + stack_high = mm->start_stack; | |
| + | |
| + if (!stack_low || stack_high <= stack_low) | |
| + return -EINVAL; | |
| + | |
| + /* | |
| + * The stack grows downward, so SP normally lies below | |
| + * start_stack and above start_brk. | |
| + */ | |
| + if (sp < stack_low || sp > stack_high) | |
| + return -EFAULT; | |
| + | |
| + *low = stack_low; | |
| + *high = stack_high; | |
| + | |
| + return 0; | |
| +} | |
| + | |
| +static unsigned long | |
| +relocate_stack_pointer(unsigned long pointer, | |
| + unsigned long old_low, | |
| + unsigned long old_high, | |
| + unsigned long new_low) | |
| +{ | |
| + if (pointer < old_low || pointer >= old_high) | |
| + return pointer; | |
| + | |
| + return new_low + (pointer - old_low); | |
| +} | |
| + | |
| +static int uml_nommu_copy_user_stack(struct task_struct *child) | |
| +{ | |
| + unsigned long parent_sp; | |
| + unsigned long stack_low; | |
| + unsigned long stack_high; | |
| + unsigned long stack_size; | |
| + unsigned long child_sp; | |
| + void *child_stack; | |
| + int ret; | |
| + | |
| + parent_sp = current->thread.regs.regs.gp[HOST_SP]; | |
| + | |
| + ret = uml_nommu_get_user_stack_range(current->mm, | |
| + parent_sp, | |
| + &stack_low, | |
| + &stack_high); | |
| + if (ret) | |
| + return ret; | |
| + | |
| + stack_size = stack_high - stack_low; | |
| + | |
| + child_stack = uml_nommu_user_stack_alloc(stack_size); | |
| + if (!child_stack) | |
| + return -ENOMEM; | |
| + | |
| + memcpy(child_stack, | |
| + (void *)stack_low, | |
| + stack_size); | |
| + | |
| + child_sp = (unsigned long)child_stack + | |
| + (parent_sp - stack_low); | |
| + | |
| + child->thread.regs.regs.gp[HOST_SP] = child_sp; | |
| + | |
| + child->thread.arch.nommu_user_stack = child_stack; | |
| + child->thread.arch.nommu_user_stack_size = stack_size; | |
| + | |
| + unsigned long parent_bp; | |
| + unsigned long child_stack_base = (unsigned long)child_stack; | |
| + | |
| + parent_bp = child->thread.regs.regs.gp[HOST_BP]; | |
| + | |
| + child->thread.regs.regs.gp[HOST_BP] = | |
| + relocate_stack_pointer(parent_bp, | |
| + stack_low, | |
| + stack_high, | |
| + child_stack_base); | |
| + | |
| + return 0; | |
| +} | |
| + | |
| int copy_thread(struct task_struct * p, const struct kernel_clone_args *args) | |
| { | |
| u64 clone_flags = args->flags; | |
| @@ -189,6 +293,14 @@ int copy_thread(struct task_struct * p, const struct kernel_clone_args *args) | |
| handler = fork_handler; | |
| arch_copy_thread(¤t->thread.arch, &p->thread.arch); | |
| + | |
| +#ifdef CONFIG_NOMMU_SWMMU | |
| + if (!(args->flags & CLONE_VM)) { | |
| + ret = uml_nommu_copy_user_stack(p); | |
| + if (ret) | |
| + return ret; | |
| + } | |
| +#endif | |
| } else { | |
| get_safe_registers(p->thread.regs.regs.gp, p->thread.regs.regs.fp); | |
| p->thread.request.thread.proc = args->fn; | |
| diff --git a/arch/x86/um/asm/processor_64.h b/arch/x86/um/asm/processor_64.h | |
| index f90159508936..6a478ee92d18 100644 | |
| --- a/arch/x86/um/asm/processor_64.h | |
| +++ b/arch/x86/um/asm/processor_64.h | |
| @@ -11,6 +11,10 @@ struct arch_thread { | |
| unsigned long debugregs[8]; | |
| int debugregs_seq; | |
| struct faultinfo faultinfo; | |
| +#ifdef CONFIG_NOMMU_SWMMU | |
| + void *nommu_user_stack; | |
| + unsigned long nommu_user_stack_size; | |
| +#endif | |
| }; | |
| #define INIT_ARCH_THREAD { .debugregs = { [ 0 ... 7 ] = 0 }, \ | |
| diff --git a/tools/testing/selftests/mm/nommu/nommu_swmmu_test.c b/tools/testing/selftests/mm/nommu/nommu_swmmu_test.c | |
| index 0f8726cc56b4..e0655fafac42 100644 | |
| --- a/tools/testing/selftests/mm/nommu/nommu_swmmu_test.c | |
| +++ b/tools/testing/selftests/mm/nommu/nommu_swmmu_test.c | |
| @@ -155,6 +155,77 @@ struct child_result { | |
| uint64_t value; | |
| }; | |
| +static int | |
| +test_eager_copy_fork(void) | |
| +{ | |
| + struct swmmu_test_object *object; | |
| + pid_t pid; | |
| + int status; | |
| + uint64_t parent_value; | |
| + | |
| + object = nommu_swmmu_alloc(sizeof(*object)); | |
| + if (!object) { | |
| + ksft_test_result_skip("SWMMU allocation is unavailable\n"); | |
| + return KSFT_SKIP; | |
| + } | |
| + | |
| + nommu_swmmu_store_u64(&object->value, | |
| + sizeof(object->value), | |
| + 41); | |
| + | |
| + pid = fork(); | |
| + if (pid < 0) { | |
| + ksft_test_result_fail("fork failed: %s\n", | |
| + strerror(errno)); | |
| + nommu_swmmu_free(object); | |
| + return KSFT_FAIL; | |
| + } | |
| + | |
| + if (pid == 0) { | |
| + _exit(0); | |
| + } | |
| + | |
| + int wait_ret; | |
| + | |
| + status = -1; | |
| + wait_ret = waitpid(pid, &status, 0); | |
| + | |
| + if (wait_ret != pid) { | |
| + ksft_test_result_fail( | |
| + "waitpid returned %d, expected %d: %s\n", | |
| + wait_ret, pid, strerror(errno)); | |
| + nommu_swmmu_free(object); | |
| + return KSFT_FAIL; | |
| + } | |
| + | |
| + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { | |
| + ksft_test_result_fail( | |
| + "child exited abnormally: status=%#x\n", | |
| + status); | |
| + nommu_swmmu_free(object); | |
| + return KSFT_FAIL; | |
| + } | |
| + | |
| + parent_value = nommu_swmmu_load_u64(&object->value, | |
| + sizeof(object->value)); | |
| + | |
| + if (parent_value != 41) { | |
| + ksft_test_result_fail( | |
| + "parent value changed to %llu\n", | |
| + (unsigned long long)parent_value); | |
| + nommu_swmmu_free(object); | |
| + return KSFT_FAIL; | |
| + } | |
| + | |
| + nommu_swmmu_free(object); | |
| + | |
| + ksft_test_result_pass( | |
| + "fork child exited and parent backing remained unchanged\n"); | |
| + | |
| + return KSFT_PASS; | |
| +} | |
| + | |
| +#if 0 | |
| static int | |
| test_eager_copy_fork(void) | |
| { | |
| @@ -274,6 +345,7 @@ test_eager_copy_fork(void) | |
| return KSFT_PASS; | |
| } | |
| +#endif | |
| int main(void) | |
| { |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment