summaryrefslogtreecommitdiff
path: root/src/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'src/kernel')
-rw-r--r--src/kernel/core/kmain.c12
-rw-r--r--src/kernel/core/monitor.c27
-rw-r--r--src/kernel/core/monitor.h5
-rw-r--r--src/kernel/core/sys.c21
-rw-r--r--src/kernel/core/sys.h10
-rw-r--r--src/kernel/ipc/shm.c16
-rw-r--r--src/kernel/ipc/shm.h5
-rw-r--r--src/kernel/lib/mutex.c1
-rw-r--r--src/kernel/mem/gdt.c18
-rw-r--r--src/kernel/mem/gdt.h9
-rw-r--r--src/kernel/mem/heap.c7
-rw-r--r--src/kernel/mem/heap.h5
-rw-r--r--src/kernel/mem/mem.c4
-rw-r--r--src/kernel/mem/mem.h3
-rw-r--r--src/kernel/mem/paging.c27
-rw-r--r--src/kernel/mem/seg.c20
-rw-r--r--src/kernel/mem/seg.h13
-rw-r--r--src/kernel/task/idt.c14
-rw-r--r--src/kernel/task/idt.h6
-rw-r--r--src/kernel/task/syscall.c2
-rw-r--r--src/kernel/task/syscall.h2
-rw-r--r--src/kernel/task/task.c55
-rw-r--r--src/kernel/task/task.h7
-rw-r--r--src/kernel/task/timer.c5
24 files changed, 232 insertions, 62 deletions
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(&regs) != 0) || regs.int_no != 14) {
if (tasking_handleException(&regs) == 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](&regs);
- } 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;