Improve context switching and scheduler APIs.
[monolithium.git] / kernel / src / process.c
1 /*
2  * process.c
3  *
4  * Copyright (C) 2017 Aleksandar Andrejevic <theflash@sdf.lonestar.org>
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU Affero General Public License as
8  * published by the Free Software Foundation, either version 3 of the
9  * License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU Affero General Public License for more details.
15  *
16  * You should have received a copy of the GNU Affero General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <process.h>
21 #include <thread.h>
22 #include <heap.h>
23 #include <syscalls.h>
24 #include <exception.h>
25 #include <exec/aout.h>
26 #include <cpu.h>
27
28 dword_t load_elf(handle_t file, thread_state_t *initial_state);
29 dword_t load_aout(handle_t file, thread_state_t *initial_state);
30
31 typedef dword_t (*loader_proc)(handle_t, thread_state_t*);
32
33 static dword_t pid_alloc_bitmap[MAX_PROCESSES / 32];
34 static lock_t pid_bitmap_lock = 0;
35
36 static loader_proc loaders[] =
37 {
38     load_elf,
39     load_aout,
40     NULL
41 };
42
43 process_t *kernel_process;
44
45 static dword_t alloc_pid()
46 {
47     int i;
48     dword_t pid = (dword_t)-1;
49
50     acquire_lock(&pid_bitmap_lock);
51
52     for (i = 0; i < MAX_PROCESSES; i++)
53     {
54         if (!test_bit(pid_alloc_bitmap, i))
55         {
56             pid = i;
57             set_bit(pid_alloc_bitmap, pid);
58             break;
59         }
60     }
61
62     release_lock(&pid_bitmap_lock);
63     return pid;
64 }
65
66 void destroy_process(process_t *proc)
67 {
68     int i;
69     proc->terminating = TRUE;
70
71     delete_address_space(&proc->memory_space);
72
73     acquire_resource_exclusive(&proc->handle_table_res);
74
75     for (i = 0; i < proc->handle_table_size / sizeof(proc->handle_table[i]); i++)
76     {
77         if (proc->handle_table[i].obj)
78         {
79             close_object_internal(proc->handle_table[i].obj);
80
81             proc->handle_table[i].obj = NULL;
82             proc->handle_count--;
83         }
84     }
85
86     release_resource(&proc->handle_table_res);
87
88     proc->terminated = TRUE;
89     dereference(&proc->header);
90 }
91
92 void process_cleanup(object_t *obj)
93 {
94     process_t *proc = (process_t*)obj;
95
96     free(proc->name);
97     dereference(&proc->current_user->header);
98
99     acquire_lock(&pid_bitmap_lock);
100     clear_bit(pid_alloc_bitmap, proc->pid);
101     release_lock(&pid_bitmap_lock);
102 }
103
104 process_t *get_current_process()
105 {
106     thread_t *thread = get_current_thread();
107     if (thread == NULL) return NULL;
108     return thread->owner_process;
109 }
110
111 sysret_t syscall_open_process(dword_t pid, handle_t *handle)
112 {
113     dword_t ret;
114     process_t *proc = NULL;
115
116     ret = enum_objects_by_type(OBJECT_PROCESS, (object_t**)&proc);
117     ASSERT(ret == ERR_SUCCESS || ret == ERR_NOMORE);
118
119     while (ret == ERR_SUCCESS)
120     {
121         if (proc->pid == pid) break;
122         ret = enum_objects_by_type(OBJECT_PROCESS, (object_t**)&proc);
123     }
124
125     ASSERT(ret == ERR_SUCCESS || ret == ERR_NOMORE);
126     if (ret == ERR_NOMORE) ret = ERR_NOTFOUND;
127
128     if (ret == ERR_SUCCESS)
129     {
130         if (proc->current_user->uid == get_current_uid() || check_privileges(PRIVILEGE_PROCESS_CONTROL))
131         {
132             ret = open_object(&proc->header, 0, handle);
133         }
134         else
135         {
136             ret = ERR_FORBIDDEN;
137         }
138
139         dereference(&proc->header);
140     }
141
142     return ret;
143 }
144
145 dword_t load_executable(handle_t file, thread_state_t *initial_state)
146 {
147     loader_proc *loader = loaders;
148
149     while (*loader)
150     {
151         dword_t ret = (*loader++)(file, initial_state);
152         if (ret == ERR_INVALID) continue;
153         return ret;
154     }
155
156     return ERR_INVALID;
157 }
158
159 void init_user_stack(uintptr_t *stack_pointer, process_params_t *parameters)
160 {
161     uintptr_t stack_top = *stack_pointer;
162
163     if (parameters->command_line)
164     {
165         *stack_pointer -= (strlen(parameters->command_line) + 1 + sizeof(uintptr_t) - 1) & ~(sizeof(uintptr_t) - 1);
166         strcpy((char*)*stack_pointer, parameters->command_line);
167         parameters->command_line = (char*)*stack_pointer;
168     }
169
170     *stack_pointer -= (sizeof(process_params_t) + sizeof(uintptr_t) - 1) & ~(sizeof(uintptr_t) - 1);
171     *(process_params_t*)*stack_pointer = *parameters;
172     parameters = (process_params_t*)*stack_pointer;
173
174     if ((stack_top - *stack_pointer) < MAX_PARAMETERS * sizeof(uintptr_t))
175     {
176         *stack_pointer = stack_top - MAX_PARAMETERS * sizeof(uintptr_t);
177     }
178
179     init_thread_stack(stack_pointer, parameters);
180 }
181
182 sysret_t syscall_create_process(const char *path, dword_t flags, process_params_t *parameters, handle_t *process_handle, handle_t *thread_handle)
183 {
184     dword_t ret;
185     handle_t file = INVALID_HANDLE;
186     process_t *current = get_current_process();
187     char *safe_path = NULL;
188     process_params_t safe_params;
189     handle_t safe_process_handle = INVALID_HANDLE;
190     handle_t safe_thread_handle = INVALID_HANDLE;
191     bool_t object_created = FALSE;
192
193     if (get_previous_mode() == USER_MODE)
194     {
195         if (!check_usermode(process_handle, sizeof(handle_t))) return ERR_BADPTR;
196         if (!check_usermode(thread_handle, sizeof(handle_t))) return ERR_BADPTR;
197
198         if (path)
199         {
200             safe_path = copy_user_string(path);
201             if (safe_path == NULL) return ERR_BADPTR;
202         }
203
204         if (parameters)
205         {
206             if (!check_usermode(parameters, sizeof(process_params_t)))
207             {
208                 free(safe_path);
209                 return ERR_BADPTR;
210             }
211
212             EH_TRY safe_params = *parameters;
213             EH_CATCH EH_ESCAPE(return ERR_BADPTR);
214             EH_DONE;
215
216             char *cmdline = copy_user_string(safe_params.command_line);
217             if (cmdline == NULL)
218             {
219                 free(safe_path);
220                 return ERR_BADPTR;
221             }
222
223             safe_params.command_line = cmdline;
224             parameters = &safe_params;
225         }
226     }
227     else
228     {
229         safe_path = (char*)path;
230
231         if (parameters)
232         {
233             safe_params = *parameters;
234             parameters = &safe_params;
235         }
236     }
237
238     if (safe_path)
239     {
240         ret = syscall(SYSCALL_OPEN_FILE, safe_path, &file, FILE_MODE_READ, 0);
241         if (ret != ERR_SUCCESS) return ret;
242     }
243
244     process_t *proc = (process_t*)malloc(sizeof(process_t));
245     if (proc == NULL) return ERR_NOMEMORY;
246     memset(proc, 0, sizeof(process_t));
247
248     init_object(&proc->header, NULL, OBJECT_PROCESS);
249
250     proc->pid = alloc_pid();
251     if (proc->pid == (dword_t)-1)
252     {
253         ret = ERR_NOMEMORY;
254         goto cleanup;
255     }
256
257     proc->name = strdup(path ? path : get_current_process()->name);
258
259     list_init(&proc->threads);
260     proc->thread_list_res = 0;
261
262     reference(&current->current_user->header);
263     proc->original_user = proc->current_user = current->current_user;
264
265     if (!(flags & PROCESS_CREATE_NO_INHERIT) && current != kernel_process)
266     {
267         dword_t i;
268         acquire_resource_shared(&current->handle_table_res);
269
270         proc->handle_table = (handle_info_t*)heap_alloc(&evictable_heap, STARTUP_HANDLE_TABLE_SIZE);
271         memset(proc->handle_table, 0, current->handle_table_size);
272         proc->handle_table_size = current->handle_table_size;
273         proc->handle_count = current->handle_count;
274         proc->handle_table_res = 0;
275
276         for (i = 0; i < current->handle_table_size; i++)
277         {
278             if (current->handle_table[i].obj)
279             {
280                 reference(current->handle_table[i].obj);
281                 current->handle_table[i].obj->open_count++;
282                 proc->handle_table[i] = current->handle_table[i];
283             }
284         }
285
286         release_resource(&current->handle_table_res);
287     }
288     else
289     {
290         proc->handle_table = (handle_info_t*)heap_alloc(&evictable_heap, STARTUP_HANDLE_TABLE_SIZE);
291         memset(proc->handle_table, 0, STARTUP_HANDLE_TABLE_SIZE);
292         proc->handle_table_size = STARTUP_HANDLE_TABLE_SIZE;
293         proc->handle_count = 0;
294         proc->handle_table_res = 0;
295     }
296
297     if (safe_path)
298     {
299         ret = create_address_space(PROCESS_POOL_ADDRESS, PROCESS_POOL_SIZE, &proc->memory_space);
300         if (ret != ERR_SUCCESS) goto cleanup;
301     }
302     else
303     {
304         ret = clone_address_space(&current->memory_space, &proc->memory_space);
305         if (ret != ERR_SUCCESS) goto cleanup;
306     }
307
308     syscall_clock_get_time(&proc->start_time);
309
310     ret = create_object(&proc->header);
311     if (ret != ERR_SUCCESS) goto cleanup;
312
313     object_created = TRUE;
314
315     thread_state_t initial_state;
316     memset(&initial_state, 0, sizeof(initial_state));
317
318     ret = open_object(&proc->header, 0, &safe_process_handle);
319     if (ret != ERR_SUCCESS) goto cleanup;
320
321     if (file != INVALID_HANDLE)
322     {
323         process_t *old_process = switch_process(proc);
324         ret = load_executable(file, &initial_state);
325         init_user_stack((uintptr_t*)&initial_state.regs.esp, parameters);
326         switch_process(old_process);
327         if (ret != ERR_SUCCESS) goto cleanup;
328     }
329     else
330     {
331         initial_state.regs = *get_current_thread()->last_context;
332         fpu_save(initial_state.fpu_state);
333         initial_state.regs.eax = CLONE_MAGIC;
334     }
335
336     if (!(flags & PROCESS_CREATE_NO_THREADS))
337     {
338         thread_t *thread;
339
340         void *kernel_stack = malloc(KERNEL_STACK_SIZE + sizeof(uintptr_t) - 1);
341         if (kernel_stack == NULL)
342         {
343             ret = ERR_NOMEMORY;
344             goto cleanup;
345         }
346
347         ret = commit_pages(kernel_stack, KERNEL_STACK_SIZE);
348         if (ret != ERR_SUCCESS) goto cleanup;
349
350         ret = create_thread_internal(proc,
351                                      &initial_state,
352                                      (flags & PROCESS_CREATE_FROZEN_THREAD) ? THREAD_CREATE_FROZEN : 0,
353                                      THREAD_PRIORITY_MID,
354                                      kernel_stack,
355                                      &thread);
356         if (ret != ERR_SUCCESS)
357         {
358             free(kernel_stack);
359             goto cleanup;
360         }
361
362         ret = open_object(&thread->header, 0, &safe_thread_handle);
363         if (ret != ERR_SUCCESS)
364         {
365             dereference(&thread->header);
366             goto cleanup;
367         }
368     }
369
370     EH_TRY
371     {
372         *process_handle = safe_process_handle;
373         *thread_handle = safe_thread_handle;
374     }
375     EH_CATCH
376     {
377         ret = ERR_BADPTR;
378     }
379     EH_DONE;
380
381 cleanup:
382     if (file != INVALID_HANDLE) syscall(SYSCALL_CLOSE_OBJECT, file);
383
384     if (object_created)
385     {
386         if (ret != ERR_SUCCESS)
387         {
388             syscall(SYSCALL_TERMINATE, proc->pid, 1);
389             if (safe_process_handle != INVALID_HANDLE) syscall_close_object(safe_process_handle);
390             if (safe_thread_handle != INVALID_HANDLE) syscall_close_object(safe_thread_handle);
391             dereference(&proc->header);
392         }
393     }
394     else
395     {
396         ASSERT(ret != ERR_SUCCESS);
397
398         if (proc->pid != (dword_t)-1)
399         {
400             acquire_lock(&pid_bitmap_lock);
401             clear_bit(pid_alloc_bitmap, proc->pid);
402             release_lock(&pid_bitmap_lock);
403         }
404
405         if (proc->name) free(proc->name);
406         free(proc);
407     }
408
409     if (get_previous_mode() == USER_MODE)
410     {
411         if (path != NULL) free(safe_path);
412         free(safe_params.command_line);
413     }
414
415     return ret;
416 }
417
418 sysret_t syscall_terminate(handle_t handle, dword_t exit_code)
419 {
420     process_t *proc;
421     thread_t *current_thread = get_current_thread();
422
423     if (handle == INVALID_HANDLE)
424     {
425         proc = get_current_process();
426         reference(&proc->header);
427     }
428     else
429     {
430         if (!reference_by_handle(handle, OBJECT_PROCESS, (object_t**)&proc)) return ERR_INVALID;
431     }
432
433     proc->terminating = TRUE;
434     proc->exit_code = exit_code;
435     syscall_clock_get_time(&proc->end_time);
436
437     acquire_resource_shared(&proc->thread_list_res);
438
439     while (proc->threads.next != &proc->threads)
440     {
441         list_entry_t *ptr;
442         thread_t *thread = NULL;
443
444         for (ptr = proc->threads.next; ptr != &proc->threads; ptr = ptr->next)
445         {
446             thread = CONTAINER_OF(ptr, thread_t, in_process_list);
447             if (thread != current_thread) break;
448         }
449
450         if (ptr != &proc->threads)
451         {
452             release_resource(&proc->thread_list_res);
453             terminate_thread_internal(thread, exit_code);
454             acquire_resource_shared(&proc->thread_list_res);
455         }
456         else
457         {
458             break;
459         }
460     }
461
462     release_resource(&proc->thread_list_res);
463
464     if (proc->threads.next == &proc->threads) destroy_process(proc);
465     dereference(&proc->header);
466
467     if (proc->threads.next == &proc->threads) return ERR_SUCCESS;
468     else return terminate_thread_internal(current_thread, exit_code);
469 }
470
471 sysret_t syscall_query_process(handle_t handle, process_info_t info_type, void *buffer, dword_t size)
472 {
473     dword_t ret = ERR_SUCCESS;
474     void *safe_buffer;
475
476     if (get_previous_mode() == USER_MODE)
477     {
478         if (!check_usermode(buffer, size)) return ERR_BADPTR;
479
480         safe_buffer = malloc(size);
481         if (safe_buffer == NULL) return ERR_NOMEMORY;
482         memset(safe_buffer, 0, size);
483     }
484     else
485     {
486         safe_buffer = buffer;
487     }
488
489     process_t *proc;
490
491     if (handle == INVALID_HANDLE)
492     {
493         proc = get_current_process();
494         reference(&proc->header);
495     }
496     else
497     {
498         if (!reference_by_handle(handle, OBJECT_PROCESS, (object_t**)&proc))
499         {
500             if (get_previous_mode() == USER_MODE) free(safe_buffer);
501             return ERR_INVALID;
502         }
503     }
504
505     switch (info_type)
506     {
507         case PROCESS_PID_INFO:
508         {
509             if (size >= sizeof(dword_t)) *((dword_t*)safe_buffer) = proc->pid;
510             else ret = ERR_SMALLBUF;
511
512             break;
513         }
514
515         case PROCESS_NAME_INFO:
516         {
517             if (size >= strlen(proc->name) + 1) strcpy(safe_buffer, proc->name);
518             else ret = ERR_SMALLBUF;
519
520             break;
521         }
522
523         case PROCESS_MEMORY_INFO:
524         {
525             ret = ERR_NOSYSCALL; // TODO
526             break;
527         }
528
529         case PROCESS_START_TIME_INFO:
530         {
531             if (size >= sizeof(clock_time_t)) *((clock_time_t*)safe_buffer) = proc->start_time;
532             else ret = ERR_SMALLBUF;
533
534             break;
535         }
536
537         case PROCESS_EXIT_CODE_INFO:
538         {
539             if (size >= sizeof(dword_t)) *((dword_t*)safe_buffer) = proc->exit_code;
540             else ret = ERR_SMALLBUF;
541
542             break;
543         }
544
545         case PROCESS_USER_INFO:
546         {
547             if (size >= 2 * sizeof(dword_t))
548             {
549                 ((dword_t*)safe_buffer)[0] = proc->original_user->uid;
550                 ((dword_t*)safe_buffer)[1] = proc->current_user->uid;
551             }
552             else
553             {
554                 ret = ERR_SMALLBUF;
555             }
556
557             break;
558         }
559
560         case PROCESS_THREAD_INFO:
561         {
562             if (size >= sizeof(dword_t))
563             {
564                 list_entry_t *ptr;
565                 dword_t i = 1, count = 0;
566
567                 acquire_resource_shared(&proc->thread_list_res);
568                 for (ptr = proc->threads.next; ptr != &proc->threads; ptr = ptr->next) count++;
569                 *((dword_t*)safe_buffer) = count;
570
571                 for (ptr = proc->threads.next; ptr != &proc->threads && i < (size / sizeof(dword_t)); ptr = ptr->next)
572                 {
573                     ((dword_t*)safe_buffer)[i++] = CONTAINER_OF(ptr, thread_t, in_process_list)->tid;
574                 }
575
576                 if (ptr != &proc->threads) ret = ERR_SMALLBUF;
577                 release_resource(&proc->thread_list_res);
578             }
579             else
580             {
581                 ret = ERR_SMALLBUF;
582             }
583
584             break;
585         }
586
587         case PROCESS_HANDLE_INFO:
588         {
589             if (size >= sizeof(dword_t))
590             {
591                 dword_t i, j = 1;
592
593                 acquire_resource_shared(&proc->handle_table_res);
594                 *((dword_t*)safe_buffer) = proc->handle_count;
595
596                 for (i = 0; i < proc->handle_table_size && j < (size / sizeof(dword_t)); i++)
597                 {
598                     if (proc->handle_table[i].obj) ((dword_t*)safe_buffer)[j++] = i;
599                 }
600
601                 if (i != proc->handle_table_size) ret = ERR_SMALLBUF;
602                 release_resource(&proc->handle_table_res);
603             }
604             else
605             {
606                 ret = ERR_SMALLBUF;
607             }
608
609             break;
610         }
611
612         default:
613         {
614             ret = ERR_INVALID;
615         }
616     }
617
618     if (get_previous_mode() == USER_MODE)
619     {
620         EH_TRY memcpy(buffer, safe_buffer, size);
621         EH_CATCH ret = ERR_BADPTR;
622         EH_DONE;
623
624         free(safe_buffer);
625     }
626
627     dereference(&proc->header);
628     return ret;
629 }
630
631 sysret_t syscall_enum_processes(dword_t *pid_array, dword_t *count)
632 {
633     dword_t ret = ERR_SUCCESS;
634     dword_t safe_count;
635     dword_t cnt = 0;
636
637     if (get_previous_mode() == USER_MODE)
638     {
639         if (!check_usermode(count, sizeof(*count))) return ERR_BADPTR;
640
641         EH_TRY safe_count = *count;
642         EH_CATCH EH_ESCAPE(return ERR_BADPTR);
643         EH_DONE;
644
645         if (!check_usermode(pid_array, sizeof(*pid_array) * safe_count))
646         {
647             ret = ERR_BADPTR;
648             goto cleanup;
649         }
650     }
651     else
652     {
653         safe_count = *count;
654     }
655
656     process_t *proc = NULL;
657     ret = enum_objects_by_type(OBJECT_PROCESS, (object_t**)&proc);
658
659     while (ret == ERR_SUCCESS)
660     {
661         if (cnt == safe_count)
662         {
663             ret = ERR_SMALLBUF;
664             break;
665         }
666
667         EH_TRY
668         {
669             pid_array[cnt++] = proc->pid;
670         }
671         EH_CATCH
672         {
673             ret = ERR_BADPTR;
674             EH_ESCAPE(break);
675         }
676         EH_DONE;
677
678         ret = enum_objects_by_type(OBJECT_PROCESS, (object_t**)&proc);
679     }
680
681     if (ret == ERR_NOMORE)
682     {
683         ret = ERR_SUCCESS;
684     }
685     else if (ret == ERR_SUCCESS)
686     {
687         dereference(&proc->header);
688         ret = ERR_SMALLBUF;
689     }
690
691 cleanup:
692     EH_TRY *count = cnt;
693     EH_CATCH ret = ERR_BADPTR;
694     EH_DONE;
695
696     return ret;
697 }
698
699 sysret_t syscall_wait_process(handle_t handle, dword_t timeout)
700 {
701     dword_t ret;
702     process_t *proc;
703
704     if (handle == INVALID_HANDLE)
705     {
706         proc = get_current_process();
707         reference(&proc->header);
708     }
709     else
710     {
711         if (!reference_by_handle(handle, OBJECT_PROCESS, (object_t**)&proc)) return ERR_INVALID;
712     }
713
714     wait_condition_t condition = { .type = WAIT_UNTIL_NOT_EQUAL, .pointer = &proc->terminated, .value = 0 };
715     ret = scheduler_wait(&condition, timeout);
716
717     dereference(&proc->header);
718     return ret;
719 }
720
721 process_t *switch_process(process_t *new_process)
722 {
723     thread_t *thread = get_current_thread();
724     process_t *old_process = get_current_process();
725
726     while (!__sync_bool_compare_and_swap(&thread->owner_process, old_process, new_process))
727     {
728         old_process = get_current_process();
729     }
730
731     set_page_directory(new_process->memory_space.page_directory);
732     return old_process;
733 }
734
735 void process_init(void)
736 {
737     memset(pid_alloc_bitmap, 0, sizeof(pid_alloc_bitmap));
738
739     kernel_process = (process_t*)malloc(sizeof(process_t));
740     ASSERT(kernel_process != NULL);
741
742     memset(kernel_process, 0, sizeof(process_t));
743     init_object(&kernel_process->header, NULL, OBJECT_PROCESS);
744
745     ASSERT(create_object(&kernel_process->header) == ERR_SUCCESS);
746
747     kernel_process->pid = 0;
748     set_bit(pid_alloc_bitmap, 0);
749     kernel_process->name = strdup("system");
750
751     memset(&kernel_process->memory_space, 0, sizeof(memory_address_space_t));
752     kernel_process->memory_space.page_directory = get_page_directory();
753
754     kernel_process->exit_code = 0;
755     kernel_process->terminating = FALSE;
756     kernel_process->terminated = FALSE;
757
758     syscall_clock_get_time(&kernel_process->start_time);
759
760     list_init(&kernel_process->threads);
761     kernel_process->thread_list_res = 0;
762
763     kernel_process->handle_table = (handle_info_t*)heap_alloc(&evictable_heap, STARTUP_HANDLE_TABLE_SIZE);
764     kernel_process->handle_table_size = STARTUP_HANDLE_TABLE_SIZE;
765     kernel_process->handle_count = 0;
766     kernel_process->handle_table_res = 0;
767 }