]> Projects (at) Tadryanom (dot) Me - AdrOS.git/commitdiff
Feat: Add Kernel Heap (kmalloc) and Preemptive Timer (PIT)
authortadryanom_bot <[email protected]>
Tue, 3 Feb 2026 04:00:08 +0000 (04:00 +0000)
committertadryanom_bot <[email protected]>
Tue, 3 Feb 2026 04:00:08 +0000 (04:00 +0000)
include/heap.h [new file with mode: 0644]
include/timer.h [new file with mode: 0644]
src/drivers/timer.c [new file with mode: 0644]
src/kernel/main.c
src/mm/heap.c [new file with mode: 0644]

diff --git a/include/heap.h b/include/heap.h
new file mode 100644 (file)
index 0000000..1d483d5
--- /dev/null
@@ -0,0 +1,11 @@
+#ifndef HEAP_H
+#define HEAP_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+void kheap_init(void);
+void* kmalloc(size_t size);
+void kfree(void* ptr);
+
+#endif
diff --git a/include/timer.h b/include/timer.h
new file mode 100644 (file)
index 0000000..315e625
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef TIMER_H
+#define TIMER_H
+
+#include <stdint.h>
+
+void timer_init(uint32_t frequency);
+
+#endif
diff --git a/src/drivers/timer.c b/src/drivers/timer.c
new file mode 100644 (file)
index 0000000..5dc9737
--- /dev/null
@@ -0,0 +1,45 @@
+#include "timer.h"
+#include "idt.h"
+#include "io.h"
+#include "uart_console.h"
+#include "process.h" // For schedule()
+
+static uint32_t tick = 0;
+
+void timer_callback(struct registers* regs) {
+    (void)regs;
+    tick++;
+    
+    // Every 100 ticks (approx 1 sec), print a dot just to show life
+    if (tick % 100 == 0) {
+        // uart_print("."); // Commented out to not pollute shell
+    }
+    
+    // PREEMPTION!
+    // Force a task switch
+    schedule();
+}
+
+void timer_init(uint32_t frequency) {
+    uart_print("[TIMER] Initializing PIT...\n");
+    
+    // Register Timer Callback (IRQ 0 -> Int 32)
+    register_interrupt_handler(32, timer_callback);
+    
+    // The value we send to the PIT divisor is the value to divide it's input clock
+    // (1193180 Hz) by, to get our required frequency.
+    uint32_t divisor = 1193180 / frequency;
+    
+    // Send the command byte.
+    // 0x36 = 0011 0110
+    // Channel 0 | Access lo/hi byte | Mode 3 (Square Wave) | 16-bit binary
+    outb(0x43, 0x36);
+    
+    // Split divisor into low and high bytes
+    uint8_t l = (uint8_t)(divisor & 0xFF);
+    uint8_t h = (uint8_t)( (divisor>>8) & 0xFF );
+    
+    // Send the frequency divisor.
+    outb(0x40, l);
+    outb(0x40, h);
+}
index e430184b7c0aa1418a50c9ed23d9a5427643ae36..78e5960ed65f3d53ab39a537cc832ec6c06a4124 100644 (file)
@@ -8,6 +8,8 @@
 #include "process.h"
 #include "keyboard.h"
 #include "shell.h"
+#include "heap.h"
+#include "timer.h"
 
 /* Check if the compiler thinks we are targeting the wrong operating system. */
 #if defined(__linux__)
