diff options
| -rw-r--r-- | arch/openrisc/Kconfig | 2 | ||||
| -rw-r--r-- | arch/openrisc/include/uapi/asm/unistd.h | 2 | ||||
| -rw-r--r-- | arch/openrisc/kernel/entry.S | 43 | ||||
| -rw-r--r-- | arch/openrisc/kernel/process.c | 163 |
4 files changed, 77 insertions, 133 deletions
diff --git a/arch/openrisc/Kconfig b/arch/openrisc/Kconfig index 05f2ba41ff1a..e7f1a2993f78 100644 --- a/arch/openrisc/Kconfig +++ b/arch/openrisc/Kconfig | |||
| @@ -22,6 +22,8 @@ config OPENRISC | |||
| 22 | select GENERIC_STRNCPY_FROM_USER | 22 | select GENERIC_STRNCPY_FROM_USER |
| 23 | select GENERIC_STRNLEN_USER | 23 | select GENERIC_STRNLEN_USER |
| 24 | select MODULES_USE_ELF_RELA | 24 | select MODULES_USE_ELF_RELA |
| 25 | select GENERIC_KERNEL_THREAD | ||
| 26 | select GENERIC_KERNEL_EXECVE | ||
| 25 | 27 | ||
| 26 | config MMU | 28 | config MMU |
| 27 | def_bool y | 29 | def_bool y |
diff --git a/arch/openrisc/include/uapi/asm/unistd.h b/arch/openrisc/include/uapi/asm/unistd.h index 437bdbb61b14..5db7bc0fa5a7 100644 --- a/arch/openrisc/include/uapi/asm/unistd.h +++ b/arch/openrisc/include/uapi/asm/unistd.h | |||
| @@ -20,6 +20,8 @@ | |||
| 20 | 20 | ||
| 21 | #define sys_mmap2 sys_mmap_pgoff | 21 | #define sys_mmap2 sys_mmap_pgoff |
| 22 | 22 | ||
| 23 | #define __ARCH_WANT_SYS_EXECVE | ||
| 24 | |||
| 23 | #include <asm-generic/unistd.h> | 25 | #include <asm-generic/unistd.h> |
| 24 | 26 | ||
| 25 | #define __NR_or1k_atomic __NR_arch_specific_syscall | 27 | #define __NR_or1k_atomic __NR_arch_specific_syscall |
diff --git a/arch/openrisc/kernel/entry.S b/arch/openrisc/kernel/entry.S index ddfcaa828b0e..c60a09df323f 100644 --- a/arch/openrisc/kernel/entry.S +++ b/arch/openrisc/kernel/entry.S | |||
| @@ -894,6 +894,16 @@ ENTRY(ret_from_fork) | |||
| 894 | l.jal schedule_tail | 894 | l.jal schedule_tail |
| 895 | l.nop | 895 | l.nop |
| 896 | 896 | ||
| 897 | /* Check if we are a kernel thread */ | ||
| 898 | l.sfeqi r20,0 | ||
| 899 | l.bf 1f | ||
| 900 | l.nop | ||
| 901 | |||
| 902 | /* ...we are a kernel thread so invoke the requested callback */ | ||
| 903 | l.jalr r20 | ||
| 904 | l.or r3,r22,r0 | ||
| 905 | |||
| 906 | 1: | ||
| 897 | /* _syscall_returns expect r11 to contain return value */ | 907 | /* _syscall_returns expect r11 to contain return value */ |
| 898 | l.lwz r11,PT_GPR11(r1) | 908 | l.lwz r11,PT_GPR11(r1) |
| 899 | 909 | ||
| @@ -915,26 +925,6 @@ ENTRY(ret_from_fork) | |||
| 915 | l.j _syscall_return | 925 | l.j _syscall_return |
| 916 | l.nop | 926 | l.nop |
| 917 | 927 | ||
| 918 | /* Since syscalls don't save call-clobbered registers, the args to | ||
| 919 | * kernel_thread_helper will need to be passed through callee-saved | ||
| 920 | * registers and copied to the parameter registers when the thread | ||
| 921 | * begins running. | ||
| 922 | * | ||
| 923 | * See arch/openrisc/kernel/process.c: | ||
| 924 | * The args are passed as follows: | ||
| 925 | * arg1 (r3) : passed in r20 | ||
| 926 | * arg2 (r4) : passed in r22 | ||
| 927 | */ | ||
| 928 | |||
| 929 | ENTRY(_kernel_thread_helper) | ||
| 930 | l.or r3,r20,r0 | ||
| 931 | l.or r4,r22,r0 | ||
| 932 | l.movhi r31,hi(kernel_thread_helper) | ||
| 933 | l.ori r31,r31,lo(kernel_thread_helper) | ||
| 934 | l.jr r31 | ||
| 935 | l.nop | ||
| 936 | |||
| 937 | |||
| 938 | /* ========================================================[ switch ] === */ | 928 | /* ========================================================[ switch ] === */ |
| 939 | 929 | ||
| 940 | /* | 930 | /* |
| @@ -1044,8 +1034,13 @@ ENTRY(_switch) | |||
| 1044 | /* Unwind stack to pre-switch state */ | 1034 | /* Unwind stack to pre-switch state */ |
| 1045 | l.addi r1,r1,(INT_FRAME_SIZE) | 1035 | l.addi r1,r1,(INT_FRAME_SIZE) |
| 1046 | 1036 | ||
| 1047 | /* Return via the link-register back to where we 'came from', where that can be | 1037 | /* Return via the link-register back to where we 'came from', where |
| 1048 | * either schedule() or return_from_fork()... */ | 1038 | * that may be either schedule(), ret_from_fork(), or |
| 1039 | * ret_from_kernel_thread(). If we are returning to a new thread, | ||
| 1040 | * we are expected to have set up the arg to schedule_tail already, | ||
| 1041 | * hence we do so here unconditionally: | ||
| 1042 | */ | ||
| 1043 | l.lwz r3,TI_STACK(r3) /* Load 'prev' as schedule_tail arg */ | ||
| 1049 | l.jr r9 | 1044 | l.jr r9 |
| 1050 | l.nop | 1045 | l.nop |
| 1051 | 1046 | ||
| @@ -1088,10 +1083,6 @@ ENTRY(sys_fork) | |||
| 1088 | l.j _fork_save_extra_regs_and_call | 1083 | l.j _fork_save_extra_regs_and_call |
| 1089 | l.addi r3,r1,0 | 1084 | l.addi r3,r1,0 |
| 1090 | 1085 | ||
| 1091 | ENTRY(sys_execve) | ||
| 1092 | l.j _sys_execve | ||
| 1093 | l.addi r6,r1,0 | ||
| 1094 | |||
| 1095 | ENTRY(sys_sigaltstack) | 1086 | ENTRY(sys_sigaltstack) |
| 1096 | l.j _sys_sigaltstack | 1087 | l.j _sys_sigaltstack |
| 1097 | l.addi r5,r1,0 | 1088 | l.addi r5,r1,0 |
diff --git a/arch/openrisc/kernel/process.c b/arch/openrisc/kernel/process.c index c35f3ab1a8d3..e0874b8e09e4 100644 --- a/arch/openrisc/kernel/process.c +++ b/arch/openrisc/kernel/process.c | |||
| @@ -109,66 +109,82 @@ void release_thread(struct task_struct *dead_task) | |||
| 109 | */ | 109 | */ |
| 110 | extern asmlinkage void ret_from_fork(void); | 110 | extern asmlinkage void ret_from_fork(void); |
| 111 | 111 | ||
| 112 | /* | ||
| 113 | * copy_thread | ||
| 114 | * @clone_flags: flags | ||
| 115 | * @usp: user stack pointer or fn for kernel thread | ||
| 116 | * @arg: arg to fn for kernel thread; always NULL for userspace thread | ||
| 117 | * @p: the newly created task | ||
| 118 | * @regs: CPU context to copy for userspace thread; always NULL for kthread | ||
| 119 | * | ||
| 120 | * At the top of a newly initialized kernel stack are two stacked pt_reg | ||
| 121 | * structures. The first (topmost) is the userspace context of the thread. | ||
| 122 | * The second is the kernelspace context of the thread. | ||
| 123 | * | ||
| 124 | * A kernel thread will not be returning to userspace, so the topmost pt_regs | ||
| 125 | * struct can be uninitialized; it _does_ need to exist, though, because | ||
| 126 | * a kernel thread can become a userspace thread by doing a kernel_execve, in | ||
| 127 | * which case the topmost context will be initialized and used for 'returning' | ||
| 128 | * to userspace. | ||
| 129 | * | ||
| 130 | * The second pt_reg struct needs to be initialized to 'return' to | ||
| 131 | * ret_from_fork. A kernel thread will need to set r20 to the address of | ||
| 132 | * a function to call into (with arg in r22); userspace threads need to set | ||
| 133 | * r20 to NULL in which case ret_from_fork will just continue a return to | ||
| 134 | * userspace. | ||
| 135 | * | ||
| 136 | * A kernel thread 'fn' may return; this is effectively what happens when | ||
| 137 | * kernel_execve is called. In that case, the userspace pt_regs must have | ||
| 138 | * been initialized (which kernel_execve takes care of, see start_thread | ||
| 139 | * below); ret_from_fork will then continue its execution causing the | ||
| 140 | * 'kernel thread' to return to userspace as a userspace thread. | ||
| 141 | */ | ||
| 142 | |||
| 112 | int | 143 | int |
| 113 | copy_thread(unsigned long clone_flags, unsigned long usp, | 144 | copy_thread(unsigned long clone_flags, unsigned long usp, |
| 114 | unsigned long unused, struct task_struct *p, struct pt_regs *regs) | 145 | unsigned long arg, struct task_struct *p, struct pt_regs *regs) |
| 115 | { | 146 | { |
| 116 | struct pt_regs *childregs; | 147 | struct pt_regs *userregs; |
| 117 | struct pt_regs *kregs; | 148 | struct pt_regs *kregs; |
| 118 | unsigned long sp = (unsigned long)task_stack_page(p) + THREAD_SIZE; | 149 | unsigned long sp = (unsigned long)task_stack_page(p) + THREAD_SIZE; |
| 119 | struct thread_info *ti; | ||
| 120 | unsigned long top_of_kernel_stack; | 150 | unsigned long top_of_kernel_stack; |
| 121 | 151 | ||
| 122 | top_of_kernel_stack = sp; | 152 | top_of_kernel_stack = sp; |
| 123 | 153 | ||
| 124 | p->set_child_tid = p->clear_child_tid = NULL; | 154 | p->set_child_tid = p->clear_child_tid = NULL; |
| 125 | 155 | ||
| 126 | /* Copy registers */ | 156 | /* Locate userspace context on stack... */ |
| 127 | /* redzone */ | 157 | sp -= STACK_FRAME_OVERHEAD; /* redzone */ |
| 128 | sp -= STACK_FRAME_OVERHEAD; | ||
| 129 | sp -= sizeof(struct pt_regs); | 158 | sp -= sizeof(struct pt_regs); |
| 130 | childregs = (struct pt_regs *)sp; | 159 | userregs = (struct pt_regs *) sp; |
| 131 | 160 | ||
| 132 | /* Copy parent registers */ | 161 | /* ...and kernel context */ |
| 133 | *childregs = *regs; | 162 | sp -= STACK_FRAME_OVERHEAD; /* redzone */ |
| 163 | sp -= sizeof(struct pt_regs); | ||
| 164 | kregs = (struct pt_regs *)sp; | ||
| 134 | 165 | ||
| 135 | if ((childregs->sr & SPR_SR_SM) == 1) { | 166 | if (unlikely(p->flags & PF_KTHREAD)) { |
| 136 | /* for kernel thread, set `current_thread_info' | 167 | memset(kregs, 0, sizeof(struct pt_regs)); |
| 137 | * and stackptr in new task | 168 | kregs->gpr[20] = usp; /* fn, kernel thread */ |
| 138 | */ | 169 | kregs->gpr[22] = arg; |
| 139 | childregs->sp = (unsigned long)task_stack_page(p) + THREAD_SIZE; | ||
| 140 | childregs->gpr[10] = (unsigned long)task_thread_info(p); | ||
| 141 | } else { | 170 | } else { |
| 142 | |||
