diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index a744978..0b74b49 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -8,6 +8,9 @@ set(KERNEL_C_SOURCES src/main.c src/gdt.c src/interrupts.c + src/memory.c + src/pci.c + src/timer.c ) set(KERNEL_ASM_SOURCES diff --git a/kernel/include/kernel/memory.h b/kernel/include/kernel/memory.h new file mode 100644 index 0000000..ff0e4a9 --- /dev/null +++ b/kernel/include/kernel/memory.h @@ -0,0 +1,29 @@ +#ifndef METALOS_KERNEL_MEMORY_H +#define METALOS_KERNEL_MEMORY_H + +#include +#include +#include "kernel/kernel.h" + +// Memory constants +#define PAGE_SIZE 4096 + +// Physical memory manager +void pmm_init(BootInfo* boot_info); +void* pmm_alloc_page(void); +void pmm_free_page(void* page); +uint64_t pmm_get_total_memory(void); +uint64_t pmm_get_free_memory(void); + +// Simple kernel heap allocator (bump allocator for now) +void heap_init(void* start, size_t size); +void* kmalloc(size_t size); +void* kcalloc(size_t num, size_t size); +void kfree(void* ptr); + +// Memory utility functions +void* memset(void* dest, int val, size_t count); +void* memcpy(void* dest, const void* src, size_t count); +int memcmp(const void* s1, const void* s2, size_t count); + +#endif // METALOS_KERNEL_MEMORY_H diff --git a/kernel/include/kernel/pci.h b/kernel/include/kernel/pci.h new file mode 100644 index 0000000..b3b0449 --- /dev/null +++ b/kernel/include/kernel/pci.h @@ -0,0 +1,31 @@ +#ifndef METALOS_KERNEL_PCI_H +#define METALOS_KERNEL_PCI_H + +#include + +// PCI Configuration Space Registers +#define PCI_CONFIG_ADDRESS 0xCF8 +#define PCI_CONFIG_DATA 0xCFC + +// PCI Device Structure +typedef struct { + uint8_t bus; + uint8_t device; + uint8_t function; + uint16_t vendor_id; + uint16_t device_id; + uint8_t class_code; + uint8_t subclass; + uint8_t prog_if; + uint8_t revision_id; + uint32_t bar[6]; // Base Address Registers +} pci_device_t; + +// PCI Functions +void pci_init(void); +uint32_t pci_read_config(uint8_t bus, uint8_t device, uint8_t function, uint8_t offset); +void pci_write_config(uint8_t bus, uint8_t device, uint8_t function, uint8_t offset, uint32_t value); +pci_device_t* pci_find_device(uint16_t vendor_id, uint16_t device_id); +void pci_enable_bus_mastering(pci_device_t* dev); + +#endif // METALOS_KERNEL_PCI_H diff --git a/kernel/include/kernel/timer.h b/kernel/include/kernel/timer.h new file mode 100644 index 0000000..3d93428 --- /dev/null +++ b/kernel/include/kernel/timer.h @@ -0,0 +1,17 @@ +#ifndef METALOS_KERNEL_TIMER_H +#define METALOS_KERNEL_TIMER_H + +#include + +// Timer frequency (Hz) +#define TIMER_FREQUENCY 1000 // 1ms per tick + +// Timer functions +void timer_init(uint32_t frequency); +uint64_t timer_get_ticks(void); +void timer_wait(uint32_t ticks); + +// Timer interrupt handler (called from ISR) +void timer_handler(void); + +#endif // METALOS_KERNEL_TIMER_H diff --git a/kernel/src/interrupts.c b/kernel/src/interrupts.c index 52eb3a8..76b0801 100644 --- a/kernel/src/interrupts.c +++ b/kernel/src/interrupts.c @@ -6,6 +6,7 @@ */ #include "kernel/interrupts.h" +#include "kernel/timer.h" // IDT entries (256 interrupts in x86_64) static idt_entry_t idt[256]; @@ -143,10 +144,13 @@ void idt_init(void) { // Generic interrupt handler void interrupt_handler(registers_t* regs) { - // Handle interrupt based on interrupt number - (void)regs; // Suppress unused warning for now + // Handle specific interrupts + if (regs->int_no == 32) { + // Timer interrupt (IRQ0) + timer_handler(); + } - // TODO: Dispatch to specific handlers based on regs->int_no + // TODO: Handle other interrupts (keyboard, etc.) // Send EOI (End of Interrupt) to PIC if this was an IRQ if (regs->int_no >= 32 && regs->int_no < 48) { diff --git a/kernel/src/main.c b/kernel/src/main.c index 8e2664f..060b5d9 100644 --- a/kernel/src/main.c +++ b/kernel/src/main.c @@ -9,6 +9,9 @@ #include "kernel/kernel.h" #include "kernel/gdt.h" #include "kernel/interrupts.h" +#include "kernel/memory.h" +#include "kernel/pci.h" +#include "kernel/timer.h" /* * Kernel main entry point @@ -18,15 +21,27 @@ * Just set up hardware and jump to the QT6 app. */ void kernel_main(BootInfo* boot_info) { - // Suppress unused parameter warning - (void)boot_info; - // Initialize GDT (Global Descriptor Table) gdt_init(); // Initialize IDT (Interrupt Descriptor Table) idt_init(); + // Initialize physical memory manager + pmm_init(boot_info); + + // Initialize kernel heap (allocate 1MB for kernel heap) + void* heap_mem = pmm_alloc_page(); + if (heap_mem) { + heap_init(heap_mem, 256 * PAGE_SIZE); // 1MB heap + } + + // Initialize timer (1000 Hz = 1ms per tick) + timer_init(TIMER_FREQUENCY); + + // Initialize PCI bus + pci_init(); + // TODO: Set up minimal page tables (identity mapped or simple offset) // TODO: Simple memory allocator (bump allocator is fine) diff --git a/kernel/src/memory.c b/kernel/src/memory.c new file mode 100644 index 0000000..5c9dd77 --- /dev/null +++ b/kernel/src/memory.c @@ -0,0 +1,169 @@ +/* + * MetalOS Kernel - Memory Management + * + * Simple physical memory manager and heap allocator + * Minimal implementation for single-app OS + */ + +#include "kernel/memory.h" + +// Physical memory bitmap +#define PAGE_SIZE 4096 +#define BITMAP_SIZE 32768 // Supports up to 128MB with 4KB pages + +static uint8_t page_bitmap[BITMAP_SIZE]; +static uint64_t total_pages = 0; +static uint64_t used_pages = 0; + +// Heap for kernel allocations +static uint8_t* heap_start = NULL; +static uint8_t* heap_current = NULL; +static uint8_t* heap_end = NULL; + +// Initialize physical memory manager +void pmm_init(BootInfo* boot_info) { + (void)boot_info; // TODO: Parse UEFI memory map + + // For now, assume 128MB of usable memory starting at 16MB + total_pages = (128 * 1024 * 1024) / PAGE_SIZE; + + // Clear bitmap + for (uint64_t i = 0; i < BITMAP_SIZE; i++) { + page_bitmap[i] = 0; + } + + used_pages = 0; +} + +// Allocate a physical page +void* pmm_alloc_page(void) { + // Find first free page in bitmap + for (uint64_t i = 0; i < total_pages; i++) { + uint64_t byte = i / 8; + uint64_t bit = i % 8; + + if (!(page_bitmap[byte] & (1 << bit))) { + // Mark as used + page_bitmap[byte] |= (1 << bit); + used_pages++; + + // Return physical address + // Assuming memory starts at 16MB + return (void*)((0x01000000UL) + (i * PAGE_SIZE)); + } + } + + // Out of memory + return NULL; +} + +// Free a physical page +void pmm_free_page(void* page) { + uint64_t addr = (uint64_t)page; + + // Calculate page index + uint64_t page_idx = (addr - 0x01000000UL) / PAGE_SIZE; + + if (page_idx >= total_pages) { + return; // Invalid address + } + + uint64_t byte = page_idx / 8; + uint64_t bit = page_idx % 8; + + // Mark as free + page_bitmap[byte] &= ~(1 << bit); + used_pages--; +} + +// Get total memory +uint64_t pmm_get_total_memory(void) { + return total_pages * PAGE_SIZE; +} + +// Get free memory +uint64_t pmm_get_free_memory(void) { + return (total_pages - used_pages) * PAGE_SIZE; +} + +// Initialize heap allocator +void heap_init(void* start, size_t size) { + heap_start = (uint8_t*)start; + heap_current = heap_start; + heap_end = heap_start + size; +} + +// Simple bump allocator (no free support in this version) +void* kmalloc(size_t size) { + if (!heap_start) { + return NULL; + } + + // Align to 16 bytes + size = (size + 15) & ~15; + + if (heap_current + size > heap_end) { + return NULL; // Out of heap memory + } + + void* ptr = heap_current; + heap_current += size; + + return ptr; +} + +// Allocate and zero memory +void* kcalloc(size_t num, size_t size) { + size_t total = num * size; + void* ptr = kmalloc(total); + + if (ptr) { + memset(ptr, 0, total); + } + + return ptr; +} + +// Free memory (not implemented in bump allocator) +void kfree(void* ptr) { + (void)ptr; + // TODO: Implement proper free with a real allocator + // For now, bump allocator doesn't support freeing +} + +// Memory utility functions + +void* memset(void* dest, int val, size_t count) { + uint8_t* d = (uint8_t*)dest; + uint8_t v = (uint8_t)val; + + for (size_t i = 0; i < count; i++) { + d[i] = v; + } + + return dest; +} + +void* memcpy(void* dest, const void* src, size_t count) { + uint8_t* d = (uint8_t*)dest; + const uint8_t* s = (const uint8_t*)src; + + for (size_t i = 0; i < count; i++) { + d[i] = s[i]; + } + + return dest; +} + +int memcmp(const void* s1, const void* s2, size_t count) { + const uint8_t* a = (const uint8_t*)s1; + const uint8_t* b = (const uint8_t*)s2; + + for (size_t i = 0; i < count; i++) { + if (a[i] != b[i]) { + return a[i] - b[i]; + } + } + + return 0; +} diff --git a/kernel/src/pci.c b/kernel/src/pci.c new file mode 100644 index 0000000..410bd93 --- /dev/null +++ b/kernel/src/pci.c @@ -0,0 +1,144 @@ +/* + * MetalOS Kernel - PCI Bus Support + * + * Minimal PCI enumeration and configuration + * Only what's needed to find and initialize the GPU + */ + +#include "kernel/pci.h" +#include "kernel/memory.h" + +// I/O port access functions +static inline void outl(uint16_t port, uint32_t value) { + __asm__ volatile("outl %0, %1" : : "a"(value), "Nd"(port)); +} + +static inline uint32_t inl(uint16_t port) { + uint32_t value; + __asm__ volatile("inl %1, %0" : "=a"(value) : "Nd"(port)); + return value; +} + +// Maximum devices we'll track +#define MAX_PCI_DEVICES 256 +static pci_device_t pci_devices[MAX_PCI_DEVICES]; +static uint32_t pci_device_count = 0; + +// Read from PCI configuration space +uint32_t pci_read_config(uint8_t bus, uint8_t device, uint8_t function, uint8_t offset) { + uint32_t address = (uint32_t)( + ((uint32_t)bus << 16) | + ((uint32_t)device << 11) | + ((uint32_t)function << 8) | + (offset & 0xFC) | + 0x80000000 + ); + + outl(PCI_CONFIG_ADDRESS, address); + return inl(PCI_CONFIG_DATA); +} + +// Write to PCI configuration space +void pci_write_config(uint8_t bus, uint8_t device, uint8_t function, uint8_t offset, uint32_t value) { + uint32_t address = (uint32_t)( + ((uint32_t)bus << 16) | + ((uint32_t)device << 11) | + ((uint32_t)function << 8) | + (offset & 0xFC) | + 0x80000000 + ); + + outl(PCI_CONFIG_ADDRESS, address); + outl(PCI_CONFIG_DATA, value); +} + +// Probe a PCI device +static void pci_probe_device(uint8_t bus, uint8_t device, uint8_t function) { + uint32_t vendor_device = pci_read_config(bus, device, function, 0x00); + uint16_t vendor_id = vendor_device & 0xFFFF; + uint16_t device_id = (vendor_device >> 16) & 0xFFFF; + + // Check if device exists + if (vendor_id == 0xFFFF) { + return; + } + + // Read class code + uint32_t class_rev = pci_read_config(bus, device, function, 0x08); + uint8_t class_code = (class_rev >> 24) & 0xFF; + uint8_t subclass = (class_rev >> 16) & 0xFF; + uint8_t prog_if = (class_rev >> 8) & 0xFF; + uint8_t revision_id = class_rev & 0xFF; + + // Store device info + if (pci_device_count < MAX_PCI_DEVICES) { + pci_device_t* dev = &pci_devices[pci_device_count++]; + dev->bus = bus; + dev->device = device; + dev->function = function; + dev->vendor_id = vendor_id; + dev->device_id = device_id; + dev->class_code = class_code; + dev->subclass = subclass; + dev->prog_if = prog_if; + dev->revision_id = revision_id; + + // Read BARs (Base Address Registers) + for (int i = 0; i < 6; i++) { + dev->bar[i] = pci_read_config(bus, device, function, 0x10 + (i * 4)); + } + } +} + +// Initialize PCI subsystem +void pci_init(void) { + // Scan all buses, devices, and functions + for (uint16_t bus = 0; bus < 256; bus++) { + for (uint8_t device = 0; device < 32; device++) { + // Check if device exists (function 0) + uint32_t vendor_device = pci_read_config(bus, device, 0, 0x00); + if ((vendor_device & 0xFFFF) == 0xFFFF) { + continue; // Device doesn't exist + } + + pci_probe_device(bus, device, 0); + + // Check if multi-function device + uint32_t header_type = pci_read_config(bus, device, 0, 0x0C); + if (header_type & 0x00800000) { + // Multi-function device, scan other functions + for (uint8_t function = 1; function < 8; function++) { + vendor_device = pci_read_config(bus, device, function, 0x00); + if ((vendor_device & 0xFFFF) != 0xFFFF) { + pci_probe_device(bus, device, function); + } + } + } + } + } +} + +// Find a PCI device by vendor and device ID +pci_device_t* pci_find_device(uint16_t vendor_id, uint16_t device_id) { + for (uint32_t i = 0; i < pci_device_count; i++) { + if (pci_devices[i].vendor_id == vendor_id && + pci_devices[i].device_id == device_id) { + return &pci_devices[i]; + } + } + return NULL; +} + +// Enable bus mastering for a device +void pci_enable_bus_mastering(pci_device_t* dev) { + if (!dev) return; + + // Read command register (offset 0x04) + uint32_t command = pci_read_config(dev->bus, dev->device, dev->function, 0x04); + + // Set bus master bit (bit 2) + command |= 0x04; + + // Write back + pci_write_config(dev->bus, dev->device, dev->function, 0x04, command); +} diff --git a/kernel/src/timer.c b/kernel/src/timer.c new file mode 100644 index 0000000..3174f4d --- /dev/null +++ b/kernel/src/timer.c @@ -0,0 +1,61 @@ +/* + * MetalOS Kernel - Timer Support + * + * Simple PIT (Programmable Interval Timer) support + * Used for scheduling and timing + */ + +#include "kernel/timer.h" + +// PIT I/O ports +#define PIT_CHANNEL0 0x40 +#define PIT_COMMAND 0x43 + +// PIT constants +#define PIT_BASE_FREQUENCY 1193182 // Hz + +// Tick counter +static volatile uint64_t timer_ticks = 0; + +// I/O port access +static inline void outb(uint16_t port, uint8_t value) { + __asm__ volatile("outb %0, %1" : : "a"(value), "Nd"(port)); +} + +// Initialize timer +void timer_init(uint32_t frequency) { + // Calculate divisor + uint32_t divisor = PIT_BASE_FREQUENCY / frequency; + + // Send command byte: channel 0, rate generator, lo/hi byte + outb(PIT_COMMAND, 0x36); + + // Send divisor + outb(PIT_CHANNEL0, (uint8_t)(divisor & 0xFF)); + outb(PIT_CHANNEL0, (uint8_t)((divisor >> 8) & 0xFF)); + + // Enable timer interrupt (IRQ0) + // Unmask IRQ0 in PIC + uint8_t mask; + __asm__ volatile("inb $0x21, %0" : "=a"(mask)); + mask &= ~0x01; // Clear bit 0 (IRQ0) + outb(0x21, mask); +} + +// Get current tick count +uint64_t timer_get_ticks(void) { + return timer_ticks; +} + +// Wait for specified number of ticks +void timer_wait(uint32_t ticks) { + uint64_t target = timer_ticks + ticks; + while (timer_ticks < target) { + __asm__ volatile("hlt"); + } +} + +// Timer interrupt handler +void timer_handler(void) { + timer_ticks++; +}