paging: wip

This commit is contained in:
Jordan ⌨️ 2025-09-25 19:05:39 +02:00
parent 2b3a27a398
commit 1f954724fe
19 changed files with 649 additions and 8 deletions

View file

@ -43,6 +43,7 @@ def _(args: StartArgs):
*[ *[
"qemu-system-x86_64", "qemu-system-x86_64",
# "-enable-kvm", # "-enable-kvm",
# "-d", "int,guest_errors,cpu_reset",
"-no-reboot", "-no-reboot",
"-no-shutdown", "-no-shutdown",
"-display", "-display",

View file

@ -68,10 +68,13 @@
] ]
}, },
"as": { "as": {
"cmd": "nasm", "cmd": "{shell.latest('clang')}",
"args": [ "args": [
"-f", "-Isrc/kernel/hal/x86_64",
"elf64" "-target",
"x86_64-none-elf",
"-c",
"-g"
] ]
} }
} }

View file

@ -50,6 +50,8 @@ SECTIONS
*(.note.gnu.build-id) *(.note.gnu.build-id)
} :rodata } :rodata
rodata_end_addr = .;
/* Move to the next memory page for .data */ /* Move to the next memory page for .data */
. = ALIGN(CONSTANT(MAXPAGESIZE)); . = ALIGN(CONSTANT(MAXPAGESIZE));

View file

