feat: implement PCI subsystem and integrate uACPI support

See previous commit

Signed-off-by: kaguya <vpshinomiya@protonmail.com>
This commit is contained in:
kaguya
2026-04-15 13:33:18 -04:00
parent 840210ddc0
commit cc8351bf8d
15 changed files with 785 additions and 68 deletions
+9
View File
@@ -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}"
-1
View File
@@ -1,7 +1,6 @@
#include "pic.h"
#include "io.h"
#include <stdbool.h>
#include <stdint.h>
#define PIC1_COMMAND_PORT 0x20
#define PIC1_DATA_PORT 0x21
+2 -1
View File
@@ -52,11 +52,12 @@ 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)
+137
View File
@@ -0,0 +1,137 @@
#include "pci.h"
#include "arch/x86_64/io.h"
#include <stdio.h>
#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;
}
+39
View File
@@ -0,0 +1,39 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
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);
+69
View File
@@ -0,0 +1,69 @@
#include "pit.h"
#include "irq.h"
#include "io.h"
#include <stdint.h>
#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;
}
+14
View File
@@ -0,0 +1,14 @@
#pragma once
#include <stdint.h>
#include "irq.h"
#include "io.h"
#include <stdint.h>
#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);
+119 -9
View File
@@ -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 <uacpi/uacpi.h>
#include <uacpi/event.h>
#include <uacpi/sleep.h>
#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,6 +145,10 @@ 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;
@@ -119,6 +164,7 @@ void kmain(void) {
fb_pitch = framebuffer->pitch / 4;
printf("Hello, Kernel!\n");
printf("Number: %d\n", 1234);
printf("Hex: %x\n", 0xBEEF);
@@ -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();
}
+34 -6
View File
@@ -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;
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;
}
+283 -49
View File
@@ -5,6 +5,8 @@
#include "video/render.h"
#include "arch/x86_64/e9.h"
#include "mp/spinlock.h"
#include <stddef.h>
#include <limits.h>
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,71 +69,284 @@ 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;
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;
if (*fmt != '%') {
fputc(*fmt++);
continue;
}
fmt++; // skip %
/* flags */
int zero_pad = 0;
if (*fmt == '0') {
zero_pad = 1;
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, ...)
+3
View File
@@ -1,6 +1,7 @@
#pragma once
#include <stdarg.h>
#include <stdint.h>
#include <stddef.h>
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);
+13
View File
@@ -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;
+1
View File
@@ -10,3 +10,4 @@ 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);
size_t strnlen(const char* str, size_t maxlen);
+50
View File
@@ -0,0 +1,50 @@
#include <stdint.h>
#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);
}
+10
View File
@@ -0,0 +1,10 @@
#include <stdint.h>
#include "arch/x86_64/pit.h"
#include "arch/x86_64/io.h"
#include "stdio.h"
uint64_t rdtsc(void);
void calibrate_tsc(void);