Unifying the Lunaix's Physical Memory Model (#28)
[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 void
20 exec_init_container(struct exec_container* param,
21                struct thread* thread,
22                ptr_t vms,
23                const char** argv,
24                const char** envp)
25 {
26     assert(thread->ustack);
27     ptr_t ustack_top = align_stack(thread->ustack->end - 1);
28     *param = (struct exec_container){ .proc = thread->process,
29                                       .vms_mnt = vms,
30                                       .exe = { .container = param },
31                                       .argv_pp = { 0, 0 },
32                                       .argv = argv,
33                                       .envp = envp,
34                                       .stack_top = ustack_top };
35 }
36
37 size_t
38 args_ptr_size(const char** paramv)
39 {
40     size_t sz = 0;
41     while (*paramv) {
42         sz++;
43         paramv++;
44     }
45
46     return (sz + 1) * sizeof(ptr_t);
47 }
48
49 static ptr_t
50 copy_to_ustack(ptr_t stack_top, ptr_t* paramv)
51 {
52     ptr_t ptr;
53     size_t sz = 0;
54
55     while ((ptr = *paramv)) {
56         sz = strlen((const char*)ptr) + 1;
57
58         stack_top -= sz;
59         memcpy((void*)stack_top, (const void*)ptr, sz);
60         *paramv = stack_top;
61
62         paramv++;
63     }
64
65     return stack_top;
66 }
67
68 static void
69 save_process_cmd(struct proc_info* proc, ptr_t* argv)
70 {
71     ptr_t ptr, *_argv = argv;
72     size_t total_sz = 0;
73     while ((ptr = *_argv)) {
74         total_sz += strlen((const char*)ptr) + 1;
75         _argv++;
76     }
77
78     if (proc->cmd) {
79         vfree(proc->cmd);
80     }
81
82     char* cmd_ = (char*)valloc(total_sz);
83     proc->cmd = cmd_;
84     proc->cmd_len = total_sz;
85
86     while ((ptr = *argv)) {
87         cmd_ = strcpy(cmd_, (const char*)ptr);
88         cmd_[-1] = ' ';
89         argv++;
90     }
91     cmd_[-1] = '\0';
92 }
93
94 // externed from mm/dmm.c
95 extern int
96 create_heap(struct proc_mm* pvms, ptr_t addr);
97
98 int
99 exec_load(struct exec_container* container, struct v_file* executable)
100 {
101     int errno = 0;
102
103     const char **argv = container->argv, **envp = container->envp;
104     const char** argv_extra = container->argv_pp;
105
106     argv_extra[0] = executable->dnode->name.value;
107
108     if ((errno = load_executable(&container->exe, executable))) {
109         goto done;
110     }
111
112     struct proc_info* proc = container->proc;
113     struct proc_mm* pvms = vmspace(proc);
114
115     if (pvms->heap) {
116         mem_unmap_region(container->vms_mnt, pvms->heap);
117         pvms->heap = NULL;
118     }
119
120     if (!argv_extra[1]) {
121         // If loading a statically linked file, then heap remapping we can do,
122         // otherwise delayed.
123         create_heap(vmspace(proc), page_aligned(container->exe.end));
124     }
125
126     if (container->vms_mnt == VMS_SELF) {
127         // we are loading executable into current addr space
128
129         ptr_t ustack = container->stack_top;
130         size_t argv_len = 0, envp_len = 0;
131         ptr_t argv_ptr = 0, envp_ptr = 0;
132
133         if (envp) {
134             argv_len = args_ptr_size(envp);
135             ustack -= envp_len;
136             envp_ptr = ustack;
137
138             memcpy((void*)ustack, (const void*)envp, envp_len);
139             ustack = copy_to_ustack(ustack, (ptr_t*)ustack);
140         } else {
141             ustack -= sizeof(ptr_t);
142             *((ptr_t*)ustack) = 0;
143         }
144
145         if (argv) {            
146             argv_len = args_ptr_size(argv);
147             ustack -= argv_len;
148
149             memcpy((void*)ustack, (const void**)argv, argv_len);
150         } else {
151             ustack -= sizeof(ptr_t);
152             *((ptr_t*)ustack) = 0;
153         }
154
155         for (size_t i = 0; i < 2 && argv_extra[i]; i++) {
156             ustack -= sizeof(ptr_t);
157             *((ptr_t*)ustack) = (ptr_t)argv_extra[i];
158             argv_len += sizeof(ptr_t);
159         }
160
161         argv_ptr = ustack;
162         ustack = copy_to_ustack(ustack, (ptr_t*)ustack);
163
164         save_process_cmd(proc, (ptr_t*)argv_ptr);
165
166         // four args (arg{c|v}, env{c|p}) for main
167         struct uexec_param* exec_param = &((struct uexec_param*)ustack)[-1];
168
169         container->stack_top = (ptr_t)exec_param;
170
171         *exec_param =
172           (struct uexec_param){ .argc = (argv_len - 1) / sizeof(ptr_t),
173                                 .argv = (char**)argv_ptr,
174                                 .envc = (envp_len - 1) / sizeof(ptr_t),
175                                 .envp = (char**)envp_ptr };
176     } else {
177         /*
178             TODO Inject to remote user stack with our procvm_remote toolsets
179                  Need a better way to factorise the argv/envp length calculating
180         */
181         fail("not implemented");
182
183     }
184
185 done:
186     return errno;
187 }
188
189 int
190 exec_load_byname(struct exec_container* container, const char* filename)
191 {
192     int errno = 0;
193     struct v_dnode* dnode;
194     struct v_file* file;
195
196     if ((errno = vfs_walk_proc(filename, &dnode, NULL, 0))) {
197         goto done;
198     }
199
200     if ((errno = vfs_open(dnode, &file))) {
201         goto done;
202     }
203
204     errno = exec_load(container, file);
205
206     // It shouldn't matter which pid we passed. As the only reader is 
207     //  in current context and we must finish read at this point,
208     //  therefore the dead-lock condition will not exist and the pid
209     //  for arbitration has no use.
210     vfs_pclose(file, container->proc->pid);
211
212 done:
213     return errno;
214 }
215
216 int
217 exec_kexecve(const char* filename, const char* argv[], const char* envp[])
218 {
219     int errno = 0;
220     struct exec_container container;
221
222     exec_init_container(&container, current_thread, VMS_SELF, argv, envp);
223
224     errno = exec_load_byname(&container, filename);
225
226     if (errno) {
227         return errno;
228     }
229
230     ptr_t entry = container.exe.entry;
231
232     assert(entry);
233     j_usr(container.stack_top, entry);
234
235     // should not reach
236
237     return errno;
238 }
239
240 __DEFINE_LXSYSCALL3(int,
241                     execve,
242                     const char*,
243                     filename,
244                     const char*,
245                     argv[],
246                     const char*,
247                     envp[])
248 {
249     int errno = 0;
250     struct exec_container container;
251
252     exec_init_container(
253       &container, current_thread, VMS_SELF, argv, envp);
254
255     if ((errno = exec_load_byname(&container, filename))) {
256         goto done;
257     }
258
259     // we will jump to new entry point (_u_start) upon syscall's
260     // return so execve 'will not return' from the perspective of it's invoker
261     eret_target(current_thread) = container.exe.entry;
262     eret_stack(current_thread) = container.stack_top;
263
264     // these become meaningless once execved!
265     current_thread->ustack_top = 0;
266     signal_reset_context(&current_thread->sigctx);
267     signal_reset_register(__current->sigreg);
268
269 done:
270     // set return value
271     store_retval(DO_STATUS(errno));
272
273     // Always yield the process that want execve!
274     schedule();
275
276     // this will never get executed!
277     return -1;
278 }