diff --git a/Makefile b/Makefile index b2499c8..1542d6d 100644 --- a/Makefile +++ b/Makefile @@ -2,16 +2,26 @@ BUILDDIR := build ELFFILE := pepperk SRC := src + +CC_PROBLEMATIC_FLAGS:=-Wno-unused-parameter -Wno-unused-variable + +ifeq ($(UBSAN),true) SOURCES := $(shell find src -name '*.c') +CC_PROBLEMATIC_FLAGS:= -fsanitize=undefined +else +SOURCES := $(shell find src -name '*.c') +SOURCES := $(filter-out src/security/ubsan.c, $(SOURCES)) +endif + OBJFILES := $(patsubst $(SRC)/%.c, $(BUILDDIR)/%.o, $(SOURCES)) CC := x86_64-elf-gcc -CC_FLAGS=-Wall -Wextra -std=gnu99 -nostdlib -ffreestanding -fno-stack-protector -fno-omit-frame-pointer -fno-stack-check -fno-PIC -ffunction-sections -fdata-sections -mcmodel=kernel -CC_PROBLEMATIC_FLAGS=-Wno-unused-parameter -Wno-unused-variable +CC_FLAGS=-Wall -Wextra -std=gnu99 -nostdlib -ffreestanding -fstack-protector -fno-omit-frame-pointer -fno-stack-check -fno-PIC -ffunction-sections -fdata-sections -mcmodel=kernel LD := x86_64-elf-ld $(ELFFILE): $(BUILDDIR) $(OBJFILES) + nasm -f bin user/hello.S -o $(BUILDDIR)/hello nasm -f elf64 src/arch/x86/idt.S -o $(BUILDDIR)/idt_stub.o $(LD) -o $(ELFFILE) -T linker.ld $(OBJFILES) $(BUILDDIR)/idt_stub.o # Get the symbols for debugging @@ -38,6 +48,7 @@ build-iso: limine/limine $(ELFFILE) cp -v $(ELFFILE) iso_root/boot mkdir -p iso_root/boot/limine cp -v limine.conf iso_root/boot/limine + cp $(BUILDDIR)/hello iso_root/boot/ mkdir -p iso_root/EFI/BOOT cp -v limine/limine-bios.sys limine/limine-bios-cd.bin limine/limine-uefi-cd.bin iso_root/boot/limine/ cp -v limine/BOOTX64.EFI iso_root/EFI/BOOT/ diff --git a/README.md b/README.md index 8ad2ad5..33254bb 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,20 @@ Compile the kernel and generate an ISO image like described above, then burn the sudo dd if=pepper.iso of=/dev/sdX ``` +## Compilation options + +By default, PepperOS is compiled without the following features: + +``` +UBSAN - undefined behavior sanitization +``` + +These features can be activated by setting them to "true" at the end of the make command, for example: + +``` +make UBSAN=true +``` + ## TODO The basics that I'm targeting are: @@ -73,4 +87,6 @@ PepperOS wouldn't be possible without the following freely-licensed software: - the [OSDev](https://osdev.org) wiki & forums - Intel 64 and IA-32 Architectures Software Developer's Manual - Documentation for the [GNU Compiler Collection](https://gcc.gnu.org/onlinedocs/gcc/) -- dreamos82's [OSDev Notes](https://github.com/dreamportdev/Osdev-Notes/tree/master) \ No newline at end of file +- dreamos82's [OSDev Notes](https://github.com/dreamportdev/Osdev-Notes/tree/master) +- the [Sortix UBsan hook implementations](https://gitlab.com/sortix/sortix/-/blob/main/libc/ubsan/ubsan.c) +- the [CSC 395](https://curtsinger.cs.grinnell.edu/teaching/2022S/CSC395/kernel/) Kernel Development course from Grinnell College \ No newline at end of file diff --git a/include/mem/gdt.h b/include/arch/gdt.h similarity index 64% rename from include/mem/gdt.h rename to include/arch/gdt.h index abe3eba..f2297b9 100644 --- a/include/mem/gdt.h +++ b/include/arch/gdt.h @@ -1,6 +1,6 @@ /* * @author xamidev - * @brief Global Descriptor Table (for legacy reasons) + * @brief Global Descriptor Table * @license GPL-3.0-only */ @@ -13,19 +13,32 @@ // we'll only use this as a requirement for paging, not more. // This means base 0 and no limit (whole address space) -#define NUM_GDT_ENTRIES 5 +#define NUM_GDT_ENTRIES 7 #define NULL_SELECTOR 0x00 #define KERNEL_CODE_SEGMENT 0x08 #define KERNEL_DATA_SEGMENT 0x10 #define USER_CODE_SEGMENT 0x18 #define USER_DATA_SEGMENT 0x20 +#define TSS_SEGMENT 0x28 struct GDTR { uint16_t limit; uint64_t address; } __attribute__((packed)); +struct tss { + uint32_t reserved0; + uint64_t rsp0; + uint64_t rsp1; + uint64_t rsp2; + uint64_t reserved1; + uint64_t ist[7]; + uint64_t reserved2; + uint16_t reserved3; + uint16_t iopb; +} __attribute__((packed)); + void gdt_init(void); #endif \ No newline at end of file diff --git a/include/arch/x86.h b/include/arch/x86.h index 1c677d8..b0b9029 100644 --- a/include/arch/x86.h +++ b/include/arch/x86.h @@ -10,6 +10,9 @@ 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); @@ -32,7 +35,7 @@ struct idtr { // All general-purpose registers (except rsp) as stored on the stack, // plus the values we pushed (vector number, error code) and the iret frame // In reverse order because the stack grows downwards. -struct cpu_status_t { +struct cpu_status { uint64_t r15; uint64_t r14; uint64_t r13; @@ -59,4 +62,6 @@ struct cpu_status_t { uint64_t iret_ss; }; +struct cpu_status* syscall_handler(struct cpu_status* regs); + #endif \ No newline at end of file diff --git a/include/boot/boot.h b/include/boot/boot.h new file mode 100644 index 0000000..cc67dd8 --- /dev/null +++ b/include/boot/boot.h @@ -0,0 +1,14 @@ +/* + * @author xamidev + * @brief Boot routines + * @license GPL-3.0-only + */ + +#ifndef BOOT_H +#define BOOT_H + +#include + +void populate_boot_context(struct boot_context* boot_ctx); + +#endif \ No newline at end of file diff --git a/include/config.h b/include/config.h index 3a1e9e3..f3564ec 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"\ @@ -20,11 +20,14 @@ "\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 -#define PROCESS_BASE 0x400000 -#define PROCESS_STACK_BASE 0x1000000 +#define PROCESS_STACK_TOP 0x80000000 /* sched */ // 1 tick = 1 ms => quantum = 10ms @@ -37,6 +40,11 @@ #define KERNEL_STACK_SIZE 65536 #define KERNEL_IDT_ENTRIES 33 +/* user */ +#define USER_STACK_TOP 0x80000000 +#define USER_STACK_PAGES 16 // 16*4096 = 64kb +#define USER_CODE_START 0x400000 // like linux + /* paging */ #define PAGING_MAX_PHYS 0x200000000 @@ -52,4 +60,7 @@ /* time */ #define TIMER_FREQUENCY 1000 +/* ssp */ +#define STACK_CHK_GUARD 0x7ABA5C007ABA5C00 + #endif 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..7a9712b 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 @@ -21,7 +22,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__); \ @@ -34,10 +35,12 @@ extern volatile uint64_t ticks; // printf("debug: [%s]: " log "\n", __FILE__, ##__VA_ARGS__); -void panic(struct cpu_status_t* ctx, const char* str); +void panic(struct cpu_status* 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); @@ -50,6 +53,8 @@ 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; + struct limine_module_response* module; }; // Are these modules initialized yet? @@ -58,6 +63,7 @@ struct init_status { bool serial; bool keyboard; bool timer; + bool all; }; #endif diff --git a/include/mem/kheap.h b/include/mem/kheap.h index ee4672d..08c7766 100644 --- a/include/mem/kheap.h +++ b/include/mem/kheap.h @@ -16,11 +16,11 @@ #include #include -struct heap_block_t { +struct heap_block { size_t size; bool free; // 1byte uint8_t reserved[7]; // (7+1 = 8 bytes) - struct heap_block_t* next; + struct heap_block* next; } __attribute__((aligned(16))); void kheap_init(void); @@ -28,5 +28,6 @@ void* kmalloc(size_t size); void kfree(void* ptr); void* kalloc_stack(void); void kheap_map_page(void); +void kheap_info(); #endif \ No newline at end of file diff --git a/include/mem/paging.h b/include/mem/paging.h index 91fc061..e7b38fe 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; @@ -32,7 +34,7 @@ extern uint64_t hhdm_off; #define PAGE_ALIGN_DOWN(x) ((x) & PTE_ADDR_MASK) #define ALIGN(size) ALIGN_UP(size, 16) -#define BLOCK_MIN_SIZE (sizeof(struct heap_block_t) + 16) +#define BLOCK_MIN_SIZE (sizeof(struct heap_block) + 16) #define PML4_INDEX(x) (((x) >> 39) & 0x1FF) #define PDPT_INDEX(x) (((x) >> 30) & 0x1FF) 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/mem/vmm.h b/include/mem/vmm.h index 3b60a84..5383622 100644 --- a/include/mem/vmm.h +++ b/include/mem/vmm.h @@ -9,26 +9,23 @@ #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); +uintptr_t vmm_alloc_user_stack(uint64_t* pml4); +uintptr_t vmm_alloc_user_code(uint64_t* pml4, void* code_addr, uint64_t code_size); + +#define VMM_USER_SPACE_START 0x0000000000001000 +#define VMM_USER_SPACE_END 0x00007FFFFFFFF000 #endif \ No newline at end of file diff --git a/include/sched/process.h b/include/sched/process.h index 02a313a..ef00d3e 100644 --- a/include/sched/process.h +++ b/include/sched/process.h @@ -10,6 +10,7 @@ #include #include #include +#include typedef enum { READY, @@ -17,23 +18,26 @@ typedef enum { DEAD } status_t; -struct process_t { +struct process { size_t pid; char name[PROCESS_NAME_MAX]; status_t status; - struct cpu_status_t* context; + struct cpu_status* context; void* root_page_table; // Process PML4 (should contain kernel PML4 in higher half [256-511] - struct process_t* next; + void* kernel_stack; // Used for interrupts (syscall: int 0x80), defines the TSS RSP0 + struct process* next; }; void process_init(void); -struct process_t* process_create(char* name, void(*function)(void*), void* arg); -void process_add(struct process_t** processes_list, struct process_t* process); -void process_delete(struct process_t** processes_list, struct process_t* process); -struct process_t* process_get_next(struct process_t* process); +struct process* process_create(char* name, void(*function)(void*), void* arg); +void process_add(struct process** processes_list, struct process* process); +void process_delete(struct process** processes_list, struct process* process); +struct process* process_get_next(struct process* process); void process_exit(void); -void process_display_list(struct process_t* processes_list); +void process_display_list(struct process* processes_list); + +void process_create_user(struct limine_file* file); #endif diff --git a/include/sched/scheduler.h b/include/sched/scheduler.h index 071321e..89251e6 100644 --- a/include/sched/scheduler.h +++ b/include/sched/scheduler.h @@ -7,7 +7,7 @@ #ifndef SCHEDULER_H #define SCHEDULER_H -struct cpu_status_t* scheduler_schedule(struct cpu_status_t* context); +struct cpu_status* scheduler_schedule(struct cpu_status* context); void scheduler_init(void); #endif \ No newline at end of file diff --git a/include/sched/spinlock.h b/include/sched/spinlock.h index 849d99d..ea4d9dd 100644 --- a/include/sched/spinlock.h +++ b/include/sched/spinlock.h @@ -1,4 +1,4 @@ -/* + /* * @author xamidev * @brief Spinlock implementation * @license GPL-3.0-only @@ -10,13 +10,13 @@ #include #include -struct spinlock_t +struct spinlock { bool locked; uint64_t rflags; }; -void spinlock_acquire(struct spinlock_t* lock); -void spinlock_release(struct spinlock_t* lock); +void spinlock_acquire(struct spinlock* lock); +void spinlock_release(struct spinlock* lock); #endif \ No newline at end of file diff --git a/include/security/ubsan.h b/include/security/ubsan.h new file mode 100644 index 0000000..0282a02 --- /dev/null +++ b/include/security/ubsan.h @@ -0,0 +1,65 @@ +/* + * @author xamidev + * @brief Undefined behavior sanitization hooks + * @license GPL-3.0-only + */ + +#ifndef UBSAN_H +#define UBSAN_H + +#include + +struct ubsan_source_location +{ + const char* filename; + uint32_t line; + uint32_t column; +}; + +struct ubsan_type_descriptor +{ + uint16_t type_kind; + uint16_t type_info; + char type_name[]; +}; + +struct ubsan_type_mismatch_v1_data +{ + struct ubsan_source_location location; + struct ubsan_type_descriptor* type; + unsigned char log_alignment; + unsigned char type_check_kind; +}; + +struct ubsan_pointer_overflow_data +{ + struct ubsan_source_location location; +}; + +struct ubsan_shift_out_of_bounds_data +{ + struct ubsan_source_location location; + struct ubsan_type_descriptor* lhs_type; + struct ubsan_type_descriptor* rhs_type; +}; + +struct ubsan_invalid_value_data +{ + struct ubsan_source_location location; + struct ubsan_type_descriptor* type; +}; + +struct ubsan_out_of_bounds_data +{ + struct ubsan_source_location location; + struct ubsan_type_descriptor* array_type; + struct ubsan_type_descriptor* index_type; +}; + +struct ubsan_overflow_data +{ + struct ubsan_source_location location; + struct ubsan_type_descriptor* type; +}; + +#endif \ No newline at end of file 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/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/limine.conf b/limine.conf index 71825ed..980ace0 100644 --- a/limine.conf +++ b/limine.conf @@ -6,3 +6,4 @@ interface_branding: Welcome to the PepperOS disk! comment: Default configuration (warning: spicy) path: boot():/boot/pepperk + module_path: boot():/boot/hello 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/gdt.c b/src/arch/x86/gdt.c new file mode 100644 index 0000000..9f2100e --- /dev/null +++ b/src/arch/x86/gdt.c @@ -0,0 +1,138 @@ +/* + * @author xamidev + * @brief Global Descriptor Table and Task State Segment setup + * @license GPL-3.0-only + */ + +#include +#include +#include +#include + +// Descriptors are 8-byte wide (64bits) +// So the selectors will be (in bytes): 0x0, 0x8, 0x10, 0x18, etc.. +uint64_t gdt_entries[NUM_GDT_ENTRIES]; +struct GDTR gdtr; + +struct tss tss = {0}; + +/* + * gdt_load - Loads Global Descriptor Table + */ +static void gdt_load() +{ + asm("lgdt %0" : : "m"(gdtr)); +} + +/* + * gdt_flush - Flushes the Global Descriptor Table + * + * This function loads new Segment Selectors to make + * the GDT changes take effect + */ +static void gdt_flush() +{ + // Here, 0x8 is the kernel code selector + // and 0x10 is the kernel data selector + asm volatile ( + "mov $0x10, %%ax \n" // Reload segments with kernel data selector + "mov %%ax, %%ds \n" + "mov %%ax, %%es \n" + "mov %%ax, %%fs \n" + "mov %%ax, %%gs \n" + "mov %%ax, %%ss \n" + + "pushq $0x8 \n" // CS reload + "lea 1f(%%rip), %%rax \n" + "push %%rax \n" + "lretq \n" + "1: \n" // Execution continues here after CS reload + : + : + : "rax", "memory" + ); +} + +/* + * get_set_entry - Sets a GDT entry + * @num: Number of the entry (index in GDT) + * @flags: Flags (Granularity, Size, Long mode) + * @access: Access byte (contains Descriptor Privilege Level) + * + * This function fills a GDT entry with the specified @flags + * and @access byte. The base and limit fields are left to zero + * because we don't use segmentation for memory management. + */ + +static void gdt_set_entry(int num, uint8_t flags, uint8_t access) +{ + uint64_t gdt_entry = 0; + + gdt_entry |= (access << 8); + gdt_entry |= (flags << 20); + + // Rest (base, limit) is always zero + gdt_entries[num] = gdt_entry << 32; +} + +/* + * gdt_set_tss - Setup the TSS entry in the GDT + * @num: Number of the entry (index in GDT) + * + * This function sets up a Task State Segment entry + * in the Global Descriptor Table. + * + * The entry is 128-bit long, so it actually takes + * two 64-bit GDT entries. + */ +static void gdt_set_tss(int num) +{ + uint64_t tss_base = (uint64_t)&tss; + uint64_t tss_limit = sizeof(struct tss) - 1; + + tss.iopb = sizeof(struct tss); + + uint64_t tss_low = 0; + tss_low |= (tss_limit & 0xFFFFULL); + tss_low |= (tss_base & 0xFFFFFFULL) << 16; + tss_low |= 0x89ULL << 40; + tss_low |= ((tss_limit >> 16) & 0xFULL) << 48; + tss_low |= ((tss_base >> 24) & 0xFFULL) << 56; + + uint64_t tss_high = (tss_base >> 32) & 0xFFFFFFFFULL; + + gdt_entries[num] = tss_low; + gdt_entries[num + 1] = tss_high; +} + +/* + * gdt_init - Global Descriptor Table initialization + * + * This function loads a new GDT in the CPU. + * It contains a null descriptor, kernel code and data + * segments, and user code and data segments. + * However, we do not use segmentation to manage memory on + * 64-bit x86, as it's deprecated. Instead, we use paging. + */ +void gdt_init() +{ + gdt_set_entry(0, 0, 0); // Null descriptor (0x0) + gdt_set_entry(1, 0xA, 0x9B); // Kernel code (0x8) + gdt_set_entry(2, 0xC, 0x93); // Kernel data (0x10) + gdt_set_entry(3, 0xA, 0xFB); // User code (0x18) + gdt_set_entry(4, 0xC, 0xF3); // User data (0x20) + gdt_set_tss(5); // TSS (0x28) + + // The -1 subtraction is some wizardry explained in the OSDev wiki -> GDT + gdtr.limit = NUM_GDT_ENTRIES * sizeof(uint64_t) - 1; + gdtr.address = (uint64_t)gdt_entries; + + // Load the GDT we created, flush the old one + gdt_load(); + gdt_flush(); + + // Load task register with new TSS + asm volatile("ltr %%ax" : : "a"(TSS_SEGMENT) : "memory"); + + DEBUG("GDT initialized"); +} \ 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 822033c..4a29524 100644 --- a/src/arch/x86/idt.c +++ b/src/arch/x86/idt.c @@ -21,9 +21,13 @@ 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; +extern struct init_status init; + /* * idt_set_entry - Sets an Interrupt Descriptor Table entry * @vector: Vector number in the IDT @@ -72,6 +76,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, vector_128_handler, 3); + idt_load(&idt); DEBUG("IDT initialized"); } @@ -101,7 +108,7 @@ static inline uint64_t read_cr2(void) * Also displays an interpretation of the thrown error code. * Then halts the system. We could implement demand paging later. */ -static void page_fault_handler(struct cpu_status_t* ctx) +static void page_fault_handler(struct cpu_status* ctx) { // It could be used to remap pages etc. to fix the fault, but right now what I'm more // interested in is getting more info out of those numbers cause i'm lost each time i have @@ -119,6 +126,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"); } @@ -129,7 +149,7 @@ static void page_fault_handler(struct cpu_status_t* ctx) * Shows detail about a General Protection Fault, * and what may have caused it. Halts the system. */ -static void gp_fault_handler(struct cpu_status_t* ctx) +static void gp_fault_handler(struct cpu_status* ctx) { DEBUG("\x1b[38;5;231mGeneral Protection Fault at rip=0x%p, err=%u (%s)\x1b[0m", ctx->iret_rip, @@ -154,12 +174,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 @@ -171,7 +185,7 @@ void kbdproc_main(void* arg) * Return: * - CPU context after interrupt */ -struct cpu_status_t* interrupt_dispatch(struct cpu_status_t* context) +struct cpu_status* interrupt_dispatch(struct cpu_status* context) { if (context == NULL) { panic(NULL, "Interrupt dispatch recieved NULL context!"); @@ -250,17 +264,22 @@ struct cpu_status_t* interrupt_dispatch(struct cpu_status_t* context) // Send an EOI so that we can continue having interrupts outb(0x20, 0x20); - if (ticks % SCHEDULER_QUANTUM == 0) { + // Scheduler is temporarily disabled to test user trampoline + /* if (ticks % SCHEDULER_QUANTUM == 0) { return scheduler_schedule(context); - } + } */ break; 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; + + case 128: // Syscall Interrupt (0x80) + syscall_handler(context); + break; default: DEBUG("Unexpected Interrupt"); diff --git a/src/arch/x86/init.c b/src/arch/x86/init.c index 9d74333..997793a 100644 --- a/src/arch/x86/init.c +++ b/src/arch/x86/init.c @@ -4,10 +4,11 @@ * @license GPL-3.0-only */ -#include +#include #include #include #include +#include /* * x86_overwrite_pat - Set PAT to WC @@ -28,6 +29,26 @@ static void x86_overwrite_pat() wrmsr(0x277, pat); } +/* + * x86_enable_fpu - Enable Floating Point Unit + * + * This function enables the Floating Point Unit, + * which allows the CPU to do floating point + * operations. + * + * Here we do not check for FPU support but we + * should. However most processors support it. + */ +static void x86_enable_fpu() +{ + size_t cr4; + __asm__ volatile("mov %%cr4, %0" : "=r"(cr4)); + cr4 |= 0x200; + __asm__ volatile("mov %0, %%cr4" :: "r"(cr4)); + uint16_t cw = 0x37F; // control word + asm volatile("fldcw %0" :: "m"(cw)); +} + /* * x86_arch_init - Initialize x86 CPU structures * @@ -41,6 +62,53 @@ static void x86_overwrite_pat() void x86_arch_init() { x86_overwrite_pat(); + x86_enable_fpu(); + 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 diff --git a/src/arch/x86/syscall.c b/src/arch/x86/syscall.c new file mode 100644 index 0000000..8adb178 --- /dev/null +++ b/src/arch/x86/syscall.c @@ -0,0 +1,62 @@ +/* + * @author xamidev + * @brief System call handling + * @license GPL-3.0-only + */ + +#include "sched/scheduler.h" +#include +#include +#include +#include + +void sys_write(unsigned int fd, const char* buf, size_t count) +{ + switch (fd) { + case 1: //stdout + for (size_t i=0; i - CPU state after system call + */ +struct cpu_status* syscall_handler(struct cpu_status* regs) +{ + DEBUG("Syscall %lx with (arg0=%lx arg1=%lx)", regs->rax, regs->rdi, regs->rsi); + + switch (regs->rax) + { + case 0: //sys_read + break; + case 1: //sys_write + sys_write(regs->rdi, (char*)regs->rsi, regs->rdx); + break; + default: + regs->rax = 0xbad515ca11; + break; + } + + return regs; +} \ No newline at end of file diff --git a/src/boot/boot.c b/src/boot/boot.c index 966f202..06a7eb4 100644 --- a/src/boot/boot.c +++ b/src/boot/boot.c @@ -9,7 +9,12 @@ * @license GPL-3.0-only */ +#include #include +#include + +__attribute__((used, section(".limine_requests_start"))) +volatile LIMINE_REQUESTS_START_MARKER; __attribute__((used, section(".limine_requests"))) volatile struct limine_framebuffer_request framebuffer_request = { @@ -35,8 +40,29 @@ volatile struct limine_kernel_address_request kerneladdr_request = { .revision = 0 }; -__attribute__((used, section(".limine_requests_start"))) -volatile LIMINE_REQUESTS_START_MARKER; +__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"))) +volatile struct limine_module_request module_request = { + .id = LIMINE_MODULE_REQUEST, + .revision = 0 +}; __attribute__((used, section(".limine_requests_end"))) -volatile LIMINE_REQUESTS_END_MARKER; \ No newline at end of file +volatile LIMINE_REQUESTS_END_MARKER; + +void populate_boot_context(struct boot_context* ctx) +{ + // Populate boot context + // This stays valid only if the BOOTLOADER_RECLAIMABLE regions are preserved + ctx->fb = framebuffer_request.response ? framebuffer_request.response->framebuffers[0] : NULL; + ctx->mmap = memmap_request.response ? memmap_request.response : NULL; + ctx->hhdm = hhdm_request.response ? hhdm_request.response : NULL; + ctx->kaddr = kerneladdr_request.response ? kerneladdr_request.response : NULL; + ctx->bootdate = date_request.response ? date_request.response : NULL; + ctx->module = module_request.response ? module_request.response : NULL; +} \ No newline at end of file diff --git a/src/debug/panic.c b/src/debug/panic.c index 22ff590..71310b2 100644 --- a/src/debug/panic.c +++ b/src/debug/panic.c @@ -18,7 +18,7 @@ extern int panic_count; */ void read_rflags(uint64_t rflags) { - DEBUG("\x1b[38;5;226m%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\x1b[38;5;231m", + printf("\x1b[38;5;226m%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\x1b[38;5;231m", CHECK_BIT(rflags, 0) ? "CF " : "", /*carry flag*/ CHECK_BIT(rflags, 2) ? "PF " : "", /*parity flag*/ CHECK_BIT(rflags, 4) ? "AF " : "", /*auxiliary carry flag*/ @@ -47,7 +47,7 @@ void read_rflags(uint64_t rflags) * Will display to terminal if it is initialized, otherwise serial only. * Can be called with or without a CPU context. */ -void panic(struct cpu_status_t* ctx, const char* str) +void panic(struct cpu_status* ctx, const char* str) { CLEAR_INTERRUPTS; panic_count += 1; diff --git a/src/io/kbd/ps2.c b/src/io/kbd/ps2.c index aea8c13..b9029d8 100644 --- a/src/io/kbd/ps2.c +++ b/src/io/kbd/ps2.c @@ -289,15 +289,21 @@ int keyboard_getline(char* output, size_t size) // Read until Enter is pressed while ((c = keyboard_getchar()) != 0x0A) { - if (index == size-1) { - output[index] = c; - output[index+1] = '\0'; - return index; + if (c == '\b') { + if (index > 0) { + index--; + output[index] = '\0'; + printf(" \b"); + } + continue; } - output[index] = c; - index++; + + if (index >= size-1) { + continue; + } + output[index++] = c; } - output[index+1] = '\0'; + output[index] = '\0'; return index; } diff --git a/src/io/serial/serial.c b/src/io/serial/serial.c index f36a450..655d479 100644 --- a/src/io/serial/serial.c +++ b/src/io/serial/serial.c @@ -11,7 +11,7 @@ extern struct init_status init; extern int panic_count; -struct spinlock_t serial_lock = {0}; +struct spinlock serial_lock = {0}; /* * outb - Writes a byte to a CPU port diff --git a/src/io/term/term.c b/src/io/term/term.c index 65b11a1..e3bd2ca 100644 --- a/src/io/term/term.c +++ b/src/io/term/term.c @@ -29,8 +29,8 @@ because this shitty implementation will be replaced one day by Flanterm extern struct flanterm_context* ft_ctx; extern struct init_status init; -struct spinlock_t term_lock = {0}; -struct spinlock_t printf_lock = {0}; +struct spinlock term_lock = {0}; +struct spinlock printf_lock = {0}; extern int panic_count; @@ -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..f4a6852 --- /dev/null +++ b/src/kapps/kshell.c @@ -0,0 +1,101 @@ +/* + * @author xamidev + * @brief PepperOS kernel shell + * @license GPL-3.0-only + */ + +#include +#include +#include +#include +#include +#include +#include