Decoupling Architectural-specific Code (#35)
[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     if (!check_itype_any(dnode->inode, F_FILE)) {
205         errno = EISDIR;
206         goto done;
207     }
208
209     errno = exec_load(container, file);
210
211     // It shouldn't matter which pid we passed. As the only reader is 
212     //  in current context and we must finish read at this point,
213     //  therefore the dead-lock condition will not exist and the pid
214     //  for arbitration has no use.
215     vfs_pclose(file, container->proc->pid);
216
217 done:
218     return errno;
219 }
220
221 int
222 exec_kexecve(const char* filename, const char* argv[], const char* envp[])
223 {
224     int errno = 0;
225     struct exec_container container;
226
227     exec_init_container(&container, current_thread, VMS_SELF, argv, envp);
228
229     errno = exec_load_byname(&container, filename);
230
231     if (errno) {
232         return errno;
233     }
234
235     ptr_t entry = container.exe.entry;
236
237     assert(entry);
238     j_usr(container.stack_top, entry);
239
240     // should not reach
241
242     return errno;
243 }
244
245 __DEFINE_LXSYSCALL3(int,
246                     execve,
247                     const char*,
248                     filename,
249                     const char*,
250                     argv[],
251                     const char*,
252                     envp[])
253 {
254     int errno = 0;
255     struct exec_container container;
256
257     exec_init_container(
258       &container, current_thread, VMS_SELF, argv, envp);
259
260     if ((errno = exec_load_byname(&container, filename))) {
261         goto done;
262     }
263
264     // we will jump to new entry point (_u_start) upon syscall's
265     // return so execve 'will not return' from the perspective of it's invoker
266     hart_flow_redirect(current_thread->hstate, 
267                           container.exe.entry, container.stack_top);
268
269     // these become meaningless once execved!
270     current_thread->ustack_top = 0;
271     signal_reset_context(&current_thread->sigctx);
272     signal_reset_registry(__current->sigreg);
273
274 done:
275     // set return value
276     store_retval(DO_STATUS(errno));
277
278     // Always yield the process that want execve!
279     schedule();
280
281     // this will never get executed!
282     return -1;
283 }