diff options
-rw-r--r-- | Grapes.fl.img | bin | 1474560 -> 1474560 bytes | |||
-rw-r--r-- | src/kernel/core/kmain.c | 12 | ||||
-rw-r--r-- | src/kernel/core/monitor.c | 27 | ||||
-rw-r--r-- | src/kernel/core/monitor.h | 5 | ||||
-rw-r--r-- | src/kernel/core/sys.c | 21 | ||||
-rw-r--r-- | src/kernel/core/sys.h | 10 | ||||
-rw-r--r-- | src/kernel/ipc/shm.c | 16 | ||||
-rw-r--r-- | src/kernel/ipc/shm.h | 5 | ||||
-rw-r--r-- | src/kernel/lib/mutex.c | 1 | ||||
-rw-r--r-- | src/kernel/mem/gdt.c | 18 | ||||
-rw-r--r-- | src/kernel/mem/gdt.h | 9 | ||||
-rw-r--r-- | src/kernel/mem/heap.c | 7 | ||||
-rw-r--r-- | src/kernel/mem/heap.h | 5 | ||||
-rw-r--r-- | src/kernel/mem/mem.c | 4 | ||||
-rw-r--r-- | src/kernel/mem/mem.h | 3 | ||||
-rw-r--r-- | src/kernel/mem/paging.c | 27 | ||||
-rw-r--r-- | src/kernel/mem/seg.c | 20 | ||||
-rw-r--r-- | src/kernel/mem/seg.h | 13 | ||||
-rw-r--r-- | src/kernel/task/idt.c | 14 | ||||
-rw-r--r-- | src/kernel/task/idt.h | 6 | ||||
-rw-r--r-- | src/kernel/task/syscall.c | 2 | ||||
-rw-r--r-- | src/kernel/task/syscall.h | 2 | ||||
-rw-r--r-- | src/kernel/task/task.c | 55 | ||||
-rw-r--r-- | src/kernel/task/task.h | 7 | ||||
-rw-r--r-- | src/kernel/task/timer.c | 5 |
25 files changed, 232 insertions, 62 deletions
diff --git a/Grapes.fl.img b/Grapes.fl.img Binary files differindex 8ebf30b..567d437 100644 --- a/Grapes.fl.img +++ b/Grapes.fl.img diff --git a/src/kernel/core/kmain.c b/src/kernel/core/kmain.c index d8f059a..cfb9935 100644 --- a/src/kernel/core/kmain.c +++ b/src/kernel/core/kmain.c @@ -10,7 +10,16 @@ #include <mem/mem.h> #include <linker/elf.h> +/* The kernel's main procedure. This function is called in loader_.asm. + This function calls the initializer functions for all system parts. + It then loads the modules the kernel was given by the bootloader. + This function never returns : once multitasking is started for good, + the execution flow of this function is never returned to. */ void kmain(struct multiboot_info_t* mbd, int32_t magic) { + monitor_clear(); + + ASSERT(magic == MULTIBOOT_BOOTLOADER_MAGIC); + size_t totalRam = 0; uint32_t i; @@ -25,8 +34,6 @@ void kmain(struct multiboot_info_t* mbd, int32_t magic) { mem_placementAddr = (mods[i].mod_end & 0xFFFFF000) + 0x1000; } - monitor_clear(); - if (magic != MULTIBOOT_BOOTLOADER_MAGIC) { PANIC("wrong multiboot magic number."); } @@ -62,4 +69,5 @@ void kmain(struct multiboot_info_t* mbd, int32_t magic) { monitor_write("Modules now RULE THE WORLD !\n"); sti(); tasking_switch(); + PANIC("Should never happen. Something probably went wrong with multitasking."); } diff --git a/src/kernel/core/monitor.c b/src/kernel/core/monitor.c index 6660fe0..0d0f5eb 100644 --- a/src/kernel/core/monitor.c +++ b/src/kernel/core/monitor.c @@ -4,6 +4,9 @@ static int cursor_x = 0, cursor_y = 0; static uint16_t *video_memory = (uint16_t*)0xE00B8000; +static uint8_t attribute = 0x07; // 0 = background = black, 7 = foreground = white + +/* For internal use only. Tells hardware to move the cursor at (cursor_x, cursor_y). */ static void move_cursor() { uint16_t cursor_location = cursor_y * 80 + cursor_x; outb(0x3D4, 14); //Sending high cursor byte @@ -12,9 +15,9 @@ static void move_cursor() { outb(0x3D5, cursor_location); } +/* For internal use only. Scrolls everything up one line. */ static void scroll() { - uint8_t attribute_byte = (0 /* black */ << 4) | (7/* white */ & 0x0F); - uint16_t blank = (attribute_byte << 8) | 0x20; + uint16_t blank = (attribute << 8) | 0x20; if (cursor_y >= 25) { int i; @@ -28,16 +31,11 @@ static void scroll() { } } +/* Put one character on the screen. This function handles special characters \b, \t, \r and \n. */ void monitor_put(char c) { - uint8_t fg = 7; //White - uint8_t bg = 0; //Black - - uint16_t attribute = (fg & 0x0F) | (bg << 4); - attribute = attribute << 8; - - if (c == 0x08 && cursor_x) { //Backspace + if (c == '\b' && cursor_x) { //Backspace cursor_x--; - } else if (c == 0x09) { //Tab + } else if (c == '\t') { //Tab cursor_x = (cursor_x + 8) & ~(8 - 1); } else if (c == '\r') { //Carriage return cursor_x = 0; @@ -45,7 +43,7 @@ void monitor_put(char c) { cursor_x = 0; cursor_y++; } else if (c >= ' ') { //Any printable character - video_memory[cursor_y * 80 + cursor_x] = c | attribute; + video_memory[cursor_y * 80 + cursor_x] = c | (attribute << 8); cursor_x++; } if (cursor_x >= 80) { @@ -57,9 +55,9 @@ void monitor_put(char c) { move_cursor(); } +/* Clears the screen and moves cursor to (0,0) (top left corner) */ void monitor_clear() { - uint8_t attribute_byte = (0 /* black */ << 4) | (15 /* white */ & 0x0F); - uint16_t blank = (attribute_byte << 8) | 0x20; + uint16_t blank = (attribute << 8) | 0x20; int i; @@ -71,12 +69,14 @@ void monitor_clear() { move_cursor(); } +/* Writes a string to the monitor */ void monitor_write(char *s) { while (*s) { monitor_put(*(s++)); } } +/* Writes a number in hexadecimal notation to the monitor */ void monitor_writeHex(uint32_t v) { int i; @@ -89,6 +89,7 @@ void monitor_writeHex(uint32_t v) { } } +/* Writes a number in decimal notation to the monitor */ void monitor_writeDec(uint32_t v) { if (v == 0) { monitor_put('0'); diff --git a/src/kernel/core/monitor.h b/src/kernel/core/monitor.h index f4605cf..c04632e 100644 --- a/src/kernel/core/monitor.h +++ b/src/kernel/core/monitor.h @@ -10,6 +10,11 @@ void monitor_writeHex(uint32_t v); void monitor_writeDec(uint32_t v); #define NL monitor_put('\n'); +#define WHERE { monitor_write("(kernel:"); \ + monitor_write(__FILE__); \ + monitor_write(":"); \ + monitor_writeDec(__LINE__); \ + monitor_write(") "); } #endif diff --git a/src/kernel/core/sys.c b/src/kernel/core/sys.c index 1e07f7c..96d4908 100644 --- a/src/kernel/core/sys.c +++ b/src/kernel/core/sys.c @@ -1,6 +1,9 @@ #include "sys.h" #include "monitor.h" +/* These four functions are wrappers around ASM operations. + These functions are used when comunicating with the system hardware. */ + void outb(uint16_t port, uint8_t value) { asm volatile("outb %1, %0" : : "dN"(port), "a"(value)); } @@ -21,14 +24,24 @@ uint16_t inw(uint16_t port) { return ret; } +/* These two functions stop the system, reporting an error message, because something bad happenned. */ + void panic(char* message, char* file, int line) { - monitor_write("\n>> PANIC: >>"); - monitor_write(message); monitor_write("<< at "); monitor_write(file); - monitor_write(":"); monitor_writeDec(line); - monitor_write("\nSystem halted -_-'"); + monitor_write("\n\nPANIC:\t\t"); monitor_write(message); + monitor_write("\n File:\t\t"); monitor_write(file); monitor_put(':'); monitor_writeDec(line); + monitor_write("\n\t\tSystem halted -_-'\n"); + asm volatile("cli; hlt"); +} + +void panic_assert(char* assertion, char* file, int line) { + monitor_write("\n\nASSERT FAILED:\t"); monitor_write(assertion); + monitor_write("\n File:\t\t"); monitor_write(file); monitor_put(':'); monitor_writeDec(line); + monitor_write("\n\t\tSystem halted -_-'\n"); asm volatile("cli; hlt"); } +/* Global system mutex. See comments in sys.h. */ + static uint32_t if_locks = 1; void cli() { diff --git a/src/kernel/core/sys.h b/src/kernel/core/sys.h index 2f8cd5e..f147922 100644 --- a/src/kernel/core/sys.h +++ b/src/kernel/core/sys.h @@ -9,11 +9,13 @@ uint8_t inb(uint16_t port); uint16_t inw(uint16_t port); #define PANIC(s) panic(s, __FILE__, __LINE__); +#define ASSERT(s) { if (!(s)) panic_assert(#s, __FILE__, __LINE__); } void panic(char* message, char* file, int line); +void panic_assert(char* assertion, char* file, int line); -void sti(); //GLOBAL SYSTEM MUTEX -void cli(); - -#define WHERE { monitor_write("(kernel:"); monitor_write(__FILE__); monitor_write(":"); monitor_writeDec(__LINE__); monitor_write(") "); } +/* For some actions, we use cli and sti as a global system mutex. + These actions include : system initialization, process creation, interrupt handling. */ +void cli(); //lock +void sti(); //unlock #endif diff --git a/src/kernel/ipc/shm.c b/src/kernel/ipc/shm.c index e760a0f..6e48613 100644 --- a/src/kernel/ipc/shm.c +++ b/src/kernel/ipc/shm.c @@ -4,6 +4,13 @@ #include <task/task.h> #include <core/sys.h> +static struct segment_map* shmseg_map(struct segment* seg, struct page_directory *pagedir, size_t offset); +static void shmseg_unmap(struct segment_map*); +static void shmseg_delete(struct segment *seg); +static int shmseg_handleFault(struct segment_map *map, size_t addr, int write); + +/* Call this function when creating a new shared memory segment. + Creates the shmseg struct and segment struct, fills them in. */ struct segment* shmseg_make(size_t len, struct process* owner) { struct shmseg *ss = kmalloc(sizeof(struct shmseg)); struct segment *se = kmalloc(sizeof(struct segment)); @@ -21,6 +28,8 @@ struct segment* shmseg_make(size_t len, struct process* owner) { return se; } +/* For internal use only. Called when the shared memory is mapped somewhere. + Creates the segment_map struct and fills it up. */ struct segment_map *shmseg_map(struct segment *seg, struct page_directory *pagedir, size_t offset) { struct segment_map *sm = kmalloc(sizeof(struct segment_map)); sm->start = offset; @@ -28,6 +37,8 @@ struct segment_map *shmseg_map(struct segment *seg, struct page_directory *paged return sm; } +/* For internal use only. Called when the shared memory is unmapped from somewhere. + Unmaps all the pages from that place. */ void shmseg_unmap(struct segment_map *sm) { size_t i; for (i = 0; i < sm->len; i += 0x1000) { @@ -36,6 +47,8 @@ void shmseg_unmap(struct segment_map *sm) { } } +/* For internal use only. Called when a page fault occurs. + Maps a page at that place, possibly requesting a free frame first. */ int shmseg_handleFault(struct segment_map *sm, size_t addr, int write) { struct shmseg *ss = sm->seg->seg_data; addr &= 0xFFFFF000; @@ -49,6 +62,8 @@ int shmseg_handleFault(struct segment_map *sm, size_t addr, int write) { return 0; } +/* For internal use only. Called when the shared memory is deleted. + Frees all the frames. */ void shmseg_delete(struct segment *seg) { struct shmseg *ss = seg->seg_data; unsigned i; @@ -58,6 +73,7 @@ void shmseg_delete(struct segment *seg) { kfree(ss->frames); } +/* Looks through the mapped segment for a process, looking for a shared memory segment at given offset. */ struct segment_map* shmseg_getByOff(struct process* pr, size_t offset) { struct segment_map* m = pr->pagedir->mappedSegs; while (m != 0) { diff --git a/src/kernel/ipc/shm.h b/src/kernel/ipc/shm.h index 895a619..5459dbb 100644 --- a/src/kernel/ipc/shm.h +++ b/src/kernel/ipc/shm.h @@ -11,11 +11,6 @@ struct shmseg { //Shared memory segment stuff struct segment* shmseg_make(size_t len, struct process* owner); -struct segment_map* shmseg_map(struct segment* seg, struct page_directory *pagedir, size_t offset); -void shmseg_unmap(struct segment_map*); -void shmseg_delete(struct segment *seg); -int shmseg_handleFault(struct segment_map *map, size_t addr, int write); - //find a shared memory segment in current address space by its offset struct segment_map* shmseg_getByOff(struct process* pr, size_t offset); diff --git a/src/kernel/lib/mutex.c b/src/kernel/lib/mutex.c index f3ed11f..2664eb0 100644 --- a/src/kernel/lib/mutex.c +++ b/src/kernel/lib/mutex.c @@ -1,6 +1,7 @@ #include "mutex.h" #include <task/task.h> +/* Internal use only. This function is atomic, meaning it cannot be interrupted by a system task switch. */ static uint32_t atomic_exchange(uint32_t* ptr, uint32_t newval) { uint32_t r; asm volatile("xchg (%%ecx), %%eax" : "=a"(r) : "c"(ptr), "a"(newval)); diff --git a/src/kernel/mem/gdt.c b/src/kernel/mem/gdt.c index e9a1be5..8ee403a 100644 --- a/src/kernel/mem/gdt.c +++ b/src/kernel/mem/gdt.c @@ -2,19 +2,23 @@ #include <stdlib.h> #include <core/monitor.h> -extern void gdt_flush(uint32_t); //ASM (idt_.asm) +extern void gdt_flush(uint32_t); //ASM (imported from idt_.asm) extern void tss_flush(); -#define GDT_ENTRIES 6 +#define GDT_ENTRIES 6 // The contents of each entry is defined in gdt_init. static struct tss_entry tss_entry; static struct gdt_entry gdt_entries[GDT_ENTRIES]; static struct gdt_ptr gdt_ptr; +/* This function is called by the task_switch function on each context switch. + It updates the TSS so that an interrupt will be handled on the correct kernel stack + (one kernel stack is allocated to each thread). */ void gdt_setKernelStack(uint32_t esp0) { tss_entry.esp0 = esp0; } +/* For internal use only. Writes one entry of the GDT with given parameters. */ static void gdt_setGate(int num, uint32_t base, uint32_t limit, uint8_t access, uint8_t gran) { gdt_entries[num].base_low = (base & 0xFFFF); gdt_entries[num].base_middle = (base >> 16) & 0xFF; @@ -26,6 +30,7 @@ static void gdt_setGate(int num, uint32_t base, uint32_t limit, uint8_t access, gdt_entries[num].access = access; } +/* For internal use only. Writes one entry of the GDT, that entry being a pointer to the TSS. */ static void gdt_writeTss(int num, uint32_t ss0, uint32_t esp0) { uint32_t base = (uint32_t)&tss_entry; uint32_t limit = base + sizeof(struct tss_entry); @@ -40,15 +45,16 @@ static void gdt_writeTss(int num, uint32_t ss0, uint32_t esp0) { tss_entry.ss = tss_entry.ds = tss_entry.es = tss_entry.fs = tss_entry.gs = 0x13; } +/* Write data to the GDT and enable it. */ void gdt_init() { gdt_ptr.limit = (sizeof(struct gdt_entry) * GDT_ENTRIES) - 1; gdt_ptr.base = (uint32_t)&gdt_entries; gdt_setGate(0, 0, 0, 0, 0); //Null segment - gdt_setGate(1, 0, 0xFFFFFFFF, 0x9A, 0xCF); //Kernel code segment - gdt_setGate(2, 0, 0xFFFFFFFF, 0x92, 0xCF); //Kernel data segment - gdt_setGate(3, 0, 0xFFFFFFFF, 0xFA, 0xCF); //User code segment - gdt_setGate(4, 0, 0xFFFFFFFF, 0xF2, 0xCF); //User data segment + gdt_setGate(1, 0, 0xFFFFFFFF, 0x9A, 0xCF); //Kernel code segment 0x08 + gdt_setGate(2, 0, 0xFFFFFFFF, 0x92, 0xCF); //Kernel data segment 0x10 + gdt_setGate(3, 0, 0xFFFFFFFF, 0xFA, 0xCF); //User code segment 0x18 + gdt_setGate(4, 0, 0xFFFFFFFF, 0xF2, 0xCF); //User data segment 0x20 gdt_writeTss(5, 0x10, 0); gdt_flush((uint32_t)&gdt_ptr); diff --git a/src/kernel/mem/gdt.h b/src/kernel/mem/gdt.h index 485002b..1b33dac 100644 --- a/src/kernel/mem/gdt.h +++ b/src/kernel/mem/gdt.h @@ -3,6 +3,11 @@ #include <types.h> +/* The GDT is one of the x86's descriptor tables. It is used for memory segmentation. + Here, we don't use segmentation to separate processes from one another (this is done with paging). + We only use segmentation to make the difference between kernel mode (ring 3) and user mode (ring 0) */ + +/* One entry of the table */ struct gdt_entry { uint16_t limit_low; uint16_t base_low; @@ -12,11 +17,15 @@ struct gdt_entry { uint8_t base_high; } __attribute__((packed)); +/* Structure defining the whole table : address and size (in bytes). */ struct gdt_ptr { uint16_t limit; uint32_t base; } __attribute__((packed)); +/* The TSS is used for hardware multitasking. + We don't use that, but we still need a TSS so that user mode process exceptions + can be handled correctly by the kernel. */ struct tss_entry { uint32_t prev_tss; // The previous TSS - if we used hardware task switching this would form a linked list. uint32_t esp0; // The stack pointer to load when we change to kernel mode. diff --git a/src/kernel/mem/heap.c b/src/kernel/mem/heap.c index b6d2cd4..b7f6c97 100644 --- a/src/kernel/mem/heap.c +++ b/src/kernel/mem/heap.c @@ -6,6 +6,7 @@ /* ******************* HEADER ****************** */ +/* For internal use only. Inserts a hole in the heap's hole index at the correct position. */ static void heapIdx_insert(struct heap *heap, struct heap_header *e) { if ((heap->idxused + sizeof(struct heap_header*) + (size_t)heap->idx) >= heap->start_addr) return; @@ -28,6 +29,7 @@ static void heapIdx_insert(struct heap *heap, struct heap_header *e) { } } +/* For internal use only. Removes a hole from the heap's hole index. */ static void heapIdx_remove(struct heap *heap, struct heap_header *e) { uint32_t iterator; for (iterator = 0; iterator < heap->idxused; iterator++) { @@ -43,6 +45,7 @@ static void heapIdx_remove(struct heap *heap, struct heap_header *e) { /* ******************** CONTENTS ********************* */ +/* Initializes the heap, creates the correct data structures. */ void heap_create(struct heap *heap, size_t start, size_t idxsize, size_t datasize, size_t maxdatasize) { uint32_t i; @@ -71,6 +74,7 @@ void heap_create(struct heap *heap, size_t start, size_t idxsize, size_t datasiz heapIdx_insert(heap, hole); } +/* For internal use only. Called by heap_alloc when necessary. Expands the heap to take more space. */ static uint32_t heap_expand(struct heap *heap, size_t quantity) { uint32_t i; @@ -116,6 +120,7 @@ static uint32_t heap_expand(struct heap *heap, size_t quantity) { return 1; } +/* For internal use only. Called by heap_free when necessary. Reduces the heap's size. */ static void heap_contract(struct heap *heap) { struct heap_footer *last_footer = (struct heap_footer*)(heap->end_addr - sizeof(struct heap_footer)); struct heap_header *last_header = last_footer->header; @@ -142,6 +147,7 @@ static void heap_contract(struct heap *heap) { } } +/* Alocate some bytes on the heap. */ void* heap_alloc(struct heap *heap, size_t sz) { size_t newsize = sz + sizeof(struct heap_header) + sizeof(struct heap_footer); uint32_t iterator = 0; @@ -185,6 +191,7 @@ void* heap_alloc(struct heap *heap, size_t sz) { return (void*)((size_t)loc + sizeof(struct heap_header)); } +/* Frees a block previously allocated on the heap. */ void heap_free(struct heap *heap, void* ptr) { if (ptr == 0) return; if ((size_t)ptr < heap->start_addr || (size_t)ptr > heap->end_addr) return; diff --git a/src/kernel/mem/heap.h b/src/kernel/mem/heap.h index 39ba37e..8f8cdcf 100644 --- a/src/kernel/mem/heap.h +++ b/src/kernel/mem/heap.h @@ -1,6 +1,11 @@ #ifndef DEF_HEAP_H #define DEF_HEAP_H +/* The heap is the data structure that permits allocating and freeing memory easily. + The functions in this file are only used by mem.c, which provides kmalloc and kfree. + The heap algorithm used is the one described here : + http://www.jamesmolloy.co.uk/tutorial_html/7.-The%20Heap.html */ + #include "types.h" struct heap_header { diff --git a/src/kernel/mem/mem.c b/src/kernel/mem/mem.c index 06242a5..47a03dc 100644 --- a/src/kernel/mem/mem.c +++ b/src/kernel/mem/mem.c @@ -22,6 +22,7 @@ static struct freepage { } freepages[FREEPAGESTOKEEP]; uint32_t freepagecount = 0; +/* For internal use only. Populates the cache of pages that can be given to requesters. */ static void get_free_pages() { static uint32_t locked = 0; uint32_t i; @@ -49,6 +50,7 @@ static void get_free_pages() { locked = 0; } +/* Gives one page from the cache to someone requesting it. */ void* kmalloc_page(size_t *phys) { cli(); get_free_pages(); @@ -72,12 +74,14 @@ void kfree_page(void* ptr) { static struct heap kheap; +/* Called on kernel start. Creates the kernel heap. */ void kheap_init() { heap_create(&kheap, (mem_placementAddr & 0xFFFFF000) + 0x1000, KHEAP_IDXSIZE, KHEAP_INITSIZE, KHEAP_MAXSIZE); kheap_working = 1; monitor_write("[KHeap] "); } +/* Allocates on the heap if possible. If not possible, allocates just after the kernel code. */ void* kmalloc(size_t size) { if (kheap_working) { return heap_alloc(&kheap, size); diff --git a/src/kernel/mem/mem.h b/src/kernel/mem/mem.h index 5417d5f..b372fae 100644 --- a/src/kernel/mem/mem.h +++ b/src/kernel/mem/mem.h @@ -1,6 +1,9 @@ #ifndef DEF_MEM_H #define DEF_MEM_H +/* mem.c provides the kernel's allocation and freeing functions. + kmalloc_page and kfree_page are used mostly for paging. */ + #include <types.h> void* kmalloc_page(size_t *phys); diff --git a/src/kernel/mem/paging.c b/src/kernel/mem/paging.c index 4992d5e..f8f69f1 100644 --- a/src/kernel/mem/paging.c +++ b/src/kernel/mem/paging.c @@ -11,7 +11,9 @@ static struct bitset frames; struct page_directory *kernel_pagedir, *current_pagedir; -/* ACCESSOR FUNCTIONS FOR STATIC BITSET */ +/************************** PHYSICAL MEMORY ALLOCATION ************************/ + +/* Allocates a page of physical memory. */ uint32_t frame_alloc() { uint32_t free = bitset_firstFree(&frames); bitset_set(&frames, free); @@ -22,6 +24,13 @@ void frame_free(uint32_t id) { bitset_clear(&frames, id); } +/************************* PAGING INITIALIZATION *****************************/ + +/* This function creates the kernel page directory. It must be called before the GDT is loaded. + It maps 0xE0000000+ to the corresponding physical kernel code, but it also maps + 0x00000000+ to that code because with the false GDT we set up in loader_.asm, + the code will be looked for at the beginning of the memory. Only when the real GDT is loaded + we can de-allocate pages at 0x00000000 ; this is done by paging_cleanup. */ void paging_init(size_t totalRam) { uint32_t i; @@ -50,6 +59,7 @@ void paging_init(size_t totalRam) { monitor_write("} [Paging] "); } +/* De-allocates pages at 0x00000000 where kernel code was read from with the GDT from loader_.asm. */ void paging_cleanup() { uint32_t i; for (i = 0; i < (mem_placementAddr - 0xE0000000) / 0x100000; i++) { @@ -59,6 +69,10 @@ void paging_cleanup() { monitor_write("[PD Cleanup] "); } +/************************* PAGING EVERYDAY USE *****************************/ + +/* Switch to a page directory. Can be done if we are sure not to be interrupted by a task switch. + Example use for cross-memory space writing in linker/elf.c */ void pagedir_switch(struct page_directory *pd) { current_pagedir = pd; asm volatile("mov %0, %%cr3" : : "r"(pd->physicalAddr)); @@ -68,6 +82,7 @@ void pagedir_switch(struct page_directory *pd) { asm volatile("mov %0, %%cr0" : : "r"(cr0)); } +/* Creates a new page directory for a process, and maps the kernel page tables on it. */ struct page_directory *pagedir_new() { uint32_t i; @@ -87,6 +102,7 @@ struct page_directory *pagedir_new() { return pd; } +/* Deletes a page directory, cleaning it up. */ void pagedir_delete(struct page_directory *pd) { uint32_t i; //Unmap segments @@ -99,6 +115,9 @@ void pagedir_delete(struct page_directory *pd) { kfree(pd); } +/* Handle a paging fault. First, looks for the corresponding segment. + If the segment was found and it handles the fault, return normally. + Else, display informatinos and return an error. */ uint32_t paging_fault(struct registers *regs) { size_t addr; struct segment_map *seg = 0; @@ -127,6 +146,9 @@ uint32_t paging_fault(struct registers *regs) { return 0; } +/* Gets the corresponding page in a page directory for a given address. + If make is set, the necessary page table can be created. + Can return 0 if make is not set. */ struct page *pagedir_getPage(struct page_directory *pd, uint32_t address, int make) { address /= 0x1000; uint32_t table_idx = address / 1024; @@ -145,6 +167,7 @@ struct page *pagedir_getPage(struct page_directory *pd, uint32_t address, int ma } } +/* Modifies a page structure so that it is mapped to a frame. */ void page_map(struct page *page, uint32_t frame, uint32_t user, uint32_t rw) { if (page != 0 && page->frame == 0 && page->present == 0) { page->present = 1; @@ -154,6 +177,7 @@ void page_map(struct page *page, uint32_t frame, uint32_t user, uint32_t rw) { } } +/* Modifies a page structure so that it is no longer mapped to a frame. */ void page_unmap(struct page *page) { if (page != 0) { page->frame = 0; @@ -161,6 +185,7 @@ void page_unmap(struct page *page) { } } +/* Same as above but also frees the frame. */ void page_unmapFree(struct page *page) { if (page != 0) { if (page->frame != 0) frame_free(page->frame); diff --git a/src/kernel/mem/seg.c b/src/kernel/mem/seg.c index e3eca67..4a33db3 100644 --- a/src/kernel/mem/seg.c +++ b/src/kernel/mem/seg.c @@ -2,6 +2,8 @@ #include "mem.h" #include <core/sys.h> +/* Call this function when mapping a segment to a page directory. + Calls the appropriate map method and updates the segment's and pagedir's information. */ struct segment_map *seg_map(struct segment* seg, struct page_directory *pagedir, size_t offset) { struct segment_map *sm = seg->map(seg, pagedir, offset); if (sm == 0) return 0; @@ -13,6 +15,9 @@ struct segment_map *seg_map(struct segment* seg, struct page_directory *pagedir, return sm; } +/* Call this function when unmapping a segment from a page directory. + The segment will automatically be deleted if it is not mapped. + Calls the appropriate unmap method and updates the segment's and pagedir's information. */ void seg_unmap(struct segment_map *map) { map->seg->unmap(map); if (map->pagedir->mappedSegs == map) { @@ -32,7 +37,14 @@ void seg_unmap(struct segment_map *map) { } // ************************************ SIMPLESEG stuff ************* + +static struct segment_map* simpleseg_map(struct segment* seg, struct page_directory* pagedir, size_t offset); +static void simpleseg_unmap(struct segment_map*); +static void simpleseg_delete(struct segment *seg); +static int simpleseg_handleFault(struct segment_map* map, size_t addr, int write); +/* Call this when creating a simpleseg. + Creates the simpleseg structure and the segment structure and fills them up. */ struct segment* simpleseg_make(size_t start, size_t len, int writable) { struct simpleseg *ss = kmalloc(sizeof(struct simpleseg)); struct segment *se = kmalloc(sizeof(struct segment)); @@ -46,6 +58,7 @@ struct segment* simpleseg_make(size_t start, size_t len, int writable) { return se; } +/* For internal use only. Called when a simpleseg is mapped to a pagedirectory. */ struct segment_map* simpleseg_map(struct segment* seg, struct page_directory* pagedir, size_t offset) { struct segment_map *sm = kmalloc(sizeof(struct segment_map)); sm->start = ((struct simpleseg*)(seg->seg_data))->start; @@ -53,6 +66,8 @@ struct segment_map* simpleseg_map(struct segment* seg, struct page_directory* pa return sm; } +/* For internal use only. Called when a simpleseg is unmapped. + Frees all the allocated pages. */ void simpleseg_unmap(struct segment_map* sm) { size_t i; for (i = sm->start; i < sm->start + sm->len; i += 0x1000) { @@ -60,6 +75,7 @@ void simpleseg_unmap(struct segment_map* sm) { } } +/* For internal use only. Handles a page fault. Can allocate and map a frame if necessary. */ int simpleseg_handleFault(struct segment_map* sm, size_t addr, int write) { struct simpleseg *ss = sm->seg->seg_data; if (write && !ss->writable) return 1; @@ -70,14 +86,16 @@ int simpleseg_handleFault(struct segment_map* sm, size_t addr, int write) { return 0; } +/* For internal use only. Called when the simpleseg is deleted. Does nothing. */ void simpleseg_delete(struct segment* seg) { } +/* Call this to resize a simpleseg. Ajusts the size and frees pages if the new size is smaller.*/ int simpleseg_resize(struct segment_map *map, size_t len) { size_t i; if (map == 0) return -1; - if (map->seg->delete != simpleseg_delete) return -2; + if (map->seg->delete != simpleseg_delete) return -2; //check segment is a simpleseg struct simpleseg *s = (struct simpleseg*)map->seg->seg_data; if (len & 0xFFF) len = (len & 0xFFFFF000) + 0x1000; diff --git a/src/kernel/mem/seg.h b/src/kernel/mem/seg.h index 26664dc..d37ba59 100644 --- a/src/kernel/mem/seg.h +++ b/src/kernel/mem/seg.h @@ -1,6 +1,9 @@ #ifndef DEF_SEG_H #define DEF_SEG_H +/* Segments are pieces of usable memory in a process' address space. + They have nothing to do with GDT segments, they are created over paging. */ + #include "paging.h" struct segment_map; @@ -8,6 +11,7 @@ struct segment { void* seg_data; int mappings; + // these 4 functions must not be used directly by anyone struct segment_map* (*map)(struct segment* seg, struct page_directory* pagedir, size_t offset); void (*unmap)(struct segment_map*); void (*delete)(struct segment* seg); @@ -21,8 +25,9 @@ struct segment_map { struct segment_map *next; }; -//parameter offset in seg_map needs not be used +//parameter offset in seg_map doesn't need to be used struct segment_map *seg_map(struct segment* seg, struct page_directory* pagedir, size_t offset); +/* When unmapping a segment, the segment is deleted if it is not mapped anywhere anymore. */ void seg_unmap(struct segment_map* map); /// ************************************* SIMPLESEG stuff ***************** @@ -32,11 +37,7 @@ struct simpleseg { size_t start, len; }; -struct segment* simpleseg_make(size_t start, size_t len, int writable); -struct segment_map* simpleseg_map(struct segment* seg, struct page_directory* pagedir, size_t offset); -void simpleseg_unmap(struct segment_map*); -void simpleseg_delete(struct segment *seg); -int simpleseg_handleFault(struct segment_map* map, size_t addr, int write); +struct segment* simpleseg_make(size_t start, size_t len, int writable); int simpleseg_resize(struct segment_map *map, size_t len); #endif diff --git a/src/kernel/task/idt.c b/src/kernel/task/idt.c index 80f13db..887ebf7 100644 --- a/src/kernel/task/idt.c +++ b/src/kernel/task/idt.c @@ -67,6 +67,8 @@ struct idt_ptr idt_ptr; static int_callback irq_handlers[16] = {0}; static struct thread* irq_wakeup[16] = {0}; +/* Called in idt_.asm when an exception fires (interrupt 0 to 31). + Tries to handle the exception, panics if fails. */ void idt_isrHandler(struct registers regs) { if ((regs.int_no == 14 && paging_fault(®s) != 0) || regs.int_no != 14) { if (tasking_handleException(®s) == 0) { @@ -80,6 +82,8 @@ void idt_isrHandler(struct registers regs) { } } +/* Called in idt_.asm when an IRQ fires (interrupt 32 to 47) + Possibly wakes up a thread that was waiting, possibly calls a handler. */ void idt_irqHandler(struct registers regs) { uint32_t doSwitch = (regs.err_code == 0); //IRQ0 = timer if (regs.err_code > 7) { @@ -97,14 +101,15 @@ void idt_irqHandler(struct registers regs) { if (doSwitch) tasking_switch(); } +/* Called in idt_.asm on a system call (interrupt 64). + Calls the correct syscall handler (if any). */ void idt_syscallHandler(struct registers regs) { - if (syscalls[regs.eax] != 0) { + if (regs.eax < NUMBER_OF_SYSCALLS && syscalls[regs.eax] != 0) { syscalls[regs.eax](®s); - } else { - monitor_write("Unhandled syscall...\n"); } } +/* For internal use only. Sets up an entry of the IDT with given parameters. */ static void idt_setGate(uint8_t num, uint32_t base, uint16_t sel, uint8_t flags) { idt_entries[num].base_lo = base & 0xFFFF; idt_entries[num].base_hi = (base >> 16) & 0xFFFF; @@ -114,6 +119,7 @@ static void idt_setGate(uint8_t num, uint32_t base, uint16_t sel, uint8_t flags) idt_entries[num].flags = flags | 0x60; } +/* Remaps the IRQs. Sets up the IDT. */ void idt_init() { idt_ptr.limit = (sizeof(struct idt_entry) * 256) - 1; idt_ptr.base = (uint32_t)&idt_entries; @@ -189,12 +195,14 @@ void idt_init() { monitor_write("[IDT] "); } +/* Sets up an IRQ handler for given IRQ. */ void idt_handleIrq(int number, int_callback func) { if (number < 16 && number >= 0) { irq_handlers[number] = func; } } +/* Tells the IRQ handler to wake up the current thread when specified IRQ fires. */ void idt_waitIrq(int number) { if (number < 16 && number >= 0 && proc_priv() <= PL_DRIVER) { irq_wakeup[number] = current_thread; diff --git a/src/kernel/task/idt.h b/src/kernel/task/idt.h index b12d6c5..1ac03e2 100644 --- a/src/kernel/task/idt.h +++ b/src/kernel/task/idt.h @@ -1,6 +1,12 @@ #ifndef DEF_IDT_H #define DEF_IDT_H +/* The IDT is the system descriptor table that tells the CPU what to do when an interrupt fires. + There are three categories of interrupts : + - Exceptions ; ex: page fault, divide by 0 + - IRQ : interrupts caused by hardware + - System calls : when an applications asks the system to do something */ + #include <types.h> struct idt_entry { diff --git a/src/kernel/task/syscall.c b/src/kernel/task/syscall.c index a5142eb..41cd0ea 100644 --- a/src/kernel/task/syscall.c +++ b/src/kernel/task/syscall.c @@ -38,7 +38,7 @@ static void thread_new_sc(struct registers* r) { thread_new(current_thread->process, (thread_entry)r->ebx, (void*)r->ecx); } -int_callback syscalls[] = { +int_callback syscalls[NUMBER_OF_SYSCALLS] = { thread_exit_sc, //0 schedule_sc, thread_sleep_sc, diff --git a/src/kernel/task/syscall.h b/src/kernel/task/syscall.h index 54af108..f03be55 100644 --- a/src/kernel/task/syscall.h +++ b/src/kernel/task/syscall.h @@ -3,6 +3,8 @@ #include "idt.h" +#define NUMBER_OF_SYSCALLS 32 + extern int_callback syscalls[]; #endif diff --git a/src/kernel/task/task.c b/src/kernel/task/task.c index 8afc609..d8670a0 100644 --- a/src/kernel/task/task.c +++ b/src/kernel/task/task.c @@ -17,19 +17,20 @@ static void thread_exit2(uint32_t reason); static void thread_delete(struct thread *th); static void process_delete(struct process *pr); +static uint32_t thread_runnable(struct thread *th); + //From task_.asm extern uint32_t read_eip(); extern void task_idle(void*); -static uint32_t thread_runnable(struct thread *th); - static uint32_t nextpid = 1; - struct process *processes = 0, *kernel_process; struct thread *threads = 0, *current_thread = 0, *idle_thread; uint32_t tasking_tmpStack[KSTACKSIZE]; +/* Sets up tasking. Called by kmain on startup. + Creates a kernel process and an IDLE thread in it. */ void tasking_init() { cli(); kernel_process = kmalloc(sizeof(struct process)); //This process must be hidden to users @@ -45,6 +46,19 @@ void tasking_init() { monitor_write("[Tasking] "); } +/* Called by the paging functions when a page table is allocated in the kernel space (>0xE0000000). + Updates the page directories of all the processes. */ +void tasking_updateKernelPagetable(uint32_t idx, struct page_table *table, uint32_t tablephysical) { + if (idx < 896) return; + struct process* it = processes; + while (it != 0) { + it->pagedir->tables[idx] = table; + it->pagedir->tablesPhysical[idx] = tablephysical; + it = it->next; + } +} + +/* Looks through the list of threads, finds the next thread to run. */ static struct thread *thread_next() { if (current_thread == 0 || current_thread == idle_thread) current_thread = threads; struct thread *ret = current_thread; @@ -60,6 +74,7 @@ static struct thread *thread_next() { } } +/* Called when a timer IRQ fires. Does a context switch. */ void tasking_switch() { if (threads == 0) PANIC("No more threads to run !"); asm volatile("cli"); @@ -94,16 +109,8 @@ void tasking_switch() { : : "r"(current_thread->ebp), "r"(current_thread->esp), "r"(current_thread->eip)); } -void tasking_updateKernelPagetable(uint32_t idx, struct page_table *table, uint32_t tablephysical) { - if (idx < 896) return; - struct process* it = processes; - while (it != 0) { - it->pagedir->tables[idx] = table; - it->pagedir->tablesPhysical[idx] = tablephysical; - it = it->next; - } -} - +/* Called when an exception happens. Provides a stack trace if it was in kernel land. + Ends the thread for most exceptions, ends the whole process for page faults. */ uint32_t tasking_handleException(struct registers *regs) { if (current_thread == 0) return 0; //No tasking yet NL; WHERE; monitor_write("Unhandled exception : "); @@ -134,6 +141,7 @@ uint32_t tasking_handleException(struct registers *regs) { return 0; } +/* Makes the current thread sleep. */ void thread_sleep(uint32_t msecs) { if (current_thread == 0) return; current_thread->state = TS_SLEEPING; @@ -141,20 +149,25 @@ void thread_sleep(uint32_t msecs) { tasking_switch(); } +/* Puts the current thread in an inactive state. */ void thread_goInactive() { current_thread->state = TS_WAKEWAIT; tasking_switch(); } +/* Wakes up the given thread. */ void thread_wakeUp(struct thread* t) { if (t->state == TS_WAKEWAIT) t->state = TS_RUNNING; } +/* Returns the privilege level of the current process. */ int proc_priv() { if (current_thread == 0 || current_thread->process == 0) return PL_UNKNOWN; return current_thread->process->privilege; } +/* For internal use only. Called by thread_exit_stackJmp on a stack that will not be deleted. + Exits current thread or process, depending on the reason. */ void thread_exit2(uint32_t reason) { //See EX_TH_* defines in task.h /* * if reason == EX_TH_NORMAL, it is just one thread exiting because it has to @@ -170,9 +183,12 @@ void thread_exit2(uint32_t reason) { //See EX_TH_* defines in task.h process_delete(pr); } retrn: + sti(); tasking_switch(); } +/* For internal use only. Called by thread_exit and process_exit. + Switches to a stack that will not be deleted when current thread is deleted. */ void thread_exit_stackJmp(uint32_t reason) { cli(); uint32_t *stack; @@ -188,15 +204,18 @@ void thread_exit_stackJmp(uint32_t reason) { "r"(stack), "r"(stack), "r"(thread_exit2), "r"(kernel_pagedir->physicalAddr)); } +/* System call. Exit the current thread. */ void thread_exit() { thread_exit_stackJmp(EX_TH_NORMAL); } +/* System call. Exit the current process. */ void process_exit(uint32_t retval) { if (retval == EX_TH_NORMAL || retval == EX_TH_EXCEPTION) retval = EX_PR_EXCEPTION; thread_exit_stackJmp(retval); } +/* Nonzero if given thread is not in a waiting state. */ static uint32_t thread_runnable(struct thread *t) { if (t->state == TS_RUNNING) return 1; if (t->state == TS_SLEEPING && timer_time() >= t->timeWait) { @@ -206,6 +225,9 @@ static uint32_t thread_runnable(struct thread *t) { return 0; } +/* For internal use only. This is called when a newly created thread first runs + (its address is the value given for EIP). + It switches to user mode if necessary and calls the entry point. */ static void thread_run(struct thread *thread, thread_entry entry_point, void *data) { pagedir_switch(thread->process->pagedir); if (thread->process->privilege >= PL_SERVICE) { //User mode ! @@ -248,6 +270,9 @@ static void thread_run(struct thread *thread, thread_entry entry_point, void *da thread_exit(0); } +/* Creates a new thread for given process. + Allocates a kernel stack and a user stack if necessary. + Sets up the kernel stack for values to be passed to thread_run. */ struct thread *thread_new(struct process *proc, thread_entry entry_point, void *data) { struct thread *t = kmalloc(sizeof(struct thread)); t->process = proc; @@ -284,6 +309,7 @@ struct thread *thread_new(struct process *proc, thread_entry entry_point, void * return t; } +/* Creates a new process. Creates a struct process and fills it up. */ struct process *process_new(struct process* parent, uint32_t uid, uint32_t privilege) { struct process* p = kmalloc(sizeof(struct process)); p->pid = (nextpid++); @@ -307,6 +333,7 @@ struct process *process_new(struct process* parent, uint32_t uid, uint32_t privi return p; } +/* Deletes given thread, freeing the stack(s). */ static void thread_delete(struct thread *th) { if (threads == th) { threads = th->next; @@ -327,6 +354,7 @@ static void thread_delete(struct thread *th) { kfree(th); } +/* Deletes a process. First, deletes all its threads. Also deletes the corresponding page directory. */ static void process_delete(struct process *pr) { struct thread *it = threads; while (it != 0) { @@ -354,6 +382,7 @@ static void process_delete(struct process *pr) { kfree(pr); } +/* System call. Called by the app to define the place for the heap. */ int process_setheapseg(size_t start, size_t end) { //syscall struct process *p = current_thread->process; if (start >= 0xE0000000 || end >= 0xE0000000) return -1; diff --git a/src/kernel/task/task.h b/src/kernel/task/task.h index 3dad63d..ef93c9f 100644 --- a/src/kernel/task/task.h +++ b/src/kernel/task/task.h @@ -57,13 +57,14 @@ void tasking_updateKernelPagetable(uint32_t idx, struct page_table *table, uint3 uint32_t tasking_handleException(struct registers *regs); void thread_sleep(uint32_t msecs); -void thread_goInactive(); //Blocks the current thread. another one must be there to wake it up at some point. +void thread_goInactive(); //Blocks the current thread. it is then waked up by another thread or a system event. void thread_wakeUp(struct thread *t); int proc_priv(); //Returns current privilege level -void thread_exit(); -void process_exit(uint32_t retval); struct thread * thread_new(struct process *proc, thread_entry entry_point, void *data); struct process* process_new(struct process *parent, uint32_t uid, uint32_t privilege); + +void thread_exit(); //syscall +void process_exit(uint32_t retval); //syscall int process_setheapseg(size_t start, size_t end); //syscall #endif diff --git a/src/kernel/task/timer.c b/src/kernel/task/timer.c index e924657..1ec1523 100644 --- a/src/kernel/task/timer.c +++ b/src/kernel/task/timer.c @@ -5,6 +5,8 @@ static uint32_t tick = 0, frequency = 0, uptime = 0; +/* Called when IRQ0 fires. Updates the uptime variable. + DOES NOT provoke a task switch. The task switch is called in idt.c (IRQ handler). */ void timer_callback(struct registers *regs) { tick++; if (tick == frequency) { @@ -13,12 +15,15 @@ void timer_callback(struct registers *regs) { } } +/* Accessor function to get machine uptime. */ uint32_t timer_uptime() { return uptime; } +/* Accessor function, gets uptime in miliseconds. */ uint32_t timer_time() { return (uptime * 1000) + (tick * 1000 / frequency); } +/* Called by kmain. Sets up the PIT and the IRQ0 handler. */ void timer_init(uint32_t freq) { frequency = freq; |