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