470a985659d6c7eb7bf5cfdfbf4c8f9b356d9967
[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 typedef dword_t (*loader_proc)(handle_t, process_params_t*, thread_state_t*);
29
30 static dword_t pid_alloc_bitmap[MAX_PROCESSES / 32];
31 static lock_t pid_bitmap_lock = 0;
32
33 static loader_proc loaders[] =
34 {
35     load_aout,
36     NULL
37 };
38
39 process_t *kernel_process;
40
41 static dword_t alloc_pid()
42 {
43     int i;
44     dword_t pid = (dword_t)-1;
45
46     acquire_lock(&pid_bitmap_lock);
47
48     for (i = 0; i < MAX_PROCESSES; i++)
49     {
50         if (!test_bit(pid_alloc_bitmap, i))
51         {
52             pid = i;
53             set_bit(pid_alloc_bitmap, pid);
54             break;
55         }
56     }
57
58     release_lock(&pid_bitmap_lock);
59     return pid;
60 }
61
62 void destroy_process(process_t *proc)
63 {
64     int i;
65     proc->terminating = TRUE;
66
67     delete_address_space(&proc->memory_space);
68
69     acquire_resource_exclusive(&proc->handle_table_res);
70
71     for (i = 0; i < proc->handle_table_size / sizeof(object_t*); i++)
72     {
73         if (proc->handle_table[i].obj)
74         {
75             close_object_internal(proc->handle_table[i].obj);
76
77             proc->handle_table[i].obj = NULL;
78             proc->handle_count--;
79         }
80     }
81
82     release_resource(&proc->handle_table_res);
83
84     proc->terminated = TRUE;
85     dereference(&proc->header);
86 }
87
88 void process_cleanup(object_t *obj)
89 {
90     process_t *proc = (process_t*)obj;
91
92     free(proc->name);
93     dereference(&proc->current_user->header);
94
95     acquire_lock(&pid_bitmap_lock);
96     clear_bit(pid_alloc_bitmap, proc->pid);
97     release_lock(&pid_bitmap_lock);
98 }
99
100 process_t *get_current_process()
101 {
102     thread_t *thread = get_current_thread();
103     if (thread == NULL) return NULL;
104     return thread->owner_process;
105 }
106
107 dword_t open_process(dword_t pid, handle_t *handle)
108 {
109     dword_t ret;
110     process_t *proc = NULL;
111
112     ret = enum_objects_by_type(OBJECT_PROCESS, (object_t**)&proc);
113     ASSERT(ret == ERR_SUCCESS || ret == ERR_NOMORE);
114
115     while (ret == ERR_SUCCESS)
116     {
117         if (proc->pid == pid) break;
118         ret = enum_objects_by_type(OBJECT_PROCESS, (object_t**)&proc);
119     }
120
121     ASSERT(ret == ERR_SUCCESS || ret == ERR_NOMORE);
122     if (ret == ERR_NOMORE) ret = ERR_NOTFOUND;
123
124     if (ret == ERR_SUCCESS)
125     {
126         if (proc->current_user->uid == get_user_id() || check_privileges(PRIVILEGE_PROCESS_CONTROL))
127         {
128             ret = open_object(&proc->header, 0, handle);
129         }
130         else
131         {
132             ret = ERR_FORBIDDEN;
133         }
134
135         dereference(&proc->header);
136     }
137
138     return ret;
139 }
140
141 dword_t load_executable(handle_t file, process_params_t *parameters, thread_state_t *initial_state)
142 {
143     loader_proc *loader = loaders;
144
145     while (*loader)
146     {
147         dword_t ret = (*loader++)(file, parameters, initial_state);
148         if (ret == ERR_INVALID) continue;
149         return ret;
150     }
151
152     return ERR_INVALID;
153 }
154
155 void init_user_stack(uintptr_t *stack_pointer, process_params_t *parameters)
156 {
157     static const byte_t program_end_code[] = {
158         /* push eax */
159         0x50,
160
161         /* push INVALID_HANDLE */
162         0x68,
163         INVALID_HANDLE & 0xFF,
164         (INVALID_HANDLE >> 8) & 0xFF,
165         (INVALID_HANDLE >> 16) & 0xFF,
166         (INVALID_HANDLE >> 24) & 0xFF,
167
168         /* mov eax, SYSCALL_TERMINATE */
169         0xB8,
170         SYSCALL_TERMINATE & 0xFF,
171         (SYSCALL_TERMINATE >> 8) & 0xFF,
172         (SYSCALL_TERMINATE >> 16) & 0xFF,
173         SYSCALL_TERMINATE >> 24,
174
175         /* mov edx, esp */
176         0x8B, 0xD4,
177
178         /* int SYSCALL_INTERRUPT */
179         0xCD, SYSCALL_INTERRUPT
180     };
181
182     uintptr_t stack_top = *stack_pointer;
183
184     *stack_pointer -= (sizeof(process_params_t) + sizeof(uintptr_t) - 1) & ~(sizeof(uintptr_t) - 1);
185     *(process_params_t*)*stack_pointer = *parameters;
186     uintptr_t parameters_addr = *stack_pointer;
187
188     *stack_pointer -= (sizeof(program_end_code) + sizeof(uintptr_t) - 1) & ~(sizeof(uintptr_t) - 1);
189     memcpy((void*)*stack_pointer, program_end_code, sizeof(program_end_code));
190     uintptr_t end_code_addr = *stack_pointer;
191
192     if ((stack_top - *stack_pointer) < MAX_PARAMETERS * sizeof(uintptr_t))
193     {
194         *stack_pointer = stack_top - MAX_PARAMETERS * sizeof(uintptr_t);
195     }
196
197     push_to_stack(stack_pointer, parameters_addr);
198     push_to_stack(stack_pointer, end_code_addr);
199 }
200
201 dword_t create_process(const char *path, dword_t flags, process_params_t *parameters, handle_t *process_handle, handle_t *thread_handle)
202 {
203     dword_t ret;
204     handle_t file = INVALID_HANDLE;
205     process_t *current = get_current_process();
206     char *safe_path = NULL;
207     process_params_t safe_params;
208     handle_t safe_process_handle = INVALID_HANDLE;
209     handle_t safe_thread_handle = INVALID_HANDLE;
210     bool_t object_created = FALSE;
211
212     if (get_previous_mode() == USER_MODE)
213     {
214         if (!check_usermode(process_handle, sizeof(handle_t))) return ERR_BADPTR;
215         if (!check_usermode(thread_handle, sizeof(handle_t))) return ERR_BADPTR;
216
217         if (path)
218         {
219             safe_path = copy_user_string(path);
220             if (safe_path == NULL) return ERR_BADPTR;
221         }
222
223         if (parameters)
224         {
225             if (!check_usermode(parameters, sizeof(process_params_t))) return ERR_BADPTR;
226
227             EH_TRY safe_params = *parameters;
228             EH_CATCH EH_ESCAPE(return ERR_BADPTR);
229             EH_DONE;
230         }
231     }
232     else
233     {
234         safe_path = (char*)path;
235         if (parameters) safe_params = *parameters;
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     proc->header.name = NULL;
249     proc->header.type = OBJECT_PROCESS;
250
251     proc->pid = alloc_pid();
252     if (proc->pid == (dword_t)-1)
253     {
254         ret = ERR_NOMEMORY;
255         goto cleanup;
256     }
257
258     proc->name = strdup(path ? path : get_current_process()->name);
259
260     list_init(&proc->threads);
261     proc->thread_list_res = 0;
262
263     reference(&current->current_user->header);
264     proc->original_user = proc->current_user = current->current_user;
265
266     if (!(flags & PROCESS_CREATE_NO_INHERIT) && current != kernel_process)
267     {
268         dword_t i;
269         acquire_resource_shared(&current->handle_table_res);
270
271         proc->handle_table = (handle_info_t*)malloc(current->handle_table_size);
272         memset(proc->handle_table, 0, current->handle_table_size);
273         proc->handle_table_size = current->handle_table_size;
274         proc->handle_count = current->handle_count;
275         proc->handle_table_res = 0;
276
277         for (i = 0; i < current->handle_table_size; i++)
278         {
279             if (current->handle_table[i].obj)
280             {
281                 reference(current->handle_table[i].obj);
282                 current->handle_table[i].obj->open_count++;
283                 proc->handle_table[i] = current->handle_table[i];
284             }
285         }
286
287         release_resource(&current->handle_table_res);
288     }
289     else
290     {
291         proc->handle_table = (handle_info_t*)heap_alloc(&evictable_heap, STARTUP_HANDLE_TABLE_SIZE);
292         memset(proc->handle_table, 0, STARTUP_HANDLE_TABLE_SIZE);
293         proc->handle_table_size = STARTUP_HANDLE_TABLE_SIZE;
294         proc->handle_count = 0;
295         proc->handle_table_res = 0;
296     }
297
298     if (safe_path)
299     {
300         ret = create_address_space(PROCESS_POOL_ADDRESS, PROCESS_POOL_SIZE, &proc->memory_space);
301         if (ret != ERR_SUCCESS) goto cleanup;
302     }
303     else
304     {
305         ret = clone_address_space(&current->memory_space, &proc->memory_space);
306         if (ret != ERR_SUCCESS) goto cleanup;
307     }
308
309     clock_get_time(&proc->start_time);
310
311     ret = create_object(&proc->header);
312     if (ret != ERR_SUCCESS) goto cleanup;
313
314     object_created = TRUE;
315
316     thread_state_t initial_state;
317     memset(&initial_state, 0, sizeof(initial_state));
318
319     ret = open_object(&proc->header, 0, &safe_process_handle);
320     if (ret != ERR_SUCCESS) goto cleanup;
321
322     if (file != INVALID_HANDLE)
323     {
324         process_t *old_process = switch_process(proc);
325         ret = load_executable(file, parameters ? &safe_params : NULL, &initial_state);
326         switch_process(old_process);
327         if (ret != ERR_SUCCESS) goto cleanup;
328     }
329     else
330     {
331         initial_state.regs = *get_current_thread()->syscall_regs;
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) close_object(safe_process_handle);
390             if (safe_thread_handle != INVALID_HANDLE) close_object(safe_thread_handle);
391         }
392
393         dereference(&proc->header);
394     }
395     else
396     {
397         ASSERT(ret != ERR_SUCCESS);
398
399         if (proc->pid != (dword_t)-1)
400         {
401             acquire_lock(&pid_bitmap_lock);
402             clear_bit(pid_alloc_bitmap, proc->pid);
403             release_lock(&pid_bitmap_lock);
404         }
405
406         if (proc->name) free(proc->name);
407         free(proc);
408     }
409
410     if (get_previous_mode() == USER_MODE && path != NULL) free(safe_path);
411     return ret;
412 }
413
414 dword_t terminate(handle_t handle, dword_t exit_code)
415 {
416     process_t *proc;
417     thread_t *current_thread = get_current_thread();
418
419     if (handle == INVALID_HANDLE)
420     {
421         proc = get_current_process();
422         reference(&proc->header);
423     }
424     else
425     {
426         if (!reference_by_handle(handle, OBJECT_PROCESS, (object_t**)&proc)) return ERR_INVALID;
427     }
428
429     proc->terminating = TRUE;
430     proc->exit_code = exit_code;
431     clock_get_time(&proc->end_time);
432
433     acquire_resource_shared(&proc->thread_list_res);
434
435     while (proc->threads.next != &proc->threads)
436     {
437         list_entry_t *ptr;
438         thread_t *thread = NULL;
439
440         for (ptr = proc->threads.next; ptr != &proc->threads; ptr = ptr->next)
441         {
442             thread = CONTAINER_OF(ptr, thread_t, in_process_list);
443             if (thread != current_thread) break;
444         }
445
446         if (ptr != &proc->threads)
447         {
448             release_resource(&proc->thread_list_res);
449             terminate_thread_internal(thread, exit_code);
450             acquire_resource_shared(&proc->thread_list_res);
451         }
452         else
453         {
454             break;
455         }
456     }
457
458     release_resource(&proc->thread_list_res);
459
460     if (proc->threads.next == &proc->threads) destroy_process(proc);
461     dereference(&proc->header);
462
463     if (proc->threads.next == &proc->threads) return ERR_SUCCESS;
464     else return terminate_thread_internal(current_thread, exit_code);
465 }
466
467 dword_t get_process_id()
468 {
469     process_t *proc = get_current_process();
470     return proc->pid;
471 }
472
473 dword_t query_process(handle_t handle, process_info_t info_type, void *buffer, dword_t size)
474 {
475     dword_t ret = ERR_SUCCESS;
476     void *safe_buffer;
477
478     if (get_previous_mode() == USER_MODE)
479     {
480         if (!check_usermode(buffer, size)) return ERR_BADPTR;
481
482         safe_buffer = malloc(size);
483         if (safe_buffer == NULL) return ERR_NOMEMORY;
484         memset(safe_buffer, 0, size);
485     }
486     else
487     {
488         safe_buffer = buffer;
489     }
490
491     process_t *proc;
492
493     if (handle == INVALID_HANDLE)
494     {
495         proc = get_current_process();
496         reference(&proc->header);
497     }
498     else
499     {
500         if (!reference_by_handle(handle, OBJECT_PROCESS, (object_t**)&proc))
501         {
502             if (get_previous_mode() == USER_MODE) free(safe_buffer);
503             return ERR_INVALID;
504         }
505     }
506
507     switch (info_type)
508     {
509         case PROCESS_PID_INFO:
510         {
511             if (size >= sizeof(dword_t)) *((dword_t*)safe_buffer) = proc->pid;
512             else ret = ERR_SMALLBUF;
513
514             break;
515         }
516
517         case PROCESS_NAME_INFO:
518         {
519             if (size >= strlen(proc->name) + 1) strcpy(safe_buffer, proc->name);
520             else ret = ERR_SMALLBUF;
521
522             break;
523         }
524
525         case PROCESS_MEMORY_INFO:
526         {
527             ret = ERR_NOSYSCALL; // TODO
528             break;
529         }
530
531         case PROCESS_START_TIME_INFO:
532         {
533             if (size >= sizeof(clock_time_t)) *((clock_time_t*)safe_buffer) = proc->start_time;
534             else ret = ERR_SMALLBUF;
535
536             break;
537         }
538
539         case PROCESS_EXIT_CODE_INFO:
540         {
541             if (size >= sizeof(dword_t)) *((dword_t*)safe_buffer) = proc->exit_code;
542             else ret = ERR_SMALLBUF;
543
544             break;
545         }
546
547         case PROCESS_USER_INFO:
548         {
549             if (size >= 2 * sizeof(dword_t))
550             {
551                 ((dword_t*)safe_buffer)[0] = proc->original_user->uid;
552                 ((dword_t*)safe_buffer)[1] = proc->current_user->uid;
553             }
554             else
555             {
556                 ret = ERR_SMALLBUF;
557             }
558
559             break;
560         }
561
562         case PROCESS_THREAD_INFO:
563         {
564             if (size >= sizeof(dword_t))
565             {
566                 list_entry_t *ptr;
567                 dword_t i = 1, count = 0;
568
569                 acquire_resource_shared(&proc->thread_list_res);
570                 for (ptr = proc->threads.next; ptr != &proc->threads; ptr = ptr->next) count++;
571                 *((dword_t*)safe_buffer) = count;
572
573                 for (ptr = proc->threads.next; ptr != &proc->threads && i < (size / sizeof(dword_t)); ptr = ptr->next)
574                 {
575                     ((dword_t*)safe_buffer)[i++] = CONTAINER_OF(ptr, thread_t, in_process_list)->tid;
576                 }
577
578                 if (ptr != &proc->threads) ret = ERR_SMALLBUF;
579                 release_resource(&proc->thread_list_res);
580             }
581             else
582             {
583                 ret = ERR_SMALLBUF;
584             }
585
586             break;
587         }
588
589         case PROCESS_HANDLE_INFO:
590         {
591             if (size >= sizeof(dword_t))
592             {
593                 dword_t i, j = 1;
594
595                 acquire_resource_shared(&proc->handle_table_res);
596                 *((dword_t*)safe_buffer) = proc->handle_count;
597
598                 for (i = 0; i < proc->handle_table_size && j < (size / sizeof(dword_t)); i++)
599                 {
600                     if (proc->handle_table[i].obj) ((dword_t*)safe_buffer)[j++] = i;
601                 }
602
603                 if (i != proc->handle_table_size) ret = ERR_SMALLBUF;
604                 release_resource(&proc->handle_table_res);
605             }
606             else
607             {
608                 ret = ERR_SMALLBUF;
609             }
610
611             break;
612         }
613
614         default:
615         {
616             ret = ERR_INVALID;
617         }
618     }
619
620     if (get_previous_mode() == USER_MODE)
621     {
622         EH_TRY memcpy(buffer, safe_buffer, size);
623         EH_CATCH ret = ERR_BADPTR;
624         EH_DONE;
625
626         free(safe_buffer);
627     }
628
629     dereference(&proc->header);
630     return ret;
631 }
632
633 dword_t enum_processes(dword_t *pid_array, dword_t *count)
634 {
635     dword_t ret = ERR_SUCCESS;
636     dword_t safe_count;
637     dword_t cnt = 0;
638
639     if (get_previous_mode() == USER_MODE)
640     {
641         if (!check_usermode(count, sizeof(*count))) return ERR_BADPTR;
642
643         EH_TRY safe_count = *count;
644         EH_CATCH EH_ESCAPE(return ERR_BADPTR);
645         EH_DONE;
646
647         if (!check_usermode(pid_array, sizeof(*pid_array) * safe_count))
648         {
649             ret = ERR_BADPTR;
650             goto cleanup;
651         }
652     }
653     else
654     {
655         safe_count = *count;
656     }
657
658     process_t *proc = NULL;
659     ret = enum_objects_by_type(OBJECT_PROCESS, (object_t**)&proc);
660
661     while (ret == ERR_SUCCESS)
662     {
663         if (cnt == safe_count)
664         {
665             ret = ERR_SMALLBUF;
666             break;
667         }
668
669         EH_TRY
670         {
671             pid_array[cnt++] = proc->pid;
672         }
673         EH_CATCH
674         {
675             ret = ERR_BADPTR;
676             EH_ESCAPE(break);
677         }
678         EH_DONE;
679
680         ret = enum_objects_by_type(OBJECT_PROCESS, (object_t**)&proc);
681     }
682
683     if (ret == ERR_NOMORE)
684     {
685         ret = ERR_SUCCESS;
686     }
687     else if (ret == ERR_SUCCESS)
688     {
689         dereference(&proc->header);
690         ret = ERR_SMALLBUF;
691     }
692
693 cleanup:
694     EH_TRY *count = cnt;
695     EH_CATCH ret = ERR_BADPTR;
696     EH_DONE;
697
698     return ret;
699 }
700
701 dword_t wait_process(handle_t handle, dword_t timeout)
702 {
703     dword_t ret;
704     process_t *proc;
705
706     if (handle == INVALID_HANDLE)
707     {
708         proc = get_current_process();
709         reference(&proc->header);
710     }
711     else
712     {
713         if (!reference_by_handle(handle, OBJECT_PROCESS, (object_t**)&proc)) return ERR_INVALID;
714     }
715
716     ret = scheduler_wait(WAIT_UNTIL_NOT_EQUAL, timeout, &proc->terminated, 0);
717
718     dereference(&proc->header);
719     return ret;
720 }
721
722 process_t *switch_process(process_t *new_process)
723 {
724     thread_t *thread = get_current_thread();
725     process_t *old_process = get_current_process();
726
727     while (!__sync_bool_compare_and_swap(&thread->owner_process, old_process, new_process))
728     {
729         old_process = get_current_process();
730     }
731
732     set_page_directory(new_process->memory_space.page_directory);
733     return old_process;
734 }
735
736 void process_init(char *root_directory)
737 {
738     memset(pid_alloc_bitmap, 0, sizeof(pid_alloc_bitmap));
739
740     kernel_process = (process_t*)malloc(sizeof(process_t));
741     ASSERT(kernel_process != NULL);
742
743     memset(kernel_process, 0, sizeof(process_t));
744     kernel_process->header.name = NULL;
745     kernel_process->header.type = OBJECT_PROCESS;
746
747     ASSERT(create_object(&kernel_process->header) == ERR_SUCCESS);
748
749     kernel_process->pid = 0;
750     set_bit(pid_alloc_bitmap, 0);
751     kernel_process->name = strdup("system");
752
753     memset(&kernel_process->memory_space, 0, sizeof(memory_address_space_t));
754     kernel_process->memory_space.page_directory = get_page_directory();
755
756     kernel_process->exit_code = 0;
757     kernel_process->terminating = FALSE;
758     kernel_process->terminated = FALSE;
759
760     clock_get_time(&kernel_process->start_time);
761
762     list_init(&kernel_process->threads);
763     kernel_process->thread_list_res = 0;
764
765     kernel_process->handle_table = (handle_info_t*)heap_alloc(&evictable_heap, STARTUP_HANDLE_TABLE_SIZE);
766     kernel_process->handle_table_size = STARTUP_HANDLE_TABLE_SIZE;
767     kernel_process->handle_count = 0;
768     kernel_process->handle_table_res = 0;
769 }