diff --git a/.gitignore b/.gitignore index 8de7009..3db12c3 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,6 @@ iso_root symbols.map symbols.S *.log -build/ \ No newline at end of file +build/ +compile_commands.json +.cache/ \ No newline at end of file diff --git a/README.md b/README.md index 70a26f1..d97dd0c 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,12 @@ First install the dependencies: `sudo apt install python3 xorriso make qemu-system` +Also, you have to get an x86_64 toolchain for compilation. The easiest way to do that on most systems is to install it from Homebrew: + +``` +brew install x86_64-elf-gcc +``` + Then, to compile the kernel and make an ISO image file: `make build-iso` To run it with QEMU, `make run` diff --git a/docs/STYLE.md b/docs/STYLE.md index c463c98..aa5e220 100644 --- a/docs/STYLE.md +++ b/docs/STYLE.md @@ -71,12 +71,16 @@ Function prototypes should include parameter names and their data types. ## Commenting -Comments should describe what a function does and why, not how it does it. +Comments should describe what a function does and why, not how it does it. The preferred way of commenting functions is the following: ```c /* - * This is the preferred style for multi-line - * comments in the Pepper kernel + * function_name - Function brief description + * @arg1: Argument 1 description + * @arg2: Argument 2 description + * + * A longer description can be featured here, explaining more + * in detail what the function does and why it does it. */ ``` diff --git a/src/boot/boot.c b/src/boot/boot.c index 20abcd1..966f202 100644 --- a/src/boot/boot.c +++ b/src/boot/boot.c @@ -1,6 +1,11 @@ /* * @author xamidev * @brief Limine requests for boot + * @description + * The kernel makes a few requests to the Limine bootloader + * in order to get precious information about the system. + * We get a framebuffer, a memory map, the address of the + * kernel in memory, and the Higher Half Direct Map offset. * @license GPL-3.0-only */ diff --git a/src/config.h b/src/config.h index 61cd7a7..1c0bf25 100644 --- a/src/config.h +++ b/src/config.h @@ -39,4 +39,7 @@ /* term */ #define TERM_HISTORY_MAX_LINES 256 +/* time */ +#define TIMER_FREQUENCY 1000 + #endif diff --git a/src/debug/misc.c b/src/debug/misc.c index 19c076f..874c328 100644 --- a/src/debug/misc.c +++ b/src/debug/misc.c @@ -10,7 +10,14 @@ extern struct boot_context boot_ctx; -// Display the memmap so we see how the memory is laid out at handoff +/* + * memmap_display - displays a memory map + * @response: Limine memory map response + * + * Displays the memory map we get from Limine + * to see different regions, their sizes, and + * how the memory is laid out at handoff. + */ void memmap_display(struct limine_memmap_response* response) { DEBUG("Got memory map from Limine: revision %u, %u entries", response->revision, response->entry_count); @@ -51,12 +58,18 @@ void memmap_display(struct limine_memmap_response* response) } } -// Display the HHDM +/* + * hhdm_display - displays the HHDM offset + * @hhdm: Limine HHDM offset response + */ void hhdm_display(struct limine_hhdm_response* hhdm) { DEBUG("Got HHDM revision=%u offset=0x%p", hhdm->revision, hhdm->offset); } +/* + * boot_mem_display - displays all memory info + */ void boot_mem_display() { memmap_display(boot_ctx.mmap); diff --git a/src/debug/panic.c b/src/debug/panic.c index e1cf694..d15a27d 100644 --- a/src/debug/panic.c +++ b/src/debug/panic.c @@ -11,6 +11,15 @@ extern struct init_status init; +/* + * panic - Kernel panic + * @ctx: CPU context (optional) + * @str: Error message + * + * Ends execution of the kernel in case of an unrecoverable error. + * Will display to terminal if it is initialized, otherwise serial only. + * Can be called with or without a CPU context. + */ void panic(struct cpu_status_t* ctx, const char* str) { CLEAR_INTERRUPTS; diff --git a/src/debug/stacktrace.c b/src/debug/stacktrace.c index f1bca8e..1717663 100644 --- a/src/debug/stacktrace.c +++ b/src/debug/stacktrace.c @@ -9,6 +9,13 @@ extern struct init_status init; +/* + * debug_stack_trace - Prints the stack trace + * @max_frames: Maximum amount of stack frames to walk + * + * Walks back the stack and gets all return values (RIP) + * and prints them to the DEBUG interface. + */ void debug_stack_trace(unsigned int max_frames) { DEBUG("*** begin stack trace ***"); @@ -52,7 +59,16 @@ typedef struct { __attribute__((weak)) extern kernel_symbol_t symbol_table[]; __attribute__((weak)) extern uint64_t symbol_count; -// binary search +/* + * debug_find_symbol - Finds the symbol name associated to an address + * @rip: Pointer to executable code + * @offset: Out pointer to reference the offset in the found function, if any + * + * Return: + * - symbol name + * "???" - no symbol table found + * "unknown" - symbol table found, but address isn't in the table + */ const char* debug_find_symbol(uintptr_t rip, uintptr_t* offset) { if (!symbol_table || symbol_count == 0) { diff --git a/src/idt/idt.c b/src/idt/idt.c index ee1fa5c..dd465e9 100644 --- a/src/idt/idt.c +++ b/src/idt/idt.c @@ -24,6 +24,12 @@ extern char vector_0_handler[]; // Timer ticks extern volatile uint64_t ticks; +/* + * idt_set_entry - Sets an Interrupt Descriptor Table entry + * @vector: Vector number in the IDT + * @handler: Pointer to the executable Interrupt Service Routine + * @dpl: Desired privilege level + */ void idt_set_entry(uint8_t vector, void* handler, uint8_t dpl) { uint64_t handler_addr = (uint64_t)handler; @@ -42,6 +48,10 @@ void idt_set_entry(uint8_t vector, void* handler, uint8_t dpl) entry->ist = 0; } +/* + * idt_load - Loads the Interrupt Descriptor Table + * @idt_addr: Address to the IDT + */ void idt_load(void* idt_addr) { // "limit" = "size" = Size of the IDT - 1 byte = (16*256)-1 = 0xFFF @@ -50,9 +60,14 @@ void idt_load(void* idt_addr) asm volatile("lidt %0" :: "m"(idt_reg)); } +/* + * idt_init - Initializes the Interrupt Descriptor Table + * + * Sets all IDT entries and their corresponding service routines, + * then loads it. + */ void idt_init() { - // Hardcoded... for (size_t i=0; i<=KERNEL_IDT_ENTRIES; i++) { // Each vector handler is 16-byte aligned, so *16 = address of that handler idt_set_entry(i, vector_0_handler + (i*16), 0); @@ -61,6 +76,15 @@ void idt_init() DEBUG("IDT initialized"); } +/* + * read_cr2 - Reads the CR2 register + * + * This function is useful because it gets the address + * that the CPU tried to access in the case of a #PF. + * + * Return: + * %val - CR2 register value + */ static inline uint64_t read_cr2(void) { uint64_t val; @@ -68,6 +92,15 @@ static inline uint64_t read_cr2(void) return val; } +/* + * page_fault_handler - Handler for #PF + * @ctx: CPU context + * + * Shows detail about a #PF, especially what instruction (RIP) + * caused it, and what address access (CR2) caused it. + * Also displays an interpretation of the thrown error code. + * Then halts the system. We could implement demand paging later. + */ static void page_fault_handler(struct cpu_status_t* ctx) { // It could be used to remap pages etc. to fix the fault, but right now what I'm more @@ -89,6 +122,13 @@ static void page_fault_handler(struct cpu_status_t* ctx) panic(ctx, "page fault"); } +/* + * gp_fault_handler - Handler for #GP + * @ctx: CPU context + * + * Shows detail about a General Protection Fault, + * and what may have caused it. Halts the system. + */ static void gp_fault_handler(struct cpu_status_t* ctx) { DEBUG("\x1b[38;5;231mGeneral Protection Fault at rip=0x%p, err=%u (%s)\x1b[0m", @@ -114,6 +154,17 @@ static void gp_fault_handler(struct cpu_status_t* ctx) panic(ctx, "gp fault"); } +/* + * interrupt_dispatch - Interrupt dispatcher + * @context: CPU context + * + * This function is where all interrupt routines go, after they passed + * through their corresponding vector handler in the IDT assembly stub. + * It catches all exceptions. + * + * Return: + * - CPU context after interrupt + */ struct cpu_status_t* interrupt_dispatch(struct cpu_status_t* context) { if (context == NULL) { diff --git a/src/idt/idt.h b/src/idt/idt.h index fab7853..de2c166 100644 --- a/src/idt/idt.h +++ b/src/idt/idt.h @@ -39,7 +39,6 @@ struct cpu_status_t { uint64_t r9; uint64_t r8; uint64_t rbp; - //uint64_t rsp; uint64_t rdi; uint64_t rsi; uint64_t rdx; diff --git a/src/io/kbd/ps2.c b/src/io/kbd/ps2.c index b28b31b..32c0d3c 100644 --- a/src/io/kbd/ps2.c +++ b/src/io/kbd/ps2.c @@ -156,6 +156,14 @@ unsigned char kbdfr_shifted[128] = 0 }; +/* + * keyboard_handler - Keyboard event handler + * + * Is called from the interrupt dispatcher. + * When a key is pressed or released, we get a scancode, and + * it is then translated to an ASCII character. + * Left Shift, Ctrl, and Alt keys are also taken into consideration. + */ void keyboard_handler() { unsigned char scancode = inb(0x60); @@ -212,6 +220,12 @@ void keyboard_handler() } } +/* + * keyboard_init - Keyboard initialization + * @layout: Desired layout + * + * Prepares the PS/2 keyboard to recieve input. + */ void keyboard_init(unsigned char layout) { // Here we might go and select PS/2, USB, or other... (once we implement multiple keyboard protocols) diff --git a/src/io/serial/serial.c b/src/io/serial/serial.c index fbf06c8..5b8e931 100644 --- a/src/io/serial/serial.c +++ b/src/io/serial/serial.c @@ -9,11 +9,25 @@ extern struct init_status init; +/* + * outb - Writes a byte to a CPU port + * @port: CPU port to write to + * @data: Byte to write + * + * Writes a single byte to the serial interface. + */ void outb(int port, unsigned char data) { __asm__ __volatile__("outb %%al, %%dx" :: "a" (data),"d" (port)); } +/* + * inb - Gets a byte in through a CPU port + * @port: The CPU port to get a byte from + * + * Return: + * - byte got from port + */ unsigned char inb(int port) { unsigned char data = 0; @@ -21,6 +35,13 @@ unsigned char inb(int port) return data; } +/* + * serial_init - Initializes serial interface + * + * Return: + * %-EIO - Input/output error + * %0 - Success + */ int serial_init() { outb(PORT + 1, 0x00); // Disable all interrupts @@ -45,12 +66,23 @@ int serial_init() return 0; } +/* + * is_transmit_empty - Check if the serial transmit register is empty + * + * Return: Non-zero if the transmit register is empty and a new + * byte can be written to the serial port, 0 otherwise. + */ static int is_transmit_empty() { return inb(PORT + 5) & 0x20; } -// Serial kernel putchar +/* + * skputc - Serial kernel putchar + * @c: character to write + * + * Writes a single character to the serial interface. + */ void skputc(char c) { // TODO: Spinlock here (serial access) @@ -58,7 +90,12 @@ void skputc(char c) outb(PORT, c); } -// Serial kernel putstring +/* + * skputs - Serial kernel puts + * @str: Message to write + * + * Writes a non-formatted string to serial output. + */ void skputs(const char* str) { unsigned int i=0; diff --git a/src/io/term/term.c b/src/io/term/term.c index cf90125..ebd979e 100644 --- a/src/io/term/term.c +++ b/src/io/term/term.c @@ -23,14 +23,22 @@ because this shitty implementation will be replaced one day by Flanterm extern struct flanterm_context* ft_ctx; extern struct init_status init; -// Overhead that could be avoided, right? (for printf) +/* + * _putchar - Writes a character to terminal + * @character: character to write + */ void _putchar(char character) { // TODO: Spinlock here (terminal access) flanterm_write(ft_ctx, &character, 1); } -// Debug-printing +/* + * kputs - Kernel puts + * @str: String to write + * + * Writes a non-formatted string to terminal + */ void kputs(const char* str) { size_t i=0; @@ -44,12 +52,26 @@ void kputs(const char* str) extern struct flanterm_context* ft_ctx; extern struct boot_context boot_ctx; +/* + * flanterm_free_wrapper - free() wrapper for Flanterm + * @ptr: pointer to free + * @size: amount of bytes to free + * + * This function exists solely because the Flanterm initialization + * function only accepts a free() function with a size parameter, + * and the default one doesn't have it. + */ void flanterm_free_wrapper(void* ptr, size_t size) { (void)size; kfree(ptr); } +/* + * term_init - Video output/terminal initialization + * + * Uses Flanterm and the framebuffer given by Limine. + */ void term_init() { uint32_t bgColor = 0x252525; diff --git a/src/kmain.c b/src/kmain.c index 4ccd31b..5a9b7bc 100644 --- a/src/kmain.c +++ b/src/kmain.c @@ -30,13 +30,25 @@ __attribute__((used, section(".limine_requests"))) volatile LIMINE_BASE_REVISION(3); -// Halt and catch fire (makes machine stall) +/* + * hcf - Halt and catch fire + * + * This function is called only in the case of an unrecoverable + * error. It halts interrupts, and stops execution. The machine + * will stay in an infinite loop state. + */ void hcf() { CLEAR_INTERRUPTS; for (;;)asm("hlt"); } -// Doing nothing (can be interrupted) +/* + * idle - Make the machine idle + * + * When there is nothing else to do, this function + * gets called. It can be interrupted, so it allows + * the scheduler, timer, and keyboard to work. + */ void idle() {SET_INTERRUPTS; for(;;)asm("hlt");} struct flanterm_context *ft_ctx; @@ -72,7 +84,14 @@ void idle_main(void* arg) extern uintptr_t kheap_start; -// This is our entry point +/* + * kmain - Kernel entry point + * + * This is where execution begins at handoff from Limine. + * The function fetches all needed information from the + * bootloader, initializes all kernel modules and structures, + * and then goes in an idle state. + */ void kmain() { CLEAR_INTERRUPTS; diff --git a/src/mem/gdt/gdt.c b/src/mem/gdt/gdt.c index fd5a318..175cab1 100644 --- a/src/mem/gdt/gdt.c +++ b/src/mem/gdt/gdt.c @@ -14,11 +14,20 @@ uint64_t gdt_entries[NUM_GDT_ENTRIES]; struct GDTR gdtr; +/* + * gdt_load - Loads Global Descriptor Table + */ static void gdt_load() { asm("lgdt %0" : : "m"(gdtr)); } +/* + * gdt_flush - Flushes the Global Descriptor Table + * + * This function loads new Segment Selectors to make + * the GDT changes take effect + */ static void gdt_flush() { // Here, 0x8 is the kernel code selector @@ -42,6 +51,15 @@ static void gdt_flush() ); } +/* + * gdt_init - Global Descriptor Table initialization + * + * This function loads a new GDT in the CPU. + * It contains a null descriptor, kernel code and data + * segments, and user code and data segments. + * However, we do not use segmentation to manage memory on + * 64-bit x86, as it's deprecated. Instead, we use paging. + */ void gdt_init() { // Null descriptor (required) diff --git a/src/mem/heap/kheap.c b/src/mem/heap/kheap.c index 5393614..9adb0d2 100644 --- a/src/mem/heap/kheap.c +++ b/src/mem/heap/kheap.c @@ -23,6 +23,15 @@ static uintptr_t end; // Kernel root table (level 4) extern uint64_t *kernel_pml4; +/* + * kheap_init - Kernel heap initialization + * + * This function physically allocates and maps enough pages + * of memory for KHEAP_SIZE, which is defined in config.h. + * + * It then creates one big heap block, which will be the + * base for a linked list. + */ void kheap_init() { kheap_start = ALIGN_UP(kernel_virt_base + KERNEL_SIZE, PAGE_SIZE); @@ -53,6 +62,18 @@ void kheap_init() DEBUG("Kernel heap initialized, head=0x%p, size=%u bytes", head, head->size); } +/* + * kmalloc - Kernel memory allocation + * @size: number of bytes to allocate + * + * Looks for a big enough free block and marks it + * as taken. Each block of memory is preceded by + * the linked list header. + * + * Return: + * - Pointer to at least bytes of usable memory + * NULL - No more memory, or no valid size given + */ void* kmalloc(size_t size) { // No size, no memory allocated! @@ -92,6 +113,14 @@ void* kmalloc(size_t size) return NULL; } +/* + * kfree - Kernel memory freeing + * @ptr: pointer to memory region to free + * + * Marks the memory block beginning at + * as free. Also merges adjacent free blocks + * to lessen fragmentation. + */ void kfree(void* ptr) { // Nothing to free @@ -113,8 +142,17 @@ void kfree(void* ptr) } } -// Should alloc enough for a stack (at least 64kb) to be used for a process. -// Should return a pointer to top of the stack (as stack grows DOWNWARDS) +/* + * kalloc_stack - Stack memory allocation + * + * Allocates a memory region of at least PROCESS_STACK_SIZE, + * to be used as a stack for a process. The pointer returned + * points to the end of the region, as the stack grows downwards. + * + * Return: + * - Pointer to a region after at least PROCESS_STACK_SIZE bytes of usable memory + * NULL - No more memory + */ void* kalloc_stack() { uint8_t* ptr = kmalloc(PROCESS_STACK_SIZE); // As it's out of kmalloc, stack is already mapped into kernel space diff --git a/src/mem/misc/utils.c b/src/mem/misc/utils.c index d43ec16..81c7b29 100644 --- a/src/mem/misc/utils.c +++ b/src/mem/misc/utils.c @@ -16,6 +16,19 @@ // We use the "restrict" keyword on pointers so that the compiler knows it can // do more optimization on them (and as it's a much used function, it's good to // be able to do that) + +/* + * memcpy - Copy memory from one place to another + * @dest: pointer to the destination region + * @src: pointer to the source region + * @n: amount of bytes to copy + * + * This function copies n bytes of memory from + * src to dest. + * + * Return: + * - Pointer to destination region + */ void* memcpy(void* restrict dest, const void* restrict src, size_t n) { uint8_t* restrict pdest = (uint8_t* restrict)dest; @@ -28,6 +41,18 @@ void* memcpy(void* restrict dest, const void* restrict src, size_t n) return dest; } +/* + * memset - Sets a memory region to given byte + * @s: pointer to memory region + * @c: byte to be written + * @n: amount of bytes to write + * + * This function writes n times the byte c + * to the memory region pointed to by s. + * + * Return: + * - Pointer to memory region + */ void* memset(void* s, int c, size_t n) { uint8_t* p = (uint8_t*)s; @@ -39,6 +64,18 @@ void* memset(void* s, int c, size_t n) return s; } +/* + * memmove - Move memory from one place to another + * @dest: pointer to the destination region + * @src: pointer to the source region + * @n: amount of bytes to move + * + * This function moves n bytes of memory from + * src to dest. + * + * Return: + * - Pointer to destination region + */ void* memmove(void *dest, const void* src, size_t n) { uint8_t* pdest = (uint8_t*)dest; @@ -56,6 +93,20 @@ void* memmove(void *dest, const void* src, size_t n) return dest; } +/* + * memcmp - Compare two memory regions + * @s1: pointer to the first region + * @s2: pointer to the second region + * @n: amount of bytes to compare + * + * This function compares n bytes of memory + * bewteen regions pointed to by s1 and s2. + * + * Return: + * %0 - if s1 and s2 are equal + * %-1 - if s1 is smaller than s2 + * %1 - if s1 is greater than s2 + */ int memcmp(const void* s1, const void* s2, size_t n) { const uint8_t* p1 = (const uint8_t*)s1; diff --git a/src/mem/paging/paging.c b/src/mem/paging/paging.c index b6bd9dc..f1b6afe 100644 --- a/src/mem/paging/paging.c +++ b/src/mem/paging/paging.c @@ -24,17 +24,41 @@ If we use 1GB huge pages: PML4 -> PDPT -> 1gb pages 4KB (regular size): PML4 -> PDPT -> PD -> PT -> 4kb pages */ +/* + * load_cr3 - Load a new value into the CR3 register + * @value: the value to load + * + * This function is used to load the physical address + * of the root page table (PML4), to switch the paging + * structures the CPU sees and uses. + */ void load_cr3(uint64_t value) { asm volatile ("mov %0, %%cr3" :: "r"(value) : "memory"); } -// To flush TLB +/* + * invlpg - Invalidates a Translation Lookaside Buffer entry + * @addr: page memory address + * + * This function is used to flush at least the TLB entrie(s) + * for the page that contains the address. + */ static inline void invlpg(void *addr) { asm volatile("invlpg (%0)" :: "r"(addr) : "memory"); } -// Allocates a 512-entry 64bit page table/directory/whatever (zeroed) +/* + * alloc_page_table - Page table allocation + * + * This function allocates enough memory for a 512-entry + * 64-bit page table, for any level (PML4/3/2). + * + * Memory allocated here is zeroed. + * + * Return: + * - Pointer to allocated page table + */ static uint64_t* alloc_page_table() { uint64_t* virt = (uint64_t*)PHYS_TO_VIRT(pmm_alloc()); @@ -49,10 +73,19 @@ static uint64_t* alloc_page_table() __attribute__((aligned(4096))) uint64_t *kernel_pml4; -// Map a page, taking virt and phys address. This will go through the paging structures -// beginning at the given root table, translate the virtual address in indexes in -// page table/directories, and then mapping the correct page table entry with the -// given physical address + flags +/* + * paging_map_page - Mapping a memory page + * @root_table: Address of the PML4 + * @virt: Virtual address + * @phys: Physical address + * @flags: Flags to set on page + * + * This function maps the physical address to the virtual + * address , using the paging structures beginning at + * . can be set according to the PTE_FLAGS enum. + * + * If a page table/directory entry is not present yet, it creates it. + */ void paging_map_page(uint64_t* root_table, uint64_t virt, uint64_t phys, uint64_t flags) { virt = PAGE_ALIGN_DOWN(virt); @@ -102,6 +135,15 @@ void paging_map_page(uint64_t* root_table, uint64_t virt, uint64_t phys, uint64_ uint64_t kernel_phys_base; uint64_t kernel_virt_base; +/* + * paging_init - Paging initialization + * @boot_ctx: Boot context structure + * + * This function initializes new paging structures, to replace + * the ones given by the bootloader. + * + * It maps the kernel, the HHDM space, and the framebuffer. + */ void paging_init(struct boot_context boot_ctx) { // We should map the kernel, GDT, IDT, stack, framebuffer. diff --git a/src/mem/paging/paging.h b/src/mem/paging/paging.h index 30cfbb5..dd55c99 100644 --- a/src/mem/paging/paging.h +++ b/src/mem/paging/paging.h @@ -41,12 +41,15 @@ extern uint64_t hhdm_off; // Page entry special bits // Bits set on a parent (directory, table) fall back to their children -#define PTE_PRESENT (1ULL << 0) -#define PTE_WRITABLE (1ULL << 1) -#define PTE_USER (1ULL << 2) -#define PTE_PWT (1ULL << 3) -#define PTE_PCD (1ULL << 4) -#define PTE_HUGE (1ULL << 7) -#define PTE_NOEXEC (1ULL << 63) +enum PTE_FLAGS +{ + PTE_PRESENT = (1ULL << 0), + PTE_WRITABLE = (1ULL << 1), + PTE_USER = (1ULL << 2), + PTE_PWT = (1ULL << 3), + PTE_PCD = (1ULL << 4), + PTE_HUGE = (1ULL << 7), + PTE_NOEXEC = (1ULL << 63) +}; #endif \ No newline at end of file diff --git a/src/mem/paging/pmm.c b/src/mem/paging/pmm.c index db3a5e9..2d8dc2c 100644 --- a/src/mem/paging/pmm.c +++ b/src/mem/paging/pmm.c @@ -24,13 +24,16 @@ First we'll have to discover the physical memory layout, and for that we can use a Limine request. */ -/* -We will look for the biggest usable physical memory region -and use this for the bitmap. The reserved memory will be ignored. -*/ - struct limine_memmap_entry* biggest_entry; +/* + * pmm_find_biggest_usable_region - Finding the biggest free memory region + * @memmap: Limine memory map + * @hhdm: Limine HHDM offset + * + * This function uses the memory map provided by the bootloader + * to find the single biggest free memory region we can use. + */ static void pmm_find_biggest_usable_region(struct limine_memmap_response* memmap, struct limine_hhdm_response* hhdm) { // Max length of a usable memory region @@ -62,6 +65,14 @@ uint64_t hhdm_off; static uintptr_t g_freelist = 0; +/* + * pmm_alloc - Allocate a physical page + * + * This function allocates a single physical page (frame) + * + * Return: + * - Address for the allocated page + */ uintptr_t pmm_alloc() { if (!g_freelist) { @@ -72,12 +83,22 @@ uintptr_t pmm_alloc() return addr; } +/* + * pmm_free - Frees a memory page + * @addr: Address to the page + */ void pmm_free(uintptr_t addr) { *(uintptr_t*) PHYS_TO_VIRT(addr) = g_freelist; g_freelist = addr; } +/* + * pmm_init_freelist - PMM freelist initialization + * + * This function marks the biggest memory region as + * free, so we can use it in pmm_alloc. + */ static void pmm_init_freelist() { // We simply call pmm_free() on each page that is marked USABLE @@ -93,6 +114,13 @@ static void pmm_init_freelist() DEBUG("%u frames in freelist, available for use (%u bytes)", page_count, page_count*PAGE_SIZE); } +/* + * pmm_init - Physical memory manager initialization + * @boot_ctx: Boot context structure + * + * This function prepares the PMM for use. + * The PMM works with a freelist. + */ void pmm_init(struct boot_context boot_ctx) { hhdm_off = boot_ctx.hhdm->offset; diff --git a/src/mem/paging/vmm.c b/src/mem/paging/vmm.c index 3e989f9..8a52b4a 100644 --- a/src/mem/paging/vmm.c +++ b/src/mem/paging/vmm.c @@ -24,6 +24,14 @@ void* vmm_pt_root = 0; // Linked list head for virtual memory objects struct vm_object* vm_objs = NULL; +/* + * Will have to be rewritten and expanded, + * to prepare for userspace. + * The platform-agnostic flags will be removed + * because as long as the kernel is x86 only, + * we don't need over complication. + * Plus I don't plan to port to other architectures +*/ uint64_t convert_x86_vm_flags(size_t flags) { diff --git a/src/sched/process.c b/src/sched/process.c index c4e879d..dc1d2df 100644 --- a/src/sched/process.c +++ b/src/sched/process.c @@ -23,13 +23,22 @@ extern uint64_t *kernel_pml4; size_t next_free_pid = 0; +/* + * process_init - Initializes process list + */ void process_init() { processes_list = NULL; current_process = NULL; } -// Only for debug +/* + * process_display_list - Debug function to display processes + * @processes_list: head of the process linked list + * + * This function prints the linked list of processes + * to the DEBUG output. + */ void process_display_list(struct process_t* processes_list) { int process_view_id = 0; @@ -42,6 +51,19 @@ void process_display_list(struct process_t* processes_list) DEBUG("NULL"); } +/* + * process_create - Create a process + * @name: name of the process + * @function: beginning of process executable code + * @arg: (optional) argument provided to process + * + * This function creates a process, gives it all + * necessary context and a stack, and adds the + * process to the linked list. + * + * Return: + * - pointer to created process + */ struct process_t* process_create(char* name, void(*function)(void*), void* arg) { CLEAR_INTERRUPTS; @@ -81,6 +103,11 @@ struct process_t* process_create(char* name, void(*function)(void*), void* arg) return proc; } +/* + * process_add - Add a process to the end of the linked list + * @processes_list: pointer to the head of the linked list + * @process: process to add at the end of the linked list + */ void process_add(struct process_t** processes_list, struct process_t* process) { if (!process) return; @@ -100,6 +127,11 @@ void process_add(struct process_t** processes_list, struct process_t* process) tmp->next = process; } +/* + * process_delete - Delete a process from the linked list + * @processes_list: pointer to head of linked list + * @process: the process to delete from the list + */ void process_delete(struct process_t** processes_list, struct process_t* process) { if (!processes_list || !*processes_list || !process) return; @@ -128,14 +160,30 @@ void process_delete(struct process_t** processes_list, struct process_t* process kfree(process); } +/* + * process_get_next - Get the next process (unused) + * @process: pointer to process + * + * Return: + * next> - process right after the one specified + */ struct process_t* process_get_next(struct process_t* process) { if (!process) return NULL; return process->next; } -// Will be used to clean up resources (if any, when we implement it) -// Just mark as DEAD then idle. Scheduler will delete process at next timer interrupt % quantum. +/* + * process_exit - Exit from a process + * + * This function is pushed to all process stacks, as a last + * return address. Once the process is done executing, it + * ends up here. + * + * Process is marked as DEAD, and then execution loops. + * Next time the scheduler sees the process, it will + * automatically delete it from the linked list. + */ void process_exit() { DEBUG("Exiting from process '%s'", current_process->name); diff --git a/src/sched/scheduler.c b/src/sched/scheduler.c index 1c9dce0..eedd3bc 100644 --- a/src/sched/scheduler.c +++ b/src/sched/scheduler.c @@ -14,12 +14,24 @@ extern struct process_t* processes_list; extern struct process_t* current_process; extern struct process_t* idle_proc; +/* + * scheduler_init - Choose the first process + */ void scheduler_init() { - // Choose first process? current_process = processes_list; } +/* + * scheduler_schedule - Main scheduling routine + * @context: CPU context of previous process + * + * Chooses the next process that we should run. + * The routine is executed every SCHEDULER_QUANTUM ticks. + * + * Return: + * - CPU context for next process + */ struct cpu_status_t* scheduler_schedule(struct cpu_status_t* context) { if (context == NULL) { diff --git a/src/string/string.c b/src/string/string.c index b21a650..3c7c7af 100644 --- a/src/string/string.c +++ b/src/string/string.c @@ -6,6 +6,16 @@ #include +/* + * strcpy - copy a NULL-terminated string + * @dest: destination buffer where the string is copied + * @src: source string to copy from + * + * Copies the string pointed to by @src (including the terminating + * NULL byte) into the buffer pointed to by @dest. + * + * Return: pointer to the destination string (@dest) + */ char* strcpy(char *dest, const char *src) { char *temp = dest; @@ -13,7 +23,21 @@ char* strcpy(char *dest, const char *src) return temp; } -// https://stackoverflow.com/questions/2488563/strcat-implementation +/* + * strcat - append a NUL-terminated string + * @dest: destination buffer containing the initial string + * @src: source string to append + * + * Appends the string pointed to by @src to the end of the string + * pointed to by @dest. The terminating NUL byte in @dest is + * overwritten and a new terminating NUL byte is added. + * + * The destination buffer must be large enough to hold the result. + * + * Taken from: https://stackoverflow.com/questions/2488563/strcat-implementation + * + * Return: pointer to the destination string (@dest) + */ char *strcat(char *dest, const char *src) { size_t i,j; @@ -26,7 +50,21 @@ char *strcat(char *dest, const char *src) return dest; } -// https://stackoverflow.com/questions/14159625/implementation-of-strncpy +/* + * strncpy - copy a string with length limit + * @dst: destination buffer + * @src: source string + * @n: maximum number of bytes to copy + * + * Copies up to @n bytes from @src to @dst. Copying stops early if a + * NULL byte is encountered in @src. If @src is shorter than @n, the + * remaining bytes in @dst are left unchanged in this implementation. + * + * Note: This differs slightly from the standard strncpy behavior, + * which pads the remaining bytes with NULL. + * + * Taken from: https://stackoverflow.com/questions/14159625/implementation-of-strncpy + */ void strncpy(char* dst, const char* src, size_t n) { size_t i = 0; diff --git a/src/time/timer.c b/src/time/timer.c index 5c591ef..bea150b 100644 --- a/src/time/timer.c +++ b/src/time/timer.c @@ -7,6 +7,7 @@ #include #include "io/serial/serial.h" #include +#include "config.h" /* For now, the timer module will be using the PIC. @@ -20,6 +21,13 @@ volatile uint64_t ticks = 0; extern struct init_status init; +/* + * pic_remap - Remap the Programmable Interrupt Controller + * + * By default, interrupts are mapped at the wrong place. + * This function remaps interrupt numbers so interrupts + * don't conflict with each other. + */ void pic_remap() { uint8_t master_mask = inb(0x21); @@ -47,6 +55,12 @@ void pic_remap() outb(0xA1, slave_mask); } +/* + * pic_enable - Enable the Programmable Interrupt Controller + * + * This function enables IRQ0 and IRQ1, which correspond to + * the timer and keyboard interrupts, respectively. + */ void pic_enable() { // Enabling IRQ0 (unmasking it) but not the others @@ -57,12 +71,15 @@ void pic_enable() } /* -Base frequency = 1.193182 MHz -1 tick per ms (divide by 1000) = roughly 1193 Hz -*/ + * pit_init - Initialization of the Programmable Interval Timer + * + * The PIT is the simplest timer we can get working on x86. + * It has a base frequency of 1.193182 MHz. + * A custom frequency can be set using TIMER_FREQUENCY macro. + */ void pit_init() { - uint32_t frequency = 1000; // 1 kHz + uint32_t frequency = TIMER_FREQUENCY; uint32_t divisor = 1193182 / frequency; // Set PIT to mode 3, channel 0 @@ -73,8 +90,12 @@ void pit_init() outb(0x40, (divisor >> 8) & 0xFF); } -// Wait n ticks -// Given that there's a tick every 1ms, wait n milliseconds +/* + * timer_wait - Wait for X ticks + * + * By default, the timer frequency is 1000Hz, meaning + * ticks are equal to milliseconds. + */ void timer_wait(uint64_t wait_ticks) { uint64_t then = ticks + wait_ticks; @@ -83,6 +104,11 @@ void timer_wait(uint64_t wait_ticks) }; } +/* + * timer_init - Initialization of the timer + * + * This function wakes the PIT. + */ void timer_init() { // Remapping the PIC, because at startup it conflicts with