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