@@ -47,17 +49,23 @@ void kernel_main(unsigned long magic, unsigned long addr) {
 #if defined(__i386__)
     vmm_init(); 
     
-    // 4. Initialize Interrupts (x86)
+    // 4. Initialize Kernel Heap
+    kheap_init();
+    
+    // 5. Initialize Interrupts (x86)
     uart_print("[AdrOS] Initializing IDT...\n");
     idt_init();
     
-    // 5. Initialize Drivers
+    // 6. Initialize Drivers
     keyboard_init();
     
-    // 6. Initialize Multitasking (Optional for Shell, but good to have)
+    // 7. Initialize Multitasking
     uart_print("[AdrOS] Initializing Scheduler...\n");
     process_init();
     
+    // 8. Start Timer (Preemption!) - 50Hz
+    timer_init(50);
+    
     // Start Shell as the main interaction loop
     shell_init();
     
@@ -68,9 +76,8 @@ void kernel_main(unsigned long magic, unsigned long addr) {
     uart_print("Welcome to AdrOS (x86/ARM/RISC-V/MIPS)!\n");
 
     // Infinite loop acting as Idle Task
-    // Shell is interrupt driven, so we just idle here.
     for(;;) {
-        // We can execute background tasks here if needed
+        // HLT puts CPU to sleep until next interrupt (Timer or Keyboard)
         #if defined(__i386__) || defined(__x86_64__)
         __asm__("hlt");
         #elif defined(__aarch64__)
@@ -78,8 +85,5 @@ void kernel_main(unsigned long magic, unsigned long addr) {
         #elif defined(__riscv)
         __asm__("wfi");
         #endif
-        
-        // If we had preemptive scheduling, the timer would wake us up.
-        // For cooperative, we assume shell is ISR based so HLT is fine.
     }
 }
diff --git a/src/mm/heap.c b/src/mm/heap.c
new file mode 100644 (file)
index 0000000..824ead9
--- /dev/null
@@ -0,0 +1,106 @@
+#include "heap.h"
+#include "pmm.h"
+#include "vmm.h"
+#include "uart_console.h"
+
+// Heap starts at 3GB + 256MB (Arbitrary safe high location)
+#define KHEAP_START 0xD0000000
+#define KHEAP_INITIAL_SIZE (10 * 1024 * 1024) // 10MB
+#define PAGE_SIZE 4096
+
+typedef struct heap_header {
+    size_t size;            // Size of the data block (excluding header)
+    uint8_t is_free;        // 1 if free, 0 if used
+    struct heap_header* next; // Next block in the list
+} heap_header_t;
+
+static heap_header_t* head = NULL;
+
+void kheap_init(void) {
+    uart_print("[HEAP] Initializing Kernel Heap...\n");
+    
+    // 1. Map pages for the heap
+    // We need to map Virtual Addresses [KHEAP_START] to [KHEAP_START + SIZE]
+    // to physical frames.
+    
+    uint32_t pages_needed = KHEAP_INITIAL_SIZE / PAGE_SIZE;
+    if (KHEAP_INITIAL_SIZE % PAGE_SIZE != 0) pages_needed++;
+    
+    uint32_t virt_addr = KHEAP_START;
+    
+    for (uint32_t i = 0; i < pages_needed; i++) {
+        void* phys_frame = pmm_alloc_page();
+        if (!phys_frame) {
+            uart_print("[HEAP] OOM during init!\n");
+            return;
+        }
+        
+        // Map it!
+        // Note: vmm_map_page expects 64-bit phys but we give it 32-bit cast
+        vmm_map_page((uint64_t)(uintptr_t)phys_frame, (uint64_t)virt_addr, 
+                     VMM_FLAG_PRESENT | VMM_FLAG_RW);
+                     
+        virt_addr += PAGE_SIZE;
+    }
+    
+    // 2. Create the initial huge free block
+    head = (heap_header_t*)KHEAP_START;
+    head->size = KHEAP_INITIAL_SIZE - sizeof(heap_header_t);
+    head->is_free = 1;
+    head->next = NULL;
+    
+    uart_print("[HEAP] Initialized 10MB at 0xD0000000.\n");
+}
+
+void* kmalloc(size_t size) {
+    if (size == 0) return NULL;
+    
+    // Align size to 8 bytes for performance/safety
+    size_t aligned_size = (size + 7) & ~7;
+    
+    heap_header_t* current = head;
+    
+    while (current) {
+        if (current->is_free && current->size >= aligned_size) {
+            // Found a block!
+            
+            // Can we split it? 
+            // Only split if remaining space is big enough for a header + minimal data
+            if (current->size > aligned_size + sizeof(heap_header_t) + 8) {
+                heap_header_t* new_block = (heap_header_t*)((uint8_t*)current + sizeof(heap_header_t) + aligned_size);
+                
+                new_block->size = current->size - aligned_size - sizeof(heap_header_t);
+                new_block->is_free = 1;
+                new_block->next = current->next;
+                
+                current->size = aligned_size;
+                current->next = new_block;
+            }
+            
+            current->is_free = 0;
+            return (void*)((uint8_t*)current + sizeof(heap_header_t));
+        }
+        current = current->next;
+    }
+    
+    uart_print("[HEAP] OOM: No block large enough!\n");
+    return NULL;
+}
+
+void kfree(void* ptr) {
+    if (!ptr) return;
+    
+    // Get header
+    heap_header_t* header = (heap_header_t*)((uint8_t*)ptr - sizeof(heap_header_t));
+    header->is_free = 1;
+    
+    // Merge with next block if free (Coalescing)
+    if (header->next && header->next->is_free) {
+        header->size += sizeof(heap_header_t) + header->next->size;
+        header->next = header->next->next;
+    }
+    
+    // TODO: We should ideally merge with PREVIOUS block too, 
+    // but a singly linked list makes that O(N). 
+    // A doubly linked list is better for production heaps.
+}