diff options
author | Anton Kling <anton@kling.gg> | 2023-10-30 22:12:14 +0100 |
---|---|---|
committer | Anton Kling <anton@kling.gg> | 2023-10-31 00:18:38 +0100 |
commit | 8a9208612eec8ddae4c418485d848ecfa0613699 (patch) | |
tree | 2f4b29200c2f0c19ae52f45bdb9b38a41b356e30 /kernel/sched | |
parent | ca76600acc8bf7a02346efa5bd8f17072210ec01 (diff) |
Meta: Move kernel and userland to their own folders.
This is to allow both the kernel and the userland to share certain
header files and to make the folder structure a bit more clear.
Diffstat (limited to 'kernel/sched')
-rw-r--r-- | kernel/sched/scheduler.c | 449 | ||||
-rw-r--r-- | kernel/sched/scheduler.h | 65 |
2 files changed, 514 insertions, 0 deletions
diff --git a/kernel/sched/scheduler.c b/kernel/sched/scheduler.c new file mode 100644 index 0000000..a6ace7d --- /dev/null +++ b/kernel/sched/scheduler.c @@ -0,0 +1,449 @@ +#include <assert.h> +#include <cpu/io.h> +#include <defs.h> +#include <drivers/pit.h> +#include <elf.h> +#include <errno.h> +#include <fs/vfs.h> + +#define STACK_LOCATION ((void *)0x90000000) +#define STACK_SIZE 0x80000 +#define HEAP_SIZE 0x400000 +#define HEAP_LOCATION (STACK_LOCATION - STACK_SIZE - HEAP_SIZE) + +process_t *ready_queue; +process_t *current_task = NULL; +uint32_t next_pid = 0; + +extern uint32_t read_eip(void); + +process_t *get_current_task(void) { return current_task; } + +void set_signal_handler(int sig, void (*handler)(int)) { + if (sig >= 20 || sig < 0) + return; + if (9 == sig) + return; + current_task->signal_handlers[sig] = handler; +} + +process_t *create_process(process_t *p) { + process_t *r; + r = ksbrk(sizeof(process_t)); + r->dead = 0; + r->pid = next_pid++; + r->esp = r->ebp = 0; + r->eip = 0; + r->sleep_until = 0; + if (!p) { + assert(1 == next_pid); + strncpy(r->program_name, "[kernel]", sizeof(current_task->program_name)); + } else + strncpy(r->program_name, "[Not yet named]", + sizeof(current_task->program_name)); + + r->cr3 = (p) ? clone_directory(get_active_pagedirectory()) + : get_active_pagedirectory(); + r->next = 0; + r->incoming_signal = 0; + r->parent = p; + r->child = NULL; + r->halt_list = NULL; + + mmu_allocate_region((void *)(0x80000000 - 0x1000), 0x1000, MMU_FLAG_RW, + r->cr3); + r->signal_handler_stack = 0x80000000; + + strcpy(r->current_working_directory, "/"); + r->data_segment_end = (p) ? p->data_segment_end : NULL; + memset((void *)r->halts, 0, 2 * sizeof(uint32_t)); + for (int i = 0; i < 100; i++) { + if (p) { + r->file_descriptors[i] = p->file_descriptors[i]; + if (r->file_descriptors[i]) { + r->file_descriptors[i]->reference_count++; + } + } + if (i < 20) + r->signal_handlers[i] = NULL; + r->read_halt_inode[i] = NULL; + r->write_halt_inode[i] = NULL; + r->disconnect_halt_inode[i] = NULL; + r->maps[i] = NULL; + } + return r; +} + +int get_free_fd(process_t *p, int allocate) { + if (!p) + p = (process_t *)current_task; + int i; + for (i = 0; i < 100; i++) + if (!p->file_descriptors[i]) + break; + if (p->file_descriptors[i]) + return -1; + if (allocate) { + vfs_fd_t *fd = p->file_descriptors[i] = kmalloc(sizeof(vfs_fd_t)); + fd->inode = kmalloc(sizeof(vfs_inode_t)); + } + return i; +} + +void tasking_init(void) { current_task = ready_queue = create_process(NULL); } + +int i = 0; +void __attribute__((optimize("O0"))) free_process(void) { + kprintf("Exiting process: %s\n", get_current_task()->program_name); + // free_process() will purge all contents such as allocated frames + // out of the current process. This will be called by exit() and + // exec*(). + + // Do a special free for shared memory which avoids labeling + // underlying frames as "unused". + for (int i = 0; i < 100; i++) { + vfs_close(i); + if (!current_task->maps[i]) + continue; + MemoryMap *m = current_task->maps[i]; + mmu_remove_virtual_physical_address_mapping(m->u_address, m->length); + } + + // NOTE: Kernel stuff begins at 0x90000000 + mmu_free_address_range((void *)0x1000, 0x90000000); +} + +void exit(int status) { + assert(current_task->pid != 1); + if (current_task->parent) { + current_task->parent->halts[WAIT_CHILD_HALT] = 0; + current_task->parent->child_rc = status; + } + process_t *new_task = current_task; + for (; new_task == current_task;) { + if (!new_task->next) + new_task = ready_queue; + new_task = new_task->next; + } + + free_process(); + // Remove current_task from list + for (process_t *tmp = ready_queue; tmp;) { + if (tmp == current_task) // current_task is ready_queue(TODO: + // Figure out whether this could even + // happen) + { + ready_queue = current_task->next; + break; + } + if (tmp->next == current_task) { + tmp->next = tmp->next->next; + break; + } + tmp = tmp->next; + } + current_task->dead = 1; + // This function will enable interrupts + switch_task(); +} + +uint32_t setup_stack(uint32_t stack_pointer, int argc, char **argv) { + mmu_allocate_region(STACK_LOCATION - STACK_SIZE, STACK_SIZE, MMU_FLAG_RW, + NULL); + flush_tlb(); + + uint32_t ptr = stack_pointer; + + char *argv_ptrs[argc + 1]; + for (int i = 0; i < argc; i++) { + char *s = argv[i]; + size_t l = strlen(s); + ptr -= l + 1; + char *b = (char *)ptr; + memcpy(b, s, l); + b[l] = '\0'; + argv_ptrs[i] = b; + } + + char **ptrs[argc + 1]; + for (int i = argc; i >= 0; i--) { + ptr -= sizeof(char *); + ptrs[i] = (char **)ptr; + if (i != argc) { + *(ptrs[i]) = argv_ptrs[i]; + } else { + *(ptrs[i]) = NULL; + } + } + + char *s = (char *)ptr; + ptr -= sizeof(char **); + *(char ***)ptr = (char **)s; + + ptr -= sizeof(int); + *(int *)ptr = argc; + return ptr; +} + +int exec(const char *filename, char **argv) { + // exec() will "takeover" the process by loading the file specified in + // filename into memory, change from ring 0 to ring 3 and jump to the + // files entry point as decided by the ELF header of the file. + int argc = 0; + for (; argv[argc];) { + argc++; + } + + uint32_t end_of_code; + void *entry = load_elf_file(filename, &end_of_code); + if (!entry) { + return 0; + } + + strncpy(current_task->program_name, filename, + sizeof(current_task->program_name)); + + current_task->data_segment_end = align_page((void *)end_of_code); + + uint32_t ptr = setup_stack(0x90000000, argc, argv); + + jump_usermode((void (*)())(entry), ptr); + ASSERT_NOT_REACHED; + return 0; +} + +int __attribute__((optimize("O0"))) fork(void) { + process_t *parent_task = (process_t *)current_task; + + process_t *new_task = create_process(parent_task); + + process_t *tmp_task = (process_t *)ready_queue; + for (; tmp_task->next;) + tmp_task = tmp_task->next; + + tmp_task->next = new_task; + + uint32_t eip = read_eip(); + + if (current_task != parent_task) { + return 0; + } + + new_task->child_rc = -1; + new_task->parent = current_task; + current_task->child = new_task; + + new_task->eip = eip; + asm("\ + mov %%esp, %0;\ + mov %%ebp, %1;" + : "=r"(new_task->esp), "=r"(new_task->ebp)); + asm("sti"); + return new_task->pid; +} + +int is_halted(process_t *process) { + for (int i = 0; i < 2; i++) + if (process->halts[i]) + return 1; + + if (isset_fdhalt(process->read_halt_inode, process->write_halt_inode, + process->disconnect_halt_inode)) { + return 1; + } + return 0; +} + +extern PageDirectory *active_directory; + +process_t *next_task(process_t *c) { + for (;;) { + c = c->next; + if (!c) + c = ready_queue; + if (c->incoming_signal) + break; + if (c->sleep_until > pit_num_ms()) + continue; + if (is_halted(c) || c->dead) + continue; + break; + } + return c; +} + +int task_save_state(void) { + asm("mov %%esp, %0" : "=r"(current_task->esp)); + asm("mov %%ebp, %0" : "=r"(current_task->ebp)); + + uint32_t eip = read_eip(); + + if (0x1 == eip) { + // Should the returned value from read_eip be equal to one it + // means that we have just switched over to this task after we + // saved the state(since the switch_task() function changes the + // eax register to 1). + return 0; + } + + current_task->eip = eip; + return 1; +} + +int kill(pid_t pid, int sig) { + process_t *p = current_task; + p = p->next; + if (!p) + p = ready_queue; + for (; p->pid != pid;) { + if (p == current_task) + break; + p = p->next; + if (!p) + p = ready_queue; + } + if (p->pid != pid) + return -ESRCH; + p->incoming_signal = sig; + return 0; +} + +void jump_signal_handler(void *func, uint32_t esp); +void switch_task() { + if (!current_task) + return; + + if (0 == task_save_state()) { + return; + } + + current_task = next_task((process_t *)current_task); + + active_directory = current_task->cr3; + + if (current_task->incoming_signal) { + uint8_t sig = current_task->incoming_signal; + current_task->incoming_signal = 0; + asm("mov %0, %%cr3" ::"r"(current_task->cr3->physical_address)); + + void *handler = current_task->signal_handlers[sig]; + if (9 == sig) { + klog("Task recieved SIGKILL", LOG_NOTE); + exit(0); + } + if (!handler) { + klog("Task recieved unhandeled signal. Killing process.", LOG_WARN); + exit(1); + } + jump_signal_handler(handler, current_task->signal_handler_stack); + } else { + asm(" \ + mov %0, %%esp; \ + mov %1, %%ebp; \ + mov %2, %%ecx; \ + mov %3, %%cr3; \ + mov $0x1, %%eax; \ + jmp *%%ecx" ::"r"(current_task->esp), + "r"(current_task->ebp), "r"(current_task->eip), + "r"(current_task->cr3->physical_address)); + } +} + +MemoryMap **get_free_map(void) { + for (int i = 0; i < 100; i++) + if (!(current_task->maps[i])) + return &(current_task->maps[i]); + assert(0); + return NULL; +} + +void *get_free_virtual_memory(size_t length) { + void *n = + (void *)((uintptr_t)(get_current_task()->data_segment_end) + length); + + void *rc = get_current_task()->data_segment_end; + get_current_task()->data_segment_end = align_page(n); + return rc; +} + +void *allocate_virtual_user_memory(size_t length, int prot, int flags) { + (void)prot; + (void)flags; + void *rc = get_free_virtual_memory(length); + if ((void *)-1 == rc) + return (void *)-1; + + mmu_allocate_region(rc, length, MMU_FLAG_RW, NULL); + return rc; +} + +void *user_kernel_mapping(void *kernel_addr, size_t length) { + void *rc = get_free_virtual_memory(length); + if ((void *)-1 == rc) + return (void *)-1; + + mmu_map_directories(rc, NULL, kernel_addr, NULL, length); + return rc; +} + +void *create_physical_mapping(void **physical_addresses, size_t length) { + void *rc = get_free_virtual_memory(length); + if ((void *)-1 == rc) + return (void *)-1; + int n = (uintptr_t)align_page((void *)length) / 0x1000; + for (int i = 0; i < n; i++) { + mmu_map_physical(rc + (i * 0x1000), NULL, physical_addresses[i], 0x1000); + } + return rc; +} + +void *mmap(void *addr, size_t length, int prot, int flags, int fd, + size_t offset) { + (void)addr; + if (0 == length) { + kprintf("EINVAL\n"); + return (void *)-EINVAL; + } + + MemoryMap **ptr = get_free_map(); + if (!ptr) { + klog("mmap(): No free memory map.", LOG_WARN); + return (void *)-1; + } + *ptr = kmalloc(sizeof(MemoryMap)); + MemoryMap *free_map = *ptr; + + if (fd == -1) { + void *rc = allocate_virtual_user_memory(length, prot, flags); + if ((void *)-1 == rc) { + kprintf("ENOMEM\n"); + return (void *)-ENOMEM; + } + free_map->u_address = rc; + free_map->k_address = NULL; + free_map->length = length; + free_map->fd = -1; + return rc; + } + + vfs_vm_object_t *vmobject = vfs_get_vm_object(fd, length, offset); + if (!vmobject) { + kprintf("ENODEV\n"); + return (void *)-ENODEV; + } + + if (vmobject->size < length) { + kprintf("EOVERFLOW\n"); + return (void *)-EOVERFLOW; // TODO: Check if this is the correct + // code. + } + + if (length > vmobject->size) + length = vmobject->size; + void *rc = create_physical_mapping(vmobject->object, length); + free_map->u_address = rc; + free_map->k_address = NULL; + free_map->length = length; + free_map->fd = fd; + return rc; +} diff --git a/kernel/sched/scheduler.h b/kernel/sched/scheduler.h new file mode 100644 index 0000000..fc92ff3 --- /dev/null +++ b/kernel/sched/scheduler.h @@ -0,0 +1,65 @@ +#ifndef SCHEDULER_H +#define SCHEDULER_H +#include <fs/ext2.h> +#include <fs/vfs.h> +#include <halts.h> +#include <mmu.h> +#include <signal.h> + +#define MAX_PATH 256 +#define KEYBOARD_HALT 0 +#define WAIT_CHILD_HALT 1 + +int fork(void); +int exec(const char *filename, char **argv); +void switch_task(); +void tasking_init(void); +void exit(int status); + +void *mmap(void *addr, size_t length, int prot, int flags, int fd, + size_t offset); +int munmap(void *addr, size_t length); +int msync(void *addr, size_t length, int flags); +int kill(pid_t pid, int sig); +void set_signal_handler(int sig, void (*handler)(int)); + +typedef struct { + void *u_address; + void *k_address; + uint32_t length; + int fd; +} MemoryMap; + +typedef struct Process process_t; + +struct Process { + uint32_t pid; + char program_name[100]; + char current_working_directory[MAX_PATH]; + uint32_t eip, esp, ebp; + uint8_t incoming_signal; + uint32_t signal_handler_stack; + void *signal_handlers[20]; + PageDirectory *cr3; + vfs_fd_t *file_descriptors[100]; + vfs_inode_t *read_halt_inode[100]; + vfs_inode_t *write_halt_inode[100]; + vfs_inode_t *disconnect_halt_inode[100]; + uint32_t halts[2]; + struct Halt *halt_list; + void *data_segment_end; + process_t *next; + process_t *parent; + // TODO: Create a linkedlist of childs so that the parent process + // can do stuff such as reap zombies and get status. + process_t *child; + MemoryMap *maps[100]; + uint32_t sleep_until; + int child_rc; + int dead; +}; + +process_t *get_current_task(void); +int get_free_fd(process_t *p, int allocate); +void free_process(void); +#endif |