From cc8351bf8dbe84dfb139df8f84027a345a024dc8 Mon Sep 17 00:00:00 2001 From: kaguya Date: Wed, 15 Apr 2026 13:33:18 -0400 Subject: [PATCH] feat: implement PCI subsystem and integrate uACPI support See previous commit Signed-off-by: kaguya --- CMakeLists.txt | 9 ++ src/arch/x86_64/i8259.c | 1 - src/arch/x86_64/irq.c | 3 +- src/arch/x86_64/pci.c | 137 +++++++++++++++++ src/arch/x86_64/pci.h | 39 +++++ src/arch/x86_64/pit.c | 69 +++++++++ src/arch/x86_64/pit.h | 14 ++ src/main.c | 128 ++++++++++++++-- src/mm/vmm.c | 42 ++++- src/stdio.c | 332 ++++++++++++++++++++++++++++++++++------ src/stdio.h | 3 + src/string.c | 13 ++ src/string.h | 3 +- src/time/time.c | 50 ++++++ src/time/time.h | 10 ++ 15 files changed, 785 insertions(+), 68 deletions(-) create mode 100644 src/arch/x86_64/pci.c create mode 100644 src/arch/x86_64/pci.h create mode 100644 src/arch/x86_64/pit.c create mode 100644 src/arch/x86_64/pit.h create mode 100644 src/time/time.c create mode 100644 src/time/time.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 70e00d7..6439ebf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,6 +11,8 @@ set(KIRKOS_VERSION_STRING "${VERSION}.${PATCHLEVEL}.${SUBLEVEL}${EXTRAVERSION}" ) +include(${CMAKE_SOURCE_DIR}/libs/uacpi/uacpi.cmake) + message(STATUS "Building KirkOS version: ${KIRKOS_VERSION_STRING}") set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) @@ -31,7 +33,14 @@ set(EXT2_ROOT "${CMAKE_SOURCE_DIR}/ext2_root") add_executable(KirkOS ${KIRKOS_SOURCES}) +target_sources(KirkOS PRIVATE + ${UACPI_SOURCES} + ${CMAKE_SOURCE_DIR}/libs/uacpi/source/kernel_api.c +) +target_include_directories(KirkOS PRIVATE + ${UACPI_INCLUDES} +) target_compile_definitions(KirkOS PRIVATE KIRKOS_VERSION="${KIRKOS_VERSION_STRING}" diff --git a/src/arch/x86_64/i8259.c b/src/arch/x86_64/i8259.c index a5a7d9b..f66967e 100644 --- a/src/arch/x86_64/i8259.c +++ b/src/arch/x86_64/i8259.c @@ -1,7 +1,6 @@ #include "pic.h" #include "io.h" #include -#include #define PIC1_COMMAND_PORT 0x20 #define PIC1_DATA_PORT 0x21 diff --git a/src/arch/x86_64/irq.c b/src/arch/x86_64/irq.c index 6416eb6..cb42a08 100644 --- a/src/arch/x86_64/irq.c +++ b/src/arch/x86_64/irq.c @@ -51,12 +51,13 @@ void x86_64_IRQ_Initialize(void) log_info(MODULE, "Found %s PIC.", g_Driver->Name); g_Driver->Initialize(PIC_REMAP_OFFSET, PIC_REMAP_OFFSET + 8, false); + // register ISR handlers for each of the 16 irq lines for (int i = 0; i < 16; i++) x86_64_ISR_RegisterHandler(PIC_REMAP_OFFSET + i, x86_64_IRQ_Handler); - x86_64_EnableInterrupts(); + g_Driver->Unmask(0); } void x86_64_IRQ_RegisterHandler(int irq, IRQHandler handler) diff --git a/src/arch/x86_64/pci.c b/src/arch/x86_64/pci.c new file mode 100644 index 0000000..238e6cf --- /dev/null +++ b/src/arch/x86_64/pci.c @@ -0,0 +1,137 @@ +#include "pci.h" +#include "arch/x86_64/io.h" +#include + +#define PCI_CFG_ADDR 0x0CF8 +#define PCI_CFG_DATA 0x0CFC + +static uint32_t pci_pack_addr(pci_address_t addr, uint16_t offset) +{ + return (1u << 31) | + ((uint32_t)addr.bus << 16) | + ((uint32_t)addr.device << 11) | + ((uint32_t)addr.function << 8) | + (offset & 0xFCu); +} + +uint8_t pci_read8(pci_address_t addr, uint16_t offset) +{ + x86_64_outl(PCI_CFG_ADDR, pci_pack_addr(addr, offset)); + return (uint8_t)(x86_64_inl(PCI_CFG_DATA) >> ((offset & 3) * 8)); +} + +uint16_t pci_read16(pci_address_t addr, uint16_t offset) +{ + x86_64_outl(PCI_CFG_ADDR, pci_pack_addr(addr, offset)); + return (uint16_t)(x86_64_inl(PCI_CFG_DATA) >> ((offset & 2) * 8)); +} + +uint32_t pci_read32(pci_address_t addr, uint16_t offset) +{ + x86_64_outl(PCI_CFG_ADDR, pci_pack_addr(addr, offset)); + return x86_64_inl(PCI_CFG_DATA); +} + +void pci_write8(pci_address_t addr, uint16_t offset, uint8_t val) +{ + uint32_t tmp = pci_read32(addr, offset & ~3u); + uint8_t shift = (offset & 3) * 8; + tmp = (tmp & ~(0xFFu << shift)) | ((uint32_t)val << shift); + x86_64_outl(PCI_CFG_ADDR, pci_pack_addr(addr, offset)); + x86_64_outl(PCI_CFG_DATA, tmp); +} + +void pci_write16(pci_address_t addr, uint16_t offset, uint16_t val) +{ + uint32_t tmp = pci_read32(addr, offset & ~3u); + uint8_t shift = (offset & 2) * 8; + tmp = (tmp & ~(0xFFFFu << shift)) | ((uint32_t)val << shift); + x86_64_outl(PCI_CFG_ADDR, pci_pack_addr(addr, offset)); + x86_64_outl(PCI_CFG_DATA, tmp); +} + +void pci_write32(pci_address_t addr, uint16_t offset, uint32_t val) +{ + x86_64_outl(PCI_CFG_ADDR, pci_pack_addr(addr, offset)); + x86_64_outl(PCI_CFG_DATA, val); +} + +static void pci_read_bars(pci_device_t* dev) +{ + for (int i = 0; i < 6; i++) { + uint16_t off = 0x10 + i * 4; + dev->bar[i] = pci_read32(dev->addr, off); + // If it's a 64-bit BAR we skip the next one (simple version) + if ((dev->bar[i] & 0x7) == 0x4) + i++; + } +} + +void pci_init(void) +{ + printf("[PCI] Scanning bus 0...\n"); + pci_print_all(); +} + +void pci_print_all(void) +{ + for (uint8_t bus = 0; bus < 1; bus++) { // start with bus 0 only + for (uint8_t dev = 0; dev < 32; dev++) { + for (uint8_t func = 0; func < 8; func++) { + pci_address_t addr = {bus, dev, func}; + + uint16_t vendor = pci_read16(addr, 0x00); + if (vendor == 0xFFFF) { + if (func == 0) break; // no device + continue; + } + + uint16_t device_id = pci_read16(addr, 0x02); + uint16_t class_sub = pci_read16(addr, 0x0A); + uint8_t prog_if = pci_read8 (addr, 0x09); + uint8_t revision = pci_read8 (addr, 0x08); + + pci_device_t d = { + .addr = addr, + .vendor_id = vendor, + .device_id = device_id, + .class_code = class_sub, + .prog_if = prog_if, + .revision = revision, + }; + pci_read_bars(&d); + + printf("[PCI] %02x:%02x.%x %04x:%04x class %04x rev %02x\n", + bus, dev, func, vendor, device_id, class_sub, revision); + + if (func == 0 && (pci_read8(addr, 0x0E) & 0x80) == 0) + break; // not multifunction + } + } + } +} + +bool pci_find_hda(pci_device_t* out_dev) +{ + // Same scan as above, but stop at first HDA device + for (uint8_t bus = 0; bus < 1; bus++) { + for (uint8_t dev = 0; dev < 32; dev++) { + for (uint8_t func = 0; func < 8; func++) { + pci_address_t addr = {bus, dev, func}; + uint16_t vendor = pci_read16(addr, 0x00); + if (vendor == 0xFFFF) continue; + + uint16_t class_sub = pci_read16(addr, 0x0A); + if (class_sub == (PCI_CLASS_AUDIO << 8) | PCI_SUBCLASS_HDA) { + out_dev->addr = addr; + out_dev->vendor_id = vendor; + out_dev->device_id = pci_read16(addr, 0x02); + out_dev->class_code = class_sub; + pci_read_bars(out_dev); + return true; + } + } + } + } + return false; +} \ No newline at end of file diff --git a/src/arch/x86_64/pci.h b/src/arch/x86_64/pci.h new file mode 100644 index 0000000..2932279 --- /dev/null +++ b/src/arch/x86_64/pci.h @@ -0,0 +1,39 @@ +#pragma once +#include +#include + +typedef struct { + uint8_t bus; + uint8_t device; + uint8_t function; +} pci_address_t; + +typedef struct { + pci_address_t addr; + uint16_t vendor_id; + uint16_t device_id; + uint16_t class_code; // (class << 8) | subclass + uint8_t prog_if; + uint8_t revision; + uint32_t bar[6]; + bool is_multifunction; +} pci_device_t; + +#define PCI_CLASS_AUDIO 0x04 +#define PCI_SUBCLASS_HDA 0x03 + +// Read/write config space (re-uses your existing uACPI PCI code) +uint8_t pci_read8 (pci_address_t addr, uint16_t offset); +uint16_t pci_read16(pci_address_t addr, uint16_t offset); +uint32_t pci_read32(pci_address_t addr, uint16_t offset); + +void pci_write8 (pci_address_t addr, uint16_t offset, uint8_t val); +void pci_write16(pci_address_t addr, uint16_t offset, uint16_t val); +void pci_write32(pci_address_t addr, uint16_t offset, uint32_t val); + +// Scan and print all PCI devices +void pci_init(void); +void pci_print_all(void); + +// Find first HDA controller (returns true if found) +bool pci_find_hda(pci_device_t* out_dev); \ No newline at end of file diff --git a/src/arch/x86_64/pit.c b/src/arch/x86_64/pit.c new file mode 100644 index 0000000..75dda6e --- /dev/null +++ b/src/arch/x86_64/pit.c @@ -0,0 +1,69 @@ +#include "pit.h" +#include "irq.h" +#include "io.h" +#include +#include "stdio.h" +#include "e9.h" +#include "limine.h" + +__attribute__((used, section(".limine_requests"))) +volatile struct limine_date_at_boot_request boot_request = { + .id = LIMINE_DATE_AT_BOOT_REQUEST_ID, + .revision = 6, +}; + +#define PIT_BASE_FREQUENCY 1193182 + +#define PIT_COMMAND_PORT 0x43 +#define PIT_CHANNEL0_PORT 0x40 + +volatile uint64_t g_Ticks = 0; +volatile uint64_t ticks = 0; +uint64_t g_Unixseconds = 0; + +/* ========================= */ +/* IRQ0 Handler (Timer Tick) */ +/* ========================= */ +void PIT_IRQ_Handler(Registers* regs) +{ + (void)regs; + g_Ticks++; + ticks++; + if (ticks >= 1000) { + g_Unixseconds++; + ticks = 0; + } + + + // You can add scheduler / time logic here later +} + +/* ========================= */ +/* Initialize PIT */ +/* ========================= */ +void x86_64_PIT_Initialize(uint32_t frequency) +{ + uint16_t divisor = (uint16_t)(PIT_BASE_FREQUENCY / frequency); + + printf("PIT divisor = %u\n", divisor); + // Send command byte: + // Channel 0 | lobyte/hibyte | mode 3 (square wave) | binary + x86_64_outb(PIT_COMMAND_PORT, 0x36); + + // Send divisor (low byte then high byte) + x86_64_outb(PIT_CHANNEL0_PORT, divisor & 0xFF); + x86_64_outb(PIT_CHANNEL0_PORT, (divisor >> 8) & 0xFF); + + g_Unixseconds = boot_request.response->timestamp; + + // Register IRQ0 handler (IRQ0 = timer) + x86_64_IRQ_RegisterHandler(0, PIT_IRQ_Handler); +} + +/* ========================= */ +/* Get tick count */ +/* ========================= */ +uint64_t PIT_GetTicks(void) +{ + return g_Ticks; +} \ No newline at end of file diff --git a/src/arch/x86_64/pit.h b/src/arch/x86_64/pit.h new file mode 100644 index 0000000..a6341fa --- /dev/null +++ b/src/arch/x86_64/pit.h @@ -0,0 +1,14 @@ +#pragma once +#include +#include "irq.h" +#include "io.h" +#include +#include "stdio.h" +#include "e9.h" + +extern volatile uint64_t g_Ticks; +extern uint64_t g_Unixseconds; + +void x86_64_PIT_Initialize(uint32_t frequency); +uint64_t PIT_GetTicks(void); +void PIT_IRQ_Handler(Registers* regs); \ No newline at end of file diff --git a/src/main.c b/src/main.c index cf542a6..4f8959b 100644 --- a/src/main.c +++ b/src/main.c @@ -19,6 +19,13 @@ #include "arch/x86_64/usermode.h" #include "syscall/syscall.h" #include "fs/vfs.h" +#include "time/time.h" +#include "arch/x86_64/pit.h" +#include +#include +#include +#include "arch/x86_64/pci.h" + uintptr_t g_hhdm_offset; #define CPU_STACK_SIZE (64 * 1024) @@ -47,6 +54,13 @@ static volatile struct limine_memmap_request memmap_request = { .revision = 6 }; +__attribute__((used, section(".limine_requests"))) +static volatile struct limine_rsdp_request rsdp_request = { + .id = LIMINE_RSDP_REQUEST_ID, + .revision = 6 +}; + + __attribute__((used, section(".limine_requests"))) @@ -82,7 +96,34 @@ static void hcf(void) { } } +extern struct kernel_pagemap; +uint64_t g_rsdp_phys; +void shutdown(void) { + uacpi_status ret2 = uacpi_prepare_for_sleep_state(UACPI_SLEEP_STATE_S5); + if (uacpi_unlikely_error(ret2)) { + printf("failed to prepare for sleep: %s", uacpi_status_to_string(ret2)); + } + + ret2 = uacpi_enter_sleep_state(UACPI_SLEEP_STATE_S5); + if (uacpi_unlikely_error(ret2)) { + printf("failed to enter sleep: %s", uacpi_status_to_string(ret2)); + } +} + +static uacpi_interrupt_ret handle_power_button(uacpi_handle ctx) { + /* + * Shut down right here using the helper we have defined above. + * + * Note that it's generally terrible practice to run any AML from + * an interrupt handler, as it's allowed to allocate, map, sleep, + * stall, acquire mutexes, etc. So, if possible in your kernel, + * instead schedule the shutdown callback to be run in a normal + * preemptible context later. + */ + shutdown(); + return UACPI_INTERRUPT_HANDLED; +} // The following will be our kernel's entry point. // If renaming kmain() to something else, make sure to change the @@ -104,9 +145,13 @@ void kmain(void) { struct limine_memmap_response *memmap_response = memmap_request.response; + struct limine_rsdp_response *rsdp_response = rsdp_request.response; + + g_rsdp_phys = (uint64_t)rsdp_response->address - MEM_PHYS_OFFSET; + if (!memmap_response) { hcf(); @@ -117,6 +162,7 @@ void kmain(void) { fb_width = framebuffer->width; fb_height = framebuffer->height; fb_pitch = framebuffer->pitch / 4; + printf("Hello, Kernel!\n"); @@ -135,23 +181,29 @@ void kmain(void) { - /*uint32_t msr_lo, msr_hi; + uint32_t msr_lo, msr_hi; asm volatile("rdmsr" : "=a"(msr_lo), "=d"(msr_hi) : "c"(0x1B)); msr_lo &= ~(1 << 11); - asm volatile("wrmsr" : : "a"(msr_lo), "d"(msr_hi), "c"(0x1B));*/ - printf("init pmm"); + asm volatile("wrmsr" : : "a"(msr_lo), "d"(msr_hi), "c"(0x1B)); + printf("rsdp: 0x%x\n", g_rsdp_phys); + printf("init pmm\n"); pmm_init(memmap_response->entries, memmap_response->entry_count); - printf("init slab"); + printf("init slab\n"); slab_init(); - printf("init vmm"); + printf("init vmm\n"); vmm_init(memmap_response->entries, memmap_response->entry_count); + printf("init gdt"); x86_64_GDT_Initialize(); x86_64_IDT_Initialize(); x86_64_ISR_Initialize(); x86_64_IRQ_Initialize(); - x86_64_EnableInterrupts(); - //lapic_timer_start(100); + x86_64_PIT_Initialize(1000); + asm volatile("sti"); + calibrate_tsc(); + + + //while (1) asm volatile("hlt"); ata_init(); ata_identify(); @@ -327,10 +379,68 @@ void kmain(void) { printf("\nKirkOS %s\n", KIRKOS_VERSION); x86_64_EnableInterrupts(); - syscall_init(); - start_userspace(); + + + + /* + * Start with this as the first step of the initialization. This loads all + * tables, brings the event subsystem online, and enters ACPI mode. We pass + * in 0 as the flags as we don't want to override any default behavior for now. + */ + uacpi_status ret = uacpi_initialize(0); + if (uacpi_unlikely_error(ret)) { + printf("uacpi_initialize error: %s", uacpi_status_to_string(ret)); + } + + /* + * Load the AML namespace. This feeds DSDT and all SSDTs to the interpreter + * for execution. + */ + ret = uacpi_namespace_load(); + if (uacpi_unlikely_error(ret)) { + printf("uacpi_namespace_load error: %s", uacpi_status_to_string(ret)); + } + + /* + * Initialize the namespace. This calls all necessary _STA/_INI AML methods, + * as well as _REG for registered operation region handlers. + */ + ret = uacpi_namespace_initialize(); + if (uacpi_unlikely_error(ret)) { + printf("uacpi_namespace_initialize error: %s", uacpi_status_to_string(ret)); + } + + /* + * Tell uACPI that we have marked all GPEs we wanted for wake (even though we haven't + * actually marked any, as we have no power management support right now). This is + * needed to let uACPI enable all unmarked GPEs that have a corresponding AML handler. + * These handlers are used by the firmware to dynamically execute AML code at runtime + * to e.g. react to thermal events or device hotplug. + */ + ret = uacpi_finalize_gpe_initialization(); + if (uacpi_unlikely_error(ret)) { + printf("uACPI GPE initialization error: %s", uacpi_status_to_string(ret)); + } + + + pci_init(); + + pci_device_t hda; + if (pci_find_hda(&hda)) { + printf("[PCI] Found HDA controller at %02x:%02x.%x %04x:%04x\n", + hda.addr.bus, hda.addr.device, hda.addr.function, + hda.vendor_id, hda.device_id); + // BAR0 is usually at hda.bar[0] & ~0xF (MMIO) + } else { + printf("[PCI] No HDA controller found!\n"); + } + + + //syscall_init(); + //start_userspace(); printf("tst"); + // We're done, just hang... hcf(); } \ No newline at end of file diff --git a/src/mm/vmm.c b/src/mm/vmm.c index 911132e..0c180eb 100644 --- a/src/mm/vmm.c +++ b/src/mm/vmm.c @@ -279,17 +279,45 @@ level4: return NULL; } - // spinlock_drop(&pagemap->lock); + spinlock_drop(&pagemap->lock); return &pml1[pml1_entry]; } uint64_t vmm_virt_to_phys(struct pagemap *pagemap, uint64_t virt) { - spinlock_acquire_or_wait(&pagemap->lock); - uint64_t *pte = vmm_virt_to_pte(pagemap, virt, false); - spinlock_drop(&pagemap->lock); - if (pte == NULL || (((*pte) & ~0xffffffffff000) & 1) == 0) - return INVALID_PHYS; + spinlock_acquire_or_wait(&pagemap->lock); - return ((*pte) & 0xffffffffff000); + size_t pml4_entry = (virt & ((uint64_t)0x1FF << 39)) >> 39; + size_t pml3_entry = (virt & ((uint64_t)0x1FF << 30)) >> 30; + size_t pml2_entry = (virt & ((uint64_t)0x1FF << 21)) >> 21; + size_t pml1_entry = (virt & ((uint64_t)0x1FF << 12)) >> 12; + + uint64_t *pml4 = pagemap->top_level; + + uint64_t *pml3 = get_next_level(pml4, pml4_entry, false); + if (!pml3) goto fail; + + uint64_t *pml2 = get_next_level(pml3, pml3_entry, false); + if (!pml2) goto fail; + + // Check for 2MiB huge page (PS bit = bit 7) + if (pml2[pml2_entry] & (1 << 7)) { + uint64_t phys = (pml2[pml2_entry] & ~0x1FFFFFULL) // 2MiB-aligned base + + (virt & 0x1FFFFFULL); // + offset within 2MiB + spinlock_drop(&pagemap->lock); + return phys; + } + + uint64_t *pml1 = get_next_level(pml2, pml2_entry, false); + if (!pml1 || !(pml1[pml1_entry] & 1)) goto fail; + + uint64_t phys = (pml1[pml1_entry] & 0x000ffffffffff000ULL) + + (virt & 0xFFFULL); + spinlock_drop(&pagemap->lock); + return phys; + +fail: + spinlock_drop(&pagemap->lock); + printf("Invalid Phys!\n"); + return INVALID_PHYS; } \ No newline at end of file diff --git a/src/stdio.c b/src/stdio.c index 2dbdb68..27888ed 100644 --- a/src/stdio.c +++ b/src/stdio.c @@ -5,6 +5,8 @@ #include "video/render.h" #include "arch/x86_64/e9.h" #include "mp/spinlock.h" +#include +#include static const uint32_t g_LogSeverityColors[] = { @@ -17,7 +19,26 @@ static const uint32_t g_LogSeverityColors[] = static spinlock_t s_printf_lock = SPINLOCK_INIT; +typedef struct { + char* buf; + size_t size; + size_t pos; +} snprintf_ctx_t; +static void buf_putc(snprintf_ctx_t* ctx, char c) +{ + if (ctx->pos + 1 < ctx->size) { + ctx->buf[ctx->pos] = c; + } + ctx->pos++; +} + +static void buf_puts(snprintf_ctx_t* ctx, const char* str) +{ + while (*str) { + buf_putc(ctx, *str++); + } +} void fputc(char c) { @@ -38,7 +59,7 @@ void fputs_colored(const char* str, uint32_t color) { const char g_HexChars[] = "0123456789abcdef"; -void fprintf_unsigned(uint64_t number, int radix) +static void buf_print_unsigned(snprintf_ctx_t* ctx, uint64_t number, int radix, int width, int zero_pad) { char buffer[32]; int pos = 0; @@ -48,73 +69,286 @@ void fprintf_unsigned(uint64_t number, int radix) number /= radix; } while (number > 0); - while (--pos >= 0) fputc(buffer[pos]); + // Apply zero-padding / width + if (zero_pad) { + while (pos < width) + buffer[pos++] = '0'; + } + + while (--pos >= 0) + buf_putc(ctx, buffer[pos]); } -void fprintf_signed(int64_t number, int radix) +static void buf_print_signed(snprintf_ctx_t* ctx, int64_t number, int radix, int width, int zero_pad) { if (number < 0) { - fputc('-'); - fprintf_unsigned(-number, radix); + buf_putc(ctx, '-'); + buf_print_unsigned(ctx, (uint64_t)(-number), radix, width, zero_pad); } else { - fprintf_unsigned(number, radix); + buf_print_unsigned(ctx, (uint64_t)number, radix, width, zero_pad); } } +int snprintf(char* buf, size_t size, const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + int ret = vsnprintf(buf, size, fmt, args); + va_end(args); + return ret; +} + +static void fprintf_unsigned(uint64_t number, int radix, int width, int zero_pad) +{ + char buffer[32]; + int pos = 0; + + do { + buffer[pos++] = g_HexChars[number % radix]; + number /= radix; + } while (number > 0); + + if (zero_pad) { + while (pos < width) + buffer[pos++] = '0'; + } + + while (--pos >= 0) + fputc(buffer[pos]); +} + +static void fprintf_signed(int64_t number, int radix, int width, int zero_pad) +{ + if (number < 0) { + fputc('-'); + fprintf_unsigned((uint64_t)(-number), radix, width, zero_pad); + } else { + fprintf_unsigned((uint64_t)number, radix, width, zero_pad); + } +} + + void vfprintf(const char* fmt, va_list args) { - int state = 0; // normal - int length = 0; // default - int radix = 10; - bool sign = false; - bool number = false; - while (*fmt) { - switch (state) { - case 0: // normal - if (*fmt == '%') state = 1; - else fputc(*fmt); - break; + if (*fmt != '%') { + fputc(*fmt++); + continue; + } + fmt++; // skip % - case 1: // length - if (*fmt == 'l') { length = 3; state = 4; } - else if (*fmt == 'h') { length = 2; state = 4; } - else goto spec; - break; - - case 4: // spec - spec: - switch (*fmt) { - case 'c': fputc((char)va_arg(args,int)); break; - case 's': fputs(va_arg(args,const char*)); break; - case 'd': - case 'i': sign = true; number = true; radix = 10; break; - case 'u': sign = false; number = true; radix = 10; break; - case 'x': - case 'X': sign = false; number = true; radix = 16; break; - case 'p': number = true; sign = false; radix = 16; break; - case 'o': sign = false; number = true; radix = 8; break; - case '%': fputc('%'); break; - } - - if (number) { - if (sign) fprintf_signed(va_arg(args,long), radix); - else fprintf_unsigned(va_arg(args,unsigned long), radix); - } - - state = 0; - length = 0; - number = false; - sign = false; - break; + /* flags */ + int zero_pad = 0; + if (*fmt == '0') { + zero_pad = 1; + fmt++; } - fmt++; + /* width */ + int width = 0; + while (*fmt >= '0' && *fmt <= '9') { + width = width * 10 + (*fmt++ - '0'); + } + + /* precision */ + int precision = -1; + if (*fmt == '.') { + fmt++; + precision = 0; + while (*fmt >= '0' && *fmt <= '9') { + precision = precision * 10 + (*fmt++ - '0'); + } + } + + /* length modifier */ + int is_long_long = 0; + if (*fmt == 'l') { + fmt++; + if (*fmt == 'l') { + is_long_long = 1; + fmt++; + } + } else if (*fmt == 'z') { // %zu, %zx + is_long_long = 1; + fmt++; + } else if (*fmt == 'h') { + fmt++; // ignore h/hh + } + + /* specifier */ + switch (*fmt++) { + case 'c': + fputc((char)va_arg(args, int)); + break; + + case 's': { + const char* s = va_arg(args, const char*); + if (!s) s = "(null)"; + if (precision >= 0) { + while (*s && precision--) fputc(*s++); + } else { + fputs(s); + } + break; + } + + case 'd': + case 'i': + if (is_long_long) + fprintf_signed(va_arg(args, long long), 10, width, zero_pad); + else + fprintf_signed(va_arg(args, long), 10, width, zero_pad); + break; + + case 'u': + if (is_long_long) + fprintf_unsigned(va_arg(args, unsigned long long), 10, width, zero_pad); + else + fprintf_unsigned(va_arg(args, unsigned long), 10, width, zero_pad); + break; + + case 'x': + case 'X': + if (is_long_long) + fprintf_unsigned(va_arg(args, unsigned long long), 16, width, zero_pad); + else + fprintf_unsigned(va_arg(args, unsigned long), 16, width, zero_pad); + break; + + case 'p': + fputs("0x"); + fprintf_unsigned(va_arg(args, uint64_t), 16, 16, 1); // always full 64-bit + break; + + case 'o': + if (is_long_long) + fprintf_unsigned(va_arg(args, unsigned long long), 8, width, zero_pad); + else + fprintf_unsigned(va_arg(args, unsigned long), 8, width, zero_pad); + break; + + case '%': + fputc('%'); + break; + + default: + fputc('%'); + fputc(*(fmt - 1)); + break; + } } } +int vsnprintf(char* buf, size_t size, const char* fmt, va_list args) +{ + snprintf_ctx_t ctx = { .buf = buf, .size = size, .pos = 0 }; + + while (*fmt) { + if (*fmt != '%') { + buf_putc(&ctx, *fmt++); + continue; + } + fmt++; + + int zero_pad = 0; + if (*fmt == '0') { zero_pad = 1; fmt++; } + + int width = 0; + while (*fmt >= '0' && *fmt <= '9') { + width = width * 10 + (*fmt++ - '0'); + } + + int precision = -1; + if (*fmt == '.') { + fmt++; + precision = 0; + while (*fmt >= '0' && *fmt <= '9') { + precision = precision * 10 + (*fmt++ - '0'); + } + } + + int is_long_long = 0; + if (*fmt == 'l') { + fmt++; + if (*fmt == 'l') { is_long_long = 1; fmt++; } + } else if (*fmt == 'z') { + is_long_long = 1; + fmt++; + } else if (*fmt == 'h') { + fmt++; + } + + switch (*fmt++) { + case 'c': + buf_putc(&ctx, (char)va_arg(args, int)); + break; + + case 's': { + const char* s = va_arg(args, const char*); + if (!s) s = "(null)"; + int max = (precision >= 0) ? precision : INT_MAX; + while (*s && max--) buf_putc(&ctx, *s++); + break; + } + + case 'd': + case 'i': + if (is_long_long) + buf_print_signed(&ctx, va_arg(args, long long), 10, width, zero_pad); + else + buf_print_signed(&ctx, va_arg(args, long), 10, width, zero_pad); + break; + + case 'u': + if (is_long_long) + buf_print_unsigned(&ctx, va_arg(args, unsigned long long), 10, width, zero_pad); + else + buf_print_unsigned(&ctx, va_arg(args, unsigned long), 10, width, zero_pad); + break; + + case 'x': + case 'X': + if (is_long_long) + buf_print_unsigned(&ctx, va_arg(args, unsigned long long), 16, width, zero_pad); + else + buf_print_unsigned(&ctx, va_arg(args, unsigned long), 16, width, zero_pad); + break; + + case 'p': + buf_puts(&ctx, "0x"); + buf_print_unsigned(&ctx, va_arg(args, uint64_t), 16, 16, 1); + break; + + case 'o': + if (is_long_long) + buf_print_unsigned(&ctx, va_arg(args, unsigned long long), 8, width, zero_pad); + else + buf_print_unsigned(&ctx, va_arg(args, unsigned long), 8, width, zero_pad); + break; + + case '%': + buf_putc(&ctx, '%'); + break; + + default: + buf_putc(&ctx, '%'); + buf_putc(&ctx, *(fmt - 1)); + break; + } + } + + if (ctx.size > 0) { + if (ctx.pos < ctx.size) + ctx.buf[ctx.pos] = '\0'; + else + ctx.buf[ctx.size - 1] = '\0'; + } + + return (int)ctx.pos; +} + void printf(const char* fmt, ...) { uint64_t flags; diff --git a/src/stdio.h b/src/stdio.h index e844495..c089248 100644 --- a/src/stdio.h +++ b/src/stdio.h @@ -1,6 +1,7 @@ #pragma once #include #include +#include void printf(const char* fmt, ...); void fputs(const char* str); @@ -8,3 +9,5 @@ void fputc(char c); void vfprintf(const char* fmt, va_list args); void fprintf(const char* fmt, ...); void fputs_colored(const char* str, uint32_t color); +int snprintf(char* buf, size_t size, const char* fmt, ...); +int vsnprintf(char* buf, size_t size, const char* fmt, va_list args); \ No newline at end of file diff --git a/src/string.c b/src/string.c index 7480498..863b2dd 100644 --- a/src/string.c +++ b/src/string.c @@ -100,6 +100,19 @@ char* strcat(char* dst, const char* src) return origDst; } +size_t strnlen(const char* str, size_t maxlen) +{ + size_t len = 0; + + if (!str) + return 0; + + while (len < maxlen && str[len]) + ++len; + + return len; +} + char* strncpy(char* dst, const char* src, size_t n) { char* origDst = dst; diff --git a/src/string.h b/src/string.h index 0b18a28..0db6f7b 100644 --- a/src/string.h +++ b/src/string.h @@ -9,4 +9,5 @@ char* strncpy(char* dst, const char* src, size_t n); char* strcat(char* dst, const char* src); int strncmp(const char* a, const char* b, size_t n); wchar_t* utf16_to_codepoint(wchar_t* string, int* codepoint); -char* codepoint_to_utf8(int codepoint, char* stringOutput); \ No newline at end of file +char* codepoint_to_utf8(int codepoint, char* stringOutput); +size_t strnlen(const char* str, size_t maxlen); \ No newline at end of file diff --git a/src/time/time.c b/src/time/time.c new file mode 100644 index 0000000..70e605b --- /dev/null +++ b/src/time/time.c @@ -0,0 +1,50 @@ +#include +#include "arch/x86_64/pit.h" +#include "arch/x86_64/io.h" +#include "stdio.h" +#include "time.h" + +extern uint64_t PIT_GetTicks(void); + +volatile uint64_t tsc_cycles_per_us = 0; + +uint64_t rdtsc(void) +{ + uint32_t lo, hi; + __asm__ volatile ("rdtsc" : "=a"(lo), "=d"(hi)); + return ((uint64_t)hi << 32) | lo; +} + + +void calibrate_tsc(void) +{ + // Use a stable PIT rate for calibration + const uint32_t pit_freq = 1000; // 100 Hz = 10ms per tick + const uint32_t calibration_ticks = 1000; // 1 second total + + + + // Wait for PIT to stabilize + uint64_t start_ticks = PIT_GetTicks(); + while (PIT_GetTicks() == start_ticks) + ; + + uint64_t start_tsc = rdtsc(); + uint64_t target_ticks = start_ticks + calibration_ticks; + + // Wait for known number of PIT ticks + while (PIT_GetTicks() < target_ticks) + ; + + uint64_t end_tsc = rdtsc(); + + // Compute elapsed time + uint64_t tsc_delta = end_tsc - start_tsc; + + // 100 ticks at 100 Hz = 1 second = 1,000,000 microseconds + uint64_t elapsed_us = 1000000ULL; + + tsc_cycles_per_us = tsc_delta / elapsed_us; + + printf("TSC calibration complete: %lu cycles/us\n", tsc_cycles_per_us); +} \ No newline at end of file diff --git a/src/time/time.h b/src/time/time.h new file mode 100644 index 0000000..34e8cfe --- /dev/null +++ b/src/time/time.h @@ -0,0 +1,10 @@ +#include +#include "arch/x86_64/pit.h" +#include "arch/x86_64/io.h" +#include "stdio.h" + +uint64_t rdtsc(void); + + + +void calibrate_tsc(void); \ No newline at end of file