refactor: striped more arch-related code from the kernel code base
[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/spike.h>
9 #include <lunaix/status.h>
10 #include <lunaix/syscall.h>
11 #include <lunaix/syscall_utils.h>
12
13 #include <sys/abi.h>
14
15 #include <klibc/string.h>
16
17 void
18 exec_container(struct exec_container* param,
19                struct proc_info* proc,
20                ptr_t vms,
21                const char** argv,
22                const char** envp)
23 {
24     *param = (struct exec_container){ .proc = proc,
25                                       .vms_mnt = vms,
26                                       .exe = { .container = param },
27                                       .argv_pp = { 0, 0 },
28                                       .argv = argv,
29                                       .envp = envp };
30 }
31
32 size_t
33 args_ptr_size(const char** paramv)
34 {
35     size_t sz = 0;
36     while (*paramv) {
37         sz++;
38         paramv++;
39     }
40
41     return (sz + 1) * sizeof(ptr_t);
42 }
43
44 ptr_t
45 copy_to_ustack(ptr_t stack_top, ptr_t* paramv)
46 {
47     ptr_t ptr;
48     size_t sz = 0;
49
50     while ((ptr = *paramv)) {
51         sz = strlen((const char*)ptr) + 1;
52
53         stack_top -= sz;
54         memcpy((void*)stack_top, (const void*)ptr, sz);
55         *paramv = stack_top;
56
57         paramv++;
58     }
59
60     return stack_top;
61 }
62
63 // externed from mm/dmm.c
64 extern int
65 create_heap(struct proc_mm* pvms, ptr_t addr);
66
67 int
68 exec_load(struct exec_container* container, struct v_file* executable)
69 {
70     int errno = 0;
71
72     const char **argv = container->argv, **envp = container->envp;
73     const char** argv_extra = container->argv_pp;
74
75     argv_extra[0] = executable->dnode->name.value;
76
77     if ((errno = load_executable(&container->exe, executable))) {
78         goto done;
79     }
80
81     struct proc_mm* pvms = &container->proc->mm;
82
83     if (pvms->heap) {
84         mem_unmap_region(container->vms_mnt, pvms->heap);
85         pvms->heap = NULL;
86     }
87
88     if (!argv_extra[1]) {
89         // If loading a statically linked file, then heap remapping we can do,
90         // otherwise delayed.
91         create_heap(&container->proc->mm, PG_ALIGN(container->exe.end));
92     }
93
94     if (container->vms_mnt == VMS_SELF) {
95         // we are loading executable into current addr space
96
97         ptr_t ustack = USTACK_TOP;
98         size_t argv_len = 0, envp_len = 0;
99         ptr_t argv_ptr = 0, envp_ptr = 0;
100
101         if (envp) {
102             argv_len = args_ptr_size(envp);
103             ustack -= envp_len;
104             envp_ptr = ustack;
105
106             memcpy((void*)ustack, (const void*)envp, envp_len);
107             ustack = copy_to_ustack(ustack, (ptr_t*)ustack);
108         }
109
110         if (argv) {
111             argv_len = args_ptr_size(argv);
112             ustack -= argv_len;
113
114             memcpy((void*)ustack, (const void**)argv, argv_len);
115             for (size_t i = 0; i < 2 && argv_extra[i]; i++) {
116                 ustack -= sizeof(ptr_t);
117                 *((ptr_t*)ustack) = (ptr_t)argv_extra[i];
118                 argv_len += sizeof(ptr_t);
119             }
120
121             argv_ptr = ustack;
122             ustack = copy_to_ustack(ustack, (ptr_t*)ustack);
123         }
124
125         // four args (arg{c|v}, env{c|p}) for main
126         struct uexec_param* exec_param = &((struct uexec_param*)ustack)[-1];
127
128         container->stack_top = (ptr_t)exec_param;
129
130         *exec_param =
131           (struct uexec_param){ .argc = (argv_len - 1) / sizeof(ptr_t),
132                                 .argv = (char**)argv_ptr,
133                                 .envc = (envp_len - 1) / sizeof(ptr_t),
134                                 .envp = (char**)envp_ptr };
135     } else {
136         /*
137             TODO need to find a way to inject argv and envp remotely
138                  this is for the support of kernel level implementation of
139                  posix_spawn
140
141             IDEA
142                 1. Allocate a orphaned physical page (i.e., do not belong to any
143                 VMA)
144                 2. Mounted to a temporary mount point in current VMA, (i.e.,
145                 PG_MOUNT_*)
146                 3. Do setup there.
147                 4. Unmount then mounted to the foreign VMA as the first stack
148                 page.
149         */
150         fail("not implemented");
151     }
152
153 done:
154     return errno;
155 }
156
157 int
158 exec_load_byname(struct exec_container* container, const char* filename)
159 {
160     int errno = 0;
161     struct v_dnode* dnode;
162     struct v_file* file;
163
164     if ((errno = vfs_walk_proc(filename, &dnode, NULL, 0))) {
165         goto done;
166     }
167
168     if ((errno = vfs_open(dnode, &file))) {
169         goto done;
170     }
171
172     errno = exec_load(container, file);
173
174 done:
175     return errno;
176 }
177
178 int
179 exec_kexecve(const char* filename, const char* argv[], const char* envp[])
180 {
181     int errno = 0;
182     struct exec_container container;
183
184     exec_container(
185       &container, (struct proc_info*)__current, VMS_SELF, argv, envp);
186
187     errno = exec_load_byname(&container, filename);
188
189     if (errno) {
190         return errno;
191     }
192
193     ptr_t entry = container.exe.entry;
194
195     assert(entry);
196     j_usr(container.stack_top, entry);
197
198     // should not reach
199
200     return errno;
201 }
202
203 __DEFINE_LXSYSCALL3(int,
204                     execve,
205                     const char*,
206                     filename,
207                     const char*,
208                     argv[],
209                     const char*,
210                     envp[])
211 {
212     int errno = 0;
213     struct exec_container container;
214
215     exec_container(
216       &container, (struct proc_info*)__current, VMS_SELF, argv, envp);
217
218     if ((errno = exec_load_byname(&container, filename))) {
219         goto done;
220     }
221
222     // we will jump to new entry point (_u_start) upon syscall's
223     // return so execve 'will not return' from the perspective of it's invoker
224     eret_target(__current) = container.exe.entry;
225     eret_stack(__current) = container.stack_top;
226
227     // these become meaningless once execved!
228     __current->ustack_top = 0;
229     proc_clear_signal(__current);
230
231 done:
232     // set return value
233     store_retval(DO_STATUS(errno));
234
235     // Always yield the process that want execve!
236     schedule();
237
238     // this will never get executed!
239     return -1;
240 }