@ -1,13 +1,8 @@
#include <allocators/slab.h>
#include <fmt>
#include <hal>
#include <kmalloc> #include <kmalloc>
#include <logger> #include <logger>
#include <string.h> #include <string.h>
void kmain(void) { void kmain(void) {
kmalloc_init();
Allocator kmalloc = kmalloc_allocator(); Allocator kmalloc = kmalloc_allocator();
char* hello = (char*)kmalloc.alloc(&kmalloc, 14); char* hello = (char*)kmalloc.alloc(&kmalloc, 14);
memset(hello, 0, 14); memset(hello, 0, 14);

View file

@ -4,6 +4,7 @@
#include <hal> #include <hal>
#include <handover-dtb> #include <handover-dtb>
#include <helpers/mem.h> #include <helpers/mem.h>
#include <kmalloc>
#include <logger> #include <logger>
#include <pmm> #include <pmm>
#include <stdio.h> #include <stdio.h>
@ -36,6 +37,7 @@ void rv32_start([[gnu::unused]] size_t hartid, uintptr_t dtb) {
pmm_init(); pmm_init();
paging_init(); paging_init();
kmalloc_init();
kmain(); kmain();
} }

View file

@ -1,8 +1,12 @@
#include <dtb> #include <dtb>
#include <kmalloc>
#include <limine> #include <limine>
#include <logger> #include <logger>
#include <pmm> #include <pmm>
#include "gdt.h"
#include "idt.h"
[[gnu::used, gnu::section(".limine_requests")]] static volatile LIMINE_BASE_REVISION(3); [[gnu::used, gnu::section(".limine_requests")]] static volatile LIMINE_BASE_REVISION(3);
[[gnu::used, gnu::section(".limine_requests_start")]] static volatile LIMINE_REQUESTS_START_MARKER; [[gnu::used, gnu::section(".limine_requests_start")]] static volatile LIMINE_REQUESTS_START_MARKER;
[[gnu::used, gnu::section(".limine_requests_end")]] static volatile LIMINE_REQUESTS_END_MARKER; [[gnu::used, gnu::section(".limine_requests_end")]] static volatile LIMINE_REQUESTS_END_MARKER;
@ -10,7 +14,10 @@
void kmain(void); void kmain(void);
void x86_64_start(void) { void x86_64_start(void) {
gdt_init();
idt_init();
pmm_init(); pmm_init();
kmalloc_init();
kmain(); kmain();
} }

View file

@ -0,0 +1,65 @@
#include <logger>
#include "regs.h"
#define IRQ0 (32)
static char* exception_types[] = {
"Division By Zero",
"Debug",
"Non Maskable Interrupt",
"Breakpoint",
"Detected Overflow",
"Out Of Bounds",
"Invalid Opcode",
"No Coprocessor",
"Double Fault",
"Coprocessor Segment Overrun",
"Bad Tss",
"Segment Not Present",
"StackFault",
"General Protection Fault",
"Page Fault",
"Unknown Interrupt",
"Coprocessor Fault",
"Alignment Check",
"Machine Check",
"SIMD Floating-Point Exception",
"Virtualization Exception",
"Control Protection Exception",
"Reserved",
"Hypervisor Injection Exception",
"paging Communication Exception",
"Security Exception",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
};
static void panic_handler(Stackframe* frame) {
print$("\n\x1b[0;31m!!! \x1b[33m---------------------------------------------------------------------------------------------------\x1b[0m\n\n");
print$(" KERNEL PANIC\n\n");
print$(" %s was raised\n", exception_types[frame->intno]);
print$(" interrupt: %x, err: %x\n\n", frame->intno, frame->err);
print$(" RAX %p RBX %p RCX %p RDX %p\n", frame->rax, frame->rbx, frame->rcx, frame->rdx);
print$(" RSI %p RDI %p RBP %p RSP %p\n", frame->rsi, frame->rdi, frame->rbp, frame->rsp);
print$(" R8 %p R9 %p R10 %p R11 %p\n", frame->r8, frame->r9, frame->r10, frame->r11);
print$(" R12 %p R13 %p R14 %p R15 %p\n", frame->r12, frame->r13, frame->r14, frame->r15);
print$(" CR0 %p CR2 %p CR3 %p CR4 %p\n", cr$(0), cr$(2), cr$(3), cr$(4));
print$(" CS %p SS %p FLG %p\n", frame->cs, frame->ss, frame->rflags);
print$(" RIP \033[7m%p\033[0m\n\n", frame->rip);
print$("\n\x1b[33m--------------------------------------------------------------------------------------------------- \x1b[0;31m!!!\x1b[0m\n\n");
}
uint64_t interrupt_handler(uintptr_t rsp) {
Stackframe* frame = (Stackframe*)rsp;
if (frame->intno < IRQ0) {
panic_handler(frame);
}
return rsp;
}

View file

@ -0,0 +1,61 @@
#include <string.h>
#include "gdt.h"
static Gdt gdt = {0};
static GdtDescriptor gdt_desc = {
.size = sizeof(gdt) - 1,
.offset = (uintptr_t)&gdt,
};
static void gdt_entry(GdtEntry* self, uint8_t access, uint8_t flags) {
memset(self, 0, sizeof(GdtEntry));
if (access == 0 && flags == 0) {
return;
}
self->access = access | GDT_ACCESS_PRESENT | GDT_ACCESS_READ_WRITE | GDT_ACCESS_DESCRIPTOR;
self->flags = flags | GDT_FLAGS_GRANULARITY;
self->base_high = 0;
self->base_middle = 0;
self->base_low = 0;
self->limit_low = 0xffff;
self->limit_high = 0x0f;
}
void gdt_load_tss(Tss* tss) {
uintptr_t tss_ptr = (uintptr_t)tss;
gdt.tss_entry = (TssEntry){
.length = sizeof(TssEntry),
.base_low = tss_ptr & 0xffff,
.base_mid = (tss_ptr >> 16) & 0xff,
.base_high = (tss_ptr >> 24) & 0xff,
.base_upper = tss_ptr >> 32,
.flags1 = TSS_FLAGS_PRESENT | TSS_FLAGS_64BITS_AVAILABLE,
.flags2 = 0,
.reserved = 0,
};
}
void gdt_init(void) {
gdt_entry(&gdt.entries[GDT_NULL_DESC], 0, 0);
gdt_entry(&gdt.entries[GDT_KERNEL_CODE], GDT_ACCESS_EXE, GDT_FLAGS_LONG_MODE);
gdt_entry(&gdt.entries[GDT_KERNEL_DATA], 0, GDT_FLAGS_SIZE);
gdt_entry(&gdt.entries[GDT_USER_DATA], GDT_ACCESS_USER, GDT_FLAGS_SIZE);
gdt_entry(&gdt.entries[GDT_USER_CODE], GDT_ACCESS_USER | GDT_ACCESS_EXE, GDT_FLAGS_LONG_MODE);
gdt_load_tss(NULL);
gdt_flush((uintptr_t)&gdt_desc);
tss_flush();
}

View file

@ -0,0 +1,82 @@
#pragma once
#include <helpers/errors.h>
#include <stddef.h>
#include <stdint.h>
enum gdtaccess : uint8_t {
GDT_ACCESS_READ_WRITE = (1 << 1),
GDT_ACCESS_EXE = (1 << 3),
GDT_ACCESS_DESCRIPTOR = (1 << 4),
GDT_ACCESS_USER = (3 << 5),
GDT_ACCESS_PRESENT = (1 << 7),
};
enum gdtflags : uint8_t {
GDT_FLAGS_LONG_MODE = (1 << 1),
GDT_FLAGS_SIZE = (1 << 2),
GDT_FLAGS_GRANULARITY = (1 << 3),
};
enum tssflags : uint8_t {
TSS_FLAGS_PRESENT = (1 << 7),
TSS_FLAGS_64BITS_AVAILABLE = (0x9),
};
enum : size_t {
GDT_NULL_DESC,
GDT_KERNEL_CODE,
GDT_KERNEL_DATA,
GDT_USER_DATA,
GDT_USER_CODE,
GDT_ENTRIES_LENGTH
};
typedef struct [[gnu::packed]] {
uint16_t limit_low;
uint16_t base_low;
uint8_t base_middle;
uint8_t access;
uint8_t limit_high : 4;
uint8_t flags : 4;
uint8_t base_high;
} GdtEntry;
typedef struct [[gnu::packed]] {
uint16_t length;
uint16_t base_low;
uint8_t base_mid;
uint8_t flags1;
uint8_t flags2;
uint8_t base_high;
uint32_t base_upper;
uint32_t reserved;
} TssEntry;
typedef struct [[gnu::packed]] {
uint32_t reserved;
uint64_t rsp[3];
uint64_t reserved0;
uint64_t ist[7];
uint32_t reserved1;
uint32_t reserved2;
uint16_t reserved3;
uint16_t iopb_offset;
} Tss;
typedef struct [[gnu::packed]] {
uint16_t size;
uintptr_t offset;
} GdtDescriptor;
typedef struct [[gnu::packed]] {
GdtEntry entries[GDT_ENTRIES_LENGTH];
TssEntry tss_entry;
} Gdt;
void gdt_init(void);
void gdt_load_tss(Tss* tss);
extern void gdt_flush(uintptr_t);
extern void tss_flush(void);

View file

@ -0,0 +1,21 @@
.globl gdt_flush
.globl tss_flush
gdt_flush:
lgdt (%rdi)
mov $0x10, %ax
mov %ax, %ss
mov %ax, %ds
mov %ax, %es
pop %rdi
mov $0x08, %rax
push %rax
push %rdi
lretq
tss_flush:
mov $0x28, %ax
ltr %ax
ret

View file

@ -0,0 +1,32 @@
#include <logger>
#include "gdt.h"
#include "idt.h"
static Idt idt;
static IdtDescriptor idt_desc = {
.size = sizeof(idt) - 1,
.offset = (uintptr_t)&idt,
};
static void idt_init_entry(IdtEntry* self, uint64_t base, uint8_t type) {
self->offset_low = base & 0xFFFF;
self->offset_mid = (base >> 16) & 0xFFFF;
self->offset_high = (base >> 32) & 0xFFFFFFFF;
self->ist = 0;
self->selector = GDT_KERNEL_CODE * 8;
self->type_attr = IDT_INT_PRESENT | type;
self->zero = 0;
}
uintptr_t idt_descriptor(void) {
return (uintptr_t)&idt_desc;
}
void idt_init(void) {
for (size_t i = 0; i < IDT_ENTRY_COUNT; i++) {
idt_init_entry(&idt.entries[i], __interrupts_vector[i], IDT_INT_GATE);
}
idt_flush(idt_descriptor());
}

View file

@ -0,0 +1,37 @@
#pragma once
#include <stdint.h>
#define IDT_ENTRY_COUNT (256)
enum {
IDT_INT_PRESENT = (1 << 7),
IDT_INT_GATE = (0xe),
};
typedef struct [[gnu::packed]] {
uint16_t size;
uintptr_t offset;
} IdtDescriptor;
typedef struct packed {
uint16_t offset_low;
uint16_t selector;
uint8_t ist;
uint8_t type_attr;
uint16_t offset_mid;
uint32_t offset_high;
uint32_t zero;
} IdtEntry;
typedef struct [[gnu::packed]] {
IdtEntry entries[IDT_ENTRY_COUNT];
} Idt;
void idt_init(void);
uintptr_t idt_descriptor(void);
extern uintptr_t __interrupts_vector[];
extern void idt_flush(uintptr_t);

View file

@ -0,0 +1,57 @@
.include "macro.s"
.altmacro
.globl idt_flush
.globl __interrupts_vector
.extern interrupt_handler
.macro INTERRUPT_NAME intno
.quad __interrupt\intno
.endm
.macro INTERRUPT_ERR intno
__interrupt\intno:
pushq $\intno
jmp __interrupt_common
.endm
.macro INTERRUPT_NOERR intno
__interrupt\intno:
pushq $0
pushq $\intno
jmp __interrupt_common
.endm
__interrupt_common:
cld
__pusha
movq %rsp, %rdi
call interrupt_handler
movq %rax, %rsp
__popa
add $16, %rsp
iretq
.set i,0
.rept 48
.if i != 8 && i != 10 && i != 11 && i != 12 && i != 13 && i != 14 && i != 17 && i != 30
INTERRUPT_NOERR %i
.else
INTERRUPT_ERR %i
.endif
.set i,i+1
.endr
.align 8
__interrupts_vector:
.set i,0
.rept 48
INTERRUPT_NAME %i
.set i,i+1
.endr
idt_flush:
lidt (%rdi)
ret

View file

@ -0,0 +1,35 @@
.macro __pusha
push %rax
push %rbx
push %rcx
push %rdx
push %rsi
push %rdi
push %rbp
push %r8
push %r9
push %r10
push %r11
push %r12
push %r13
push %r14
push %r15
.endm
.macro __popa
pop %r15
pop %r14
pop %r13
pop %r12
pop %r11
pop %r10
pop %r9
pop %r8
pop %rbp
pop %rdi
pop %rsi
pop %rdx
pop %rcx
pop %rbx
pop %rax
.endm

View file

@ -0,0 +1,127 @@
#include <hal>
#include <helpers/mem.h>
#include <logger>
#include <pmm>
#include <string.h>
#include "paging.h"
static uint64_t* kernel_page_table = NULL;
static size_t mapping_page_size = mib$(2);
static int64_t transform_flags(uint8_t flags) {
int64_t ret = PAGE_NO_EXECUTE | PAGE_PRESENT;
if (flags & HAL_MAP_READ) {
}
if (flags & HAL_MAP_NONE) {
ret &= ~PAGE_PRESENT;
}
if (flags & HAL_MAP_WRITE) {
ret |= PAGE_WRITABLE;
}
if (flags & HAL_MAP_EXEC) {
ret &= ~PAGE_NO_EXECUTE;
}
if (flags & HAL_MAP_USER) {
ret |= PAGE_USER;
}
if (flags & HAL_MAP_HUGE) {
ret |= PAGE_HUGE;
}
return ret;
}
static void* paging_get_pml_alloc(uint64_t* table, size_t index, bool alloc) {
if (table[index] & PAGE_PRESENT) {
return hal_mmap_l2h(PAGE_GET_PHYS(table[index]));
} else if (alloc) {
Allocator pmm = pmm_allocator();
void* ptr = pmm.alloc(&pmm, page_size());
if (ptr == NULL) {
return NULL;
}
memset(ptr, 0, page_size());
table[index] = (uint64_t)hal_mmap_h2l((uint64_t)ptr) | PAGE_PRESENT | PAGE_WRITABLE | PAGE_USER;
return ptr;
}
return NULL;
}
static HalMappingError map_page(uint64_t* table, uintptr_t vaddr, uintptr_t paddr, uint32_t flag) {
if (!aligned$(vaddr, page_size())) {
return HAL_MAP_VIRT_UNALIGNED;
}
if (!aligned$(paddr, page_size())) {
return HAL_MAP_PHYS_UNALIGNED;
}
size_t pml1_entry = PMLX_GET_INDEX(vaddr, 0);
size_t pml2_entry = PMLX_GET_INDEX(vaddr, 1);
size_t pml3_entry = PMLX_GET_INDEX(vaddr, 2);
size_t pml4_entry = PMLX_GET_INDEX(vaddr, 3);
uintptr_t* pml3 = paging_get_pml_alloc(table, pml4_entry, true);
if (pml3 == NULL) {
return HAL_MAP_COULDNT_ALLOCATE;
}
if (mapping_page_size == gib$(1) && flag & PAGE_HUGE) {
pml3[pml3_entry] = paddr | flag;
return HAL_MAP_OK;
}
uintptr_t* pml2 = paging_get_pml_alloc(pml3, pml3_entry, true);
if (pml3 == NULL) {
return HAL_MAP_COULDNT_ALLOCATE;
}
if (flag & PAGE_HUGE) {
pml2[pml2_entry] = paddr | flag;
return HAL_MAP_OK;
}
uintptr_t* pml1 = paging_get_pml_alloc(pml2, pml2_entry, true);
if (pml1 == NULL) {
return HAL_MAP_COULDNT_ALLOCATE;
}
pml1[pml1_entry] = paddr | flag;
return HAL_MAP_OK;
}
HalMappingError hal_map_page(HalPage page, uintptr_t virt, uintptr_t phys, size_t length, uint8_t flags) {
size_t len = align_up$(length, page_size());
uintptr_t aligned_virt = align_down$(virt, page_size());
uintptr_t aligned_phys = align_down$(phys, page_size());
uint32_t arch_flags = transform_flags(flags);
for (size_t i = 0; i < len; i += page_size()) {
HalMappingError err = map_page(page.ptr, aligned_virt + i, aligned_phys + i, arch_flags);
if (err != HAL_MAP_OK) {
return err;
}
}
return HAL_MAP_OK;
}
void paging_init(void) {
Allocator pmm = pmm_allocator();
kernel_page_table = pmm.alloc(&pmm, page_size());
if (kernel_page_table == NULL) [[clang::unlikely]] {
panic$("Couldn't allocate kernel page table");
}
memset(kernel_page_table, 0, page_size());
}

View file

@ -0,0 +1,26 @@
#pragma once
#include <stdint.h>
#define PAGE_GET_PHYS(x) (x & 0x000ffffffffff000)
#define PAGE_GET_FLAGS(x) (x & 0xfff)
#define PMLX_GET_INDEX(addr, level) (((uint64_t)addr & ((uint64_t)0x1ff << (12 + level * 9))) >> (12 + level * 9))
enum pml_fields : uint64_t {
PAGE_PRESENT = 1 << 0,
PAGE_WRITABLE = 1 << 1,
PAGE_USER = 1 << 2,
PAGE_WRITE_THROUGH = 1 << 3,
PAGE_NO_CACHE = 1 << 4,
PAGE_ACCESSED = 1 << 5,
PAGE_DIRTY = 1 << 6,
PAGE_HUGE = 1 << 7,
PAGE_GLOBAL = 1 << 8,
PAGE_NO_EXECUTE = 1 << 63,
};
struct [[gnu::packed]] _hal_page {
uint64_t* ptr;
};
void paging_init(void);

View file

@ -0,0 +1,46 @@
#include "regs.h"
static uint64_t read_cr(uint8_t n) {
uint64_t value = 0;
switch (n) {
case 0:
__asm__ volatile("mov %%cr0, %0" : "=r"(value));
break;
case 2:
__asm__ volatile("mov %%cr2, %0" : "=r"(value));
break;
case 3:
__asm__ volatile("mov %%cr3, %0" : "=r"(value));
break;
case 4:
__asm__ volatile("mov %%cr4, %0" : "=r"(value));
break;
}
return value;
}
static void write_cr(uint8_t n, uint64_t value) {
switch (n) {
case 0:
__asm__ volatile("mov %0, %%cr0" : : "r"(value) : "memory");
break;
case 3:
__asm__ volatile("mov %0, %%cr3" : : "r"(value) : "memory");
break;
case 4:
__asm__ volatile("mov %0, %%cr4" : : "r"(value) : "memory");
break;
default:
// CR1, CR2 are either invalid or read-only
break;
}
}
uint64_t __cr(size_t n, uint8_t no, uint64_t val) {
if (n == 1) {
return read_cr(no);
}
write_cr(no, val);
return 0;
}

View file

@ -0,0 +1,36 @@
#pragma once
#include <helpers/macro.h>
#include <stddef.h>
#include <stdint.h>
typedef struct [[gnu::packed]] {
uint64_t r15;
uint64_t r14;
uint64_t r13;
uint64_t r12;
uint64_t r11;
uint64_t r10;
uint64_t r9;
uint64_t r8;
uint64_t rbp;
uint64_t rdi;
uint64_t rsi;
uint64_t rdx;
uint64_t rcx;
uint64_t rbx;
uint64_t rax;
uint64_t intno;
uint64_t err;
uint64_t rip;
uint64_t cs;
uint64_t rflags;
uint64_t rsp;
uint64_t ss;
} Stackframe;
uint64_t __cr(size_t n, uint8_t no, uint64_t val);
#define cr$(...) __cr(GET_ARG_COUNT(__VA_ARGS__), __VA_ARGS__, 0)

6
src/libs/helpers/macro.h Normal file
View file

@ -0,0 +1,6 @@
#pragma once
#define GET_ARG_COUNT(__args...) \
__GET_ARG_COUNT(0, ##__args, 70, 69, 68, 67, 66, 65, 64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
#define __GET_ARG_COUNT(_0, _1_, _2_, _3_, _4_, _5_, _6_, _7_, _8_, _9_, _10_, _11_, _12_, _13_, _14_, _15_, _16_, _17_, _18_, _19_, _20_, _21_, _22_, _23_, _24_, _25_, _26_, _27_, _28_, _29_, _30_, _31_, _32_, _33_, _34_, _35_, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, _65, _66, _67, _68, _69, _70, count, ...) \
count