mirror of
https://github.com/Tom-on64/kern.git
synced 2026-11-03 18:22:22 +00:00
IMPLEMENTED TASKS!
This commit is contained in:
+29
-25
@@ -3,37 +3,41 @@
|
||||
|
||||
isr_common:
|
||||
;; Push registers to the int_frame_t struct
|
||||
push eax
|
||||
push gs
|
||||
push fs
|
||||
push es
|
||||
|
||||
pushad
|
||||
;; push eax
|
||||
;; push ecx
|
||||
;; push edx
|
||||
;; push ebx
|
||||
;; push esp
|
||||
;; push ebp
|
||||
;; push esi
|
||||
;; push edi
|
||||
push ds
|
||||
push ebp
|
||||
push edi
|
||||
push esi
|
||||
push edx
|
||||
push ecx
|
||||
push es
|
||||
push fs
|
||||
push gs
|
||||
|
||||
;; Load kernel data segment
|
||||
push ebx
|
||||
push esp
|
||||
mov bx, 0x10
|
||||
mov ds, bx
|
||||
mov es, bx
|
||||
mov fs, bx
|
||||
mov gs, bx
|
||||
pop ebx
|
||||
|
||||
[extern isr_handle_interrupt]
|
||||
call isr_handle_interrupt
|
||||
isr_return:
|
||||
add esp, 4 ; Keep ESP
|
||||
pop ebx
|
||||
pop ecx
|
||||
pop edx
|
||||
pop esi
|
||||
pop edi
|
||||
pop ebp
|
||||
pop ds
|
||||
pop es
|
||||
pop fs
|
||||
[global isr_return]
|
||||
isr_return: ; Also used by task switching
|
||||
pop gs
|
||||
add esp, 4 ; Keep EAX
|
||||
add esp, 8 ; Pop interupt number and error code
|
||||
|
||||
iretd ; Pop CS, EIP & EFLAGS (Also SS & ESP if it's from userspace)
|
||||
pop fs
|
||||
pop es
|
||||
pop ds
|
||||
popad
|
||||
add esp, 8 ; Pop interupt number and error code
|
||||
iretd ; Pop CS, EIP & EFLAGS (Also SS & ESP if it's from userspace)
|
||||
|
||||
%macro ISR_WITH_ERRNO 1
|
||||
isr%1:
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <paging.h>
|
||||
#include <serial.h>
|
||||
#include <timer.h>
|
||||
#include <task.h>
|
||||
#include <gdt.h>
|
||||
#include <isr.h>
|
||||
#include <pmm.h>
|
||||
@@ -31,6 +32,7 @@ void kmain(void* ptr, uint32_t magic) {
|
||||
tty_puts("kern.\n\n");
|
||||
|
||||
if (timer_init() != 0) panic("Failed to initialize timer.");
|
||||
if (task_init() != 0) panic("Failed to initialize tasking.");
|
||||
|
||||
// TODO: Scheduler
|
||||
// TODO: Syscalls
|
||||
|
||||
+7
-7
@@ -68,7 +68,7 @@ void pag_map(uint32_t vaddr, uint32_t paddr, uint32_t flags) {
|
||||
|
||||
// Allocate a pagetab if it didn't exist
|
||||
if (!(pagedir[pdi] & PAGE_FLAG_PRESENT)) {
|
||||
uint32_t tabaddr = pmm_bitmap.ready ? (uint32_t)pmm_alloc() : pag_temp_frame();
|
||||
uint32_t tabaddr = pmm_bitmap.ready ? pmm_alloc() : pag_temp_frame();
|
||||
|
||||
pagedir[pdi] = tabaddr |
|
||||
PAGE_FLAG_PRESENT | PAGE_FLAG_WRITE | PAGE_FLAG_OWNER | flags;
|
||||
@@ -125,14 +125,14 @@ uint32_t pag_umap(uint32_t vaddr) {
|
||||
uint32_t pde = pagedir[pdi];
|
||||
if (remove && pde & PAGE_FLAG_OWNER) {
|
||||
uint32_t tabaddr = PHYS_ADDR(pde);
|
||||
pmm_free((void*)tabaddr);
|
||||
pmm_free(tabaddr);
|
||||
pagedir[pdi] = 0;
|
||||
}
|
||||
|
||||
invalidate(vaddr);
|
||||
|
||||
// Free the page frame
|
||||
if (pte & PAGE_FLAG_OWNER) pmm_free((void*)PHYS_ADDR(pte));
|
||||
if (pte & PAGE_FLAG_OWNER) pmm_free(PHYS_ADDR(pte));
|
||||
|
||||
if (prevpd != NULL) {
|
||||
pag_sync_pagedir(); // Sync it to other pagedirs
|
||||
@@ -142,13 +142,13 @@ uint32_t pag_umap(uint32_t vaddr) {
|
||||
return pte;
|
||||
}
|
||||
|
||||
void* pag_virt_to_phys(uint32_t vaddr) {
|
||||
uint32_t pag_virt_to_phys(uint32_t vaddr) {
|
||||
uint32_t pdi = vaddr >> 22;
|
||||
uint32_t pti = vaddr >> 12 & 0x3FF;
|
||||
|
||||
uint32_t* pagetab = PAGETAB(pdi);
|
||||
|
||||
return (void*)((pagetab[pti] & ~0xFFF) + ((uint32_t)vaddr & 0xFFF));
|
||||
return (pagetab[pti] & ~0xFFF) + ((uint32_t)vaddr & 0xFFF);
|
||||
}
|
||||
|
||||
uint32_t* pag_get_pagedir() {
|
||||
@@ -224,11 +224,11 @@ void pag_free_pagedir(uint32_t* pagedir) {
|
||||
for (size_t j = 0; j < 1024; j++) {
|
||||
uint32_t pte = pagetab[j];
|
||||
|
||||
if (pte & PAGE_FLAG_OWNER) pmm_free((void*)PHYS_ADDR(pte));
|
||||
if (pte & PAGE_FLAG_OWNER) pmm_free(PHYS_ADDR(pte));
|
||||
}
|
||||
memset(pagetab, 0, PAGE_SIZE);
|
||||
|
||||
if (pde & PAGE_FLAG_OWNER) pmm_free((void*)PHYS_ADDR(pde));
|
||||
if (pde & PAGE_FLAG_OWNER) pmm_free(PHYS_ADDR(pde));
|
||||
pd[i] = 0;
|
||||
}
|
||||
|
||||
|
||||
+6
-6
@@ -83,19 +83,19 @@ int pmm_init(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void* pmm_alloc(void) {
|
||||
if (pmm_bitmap.blocks - pmm_bitmap.used == 0) return NULL;
|
||||
uint32_t pmm_alloc(void) {
|
||||
if (pmm_bitmap.blocks - pmm_bitmap.used == 0) return 0;
|
||||
|
||||
size_t base = bmap_get_area(&pmm_bitmap, 1, 0);
|
||||
if (base == BMAP_NOT_FOUND) return NULL;
|
||||
if (base == BMAP_NOT_FOUND) return 0;
|
||||
|
||||
bmap_set(&pmm_bitmap, base, 1);
|
||||
|
||||
return bmap_ptr(&pmm_bitmap, base);
|
||||
return (uint32_t)bmap_ptr(&pmm_bitmap, base);
|
||||
}
|
||||
|
||||
void pmm_free(void* page) {
|
||||
size_t base = (size_t)page / BLOCK_SIZE;
|
||||
void pmm_free(uint32_t page) {
|
||||
size_t base = page / BLOCK_SIZE;
|
||||
bmap_set_area(&pmm_bitmap, base, 1, 0);
|
||||
bmap_set(&pmm_bitmap, 0, 1);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,247 @@
|
||||
#include <kmalloc.h>
|
||||
#include <kernel.h>
|
||||
#include <paging.h>
|
||||
#include <string.h>
|
||||
#include <gdt.h>
|
||||
#include <isr.h>
|
||||
#include <pmm.h>
|
||||
|
||||
#include <task.h>
|
||||
|
||||
struct task* task_first;
|
||||
struct task* task_dummy;
|
||||
struct task* task_current;
|
||||
|
||||
int task_init(void) {
|
||||
task_first = kzalloc(sizeof(*task_first));
|
||||
task_dummy = kzalloc(sizeof(*task_dummy));
|
||||
if (task_first == NULL || task_dummy == NULL) return 1;
|
||||
|
||||
|
||||
// The first task is what's running right now @~@
|
||||
task_current = task_first;
|
||||
task_current->id = TASK_KERNEL;
|
||||
task_current->state = TASK_STATE_READY;
|
||||
task_current->pagedir = pag_get_pagedir();
|
||||
task_current->kernel_task = 1;
|
||||
|
||||
debugf("[task] Multitasking initiated.\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void task_create(size_t id, uint32_t eip, int kernel_task, uint32_t* pagedir, int argc, char** argv) {
|
||||
__asm__ volatile ("cli"); // Make sure we don't get interrupted
|
||||
|
||||
uint32_t kstack = (uint32_t)kmalloc(0x1000 - 16) + (0x1000 - 16);
|
||||
uint8_t* kesp = (uint8_t*)kstack;
|
||||
|
||||
kesp -= sizeof(struct isr_int_frame);
|
||||
struct isr_int_frame* trap = (struct isr_int_frame*)kesp;
|
||||
memset(trap, 0, sizeof(*trap));
|
||||
|
||||
uint16_t codesel = kernel_task ? GDT_KERNEL_CODE : GDT_USER_CODE;
|
||||
uint16_t datasel = kernel_task ? GDT_KERNEL_DATA : GDT_USER_DATA;
|
||||
|
||||
trap->cs = codesel;
|
||||
trap->ds = datasel;
|
||||
trap->es = datasel;
|
||||
trap->fs = datasel;
|
||||
trap->gs = datasel;
|
||||
|
||||
trap->user_ss = datasel;
|
||||
trap->user_esp = USER_STACK;
|
||||
|
||||
trap->eflags = 0x200; // Enable interrupts
|
||||
trap->eip = eip;
|
||||
|
||||
kesp -= sizeof(struct isr_int_frame);
|
||||
struct task_switch_ctx* ctx = (struct task_switch_ctx*)kesp;
|
||||
ctx->edi = 0;
|
||||
ctx->esi = 0;
|
||||
ctx->ebx = 0;
|
||||
ctx->ebp = 0;
|
||||
|
||||
ctx->eip = (uint32_t)isr_return;
|
||||
|
||||
struct task* current = task_first;
|
||||
while (current != NULL) {
|
||||
if (current->next == NULL) break;
|
||||
current = current->next;
|
||||
}
|
||||
|
||||
struct task* new = kzalloc(sizeof(*new));
|
||||
current->next = new;
|
||||
|
||||
new->kstack_bottom = kstack;
|
||||
new->kstack_pointer = (size_t)kesp;
|
||||
new->id = id;
|
||||
new->kernel_task = kernel_task;
|
||||
new->state = TASK_STATE_READY;
|
||||
new->pagedir = pagedir;
|
||||
new->heap_start = USER_HEAP;
|
||||
new->heap_end = USER_HEAP;
|
||||
|
||||
// We don't need to setup a stack frame
|
||||
if (kernel_task) {
|
||||
__asm__ volatile ("sti");
|
||||
return;
|
||||
}
|
||||
|
||||
void* old_pagedir = pag_get_pagedir();
|
||||
pag_set_pagedir(new->pagedir);
|
||||
|
||||
// Setup argc, argv & environ
|
||||
size_t arg_len = 0;
|
||||
for (int i = 0; i < argc; i++) arg_len += strlen(argv[i]) + 1;
|
||||
size_t page_count = dceil(4 + (argc * 4) + 10 + arg_len, PAGE_SIZE);
|
||||
|
||||
for (size_t i = 0; i < page_count; i++) {
|
||||
uint32_t paddr = pmm_alloc();
|
||||
if (paddr == 0) { // Not enough memory
|
||||
// TODO: Do pmm_free() for each page
|
||||
kfree(new);
|
||||
kfree((void*)kstack);
|
||||
pag_set_pagedir(old_pagedir);
|
||||
return;
|
||||
}
|
||||
pag_map(USER_STACK + i * PAGE_SIZE, (uint32_t)paddr, 0);
|
||||
}
|
||||
|
||||
int* argc_ptr = (int*)USER_STACK;
|
||||
char** argv_ptr = (char**)(USER_STACK + 4);
|
||||
*argc_ptr = argc;
|
||||
|
||||
size_t cur = 0;
|
||||
for (int i = 0; i < argc; i++) {
|
||||
size_t len = strlen(argv[i]);
|
||||
char* ptr = (char*)((size_t)argv_ptr + argc * 4 + 4 + cur);
|
||||
|
||||
memcpy(ptr, argv[i], len);
|
||||
ptr[len] = '\0';
|
||||
|
||||
argv_ptr[i] = ptr;
|
||||
cur += len + 1;
|
||||
}
|
||||
|
||||
pag_set_pagedir(old_pagedir);
|
||||
}
|
||||
|
||||
void task_kill(size_t id) {
|
||||
__asm__ volatile ("cli");
|
||||
|
||||
struct task* current = task_first;
|
||||
while (current != NULL) {
|
||||
if (current->next && current->next->id == id) break;
|
||||
current = current->next;
|
||||
}
|
||||
|
||||
struct task* task = current->next;
|
||||
if (task == NULL || task->state == TASK_STATE_DEAD) return;
|
||||
current->next = task->next;
|
||||
|
||||
void* kstack = (void*)(task->kstack_bottom + 0x1000 - 16);
|
||||
kfree(kstack);
|
||||
|
||||
uint32_t* old_pagedir = pag_get_pagedir();
|
||||
pag_set_pagedir(task->pagedir);
|
||||
size_t heap_start = dceil(task->heap_start, PAGE_SIZE);
|
||||
size_t heap_end = dceil(task->heap_end, PAGE_SIZE);
|
||||
|
||||
if (heap_end > heap_start) {
|
||||
size_t num = heap_end - heap_start;
|
||||
|
||||
for (size_t i = 0; i < num; i++) {
|
||||
uint32_t vaddr = heap_start * PAGE_SIZE + i * PAGE_SIZE;
|
||||
uint32_t paddr = pag_virt_to_phys(vaddr);
|
||||
pmm_free(paddr);
|
||||
}
|
||||
}
|
||||
|
||||
pag_set_pagedir(old_pagedir);
|
||||
pag_free_pagedir(task->pagedir);
|
||||
|
||||
// Workaround so we don't free a task we're in
|
||||
if (task_current == task) {
|
||||
memset(task_dummy, 0, sizeof(*task_dummy));
|
||||
task_current = task_dummy;
|
||||
}
|
||||
kfree(task);
|
||||
|
||||
// schedule();
|
||||
__asm__ volatile ("sti");
|
||||
}
|
||||
|
||||
void task_set_user_heap(struct task* task, size_t heap_end) {
|
||||
if (heap_end <= task->heap_start) {
|
||||
debugf("[task] Task %d tried to shrink heap bellow 0.\n", task->id);
|
||||
task_kill(task->id);
|
||||
return;
|
||||
}
|
||||
|
||||
int old_page_top = dceil(task->heap_end, PAGE_SIZE);
|
||||
int new_page_top = dceil(heap_end, PAGE_SIZE);
|
||||
|
||||
if (new_page_top == old_page_top) return;
|
||||
else if (new_page_top > old_page_top) { // Grow
|
||||
int num = new_page_top - old_page_top;
|
||||
|
||||
for (size_t i = 0; i < num; i++) {
|
||||
uint32_t paddr = (uint32_t)pmm_alloc();
|
||||
if (paddr == 0) {
|
||||
debugf("[task] Task %d tried to grow heap too much.\n", task->id);
|
||||
task_kill(task->id);
|
||||
}
|
||||
uint32_t vaddr = old_page_top * PAGE_SIZE + i * PAGE_SIZE;
|
||||
pag_map(vaddr, paddr, PAGE_FLAG_USER);
|
||||
memset((void*)vaddr, 0, PAGE_SIZE);
|
||||
}
|
||||
} else { // Shrink
|
||||
int num = old_page_top - new_page_top;
|
||||
|
||||
for (size_t i = 0; i < num; i++) {
|
||||
uint32_t vaddr = old_page_top * PAGE_SIZE + i * PAGE_SIZE;
|
||||
uint32_t paddr = pag_virt_to_phys(vaddr);
|
||||
pmm_free(paddr);
|
||||
pag_umap(vaddr);
|
||||
}
|
||||
}
|
||||
|
||||
task->heap_end = heap_end;
|
||||
}
|
||||
|
||||
struct task* task_get(size_t id) {
|
||||
struct task* find = task_first;
|
||||
while (find != NULL) {
|
||||
if (find->id == id) break;
|
||||
find = find->next;
|
||||
}
|
||||
|
||||
return find;
|
||||
}
|
||||
|
||||
uint8_t task_get_state(size_t id) {
|
||||
struct task* t = task_get(id);
|
||||
if (t == NULL) return TASK_STATE_DEAD;
|
||||
return t->state;
|
||||
}
|
||||
|
||||
void task_set_state(size_t id, uint8_t state) {
|
||||
struct task* t = task_get(id);
|
||||
if (t == NULL) return;
|
||||
t->state = state;
|
||||
}
|
||||
|
||||
size_t task_create_id(void) {
|
||||
struct task* current = task_first;
|
||||
|
||||
size_t max = current->id;
|
||||
current = current->next;
|
||||
while (current != NULL) {
|
||||
if (max < current->id) max = current->id;
|
||||
current = current->next;
|
||||
}
|
||||
|
||||
return max + 1;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
bits 32
|
||||
|
||||
;; Prototype:
|
||||
;; void switch_context(struct task* old, struct task* new);
|
||||
;; Description:
|
||||
;; Pushes register state onto the old tasks kstack and pops the new state.
|
||||
global task_switch
|
||||
task_switch:
|
||||
mov eax, [esp + 4] ; eax = struct task* old
|
||||
mov edx, [esp + 8] ; ebx = struct task* nex
|
||||
|
||||
;; Push registers that weren't pushed by the CDECL call
|
||||
push ebp
|
||||
push ebx
|
||||
push esi
|
||||
push edi
|
||||
|
||||
;; Swap the stack pointers
|
||||
mov [eax + 4], esp ; old->kstack_pointer = ESP
|
||||
mov esp, [edx + 4] ; ESP = new->kstack_pointer
|
||||
|
||||
;; Change the pagedir
|
||||
mov eax, [edx + 12] ; EAX = new->pagedir
|
||||
sub eax, 0xC0000000 ; Convert to physical address
|
||||
mov cr3, eax
|
||||
|
||||
;; Pop register that weren't pushed by the CDECL call
|
||||
pop edi
|
||||
pop esi
|
||||
pop ebx
|
||||
pop ebp
|
||||
|
||||
;; This return will pop task_switch_ctx.eip
|
||||
ret
|
||||
|
||||
Reference in New Issue
Block a user