From db368991526898148d0c85c65a8e56502e85949e Mon Sep 17 00:00:00 2001 From: xamidev Date: Sat, 21 Mar 2026 10:36:54 +0100 Subject: [PATCH 01/17] Add kprintf for DEBUG(); differentiated from printf() --- include/arch/x86.h | 2 ++ include/io/term/term.h | 1 + include/kernel.h | 5 +++- src/arch/x86/idt.c | 3 ++ src/arch/x86/syscall.c | 26 +++++++++++++++++ src/io/term/term.c | 64 ++++++++++++++++++++++++++++++++++++++++++ src/kapps/kshell.c | 20 +++++++++++++ src/kmain.c | 8 +----- 8 files changed, 121 insertions(+), 8 deletions(-) create mode 100644 src/arch/x86/syscall.c create mode 100644 src/kapps/kshell.c diff --git a/include/arch/x86.h b/include/arch/x86.h index 1c677d8..2c04dcf 100644 --- a/include/arch/x86.h +++ b/include/arch/x86.h @@ -59,4 +59,6 @@ struct cpu_status_t { uint64_t iret_ss; }; +struct cpu_status_t* syscall_handler(struct cpu_status_t* regs); + #endif \ No newline at end of file diff --git a/include/io/term/term.h b/include/io/term/term.h index 4ce1a32..c4b8cfb 100644 --- a/include/io/term/term.h +++ b/include/io/term/term.h @@ -11,5 +11,6 @@ void kputs(const char* str); void term_init(void); int printf(const char* fmt, ...); void internal_putc(int c, void *_); +int kprintf(const char* fmt, ...); #endif diff --git a/include/kernel.h b/include/kernel.h index 98b43ac..116ba09 100644 --- a/include/kernel.h +++ b/include/kernel.h @@ -21,7 +21,7 @@ enum ErrorCodes { #include extern volatile uint64_t ticks; -#define DEBUG(log, ...) printf("[%8u] debug: <%s>: " log "\r\n", ticks, __func__, ##__VA_ARGS__) +#define DEBUG(log, ...) kprintf("[%8u] debug: <%s>: " log "\r\n", ticks, __func__, ##__VA_ARGS__) /* #define DEBUG(log, ...) \ printf("debug: [%s]: " log "\r\n", __FILE__, ##__VA_ARGS__); \ @@ -38,6 +38,8 @@ void panic(struct cpu_status_t* ctx, const char* str); void hcf(void); void idle(void); +void pedicel_main(void* arg); + /* debug */ void debug_stack_trace(unsigned int max_frames); const char* debug_find_symbol(uintptr_t rip, uintptr_t* offset); @@ -58,6 +60,7 @@ struct init_status { bool serial; bool keyboard; bool timer; + bool all; }; #endif diff --git a/src/arch/x86/idt.c b/src/arch/x86/idt.c index 822033c..b698162 100644 --- a/src/arch/x86/idt.c +++ b/src/arch/x86/idt.c @@ -72,6 +72,9 @@ void idt_init() // Each vector handler is 16-byte aligned, so *16 = address of that handler idt_set_entry(i, vector_0_handler + (i*16), 0); } + + idt_set_entry(0x80, syscall_handler, 0); + idt_load(&idt); DEBUG("IDT initialized"); } diff --git a/src/arch/x86/syscall.c b/src/arch/x86/syscall.c new file mode 100644 index 0000000..380c5fc --- /dev/null +++ b/src/arch/x86/syscall.c @@ -0,0 +1,26 @@ +/* + * @author xamidev + * @brief System call handling + * @license GPL-3.0-only + */ + +#include +#include + +struct cpu_status_t* syscall_handler(struct cpu_status_t* regs) +{ + DEBUG("Syscall %lx with argument %lx", regs->rdi, regs->rsi); + + switch (regs->rdi) + { + case 0: + break; + case 1: + break; + default: + regs->rsi = 0xdeadbeef; + break; + } + + return regs; +} \ No newline at end of file diff --git a/src/io/term/term.c b/src/io/term/term.c index 65b11a1..e4f7546 100644 --- a/src/io/term/term.c +++ b/src/io/term/term.c @@ -65,6 +65,38 @@ void internal_putc(int c, void *_) } } +/* + * debug_putc - Internal DEBUG putchar function + * @c: char to print + * @_: (unused, for nanoprintf) + * + * Prints a character to the terminal if it's ready and if + * the kernel is still initializing, and also always to the + * serial interface if it's ready. + */ +void debug_putc(int c, void *_) +{ + (void)_; + char ch = (char)c; + + if (init.terminal && (!init.all || panic_count > 0)) { + if (panic_count == 0) { + spinlock_acquire(&term_lock); + flanterm_write(ft_ctx, &ch, 1); + spinlock_release(&term_lock); + } else { + flanterm_write(ft_ctx, &ch, 1); + } + } + + if (init.serial) { + if (ch == '\n') { + skputc('\r'); + } + skputc(ch); + } +} + /* * printf - Fromatted printing * @fmt: format string @@ -96,6 +128,38 @@ int printf(const char* fmt, ...) return -1; } +/* + * kprintf - Fromatted DEBUG printing + * @fmt: format string + * @...: variadic arguments + * + * Wrapper for nanoprintf; to be used only for + * kernel/debug messages. + * + * Return: + * - number of characters sent to the callback + * %-1 - error + */ +int kprintf(const char* fmt, ...) +{ + if (panic_count == 0) { + spinlock_acquire(&printf_lock); + va_list args; + va_start(args, fmt); + int ret = npf_vpprintf(debug_putc, NULL, fmt, args); + va_end(args); + spinlock_release(&printf_lock); + return ret; + } else { + va_list args; + va_start(args, fmt); + int ret = npf_vpprintf(debug_putc, NULL, fmt, args); + va_end(args); + return ret; + } + return -1; +} + /* * kputs - Kernel puts * @str: String to write diff --git a/src/kapps/kshell.c b/src/kapps/kshell.c new file mode 100644 index 0000000..b416d69 --- /dev/null +++ b/src/kapps/kshell.c @@ -0,0 +1,20 @@ +/* + * @author xamidev + * @brief PepperOS kernel shell + * @license GPL-3.0-only + */ + +#include + +/* + * pedicel_main - Kernel shell main function + * @arg: argument (optional) + * + * This is the entry point for the kernel shell process. + * It is used to start programs and to test different things + * on different real hardware easily. + */ +void pedicel_main(void* arg) +{ + printf("Entering the kernel shell...\r\n"); +} \ No newline at end of file diff --git a/src/kmain.c b/src/kmain.c index 1a83b8f..d85ef7a 100644 --- a/src/kmain.c +++ b/src/kmain.c @@ -66,12 +66,6 @@ extern struct process_t* processes_list; extern struct process_t* current_process; struct process_t* idle_proc; -// Never gets executed although pedicel is scheduled? -void pedicel_main(void* arg) -{ - printf("\n\n\rWelcome to PepperOS! Pedicel speaking.\r\nNothing left to do, let's go idle!\r\n"); -} - void idle_main(void* arg) { for (;;) { @@ -125,10 +119,10 @@ void kmain() process_init(); idle_proc = process_create("idle", (void*)idle_main, 0); process_create("pedicel", (void*)pedicel_main, 0); - process_create("thing", thing_main, NULL); scheduler_init(); printf(PEPPEROS_SPLASH); + init.all = true; idle(); } From 3ae56bbad549b70329a0428cf035f270c403d33c Mon Sep 17 00:00:00 2001 From: xamidev Date: Sat, 21 Mar 2026 11:34:46 +0100 Subject: [PATCH 02/17] Kernel debug shell --- include/config.h | 4 ++++ include/string/string.h | 1 + src/arch/x86/idt.c | 23 ++++++++++++++++------- src/kapps/kshell.c | 38 +++++++++++++++++++++++++++++++++++++- src/string/string.c | 29 +++++++++++++++++++++++++++++ 5 files changed, 87 insertions(+), 8 deletions(-) diff --git a/include/config.h b/include/config.h index 3a1e9e3..30a79f3 100644 --- a/include/config.h +++ b/include/config.h @@ -20,6 +20,10 @@ "\x1b[38;5;196m/_/ /_/ /_/ \r\n\x1b[0m"\ " --- version \x1b[38;5;220m"PEPPEROS_VERSION_MAJOR"."PEPPEROS_VERSION_MINOR"."PEPPEROS_VERSION_PATCH"\x1b[0m built on \x1b[38;5;40m"__DATE__" "__TIME__"\x1b[0m\r\n" +/* pedicel */ +#define PEDICEL_PROMPT "pedicel$ " +#define PEDICEL_INPUT_SIZE 128 + /* process */ #define PROCESS_NAME_MAX 64 #define PROCESS_STACK_SIZE 0x10000 // 64kb diff --git a/include/string/string.h b/include/string/string.h index dca35bc..78859ec 100644 --- a/include/string/string.h +++ b/include/string/string.h @@ -12,5 +12,6 @@ char *strcpy(char *dest, const char *src); char *strcat(char *dest, const char *src); void strncpy(char* dst, const char* src, size_t n); +int strncmp(const char* s1, const char* s2, size_t n); #endif \ No newline at end of file diff --git a/src/arch/x86/idt.c b/src/arch/x86/idt.c index b698162..eb99a4a 100644 --- a/src/arch/x86/idt.c +++ b/src/arch/x86/idt.c @@ -24,6 +24,8 @@ extern char vector_0_handler[]; // Timer ticks extern volatile uint64_t ticks; +extern struct init_status init; + /* * idt_set_entry - Sets an Interrupt Descriptor Table entry * @vector: Vector number in the IDT @@ -122,6 +124,19 @@ static void page_fault_handler(struct cpu_status_t* ctx) CHECK_BIT(ctx->error_code, 7) ? " SGX_VIOLATION" : "", cr2); + if (init.all) { + printf("\x1b[38;5;231mPage Fault at rip=0x%p, err=%u (%s%s%s%s%s%s%s%s) when accessing addr=0x%p\x1b[0m", ctx->iret_rip, ctx->error_code, + CHECK_BIT(ctx->error_code, 0) ? "PAGE_PROTECTION_VIOLATION " : "PAGE_NOT_PRESENT ", + CHECK_BIT(ctx->error_code, 1) ? "ON_WRITE " : "ON_READ ", + CHECK_BIT(ctx->error_code, 2) ? "IN_USER_MODE" : "IN_KERNEL_MODE", + CHECK_BIT(ctx->error_code, 3) ? " WAS_RESERVED" : "", + CHECK_BIT(ctx->error_code, 4) ? " ON_INSTRUCTION_FETCH" : "", + CHECK_BIT(ctx->error_code, 5) ? " PK_VIOLATION" : "", + CHECK_BIT(ctx->error_code, 6) ? " ON_SHADOWSTACK_ACCESS" : "", + CHECK_BIT(ctx->error_code, 7) ? " SGX_VIOLATION" : "", + cr2); + } + panic(ctx, "page fault"); } @@ -157,12 +172,6 @@ static void gp_fault_handler(struct cpu_status_t* ctx) panic(ctx, "gp fault"); } -// DEBUG -void kbdproc_main(void* arg) -{ - printf("Key pressed/released.\r\n"); -} - /* * interrupt_dispatch - Interrupt dispatcher * @context: CPU context @@ -261,7 +270,7 @@ struct cpu_status_t* interrupt_dispatch(struct cpu_status_t* context) case 33: // Keyboard Interrupt keyboard_handler(); - process_create("keyboard-initiated", kbdproc_main, NULL); // DEBUG + //process_create("keyboard-initiated", kbdproc_main, NULL); // DEBUG outb(0x20, 0x20); break; diff --git a/src/kapps/kshell.c b/src/kapps/kshell.c index b416d69..7090e6e 100644 --- a/src/kapps/kshell.c +++ b/src/kapps/kshell.c @@ -5,6 +5,11 @@ */ #include +#include +#include +#include +#include +#include /* * pedicel_main - Kernel shell main function @@ -13,8 +18,39 @@ * This is the entry point for the kernel shell process. * It is used to start programs and to test different things * on different real hardware easily. + * + * Named after the root part of the pepper. */ void pedicel_main(void* arg) { - printf("Entering the kernel shell...\r\n"); + printf("Welcome to the kernel shell!\r\nType 'help' for a list of commands.\r\n"); + + for (;;) { + char input_buf[PEDICEL_INPUT_SIZE] = {0}; + printf(PEDICEL_PROMPT); + keyboard_getline(input_buf, PEDICEL_INPUT_SIZE); + + if (strncmp(input_buf, "help", 4) == 0) { + printf("\r\npanic - trigger a test panic\r\n" + "syscall - trigger int 0x80\r\n" + "pf - trigger a page fault\r\n"); + continue; + } + + if (strncmp(input_buf, "panic", 5) == 0) { + panic(NULL, "test panic"); + } + + if (strncmp(input_buf, "syscall", 7) == 0) { + __asm__ volatile("int $0x80"); + continue; + } + + if (strncmp(input_buf, "pf", 2) == 0) { + volatile uint64_t* fault = (uint64_t*)0xdeadbeef; + fault[0] = 1; + } + + printf("%s: command not found\r\n", input_buf); + } } \ No newline at end of file diff --git a/src/string/string.c b/src/string/string.c index 3c7c7af..6b51c58 100644 --- a/src/string/string.c +++ b/src/string/string.c @@ -69,4 +69,33 @@ void strncpy(char* dst, const char* src, size_t n) { size_t i = 0; while(i++ != n && (*dst++ = *src++)); +} + + +/* + * strncmp - compare two strings up to n characters + * @s1: first string + * @s2: second string + * @n: number of bytes to compare + * + * Taken from: https://github.com/DevSolar/pdclib/blob/master/functions/string/strncmp.c + * + * Return: + * $0 - @s1 and @s2 are equal + * $<0 - @s1 is less than @s2 + * $>0 - @s1 is greater than @s2 + */ +int strncmp(const char* s1, const char* s2, size_t n) +{ + while ( n && *s1 && ( *s1 == *s2 ) ) { + ++s1; + ++s2; + --n; + } + if ( n == 0 ) { + return 0; + } + else { + return ( *(unsigned char *)s1 - *(unsigned char *)s2 ); + } } \ No newline at end of file From ac788c55d3e2c7968bf32db41e6b0a6c542728a7 Mon Sep 17 00:00:00 2001 From: xamidev Date: Sun, 22 Mar 2026 09:03:43 +0100 Subject: [PATCH 03/17] Upgrade VMM for processes --- include/config.h | 3 +- include/mem/paging.h | 2 + include/mem/vmm.h | 29 +++-- src/arch/x86/idt.S | 9 ++ src/arch/x86/idt.c | 8 +- src/kapps/kshell.c | 1 + src/kmain.c | 1 + src/mem/paging.c | 4 +- src/mem/vmm.c | 245 ++++++++++++++++++++++++++++++++++--------- 9 files changed, 233 insertions(+), 69 deletions(-) diff --git a/include/config.h b/include/config.h index 30a79f3..d553854 100644 --- a/include/config.h +++ b/include/config.h @@ -27,8 +27,7 @@ /* process */ #define PROCESS_NAME_MAX 64 #define PROCESS_STACK_SIZE 0x10000 // 64kb -#define PROCESS_BASE 0x400000 -#define PROCESS_STACK_BASE 0x1000000 +#define PROCESS_STACK_TOP 0x80000000 /* sched */ // 1 tick = 1 ms => quantum = 10ms diff --git a/include/mem/paging.h b/include/mem/paging.h index 91fc061..f5cf406 100644 --- a/include/mem/paging.h +++ b/include/mem/paging.h @@ -16,9 +16,11 @@ void paging_init(struct boot_context boot_ctx); void paging_map_page(uint64_t* root_table, uint64_t virt, uint64_t phys, uint64_t flags); +uint64_t* alloc_page_table(); // To swap root page tables void load_cr3(uint64_t value); +void invlpg(void *addr); extern uint64_t hhdm_off; diff --git a/include/mem/vmm.h b/include/mem/vmm.h index 3b60a84..64e1f0c 100644 --- a/include/mem/vmm.h +++ b/include/mem/vmm.h @@ -9,26 +9,21 @@ #include #include +#include -/* -This will be our linked list of virtual memory objects. -Flags here aren't x86 flags, they are platform-agnostic -kernel-defined flags. -*/ - -struct vm_object { - uintptr_t base; - size_t length; - size_t flags; - struct vm_object* next; +struct vmm_context { + uint64_t* pml4; }; -// Flags bitfield -#define VM_FLAG_NONE 0 -#define VM_FLAG_WRITE (1 << 0) -#define VM_FLAG_EXEC (1 << 1) -#define VM_FLAG_USER (1 << 2) - void vmm_init(void); +void* vmm_alloc_region(uint64_t* pml4, size_t pages, uint64_t flags); +bool vmm_is_mapped(uint64_t* pml4, uint64_t virt); +void vmm_unmap(uint64_t* pml4, uint64_t virt); +void* vmm_map(uint64_t* pml4, uint64_t virt, uint64_t flags); +uint64_t* vmm_create_address_space(); +uint64_t vmm_virt_to_phys(uint64_t* pml4, uint64_t virt); + +#define VMM_USER_SPACE_START 0x0000000000001000 +#define VMM_USER_SPACE_END 0x00007FFFFFFFF000 #endif \ No newline at end of file diff --git a/src/arch/x86/idt.S b/src/arch/x86/idt.S index 52eb314..c4e863c 100644 --- a/src/arch/x86/idt.S +++ b/src/arch/x86/idt.S @@ -32,6 +32,8 @@ global vector_19_handler global vector_20_handler global vector_21_handler +global vector_128_handler + interrupt_stub: ; We'll push all general-purpose registers to the stack, ; so they're intact and don't bother the code that was @@ -312,4 +314,11 @@ align 16 vector_33_handler: push qword 0 push qword 33 + jmp interrupt_stub + +; Syscall Interrupt (0x80) +align 16 +vector_128_handler: + push qword 0 + push qword 128 jmp interrupt_stub \ No newline at end of file diff --git a/src/arch/x86/idt.c b/src/arch/x86/idt.c index eb99a4a..c4b17c2 100644 --- a/src/arch/x86/idt.c +++ b/src/arch/x86/idt.c @@ -21,6 +21,8 @@ struct idtr idt_reg; // Address to our first interrupt handler extern char vector_0_handler[]; +extern char vector_128_handler[]; + // Timer ticks extern volatile uint64_t ticks; @@ -75,7 +77,7 @@ void idt_init() idt_set_entry(i, vector_0_handler + (i*16), 0); } - idt_set_entry(0x80, syscall_handler, 0); + idt_set_entry(0x80, vector_128_handler, 0); idt_load(&idt); DEBUG("IDT initialized"); @@ -273,6 +275,10 @@ struct cpu_status_t* interrupt_dispatch(struct cpu_status_t* context) //process_create("keyboard-initiated", kbdproc_main, NULL); // DEBUG outb(0x20, 0x20); break; + + case 128: // Syscall Interrupt (0x80) + syscall_handler(context); + break; default: DEBUG("Unexpected Interrupt"); diff --git a/src/kapps/kshell.c b/src/kapps/kshell.c index 7090e6e..b1c77f1 100644 --- a/src/kapps/kshell.c +++ b/src/kapps/kshell.c @@ -42,6 +42,7 @@ void pedicel_main(void* arg) } if (strncmp(input_buf, "syscall", 7) == 0) { + __asm__ volatile("mov $0x00, %rdi"); __asm__ volatile("int $0x80"); continue; } diff --git a/src/kmain.c b/src/kmain.c index d85ef7a..4f57851 100644 --- a/src/kmain.c +++ b/src/kmain.c @@ -124,5 +124,6 @@ void kmain() printf(PEPPEROS_SPLASH); init.all = true; + idle(); } diff --git a/src/mem/paging.c b/src/mem/paging.c index 44290ea..4b76536 100644 --- a/src/mem/paging.c +++ b/src/mem/paging.c @@ -43,7 +43,7 @@ void load_cr3(uint64_t value) { * This function is used to flush at least the TLB entrie(s) * for the page that contains the address. */ -static inline void invlpg(void *addr) +void invlpg(void *addr) { asm volatile("invlpg (%0)" :: "r"(addr) : "memory"); } @@ -59,7 +59,7 @@ static inline void invlpg(void *addr) * Return: * - Pointer to allocated page table */ -static uint64_t* alloc_page_table() +uint64_t* alloc_page_table() { uint64_t* virt = (uint64_t*)PHYS_TO_VIRT(pmm_alloc()); diff --git a/src/mem/vmm.c b/src/mem/vmm.c index 647d49b..46de4fc 100644 --- a/src/mem/vmm.c +++ b/src/mem/vmm.c @@ -19,60 +19,211 @@ compared to the PMM which allocs/frees 4kb frames ("physical pages"). #include #include -void* vmm_pt_root = 0; - -// Linked list head for virtual memory objects -struct vm_object* vm_objs = NULL; - -/* - * Will have to be rewritten and expanded, - * to prepare for userspace. - * The platform-agnostic flags will be removed - * because as long as the kernel is x86 only, - * we don't need over complication. - * Plus I don't plan to port to other architectures -*/ - -uint64_t convert_x86_vm_flags(size_t flags) -{ - uint64_t value = 0; - if (flags & VM_FLAG_WRITE) - { - value |= PTE_WRITABLE; - } - if (flags & VM_FLAG_USER) - { - value |= PTE_USER; - } - if ((flags & VM_FLAG_EXEC) == 0) - { - value |= PTE_NOEXEC; - } - return value; -} - extern uint64_t *kernel_pml4; -void vmm_setup_pt_root() +/* + * vmm_switch_to - Switch to a different VMM context + * @ctx: VMM context to switch to + * + * This function makes the CPU switch to another + * virtual memory context, by using the PML4 address + * specified in the VMM context pointed to by @ctx. + */ +void vmm_switch_to(struct vmm_context* ctx) { - // We alloc a physical page (frame) for the pointer, then map it - // to virt (pointer) - uintptr_t phys = pmm_alloc(); - vmm_pt_root = (void*)kernel_pml4; - paging_map_page(kernel_pml4, (uint64_t)vmm_pt_root, phys, convert_x86_vm_flags(VM_FLAG_WRITE | VM_FLAG_EXEC)); - DEBUG("VMM setup: vmm_pt_root=0x%p (phys=0x%p)", vmm_pt_root, phys); + if (!ctx || !ctx->pml4) { + panic(NULL, "Attempted to switch to bad PML4!"); + } + uint64_t pml4 = VIRT_TO_PHYS(ctx->pml4); + asm volatile ("mov %0, %%cr3" :: "r"(pml4) : "memory"); } -/* void* vmm_alloc(size_t length, size_t flags) +/* + * vmm_virt_to_phys - Translate from virtual to physical address + * @pml4: virtual address of the Page Map Level 4 (root page table) + * @virt: virtual address to translate + * + * This function goes through page table structures, beginning at + * the root page table which lives at @pml4, and translates @virt + * to a physical address, if it's found in the tables. + * + * Return: + * - physical address + * %-1 - address is not present in page tables pointed to by @pml4 + */ +uint64_t vmm_virt_to_phys(uint64_t* pml4, uint64_t virt) { - // We will try to allocate at least length bytes, which have to be rounded UP to - // the next page so its coherent with the PMM - size_t len = ALIGN_UP(length, PAGE_SIZE); + uint64_t pml4_i = PML4_INDEX(virt); + uint64_t pdpt_i = PDPT_INDEX(virt); + uint64_t pd_i = PD_INDEX(virt); + uint64_t pt_i = PT_INDEX(virt); - // Need to implement this (as linked list) - // but for now kernel heap is sufficient - // The VMM will prob be more useful when we have userspace -} */ + if (!(pml4[pml4_i] & PTE_PRESENT)) return -1; + uint64_t* pdpt = (uint64_t*)PHYS_TO_VIRT(pml4[pml4_i] & PTE_ADDR_MASK); + + if (!(pdpt[pdpt_i] & PTE_PRESENT)) return -1; + uint64_t* pd = (uint64_t*)PHYS_TO_VIRT(pdpt[pdpt_i] & PTE_ADDR_MASK); + + if (!(pd[pd_i] & PTE_PRESENT)) return -1; + uint64_t* pt = (uint64_t*)PHYS_TO_VIRT(pd[pd_i] & PTE_ADDR_MASK); + + if (!(pt[pt_i] & PTE_PRESENT)) return -1; + + uint64_t phys = (pt[pt_i] & PTE_ADDR_MASK) + (virt & 0xFFF); + return phys; +} + +/* + * vmm_create_address_space - Create a new address space + * + * This function allocates a PML4, and then copies the kernel + * page tables into it. + * + * Return: + * - address of the new PML4 + * NULL - on error (couldn't allocate a page table) + */ +uint64_t* vmm_create_address_space() +{ + uint64_t* pml4 = alloc_page_table(); + if (!pml4) return NULL; + + for (size_t i=256; i<512; i++) { + pml4[i] = kernel_pml4[i]; + } + + return pml4; +} + +/* + * vmm_map - Map & allocate a page + * @pml4: Page Map Level 4 (root table) + * @virt: Virtual address to map + * @flags: Flags to apply on page + * + * This function allocates a page frame with the PMM, + * and maps this page to the provided @virt address, + * with the corresponding @flags. + * + * Return: + * - virtual address + */ +void* vmm_map(uint64_t* pml4, uint64_t virt, uint64_t flags) +{ + uint64_t phys = pmm_alloc(); + if (!phys) { + panic(NULL, "VMM/PMM out of memory!"); + } + + paging_map_page(pml4, virt, phys, flags | PTE_PRESENT); + return (void*)virt; +} + +/* + * vmm_unmap - Unmap & free a page + * @pml4: Page Map Level 4 (root table) + * @virt: Virtual address to unmap + * + * This function frees a page frame with the PMM, + * and unmaps the virtual page at @virt. + */ +void vmm_unmap(uint64_t* pml4, uint64_t virt) +{ + uint64_t pml4_i = PML4_INDEX(virt); + uint64_t pdpt_i = PDPT_INDEX(virt); + uint64_t pd_i = PD_INDEX(virt); + uint64_t pt_i = PT_INDEX(virt); + + if (!(pml4[pml4_i] & PTE_PRESENT)) return; + uint64_t* pdpt = (uint64_t*)PHYS_TO_VIRT(pml4[pml4_i] & PTE_ADDR_MASK); + + if (!(pdpt[pdpt_i] & PTE_PRESENT)) return; + uint64_t* pd = (uint64_t*)PHYS_TO_VIRT(pdpt[pdpt_i] & PTE_ADDR_MASK); + + if (!(pd[pd_i] & PTE_PRESENT)) return; + uint64_t* pt = (uint64_t*)PHYS_TO_VIRT(pd[pd_i] & PTE_ADDR_MASK); + + if (!(pt[pt_i] & PTE_PRESENT)) return; + + uint64_t phys = pt[pt_i] & PTE_ADDR_MASK; + pmm_free(phys); + + pt[pt_i] = 0; + + invlpg((void*)virt); +} + +/* + * vmm_is_mapped - Check if an address is mapped + * @pml4: Page Map Level 4 (root table) + * @virt: Virtual address to check + * + * This function checks if the @virt address is + * mapped in the tables pointed to by @pml4. + * + * Return: + * true - @virt is mapped in tables of @pml4 + * false - @virt is not mapped there + */ +bool vmm_is_mapped(uint64_t* pml4, uint64_t virt) +{ + uint64_t pml4_i = PML4_INDEX(virt); + uint64_t pdpt_i = PDPT_INDEX(virt); + uint64_t pd_i = PD_INDEX(virt); + uint64_t pt_i = PT_INDEX(virt); + + if (!(pml4[pml4_i] & PTE_PRESENT)) return false; + uint64_t* pdpt = (uint64_t*)PHYS_TO_VIRT(pml4[pml4_i] & PTE_ADDR_MASK); + + if (!(pdpt[pdpt_i] & PTE_PRESENT)) return false; + uint64_t* pd = (uint64_t*)PHYS_TO_VIRT(pdpt[pdpt_i] & PTE_ADDR_MASK); + + if (!(pd[pd_i] & PTE_PRESENT)) return false; + uint64_t* pt = (uint64_t*)PHYS_TO_VIRT(pd[pd_i] & PTE_ADDR_MASK); + + return (pt[pt_i] & PTE_PRESENT); +} + +/* + * vmm_alloc_range - Map and allocate a memory range + * @pml4: Page Map Level 4 (root table) + * @pages: Amount of pages to allocate/map + * @flags: Flags to put on mapped pages + * + * This function looks for enough space in page tables + * to map @pages pages, then maps them into the provided + * @pml4 with the provided @flags and allocates them. + * + * Return: + * - the starting virtual address for the mapped range + */ +void* vmm_alloc_region(uint64_t* pml4, size_t pages, uint64_t flags) +{ + uint64_t found_pages = 0; + uint64_t start_virt = VMM_USER_SPACE_START; + + for (uint64_t curr = VMM_USER_SPACE_START; curr < VMM_USER_SPACE_END; curr += PAGE_SIZE) { + if (!vmm_is_mapped(pml4, curr)) { + if (found_pages == 0) start_virt = curr; + found_pages++; + } else { + found_pages = 0; + } + + if (found_pages == pages) { + for (size_t i = 0; i < pages; i++) { + uint64_t addr_to_map = start_virt + (i * PAGE_SIZE); + if (!vmm_map(pml4, addr_to_map, flags)) { + panic(NULL, "VMM out of memory!"); + } + } + return (void*)start_virt; + } + } + + panic(NULL, "VMM out of memory!"); + return NULL; +} void vmm_init() { From 532953da4d3bb2fb2e8bbd3ccbd2dc5eb5956351 Mon Sep 17 00:00:00 2001 From: xamidev Date: Thu, 26 Mar 2026 16:53:39 +0100 Subject: [PATCH 04/17] CPU Name identification --- include/arch/x86.h | 4 ++++ src/arch/x86/cpuid.c | 27 +++++++++++++++++++++++++ src/arch/x86/init.c | 47 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 78 insertions(+) diff --git a/include/arch/x86.h b/include/arch/x86.h index 2c04dcf..0e6ad58 100644 --- a/include/arch/x86.h +++ b/include/arch/x86.h @@ -10,6 +10,10 @@ void wrmsr(uint32_t msr, uint64_t value); bool x86_has_msr(); void x86_arch_init(); + +void x86_cpu_identification(); +int cpuid_get_vendor_string(char* str); + /* Interrupt Descriptor Table */ void idt_init(void); diff --git a/src/arch/x86/cpuid.c b/src/arch/x86/cpuid.c index b4a9a55..bbe31a4 100644 --- a/src/arch/x86/cpuid.c +++ b/src/arch/x86/cpuid.c @@ -6,6 +6,8 @@ #include #include +#include +#include /* * cpuid - Wrapper for CPUID instruction @@ -18,4 +20,29 @@ void cpuid(uint32_t leaf, uint32_t* eax, uint32_t* ebx, uint32_t* ecx, uint32_t* edx) { __asm__ volatile("cpuid" : "=a"(*eax), "=b"(*ebx), "=c"(*ecx), "=d"(*edx) : "a"(leaf)); +} + +/* + * cpuid_get_vendor_string - Get the CPU vendor string + * @str: String at least 13 bytes long (for output) + * + * Return: + * %0 - on success + */ +int cpuid_get_vendor_string(char* str) +{ + uint32_t eax, ebx, ecx, edx; + + cpuid(0, &eax, &ebx, &ecx, &edx); + char output[13] = {0}; + + uint32_t regs[3] = {ebx, edx, ecx}; + for (unsigned int j=0; j<3; j++) { + for (unsigned int i=0; i<4; i++) { + output[4*j+i] = (char)((regs[j] >> 8*i) & 0xff); + } + } + + strncpy(str, output, 13); + return 0; } \ No newline at end of file diff --git a/src/arch/x86/init.c b/src/arch/x86/init.c index 9d74333..bc4b461 100644 --- a/src/arch/x86/init.c +++ b/src/arch/x86/init.c @@ -8,6 +8,7 @@ #include #include #include +#include /* * x86_overwrite_pat - Set PAT to WC @@ -41,6 +42,52 @@ static void x86_overwrite_pat() void x86_arch_init() { x86_overwrite_pat(); + x86_cpu_identification(); idt_init(); gdt_init(); +} + +/* + * cpu_supports_brandstring - Does the CPU support brand strings? + * + * Return: + * true - if it does + * false - if it doesn't +*/ +bool cpu_supports_brandstring() { + uint32_t eax, ebx, ecx, edx; + cpuid(0x80000000, &eax, &ebx, &ecx, &edx); + + if (eax < 0x80000004) { + return false; + } else { + return true; + } +} + +/* + * x86_cpu_idenfitication - get info about the CPU + * + * This function displays the CPU vendor name or the + * extended "brand string" if it's supported, on + * debug output. +*/ +void x86_cpu_identification() +{ + if (cpu_supports_brandstring()) { + uint32_t regs[12]; + // Some CPUs don't return null-terminated values so we do it as a failsafe default + char str[sizeof(regs)+1] = {0}; + cpuid(0x80000002, ®s[0], ®s[1], ®s[2], ®s[3]); + cpuid(0x80000003, ®s[4], ®s[5], ®s[6], ®s[7]); + cpuid(0x80000004, ®s[8], ®s[9], ®s[10], ®s[11]); + + memcpy(str, regs, sizeof(regs)); + str[sizeof(regs)] = '\0'; + DEBUG("CPU: %s", str); + } else { + char vendor_string[13] = {0}; + cpuid_get_vendor_string(vendor_string); + DEBUG("CPU vendor is: %s", vendor_string); + } } \ No newline at end of file From 0fbaf6d26e3fcb59b536357f3e45b75c1c3d6434 Mon Sep 17 00:00:00 2001 From: xamidev Date: Thu, 26 Mar 2026 17:59:02 +0100 Subject: [PATCH 05/17] Date functions (get current time) --- include/config.h | 2 +- include/kernel.h | 2 + include/mem/utils.h | 1 + include/time/date.h | 25 +++++++++++++ src/boot/boot.c | 6 +++ src/kapps/kshell.c | 16 +++++++- src/kmain.c | 4 ++ src/time/date.c | 89 +++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 142 insertions(+), 3 deletions(-) create mode 100644 include/time/date.h create mode 100644 src/time/date.c diff --git a/include/config.h b/include/config.h index d553854..f557563 100644 --- a/include/config.h +++ b/include/config.h @@ -10,7 +10,7 @@ /* version */ #define PEPPEROS_VERSION_MAJOR "0" #define PEPPEROS_VERSION_MINOR "0" -#define PEPPEROS_VERSION_PATCH "58" +#define PEPPEROS_VERSION_PATCH "109" #define PEPPEROS_SPLASH \ "\x1b[38;5;196m \x1b[38;5;231m____ _____\r\n\x1b[0m"\ "\x1b[38;5;196m ____ ___ ____ ____ ___ _____\x1b[38;5;231m/ __ \\/ ___/\r\n\x1b[0m"\ diff --git a/include/kernel.h b/include/kernel.h index 116ba09..04836ca 100644 --- a/include/kernel.h +++ b/include/kernel.h @@ -7,6 +7,7 @@ #ifndef KERNEL_H #define KERNEL_H +#include "limine.h" enum ErrorCodes { ENOMEM, EIO @@ -52,6 +53,7 @@ struct boot_context { struct limine_memmap_response* mmap; struct limine_hhdm_response* hhdm; struct limine_kernel_address_response* kaddr; + struct limine_boot_time_response* bootdate; }; // Are these modules initialized yet? diff --git a/include/mem/utils.h b/include/mem/utils.h index 197bf12..e3162e4 100644 --- a/include/mem/utils.h +++ b/include/mem/utils.h @@ -8,6 +8,7 @@ #define MEM_UTILS_H #include +#include void* memcpy(void* restrict dest, const void* restrict src, size_t n); void* memset(void* s, int c, size_t n); diff --git a/include/time/date.h b/include/time/date.h new file mode 100644 index 0000000..f3bf2a6 --- /dev/null +++ b/include/time/date.h @@ -0,0 +1,25 @@ +/* + * @author xamidev + * @brief Date helper functions + * @license GPL-3.0-only + */ + +#ifndef DATE_H +#define DATE_H + +#include + +struct date { + uint64_t year; + uint8_t month; + uint8_t day; + + uint8_t hour; + uint8_t minute; + uint8_t second; +}; + +struct date date_timestamp_to_date(uint64_t timestamp); +struct date date_now(); + +#endif \ No newline at end of file diff --git a/src/boot/boot.c b/src/boot/boot.c index 966f202..73d1324 100644 --- a/src/boot/boot.c +++ b/src/boot/boot.c @@ -35,6 +35,12 @@ volatile struct limine_kernel_address_request kerneladdr_request = { .revision = 0 }; +__attribute__((used, section(".limine_requests"))) +volatile struct limine_boot_time_request date_request = { + .id = LIMINE_BOOT_TIME_REQUEST, + .revision = 0 +}; + __attribute__((used, section(".limine_requests_start"))) volatile LIMINE_REQUESTS_START_MARKER; diff --git a/src/kapps/kshell.c b/src/kapps/kshell.c index b1c77f1..eaef7ed 100644 --- a/src/kapps/kshell.c +++ b/src/kapps/kshell.c @@ -10,6 +10,7 @@ #include #include #include +#include