Files
KirkOS/src/main.c
T
kaguya e6e8b1209b sys: Refactor GDT initialization, enhance syscall handling, and improve user mode entry
Finally we got usermode working again, It took awhile but we got it done. A few things we did in this commit:

- Simplified GDT entry initialization in gdt.c for kernel and user segments.
- Fixed TSS structure for task switching.
- Implemented cr2 handling in a page fault.
- Enhanced user stack setup in usermode.c to return the correct RSP.
- Improved syscall implementation in syscall.c, including new syscall numbers.
- Updated syscall entry to correctly handle context switching and argument passing.
- Refactored init.c to demonstrate file operations using syscalls.
- Added new syscalls for file operations in syscalls.h.
- Modified VFS to handle leading slashes in paths correctly.

Signed-off-by: kaguya <vpshinomiya@protonmail.com>
2026-04-26 02:06:28 -04:00

357 lines
9.4 KiB
C

#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <limine.h>
#include "video/render.h"
#include "video/tga.h"
#include "stdio.h"
#include "arch/x86_64/gdt.h"
#include "arch/x86_64/idt.h"
#include "arch/x86_64/isr.h"
#include "arch/x86_64/irq.h"
#include "mm/memory.h"
#include "mm/pmm.h"
#include "mm/vmm.h"
#include "arch/x86_64/ata.h"
#include "fs/ext2.h"
#include "string.h"
#include "arch/x86_64/io.h"
#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"
#include "sound/hda.h"
#include "sound/pcm.h"
#include "input/input.h"
#include "arch/x86_64/apic.h"
#include "arch/x86_64/ioapic.h"
#include "input/ps2.h"
uintptr_t g_hhdm_offset;
#define CPU_STACK_SIZE (64 * 1024)
// Set the base revision to 6, this is recommended as this is the latest
// base revision described by the Limine boot protocol specification.
// See specification for further info.
__attribute__((used, section(".limine_requests")))
static volatile uint64_t limine_base_revision[] = LIMINE_BASE_REVISION(6);
// The Limine requests can be placed anywhere, but it is important that
// the compiler does not optimise them away, so, usually, they should
// be made volatile or equivalent, _and_ they should be accessed at least
// once or marked as used with the "used" attribute as done here.
__attribute__((used, section(".limine_requests")))
static volatile struct limine_framebuffer_request framebuffer_request = {
.id = LIMINE_FRAMEBUFFER_REQUEST_ID,
.revision = 6
};
__attribute__((used, section(".limine_requests")))
static volatile struct limine_memmap_request memmap_request = {
.id = LIMINE_MEMMAP_REQUEST_ID,
.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")))
volatile struct limine_stack_size_request stack_size_request = {
.id = LIMINE_STACK_SIZE_REQUEST_ID,
.revision = 0,
.stack_size = CPU_STACK_SIZE,
};
// Finally, define the start and end markers for the Limine requests.
// These can also be moved anywhere, to any .c file, as seen fit.
__attribute__((used, section(".limine_requests_start")))
static volatile uint64_t limine_requests_start_marker[] = LIMINE_REQUESTS_START_MARKER;
__attribute__((used, section(".limine_requests_end")))
static volatile uint64_t limine_requests_end_marker[] = LIMINE_REQUESTS_END_MARKER;
// Halt and catch fire function.
static void hcf(void) {
for (;;) {
asm volatile ("pause");
}
}
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;
}
void kmain(void) {
if (LIMINE_BASE_REVISION_SUPPORTED(limine_base_revision) == false) {
hcf();
}
uint64_t rsp;
asm volatile("mov %%rsp, %0" : "=r"(rsp));
// Ensure we got a framebuffer.
if (framebuffer_request.response == NULL
|| framebuffer_request.response->framebuffer_count < 1) {
hcf();
}
// Fetch the first framebuffer.
struct limine_framebuffer *framebuffer = framebuffer_request.response->framebuffers[0];
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();
}
fb = framebuffer->address;
fb_width = framebuffer->width;
fb_height = framebuffer->height;
fb_pitch = framebuffer->pitch / 4;
printf("Hello, Kernel!\n");
printf("Number: %d\n", 1234);
printf("Hex: %x\n", 0xBEEF);
for (size_t i = 0; i < memmap_response->entry_count; i++) {
struct limine_memmap_entry *entry = memmap_response->entries[i];
printf("Base: 0x%x%x, Length: 0x%x%x, Type: %d\n",
(uint32_t)(entry->base >> 32), (uint32_t)entry->base,
(uint32_t)(entry->length >> 32), (uint32_t)entry->length,
entry->type);
}
x86_64_DisableInterrupts();
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("rsdp: 0x%x\n", g_rsdp_phys);
printf("init pmm\n");
pmm_init(memmap_response->entries, memmap_response->entry_count);
printf("init slab\n");
slab_init();
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_PIT_Initialize(1000);
asm volatile("sti");
calibrate_tsc();
//while (1) asm volatile("hlt");
ata_init();
ata_identify();
if (!ext2_read_superblock()) {
printf("EXT2 failed\n");
hcf();
}
if (!ext2_read_group_desc_table()) {
printf("GDT failed\n");
hcf();
}
ext2_read_root_dir();
uint32_t inum = ext2_resolve_path("charlie.tga");
if (!inum) {
printf("file not found\n");
hcf();
}
ext2_inode_t inode;
if (!ext2_read_inode(inum, &inode)) {
printf("inode read failed\n");
hcf();
}
uint32_t tga_size = inode.i_size;
if (!tga_size) {
printf("tga file is empty\n");
hcf();
}
uint8_t *file_buf = kmalloc(tga_size);
if (!file_buf) {
printf("OOM allocating tga buffer (%u bytes)\n", tga_size);
hcf();
}
if (!ext2_read_file(&inode, file_buf)) {
printf("read failed\n");
kfree(file_buf);
hcf();
}
uint32_t *img = tga_parse(file_buf, tga_size);
kfree(file_buf); // free the raw file buffer as soon as parse is done
if (!img) {
printf("tga parse failed\n");
hcf();
}
int x = 0;
int y = 0;
int new_w = img[0] * 5;
int new_h = img[1] * 5;
clear_screen(0xFF1E1E1E);
draw_image_bilinear(img, x, y, new_w, new_h);
kfree(img);
//clear_screen(0xFF1E1E1E);
printf("\nKirkOS %s\n", KIRKOS_VERSION);
x86_64_EnableInterrupts();
/*
* 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);
if (hda_init(&hda)) {
pcm_play_file("kirky.wav");
}
} else {
printf("[PCI] No HDA controller found!\n");
}*/
lapic_init();
ioapic_init();
irq_redirect_to_apic(0, 0x20, lapic_id(), false);
printf("tst");
input_init();
ps2_kbd_init();
gdt_load_tss((size_t)&kernel_tss);
kernel_tss.rsp0 = rsp;
syscall_init();
start_userspace();
// We're done, just hang...
hcf();
}