Architectural Support: x86_64 (#37)
[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 <sys/abi.h>
15 #include <sys/mm/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     
206     errno = load_executable(&container->exe, executable);
207     if (errno) {
208         goto done;
209     }
210
211 done:
212     return errno;
213 }
214
215 int
216 exec_load_byname(struct exec_host* container, const char* filename)
217 {
218     int errno = 0;
219     struct v_dnode* dnode;
220     struct v_file* file;
221
222     if ((errno = vfs_walk_proc(filename, &dnode, NULL, 0))) {
223         goto done;
224     }
225
226     if ((errno = vfs_open(dnode, &file))) {
227         goto done;
228     }
229
230     if (!check_itype_any(dnode->inode, F_FILE)) {
231         errno = EISDIR;
232         goto done;
233     }
234
235     errno = exec_load(container, file);
236
237     // It shouldn't matter which pid we passed. As the only reader is 
238     //  in current context and we must finish read at this point,
239     //  therefore the dead-lock condition will not exist and the pid
240     //  for arbitration has no use.
241     vfs_pclose(file, container->proc->pid);
242
243 done:
244     return errno;
245 }
246
247 int
248 exec_kexecve(const char* filename, const char* argv[], const char* envp[])
249 {
250     int errno = 0;
251     struct exec_host container;
252
253     assert(argv && envp);
254
255     exec_init_container(&container, current_thread, VMS_SELF, argv, envp);
256
257     errno = exec_load_byname(&container, filename);
258
259     if (errno) {
260         return errno;
261     }
262
263     ptr_t entry = container.exe.entry;
264
265     assert(entry);
266     j_usr(container.stack_top, entry);
267
268     // should not reach
269
270     return errno;
271 }
272
273 __DEFINE_LXSYSCALL3(int,
274                     execve,
275                     const char*,
276                     filename,
277                     const char*,
278                     argv[],
279                     const char*,
280                     envp[])
281 {
282     int errno = 0;
283     struct exec_host container;
284
285     if (!argv || !envp) {
286         errno = EINVAL;
287         goto done;
288     }
289
290     exec_init_container(
291       &container, current_thread, VMS_SELF, argv, envp);
292
293     if ((errno = exec_load_byname(&container, filename))) {
294         goto done;
295     }
296
297     // we will jump to new entry point (_u_start) upon syscall's
298     // return so execve 'will not return' from the perspective of it's invoker
299     exec_arch_prepare_entry(current_thread, &container);
300
301     // these become meaningless once execved!
302     current_thread->ustack_top = 0;
303     signal_reset_context(&current_thread->sigctx);
304     signal_reset_registry(__current->sigreg);
305
306 done:
307     // set return value
308     store_retval(DO_STATUS(errno));
309
310     // Always yield the process that want execve!
311     schedule();
312
313     // this will never get executed!
314     return -1;
315 }