5ac00b3dbb99577bdd7f5a13281b073bdf13bd85
[lunaix-os.git] / lunaix-os / kernel / exe / exec.c
1 #include <lunaix/exec.h>
2 #include <lunaix/fs.h>
3 #include <lunaix/load.h>
4 #include <lunaix/mm/mmap.h>
5 #include <lunaix/mm/valloc.h>
6 #include <lunaix/mm/vmm.h>
7 #include <lunaix/process.h>
8 #include <lunaix/sched.h>
9 #include <lunaix/spike.h>
10 #include <lunaix/status.h>
11 #include <lunaix/syscall.h>
12 #include <lunaix/syscall_utils.h>
13
14 #include <asm/abi.h>
15 #include <asm/mm_defs.h>
16
17 #include <klibc/string.h>
18
19 #define _push(ptr, type, val)               \
20     do {                                    \
21         ptr = __ptr(&((type*)ptr)[-1]);   \
22         *((type*)ptr) = (val);            \
23     } while(0)
24
25 static int
26 __place_arrayptrs(struct exec_host* container, struct exec_arrptr* param)
27 {
28     int len;
29     ptr_t usp, usp_top;
30     ptr_t* ptrs;
31     unsigned int* reloc_off;
32     
33     usp_top = container->stack_top;
34     usp  = usp_top;
35     ptrs = (ptr_t*)param->raw;
36
37     if (!param->len) {
38         _push(usp, unsigned int, 0);
39
40         goto done;
41     }
42
43     len = param->len;
44     reloc_off = valloc(len * sizeof(unsigned int));
45
46     char* el;
47     size_t el_sz, sz_acc = 0;
48     for (int i = 0; i < len; i++)
49     {
50         el= (char*)ptrs[i];
51         el_sz = strnlen(el, MAX_PARAM_SIZE) + 1;
52
53         usp -= el_sz;
54         sz_acc += el_sz;
55         strncpy((char*)usp, el, el_sz);
56         
57         reloc_off[i] = sz_acc;
58     }
59
60     param->size = sz_acc;
61     
62     ptr_t* toc = (ptr_t*)(usp) - 1;
63     toc[0] = 0;
64
65     toc = &toc[-1];
66     for (int i = 0, j = len - 1; i < len; i++, j--)
67     {
68         toc[-i] = usp_top - (ptr_t)reloc_off[j];
69     }
70
71     toc[-len] = (ptr_t)len;
72
73     usp = __ptr(&toc[-len]);
74     param->copied = __ptr(&toc[-len + 1]);
75
76     vfree(reloc_off);
77
78 done:
79     container->stack_top = usp;
80     return 0;
81 }
82
83 void
84 exec_init_container(struct exec_host* param,
85                struct thread* thread,
86                ptr_t vms,
87                const char** argv,
88                const char** envp)
89 {
90     assert(thread->ustack);
91     ptr_t ustack_top = align_stack(thread->ustack->end - 1);
92     *param = (struct exec_host) 
93     { 
94         .proc = thread->process,
95         .vms_mnt = vms,
96         .exe = { 
97             .container = param 
98         },
99         .argv = { 
100             .raw = __ptr(argv) 
101         },
102         .envp = {
103             .raw = __ptr(envp)
104         },
105         .stack_top = ustack_top 
106     };
107 }
108
109 int
110 count_length(struct exec_arrptr* param)
111 {
112     int i = 0;
113     ptr_t* arr = (ptr_t*)param->raw;
114
115     if (!arr) {
116         param->len = 0;
117         return 0;
118     }
119
120     for (; i < MAX_PARAM_LEN && arr[i]; i++);
121     
122     param->len = i;
123     return i > 0 && arr[i] ? E2BIG : 0;
124 }
125
126 static void
127 save_process_cmd(struct proc_info* proc, struct exec_arrptr* argv)
128 {
129     ptr_t ptr, *argv_ptrs;
130     char* cmd_;
131
132     if (proc->cmd) {
133         vfree(proc->cmd);
134     }
135
136     argv_ptrs = (ptr_t*)argv->copied;
137     cmd_  = (char*)valloc(argv->size);
138
139     proc->cmd = cmd_;
140     while ((ptr = *argv_ptrs)) {
141         cmd_ = strcpy(cmd_, (const char*)ptr);
142         cmd_[-1] = ' ';
143         argv_ptrs++;
144     }
145     cmd_[-1] = '\0';
146     
147     proc->cmd_len = argv->size;
148 }
149
150
151 static int
152 __place_params(struct exec_host* container)
153 {
154     int errno = 0;
155
156     errno = __place_arrayptrs(container, &container->envp);
157     if (errno) {
158         goto done;
159     }
160
161     errno = __place_arrayptrs(container, &container->argv);
162
163 done:
164     return errno;
165 }
166
167 int
168 exec_load(struct exec_host* container, struct v_file* executable)
169 {
170     int errno = 0;
171
172     struct exec_arrptr* argv = &container->argv;
173     struct exec_arrptr* envp = &container->envp;
174
175     struct proc_info* proc = container->proc;
176     struct proc_mm* pvms = vmspace(proc);
177
178     if (pvms->heap) {
179         mem_unmap_region(container->vms_mnt, pvms->heap);
180         pvms->heap = NULL;
181     }
182
183     if (!active_vms(container->vms_mnt)) {
184         /*
185             TODO Setup remote mapping of user stack for later use
186         */
187         fail("not implemented");
188
189     }
190
191     if ((errno = count_length(argv))) {
192         goto done;
193     }
194
195     if ((errno = count_length(envp))) {
196         goto done;
197     }
198
199     errno = __place_params(container);
200     if (errno) {
201         goto done;
202     }
203
204     save_process_cmd(proc, argv);
205     container->inode = executable->inode;
206     
207     errno = load_executable(&container->exe, executable);
208     if (errno) {
209         goto done;
210     }
211
212 done:
213     return errno;
214 }
215
216 int
217 exec_load_byname(struct exec_host* container, const char* filename)
218 {
219     int errno = 0;
220     struct v_dnode* dnode;
221     struct v_file* file;
222
223     if ((errno = vfs_walk_proc(filename, &dnode, NULL, 0))) {
224         goto done;
225     }
226
227     if (!check_allow_execute(dnode->inode)) {
228         errno = EPERM;
229         goto done;
230     }
231
232     if (!check_itype_any(dnode->inode, F_FILE)) {
233         errno = EISDIR;
234         goto done;
235     }
236     
237     if ((errno = vfs_open(dnode, &file))) {
238         goto done;
239     }
240
241     errno = exec_load(container, file);
242
243     // It shouldn't matter which pid we passed. As the only reader is 
244     //  in current context and we must finish read at this point,
245     //  therefore the dead-lock condition will not exist and the pid
246     //  for arbitration has no use.
247     vfs_pclose(file, container->proc->pid);
248
249 done:
250     return errno;
251 }
252
253 int
254 exec_kexecve(const char* filename, const char* argv[], const char* envp[])
255 {
256     int errno = 0;
257     struct exec_host container;
258
259     assert(argv && envp);
260
261     exec_init_container(&container, current_thread, VMS_SELF, argv, envp);
262
263     errno = exec_load_byname(&container, filename);
264
265     if (errno) {
266         return errno;
267     }
268
269     ptr_t entry = container.exe.entry;
270
271     assert(entry);
272     j_usr(container.stack_top, entry);
273
274     // should not reach
275
276     return errno;
277 }
278
279 __DEFINE_LXSYSCALL3(int,
280                     execve,
281                     const char*,
282                     filename,
283                     const char*,
284                     argv[],
285                     const char*,
286                     envp[])
287 {
288     int errno = 0;
289     int acl;
290     struct exec_host container;
291
292     if (!argv || !envp) {
293         errno = EINVAL;
294         goto done;
295     }
296
297     exec_init_container(
298       &container, current_thread, VMS_SELF, argv, envp);
299
300     if ((errno = exec_load_byname(&container, filename))) {
301         goto done;
302     }
303
304     // we will jump to new entry point (_u_start) upon syscall's
305     // return so execve 'will not return' from the perspective of it's invoker
306     exec_arch_prepare_entry(current_thread, &container);
307
308     // these become meaningless once execved!
309     current_thread->ustack_top = 0;
310     signal_reset_context(&current_thread->sigctx);
311     signal_reset_registry(__current->sigreg);
312
313     acl = container.inode->acl;
314     if (fsacl_test(acl, suid)) {
315         current_set_euid(container.inode->uid);
316     }
317
318     if (fsacl_test(acl, sgid)) {
319         current_set_egid(container.inode->gid);
320     }
321
322 done:
323     // set return value
324     store_retval(DO_STATUS(errno));
325
326     // Always yield the process that want execve!
327     schedule();
328
329     // this will never get executed!
330     return -1;
331 }