diff --git a/Makefile b/Makefile index 90b48e8..8336179 100644 --- a/Makefile +++ b/Makefile @@ -42,6 +42,9 @@ export BUILD_DRIVERS_HPET = 1 export BUILD_DRIVERS_AHCI = 1 +export BUILD_DRIVERS_GPT = 1 +export BUILD_DRIVERS_EXT2 = 1 + # End of options ifdef BUILD_DRIVERS_AHCI @@ -71,7 +74,7 @@ TEST_TARGETS := \ TEST_CANIDATES := $(patsubst $(OBJ_DIR)/test_%.a,test-%,$(TEST_TARGETS)) TEST_EXEC := $(basename $(TEST_TARGETS)) -COPY_DOC_TO := $(OBJ_DIR)/rootfs/doc/ModulOS/ +COPY_DOC_TO := $(OBJ_DIR)/rootfs/usr/share/doc/ModulOS/ SRC := $(shell find . -type f \( -name "*.c" -o -name "*.S" -o -name "*.h" \)) @@ -97,7 +100,7 @@ test-all: $(TEST_CANIDATES) .PHONY: $(TEST_CANIDATES) $(TEST_CANIDATES): test-%: $(OBJ_DIR)/test_% - ./$< + -./$< .PHONY: $(SUBDIRS) $(SUBDIRS): @@ -127,7 +130,7 @@ $(OBJ_DIR)/stub.img: | $(OBJ_DIR)/ parted -s $@ mklabel gpt parted -s $@ mkpart ESP fat32 4MiB 52MiB parted -s $@ set 1 esp on - parted -s $@ mkpart rootfs ext4 52MiB 100% + parted -s $@ mkpart rootfs ext2 52MiB 100% $(OBJ_DIR)/stub-esp.dummy: $(OBJ_DIR)/stub.img $(OBJ_DIR)/modulos $(OBJ_DIR)/esp.img mcopy -o -i $(OBJ_DIR)/esp.img $(OBJ_DIR)/modulos ::/ @@ -137,7 +140,7 @@ $(OBJ_DIR)/stub-esp.dummy: $(OBJ_DIR)/stub.img $(OBJ_DIR)/modulos $(OBJ_DIR)/esp # 54525952 is for 52MiB offset (512 * 1024 * 1024) # 1034240 is for 52MiB initial (4MiB align + 48MiB ESP) and 4MiB tail for gpt $(OBJ_DIR)/stub-fs.dummy: $(OBJ_DIR)/stub.img copy-doc - yes | mke2fs -L rootfs -E offset=54525952 -d $(OBJ_DIR)/rootfs/ -t ext4 \ + yes | mke2fs -L rootfs -E offset=54525952 -d $(OBJ_DIR)/rootfs/ -t ext2 \ -b 4096 $< 1034240 touch $@ diff --git a/boot/multiboot2/boot.S b/boot/multiboot2/boot.S index 31f0fb1..a69fec4 100644 --- a/boot/multiboot2/boot.S +++ b/boot/multiboot2/boot.S @@ -231,6 +231,10 @@ ktransfer: pushq $0 pushq $0 call multiboot2_init + movq init_stack_vaddr, %rsp + addq $0x5000, %rsp + pushq $0 + pushq $0 jmp kentry .section .data.boot @@ -330,3 +334,10 @@ stack: .rept STACK_SIZE .byte 0 .endr + + /* pointer to guarded stack */ +.globl init_stack_vaddr + init_stack_vaddr: .quad 0 + +.globl init_stack_paddr + init_stack_paddr: .quad 0 diff --git a/boot/multiboot2/init.c b/boot/multiboot2/init.c index b9fae84..fc1f7b6 100644 --- a/boot/multiboot2/init.c +++ b/boot/multiboot2/init.c @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -104,6 +105,9 @@ extern volatile struct gdt_t gdt[GDT_NUM_ENTRIES]; static volatile struct mb2_tag_t* memmap_tag; +extern uint64_t init_stack_paddr; +extern uint64_t init_stack_vaddr; + static void first_segment(uint64_t* handle) { *handle = 0; } @@ -202,6 +206,15 @@ void multiboot2_init(struct mb2_info_t* info) { i = (i + 7) & ~MOD8_MASK; } + init_stack_paddr = mm_alloc_p(PAGE_SIZE_4K * 4); + init_stack_vaddr = mm_alloc_v(PAGE_SIZE_4K * 5); + + paging_map(init_stack_vaddr + PAGE_SIZE_4K * 1, init_stack_paddr + PAGE_SIZE_4K * 0, PAGE_PRESENT | PAGE_RW, PAGE_4K); + paging_map(init_stack_vaddr + PAGE_SIZE_4K * 2, init_stack_paddr + PAGE_SIZE_4K * 1, PAGE_PRESENT | PAGE_RW, PAGE_4K); + paging_map(init_stack_vaddr + PAGE_SIZE_4K * 3, init_stack_paddr + PAGE_SIZE_4K * 2, PAGE_PRESENT | PAGE_RW, PAGE_4K); + paging_map(init_stack_vaddr + PAGE_SIZE_4K * 4, init_stack_paddr + PAGE_SIZE_4K * 3, PAGE_PRESENT | PAGE_RW, PAGE_4K); + paging_install_guard(init_stack_vaddr); + boot_context.gdt = &gdt; return; } diff --git a/drivers/Makefile b/drivers/Makefile index d39735e..8b886e4 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -43,6 +43,14 @@ ifdef BUILD_DRIVERS_SATA $(call add_directory,sata,SATA) endif +ifdef BUILD_DRIVERS_GPT +$(call add_directory,gpt,GPT) +endif + +ifdef BUILD_DRIVERS_EXT2 +$(call add_directory,ext2,EXT2) +endif + include $(SRC_TREE_ROOT)/scripts/Makefile.kcflags include $(SRC_TREE_ROOT)/scripts/Makefile.targets diff --git a/drivers/ahci/ahci.c b/drivers/ahci/ahci.c index 153667f..bcea8c2 100644 --- a/drivers/ahci/ahci.c +++ b/drivers/ahci/ahci.c @@ -77,8 +77,8 @@ #define SSTS_DET_MASK 0x000Fu #define SSTS_DET_EST 0x0003u -#define CL_SIZE 256 -#define FIS_SIZE 1024 +#define CL_SIZE 1024 +#define FIS_SIZE 256 #define SLOT_NO_SLOT 0xFF @@ -174,11 +174,12 @@ static uint8_t find_slot(struct ahci_t* ahci, uint32_t port) { read = hba_read(ahci, PXCI_OFF(port)); for (slot = 0; slot < ahci->num_com_slots; slot++) { - if (!(ahci->used_com && (1u << slot)) && !(read && (1u << slot))) { + if (!(ahci->used_com & (1u << slot)) && !(read & (1u << slot))) { return slot; } } + //TODO: assert valid slot before using return SLOT_NO_SLOT; } @@ -245,7 +246,7 @@ static enum disk_error_t ahci_read_lba(void* cntx, void* buffer, uint64_t lba, u port_clear_errors(ahci, port); - kmemset(&ahci->ports[port]->recv_fis, 0, sizeof(struct ahci_recv_fis_t)); + kmemset(ahci->ports[port]->recv_fis, 0, sizeof(struct ahci_recv_fis_t)); ahci->ports[port]->com_list[slot].flg = 5; ahci->ports[port]->com_list[slot].prdtl = 1; @@ -371,7 +372,7 @@ static enum disk_error_t ahci_write_lba(void* cntx, void* buffer, uint64_t lba, port_clear_errors(ahci, port); - kmemset(&ahci->ports[port]->recv_fis, 0, sizeof(struct ahci_recv_fis_t)); + kmemset(ahci->ports[port]->recv_fis, 0, sizeof(struct ahci_recv_fis_t)); ahci->ports[port]->com_list[slot].flg = 5 | SATA_FIS_CMD_W; ahci->ports[port]->com_list[slot].prdtl = 1; @@ -453,7 +454,7 @@ static enum disk_error_t ahci_flush_cache(void* cntx) { port_clear_errors(ahci, port); - kmemset(&ahci->ports[port]->recv_fis, 0, sizeof(struct ahci_recv_fis_t)); + kmemset(ahci->ports[port]->recv_fis, 0, sizeof(struct ahci_recv_fis_t)); ahci->ports[port]->com_list[slot].flg = 5; ahci->ports[port]->com_list[slot].prdtl = 0; @@ -534,7 +535,7 @@ static void port_identify(struct ahci_t* ahci, uint32_t port) { port_clear_errors(ahci, port); - kmemset(&ahci->ports[port]->recv_fis, 0, sizeof(struct ahci_recv_fis_t)); + kmemset(ahci->ports[port]->recv_fis, 0, sizeof(struct ahci_recv_fis_t)); ahci->ports[port]->com_list[slot].flg = 5; ahci->ports[port]->com_list[slot].prdtl = 1; @@ -565,7 +566,7 @@ static void port_identify(struct ahci_t* ahci, uint32_t port) { lba48 = *(uint64_t*)&identity[100]; - logging_log_info("Found ATA drive %s 0x%lx", &model[0], lba48); + logging_log_debug("Found ATA drive %s 0x%lx", &model[0], lba48); paging_unmap((uint64_t)identity, PAGE_4K); diff --git a/drivers/apic/apic_init.c b/drivers/apic/apic_init.c index b0f4e5f..d337450 100644 --- a/drivers/apic/apic_init.c +++ b/drivers/apic/apic_init.c @@ -96,7 +96,8 @@ extern uint8_t gdt; extern uint8_t gdt_end; extern uint8_t kernel_pml4; -uint64_t* init_stacks; +uint64_t* init_stacks_paddr; +uint64_t* init_stacks_vaddr; volatile struct gdt_t(** ap_gdts)[GDT_NUM_ENTRIES]; volatile struct gdt_ptr_64_t** ap_gdt_ptr_64; uint8_t* ap_init_locks; @@ -113,13 +114,16 @@ void apic_init(void) { error_vector = idt_get_vector(); // install idts - idt_install(timer_vector, (uint64_t)apic_isr_timer, GDT_CODE_SEL, IST_SCHED, IDT_GATE_INT, 0); - idt_install(error_vector, (uint64_t)apic_isr_error, GDT_CODE_SEL, 0, IDT_GATE_INT, 0); + idt_install(timer_vector, (uint64_t)apic_isr_timer, GDT_CODE_SEL, IST_SCHED, IDT_GATE_TRP, 0); + idt_install(error_vector, (uint64_t)apic_isr_error, GDT_CODE_SEL, 0, IDT_GATE_TRP, 0); + + apic_init_shootdowns(num_apic); apic_init_ap(); // init stacks - init_stacks = kmalloc(sizeof(uint64_t*) * num_apic); + init_stacks_vaddr = kmalloc(sizeof(uint64_t) * num_apic); + init_stacks_paddr = kmalloc(sizeof(uint64_t) * num_apic); proc_data_ptr = kmalloc(sizeof(struct proc_data_t*) * num_apic); proc_data_ptr[0] = &bsp_proc_data; @@ -130,7 +134,42 @@ void apic_init(void) { for (--num_apic; num_apic; num_apic--) { proc_data_ptr[num_apic] = kmalloc(sizeof(struct proc_data_t)); - init_stacks[num_apic] = (uint64_t)kmalloc(INIT_STACK_SIZE) + INIT_STACK_SIZE; + init_stacks_vaddr[num_apic] = mm_alloc_v(PAGE_SIZE_4K * 5); + + if (!init_stacks_vaddr[num_apic]) { + logging_log_error("Failed to allocate stack"); + panic(PANIC_NO_MEM); + } + + init_stacks_paddr[num_apic] = mm_alloc_p(PAGE_SIZE_4K * 4); + + if (!init_stacks_paddr[num_apic]) { + logging_log_error("Failed to allocate stack"); + mm_free_v(init_stacks_vaddr[num_apic], PAGE_SIZE_4K * 5); + panic(PANIC_NO_MEM); + } + + paging_map( + init_stacks_vaddr[num_apic] + PAGE_SIZE_4K * 1, + init_stacks_paddr[num_apic] + PAGE_SIZE_4K * 0, + PAGE_PRESENT | PAGE_RW, + PAGE_4K); + paging_map( + init_stacks_vaddr[num_apic] + PAGE_SIZE_4K * 2, + init_stacks_paddr[num_apic] + PAGE_SIZE_4K * 1, + PAGE_PRESENT | PAGE_RW, + PAGE_4K); + paging_map( + init_stacks_vaddr[num_apic] + PAGE_SIZE_4K * 3, + init_stacks_paddr[num_apic] + PAGE_SIZE_4K * 2, + PAGE_PRESENT | PAGE_RW, + PAGE_4K); + paging_map( + init_stacks_vaddr[num_apic] + PAGE_SIZE_4K * 4, + init_stacks_paddr[num_apic] + PAGE_SIZE_4K * 3, + PAGE_PRESENT | PAGE_RW, + PAGE_4K); + paging_install_guard(init_stacks_vaddr[num_apic]); ap_gdts[num_apic] = kmalloc(sizeof(struct gdt_t[GDT_NUM_ENTRIES])); ap_gdt_ptr_64[num_apic] = kmalloc(sizeof(struct gdt_ptr_64_t)); @@ -242,6 +281,8 @@ void apic_init_ap(void) { apic_write_lve(APIC_REG_ERE, error_vector, APIC_LVT_MT_FIXED | APIC_LVT_TRG_EDGE, 0); + apic_register_barrier(apic_id); + // enable apic apic_write_reg(APIC_REG_ESR, 0); apic_write_reg(APIC_REG_SPR, PIC_SPURIOUS_VEC | APIC_ASE); // pic and apic spurious both only iret, so reuse @@ -311,7 +352,7 @@ void apic_start_ap(void) { (uint16_t)((uint64_t)&gdt_end - (uint64_t)&gdt - 1); // gdt64 ptr *(volatile uint64_t*)paging_ident(AP_ARB_BASE + 0xa) = (uint64_t)&gdt; *(volatile uint32_t*)paging_ident(AP_ARB_BASE + 0x12) = (uint32_t)(uint64_t)&kernel_pml4; - *(volatile uint64_t*)paging_ident(AP_ARB_BASE + 0x16) = (uint64_t)init_stacks; + *(volatile uint64_t*)paging_ident(AP_ARB_BASE + 0x16) = (uint64_t)init_stacks_vaddr; uint64_t handle; struct acpi_madt_ics_local_apic_t* local_apic; diff --git a/drivers/apic/ipi.c b/drivers/apic/ipi.c index cb7ee82..1b18e54 100644 --- a/drivers/apic/ipi.c +++ b/drivers/apic/ipi.c @@ -15,19 +15,64 @@ * along with this program. If not, see */ +#include + #include #include +#include #include +#include +#include +#include +#include + +#include -#define ICR_LEVEL 0x8000 -#define ICR_ASSERT 0x4000 -#define ICR_DS 0x1000 -#define ICR_LO_INIT 0x0500 -#define ICR_LO_SIPI (0x0600 | AP_ENTRY_PAGE) +#define ICR_LEVEL 0x8000u +#define ICR_ASSERT 0x4000u +#define ICR_DS 0x1000u +#define ICR_LO_INIT 0x0500u +#define ICR_LO_SIPI (0x0600u | AP_ENTRY_PAGE) #define ICR_PID_SHFT 24 +static uint8_t tlb_shootdown_lock; +static uint8_t* registered_barrier; +static volatile uint8_t* tlb_shootdown_barrier = 0; +static uint8_t tlb_shootdown_vector; +static uint8_t barrier_len; +static volatile uint64_t tlb_shootdown_addr; + +struct shootdown_node_t { + struct shootdown_node_t* next; + uint8_t apic_id; +}; + +static struct shootdown_node_t* shootdown_list; + +static uint8_t ipi_lock; + +void apic_ipi_init(void) { + lock_init(&ipi_lock); +} + +void apic_init_shootdowns(uint8_t num_apic) { + lock_init(&tlb_shootdown_lock); + + registered_barrier = kmalloc(sizeof(uint8_t) * num_apic); + + kmemset(registered_barrier, 0, sizeof(uint8_t) * num_apic); + shootdown_list = 0; + + tlb_shootdown_barrier = kmalloc(sizeof(uint8_t) * num_apic); + barrier_len = num_apic; + + tlb_shootdown_vector = idt_get_vector(); + + idt_install(tlb_shootdown_vector, (uint64_t)apic_isr_tlb_shootdown, GDT_CODE_SEL, 0, IDT_GATE_INT, 0); +} + void apic_wait_for_ipi(void) { while (apic_read_reg(APIC_REG_ICL) & ICR_DS) { cpu_pause(); @@ -35,16 +80,87 @@ void apic_wait_for_ipi(void) { } void apic_send_ipi_init_set(uint8_t apic_id) { + lock_acquire(&ipi_lock); + apic_wait_for_ipi(); apic_write_reg(APIC_REG_ICH, (uint32_t)apic_id << ICR_PID_SHFT); apic_write_reg(APIC_REG_ICL, ICR_LO_INIT | ICR_LEVEL | ICR_ASSERT ); + lock_release(&ipi_lock); } void apic_send_ipi_init_clear(uint8_t apic_id) { + lock_acquire(&ipi_lock); + apic_wait_for_ipi(); apic_write_reg(APIC_REG_ICH, (uint32_t)apic_id << ICR_PID_SHFT); apic_write_reg(APIC_REG_ICL, ICR_LO_INIT | ICR_LEVEL); + lock_release(&ipi_lock); } void apic_send_ipi_sipi(uint8_t apic_id) { + lock_acquire(&ipi_lock); + apic_wait_for_ipi(); apic_write_reg(APIC_REG_ICH, (uint32_t)apic_id << ICR_PID_SHFT); apic_write_reg(APIC_REG_ICL, ICR_LO_SIPI); + lock_release(&ipi_lock); +} + +static inline uint8_t check_barrier(volatile uint8_t* barrier) { + uint8_t i; + for (i = 0; i < barrier_len; i++) { + if (barrier[i]) { + return 1; + } + } + + return 0; +} + +void apic_tlb_shootdown(uint64_t vaddr) { + struct shootdown_node_t* node; + if (!tlb_shootdown_barrier) { + return; + } + + lock_acquire(&tlb_shootdown_lock); + + uint8_t i; + for (i = 0; i < barrier_len; i++) { + tlb_shootdown_barrier[i] = registered_barrier[i]; + } + + tlb_shootdown_addr = vaddr; + + + for (node = shootdown_list; node; node = node->next) { + lock_acquire(&ipi_lock); + apic_wait_for_ipi(); + apic_wait_for_ipi(); + apic_write_reg(APIC_REG_ICH, (uint32_t)node->apic_id << ICR_PID_SHFT); + apic_write_reg(APIC_REG_ICL, tlb_shootdown_vector | ICR_ASSERT); + lock_release(&ipi_lock); + } + + while (check_barrier(tlb_shootdown_barrier)) cpu_pause(); + + lock_release(&tlb_shootdown_lock); +} + +void apic_tlb_shootdown_dispatch(void) { + cpu_invlpg(tlb_shootdown_addr); + + tlb_shootdown_barrier[proc_data_get()->arb_id] = 0; + + apic_write_reg(APIC_REG_EOI, APIC_EOI); +} + +void apic_register_barrier(uint8_t apic_id) { + struct shootdown_node_t* node = kmalloc(sizeof(struct shootdown_node_t)); + node->apic_id = apic_id; + + lock_acquire(&tlb_shootdown_lock); + + node->next = shootdown_list; + shootdown_list = node; + registered_barrier[proc_data_get()->arb_id] = 1; + + lock_release(&tlb_shootdown_lock); } diff --git a/drivers/apic/isr.S b/drivers/apic/isr.S index f13430e..ea239b5 100644 --- a/drivers/apic/isr.S +++ b/drivers/apic/isr.S @@ -35,6 +35,40 @@ pushq %rbp movq %rsp, %rdi jmp process_preempt_entry +.globl apic_isr_tlb_shootdown +apic_isr_tlb_shootdown: +pushq %rax +pushq %rcx +pushq %rdx +pushq %rsi +pushq %rdi +pushq %r8 +pushq %r9 +pushq %r10 +pushq %r11 +pushq %rbp + +movq %rsp, %rbp +movq %rsp, %rax +andq $0xF, %rax +jz .aligned_tlb +subq %rax, %rsp +.aligned_tlb: +call apic_tlb_shootdown_dispatch +movq %rbp, %rsp + +popq %rbp +popq %r11 +popq %r10 +popq %r9 +popq %r8 +popq %rdi +popq %rsi +popq %rdx +popq %rcx +popq %rax +iretq + .globl apic_isr_error apic_isr_error: pushq %rax diff --git a/drivers/disk/disk.c b/drivers/disk/disk.c index b6dacbe..b438e65 100644 --- a/drivers/disk/disk.c +++ b/drivers/disk/disk.c @@ -20,6 +20,10 @@ #include +#ifdef GPT +#include +#endif /* GPT */ + #include #include @@ -58,6 +62,12 @@ struct disk_t* disk_add(void* cntx, disk_lba_read_t read, disk_lba_write_t write disk_list = disk; lock_release(&disk_lock); +#ifdef GPT + if (gpt_find_partitions(disk)) { + return disk; + } +#endif /* GPT */ + return disk; } @@ -86,3 +96,7 @@ enum disk_error_t disk_write(struct disk_t* disk, void* buffer, uint64_t lba, ui enum disk_error_t disk_flush(struct disk_t* disk) { return disk->flush(disk->cntx); } + +uint64_t disk_get_id(struct disk_t* disk) { + return disk->id; +} diff --git a/drivers/ext2/ext2.c b/drivers/ext2/ext2.c new file mode 100644 index 0000000..bc8e6b9 --- /dev/null +++ b/drivers/ext2/ext2.c @@ -0,0 +1,678 @@ +/* ext2.c - Second Extended File System driver implementation */ +/* Copyright (C) 2026 Ebrahim Aleem +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see +*/ + +#include + +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#define SUPERBLOCK_LBA 2 +#define SUPERBLOCK_SECTORS 2 + +#define DIRECT_BLOCKS 12 +#define INDIR_1 12 +#define INDIR_2 13 +#define INDIR_3 14 + +#define BLOCK_OK 0 +#define BLOCK_RETRY 1 +#define BLOCK_LAST 2 +#define BLOCK_ERROR 3 + +#define EXT2_FT_DIR 2 + +#define EXT2_SUPER_MAGIC 0xEF53 + +#define EXT2_ROOT_INO 2 + +#define EXT2_S_IFREG 0x8000 +#define EXT2_S_IFDIR 0x4000 + +#define DIRLS_SET 0x8000000000000000 + +struct ext2_superblock_t { + uint32_t s_inodes_count; + uint32_t s_blocks_count; + uint32_t s_r_blocks_count; + uint32_t s_free_blocks_count; + uint32_t s_free_inodes_count; + uint32_t s_first_data_block; + uint32_t s_log_block_size; + uint32_t s_log_frag_size; + uint32_t s_blocks_per_group; + uint32_t s_frags_per_group; + uint32_t s_inodes_per_group; + uint32_t s_mtime; + uint32_t s_wtime; + uint16_t s_mnt_count; + uint16_t s_max_mnt_count; + uint16_t s_magic; + uint16_t s_state; + uint16_t s_errors; + uint16_t s_minor_rev_level; + uint32_t s_lastcheck; + uint32_t s_checkinterval; + uint32_t s_creator_os; + uint32_t s_rev_level; + uint16_t s_def_resuid; + uint16_t s_def_resgid; + + /* ext2_dynamic_rev */ + uint32_t s_first_ino; + uint16_t s_inode_size; + uint16_t s_block_group_nr; + uint32_t s_feature_compat; + uint32_t s_feature_incompat; + uint32_t s_feature_ro_compat; + uint8_t s_uuid[16]; + uint8_t s_volume_name[16]; + uint8_t s_last_mounted[64]; + uint32_t s_algo_bitmap; + + /* perf. hints */ + uint8_t s_prealloc_blocks; + uint8_t s_prealloc_dir_blocks; + uint16_t align; + + /* journaling */ + uint8_t s_journal_uuid[16]; + uint32_t s_hournal_inum; + uint32_t s_journal_dev; + uint32_t s_last_orphan; + + /* dir. indexing */ + uint32_t s_hash_seed[4]; + uint8_t s_def_hash_version; + uint8_t resv0[3]; + + /* other */ + uint32_t s_default_mount_options; + uint32_t s_first_meta_bg; + uint8_t resv1[760]; +} __attribute__((packed)); + +struct ext2_bg_desc_t { + uint32_t bg_block_bitmap; + uint32_t bg_inode_bitmap; + uint32_t bg_inode_table; + uint16_t bg_free_blocks_count; + uint16_t bg_free_inodes_count; + uint16_t bg_used_dirs_count; + uint16_t bg_pad; + uint8_t bg_reserved[12]; +} __attribute__((packed)); + +struct ext2_inode_t { + uint16_t i_mode; + uint16_t i_uid; + uint32_t i_size; + uint32_t i_atime; + uint32_t i_ctime; + uint32_t i_mtime; + uint32_t i_dtime; + uint16_t i_gid; + uint16_t i_links_count; + uint32_t i_blocks; + uint32_t i_flags; + uint32_t i_osd1; + uint32_t i_block[15]; + uint32_t i_generation; + uint32_t i_file_acl; + uint32_t i_dir_acl; + uint32_t i_faddr; + uint8_t i_osd2[12]; +} __attribute__((packed)); + +struct ext2_ll_dir_entry_t { + uint32_t inode; + uint16_t rec_len; + uint8_t name_len; + uint8_t file_type; + uint8_t name[]; +} __attribute__((packed)); + +_Static_assert(sizeof(struct ext2_superblock_t) == 1024, "Bad ext2 superblock size"); +_Static_assert(sizeof(struct ext2_bg_desc_t) == 32, "Bad ext2 bg descriptor size"); +_Static_assert(sizeof(struct ext2_inode_t) == 128, "Bad exte2 inode size"); + +struct ext2_t { + uint64_t start_lba; + uint64_t end_lba; + struct ext2_superblock_t* superblock; + struct ext2_bg_desc_t* bgdt; + struct disk_t* disk; +}; + +struct ext2_inode_handle_t { + struct ext2_t* ext2; + uint64_t inode_index; + uint64_t seek; + uint64_t seek_block; +}; + +struct ext2_block_track_t { + struct ext2_inode_handle_t* handle; + struct ext2_inode_t* inode; + uint64_t off; + uint64_t block; +}; + +static uint8_t label_rootfs[16] = {'r', 'o', 'o', 't', 'f', 's', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +static void* read_block(uint64_t block, struct ext2_t* ext2) { + const uint64_t block_size = 1024u << ext2->superblock->s_log_block_size; + + void* buffer = kmalloc(block_size); + + const uint64_t lba = ext2->start_lba + block * block_size / SECTOR_SIZE; + + if (lba > ext2->end_lba) { + logging_log_error("Attempt to read beyond ext2 end lba (0x%x > 0x%x)", lba, ext2->end_lba); + } + + if (disk_read(ext2->disk, buffer, lba, (uint16_t)(block_size / SECTOR_SIZE)) != DISK_OK) { + logging_log_error("Failed to read"); + kfree(buffer); + return 0; + } + + return buffer; +} + +static uint8_t get_inode(const struct ext2_inode_handle_t* inode_handle, struct ext2_inode_t* inode) { + const struct ext2_superblock_t* superblock = inode_handle->ext2->superblock; + const struct ext2_bg_desc_t* bgdt = inode_handle->ext2->bgdt; + + const uint64_t block_size = 1024u << superblock->s_log_block_size; + const uint64_t block_group = (inode_handle->inode_index - 1) / superblock->s_inodes_per_group; + const uint64_t lcl_inode_idx = (inode_handle->inode_index - 1) % superblock->s_inodes_per_group; + const uint64_t lcl_inode_off = lcl_inode_idx * superblock->s_inode_size; + const uint64_t lcl_inode_blk = lcl_inode_off / block_size; + const uint64_t inode_off = lcl_inode_off % block_size; + + const uint64_t inode_table_block = bgdt[block_group].bg_inode_table + lcl_inode_blk; + void* buffer = read_block(inode_table_block, inode_handle->ext2); + + if (!buffer) { + logging_log_error("Failed to read inode %lu", inode_handle->inode_index); + kfree(buffer); + return 1; + } + + *inode = *(struct ext2_inode_t*)((uint64_t)buffer + inode_off); + kfree(buffer); + return 0; +} + +static uint8_t get_block_index(struct ext2_block_track_t* bt, uint64_t* index) { + uint64_t block = bt->block; + struct ext2_inode_t* inode = bt->inode; + struct ext2_t* ext2 = bt->handle->ext2; + + + if (block < DIRECT_BLOCKS) { + *index = inode->i_block[block]; + + if (!*index) { + bt->block += 1; + return BLOCK_RETRY; + } + + return BLOCK_OK; + } + + block -= DIRECT_BLOCKS; + + const uint64_t block_size = 1024u << ext2->superblock->s_log_block_size; + const uint64_t indir1 = block_size / sizeof(uint32_t); + + uint32_t* buffer; + + if (block < indir1) { + *index = inode->i_block[INDIR_1]; + + if (!*index) { + bt->block += indir1; + return BLOCK_RETRY; + } + + buffer = read_block(*index, ext2); + + if (!buffer) { + return BLOCK_ERROR; + } + + *index = buffer[block]; + kfree(buffer); + + if (!*index) { + bt->block += 1; + return BLOCK_RETRY; + } + + return BLOCK_OK; + } + + block -= indir1; + + const uint64_t indir2 = indir1 * indir1; + + + if (block < indir2) { + *index = inode->i_block[INDIR_2]; + + if (!*index) { + bt->block += indir2; + return BLOCK_RETRY; + } + + buffer = read_block(*index, ext2); + + if (!buffer) { + return BLOCK_ERROR; + } + + *index = buffer[block / indir1]; + kfree(buffer); + + if (!*index) { + bt->block += indir1; + return BLOCK_RETRY; + } + + buffer = read_block(*index, ext2); + + if (!buffer) { + return BLOCK_ERROR; + } + + *index = buffer[block % indir1]; + kfree(buffer); + + if (!*index) { + bt->block += 1; + return BLOCK_RETRY; + } + + return BLOCK_OK; + } + + block -= indir2; + + const uint64_t indir3 = indir2 * indir1; + + if (block < indir3) { + *index = inode->i_block[INDIR_3]; + + if (!*index) { + bt->block += indir3; + return BLOCK_RETRY; + } + + buffer = read_block(*index, ext2); + + if (!buffer) { + return BLOCK_ERROR; + } + + *index = buffer[block / indir2]; + kfree(buffer); + + if (!*index) { + bt->block += indir2; + return BLOCK_RETRY; + } + + buffer = read_block(*index, ext2); + + if (!buffer) { + return BLOCK_ERROR; + } + + *index = buffer[(block % indir2) / indir1]; + kfree(buffer); + + if (!*index) { + bt->block += indir1; + return BLOCK_RETRY; + } + + buffer = read_block(*index, ext2); + + if (!buffer) { + return BLOCK_ERROR; + } + + *index = buffer[block % indir1]; + kfree(buffer); + + if (!*index) { + bt->block += 1; + return BLOCK_RETRY; + } + + return BLOCK_OK; + } + + return BLOCK_LAST; +} + +static inline size_t path_entry_len(char* path) { + size_t len = 0; + + for (; *path && *path != '/'; path++) { + len++; + } + + return len; +} + + +static struct file_handle_t* ext2_open(struct mount_cntx_t* cntx, char* path) { + struct ext2_inode_t inode; + size_t path_len; + struct ext2_block_track_t track; + uint64_t block_index; + uint8_t cntrl = 0; + + + track.handle = kmalloc(sizeof(struct ext2_inode_handle_t)); + + if (!track.handle) { + return 0; + } + + track.handle->ext2 = (struct ext2_t*)cntx; + track.handle->inode_index = EXT2_ROOT_INO; + track.handle->seek = 0; + track.handle->seek_block = 0; + + const struct ext2_superblock_t* superblock = track.handle->ext2->superblock; + const uint64_t block_size = 1024u << superblock->s_log_block_size; + const uint64_t size = (uint64_t)inode.i_size | ((uint64_t)inode.i_dir_acl << 32); + void* buffer; + + while (*path && cntrl != 2) { + path_len = path_entry_len(path); + + if (get_inode(track.handle, &inode)) { + return 0; + } + + track.inode = &inode; + track.block = 0; + track.off = 0; + + cntrl = 1; + while (cntrl == 1) { + if (track.block * block_size > size) { + cntrl = 2; + break; + } + + if (track.off >= block_size) { + track.block++; + track.off = 0; + } + + switch (get_block_index(&track, &block_index)) { + case BLOCK_OK: + buffer = read_block(block_index, track.handle->ext2); + if (!buffer) { + kfree(track.handle); + return 0; + } + + struct ext2_ll_dir_entry_t* entry = (struct ext2_ll_dir_entry_t*)((uint64_t)buffer + track.off); + + if (entry->inode != 0) { + if (entry->name_len == path_len && !kmemcmp(entry->name, path, path_len)) { + path += path_len; + + // handle non EOS + if (*path == '/') { + path++; + } + + track.handle->inode_index = entry->inode; + + if (*path && entry->file_type != EXT2_FT_DIR) { + cntrl = 2; // file not found + } + else { + cntrl = 0; + } + + kfree(buffer); + break; + } + } + + track.off += entry->rec_len; + kfree(buffer); + __attribute__((fallthrough)); + case BLOCK_RETRY: + continue; + case BLOCK_LAST: + cntrl = 2; // file not found + break; + case BLOCK_ERROR: + default: + kfree(track.handle); + return 0; + } + } + } + + if (*path || cntrl == 2) { + kfree(track.handle); // file not found + return 0; + } + + return (struct file_handle_t*)track.handle; +} + +static void ext2_close(struct file_handle_t* handle) { + kfree(handle); +} + +static enum file_status_t ext2_stat(struct file_handle_t* handle, struct file_info_t* info) { + struct ext2_inode_t inode; + struct ext2_inode_handle_t* inode_handle = (struct ext2_inode_handle_t*)handle; + + if (!inode_handle || get_inode(inode_handle, &inode)) { + return FILE_ERROR; + } + + if (inode.i_mode & EXT2_S_IFREG) { + info->type = FILE_TYPE_REG; + } + else if (inode.i_mode & EXT2_S_IFDIR) { + info->type = FILE_TYPE_DIR; + } + else { + return FILE_NO_SUPPORT; + } + + info->size = (uint64_t)inode.i_size | ((uint64_t)inode.i_dir_acl << 32); + + return FILE_OK; +} + +static size_t ext2_read(struct file_handle_t* handle, void* buffer, size_t count) { + struct ext2_inode_t inode; + struct ext2_inode_handle_t* inode_handle = (struct ext2_inode_handle_t*)handle; + struct ext2_block_track_t track; + uint8_t* block_buffer = 0; + + if (!inode_handle || get_inode(inode_handle, &inode)) { + return 0; + } + + const uint64_t size = (uint64_t)inode.i_size | ((uint64_t)inode.i_dir_acl << 32); + const struct ext2_superblock_t* superblock = inode_handle->ext2->superblock; + const uint64_t block_size = 1024u << superblock->s_log_block_size; + + size_t read = 0; + uint64_t index; + uint64_t write_seek = 0; + size_t write_len; + + while (count) { + track.block = inode_handle->seek_block; + track.inode = &inode; + track.handle = inode_handle; + + if (track.block * block_size > size) { + break; + } + + write_len = block_size; // default full block + + if (count < block_size) { + // read partial, only requested + write_len = count; + } + + if (write_len + inode_handle->seek > block_size) { + // read partial, not past block + write_len = block_size - inode_handle->seek; + } + + switch (get_block_index(&track, &index)) { + case BLOCK_OK: + block_buffer = read_block(index, inode_handle->ext2); + + if (!block_buffer) { + return read; + } + + kmemcpy((uint8_t*)buffer + write_seek, block_buffer + inode_handle->seek, write_len); + + kfree(block_buffer); + break; + case BLOCK_RETRY: + kmemset((uint8_t*)buffer + write_seek, 0, write_len); + break; + default: + return read; + } + + write_seek += write_len; + inode_handle->seek += write_len; + count -= write_len; + read += write_len; + + if (inode_handle->seek == block_size) { + inode_handle->seek = 0; + inode_handle->seek_block++; + } + } + + return read; +} + +uint8_t ext2_attempt_init(struct disk_t* disk, uint64_t start_lba, uint64_t end_lba) { + struct ext2_superblock_t* superblock = kmalloc(sizeof(struct ext2_superblock_t)); + struct ext2_bg_desc_t* bgdt; + uint64_t bgdt_size, adj, bgdt_start_lba; + + if (disk_read(disk, superblock, start_lba + SUPERBLOCK_LBA, SUPERBLOCK_SECTORS) != DISK_OK) { + logging_log_error("Failed to read disk %lu @ %u/%u", disk_get_id(disk), SUPERBLOCK_LBA, SUPERBLOCK_SECTORS); + kfree(superblock); + return 0; + } + + if (superblock->s_magic != EXT2_SUPER_MAGIC) { + kfree(superblock); + return 0; + } + + bgdt_start_lba = start_lba + (1u << (1 + superblock->s_log_block_size)); + + bgdt_size = (1 + superblock->s_blocks_count / superblock->s_blocks_per_group) * sizeof(struct ext2_bg_desc_t); + adj = bgdt_size % SECTOR_SIZE; + if (adj) { + bgdt_size += SECTOR_SIZE - adj; + } + + bgdt = kmalloc(bgdt_size); + if (disk_read(disk, bgdt, bgdt_start_lba, (uint16_t)(bgdt_size / SECTOR_SIZE)) != DISK_OK) { + logging_log_error("Failed to read disk %lu @ %u/%u", disk_get_id(disk), SUPERBLOCK_LBA, SUPERBLOCK_SECTORS); + kfree(superblock); + kfree(bgdt); + return 0; + } + + logging_log_info("Found ext2 filesystem %16.16s", superblock->s_volume_name); + + struct ext2_t* ext2 = kmalloc(sizeof(struct ext2_t)); + ext2->start_lba = start_lba; + ext2->end_lba = end_lba; + ext2->superblock = superblock; + ext2->bgdt = bgdt; + ext2->disk = disk; + + logging_log_debug("ext2 blocks: 0x%x x 0x%x (0x%lX)", + 1024u << superblock->s_log_block_size, superblock->s_blocks_count, + (uint64_t)(1024u << superblock->s_log_block_size) * (uint64_t)superblock->s_blocks_count); + + if (!kmemcmp(label_rootfs, superblock->s_volume_name, sizeof(superblock->s_volume_name))) { + if (fs_mount( + "/", + (struct mount_cntx_t*)ext2, + ext2_open, + ext2_close, + ext2_stat, + ext2_read + ) != FILE_OK) { + logging_log_error("Failed to mount rootfs"); + panic(PANIC_STATE); + } + + struct file_info_t stat_buf; + enum file_status_t sts; + + struct fs_handle_t* root_handle = fs_open("/"); + if (!root_handle) { + logging_log_error("Failed to open root directory"); + } + + if ((sts = fs_stat(root_handle, &stat_buf)) != FILE_OK) { + logging_log_error("Failed to stat root directory %u", (uint32_t)sts); + } + + logging_log_debug("Root directory %u (%u)", stat_buf.size, stat_buf.type); + + fs_close(root_handle); + } + + //TODO: mount other volumes + + return 1; +} diff --git a/drivers/gpt/gpt.c b/drivers/gpt/gpt.c new file mode 100644 index 0000000..524f100 --- /dev/null +++ b/drivers/gpt/gpt.c @@ -0,0 +1,132 @@ +/* gpt.c - GUID partition table driver */ +/* Copyright (C) 2026 Ebrahim Aleem +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see +*/ + +#include + +#include + +#include + +#ifdef EXT2 +#include +#endif /* EXT2 */ + +#include +#include + +#include +#include + +#define GPT_LBA 1 +#define GPT_SIG "EFI PART" + +struct gpt_partition_table_t { + uint8_t sig[8]; + uint32_t rev; + uint32_t header_size; + uint32_t crc32; + uint32_t resv0; + uint64_t curr_lba; + uint64_t alt_lba; + uint64_t first_block; + uint64_t last_block; + uint8_t guid[16]; + uint64_t partition_array_lba; + uint32_t partition_array_entry_count; + uint32_t partition_array_entry_size; + uint32_t partition_array_crc32; +} __attribute__((packed)); + +struct gpt_partition_entry_t { + uint8_t part_type[16]; + uint8_t guid[16]; + uint64_t start_lba; + uint64_t end_lba; + uint64_t attr; + uint8_t name[]; +} __attribute__((packed)); + +uint8_t gpt_find_partitions(struct disk_t* disk) { + struct gpt_partition_table_t* gpt = kmalloc(SECTOR_SIZE); + uint32_t crc; + uint64_t partition_array_size; + void* partition_array_base; + struct gpt_partition_entry_t* entry; + uint8_t j; + uint32_t i; + + if (disk_read(disk, gpt, GPT_LBA, 1) != DISK_OK) { + logging_log_error("Failed to read gpt lba of disk %lu", disk_get_id(disk)); + kfree(gpt); + return 0; + } + + if (kmemcmp(gpt->sig, GPT_SIG, sizeof(gpt->sig))) { + kfree(gpt); + return 0; + } + + logging_log_debug("Found GPT on disk %lu", disk_get_id(disk)); + + crc = gpt->crc32; + gpt->crc32 = 0; + + if (crc32_ansi(gpt, sizeof(*gpt)) != crc) { + logging_log_error("Bad checksum for GPT. Skipping disk"); + kfree(gpt); + return 0; + } + + partition_array_size = gpt->partition_array_entry_count * gpt->partition_array_entry_size; + partition_array_base = kmalloc(SECTOR_SIZE * ((partition_array_size / SECTOR_SIZE) + 1)); + + if (disk_read(disk, partition_array_base, gpt->partition_array_lba, + (uint16_t)(partition_array_size / SECTOR_SIZE) + 1) != DISK_OK) { + logging_log_error("Failed to read GPT partition array. Skipping disk"); + kfree(gpt); + kfree(partition_array_base); + return 0; + } + + if(crc32_ansi((void*)partition_array_base, partition_array_size) != gpt->partition_array_crc32) { + logging_log_error("Bad checksum for GPT partition array. Skipping disk"); + kfree(gpt); + kfree(partition_array_base); + return 0; + } + + for (i = 0; i < gpt->partition_array_entry_count; i++) { + entry = (struct gpt_partition_entry_t*)((uint64_t)partition_array_base + i * gpt->partition_array_entry_size); + for (j = 0; j < 16; j++) { + if (entry->part_type[j]) { + logging_log_debug("Found GPT partition on %lu @ %u", disk_get_id(disk), i); + +#ifdef EXT2 + if (ext2_attempt_init(disk, entry->start_lba, entry->end_lba)) { + break; + } +#endif /* EXT2 */ + break; + } + } + } + + kfree(gpt); + kfree(partition_array_base); + + return 0; +} diff --git a/drivers/include/acpica/platform/acmodulos.h b/drivers/include/acpica/platform/acmodulos.h index dad0fd3..3902e70 100644 --- a/drivers/include/acpica/platform/acmodulos.h +++ b/drivers/include/acpica/platform/acmodulos.h @@ -172,6 +172,8 @@ #include #include +#include + #ifdef ACPI_USE_STANDARD_HEADERS #undef ACPI_USE_STANDARD_HEADERS #endif /* ACPI_USE_STANDARD_HEADERS */ @@ -210,7 +212,7 @@ static uint8_t _stub(void) { return 0; } -#define ACPI_FLUSH_CPU_CACHE() _stub() //TODO cpu_instr tlb invl +#define ACPI_FLUSH_CPU_CACHE() cpu_wbinvd(); #define ACPI_ACQUIRE_GLOBAL_LOCK(facsPtr, acc) acc = _stub() //TODO: sync #define ACPI_RELEASE_GLOBAL_LOCK(facsPtr, pen) pen = _stub() diff --git a/drivers/include/apic/ipi.h b/drivers/include/apic/ipi.h index a854b10..6d6e99e 100644 --- a/drivers/include/apic/ipi.h +++ b/drivers/include/apic/ipi.h @@ -22,9 +22,17 @@ #define AP_ENTRY_PAGE 8 +extern void apic_init_shootdowns(uint8_t num_apic); +extern void apic_ipi_init(void); + extern void apic_wait_for_ipi(void); extern void apic_send_ipi_init_set(uint8_t apic_id); extern void apic_send_ipi_init_clear(uint8_t apic_id); extern void apic_send_ipi_sipi(uint8_t apic_id); +extern void apic_tlb_shootdown(uint64_t vaddr); +extern void apic_tlb_shootdown_dispatch(void); + +extern void apic_register_barrier(uint8_t apic_id); + #endif /* DRIVERS_APIC_IPI_H */ diff --git a/drivers/include/apic/isr.h b/drivers/include/apic/isr.h index 9c20c76..9701eaf 100644 --- a/drivers/include/apic/isr.h +++ b/drivers/include/apic/isr.h @@ -22,4 +22,6 @@ extern void apic_isr_timer(void) __attribute__((noreturn)); extern void apic_isr_error(void); +extern void apic_isr_tlb_shootdown(void); + #endif /* DRIVERS_APIC_ISR_H */ diff --git a/drivers/include/disk/disk.h b/drivers/include/disk/disk.h index ef06dae..e237fc4 100644 --- a/drivers/include/disk/disk.h +++ b/drivers/include/disk/disk.h @@ -23,6 +23,8 @@ #define DISK_ID_FIRST 0 +#define SECTOR_SIZE 512 + enum disk_error_t { DISK_OK, DISK_ERROR @@ -44,4 +46,6 @@ extern enum disk_error_t disk_read(struct disk_t* disk, void* pbuffer, uint64_t extern enum disk_error_t disk_write(struct disk_t* disk, void* pbuffer, uint64_t lba, uint16_t count); extern enum disk_error_t disk_flush(struct disk_t* disk); +extern uint64_t disk_get_id(struct disk_t* disk); + #endif /* DRIVERS_DISK_DISK_H */ diff --git a/drivers/include/ext2/ext2.h b/drivers/include/ext2/ext2.h new file mode 100644 index 0000000..99d4a94 --- /dev/null +++ b/drivers/include/ext2/ext2.h @@ -0,0 +1,27 @@ +/* ext2.c - Second Extended File System driver interface */ +/* Copyright (C) 2026 Ebrahim Aleem +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see +*/ + +#ifndef DRIVERS_EXT2_EXT2_H +#define DRIVERS_EXT2_EXT2_H + +#include + +#include + +extern uint8_t ext2_attempt_init(struct disk_t* disk, uint64_t start_lba, uint64_t end_lba); + +#endif /* DRIVERS_EXT2_EXT2_H */ diff --git a/drivers/include/gpt/gpt.h b/drivers/include/gpt/gpt.h new file mode 100644 index 0000000..8f27427 --- /dev/null +++ b/drivers/include/gpt/gpt.h @@ -0,0 +1,27 @@ +/* gpt.h - GUID partition table driver interface */ +/* Copyright (C) 2026 Ebrahim Aleem +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see +*/ + +#ifndef DRIVERS_GPT_GPT_H +#define DRIVERS_GPT_GPT_H + +#include + +#include + +extern uint8_t gpt_find_partitions(struct disk_t* disk); + +#endif /* DRIVERS_GPT_GPT_H */ diff --git a/drivers/ioapic/routing.c b/drivers/ioapic/routing.c index a38aa0c..e48eac4 100644 --- a/drivers/ioapic/routing.c +++ b/drivers/ioapic/routing.c @@ -138,7 +138,7 @@ void ioapic_routing_init(uint64_t num_gsi) { logging_log_info("Found SCI on GSI 0x%lX", sci_gsi); const uint8_t sci_v = idt_get_vector(); - idt_install(sci_v, (uint64_t)sci_isr, GDT_CODE_SEL, 0, IDT_GATE_INT, 0); + idt_install(sci_v, (uint64_t)sci_isr, GDT_CODE_SEL, 0, IDT_GATE_TRP, 0); ioapic_conf_gsi(sci_gsi, sci_v, IOAPIC_REDIR_TRG_LVL | IOAPIC_REDIR_POL_LO, bsp_apic_id); } diff --git a/drivers/pic_8259/pic.c b/drivers/pic_8259/pic.c index 9972e34..81d983f 100644 --- a/drivers/pic_8259/pic.c +++ b/drivers/pic_8259/pic.c @@ -63,8 +63,8 @@ void pic_disab(void) { outb(PIC2_DATA, ICW4_8086); io_wait(); - idt_install(PIC1_IRQ_BASE + PIC_SPURIOUS, (uint64_t)pic_spurious_master, GDT_CODE_SEL, 0, IDT_GATE_INT, 0); - idt_install(PIC2_IRQ_BASE + PIC_SPURIOUS, (uint64_t)pic_spurious_slave, GDT_CODE_SEL, 0, IDT_GATE_INT, 0); + idt_install(PIC1_IRQ_BASE + PIC_SPURIOUS, (uint64_t)pic_spurious_master, GDT_CODE_SEL, 0, IDT_GATE_TRP, 0); + idt_install(PIC2_IRQ_BASE + PIC_SPURIOUS, (uint64_t)pic_spurious_slave, GDT_CODE_SEL, 0, IDT_GATE_TRP, 0); // mask outb(PIC1_DATA, 0xff); diff --git a/kernel/core/alloc.c b/kernel/core/alloc.c index eb155ea..b413663 100644 --- a/kernel/core/alloc.c +++ b/kernel/core/alloc.c @@ -25,6 +25,7 @@ #include #include #include +#include #define HEADER_SIZE_MASK 0xFFFFFFFFFFFFFFF8uLL #define HEADER_USED 0x1uLL @@ -151,6 +152,9 @@ void kfree(void* ptr) { struct alloc_header_t* header = (struct alloc_header_t*)((uint64_t)ptr - sizeof(struct alloc_header_t)); if (!(header->header & HEADER_USED)) { logging_log_warning("Double free @ 0x%lx", ptr); +#ifdef DEBUG + cpu_trap(); +#endif /* DEBUG */ return; } diff --git a/kernel/core/ap.S b/kernel/core/ap.S index b1b8ea5..981e8eb 100644 --- a/kernel/core/ap.S +++ b/kernel/core/ap.S @@ -111,6 +111,7 @@ movb %bl, %dil movq AP_STACKS, %rax movq (%rax,%rdi,8), %rsp +addq $0x5000, %rsp // switch to correct gdt movq ap_gdt_ptr_64, %rax diff --git a/kernel/core/cpu_instr.S b/kernel/core/cpu_instr.S index 4359a14..4f2c763 100644 --- a/kernel/core/cpu_instr.S +++ b/kernel/core/cpu_instr.S @@ -41,6 +41,11 @@ cpu_pause: pause ret +.globl cpu_invlpg +cpu_invlpg: +invlpg (%rdi) +ret + .globl cpu_wait_loop cpu_wait_loop: hlt @@ -51,3 +56,18 @@ cpu_halt_loop: cli hlt jmp cpu_halt_loop + +.globl cpu_trap +cpu_trap: +// setting a breakpoint here will catch traps +ret + +.globl cpu_read_cr2 +cpu_read_cr2: +movq %cr2, %rax +ret + +.globl cpu_wbinvd +cpu_wbinvd: +wbinvd +ret diff --git a/kernel/core/exception_dispatch.c b/kernel/core/exception_dispatch.c index 9b66fab..c7bde50 100644 --- a/kernel/core/exception_dispatch.c +++ b/kernel/core/exception_dispatch.c @@ -20,11 +20,90 @@ #include #include #include +#include +#include + +#define VECTOR_DE 0x00 +#define VECTOR_DB 0x01 +#define VECTOR_NMI 0x02 +#define VECTOR_BP 0x03 +#define VECTOR_OF 0x04 +#define VECTOR_BR 0x05 +#define VECTOR_UD 0x06 +#define VECTOR_NM 0x07 +#define VECTOR_DF 0x08 +#define VECTOR_TS 0x0A +#define VECTOR_NP 0x0B +#define VECTOR_SS 0x0C +#define VECTOR_GP 0x0D +#define VECTOR_PF 0x0E +#define VECTOR_MF 0x10 +#define VECTOR_AC 0x11 +#define VECTOR_MC 0x12 +#define VECTOR_XM 0x13 +#define VECTOR_VE 0x14 +#define VECTOR_CP 0x15 +#define VECTOR_HV 0x1C +#define VECTOR_VC 0x1D +#define VECTOR_SX 0x1E + +static inline const char* get_exception_name(uint64_t vector) { + switch (vector) { + case VECTOR_DE: + return "#DE"; + case VECTOR_DB: + return "#DB"; + case VECTOR_NMI: + return "NMI"; + case VECTOR_BP: + return "#BP"; + case VECTOR_OF: + return "#OF"; + case VECTOR_BR: + return "#BR"; + case VECTOR_UD: + return "#UD"; + case VECTOR_NM: + return "#NM"; + case VECTOR_DF: + return "#DF"; + case VECTOR_TS: + return "#TS"; + case VECTOR_NP: + return "#NP"; + case VECTOR_SS: + return "#SS"; + case VECTOR_GP: + return "#GP"; + case VECTOR_PF: + return "#PF"; + case VECTOR_MF: + return "#MF"; + case VECTOR_AC: + return "#AC"; + case VECTOR_MC: + return "#MC"; + case VECTOR_XM: + return "#XM"; + case VECTOR_VE: + return "#VE"; + case VECTOR_CP: + return "#CP"; + case VECTOR_HV: + return "#HV"; + case VECTOR_VC: + return "#VC"; + case VECTOR_SX: + return "#SX"; + default: + return ""; + } +} void exception_dispatch(struct exception_context_t* context) { //TODO: remove reduntant rsp push - logging_log_error("Unrecoverable exception 0x%lX (0x%lX) @ 0x%lX", - context->vector, context->code, context->rip); + logging_log_error("Unrecoverable exception 0x%lX %s (0x%lX) @ 0x%lX", + context->vector, get_exception_name(context->vector), context->code, context->rip); logging_log_debug("Register Dump:\r\nrax 0x%lX\r\nrbx 0x%lX\r\nrcx 0x%lX\r\nrdx 0x%lX\ \r\nrsi 0x%lX\r\nrdi 0x%lX\r\nrbp 0x%lX\r\nrsp 0x%lX\r\nr8 0x%lX\r\nr9 0x%lX\r\nr10 0x%lX\ @@ -36,5 +115,15 @@ void exception_dispatch(struct exception_context_t* context) { context->r12, context->r13, context->r14, context->r15, context->rflags, context->cs, context->ss, context->rip); + switch (context->vector) { + case VECTOR_PF: + if (paging_check_guard(cpu_read_cr2())) { + logging_log_error("Stack Overflow"); + } + break; + default: + break; + } + panic(PANIC_STATE); } diff --git a/kernel/core/fs.c b/kernel/core/fs.c new file mode 100644 index 0000000..dbbdddc --- /dev/null +++ b/kernel/core/fs.c @@ -0,0 +1,235 @@ +/* fs.c - kernel file system layer */ +/* Copyright (C) 2026 Ebrahim Aleem +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see +*/ + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +static uint8_t fs_lock; + +struct vfs_mount_t { + struct mount_cntx_t* cntx; + + fs_open_t open; + fs_close_t close; + fs_stat_t stat; + fs_read_t read; +}; + +struct fs_handle_t { + struct vfs_mount_t* mount; + struct file_handle_t* handle; +}; + +struct vfs_tree_node_t { + struct vfs_tree_node_t* co; + struct vfs_tree_node_t* sub; + + const char* name; + + struct vfs_mount_t* mount; +}; + +static struct vfs_tree_node_t vfs_root; + +static inline char* path_next(char* path, size_t* len) { + *len = 0; + + while (*path && *path != '/') { + path++; + (*len)++; + } + + if (*path) { + return path + 1; + } + + return path; +} + +void fs_init(void) { + lock_init(&fs_lock); + + vfs_root.co = 0; + vfs_root.sub = 0; + vfs_root.name = ""; + vfs_root.mount = 0; +} + +enum file_status_t fs_mount( + const char* mountpoint, + struct mount_cntx_t* cntx, + fs_open_t open, + fs_close_t close, + fs_stat_t stat, + fs_read_t read + ) { + + if (kstrcmp(mountpoint, "") && !vfs_root.mount) { + vfs_root.mount = kmalloc(sizeof(struct vfs_mount_t)); + + if (!vfs_root.mount) { + logging_log_error("Failed to mount root"); + return FILE_ERROR; + } + + vfs_root.mount->cntx = cntx; + vfs_root.mount->open = open; + vfs_root.mount->close = close; + vfs_root.mount->stat = stat; + vfs_root.mount->read = read; + + return FILE_OK; + } + + //TODO: support non root mount + logging_log_error("Can only mount root"); + return FILE_ERROR; +} + +struct fs_handle_t* fs_open(const char* path) { + struct vfs_tree_node_t* node = &vfs_root, * walk = 0; + size_t len = kstrlen(path); + char* clean_path = kmalloc(len + 1); + char* mount_path = (char*)""; + struct vfs_mount_t* mount = 0; + + + uint64_t num_chars = 0; + uint64_t skip = 0; + uint8_t dot_only = 1; + + const char* path_read = path + len; + char* path_write = clean_path + len; + + for (; path_read >= path; --path_read) { + switch (*path_read) { + case '/': // absolute paths must start with / + if (dot_only && num_chars == 1) { + path_write += num_chars; + } + else if (dot_only && num_chars == 2) { + skip++; + path_write += num_chars; + } + else if (num_chars > 0) { + if (skip) { + path_write += num_chars; + skip--; + } + else { + *path_write = '/'; + path_write--; + } + } + + num_chars = 0; + dot_only = 1; + break; + default: + dot_only = 0; + __attribute__((fallthrough)); + case '.': + num_chars++; + __attribute__((fallthrough)); + case 0: + *path_write = *path_read; + path_write--; + break; + } + } + + path_write++; // one to rewind write pointer + + if (*path_write == '/') { + path_write++; // one to skip / + } + + + lock_acquire(&fs_lock); + + do { + if (node->mount) { + mount_path = path_write; + mount = node->mount; + } + + path_read = path_write; + path_write = path_next(path_write, &len); + + for (walk = node->sub; walk; walk = walk->co) { + if (kstrlen(node->name) == len && kmemcmp(node->name, path_read, len) == 0) { + node = walk; + break; + } + } + + } while (walk && node != walk); + + lock_release(&fs_lock); + + if (!mount) { + kfree(clean_path); + return 0; + } + + struct file_handle_t* handle = mount->open(mount->cntx, mount_path); + + kfree(clean_path); + + struct fs_handle_t* fs_handle = kmalloc(sizeof(struct fs_handle_t)); + fs_handle->handle = handle; + fs_handle->mount = mount; + return fs_handle; +} + +void fs_close(struct fs_handle_t* handle) { + lock_acquire(&fs_lock); + + handle->mount->close(handle->handle); + lock_release(&fs_lock); + + kfree(handle); +} + +enum file_status_t fs_stat(struct fs_handle_t* handle, struct file_info_t* info) { + lock_acquire(&fs_lock); + + enum file_status_t ret = handle->mount->stat(handle->handle, info); + lock_release(&fs_lock); + return ret; +} + +size_t fs_read(struct fs_handle_t* handle, void* buffer, size_t count) { + size_t ret; + + lock_acquire(&fs_lock); + ret = handle->mount->read(handle->handle, buffer, count); + lock_release(&fs_lock); + + return ret; +} diff --git a/kernel/core/idt.c b/kernel/core/idt.c index 92ef1fe..8785133 100644 --- a/kernel/core/idt.c +++ b/kernel/core/idt.c @@ -61,7 +61,7 @@ void idt_init(void) { idt_install(0x0b, (uint64_t)isr_0b, GDT_CODE_SEL, 0, IDT_GATE_TRP, 0); idt_install(0x0c, (uint64_t)isr_0c, GDT_CODE_SEL, 0, IDT_GATE_TRP, 0); idt_install(0x0d, (uint64_t)isr_0d, GDT_CODE_SEL, 0, IDT_GATE_TRP, 0); - idt_install(0x0e, (uint64_t)isr_0e, GDT_CODE_SEL, 0, IDT_GATE_TRP, 0); + idt_install(0x0e, (uint64_t)isr_0e, GDT_CODE_SEL, IST_PF, IDT_GATE_INT, 0); idt_install(0x10, (uint64_t)isr_10, GDT_CODE_SEL, 0, IDT_GATE_TRP, 0); idt_install(0x11, (uint64_t)isr_11, GDT_CODE_SEL, 0, IDT_GATE_TRP, 0); idt_install(0x12, (uint64_t)isr_12, GDT_CODE_SEL, IST_ABORT, IDT_GATE_INT, 0); diff --git a/kernel/core/kentry.c b/kernel/core/kentry.c index d1049ba..c821d03 100644 --- a/kernel/core/kentry.c +++ b/kernel/core/kentry.c @@ -30,6 +30,7 @@ #include #include #include +#include #include @@ -54,6 +55,12 @@ extern uint8_t ap_bootstrap_end; extern volatile struct gdt_t(** ap_gdts)[GDT_NUM_ENTRIES]; extern uint8_t* ap_init_locks; +extern uint64_t init_stack_vaddr; +extern uint64_t init_stack_paddr; + +extern uint64_t* init_stacks_paddr; +extern uint64_t* init_stacks_vaddr; + void kentry(void) { logging_log_debug("Kernel Entry"); @@ -65,7 +72,7 @@ void kentry(void) { logging_log_debug("TSS and IDT init"); tss_init((void*)paging_ident((uint64_t)boot_context.gdt)); - process_init(0); + process_init(init_stack_vaddr, init_stack_paddr); idt_init(); scheduler_init(); logging_log_debug("TSS and IDT init done"); @@ -80,15 +87,18 @@ void kentry(void) { logging_log_debug("System clock initialized."); logging_log_debug("APIC and IOAPIC init"); + apic_ipi_init(); pic_disab(); apic_init(); apic_timer_calib(apic_get_bsp_id()); apic_nmi_enab(); ioapic_init(); + cpu_sti(); logging_log_debug("APIC and IOAPIC init done"); logging_log_debug("Early PCIE init"); disk_init(); + fs_init(); pcie_init(); pcie_enumerate(); logging_log_debug("Early PCIE init done"); @@ -123,7 +133,7 @@ void kapentry(uint64_t arb_id) { logging_log_debug("AP TSS and IDT init"); tss_init(ap_gdts[proc_data_get()->arb_id]); - process_init_ap(init_stacks[arb_id] - INIT_STACK_SIZE); + process_init_ap(init_stacks_vaddr[arb_id], init_stacks_paddr[arb_id]); idt_init_ap(); logging_log_debug("AP TSS and IDT init done"); @@ -131,6 +141,7 @@ void kapentry(uint64_t arb_id) { apic_init_ap(); apic_timer_calib(apic_get_bsp_id()); apic_nmi_enab(); + cpu_sti(); logging_log_debug("AP APIC init done"); logging_log_info("AP init complete"); diff --git a/kernel/core/mm.c b/kernel/core/mm.c index 2a77341..278c4a9 100644 --- a/kernel/core/mm.c +++ b/kernel/core/mm.c @@ -24,6 +24,7 @@ #include #include #include +#include #include @@ -80,19 +81,24 @@ static void free_node(struct mm_tree_node_t* node) { } static struct mm_tree_node_t* find_base_node(uint64_t base, struct mm_tree_node_t* root, struct mm_tree_node_t* parent) { - if (!root) { - return parent; - } + while (1) { + if (!root) { + return parent; + } - if (WITHIN_NODE(base, root->base, root->limit)) { - return root; - } + if (WITHIN_NODE(base, root->base, root->limit)) { + return root; + } - if (base < root->base) { - return find_base_node(base, root->less, root); - } + parent = root; - return find_base_node(base, root->more, root); + if (base < root->base) { + root = root->less; + } + else { + root = root->more; + } + } } static void attach_node(struct mm_tree_node_t* root, struct mm_tree_node_t* node) { @@ -179,9 +185,15 @@ static void mm_free(uint64_t base, uint64_t size, struct mm_tree_node_t* root, u if (WITHIN_NODE(base, node->base, node->limit)) { if (root == p_tree) { logging_log_warning("Double free @ 0x%lx on p_tree", base); +#ifdef DEBUG + cpu_trap(); +#endif /* DEBUG */ } else { logging_log_warning("Double free @ 0x%lx on v_tree", base); +#ifdef DEBUG + cpu_trap(); +#endif /* DEBUG */ } } else if (base < node->base) { diff --git a/kernel/core/paging.c b/kernel/core/paging.c index 974e902..cfac405 100644 --- a/kernel/core/paging.c +++ b/kernel/core/paging.c @@ -24,9 +24,12 @@ #include #include #include +#include #include +#include + #define PAGE_PS 0x80 #define PAGE_ADDR_MASK 0x0000FFFFFFFFF000 @@ -37,6 +40,8 @@ #define GET_PDPT_INDEX(addr) ((addr & 0x7FC0000000) >> 30) #define GET_PML4_INDEX(addr) ((addr & 0xFF8000000000) >> 39) +#define AVL_GUARD 0x800 + extern uint64_t kernel_pml4[512]; static uint8_t paging_lock; @@ -78,26 +83,7 @@ static enum page_size_t page_walk(uint64_t vaddr, uint64_t** access) { return PAGE_4K; } -void paging_init(void) { - lock_init(&paging_lock); -} - -uint64_t paging_map(uint64_t vaddr, uint64_t paddr, uint16_t flg, enum page_size_t page_size) { - uint64_t* access; - enum page_size_t lvl = page_walk(vaddr, &access); - - if (lvl < page_size) { - logging_log_error("Cannot override page of finer granularity from 0x%lx-0x%lx (%u) to 0x%lx-0x%lx (%u)", - vaddr, *access - IDENT_BASE, (uint32_t)lvl, vaddr, paddr | flg, (uint32_t)page_size); - return *access - IDENT_BASE; - } - - if (*access & PAGE_PRESENT) { - logging_log_error("Cannot override page from 0x%lx-0x%lx (%u) to 0x%lx-0x%lx (%u)", - vaddr, *access, (uint32_t)lvl, vaddr, paddr | flg, (uint32_t)page_size); - return *access; - } - +static uint64_t* increase_granularity(uint64_t vaddr, uint64_t* access, enum page_size_t lvl, enum page_size_t page_size) { for (; lvl > page_size; lvl--) { *access = mm_alloc_p(0x1000); if (!access) { @@ -120,6 +106,34 @@ uint64_t paging_map(uint64_t vaddr, uint64_t paddr, uint16_t flg, enum page_size } } + return access; +} + +void paging_init(void) { + lock_init(&paging_lock); +} + +uint64_t paging_map(uint64_t vaddr, uint64_t paddr, uint16_t flg, enum page_size_t page_size) { + uint64_t* access; + enum page_size_t lvl = page_walk(vaddr, &access); + + if (lvl < page_size) { + logging_log_error("Cannot override page of finer granularity from 0x%lx-0x%lx (%u) to 0x%lx-0x%lx (%u)", + vaddr, *access - IDENT_BASE, (uint32_t)lvl, vaddr, paddr | flg, (uint32_t)page_size); + return *access - IDENT_BASE; + } + + if (*access & PAGE_PRESENT) { + logging_log_error("Cannot override page from 0x%lx-0x%lx (%u) to 0x%lx-0x%lx (%u)", + vaddr, *access, (uint32_t)lvl, vaddr, paddr | flg, (uint32_t)page_size); + return *access; + } + + access = increase_granularity(vaddr, access, lvl, page_size); + if (!access) { + return 0; + } + if (page_size != PAGE_4K) { flg |= PAGE_PS; } @@ -137,9 +151,55 @@ void paging_unmap(uint64_t vaddr, enum page_size_t page_size) { return; } + *access = 0; + apic_tlb_shootdown(vaddr); } uint64_t paging_ident(uint64_t paddr) { return paddr + IDENT_BASE; } + +void paging_install_guard(uint64_t vaddr) { + uint64_t* access; + lock_acquire(&paging_lock); + enum page_size_t lvl = page_walk(vaddr, &access); + + if (*access & PAGE_PRESENT) { + logging_log_error("Cannot install page guard over mapped page"); + panic(PANIC_STATE); + } + + access = increase_granularity(vaddr, access, lvl, PAGE_4K); + if (!access) { + logging_log_error("Failed to install page guard"); + panic(PANIC_STATE); + } + + *access |= AVL_GUARD; + lock_release(&paging_lock); +} + +void paging_remove_guard(uint64_t vaddr) { + uint64_t* access; + lock_acquire(&paging_lock); + enum page_size_t lvl = page_walk(vaddr, &access); + + if (lvl != PAGE_4K || !(*access & AVL_GUARD)) { + logging_log_error("Attempted to remove guard from ungaurded page @ 0x%x", vaddr); + } + + *access = 0; + lock_release(&paging_lock); +} + +uint8_t paging_check_guard(uint64_t vaddr) { + uint64_t* access, access_value; + lock_acquire(&paging_lock); + enum page_size_t lvl = page_walk(vaddr, &access); + + access_value = *access; + lock_release(&paging_lock); + + return lvl == PAGE_4K && (access_value & AVL_GUARD); +} diff --git a/kernel/core/panic.c b/kernel/core/panic.c index 51c7274..6fa34a5 100644 --- a/kernel/core/panic.c +++ b/kernel/core/panic.c @@ -37,5 +37,9 @@ void panic(enum panic_code_t code) { logging_log_error("Panic 0x%lX %s", (uint64_t)code, panic_names[code] ? panic_names[code] : panic_names[PANIC_UNK]); - process_kill_current(); +#ifdef DEBUG + cpu_trap(); +#endif /* DEBUG */ + + cpu_halt_loop(); } diff --git a/kernel/core/process.c b/kernel/core/process.c index 5a04c9c..e9414be 100644 --- a/kernel/core/process.c +++ b/kernel/core/process.c @@ -26,12 +26,16 @@ #include #include #include +#include +#include #include #define INIT_RFLG 0x200 #define INIT_STACK_SIZE 0x4000 +// change manual paging map when adjusting stack size + static uint64_t next_pid; static uint8_t lock_proc; @@ -40,10 +44,10 @@ __attribute__((noreturn)) static void function_setup(process_function_t func, vo process_kill_current(); } -void process_init(uint64_t init_rsp) { +void process_init(uint64_t init_rsp_vaddr, uint64_t init_rsp_paddr) { next_pid = 1; lock_init(&lock_proc); - process_init_ap(init_rsp); + process_init_ap(init_rsp_vaddr, init_rsp_paddr); } static uint64_t assign_pid(void) { @@ -57,17 +61,45 @@ uint64_t process_get_pid(void) { return proc_data_get()->current_process->pid; } -void process_init_ap(uint64_t init_rsp) { +void process_init_ap(uint64_t init_rsp_vaddr, uint64_t init_rsp_paddr) { struct pcb_t* pcb = kmalloc(sizeof(struct pcb_t)); - pcb->init_rsp = init_rsp; - pcb->init_k_rsp = 0; + pcb->init_rsp_vaddr = init_rsp_vaddr; + pcb->init_rsp_paddr = init_rsp_paddr; + pcb->init_k_rsp_vaddr = + pcb->init_k_rsp_paddr = 0; pcb->sched_cntr = SCHED_SKIP; proc_data_get()->current_process = pcb; proc_data_get()->current_process->pid = assign_pid(); } struct pcb_t* process_from_vaddr(uint64_t vaddr) { - struct pcb_t* pcb = kmalloc(sizeof(struct pcb_t)); + struct pcb_t* pcb; + uint64_t stack_vaddr; + uint64_t stack_paddr; + + stack_vaddr = mm_alloc_v(INIT_STACK_SIZE + PAGE_SIZE_4K); // extra guard page + if (!stack_vaddr) { + logging_log_error("Failed to allocate stack"); + panic(PANIC_NO_MEM); + } + + stack_paddr = mm_alloc_p(INIT_STACK_SIZE); + if (!stack_paddr) { + mm_free_v(stack_vaddr, INIT_STACK_SIZE + PAGE_SIZE_4K); + logging_log_error("Failed to allocate stack"); + panic(PANIC_NO_MEM); + } + + _Static_assert(INIT_STACK_SIZE == 4 * PAGE_SIZE_4K, "stack size must be page size multiple of four"); + // add for increased stack size + paging_map(stack_vaddr + 1 * PAGE_SIZE_4K, stack_paddr + 0 * PAGE_SIZE_4K, PAGE_PRESENT | PAGE_RW, PAGE_4K); + paging_map(stack_vaddr + 2 * PAGE_SIZE_4K, stack_paddr + 1 * PAGE_SIZE_4K, PAGE_PRESENT | PAGE_RW, PAGE_4K); + paging_map(stack_vaddr + 3 * PAGE_SIZE_4K, stack_paddr + 2 * PAGE_SIZE_4K, PAGE_PRESENT | PAGE_RW, PAGE_4K); + paging_map(stack_vaddr + 4 * PAGE_SIZE_4K, stack_paddr + 3 * PAGE_SIZE_4K, PAGE_PRESENT | PAGE_RW, PAGE_4K); + // leave last page unmapped as guard + paging_install_guard(stack_vaddr); + + pcb = kmalloc(sizeof(struct pcb_t)); pcb->rax = pcb->rbx = @@ -85,10 +117,12 @@ struct pcb_t* process_from_vaddr(uint64_t vaddr) { pcb->r14 = pcb->r15 = 0; - pcb->rsp = - pcb->init_rsp = (uint64_t)kmalloc(INIT_STACK_SIZE); - pcb->init_k_rsp = 0; - pcb->rsp += INIT_STACK_SIZE; + pcb->rsp = stack_vaddr + PAGE_SIZE_4K * 5; + pcb->init_rsp_vaddr = stack_vaddr; + pcb->init_rsp_paddr = stack_paddr; + + pcb->init_k_rsp_vaddr = + pcb->init_k_rsp_paddr = 0; pcb->k_rsp_lo = 0; pcb->k_rsp_hi = 0; @@ -115,19 +149,26 @@ struct pcb_t* process_from_func(process_function_t func, void* cntx) { } void process_kill_current(void) { - cpu_cli(); lock_acquire(&lock_proc); struct pcb_t* pcb = proc_data_get()->current_process; pcb->sched_cntr = SCHED_KILL; logging_log_debug("Killed %ld", pcb->pid); lock_release(&lock_proc); - cpu_sti(); cpu_wait_loop(); } void process_discard(struct pcb_t* pcb) { - kfree((void*)pcb->init_rsp); + _Static_assert(INIT_STACK_SIZE == 4 * PAGE_SIZE_4K, "stack size must be page size multiple of four"); + paging_unmap(pcb->init_rsp_vaddr + 1 * PAGE_SIZE_4K, PAGE_4K); + paging_unmap(pcb->init_rsp_vaddr + 2 * PAGE_SIZE_4K, PAGE_4K); + paging_unmap(pcb->init_rsp_vaddr + 3 * PAGE_SIZE_4K, PAGE_4K); + paging_unmap(pcb->init_rsp_vaddr + 4 * PAGE_SIZE_4K, PAGE_4K); + paging_remove_guard(pcb->init_rsp_vaddr); + + mm_free_v(pcb->init_rsp_vaddr, INIT_STACK_SIZE + PAGE_SIZE_4K); + mm_free_p(pcb->init_rsp_paddr, INIT_STACK_SIZE); + kfree(pcb); } @@ -154,7 +195,5 @@ void process_preempt_entry(struct preempt_frame_t* context) { pcb->rflags = context->rflags; pcb->ss = context->ss; - apic_write_reg(APIC_REG_EOI, APIC_EOI); - scheduler_run(); } diff --git a/kernel/core/scheduler.c b/kernel/core/scheduler.c index be58fd8..e0da3d8 100644 --- a/kernel/core/scheduler.c +++ b/kernel/core/scheduler.c @@ -25,6 +25,8 @@ #include #include +#include + static uint8_t lock_sched; static volatile struct pcb_t* active_queue; static volatile struct pcb_t* active_queue_tail; @@ -55,11 +57,12 @@ void scheduler_run(void) { struct pcb_t* current_pcb = pd->current_process; switch (current_pcb->sched_cntr) { case SCHED_SKIP: + cpu_cli(); + apic_write_reg(APIC_REG_EOI, APIC_EOI); process_resume(current_pcb); break; case SCHED_KILL: - kfree((void*)current_pcb->init_rsp); - kfree((void*)current_pcb->init_k_rsp); + process_discard(current_pcb); break; default: scheduler_schedule(current_pcb); @@ -85,5 +88,8 @@ void scheduler_run(void) { pd->tss->rsp0_lo = run->k_rsp_lo; pd->tss->rsp0_hi = run->k_rsp_hi; pd->current_process = run; + + cpu_cli(); + apic_write_reg(APIC_REG_EOI, APIC_EOI); process_resume(run); } diff --git a/kernel/core/tss.c b/kernel/core/tss.c index 800550d..fdc1408 100644 --- a/kernel/core/tss.c +++ b/kernel/core/tss.c @@ -29,6 +29,7 @@ #define IST_ABORT_SIZE 0x1000 #define IST_SCHED_SIZE 0x1000 +#define IST_PF_SIZE 0x1000 #define IST_LO_MASK 0xFFFFFFFF #define IST_HI_SHFT 32 @@ -48,6 +49,10 @@ void tss_init(volatile struct gdt_t(* gdt)[GDT_NUM_ENTRIES]) { tss->ist2_lo = ist2 & IST_LO_MASK; tss->ist2_hi = (uint32_t)(ist2 >> IST_HI_SHFT); + const uint64_t ist3 = (uint64_t)kmalloc(IST_SCHED_SIZE) + IST_SCHED_SIZE; + tss->ist3_lo = ist3 & IST_LO_MASK; + tss->ist3_hi = (uint32_t)(ist3 >> IST_HI_SHFT); + logging_log_debug("New TSS @ 0x%lX - 0x%lX 0x%lX (ist1) 0x%lX (ist2)", (uint64_t)tss, ist1, ist2); diff --git a/kernel/include/core/cpu_instr.h b/kernel/include/core/cpu_instr.h index e6b4908..46de151 100644 --- a/kernel/include/core/cpu_instr.h +++ b/kernel/include/core/cpu_instr.h @@ -30,8 +30,16 @@ extern void cpu_sti(void); extern void cpu_pause(void); +extern void cpu_invlpg(uint64_t addr); + extern void cpu_wait_loop(void) __attribute__((noreturn)); extern void cpu_halt_loop(void) __attribute__((noreturn)); +extern void cpu_trap(void); + +extern uint64_t cpu_read_cr2(void); + +extern void cpu_wbinvd(void); + #endif /* KERNEL_CORE_CPU_INSTR_H */ diff --git a/kernel/include/core/fs.h b/kernel/include/core/fs.h new file mode 100644 index 0000000..7f05c67 --- /dev/null +++ b/kernel/include/core/fs.h @@ -0,0 +1,70 @@ +/* fs.h - kernel file system layer interface */ +/* Copyright (C) 2026 Ebrahim Aleem +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see +*/ + +#ifndef KERNEL_CORE_FS_H +#define KERNEL_CORE_FS_H + +#include +#include + +struct mount_cntx_t; + +struct file_handle_t; + +struct fs_handle_t; + +enum file_status_t { + FILE_OK, + FILE_ERROR, + FILE_DNE, + FILE_BUSY, + FILE_NO_SUPPORT +}; + +struct file_info_t { + enum { + FILE_TYPE_REG, + FILE_TYPE_DIR + } type; + uint64_t size; +}; + + +typedef struct file_handle_t* (*fs_open_t)(struct mount_cntx_t*, char*); +typedef void (*fs_close_t)(struct file_handle_t*); + +typedef enum file_status_t (*fs_stat_t)(struct file_handle_t*, struct file_info_t*); +typedef size_t (*fs_read_t)(struct file_handle_t*, void*, size_t); + +void fs_init(void); + +enum file_status_t fs_mount( + const char* mountpoint, + struct mount_cntx_t* cntx, + fs_open_t open, + fs_close_t close, + fs_stat_t stat, + fs_read_t read + ); + +extern struct fs_handle_t* fs_open(const char* path); +extern void fs_close(struct fs_handle_t* handle); + +extern enum file_status_t fs_stat(struct fs_handle_t* handle, struct file_info_t* info); +extern size_t fs_read(struct fs_handle_t* handle, void* buffer, size_t count); + +#endif /* KERNEL_CORE_FS_H */ diff --git a/kernel/include/core/kentry.h b/kernel/include/core/kentry.h index c8b8c29..0f6f1c1 100644 --- a/kernel/include/core/kentry.h +++ b/kernel/include/core/kentry.h @@ -25,8 +25,6 @@ #include -#define INIT_STACK_SIZE 0x4000 - #ifdef GRAPHICSBASE #include #endif /* GRAPHICSBASE */ diff --git a/kernel/include/core/paging.h b/kernel/include/core/paging.h index 82319a7..a1e2122 100644 --- a/kernel/include/core/paging.h +++ b/kernel/include/core/paging.h @@ -47,5 +47,8 @@ extern uint64_t paging_map(uint64_t vaddr, uint64_t paddr, uint16_t flg, enum pa extern void paging_unmap(uint64_t vaddr, enum page_size_t page_size); extern uint64_t paging_ident(uint64_t paddr); +extern void paging_install_guard(uint64_t vaddr); +extern void paging_remove_guard(uint64_t vaddr); +extern uint8_t paging_check_guard(uint64_t vaddr); #endif /* KERNEL_CORE_PAGING_H */ diff --git a/kernel/include/core/process.h b/kernel/include/core/process.h index da49c6b..ee2a369 100644 --- a/kernel/include/core/process.h +++ b/kernel/include/core/process.h @@ -51,16 +51,20 @@ struct pcb_t { uint64_t pid; uint32_t k_rsp_lo; uint32_t k_rsp_hi; - uint64_t init_rsp; - uint64_t init_k_rsp; + uint64_t init_rsp_vaddr; + uint64_t init_rsp_paddr; + uint64_t init_k_rsp_vaddr; + uint64_t init_k_rsp_paddr; + + struct pcb_t* next; + enum { SCHED_READY, SCHED_KILL, SCHED_SKIP } sched_cntr; - struct pcb_t* next; -}; +} __attribute__((packed)); struct preempt_frame_t { uint64_t rbp; @@ -90,9 +94,9 @@ typedef void (*process_function_t)(void* cntx); extern uint64_t process_get_pid(void); -extern void process_init(uint64_t init_rsp); +extern void process_init(uint64_t init_rsp_vaddr, uint64_t init_rsp_paddr); -extern void process_init_ap(uint64_t init_rsp); +extern void process_init_ap(uint64_t init_rsp_vaddr, uint64_t init_rsp_paddr); extern struct pcb_t* process_from_vaddr(uint64_t vaddr); diff --git a/kernel/include/core/tss.h b/kernel/include/core/tss.h index f201afd..9913cf9 100644 --- a/kernel/include/core/tss.h +++ b/kernel/include/core/tss.h @@ -22,6 +22,7 @@ #define IST_ABORT 1 #define IST_SCHED 2 +#define IST_PF 3 struct tss_t { uint32_t resv0; diff --git a/kernel/include/lib/hash.h b/kernel/include/lib/hash.h index e63b734..763a90d 100644 --- a/kernel/include/lib/hash.h +++ b/kernel/include/lib/hash.h @@ -23,4 +23,6 @@ extern uint8_t hash_byte_sum(const void* ptr, size_t c); +extern uint32_t crc32_ansi(const void* data, size_t length); + #endif /* KERNEL_LIB_HASH_H */ diff --git a/kernel/include/lib/kstrcmp.h b/kernel/include/lib/kstrcmp.h new file mode 100644 index 0000000..03d008f --- /dev/null +++ b/kernel/include/lib/kstrcmp.h @@ -0,0 +1,23 @@ +/* kstrcmp.h - kernel string compare interface */ +/* Copyright (C) 2026 Ebrahim Aleem +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see +*/ + +#ifndef KERNEL_LIB_KSTRCMP_H +#define KERNEL_LIB_KSTRCMP_H + +extern int kstrcmp(const char* s1, const char* s2); + +#endif /* KERNEL_LIB_KSTRCMP_H */ diff --git a/kernel/include/lib/kstrcpy.h b/kernel/include/lib/kstrcpy.h new file mode 100644 index 0000000..b591a4c --- /dev/null +++ b/kernel/include/lib/kstrcpy.h @@ -0,0 +1,26 @@ +/* kstrcpy.h - kernel string copy interface */ +/* Copyright (C) 2026 Ebrahim Aleem +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see +*/ + +#ifndef KERNEL_LIB_KSTRCPY_H +#define KERNEL_LIB_KSTRCPY_H + +extern char* kstrcpy(char* dest, const char* src); + +extern char* kstrcpy_no_null(char* dest, const char* src); + +#endif /* KERNEL_LIB_KSTRCPY_H */ + diff --git a/kernel/include/lib/kstrlen.h b/kernel/include/lib/kstrlen.h new file mode 100644 index 0000000..45a3485 --- /dev/null +++ b/kernel/include/lib/kstrlen.h @@ -0,0 +1,25 @@ +/* kstrlen.h - kernel strlen interface */ +/* Copyright (C) 2026 Ebrahim Aleem +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see +*/ + +#ifndef KERNEL_LIB_KSTRLEN_H +#define KERNEL_LIB_KSTRLEN_H + +#include + +extern size_t kstrlen(const char* s); + +#endif /* KERNEL_LIB_KSTRLEN_H */ diff --git a/kernel/lib/hash.c b/kernel/lib/hash.c index bddb886..501dcf0 100644 --- a/kernel/lib/hash.c +++ b/kernel/lib/hash.c @@ -29,3 +29,24 @@ uint8_t hash_byte_sum(const void* ptr, size_t c) { return sum; } + +uint32_t crc32_ansi(const void* data, size_t length) { + uint32_t crc = 0xFFFFFFFF; + const uint8_t *p = (const uint8_t*)data; + uint8_t i; + + while (length--) { + crc ^= *p++; + + for (i = 0; i < 8; i++) { + if (crc & 1) { + crc = (crc >> 1) ^ 0xEDB88320; + } + else { + crc >>= 1; + } + } + } + + return crc ^ 0xFFFFFFFF; +} diff --git a/kernel/lib/kstrcmp.c b/kernel/lib/kstrcmp.c new file mode 100644 index 0000000..e5b04d4 --- /dev/null +++ b/kernel/lib/kstrcmp.c @@ -0,0 +1,27 @@ +/* kstrcmp.c - kernel string compare implementation */ +/* Copyright (C) 2026 Ebrahim Aleem +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see +*/ + +#include + +int kstrcmp(const char* s1, const char* s2) { + while (*s1 && *s2 && *s1 == *s2) { + s1++; + s2++; + } + + return (int)(*s1 - *s2); +} diff --git a/kernel/lib/kstrcpy.c b/kernel/lib/kstrcpy.c new file mode 100644 index 0000000..a24e571 --- /dev/null +++ b/kernel/lib/kstrcpy.c @@ -0,0 +1,39 @@ +/* kstrcpy.c - kernel string copy implementation */ +/* Copyright (C) 2026 Ebrahim Aleem +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see +*/ + +#include + +char* kstrcpy(char* dest, const char* src) { + while (*src) { + *dest = *src; + src++; + dest++; + } + + *dest = 0; + return dest + 1; +} + +char* kstrcpy_no_null(char* dest, const char* src) { + while (*src) { + *dest = *src; + src++; + dest++; + } + + return dest; +} diff --git a/kernel/lib/kstrlen.c b/kernel/lib/kstrlen.c new file mode 100644 index 0000000..4140dab --- /dev/null +++ b/kernel/lib/kstrlen.c @@ -0,0 +1,28 @@ +/* kstrlen.c - kernel strlen implementation */ +/* Copyright (C) 2026 Ebrahim Aleem +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see +*/ + +#include + +#include + +size_t kstrlen(const char* s) { + size_t size = 0; + for (; *s; s++) size++; + + return size; +} + diff --git a/test/lib/test.c b/test/lib/test.c index 7c36b46..d180e56 100644 --- a/test/lib/test.c +++ b/test/lib/test.c @@ -18,12 +18,16 @@ #include #include #include +#include #include #include #include #include +#include +#include +#include #include #define MEM_TEST_SIZE 256 @@ -67,8 +71,59 @@ TEST("kmemcpy") { free(copy); } +TEST("kstrlen") { + ASSERT_TRUE(kstrlen("") == 0, "fails empty string"); + ASSERT_TRUE(kstrlen("1") == 1, "fails 1 string"); + ASSERT_TRUE(kstrlen("12") == 2, "fails 12 string"); + ASSERT_TRUE(kstrlen("\t\n\r\0") == 3, "fails control character string"); +} + +TEST("kstrcpy") { + char* original = malloc(MEM_TEST_SIZE); + char* copy = malloc(MEM_TEST_SIZE); + + for (uint64_t i = 0; i < MEM_TEST_SIZE - 1; i += (i % 7) + 1) { + original[i] = (char)(i ^ (i * i)) | 1; + } + + kstrcpy(copy, original); + ASSERT_TRUE(!strcmp(original, copy), "string comparison fails"); + + free(original); + free(copy); +} + +TEST("kstrcpy_no_null") { + char* original = malloc(MEM_TEST_SIZE); + char* copy = malloc(MEM_TEST_SIZE); + + for (uint64_t i = 0; i < MEM_TEST_SIZE - 1; i += (i % 7) + 1) { + original[i] = (char)(i ^ (i * i)) | 1; + } + copy[MEM_TEST_SIZE - 1] = 0xAA; + + kstrcpy_no_null(copy, original); + ASSERT_TRUE(!memcmp(original, copy, strlen(original)), "string comparison fails"); + ASSERT_TRUE(copy[MEM_TEST_SIZE - 1] == (char)0xAA, "overwritten ending byte"); + + free(original); + free(copy); +} + +TEST("kstrcmp") { + ASSERT_TRUE(kstrcmp("", "") == 0, "kernel memcmp fails zero size"); + ASSERT_TRUE(kstrcmp(" a", " b") < 0, "kernel strcmp flips sign"); + ASSERT_TRUE(kstrcmp(" 4", " 2") > 0, "kernel strcmp flips sign"); + ASSERT_TRUE(kstrcmp(" a\0b", " a\0c") == 0, "kernel strcmp reads too many bytes"); +} + TEST("hash_byte_sum") { ASSERT_TRUE(hash_byte_sum((uint8_t*)0xdeadbeef, 0) == 0, "fails invalid ptr"); ASSERT_TRUE(hash_byte_sum((uint8_t*)"\0\1\2\3\0\0\1\2", 8) == 9, "fails basic sum"); ASSERT_TRUE(hash_byte_sum((uint8_t*)"\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 8) == 248, "fails overflow"); } + +TEST("crc32_ansi") { + ASSERT_TRUE(crc32_ansi("Hello, World!", 13) == 0xec4ac3d0, "fails expected checksum"); + ASSERT_FALSE(crc32_ansi("Hello, World!", 12) == 0xec4ac3d0, "fails unexpected checksum"); +}