edit readme
[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 exec_str_size(const char** str_arr, size_t* length)
33 {
34     if (!str_arr) {
35         *length = 0;
36         return 0;
37     }
38
39     const char* chr = *str_arr;
40     size_t sz = 0, len = 0;
41
42     while (chr) {
43         sz += strlen(chr);
44         len++;
45
46         chr = *(str_arr++);
47     }
48
49     *length = len;
50     return sz + sizeof(char*);
51 }
52
53 // externed from mm/dmm.c
54 extern int
55 create_heap(struct proc_mm* pvms, ptr_t addr);
56
57 int
58 exec_load(struct exec_container* container, struct v_file* executable)
59 {
60     int errno = 0;
61
62     const char **argv = container->argv, **envp = container->envp;
63     size_t argv_len, envp_len;
64     size_t sz_argv = exec_str_size(argv, &argv_len);
65     size_t sz_envp = exec_str_size(envp, &envp_len);
66     size_t var_sz = ROUNDUP(sz_envp, PG_SIZE);
67     const char** argv_extra = container->argv_pp;
68
69     argv_extra[0] = executable->dnode->name.value;
70
71     if (var_sz / PG_SIZE > MAX_VAR_PAGES) {
72         errno = E2BIG;
73         goto done;
74     }
75
76     if ((errno = load_executable(&container->exe, executable))) {
77         goto done;
78     }
79
80     struct proc_mm* pvms = &container->proc->mm;
81
82     // A dedicated place for process variables (e.g. envp)
83     struct mmap_param map_vars = { .pvms = pvms,
84                                    .vms_mnt = container->vms_mnt,
85                                    .flags = MAP_ANON | MAP_PRIVATE | MAP_FIXED,
86                                    .type = REGION_TYPE_VARS,
87                                    .proct = PROT_READ,
88                                    .mlen = MAX_VAR_PAGES * PG_SIZE };
89
90     void* mapped;
91
92     if (pvms->heap) {
93         mem_unmap_region(container->vms_mnt, pvms->heap);
94         pvms->heap = NULL;
95     }
96
97     if (!argv_extra[1]) {
98         // If loading a statically linked file, then heap remapping we can do,
99         // otherwise delayed.
100         create_heap(&container->proc->mm, PG_ALIGN(container->exe.end));
101     }
102
103     if ((errno = mem_map(&mapped, NULL, UMMAP_END, NULL, &map_vars))) {
104         goto done;
105     }
106
107     if (container->vms_mnt == VMS_SELF) {
108         // we are loading executable into current addr space
109
110         // make some handy infos available to user space
111         if (envp)
112             memcpy(mapped, (void*)envp, sz_envp);
113
114         void* ustack = (void*)USTACK_TOP;
115
116         if (argv) {
117             ustack = (void*)((ptr_t)ustack - sz_argv);
118             memcpy(ustack, (void*)argv, sz_argv);
119         }
120
121         for (size_t i = 0; i < 2 && argv_extra[i]; i++, argv_len++) {
122             const char* extra_arg = argv_extra[i];
123             size_t str_len = strlen(extra_arg);
124
125             ustack = (void*)((ptr_t)ustack - str_len);
126             memcpy(ustack, (const void*)extra_arg, str_len);
127         }
128
129         // four args (arg{c|v}, env{c|p}) for main
130         struct uexec_param* exec_param = &((struct uexec_param*)ustack)[-1];
131
132         container->stack_top = (ptr_t)exec_param;
133
134         *exec_param = (struct uexec_param){
135             .argc = argv_len, .argv = ustack, .envc = envp_len, .envp = mapped
136         };
137
138     } else {
139         /*
140             TODO need to find a way to inject argv and envp remotely
141                  this is for the support of kernel level implementation of
142                  posix_spawn
143
144             IDEA
145                 1. Allocate a orphaned physical page (i.e., do not belong to any
146                 VMA)
147                 2. Mounted to a temporary mount point in current VMA, (i.e.,
148                 PG_MOUNT_*)
149                 3. Do setup there.
150                 4. Unmount then mounted to the foreign VMA as the first stack
151                 page.
152         */
153         fail("not implemented");
154     }
155
156 done:
157     return errno;
158 }
159
160 int
161 exec_load_byname(struct exec_container* container, const char* filename)
162 {
163     int errno = 0;
164     struct v_dnode* dnode;
165     struct v_file* file;
166
167     if ((errno = vfs_walk_proc(filename, &dnode, NULL, 0))) {
168         goto done;
169     }
170
171     if ((errno = vfs_open(dnode, &file))) {
172         goto done;
173     }
174
175     errno = exec_load(container, file);
176
177 done:
178     return errno;
179 }
180
181 int
182 exec_kexecve(const char* filename, const char* argv[], const char* envp[])
183 {
184     int errno = 0;
185     struct exec_container container;
186
187     exec_container(
188       &container, (struct proc_info*)__current, VMS_SELF, argv, envp);
189
190     errno = exec_load_byname(&container, filename);
191
192     if (errno) {
193         return errno;
194     }
195
196     ptr_t entry = container.exe.entry;
197
198     assert(entry);
199     j_usr(container.stack_top, entry);
200
201     // should not reach
202
203     return errno;
204 }
205
206 __DEFINE_LXSYSCALL3(int,
207                     execve,
208                     const char*,
209                     filename,
210                     const char*,
211                     argv[],
212                     const char*,
213                     envp[])
214 {
215     int errno = 0;
216     struct exec_container container;
217
218     exec_container(
219       &container, (struct proc_info*)__current, VMS_SELF, argv, envp);
220
221     if ((errno = exec_load_byname(&container, filename))) {
222         goto done;
223     }
224
225     // we will jump to new entry point (_u_start) upon syscall's
226     // return so execve 'will not return' from the perspective of it's invoker
227     volatile struct exec_param* execp = __current->intr_ctx.execp;
228     execp->esp = container.stack_top;
229     execp->eip = container.exe.entry;
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 }