diff --git a/CMakeLists.txt b/CMakeLists.txt index 195c36d..732c52e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.16) -project(KirkOS C ASM) +project(KirkOS C ASM_NASM) set(VERSION 1) set(PATCHLEVEL 0) @@ -17,9 +17,11 @@ message(STATUS "Building KirkOS version: ${KIRKOS_VERSION_STRING}") set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) +set(CMAKE_ASM_NASM_OBJECT_FORMAT elf64) + file(GLOB_RECURSE KIRKOS_SOURCES "${CMAKE_SOURCE_DIR}/src/*.c" - "${CMAKE_SOURCE_DIR}/src/*.S" + "${CMAKE_SOURCE_DIR}/src/*.asm" ) @@ -51,6 +53,7 @@ target_compile_definitions(KirkOS PRIVATE + add_custom_command( OUTPUT ${ISO_ROOT}/boot/KirkOS DEPENDS KirkOS @@ -68,6 +71,7 @@ add_custom_target(limine_files COMMAND ${CMAKE_COMMAND} -E make_directory ${ISO_ROOT}/EFI/BOOT COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/limine.conf ${ISO_ROOT}/boot/limine/ + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/ramdisk.tar ${ISO_ROOT}/boot/ COMMAND ${CMAKE_COMMAND} -E copy ${LIMINE_DIR}/limine-bios.sys ${ISO_ROOT}/boot/limine/ COMMAND ${CMAKE_COMMAND} -E copy ${LIMINE_DIR}/limine-bios-cd.bin ${ISO_ROOT}/boot/limine/ COMMAND ${CMAKE_COMMAND} -E copy ${LIMINE_DIR}/limine-uefi-cd.bin ${ISO_ROOT}/boot/limine/ @@ -106,9 +110,9 @@ add_custom_target(run COMMAND qemu-system-x86_64 -cdrom ${ISO} -hda ${DISK_IMG} - -debugcon stdio + -serial stdio -m 8G - -smp 2 + -smp 4 -device ich9-intel-hda -device hda-duplex -cpu qemu64,+fsgsbase @@ -119,7 +123,7 @@ add_custom_target(debug COMMAND qemu-system-x86_64 -cdrom ${ISO} -hda ${DISK_IMG} - -debugcon stdio + -serial stdio -s -S -d int,cpu_reset -m 8G @@ -129,6 +133,20 @@ add_custom_target(debug -cpu qemu64,+fsgsbase ) +add_custom_command(TARGET KirkOS POST_BUILD + COMMAND ${CMAKE_OBJCOPY} --only-keep-debug + $ + ${CMAKE_BINARY_DIR}/KirkOS.sym + + COMMAND ${CMAKE_OBJCOPY} --strip-debug + $ + + COMMAND ${CMAKE_OBJCOPY} --add-gnu-debuglink=${CMAKE_BINARY_DIR}/KirkOS.sym + $ + + COMMENT "Splitting debug symbols (KirkOS.sym)" +) + target_include_directories(KirkOS PRIVATE "${CMAKE_SOURCE_DIR}/src" ) @@ -137,10 +155,13 @@ target_include_directories(KirkOS PRIVATE # ---------------------------------------- # DEFAULT C FLAGS (cacheable overrides) # ---------------------------------------- -set(KIRKOS_CFLAGS "-g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fno-PIC -ffunction-sections -fdata-sections -m64 -march=x86-64 -mabi=sysv -mno-80387 -mno-red-zone -mcmodel=kernel" +set(KIRKOS_CFLAGS "-g3 -m64 -march=x86-64 -mgeneral-regs-only -fsanitize=undefined -Wall -Wextra -Wno-error -Wno-builtin-declaration-mismatch -mno-red-zone -std=gnu11 -fms-extensions -ffreestanding -fno-stack-protector -fno-stack-check -fpie -MMD" CACHE STRING "Default CFLAGS for KirkOS") -set(KIRKOS_LDFLAGS "-nostdlib -static -Wl,--gc-sections -Wl,-T,../linker.lds -Wl,-z,max-page-size=0x1000 -Wl,-m,elf_x86_64" +set(KIRKOS_ASM_NASM_FLAGS "-MD -MP -f elf64 -g" + CACHE STRING "Default ASM Flags for NASM in KirkOS") + +set(KIRKOS_LDFLAGS "-nostdlib -Wl,-static,-pie -Wl,--gc-sections -Wl,--no-dynamic-linker -Wl,-z,max-page-size=0x1000 -Wl,-T../linker.lds -Wl,-m,elf_x86_64" CACHE STRING "Default LDFLAGS for KirkOS") # ---------------------------------------- @@ -149,5 +170,15 @@ set(KIRKOS_LDFLAGS "-nostdlib -static -Wl,--gc-sections -Wl,-T,../linker.lds -Wl separate_arguments(_cflags UNIX_COMMAND "${KIRKOS_CFLAGS}") separate_arguments(_ldflags UNIX_COMMAND "${KIRKOS_LDFLAGS}") -target_compile_options(KirkOS PRIVATE ${_cflags}) +separate_arguments(_asmflags UNIX_COMMAND "${KIRKOS_ASM_NASM_FLAGS}") + +set_target_properties(KirkOS PROPERTIES + LINK_FLAGS "-Wl,--build-id=none" +) + +target_compile_options(KirkOS PRIVATE + $<$:-masm=intel> + $<$:${_cflags}> + $<$:${_asmflags}> +) target_link_options(KirkOS PRIVATE ${_ldflags}) \ No newline at end of file diff --git a/ext2_root/bin/ls b/ext2_root/bin/ls new file mode 100755 index 0000000..0b28261 Binary files /dev/null and b/ext2_root/bin/ls differ diff --git a/ext2_root/bin/oksh b/ext2_root/bin/oksh new file mode 100755 index 0000000..73f7738 Binary files /dev/null and b/ext2_root/bin/oksh differ diff --git a/ext2_root/bin/pwd b/ext2_root/bin/pwd new file mode 100755 index 0000000..a23cfdb Binary files /dev/null and b/ext2_root/bin/pwd differ diff --git a/ext2_root/helloworld.elf b/ext2_root/helloworld.elf index 5423367..2370d92 100755 Binary files a/ext2_root/helloworld.elf and b/ext2_root/helloworld.elf differ diff --git a/ext2_root/usr/include/abi-bits/limits.h b/ext2_root/usr/include/abi-bits/limits.h index 472b3ec..aa753b1 100755 --- a/ext2_root/usr/include/abi-bits/limits.h +++ b/ext2_root/usr/include/abi-bits/limits.h @@ -6,5 +6,6 @@ #define HOST_NAME_MAX 64 #define NAME_MAX 255 #define OPEN_MAX 256 +#define _POSIX_PATH_MAX 256 #endif /*_ABIBITS_LIMITS_H */ \ No newline at end of file diff --git a/ext2_root/usr/include/abi-bits/sigset_t.h b/ext2_root/usr/include/abi-bits/sigset_t.h deleted file mode 100644 index 6cf9a05..0000000 --- a/ext2_root/usr/include/abi-bits/sigset_t.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef _ABIBITS_SIGSET_T_H -#define _ABIBITS_SIGSET_T_H - -typedef struct { - unsigned long __sig[1024 / (8 * sizeof(long))]; -} sigset_t; - -#endif /* _ABIBITS_SIGSET_T_H */ \ No newline at end of file diff --git a/ext2_root/usr/include/asm/ioctls.h b/ext2_root/usr/include/asm/ioctls.h new file mode 100644 index 0000000..f3606a3 --- /dev/null +++ b/ext2_root/usr/include/asm/ioctls.h @@ -0,0 +1,20 @@ +#ifndef _ABIBITS_IOCTLS_H +#define _ABIBITS_IOCTLS_H + +#define TIOCEXCL 0x540C +#define TIOCNXCL 0x540D + +#define SIOCPROTOPRIVATE 0x89E0 +#define SIOCGSTAMP 0x8906 +#define SIOCGIFNAME 0x8910 +#define SIOCGIFCONF 0x8912 +#define SIOCGIFFLAGS 0x8913 +#define SIOCSIFFLAGS 0x8914 +#define SIOCGIFADDR 0x8915 +#define SIOCGIFINDEX 0x8933 +#define SIOCATMARK 0x8905 +#define SIOCGIFHWADDR 0x8927 +#define SIOCGIFBRDADDR 0x8919 +#define SIOCGIFNETMASK 0x891B + +#endif /* _ABIBITS_IOCTLS_H */ diff --git a/ext2_root/usr/include/unistd.h b/ext2_root/usr/include/unistd.h index 8fcaeab..f90b20d 100644 --- a/ext2_root/usr/include/unistd.h +++ b/ext2_root/usr/include/unistd.h @@ -264,7 +264,7 @@ unsigned int alarm(unsigned int __seconds); int chdir(const char *__path); int chown(const char *__path, uid_t __uid, gid_t __gid); int close(int __fd); -ssize_t confstr(int __name, char *__buf, size_t __size); +size_t confstr(int __name, char *__buf, size_t __size); char *ctermid(char *__s); int dup(int __fd); int dup2(int __src_fd, int __dest_fd); diff --git a/ext2_root/usr/lib/Scrt1.o b/ext2_root/usr/lib/Scrt1.o deleted file mode 100644 index ff5ee5e..0000000 Binary files a/ext2_root/usr/lib/Scrt1.o and /dev/null differ diff --git a/ext2_root/usr/lib/libc.a b/ext2_root/usr/lib/libc.a index 34a9922..348db67 100644 Binary files a/ext2_root/usr/lib/libc.a and b/ext2_root/usr/lib/libc.a differ diff --git a/ext2_root/usr/lib/libdl.a b/ext2_root/usr/lib/libdl.a index cb74d6f..2c2f479 100644 Binary files a/ext2_root/usr/lib/libdl.a and b/ext2_root/usr/lib/libdl.a differ diff --git a/ext2_root/usr/lib/libm.a b/ext2_root/usr/lib/libm.a index 54c8926..604dc0a 100644 Binary files a/ext2_root/usr/lib/libm.a and b/ext2_root/usr/lib/libm.a differ diff --git a/ext2_root/usr/lib/libpthread.a b/ext2_root/usr/lib/libpthread.a index 0c55c45..2beef70 100644 Binary files a/ext2_root/usr/lib/libpthread.a and b/ext2_root/usr/lib/libpthread.a differ diff --git a/ext2_root/usr/lib/libresolv.a b/ext2_root/usr/lib/libresolv.a index 3562441..565ba95 100644 Binary files a/ext2_root/usr/lib/libresolv.a and b/ext2_root/usr/lib/libresolv.a differ diff --git a/ext2_root/usr/lib/librt.a b/ext2_root/usr/lib/librt.a index 3cf7dad..87f6d0f 100644 Binary files a/ext2_root/usr/lib/librt.a and b/ext2_root/usr/lib/librt.a differ diff --git a/ext2_root/usr/lib/libssp.a b/ext2_root/usr/lib/libssp.a index dbd4c14..47cb7b2 100644 Binary files a/ext2_root/usr/lib/libssp.a and b/ext2_root/usr/lib/libssp.a differ diff --git a/ext2_root/usr/lib/libssp_nonshared.a b/ext2_root/usr/lib/libssp_nonshared.a index 55bd909..244bac2 100644 Binary files a/ext2_root/usr/lib/libssp_nonshared.a and b/ext2_root/usr/lib/libssp_nonshared.a differ diff --git a/ext2_root/usr/lib/libutil.a b/ext2_root/usr/lib/libutil.a index e545d04..0c9bdb0 100644 Binary files a/ext2_root/usr/lib/libutil.a and b/ext2_root/usr/lib/libutil.a differ diff --git a/iso_root/boot/limine/limine.conf b/iso_root/boot/limine/limine.conf index 72d383a..9dcf7c6 100644 --- a/iso_root/boot/limine/limine.conf +++ b/iso_root/boot/limine/limine.conf @@ -2,9 +2,24 @@ timeout: 5 # The entry name that will be displayed in the boot menu. -/kirkOS - # We use the Limine boot protocol. - protocol: limine +/KirkOS +//KirkOS +protocol: limine +kaslr: no +path: boot():/boot/kirkos +cmdline: kprintf +module_path: boot():/boot/ramdisk.tar - # Path to the kernel to boot. boot():/ represents the partition on which limine.conf is located. - path: boot():/boot/kirkos \ No newline at end of file +//KirkOS (Panic on deadlock) +protocol: limine +kaslr: no +path: boot():/boot/kirkos +cmdline: kprintf panic-on-deadlock +module_path: boot():/ramdisk.tar + +//KirkOS (NSA is watching me) +protocol: limine +kaslr: yes +path: boot():/boot/kirkos +cmdline: kprintf dont-trust-cpu-random-seed +module_path: boot():/ramdisk.tar \ No newline at end of file diff --git a/iso_root/boot/ramdisk.tar b/iso_root/boot/ramdisk.tar new file mode 100644 index 0000000..99b87ec Binary files /dev/null and b/iso_root/boot/ramdisk.tar differ diff --git a/libs/uacpi/source/kernel_api.c b/libs/uacpi/source/kernel_api.c index c14df59..22fc809 100644 --- a/libs/uacpi/source/kernel_api.c +++ b/libs/uacpi/source/kernel_api.c @@ -2,12 +2,14 @@ #include #include "mm/memory.h" #include "arch/x86_64/cpu/io.h" -#include "arch/x86_64/sys/irq.h" #include "arch/x86_64/sys/pit.h" #include "arch/x86_64/sys/tsc.h" #include #include +#include "libk/debug.h" #include "mm/vmm.h" +#include "arch/x86_64/sys/timer.h" +#include "arch/x86_64/boot/isr.h" extern uint64_t g_rsdp_phys; @@ -70,19 +72,19 @@ void uacpi_kernel_unmap(void *addr, uacpi_size len) static const char *uacpi_log_prefix(uacpi_log_level level) { switch (level) { - case UACPI_LOG_DEBUG: return "[uACPI DBG ] "; - case UACPI_LOG_TRACE: return "[uACPI TRC ] "; - case UACPI_LOG_INFO: return "[uACPI INFO] "; - case UACPI_LOG_WARN: return "[uACPI WARN] "; - case UACPI_LOG_ERROR: return "[uACPI ERR ] "; - default: return "[uACPI ] "; + case UACPI_LOG_DEBUG: return "[acpi] DBG: "; + case UACPI_LOG_TRACE: return "[acpi] TRC: "; + case UACPI_LOG_INFO: return "[acpi] INFO: "; + case UACPI_LOG_WARN: return "[acpi] WARN: "; + case UACPI_LOG_ERROR: return "[acpi] ERR: "; + default: return "[acpi]: "; } } #ifndef UACPI_FORMATTED_LOGGING void uacpi_kernel_log(uacpi_log_level level, const uacpi_char *msg) { - printf("%s%s", uacpi_log_prefix(level), msg); + kprintf("%s%s", uacpi_log_prefix(level), msg); } #else UACPI_PRINTF_DECL(2, 3) @@ -97,7 +99,7 @@ void uacpi_kernel_log(uacpi_log_level level, const uacpi_char *fmt, ...) void uacpi_kernel_vlog(uacpi_log_level level, const uacpi_char *fmt, uacpi_va_list va) { - printf("%s", uacpi_log_prefix(level)); + kprintf("%s", uacpi_log_prefix(level)); vfprintf(fmt, va); } #endif @@ -348,7 +350,7 @@ void uacpi_kernel_free(void *mem, uacpi_size size_hint) */ uacpi_u64 uacpi_kernel_get_nanoseconds_since_boot(void) { - return rdtsc() / (tsc_cycles_per_us / 1000); // rough ns + return (rdtsc() * 1000ULL) / tsc_cycles_per_us; } /** @@ -368,13 +370,7 @@ void uacpi_kernel_stall(uacpi_u8 usec) */ void uacpi_kernel_sleep(uacpi_u64 msec) { - uint64_t start = g_Ticks; - uint64_t target = start + msec; - - while (g_Ticks < target) - { - __asm__ volatile("hlt"); - } + timer_sleep(msec); } typedef struct { atomic_flag flag; } uacpi_mutex_impl_t; @@ -426,13 +422,26 @@ void uacpi_kernel_free_event(uacpi_handle handle) uacpi_interrupt_state uacpi_kernel_disable_interrupts(void) { uint64_t flags; - asm volatile("pushfq; pop %0; cli" : "=r"(flags) :: "memory"); + asm volatile( + "pushfq\n\t" + "pop %0\n\t" + "cli" + : "=r"(flags) + : + : "memory" + ); return (uacpi_interrupt_state)flags; } void uacpi_kernel_restore_interrupts(uacpi_interrupt_state state) { - asm volatile("push %0; popfq" :: "r"((uint64_t)state) : "memory", "cc"); + asm volatile( + "push %0\n\t" + "popfq" + : + : "r"((uint64_t)state) + : "memory", "cc" + ); } @@ -542,10 +551,10 @@ uacpi_status uacpi_kernel_handle_firmware_request(uacpi_firmware_request *req) { switch (req->type) { case UACPI_FIRMWARE_REQUEST_TYPE_BREAKPOINT: - printf("[uACPI] AML Breakpoint\n"); + kprintf("[uACPI] AML Breakpoint\n"); return UACPI_STATUS_OK; case UACPI_FIRMWARE_REQUEST_TYPE_FATAL: - printf("[uACPI] AML Fatal! type=0x%x code=0x%x arg=0x%lx\n", + kprintf("[uACPI] AML Fatal! type=0x%x code=0x%x arg=0x%lx\n", req->fatal.type, req->fatal.code, (unsigned long)req->fatal.arg); for (;;) asm volatile("hlt"); @@ -573,9 +582,9 @@ typedef struct { static uacpi_irq_entry_t uacpi_irq_table[UACPI_MAX_IRQS]; -static void uacpi_irq_shim(Registers *regs) +static void uacpi_irq_shim(registers_t *regs) { - int irq = (int)regs->interrupt - 0x20; /* PIC_REMAP_OFFSET */ + int irq = (int)regs->isrNumber - 0x20; /* PIC_REMAP_OFFSET */ if ((unsigned)irq < UACPI_MAX_IRQS && uacpi_irq_table[irq].fn) uacpi_irq_table[irq].fn(uacpi_irq_table[irq].ctx); } @@ -589,7 +598,7 @@ uacpi_status uacpi_kernel_install_interrupt_handler( uacpi_irq_table[irq].fn = handler; uacpi_irq_table[irq].ctx = ctx; - x86_64_IRQ_RegisterHandler((int)irq, uacpi_irq_shim); + isr_register_handler((int)irq, uacpi_irq_shim); /* Return a non-NULL token the caller can pass to uninstall */ *out_irq_handle = (uacpi_handle)(uintptr_t)(irq + 1); @@ -610,7 +619,7 @@ uacpi_status uacpi_kernel_uninstall_interrupt_handler( uacpi_irq_table[irq].fn = NULL; uacpi_irq_table[irq].ctx = NULL; - x86_64_IRQ_RegisterHandler((int)irq, NULL); + isr_register_handler((int)irq, NULL); return UACPI_STATUS_OK; } @@ -646,18 +655,42 @@ void uacpi_kernel_free_spinlock(uacpi_handle handle) uacpi_cpu_flags uacpi_kernel_lock_spinlock(uacpi_handle handle) { uint64_t flags; - asm volatile("pushfq; pop %0; cli" : "=r"(flags) :: "memory"); + + asm volatile( + "pushfq\n\t" + "pop %0\n\t" + "cli" + : "=r"(flags) + : + : "memory" + ); + uacpi_spinlock_impl_t *sl = handle; + while (atomic_flag_test_and_set_explicit(&sl->flag, memory_order_acquire)) - asm volatile("pause" ::: "memory"); + asm volatile( + "pause" + : + : + : "memory" + ); + return (uacpi_cpu_flags)flags; } void uacpi_kernel_unlock_spinlock(uacpi_handle handle, uacpi_cpu_flags saved) { uacpi_spinlock_impl_t *sl = handle; + atomic_flag_clear_explicit(&sl->flag, memory_order_release); - asm volatile("push %0; popfq" :: "r"((uint64_t)saved) : "memory", "cc"); + + asm volatile( + "push %0\n\t" + "popfq" + : + : "r"((uint64_t)saved) + : "memory", "cc" + ); } typedef enum uacpi_work_type { diff --git a/limine.conf b/limine.conf index 48266ad..0de618f 100644 --- a/limine.conf +++ b/limine.conf @@ -2,9 +2,24 @@ timeout: 5 # The entry name that will be displayed in the boot menu. -/kirkOS - # We use the Limine boot protocol. - protocol: limine +/KirkOS +//KirkOS +protocol: limine +kaslr: no +path: boot():/boot/KirkOS +cmdline: kprintf +module_path: boot():/boot/ramdisk.tar - # Path to the kernel to boot. boot():/ represents the partition on which limine.conf is located. - path: boot():/boot/KirkOS \ No newline at end of file +//KirkOS (Panic on deadlock) +protocol: limine +kaslr: no +path: boot():/boot/KirkOS +cmdline: kprintf panic-on-deadlock +module_path: boot():/boot/ramdisk.tar + +//KirkOS (NSA is watching me) +protocol: limine +kaslr: yes +path: boot():/boot/KirkOS +cmdline: kprintf dont-trust-cpu-random-seed +module_path: boot():/boot/ramdisk.tar \ No newline at end of file diff --git a/limine/BOOTIA32.EFI b/limine/BOOTIA32.EFI index adda6a0..008cbf8 100644 Binary files a/limine/BOOTIA32.EFI and b/limine/BOOTIA32.EFI differ diff --git a/limine/BOOTX64.EFI b/limine/BOOTX64.EFI index 3537d57..50dda0e 100644 Binary files a/limine/BOOTX64.EFI and b/limine/BOOTX64.EFI differ diff --git a/limine/limine-bios-cd.bin b/limine/limine-bios-cd.bin index f0e60a1..c09a328 100644 Binary files a/limine/limine-bios-cd.bin and b/limine/limine-bios-cd.bin differ diff --git a/limine/limine-bios-pxe.bin b/limine/limine-bios-pxe.bin index e463d64..3cb66f3 100644 Binary files a/limine/limine-bios-pxe.bin and b/limine/limine-bios-pxe.bin differ diff --git a/limine/limine-bios.sys b/limine/limine-bios.sys index ef5574d..3aa121b 100644 Binary files a/limine/limine-bios.sys and b/limine/limine-bios.sys differ diff --git a/limine/limine-uefi-cd.bin b/limine/limine-uefi-cd.bin index 77c49f5..eca3727 100644 Binary files a/limine/limine-uefi-cd.bin and b/limine/limine-uefi-cd.bin differ diff --git a/linker.lds b/linker.lds index 04121ea..5dbbd10 100644 --- a/linker.lds +++ b/linker.lds @@ -1,81 +1,68 @@ /* Tell the linker that we want an x86_64 ELF64 output file */ OUTPUT_FORMAT(elf64-x86-64) +OUTPUT_ARCH(i386:x86-64) -/* We want the symbol kmain to be our entry point */ -ENTRY(kmain) +/* We want the symbol arch_entry to be our entry point */ +ENTRY(_entry) /* Define the program headers we want so the bootloader gives us the right */ -/* MMU permissions; this also allows us to exert more control over the linking */ -/* process. */ -PHDRS -{ - limine_requests PT_LOAD; - text PT_LOAD; - rodata PT_LOAD; - data PT_LOAD; +/* MMU permissions */ +PHDRS { + text PT_LOAD FLAGS((1 << 0) | (1 << 2)) ; /* Execute + Read */ + rodata PT_LOAD FLAGS((1 << 2)) ; /* Read only */ + data PT_LOAD FLAGS((1 << 1) | (1 << 2)) ; /* Write + Read */ + dynamic PT_DYNAMIC FLAGS((1 << 1) | (1 << 2)) ; /* Dynamic segment needed for PIE */ } -SECTIONS -{ - /* We want to be placed in the topmost 2GiB of the address space, for optimisations */ - /* and because that is what the Limine spec mandates. */ - /* Any address in this region will do, but often 0xffffffff80000000 is chosen as */ - /* that is the beginning of the region. */ - . = 0xffffffff80000000; +SECTIONS { + /* We wanna be placed in the topmost 2GiB of the address space, for optimisations */ + /* and because that is what the Limine spec mandates. */ + /* Any address in this region will do, but often 0xffffffff80000000 is chosen as */ + /* that is the beginning of the region. */ + . = 0xffffffff80000000; - /* Define a section to contain the Limine requests and assign it to its own PHDR */ - .limine_requests : { - KEEP(*(.limine_requests_start)) - KEEP(*(.limine_requests)) - KEEP(*(.limine_requests_end)) - } :limine_requests + text_start_addr = .; - /* Move to the next memory page for .text */ - . = ALIGN(CONSTANT(MAXPAGESIZE)); - __text_start_addr = .; - .text : { - *(.text .text.*) - } :text - __text_end_addr = .; + .text : { + *(.text .text.*) + } :text - /* Move to the next memory page for .rodata */ - . = ALIGN(CONSTANT(MAXPAGESIZE)); - __rodata_start_addr = .; - .rodata : { - *(.rodata .rodata.*) - } :rodata - __rodata_end_addr = .; + text_end_addr = .; - /* Add a .note.gnu.build-id output section in case a build ID flag is added to the */ - /* linker command. */ - .note.gnu.build-id : { - *(.note.gnu.build-id) - } :rodata + /* Move to the next memory page for .rodata */ + . += CONSTANT(MAXPAGESIZE); - /* Move to the next memory page for .data */ - . = ALIGN(CONSTANT(MAXPAGESIZE)); - __data_start_addr = .; - .data : { - *(.data .data.*) - } :data - __data_end_addr = .; + rodata_start_addr = .; - /* NOTE: .bss needs to be the last thing mapped to :data, otherwise lots of */ - /* unnecessary zeros will be written to the binary. */ - /* If you need, for example, .init_array and .fini_array, those should be placed */ - /* above this. */ - __bss_start_addr = .; - .bss : { - *(.bss .bss.*) - *(COMMON) - } :data - __bss_end_addr = .; + .rodata : { + *(.rodata .rodata.*) + } :rodata - __kernel_end = .; + rodata_end_addr = .; - /* Discard .note.* and .eh_frame* since they may cause issues on some hosts. */ - /DISCARD/ : { - *(.eh_frame*) - *(.note .note.*) - } -} \ No newline at end of file + /* Move to the next memory page for .data */ + . += CONSTANT(MAXPAGESIZE); + + data_start_addr = .; + + .data : { + *(.data .data.*) + } :data + + .dynamic : { + *(.dynamic) + } :data :dynamic + + .bss : { + *(.bss .bss.*) + *(COMMON) + } :data + + data_end_addr = .; + + /* Discard notes and eh_frame since they may cause issues on some hosts. */ + /DISCARD/ : { + *(.eh_frame) + *(.note .note.*) + } +} diff --git a/src/drivers/audio/hda.c b/oldsrc/audio/hda.c similarity index 93% rename from src/drivers/audio/hda.c rename to oldsrc/audio/hda.c index 7b76459..1af9323 100644 --- a/src/drivers/audio/hda.c +++ b/oldsrc/audio/hda.c @@ -3,6 +3,7 @@ #include "mm/pmm.h" #include "mm/memory.h" #include "libk/stdio.h" +#include "libk/debug.h" /* ── Module globals ─────────────────────────────────────────────────────────── */ @@ -78,7 +79,7 @@ static uint64_t hda_send_verb(uint32_t cmd) return g_rirb[g_rirb_rp]; } } - printf("[HDA] verb timeout cmd=%08x\n", cmd); + kprintf("[HDA] verb timeout cmd=%08x\n", cmd); return (uint64_t)-1; } @@ -144,7 +145,7 @@ static bool corb_init(void) if (r8(HDA_CORBCTL) & 0x02) return true; asm volatile("pause"); } - printf("[HDA] CORB DMA failed to start\n"); + kprintf("[HDA] CORB DMA failed to start\n"); return false; } @@ -183,7 +184,7 @@ static bool rirb_init(void) if (r8(HDA_RIRBCTL) & 0x02) return true; asm volatile("pause"); } - printf("[HDA] RIRB DMA failed to start\n"); + kprintf("[HDA] RIRB DMA failed to start\n"); return false; } @@ -218,7 +219,7 @@ static bool enumerate_codec(uint8_t cad) uint32_t type = hda_get_param(cad, n, HDA_PARAM_FUNC_GROUP_TYPE) & 0xFF; if (type == 0x01) { afg = n; break; } } - if (!afg) { printf("[HDA] no AFG on codec %u\n", cad); return false; } + if (!afg) { kprintf("[HDA] no AFG on codec %u\n", cad); return false; } /* Enumerate widgets */ uint32_t w_info = hda_get_param(cad, afg, HDA_PARAM_NODE_COUNT); @@ -241,11 +242,11 @@ static bool enumerate_codec(uint8_t cad) } } - if (!dac) { printf("[HDA] no DAC found\n"); return false; } - if (!pin) { printf("[HDA] no output pin found\n"); return false; } + if (!dac) { kprintf("[HDA] no DAC found\n"); return false; } + if (!pin) { kprintf("[HDA] no output pin found\n"); return false; } g_afg = afg; g_dac = dac; g_pin = pin; - printf("[HDA] codec=%u AFG=%u DAC=%u Pin=%u\n", cad, afg, dac, pin); + kprintf("[HDA] codec=%u AFG=%u DAC=%u Pin=%u\n", cad, afg, dac, pin); /* Select DAC in pin's connection list if needed */ uint8_t clen = (uint8_t)(hda_get_param(cad, pin, HDA_PARAM_CONN_LIST_LEN) & 0x7F); @@ -276,7 +277,7 @@ static uint16_t calc_format(hda_format_t f) case 24: fmt |= (3u << 4); break; case 32: fmt |= (4u << 4); break; default: fmt |= (1u << 4); - printf("[HDA] unknown bit depth %u, using 16-bit\n", f.bits_per_sample); + kprintf("[HDA] unknown bit depth %u, using 16-bit\n", f.bits_per_sample); } /* @@ -298,7 +299,7 @@ static uint16_t calc_format(hda_format_t f) case 176400: fmt |= (1u << 14) | (3u << 11) | (0u << 8); break; /* 44100*4/ 1 */ case 192000: fmt |= (0u << 14) | (3u << 11) | (0u << 8); break; /* 48000*4/ 1 */ default: - printf("[HDA] unsupported sample rate %u, defaulting to 48000\n", + kprintf("[HDA] unsupported sample rate %u, defaulting to 48000\n", f.sample_rate); } return fmt; @@ -347,13 +348,13 @@ bool hda_init(pci_device_t *dev) } if (!mmio_phys) { - printf("[HDA] BAR0 is zero, controller not properly enumerated by firmware\n"); + kprintf("[HDA] BAR0 is zero, controller not properly enumerated by firmware\n"); return false; } /* Map via HHDM (all physical memory is already mapped with MEM_PHYS_OFFSET) */ g_mmio = mmio_phys + MEM_PHYS_OFFSET; - printf("[HDA] MMIO phys=%lx virt=%lx\n", (uint64_t)mmio_phys, (uint64_t)g_mmio); + kprintf("[HDA] MMIO phys=%lx virt=%lx\n", (uint64_t)mmio_phys, (uint64_t)g_mmio); /* ── 3. Controller reset ─────────────────────────────────────────────────── */ /* Assert reset */ @@ -379,45 +380,45 @@ bool hda_init(pci_device_t *dev) uint16_t gcap = r16(HDA_GCAP); g_iss = (gcap >> 8) & 0xF; /* number of input SDs */ uint8_t oss = (gcap >> 12) & 0xF; /* number of output SDs */ - printf("[HDA] gcap=%04x ISS=%u OSS=%u\n", gcap, g_iss, oss); - if (!oss) { printf("[HDA] no output streams\n"); return false; } + kprintf("[HDA] gcap=%04x ISS=%u OSS=%u\n", gcap, g_iss, oss); + if (!oss) { kprintf("[HDA] no output streams\n"); return false; } /* Output SD 0 starts immediately after all input SDs */ g_sd_base = 0x80u + (uint32_t)g_iss * HDA_SD_STRIDE; /* ── 5. CORB + RIRB ─────────────────────────────────────────────────────── */ - if (!corb_init()) { printf("[HDA] CORB init failed\n"); return false; } - if (!rirb_init()) { printf("[HDA] RIRB init failed\n"); return false; } + if (!corb_init()) { kprintf("[HDA] CORB init failed\n"); return false; } + if (!rirb_init()) { kprintf("[HDA] RIRB init failed\n"); return false; } /* ── 6. Detect and enumerate codecs ─────────────────────────────────────── */ uint16_t statests = r16(HDA_STATESTS); - if (!statests) { printf("[HDA] no codecs detected\n"); return false; } + if (!statests) { kprintf("[HDA] no codecs detected\n"); return false; } bool found = false; for (uint8_t c = 0; c < 15; c++) { if (!(statests & (1u << c))) continue; - printf("[HDA] codec %u present\n", c); + kprintf("[HDA] codec %u present\n", c); if (!found && enumerate_codec(c)) { g_codec = c; found = true; } } - if (!found) { printf("[HDA] no usable codec\n"); return false; } + if (!found) { kprintf("[HDA] no usable codec\n"); return false; } /* ── 7. Allocate persistent BDL (1 page, 256 possible entries × 16 bytes) ─ */ void *bdl_phys = pmm_allocz(1); - if (!bdl_phys) { printf("[HDA] OOM: BDL\n"); return false; } + if (!bdl_phys) { kprintf("[HDA] OOM: BDL\n"); return false; } g_bdl_phys = (uintptr_t)bdl_phys; g_bdl = (hda_bdl_entry_t *)((uintptr_t)bdl_phys + MEM_PHYS_OFFSET); - printf("[HDA] init complete\n"); + kprintf("[HDA] init complete\n"); return true; } bool hda_play_pcm(uintptr_t phys_buf, uint32_t size, hda_format_t fmt) { if (!g_mmio || !g_dac || !g_pin) { - printf("[HDA] not initialised\n"); + kprintf("[HDA] not initialised\n"); return false; } if (!phys_buf || !size) return false; @@ -498,7 +499,7 @@ bool hda_play_pcm(uintptr_t phys_buf, uint32_t size, hda_format_t fmt) ctl &= ~HDA_SD_CTL_SRST; sd_w8(HDA_SD_CTL0, ctl); - printf("[HDA] playback started: %u Hz %u-bit %uch %u bytes\n", + kprintf("[HDA] playback started: %u Hz %u-bit %uch %u bytes\n", fmt.sample_rate, fmt.bits_per_sample, fmt.channels, size); return true; } @@ -524,7 +525,7 @@ void hda_wait_complete(void) while (!(sd_r8(HDA_SD_STS) & HDA_SD_STS_BCIS)) asm volatile("pause"); hda_stop(); - printf("[HDA] playback complete\n"); + kprintf("[HDA] playback complete\n"); } bool hda_is_playing(void) diff --git a/src/drivers/audio/hda.h b/oldsrc/audio/hda.h similarity index 100% rename from src/drivers/audio/hda.h rename to oldsrc/audio/hda.h diff --git a/src/drivers/audio/pcm.c b/oldsrc/audio/pcm.c similarity index 90% rename from src/drivers/audio/pcm.c rename to oldsrc/audio/pcm.c index a59f7f3..106e83d 100644 --- a/src/drivers/audio/pcm.c +++ b/oldsrc/audio/pcm.c @@ -1,10 +1,10 @@ #include "pcm.h" #include "hda.h" -#include "fs/ext2.h" #include "mm/pmm.h" #include "mm/vmm.h" #include "mm/memory.h" -#include "stdio.h" +#include "libk/stdio.h" +#include "libk/debug.h" /* ── WAV chunk scanner ──────────────────────────────────────────────────────── */ @@ -44,7 +44,7 @@ static bool parse_wav(const uint8_t *buf, uint32_t buf_size, uint16_t audio_fmt = *(uint16_t *)(buf + pos + 0); if (audio_fmt != 1) { - printf("[PCM] WAV audio format %u not supported (only PCM=1)\n", + kprintf("[PCM] WAV audio format %u not supported (only PCM=1)\n", audio_fmt); return false; } @@ -79,19 +79,19 @@ static bool play_from_ext2(const char *filename, /* ── 1. Resolve path and read inode ─────────────────────────────────────── */ uint32_t inum = ext2_resolve_path(filename); if (!inum) { - printf("[PCM] file not found: %s\n", filename); + kprintf("[PCM] file not found: %s\n", filename); return false; } ext2_inode_t inode; if (!ext2_read_inode(inum, &inode)) { - printf("[PCM] failed to read inode for %s\n", filename); + kprintf("[PCM] failed to read inode for %s\n", filename); return false; } uint32_t file_size = inode.i_size; if (!file_size) { - printf("[PCM] %s is empty\n", filename); + kprintf("[PCM] %s is empty\n", filename); return false; } @@ -103,7 +103,7 @@ static bool play_from_ext2(const char *filename, uint32_t pages = (file_size + PAGE_SIZE - 1) / PAGE_SIZE; void *phys = pmm_alloc(pages); if (!phys) { - printf("[PCM] OOM: cannot allocate %u pages for %s\n", pages, filename); + kprintf("[PCM] OOM: cannot allocate %u pages for %s\n", pages, filename); return false; } uint8_t *virt = (uint8_t *)((uintptr_t)phys + MEM_PHYS_OFFSET); @@ -113,7 +113,7 @@ static bool play_from_ext2(const char *filename, /* ── 3. Read file data ───────────────────────────────────────────────────── */ if (!ext2_read_file(&inode, virt)) { - printf("[PCM] failed to read %s\n", filename); + kprintf("[PCM] failed to read %s\n", filename); pmm_free(phys, pages); return false; } @@ -133,14 +133,14 @@ static bool play_from_ext2(const char *filename, /* Adjust the physical pointer to skip the WAV headers */ audio_phys += data_offset; audio_size = data_size; - printf("[PCM] WAV: %u Hz %u-bit %u ch %u bytes of audio\n", + kprintf("[PCM] WAV: %u Hz %u-bit %u ch %u bytes of audio\n", fmt.sample_rate, fmt.bits_per_sample, fmt.channels, audio_size); } else { /* Not a WAV – assume raw 48 kHz, 16-bit stereo */ fmt.sample_rate = 48000; fmt.bits_per_sample = 16; fmt.channels = 2; - printf("[PCM] Raw PCM assumed: 48000 Hz 16-bit stereo %u bytes\n", + kprintf("[PCM] Raw PCM assumed: 48000 Hz 16-bit stereo %u bytes\n", audio_size); } } @@ -150,7 +150,7 @@ static bool play_from_ext2(const char *filename, if (ok) hda_wait_complete(); /* blocks until the stream finishes */ else - printf("[PCM] hda_play_pcm failed\n"); + kprintf("[PCM] hda_play_pcm failed\n"); /* ── 6. Free the DMA buffer ─────────────────────────────────────────────── */ pmm_free(phys, pages); diff --git a/src/drivers/audio/pcm.h b/oldsrc/audio/pcm.h similarity index 100% rename from src/drivers/audio/pcm.h rename to oldsrc/audio/pcm.h diff --git a/src/fs/ext2.c b/oldsrc/ext2.c similarity index 97% rename from src/fs/ext2.c rename to oldsrc/ext2.c index 409bb4e..21f73a7 100644 --- a/src/fs/ext2.c +++ b/oldsrc/ext2.c @@ -6,6 +6,7 @@ #include "libk/string.h" #include "mp/spinlock.h" #include "arch/x86_64/sys/pit.h" +#include "libk/debug.h" // ── module state ───────────────────────────────────────────────────────────── @@ -70,7 +71,7 @@ bool ext2_read_superblock_internal(void) { if (!tmp) return false; if (!ata_read_sectors(2, 2, tmp)) { - printf("EXT2: failed to read superblock\n"); + kprintf("EXT2: failed to read superblock\n"); kfree(tmp); return false; } @@ -78,7 +79,7 @@ bool ext2_read_superblock_internal(void) { kfree(tmp); if (sb.s_magic != EXT2_MAGIC) { - printf("EXT2: bad magic 0x%04x\n", sb.s_magic); + kprintf("EXT2: bad magic 0x%04x\n", sb.s_magic); return false; } @@ -86,7 +87,7 @@ bool ext2_read_superblock_internal(void) { num_groups = (sb.s_blocks_count + sb.s_blocks_per_group - 1) / sb.s_blocks_per_group; - printf("EXT2: OK block_size=%u groups=%u\n", block_size, num_groups); + kprintf("EXT2: OK block_size=%u groups=%u\n", block_size, num_groups); return true; } @@ -124,7 +125,7 @@ bool ext2_read_group_desc_table_internal(void) { uint8_t* buf = kmalloc(nblocks * block_size); if (!buf) { - printf("nah"); + kprintf("nah"); return false; } @@ -532,7 +533,7 @@ static void ext2_free_inode_blocks(ext2_inode_t* inode) { bool ext2_read_file_internal(ext2_inode_t* inode, uint8_t* buf) { if (((inode->i_mode & 0xF000) != EXT2_S_IFREG) && ((inode->i_mode & 0xF000) != EXT2_S_IFDIR)) { - printf("EXT2: not a regular file or directory\n"); + kprintf("EXT2: not a regular file or directory\n"); return false; } uint32_t size = inode->i_size; @@ -909,7 +910,7 @@ bool ext2_read_dir_internal(ext2_inode_t* dir) { uint8_t nl = e->name_len > EXT2_NAME_LEN ? EXT2_NAME_LEN : e->name_len; memcpy(name, e->name, nl); name[nl] = '\0'; - printf(" %s inode=%u %s\n", + kprintf(" %s inode=%u %s\n", name, e->inode, ext2_file_type_string(e->file_type)); } off += e->rec_len; @@ -930,7 +931,7 @@ bool ext2_read_dir(ext2_inode_t* dir) { bool ext2_read_root_dir_internal(void) { ext2_inode_t root; if (!ext2_read_inode_internal(2, &root)) return false; - printf("Root directory:\n"); + kprintf("Root directory:\n"); return ext2_read_dir_internal(&root); } @@ -946,21 +947,21 @@ bool ext2_read_root_dir(void) { bool ext2_read_file_from_root_internal(const char* name, uint8_t* buf, uint32_t* size) { ext2_inode_t root; - printf("EXT2: reading file from root: %s\n", name); + kprintf("EXT2: reading file from root: %s\n", name); if (!ext2_read_inode_internal(2, &root)) return false; - printf("EXT2: root inode: size=%u blocks=%u\n", root.i_size, root.i_blocks); + kprintf("EXT2: root inode: size=%u blocks=%u\n", root.i_size, root.i_blocks); uint32_t inum; if (!ext2_find_in_dir_internal(&root, name, &inum)) { - printf("EXT2: not found: %s\n", name); + kprintf("EXT2: not found: %s\n", name); return false; } - printf("EXT2: found in root: inum=%u\n", inum); + kprintf("EXT2: found in root: inum=%u\n", inum); ext2_inode_t fi; if (!ext2_read_inode_internal(inum, &fi)) return false; - printf("EXT2: file inode: size=%u blocks=%u\n", fi.i_size, fi.i_blocks); + kprintf("EXT2: file inode: size=%u blocks=%u\n", fi.i_size, fi.i_blocks); *size = fi.i_size; - printf("EXT2: read file: size=%u\n", *size); + kprintf("EXT2: read file: size=%u\n", *size); return ext2_read_file_internal(&fi, buf); } @@ -983,7 +984,7 @@ bool ext2_write_file_from_root_internal(const char* name, const uint8_t* data, uint32_t inum; if (!ext2_find_in_dir_internal(&root, name, &inum)) { - printf("EXT2: not found: %s\n", name); + kprintf("EXT2: not found: %s\n", name); return false; } ext2_inode_t fi; @@ -1009,7 +1010,7 @@ bool ext2_create_file_internal(ext2_inode_t* dir, uint32_t dir_inum, const char* name, uint16_t mode, uint32_t* out_inum) { if (dir_lookup_internal(dir, name)) { - printf("EXT2: already exists: %s\n", name); + kprintf("EXT2: already exists: %s\n", name); return false; } @@ -1045,7 +1046,7 @@ bool ext2_mkdir_internal(ext2_inode_t* parent, uint32_t parent_inum, const char* name, uint32_t* out_inum) { if (dir_lookup_internal(parent, name)) { - printf("EXT2: already exists: %s\n", name); + kprintf("EXT2: already exists: %s\n", name); return false; } @@ -1113,13 +1114,13 @@ bool ext2_mkdir(ext2_inode_t* parent, uint32_t parent_inum, bool ext2_unlink_internal(ext2_inode_t* dir, uint32_t dir_inum, const char* name) { uint32_t inum = dir_lookup_internal(dir, name); - if (!inum) { printf("EXT2: not found: %s\n", name); return false; } + if (!inum) { kprintf("EXT2: not found: %s\n", name); return false; } ext2_inode_t fi; if (!ext2_read_inode_internal(inum, &fi)) return false; if ((fi.i_mode & 0xF000) == EXT2_S_IFDIR) { - printf("EXT2: is a directory, use rmdir\n"); + kprintf("EXT2: is a directory, use rmdir\n"); return false; } if (!dir_remove_entry_internal(dir, dir_inum, name)) return false; @@ -1148,17 +1149,17 @@ bool ext2_unlink(ext2_inode_t* dir, uint32_t dir_inum, const char* name) { bool ext2_rmdir_internal(ext2_inode_t* parent, uint32_t parent_inum, const char* name) { uint32_t inum = dir_lookup_internal(parent, name); - if (!inum) { printf("EXT2: not found: %s\n", name); return false; } + if (!inum) { kprintf("EXT2: not found: %s\n", name); return false; } ext2_inode_t dir; if (!ext2_read_inode_internal(inum, &dir)) return false; if ((dir.i_mode & 0xF000) != EXT2_S_IFDIR) { - printf("EXT2: not a directory: %s\n", name); + kprintf("EXT2: not a directory: %s\n", name); return false; } if (!dir_is_empty_internal(&dir)) { - printf("EXT2: directory not empty: %s\n", name); + kprintf("EXT2: directory not empty: %s\n", name); return false; } @@ -1189,7 +1190,7 @@ bool ext2_rename_internal(ext2_inode_t* src_dir, uint32_t src_inum, const char* old_name, const char* new_name) { uint32_t inum = dir_lookup_internal(src_dir, old_name); - if (!inum) { printf("EXT2: not found: %s\n", old_name); return false; } + if (!inum) { kprintf("EXT2: not found: %s\n", old_name); return false; } ext2_inode_t fi; if (!ext2_read_inode_internal(inum, &fi)) return false; @@ -1203,7 +1204,7 @@ bool ext2_rename_internal(ext2_inode_t* src_dir, uint32_t src_inum, ext2_read_inode_internal(existing, &ex); if ((ex.i_mode & 0xF000) == EXT2_S_IFDIR) { if (!dir_is_empty_internal(&ex)) { - printf("EXT2: rename: target dir not empty\n"); + kprintf("EXT2: rename: target dir not empty\n"); return false; } ext2_rmdir_internal(dst_dir, dst_inum, new_name); @@ -1261,7 +1262,7 @@ bool ext2_symlink_internal(ext2_inode_t* dir, uint32_t dir_inum, const char* name, const char* target) { if (dir_lookup_internal(dir, name)) { - printf("EXT2: already exists: %s\n", name); + kprintf("EXT2: already exists: %s\n", name); return false; } diff --git a/src/fs/ext2.h b/oldsrc/ext2.h similarity index 100% rename from src/fs/ext2.h rename to oldsrc/ext2.h diff --git a/src/drivers/input/input.c b/oldsrc/input/input.c similarity index 91% rename from src/drivers/input/input.c rename to oldsrc/input/input.c index 7342b72..e67a05c 100644 --- a/src/drivers/input/input.c +++ b/oldsrc/input/input.c @@ -6,6 +6,7 @@ #include "mm/memory.h" #include "libk/string.h" #include +#include "libk/debug.h" /* ------------------------------------------------------------------ * * Event ring-buffer @@ -25,28 +26,32 @@ static spinlock_t s_lock = SPINLOCK_INIT; /* Called from IRQ context */ void input_push_char(char c) { - spinlock_acquire_or_wait(&s_lock); + uint64_t flags; + spinlock_acquire_irqsave(&s_lock, &flags); size_t next = (s_console_wpos + 1) % CONSOLE_BUF_SIZE; if (next != s_console_rpos) { /* drop if full */ s_console_buf[s_console_wpos] = c; s_console_wpos = next; } - spinlock_drop(&s_lock); + spinlock_release_irqrestore(&s_lock, flags); } int input_read_console(void *buf, size_t len) { uint8_t *p = buf; size_t count = 0; + uint64_t flags; + + spinlock_acquire_irqsave(&s_lock, &flags); - spinlock_acquire_or_wait(&s_lock); while (count < len && s_console_rpos != s_console_wpos) { p[count++] = s_console_buf[s_console_rpos]; s_console_rpos = (s_console_rpos + 1) % CONSOLE_BUF_SIZE; } - spinlock_drop(&s_lock); - return (int)count; /* 0 = no data yet (non-blocking) */ + spinlock_release_irqrestore(&s_lock, flags); + + return (int)count; } static void push_event(const input_event_t *ev) @@ -141,5 +146,5 @@ void input_register_devnodes(void) void input_init(void) { input_register_devnodes(); - printf("input: subsystem initialized\n"); + kprintf("input: subsystem initialized\n"); } \ No newline at end of file diff --git a/src/drivers/input/input.h b/oldsrc/input/input.h similarity index 100% rename from src/drivers/input/input.h rename to oldsrc/input/input.h diff --git a/src/drivers/input/ps2.c b/oldsrc/input/ps2.c similarity index 98% rename from src/drivers/input/ps2.c rename to oldsrc/input/ps2.c index fcf0488..e5d6d6d 100644 --- a/src/drivers/input/ps2.c +++ b/oldsrc/input/ps2.c @@ -6,6 +6,7 @@ #include "arch/x86_64/sys/ioapic.h" #include "arch/x86_64/sys/apic.h" #include "libk/stdio.h" +#include "libk/debug.h" #include "drivers/video/render.h" /* ── PS/2 I/O ports ───────────────────────────────────────────────────────── */ @@ -154,12 +155,6 @@ void ps2_kbd_handler(Registers *regs) if (pressed && ascii) { input_push_char(ascii); - - if (ascii == '\b') { - backspace(); - } else { - putchar(ascii); - } } } @@ -195,6 +190,6 @@ void ps2_kbd_init(void) x86_64_APIC_IRQ_RedirectAndRegister(1, 0x21, ps2_kbd_handler); - printf("[PS2] Keyboard driver initialised (IRQ%d vector 0x%02x)\n", + kprintf("[PS2] Keyboard driver initialised (IRQ%d vector 0x%02x)\n", KBD_IRQ, KBD_IDT_VECTOR); } \ No newline at end of file diff --git a/src/drivers/input/ps2.h b/oldsrc/input/ps2.h similarity index 100% rename from src/drivers/input/ps2.h rename to oldsrc/input/ps2.h diff --git a/src/drivers/rand/random.c b/oldsrc/rand/random.c similarity index 92% rename from src/drivers/rand/random.c rename to oldsrc/rand/random.c index 40cb784..d6b25c8 100644 --- a/src/drivers/rand/random.c +++ b/oldsrc/rand/random.c @@ -5,8 +5,11 @@ #include "arch/x86_64/sys/pit.h" #include "errno.h" #include -#include +#include "libk/string.h" +#include "mm/memory.h" #include "limine.h" +#include "libk/debug.h" +#include "libk/debug.h" /* ChaCha20 core (public-domain, 20-round) */ static inline void chacha20_quarterround(uint32_t *a, uint32_t *b, uint32_t *c, uint32_t *d) { @@ -124,7 +127,7 @@ int getrandom(void *buf, size_t buflen, unsigned int flags) } void random_init(void) { - printf("Initializing ChaCha20-based CSPRNG for /dev/random and /dev/urandom...\n"); + kprintf("Initializing ChaCha20-based CSPRNG for /dev/random and /dev/urandom...\n"); uint8_t entropy[64] = {0}; size_t epos = 0; @@ -162,7 +165,7 @@ void random_init(void) { uint32_t eax, ebx, ecx, edx; asm volatile("cpuid" : "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx) : "a"(1)); if (ecx & (1u << 30)) { - printf("RDRAND detected - using hardware RNG for seeding\n"); + kprintf("RDRAND detected - using hardware RNG for seeding\n"); for (int i = 0; i < 4 && epos + 8 <= sizeof(entropy); ++i) { uint64_t rd; asm volatile("1: rdrand %0\n\tjnc 1b" : "=r"(rd)); @@ -192,11 +195,11 @@ void random_init(void) { rng_counter = rdtsc(); /* random starting counter */ rng_seeded = true; - printf("ChaCha20 RNG seeded with %zu bytes of entropy (TSC, PIT, RDRAND, boot time, unix time, etc.)\n", epos); + kprintf("ChaCha20 RNG seeded with %zu bytes of entropy (TSC, PIT, RDRAND, boot time, unix time, etc.)\n", epos); /* Register both devices (open as "/dev/random" and "/dev/urandom") */ VFS_RegisterCharDev("/dev/random", random_read, random_write); VFS_RegisterCharDev("/dev/urandom", random_read, random_write); - printf("Registered chardevs: /dev/random and /dev/urandom (ChaCha20 CSPRNG)\n"); + kprintf("Registered chardevs: /dev/random and /dev/urandom (ChaCha20 CSPRNG)\n"); } \ No newline at end of file diff --git a/src/drivers/rand/random.h b/oldsrc/rand/random.h similarity index 100% rename from src/drivers/rand/random.h rename to oldsrc/rand/random.h diff --git a/ramdisk.tar b/ramdisk.tar new file mode 100644 index 0000000..bb66efe Binary files /dev/null and b/ramdisk.tar differ diff --git a/src/arch/x86_64/asm/asm.c b/src/arch/x86_64/asm/asm.c new file mode 100644 index 0000000..4eae3b4 --- /dev/null +++ b/src/arch/x86_64/asm/asm.c @@ -0,0 +1,44 @@ +#include "asm.h" +#include + +void halt(void) { + asm("hlt"); +} + +void cli(void) { + asm("cli"); +} + +void sti(void) { + asm("sti"); +} + +void pause(void) { + asm("pause"); +} + +void nop(void) { + asm("nop"); +} + +void dbgbrk(void) { + asm("int 3"); +} + +bool int_state(void) { + uint64_t flags; + asm volatile("pushfq; pop %0" : "=rm"(flags)::"memory"); + return flags & (1 << 9); +} + + +bool int_toggle(bool state) { + bool ret = int_state(); + if (state) { + sti(); + } else { + cli(); + } + return ret; +} + diff --git a/src/arch/x86_64/asm/asm.h b/src/arch/x86_64/asm/asm.h new file mode 100644 index 0000000..8b4cd3b --- /dev/null +++ b/src/arch/x86_64/asm/asm.h @@ -0,0 +1,20 @@ +#pragma once +#include +#include + +#define FLAT_PTR(PTR) (*((uintptr_t(*)[])(PTR))) + +#define BYTE_PTR(PTR) (*((uint8_t *)(PTR))) +#define WORD_PTR(PTR) (*((uint16_t *)(PTR))) +#define DWORD_PTR(PTR) (*((uint32_t *)(PTR))) +#define QWORD_PTR(PTR) (*((uint64_t *)(PTR))) + + +void halt(void); +void cli(void); +void sti(void); +void pause(void); +void nop(void); +void dbgbrk(void); +bool int_state(void); +bool int_toggle(bool state); \ No newline at end of file diff --git a/src/arch/x86_64/asm/mem.asm b/src/arch/x86_64/asm/mem.asm new file mode 100644 index 0000000..33a5723 --- /dev/null +++ b/src/arch/x86_64/asm/mem.asm @@ -0,0 +1,66 @@ +; Taken from Limine +; Added memset32 +section .text + +global memcpy +memcpy: + mov rcx, rdx + mov rax, rdi + rep movsb + ret + +global memset +memset: + push rdi + mov rax, rsi + mov rcx, rdx + rep stosb + pop rax + ret + +global memset32 +memset32: + push rdi + mov rax, rsi + mov rcx, rdx + rep stosd + pop rax + ret + +global memmove +memmove: + mov rcx, rdx + mov rax, rdi + + cmp rdi, rsi + ja .copy_backwards + + rep movsb + jmp .done + + .copy_backwards: + lea rdi, [rdi+rcx-1] + lea rsi, [rsi+rcx-1] + std + rep movsb + cld + + .done: + ret + +global memcmp +memcmp: + mov rcx, rdx + repe cmpsb + je .equal + + mov al, byte [rdi-1] + sub al, byte [rsi-1] + movsx rax, al + jmp .done + + .equal: + xor eax, eax + + .done: + ret diff --git a/src/arch/x86_64/boot/gdt.c b/src/arch/x86_64/boot/gdt.c index bd940e1..8bea316 100644 --- a/src/arch/x86_64/boot/gdt.c +++ b/src/arch/x86_64/boot/gdt.c @@ -2,8 +2,7 @@ #include "mm/memory.h" #include "mp/spinlock.h" #include - - +#include "libk/debug.h" struct gdt_desc { uint16_t limit; @@ -37,40 +36,28 @@ struct gdtr { struct gdtr gdt = {0}; struct gdt_ptr gdt_pointer = {0}; -struct tss kernel_tss = {0}; -spinlock_t s_gdt_lock = SPINLOCK_INIT; +spinlock_t gdt_lock; + -extern void gdt_reload(void); extern void tss_reload(void); - -void x86_64_GDT_Initialize(void) { - uint64_t flags; - spinlock_acquire_irqsave(&s_gdt_lock, &flags); - - // null desc - memset(&gdt.entries[0], 0, sizeof(struct gdt_desc)); +void gdt_init(void) { + spinlock_acquire_or_wait(&gdt_lock); // Kernel code - gdt.entries[1].limit = 0xFFFF; - gdt.entries[1].access = 0x9A; - gdt.entries[1].granularity = 0x20; + gdt.entries[1].access = 0b10011010; + gdt.entries[1].granularity = 0b00100000; // Kernel data - gdt.entries[2].limit = 0xFFFF; - gdt.entries[2].access = 0x92; - gdt.entries[2].granularity = 0x00; + gdt.entries[2].access = 0b10010010; // User data - gdt.entries[3].limit = 0xFFFF; - gdt.entries[3].access = 0xF2; - gdt.entries[3].granularity = 0x00; + gdt.entries[3].access = 0b11110010; // User code - gdt.entries[4].limit = 0xFFFF; - gdt.entries[4].access = 0xFA; - gdt.entries[4].granularity = 0x20; + gdt.entries[4].access = 0b11111010; + gdt.entries[4].granularity = 0b00100000; - // TSS 0x28 + // TSS gdt.tss.length = sizeof(struct tss); gdt.tss.flags1 = 0b10001001; @@ -80,12 +67,11 @@ void x86_64_GDT_Initialize(void) { gdt_reload(); tss_reload(); - spinlock_release_irqrestore(&s_gdt_lock, flags); + spinlock_drop(&gdt_lock); } void gdt_load_tss(size_t addr) { - uint64_t flags; - spinlock_acquire_irqsave(&s_gdt_lock, &flags); + spinlock_acquire_or_wait(&gdt_lock); gdt.tss.base_low = (uint16_t)addr; gdt.tss.base_mid = (uint8_t)(addr >> 16); gdt.tss.flags1 = 0b10001001; @@ -93,5 +79,5 @@ void gdt_load_tss(size_t addr) { gdt.tss.base_upper32 = (uint32_t)(addr >> 32); tss_reload(); - spinlock_release_irqrestore(&s_gdt_lock, flags); + spinlock_drop(&gdt_lock); } diff --git a/src/arch/x86_64/boot/gdt.h b/src/arch/x86_64/boot/gdt.h index fd79dc8..98de9c1 100644 --- a/src/arch/x86_64/boot/gdt.h +++ b/src/arch/x86_64/boot/gdt.h @@ -3,14 +3,13 @@ #include - #define GDT_KERNEL_BASE 0x0 #define GDT_KERNEL_CODE64 0x8 #define GDT_KERNEL_DATA64 0x10 #define GDT_USER_BASE 0x18 -#define GDT_USER_DATA64 0x18 -#define GDT_USER_CODE64 0x20 -#define GDT_TSS 0x28 +#define GDT_USER_DATA64 0x20 +#define GDT_USER_CODE64 0x28 +#define GDT_TSS 0x30 struct tss { uint32_t reserved; @@ -30,7 +29,7 @@ struct tss { uint16_t iomap_base; } __attribute__((packed)); -extern struct tss kernel_tss; +extern void gdt_reload(void); -void x86_64_GDT_Initialize(void); +void gdt_init(void); void gdt_load_tss(size_t addr); \ No newline at end of file diff --git a/src/arch/x86_64/boot/gdt_asm.S b/src/arch/x86_64/boot/gdt_asm.S deleted file mode 100644 index b2c8417..0000000 --- a/src/arch/x86_64/boot/gdt_asm.S +++ /dev/null @@ -1,27 +0,0 @@ -.intel_syntax noprefix -.global x86_64_GDT_Load -.extern gdt_pointer - -.global gdt_reload -gdt_reload: - lgdt [rip + gdt_pointer] - - push 0x08 - lea rax, [rip + .flush] - push rax - lretq - -.flush: - mov ax, 0x10 - mov ds, ax - mov es, ax - mov fs, ax - mov gs, ax - mov ss, ax - ret - -.global tss_reload -tss_reload: - mov ax, 0x2B - ltr ax - ret \ No newline at end of file diff --git a/src/arch/x86_64/boot/gdt_asm.asm b/src/arch/x86_64/boot/gdt_asm.asm new file mode 100644 index 0000000..a2da2da --- /dev/null +++ b/src/arch/x86_64/boot/gdt_asm.asm @@ -0,0 +1,23 @@ +extern gdt_pointer +global gdt_reload + +gdt_reload: + lgdt [rel gdt_pointer] + push 8 + lea rax, [rel .flush] + push rax + retfq +.flush: + mov eax, 0x10 + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + mov ss, ax + ret + +global tss_reload +tss_reload: + mov ax, 0x2B + ltr ax + ret diff --git a/src/arch/x86_64/boot/idt.c b/src/arch/x86_64/boot/idt.c index 72bb696..ece1da7 100644 --- a/src/arch/x86_64/boot/idt.c +++ b/src/arch/x86_64/boot/idt.c @@ -1,35 +1,38 @@ #include "idt.h" -#include "libk/binary.h" +#include +#include +struct idt_entry { + uint16_t offset_lo; + uint16_t selector; + uint8_t ist; + uint8_t flags; + uint16_t offset_mid; + uint32_t offset_hi; + uint32_t zero; +} __attribute__((packed)); -IDTEntry g_IDT[256]; -IDTDescriptor g_IDTDescriptor = { sizeof(g_IDT) - 1, g_IDT }; +struct idt_register { + uint16_t size; + uint64_t address; +} __attribute__((packed)); -extern void x86_64_IDT_Load(IDTDescriptor* descriptor); +struct idt_entry idt[256] = {0}; -void x86_64_IDT_SetGate(int interrupt, void* base, uint16_t segmentDescriptor, uint8_t flags) -{ - uintptr_t addr = (uintptr_t)base; - g_IDT[interrupt].BaseLow = addr & 0xFFFF; - g_IDT[interrupt].SegmentSelector = segmentDescriptor; - g_IDT[interrupt].IST = 0; - g_IDT[interrupt].Flags = flags; - g_IDT[interrupt].BaseMid = (addr >> 16) & 0xFFFF; - g_IDT[interrupt].BaseHigh = (addr >> 32) & 0xFFFFFFFF; - g_IDT[interrupt].Reserved = 0; -} -void x86_64_IDT_EnableGate(int interrupt) -{ - FLAG_SET(g_IDT[interrupt].Flags, IDT_FLAG_PRESENT); +void idt_set_gate(size_t vec, void *handler, uint8_t ist) { + uint64_t p = (uint64_t)handler; + + idt[vec].offset_lo = (uint16_t)p; + idt[vec].selector = 8; + idt[vec].ist = ist; + idt[vec].flags = 0x8E; + idt[vec].offset_mid = (uint16_t)(p >> 16); + idt[vec].offset_hi = (uint32_t)(p >> 32); + idt[vec].zero = 0; } -void x86_64_IDT_DisableGate(int interrupt) -{ - FLAG_UNSET(g_IDT[interrupt].Flags, IDT_FLAG_PRESENT); +void idt_reload(void) { + struct idt_register idt_ptr = {sizeof(idt) - 1, (uint64_t)idt}; + + asm volatile("lidtq %0" : : "m"(idt_ptr)); } - - -void x86_64_IDT_Initialize(void) -{ - x86_64_IDT_Load(&g_IDTDescriptor); -} \ No newline at end of file diff --git a/src/arch/x86_64/boot/idt.h b/src/arch/x86_64/boot/idt.h index c033f0a..ec8fc19 100644 --- a/src/arch/x86_64/boot/idt.h +++ b/src/arch/x86_64/boot/idt.h @@ -1,44 +1,7 @@ #pragma once #include +#include - -typedef enum -{ - IDT_FLAG_GATE_TASK = 0x5, - IDT_FLAG_GATE_16BIT_INT = 0x6, - IDT_FLAG_GATE_16BIT_TRAP = 0x7, - IDT_FLAG_GATE_32BIT_INT = 0xE, - IDT_FLAG_GATE_32BIT_TRAP = 0xF, - - IDT_FLAG_RING0 = (0 << 5), - IDT_FLAG_RING1 = (1 << 5), - IDT_FLAG_RING2 = (2 << 5), - IDT_FLAG_RING3 = (3 << 5), - - IDT_FLAG_PRESENT = 0x80, - -} IDT_FLAGS; - -typedef struct -{ - uint16_t BaseLow; - uint16_t SegmentSelector; - uint8_t IST; // Interrupt Stack Table (0 = default) - uint8_t Flags; - uint16_t BaseMid; - uint32_t BaseHigh; - uint32_t Reserved; -} __attribute__((packed)) IDTEntry; - -typedef struct -{ - uint16_t Limit; - IDTEntry* Ptr; -} __attribute__((packed)) IDTDescriptor; - - -void x86_64_IDT_Initialize(void); -void x86_64_IDT_SetGate(int interrupt, void* base, uint16_t segmentDescriptor, uint8_t flags); -void x86_64_IDT_EnableGate(int interrupt); -void x86_64_IDT_DisableGate(int interrupt); \ No newline at end of file +void idt_set_gate(size_t vec, void *handler, uint8_t ist); +void idt_reload(void); \ No newline at end of file diff --git a/src/arch/x86_64/boot/idt_asm.S b/src/arch/x86_64/boot/idt_asm.S deleted file mode 100644 index 574134c..0000000 --- a/src/arch/x86_64/boot/idt_asm.S +++ /dev/null @@ -1,6 +0,0 @@ -.intel_syntax noprefix -.global x86_64_IDT_Load - -x86_64_IDT_Load: - lidt [rdi] # descriptor in RDI - ret \ No newline at end of file diff --git a/src/arch/x86_64/boot/isr.c b/src/arch/x86_64/boot/isr.c index 9f8bf09..7c09b2d 100644 --- a/src/arch/x86_64/boot/isr.c +++ b/src/arch/x86_64/boot/isr.c @@ -1,124 +1,362 @@ -// ... includes ... #include "arch/x86_64/cpu/io.h" #include "libk/debug.h" #include "arch/x86_64/boot/isr.h" #include "idt.h" -#define MODULE "ISR" +#include "mm/vmm.h" +#include "arch/x86_64/cpu/reg.h" +#include "mp/mp.h" +#include "mm/memory.h" +#include "mm/pmm.h" +#include "arch/x86_64/sys/halt.h" +#include "sched/sched.h" -static inline uint64_t x86_64_read_cr2(void) -{ - uint64_t value; - __asm__ volatile ("mov %%cr2, %0" : "=r"(value)); - return value; +void isr_install(void) { + idt_set_gate(0, isr0, 0); + idt_set_gate(1, isr1, 0); + idt_set_gate(2, isr2, 0); + idt_set_gate(3, isr3, 0); + idt_set_gate(4, isr4, 0); + idt_set_gate(5, isr5, 0); + idt_set_gate(6, isr6, 0); + idt_set_gate(7, isr7, 0); + idt_set_gate(8, isr8, 0); + idt_set_gate(9, isr9, 0); + idt_set_gate(10, isr10, 0); + idt_set_gate(11, isr11, 0); + idt_set_gate(12, isr12, 0); + idt_set_gate(13, isr13, 0); + idt_set_gate(14, isr14, 2); + idt_set_gate(15, isr15, 0); + idt_set_gate(16, isr16, 0); + idt_set_gate(17, isr17, 0); + idt_set_gate(18, isr18, 0); + idt_set_gate(19, isr19, 0); + idt_set_gate(20, isr20, 0); + idt_set_gate(21, isr21, 0); + idt_set_gate(22, isr22, 0); + idt_set_gate(23, isr23, 0); + idt_set_gate(24, isr24, 0); + idt_set_gate(25, isr25, 0); + idt_set_gate(26, isr26, 0); + idt_set_gate(27, isr27, 0); + idt_set_gate(28, isr28, 0); + idt_set_gate(29, isr29, 0); + idt_set_gate(30, isr30, 0); + idt_set_gate(31, isr31, 0); + idt_set_gate(32, isr32, 0); + idt_set_gate(33, isr33, 0); + idt_set_gate(34, isr34, 0); + idt_set_gate(35, isr35, 0); + idt_set_gate(36, isr36, 0); + idt_set_gate(37, isr37, 0); + idt_set_gate(38, isr38, 0); + idt_set_gate(39, isr39, 0); + idt_set_gate(40, isr40, 0); + idt_set_gate(41, isr41, 0); + idt_set_gate(42, isr42, 0); + idt_set_gate(43, isr43, 0); + idt_set_gate(44, isr44, 0); + idt_set_gate(45, isr45, 0); + idt_set_gate(46, isr46, 0); + idt_set_gate(47, isr47, 0); + idt_set_gate(48, isr48, 1); + idt_set_gate(49, isr49, 0); + idt_set_gate(50, isr50, 0); + idt_set_gate(51, isr51, 0); + idt_set_gate(52, isr52, 0); + idt_set_gate(53, isr53, 0); + idt_set_gate(54, isr54, 0); + idt_set_gate(55, isr55, 0); + idt_set_gate(56, isr56, 0); + idt_set_gate(57, isr57, 0); + idt_set_gate(58, isr58, 0); + idt_set_gate(59, isr59, 0); + idt_set_gate(60, isr60, 0); + idt_set_gate(61, isr61, 0); + idt_set_gate(62, isr62, 0); + idt_set_gate(63, isr63, 0); + idt_set_gate(64, isr64, 0); + idt_set_gate(65, isr65, 0); + idt_set_gate(66, isr66, 0); + idt_set_gate(67, isr67, 0); + idt_set_gate(68, isr68, 0); + idt_set_gate(69, isr69, 0); + idt_set_gate(70, isr70, 0); + idt_set_gate(71, isr71, 0); + idt_set_gate(72, isr72, 0); + idt_set_gate(73, isr73, 0); + idt_set_gate(74, isr74, 0); + idt_set_gate(75, isr75, 0); + idt_set_gate(76, isr76, 0); + idt_set_gate(77, isr77, 0); + idt_set_gate(78, isr78, 0); + idt_set_gate(79, isr79, 0); + idt_set_gate(80, isr80, 0); + idt_set_gate(81, isr81, 0); + idt_set_gate(82, isr82, 0); + idt_set_gate(83, isr83, 0); + idt_set_gate(84, isr84, 0); + idt_set_gate(85, isr85, 0); + idt_set_gate(86, isr86, 0); + idt_set_gate(87, isr87, 0); + idt_set_gate(88, isr88, 0); + idt_set_gate(89, isr89, 0); + idt_set_gate(90, isr90, 0); + idt_set_gate(91, isr91, 0); + idt_set_gate(92, isr92, 0); + idt_set_gate(93, isr93, 0); + idt_set_gate(94, isr94, 0); + idt_set_gate(95, isr95, 0); + idt_set_gate(96, isr96, 0); + idt_set_gate(97, isr97, 0); + idt_set_gate(98, isr98, 0); + idt_set_gate(99, isr99, 0); + idt_set_gate(100, isr100, 0); + idt_set_gate(101, isr101, 0); + idt_set_gate(102, isr102, 0); + idt_set_gate(103, isr103, 0); + idt_set_gate(104, isr104, 0); + idt_set_gate(105, isr105, 0); + idt_set_gate(106, isr106, 0); + idt_set_gate(107, isr107, 0); + idt_set_gate(108, isr108, 0); + idt_set_gate(109, isr109, 0); + idt_set_gate(110, isr110, 0); + idt_set_gate(111, isr111, 0); + idt_set_gate(112, isr112, 0); + idt_set_gate(113, isr113, 0); + idt_set_gate(114, isr114, 0); + idt_set_gate(115, isr115, 0); + idt_set_gate(116, isr116, 0); + idt_set_gate(117, isr117, 0); + idt_set_gate(118, isr118, 0); + idt_set_gate(119, isr119, 0); + idt_set_gate(120, isr120, 0); + idt_set_gate(121, isr121, 0); + idt_set_gate(122, isr122, 0); + idt_set_gate(123, isr123, 0); + idt_set_gate(124, isr124, 0); + idt_set_gate(125, isr125, 0); + idt_set_gate(126, isr126, 0); + idt_set_gate(127, isr127, 0); + idt_set_gate(128, isr128, 0); + idt_set_gate(129, isr129, 0); + idt_set_gate(130, isr130, 0); + idt_set_gate(131, isr131, 0); + idt_set_gate(132, isr132, 0); + idt_set_gate(133, isr133, 0); + idt_set_gate(134, isr134, 0); + idt_set_gate(135, isr135, 0); + idt_set_gate(136, isr136, 0); + idt_set_gate(137, isr137, 0); + idt_set_gate(138, isr138, 0); + idt_set_gate(139, isr139, 0); + idt_set_gate(140, isr140, 0); + idt_set_gate(141, isr141, 0); + idt_set_gate(142, isr142, 0); + idt_set_gate(143, isr143, 0); + idt_set_gate(144, isr144, 0); + idt_set_gate(145, isr145, 0); + idt_set_gate(146, isr146, 0); + idt_set_gate(147, isr147, 0); + idt_set_gate(148, isr148, 0); + idt_set_gate(149, isr149, 0); + idt_set_gate(150, isr150, 0); + idt_set_gate(151, isr151, 0); + idt_set_gate(152, isr152, 0); + idt_set_gate(153, isr153, 0); + idt_set_gate(154, isr154, 0); + idt_set_gate(155, isr155, 0); + idt_set_gate(156, isr156, 0); + idt_set_gate(157, isr157, 0); + idt_set_gate(158, isr158, 0); + idt_set_gate(159, isr159, 0); + idt_set_gate(160, isr160, 0); + idt_set_gate(161, isr161, 0); + idt_set_gate(162, isr162, 0); + idt_set_gate(163, isr163, 0); + idt_set_gate(164, isr164, 0); + idt_set_gate(165, isr165, 0); + idt_set_gate(166, isr166, 0); + idt_set_gate(167, isr167, 0); + idt_set_gate(168, isr168, 0); + idt_set_gate(169, isr169, 0); + idt_set_gate(170, isr170, 0); + idt_set_gate(171, isr171, 0); + idt_set_gate(172, isr172, 0); + idt_set_gate(173, isr173, 0); + idt_set_gate(174, isr174, 0); + idt_set_gate(175, isr175, 0); + idt_set_gate(176, isr176, 0); + idt_set_gate(177, isr177, 0); + idt_set_gate(178, isr178, 0); + idt_set_gate(179, isr179, 0); + idt_set_gate(180, isr180, 0); + idt_set_gate(181, isr181, 0); + idt_set_gate(182, isr182, 0); + idt_set_gate(183, isr183, 0); + idt_set_gate(184, isr184, 0); + idt_set_gate(185, isr185, 0); + idt_set_gate(186, isr186, 0); + idt_set_gate(187, isr187, 0); + idt_set_gate(188, isr188, 0); + idt_set_gate(189, isr189, 0); + idt_set_gate(190, isr190, 0); + idt_set_gate(191, isr191, 0); + idt_set_gate(192, isr192, 0); + idt_set_gate(193, isr193, 0); + idt_set_gate(194, isr194, 0); + idt_set_gate(195, isr195, 0); + idt_set_gate(196, isr196, 0); + idt_set_gate(197, isr197, 0); + idt_set_gate(198, isr198, 0); + idt_set_gate(199, isr199, 0); + idt_set_gate(200, isr200, 0); + idt_set_gate(201, isr201, 0); + idt_set_gate(202, isr202, 0); + idt_set_gate(203, isr203, 0); + idt_set_gate(204, isr204, 0); + idt_set_gate(205, isr205, 0); + idt_set_gate(206, isr206, 0); + idt_set_gate(207, isr207, 0); + idt_set_gate(208, isr208, 0); + idt_set_gate(209, isr209, 0); + idt_set_gate(210, isr210, 0); + idt_set_gate(211, isr211, 0); + idt_set_gate(212, isr212, 0); + idt_set_gate(213, isr213, 0); + idt_set_gate(214, isr214, 0); + idt_set_gate(215, isr215, 0); + idt_set_gate(216, isr216, 0); + idt_set_gate(217, isr217, 0); + idt_set_gate(218, isr218, 0); + idt_set_gate(219, isr219, 0); + idt_set_gate(220, isr220, 0); + idt_set_gate(221, isr221, 0); + idt_set_gate(222, isr222, 0); + idt_set_gate(223, isr223, 0); + idt_set_gate(224, isr224, 0); + idt_set_gate(225, isr225, 0); + idt_set_gate(226, isr226, 0); + idt_set_gate(227, isr227, 0); + idt_set_gate(228, isr228, 0); + idt_set_gate(229, isr229, 0); + idt_set_gate(230, isr230, 0); + idt_set_gate(231, isr231, 0); + idt_set_gate(232, isr232, 0); + idt_set_gate(233, isr233, 0); + idt_set_gate(234, isr234, 0); + idt_set_gate(235, isr235, 0); + idt_set_gate(236, isr236, 0); + idt_set_gate(237, isr237, 0); + idt_set_gate(238, isr238, 0); + idt_set_gate(239, isr239, 0); + idt_set_gate(240, isr240, 0); + idt_set_gate(241, isr241, 0); + idt_set_gate(242, isr242, 0); + idt_set_gate(243, isr243, 0); + idt_set_gate(244, isr244, 0); + idt_set_gate(245, isr245, 0); + idt_set_gate(246, isr246, 0); + idt_set_gate(247, isr247, 0); + idt_set_gate(248, isr248, 0); + idt_set_gate(249, isr249, 0); + idt_set_gate(250, isr250, 0); + idt_set_gate(251, isr251, 0); + idt_set_gate(252, isr252, 0); + idt_set_gate(253, isr253, 0); + idt_set_gate(254, isr254, 0); + idt_set_gate(255, isr255, 0); + idt_reload(); } -ISRHandler g_ISRHandlers[256]; -static const char* const g_Exceptions[] = { - "Divide by zero error", - "Debug", - "Non-maskable Interrupt", - "Breakpoint", - "Overflow", - "Bound Range Exceeded", - "Invalid Opcode", - "Device Not Available", - "Double Fault", - "Coprocessor Segment Overrun", - "Invalid TSS", - "Segment Not Present", - "Stack-Segment Fault", - "General Protection Fault", - "Page Fault", - "", - "x87 Floating-Point Exception", - "Alignment Check", - "Machine Check", - "SIMD Floating-Point Exception", - "Virtualization Exception", - "Control Protection Exception ", - "", - "", - "", - "", - "", - "", - "Hypervisor Injection Exception", - "VMM Communication Exception", - "Security Exception", - "" -}; +static const char *isr_exception_messages[] = {"Divide by zero", + "Debug", + "NMI", + "Breakpoint", + "Overflow", + "Bound Range Exceeded", + "Invalid Opcode", + "Device Not Available", + "Double fault", + "Co-processor Segment Overrun", + "Invalid TSS", + "Segment not present", + "Stack-Segment Fault", + "GPF", + "Page Fault", + "Reserved", + "x87 Floating Point Exception", + "alignment check", + "Machine check", + "SIMD floating-point exception", + "Virtualization Exception", + "Deadlock", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Security Exception", + "Reserved", + "Triple Fault", + "FPU error"}; -extern void x86_64_ISR_InitializeGates(void); // defined in isrs_gen.c +static event_handlers_t event_handlers[256] = {NULL}; -void x86_64_ISR_Initialize(void) -{ - x86_64_ISR_InitializeGates(); - for (int i = 0; i < 256; i++) - x86_64_IDT_EnableGate(i); - x86_64_IDT_DisableGate(0x80); // syscall gate +void isr_register_handler(int n, void *handler) { + event_handlers[n] = handler; } -void page_fault_handler(Registers* regs, uint64_t cr2) -{ +void isr_handle(registers_t *r) { + if (r->cs & 0x3) { + swapgs(); + } + if (r->isrNumber < 256 && event_handlers[r->isrNumber] != NULL) { + event_handlers[r->isrNumber](r); + } else { + if (r->isrNumber < 32) { + if (r->cs & 0x3) { + struct thread *thrd = sched_get_running_thread(); + kprintf( + "Killing user thread tid %d under process %s for exception " + "%s\n", + thrd->tid, thrd->mother_proc->name, + isr_exception_messages[r->isrNumber]); + kprintf("User thread crashed at address: %p\n", r->rip); + thread_kill(thrd, true); + } else { + halt_other_cpus(); + kprintffos(0, "AH! UNHANDLED EXCEPTION!\n"); + kprintffos(0, "RIP: %p RBP: %p RSP: %p\n", r->rip, r->rbp, + r->rsp); + kprintffos(0, "RAX: %p RBX: %p RCX: %p\n", r->rax, r->rbx, + r->rcx); + kprintffos(0, "RDX: %p RDI: %p RSI: %p\n", r->rdx, r->rdi, + r->rsi); + kprintffos(0, "R8 : %p R9 : %p R10: %p\n", r->r8, r->r9, + r->r10); + kprintffos(0, "R11: %p R12: %p R13: %p\n", r->r11, r->r12, + r->r13); + kprintffos(0, "R14: %p R15: %p ERR: 0b%b\n", r->r14, r->r15, + r->errorCode); + kprintffos(0, "CS : %p SS : %p RFLAGS: %p\n", r->cs, r->ss, + r->rflags); + kprintffos(0, "FS : %p UGS: %p KGS: %p\n", read_fs_base(), + read_user_gs(), read_kernel_gs()); + panic_((void *)r->rip, (void *)r->rbp, + "Unhandled Exception: %s\n", + isr_exception_messages[r->isrNumber]); + } + } + } - - // bit 0: present - // bit 1: write - // bit 2: user-mode - // bit 3: reserved overwrite - // bit 4: instruction fetch - - log_crit(MODULE, "KERNEL PANIC! Exception %d (%s)", regs->interrupt, g_Exceptions[regs->interrupt]); - - log_crit(MODULE, " rax=%x rbx=%x rcx=%x rdx=%x rsi=%x rdi=%x", - regs->rax, regs->rbx, regs->rcx, regs->rdx, regs->rsi, regs->rdi); - - log_crit(MODULE, " rsp=%x rbp=%x rip=%x rflags=%x cs=%x ss=%x", - regs->rsp, regs->rbp, regs->rip, regs->rflags, regs->cs, regs->ss); - - log_crit(MODULE, " interrupt=%x errorcode=%x", regs->interrupt, regs->error); - - log_crit("PAGEFAULT", "Fault at address %lx (rip=%lx, err=%x)", - cr2, regs->rip, regs->error); - - log_crit(MODULE, "KERNEL PANIC!"); - - x86_64_Panic(); // or attempt recovery + if (r->cs & 0x3) { + swapgs(); + } } - -void __attribute__((used)) x86_64_ISR_Handler(Registers* regs) -{ - if (regs->interrupt == 14) - { - uint64_t cr2 = x86_64_read_cr2(); - page_fault_handler(regs, cr2); - - } - - if (g_ISRHandlers[regs->interrupt]) - g_ISRHandlers[regs->interrupt](regs); - else if (regs->interrupt >= 32) - log_err(MODULE, "Unhandled interrupt %d!", regs->interrupt); - else { - // panic log (use regs->rax etc. now) - log_crit(MODULE, "KERNEL PANIC! Exception %d (%s)", regs->interrupt, g_Exceptions[regs->interrupt]); - // ... print registers with rax/rbx etc. ... - log_crit(MODULE, " rax=%x rbx=%x rcx=%x rdx=%x rsi=%x rdi=%x", - regs->rax, regs->rbx, regs->rcx, regs->rdx, regs->rsi, regs->rdi); - - log_crit(MODULE, " rsp=%x rbp=%x rip=%x rflags=%x cs=%x ss=%x", - regs->rsp, regs->rbp, regs->rip, regs->rflags, regs->cs, regs->ss); - - log_crit(MODULE, " interrupt=%x errorcode=%x", regs->interrupt, regs->error); - - log_crit(MODULE, "KERNEL PANIC!"); - x86_64_Panic(); - } -} - -void x86_64_ISR_RegisterHandler(int interrupt, ISRHandler handler) -{ - g_ISRHandlers[interrupt] = handler; - x86_64_IDT_EnableGate(interrupt); -} \ No newline at end of file diff --git a/src/arch/x86_64/boot/isr.h b/src/arch/x86_64/boot/isr.h index f12ea68..6ca5b50 100644 --- a/src/arch/x86_64/boot/isr.h +++ b/src/arch/x86_64/boot/isr.h @@ -1,15 +1,46 @@ #pragma once #include +#include "arch/x86_64/cpu/reg.h" -typedef struct -{ - uint64_t r15, r14, r13, r12, r11, r10, r9, r8; - uint64_t rdi, rsi, rbp, rdx, rcx, rbx, rax; - uint64_t interrupt, error; - uint64_t rip, cs, rflags, rsp, ss; -} __attribute__((packed)) Registers; +#define PASTER(x, y) x##y +#define EVALUATOR(x, y) PASTER(x, y) +// This first macro makes functions like "void isr0(void)" +// But the number is summed up on each call +#define ONE EVALUATOR(void isr, __COUNTER__)(void) +// Different granularities +#define TWO \ + ONE; \ + ONE +#define TEN \ + TWO; \ + TWO; \ + TWO; \ + TWO; \ + TWO +#define TWENTY \ + TEN; \ + TEN +#define HUNDRED \ + TWENTY; \ + TWENTY; \ + TWENTY; \ + TWENTY; \ + TWENTY +// Define 255 ISRs based on previous granularities +#define DEFISR \ + HUNDRED; \ + HUNDRED; \ + TWENTY; \ + TWENTY; \ + TEN; \ + TWO; \ + TWO; \ + TWO -typedef void (*ISRHandler)(Registers* regs); +DEFISR; -void x86_64_ISR_Initialize(void); -void x86_64_ISR_RegisterHandler(int interrupt, ISRHandler handler); \ No newline at end of file +typedef void (*event_handlers_t)(registers_t *); + +void isr_install(void); +extern void isr_handle(registers_t *r); +void isr_register_handler(int n, void *handler); \ No newline at end of file diff --git a/src/arch/x86_64/boot/isr_asm.S b/src/arch/x86_64/boot/isr_asm.S deleted file mode 100644 index 9066d91..0000000 --- a/src/arch/x86_64/boot/isr_asm.S +++ /dev/null @@ -1,58 +0,0 @@ -.intel_syntax noprefix -.extern x86_64_ISR_Handler - -.macro ISR_NOERRORCODE num - .global x86_64_ISR\num -x86_64_ISR\num: - push 0 - push \num - jmp isr_common -.endm - -.macro ISR_ERRORCODE num - .global x86_64_ISR\num -x86_64_ISR\num: - push \num - jmp isr_common -.endm - -.include "arch/x86_64/boot/isrs_gen.inc" - -isr_common: - push rax - push rbx - push rcx - push rdx - push rbp - push rsi - push rdi - push r8 - push r9 - push r10 - push r11 - push r12 - push r13 - push r14 - push r15 - - mov rdi, rsp // Registers* in RDI - call x86_64_ISR_Handler - - pop r15 - pop r14 - pop r13 - pop r12 - pop r11 - pop r10 - pop r9 - pop r8 - pop rdi - pop rsi - pop rbp - pop rdx - pop rcx - pop rbx - pop rax - - add rsp, 16 // pop interrupt + error code - iretq \ No newline at end of file diff --git a/src/arch/x86_64/boot/isr_asm.asm b/src/arch/x86_64/boot/isr_asm.asm new file mode 100644 index 0000000..2189578 --- /dev/null +++ b/src/arch/x86_64/boot/isr_asm.asm @@ -0,0 +1,87 @@ + +%macro pushall 0 + +push rax +push rbx +push rcx +push rdx +push rbp +push rdi +push rsi +push r8 +push r9 +push r10 +push r11 +push r12 +push r13 +push r14 +push r15 + +%endmacro + +%macro popall 0 + +pop r15 +pop r14 +pop r13 +pop r12 +pop r11 +pop r10 +pop r9 +pop r8 +pop rsi +pop rdi +pop rbp +pop rdx +pop rcx +pop rbx +pop rax + +%endmacro + +extern isr_handle + +; Common handler for the ISRs +isr_common_format: + pushall + cld + mov rdi, rsp + xor rbp, rbp + call isr_handle + popall + add rsp, 24 + iretq + +%macro isr 1 + +global isr%1 +isr%1: + push 0 + push %1 + push fs + jmp isr_common_format + +%endmacro + +%macro error_isr 1 + +global isr%1 +isr%1: + push %1 + push fs + jmp isr_common_format + +%endmacro + +%define has_errcode(i) (i == 8 || (i >= 10 && i <= 14) || i == 17 || i == 21 || i == 29 || i == 30) + +; Define ISRs +%assign i 0 +%rep 256 +%if !has_errcode(i) + isr i +%else + error_isr i +%endif +%assign i i + 1 +%endrep diff --git a/src/arch/x86_64/boot/isrs_gen.c b/src/arch/x86_64/boot/isrs_gen.c deleted file mode 100644 index 82f3a07..0000000 --- a/src/arch/x86_64/boot/isrs_gen.c +++ /dev/null @@ -1,520 +0,0 @@ -// !!! THIS FILE IS AUTOGENERATED !!! -#include "idt.h" -#include "gdt.h" - -void x86_64_ISR0(); -void x86_64_ISR1(); -void x86_64_ISR2(); -void x86_64_ISR3(); -void x86_64_ISR4(); -void x86_64_ISR5(); -void x86_64_ISR6(); -void x86_64_ISR7(); -void x86_64_ISR8(); -void x86_64_ISR9(); -void x86_64_ISR10(); -void x86_64_ISR11(); -void x86_64_ISR12(); -void x86_64_ISR13(); -void x86_64_ISR14(); -void x86_64_ISR15(); -void x86_64_ISR16(); -void x86_64_ISR17(); -void x86_64_ISR18(); -void x86_64_ISR19(); -void x86_64_ISR20(); -void x86_64_ISR21(); -void x86_64_ISR22(); -void x86_64_ISR23(); -void x86_64_ISR24(); -void x86_64_ISR25(); -void x86_64_ISR26(); -void x86_64_ISR27(); -void x86_64_ISR28(); -void x86_64_ISR29(); -void x86_64_ISR30(); -void x86_64_ISR31(); -void x86_64_ISR32(); -void x86_64_ISR33(); -void x86_64_ISR34(); -void x86_64_ISR35(); -void x86_64_ISR36(); -void x86_64_ISR37(); -void x86_64_ISR38(); -void x86_64_ISR39(); -void x86_64_ISR40(); -void x86_64_ISR41(); -void x86_64_ISR42(); -void x86_64_ISR43(); -void x86_64_ISR44(); -void x86_64_ISR45(); -void x86_64_ISR46(); -void x86_64_ISR47(); -void x86_64_ISR48(); -void x86_64_ISR49(); -void x86_64_ISR50(); -void x86_64_ISR51(); -void x86_64_ISR52(); -void x86_64_ISR53(); -void x86_64_ISR54(); -void x86_64_ISR55(); -void x86_64_ISR56(); -void x86_64_ISR57(); -void x86_64_ISR58(); -void x86_64_ISR59(); -void x86_64_ISR60(); -void x86_64_ISR61(); -void x86_64_ISR62(); -void x86_64_ISR63(); -void x86_64_ISR64(); -void x86_64_ISR65(); -void x86_64_ISR66(); -void x86_64_ISR67(); -void x86_64_ISR68(); -void x86_64_ISR69(); -void x86_64_ISR70(); -void x86_64_ISR71(); -void x86_64_ISR72(); -void x86_64_ISR73(); -void x86_64_ISR74(); -void x86_64_ISR75(); -void x86_64_ISR76(); -void x86_64_ISR77(); -void x86_64_ISR78(); -void x86_64_ISR79(); -void x86_64_ISR80(); -void x86_64_ISR81(); -void x86_64_ISR82(); -void x86_64_ISR83(); -void x86_64_ISR84(); -void x86_64_ISR85(); -void x86_64_ISR86(); -void x86_64_ISR87(); -void x86_64_ISR88(); -void x86_64_ISR89(); -void x86_64_ISR90(); -void x86_64_ISR91(); -void x86_64_ISR92(); -void x86_64_ISR93(); -void x86_64_ISR94(); -void x86_64_ISR95(); -void x86_64_ISR96(); -void x86_64_ISR97(); -void x86_64_ISR98(); -void x86_64_ISR99(); -void x86_64_ISR100(); -void x86_64_ISR101(); -void x86_64_ISR102(); -void x86_64_ISR103(); -void x86_64_ISR104(); -void x86_64_ISR105(); -void x86_64_ISR106(); -void x86_64_ISR107(); -void x86_64_ISR108(); -void x86_64_ISR109(); -void x86_64_ISR110(); -void x86_64_ISR111(); -void x86_64_ISR112(); -void x86_64_ISR113(); -void x86_64_ISR114(); -void x86_64_ISR115(); -void x86_64_ISR116(); -void x86_64_ISR117(); -void x86_64_ISR118(); -void x86_64_ISR119(); -void x86_64_ISR120(); -void x86_64_ISR121(); -void x86_64_ISR122(); -void x86_64_ISR123(); -void x86_64_ISR124(); -void x86_64_ISR125(); -void x86_64_ISR126(); -void x86_64_ISR127(); -void x86_64_ISR128(); -void x86_64_ISR129(); -void x86_64_ISR130(); -void x86_64_ISR131(); -void x86_64_ISR132(); -void x86_64_ISR133(); -void x86_64_ISR134(); -void x86_64_ISR135(); -void x86_64_ISR136(); -void x86_64_ISR137(); -void x86_64_ISR138(); -void x86_64_ISR139(); -void x86_64_ISR140(); -void x86_64_ISR141(); -void x86_64_ISR142(); -void x86_64_ISR143(); -void x86_64_ISR144(); -void x86_64_ISR145(); -void x86_64_ISR146(); -void x86_64_ISR147(); -void x86_64_ISR148(); -void x86_64_ISR149(); -void x86_64_ISR150(); -void x86_64_ISR151(); -void x86_64_ISR152(); -void x86_64_ISR153(); -void x86_64_ISR154(); -void x86_64_ISR155(); -void x86_64_ISR156(); -void x86_64_ISR157(); -void x86_64_ISR158(); -void x86_64_ISR159(); -void x86_64_ISR160(); -void x86_64_ISR161(); -void x86_64_ISR162(); -void x86_64_ISR163(); -void x86_64_ISR164(); -void x86_64_ISR165(); -void x86_64_ISR166(); -void x86_64_ISR167(); -void x86_64_ISR168(); -void x86_64_ISR169(); -void x86_64_ISR170(); -void x86_64_ISR171(); -void x86_64_ISR172(); -void x86_64_ISR173(); -void x86_64_ISR174(); -void x86_64_ISR175(); -void x86_64_ISR176(); -void x86_64_ISR177(); -void x86_64_ISR178(); -void x86_64_ISR179(); -void x86_64_ISR180(); -void x86_64_ISR181(); -void x86_64_ISR182(); -void x86_64_ISR183(); -void x86_64_ISR184(); -void x86_64_ISR185(); -void x86_64_ISR186(); -void x86_64_ISR187(); -void x86_64_ISR188(); -void x86_64_ISR189(); -void x86_64_ISR190(); -void x86_64_ISR191(); -void x86_64_ISR192(); -void x86_64_ISR193(); -void x86_64_ISR194(); -void x86_64_ISR195(); -void x86_64_ISR196(); -void x86_64_ISR197(); -void x86_64_ISR198(); -void x86_64_ISR199(); -void x86_64_ISR200(); -void x86_64_ISR201(); -void x86_64_ISR202(); -void x86_64_ISR203(); -void x86_64_ISR204(); -void x86_64_ISR205(); -void x86_64_ISR206(); -void x86_64_ISR207(); -void x86_64_ISR208(); -void x86_64_ISR209(); -void x86_64_ISR210(); -void x86_64_ISR211(); -void x86_64_ISR212(); -void x86_64_ISR213(); -void x86_64_ISR214(); -void x86_64_ISR215(); -void x86_64_ISR216(); -void x86_64_ISR217(); -void x86_64_ISR218(); -void x86_64_ISR219(); -void x86_64_ISR220(); -void x86_64_ISR221(); -void x86_64_ISR222(); -void x86_64_ISR223(); -void x86_64_ISR224(); -void x86_64_ISR225(); -void x86_64_ISR226(); -void x86_64_ISR227(); -void x86_64_ISR228(); -void x86_64_ISR229(); -void x86_64_ISR230(); -void x86_64_ISR231(); -void x86_64_ISR232(); -void x86_64_ISR233(); -void x86_64_ISR234(); -void x86_64_ISR235(); -void x86_64_ISR236(); -void x86_64_ISR237(); -void x86_64_ISR238(); -void x86_64_ISR239(); -void x86_64_ISR240(); -void x86_64_ISR241(); -void x86_64_ISR242(); -void x86_64_ISR243(); -void x86_64_ISR244(); -void x86_64_ISR245(); -void x86_64_ISR246(); -void x86_64_ISR247(); -void x86_64_ISR248(); -void x86_64_ISR249(); -void x86_64_ISR250(); -void x86_64_ISR251(); -void x86_64_ISR252(); -void x86_64_ISR253(); -void x86_64_ISR254(); -void x86_64_ISR255(); - -void x86_64_ISR_InitializeGates() -{ - x86_64_IDT_SetGate(0, x86_64_ISR0, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(1, x86_64_ISR1, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(2, x86_64_ISR2, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(3, x86_64_ISR3, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(4, x86_64_ISR4, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(5, x86_64_ISR5, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(6, x86_64_ISR6, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(7, x86_64_ISR7, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(8, x86_64_ISR8, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(9, x86_64_ISR9, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(10, x86_64_ISR10, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(11, x86_64_ISR11, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(12, x86_64_ISR12, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(13, x86_64_ISR13, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(14, x86_64_ISR14, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(15, x86_64_ISR15, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(16, x86_64_ISR16, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(17, x86_64_ISR17, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(18, x86_64_ISR18, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(19, x86_64_ISR19, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(20, x86_64_ISR20, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(21, x86_64_ISR21, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(22, x86_64_ISR22, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(23, x86_64_ISR23, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(24, x86_64_ISR24, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(25, x86_64_ISR25, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(26, x86_64_ISR26, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(27, x86_64_ISR27, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(28, x86_64_ISR28, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(29, x86_64_ISR29, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(30, x86_64_ISR30, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(31, x86_64_ISR31, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(32, x86_64_ISR32, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(33, x86_64_ISR33, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(34, x86_64_ISR34, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(35, x86_64_ISR35, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(36, x86_64_ISR36, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(37, x86_64_ISR37, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(38, x86_64_ISR38, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(39, x86_64_ISR39, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(40, x86_64_ISR40, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(41, x86_64_ISR41, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(42, x86_64_ISR42, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(43, x86_64_ISR43, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(44, x86_64_ISR44, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(45, x86_64_ISR45, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(46, x86_64_ISR46, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(47, x86_64_ISR47, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(48, x86_64_ISR48, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(49, x86_64_ISR49, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(50, x86_64_ISR50, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(51, x86_64_ISR51, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(52, x86_64_ISR52, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(53, x86_64_ISR53, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(54, x86_64_ISR54, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(55, x86_64_ISR55, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(56, x86_64_ISR56, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(57, x86_64_ISR57, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(58, x86_64_ISR58, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(59, x86_64_ISR59, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(60, x86_64_ISR60, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(61, x86_64_ISR61, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(62, x86_64_ISR62, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(63, x86_64_ISR63, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(64, x86_64_ISR64, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(65, x86_64_ISR65, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(66, x86_64_ISR66, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(67, x86_64_ISR67, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(68, x86_64_ISR68, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(69, x86_64_ISR69, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(70, x86_64_ISR70, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(71, x86_64_ISR71, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(72, x86_64_ISR72, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(73, x86_64_ISR73, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(74, x86_64_ISR74, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(75, x86_64_ISR75, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(76, x86_64_ISR76, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(77, x86_64_ISR77, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(78, x86_64_ISR78, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(79, x86_64_ISR79, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(80, x86_64_ISR80, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(81, x86_64_ISR81, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(82, x86_64_ISR82, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(83, x86_64_ISR83, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(84, x86_64_ISR84, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(85, x86_64_ISR85, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(86, x86_64_ISR86, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(87, x86_64_ISR87, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(88, x86_64_ISR88, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(89, x86_64_ISR89, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(90, x86_64_ISR90, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(91, x86_64_ISR91, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(92, x86_64_ISR92, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(93, x86_64_ISR93, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(94, x86_64_ISR94, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(95, x86_64_ISR95, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(96, x86_64_ISR96, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(97, x86_64_ISR97, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(98, x86_64_ISR98, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(99, x86_64_ISR99, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(100, x86_64_ISR100, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(101, x86_64_ISR101, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(102, x86_64_ISR102, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(103, x86_64_ISR103, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(104, x86_64_ISR104, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(105, x86_64_ISR105, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(106, x86_64_ISR106, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(107, x86_64_ISR107, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(108, x86_64_ISR108, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(109, x86_64_ISR109, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(110, x86_64_ISR110, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(111, x86_64_ISR111, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(112, x86_64_ISR112, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(113, x86_64_ISR113, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(114, x86_64_ISR114, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(115, x86_64_ISR115, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(116, x86_64_ISR116, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(117, x86_64_ISR117, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(118, x86_64_ISR118, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(119, x86_64_ISR119, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(120, x86_64_ISR120, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(121, x86_64_ISR121, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(122, x86_64_ISR122, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(123, x86_64_ISR123, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(124, x86_64_ISR124, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(125, x86_64_ISR125, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(126, x86_64_ISR126, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(127, x86_64_ISR127, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(128, x86_64_ISR128, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(129, x86_64_ISR129, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(130, x86_64_ISR130, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(131, x86_64_ISR131, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(132, x86_64_ISR132, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(133, x86_64_ISR133, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(134, x86_64_ISR134, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(135, x86_64_ISR135, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(136, x86_64_ISR136, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(137, x86_64_ISR137, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(138, x86_64_ISR138, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(139, x86_64_ISR139, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(140, x86_64_ISR140, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(141, x86_64_ISR141, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(142, x86_64_ISR142, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(143, x86_64_ISR143, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(144, x86_64_ISR144, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(145, x86_64_ISR145, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(146, x86_64_ISR146, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(147, x86_64_ISR147, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(148, x86_64_ISR148, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(149, x86_64_ISR149, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(150, x86_64_ISR150, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(151, x86_64_ISR151, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(152, x86_64_ISR152, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(153, x86_64_ISR153, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(154, x86_64_ISR154, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(155, x86_64_ISR155, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(156, x86_64_ISR156, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(157, x86_64_ISR157, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(158, x86_64_ISR158, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(159, x86_64_ISR159, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(160, x86_64_ISR160, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(161, x86_64_ISR161, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(162, x86_64_ISR162, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(163, x86_64_ISR163, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(164, x86_64_ISR164, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(165, x86_64_ISR165, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(166, x86_64_ISR166, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(167, x86_64_ISR167, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(168, x86_64_ISR168, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(169, x86_64_ISR169, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(170, x86_64_ISR170, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(171, x86_64_ISR171, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(172, x86_64_ISR172, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(173, x86_64_ISR173, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(174, x86_64_ISR174, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(175, x86_64_ISR175, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(176, x86_64_ISR176, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(177, x86_64_ISR177, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(178, x86_64_ISR178, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(179, x86_64_ISR179, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(180, x86_64_ISR180, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(181, x86_64_ISR181, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(182, x86_64_ISR182, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(183, x86_64_ISR183, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(184, x86_64_ISR184, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(185, x86_64_ISR185, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(186, x86_64_ISR186, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(187, x86_64_ISR187, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(188, x86_64_ISR188, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(189, x86_64_ISR189, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(190, x86_64_ISR190, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(191, x86_64_ISR191, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(192, x86_64_ISR192, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(193, x86_64_ISR193, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(194, x86_64_ISR194, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(195, x86_64_ISR195, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(196, x86_64_ISR196, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(197, x86_64_ISR197, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(198, x86_64_ISR198, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(199, x86_64_ISR199, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(200, x86_64_ISR200, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(201, x86_64_ISR201, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(202, x86_64_ISR202, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(203, x86_64_ISR203, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(204, x86_64_ISR204, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(205, x86_64_ISR205, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(206, x86_64_ISR206, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(207, x86_64_ISR207, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(208, x86_64_ISR208, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(209, x86_64_ISR209, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(210, x86_64_ISR210, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(211, x86_64_ISR211, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(212, x86_64_ISR212, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(213, x86_64_ISR213, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(214, x86_64_ISR214, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(215, x86_64_ISR215, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(216, x86_64_ISR216, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(217, x86_64_ISR217, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(218, x86_64_ISR218, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(219, x86_64_ISR219, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(220, x86_64_ISR220, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(221, x86_64_ISR221, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(222, x86_64_ISR222, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(223, x86_64_ISR223, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(224, x86_64_ISR224, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(225, x86_64_ISR225, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(226, x86_64_ISR226, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(227, x86_64_ISR227, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(228, x86_64_ISR228, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(229, x86_64_ISR229, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(230, x86_64_ISR230, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(231, x86_64_ISR231, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(232, x86_64_ISR232, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(233, x86_64_ISR233, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(234, x86_64_ISR234, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(235, x86_64_ISR235, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(236, x86_64_ISR236, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(237, x86_64_ISR237, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(238, x86_64_ISR238, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(239, x86_64_ISR239, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(240, x86_64_ISR240, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(241, x86_64_ISR241, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(242, x86_64_ISR242, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(243, x86_64_ISR243, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(244, x86_64_ISR244, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(245, x86_64_ISR245, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(246, x86_64_ISR246, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(247, x86_64_ISR247, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(248, x86_64_ISR248, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(249, x86_64_ISR249, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(250, x86_64_ISR250, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(251, x86_64_ISR251, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(252, x86_64_ISR252, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(253, x86_64_ISR253, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(254, x86_64_ISR254, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); - x86_64_IDT_SetGate(255, x86_64_ISR255, GDT_KERNEL_CODE64, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT); -} \ No newline at end of file diff --git a/src/arch/x86_64/boot/isrs_gen.inc b/src/arch/x86_64/boot/isrs_gen.inc deleted file mode 100644 index 38c018b..0000000 --- a/src/arch/x86_64/boot/isrs_gen.inc +++ /dev/null @@ -1,256 +0,0 @@ -ISR_NOERRORCODE 0 -ISR_NOERRORCODE 1 -ISR_NOERRORCODE 2 -ISR_NOERRORCODE 3 -ISR_NOERRORCODE 4 -ISR_NOERRORCODE 5 -ISR_NOERRORCODE 6 -ISR_NOERRORCODE 7 -ISR_ERRORCODE 8 -ISR_NOERRORCODE 9 -ISR_ERRORCODE 10 -ISR_ERRORCODE 11 -ISR_ERRORCODE 12 -ISR_ERRORCODE 13 -ISR_ERRORCODE 14 -ISR_NOERRORCODE 15 -ISR_NOERRORCODE 16 -ISR_ERRORCODE 17 -ISR_NOERRORCODE 18 -ISR_NOERRORCODE 19 -ISR_NOERRORCODE 20 -ISR_ERRORCODE 21 -ISR_NOERRORCODE 22 -ISR_NOERRORCODE 23 -ISR_NOERRORCODE 24 -ISR_NOERRORCODE 25 -ISR_NOERRORCODE 26 -ISR_NOERRORCODE 27 -ISR_NOERRORCODE 28 -ISR_NOERRORCODE 29 -ISR_NOERRORCODE 30 -ISR_NOERRORCODE 31 -ISR_NOERRORCODE 32 -ISR_NOERRORCODE 33 -ISR_NOERRORCODE 34 -ISR_NOERRORCODE 35 -ISR_NOERRORCODE 36 -ISR_NOERRORCODE 37 -ISR_NOERRORCODE 38 -ISR_NOERRORCODE 39 -ISR_NOERRORCODE 40 -ISR_NOERRORCODE 41 -ISR_NOERRORCODE 42 -ISR_NOERRORCODE 43 -ISR_NOERRORCODE 44 -ISR_NOERRORCODE 45 -ISR_NOERRORCODE 46 -ISR_NOERRORCODE 47 -ISR_NOERRORCODE 48 -ISR_NOERRORCODE 49 -ISR_NOERRORCODE 50 -ISR_NOERRORCODE 51 -ISR_NOERRORCODE 52 -ISR_NOERRORCODE 53 -ISR_NOERRORCODE 54 -ISR_NOERRORCODE 55 -ISR_NOERRORCODE 56 -ISR_NOERRORCODE 57 -ISR_NOERRORCODE 58 -ISR_NOERRORCODE 59 -ISR_NOERRORCODE 60 -ISR_NOERRORCODE 61 -ISR_NOERRORCODE 62 -ISR_NOERRORCODE 63 -ISR_NOERRORCODE 64 -ISR_NOERRORCODE 65 -ISR_NOERRORCODE 66 -ISR_NOERRORCODE 67 -ISR_NOERRORCODE 68 -ISR_NOERRORCODE 69 -ISR_NOERRORCODE 70 -ISR_NOERRORCODE 71 -ISR_NOERRORCODE 72 -ISR_NOERRORCODE 73 -ISR_NOERRORCODE 74 -ISR_NOERRORCODE 75 -ISR_NOERRORCODE 76 -ISR_NOERRORCODE 77 -ISR_NOERRORCODE 78 -ISR_NOERRORCODE 79 -ISR_NOERRORCODE 80 -ISR_NOERRORCODE 81 -ISR_NOERRORCODE 82 -ISR_NOERRORCODE 83 -ISR_NOERRORCODE 84 -ISR_NOERRORCODE 85 -ISR_NOERRORCODE 86 -ISR_NOERRORCODE 87 -ISR_NOERRORCODE 88 -ISR_NOERRORCODE 89 -ISR_NOERRORCODE 90 -ISR_NOERRORCODE 91 -ISR_NOERRORCODE 92 -ISR_NOERRORCODE 93 -ISR_NOERRORCODE 94 -ISR_NOERRORCODE 95 -ISR_NOERRORCODE 96 -ISR_NOERRORCODE 97 -ISR_NOERRORCODE 98 -ISR_NOERRORCODE 99 -ISR_NOERRORCODE 100 -ISR_NOERRORCODE 101 -ISR_NOERRORCODE 102 -ISR_NOERRORCODE 103 -ISR_NOERRORCODE 104 -ISR_NOERRORCODE 105 -ISR_NOERRORCODE 106 -ISR_NOERRORCODE 107 -ISR_NOERRORCODE 108 -ISR_NOERRORCODE 109 -ISR_NOERRORCODE 110 -ISR_NOERRORCODE 111 -ISR_NOERRORCODE 112 -ISR_NOERRORCODE 113 -ISR_NOERRORCODE 114 -ISR_NOERRORCODE 115 -ISR_NOERRORCODE 116 -ISR_NOERRORCODE 117 -ISR_NOERRORCODE 118 -ISR_NOERRORCODE 119 -ISR_NOERRORCODE 120 -ISR_NOERRORCODE 121 -ISR_NOERRORCODE 122 -ISR_NOERRORCODE 123 -ISR_NOERRORCODE 124 -ISR_NOERRORCODE 125 -ISR_NOERRORCODE 126 -ISR_NOERRORCODE 127 -ISR_NOERRORCODE 128 -ISR_NOERRORCODE 129 -ISR_NOERRORCODE 130 -ISR_NOERRORCODE 131 -ISR_NOERRORCODE 132 -ISR_NOERRORCODE 133 -ISR_NOERRORCODE 134 -ISR_NOERRORCODE 135 -ISR_NOERRORCODE 136 -ISR_NOERRORCODE 137 -ISR_NOERRORCODE 138 -ISR_NOERRORCODE 139 -ISR_NOERRORCODE 140 -ISR_NOERRORCODE 141 -ISR_NOERRORCODE 142 -ISR_NOERRORCODE 143 -ISR_NOERRORCODE 144 -ISR_NOERRORCODE 145 -ISR_NOERRORCODE 146 -ISR_NOERRORCODE 147 -ISR_NOERRORCODE 148 -ISR_NOERRORCODE 149 -ISR_NOERRORCODE 150 -ISR_NOERRORCODE 151 -ISR_NOERRORCODE 152 -ISR_NOERRORCODE 153 -ISR_NOERRORCODE 154 -ISR_NOERRORCODE 155 -ISR_NOERRORCODE 156 -ISR_NOERRORCODE 157 -ISR_NOERRORCODE 158 -ISR_NOERRORCODE 159 -ISR_NOERRORCODE 160 -ISR_NOERRORCODE 161 -ISR_NOERRORCODE 162 -ISR_NOERRORCODE 163 -ISR_NOERRORCODE 164 -ISR_NOERRORCODE 165 -ISR_NOERRORCODE 166 -ISR_NOERRORCODE 167 -ISR_NOERRORCODE 168 -ISR_NOERRORCODE 169 -ISR_NOERRORCODE 170 -ISR_NOERRORCODE 171 -ISR_NOERRORCODE 172 -ISR_NOERRORCODE 173 -ISR_NOERRORCODE 174 -ISR_NOERRORCODE 175 -ISR_NOERRORCODE 176 -ISR_NOERRORCODE 177 -ISR_NOERRORCODE 178 -ISR_NOERRORCODE 179 -ISR_NOERRORCODE 180 -ISR_NOERRORCODE 181 -ISR_NOERRORCODE 182 -ISR_NOERRORCODE 183 -ISR_NOERRORCODE 184 -ISR_NOERRORCODE 185 -ISR_NOERRORCODE 186 -ISR_NOERRORCODE 187 -ISR_NOERRORCODE 188 -ISR_NOERRORCODE 189 -ISR_NOERRORCODE 190 -ISR_NOERRORCODE 191 -ISR_NOERRORCODE 192 -ISR_NOERRORCODE 193 -ISR_NOERRORCODE 194 -ISR_NOERRORCODE 195 -ISR_NOERRORCODE 196 -ISR_NOERRORCODE 197 -ISR_NOERRORCODE 198 -ISR_NOERRORCODE 199 -ISR_NOERRORCODE 200 -ISR_NOERRORCODE 201 -ISR_NOERRORCODE 202 -ISR_NOERRORCODE 203 -ISR_NOERRORCODE 204 -ISR_NOERRORCODE 205 -ISR_NOERRORCODE 206 -ISR_NOERRORCODE 207 -ISR_NOERRORCODE 208 -ISR_NOERRORCODE 209 -ISR_NOERRORCODE 210 -ISR_NOERRORCODE 211 -ISR_NOERRORCODE 212 -ISR_NOERRORCODE 213 -ISR_NOERRORCODE 214 -ISR_NOERRORCODE 215 -ISR_NOERRORCODE 216 -ISR_NOERRORCODE 217 -ISR_NOERRORCODE 218 -ISR_NOERRORCODE 219 -ISR_NOERRORCODE 220 -ISR_NOERRORCODE 221 -ISR_NOERRORCODE 222 -ISR_NOERRORCODE 223 -ISR_NOERRORCODE 224 -ISR_NOERRORCODE 225 -ISR_NOERRORCODE 226 -ISR_NOERRORCODE 227 -ISR_NOERRORCODE 228 -ISR_NOERRORCODE 229 -ISR_NOERRORCODE 230 -ISR_NOERRORCODE 231 -ISR_NOERRORCODE 232 -ISR_NOERRORCODE 233 -ISR_NOERRORCODE 234 -ISR_NOERRORCODE 235 -ISR_NOERRORCODE 236 -ISR_NOERRORCODE 237 -ISR_NOERRORCODE 238 -ISR_NOERRORCODE 239 -ISR_NOERRORCODE 240 -ISR_NOERRORCODE 241 -ISR_NOERRORCODE 242 -ISR_NOERRORCODE 243 -ISR_NOERRORCODE 244 -ISR_NOERRORCODE 245 -ISR_NOERRORCODE 246 -ISR_NOERRORCODE 247 -ISR_NOERRORCODE 248 -ISR_NOERRORCODE 249 -ISR_NOERRORCODE 250 -ISR_NOERRORCODE 251 -ISR_NOERRORCODE 252 -ISR_NOERRORCODE 253 -ISR_NOERRORCODE 254 -ISR_NOERRORCODE 255 diff --git a/src/arch/x86_64/bus/ata.c b/src/arch/x86_64/bus/ata.c index 54cd467..dfba28b 100644 --- a/src/arch/x86_64/bus/ata.c +++ b/src/arch/x86_64/bus/ata.c @@ -1,7 +1,7 @@ #include "ata.h" #include "arch/x86_64/cpu/io.h" -#include "libk/stdio.h" #include +#include "libk/debug.h" @@ -56,7 +56,7 @@ void ata_init(void) ata_wait_ready(); x86_64_outb(ATA_PRIMARY_DRIVE, 0xA0); // select master ata_wait_ready(); - printf("ATA: Primary master (hda) initialized\n"); + kprintf("ATA: Primary master (hda) initialized\n"); } void ata_identify(void) @@ -71,30 +71,30 @@ void ata_identify(void) x86_64_outb(ATA_PRIMARY_COMMAND, ATA_CMD_IDENTIFY); if (x86_64_inb(ATA_PRIMARY_STATUS) == 0) { - printf("ATA: No primary master drive detected\n"); + kprintf("ATA: No primary master drive detected\n"); return; } if (!ata_poll()) { - printf("ATA poll failed at ATA_IDENTIFY\n"); + kprintf("ATA poll failed at ATA_IDENTIFY\n"); } if (x86_64_inb(ATA_PRIMARY_STATUS) & ATA_STATUS_ERR) { - printf("ATA: Identify error\n"); + kprintf("ATA: Identify error\n"); return; } x86_64_insw(ATA_PRIMARY_DATA, buffer, 256); - printf("ATA: Disk model: "); + kprintf("ATA: Disk model: "); for (int i = 27; i <= 46; i++) { uint16_t w = buffer[i]; char high = w >> 8; char low = w & 0xFF; - printf("%c%c", high, low); // swap bytes + kprintf("%c%c", high, low); // swap bytes } - printf("\n"); - printf("ATA: Total sectors: %lu\n", (uint64_t)buffer[60] | ((uint64_t)buffer[61] << 16)); + kprintf("\n"); + kprintf("ATA: Total sectors: %lu\n", (uint64_t)buffer[60] | ((uint64_t)buffer[61] << 16)); } bool ata_read_sectors(uint64_t lba, uint8_t sector_count, void* buffer) @@ -114,11 +114,11 @@ bool ata_read_sectors(uint64_t lba, uint8_t sector_count, void* buffer) uint16_t* buf = buffer; for (uint8_t i = 0; i < sector_count; i++) { if (!ata_poll()) { - printf("ATA poll failed at ATA_read_sectors\n"); + kprintf("ATA poll failed at ATA_read_sectors\n"); } if (x86_64_inb(ATA_PRIMARY_STATUS) & ATA_STATUS_ERR) { - printf("ATA read error at LBA %lu\n", lba + i); + kprintf("ATA read error at LBA %lu\n", lba + i); return false; } diff --git a/src/arch/x86_64/bus/mmio.h b/src/arch/x86_64/bus/mmio.h new file mode 100644 index 0000000..3df22b9 --- /dev/null +++ b/src/arch/x86_64/bus/mmio.h @@ -0,0 +1,61 @@ +#pragma once +#include +#include "arch/x86_64/asm/asm.h" + +static inline void mmoutb(void *addr, uint8_t value) { + asm volatile("mov %0, %1\n\t" + : "=m"(BYTE_PTR(addr)) + : "r"(value) + : "memory"); +} + +static inline void mmoutw(void *addr, uint16_t value) { + asm volatile("mov %0, %1\n\t" + : "=m"(WORD_PTR(addr)) + : "r"(value) + : "memory"); +} + +static inline void mmoutd(void *addr, uint32_t value) { + asm volatile("mov %0, %1\n\t" + : "=m"(DWORD_PTR(addr)) + : "r"(value) + : "memory"); +} + +static inline void mmoutq(void *addr, uint64_t value) { + asm volatile("mov %0, %1\n\t" + : "=m"(QWORD_PTR(addr)) + : "r"(value) + : "memory"); +} + +static inline uint8_t mminb(void *addr) { + uint8_t ret; + asm volatile("mov %0, %1\n\t" : "=r"(ret) : "m"(BYTE_PTR(addr)) : "memory"); + return ret; +} + +static inline uint16_t mminw(void *addr) { + uint16_t ret; + asm volatile("mov %0, %1\n\t" : "=r"(ret) : "m"(WORD_PTR(addr)) : "memory"); + return ret; +} + +static inline uint32_t mmind(void *addr) { + uint32_t ret; + asm volatile("mov %0, %1\n\t" + : "=r"(ret) + : "m"(DWORD_PTR(addr)) + : "memory"); + return ret; +} + +static inline uint64_t mminq(void *addr) { + uint64_t ret; + asm volatile("mov %0, %1\n\t" + : "=r"(ret) + : "m"(QWORD_PTR(addr)) + : "memory"); + return ret; +} \ No newline at end of file diff --git a/src/arch/x86_64/bus/pci.c b/src/arch/x86_64/bus/pci.c index be8b427..843f3dc 100644 --- a/src/arch/x86_64/bus/pci.c +++ b/src/arch/x86_64/bus/pci.c @@ -1,6 +1,6 @@ #include "pci.h" #include "arch/x86_64/cpu/io.h" -#include "libk/stdio.h" +#include "libk/debug.h" #define PCI_CFG_ADDR 0x0CF8 #define PCI_CFG_DATA 0x0CFC @@ -69,7 +69,7 @@ static void pci_read_bars(pci_device_t* dev) void pci_init(void) { - printf("[PCI] Scanning bus 0...\n"); + kprintf("[PCI] Scanning bus 0...\n"); pci_print_all(); } @@ -101,7 +101,7 @@ void pci_print_all(void) }; pci_read_bars(&d); - printf("[PCI] %02x:%02x.%x %04x:%04x class %04x rev %02x\n", + kprintf("[PCI] %02x:%02x.%x %04x:%04x class %04x rev %02x\n", bus, dev, func, vendor, device_id, class_sub, revision); if (func == 0 && (pci_read8(addr, 0x0E) & 0x80) == 0) diff --git a/src/arch/x86_64/cpu/amd_syscall.asm b/src/arch/x86_64/cpu/amd_syscall.asm new file mode 100644 index 0000000..84d07db --- /dev/null +++ b/src/arch/x86_64/cpu/amd_syscall.asm @@ -0,0 +1,63 @@ +section .text + +extern syscall_handler +global amd_syscall_entry +amd_syscall_entry: + swapgs + + mov qword [gs:0016], rsp + mov rsp, qword [gs:0008] + + push 0x1b ; ss + push qword [gs:0016] ; rsp + push r11 ; rflags + push 0x23 ; cs + push rcx ; rip + + sub rsp, 24 + + push rax + push rbx + push rcx + push rdx + push rbp + push rdi + push rsi + push r8 + push r9 + push r10 + push r11 + push r12 + push r13 + push r14 + push r15 + + mov rdi, rsp + sti + call syscall_handler + + cli + + pop r15 + pop r14 + pop r13 + pop r12 + pop r11 + pop r10 + pop r9 + pop r8 + pop rsi + pop rdi + pop rbp + pop rdx + pop rcx + pop rbx + pop rax + + add rsp, 24 + + mov rsp, qword [gs:0016] + + swapgs + + o64 sysret diff --git a/src/arch/x86_64/cpu/cr.h b/src/arch/x86_64/cpu/cr.h new file mode 100644 index 0000000..54c8a35 --- /dev/null +++ b/src/arch/x86_64/cpu/cr.h @@ -0,0 +1,11 @@ +#pragma once + +#define write_cr(reg, val) \ + asm volatile("mov cr" reg ", %0" ::"r"(val) : "memory"); + +#define read_cr(reg) \ + ({ \ + size_t cr; \ + asm volatile("mov %0, cr" reg : "=r"(cr)::"memory"); \ + cr; \ + }) \ No newline at end of file diff --git a/src/arch/x86_64/cpu/features.h b/src/arch/x86_64/cpu/features.h new file mode 100644 index 0000000..1041c61 --- /dev/null +++ b/src/arch/x86_64/cpu/features.h @@ -0,0 +1,10 @@ +#pragma once + + +#define CPUID_INVARIANT_TSC (1 << 8) +#define CPUID_TSC_DEADLINE (1 << 24) +#define CPUID_SMEP (1 << 7) +#define CPUID_SMAP (1 << 20) +#define CPUID_UMIP (1 << 2) +#define CPUID_X2APIC (1 << 21) +#define CPUID_GBPAGE (1 << 26) \ No newline at end of file diff --git a/src/arch/x86_64/cpu/io.h b/src/arch/x86_64/cpu/io.h index 39b64f0..459848d 100644 --- a/src/arch/x86_64/cpu/io.h +++ b/src/arch/x86_64/cpu/io.h @@ -13,4 +13,4 @@ void x86_64_iowait(void); void x86_64_Panic(void) __attribute__((noreturn)); void x86_64_EnableInterrupts(void); void x86_64_DisableInterrupts(void); -void x86_64_crashme(void); \ No newline at end of file +void x86_64_crashme(void); diff --git a/src/arch/x86_64/cpu/io_asm.S b/src/arch/x86_64/cpu/io_asm.asm similarity index 64% rename from src/arch/x86_64/cpu/io_asm.S rename to src/arch/x86_64/cpu/io_asm.asm index be7eb23..be36df5 100644 --- a/src/arch/x86_64/cpu/io_asm.S +++ b/src/arch/x86_64/cpu/io_asm.asm @@ -1,96 +1,102 @@ -.intel_syntax noprefix +section .text -.global x86_64_outb +global x86_64_outb x86_64_outb: mov dx, di mov al, sil out dx, al ret -.global x86_64_inb +global x86_64_inb x86_64_inb: mov dx, di xor eax, eax in al, dx ret -.global x86_64_inw +global x86_64_inw x86_64_inw: mov dx, di xor eax, eax in ax, dx ret -.global x86_64_outw +global x86_64_outw x86_64_outw: mov dx, di mov ax, si out dx, ax ret -.global x86_64_outl +global x86_64_outl x86_64_outl: mov dx, di mov eax, esi out dx, eax ret -.global x86_64_inl +global x86_64_inl x86_64_inl: mov dx, di xor eax, eax in eax, dx ret -.global x86_64_insw +global x86_64_insw x86_64_insw: test rdx, rdx - jz .inswdone + jz .done + mov rcx, rdx mov dx, di mov rdi, rsi cld rep insw -.inswdone: + +.done: ret -.global x86_64_outsw +global x86_64_outsw x86_64_outsw: test rdx, rdx - jz .outswdone + jz .done + mov rcx, rdx mov dx, di - mov rsi, rsi + ; RSI already contains source pointer cld rep outsw -.outswdone: + +.done: ret -.global x86_64_Panic +global x86_64_Panic x86_64_Panic: cli hlt -1: jmp 1b -.global x86_64_EnableInterrupts +.hang: + jmp .hang + +global x86_64_EnableInterrupts x86_64_EnableInterrupts: sti ret -.global x86_64_DisableInterrupts +global x86_64_DisableInterrupts x86_64_DisableInterrupts: cli ret -.global x86_64_iowait +global x86_64_iowait x86_64_iowait: - out 0x80, al // classic I/O wait + out 0x80, al ; classic I/O wait ret -.global x86_64_crashme +global x86_64_crashme x86_64_crashme: xor rdx, rdx mov rax, 1 - mov rbx, 0 + xor rbx, rbx div rbx ret \ No newline at end of file diff --git a/src/arch/x86_64/cpu/msr.h b/src/arch/x86_64/cpu/msr.h new file mode 100644 index 0000000..c1cad98 --- /dev/null +++ b/src/arch/x86_64/cpu/msr.h @@ -0,0 +1,15 @@ +#pragma once +#include + +static inline uint64_t rdmsr(uint32_t msr) { + uint32_t edx, eax; + asm volatile("rdmsr" : "=a"(eax), "=d"(edx) : "c"(msr) : "memory"); + return ((uint64_t)edx << 32) | eax; +} + +static inline void wrmsr(uint32_t msr, uint64_t value) { + uint32_t edx = value >> 32; + uint32_t eax = (uint32_t)value; + asm volatile("wrmsr" ::"a"(eax), "c"(msr), "d"(edx) : "memory"); +} + diff --git a/src/arch/x86_64/cpu/reg.h b/src/arch/x86_64/cpu/reg.h new file mode 100644 index 0000000..95d8bf8 --- /dev/null +++ b/src/arch/x86_64/cpu/reg.h @@ -0,0 +1,28 @@ +#pragma once +#include + +typedef struct { + uint64_t r15; + uint64_t r14; + uint64_t r13; + uint64_t r12; + uint64_t r11; + uint64_t r10; + uint64_t r9; + uint64_t r8; + uint64_t rsi; + uint64_t rdi; + uint64_t rbp; + uint64_t rdx; + uint64_t rcx; + uint64_t rbx; + uint64_t rax; + uint64_t core; + uint64_t isrNumber; + uint64_t errorCode; + uint64_t rip; + uint64_t cs; + uint64_t rflags; + uint64_t rsp; + uint64_t ss; +} __attribute__((packed)) registers_t; \ No newline at end of file diff --git a/src/arch/x86_64/cpu/syscall_handle.c b/src/arch/x86_64/cpu/syscall_handle.c new file mode 100644 index 0000000..e45c60a --- /dev/null +++ b/src/arch/x86_64/cpu/syscall_handle.c @@ -0,0 +1,85 @@ +#include "mp/mp.h" +#include "libk/debug.h" +#include "libk/errno.h" +#include "mm/vmm.h" +#include "sched/sched.h" +#include "sched/syscall.h" +#include "arch/x86_64/boot/isr.h" +#include "arch/x86_64/sys/prcb.h" + +void syscall_handler(registers_t *reg) { + cli(); + prcb_return_current_cpu()->running_thread->reg = *reg; + prcb_return_current_cpu()->running_thread->stack = + prcb_return_current_cpu()->user_stack; + prcb_return_current_cpu()->fpu_save( + prcb_return_current_cpu()->running_thread->fpu_storage); + sti(); + + struct syscall_arguments args = {.syscall_nr = reg->rax, + .args0 = reg->rdi, + .args1 = reg->rsi, + .args2 = reg->rdx, + .args3 = reg->r10, + .args4 = reg->r8, + .args5 = reg->r9, + .ret = reg->rax}; + + syscall_handle(&args); + + int64_t ret = (int64_t)args.ret; + if (ret < 0) { + ret = -((int)errno); + reg->rax = ret; + } else + reg->rax = args.ret; +} + +void syscall_install_handler(void) { + isr_register_handler(0x80, syscall_handler); +} + +uint64_t syscall_helper_user_to_kernel_address(uintptr_t user_addr) { + struct process *proc = sched_get_running_thread()->mother_proc; + struct pagemap *target_pagemap = proc->process_pagemap; + + uint64_t kernel_addr = vmm_virt_to_kernel(target_pagemap, user_addr); + return kernel_addr; +} + +bool syscall_helper_copy_to_user(uintptr_t user_addr, void *buffer, + size_t count) { + vmm_switch_pagemap(kernel_pagemap); + + struct process *proc = sched_get_running_thread()->mother_proc; + struct pagemap *target_pagemap = proc->process_pagemap; + + uint64_t kernel_addr = vmm_virt_to_kernel(target_pagemap, user_addr); + + if (!kernel_addr) { + errno = EFAULT; + return false; + } + + memcpy((void *)kernel_addr, buffer, count); + vmm_switch_pagemap(target_pagemap); + return true; +} + +bool syscall_helper_copy_from_user(uintptr_t user_addr, void *buffer, + size_t count) { + vmm_switch_pagemap(kernel_pagemap); + + struct process *proc = sched_get_running_thread()->mother_proc; + struct pagemap *target_pagemap = proc->process_pagemap; + + uint64_t kernel_addr = vmm_virt_to_kernel(target_pagemap, user_addr); + + if (!kernel_addr) { + errno = EFAULT; + return false; + } + memcpy(buffer, (void *)kernel_addr, count); + vmm_switch_pagemap(target_pagemap); + return true; +} \ No newline at end of file diff --git a/src/arch/x86_64/cpu/usermode.c b/src/arch/x86_64/cpu/usermode.c deleted file mode 100644 index 05503b8..0000000 --- a/src/arch/x86_64/cpu/usermode.c +++ /dev/null @@ -1,131 +0,0 @@ -#include -#include "libk/string.h" -#include "mm/pmm.h" -#include "mm/vmm.h" -#include "mm/memory.h" -#include "libk/stdio.h" -#include "fs/elf.h" -#include "sched/scheduler.h" - -extern uintptr_t g_hhdm_offset; - -#define USER_STACK_TOP 0x00007FFFFFFFE000ULL -#define USER_STACK_PAGES 8 -#define USER_STACK_SIZE (USER_STACK_PAGES * PAGE_SIZE) - - - - - -static uint64_t user_stack_phys_base = 0; - -//extern struct pagemap *kernel_pagemap; - -struct pagemap *create_user_pagemap(void) -{ - struct pagemap *pm = kmalloc(sizeof(struct pagemap)); - if (!pm) { - printf("Failed to allocate user pagemap struct!\n"); - return NULL; - } - - spinlock_init(&pm->lock); - - /* Allocate a fresh PML4 (physical page) */ - pm->top_level = (uint64_t *)((uintptr_t)pmm_allocz(1) + MEM_PHYS_OFFSET); - if (!pm->top_level) { - printf("Failed to allocate user PML4!\n"); - kfree(pm); - return NULL; - } - - /* Copy kernel higher-half mappings (kernel + HHDM) */ - for (size_t i = 0; i < 512; i++) { - pm->top_level[i] = kernel_pagemap->top_level[i]; - } - - for (size_t i = 0; i < 256; i++) { - pm->top_level[i] = 0; - } - - /* Lower half remains zero (user address space) */ - printf("[usermode] user pagemap created (PML4 phys = 0x%lx)\n", - (uint64_t)pm->top_level - MEM_PHYS_OFFSET); - - return pm; -} - -uintptr_t setup_user_stack(struct pagemap *pagemap) -{ - user_stack_phys_base = (uint64_t)pmm_allocz(USER_STACK_PAGES); - - if (!user_stack_phys_base) { - printf("Failed to allocate user stack pages!\n"); - for (;;); - } - - uintptr_t stack_bottom = USER_STACK_TOP - USER_STACK_SIZE; - - for (int i = 0; i < USER_STACK_PAGES; i++) { - uintptr_t virt = stack_bottom + i * PAGE_SIZE; - uintptr_t phys = user_stack_phys_base + i * PAGE_SIZE; - - if (!vmm_map_page(pagemap, - virt, - phys, - PAGE_READ | PAGE_WRITE | PAGE_USER, - Size4KiB)) - { - printf("Failed mapping user stack page\n"); - for (;;); - } - - } - - uintptr_t rsp = USER_STACK_TOP; - rsp &= ~0xFULL; - return rsp; -} - - -void start_userspace(void) -{ - struct pagemap *user_pagemap = create_user_pagemap(); - if (!user_pagemap) { - printf("Failed to create user pagemap\n"); - for (;;); - } - - void *elf_entry = NULL; - uint64_t tls_fs_base = 0; - uint64_t phdr_va = 0; - uint16_t phent = 0; - uint16_t phnum = 0; - - if (!ELF_Read("helloworld.elf", - &elf_entry, - user_pagemap, - &tls_fs_base, - &phdr_va, - &phent, - &phnum)) { - printf("Failed to load helloworld.elf\n"); - for(;;); - } - - printf("ELF: entry=0x%lx TLS_FS=0x%lx PHDR=0x%lx PHENT=0x%x PHNUM=%u\n", - (uint64_t)elf_entry, tls_fs_base, phdr_va, phent, phnum); - - uintptr_t user_rsp = setup_user_stack(user_pagemap); - - printf("Entering usermode RIP=%p RSP=%p\n", elf_entry, (void*)user_rsp); - - sched_create_user_task("init", - (uint64_t)elf_entry, - user_rsp, - user_pagemap, - tls_fs_base, - phdr_va, - phent, - phnum); -} \ No newline at end of file diff --git a/src/arch/x86_64/cpu/usermode.h b/src/arch/x86_64/cpu/usermode.h deleted file mode 100644 index 90d1725..0000000 --- a/src/arch/x86_64/cpu/usermode.h +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -#include - -void start_userspace(void); \ No newline at end of file diff --git a/src/arch/x86_64/debug/backtrace.c b/src/arch/x86_64/debug/backtrace.c new file mode 100644 index 0000000..6d1d01f --- /dev/null +++ b/src/arch/x86_64/debug/backtrace.c @@ -0,0 +1,54 @@ +#include "libk/debug.h" +#include +#include "mm/vmm.h" + +void backtrace(uintptr_t *bp) { + kprintf("Stack trace:\n"); + uintptr_t *rbp = (uintptr_t *)bp; + + if (rbp == NULL) + asm volatile("mov %%rbp, %0" : "=g"(rbp)::"memory"); + + if (rbp == NULL || (uintptr_t)(rbp) < MEM_PHYS_OFFSET) + return; + + for (;;) { + uintptr_t *old_rbp = (uintptr_t *)rbp[0]; + uintptr_t *rip = (uintptr_t *)rbp[1]; + + if (rip == NULL || old_rbp == NULL || + ((uintptr_t)rip) < MEM_PHYS_OFFSET) + break; + + kprintf("%p\n", rip); + + rbp = old_rbp; + } + + + kprintf("Kernel base: %p Mem phys base: %p\n", KERNEL_BASE, + MEM_PHYS_OFFSET); +} + +void backtrace_unsafe(uintptr_t *bp) { + kprintf("Stack trace:\n"); + uintptr_t *rbp = (uintptr_t *)bp; + + if (rbp == NULL) + asm volatile("mov %%rbp, %0" : "=g"(rbp)::"memory"); + + if (rbp == NULL) + return; + + for (;;) { + uintptr_t *old_rbp = (uintptr_t *)rbp[0]; + uintptr_t *rip = (uintptr_t *)rbp[1]; + + if (rip == NULL || old_rbp == NULL) + break; + + kprintf("%p\n", rip); + + rbp = old_rbp; + } +} diff --git a/src/arch/x86_64/debug/backtrace.h b/src/arch/x86_64/debug/backtrace.h new file mode 100644 index 0000000..e69de29 diff --git a/src/arch/x86_64/debug/breakpoint.c b/src/arch/x86_64/debug/breakpoint.c new file mode 100644 index 0000000..fd2a33b --- /dev/null +++ b/src/arch/x86_64/debug/breakpoint.c @@ -0,0 +1,153 @@ +#include +#include +#include "arch/x86_64/boot/isr.h" +#include "libk/debug.h" +#include "arch/x86_64/sys/halt.h" +#include "mp/mp.h" +#include "sched/sched.h" +#include "arch/x86_64/serial/serial.h" + +static void backtrace_dump(registers_t *reg) { + kprintffos(0, "============ Backtrace ==========\n"); + + kprintffos(0, "-> %p\n", reg->rip); + + uintptr_t *rbp = (uintptr_t *)reg->rbp; + + if (rbp == NULL) + asm volatile("mov %%rbp, %0" : "=g"(rbp)::"memory"); + + if (rbp == NULL) + return; + + for (;;) { + uintptr_t *old_rbp = (uintptr_t *)rbp[0]; + uintptr_t *rip = (uintptr_t *)rbp[1]; + + if (rip == NULL || old_rbp == NULL) + break; + + kprintffos(0, "%p\n", rip); + + rbp = old_rbp; + } + + kprintffos(0, "============ End of dumps ==========\n"); +} + +static void register_dump(registers_t *reg) { + kprintffos(0, "========= Register dumps =========\n"); + kprintffos(0, "RIP: %p RBP: %p RSP: %p\n", reg->rip, reg->rbp, reg->rsp); + kprintffos(0, "RAX: %p RBX: %p RCX: %p\n", reg->rax, reg->rbx, reg->rcx); + kprintffos(0, "RDX: %p RDI: %p RSI: %p\n", reg->rdx, reg->rdi, reg->rsi); + kprintffos(0, "R8 : %p R9 : %p R10: %p\n", reg->r8, reg->r9, reg->r10); + kprintffos(0, "R11: %p R12: %p R13: %p\n", reg->r11, reg->r12, reg->r13); + kprintffos(0, "R14: %p R15: %p\n", reg->r14, reg->r15); + kprintffos(0, "CS : %p SS : %p RFLAGS: %p\n", reg->cs, reg->ss, + reg->rflags); + kprintffos(0, "FS: %p UGS: %p KGS: %p\n", read_fs_base(), read_user_gs(), + read_kernel_gs()); + + kprintffos(0, "============ End of dumps ==========\n"); +} + +static void tss_dump(void) { + kprintffos(0, "============ TSS Dumps ==========\n"); + + kprintffos(0, "tss.rsp0: %p\n", prcb_return_current_cpu()->cpu_tss.rsp0); + kprintffos(0, "tss.rsp1: %p\n", prcb_return_current_cpu()->cpu_tss.rsp1); + kprintffos(0, "tss.rsp2: %p\n", prcb_return_current_cpu()->cpu_tss.rsp2); + + kprintffos(0, "tss.ist1: %p\n", prcb_return_current_cpu()->cpu_tss.ist1); + kprintffos(0, "tss.ist2: %p\n", prcb_return_current_cpu()->cpu_tss.ist2); + kprintffos(0, "tss.ist3: %p\n", prcb_return_current_cpu()->cpu_tss.ist3); + kprintffos(0, "tss.ist4: %p\n", prcb_return_current_cpu()->cpu_tss.ist4); + kprintffos(0, "tss.ist5: %p\n", prcb_return_current_cpu()->cpu_tss.ist5); + kprintffos(0, "tss.ist6: %p\n", prcb_return_current_cpu()->cpu_tss.ist6); + kprintffos(0, "tss.ist7: %p\n", prcb_return_current_cpu()->cpu_tss.ist7); + + kprintffos(0, "tss.iomap_base: %p\n", + prcb_return_current_cpu()->cpu_tss.iomap_base); + + kprintffos(0, "============ End of dumps ==========\n"); +} + +static void prcb_dump(void) { + kprintffos(0, "============ PRCB Dumps ==========\n"); + + kprintffos(0, "prcb->cpu_number: %u\n", + prcb_return_current_cpu()->cpu_number); + kprintffos(0, "prcb->kernel_stack: %p\n", + prcb_return_current_cpu()->kernel_stack); + kprintffos(0, "prcb->user_stack: %p\n", + prcb_return_current_cpu()->user_stack); + kprintffos(0, "prcb->running_thread: %p\n", + prcb_return_current_cpu()->running_thread); + kprintffos(0, "prcb->sched_ticks: %p\n", + prcb_return_current_cpu()->user_stack); + + kprintffos(0, "prcb->lapic_id: %u\n", prcb_return_current_cpu()->lapic_id); + kprintffos(0, "prcb->fpu_storage_size: %p\n", + prcb_return_current_cpu()->fpu_storage_size); + kprintffos(0, "prcb->fpu_save: %p\n", prcb_return_current_cpu()->fpu_save); + kprintffos(0, "prcb->fpu_restore: %p\n", + prcb_return_current_cpu()->fpu_restore); + + kprintffos(0, "============ End of dumps ==========\n"); +} + +void breakpoint_handler(registers_t *reg) { + if (reg->cs & 0x3) { + kprintffos(0, "Breakpoint hit in user!\n"); + thread_kill(prcb_return_current_cpu()->running_thread, true); + return; + } else { + kprintffos(0, "Breakpoint hit in kernel!\n"); + } + + pause_other_cpus(); + + kprintffos(0, "=========== Start of dumps =========\n"); + kprintffos(0, "Breakpoint hit on CPU%u\n", + prcb_return_current_cpu()->cpu_number); + + kprintffos(0, "========= Register dumps =========\n"); + kprintffos(0, "RIP: %p RBP: %p RSP: %p\n", reg->rip, reg->rbp, reg->rsp); + kprintffos(0, "RAX: %p RBX: %p RCX: %p\n", reg->rax, reg->rbx, reg->rcx); + kprintffos(0, "RDX: %p RDI: %p RSI: %p\n", reg->rdx, reg->rdi, reg->rsi); + kprintffos(0, "R8 : %p R9 : %p R10: %p\n", reg->r8, reg->r9, reg->r10); + kprintffos(0, "R11: %p R12: %p R13: %p\n", reg->r11, reg->r12, reg->r13); + kprintffos(0, "R14: %p R15: %p\n", reg->r14, reg->r15); + kprintffos(0, "CS : %p SS : %p RFLAGS: %p\n", reg->cs, reg->ss, + reg->rflags); + kprintffos(0, "FS: %p UGS: %p KGS: %p\n", read_fs_base(), read_user_gs(), + read_kernel_gs()); + + kprintffos(0, "============ End of dumps ==========\n"); + + char option = 0; + + while (option != 'C') { + kprintffos(0, "(C)ontinue, Dump (P)RCB, Dump (R)egisters, Dump (T)SS, " + "(B)acktrace?\n"); + option = serial_getchar(); + serial_putchar('\n'); + switch (option) { + case 'P': + prcb_dump(); + break; + case 'R': + register_dump(reg); + break; + case 'T': + tss_dump(); + break; + case 'B': + backtrace_dump(reg); + default: + break; + } + } + + unpause_other_cpus(); +} diff --git a/src/arch/x86_64/debug/tinyubsan.c b/src/arch/x86_64/debug/tinyubsan.c new file mode 100644 index 0000000..3c62c72 --- /dev/null +++ b/src/arch/x86_64/debug/tinyubsan.c @@ -0,0 +1,159 @@ +#include "libk/debug.h" +#include "libk/kargs.h" +#include + +struct tu_source_location { + const char *file; + uint32_t line; + uint32_t column; +}; + +struct tu_type_descriptor { + uint16_t kind; + uint16_t info; + char name[]; +}; + +struct tu_overflow_data { + struct tu_source_location location; + struct tu_type_descriptor *type; +}; + +struct tu_shift_out_of_bounds_data { + struct tu_source_location location; + struct tu_type_descriptor *left_type; + struct tu_type_descriptor *right_type; +}; + +struct tu_invalid_value_data { + struct tu_source_location location; + struct tu_type_descriptor *type; +}; + +struct tu_array_out_of_bounds_data { + struct tu_source_location location; + struct tu_type_descriptor *array_type; + struct tu_type_descriptor *index_type; +}; + +struct tu_type_mismatch_v1_data { + struct tu_source_location location; + struct tu_type_descriptor *type; + unsigned char log_alignment; + unsigned char type_check_kind; +}; + +struct tu_negative_vla_data { + struct tu_source_location location; + struct tu_type_descriptor *type; +}; + +struct tu_nonnull_return_data { + struct tu_source_location location; +}; + +struct tu_nonnull_arg_data { + struct tu_source_location location; +}; + +struct tu_unreachable_data { + struct tu_source_location location; +}; + +struct tu_invalid_builtin_data { + struct tu_source_location location; + unsigned char kind; +}; + +#ifdef __cplusplus +extern "C" { +#endif + +extern bool print_now; +extern bool put_to_fb; + +static void tu_print_location(const char *message, + struct tu_source_location loc) { + if (print_now && + !(kernel_arguments.kernel_args & KERNEL_ARGS_SUPPRESS_UBSAN)) { + kprintffos(0, "tinyubsan: %s at file %s, line %d, column %d\n", message, + loc.file, loc.line, loc.column); + put_to_fb = 1; + } +} + +void __ubsan_handle_add_overflow(struct tu_overflow_data *data) { + tu_print_location("addition overflow", data->location); +} + +void __ubsan_handle_sub_overflow(struct tu_overflow_data *data) { + tu_print_location("subtraction overflow", data->location); +} + +void __ubsan_handle_mul_overflow(struct tu_overflow_data *data) { + tu_print_location("multiplication overflow", data->location); +} + +void __ubsan_handle_divrem_overflow(struct tu_overflow_data *data) { + tu_print_location("division overflow", data->location); +} + +void __ubsan_handle_negate_overflow(struct tu_overflow_data *data) { + tu_print_location("negation overflow", data->location); +} + +void __ubsan_handle_pointer_overflow(struct tu_overflow_data *data) { + tu_print_location("pointer overflow", data->location); +} + +void __ubsan_handle_shift_out_of_bounds( + struct tu_shift_out_of_bounds_data *data) { + tu_print_location("shift out of bounds", data->location); +} + +void __ubsan_handle_load_invalid_value(struct tu_invalid_value_data *data) { + tu_print_location("invalid load value", data->location); +} + +void __ubsan_handle_out_of_bounds(struct tu_array_out_of_bounds_data *data) { + tu_print_location("array out of bounds", data->location); +} + +void __ubsan_handle_type_mismatch_v1(struct tu_type_mismatch_v1_data *data, + uintptr_t ptr) { + if (!ptr) { + tu_print_location("use of NULL pointer", data->location); + } + + else if (ptr & ((1 << data->log_alignment) - 1)) { + // tu_print_location("use of misaligned pointer", data->location); + } else { + tu_print_location("no space for object", data->location); + } +} + +void __ubsan_handle_vla_bound_not_positive(struct tu_negative_vla_data *data) { + tu_print_location("variable-length argument is negative", data->location); +} + +void __ubsan_handle_nonnull_return(struct tu_nonnull_return_data *data) { + tu_print_location("non-null return is null", data->location); +} + +void __ubsan_handle_nonnull_arg(struct tu_nonnull_arg_data *data) { + tu_print_location("non-null argument is null", data->location); +} + +void __ubsan_handle_builtin_unreachable(struct tu_unreachable_data *data) { + + tu_print_location("unreachable code reached", data->location); +} + +void __ubsan_handle_invalid_builtin(struct tu_invalid_builtin_data *data) { + + tu_print_location("invalid builtin", data->location); +} + +#ifdef __cplusplus +} +#endif diff --git a/src/arch/x86_64/fw/acpi.c b/src/arch/x86_64/fw/acpi.c new file mode 100644 index 0000000..8193691 --- /dev/null +++ b/src/arch/x86_64/fw/acpi.c @@ -0,0 +1,66 @@ +#include "arch/x86_64/sys/tsc.h" +#include +#include +#include +#include +#include "arch/x86_64/sys/timer.h" +#include "madt.h" +#include "libk/debug.h" + +void acpi_init() { + + calibrate_tsc(); + /* + * Start with this as the first step of the initialization. This loads all + * tables, brings the event subsystem online, and enters ACPI mode. We pass + * in 0 as the flags as we don't want to override any default behavior for now. + */ + uacpi_status ret = uacpi_initialize(0); + if (uacpi_unlikely_error(ret)) { + kprintf("uacpi_initialize error: %s", uacpi_status_to_string(ret)); + } + + /* + * Load the AML namespace. This feeds DSDT and all SSDTs to the interpreter + * for execution. + */ + ret = uacpi_namespace_load(); + if (uacpi_unlikely_error(ret)) { + kprintf("uacpi_namespace_load error: %s", uacpi_status_to_string(ret)); + } + + /* + * Initialize the namespace. This calls all necessary _STA/_INI AML methods, + * as well as _REG for registered operation region handlers. + */ + ret = uacpi_namespace_initialize(); + if (uacpi_unlikely_error(ret)) { + kprintf("uacpi_namespace_initialize error: %s", uacpi_status_to_string(ret)); + } + + /* + * Tell uACPI that we have marked all GPEs we wanted for wake (even though we haven't + * actually marked any, as we have no power management support right now). This is + * needed to let uACPI enable all unmarked GPEs that have a corresponding AML handler. + * These handlers are used by the firmware to dynamically execute AML code at runtime + * to e.g. react to thermal events or device hotplug. + */ + ret = uacpi_finalize_gpe_initialization(); + if (uacpi_unlikely_error(ret)) { + kprintf("uACPI GPE initialization error: %s", uacpi_status_to_string(ret)); + } + + + timer_init(); + madt_init(); +} + +void *acpi_find_sdt(const char *signature) { + struct uacpi_table sdt; + uacpi_status st = uacpi_table_find_by_signature(signature, &sdt); + if (uacpi_unlikely_error(st)) { + panic("Could not find %s: %s\n", signature, uacpi_status_to_string(st)); + } + return (void *)sdt.ptr; + +} \ No newline at end of file diff --git a/src/arch/x86_64/fw/acpi.h b/src/arch/x86_64/fw/acpi.h new file mode 100644 index 0000000..af8b003 --- /dev/null +++ b/src/arch/x86_64/fw/acpi.h @@ -0,0 +1,25 @@ +#pragma once +#include + +typedef struct acpi_header_t { + char signature[4]; + uint32_t length; + uint8_t revision; + uint8_t checksum; + char oem[6]; + char oem_table[8]; + uint32_t oem_revision; + uint32_t creator_id; + uint32_t creator_revision; +} __attribute__((packed)) acpi_header_t; + +typedef struct acpi_gas_t { + uint8_t address_space; + uint8_t bit_width; + uint8_t bit_offset; + uint8_t access_size; + uint64_t base; +} __attribute__((packed)) acpi_gas_t; + +void acpi_init(); +void *acpi_find_sdt(const char *signature); \ No newline at end of file diff --git a/src/arch/x86_64/fw/madt.c b/src/arch/x86_64/fw/madt.c new file mode 100644 index 0000000..f359f9a --- /dev/null +++ b/src/arch/x86_64/fw/madt.c @@ -0,0 +1,68 @@ +#include +#include "madt.h" +#include +#include "libk/debug.h" +#include "arch/x86_64/asm/asm.h" + + +struct madt *madt; + +lapic_vec_t madt_local_apics; +ioapic_vec_t madt_io_apics; +iso_vec_t madt_isos; +nmi_vec_t madt_nmis; + +uintptr_t lapic_addr = 0; + +uintptr_t acpi_get_lapic(void) { + return lapic_addr; +} + +void madt_init(void) { + vec_init(&madt_local_apics); + vec_init(&madt_io_apics); + vec_init(&madt_isos); + vec_init(&madt_nmis); + + struct uacpi_table madt_table; + uacpi_status st = uacpi_table_find_by_signature("APIC", &madt_table); + if (uacpi_unlikely_error(st)) { + panic("Could not find MADT: %s\n", uacpi_status_to_string(st)); + } + + madt = (struct madt *)madt_table.ptr; + + lapic_addr = madt->local_controller_addr; + kprintf("MADT: MADT at %p\n", madt); + for (uint8_t *madt_ptr = (uint8_t *)madt->madt_entries_begin; + (uintptr_t)madt_ptr < (uintptr_t)madt + madt->sdt.length; + madt_ptr += *(madt_ptr + 1)) { + switch (*(madt_ptr)) { + case 0: + // Processor local APIC + kprintf("MADT: Got local APIC 0x%x\n", madt_local_apics.length); + vec_push(&madt_local_apics, (void *)madt_ptr); + break; + case 1: + // I/O APIC + kprintf("MADT: Got IO APIC 0x%x\n", madt_io_apics.length); + vec_push(&madt_io_apics, (void *)madt_ptr); + break; + case 2: + // Interrupt source override + kprintf("MADT: Got ISO 0x%x\n", madt_isos.length); + vec_push(&madt_isos, (void *)madt_ptr); + break; + case 4: + // NMI + kprintf("MADT: Got NMI 0x%x\n", madt_nmis.length); + vec_push(&madt_nmis, (void *)madt_ptr); + break; + case 5: + // Local APIC address override + lapic_addr = QWORD_PTR(madt_ptr + 4); + break; + } + } + +} \ No newline at end of file diff --git a/src/arch/x86_64/fw/madt.h b/src/arch/x86_64/fw/madt.h new file mode 100644 index 0000000..898aa4b --- /dev/null +++ b/src/arch/x86_64/fw/madt.h @@ -0,0 +1,64 @@ +#pragma once +#include +#include "libk/vec.h" +#include +#include "acpi.h" + + + + +struct madt { + acpi_header_t sdt; + uint32_t local_controller_addr; + uint32_t flags; + char madt_entries_begin[]; +} __attribute__((packed)); + +struct madt_header { + uint8_t type; + uint8_t length; +} __attribute__((packed)); + +struct madt_lapic { + struct madt_header madt_header; + uint8_t processor_id; + uint8_t apic_id; + uint32_t flags; +} __attribute__((packed)); + +struct madt_ioapic { + struct madt_header madt_header; + uint8_t apic_id; + uint8_t reserved; + uint32_t addr; + uint32_t gsib; +} __attribute__((packed)); + +struct madt_iso { + struct madt_header madt_header; + uint8_t bus_source; + uint8_t irq_source; + uint32_t gsi; + uint16_t flags; +} __attribute__((packed)); + +struct madt_nmi { + struct madt_header madt_header; + uint8_t processor; + uint16_t flags; + uint8_t lint; +} __attribute__((packed)); + +typedef vec_t(struct madt_lapic *) lapic_vec_t; +typedef vec_t(struct madt_ioapic *) ioapic_vec_t; +typedef vec_t(struct madt_iso *) iso_vec_t; +typedef vec_t(struct madt_nmi *) nmi_vec_t; + +extern struct madt *madt; +extern lapic_vec_t madt_local_apics; +extern ioapic_vec_t madt_io_apics; +extern iso_vec_t madt_isos; +extern nmi_vec_t madt_nmis; + +uintptr_t acpi_get_lapic(void); +void madt_init(void); \ No newline at end of file diff --git a/src/arch/x86_64/sched/context.c b/src/arch/x86_64/sched/context.c new file mode 100644 index 0000000..cd8bc06 --- /dev/null +++ b/src/arch/x86_64/sched/context.c @@ -0,0 +1,297 @@ +#include +#include +#include +#include "mm/vmm.h" +#include "mm/memory.h" +#include "mm/pmm.h" +#include "mm/slab.h" +#include "mp/mp.h" +#include "mp/spinlock.h" +#include "fs/elf.h" +#include "sched/sched_types.h" +#include "sched/sched.h" +#include "arch/x86_64/cpu/reg.h" +#include "mm/mmap.h" +#include "libk/string.h" +#include "libk/misc.h" + + +void thread_setup_context(struct thread *thrd, uintptr_t pc_address, + uint64_t arguments, bool user) { + + bool old_state = int_toggle(false); + thrd->reg.rip = pc_address; + thrd->reg.rdi = arguments; + thrd->kernel_stack = ((uint64_t)pmm_allocz(CPU_STACK_SIZE / PAGE_SIZE) + + MEM_PHYS_OFFSET + CPU_STACK_SIZE); + thrd->fpu_storage = + (void *)((uintptr_t)pmm_allocz(DIV_ROUNDUP( + prcb_return_current_cpu()->fpu_storage_size, PAGE_SIZE)) + + MEM_PHYS_OFFSET); + thrd->pf_stack = ((uint64_t)pmm_allocz(CPU_STACK_SIZE / PAGE_SIZE) + + MEM_PHYS_OFFSET + CPU_STACK_SIZE); + + struct process *proc = thrd->mother_proc; + + if (user) { + thrd->reg.cs = 0x23; + thrd->reg.ss = 0x1b; + thrd->reg.rsp = (uint64_t)pmm_allocz(STACK_SIZE / PAGE_SIZE); + thrd->stack = thrd->reg.rsp; + + mmap_range(proc->process_pagemap, proc->stack_top - STACK_SIZE, + (uintptr_t)thrd->reg.rsp, STACK_SIZE, PROT_READ | PROT_WRITE, + MAP_ANONYMOUS); + + thrd->reg.rsp = proc->stack_top; + proc->stack_top -= STACK_SIZE; + + prcb_return_current_cpu()->fpu_restore(thrd->fpu_storage); + uint16_t default_fcw = 0b1100111111; + asm volatile("fldcw %0" ::"m"(default_fcw) : "memory"); + uint32_t default_mxcsr = 0b1111110000000; + asm volatile("ldmxcsr %0" ::"m"(default_mxcsr) : "memory"); + prcb_return_current_cpu()->fpu_save(thrd->fpu_storage); + + thrd->fs_base = 0; + thrd->gs_base = 0; + } else { + thrd->reg.cs = 0x08; + thrd->reg.ss = 0x10; + + thrd->stack = thrd->kernel_stack; + thrd->reg.rsp = thrd->stack; + + thrd->fs_base = read_fs_base(); + thrd->gs_base = read_kernel_gs(); + } + + thrd->reg.rflags = 0x202; + int_toggle(old_state); +} + +void thread_setup_context_from_user(struct thread *thrd, uintptr_t pc_address, + uintptr_t sp) { + bool old_state = int_toggle(false); + thrd->reg.rip = pc_address; + thrd->kernel_stack = ((uint64_t)pmm_allocz(CPU_STACK_SIZE / PAGE_SIZE) + + MEM_PHYS_OFFSET + CPU_STACK_SIZE); + thrd->fpu_storage = + (void *)((uintptr_t)pmm_allocz(DIV_ROUNDUP( + prcb_return_current_cpu()->fpu_storage_size, PAGE_SIZE)) + + MEM_PHYS_OFFSET); + + struct process *proc = thrd->mother_proc; + thrd->reg.cs = 0x23; + thrd->reg.ss = 0x1b; + thrd->reg.rsp = sp; + thrd->stack = thrd->reg.rsp; + + thrd->pf_stack = ((uint64_t)pmm_allocz(CPU_STACK_SIZE / PAGE_SIZE) + + MEM_PHYS_OFFSET + CPU_STACK_SIZE); + + prcb_return_current_cpu()->fpu_restore(thrd->fpu_storage); + uint16_t default_fcw = 0b1100111111; + asm volatile("fldcw %0" ::"m"(default_fcw) : "memory"); + uint32_t default_mxcsr = 0b1111110000000; + asm volatile("ldmxcsr %0" ::"m"(default_mxcsr) : "memory"); + prcb_return_current_cpu()->fpu_save(thrd->fpu_storage); + + thrd->fs_base = 0; + thrd->gs_base = 0; + + thrd->reg.rflags = 0x202; + int_toggle(old_state); +} + +// I am still surprised that I still know how this works. +// I will properly document it sometime soon. +void thread_setup_context_for_execve(struct thread *thrd, uintptr_t pc_address, + char **argv, char **envp) { + bool old_state = int_toggle(false); + struct process *proc = thrd->mother_proc; + + thrd->reg.rip = pc_address; + thrd->reg.rsp = (uint64_t)pmm_allocz(STACK_SIZE / PAGE_SIZE); + thrd->stack = thrd->reg.rsp; + thrd->reg.cs = 0x23; + thrd->reg.ss = 0x1b; + + mmap_range(proc->process_pagemap, proc->stack_top - STACK_SIZE, + (uintptr_t)thrd->reg.rsp, STACK_SIZE + 1, PROT_READ | PROT_WRITE, + MAP_ANONYMOUS); + + thrd->reg.rsp = proc->stack_top; + + thrd->reg.rflags = 0x202; + + prcb_return_current_cpu()->fpu_restore(thrd->fpu_storage); + uint16_t default_fcw = 0b1100111111; + asm volatile("fldcw %0" ::"m"(default_fcw) : "memory"); + uint32_t default_mxcsr = 0b1111110000000; + asm volatile("ldmxcsr %0" ::"m"(default_mxcsr) : "memory"); + prcb_return_current_cpu()->fpu_save(thrd->fpu_storage); + + thrd->fs_base = 0; + thrd->gs_base = 0; + + struct auxval auxv = proc->auxv; + + // the stack structure. + // the address values are not accurate. + /* + * 0x70000000000 - "USER=root\0" // envp[0][9] + * 0x6fffffffff6 - proc->name // argv[0][255] + * 0x6fffffffef6 - 0x0, 0x0 // zeros + * 0x6fffffffee6 - AT_ENTRY + * 0x6fffffffede - 0x400789 // example values + * 0x6fffffffed6 - AT_PHDR + * 0x6fffffffece - 2 + * 0x6fffffffec6 - AT_PHENT + * 0x6fffffffebe - 7 + * 0x6fffffffeb6 - AT_PHNUM + * 0x6fffffffeae - 5 + * 0x6fffffffea6 - 0x0 // START OF ENVP + * 0x6fffffffe9e - 0x6fffffffff6 // pointer to envp[0] + * 0x6fffffffe96 - 0x0 // START OF ARGV + * 0x6fffffffe8e - 0x6fffffffef6 // pointer to argv[0] + * 0x6fffffffe86 - 1 // argc + */ + + uint64_t *stack = (uint64_t *)(thrd->stack + STACK_SIZE + MEM_PHYS_OFFSET); + + int envp_len = 0; + uint64_t address_difference = 0; + + uint8_t *stack_but_in_bytes = (uint8_t *)stack; + + for (envp_len = 0; envp[envp_len] != NULL; envp_len++) { + stack_but_in_bytes -= (strlen(envp[envp_len]) + 1); + memcpy((void *)stack_but_in_bytes, envp[envp_len], + strlen(envp[envp_len]) + 1); + } + + stack = (uint64_t *)stack_but_in_bytes; + address_difference = + (thrd->stack + STACK_SIZE) - ((uint64_t)stack - MEM_PHYS_OFFSET); + uint64_t addr_to_env = (uint64_t)proc->stack_top - address_difference; + + int argv_len; + for (argv_len = 0; argv[argv_len] != NULL; argv_len++) { + stack_but_in_bytes -= (strlen(argv[argv_len]) + 1); + memcpy((void *)stack_but_in_bytes, argv[argv_len], + strlen(argv[argv_len]) + 1); + } + + stack = (uint64_t *)stack_but_in_bytes; + address_difference = + (thrd->stack + STACK_SIZE) - ((uint64_t)stack - MEM_PHYS_OFFSET); + uint64_t addr_to_arg = (uint64_t)proc->stack_top - address_difference; + + // alignments + + stack = (uintptr_t *)ALIGN_DOWN((uintptr_t)stack, 16); + if (((argv_len + envp_len + 1) & 1) != 0) + stack--; + + *(--stack) = 0; + *(--stack) = 0; + stack -= 2; + stack[0] = 23; // AT_SECURE + stack[1] = 0; + stack -= 2; + stack[0] = 9; + stack[1] = auxv.at_entry; + stack -= 2; + stack[0] = 3; + stack[1] = auxv.at_phdr; + stack -= 2; + stack[0] = 4; + stack[1] = auxv.at_phent; + stack -= 2; + stack[0] = 5; + stack[1] = auxv.at_phnum; + + *(--stack) = 0; + + stack -= envp_len; + + uint64_t offset = 0; + for (int i = envp_len - 1; i >= 0; i--) { + if (i != envp_len - 1) { + offset += strlen(envp[i + 1]) + 1; + } + stack[i] = addr_to_env + offset; + } + + *(--stack) = 0; + + stack -= argv_len; + + offset = 0; + for (int i = argv_len - 1; i >= 0; i--) { + if (i != argv_len - 1) { + offset += strlen(argv[i + 1]) + 1; + } + stack[i] = addr_to_arg + offset; + } + + *(--stack) = argv_len; + + address_difference = + (thrd->stack + STACK_SIZE) - ((uint64_t)stack - MEM_PHYS_OFFSET); + + thrd->reg.rsp -= address_difference; + proc->stack_top -= STACK_SIZE; + int_toggle(old_state); +} + +void thread_fork_context(struct thread *thrd, struct thread *fthrd) { + bool old_state = int_toggle(false); + fthrd->kernel_stack = ((uint64_t)pmm_allocz(CPU_STACK_SIZE / PAGE_SIZE) + + MEM_PHYS_OFFSET + CPU_STACK_SIZE); + fthrd->pf_stack = ((uint64_t)pmm_allocz(CPU_STACK_SIZE / PAGE_SIZE) + + MEM_PHYS_OFFSET + CPU_STACK_SIZE); + fthrd->reg = thrd->reg; + fthrd->reg.rax = 0; + fthrd->reg.rbx = 0; + fthrd->fs_base = thrd->fs_base; + fthrd->gs_base = thrd->gs_base; + fthrd->fpu_storage = + (void *)((uintptr_t)pmm_allocz(DIV_ROUNDUP( + prcb_return_current_cpu()->fpu_storage_size, PAGE_SIZE)) + + MEM_PHYS_OFFSET); + + memcpy(fthrd->fpu_storage, thrd->fpu_storage, + prcb_return_current_cpu()->fpu_storage_size); + int_toggle(old_state); +} + +void thread_destroy_context(struct thread *thrd) { + bool old_state = int_toggle(false); + pmm_free((void *)(thrd->kernel_stack - MEM_PHYS_OFFSET - CPU_STACK_SIZE), + CPU_STACK_SIZE / PAGE_SIZE); + pmm_free((void *)(thrd->pf_stack - MEM_PHYS_OFFSET - CPU_STACK_SIZE), + CPU_STACK_SIZE / PAGE_SIZE); + pmm_free( + (void *)((uint64_t)thrd->fpu_storage - MEM_PHYS_OFFSET), + DIV_ROUNDUP(prcb_return_current_cpu()->fpu_storage_size, PAGE_SIZE)); + int_toggle(old_state); +} + +void process_setup_context(struct process *proc, bool user) { + if (user) { + proc->process_pagemap = vmm_new_pagemap(); + } else { + proc->process_pagemap = kernel_pagemap; + } +} + +void process_fork_context(struct process *proc, struct process *fproc) { + fproc->process_pagemap = vmm_fork_pagemap(proc->process_pagemap); +} + +void process_destroy_context(struct process *proc) { + (void)proc; + vmm_destroy_pagemap(proc->process_pagemap); +} diff --git a/src/arch/x86_64/sched/prctl.c b/src/arch/x86_64/sched/prctl.c new file mode 100644 index 0000000..54836cb --- /dev/null +++ b/src/arch/x86_64/sched/prctl.c @@ -0,0 +1,43 @@ +#include +#include +#include +#include "mm/vmm.h" +#include "mm/memory.h" +#include "mm/pmm.h" +#include "mm/slab.h" +#include "mp/mp.h" +#include "mp/spinlock.h" +#include "sched/syscall.h" +#include "sched/sched.h" +#include "libk/errno.h" + +#define ARCH_SET_GS 0x1001 +#define ARCH_SET_FS 0x1002 +#define ARCH_GET_FS 0x1003 +#define ARCH_GET_GS 0x1004 + +void syscall_prctl(struct syscall_arguments *args) { + int option = (int)args->args0; + uint64_t value = args->args1; + switch (option) { + case ARCH_SET_GS: { + sched_get_running_thread()->gs_base = value; + // set_user_gs(prcb_return_current_cpu()->running_thread->gs_base); + break; + } + case ARCH_GET_GS: + args->ret = read_user_gs(); + break; + case ARCH_SET_FS: { + sched_get_running_thread()->fs_base = value; + set_fs_base(sched_get_running_thread()->fs_base); + break; + } + case ARCH_GET_FS: + args->ret = read_fs_base(); + break; + default: + errno = EINVAL; + break; + } +} diff --git a/src/arch/x86_64/sched/resched.c b/src/arch/x86_64/sched/resched.c new file mode 100644 index 0000000..5b4b65d --- /dev/null +++ b/src/arch/x86_64/sched/resched.c @@ -0,0 +1,125 @@ +#include +#include +#include +#include "mm/vmm.h" +#include "mm/memory.h" +#include "mm/pmm.h" +#include "mm/slab.h" +#include "mp/mp.h" +#include "mp/spinlock.h" +#include "arch/x86_64/cpu/reg.h" +#include "arch/x86_64/sys/timer.h" +#include "sched/sched.h" +#include "arch/x86_64/sys/apic.h" +#include "libk/time.h" +#include "arch/x86_64/sys/prcb.h" + + +extern uint32_t smp_bsp_lapic_id; + +extern void resched_context_switch(registers_t *reg); + +void sched_yield(bool save) { + cli(); + timer_stop_sched(); + + struct thread *thrd = sched_get_running_thread(); + + if (save) { + spinlock_acquire_or_wait(&thrd->yield_lock); + } else { + spinlock_drop(&thrd->lock); + prcb_return_current_cpu()->running_thread = NULL; + } + + apic_send_ipi(prcb_return_current_cpu()->lapic_id, 48); + sti(); + + if (save) { + spinlock_acquire_or_wait( + &thrd->yield_lock); // the lock is released when the thread is + // rescheduled + spinlock_drop(&thrd->yield_lock); + } else { + for (;;) { + halt(); + } + } +} + +void sched_trigger_yield(uint64_t cpu_number) { + struct prcb *target = NULL; + for (size_t i = 0; i < prcb_return_installed_cpus(); i++) { + if (prcbs[i].cpu_number == cpu_number) { + target = &prcbs[i]; + break; + } + } + if (target) { + apic_send_ipi(target->lapic_id, 48); + } +} + +uint64_t last = 0; + +void resched(registers_t *reg) { + vmm_switch_pagemap(kernel_pagemap); + prcb_return_current_cpu()->sched_ticks++; + timer_stop_sched(); + + struct thread *running_thrd = prcb_return_current_cpu()->running_thread; + + if (prcb_return_current_cpu()->lapic_id == smp_bsp_lapic_id) { + uint64_t current = timer_count(); + if (timer_handler((current - last) * 1000000)) { + last = current; + } + } + + if (running_thrd) { + spinlock_drop(&running_thrd->yield_lock); + running_thrd->reg = *reg; + running_thrd->fs_base = read_fs_base(); + running_thrd->gs_base = read_user_gs(); + prcb_return_current_cpu()->fpu_save(running_thrd->fpu_storage); + running_thrd->last_scheduled = timer_count(); + running_thrd->running_on_cpu = -1; + running_thrd->stack = prcb_return_current_cpu()->user_stack; + if (running_thrd->state == THREAD_NORMAL) { + running_thrd->state = THREAD_READY_TO_RUN; + } + spinlock_drop(&running_thrd->lock); + } + + running_thrd = sched_get_next_thread(running_thrd); + + if (running_thrd == NULL) { + apic_eoi(); + prcb_return_current_cpu()->running_thread = NULL; + timer_sched_oneshot(48, 20000); + for (;;) { + sti(); + halt(); + } + } + + prcb_return_current_cpu()->running_thread = running_thrd; + prcb_return_current_cpu()->cpu_tss.ist2 = running_thrd->pf_stack; + + prcb_return_current_cpu()->kernel_stack = running_thrd->kernel_stack; + prcb_return_current_cpu()->user_stack = running_thrd->stack; + prcb_return_current_cpu()->running_thread->state = THREAD_NORMAL; + prcb_return_current_cpu()->fpu_restore(running_thrd->fpu_storage); + + running_thrd->running_on_cpu = prcb_return_current_cpu()->cpu_number; + + set_fs_base(running_thrd->fs_base); + // set_user_gs(running_thrd->gs_base); + + vmm_switch_pagemap(running_thrd->mother_proc->process_pagemap); + + apic_eoi(); + + timer_sched_oneshot(48, running_thrd->runtime); + resched_context_switch(&running_thrd->reg); +} diff --git a/src/arch/x86_64/sched/switch.asm b/src/arch/x86_64/sched/switch.asm new file mode 100644 index 0000000..46ffc7a --- /dev/null +++ b/src/arch/x86_64/sched/switch.asm @@ -0,0 +1,21 @@ +global resched_context_switch + +resched_context_switch: + mov rsp, rdi + pop r15 + pop r14 + pop r13 + pop r12 + pop r11 + pop r10 + pop r9 + pop r8 + pop rsi + pop rdi + pop rbp + pop rdx + pop rcx + pop rbx + pop rax + add rsp, 24 + iretq diff --git a/src/arch/x86_64/serial/serial.c b/src/arch/x86_64/serial/serial.c new file mode 100644 index 0000000..7cb5c4b --- /dev/null +++ b/src/arch/x86_64/serial/serial.c @@ -0,0 +1,64 @@ +#include "serial.h" +#include +#include +#include "arch/x86_64/cpu/io.h" + +void serial_init(void) { + x86_64_outb(COM1 + 1, 0x1); + x86_64_outb(COM1 + 3, 0x80); + x86_64_outb(COM1, 0x1); + x86_64_outb(COM1 + 1, 0x0); + x86_64_outb(COM1 + 3, 0x3); + x86_64_outb(COM1 + 2, 0xC7); + x86_64_outb(COM1 + 4, 0xB); +} + +static inline bool is_transmit_empty(void) { + return (x86_64_inb(COM1 + 5) & 0b1000000) != 0; +} + +static inline void transmit_data(uint8_t value) { + while (!is_transmit_empty()) { + asm volatile("pause"); + } + x86_64_outb(COM1, value); +} + +void serial_putchar(char ch) { + transmit_data(ch); +} + +void serial_puts(char *str) { + while (*str) { + if (*str == '\n') + transmit_data('\r'); + transmit_data(*str++); + } +} + +int serial_received(void) { + return x86_64_inb(COM1 + 5) & 1; +} + +char serial_get_byte(void) { + while (serial_received() == 0) + ; + + return x86_64_inb(COM1); +} + +char serial_getchar(void) { + char c = '\0'; + char last_c = c; + while (1) { + if (c != '\0') + serial_putchar('\b'); + serial_putchar(c); + c = serial_get_byte(); + if (c == '\r') + break; + + last_c = c; + } + return last_c; +} diff --git a/src/arch/x86_64/serial/serial.h b/src/arch/x86_64/serial/serial.h new file mode 100644 index 0000000..4ac21ac --- /dev/null +++ b/src/arch/x86_64/serial/serial.h @@ -0,0 +1,9 @@ +#pragma once + +#define COM1 0x3F8 + +void serial_init(void); +void serial_putchar(char ch); +void serial_puts(char *str); +char serial_get_byte(void); +char serial_getchar(void); \ No newline at end of file diff --git a/src/arch/x86_64/sys/apic.c b/src/arch/x86_64/sys/apic.c index 0757aba..6c150ca 100644 --- a/src/arch/x86_64/sys/apic.c +++ b/src/arch/x86_64/sys/apic.c @@ -2,146 +2,249 @@ #include "mm/vmm.h" #include "mm/memory.h" #include "arch/x86_64/cpu/io.h" -#include "libk/stdio.h" -/* ── Internal state ───────────────────────────────────────────────────────── */ -static volatile uint32_t *g_lapic = NULL; /* Virtual address of LAPIC MMIO */ +#include "arch/x86_64/cpu/features.h" +#include "libk/debug.h" +#include "mp/mp.h" +#include "arch/x86_64/sys/prcb.h" +#include "arch/x86_64/fw/madt.h" +#include "pic.h" +#include "timer.h" +#include +#include "arch/x86_64/bus/mmio.h" -/* ── Low-level helpers ────────────────────────────────────────────────────── */ +static uintptr_t lapic_addr = 0; +static bool x2apic = false; -static inline uint32_t lapic_read(uint32_t reg) { - return g_lapic[reg >> 2]; +// Converts xAPIC MMIO offset into x2APIC MSR +static inline uint32_t reg_to_x2apic(uint32_t reg) { + uint32_t x2apic_reg = 0; + // MSR 831H is reserved; read/write operations cause general-protection + // exceptions. The contents of the APIC register at MMIO offset 310H are + // accessible in x2APIC mode through the MSR at address 830H + // -- Intel SDM Volume 3A 10.12.1.2 Note 4 + if (reg == 0x310) { + x2apic_reg = 0x30; + } else { + x2apic_reg = reg >> 4; + } + return x2apic_reg + 0x800; } -static inline void lapic_write(uint32_t reg, uint32_t val) { - g_lapic[reg >> 2] = val; - /* Serialise: read back a read-only register to ensure the write landed - * before we continue (required by the APIC spec). */ - (void)g_lapic[LAPIC_ID >> 2]; +uint32_t lapic_read(uint32_t reg) { + if (x2apic) { + return rdmsr(reg_to_x2apic(reg)); + } + return mmind((void *)lapic_addr + MEM_PHYS_OFFSET + reg); } -static inline void rdmsr(uint32_t msr, uint32_t *lo, uint32_t *hi) { - asm volatile("rdmsr" : "=a"(*lo), "=d"(*hi) : "c"(msr)); +void lapic_write(uint32_t reg, uint32_t value) { + if (x2apic) { + wrmsr(reg_to_x2apic(reg), value); + } else { + mmoutd((void *)lapic_addr + MEM_PHYS_OFFSET + reg, value); + } } -static inline void wrmsr(uint32_t msr, uint32_t lo, uint32_t hi) { - asm volatile("wrmsr" : : "a"(lo), "d"(hi), "c"(msr)); +static void lapic_set_nmi(uint8_t vec, uint8_t current_processor_id, + uint8_t processor_id, uint16_t flags, uint8_t lint) { + // A value of 0xFF means all the processors + if (processor_id != 0xFF) { + if (current_processor_id != processor_id) { + return; + } + } + + // Set to raise in vector number "vec" and set NMI flag + uint32_t nmi = 0x400 | vec; + + // Set to active low if needed + if (flags & 2) { + nmi |= 1 << 13; + } + + // Set to level triggered if needed + if (flags & 8) { + nmi |= 1 << 15; + } + + // Use the proper LINT register + if (lint == 0) { + lapic_write(0x350, nmi); + } else if (lint == 1) { + lapic_write(0x360, nmi); + } } -/* ── Public API ───────────────────────────────────────────────────────────── */ - -uint64_t lapic_get_phys_base(void) { - uint32_t lo, hi; - rdmsr(IA32_APIC_BASE_MSR, &lo, &hi); - /* Physical base is in bits [35:12] of the 64-bit MSR */ - return ((uint64_t)(hi & 0xFu) << 32) | (lo & 0xFFFFF000u); +uint8_t lapic_get_id(void) { + return (uint8_t)(lapic_read(0x20) >> 24); } -void lapic_eoi(void) { - lapic_write(LAPIC_EOI, 0); +void lapic_init(uint8_t processor_id) { + kprintf("LAPIC: Setting up LAPIC on Processor %u\n", processor_id); + uint64_t apic_msr = rdmsr(0x1B); + // Set APIC enable flag + apic_msr |= 1 << 11; + uint32_t a = 0, b = 0, c = 0, d = 0; + if (__get_cpuid(1, &a, &b, &c, &d)) { + if (c & CPUID_X2APIC) { + x2apic = true; + // Set x2APIC flag if support is detected + apic_msr |= 1 << 10; + } + } + wrmsr(0x1B, apic_msr); + + // Initialize local APIC + lapic_write(0x80, 0); + lapic_write(0xF0, lapic_read(0xF0) | 0x100); + if (!x2apic) { + lapic_write(0xE0, 0xF0000000); + lapic_write(0xD0, lapic_read(0x20)); + } + + // Set NMIs according to the MADT + for (int i = 0; i < madt_nmis.length; i++) { + struct madt_nmi *nmi = madt_nmis.data[i]; + lapic_set_nmi(2, processor_id, nmi->processor, nmi->flags, nmi->lint); + } + + // Set up APIC timer + + // Tell APIC timer to divide by 16 + lapic_write(0x3E0, 3); + // Set timer init counter to -1 + lapic_write(0x380, 0xFFFFFFFF); + + timer_sleep(10); + + // Stop the APIC timer + lapic_write(0x320, 0x10000); + + // How much the APIC timer ticked in 10ms + prcb_return_current_cpu()->tick_in_10ms = 0xFFFFFFFF - lapic_read(0x390); + + // With divider 16 + lapic_write(0x3E0, 3); + lapic_write(0x380, prcb_return_current_cpu()->tick_in_10ms / 10); } -uint32_t lapic_id(void) { - /* On xAPIC the LAPIC ID sits in bits [31:24] of the ID register */ - return lapic_read(LAPIC_ID) >> 24; +static uint32_t ioapic_read(uintptr_t ioapic_address, size_t reg) { + mmoutd((void *)ioapic_address + MEM_PHYS_OFFSET, reg & 0xFF); + return mmind((void *)ioapic_address + MEM_PHYS_OFFSET + 16); } -void lapic_init(void) { - /* - * ── Step 1: Re-enable the APIC global enable bit ───────────────────── - * - * Earlier in kmain (before pmm_init) we cleared bit 11 of IA32_APIC_BASE - * so that the i8259 PIC would raise interrupts properly under Limine. - * Now that the LAPIC is being brought online we must restore that bit; - * without it the LAPIC is completely off and nothing below will work. - */ - uint32_t lo, hi; - rdmsr(IA32_APIC_BASE_MSR, &lo, &hi); - lo |= IA32_APIC_BASE_ENABLE; - wrmsr(IA32_APIC_BASE_MSR, lo, hi); +static void ioapic_write(uintptr_t ioapic_address, size_t reg, uint32_t data) { + mmoutd((void *)ioapic_address + MEM_PHYS_OFFSET, reg & 0xFF); + mmoutd((void *)ioapic_address + MEM_PHYS_OFFSET + 16, data); +} - /* - * ── Step 2: Locate the LAPIC MMIO window ───────────────────────────── - * - * The physical address is almost always 0xFEE00000 but we read it from - * the MSR to be correct. vmm_init() already identity-maps all physical - * memory through the HHDM (up to 256 GiB), so the MMIO page is reachable - * at phys + MEM_PHYS_OFFSET with no extra mapping needed. - * - * TODO: For strict correctness the LAPIC page should be mapped as - * uncacheable (PAT / PCD). In practice, QEMU / BOCHS work fine - * because MTRRs mark the 0xFEE00000 range as UC by default. - */ - uint64_t phys_base = ((uint64_t)(hi & 0xFu) << 32) | (lo & 0xFFFFF000u); - printf("[LAPIC] Physical base: 0x%08x%08x\n", - (uint32_t)(phys_base >> 32), (uint32_t)phys_base); +static uint32_t get_gsi_count(uintptr_t ioapic_address) { + return (ioapic_read(ioapic_address, 1) & 0xFF0000) >> 16; +} - g_lapic = (volatile uint32_t *)(phys_base + MEM_PHYS_OFFSET); +static struct madt_ioapic *get_ioapic_by_gsi(uint32_t gsi) { + // Search through every I/O APIC to find its GSI + for (int i = 0; i < madt_io_apics.length; i++) { + struct madt_ioapic *ioapic = madt_io_apics.data[i]; + if (ioapic->gsib <= gsi && + ioapic->gsib + get_gsi_count(ioapic->addr) > gsi) { + return ioapic; + } + } - /* - * ── Step 3: Software-enable the LAPIC via the SVR ──────────────────── - * - * Bit 8 = Software Enable. The lower 8 bits are the "spurious interrupt - * vector" delivered if the CPU acknowledges an interrupt that was - * retracted by the LAPIC; 0xFF is the conventional choice. - */ - lapic_write(LAPIC_SVR, LAPIC_SVR_ENABLE | 0xFF); + // Return NULL if none was found + return NULL; +} - /* - * ── Step 4: Configure LINT0 as ExtINT (i8259 pass-through) ────────── - * - * On a standard PC the i8259 INTR pin is wired to the BSP's LINT0. - * Setting LINT0 to ExtINT delivery mode makes the LAPIC act as a - * transparent relay: the i8259 interrupt acknowledge cycle goes through - * normally, the full 8-bit vector comes from the i8259, and the CPU's - * existing IDT entries + irq.c EOI code all keep working unchanged. - * - * This is the "Virtual Wire" mode described in the Intel MP Spec. - */ - lapic_write(LAPIC_LVT_LINT0, LAPIC_LVT_DM_EXTINT); +void ioapic_redirect_gsi(uint32_t gsi, uint8_t vec, uint16_t flags) { + // Get I/O APIC address of the GSI + size_t io_apic = get_ioapic_by_gsi(gsi)->addr; - /* - * ── Step 5: Configure LINT1 as NMI ─────────────────────────────────── - * - * LINT1 is the NMI pin on standard PCs. Delivery mode = 4 (NMI), - * unmasked, edge-triggered (the default when LAPIC_LVT_LEVEL is clear). - */ - lapic_write(LAPIC_LVT_LINT1, LAPIC_LVT_DM_NMI); + uint32_t low_index = 0x10 + (gsi - get_ioapic_by_gsi(gsi)->gsib) * 2; + uint32_t high_index = low_index + 1; - /* - * ── Step 6: Mask LVT entries we are not yet using ──────────────────── - * - * Timer, error, and (if present) thermal / perf entries all start masked. - * Assign distinct vectors in the 0xF0-0xFE range so that if one fires - * spuriously the IDT handler can identify it and send EOI. - */ - lapic_write(LAPIC_LVT_TIMER, LAPIC_LVT_MASKED | 0xFD); - lapic_write(LAPIC_LVT_ERROR, LAPIC_LVT_MASKED | 0xFE); + uint32_t high = ioapic_read(io_apic, high_index); - /* Version register: bits [23:16] = max LVT entry index */ - uint32_t ver = lapic_read(LAPIC_VER); - uint32_t max_lvt = (ver >> 16) & 0xFF; - if (max_lvt >= 4) lapic_write(LAPIC_LVT_THERMAL, LAPIC_LVT_MASKED | 0xFC); - if (max_lvt >= 5) lapic_write(LAPIC_LVT_PERF, LAPIC_LVT_MASKED | 0xFB); + // Set APIC ID + high &= ~0xFF000000; + high |= ioapic_read(io_apic, 0) << 24; + ioapic_write(io_apic, high_index, high); - /* - * ── Step 7: Clear the Error Status Register ─────────────────────────── - * - * The APIC spec requires writing ESR twice to clear stale error bits. - */ - lapic_write(LAPIC_ESR, 0); - lapic_write(LAPIC_ESR, 0); + uint32_t low = ioapic_read(io_apic, low_index); - /* Dismiss any stale in-service interrupt */ - lapic_write(LAPIC_EOI, 0); + // Unmask the IRQ + low &= ~(1 << 16); - /* - * ── Step 8: Set Task Priority to 0 ─────────────────────────────────── - * - * TPR = 0 means the CPU will accept all interrupt priorities. - * Raise this later if need to block lower-priority interrupts. - */ - lapic_write(LAPIC_TPR, 0); + // Set to physical delivery mode + low &= ~(1 << 11); + + // Set to fixed delivery mode + low &= ~0x700; + + // Set delivery vector + low &= ~0xFF; + low |= vec; + + // Active high(0) or low(1) + if (flags & 2) { + low |= 1 << 13; + } + + // Edge(0) or level(1) triggered + if (flags & 8) { + low |= 1 << 15; + } + + ioapic_write(io_apic, low_index, low); +} + +void ioapic_redirect_irq(uint32_t irq, uint8_t vect) { + // Use ISO table to find flags and interrupt overrides + for (int i = 0; i < madt_isos.length; i++) { + if (madt_isos.data[i]->irq_source == irq) { + ioapic_redirect_gsi(madt_isos.data[i]->gsi, vect, + madt_isos.data[i]->flags); + return; + } + } + + ioapic_redirect_gsi(irq, vect, 0); +} + +void apic_send_ipi(uint32_t lapic_id, uint32_t flags) { + if (x2apic) { + // Write MSR directly, because lapic_write receives a 32-bit argument + // Whilst in x2APIC, 0x830 is a 64-bit register + wrmsr(0x830, ((uint64_t)lapic_id << 32) | flags); + } else { + lapic_write(0x310, (lapic_id << 24)); + lapic_write(0x300, flags); + } +} + +void apic_eoi(void) { + // Writing any other value different than 0 may cause a #GP exception + lapic_write(0xB0, 0); +} + +void timer_stop_sched(void) { + lapic_write(0x380, 0); + lapic_write(0x320, (1 << 16)); +} + +void timer_sched_oneshot(uint8_t isr, uint32_t us) { + timer_stop_sched(); + lapic_write(0x320, isr | 0x20000); + lapic_write(0x3E0, 3); + lapic_write(0x380, + ((prcb_return_current_cpu()->tick_in_10ms * (us / 1000))) / 10); +} + +void apic_init(void) { + pic_init(); + + lapic_addr = acpi_get_lapic(); + + ioapic_redirect_irq(0, 48); +} - printf("[LAPIC] Online. ID=%u version=0x%02x max_lvt=%u\n", - lapic_id(), ver & 0xFF, max_lvt); -} \ No newline at end of file diff --git a/src/arch/x86_64/sys/apic.h b/src/arch/x86_64/sys/apic.h index 5ba43c9..2afbf1b 100644 --- a/src/arch/x86_64/sys/apic.h +++ b/src/arch/x86_64/sys/apic.h @@ -2,73 +2,13 @@ #include #include -/* - * Local APIC (xAPIC mode, MMIO access) - * Register offsets are byte offsets; we shift right by 2 for uint32_t indexing. - */ -/* ── Register offsets ─────────────────────────────────────────────────────── */ -#define LAPIC_ID 0x020 /* LAPIC ID (bits [27:24] on P4, [31:24] on modern) */ -#define LAPIC_VER 0x030 /* Version register */ -#define LAPIC_TPR 0x080 /* Task Priority Register */ -#define LAPIC_APR 0x090 /* Arbitration Priority (RO) */ -#define LAPIC_PPR 0x0A0 /* Processor Priority (RO) */ -#define LAPIC_EOI 0x0B0 /* End-Of-Interrupt (write 0 to ack) */ -#define LAPIC_RRD 0x0C0 /* Remote Read (RO) */ -#define LAPIC_LDR 0x0D0 /* Logical Destination */ -#define LAPIC_DFR 0x0E0 /* Destination Format */ -#define LAPIC_SVR 0x0F0 /* Spurious Interrupt Vector */ -#define LAPIC_ESR 0x280 /* Error Status Register */ -#define LAPIC_ICR_LO 0x300 /* Interrupt Command (low 32 bits) */ -#define LAPIC_ICR_HI 0x310 /* Interrupt Command (high 32 bits) */ -#define LAPIC_LVT_TIMER 0x320 /* LVT: APIC timer */ -#define LAPIC_LVT_THERMAL 0x330 /* LVT: Thermal sensor */ -#define LAPIC_LVT_PERF 0x340 /* LVT: Performance monitoring */ -#define LAPIC_LVT_LINT0 0x350 /* LVT: Local interrupt pin 0 */ -#define LAPIC_LVT_LINT1 0x360 /* LVT: Local interrupt pin 1 */ -#define LAPIC_LVT_ERROR 0x370 /* LVT: Error */ -#define LAPIC_TIMER_ICR 0x380 /* Timer Initial Count */ -#define LAPIC_TIMER_CCR 0x390 /* Timer Current Count (RO) */ -#define LAPIC_TIMER_DCR 0x3E0 /* Timer Divide Configuration */ - -/* ── SVR flags ────────────────────────────────────────────────────────────── */ -#define LAPIC_SVR_ENABLE (1u << 8) /* Software-enable the LAPIC */ - -/* ── LVT delivery modes (bits [10:8]) ────────────────────────────────────── */ -#define LAPIC_LVT_DM_FIXED (0u << 8) /* Fixed delivery */ -#define LAPIC_LVT_DM_SMI (2u << 8) -#define LAPIC_LVT_DM_NMI (4u << 8) /* NMI */ -#define LAPIC_LVT_DM_EXTINT (7u << 8) /* ExtINT: pass-through from i8259 */ - -/* ── LVT misc flags ───────────────────────────────────────────────────────── */ -#define LAPIC_LVT_MASKED (1u << 16) /* Mask this LVT entry */ -#define LAPIC_LVT_LEVEL (1u << 15) /* Level-triggered (vs edge) */ -#define LAPIC_LVT_ACTIVE_LOW (1u << 13) /* Active-low polarity */ - -/* ── IA32_APIC_BASE MSR ───────────────────────────────────────────────────── */ -#define IA32_APIC_BASE_MSR 0x1B -#define IA32_APIC_BASE_ENABLE (1u << 11) /* APIC global enable bit */ -#define IA32_APIC_BASE_BSP (1u << 8) /* Bootstrap processor flag */ - -/* ── Public API ───────────────────────────────────────────────────────────── */ - -/** - * lapic_init - Map the LAPIC MMIO, re-enable the APIC global bit in the MSR - * (which was cleared earlier to let the i8259 work), then bring - * the LAPIC online with LINT0=ExtINT so the i8259/PIT path is - * completely preserved. - * - * Call AFTER uacpi_initialize() so that paging and the heap are live. - */ -void lapic_init(void); - -/** - * lapic_eoi - Signal end-of-interrupt to the LAPIC. - */ -void lapic_eoi(void); - -/** lapic_id - Return the LAPIC ID of the current CPU. */ -uint32_t lapic_id(void); - -/** lapic_get_phys_base - Return the physical base address from the MSR. */ -uint64_t lapic_get_phys_base(void); \ No newline at end of file +void apic_eoi(void); +void apic_init(void); +void apic_send_ipi(uint32_t lapic_id, uint32_t flags); +void ioapic_redirect_irq(uint32_t irq, uint8_t vect); +uint32_t lapic_read(uint32_t reg); +void lapic_write(uint32_t reg, uint32_t value); +uint8_t lapic_get_id(void); +void lapic_init(uint8_t processor_id); +void timer_sched_oneshot(uint8_t isr, uint32_t us); \ No newline at end of file diff --git a/src/arch/x86_64/sys/halt.c b/src/arch/x86_64/sys/halt.c new file mode 100644 index 0000000..5f292c1 --- /dev/null +++ b/src/arch/x86_64/sys/halt.c @@ -0,0 +1,59 @@ + +#include +#include +#include "arch/x86_64/asm/asm.h" +#include "halt.h" +#include "arch/x86_64/sys/apic.h" + + +extern bool is_smp; +bool is_halting = false; +uint8_t is_pausing = false; + +void halt_current_cpu(void) { + for (;;) { + cli(); + halt(); + } +} + +void halt_other_cpus(void) { + if (!is_smp) + return; + + is_halting = true; + + uint64_t icr = 0; + icr |= (0b100) << 8; // set delivery mode to nmi + icr |= (0b11) << 18; // set destination shorthand to all excluding self + lapic_write(0x300, icr); + lapic_write(0x310, icr >> 32); +} + +void pause_other_cpus(void) { + if (!is_smp) + return; + + is_pausing = 0; + is_pausing |= PAUSING; + + uint64_t icr = 0; + icr |= (0b100) << 8; + icr |= (0b11) << 18; + lapic_write(0x300, icr); + lapic_write(0x310, icr >> 32); +} + +void unpause_other_cpus(void) { + if (!is_smp) + return; + + is_pausing = 0; + is_pausing |= UNPAUSING; + + uint64_t icr = 0; + icr |= (0b100) << 8; + icr |= (0b11) << 18; + lapic_write(0x300, icr); + lapic_write(0x310, icr >> 32); +} diff --git a/src/arch/x86_64/sys/halt.h b/src/arch/x86_64/sys/halt.h new file mode 100644 index 0000000..ee8d221 --- /dev/null +++ b/src/arch/x86_64/sys/halt.h @@ -0,0 +1,9 @@ +#pragma once + +#define PAUSING (1 << 0) +#define UNPAUSING (1 << 1) + +void halt_current_cpu(void); +void halt_other_cpus(void); +void pause_other_cpus(void); +void unpause_other_cpus(void); diff --git a/src/arch/x86_64/sys/hpet.h b/src/arch/x86_64/sys/hpet.h new file mode 100644 index 0000000..8abc2b1 --- /dev/null +++ b/src/arch/x86_64/sys/hpet.h @@ -0,0 +1,32 @@ +#include +#include +#include "arch/x86_64/fw/acpi.h" + +struct hpet_table { + acpi_header_t header; + uint8_t hardware_rev_id; + uint8_t comparator_count : 5; + uint8_t counter_size : 1; + uint8_t reserved : 1; + uint8_t legacy_replacement : 1; + uint16_t pci_vendor_id; + acpi_gas_t address; + uint8_t hpet_number; + uint16_t minimum_tick; + uint8_t page_protection; +} __attribute__((packed)); + +struct hpet { + uint64_t general_capabilities; + uint64_t reserved; + uint64_t general_configuration; + uint64_t reserved2; + uint64_t general_int_status; + uint64_t reserved3; + uint64_t reserved4[24]; + uint64_t main_counter_value; + uint64_t reserved5; +}; + +uint64_t hpet_counter_value(void); +void hpet_sleep(uint64_t us); \ No newline at end of file diff --git a/src/arch/x86_64/sys/i8259.c b/src/arch/x86_64/sys/i8259.c deleted file mode 100644 index 40a9b48..0000000 --- a/src/arch/x86_64/sys/i8259.c +++ /dev/null @@ -1,166 +0,0 @@ -#include "pic.h" -#include "arch/x86_64/cpu/io.h" -#include - -#define PIC1_COMMAND_PORT 0x20 -#define PIC1_DATA_PORT 0x21 -#define PIC2_COMMAND_PORT 0xA0 -#define PIC2_DATA_PORT 0xA1 - -// Initialization Control Word 1 -// ----------------------------- -// 0 IC4 if set, the PIC expects to receive ICW4 during initialization -// 1 SGNL if set, only 1 PIC in the system; if unset, the PIC is cascaded with slave PICs -// and ICW3 must be sent to controller -// 2 ADI call address interval, set: 4, not set: 8; ignored on x86, set to 0 -// 3 LTIM if set, operate in level triggered mode; if unset, operate in edge triggered mode -// 4 INIT set to 1 to initialize PIC -// 5-7 ignored on x86, set to 0 - -enum { - PIC_ICW1_ICW4 = 0x01, - PIC_ICW1_SINGLE = 0x02, - PIC_ICW1_INTERVAL4 = 0x04, - PIC_ICW1_LEVEL = 0x08, - PIC_ICW1_INITIALIZE = 0x10 -} PIC_ICW1; - - -// Initialization Control Word 4 -// ----------------------------- -// 0 uPM if set, PIC is in 80x86 mode; if cleared, in MCS-80/85 mode -// 1 AEOI if set, on last interrupt acknowledge pulse, controller automatically performs -// end of interrupt operation -// 2 M/S only use if BUF is set; if set, selects buffer master; otherwise, selects buffer slave -// 3 BUF if set, controller operates in buffered mode -// 4 SFNM specially fully nested mode; used in systems with large number of cascaded controllers -// 5-7 reserved, set to 0 -enum { - PIC_ICW4_8086 = 0x1, - PIC_ICW4_AUTO_EOI = 0x2, - PIC_ICW4_BUFFER_MASTER = 0x4, - PIC_ICW4_BUFFER_SLAVE = 0x0, - PIC_ICW4_BUFFERRED = 0x8, - PIC_ICW4_SFNM = 0x10, -} PIC_ICW4; - - -enum { - PIC_CMD_END_OF_INTERRUPT = 0x20, - PIC_CMD_READ_IRR = 0x0A, - PIC_CMD_READ_ISR = 0x0B, -} PIC_CMD; - - -static uint16_t g_PicMask = 0xffff; -static bool g_AutoEoi = false; - -void i8259_SetMask(uint16_t newMask) -{ - g_PicMask = newMask; - x86_64_outb(PIC1_DATA_PORT, g_PicMask & 0xFF); - x86_64_iowait(); - x86_64_outb(PIC2_DATA_PORT, g_PicMask >> 8); - x86_64_iowait(); -} - -uint16_t i8259_GetMask() -{ - return x86_64_inb(PIC1_DATA_PORT) | (x86_64_inb(PIC2_DATA_PORT) << 8); -} - -void i8259_Configure(uint8_t offsetPic1, uint8_t offsetPic2, bool autoEoi) -{ - // Mask everything - i8259_SetMask(0xFFFF); - - // initialization control word 1 - x86_64_outb(PIC1_COMMAND_PORT, PIC_ICW1_ICW4 | PIC_ICW1_INITIALIZE); - x86_64_iowait(); - x86_64_outb(PIC2_COMMAND_PORT, PIC_ICW1_ICW4 | PIC_ICW1_INITIALIZE); - x86_64_iowait(); - - // initialization control word 2 - the offsets - x86_64_outb(PIC1_DATA_PORT, offsetPic1); - x86_64_iowait(); - x86_64_outb(PIC2_DATA_PORT, offsetPic2); - x86_64_iowait(); - - // initialization control word 3 - x86_64_outb(PIC1_DATA_PORT, 0x4); // tell PIC1 that it has a slave at IRQ2 (0000 0100) - x86_64_iowait(); - x86_64_outb(PIC2_DATA_PORT, 0x2); // tell PIC2 its cascade identity (0000 0010) - x86_64_iowait(); - - // initialization control word 4 - uint8_t icw4 = PIC_ICW4_8086; - if (autoEoi) { - icw4 |= PIC_ICW4_AUTO_EOI; - } - - x86_64_outb(PIC1_DATA_PORT, icw4); - x86_64_iowait(); - x86_64_outb(PIC2_DATA_PORT, icw4); - x86_64_iowait(); - - // mask all interrupts until they are enabled by the device driver - i8259_SetMask(0xFFFF); -} - -void i8259_SendEndOfInterrupt(int irq) -{ - if (irq >= 8) - x86_64_outb(PIC2_COMMAND_PORT, PIC_CMD_END_OF_INTERRUPT); - x86_64_outb(PIC1_COMMAND_PORT, PIC_CMD_END_OF_INTERRUPT); -} - -void i8259_Disable() -{ - i8259_SetMask(0xFFFF); -} - -void i8259_Mask(int irq) -{ - i8259_SetMask(g_PicMask | (1 << irq)); -} - -void i8259_Unmask(int irq) -{ - i8259_SetMask(g_PicMask & ~(1 << irq)); -} - -uint16_t i8259_ReadIrqRequestRegister() -{ - x86_64_outb(PIC1_COMMAND_PORT, PIC_CMD_READ_IRR); - x86_64_outb(PIC2_COMMAND_PORT, PIC_CMD_READ_IRR); - return ((uint16_t)x86_64_inb(PIC2_COMMAND_PORT)) | (((uint16_t)x86_64_inb(PIC2_COMMAND_PORT)) << 8); -} - -uint16_t i8259_ReadInServiceRegister() -{ - x86_64_outb(PIC1_COMMAND_PORT, PIC_CMD_READ_ISR); - x86_64_outb(PIC2_COMMAND_PORT, PIC_CMD_READ_ISR); - return ((uint16_t)x86_64_inb(PIC2_COMMAND_PORT)) | (((uint16_t)x86_64_inb(PIC2_COMMAND_PORT)) << 8); -} - -bool i8259_Probe() -{ - i8259_Disable(); - i8259_SetMask(0x1337); - return i8259_GetMask() == 0x1337; -} - -static const PICDriver g_PicDriver = { - .Name = "8259 PIC", - .Probe = &i8259_Probe, - .Initialize = &i8259_Configure, - .Disable = &i8259_Disable, - .SendEndOfInterrupt = &i8259_SendEndOfInterrupt, - .Mask = &i8259_Mask, - .Unmask = &i8259_Unmask, -}; - -const PICDriver* i8259_GetDriver() -{ - return &g_PicDriver; -} \ No newline at end of file diff --git a/src/arch/x86_64/sys/i8259.h b/src/arch/x86_64/sys/i8259.h deleted file mode 100644 index fbeb6ad..0000000 --- a/src/arch/x86_64/sys/i8259.h +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once - -#include "pic.h" - -const PICDriver* i8259_GetDriver(); -uint16_t i8259_GetMask(); -void i8259_SetMask(uint16_t newMask); - -void i8259_Configure(uint8_t offsetPic1, uint8_t offsetPic2, bool autoEoi); -void i8259_Disable(); \ No newline at end of file diff --git a/src/arch/x86_64/sys/ioapic.c b/src/arch/x86_64/sys/ioapic.c deleted file mode 100644 index 0bcc31c..0000000 --- a/src/arch/x86_64/sys/ioapic.c +++ /dev/null @@ -1,359 +0,0 @@ -#include "ioapic.h" -#include "apic.h" -#include "mm/vmm.h" -#include "mm/memory.h" -#include "libk/stdio.h" -#include /* uacpi_table_find_by_signature / uacpi_table_unref */ -#include "pic.h" - - -/* ══════════════════════════════════════════════════════════════════════════ - * ACPI MADT structures - * (Defined locally so we don't depend on uACPI's internal acpi.h layout.) - * ══════════════════════════════════════════════════════════════════════════ */ - -extern const PICDriver* g_Driver; // old pic driver - -bool g_IOAPIC = false; // IOAPIC enabled - -typedef struct { - uint8_t signature[4]; /* "APIC" */ - uint32_t length; - uint8_t revision; - uint8_t checksum; - uint8_t oem_id[6]; - uint8_t oem_table_id[8]; - uint32_t oem_revision; - uint32_t creator_id; - uint32_t creator_revision; - /* --- MADT-specific -------------------------------------------------- */ - uint32_t local_apic_addr; /* Default LAPIC physical address (32-bit) */ - uint32_t flags; /* bit 0 = dual 8259 PICs present */ - /* followed by variable-length Interrupt Controller Structure entries */ -} __attribute__((packed)) madt_t; - -typedef struct { - uint8_t type; - uint8_t length; -} __attribute__((packed)) madt_entry_hdr_t; - -/* Type 0: Processor Local APIC */ -typedef struct { - madt_entry_hdr_t hdr; - uint8_t uid; - uint8_t apic_id; - uint32_t flags; /* bit 0 = enabled */ -} __attribute__((packed)) madt_lapic_t; - -/* Type 1: I/O APIC */ -typedef struct { - madt_entry_hdr_t hdr; - uint8_t id; - uint8_t reserved; - uint32_t address; /* Physical MMIO base */ - uint32_t gsi_base; /* First GSI handled by this IOAPIC */ -} __attribute__((packed)) madt_ioapic_t; - -/* Type 2: Interrupt Source Override */ -typedef struct { - madt_entry_hdr_t hdr; - uint8_t bus; /* 0 = ISA */ - uint8_t source; /* ISA IRQ number */ - uint32_t gsi; /* Remapped GSI */ - uint16_t flags; /* bits [1:0] = polarity, bits [3:2] = trigger mode */ -} __attribute__((packed)) madt_iso_t; - -/* Type 4: Local APIC NMI */ -typedef struct { - madt_entry_hdr_t hdr; - uint8_t uid; /* 0xFF = all processors */ - uint16_t flags; - uint8_t lint; /* 0 or 1 */ -} __attribute__((packed)) madt_nmi_t; - -/* ══════════════════════════════════════════════════════════════════════════ - * Internal state - * ══════════════════════════════════════════════════════════════════════════ */ - -typedef struct { - volatile uint32_t *base; /* Virtual address of IOAPIC MMIO */ - uint32_t gsi_base; - uint32_t gsi_count; /* Number of redirection entries */ -} ioapic_t; - -static ioapic_t g_ioapics[IOAPIC_MAX]; -static int g_ioapic_count = 0; - -/* ISA interrupt source overrides (max 16 ISA IRQs) */ -typedef struct { - bool present; - uint32_t gsi; - bool active_low; - bool level; -} iso_t; -static iso_t g_iso[16]; /* indexed by ISA IRQ (source) */ - -/* ══════════════════════════════════════════════════════════════════════════ - * Low-level IOAPIC MMIO access - * - * The IOAPIC has two MMIO registers: - * base+0x00 IOREGSEL – index register (write which register to access) - * base+0x10 IOWIN – data window (read/write the selected register) - * ══════════════════════════════════════════════════════════════════════════ */ - -static uint32_t ioapic_read(const ioapic_t *io, uint8_t reg) { - *((volatile uint32_t *)(io->base + (0x00 >> 2))) = reg; - return *((volatile uint32_t *)(io->base + (0x10 >> 2))); -} - -static void ioapic_write(const ioapic_t *io, uint8_t reg, uint32_t val) { - *((volatile uint32_t *)(io->base + (0x00 >> 2))) = reg; - *((volatile uint32_t *)(io->base + (0x10 >> 2))) = val; -} - -/* ── Find which IOAPIC owns a given GSI ─────────────────────────────────── */ - -static ioapic_t *ioapic_for_gsi(uint32_t gsi) { - for (int i = 0; i < g_ioapic_count; i++) { - ioapic_t *io = &g_ioapics[i]; - if (gsi >= io->gsi_base && gsi < io->gsi_base + io->gsi_count) - return io; - } - return NULL; -} - -/* ══════════════════════════════════════════════════════════════════════════ - * Public API - * ══════════════════════════════════════════════════════════════════════════ */ - -uint32_t ioapic_gsi_for_isa_irq(uint8_t isa_irq) { - if (isa_irq < 16 && g_iso[isa_irq].present) - return g_iso[isa_irq].gsi; - return isa_irq; -} - -void ioapic_redirect(uint32_t gsi, uint8_t vector, uint8_t dest_lapic, - bool active_low, bool level, bool masked) { - ioapic_t *io = ioapic_for_gsi(gsi); - if (!io) { - printf("[IOAPIC] No IOAPIC for GSI %u\n", gsi); - return; - } - - uint8_t idx = (uint8_t)(gsi - io->gsi_base); - uint32_t lo = (uint32_t)vector - | IOAPIC_RTE_DM_FIXED - | (active_low ? IOAPIC_RTE_ACTIVE_LOW : 0) - | (level ? IOAPIC_RTE_LEVEL : 0) - | (masked ? IOAPIC_RTE_MASKED : 0); - uint32_t hi = (uint32_t)dest_lapic << 24; - - /* Write high word first, then low (avoids momentary spurious delivery) */ - ioapic_write(io, IOAPIC_REDTBL_HI(idx), hi); - ioapic_write(io, IOAPIC_REDTBL_LO(idx), lo); -} - -void ioapic_mask_gsi(uint32_t gsi) { - ioapic_t *io = ioapic_for_gsi(gsi); - if (!io) return; - uint8_t idx = (uint8_t)(gsi - io->gsi_base); - uint32_t lo = ioapic_read(io, IOAPIC_REDTBL_LO(idx)); - ioapic_write(io, IOAPIC_REDTBL_LO(idx), lo | IOAPIC_RTE_MASKED); -} - -void ioapic_unmask_gsi(uint32_t gsi) { - ioapic_t *io = ioapic_for_gsi(gsi); - if (!io) return; - uint8_t idx = (uint8_t)(gsi - io->gsi_base); - uint32_t lo = ioapic_read(io, IOAPIC_REDTBL_LO(idx)); - ioapic_write(io, IOAPIC_REDTBL_LO(idx), lo & ~IOAPIC_RTE_MASKED); -} - -/* ══════════════════════════════════════════════════════════════════════════ - * Initialisation - * ══════════════════════════════════════════════════════════════════════════ */ - -void ioapic_init(void) { - /* - * ── Step 1: Find the MADT via uACPI ────────────────────────────────── - * - * The MADT signature in ACPI is "APIC" (not "MADT"). - * uacpi_table_find_by_signature returns a handle whose .ptr field points - * to the raw table data in (HHDM-mapped) physical memory. - */ - struct uacpi_table madt_table; - uacpi_status st = uacpi_table_find_by_signature("APIC", &madt_table); - if (uacpi_unlikely_error(st)) { - printf("[IOAPIC] Could not find MADT: %s\n", uacpi_status_to_string(st)); - return; - } - - madt_t *madt = (madt_t *)madt_table.ptr; - printf("[IOAPIC] MADT @ virt 0x%lx len=%u lapic_addr=0x%08x\n", - (uint64_t)madt, madt->length, madt->local_apic_addr); - - /* - * ── Step 2: Walk the MADT entry list ───────────────────────────────── - * - * Entries start immediately after the fixed 44-byte MADT header and run - * until madt->length bytes from the table base. - */ - const uint8_t *entry_ptr = (const uint8_t *)madt + sizeof(madt_t); - const uint8_t *madt_end = (const uint8_t *)madt + madt->length; - - while (entry_ptr < madt_end) { - const madt_entry_hdr_t *hdr = (const madt_entry_hdr_t *)entry_ptr; - - if (hdr->length < 2) { - printf("[IOAPIC] MADT entry with length < 2, stopping parse\n"); - break; - } - - switch (hdr->type) { - - /* ── Type 0: Processor Local APIC ──────────────────────────────── */ - case 0: { - const madt_lapic_t *e = (const madt_lapic_t *)entry_ptr; - printf("[IOAPIC] MADT[0] Processor UID=%u LAPIC ID=%u flags=0x%x%s\n", - e->uid, e->apic_id, e->flags, - (e->flags & 1) ? "" : " (disabled)"); - break; - } - - /* ── Type 1: I/O APIC ───────────────────────────────────────────── */ - case 1: { - const madt_ioapic_t *e = (const madt_ioapic_t *)entry_ptr; - if (g_ioapic_count >= IOAPIC_MAX) { - printf("[IOAPIC] Too many IOAPICs, skipping ID=%u\n", e->id); - break; - } - - ioapic_t *io = &g_ioapics[g_ioapic_count]; - io->gsi_base = e->gsi_base; - - /* - * Map the IOAPIC MMIO page. Like the LAPIC, the HHDM covers the - * IOAPIC's physical address (typically 0xFEC00000) so we just add - * MEM_PHYS_OFFSET. Two MMIO registers are accessed (offsets 0 and - * 0x10) so one 4 KiB page is sufficient. - * - * TODO: Mark the page UC (cache-disable) in the PTE when VMM - * gains support for PAT / PCD flags. - */ - uint64_t phys = (uint64_t)e->address; - uintptr_t virt = (uintptr_t)phys + MEM_PHYS_OFFSET; - /* The HHDM loop in vmm_init already covered this range; if not, - * uncomment the explicit map call below: */ - /* vmm_map_page(kernel_pagemap, virt, phys, - PAGE_READ | PAGE_WRITE | PAGE_NO_EXECUTE, Size4KiB); */ - io->base = (volatile uint32_t *)virt; - - /* Read version register to discover number of redirection entries */ - uint32_t ver = ioapic_read(io, IOAPIC_REG_VER); - io->gsi_count = ((ver >> 16) & 0xFF) + 1; /* bits [23:16] = max entry index */ - - printf("[IOAPIC] MADT[1] ID=%u phys=0x%08x GSI base=%u entries=%u\n", - e->id, e->address, io->gsi_base, io->gsi_count); - - /* - * ── Mask every redirection entry ───────────────────────────── - * - * We keep all entries masked at boot time. Legacy ISA IRQs are - * handled by the i8259 → LAPIC LINT0 ExtINT path, not via the - * IOAPIC. PCI devices can be connected later with ioapic_redirect(). - */ - for (uint32_t n = 0; n < io->gsi_count; n++) { - ioapic_write(io, IOAPIC_REDTBL_HI(n), 0); - ioapic_write(io, IOAPIC_REDTBL_LO(n), IOAPIC_RTE_MASKED | 0xFF); - } - - g_ioapic_count++; - break; - } - - /* ── Type 2: Interrupt Source Override ──────────────────────────── */ - case 2: { - const madt_iso_t *e = (const madt_iso_t *)entry_ptr; - - /* flags bits [1:0]: 00/11 = conforms to bus (ISA = active high edge) - * 01 = active high, 11 = active low - * flags bits [3:2]: 00/11 = conforms to bus (ISA = edge) - * 01 = edge, 11 = level */ - bool active_low = ((e->flags & 0x3) == 3); - bool level = ((e->flags >> 2) & 0x3) == 3; - - printf("[IOAPIC] MADT[2] ISA IRQ %u -> GSI %u %s %s\n", - e->source, e->gsi, - active_low ? "active-low" : "active-high", - level ? "level" : "edge"); - - if (e->source < 16) { - g_iso[e->source].present = true; - g_iso[e->source].gsi = e->gsi; - g_iso[e->source].active_low = active_low; - g_iso[e->source].level = level; - } - break; - } - - /* ── Type 4: Local APIC NMI ─────────────────────────────────────── */ - case 4: { - const madt_nmi_t *e = (const madt_nmi_t *)entry_ptr; - printf("[IOAPIC] MADT[4] NMI UID=0x%02x LINT%u\n", - e->uid, e->lint); - break; - } - - default: - printf("[IOAPIC] MADT entry type=%u length=%u (skipped)\n", - hdr->type, hdr->length); - break; - } - - entry_ptr += hdr->length; - } - - uacpi_table_unref(&madt_table); - - printf("[IOAPIC] Init done: %d IOAPIC(s) found\n", g_ioapic_count); -} -void irq_redirect_to_apic(uint8_t isa_irq, uint8_t vector, - uint8_t dest_lapic, bool masked) -{ - if (isa_irq >= 16) { - printf("[IRQ] irq_redirect_to_apic: ISA IRQ %u out of range\n", isa_irq); - return; - } - - uint32_t gsi = ioapic_gsi_for_isa_irq(isa_irq); - - /* Get polarity/trigger from MADT ISO or use ISA defaults */ - bool active_low = false; - bool level = false; - - if (isa_irq < 16 && g_iso[isa_irq].present) { - active_low = g_iso[isa_irq].active_low; - level = g_iso[isa_irq].level; - } else { - /* Standard ISA: active-high, edge-triggered (except some like IRQ0/8) */ - if (isa_irq == 0 || isa_irq == 8) { - level = true; /* often level on real hardware */ - } - } - - /* Mask in the 8259 so it stops firing through LINT0 */ - if (g_Driver) { - g_Driver->Mask(isa_irq); - } - - /* Programme IOAPIC redirection entry */ - ioapic_redirect(gsi, vector, dest_lapic, - active_low, level, masked); - - printf("[IRQ] Redirected ISA IRQ %u -> GSI %u vector 0x%02x LAPIC %u %s %s\n", - isa_irq, gsi, vector, dest_lapic, - active_low ? "active-low" : "active-high", - level ? "level" : "edge"); - - g_IOAPIC = true; -} \ No newline at end of file diff --git a/src/arch/x86_64/sys/ioapic.h b/src/arch/x86_64/sys/ioapic.h deleted file mode 100644 index 15e7ab7..0000000 --- a/src/arch/x86_64/sys/ioapic.h +++ /dev/null @@ -1,76 +0,0 @@ -#pragma once -#include -#include - -/* - * I/O APIC driver - * - * The IOAPIC is found by parsing the ACPI MADT table via uACPI. - * All redirection entries are masked at init time; use ioapic_redirect() / - * ioapic_unmask_irq() to enable individual GSIs for PCI devices later. - * - * Legacy ISA IRQs (0-15) are intentionally left masked here because they are - * delivered through the i8259 → LAPIC LINT0 ExtINT path instead. - */ - -/* Maximum IOAPICs supported (1 is typical for QEMU/BOCHS) */ -#define IOAPIC_MAX 4 - -/* ── IOAPIC MMIO register indices (written to IOREGSEL) ─────────────────── */ -#define IOAPIC_REG_ID 0x00 /* APIC ID */ -#define IOAPIC_REG_VER 0x01 /* Version / max redirection entries */ -#define IOAPIC_REG_ARB 0x02 /* Arbitration ID */ -#define IOAPIC_REDTBL_LO(n) (0x10 + (n) * 2) /* Redirection entry n, low */ -#define IOAPIC_REDTBL_HI(n) (0x10 + (n) * 2 + 1) /* Redirection entry n, high */ - -/* ── Redirection table entry flags (low 32-bit word) ────────────────────── */ -#define IOAPIC_RTE_MASKED (1u << 16) /* 1 = masked (disabled) */ -#define IOAPIC_RTE_LEVEL (1u << 15) /* 1 = level-triggered, 0 = edge */ -#define IOAPIC_RTE_ACTIVE_LOW (1u << 13) /* 1 = active low, 0 = active high */ -#define IOAPIC_RTE_LOGICAL (1u << 11) /* 1 = logical destination mode */ -#define IOAPIC_RTE_DM_FIXED (0u << 8) /* Fixed delivery */ -#define IOAPIC_RTE_DM_NMI (4u << 8) /* NMI */ -/* bits [7:0] hold the IDT vector */ - -/* ── Public API ──────────────────────────────────────────────────────────── */ - -/** - * ioapic_init - Parse the ACPI MADT, locate all I/O APICs and their GSI - * bases, record any ISA interrupt source overrides, then mask - * every redirection entry. - * - * Must be called after uacpi_initialize() (needs MADT accessible). - */ -void ioapic_init(void); - -/** - * ioapic_redirect - Programme a single redirection table entry. - * - * @gsi Global System Interrupt number (from MADT or known fixed GSI). - * @vector IDT vector the CPU will see (e.g. 0x20 + irq_number). - * @dest_lapic Physical LAPIC ID of the target CPU. - * @active_low true if device uses active-low polarity. - * @level true if device uses level-triggered signalling. - * @masked true to programme the entry but leave it masked. - */ -void ioapic_redirect(uint32_t gsi, uint8_t vector, uint8_t dest_lapic, - bool active_low, bool level, bool masked); - -/** - * ioapic_mask_gsi - Mask (disable) a GSI redirection entry. - * ioapic_unmask_gsi - Unmask (enable) a GSI redirection entry. - */ -void ioapic_mask_gsi(uint32_t gsi); -void ioapic_unmask_gsi(uint32_t gsi); - -/** - * ioapic_gsi_for_isa_irq - Look up the GSI that corresponds to an ISA IRQ - * number, accounting for any MADT interrupt source - * overrides (e.g. ISA IRQ 0 → GSI 2 on BOCHS). - * - * Returns the GSI, or the IRQ number itself if no override is defined. - */ -uint32_t ioapic_gsi_for_isa_irq(uint8_t isa_irq); - -void irq_redirect_to_apic(uint8_t isa_irq, uint8_t vector, uint8_t dest_lapic, bool masked); - diff --git a/src/arch/x86_64/sys/irq.c b/src/arch/x86_64/sys/irq.c deleted file mode 100644 index 0034a35..0000000 --- a/src/arch/x86_64/sys/irq.c +++ /dev/null @@ -1,112 +0,0 @@ -#include "irq.h" -#include "i8259.h" -#include "arch/x86_64/cpu/io.h" -#include -#include "libk/arrays.h" -#include "libk/stdio.h" -#include "libk/debug.h" -#include "apic.h" -#include "ioapic.h" - -#define PIC_REMAP_OFFSET 0x20 -#define MODULE "PIC" - -IRQHandler g_IRQHandlers[16]; -static IRQHandler g_APICHandlers[256]; -const PICDriver* g_Driver = NULL; - -extern bool g_IOAPIC; - - - -void x86_64_IRQ_Handler(Registers *regs) -{ - int irq = regs->interrupt - PIC_REMAP_OFFSET; - - g_Driver->SendEndOfInterrupt(irq); - - if (g_IRQHandlers[irq] != NULL) - { - // handle IRQ - g_IRQHandlers[irq](regs); - } - else - { - log_warn(MODULE, "Unhandled IRQ %d...", irq); - } - - - - - -} - -void x86_64_APIC_IRQ_Handler(Registers* regs) -{ - uint8_t vector = regs->interrupt; - lapic_eoi(); - - if (g_APICHandlers[vector] != NULL) { - g_APICHandlers[vector](regs); - } else { - log_warn("APIC", "Unhandled vector 0x%02x", vector); - } - - // ← This is the key difference from PIC! -} - - -void x86_64_IRQ_Initialize(void) -{ - const PICDriver* drivers[] = { - i8259_GetDriver(), - }; - - for (int i = 0; i < SIZE(drivers); i++) { - if (drivers[i]->Probe()) { - g_Driver = drivers[i]; - } - } - - if (g_Driver == NULL) { - log_warn(MODULE, "No PIC found!"); - return; - } - - log_info(MODULE, "Found %s PIC.", g_Driver->Name); - g_Driver->Initialize(PIC_REMAP_OFFSET, PIC_REMAP_OFFSET + 8, false); - - - // register ISR handlers for each of the 16 irq lines - for (int i = 0; i < 16; i++) - x86_64_ISR_RegisterHandler(PIC_REMAP_OFFSET + i, x86_64_IRQ_Handler); - - g_Driver->Unmask(0); -} - -void x86_64_IRQ_RegisterHandler(int irq, IRQHandler handler) -{ - g_IRQHandlers[irq] = handler; -} - -void x86_64_APIC_IRQ_RegisterHandler(uint8_t vector, IRQHandler handler) -{ - if (vector < 32) { - printf("[APIC] Warning: vector %u is in reserved range!\n", vector); - } - g_APICHandlers[vector] = handler; - x86_64_ISR_RegisterHandler(vector, x86_64_APIC_IRQ_Handler); -} - -void x86_64_APIC_IRQ_RedirectAndRegister(uint8_t isa_irq, - uint8_t vector, - IRQHandler handler) -{ - x86_64_APIC_IRQ_RegisterHandler(vector, handler); - irq_redirect_to_apic(isa_irq, vector, lapic_id(), false); -} - -void x86_64_IRQ_Unmask(int irq) -{ - if (g_Driver) g_Driver->Unmask(irq); -} \ No newline at end of file diff --git a/src/arch/x86_64/sys/irq.h b/src/arch/x86_64/sys/irq.h deleted file mode 100644 index 4684e56..0000000 --- a/src/arch/x86_64/sys/irq.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once -#include "arch/x86_64/boot/isr.h" - - -typedef void (*IRQHandler)(Registers* regs); - -void x86_64_IRQ_Initialize(); -void x86_64_IRQ_RegisterHandler(int irq, IRQHandler handler); -void x86_64_IRQ_Unmask(int irq); -void x86_64_APIC_IRQ_RegisterHandler(uint8_t vector, IRQHandler handler); -void x86_64_APIC_IRQ_RedirectAndRegister(uint8_t isa_irq, - uint8_t vector, - IRQHandler handler); \ No newline at end of file diff --git a/src/arch/x86_64/sys/pic.c b/src/arch/x86_64/sys/pic.c new file mode 100644 index 0000000..b300042 --- /dev/null +++ b/src/arch/x86_64/sys/pic.c @@ -0,0 +1,29 @@ +#include "pic.h" +#include "arch/x86_64/cpu/io.h" +#include +#include "arch/x86_64/fw/madt.h" + +static void pic_disable(void) { + x86_64_outb(0xA1, 0xFF); + x86_64_outb(0x21, 0xFF); +} + +void pic_init(void) { + // There isn't any PIC + if (!(madt->flags & 1)) { + return; + } + // Remap the PIC + x86_64_outb(0x20, 0x11); + x86_64_outb(0xA0, 0x11); + x86_64_outb(0x21, 0x20); + x86_64_outb(0xA1, 0x28); + x86_64_outb(0x21, 4); + x86_64_outb(0xA1, 2); + x86_64_outb(0x21, 1); + x86_64_outb(0xA1, 1); + x86_64_outb(0x21, 0); + x86_64_outb(0xA1, 0); + + pic_disable(); +} diff --git a/src/arch/x86_64/sys/pic.h b/src/arch/x86_64/sys/pic.h index 00a9fbd..d35f757 100644 --- a/src/arch/x86_64/sys/pic.h +++ b/src/arch/x86_64/sys/pic.h @@ -1,14 +1,3 @@ #pragma once -#include -#include - -typedef struct { - const char* Name; - bool (*Probe)(); - void (*Initialize)(uint8_t offsetPic1, uint8_t offsetPic2, bool autoEoi); - void (*Disable)(); - void (*SendEndOfInterrupt)(int irq); - void (*Mask)(int irq); - void (*Unmask)(int irq); -} PICDriver; \ No newline at end of file +void pic_init(void); \ No newline at end of file diff --git a/src/arch/x86_64/sys/pit.c b/src/arch/x86_64/sys/pit.c deleted file mode 100644 index 7b90331..0000000 --- a/src/arch/x86_64/sys/pit.c +++ /dev/null @@ -1,74 +0,0 @@ -#include "pit.h" -#include "irq.h" -#include "arch/x86_64/cpu/io.h" -#include -#include "libk/stdio.h" -#include "e9.h" -#include "limine.h" -#include "apic.h" -#include "sched/scheduler.h" - -__attribute__((used, section(".limine_requests"))) -volatile struct limine_date_at_boot_request boot_request = { - .id = LIMINE_DATE_AT_BOOT_REQUEST_ID, - .revision = 6, -}; - -#define PIT_BASE_FREQUENCY 1193182 - -#define PIT_COMMAND_PORT 0x43 -#define PIT_CHANNEL0_PORT 0x40 - -volatile uint64_t g_Ticks = 0; -volatile uint64_t ticks = 0; -uint64_t g_Unixseconds = 0; -extern bool g_IOAPIC; -/* ========================= */ -/* IRQ0 Handler (Timer Tick) */ -/* ========================= */ -void PIT_IRQ_Handler(Registers* regs) -{ - (void)regs; - g_Ticks++; - ticks++; - if (ticks >= 1000) { - g_Unixseconds++; - ticks = 0; - } - - if (g_IOAPIC == true) { - lapic_eoi(); - } - - sched_tick(); -} - -/* ========================= */ -/* Initialize PIT */ -/* ========================= */ -void x86_64_PIT_Initialize(uint32_t frequency) -{ - uint16_t divisor = (uint16_t)(PIT_BASE_FREQUENCY / frequency); - - printf("PIT divisor = %u\n", divisor); - // Send command byte: - // Channel 0 | lobyte/hibyte | mode 3 (square wave) | binary - x86_64_outb(PIT_COMMAND_PORT, 0x36); - - // Send divisor (low byte then high byte) - x86_64_outb(PIT_CHANNEL0_PORT, divisor & 0xFF); - x86_64_outb(PIT_CHANNEL0_PORT, (divisor >> 8) & 0xFF); - - g_Unixseconds = boot_request.response->timestamp; - - // Register IRQ0 handler (IRQ0 = timer) - x86_64_IRQ_RegisterHandler(0, PIT_IRQ_Handler); -} - -/* ========================= */ -/* Get tick count */ -/* ========================= */ -uint64_t PIT_GetTicks(void) -{ - return g_Ticks; -} \ No newline at end of file diff --git a/src/arch/x86_64/sys/pit.h b/src/arch/x86_64/sys/pit.h index 57ad5f9..bec3083 100644 --- a/src/arch/x86_64/sys/pit.h +++ b/src/arch/x86_64/sys/pit.h @@ -1,13 +1,11 @@ #pragma once #include -#include "irq.h" #include "arch/x86_64/cpu/io.h" -#include "libk/stdio.h" #include "e9.h" -extern volatile uint64_t g_Ticks; -extern uint64_t g_Unixseconds; +#define PIT_DIVIDEND ((uint64_t)1193182) -void x86_64_PIT_Initialize(uint32_t frequency); -uint64_t PIT_GetTicks(void); -void PIT_IRQ_Handler(Registers* regs); \ No newline at end of file +uint16_t pit_counter_value(void); +void pit_set_reload_value(uint16_t new_count); +void pit_set_frequency(uint64_t frequency); +void pit_init(); diff --git a/src/arch/x86_64/sys/prcb.h b/src/arch/x86_64/sys/prcb.h new file mode 100644 index 0000000..24befed --- /dev/null +++ b/src/arch/x86_64/sys/prcb.h @@ -0,0 +1,24 @@ +#pragma once + +#include +#include +#include "arch/x86_64/boot/gdt.h" + +struct prcb { + uint64_t cpu_number; + uint64_t kernel_stack; + uint64_t user_stack; + struct thread *running_thread; + uint64_t sched_ticks; + struct tss cpu_tss; + uint32_t lapic_id; + size_t fpu_storage_size; + void (*fpu_save)(void *); + void (*fpu_restore)(void *); + uint32_t tick_in_10ms; +}; + +extern struct prcb *prcbs; + +struct prcb *prcb_return_current_cpu(void); +size_t prcb_return_installed_cpus(void); diff --git a/src/arch/x86_64/sys/timer.c b/src/arch/x86_64/sys/timer.c new file mode 100644 index 0000000..b4c04f2 --- /dev/null +++ b/src/arch/x86_64/sys/timer.c @@ -0,0 +1,127 @@ +#include +#include +#include "pit.h" +#include +#include "arch/x86_64/cpu/io.h" +#include "libk/time.h" +#include "arch/x86_64/asm/asm.h" +#include "mm/vmm.h" +#include "hpet.h" +#include "arch/x86_64/bus/mmio.h" +#include "libk/debug.h" + +static struct hpet_table *hpet_table = NULL; +static struct hpet *hpet = NULL; +static uint32_t clk = 0; +static bool timer_installed_b = false; +extern uint32_t smp_bsp_lapic_id; + + + +static volatile struct limine_boot_time_request boot_time_request = { + .id = LIMINE_BOOT_TIME_REQUEST, .revision = 0}; + + +bool hpet_init(void) { + hpet_table = acpi_find_sdt("HPET"); + if (!hpet_table) { + kprintf("HPET: No HPET found!\n"); + return false; + } + hpet = (struct hpet *)(hpet_table->address.base + MEM_PHYS_OFFSET); + + kprintf("HPET: HPET at %p\n", (void *)hpet); + + clk = hpet->general_capabilities >> 32; + + /* + * General Configuration Register + * 0 - main counter is halted, timer interrupts are disabled + * 1 - main counter is running, timer interrupts are allowed if enabled + */ + mmoutq(&hpet->general_configuration, 0); + mmoutq(&hpet->main_counter_value, 0); + mmoutq(&hpet->general_configuration, 1); + timer_installed_b = true; + return true; +} + +uint64_t hpet_counter_value(void) { + return mminq(&hpet->main_counter_value); +} + +void hpet_sleep(uint64_t us) { + uint64_t target = hpet_counter_value() + (us * 1000000000) / clk; + while (hpet_counter_value() < target) + pause(); +} + +void pit_set_frequency(uint64_t frequency) { + uint64_t new_divisor = PIT_DIVIDEND / frequency; + if (PIT_DIVIDEND % frequency > frequency / 2) { + new_divisor++; + } + pit_set_reload_value((uint16_t)new_divisor); +} + +void pit_set_reload_value(uint16_t new_count) { + x86_64_outb(0x43, 0x34); + x86_64_outb(0x40, (uint8_t)new_count); + x86_64_outb(0x40, (uint8_t)(new_count >> 8)); +} + +void pit_init(void) { + timer_installed_b = true; + pit_set_reload_value(0xffff); + pit_set_frequency(TIMER_FREQ); +} + + +uint16_t pit_counter_value(void) { + x86_64_outb(0x43, 0x00); + uint8_t lo = x86_64_inb(0x40); + uint8_t hi = x86_64_inb(0x40); + return ((uint16_t)hi << 8) | lo; +} + + +void pit_sleep(uint64_t ms) { + uint64_t target = pit_counter_value() + ms; + while (pit_counter_value() < target) + pause(); +} + + + +void timer_init(void) { + struct limine_boot_time_response *boot_time_resp = + boot_time_request.response; + + time_realtime.tv_sec = boot_time_resp->boot_time; + + if (hpet_init()) { + return; + } + pit_init(); +} + + + +bool timer_installed(void) { + return timer_installed_b; +} + +void timer_sleep(uint64_t ms) { + if (hpet_table) { + return hpet_sleep(ms * 1000); + } + pit_sleep(ms); +} + +uint64_t timer_count(void) { + if (hpet_table) { + return (hpet_counter_value() * clk) / (1000000000000); + } + return pit_counter_value(); +} + diff --git a/src/arch/x86_64/sys/timer.h b/src/arch/x86_64/sys/timer.h new file mode 100644 index 0000000..2cb7b4e --- /dev/null +++ b/src/arch/x86_64/sys/timer.h @@ -0,0 +1,10 @@ +#pragma once +#include +#include + +bool timer_installed(void); +void timer_init(void); +void timer_sleep(uint64_t ms); +uint64_t timer_count(void); +void timer_stop_sched(void); +void timer_sched_oneshot(uint8_t isr, uint32_t us); \ No newline at end of file diff --git a/src/arch/x86_64/sys/tsc.c b/src/arch/x86_64/sys/tsc.c index b9ff044..2f7aacf 100644 --- a/src/arch/x86_64/sys/tsc.c +++ b/src/arch/x86_64/sys/tsc.c @@ -1,49 +1,58 @@ #include #include "arch/x86_64/sys/pit.h" #include "arch/x86_64/cpu/io.h" -#include "libk/stdio.h" #include "tsc.h" - -extern uint64_t PIT_GetTicks(void); +#include "libk/debug.h" +#include "arch/x86_64/asm/asm.h" +#include "libk/time.h" volatile uint64_t tsc_cycles_per_us = 0; -uint64_t rdtsc(void) +inline uint64_t rdtsc(void) { uint32_t lo, hi; + __asm__ volatile ("rdtsc" : "=a"(lo), "=d"(hi)); + return ((uint64_t)hi << 32) | lo; } - void calibrate_tsc(void) { - // Use a stable PIT rate for calibration - const uint32_t pit_freq = 1000; // 1000 Hz = 1ms per tick - const uint32_t calibration_ticks = 1000; // 1 second total + // We measure over one full PIT cycle (~54.9 ms at max reload) + const uint16_t pit_start = 0xFFFF; + pit_set_reload_value(pit_start); - - // Wait for PIT to stabilize - uint64_t start_ticks = PIT_GetTicks(); - while (PIT_GetTicks() == start_ticks) - ; - + // latch initial state uint64_t start_tsc = rdtsc(); - uint64_t target_ticks = start_ticks + calibration_ticks; + uint16_t start = pit_counter_value(); - // Wait for known number of PIT ticks - while (PIT_GetTicks() < target_ticks) - ; + uint16_t current; + + // wait for wrap-around (start > current) + do { + current = pit_counter_value(); + pause(); + } while (current <= start); uint64_t end_tsc = rdtsc(); - // Compute elapsed time - uint64_t tsc_delta = end_tsc - start_tsc; + uint16_t ticks = (start - current); - uint64_t elapsed_us = 1000000ULL; + // PIT frequency = 1193182 Hz + // so each tick = 1 / 1193182 seconds + // convert PIT ticks -> microseconds: + + uint64_t elapsed_us = + (ticks * 1000000ULL) / PIT_DIVIDEND; + + uint64_t tsc_delta = end_tsc - start_tsc; tsc_cycles_per_us = tsc_delta / elapsed_us; - printf("TSC calibration complete: %lu cycles/us\n", tsc_cycles_per_us); + kprintf("TSC calibrated: %lu cycles/us\n", tsc_cycles_per_us); + + // restore PIT frequency for system timer + pit_set_frequency(TIMER_FREQ); } \ No newline at end of file diff --git a/src/arch/x86_64/sys/tsc.h b/src/arch/x86_64/sys/tsc.h index ae61562..3f5fd91 100644 --- a/src/arch/x86_64/sys/tsc.h +++ b/src/arch/x86_64/sys/tsc.h @@ -1,7 +1,6 @@ #include #include "arch/x86_64/sys/pit.h" #include "arch/x86_64/cpu/io.h" -#include "libk/stdio.h" uint64_t rdtsc(void); diff --git a/src/drivers/fb/fb.c b/src/drivers/fb/fb.c new file mode 100644 index 0000000..ace5b0c --- /dev/null +++ b/src/drivers/fb/fb.c @@ -0,0 +1,106 @@ +#include "terminal/backends/fb.h" +#include "libk/debug.h" +#include "fb.h" +#include "mm/memory.h" +#include "mp/spinlock.h" +#include "mm/slab.h" +#include "libk/string.h" + +uint8_t framebuffer_initialised = 0; + +struct framebuffer framebuff; +static struct flanterm_context *ctx; + +static void kffree(void *addr, size_t sz) { + (void)sz; + kfree(addr); +} + +void framebuffer_init(struct framebuffer *fb) { + framebuff.address = fb->address; + framebuff.pitch = fb->pitch; + framebuff.bpp = fb->bpp; + framebuff.width = fb->width; + framebuff.height = fb->height; + framebuff.tex_x = fb->tex_x; + framebuff.tex_y = fb->tex_y; + framebuff.tex_color = fb->tex_color; + framebuff.tex_height = fb->height / 16; + framebuff.tex_width = fb->width / 8; + framebuff.back_address = kmalloc(framebuff.pitch * framebuff.height); + framebuff.bg_color = fb->bg_color; + + framebuff.color_masks[0] = fb->color_masks[0]; + framebuff.color_masks[1] = fb->color_masks[1]; + framebuff.color_masks[2] = fb->color_masks[2]; + + ctx = flanterm_fb_init(kmalloc, kffree, (void *)fb->address, fb->width, + fb->height, fb->pitch, fb->color_masks[0].length, + fb->color_masks[0].offset, fb->color_masks[1].length, + fb->color_masks[1].offset, fb->color_masks[2].length, + fb->color_masks[2].offset, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, 0, 0, 1, 1, 1, 0); + + framebuff.ctx = ctx; + + framebuffer_clear(fb->tex_color, fb->bg_color); + + framebuffer_initialised = 1; +} + +void framebuffer_set_callback(void (*callback)(struct flanterm_context *, + uint64_t, uint64_t, uint64_t, + uint64_t)) { + if (ctx) { + ctx->callback = callback; + } +} + +static void ultoa_strcat(char *dest, uint64_t src, bool add_semicolon) { + char buf[65]; + strcat(dest, ultoa(src, buf, 10)); + if (add_semicolon == true) { + strcat(dest, ";"); + } +} + +// Format: 0xAARRGGBB, alpha is ignored +void framebuffer_clear(uint32_t foreground, uint32_t background) { + uint8_t red_fg = (foreground & 0xFF0000) >> 16; + uint8_t green_fg = (foreground & 0xFF00) >> 8; + uint8_t blue_fg = foreground & 0xFF; + + uint8_t red_bg = (background & 0xFF0000) >> 16; + uint8_t green_bg = (background & 0xFF00) >> 8; + uint8_t blue_bg = background & 0xFF; + + char result[128] = "\033[38;2;"; + + ultoa_strcat(result, red_fg, true); + ultoa_strcat(result, green_fg, true); + ultoa_strcat(result, blue_fg, true); + + strcat(result, "48;2;"); + + ultoa_strcat(result, red_bg, true); + ultoa_strcat(result, green_bg, true); + ultoa_strcat(result, blue_bg, false); + + strcat(result, "m"); + + flanterm_write(ctx, result, strlen(result)); + flanterm_write(ctx, "\033[2J", 5); + flanterm_write(ctx, "\033[H", 4); +} + +void framebuffer_putchar(char c) { + flanterm_write(ctx, &c, 1); +} + +void framebuffer_puts(char *string) { + flanterm_write(ctx, string, strlen(string)); +} + +struct framebuffer *framebuffer_info(void) { + return &framebuff; +} diff --git a/src/drivers/fb/fb.h b/src/drivers/fb/fb.h new file mode 100644 index 0000000..2e74021 --- /dev/null +++ b/src/drivers/fb/fb.h @@ -0,0 +1,99 @@ +#include "../fb/terminal/flanterm.h" +#include "fs/devtmpfs.h" +#include +#include + +// taken from linux/include/uapi/linux/fb.h +struct fb_bitfield { + uint32_t offset; /* beginning of bitfield */ + uint32_t length; /* length of bitfield */ + uint32_t msb_right; /* != 0 : Most significant bit is */ + /* right */ +}; + +struct fb_var_screeninfo { + uint32_t xres; /* visible resolution */ + uint32_t yres; + uint32_t xres_virtual; /* virtual resolution */ + uint32_t yres_virtual; + uint32_t xoffset; /* offset from virtual to visible */ + uint32_t yoffset; /* resolution */ + + uint32_t bits_per_pixel; /* guess what */ + uint32_t grayscale; /* 0 = color, 1 = grayscale, */ + /* >1 = FOURCC */ + struct fb_bitfield red; /* bitfield in fb mem if true color, */ + struct fb_bitfield green; /* else only length is significant */ + struct fb_bitfield blue; + struct fb_bitfield transp; /* transparency */ + + uint32_t nonstd; /* != 0 Non standard pixel format */ + + uint32_t activate; /* see FB_ACTIVATE_* */ + + uint32_t height; /* height of picture in mm */ + uint32_t width; /* width of picture in mm */ + + uint32_t accel_flags; /* (OBSOLETE) see fb_info.flags */ + + /* Timing: All values in pixclocks, except pixclock (of course) */ + uint32_t pixclock; /* pixel clock in ps (pico seconds) */ + uint32_t left_margin; /* time from sync to picture */ + uint32_t right_margin; /* time from picture to sync */ + uint32_t upper_margin; /* time from sync to picture */ + uint32_t lower_margin; + uint32_t hsync_len; /* length of horizontal sync */ + uint32_t vsync_len; /* length of vertical sync */ + uint32_t sync; /* see FB_SYNC_* */ + uint32_t vmode; /* see FB_VMODE_* */ + uint32_t rotate; /* angle we rotate counter clockwise */ + uint32_t colorspace; /* colorspace for FOURCC-based modes */ + uint32_t reserved[4]; /* Reserved for future compatibility */ +}; + +struct fb_fix_screeninfo { + char id[16]; /* identification string eg "TT Builtin" */ + unsigned long smem_start; /* Start of frame buffer mem */ + /* (physical address) */ + uint32_t smem_len; /* Length of frame buffer mem */ + uint32_t type; /* see FB_TYPE_* */ + uint32_t type_aux; /* Interleave for interleaved Planes */ + uint32_t visual; /* see FB_VISUAL_* */ + uint16_t xpanstep; /* zero if no hardware panning */ + uint16_t ypanstep; /* zero if no hardware panning */ + uint16_t ywrapstep; /* zero if no hardware ywrap */ + uint32_t line_length; /* length of a line in bytes */ + unsigned long mmio_start; /* Start of Memory Mapped I/O */ + /* (physical address) */ + uint32_t mmio_len; /* Length of Memory Mapped I/O */ + uint32_t accel; /* Indicate to driver which */ + /* specific chip/card we have */ + uint16_t capabilities; /* see FB_CAP_* */ + uint16_t reserved[2]; /* Reserved for future compatibility */ +}; + +struct framebuffer { + uint32_t *address; + uint32_t *back_address; + size_t pitch, bpp; + uint16_t width, height; + size_t tex_x, tex_y; + uint16_t tex_width, tex_height; + uint32_t tex_color; + uint32_t bg_color; + struct fb_bitfield color_masks[3]; + struct flanterm_context *ctx; +}; + +extern struct framebuffer framebuff; + +void framebuffer_init(struct framebuffer *fb); +void framebuffer_set_callback(void (*callback)(struct flanterm_context *, + uint64_t, uint64_t, uint64_t, + uint64_t)); +void framebuffer_clear(uint32_t foreground, uint32_t background); +void framebuffer_putchar(char c); +void framebuffer_puts(char *string); +struct framebuffer *framebuffer_info(void); + +void fbdev_init(void); diff --git a/src/drivers/fb/fbdev.c b/src/drivers/fb/fbdev.c new file mode 100644 index 0000000..ac2c53f --- /dev/null +++ b/src/drivers/fb/fbdev.c @@ -0,0 +1,128 @@ +#include "libk/debug.h" +#include "libk/errno.h" +#include "fb.h" +#include "fs/vfs.h" +#include "mm/memory.h" +#include "mm/mmap.h" + +#define FB_ACTIVATE_NOW 0 /* set values immediately (or vbl)*/ +#define FB_ACTIVATE_NXTOPEN 1 /* activate on next open */ +#define FB_ACTIVATE_TEST 2 /* don't set, round up impossible */ +#define FB_ACTIVATE_MASK 15 + +#define FB_VMODE_NONINTERLACED 0 /* non interlaced */ +#define FB_VMODE_INTERLACED 1 /* interlaced */ +#define FB_VMODE_DOUBLE 2 /* double scan */ +#define FB_VMODE_ODD_FLD_FIRST 4 /* interlaced: top line first */ +#define FB_VMODE_MASK 255 + +#define FB_TYPE_PACKED_PIXELS 0 /* Packed Pixels */ +#define FB_TYPE_PLANES 1 /* Non interleaved planes */ +#define FB_TYPE_INTERLEAVED_PLANES 2 /* Interleaved planes */ +#define FB_TYPE_TEXT 3 /* Text/attributes */ +#define FB_TYPE_VGA_PLANES 4 /* EGA/VGA planes */ +#define FB_TYPE_FOURCC 5 /* Type identified by a V4L2 FOURCC */ + +#define FB_VISUAL_MONO01 0 /* Monochr. 1=Black 0=White */ +#define FB_VISUAL_MONO10 1 /* Monochr. 1=White 0=Black */ +#define FB_VISUAL_TRUECOLOR 2 /* True color */ +#define FB_VISUAL_PSEUDOCOLOR 3 /* Pseudo color (like atari) */ +#define FB_VISUAL_DIRECTCOLOR 4 /* Direct color */ +#define FB_VISUAL_STATIC_PSEUDOCOLOR 5 /* Pseudo color readonly */ +#define FB_VISUAL_FOURCC 6 /* Visual identified by a V4L2 FOURCC */ + +#define FBIOGET_VSCREENINFO 0x4600 +#define FBIOPUT_VSCREENINFO 0x4601 +#define FBIOGET_FSCREENINFO 0x4602 + +#define FBIOBLANK 0x4611 + +static struct fb_var_screeninfo framebuffer_var_info = {0}; +static struct fb_fix_screeninfo framebuffer_fix_info = {0}; +static struct resource *framebuff_res; + +static ssize_t fbdev_write(struct resource *this, + struct f_description *description, const void *buf, + off_t offset, size_t count) { + (void)description; + spinlock_acquire_or_wait(&this->lock); + memcpy((void *)(framebuff.address + offset), buf, count); + spinlock_drop(&this->lock); + return 0; +} + +static void *fbdev_mmap(struct resource *this, size_t file_page, int flags) { + (void)flags; + spinlock_acquire_or_wait(&this->lock); + size_t offset = file_page * PAGE_SIZE; + + if (offset >= (framebuff.height * framebuff.pitch)) { + return NULL; + } + + spinlock_drop(&this->lock); + + return (void *)(((uint64_t)(framebuff.address) - MEM_PHYS_OFFSET) + offset); +} + +static int fbdev_ioctl(struct resource *this, struct f_description *description, + uint64_t request, uint64_t arg) { + switch (request) { + case FBIOGET_VSCREENINFO: + *(struct fb_var_screeninfo *)arg = framebuffer_var_info; + return 0; + case FBIOGET_FSCREENINFO: + *(struct fb_fix_screeninfo *)arg = framebuffer_fix_info; + return 0; + case FBIOPUT_VSCREENINFO: + spinlock_acquire_or_wait(&this->lock); + framebuffer_var_info = *(struct fb_var_screeninfo *)arg; + spinlock_drop(&this->lock); + return 0; + case FBIOBLANK: + return 0; + } + + return resource_default_ioctl(this, description, request, arg); +} + +void fbdev_init(void) { + extern uint8_t framebuffer_initialised; + if (!framebuffer_initialised) + return; + + framebuffer_fix_info.smem_len = framebuff.pitch * framebuff.height; + framebuffer_fix_info.mmio_len = framebuff.pitch * framebuff.height; + framebuffer_fix_info.line_length = framebuff.pitch; + framebuffer_fix_info.type = FB_TYPE_PACKED_PIXELS; + framebuffer_fix_info.visual = FB_VISUAL_TRUECOLOR; + + framebuffer_var_info.xres = framebuff.width; + framebuffer_var_info.yres = framebuff.height; + framebuffer_var_info.xres_virtual = framebuff.width; + framebuffer_var_info.yres_virtual = framebuff.height; + framebuffer_var_info.bits_per_pixel = framebuff.bpp; + framebuffer_var_info.red = framebuff.color_masks[0]; + framebuffer_var_info.green = framebuff.color_masks[1]; + framebuffer_var_info.blue = framebuff.color_masks[2]; + framebuffer_var_info.activate = FB_ACTIVATE_NOW; + framebuffer_var_info.vmode = FB_VMODE_NONINTERLACED; + framebuffer_var_info.width = -1; + framebuffer_var_info.height = -1; + + framebuff_res = resource_create(sizeof(struct resource)); + + framebuff_res->ioctl = fbdev_ioctl; + framebuff_res->write = fbdev_write; + framebuff_res->mmap = fbdev_mmap; + framebuff_res->can_mmap = true; + + framebuff_res->stat.st_size = 0; + framebuff_res->stat.st_blocks = 0; + framebuff_res->stat.st_blksize = 4096; + framebuff_res->stat.st_rdev = resource_create_dev_id(); + framebuff_res->stat.st_mode = 0666 | S_IFCHR; + + devtmpfs_add_device(framebuff_res, "fbdev"); + vfs_symlink(vfs_root, "/dev/fbdev", "/dev/fb0"); +} diff --git a/src/drivers/fb/terminal/.gitignore b/src/drivers/fb/terminal/.gitignore new file mode 100644 index 0000000..cc62e43 --- /dev/null +++ b/src/drivers/fb/terminal/.gitignore @@ -0,0 +1,2 @@ +*.d +*.o \ No newline at end of file diff --git a/src/drivers/fb/terminal/LICENSE b/src/drivers/fb/terminal/LICENSE new file mode 100644 index 0000000..bc639fe --- /dev/null +++ b/src/drivers/fb/terminal/LICENSE @@ -0,0 +1,22 @@ +Copyright (C) 2022-2024 mintsuki and contributors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/src/drivers/fb/terminal/README.md b/src/drivers/fb/terminal/README.md new file mode 100644 index 0000000..37cae29 --- /dev/null +++ b/src/drivers/fb/terminal/README.md @@ -0,0 +1,43 @@ +# Flanterm + +Flanterm is a fast and reasonably complete terminal emulator with support for +multiple output backends. Included is a fast framebuffer backend. + +### Quick usage + +To quickly set up and use a framebuffer Flanterm instance, it is possible to +use the `flanterm_fb_init()` function as such: +```c +#include +#include + +struct flanterm_context *ft_ctx = flanterm_fb_init( + NULL, + NULL, + framebuffer_ptr, width, height, pitch, + red_mask_size, red_mask_shift, + green_mask_size, green_mask_shift, + blue_mask_size, blue_mask_shift, + NULL, + NULL, NULL, + NULL, NULL, + NULL, NULL, + NULL, 0, 0, 1, + 0, 0, + 0 + ); +``` +Where `framebuffer_ptr, width, height, pitch` and `{red,green,blue}_mask_{size,shift}` +represent the corresponding info about the framebuffer to use for this given instance. + +The meaning of the other arguments can be found in `backends/fb.h`. + +To then print to the terminal instance, simply use the `flanterm_write()` +function on the given instance. For example: +```c +#include + +const char msg[] = "Hello world\n"; + +flanterm_write(ft_ctx, msg, sizeof(msg)); +``` diff --git a/src/drivers/fb/terminal/backends/fb.c b/src/drivers/fb/terminal/backends/fb.c new file mode 100644 index 0000000..0fd9150 --- /dev/null +++ b/src/drivers/fb/terminal/backends/fb.c @@ -0,0 +1,1183 @@ +/* Copyright (C) 2022-2024 mintsuki and contributors. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include "../flanterm.h" +#include "fb.h" + +void *memset(void *, int, size_t); +void *memcpy(void *, const void *, size_t); + +#ifndef FLANTERM_FB_DISABLE_BUMP_ALLOC + +#ifndef FLANTERM_FB_BUMP_ALLOC_POOL_SIZE +#define FLANTERM_FB_BUMP_ALLOC_POOL_SIZE 873000 + +#define FLANTERM_FB_WIDTH_LIMIT 1920 +#define FLANTERM_FB_HEIGHT_LIMIT 1200 +#endif + +static uint8_t bump_alloc_pool[FLANTERM_FB_BUMP_ALLOC_POOL_SIZE]; +static size_t bump_alloc_ptr = 0; + +static void *bump_alloc(size_t s) { + size_t next_ptr = bump_alloc_ptr + s; + if (next_ptr > FLANTERM_FB_BUMP_ALLOC_POOL_SIZE) { + return NULL; + } + void *ret = &bump_alloc_pool[bump_alloc_ptr]; + bump_alloc_ptr = next_ptr; + return ret; +} + +static bool bump_allocated_instance = false; + +#endif + +// Builtin font originally taken from: +// https://github.com/viler-int10h/vga-text-mode-fonts/raw/master/FONTS/PC-OTHER/TOSH-SAT.F16 +static const uint8_t builtin_font[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x42, 0x81, 0x81, 0xa5, 0xa5, 0x81, + 0x81, 0xa5, 0x99, 0x81, 0x42, 0x3c, 0x00, 0x00, 0x00, 0x3c, 0x7e, 0xff, + 0xff, 0xdb, 0xdb, 0xff, 0xff, 0xdb, 0xe7, 0xff, 0x7e, 0x3c, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x6c, 0xfe, 0xfe, 0xfe, 0x7c, 0x7c, 0x38, 0x38, 0x10, + 0x10, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x38, 0x38, 0x7c, 0x7c, 0xfe, + 0x7c, 0x7c, 0x38, 0x38, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, + 0x3c, 0x3c, 0xdb, 0xff, 0xff, 0xdb, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0xff, 0x66, 0x18, 0x18, + 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x78, + 0x78, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0xcc, 0x84, 0x84, 0xcc, 0x78, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3, 0x99, 0xbd, + 0xbd, 0x99, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x1e, + 0x0e, 0x1e, 0x32, 0x78, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0xfc, 0x30, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x18, 0x1c, 0x1e, 0x16, 0x12, + 0x10, 0x10, 0x70, 0xf0, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x30, 0x38, 0x2c, + 0x26, 0x32, 0x3a, 0x2e, 0x26, 0x22, 0x62, 0xe2, 0xc6, 0x0e, 0x0c, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x18, 0xdb, 0x3c, 0xe7, 0x3c, 0xdb, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc0, 0xe0, 0xf8, 0xfe, + 0xf8, 0xe0, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x06, 0x0e, 0x3e, 0xfe, 0x3e, 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x30, 0x78, 0xfc, 0x30, 0x30, 0x30, 0x30, 0x30, 0xfc, 0x78, + 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, + 0xcc, 0xcc, 0x00, 0xcc, 0xcc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xdb, + 0xdb, 0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0x60, 0x38, 0x6c, 0xc6, 0xc6, 0x6c, 0x38, 0x0c, + 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x78, + 0xfc, 0x30, 0x30, 0x30, 0x30, 0x30, 0xfc, 0x78, 0x30, 0xfc, 0x00, 0x00, + 0x00, 0x00, 0x30, 0x78, 0xfc, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0xfc, 0x78, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x0c, 0xfe, 0xfe, 0x0c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x60, 0xfe, 0xfe, 0x60, 0x30, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, + 0xc0, 0xc0, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x24, 0x66, 0xff, 0xff, 0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x38, 0x38, 0x7c, 0x7c, 0xfe, 0xfe, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x7c, 0x7c, + 0x38, 0x38, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x30, 0x78, 0x78, 0x78, 0x78, 0x30, 0x30, 0x30, 0x00, 0x30, + 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x6c, + 0x6c, 0xfe, 0x6c, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x18, 0x7c, 0xc6, 0xc0, 0xc0, 0x7c, 0x06, 0x06, 0xc6, 0x7c, + 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0x0c, 0x0c, 0x18, 0x38, + 0x30, 0x60, 0x60, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, + 0x6c, 0x38, 0x30, 0x76, 0xde, 0xcc, 0xcc, 0xde, 0x76, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x30, 0x60, 0x60, 0x60, 0x60, + 0x60, 0x60, 0x60, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x30, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x30, 0x60, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x38, 0xfe, 0x38, 0x6c, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, + 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x06, + 0x0c, 0x0c, 0x18, 0x38, 0x30, 0x60, 0x60, 0xc0, 0xc0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xd6, 0xd6, 0xd6, 0xc6, 0xc6, 0xc6, + 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x38, 0x78, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, + 0x06, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0xc0, 0xfe, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0x06, 0x06, 0x3c, 0x06, 0x06, 0x06, 0x06, 0xc6, + 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x0c, 0x1c, 0x3c, 0x6c, 0xcc, + 0xfe, 0x0c, 0x0c, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc0, + 0xc0, 0xc0, 0xfc, 0x06, 0x06, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x60, 0xc0, 0xc0, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc6, 0x06, 0x06, 0x0c, 0x18, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, + 0xc6, 0xc6, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x06, 0x06, 0x0c, + 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, + 0x18, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x30, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x60, 0x30, 0x18, 0x0c, + 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, + 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x60, + 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x06, 0x0c, 0x18, 0x30, 0x30, 0x00, 0x30, + 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xde, 0xde, + 0xde, 0xde, 0xc0, 0xc0, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, + 0xc6, 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfc, 0xc6, 0xc6, 0xc6, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0xc0, 0xc0, + 0xc0, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xcc, + 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xcc, 0xf8, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0xfc, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, + 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0xfc, 0xc0, + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, + 0xc0, 0xc0, 0xc0, 0xde, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, + 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xc6, 0xcc, 0xd8, 0xf0, 0xe0, 0xf0, 0xd8, 0xcc, 0xc6, + 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, + 0xc0, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, + 0xee, 0xfe, 0xd6, 0xd6, 0xd6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xc6, 0xe6, 0xe6, 0xf6, 0xde, 0xce, 0xce, 0xc6, 0xc6, + 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xc6, + 0xc6, 0xc6, 0xc6, 0xfc, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xf6, 0xda, + 0x6c, 0x06, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0xfc, + 0xd8, 0xcc, 0xcc, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, + 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xd6, 0xd6, 0xd6, 0xd6, 0xfe, 0x6c, + 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x38, + 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xcc, + 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0x06, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0xc0, 0xc0, + 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x60, 0x60, 0x60, 0x60, 0x60, + 0x60, 0x60, 0x60, 0x60, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, + 0x60, 0x60, 0x30, 0x38, 0x18, 0x0c, 0x0c, 0x06, 0x06, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x18, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x06, 0x06, + 0x7e, 0xc6, 0xc6, 0xc6, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, + 0xc0, 0xdc, 0xe6, 0xc6, 0xc6, 0xc6, 0xc6, 0xe6, 0xdc, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0xc0, 0xc0, 0xc6, + 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x06, 0x76, 0xce, 0xc6, + 0xc6, 0xc6, 0xc6, 0xce, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7c, 0xc6, 0xc6, 0xfe, 0xc0, 0xc0, 0xc0, 0x7e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1c, 0x36, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xce, 0xc6, + 0xc6, 0xc6, 0xce, 0x76, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0xc0, 0xc0, + 0xc0, 0xdc, 0xe6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x1e, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0xc0, 0xc0, + 0xc0, 0xc6, 0xcc, 0xd8, 0xf0, 0xf0, 0xd8, 0xcc, 0xc6, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xec, 0xfe, 0xd6, + 0xd6, 0xd6, 0xd6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xdc, 0xe6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0xe6, 0xc6, + 0xc6, 0xc6, 0xe6, 0xdc, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x76, 0xce, 0xc6, 0xc6, 0xc6, 0xce, 0x76, 0x06, 0x06, 0x06, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0xe6, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, + 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc0, + 0x70, 0x1c, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, + 0x30, 0xfe, 0x30, 0x30, 0x30, 0x30, 0x30, 0x36, 0x1c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0x6c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xc6, 0xc6, 0xd6, 0xd6, 0xd6, 0xd6, 0xfe, 0x6c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0x6c, 0x38, 0x38, 0x6c, 0xc6, + 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xce, 0x76, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xfe, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0xfe, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1c, 0x30, 0x30, 0x30, 0x30, 0xe0, 0x30, 0x30, 0x30, 0x30, + 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x30, + 0x30, 0x30, 0x30, 0x1c, 0x30, 0x30, 0x30, 0x30, 0xe0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x38, 0x38, 0x6c, + 0x6c, 0xc6, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x66, 0x3c, 0x18, 0xcc, 0x78, 0x00, + 0x00, 0x00, 0x6c, 0x6c, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0x7c, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, 0x00, 0x7c, 0xc6, 0xc6, + 0xfe, 0xc0, 0xc0, 0xc0, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, + 0x00, 0x7c, 0x06, 0x06, 0x7e, 0xc6, 0xc6, 0xc6, 0x7e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x6c, 0x6c, 0x00, 0x7c, 0x06, 0x06, 0x7e, 0xc6, 0xc6, 0xc6, + 0x7e, 0x00, 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0x06, 0x06, + 0x7e, 0xc6, 0xc6, 0xc6, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0x38, + 0x00, 0x7c, 0x06, 0x06, 0x7e, 0xc6, 0xc6, 0xc6, 0x7e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0xc0, 0xc0, 0xc6, + 0x7c, 0x18, 0x0c, 0x38, 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, + 0xfe, 0xc0, 0xc0, 0xc0, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x6c, + 0x00, 0x7c, 0xc6, 0xc6, 0xfe, 0xc0, 0xc0, 0xc0, 0x7e, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xc6, 0xfe, 0xc0, 0xc0, 0xc0, + 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x6c, 0x00, 0x38, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, + 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x3c, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, + 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x38, 0x6c, 0x38, 0x00, + 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, + 0x18, 0x30, 0x60, 0x00, 0xfe, 0xc0, 0xc0, 0xfc, 0xc0, 0xc0, 0xc0, 0xc0, + 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xec, 0x36, 0x36, + 0x76, 0xde, 0xd8, 0xd8, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x3c, + 0x6c, 0xcc, 0xcc, 0xfe, 0xcc, 0xcc, 0xcc, 0xcc, 0xce, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, + 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x38, 0x6c, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0x7c, 0x00, 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x00, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x6c, + 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xce, 0x76, 0x06, 0xc6, 0x7c, 0x00, + 0x6c, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0x7c, 0x00, 0x00, 0x00, 0x6c, 0x6c, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, + 0x30, 0x78, 0xcc, 0xc0, 0xc0, 0xcc, 0x78, 0x30, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x6c, 0x60, 0x60, 0x60, 0xf8, 0x60, 0x60, 0x60, 0xe6, + 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0xfc, + 0x30, 0xfc, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xcc, + 0xcc, 0xf8, 0xc4, 0xcc, 0xde, 0xcc, 0xcc, 0xcc, 0xc6, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x1b, 0x18, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x18, 0x18, + 0x18, 0xd8, 0x70, 0x00, 0x00, 0x0c, 0x18, 0x30, 0x00, 0x7c, 0x06, 0x06, + 0x7e, 0xc6, 0xc6, 0xc6, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, + 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, + 0x00, 0x0c, 0x18, 0x30, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0x7c, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, 0x00, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x00, + 0x00, 0xdc, 0xe6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, + 0x76, 0xdc, 0x00, 0xc6, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6, + 0xc6, 0x00, 0x00, 0x00, 0x00, 0x78, 0xd8, 0xd8, 0x6c, 0x00, 0xfc, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0x6c, + 0x38, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x30, 0x60, 0xc0, 0xc6, 0xc6, + 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xfe, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc0, 0xc2, 0xc6, 0xcc, 0xd8, 0x30, 0x60, 0xdc, 0x86, 0x0c, + 0x18, 0x3e, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc2, 0xc6, 0xcc, 0xd8, 0x30, + 0x66, 0xce, 0x9e, 0x3e, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, + 0x00, 0x30, 0x30, 0x30, 0x78, 0x78, 0x78, 0x78, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x6c, 0xd8, 0x6c, 0x36, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x6c, 0x36, + 0x6c, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x88, 0x22, 0x88, + 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, + 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, + 0x55, 0xaa, 0x55, 0xaa, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, + 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0xf8, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0xf8, 0x18, + 0x18, 0xf8, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0xf6, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0x18, + 0x18, 0xf8, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x36, 0x36, 0x36, 0x36, + 0x36, 0xf6, 0xf6, 0x06, 0x06, 0xf6, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x06, + 0x06, 0xf6, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0xf6, 0xf6, 0x06, 0x06, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xfe, 0xfe, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0xf8, 0x18, + 0x18, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xf8, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x1f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x1f, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0xff, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x1f, 0x18, 0x18, 0x1f, 0x1f, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, + 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x37, 0x37, 0x30, 0x30, 0x3f, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x3f, 0x30, 0x30, 0x37, 0x37, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xf7, 0xf7, 0x00, + 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x00, 0x00, 0xf7, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x37, 0x30, 0x30, 0x37, 0x37, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, + 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x36, 0x36, 0x36, + 0x36, 0xf7, 0xf7, 0x00, 0x00, 0xf7, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x3f, + 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x1f, 0x1f, 0x18, 0x18, 0x1f, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x1f, 0x18, 0x18, 0x1f, 0x1f, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, + 0x3f, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0xff, 0xff, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0xff, 0x18, 0x18, 0xff, 0xff, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, + 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x1f, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xf0, 0xf0, 0xf0, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x76, 0xd6, 0xdc, 0xc8, 0xc8, 0xdc, 0xd6, 0x76, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0xd8, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, + 0xd8, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0xfe, 0xc6, 0xc6, 0xc0, 0xc0, 0xc0, + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7e, 0xfe, 0x24, 0x24, 0x24, 0x24, 0x66, 0xc6, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0xfe, 0xc2, 0x60, 0x30, 0x18, 0x30, 0x60, 0xc2, 0xfe, + 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xc8, 0xcc, + 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x76, 0x6c, 0x60, 0xc0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0xfc, 0x98, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x30, 0x30, 0x78, 0xcc, 0xcc, + 0xcc, 0x78, 0x30, 0x30, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, + 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x6c, 0x6c, 0x6c, + 0xee, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0xcc, 0x60, 0x30, 0x78, 0xcc, + 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x76, 0xbb, 0x99, 0x99, 0xdd, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x06, 0x3c, 0x6c, 0xce, 0xd6, 0xd6, 0xe6, 0x6c, 0x78, + 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x30, 0x60, 0xc0, 0xc0, 0xfe, + 0xc0, 0xc0, 0x60, 0x30, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, + 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0xfc, + 0x30, 0x30, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x60, 0x30, 0x18, 0x0c, 0x18, 0x30, 0x60, 0x00, 0xfc, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x30, 0x60, 0xc0, 0x60, 0x30, 0x18, 0x00, + 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x36, 0x36, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00, 0xfc, 0x00, 0x30, 0x30, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x00, + 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0xcc, 0xcc, + 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xcc, 0x6c, 0x3c, 0x1c, 0x0c, 0x00, 0x00, + 0x00, 0xd8, 0xec, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0x0c, 0x18, 0x30, 0x60, 0x7c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 +}; + +static inline __attribute__((always_inline)) uint32_t convert_colour(struct flanterm_context *_ctx, uint32_t colour) { + struct flanterm_fb_context *ctx = (void *)_ctx; + uint32_t r = (colour >> 16) & 0xff; + uint32_t g = (colour >> 8) & 0xff; + uint32_t b = colour & 0xff; + return (r << ctx->red_mask_shift) | (g << ctx->green_mask_shift) | (b << ctx->blue_mask_shift); +} + +static void flanterm_fb_save_state(struct flanterm_context *_ctx) { + struct flanterm_fb_context *ctx = (void *)_ctx; + ctx->saved_state_text_fg = ctx->text_fg; + ctx->saved_state_text_bg = ctx->text_bg; + ctx->saved_state_cursor_x = ctx->cursor_x; + ctx->saved_state_cursor_y = ctx->cursor_y; +} + +static void flanterm_fb_restore_state(struct flanterm_context *_ctx) { + struct flanterm_fb_context *ctx = (void *)_ctx; + ctx->text_fg = ctx->saved_state_text_fg; + ctx->text_bg = ctx->saved_state_text_bg; + ctx->cursor_x = ctx->saved_state_cursor_x; + ctx->cursor_y = ctx->saved_state_cursor_y; +} + +static void flanterm_fb_swap_palette(struct flanterm_context *_ctx) { + struct flanterm_fb_context *ctx = (void *)_ctx; + uint32_t tmp = ctx->text_bg; + ctx->text_bg = ctx->text_fg; + ctx->text_fg = tmp; +} + +static void plot_char(struct flanterm_context *_ctx, struct flanterm_fb_char *c, size_t x, size_t y) { + struct flanterm_fb_context *ctx = (void *)_ctx; + + if (x >= _ctx->cols || y >= _ctx->rows) { + return; + } + + uint32_t default_bg = ctx->default_bg; + + x = ctx->offset_x + x * ctx->glyph_width; + y = ctx->offset_y + y * ctx->glyph_height; + + bool *glyph = &ctx->font_bool[c->c * ctx->font_height * ctx->font_width]; + // naming: fx,fy for font coordinates, gx,gy for glyph coordinates + for (size_t gy = 0; gy < ctx->glyph_height; gy++) { + uint8_t fy = gy / ctx->font_scale_y; + volatile uint32_t *fb_line = ctx->framebuffer + x + (y + gy) * (ctx->pitch / 4); + uint32_t *canvas_line = ctx->canvas + x + (y + gy) * ctx->width; + for (size_t fx = 0; fx < ctx->font_width; fx++) { + bool draw = glyph[fy * ctx->font_width + fx]; + for (size_t i = 0; i < ctx->font_scale_x; i++) { + size_t gx = ctx->font_scale_x * fx + i; + uint32_t bg, fg; + if (ctx->canvas != NULL) { + bg = c->bg == 0xffffffff ? canvas_line[gx] : c->bg; + fg = c->fg == 0xffffffff ? canvas_line[gx] : c->fg; + } else { + bg = c->bg == 0xffffffff ? default_bg : c->bg; + fg = c->fg == 0xffffffff ? default_bg : c->fg; + } + fb_line[gx] = draw ? fg : bg; + } + } + } +} + +#ifdef FLANTERM_FB_ENABLE_MASKING +static void plot_char_masked(struct flanterm_context *_ctx, struct flanterm_fb_char *old, struct flanterm_fb_char *c, size_t x, size_t y) { + struct flanterm_fb_context *ctx = (void *)_ctx; + + if (x >= _ctx->cols || y >= _ctx->rows) { + return; + } + + x = ctx->offset_x + x * ctx->glyph_width; + y = ctx->offset_y + y * ctx->glyph_height; + + uint32_t default_bg = ctx->default_bg; + + bool *new_glyph = &ctx->font_bool[c->c * ctx->font_height * ctx->font_width]; + bool *old_glyph = &ctx->font_bool[old->c * ctx->font_height * ctx->font_width]; + for (size_t gy = 0; gy < ctx->glyph_height; gy++) { + uint8_t fy = gy / ctx->font_scale_y; + volatile uint32_t *fb_line = ctx->framebuffer + x + (y + gy) * (ctx->pitch / 4); + uint32_t *canvas_line = ctx->canvas + x + (y + gy) * ctx->width; + for (size_t fx = 0; fx < ctx->font_width; fx++) { + bool old_draw = old_glyph[fy * ctx->font_width + fx]; + bool new_draw = new_glyph[fy * ctx->font_width + fx]; + if (old_draw == new_draw) + continue; + for (size_t i = 0; i < ctx->font_scale_x; i++) { + size_t gx = ctx->font_scale_x * fx + i; + uint32_t bg, fg; + if (ctx->canvas != NULL) { + bg = c->bg == 0xffffffff ? canvas_line[gx] : c->bg; + fg = c->fg == 0xffffffff ? canvas_line[gx] : c->fg; + } else { + bg = c->bg == 0xffffffff ? default_bg : c->bg; + fg = c->fg == 0xffffffff ? default_bg : c->fg; + } + fb_line[gx] = new_draw ? fg : bg; + } + } + } +} +#endif + +static inline bool compare_char(struct flanterm_fb_char *a, struct flanterm_fb_char *b) { + return !(a->c != b->c || a->bg != b->bg || a->fg != b->fg); +} + +static void push_to_queue(struct flanterm_context *_ctx, struct flanterm_fb_char *c, size_t x, size_t y) { + struct flanterm_fb_context *ctx = (void *)_ctx; + + if (x >= _ctx->cols || y >= _ctx->rows) { + return; + } + + size_t i = y * _ctx->cols + x; + + struct flanterm_fb_queue_item *q = ctx->map[i]; + + if (q == NULL) { + if (compare_char(&ctx->grid[i], c)) { + return; + } + q = &ctx->queue[ctx->queue_i++]; + q->x = x; + q->y = y; + ctx->map[i] = q; + } + + q->c = *c; +} + +static void flanterm_fb_revscroll(struct flanterm_context *_ctx) { + struct flanterm_fb_context *ctx = (void *)_ctx; + + for (size_t i = (_ctx->scroll_bottom_margin - 1) * _ctx->cols - 1; + i >= _ctx->scroll_top_margin * _ctx->cols; i--) { + if (i == (size_t)-1) { + break; + } + struct flanterm_fb_char *c; + struct flanterm_fb_queue_item *q = ctx->map[i]; + if (q != NULL) { + c = &q->c; + } else { + c = &ctx->grid[i]; + } + push_to_queue(_ctx, c, (i + _ctx->cols) % _ctx->cols, (i + _ctx->cols) / _ctx->cols); + } + + // Clear the first line of the screen. + struct flanterm_fb_char empty; + empty.c = ' '; + empty.fg = ctx->text_fg; + empty.bg = ctx->text_bg; + for (size_t i = 0; i < _ctx->cols; i++) { + push_to_queue(_ctx, &empty, i, _ctx->scroll_top_margin); + } +} + +static void flanterm_fb_scroll(struct flanterm_context *_ctx) { + struct flanterm_fb_context *ctx = (void *)_ctx; + + for (size_t i = (_ctx->scroll_top_margin + 1) * _ctx->cols; + i < _ctx->scroll_bottom_margin * _ctx->cols; i++) { + struct flanterm_fb_char *c; + struct flanterm_fb_queue_item *q = ctx->map[i]; + if (q != NULL) { + c = &q->c; + } else { + c = &ctx->grid[i]; + } + push_to_queue(_ctx, c, (i - _ctx->cols) % _ctx->cols, (i - _ctx->cols) / _ctx->cols); + } + + // Clear the last line of the screen. + struct flanterm_fb_char empty; + empty.c = ' '; + empty.fg = ctx->text_fg; + empty.bg = ctx->text_bg; + for (size_t i = 0; i < _ctx->cols; i++) { + push_to_queue(_ctx, &empty, i, _ctx->scroll_bottom_margin - 1); + } +} + +static void flanterm_fb_clear(struct flanterm_context *_ctx, bool move) { + struct flanterm_fb_context *ctx = (void *)_ctx; + + struct flanterm_fb_char empty; + empty.c = ' '; + empty.fg = ctx->text_fg; + empty.bg = ctx->text_bg; + for (size_t i = 0; i < _ctx->rows * _ctx->cols; i++) { + push_to_queue(_ctx, &empty, i % _ctx->cols, i / _ctx->cols); + } + + if (move) { + ctx->cursor_x = 0; + ctx->cursor_y = 0; + } +} + +static void flanterm_fb_set_cursor_pos(struct flanterm_context *_ctx, size_t x, size_t y) { + struct flanterm_fb_context *ctx = (void *)_ctx; + + if (x >= _ctx->cols) { + if ((int)x < 0) { + x = 0; + } else { + x = _ctx->cols - 1; + } + } + if (y >= _ctx->rows) { + if ((int)y < 0) { + y = 0; + } else { + y = _ctx->rows - 1; + } + } + ctx->cursor_x = x; + ctx->cursor_y = y; +} + +static void flanterm_fb_get_cursor_pos(struct flanterm_context *_ctx, size_t *x, size_t *y) { + struct flanterm_fb_context *ctx = (void *)_ctx; + + *x = ctx->cursor_x >= _ctx->cols ? _ctx->cols - 1 : ctx->cursor_x; + *y = ctx->cursor_y >= _ctx->rows ? _ctx->rows - 1 : ctx->cursor_y; +} + +static void flanterm_fb_move_character(struct flanterm_context *_ctx, size_t new_x, size_t new_y, size_t old_x, size_t old_y) { + struct flanterm_fb_context *ctx = (void *)_ctx; + + if (old_x >= _ctx->cols || old_y >= _ctx->rows + || new_x >= _ctx->cols || new_y >= _ctx->rows) { + return; + } + + size_t i = old_x + old_y * _ctx->cols; + + struct flanterm_fb_char *c; + struct flanterm_fb_queue_item *q = ctx->map[i]; + if (q != NULL) { + c = &q->c; + } else { + c = &ctx->grid[i]; + } + + push_to_queue(_ctx, c, new_x, new_y); +} + +static void flanterm_fb_set_text_fg(struct flanterm_context *_ctx, size_t fg) { + struct flanterm_fb_context *ctx = (void *)_ctx; + + ctx->text_fg = ctx->ansi_colours[fg]; +} + +static void flanterm_fb_set_text_bg(struct flanterm_context *_ctx, size_t bg) { + struct flanterm_fb_context *ctx = (void *)_ctx; + + ctx->text_bg = ctx->ansi_colours[bg]; +} + +static void flanterm_fb_set_text_fg_bright(struct flanterm_context *_ctx, size_t fg) { + struct flanterm_fb_context *ctx = (void *)_ctx; + + ctx->text_fg = ctx->ansi_bright_colours[fg]; +} + +static void flanterm_fb_set_text_bg_bright(struct flanterm_context *_ctx, size_t bg) { + struct flanterm_fb_context *ctx = (void *)_ctx; + + ctx->text_bg = ctx->ansi_bright_colours[bg]; +} + +static void flanterm_fb_set_text_fg_rgb(struct flanterm_context *_ctx, uint32_t fg) { + struct flanterm_fb_context *ctx = (void *)_ctx; + + ctx->text_fg = convert_colour(_ctx, fg); +} + +static void flanterm_fb_set_text_bg_rgb(struct flanterm_context *_ctx, uint32_t bg) { + struct flanterm_fb_context *ctx = (void *)_ctx; + + ctx->text_bg = convert_colour(_ctx, bg); +} + +static void flanterm_fb_set_text_fg_default(struct flanterm_context *_ctx) { + struct flanterm_fb_context *ctx = (void *)_ctx; + + ctx->text_fg = ctx->default_fg; +} + +static void flanterm_fb_set_text_bg_default(struct flanterm_context *_ctx) { + struct flanterm_fb_context *ctx = (void *)_ctx; + + ctx->text_bg = 0xffffffff; +} + +static void flanterm_fb_set_text_fg_default_bright(struct flanterm_context *_ctx) { + struct flanterm_fb_context *ctx = (void *)_ctx; + + ctx->text_fg = ctx->default_fg_bright; +} + +static void flanterm_fb_set_text_bg_default_bright(struct flanterm_context *_ctx) { + struct flanterm_fb_context *ctx = (void *)_ctx; + + ctx->text_bg = ctx->default_bg_bright; +} + +static void draw_cursor(struct flanterm_context *_ctx) { + struct flanterm_fb_context *ctx = (void *)_ctx; + + if (ctx->cursor_x >= _ctx->cols || ctx->cursor_y >= _ctx->rows) { + return; + } + + size_t i = ctx->cursor_x + ctx->cursor_y * _ctx->cols; + + struct flanterm_fb_char c; + struct flanterm_fb_queue_item *q = ctx->map[i]; + if (q != NULL) { + c = q->c; + } else { + c = ctx->grid[i]; + } + uint32_t tmp = c.fg; + c.fg = c.bg; + c.bg = tmp; + plot_char(_ctx, &c, ctx->cursor_x, ctx->cursor_y); + if (q != NULL) { + ctx->grid[i] = q->c; + ctx->map[i] = NULL; + } +} + +static void flanterm_fb_double_buffer_flush(struct flanterm_context *_ctx) { + struct flanterm_fb_context *ctx = (void *)_ctx; + + if (_ctx->cursor_enabled) { + draw_cursor(_ctx); + } + + for (size_t i = 0; i < ctx->queue_i; i++) { + struct flanterm_fb_queue_item *q = &ctx->queue[i]; + size_t offset = q->y * _ctx->cols + q->x; + if (ctx->map[offset] == NULL) { + continue; + } + #ifdef FLANTERM_FB_ENABLE_MASKING + struct flanterm_fb_char *old = &ctx->grid[offset]; + if (q->c.bg == old->bg && q->c.fg == old->fg) { + plot_char_masked(_ctx, old, &q->c, q->x, q->y); + } else { + plot_char(_ctx, &q->c, q->x, q->y); + } + #else + plot_char(_ctx, &q->c, q->x, q->y); + #endif + ctx->grid[offset] = q->c; + ctx->map[offset] = NULL; + } + + if ((ctx->old_cursor_x != ctx->cursor_x || ctx->old_cursor_y != ctx->cursor_y) || _ctx->cursor_enabled == false) { + if (ctx->old_cursor_x < _ctx->cols && ctx->old_cursor_y < _ctx->rows) { + plot_char(_ctx, &ctx->grid[ctx->old_cursor_x + ctx->old_cursor_y * _ctx->cols], ctx->old_cursor_x, ctx->old_cursor_y); + } + } + + ctx->old_cursor_x = ctx->cursor_x; + ctx->old_cursor_y = ctx->cursor_y; + + ctx->queue_i = 0; +} + +static void flanterm_fb_raw_putchar(struct flanterm_context *_ctx, uint8_t c) { + struct flanterm_fb_context *ctx = (void *)_ctx; + + if (ctx->cursor_x >= _ctx->cols && (ctx->cursor_y < _ctx->scroll_bottom_margin - 1 || _ctx->scroll_enabled)) { + ctx->cursor_x = 0; + ctx->cursor_y++; + if (ctx->cursor_y == _ctx->scroll_bottom_margin) { + ctx->cursor_y--; + flanterm_fb_scroll(_ctx); + } + if (ctx->cursor_y >= _ctx->cols) { + ctx->cursor_y = _ctx->cols - 1; + } + } + + struct flanterm_fb_char ch; + ch.c = c; + ch.fg = ctx->text_fg; + ch.bg = ctx->text_bg; + push_to_queue(_ctx, &ch, ctx->cursor_x++, ctx->cursor_y); +} + +static void flanterm_fb_full_refresh(struct flanterm_context *_ctx) { + struct flanterm_fb_context *ctx = (void *)_ctx; + + uint32_t default_bg = ctx->default_bg; + + for (size_t y = 0; y < ctx->height; y++) { + for (size_t x = 0; x < ctx->width; x++) { + if (ctx->canvas != NULL) { + ctx->framebuffer[y * (ctx->pitch / sizeof(uint32_t)) + x] = ctx->canvas[y * ctx->width + x]; + } else { + ctx->framebuffer[y * (ctx->pitch / sizeof(uint32_t)) + x] = default_bg; + } + } + } + + for (size_t i = 0; i < (size_t)_ctx->rows * _ctx->cols; i++) { + size_t x = i % _ctx->cols; + size_t y = i / _ctx->cols; + + plot_char(_ctx, &ctx->grid[i], x, y); + } + + if (_ctx->cursor_enabled) { + draw_cursor(_ctx); + } +} + +static void flanterm_fb_deinit(struct flanterm_context *_ctx, void (*_free)(void *, size_t)) { + struct flanterm_fb_context *ctx = (void *)_ctx; + + if (_free == NULL) { +#ifndef FLANTERM_FB_DISABLE_BUMP_ALLOC + if (bump_allocated_instance == true) { + bump_alloc_ptr = 0; + bump_allocated_instance = false; + } +#endif + return; + } + + _free(ctx->font_bits, ctx->font_bits_size); + _free(ctx->font_bool, ctx->font_bool_size); + _free(ctx->grid, ctx->grid_size); + _free(ctx->queue, ctx->queue_size); + _free(ctx->map, ctx->map_size); + + if (ctx->canvas != NULL) { + _free(ctx->canvas, ctx->canvas_size); + } + + _free(ctx, sizeof(struct flanterm_fb_context)); +} + +struct flanterm_context *flanterm_fb_init( + void *(*_malloc)(size_t), + void (*_free)(void *, size_t), + uint32_t *framebuffer, size_t width, size_t height, size_t pitch, + uint8_t red_mask_size, uint8_t red_mask_shift, + uint8_t green_mask_size, uint8_t green_mask_shift, + uint8_t blue_mask_size, uint8_t blue_mask_shift, + uint32_t *canvas, + uint32_t *ansi_colours, uint32_t *ansi_bright_colours, + uint32_t *default_bg, uint32_t *default_fg, + uint32_t *default_bg_bright, uint32_t *default_fg_bright, + void *font, size_t font_width, size_t font_height, size_t font_spacing, + size_t font_scale_x, size_t font_scale_y, + size_t margin +) { + if (font_scale_x == 0 || font_scale_y == 0) { + font_scale_x = 1; + font_scale_y = 1; + if (width >= (1920 + 1920 / 3) && height >= (1080 + 1080 / 3)) { + font_scale_x = 2; + font_scale_y = 2; + } + if (width >= (3840 + 3840 / 3) && height >= (2160 + 2160 / 3)) { + font_scale_x = 4; + font_scale_y = 4; + } + } + + if (red_mask_size < 8 || red_mask_size != green_mask_size || red_mask_size != blue_mask_size) { + return NULL; + } + + if (_malloc == NULL) { +#ifndef FLANTERM_FB_DISABLE_BUMP_ALLOC + if (bump_allocated_instance == true) { + return NULL; + } + _malloc = bump_alloc; + // Limit terminal size if needed + if (width > FLANTERM_FB_WIDTH_LIMIT || height > FLANTERM_FB_HEIGHT_LIMIT) { + size_t width_limit = width > FLANTERM_FB_WIDTH_LIMIT ? FLANTERM_FB_WIDTH_LIMIT : width; + size_t height_limit = height > FLANTERM_FB_HEIGHT_LIMIT ? FLANTERM_FB_HEIGHT_LIMIT : height; + + framebuffer = (uint32_t *)((uintptr_t)framebuffer + ((((height / 2) - (height_limit / 2)) * pitch) + (((width / 2) - (width_limit / 2)) * 4))); + + width = width_limit; + height = height_limit; + } + + // Force disable canvas + canvas = NULL; +#else + return NULL; +#endif + } + + struct flanterm_fb_context *ctx = NULL; + ctx = _malloc(sizeof(struct flanterm_fb_context)); + if (ctx == NULL) { + goto fail; + } + + struct flanterm_context *_ctx = (void *)ctx; + memset(ctx, 0, sizeof(struct flanterm_fb_context)); + + ctx->red_mask_size = red_mask_size; + ctx->red_mask_shift = red_mask_shift + (red_mask_size - 8); + ctx->green_mask_size = green_mask_size; + ctx->green_mask_shift = green_mask_shift + (green_mask_size - 8); + ctx->blue_mask_size = blue_mask_size; + ctx->blue_mask_shift = blue_mask_shift + (blue_mask_size - 8); + + if (ansi_colours != NULL) { + for (size_t i = 0; i < 8; i++) { + ctx->ansi_colours[i] = convert_colour(_ctx, ansi_colours[i]); + } + } else { + ctx->ansi_colours[0] = convert_colour(_ctx, 0x00000000); // black + ctx->ansi_colours[1] = convert_colour(_ctx, 0x00aa0000); // red + ctx->ansi_colours[2] = convert_colour(_ctx, 0x0000aa00); // green + ctx->ansi_colours[3] = convert_colour(_ctx, 0x00aa5500); // brown + ctx->ansi_colours[4] = convert_colour(_ctx, 0x000000aa); // blue + ctx->ansi_colours[5] = convert_colour(_ctx, 0x00aa00aa); // magenta + ctx->ansi_colours[6] = convert_colour(_ctx, 0x0000aaaa); // cyan + ctx->ansi_colours[7] = convert_colour(_ctx, 0x00aaaaaa); // grey + } + + if (ansi_bright_colours != NULL) { + for (size_t i = 0; i < 8; i++) { + ctx->ansi_bright_colours[i] = convert_colour(_ctx, ansi_bright_colours[i]); + } + } else { + ctx->ansi_bright_colours[0] = convert_colour(_ctx, 0x00555555); // black + ctx->ansi_bright_colours[1] = convert_colour(_ctx, 0x00ff5555); // red + ctx->ansi_bright_colours[2] = convert_colour(_ctx, 0x0055ff55); // green + ctx->ansi_bright_colours[3] = convert_colour(_ctx, 0x00ffff55); // brown + ctx->ansi_bright_colours[4] = convert_colour(_ctx, 0x005555ff); // blue + ctx->ansi_bright_colours[5] = convert_colour(_ctx, 0x00ff55ff); // magenta + ctx->ansi_bright_colours[6] = convert_colour(_ctx, 0x0055ffff); // cyan + ctx->ansi_bright_colours[7] = convert_colour(_ctx, 0x00ffffff); // grey + } + + if (default_bg != NULL) { + ctx->default_bg = convert_colour(_ctx, *default_bg); + } else { + ctx->default_bg = 0x00000000; // background (black) + } + + if (default_fg != NULL) { + ctx->default_fg = convert_colour(_ctx, *default_fg); + } else { + ctx->default_fg = convert_colour(_ctx, 0x00aaaaaa); // foreground (grey) + } + + if (default_bg_bright != NULL) { + ctx->default_bg_bright = convert_colour(_ctx, *default_bg_bright); + } else { + ctx->default_bg_bright = convert_colour(_ctx, 0x00555555); // background (black) + } + + if (default_fg_bright != NULL) { + ctx->default_fg_bright = convert_colour(_ctx, *default_fg_bright); + } else { + ctx->default_fg_bright = convert_colour(_ctx, 0x00ffffff); // foreground (grey) + } + + ctx->text_fg = ctx->default_fg; + ctx->text_bg = 0xffffffff; + + ctx->framebuffer = (void *)framebuffer; + ctx->width = width; + ctx->height = height; + ctx->pitch = pitch; + +#define FONT_BYTES ((font_width * font_height * FLANTERM_FB_FONT_GLYPHS) / 8) + + if (font != NULL) { + ctx->font_width = font_width; + ctx->font_height = font_height; + ctx->font_bits_size = FONT_BYTES; + ctx->font_bits = _malloc(ctx->font_bits_size); + if (ctx->font_bits == NULL) { + goto fail; + } + memcpy(ctx->font_bits, font, ctx->font_bits_size); + } else { + ctx->font_width = font_width = 8; + ctx->font_height = font_height = 16; + ctx->font_bits_size = FONT_BYTES; + font_spacing = 1; + ctx->font_bits = _malloc(ctx->font_bits_size); + if (ctx->font_bits == NULL) { + goto fail; + } + memcpy(ctx->font_bits, builtin_font, ctx->font_bits_size); + } + +#undef FONT_BYTES + + ctx->font_width += font_spacing; + + ctx->font_bool_size = FLANTERM_FB_FONT_GLYPHS * font_height * ctx->font_width * sizeof(bool); + ctx->font_bool = _malloc(ctx->font_bool_size); + if (ctx->font_bool == NULL) { + goto fail; + } + + for (size_t i = 0; i < FLANTERM_FB_FONT_GLYPHS; i++) { + uint8_t *glyph = &ctx->font_bits[i * font_height]; + + for (size_t y = 0; y < font_height; y++) { + // NOTE: the characters in VGA fonts are always one byte wide. + // 9 dot wide fonts have 8 dots and one empty column, except + // characters 0xC0-0xDF replicate column 9. + for (size_t x = 0; x < 8; x++) { + size_t offset = i * font_height * ctx->font_width + y * ctx->font_width + x; + + if ((glyph[y] & (0x80 >> x))) { + ctx->font_bool[offset] = true; + } else { + ctx->font_bool[offset] = false; + } + } + // fill columns above 8 like VGA Line Graphics Mode does + for (size_t x = 8; x < ctx->font_width; x++) { + size_t offset = i * font_height * ctx->font_width + y * ctx->font_width + x; + + if (i >= 0xc0 && i <= 0xdf) { + ctx->font_bool[offset] = (glyph[y] & 1); + } else { + ctx->font_bool[offset] = false; + } + } + } + } + + ctx->font_scale_x = font_scale_x; + ctx->font_scale_y = font_scale_y; + + ctx->glyph_width = ctx->font_width * font_scale_x; + ctx->glyph_height = font_height * font_scale_y; + + _ctx->cols = (ctx->width - margin * 2) / ctx->glyph_width; + _ctx->rows = (ctx->height - margin * 2) / ctx->glyph_height; + + ctx->offset_x = margin + ((ctx->width - margin * 2) % ctx->glyph_width) / 2; + ctx->offset_y = margin + ((ctx->height - margin * 2) % ctx->glyph_height) / 2; + + ctx->grid_size = _ctx->rows * _ctx->cols * sizeof(struct flanterm_fb_char); + ctx->grid = _malloc(ctx->grid_size); + if (ctx->grid == NULL) { + goto fail; + } + for (size_t i = 0; i < _ctx->rows * _ctx->cols; i++) { + ctx->grid[i].c = ' '; + ctx->grid[i].fg = ctx->text_fg; + ctx->grid[i].bg = ctx->text_bg; + } + + ctx->queue_size = _ctx->rows * _ctx->cols * sizeof(struct flanterm_fb_queue_item); + ctx->queue = _malloc(ctx->queue_size); + if (ctx->queue == NULL) { + goto fail; + } + ctx->queue_i = 0; + memset(ctx->queue, 0, ctx->queue_size); + + ctx->map_size = _ctx->rows * _ctx->cols * sizeof(struct flanterm_fb_queue_item *); + ctx->map = _malloc(ctx->map_size); + if (ctx->map == NULL) { + goto fail; + } + memset(ctx->map, 0, ctx->map_size); + + if (canvas != NULL) { + ctx->canvas_size = ctx->width * ctx->height * sizeof(uint32_t); + ctx->canvas = _malloc(ctx->canvas_size); + if (ctx->canvas == NULL) { + goto fail; + } + for (size_t i = 0; i < ctx->width * ctx->height; i++) { + ctx->canvas[i] = convert_colour(_ctx, canvas[i]); + } + } + + _ctx->raw_putchar = flanterm_fb_raw_putchar; + _ctx->clear = flanterm_fb_clear; + _ctx->set_cursor_pos = flanterm_fb_set_cursor_pos; + _ctx->get_cursor_pos = flanterm_fb_get_cursor_pos; + _ctx->set_text_fg = flanterm_fb_set_text_fg; + _ctx->set_text_bg = flanterm_fb_set_text_bg; + _ctx->set_text_fg_bright = flanterm_fb_set_text_fg_bright; + _ctx->set_text_bg_bright = flanterm_fb_set_text_bg_bright; + _ctx->set_text_fg_rgb = flanterm_fb_set_text_fg_rgb; + _ctx->set_text_bg_rgb = flanterm_fb_set_text_bg_rgb; + _ctx->set_text_fg_default = flanterm_fb_set_text_fg_default; + _ctx->set_text_bg_default = flanterm_fb_set_text_bg_default; + _ctx->set_text_fg_default_bright = flanterm_fb_set_text_fg_default_bright; + _ctx->set_text_bg_default_bright = flanterm_fb_set_text_bg_default_bright; + _ctx->move_character = flanterm_fb_move_character; + _ctx->scroll = flanterm_fb_scroll; + _ctx->revscroll = flanterm_fb_revscroll; + _ctx->swap_palette = flanterm_fb_swap_palette; + _ctx->save_state = flanterm_fb_save_state; + _ctx->restore_state = flanterm_fb_restore_state; + _ctx->double_buffer_flush = flanterm_fb_double_buffer_flush; + _ctx->full_refresh = flanterm_fb_full_refresh; + _ctx->deinit = flanterm_fb_deinit; + + flanterm_context_reinit(_ctx); + flanterm_fb_full_refresh(_ctx); + +#ifndef FLANTERM_FB_DISABLE_BUMP_ALLOC + if (_malloc == bump_alloc) { + bump_allocated_instance = true; + } +#endif + + return _ctx; + +fail: + if (ctx == NULL) { + return NULL; + } + +#ifndef FLANTERM_FB_DISABLE_BUMP_ALLOC + if (_malloc == bump_alloc) { + bump_alloc_ptr = 0; + return NULL; + } +#endif + + if (_free == NULL) { + return NULL; + } + + if (ctx->canvas != NULL) { + _free(ctx->canvas, ctx->canvas_size); + } + if (ctx->map != NULL) { + _free(ctx->map, ctx->map_size); + } + if (ctx->queue != NULL) { + _free(ctx->queue, ctx->queue_size); + } + if (ctx->grid != NULL) { + _free(ctx->grid, ctx->grid_size); + } + if (ctx->font_bool != NULL) { + _free(ctx->font_bool, ctx->font_bool_size); + } + if (ctx->font_bits != NULL) { + _free(ctx->font_bits, ctx->font_bits_size); + } + if (ctx != NULL) { + _free(ctx, sizeof(struct flanterm_fb_context)); + } + + return NULL; +} diff --git a/src/drivers/fb/terminal/backends/fb.h b/src/drivers/fb/terminal/backends/fb.h new file mode 100644 index 0000000..bdc888b --- /dev/null +++ b/src/drivers/fb/terminal/backends/fb.h @@ -0,0 +1,136 @@ +/* Copyright (C) 2022-2024 mintsuki and contributors. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLANTERM_FB_H +#define FLANTERM_FB_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +#include "../flanterm.h" + +#define FLANTERM_FB_FONT_GLYPHS 256 + +struct flanterm_fb_char { + uint32_t c; + uint32_t fg; + uint32_t bg; +}; + +struct flanterm_fb_queue_item { + size_t x, y; + struct flanterm_fb_char c; +}; + +struct flanterm_fb_context { + struct flanterm_context term; + + size_t font_width; + size_t font_height; + size_t glyph_width; + size_t glyph_height; + + size_t font_scale_x; + size_t font_scale_y; + + size_t offset_x, offset_y; + + volatile uint32_t *framebuffer; + size_t pitch; + size_t width; + size_t height; + size_t bpp; + + uint8_t red_mask_size, red_mask_shift; + uint8_t green_mask_size, green_mask_shift; + uint8_t blue_mask_size, blue_mask_shift; + + size_t font_bits_size; + uint8_t *font_bits; + size_t font_bool_size; + bool *font_bool; + + uint32_t ansi_colours[8]; + uint32_t ansi_bright_colours[8]; + uint32_t default_fg, default_bg; + uint32_t default_fg_bright, default_bg_bright; + + size_t canvas_size; + uint32_t *canvas; + + size_t grid_size; + size_t queue_size; + size_t map_size; + + struct flanterm_fb_char *grid; + + struct flanterm_fb_queue_item *queue; + size_t queue_i; + + struct flanterm_fb_queue_item **map; + + uint32_t text_fg; + uint32_t text_bg; + size_t cursor_x; + size_t cursor_y; + + uint32_t saved_state_text_fg; + uint32_t saved_state_text_bg; + size_t saved_state_cursor_x; + size_t saved_state_cursor_y; + + size_t old_cursor_x; + size_t old_cursor_y; +}; + +struct flanterm_context *flanterm_fb_init( + /* If _malloc and _free are nulled, use the bump allocated instance (1 use only). */ + void *(*_malloc)(size_t), + void (*_free)(void *, size_t), + uint32_t *framebuffer, size_t width, size_t height, size_t pitch, + uint8_t red_mask_size, uint8_t red_mask_shift, + uint8_t green_mask_size, uint8_t green_mask_shift, + uint8_t blue_mask_size, uint8_t blue_mask_shift, + uint32_t *canvas, /* If nulled, no canvas. */ + uint32_t *ansi_colours, uint32_t *ansi_bright_colours, /* If nulled, default. */ + uint32_t *default_bg, uint32_t *default_fg, /* If nulled, default. */ + uint32_t *default_bg_bright, uint32_t *default_fg_bright, /* If nulled, default. */ + /* If font is null, use default font and font_width and font_height ignored. */ + void *font, size_t font_width, size_t font_height, size_t font_spacing, + /* If scale_x and scale_y are 0, automatically scale font based on resolution. */ + size_t font_scale_x, size_t font_scale_y, + size_t margin +); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/drivers/fb/terminal/flanterm.c b/src/drivers/fb/terminal/flanterm.c new file mode 100644 index 0000000..0126382 --- /dev/null +++ b/src/drivers/fb/terminal/flanterm.c @@ -0,0 +1,1346 @@ +/* Copyright (C) 2022-2024 mintsuki and contributors. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include "flanterm.h" + +// Tries to implement this standard for terminfo +// https://man7.org/linux/man-pages/man4/console_codes.4.html + +static const uint32_t col256[] = { + 0x000000, 0x00005f, 0x000087, 0x0000af, 0x0000d7, 0x0000ff, 0x005f00, 0x005f5f, + 0x005f87, 0x005faf, 0x005fd7, 0x005fff, 0x008700, 0x00875f, 0x008787, 0x0087af, + 0x0087d7, 0x0087ff, 0x00af00, 0x00af5f, 0x00af87, 0x00afaf, 0x00afd7, 0x00afff, + 0x00d700, 0x00d75f, 0x00d787, 0x00d7af, 0x00d7d7, 0x00d7ff, 0x00ff00, 0x00ff5f, + 0x00ff87, 0x00ffaf, 0x00ffd7, 0x00ffff, 0x5f0000, 0x5f005f, 0x5f0087, 0x5f00af, + 0x5f00d7, 0x5f00ff, 0x5f5f00, 0x5f5f5f, 0x5f5f87, 0x5f5faf, 0x5f5fd7, 0x5f5fff, + 0x5f8700, 0x5f875f, 0x5f8787, 0x5f87af, 0x5f87d7, 0x5f87ff, 0x5faf00, 0x5faf5f, + 0x5faf87, 0x5fafaf, 0x5fafd7, 0x5fafff, 0x5fd700, 0x5fd75f, 0x5fd787, 0x5fd7af, + 0x5fd7d7, 0x5fd7ff, 0x5fff00, 0x5fff5f, 0x5fff87, 0x5fffaf, 0x5fffd7, 0x5fffff, + 0x870000, 0x87005f, 0x870087, 0x8700af, 0x8700d7, 0x8700ff, 0x875f00, 0x875f5f, + 0x875f87, 0x875faf, 0x875fd7, 0x875fff, 0x878700, 0x87875f, 0x878787, 0x8787af, + 0x8787d7, 0x8787ff, 0x87af00, 0x87af5f, 0x87af87, 0x87afaf, 0x87afd7, 0x87afff, + 0x87d700, 0x87d75f, 0x87d787, 0x87d7af, 0x87d7d7, 0x87d7ff, 0x87ff00, 0x87ff5f, + 0x87ff87, 0x87ffaf, 0x87ffd7, 0x87ffff, 0xaf0000, 0xaf005f, 0xaf0087, 0xaf00af, + 0xaf00d7, 0xaf00ff, 0xaf5f00, 0xaf5f5f, 0xaf5f87, 0xaf5faf, 0xaf5fd7, 0xaf5fff, + 0xaf8700, 0xaf875f, 0xaf8787, 0xaf87af, 0xaf87d7, 0xaf87ff, 0xafaf00, 0xafaf5f, + 0xafaf87, 0xafafaf, 0xafafd7, 0xafafff, 0xafd700, 0xafd75f, 0xafd787, 0xafd7af, + 0xafd7d7, 0xafd7ff, 0xafff00, 0xafff5f, 0xafff87, 0xafffaf, 0xafffd7, 0xafffff, + 0xd70000, 0xd7005f, 0xd70087, 0xd700af, 0xd700d7, 0xd700ff, 0xd75f00, 0xd75f5f, + 0xd75f87, 0xd75faf, 0xd75fd7, 0xd75fff, 0xd78700, 0xd7875f, 0xd78787, 0xd787af, + 0xd787d7, 0xd787ff, 0xd7af00, 0xd7af5f, 0xd7af87, 0xd7afaf, 0xd7afd7, 0xd7afff, + 0xd7d700, 0xd7d75f, 0xd7d787, 0xd7d7af, 0xd7d7d7, 0xd7d7ff, 0xd7ff00, 0xd7ff5f, + 0xd7ff87, 0xd7ffaf, 0xd7ffd7, 0xd7ffff, 0xff0000, 0xff005f, 0xff0087, 0xff00af, + 0xff00d7, 0xff00ff, 0xff5f00, 0xff5f5f, 0xff5f87, 0xff5faf, 0xff5fd7, 0xff5fff, + 0xff8700, 0xff875f, 0xff8787, 0xff87af, 0xff87d7, 0xff87ff, 0xffaf00, 0xffaf5f, + 0xffaf87, 0xffafaf, 0xffafd7, 0xffafff, 0xffd700, 0xffd75f, 0xffd787, 0xffd7af, + 0xffd7d7, 0xffd7ff, 0xffff00, 0xffff5f, 0xffff87, 0xffffaf, 0xffffd7, 0xffffff, + 0x080808, 0x121212, 0x1c1c1c, 0x262626, 0x303030, 0x3a3a3a, 0x444444, 0x4e4e4e, + 0x585858, 0x626262, 0x6c6c6c, 0x767676, 0x808080, 0x8a8a8a, 0x949494, 0x9e9e9e, + 0xa8a8a8, 0xb2b2b2, 0xbcbcbc, 0xc6c6c6, 0xd0d0d0, 0xdadada, 0xe4e4e4, 0xeeeeee +}; + +#define CHARSET_DEFAULT 0 +#define CHARSET_DEC_SPECIAL 1 + +void flanterm_context_reinit(struct flanterm_context *ctx) { + ctx->tab_size = 8; + ctx->autoflush = true; + ctx->cursor_enabled = true; + ctx->scroll_enabled = true; + ctx->control_sequence = false; + ctx->escape = false; + ctx->osc = false; + ctx->osc_escape = false; + ctx->rrr = false; + ctx->discard_next = false; + ctx->bold = false; + ctx->bg_bold = false; + ctx->reverse_video = false; + ctx->dec_private = false; + ctx->insert_mode = false; + ctx->unicode_remaining = 0; + ctx->g_select = 0; + ctx->charsets[0] = CHARSET_DEFAULT; + ctx->charsets[1] = CHARSET_DEC_SPECIAL; + ctx->current_charset = 0; + ctx->escape_offset = 0; + ctx->esc_values_i = 0; + ctx->saved_cursor_x = 0; + ctx->saved_cursor_y = 0; + ctx->current_primary = (size_t)-1; + ctx->current_bg = (size_t)-1; + ctx->scroll_top_margin = 0; + ctx->scroll_bottom_margin = ctx->rows; + ctx->oob_output = FLANTERM_OOB_OUTPUT_ONLCR; +} + +static void flanterm_putchar(struct flanterm_context *ctx, uint8_t c); + +void flanterm_write(struct flanterm_context *ctx, const char *buf, size_t count) { + for (size_t i = 0; i < count; i++) { + flanterm_putchar(ctx, buf[i]); + } + + if (ctx->autoflush) { + ctx->double_buffer_flush(ctx); + } +} + +static void sgr(struct flanterm_context *ctx) { + size_t i = 0; + + if (!ctx->esc_values_i) + goto def; + + for (; i < ctx->esc_values_i; i++) { + size_t offset; + + if (ctx->esc_values[i] == 0) { +def: + if (ctx->reverse_video) { + ctx->reverse_video = false; + ctx->swap_palette(ctx); + } + ctx->bold = false; + ctx->bg_bold = false; + ctx->current_primary = (size_t)-1; + ctx->current_bg = (size_t)-1; + ctx->set_text_bg_default(ctx); + ctx->set_text_fg_default(ctx); + continue; + } + + else if (ctx->esc_values[i] == 1) { + ctx->bold = true; + if (ctx->current_primary != (size_t)-1) { + if (!ctx->reverse_video) { + ctx->set_text_fg_bright(ctx, ctx->current_primary); + } else { + ctx->set_text_bg_bright(ctx, ctx->current_primary); + } + } else { + if (!ctx->reverse_video) { + ctx->set_text_fg_default_bright(ctx); + } else { + ctx->set_text_bg_default_bright(ctx); + } + } + continue; + } + + else if (ctx->esc_values[i] == 5) { + ctx->bg_bold = true; + if (ctx->current_bg != (size_t)-1) { + if (!ctx->reverse_video) { + ctx->set_text_bg_bright(ctx, ctx->current_bg); + } else { + ctx->set_text_fg_bright(ctx, ctx->current_bg); + } + } else { + if (!ctx->reverse_video) { + ctx->set_text_bg_default_bright(ctx); + } else { + ctx->set_text_fg_default_bright(ctx); + } + } + continue; + } + + else if (ctx->esc_values[i] == 22) { + ctx->bold = false; + if (ctx->current_primary != (size_t)-1) { + if (!ctx->reverse_video) { + ctx->set_text_fg(ctx, ctx->current_primary); + } else { + ctx->set_text_bg(ctx, ctx->current_primary); + } + } else { + if (!ctx->reverse_video) { + ctx->set_text_fg_default(ctx); + } else { + ctx->set_text_bg_default(ctx); + } + } + continue; + } + + else if (ctx->esc_values[i] == 25) { + ctx->bg_bold = false; + if (ctx->current_bg != (size_t)-1) { + if (!ctx->reverse_video) { + ctx->set_text_bg(ctx, ctx->current_bg); + } else { + ctx->set_text_fg(ctx, ctx->current_bg); + } + } else { + if (!ctx->reverse_video) { + ctx->set_text_bg_default(ctx); + } else { + ctx->set_text_fg_default(ctx); + } + } + continue; + } + + else if (ctx->esc_values[i] >= 30 && ctx->esc_values[i] <= 37) { + offset = 30; + ctx->current_primary = ctx->esc_values[i] - offset; + + if (ctx->reverse_video) { + goto set_bg; + } + +set_fg: + if ((ctx->bold && !ctx->reverse_video) + || (ctx->bg_bold && ctx->reverse_video)) { + ctx->set_text_fg_bright(ctx, ctx->esc_values[i] - offset); + } else { + ctx->set_text_fg(ctx, ctx->esc_values[i] - offset); + } + continue; + } + + else if (ctx->esc_values[i] >= 40 && ctx->esc_values[i] <= 47) { + offset = 40; + ctx->current_bg = ctx->esc_values[i] - offset; + + if (ctx->reverse_video) { + goto set_fg; + } + +set_bg: + if ((ctx->bold && ctx->reverse_video) + || (ctx->bg_bold && !ctx->reverse_video)) { + ctx->set_text_bg_bright(ctx, ctx->esc_values[i] - offset); + } else { + ctx->set_text_bg(ctx, ctx->esc_values[i] - offset); + } + continue; + } + + else if (ctx->esc_values[i] >= 90 && ctx->esc_values[i] <= 97) { + offset = 90; + ctx->current_primary = ctx->esc_values[i] - offset; + + if (ctx->reverse_video) { + goto set_bg_bright; + } + +set_fg_bright: + ctx->set_text_fg_bright(ctx, ctx->esc_values[i] - offset); + continue; + } + + else if (ctx->esc_values[i] >= 100 && ctx->esc_values[i] <= 107) { + offset = 100; + ctx->current_bg = ctx->esc_values[i] - offset; + + if (ctx->reverse_video) { + goto set_fg_bright; + } + +set_bg_bright: + ctx->set_text_bg_bright(ctx, ctx->esc_values[i] - offset); + continue; + } + + else if (ctx->esc_values[i] == 39) { + ctx->current_primary = (size_t)-1; + + if (ctx->reverse_video) { + ctx->swap_palette(ctx); + } + + if (!ctx->bold) { + ctx->set_text_fg_default(ctx); + } else { + ctx->set_text_fg_default_bright(ctx); + } + + if (ctx->reverse_video) { + ctx->swap_palette(ctx); + } + + continue; + } + + else if (ctx->esc_values[i] == 49) { + ctx->current_bg = (size_t)-1; + + if (ctx->reverse_video) { + ctx->swap_palette(ctx); + } + + if (!ctx->bg_bold) { + ctx->set_text_bg_default(ctx); + } else { + ctx->set_text_bg_default_bright(ctx); + } + + if (ctx->reverse_video) { + ctx->swap_palette(ctx); + } + + continue; + } + + else if (ctx->esc_values[i] == 7) { + if (!ctx->reverse_video) { + ctx->reverse_video = true; + ctx->swap_palette(ctx); + } + continue; + } + + else if (ctx->esc_values[i] == 27) { + if (ctx->reverse_video) { + ctx->reverse_video = false; + ctx->swap_palette(ctx); + } + continue; + } + + // 256/RGB + else if (ctx->esc_values[i] == 38 || ctx->esc_values[i] == 48) { + bool fg = ctx->esc_values[i] == 38; + + i++; + if (i >= ctx->esc_values_i) { + break; + } + + switch (ctx->esc_values[i]) { + case 2: { // RGB + if (i + 3 >= ctx->esc_values_i) { + goto out; + } + + uint32_t rgb_value = 0; + + rgb_value |= ctx->esc_values[i + 1] << 16; + rgb_value |= ctx->esc_values[i + 2] << 8; + rgb_value |= ctx->esc_values[i + 3]; + + i += 3; + + (fg ? ctx->set_text_fg_rgb : ctx->set_text_bg_rgb)(ctx, rgb_value); + + break; + } + case 5: { // 256 colors + if (i + 1 >= ctx->esc_values_i) { + goto out; + } + + uint32_t col = ctx->esc_values[i + 1]; + + i++; + + if (col < 8) { + (fg ? ctx->set_text_fg : ctx->set_text_bg)(ctx, col); + } else if (col < 16) { + (fg ? ctx->set_text_fg_bright : ctx->set_text_bg_bright)(ctx, col - 8); + } else if (col < 256) { + uint32_t rgb_value = col256[col - 16]; + (fg ? ctx->set_text_fg_rgb : ctx->set_text_bg_rgb)(ctx, rgb_value); + } + + break; + } + default: continue; + } + } + } + +out:; +} + +static void dec_private_parse(struct flanterm_context *ctx, uint8_t c) { + ctx->dec_private = false; + + if (ctx->esc_values_i == 0) { + return; + } + + bool set; + + switch (c) { + case 'h': + set = true; break; + case 'l': + set = false; break; + default: + return; + } + + switch (ctx->esc_values[0]) { + case 25: { + if (set) { + ctx->cursor_enabled = true; + } else { + ctx->cursor_enabled = false; + } + return; + } + } + + if (ctx->callback != NULL) { + ctx->callback(ctx, FLANTERM_CB_DEC, ctx->esc_values_i, (uintptr_t)ctx->esc_values, c); + } +} + +static void linux_private_parse(struct flanterm_context *ctx) { + if (ctx->esc_values_i == 0) { + return; + } + + if (ctx->callback != NULL) { + ctx->callback(ctx, FLANTERM_CB_LINUX, ctx->esc_values_i, (uintptr_t)ctx->esc_values, 0); + } +} + +static void mode_toggle(struct flanterm_context *ctx, uint8_t c) { + if (ctx->esc_values_i == 0) { + return; + } + + bool set; + + switch (c) { + case 'h': + set = true; break; + case 'l': + set = false; break; + default: + return; + } + + switch (ctx->esc_values[0]) { + case 4: + ctx->insert_mode = set; return; + } + + if (ctx->callback != NULL) { + ctx->callback(ctx, FLANTERM_CB_MODE, ctx->esc_values_i, (uintptr_t)ctx->esc_values, c); + } +} + +static void osc_parse(struct flanterm_context *ctx, uint8_t c) { + if (ctx->osc_escape && c == '\\') { + goto cleanup; + } + + ctx->osc_escape = false; + + switch (c) { + case 0x1b: + ctx->osc_escape = true; + break; + case '\a': + goto cleanup; + } + + return; + +cleanup: + ctx->osc_escape = false; + ctx->osc = false; + ctx->escape = false; +} + +static void control_sequence_parse(struct flanterm_context *ctx, uint8_t c) { + if (ctx->escape_offset == 2) { + switch (c) { + case '[': + ctx->discard_next = true; + goto cleanup; + case '?': + ctx->dec_private = true; + return; + } + } + + if (c >= '0' && c <= '9') { + if (ctx->esc_values_i == FLANTERM_MAX_ESC_VALUES) { + return; + } + ctx->rrr = true; + ctx->esc_values[ctx->esc_values_i] *= 10; + ctx->esc_values[ctx->esc_values_i] += c - '0'; + return; + } + + if (ctx->rrr == true) { + ctx->esc_values_i++; + ctx->rrr = false; + if (c == ';') + return; + } else if (c == ';') { + if (ctx->esc_values_i == FLANTERM_MAX_ESC_VALUES) { + return; + } + ctx->esc_values[ctx->esc_values_i] = 0; + ctx->esc_values_i++; + return; + } + + size_t esc_default; + switch (c) { + case 'J': case 'K': case 'q': + esc_default = 0; break; + default: + esc_default = 1; break; + } + + for (size_t i = ctx->esc_values_i; i < FLANTERM_MAX_ESC_VALUES; i++) { + ctx->esc_values[i] = esc_default; + } + + if (ctx->dec_private == true) { + dec_private_parse(ctx, c); + goto cleanup; + } + + bool r = ctx->scroll_enabled; + ctx->scroll_enabled = false; + size_t x, y; + ctx->get_cursor_pos(ctx, &x, &y); + + switch (c) { + case 'F': + x = 0; + // FALLTHRU + case 'A': { + if (ctx->esc_values[0] > y) + ctx->esc_values[0] = y; + size_t orig_y = y; + size_t dest_y = y - ctx->esc_values[0]; + bool will_be_in_scroll_region = false; + if ((ctx->scroll_top_margin >= dest_y && ctx->scroll_top_margin <= orig_y) + || (ctx->scroll_bottom_margin >= dest_y && ctx->scroll_bottom_margin <= orig_y)) { + will_be_in_scroll_region = true; + } + if (will_be_in_scroll_region && dest_y < ctx->scroll_top_margin) { + dest_y = ctx->scroll_top_margin; + } + ctx->set_cursor_pos(ctx, x, dest_y); + break; + } + case 'E': + x = 0; + // FALLTHRU + case 'e': + case 'B': { + if (y + ctx->esc_values[0] > ctx->rows - 1) + ctx->esc_values[0] = (ctx->rows - 1) - y; + size_t orig_y = y; + size_t dest_y = y + ctx->esc_values[0]; + bool will_be_in_scroll_region = false; + if ((ctx->scroll_top_margin >= orig_y && ctx->scroll_top_margin <= dest_y) + || (ctx->scroll_bottom_margin >= orig_y && ctx->scroll_bottom_margin <= dest_y)) { + will_be_in_scroll_region = true; + } + if (will_be_in_scroll_region && dest_y >= ctx->scroll_bottom_margin) { + dest_y = ctx->scroll_bottom_margin - 1; + } + ctx->set_cursor_pos(ctx, x, dest_y); + break; + } + case 'a': + case 'C': + if (x + ctx->esc_values[0] > ctx->cols - 1) + ctx->esc_values[0] = (ctx->cols - 1) - x; + ctx->set_cursor_pos(ctx, x + ctx->esc_values[0], y); + break; + case 'D': + if (ctx->esc_values[0] > x) + ctx->esc_values[0] = x; + ctx->set_cursor_pos(ctx, x - ctx->esc_values[0], y); + break; + case 'c': + if (ctx->callback != NULL) { + ctx->callback(ctx, FLANTERM_CB_PRIVATE_ID, 0, 0, 0); + } + break; + case 'd': + ctx->esc_values[0] -= 1; + if (ctx->esc_values[0] >= ctx->rows) + ctx->esc_values[0] = ctx->rows - 1; + ctx->set_cursor_pos(ctx, x, ctx->esc_values[0]); + break; + case 'G': + case '`': + ctx->esc_values[0] -= 1; + if (ctx->esc_values[0] >= ctx->cols) + ctx->esc_values[0] = ctx->cols - 1; + ctx->set_cursor_pos(ctx, ctx->esc_values[0], y); + break; + case 'H': + case 'f': + if (ctx->esc_values[0] != 0) { + ctx->esc_values[0]--; + } + if (ctx->esc_values[1] != 0) { + ctx->esc_values[1]--; + } + if (ctx->esc_values[1] >= ctx->cols) + ctx->esc_values[1] = ctx->cols - 1; + if (ctx->esc_values[0] >= ctx->rows) + ctx->esc_values[0] = ctx->rows - 1; + ctx->set_cursor_pos(ctx, ctx->esc_values[1], ctx->esc_values[0]); + break; + case 'M': { + size_t count = ctx->esc_values[0] > ctx->rows ? ctx->rows : ctx->esc_values[0]; + for (size_t i = 0; i < count; i++) { + ctx->scroll(ctx); + } + break; + } + case 'L': { + size_t old_scroll_top_margin = ctx->scroll_top_margin; + ctx->scroll_top_margin = y; + size_t count = ctx->esc_values[0] > ctx->rows ? ctx->rows : ctx->esc_values[0]; + for (size_t i = 0; i < count; i++) { + ctx->revscroll(ctx); + } + ctx->scroll_top_margin = old_scroll_top_margin; + break; + } + case 'n': + switch (ctx->esc_values[0]) { + case 5: + if (ctx->callback != NULL) { + ctx->callback(ctx, FLANTERM_CB_STATUS_REPORT, 0, 0, 0); + } + break; + case 6: + if (ctx->callback != NULL) { + ctx->callback(ctx, FLANTERM_CB_POS_REPORT, x + 1, y + 1, 0); + } + break; + } + break; + case 'q': + if (ctx->callback != NULL) { + ctx->callback(ctx, FLANTERM_CB_KBD_LEDS, ctx->esc_values[0], 0, 0); + } + break; + case 'J': + switch (ctx->esc_values[0]) { + case 0: { + size_t rows_remaining = ctx->rows - (y + 1); + size_t cols_diff = ctx->cols - (x + 1); + size_t to_clear = rows_remaining * ctx->cols + cols_diff + 1; + for (size_t i = 0; i < to_clear; i++) { + ctx->raw_putchar(ctx, ' '); + } + ctx->set_cursor_pos(ctx, x, y); + break; + } + case 1: { + ctx->set_cursor_pos(ctx, 0, 0); + bool b = false; + for (size_t yc = 0; yc < ctx->rows; yc++) { + for (size_t xc = 0; xc < ctx->cols; xc++) { + ctx->raw_putchar(ctx, ' '); + if (xc == x && yc == y) { + ctx->set_cursor_pos(ctx, x, y); + b = true; + break; + } + } + if (b == true) + break; + } + break; + } + case 2: + case 3: + ctx->clear(ctx, false); + break; + } + break; + case '@': + for (size_t i = ctx->cols - 1; ; i--) { + ctx->move_character(ctx, i + ctx->esc_values[0], y, i, y); + ctx->set_cursor_pos(ctx, i, y); + ctx->raw_putchar(ctx, ' '); + if (i == x) { + break; + } + } + ctx->set_cursor_pos(ctx, x, y); + break; + case 'P': + for (size_t i = x + ctx->esc_values[0]; i < ctx->cols; i++) + ctx->move_character(ctx, i - ctx->esc_values[0], y, i, y); + ctx->set_cursor_pos(ctx, ctx->cols - ctx->esc_values[0], y); + // FALLTHRU + case 'X': { + size_t count = ctx->esc_values[0] > ctx->cols ? ctx->cols : ctx->esc_values[0]; + for (size_t i = 0; i < count; i++) + ctx->raw_putchar(ctx, ' '); + ctx->set_cursor_pos(ctx, x, y); + break; + } + case 'm': + sgr(ctx); + break; + case 's': + ctx->get_cursor_pos(ctx, &ctx->saved_cursor_x, &ctx->saved_cursor_y); + break; + case 'u': + ctx->set_cursor_pos(ctx, ctx->saved_cursor_x, ctx->saved_cursor_y); + break; + case 'K': + switch (ctx->esc_values[0]) { + case 0: { + for (size_t i = x; i < ctx->cols; i++) + ctx->raw_putchar(ctx, ' '); + ctx->set_cursor_pos(ctx, x, y); + break; + } + case 1: { + ctx->set_cursor_pos(ctx, 0, y); + for (size_t i = 0; i < x; i++) + ctx->raw_putchar(ctx, ' '); + break; + } + case 2: { + ctx->set_cursor_pos(ctx, 0, y); + for (size_t i = 0; i < ctx->cols; i++) + ctx->raw_putchar(ctx, ' '); + ctx->set_cursor_pos(ctx, x, y); + break; + } + } + break; + case 'r': + if (ctx->esc_values[0] == 0) { + ctx->esc_values[0] = 1; + } + if (ctx->esc_values[1] == 0) { + ctx->esc_values[1] = 1; + } + ctx->scroll_top_margin = 0; + ctx->scroll_bottom_margin = ctx->rows; + if (ctx->esc_values_i > 0) { + ctx->scroll_top_margin = ctx->esc_values[0] - 1; + } + if (ctx->esc_values_i > 1) { + ctx->scroll_bottom_margin = ctx->esc_values[1]; + } + if (ctx->scroll_top_margin >= ctx->rows + || ctx->scroll_bottom_margin > ctx->rows + || ctx->scroll_top_margin >= (ctx->scroll_bottom_margin - 1)) { + ctx->scroll_top_margin = 0; + ctx->scroll_bottom_margin = ctx->rows; + } + ctx->set_cursor_pos(ctx, 0, 0); + break; + case 'l': + case 'h': + mode_toggle(ctx, c); + break; + case ']': + linux_private_parse(ctx); + break; + } + + ctx->scroll_enabled = r; + +cleanup: + ctx->control_sequence = false; + ctx->escape = false; +} + +static void restore_state(struct flanterm_context *ctx) { + ctx->bold = ctx->saved_state_bold; + ctx->bg_bold = ctx->saved_state_bg_bold; + ctx->reverse_video = ctx->saved_state_reverse_video; + ctx->current_charset = ctx->saved_state_current_charset; + ctx->current_primary = ctx->saved_state_current_primary; + ctx->current_bg = ctx->saved_state_current_bg; + + ctx->restore_state(ctx); +} + +static void save_state(struct flanterm_context *ctx) { + ctx->save_state(ctx); + + ctx->saved_state_bold = ctx->bold; + ctx->saved_state_bg_bold = ctx->bg_bold; + ctx->saved_state_reverse_video = ctx->reverse_video; + ctx->saved_state_current_charset = ctx->current_charset; + ctx->saved_state_current_primary = ctx->current_primary; + ctx->saved_state_current_bg = ctx->current_bg; +} + +static void escape_parse(struct flanterm_context *ctx, uint8_t c) { + ctx->escape_offset++; + + if (ctx->osc == true) { + osc_parse(ctx, c); + return; + } + + if (ctx->control_sequence == true) { + control_sequence_parse(ctx, c); + return; + } + + size_t x, y; + ctx->get_cursor_pos(ctx, &x, &y); + + switch (c) { + case ']': + ctx->osc_escape = false; + ctx->osc = true; + return; + case '[': + for (size_t i = 0; i < FLANTERM_MAX_ESC_VALUES; i++) + ctx->esc_values[i] = 0; + ctx->esc_values_i = 0; + ctx->rrr = false; + ctx->control_sequence = true; + return; + case '7': + save_state(ctx); + break; + case '8': + restore_state(ctx); + break; + case 'c': + flanterm_context_reinit(ctx); + ctx->clear(ctx, true); + break; + case 'D': + if (y == ctx->scroll_bottom_margin - 1) { + ctx->scroll(ctx); + ctx->set_cursor_pos(ctx, x, y); + } else { + ctx->set_cursor_pos(ctx, x, y + 1); + } + break; + case 'E': + if (y == ctx->scroll_bottom_margin - 1) { + ctx->scroll(ctx); + ctx->set_cursor_pos(ctx, 0, y); + } else { + ctx->set_cursor_pos(ctx, 0, y + 1); + } + break; + case 'M': + // "Reverse linefeed" + if (y == ctx->scroll_top_margin) { + ctx->revscroll(ctx); + ctx->set_cursor_pos(ctx, 0, y); + } else { + ctx->set_cursor_pos(ctx, 0, y - 1); + } + break; + case 'Z': + if (ctx->callback != NULL) { + ctx->callback(ctx, FLANTERM_CB_PRIVATE_ID, 0, 0, 0); + } + break; + case '(': + case ')': + ctx->g_select = c - '\''; + break; + } + + ctx->escape = false; +} + +static bool dec_special_print(struct flanterm_context *ctx, uint8_t c) { +#define FLANTERM_DEC_SPCL_PRN(C) ctx->raw_putchar(ctx, (C)); return true; + switch (c) { + case '`': FLANTERM_DEC_SPCL_PRN(0x04) + case '0': FLANTERM_DEC_SPCL_PRN(0xdb) + case '-': FLANTERM_DEC_SPCL_PRN(0x18) + case ',': FLANTERM_DEC_SPCL_PRN(0x1b) + case '.': FLANTERM_DEC_SPCL_PRN(0x19) + case 'a': FLANTERM_DEC_SPCL_PRN(0xb1) + case 'f': FLANTERM_DEC_SPCL_PRN(0xf8) + case 'g': FLANTERM_DEC_SPCL_PRN(0xf1) + case 'h': FLANTERM_DEC_SPCL_PRN(0xb0) + case 'j': FLANTERM_DEC_SPCL_PRN(0xd9) + case 'k': FLANTERM_DEC_SPCL_PRN(0xbf) + case 'l': FLANTERM_DEC_SPCL_PRN(0xda) + case 'm': FLANTERM_DEC_SPCL_PRN(0xc0) + case 'n': FLANTERM_DEC_SPCL_PRN(0xc5) + case 'q': FLANTERM_DEC_SPCL_PRN(0xc4) + case 's': FLANTERM_DEC_SPCL_PRN(0x5f) + case 't': FLANTERM_DEC_SPCL_PRN(0xc3) + case 'u': FLANTERM_DEC_SPCL_PRN(0xb4) + case 'v': FLANTERM_DEC_SPCL_PRN(0xc1) + case 'w': FLANTERM_DEC_SPCL_PRN(0xc2) + case 'x': FLANTERM_DEC_SPCL_PRN(0xb3) + case 'y': FLANTERM_DEC_SPCL_PRN(0xf3) + case 'z': FLANTERM_DEC_SPCL_PRN(0xf2) + case '~': FLANTERM_DEC_SPCL_PRN(0xfa) + case '_': FLANTERM_DEC_SPCL_PRN(0xff) + case '+': FLANTERM_DEC_SPCL_PRN(0x1a) + case '{': FLANTERM_DEC_SPCL_PRN(0xe3) + case '}': FLANTERM_DEC_SPCL_PRN(0x9c) + } +#undef FLANTERM_DEC_SPCL_PRN + + return false; +} + +// Following wcwidth related code inherited from: +// https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c + +struct interval { + uint32_t first; + uint32_t last; +}; + +/* auxiliary function for binary search in interval table */ +static int bisearch(uint32_t ucs, const struct interval *table, int max) { + int min = 0; + int mid; + + if (ucs < table[0].first || ucs > table[max].last) + return 0; + while (max >= min) { + mid = (min + max) / 2; + if (ucs > table[mid].last) + min = mid + 1; + else if (ucs < table[mid].first) + max = mid - 1; + else + return 1; + } + + return 0; +} + +int mk_wcwidth(uint32_t ucs) { + /* sorted list of non-overlapping intervals of non-spacing characters */ + /* generated by "uniset +cat=Me +cat=Mn +cat=Cf -00AD +1160-11FF +200B c" */ + static const struct interval combining[] = { + { 0x0300, 0x036F }, { 0x0483, 0x0486 }, { 0x0488, 0x0489 }, + { 0x0591, 0x05BD }, { 0x05BF, 0x05BF }, { 0x05C1, 0x05C2 }, + { 0x05C4, 0x05C5 }, { 0x05C7, 0x05C7 }, { 0x0600, 0x0603 }, + { 0x0610, 0x0615 }, { 0x064B, 0x065E }, { 0x0670, 0x0670 }, + { 0x06D6, 0x06E4 }, { 0x06E7, 0x06E8 }, { 0x06EA, 0x06ED }, + { 0x070F, 0x070F }, { 0x0711, 0x0711 }, { 0x0730, 0x074A }, + { 0x07A6, 0x07B0 }, { 0x07EB, 0x07F3 }, { 0x0901, 0x0902 }, + { 0x093C, 0x093C }, { 0x0941, 0x0948 }, { 0x094D, 0x094D }, + { 0x0951, 0x0954 }, { 0x0962, 0x0963 }, { 0x0981, 0x0981 }, + { 0x09BC, 0x09BC }, { 0x09C1, 0x09C4 }, { 0x09CD, 0x09CD }, + { 0x09E2, 0x09E3 }, { 0x0A01, 0x0A02 }, { 0x0A3C, 0x0A3C }, + { 0x0A41, 0x0A42 }, { 0x0A47, 0x0A48 }, { 0x0A4B, 0x0A4D }, + { 0x0A70, 0x0A71 }, { 0x0A81, 0x0A82 }, { 0x0ABC, 0x0ABC }, + { 0x0AC1, 0x0AC5 }, { 0x0AC7, 0x0AC8 }, { 0x0ACD, 0x0ACD }, + { 0x0AE2, 0x0AE3 }, { 0x0B01, 0x0B01 }, { 0x0B3C, 0x0B3C }, + { 0x0B3F, 0x0B3F }, { 0x0B41, 0x0B43 }, { 0x0B4D, 0x0B4D }, + { 0x0B56, 0x0B56 }, { 0x0B82, 0x0B82 }, { 0x0BC0, 0x0BC0 }, + { 0x0BCD, 0x0BCD }, { 0x0C3E, 0x0C40 }, { 0x0C46, 0x0C48 }, + { 0x0C4A, 0x0C4D }, { 0x0C55, 0x0C56 }, { 0x0CBC, 0x0CBC }, + { 0x0CBF, 0x0CBF }, { 0x0CC6, 0x0CC6 }, { 0x0CCC, 0x0CCD }, + { 0x0CE2, 0x0CE3 }, { 0x0D41, 0x0D43 }, { 0x0D4D, 0x0D4D }, + { 0x0DCA, 0x0DCA }, { 0x0DD2, 0x0DD4 }, { 0x0DD6, 0x0DD6 }, + { 0x0E31, 0x0E31 }, { 0x0E34, 0x0E3A }, { 0x0E47, 0x0E4E }, + { 0x0EB1, 0x0EB1 }, { 0x0EB4, 0x0EB9 }, { 0x0EBB, 0x0EBC }, + { 0x0EC8, 0x0ECD }, { 0x0F18, 0x0F19 }, { 0x0F35, 0x0F35 }, + { 0x0F37, 0x0F37 }, { 0x0F39, 0x0F39 }, { 0x0F71, 0x0F7E }, + { 0x0F80, 0x0F84 }, { 0x0F86, 0x0F87 }, { 0x0F90, 0x0F97 }, + { 0x0F99, 0x0FBC }, { 0x0FC6, 0x0FC6 }, { 0x102D, 0x1030 }, + { 0x1032, 0x1032 }, { 0x1036, 0x1037 }, { 0x1039, 0x1039 }, + { 0x1058, 0x1059 }, { 0x1160, 0x11FF }, { 0x135F, 0x135F }, + { 0x1712, 0x1714 }, { 0x1732, 0x1734 }, { 0x1752, 0x1753 }, + { 0x1772, 0x1773 }, { 0x17B4, 0x17B5 }, { 0x17B7, 0x17BD }, + { 0x17C6, 0x17C6 }, { 0x17C9, 0x17D3 }, { 0x17DD, 0x17DD }, + { 0x180B, 0x180D }, { 0x18A9, 0x18A9 }, { 0x1920, 0x1922 }, + { 0x1927, 0x1928 }, { 0x1932, 0x1932 }, { 0x1939, 0x193B }, + { 0x1A17, 0x1A18 }, { 0x1B00, 0x1B03 }, { 0x1B34, 0x1B34 }, + { 0x1B36, 0x1B3A }, { 0x1B3C, 0x1B3C }, { 0x1B42, 0x1B42 }, + { 0x1B6B, 0x1B73 }, { 0x1DC0, 0x1DCA }, { 0x1DFE, 0x1DFF }, + { 0x200B, 0x200F }, { 0x202A, 0x202E }, { 0x2060, 0x2063 }, + { 0x206A, 0x206F }, { 0x20D0, 0x20EF }, { 0x302A, 0x302F }, + { 0x3099, 0x309A }, { 0xA806, 0xA806 }, { 0xA80B, 0xA80B }, + { 0xA825, 0xA826 }, { 0xFB1E, 0xFB1E }, { 0xFE00, 0xFE0F }, + { 0xFE20, 0xFE23 }, { 0xFEFF, 0xFEFF }, { 0xFFF9, 0xFFFB }, + { 0x10A01, 0x10A03 }, { 0x10A05, 0x10A06 }, { 0x10A0C, 0x10A0F }, + { 0x10A38, 0x10A3A }, { 0x10A3F, 0x10A3F }, { 0x1D167, 0x1D169 }, + { 0x1D173, 0x1D182 }, { 0x1D185, 0x1D18B }, { 0x1D1AA, 0x1D1AD }, + { 0x1D242, 0x1D244 }, { 0xE0001, 0xE0001 }, { 0xE0020, 0xE007F }, + { 0xE0100, 0xE01EF } + }; + + /* test for 8-bit control characters */ + if (ucs == 0) + return 0; + if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0)) + return 1; + + /* binary search in table of non-spacing characters */ + if (bisearch(ucs, combining, + sizeof(combining) / sizeof(struct interval) - 1)) + return 0; + + /* if we arrive here, ucs is not a combining or C0/C1 control character */ + + return 1 + + (ucs >= 0x1100 && + (ucs <= 0x115f || /* Hangul Jamo init. consonants */ + ucs == 0x2329 || ucs == 0x232a || + (ucs >= 0x2e80 && ucs <= 0xa4cf && + ucs != 0x303f) || /* CJK ... Yi */ + (ucs >= 0xac00 && ucs <= 0xd7a3) || /* Hangul Syllables */ + (ucs >= 0xf900 && ucs <= 0xfaff) || /* CJK Compatibility Ideographs */ + (ucs >= 0xfe10 && ucs <= 0xfe19) || /* Vertical forms */ + (ucs >= 0xfe30 && ucs <= 0xfe6f) || /* CJK Compatibility Forms */ + (ucs >= 0xff00 && ucs <= 0xff60) || /* Fullwidth Forms */ + (ucs >= 0xffe0 && ucs <= 0xffe6) || + (ucs >= 0x20000 && ucs <= 0x2fffd) || + (ucs >= 0x30000 && ucs <= 0x3fffd))); +} + +// End of https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c inherited code + +static int unicode_to_cp437(uint64_t code_point) { + switch (code_point) { + case 0x263a: return 1; + case 0x263b: return 2; + case 0x2665: return 3; + case 0x2666: return 4; + case 0x2663: return 5; + case 0x2660: return 6; + case 0x2022: return 7; + case 0x25d8: return 8; + case 0x25cb: return 9; + case 0x25d9: return 10; + case 0x2642: return 11; + case 0x2640: return 12; + case 0x266a: return 13; + case 0x266b: return 14; + case 0x263c: return 15; + case 0x25ba: return 16; + case 0x25c4: return 17; + case 0x2195: return 18; + case 0x203c: return 19; + case 0x00b6: return 20; + case 0x00a7: return 21; + case 0x25ac: return 22; + case 0x21a8: return 23; + case 0x2191: return 24; + case 0x2193: return 25; + case 0x2192: return 26; + case 0x2190: return 27; + case 0x221f: return 28; + case 0x2194: return 29; + case 0x25b2: return 30; + case 0x25bc: return 31; + + case 0x2302: return 127; + case 0x00c7: return 128; + case 0x00fc: return 129; + case 0x00e9: return 130; + case 0x00e2: return 131; + case 0x00e4: return 132; + case 0x00e0: return 133; + case 0x00e5: return 134; + case 0x00e7: return 135; + case 0x00ea: return 136; + case 0x00eb: return 137; + case 0x00e8: return 138; + case 0x00ef: return 139; + case 0x00ee: return 140; + case 0x00ec: return 141; + case 0x00c4: return 142; + case 0x00c5: return 143; + case 0x00c9: return 144; + case 0x00e6: return 145; + case 0x00c6: return 146; + case 0x00f4: return 147; + case 0x00f6: return 148; + case 0x00f2: return 149; + case 0x00fb: return 150; + case 0x00f9: return 151; + case 0x00ff: return 152; + case 0x00d6: return 153; + case 0x00dc: return 154; + case 0x00a2: return 155; + case 0x00a3: return 156; + case 0x00a5: return 157; + case 0x20a7: return 158; + case 0x0192: return 159; + case 0x00e1: return 160; + case 0x00ed: return 161; + case 0x00f3: return 162; + case 0x00fa: return 163; + case 0x00f1: return 164; + case 0x00d1: return 165; + case 0x00aa: return 166; + case 0x00ba: return 167; + case 0x00bf: return 168; + case 0x2310: return 169; + case 0x00ac: return 170; + case 0x00bd: return 171; + case 0x00bc: return 172; + case 0x00a1: return 173; + case 0x00ab: return 174; + case 0x00bb: return 175; + case 0x2591: return 176; + case 0x2592: return 177; + case 0x2593: return 178; + case 0x2502: return 179; + case 0x2524: return 180; + case 0x2561: return 181; + case 0x2562: return 182; + case 0x2556: return 183; + case 0x2555: return 184; + case 0x2563: return 185; + case 0x2551: return 186; + case 0x2557: return 187; + case 0x255d: return 188; + case 0x255c: return 189; + case 0x255b: return 190; + case 0x2510: return 191; + case 0x2514: return 192; + case 0x2534: return 193; + case 0x252c: return 194; + case 0x251c: return 195; + case 0x2500: return 196; + case 0x253c: return 197; + case 0x255e: return 198; + case 0x255f: return 199; + case 0x255a: return 200; + case 0x2554: return 201; + case 0x2569: return 202; + case 0x2566: return 203; + case 0x2560: return 204; + case 0x2550: return 205; + case 0x256c: return 206; + case 0x2567: return 207; + case 0x2568: return 208; + case 0x2564: return 209; + case 0x2565: return 210; + case 0x2559: return 211; + case 0x2558: return 212; + case 0x2552: return 213; + case 0x2553: return 214; + case 0x256b: return 215; + case 0x256a: return 216; + case 0x2518: return 217; + case 0x250c: return 218; + case 0x2588: return 219; + case 0x2584: return 220; + case 0x258c: return 221; + case 0x2590: return 222; + case 0x2580: return 223; + case 0x03b1: return 224; + case 0x00df: return 225; + case 0x0393: return 226; + case 0x03c0: return 227; + case 0x03a3: return 228; + case 0x03c3: return 229; + case 0x00b5: return 230; + case 0x03c4: return 231; + case 0x03a6: return 232; + case 0x0398: return 233; + case 0x03a9: return 234; + case 0x03b4: return 235; + case 0x221e: return 236; + case 0x03c6: return 237; + case 0x03b5: return 238; + case 0x2229: return 239; + case 0x2261: return 240; + case 0x00b1: return 241; + case 0x2265: return 242; + case 0x2264: return 243; + case 0x2320: return 244; + case 0x2321: return 245; + case 0x00f7: return 246; + case 0x2248: return 247; + case 0x00b0: return 248; + case 0x2219: return 249; + case 0x00b7: return 250; + case 0x221a: return 251; + case 0x207f: return 252; + case 0x00b2: return 253; + case 0x25a0: return 254; + } + + return -1; +} + +static void flanterm_putchar(struct flanterm_context *ctx, uint8_t c) { + if (ctx->discard_next || (c == 0x18 || c == 0x1a)) { + ctx->discard_next = false; + ctx->escape = false; + ctx->control_sequence = false; + ctx->unicode_remaining = 0; + ctx->osc = false; + ctx->osc_escape = false; + ctx->g_select = 0; + return; + } + + if (ctx->unicode_remaining != 0) { + if ((c & 0xc0) != 0x80) { + ctx->unicode_remaining = 0; + goto unicode_error; + } + + ctx->unicode_remaining--; + ctx->code_point |= (uint64_t)(c & 0x3f) << (6 * ctx->unicode_remaining); + if (ctx->unicode_remaining != 0) { + return; + } + + int cc = unicode_to_cp437(ctx->code_point); + + if (cc == -1) { + size_t replacement_width = (size_t)mk_wcwidth(ctx->code_point); + if (replacement_width > 0) { + ctx->raw_putchar(ctx, 0xfe); + } + for (size_t i = 1; i < replacement_width; i++) { + ctx->raw_putchar(ctx, ' '); + } + } else { + ctx->raw_putchar(ctx, cc); + } + return; + } + +unicode_error: + if (c >= 0xc0 && c <= 0xf7) { + if (c >= 0xc0 && c <= 0xdf) { + ctx->unicode_remaining = 1; + ctx->code_point = (uint64_t)(c & 0x1f) << 6; + } else if (c >= 0xe0 && c <= 0xef) { + ctx->unicode_remaining = 2; + ctx->code_point = (uint64_t)(c & 0x0f) << (6 * 2); + } else if (c >= 0xf0 && c <= 0xf7) { + ctx->unicode_remaining = 3; + ctx->code_point = (uint64_t)(c & 0x07) << (6 * 3); + } + return; + } + + if (ctx->escape == true) { + escape_parse(ctx, c); + return; + } + + if (ctx->g_select) { + ctx->g_select--; + switch (c) { + case 'B': + ctx->charsets[ctx->g_select] = CHARSET_DEFAULT; break; + case '0': + ctx->charsets[ctx->g_select] = CHARSET_DEC_SPECIAL; break; + } + ctx->g_select = 0; + return; + } + + size_t x, y; + ctx->get_cursor_pos(ctx, &x, &y); + + switch (c) { + case 0x00: + case 0x7f: + return; + case 0x1b: + ctx->escape_offset = 0; + ctx->escape = true; + return; + case '\t': + if ((x / ctx->tab_size + 1) >= ctx->cols) { + ctx->set_cursor_pos(ctx, ctx->cols - 1, y); + return; + } + ctx->set_cursor_pos(ctx, (x / ctx->tab_size + 1) * ctx->tab_size, y); + return; + case 0x0b: + case 0x0c: + case '\n': + if (y == ctx->scroll_bottom_margin - 1) { + ctx->scroll(ctx); + ctx->set_cursor_pos(ctx, (ctx->oob_output & FLANTERM_OOB_OUTPUT_ONLCR) ? 0 : x, y); + } else { + ctx->set_cursor_pos(ctx, (ctx->oob_output & FLANTERM_OOB_OUTPUT_ONLCR) ? 0 : x, y + 1); + } + return; + case '\b': + ctx->set_cursor_pos(ctx, x - 1, y); + return; + case '\r': + ctx->set_cursor_pos(ctx, 0, y); + return; + case '\a': + // The bell is handled by the kernel + if (ctx->callback != NULL) { + ctx->callback(ctx, FLANTERM_CB_BELL, 0, 0, 0); + } + return; + case 14: + // Move to G1 set + ctx->current_charset = 1; + return; + case 15: + // Move to G0 set + ctx->current_charset = 0; + return; + } + + if (ctx->insert_mode == true) { + for (size_t i = ctx->cols - 1; ; i--) { + ctx->move_character(ctx, i + 1, y, i, y); + if (i == x) { + break; + } + } + } + + // Translate character set + switch (ctx->charsets[ctx->current_charset]) { + case CHARSET_DEFAULT: + break; + case CHARSET_DEC_SPECIAL: + if (dec_special_print(ctx, c)) { + return; + } + break; + } + + if (c >= 0x20 && c <= 0x7e) { + ctx->raw_putchar(ctx, c); + } else { + ctx->raw_putchar(ctx, 0xfe); + } +} diff --git a/src/drivers/fb/terminal/flanterm.h b/src/drivers/fb/terminal/flanterm.h new file mode 100644 index 0000000..9cc5ec2 --- /dev/null +++ b/src/drivers/fb/terminal/flanterm.h @@ -0,0 +1,137 @@ +/* Copyright (C) 2022-2024 mintsuki and contributors. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLANTERM_H +#define FLANTERM_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +#define FLANTERM_MAX_ESC_VALUES 16 + +#define FLANTERM_CB_DEC 10 +#define FLANTERM_CB_BELL 20 +#define FLANTERM_CB_PRIVATE_ID 30 +#define FLANTERM_CB_STATUS_REPORT 40 +#define FLANTERM_CB_POS_REPORT 50 +#define FLANTERM_CB_KBD_LEDS 60 +#define FLANTERM_CB_MODE 70 +#define FLANTERM_CB_LINUX 80 + +#define FLANTERM_OOB_OUTPUT_OCRNL (1 << 0) +#define FLANTERM_OOB_OUTPUT_OFDEL (1 << 1) +#define FLANTERM_OOB_OUTPUT_OFILL (1 << 2) +#define FLANTERM_OOB_OUTPUT_OLCUC (1 << 3) +#define FLANTERM_OOB_OUTPUT_ONLCR (1 << 4) +#define FLANTERM_OOB_OUTPUT_ONLRET (1 << 5) +#define FLANTERM_OOB_OUTPUT_ONOCR (1 << 6) +#define FLANTERM_OOB_OUTPUT_OPOST (1 << 7) + +struct flanterm_context { + /* internal use */ + + size_t tab_size; + bool autoflush; + bool cursor_enabled; + bool scroll_enabled; + bool control_sequence; + bool escape; + bool osc; + bool osc_escape; + bool rrr; + bool discard_next; + bool bold; + bool bg_bold; + bool reverse_video; + bool dec_private; + bool insert_mode; + uint64_t code_point; + size_t unicode_remaining; + uint8_t g_select; + uint8_t charsets[2]; + size_t current_charset; + size_t escape_offset; + size_t esc_values_i; + size_t saved_cursor_x; + size_t saved_cursor_y; + size_t current_primary; + size_t current_bg; + size_t scroll_top_margin; + size_t scroll_bottom_margin; + uint32_t esc_values[FLANTERM_MAX_ESC_VALUES]; + uint64_t oob_output; + bool saved_state_bold; + bool saved_state_bg_bold; + bool saved_state_reverse_video; + size_t saved_state_current_charset; + size_t saved_state_current_primary; + size_t saved_state_current_bg; + + /* to be set by backend */ + + size_t rows, cols; + + void (*raw_putchar)(struct flanterm_context *, uint8_t c); + void (*clear)(struct flanterm_context *, bool move); + void (*set_cursor_pos)(struct flanterm_context *, size_t x, size_t y); + void (*get_cursor_pos)(struct flanterm_context *, size_t *x, size_t *y); + void (*set_text_fg)(struct flanterm_context *, size_t fg); + void (*set_text_bg)(struct flanterm_context *, size_t bg); + void (*set_text_fg_bright)(struct flanterm_context *, size_t fg); + void (*set_text_bg_bright)(struct flanterm_context *, size_t bg); + void (*set_text_fg_rgb)(struct flanterm_context *, uint32_t fg); + void (*set_text_bg_rgb)(struct flanterm_context *, uint32_t bg); + void (*set_text_fg_default)(struct flanterm_context *); + void (*set_text_bg_default)(struct flanterm_context *); + void (*set_text_fg_default_bright)(struct flanterm_context *); + void (*set_text_bg_default_bright)(struct flanterm_context *); + void (*move_character)(struct flanterm_context *, size_t new_x, size_t new_y, size_t old_x, size_t old_y); + void (*scroll)(struct flanterm_context *); + void (*revscroll)(struct flanterm_context *); + void (*swap_palette)(struct flanterm_context *); + void (*save_state)(struct flanterm_context *); + void (*restore_state)(struct flanterm_context *); + void (*double_buffer_flush)(struct flanterm_context *); + void (*full_refresh)(struct flanterm_context *); + void (*deinit)(struct flanterm_context *, void (*)(void *, size_t)); + + /* to be set by client */ + + void (*callback)(struct flanterm_context *, uint64_t, uint64_t, uint64_t, uint64_t); +}; + +void flanterm_context_reinit(struct flanterm_context *ctx); +void flanterm_write(struct flanterm_context *ctx, const char *buf, size_t count); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/drivers/tty/console.c b/src/drivers/tty/console.c new file mode 100644 index 0000000..b8de866 --- /dev/null +++ b/src/drivers/tty/console.c @@ -0,0 +1,154 @@ +#include "libk/debug.h" +#include "console.h" +#include "termios.h" +#include "libk/errno.h" +#include "drivers/fb/fb.h" +#include "mm/memory.h" +#include "libk/module.h" +#include "libk/resource.h" +#include +#include +#include + +struct console { + struct resource res; + struct termios term; + size_t width, height; + bool decckm; +}; + +struct console *console_device = NULL; + +static ssize_t console_write(struct resource *this, + struct f_description *description, const void *buf, + off_t offset, size_t count) { + (void)description; + (void)offset; + + if (!buf) { + errno = EFAULT; + return -1; + } + + spinlock_acquire_or_wait(&this->lock); + + char *r = (char *)buf; + for (size_t i = 0; i < count; i++) { + framebuffer_putchar(r[i]); + } + + spinlock_drop(&this->lock); + return count; +} + +int console_ioctl(struct resource *this, struct f_description *description, + uint64_t request, uint64_t arg) { + (void)description; + spinlock_acquire_or_wait(&this->lock); + + int ret = 0; + + switch (request) { + case TCGETS: { + struct termios *t = (void *)arg; + if (t) + *t = console_device->term; + break; + } + case TCSETS: + case TCSETSW: + case TCSETSF: { + struct termios *t = (void *)arg; + if (t) + console_device->term = *t; + break; + } + case TIOCGWINSZ: { + struct winsize *w = (void *)arg; + if (w) { + w->ws_row = framebuff.ctx->rows; + w->ws_col = framebuff.ctx->cols; + w->ws_xpixel = framebuff.width; + w->ws_ypixel = framebuff.height; + } else { + errno = EINVAL; + ret = -1; + } + break; + } + default: + errno = EINVAL; + ret = -1; + break; + } + + spinlock_drop(&this->lock); + return ret; +} + +static void dec_private(uint64_t esc_val_count, uint32_t *esc_values, + uint64_t final) { + (void)esc_val_count; + + switch (esc_values[0]) { + case 1: + switch (final) { + case 'h': + console_device->decckm = true; + break; + case 'l': + console_device->decckm = false; + break; + default: + break; + } + } +} + +static void term_callback(struct flanterm_context *term, uint64_t t, uint64_t a, + uint64_t b, uint64_t c) { + (void)term; + + switch (t) { + case 10: + dec_private(a, (void *)b, c); + } +} + +void console_init(void) { + console_device = resource_create(sizeof(struct console)); + + console_device->res.stat.st_size = 0; + console_device->res.stat.st_blocks = 0; + console_device->res.stat.st_blksize = 4096; + console_device->res.stat.st_rdev = resource_create_dev_id(); + console_device->res.stat.st_mode = 0644 | S_IFCHR; + + console_device->width = framebuff.width / 8; + console_device->height = framebuff.height / 16; + + console_device->term.c_iflag = BRKINT | IGNPAR | ICRNL | IXON | IMAXBEL; + console_device->term.c_oflag = OPOST | ONLCR; + console_device->term.c_cflag = CS8 | CREAD; + console_device->term.c_lflag = + ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHOCTL | ECHOKE; + console_device->term.c_cc[VINTR] = CTRL('C'); + console_device->term.c_cc[VEOF] = CTRL('D'); + console_device->term.c_cc[VSUSP] = CTRL('Z'); + + console_device->term.ibaud = 38400; + console_device->term.obaud = 38400; + + console_device->res.status |= POLLOUT; + + console_device->res.write = console_write; + console_device->res.ioctl = console_ioctl; + + console_device->decckm = false; + + devtmpfs_add_device((struct resource *)console_device, "console"); + + kprintffos(false, "Bye bye!\n"); + framebuffer_clear(0x00eee8d5, 0); + framebuff.ctx->callback = term_callback; +} diff --git a/src/drivers/tty/console.h b/src/drivers/tty/console.h new file mode 100644 index 0000000..454cc30 --- /dev/null +++ b/src/drivers/tty/console.h @@ -0,0 +1,3 @@ +#pragma once + +void console_init(void); diff --git a/src/drivers/tty/pty.c b/src/drivers/tty/pty.c new file mode 100644 index 0000000..ef06e5d --- /dev/null +++ b/src/drivers/tty/pty.c @@ -0,0 +1,518 @@ +#include "libk/debug.h" +#include "pty.h" +#include "libk/errno.h" +#include "mm/memory.h" +#include "sched/sched.h" +#include "fs/devtmpfs.h" +#include +#include "libk/string.h" + +static int pty_number = 0; + +static int pty_ioctl(struct resource *this, struct f_description *description, + uint64_t request, uint64_t arg) { + (void)description; + spinlock_acquire_or_wait(&this->lock); + + struct pty_slave *ps = (struct pty_slave *)this; + struct pty *p = ps->pty; + + int ret = 0; + + switch (request) { + case TCGETS: { + struct termios *t = (void *)arg; + if (t) + *t = p->term; + break; + } + case TCSETS: + case TCSETSW: + case TCSETSF: { + struct termios *t = (void *)arg; + if (t) + p->term = *t; + break; + } + case TIOCGWINSZ: { + struct winsize *w = (void *)arg; + if (w) + *w = p->ws; + break; + } + case TIOCSPGRP: { + break; + } + case TIOCSWINSZ: { + struct winsize *w = (void *)arg; + if (w) + p->ws = *w; + break; + } + case TIOCGPGRP: { + int *n = (int *)arg; + if (n) + *n = sched_get_running_thread()->mother_proc->pid; + break; + } + case TIOCGSID: { + int *n = (int *)arg; + if (n) + *n = sched_get_running_thread()->mother_proc->pid; + break; + } + case TIOCGPTN: { + int *n = (int *)arg; + if (n) + *n = p->name; + break; + } + case TIOCSPTLCK: { + break; + } + case TIOCSCTTY: { + break; + } + default: + errno = EINVAL; + ret = -1; + break; + } + + spinlock_drop(&this->lock); + return ret; +} + +static bool pty_master_unref(struct resource *this, + struct f_description *description) { + (void)description; + struct pty_master *pm = (struct pty_master *)this; + struct pty *p = pm->pty; + this->refcount--; + if (!this->refcount) { + p->pm->res.status |= POLLHUP; + p->pm->res.status &= ~POLLIN; + p->pm->res.status &= ~POLLOUT; + event_trigger(&p->ps->res.event, false); + kfree(p->in.data); + kfree(p->out.data); + } + return true; +} + +static ssize_t pty_master_read(struct resource *this, + struct f_description *description, void *buf, + off_t offset, size_t count) { + + (void)description; + (void)offset; + spinlock_acquire_or_wait(&this->lock); + + struct pty_master *pm = (struct pty_master *)this; + struct pty *p = pm->pty; + + ssize_t ret = 0; + + while (p->in.used == 0) { + if (p->ps->closed) { + errno = EIO; + ret = -1; + spinlock_drop(&this->lock); + return ret; + } + if (description->flags & O_NONBLOCK) { + goto end; + } + spinlock_drop(&this->lock); + struct event *events[] = {&p->pm->res.event}; + if (event_await(events, 1, true) < 0) { + errno = EINTR; + ret = -1; + return ret; + } + spinlock_acquire_or_wait(&this->lock); + } + + if (p->in.used < count) { + count = p->in.used; + } + + size_t before_wrap = 0, after_wrap = 0, new_ptr = 0; + if (p->in.read_ptr + count > p->in.data_length) { + before_wrap = p->in.data_length - p->in.read_ptr; + after_wrap = count - before_wrap; + new_ptr = after_wrap; + } else { + before_wrap = count; + after_wrap = 0; + new_ptr = p->in.read_ptr + count; + + if (new_ptr == p->in.data_length) { + new_ptr = 0; + } + } + + memcpy(buf, p->in.data + p->in.read_ptr, before_wrap); + if (after_wrap) { + memcpy(buf + before_wrap, p->in.data, after_wrap); + } + + p->in.read_ptr = new_ptr; + p->in.used -= count; + if (p->in.used < p->in.data_length) { + p->ps->res.status |= POLLOUT; + } + + if (p->in.used == 0) { + this->status &= ~POLLIN; + } + + event_trigger(&p->ps->res.event, false); + + ret = count; +end: + spinlock_drop(&this->lock); + return ret; +} + +static ssize_t pty_master_write(struct resource *this, + struct f_description *description, + const void *buf, off_t offset, size_t count) { + (void)description; + (void)offset; + + struct pty_master *pm = (struct pty_master *)this; + struct pty *p = pm->pty; + ssize_t ret = 0; + + spinlock_acquire_or_wait(&p->ps->res.lock); + + if (p->out.used == p->out.data_length) { + spinlock_drop(&p->ps->res.lock); + struct event *events[] = {&p->ps->res.event}; + if (event_await(events, 1, true) < 0) { + errno = EINTR; + ret = -1; + return ret; + } + spinlock_acquire_or_wait(&p->ps->res.lock); + } + + if (p->out.used + count > p->out.data_length) { + count = p->out.data_length - p->out.used; + } + + size_t before_wrap = 0, after_wrap = 0, new_ptr = 0; + if (p->out.write_ptr + count > p->out.data_length) { + before_wrap = p->out.data_length - p->out.write_ptr; + after_wrap = count - before_wrap; + new_ptr = after_wrap; + } else { + before_wrap = count; + after_wrap = 0; + new_ptr = p->out.write_ptr + count; + + if (new_ptr == p->out.data_length) { + new_ptr = 0; + } + } + + memcpy(p->out.data + p->out.write_ptr, buf, before_wrap); + if (after_wrap) { + memcpy(p->out.data, buf + before_wrap, after_wrap); + } + + p->out.write_ptr = new_ptr; + p->out.used += count; + + if (p->out.used == p->out.data_length) { + this->status &= ~POLLOUT; + } + + p->ps->res.status |= POLLIN; + + event_trigger(&p->ps->res.event, false); + ret = count; + + spinlock_drop(&p->ps->res.lock); + return ret; +} + +static bool pty_slave_unref(struct resource *this, + struct f_description *description) { + (void)description; + struct pty_slave *ps = (struct pty_slave *)this; + struct pty *p = ps->pty; + this->refcount--; + if (!this->refcount) { + ps->closed = true; + p->pm->res.status |= POLLHUP; + p->pm->res.status &= ~POLLIN; + p->pm->res.status &= ~POLLOUT; + event_trigger(&ps->pty->pm->res.event, false); + } + return true; +} + +static ssize_t pty_slave_read(struct resource *this, + struct f_description *description, void *buf, + off_t offset, size_t count) { + (void)description; + (void)offset; + spinlock_acquire_or_wait(&this->lock); + + struct pty_slave *ps = (struct pty_slave *)this; + struct pty *p = ps->pty; + + ssize_t ret = 0; + + while (p->out.used == 0) { + if (description->flags & O_NONBLOCK) { + goto end; + } + spinlock_drop(&this->lock); + struct event *events[] = {&p->ps->res.event}; + if (event_await(events, 1, true) < 0) { + errno = EINTR; + ret = -1; + return ret; + } + spinlock_acquire_or_wait(&this->lock); + } + + if (p->out.used < count) { + count = p->out.used; + } + + size_t before_wrap = 0, after_wrap = 0, new_ptr = 0; + if (p->out.read_ptr + count > p->out.data_length) { + before_wrap = p->out.data_length - p->out.read_ptr; + after_wrap = count - before_wrap; + new_ptr = after_wrap; + } else { + before_wrap = count; + after_wrap = 0; + new_ptr = p->out.read_ptr + count; + + if (new_ptr == p->out.data_length) { + new_ptr = 0; + } + } + + memcpy(buf, p->out.data + p->out.read_ptr, before_wrap); + if (after_wrap) { + memcpy(buf + before_wrap, p->out.data, after_wrap); + } + + p->out.read_ptr = new_ptr; + p->out.used -= count; + + if (p->out.used < p->out.data_length) { + p->pm->res.status |= POLLOUT; + } + + if (p->out.used == 0) { + this->status &= ~POLLIN; + } + + event_trigger(&p->pm->res.event, false); + + ret = count; +end: + spinlock_drop(&this->lock); + return ret; +} + +static ssize_t pty_slave_write(struct resource *this, + struct f_description *description, + const void *buf, off_t offset, size_t count) { + (void)description; + (void)offset; + + struct pty_slave *ps = (struct pty_slave *)this; + struct pty *p = ps->pty; + ssize_t ret = 0; + + spinlock_acquire_or_wait(&p->pm->res.lock); + if (p->in.used == p->in.data_length) { + spinlock_drop(&p->pm->res.lock); + struct event *events[] = {&p->pm->res.event}; + if (event_await(events, 1, true) < 0) { + errno = EINTR; + ret = -1; + return ret; + } + spinlock_acquire_or_wait(&p->pm->res.lock); + } + + char *buf_to_write_from = buf; + size_t newline_count = 0; + if ((p->term.c_oflag & ONLCR) && (p->term.c_oflag & OPOST)) { + char *buf_but_char = buf; + buf_to_write_from = kmalloc(count * 2); + if (!buf_to_write_from) { + spinlock_drop(&p->pm->res.lock); + errno = ENOMEM; + return -1; + } + memzero(buf_to_write_from, count * 2); + size_t k = 0; + for (size_t i = 0; i < count; i++) { + if (buf_but_char[i] == '\n') { + buf_to_write_from[k++] = '\r'; + newline_count++; + } + buf_to_write_from[k++] = buf_but_char[i]; + } + + if (newline_count == 0) { + kfree(buf_to_write_from); + buf_to_write_from = buf; + } + + count += newline_count; + } + + if (p->in.used + count > p->in.data_length) { + count = p->in.data_length - p->in.used; + } + + size_t before_wrap = 0, after_wrap = 0, new_ptr = 0; + if (p->in.write_ptr + count > p->in.data_length) { + before_wrap = p->in.data_length - p->in.write_ptr; + after_wrap = count - before_wrap; + new_ptr = after_wrap; + } else { + before_wrap = count; + after_wrap = 0; + new_ptr = p->in.write_ptr + count; + + if (new_ptr == p->in.data_length) { + new_ptr = 0; + } + } + + memcpy(p->in.data + p->in.write_ptr, buf_to_write_from, before_wrap); + if (after_wrap) { + memcpy(p->in.data, buf_to_write_from + before_wrap, after_wrap); + } + + p->in.write_ptr = new_ptr; + p->in.used += count; + + if (buf_to_write_from != buf) { + kfree(buf_to_write_from); + } + + if (p->in.used == p->in.data_length) { + this->status &= ~POLLOUT; + } + + count -= newline_count; + + p->pm->res.status |= POLLIN; + + event_trigger(&p->pm->res.event, false); + ret = count; + + spinlock_drop(&p->pm->res.lock); + return ret; +} + +void syscall_openpty(struct syscall_arguments *args) { + struct process *proc = sched_get_running_thread()->mother_proc; + + int *fds = (int *)(args->args0); + if (fds == NULL) { + errno = EFAULT; + args->ret = -1; + return; + } + + struct pty *p = kmalloc(sizeof(struct pty)); + if (!p) { + errno = ENOMEM; + args->ret = -1; + return; + } + + p->name = pty_number++; + + p->in.data = kmalloc(PAGE_SIZE * 32); + memzero(p->in.data, PAGE_SIZE * 32); + p->in.data_length = PAGE_SIZE * 32; + p->in.used = 0; + p->in.read_ptr = 0; + p->in.write_ptr = 0; + + p->out.data = kmalloc(PAGE_SIZE * 32); + memzero(p->out.data, PAGE_SIZE * 32); + p->out.data_length = PAGE_SIZE * 32; + p->out.used = 0; + p->out.read_ptr = 0; + p->out.write_ptr = 0; + + p->term.c_iflag = IGNBRK | BRKINT | IGNPAR | ICRNL | IXON; + p->term.c_oflag = OPOST | ONLCR; + p->term.c_cflag = CS8 | CREAD | HUPCL; + p->term.c_lflag = + ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHOCTL | ECHOKE | IEXTEN; + p->term.c_cc[VINTR] = CTRL('C'); + p->term.c_cc[VEOF] = CTRL('D'); + p->term.c_cc[VSUSP] = CTRL('Z'); + + p->term.ibaud = 38400; + p->term.obaud = 38400; + + p->ws.ws_row = 24; + p->ws.ws_col = 80; + + struct pty_slave *ps = resource_create(sizeof(struct pty_slave)); + struct pty_master *pm = resource_create(sizeof(struct pty_master)); + + if (!ps || !pm) { + errno = ENOMEM; + args->ret = -1; + return; + } + + ps->pty = p; + ps->res.read = pty_slave_read; + ps->res.write = pty_slave_write; + pm->res.unref = pty_slave_unref; + ps->res.ioctl = pty_ioctl; + ps->res.stat.st_size = 0; + ps->res.stat.st_blocks = 0; + ps->res.stat.st_blksize = 4096; + ps->res.stat.st_rdev = resource_create_dev_id(); + ps->res.stat.st_mode = 0644 | S_IFCHR; + ps->res.status = POLLOUT; + + pm->pty = p; + pm->res.read = pty_master_read; + pm->res.write = pty_master_write; + pm->res.unref = pty_master_unref; + pm->res.ioctl = pty_ioctl; + pm->res.status = POLLOUT; + + p->ps = ps; + p->pm = pm; + + fds[0] = fdnum_create_from_resource(proc, (struct resource *)pm, O_RDWR, 0, + false); + fds[1] = fdnum_create_from_resource(proc, (struct resource *)ps, O_RDWR, 0, + false); + + if (fds[0] == -1 || fds[1] == -1) { + args->ret = -1; + } + + char name[5 + 3 + 1] = "pty"; + char num[3 + 1] = {0}; + ultoa(p->name, num, 10); + strcat(name, num); + + devtmpfs_add_device((struct resource *)ps, name); + + args->ret = 0; +} diff --git a/src/drivers/tty/pty.h b/src/drivers/tty/pty.h new file mode 100644 index 0000000..03a371a --- /dev/null +++ b/src/drivers/tty/pty.h @@ -0,0 +1,37 @@ +#pragma once +#include +#include +#include "termios.h" +#include "sched/syscall.h" +#include "libk/resource.h" + +struct ring_buffer { + uint8_t *data; + size_t data_length; + uintptr_t read_ptr; + uintptr_t write_ptr; + size_t used; +}; + +struct pty { + int name; + struct pty_slave *ps; + struct pty_master *pm; + struct termios term; + struct winsize ws; + struct ring_buffer in; + struct ring_buffer out; +}; + +struct pty_slave { + struct resource res; + struct pty *pty; + bool closed; +}; + +struct pty_master { + struct resource res; + struct pty *pty; +}; + +void syscall_openpty(struct syscall_arguments *args); diff --git a/src/drivers/tty/termios.h b/src/drivers/tty/termios.h new file mode 100644 index 0000000..620ec81 --- /dev/null +++ b/src/drivers/tty/termios.h @@ -0,0 +1,182 @@ +#pragma once + +typedef unsigned char cc_t; +typedef unsigned int speed_t; +typedef unsigned int tcflag_t; + +// indices for the c_cc array in struct termios +#define NCCS 32 +#define VINTR 0 +#define VQUIT 1 +#define VERASE 2 +#define VKILL 3 +#define VEOF 4 +#define VTIME 5 +#define VMIN 6 +#define VSWTC 7 +#define VSTART 8 +#define VSTOP 9 +#define VSUSP 10 +#define VEOL 11 +#define VREPRINT 12 +#define VDISCARD 13 +#define VWERASE 14 +#define VLNEXT 15 +#define VEOL2 16 + +#define TTY_BUF_SIZE 1024 +// 0x54 is just a magic number to make these relatively unique ('T') +#define TCGETS 0x5401 +#define TCSETS 0x5402 +#define TCSETSW 0x5403 +#define TCSETSF 0x5404 +#define TCGETA 0x5405 +#define TCSETA 0x5406 +#define TCSETAW 0x5407 +#define TCSETAF 0x5408 +#define TCSBRK 0x5409 +#define TCXONC 0x540A +#define TCFLSH 0x540B +#define TIOCEXCL 0x540C +#define TIOCNXCL 0x540D +#define TIOCSCTTY 0x540E +#define TIOCGPGRP 0x540F +#define TIOCSPGRP 0x5410 +#define TIOCOUTQ 0x5411 +#define TIOCSTI 0x5412 +#define TIOCGWINSZ 0x5413 +#define TIOCSWINSZ 0x5414 +#define TIOCMGET 0x5415 +#define TIOCMBIS 0x5416 +#define TIOCMBIC 0x5417 +#define TIOCMSET 0x5418 +#define TIOCGSOFTCAR 0x5419 +#define TIOCSSOFTCAR 0x541A + +// bitwise flags for c_iflag in struct termios +#define IGNBRK 0000001 +#define BRKINT 0000002 +#define IGNPAR 0000004 +#define PARMRK 0000010 +#define INPCK 0000020 +#define ISTRIP 0000040 +#define INLCR 0000100 +#define IGNCR 0000200 +#define ICRNL 0000400 +#define IUCLC 0001000 +#define IXON 0002000 +#define IXANY 0004000 +#define IXOFF 0010000 +#define IMAXBEL 0020000 +#define IUTF8 0040000 + +// bitwise flags for c_oflag in struct termios +#define OPOST 0000001 +#define OLCUC 0000002 +#define ONLCR 0000004 +#define OCRNL 0000010 +#define ONOCR 0000020 +#define ONLRET 0000040 +#define OFILL 0000100 +#define OFDEL 0000200 + +#define NLDLY 0000400 +#define NL0 0000000 +#define NL1 0000400 + +#define CRDLY 0003000 +#define CR0 0000000 +#define CR1 0001000 +#define CR2 0002000 +#define CR3 0003000 + +#define TABDLY 0014000 +#define TAB0 0000000 +#define TAB1 0004000 +#define TAB2 0010000 +#define TAB3 0014000 + +#define BSDLY 0020000 +#define BS0 0000000 +#define BS1 0020000 + +#define FFDLY 0100000 +#define FF0 0000000 +#define FF1 0100000 + +#define VTDLY 0040000 +#define VT0 0000000 +#define VT1 0040000 + +// bitwise constants for c_cflag in struct termios +#define CSIZE 0000060 +#define CS5 0000000 +#define CS6 0000020 +#define CS7 0000040 +#define CS8 0000060 + +#define CSTOPB 0000100 +#define CREAD 0000200 +#define PARENB 0000400 +#define PARODD 0001000 +#define HUPCL 0002000 +#define CLOCAL 0004000 + +// bitwise constants for c_lflag in struct termios +#define ISIG 0000001 +#define ICANON 0000002 +#define ECHO 0000010 +#define ECHOE 0000020 +#define ECHOK 0000040 +#define ECHONL 0000100 +#define NOFLSH 0000200 +#define TOSTOP 0000400 +#define IEXTEN 0100000 + +#define EXTA 0000016 +#define EXTB 0000017 +#define CBAUD 0010017 +#define CBAUDEX 0010000 +#define CIBAUD 002003600000 +#define CMSPAR 010000000000 +#define CRTSCTS 020000000000 + +#define XCASE 0000004 +#define ECHOCTL 0001000 +#define ECHOPRT 0002000 +#define ECHOKE 0004000 +#define FLUSHO 0010000 +#define PENDIN 0040000 +#define EXTPROC 0200000 + +#define XTABS 0014000 + +struct winsize { + unsigned short ws_row; + unsigned short ws_col; + unsigned short ws_xpixel; + unsigned short ws_ypixel; +}; + +#define NCC 8 +struct termio { + unsigned short c_iflag; /* input mode flags */ + unsigned short c_oflag; /* output mode flags */ + unsigned short c_cflag; /* control mode flags */ + unsigned short c_lflag; /* local mode flags */ + unsigned char c_line; /* line discipline */ + unsigned char c_cc[NCC]; /* control characters */ +}; + +struct termios { + tcflag_t c_iflag; + tcflag_t c_oflag; + tcflag_t c_cflag; + tcflag_t c_lflag; + cc_t c_line; + cc_t c_cc[NCCS]; + speed_t ibaud; + speed_t obaud; +}; + +#define CTRL(x) ((x) & 037) \ No newline at end of file diff --git a/src/drivers/video/font.h b/src/drivers/video/font.h deleted file mode 100644 index f6e8866..0000000 --- a/src/drivers/video/font.h +++ /dev/null @@ -1,457 +0,0 @@ -#pragma once -#include - - -unsigned char zap_light16_psf[] = { - 0x36, 0x04, 0x02, 0x10, 0x00, 0x00, 0x00, 0x3e, 0x63, 0x5d, 0x7d, 0x7b, - 0x77, 0x77, 0x7f, 0x77, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x7e, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x22, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x7f, 0x04, 0x08, 0x10, 0x7f, 0x20, - 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x10, 0x20, 0x40, - 0x20, 0x10, 0x08, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x10, 0x08, 0x04, 0x02, 0x04, 0x08, 0x10, 0x00, 0x3e, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x7c, - 0xfe, 0x7c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x30, - 0x10, 0x11, 0x12, 0x04, 0x08, 0x12, 0x26, 0x4a, 0x0f, 0x02, 0x02, 0x00, - 0x00, 0x00, 0x10, 0x30, 0x10, 0x11, 0x12, 0x04, 0x08, 0x10, 0x26, 0x49, - 0x02, 0x04, 0x0f, 0x00, 0x00, 0x00, 0x70, 0x08, 0x30, 0x09, 0x72, 0x04, - 0x08, 0x12, 0x26, 0x4a, 0x0f, 0x02, 0x02, 0x00, 0x00, 0x00, 0x08, 0x08, - 0x08, 0x08, 0x08, 0x00, 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, - 0x00, 0x24, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x30, 0x00, 0x00, 0x07, 0x08, - 0x08, 0x08, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x70, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x08, 0x08, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, - 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x3e, 0x08, 0x08, - 0x08, 0x08, 0x3e, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, - 0xa4, 0xa4, 0x48, 0x10, 0x10, 0x2a, 0x55, 0x55, 0x8a, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0xf1, 0x5b, 0x55, 0x51, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x49, 0x49, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x08, 0x10, 0x20, 0x10, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x08, 0x04, 0x08, 0x10, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x24, 0x24, 0x12, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x24, 0x24, - 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, - 0x24, 0x24, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x24, 0x24, 0x24, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x08, 0x00, - 0x42, 0x3c, 0x00, 0x3c, 0x42, 0x42, 0x40, 0x40, 0x4e, 0x42, 0x42, 0x42, - 0x3c, 0x00, 0x00, 0x00, 0x00, 0x42, 0x3c, 0x00, 0x00, 0x3a, 0x46, 0x42, - 0x42, 0x42, 0x46, 0x3a, 0x02, 0x02, 0x42, 0x3c, 0x08, 0x08, 0x00, 0x3e, - 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3e, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, - 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x42, 0x42, 0x40, 0x30, - 0x0c, 0x02, 0x42, 0x42, 0x3c, 0x08, 0x08, 0x30, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x3c, 0x42, 0x40, 0x30, 0x0c, 0x02, 0x42, 0x3c, 0x08, 0x08, 0x30, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, - 0x08, 0x08, 0x00, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 0x24, 0x24, 0x24, - 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x24, 0x24, 0x24, 0x7e, 0x24, 0x24, 0x7e, 0x24, 0x24, - 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x3e, 0x49, 0x48, 0x48, - 0x3e, 0x09, 0x09, 0x49, 0x3e, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 0x31, - 0x4a, 0x4a, 0x34, 0x08, 0x08, 0x16, 0x29, 0x29, 0x46, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x18, 0x24, 0x24, 0x24, 0x18, 0x28, 0x45, 0x42, 0x46, - 0x39, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x08, - 0x08, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x08, 0x08, 0x04, 0x00, 0x00, - 0x00, 0x00, 0x20, 0x10, 0x10, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x10, - 0x10, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x49, 0x2a, - 0x1c, 0x2a, 0x49, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x08, 0x08, 0x08, 0x7f, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, - 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x02, 0x02, 0x04, 0x04, 0x08, 0x08, 0x10, 0x10, 0x20, - 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x42, 0x42, 0x42, 0x4a, - 0x52, 0x42, 0x42, 0x42, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, - 0x18, 0x28, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3e, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x3c, 0x42, 0x42, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, - 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x42, 0x42, 0x02, 0x1c, - 0x02, 0x02, 0x42, 0x42, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, - 0x0c, 0x14, 0x24, 0x44, 0x44, 0x7e, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x7e, 0x40, 0x40, 0x40, 0x7c, 0x02, 0x02, 0x02, 0x42, - 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x20, 0x40, 0x40, 0x7c, - 0x42, 0x42, 0x42, 0x42, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, - 0x02, 0x02, 0x04, 0x04, 0x08, 0x08, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x3c, 0x42, 0x42, 0x42, 0x3c, 0x42, 0x42, 0x42, 0x42, - 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x42, 0x42, 0x42, 0x42, - 0x3e, 0x02, 0x02, 0x04, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, 0x00, 0x08, - 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x08, 0x10, 0x20, - 0x40, 0x20, 0x10, 0x08, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08, 0x04, 0x02, 0x04, 0x08, 0x10, - 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x42, 0x42, 0x02, 0x04, - 0x08, 0x08, 0x00, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, - 0x22, 0x4a, 0x56, 0x52, 0x52, 0x52, 0x4e, 0x20, 0x1e, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x18, 0x24, 0x24, 0x42, 0x42, 0x7e, 0x42, 0x42, 0x42, - 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x42, 0x42, 0x42, 0x7c, - 0x42, 0x42, 0x42, 0x42, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, - 0x42, 0x42, 0x40, 0x40, 0x40, 0x40, 0x42, 0x42, 0x3c, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x78, 0x44, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x44, - 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x40, 0x40, 0x40, 0x7c, - 0x40, 0x40, 0x40, 0x40, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, - 0x40, 0x40, 0x40, 0x7c, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x3c, 0x42, 0x42, 0x40, 0x40, 0x4e, 0x42, 0x42, 0x42, - 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x42, 0x42, 0x42, 0x7e, - 0x42, 0x42, 0x42, 0x42, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, - 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3e, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x1f, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x44, 0x44, - 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x44, 0x48, 0x50, 0x60, - 0x60, 0x50, 0x48, 0x44, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, - 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x7e, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x41, 0x63, 0x63, 0x55, 0x55, 0x49, 0x49, 0x41, 0x41, - 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x62, 0x62, 0x52, 0x52, - 0x4a, 0x4a, 0x46, 0x46, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, - 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3c, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x7c, 0x42, 0x42, 0x42, 0x42, 0x7c, 0x40, 0x40, 0x40, - 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x42, 0x42, 0x42, 0x42, - 0x42, 0x42, 0x5a, 0x66, 0x3c, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, - 0x42, 0x42, 0x42, 0x7c, 0x48, 0x44, 0x44, 0x42, 0x42, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x3c, 0x42, 0x42, 0x40, 0x30, 0x0c, 0x02, 0x42, 0x42, - 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x08, 0x08, 0x08, 0x08, - 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, - 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3c, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x41, 0x41, 0x41, 0x22, 0x22, 0x22, 0x14, 0x14, 0x08, - 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x41, 0x41, 0x49, 0x49, - 0x55, 0x55, 0x63, 0x63, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, - 0x42, 0x24, 0x24, 0x18, 0x18, 0x24, 0x24, 0x42, 0x42, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x41, 0x41, 0x22, 0x22, 0x14, 0x08, 0x08, 0x08, 0x08, - 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x02, 0x02, 0x04, 0x08, - 0x10, 0x20, 0x40, 0x40, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x10, - 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x1c, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x20, 0x20, 0x10, 0x10, 0x08, 0x08, 0x04, 0x04, 0x02, - 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x08, 0x08, 0x08, 0x08, 0x08, - 0x08, 0x08, 0x08, 0x08, 0x08, 0x38, 0x00, 0x00, 0x00, 0x08, 0x14, 0x22, - 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x7f, 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x08, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x3c, 0x42, 0x02, 0x3e, 0x42, 0x42, 0x46, 0x3a, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x40, 0x40, 0x40, 0x5c, 0x62, 0x42, 0x42, 0x42, 0x42, 0x62, - 0x5c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x42, 0x40, - 0x40, 0x40, 0x40, 0x42, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, - 0x02, 0x3a, 0x46, 0x42, 0x42, 0x42, 0x42, 0x46, 0x3a, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x42, 0x42, 0x7e, 0x40, 0x40, 0x42, - 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x10, 0x10, 0x10, 0x7c, 0x10, - 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x3a, 0x46, 0x42, 0x42, 0x42, 0x46, 0x3a, 0x02, 0x02, 0x42, 0x3c, - 0x00, 0x00, 0x40, 0x40, 0x40, 0x5c, 0x62, 0x42, 0x42, 0x42, 0x42, 0x42, - 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x00, 0x18, 0x08, 0x08, - 0x08, 0x08, 0x08, 0x08, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04, - 0x00, 0x0c, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x44, 0x38, - 0x00, 0x00, 0x40, 0x40, 0x40, 0x44, 0x48, 0x50, 0x60, 0x50, 0x48, 0x44, - 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x08, 0x08, 0x08, 0x08, 0x08, - 0x08, 0x08, 0x08, 0x08, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x76, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x5c, 0x62, 0x42, 0x42, 0x42, 0x42, 0x42, - 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x42, 0x42, - 0x42, 0x42, 0x42, 0x42, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x5c, 0x62, 0x42, 0x42, 0x42, 0x42, 0x62, 0x5c, 0x40, 0x40, 0x40, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, 0x46, 0x42, 0x42, 0x42, 0x42, 0x46, - 0x3a, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5c, 0x62, 0x40, - 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x3c, 0x42, 0x40, 0x30, 0x0c, 0x02, 0x42, 0x3c, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x10, 0x10, 0x10, 0x10, 0x7c, 0x10, 0x10, 0x10, 0x10, 0x10, - 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x42, 0x42, - 0x42, 0x42, 0x42, 0x46, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x41, 0x41, 0x41, 0x22, 0x22, 0x14, 0x14, 0x08, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x41, 0x49, 0x49, 0x49, 0x49, 0x49, - 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x42, 0x24, - 0x18, 0x18, 0x24, 0x42, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x42, 0x42, 0x42, 0x42, 0x42, 0x46, 0x3a, 0x02, 0x02, 0x42, 0x3c, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, - 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x08, 0x08, 0x08, 0x08, 0x30, - 0x08, 0x08, 0x08, 0x08, 0x08, 0x06, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, - 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, - 0x00, 0x00, 0x30, 0x08, 0x08, 0x08, 0x08, 0x06, 0x08, 0x08, 0x08, 0x08, - 0x08, 0x30, 0x00, 0x00, 0x00, 0x31, 0x49, 0x46, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x30, 0x0c, 0x00, 0x18, 0x24, 0x24, 0x42, 0x42, 0x7e, 0x42, 0x42, 0x42, - 0x42, 0x00, 0x00, 0x00, 0x0c, 0x30, 0x00, 0x18, 0x24, 0x24, 0x42, 0x42, - 0x7e, 0x42, 0x42, 0x42, 0x42, 0x00, 0x00, 0x00, 0x18, 0x24, 0x00, 0x18, - 0x24, 0x24, 0x42, 0x42, 0x7e, 0x42, 0x42, 0x42, 0x42, 0x00, 0x00, 0x00, - 0x32, 0x4c, 0x00, 0x18, 0x24, 0x24, 0x42, 0x42, 0x7e, 0x42, 0x42, 0x42, - 0x42, 0x00, 0x00, 0x00, 0x24, 0x24, 0x00, 0x18, 0x24, 0x24, 0x42, 0x42, - 0x7e, 0x42, 0x42, 0x42, 0x42, 0x00, 0x00, 0x00, 0x18, 0x24, 0x24, 0x18, - 0x24, 0x24, 0x42, 0x42, 0x7e, 0x42, 0x42, 0x42, 0x42, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x1f, 0x28, 0x48, 0x48, 0x7e, 0x48, 0x48, 0x48, 0x48, - 0x4f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x42, 0x42, 0x40, 0x40, - 0x40, 0x40, 0x42, 0x42, 0x3c, 0x08, 0x08, 0x30, 0x30, 0x0c, 0x00, 0x7e, - 0x40, 0x40, 0x40, 0x7c, 0x40, 0x40, 0x40, 0x40, 0x7e, 0x00, 0x00, 0x00, - 0x0c, 0x30, 0x00, 0x7e, 0x40, 0x40, 0x40, 0x7c, 0x40, 0x40, 0x40, 0x40, - 0x7e, 0x00, 0x00, 0x00, 0x18, 0x24, 0x00, 0x7e, 0x40, 0x40, 0x40, 0x7c, - 0x40, 0x40, 0x40, 0x40, 0x7e, 0x00, 0x00, 0x00, 0x24, 0x24, 0x00, 0x7e, - 0x40, 0x40, 0x40, 0x7c, 0x40, 0x40, 0x40, 0x40, 0x7e, 0x00, 0x00, 0x00, - 0x30, 0x0c, 0x00, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, - 0x3e, 0x00, 0x00, 0x00, 0x06, 0x18, 0x00, 0x3e, 0x08, 0x08, 0x08, 0x08, - 0x08, 0x08, 0x08, 0x08, 0x3e, 0x00, 0x00, 0x00, 0x18, 0x24, 0x00, 0x3e, - 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3e, 0x00, 0x00, 0x00, - 0x22, 0x22, 0x00, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, - 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x44, 0x42, 0x42, 0xf2, - 0x42, 0x42, 0x42, 0x44, 0x78, 0x00, 0x00, 0x00, 0x32, 0x4c, 0x00, 0x42, - 0x62, 0x62, 0x52, 0x52, 0x4a, 0x4a, 0x46, 0x46, 0x42, 0x00, 0x00, 0x00, - 0x30, 0x0c, 0x00, 0x3c, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, - 0x3c, 0x00, 0x00, 0x00, 0x0c, 0x30, 0x00, 0x3c, 0x42, 0x42, 0x42, 0x42, - 0x42, 0x42, 0x42, 0x42, 0x3c, 0x00, 0x00, 0x00, 0x18, 0x24, 0x00, 0x3c, - 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3c, 0x00, 0x00, 0x00, - 0x32, 0x4c, 0x00, 0x3c, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, - 0x3c, 0x00, 0x00, 0x00, 0x24, 0x24, 0x00, 0x3c, 0x42, 0x42, 0x42, 0x42, - 0x42, 0x42, 0x42, 0x42, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x22, 0x14, 0x08, 0x14, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x02, 0x3a, 0x44, 0x46, 0x4a, 0x4a, 0x52, 0x52, 0x62, 0x22, - 0x5c, 0x40, 0x00, 0x00, 0x30, 0x0c, 0x00, 0x42, 0x42, 0x42, 0x42, 0x42, - 0x42, 0x42, 0x42, 0x42, 0x3c, 0x00, 0x00, 0x00, 0x0c, 0x30, 0x00, 0x42, - 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3c, 0x00, 0x00, 0x00, - 0x18, 0x24, 0x00, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, - 0x3c, 0x00, 0x00, 0x00, 0x24, 0x24, 0x00, 0x42, 0x42, 0x42, 0x42, 0x42, - 0x42, 0x42, 0x42, 0x42, 0x3c, 0x00, 0x00, 0x00, 0x06, 0x18, 0x00, 0x41, - 0x41, 0x22, 0x22, 0x14, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x40, 0x40, 0x7c, 0x42, 0x42, 0x42, 0x42, 0x7c, 0x40, - 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x44, 0x4c, 0x50, 0x50, - 0x4c, 0x42, 0x42, 0x52, 0x4c, 0x00, 0x00, 0x00, 0xaa, 0x55, 0xaa, 0x55, - 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, - 0x00, 0x00, 0x00, 0x08, 0x08, 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, - 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x3e, 0x49, 0x48, - 0x48, 0x48, 0x48, 0x49, 0x3e, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 0x1c, - 0x20, 0x20, 0x20, 0x78, 0x20, 0x20, 0x20, 0x22, 0x7e, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x0e, 0x11, 0x20, 0x7e, 0x20, 0x7c, 0x20, 0x11, - 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x41, 0x22, 0x14, 0x08, - 0x3e, 0x08, 0x3e, 0x08, 0x08, 0x00, 0x00, 0x00, 0x24, 0x18, 0x00, 0x3c, - 0x42, 0x42, 0x40, 0x30, 0x0c, 0x02, 0x42, 0x42, 0x3c, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x1c, 0x22, 0x20, 0x18, 0x24, 0x22, 0x22, 0x12, 0x0c, 0x02, - 0x22, 0x1c, 0x00, 0x00, 0x00, 0x24, 0x18, 0x00, 0x00, 0x3c, 0x42, 0x40, - 0x30, 0x0c, 0x02, 0x42, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, - 0x42, 0x99, 0xa5, 0xa1, 0xa1, 0xa5, 0x99, 0x42, 0x3c, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x38, 0x04, 0x3c, 0x44, 0x3c, 0x00, 0x7c, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x24, - 0x48, 0x24, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x7e, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x1c, 0x22, 0x22, 0x22, 0x1c, 0x22, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x42, 0xb9, 0xa5, 0xa5, - 0xb9, 0xa9, 0xa5, 0x42, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x18, 0x24, 0x24, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x08, 0x7f, 0x08, - 0x08, 0x08, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x44, - 0x04, 0x18, 0x20, 0x40, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x38, 0x44, 0x04, 0x38, 0x04, 0x44, 0x38, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x24, 0x18, 0x00, 0x7e, 0x02, 0x02, 0x04, 0x08, - 0x10, 0x20, 0x40, 0x40, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x66, 0x5a, 0x40, 0x40, 0x40, - 0x00, 0x00, 0x00, 0x3e, 0x7a, 0x7a, 0x7a, 0x7a, 0x3a, 0x0a, 0x0a, 0x0a, - 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, - 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x18, 0x00, - 0x00, 0x7e, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x7e, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x10, 0x30, 0x50, 0x10, 0x10, 0x10, 0x7c, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x44, 0x44, 0x44, 0x38, 0x00, - 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x48, 0x24, 0x12, 0x24, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x37, 0x48, 0x48, 0x48, 0x4e, 0x48, 0x48, 0x48, 0x48, - 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x49, 0x49, - 0x4f, 0x48, 0x48, 0x49, 0x36, 0x00, 0x00, 0x00, 0x22, 0x22, 0x00, 0x41, - 0x41, 0x22, 0x22, 0x14, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x10, 0x10, 0x00, 0x10, 0x10, 0x20, 0x40, 0x42, 0x42, - 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x08, 0x08, - 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x08, 0x08, 0x08, - 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xf8, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, - 0x08, 0x08, 0x08, 0x08, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0xf8, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, - 0x0f, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, - 0x08, 0x08, 0x08, 0x08, 0xf8, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x08, 0x08, 0x08, - 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, - 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x08, 0x08, - 0x08, 0x08, 0x08, 0x08, 0xff, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, - 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, - 0x88, 0x22, 0x88, 0x22, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, - 0x14, 0x14, 0x14, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, - 0x10, 0x17, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0xfc, 0x04, 0xf4, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, - 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x17, 0x10, 0x1f, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0xf4, - 0x04, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x14, 0x14, 0x14, - 0x14, 0x14, 0x14, 0x17, 0x10, 0x17, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, - 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0xf4, 0x04, 0xf4, 0x14, 0x14, - 0x14, 0x14, 0x14, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, - 0x00, 0xf7, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, - 0x14, 0x14, 0x14, 0xf7, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0xf7, 0x00, 0xf7, 0x14, 0x14, - 0x14, 0x14, 0x14, 0x14, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x08, 0x1c, 0x2a, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x08, 0x08, 0x2a, 0x1c, 0x08, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x20, - 0x7f, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x08, 0x04, 0xfe, 0x04, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x30, 0x0c, 0x00, 0x00, 0x3c, 0x42, 0x02, 0x3e, 0x42, 0x42, 0x46, - 0x3a, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x30, 0x00, 0x00, 0x3c, 0x42, 0x02, - 0x3e, 0x42, 0x42, 0x46, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x18, 0x24, 0x00, - 0x00, 0x3c, 0x42, 0x02, 0x3e, 0x42, 0x42, 0x46, 0x3a, 0x00, 0x00, 0x00, - 0x00, 0x32, 0x4c, 0x00, 0x00, 0x3c, 0x42, 0x02, 0x3e, 0x42, 0x42, 0x46, - 0x3a, 0x00, 0x00, 0x00, 0x00, 0x24, 0x24, 0x00, 0x00, 0x3c, 0x42, 0x02, - 0x3e, 0x42, 0x42, 0x46, 0x3a, 0x00, 0x00, 0x00, 0x18, 0x24, 0x24, 0x18, - 0x00, 0x3c, 0x42, 0x02, 0x3e, 0x42, 0x42, 0x46, 0x3a, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x49, 0x09, 0x3f, 0x48, 0x48, 0x49, - 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x42, 0x40, - 0x40, 0x40, 0x40, 0x42, 0x3c, 0x08, 0x08, 0x30, 0x00, 0x30, 0x0c, 0x00, - 0x00, 0x3c, 0x42, 0x42, 0x7e, 0x40, 0x40, 0x42, 0x3c, 0x00, 0x00, 0x00, - 0x00, 0x0c, 0x30, 0x00, 0x00, 0x3c, 0x42, 0x42, 0x7e, 0x40, 0x40, 0x42, - 0x3c, 0x00, 0x00, 0x00, 0x00, 0x18, 0x24, 0x00, 0x00, 0x3c, 0x42, 0x42, - 0x7e, 0x40, 0x40, 0x42, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x24, 0x24, 0x00, - 0x00, 0x3c, 0x42, 0x42, 0x7e, 0x40, 0x40, 0x42, 0x3c, 0x00, 0x00, 0x00, - 0x00, 0x30, 0x0c, 0x00, 0x00, 0x18, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, - 0x3e, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x30, 0x00, 0x00, 0x18, 0x08, 0x08, - 0x08, 0x08, 0x08, 0x08, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x18, 0x24, 0x00, - 0x00, 0x18, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3e, 0x00, 0x00, 0x00, - 0x00, 0x24, 0x24, 0x00, 0x00, 0x18, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, - 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x18, 0x64, 0x02, 0x3a, 0x46, - 0x42, 0x42, 0x42, 0x42, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x32, 0x4c, 0x00, - 0x00, 0x5c, 0x62, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x00, 0x00, 0x00, - 0x00, 0x30, 0x0c, 0x00, 0x00, 0x3c, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, - 0x3c, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x30, 0x00, 0x00, 0x3c, 0x42, 0x42, - 0x42, 0x42, 0x42, 0x42, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x18, 0x24, 0x00, - 0x00, 0x3c, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3c, 0x00, 0x00, 0x00, - 0x00, 0x32, 0x4c, 0x00, 0x00, 0x3c, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, - 0x3c, 0x00, 0x00, 0x00, 0x00, 0x24, 0x24, 0x00, 0x00, 0x3c, 0x42, 0x42, - 0x42, 0x42, 0x42, 0x42, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x10, 0x10, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x02, 0x3c, 0x46, 0x4a, 0x4a, 0x52, 0x52, 0x62, - 0x3c, 0x40, 0x00, 0x00, 0x00, 0x30, 0x0c, 0x00, 0x00, 0x42, 0x42, 0x42, - 0x42, 0x42, 0x42, 0x46, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x30, 0x00, - 0x00, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x46, 0x3a, 0x00, 0x00, 0x00, - 0x00, 0x18, 0x24, 0x00, 0x00, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x46, - 0x3a, 0x00, 0x00, 0x00, 0x00, 0x24, 0x24, 0x00, 0x00, 0x42, 0x42, 0x42, - 0x42, 0x42, 0x42, 0x46, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x30, 0x00, - 0x00, 0x42, 0x42, 0x42, 0x42, 0x42, 0x46, 0x3a, 0x02, 0x02, 0x42, 0x3c, - 0x00, 0x00, 0x40, 0x40, 0x40, 0x5c, 0x62, 0x42, 0x42, 0x42, 0x42, 0x62, - 0x5c, 0x40, 0x40, 0x40, 0x00, 0x24, 0x24, 0x00, 0x00, 0x42, 0x42, 0x42, - 0x42, 0x42, 0x46, 0x3a, 0x02, 0x02, 0x42, 0x3c, 0xfd, 0xff, 0xff, 0xff, - 0xc0, 0x03, 0xff, 0xff, 0x60, 0x22, 0xff, 0xff, 0x64, 0x22, 0xff, 0xff, - 0x65, 0x22, 0xff, 0xff, 0xa0, 0x25, 0xac, 0x25, 0xae, 0x25, 0xfc, 0x25, - 0xfe, 0x25, 0x1b, 0x2b, 0x0e, 0x22, 0xff, 0xff, 0xc6, 0x25, 0x66, 0x26, - 0x25, 0x2b, 0x27, 0x2b, 0xff, 0xff, 0xbc, 0x00, 0xff, 0xff, 0xbd, 0x00, - 0xff, 0xff, 0xbe, 0x00, 0xff, 0xff, 0xa6, 0x00, 0xff, 0xff, 0xa8, 0x00, - 0xff, 0xff, 0xb8, 0x00, 0xff, 0xff, 0x92, 0x01, 0xff, 0xff, 0x20, 0x20, - 0xff, 0xff, 0x21, 0x20, 0xff, 0xff, 0x30, 0x20, 0xff, 0xff, 0x22, 0x21, - 0xff, 0xff, 0x26, 0x20, 0xff, 0xff, 0x39, 0x20, 0xff, 0xff, 0x3a, 0x20, - 0xff, 0xff, 0x1c, 0x20, 0x1f, 0x20, 0x36, 0x20, 0xff, 0xff, 0x1d, 0x20, - 0xba, 0x02, 0xdd, 0x02, 0xee, 0x02, 0x33, 0x20, 0xff, 0xff, 0x1e, 0x20, - 0xff, 0xff, 0x42, 0x2e, 0xff, 0xff, 0x41, 0x2e, 0xce, 0x02, 0xff, 0xff, - 0x1e, 0x01, 0xff, 0xff, 0x1f, 0x01, 0xff, 0xff, 0x30, 0x01, 0xff, 0xff, - 0x31, 0x01, 0xff, 0xff, 0x5e, 0x01, 0xff, 0xff, 0x5f, 0x01, 0xff, 0xff, - 0x20, 0x00, 0xa0, 0x00, 0x00, 0x20, 0x01, 0x20, 0x02, 0x20, 0x03, 0x20, - 0x04, 0x20, 0x05, 0x20, 0x06, 0x20, 0x07, 0x20, 0x08, 0x20, 0x09, 0x20, - 0x0a, 0x20, 0x2f, 0x20, 0x5f, 0x20, 0xff, 0xff, 0x21, 0x00, 0xff, 0xff, - 0x22, 0x00, 0xff, 0xff, 0x23, 0x00, 0xff, 0xff, 0x24, 0x00, 0xff, 0xff, - 0x25, 0x00, 0xff, 0xff, 0x26, 0x00, 0xff, 0xff, 0x27, 0x00, 0xb4, 0x00, - 0xb9, 0x02, 0xbc, 0x02, 0xca, 0x02, 0x19, 0x20, 0x32, 0x20, 0xff, 0xff, - 0x28, 0x00, 0xff, 0xff, 0x29, 0x00, 0xff, 0xff, 0x2a, 0x00, 0x4e, 0x20, - 0x17, 0x22, 0xff, 0xff, 0x2b, 0x00, 0xff, 0xff, 0x2c, 0x00, 0xcf, 0x02, - 0x1a, 0x20, 0xff, 0xff, 0x2d, 0x00, 0xad, 0x00, 0x10, 0x20, 0x11, 0x20, - 0x12, 0x20, 0x13, 0x20, 0x43, 0x20, 0x12, 0x22, 0xff, 0xff, 0x2e, 0x00, - 0x24, 0x20, 0xff, 0xff, 0x2f, 0x00, 0x44, 0x20, 0x15, 0x22, 0xff, 0xff, - 0x30, 0x00, 0xff, 0xff, 0x31, 0x00, 0xff, 0xff, 0x32, 0x00, 0xff, 0xff, - 0x33, 0x00, 0xff, 0xff, 0x34, 0x00, 0xff, 0xff, 0x35, 0x00, 0xff, 0xff, - 0x36, 0x00, 0xff, 0xff, 0x37, 0x00, 0xff, 0xff, 0x38, 0x00, 0xff, 0xff, - 0x39, 0x00, 0xff, 0xff, 0x3a, 0x00, 0x36, 0x22, 0xff, 0xff, 0x3b, 0x00, - 0xff, 0xff, 0x3c, 0x00, 0xff, 0xff, 0x3d, 0x00, 0x40, 0x2e, 0xff, 0xff, - 0x3e, 0x00, 0xff, 0xff, 0x3f, 0x00, 0xff, 0xff, 0x40, 0x00, 0xff, 0xff, - 0x41, 0x00, 0xff, 0xff, 0x42, 0x00, 0xff, 0xff, 0x43, 0x00, 0xff, 0xff, - 0x44, 0x00, 0xff, 0xff, 0x45, 0x00, 0xff, 0xff, 0x46, 0x00, 0xff, 0xff, - 0x47, 0x00, 0xff, 0xff, 0x48, 0x00, 0xff, 0xff, 0x49, 0x00, 0xff, 0xff, - 0x4a, 0x00, 0xff, 0xff, 0x4b, 0x00, 0x2a, 0x21, 0xff, 0xff, 0x4c, 0x00, - 0xff, 0xff, 0x4d, 0x00, 0xff, 0xff, 0x4e, 0x00, 0xff, 0xff, 0x4f, 0x00, - 0xff, 0xff, 0x50, 0x00, 0xff, 0xff, 0x51, 0x00, 0xff, 0xff, 0x52, 0x00, - 0xff, 0xff, 0x53, 0x00, 0xff, 0xff, 0x54, 0x00, 0xff, 0xff, 0x55, 0x00, - 0xff, 0xff, 0x56, 0x00, 0xff, 0xff, 0x57, 0x00, 0xff, 0xff, 0x58, 0x00, - 0xff, 0xff, 0x59, 0x00, 0xff, 0xff, 0x5a, 0x00, 0xff, 0xff, 0x5b, 0x00, - 0xff, 0xff, 0x5c, 0x00, 0xf5, 0x29, 0xff, 0xff, 0x5d, 0x00, 0xff, 0xff, - 0x5e, 0x00, 0xc4, 0x02, 0xc6, 0x02, 0x03, 0x23, 0xff, 0xff, 0x5f, 0x00, - 0xff, 0xff, 0x60, 0x00, 0xbb, 0x02, 0xbd, 0x02, 0xcb, 0x02, 0x18, 0x20, - 0x1b, 0x20, 0x35, 0x20, 0xff, 0xff, 0x61, 0x00, 0xff, 0xff, 0x62, 0x00, - 0xff, 0xff, 0x63, 0x00, 0xff, 0xff, 0x64, 0x00, 0xff, 0xff, 0x65, 0x00, - 0xff, 0xff, 0x66, 0x00, 0xff, 0xff, 0x67, 0x00, 0xff, 0xff, 0x68, 0x00, - 0xff, 0xff, 0x69, 0x00, 0xff, 0xff, 0x6a, 0x00, 0xff, 0xff, 0x6b, 0x00, - 0xff, 0xff, 0x6c, 0x00, 0xff, 0xff, 0x6d, 0x00, 0xff, 0xff, 0x6e, 0x00, - 0xff, 0xff, 0x6f, 0x00, 0xff, 0xff, 0x70, 0x00, 0xff, 0xff, 0x71, 0x00, - 0xff, 0xff, 0x72, 0x00, 0xff, 0xff, 0x73, 0x00, 0xff, 0xff, 0x74, 0x00, - 0xff, 0xff, 0x75, 0x00, 0xff, 0xff, 0x76, 0x00, 0xff, 0xff, 0x77, 0x00, - 0xff, 0xff, 0x78, 0x00, 0xff, 0xff, 0x79, 0x00, 0xff, 0xff, 0x7a, 0x00, - 0xff, 0xff, 0x7b, 0x00, 0xff, 0xff, 0x7c, 0x00, 0x23, 0x22, 0xff, 0xff, - 0x7d, 0x00, 0xff, 0xff, 0x7e, 0x00, 0xdc, 0x02, 0xff, 0xff, 0x22, 0x20, - 0x19, 0x22, 0xcf, 0x25, 0xff, 0xff, 0xc0, 0x00, 0xff, 0xff, 0xc1, 0x00, - 0xff, 0xff, 0xc2, 0x00, 0xff, 0xff, 0xc3, 0x00, 0xff, 0xff, 0xc4, 0x00, - 0xff, 0xff, 0xc5, 0x00, 0x2b, 0x21, 0xff, 0xff, 0xc6, 0x00, 0xff, 0xff, - 0xc7, 0x00, 0xff, 0xff, 0xc8, 0x00, 0xff, 0xff, 0xc9, 0x00, 0xff, 0xff, - 0xca, 0x00, 0xff, 0xff, 0xcb, 0x00, 0xff, 0xff, 0xcc, 0x00, 0xff, 0xff, - 0xcd, 0x00, 0xff, 0xff, 0xce, 0x00, 0xff, 0xff, 0xcf, 0x00, 0xff, 0xff, - 0xd0, 0x00, 0x10, 0x01, 0xff, 0xff, 0xd1, 0x00, 0xff, 0xff, 0xd2, 0x00, - 0xff, 0xff, 0xd3, 0x00, 0xff, 0xff, 0xd4, 0x00, 0xff, 0xff, 0xd5, 0x00, - 0xff, 0xff, 0xd6, 0x00, 0xff, 0xff, 0xd7, 0x00, 0xff, 0xff, 0xd8, 0x00, - 0xff, 0xff, 0xd9, 0x00, 0xff, 0xff, 0xda, 0x00, 0xff, 0xff, 0xdb, 0x00, - 0xff, 0xff, 0xdc, 0x00, 0xff, 0xff, 0xdd, 0x00, 0xff, 0xff, 0xde, 0x00, - 0xff, 0xff, 0xdf, 0x00, 0xff, 0xff, 0x92, 0x25, 0xff, 0xff, 0xa1, 0x00, - 0xff, 0xff, 0xa2, 0x00, 0xff, 0xff, 0xa3, 0x00, 0xff, 0xff, 0xac, 0x20, - 0xff, 0xff, 0xa5, 0x00, 0xff, 0xff, 0x60, 0x01, 0xff, 0xff, 0xa7, 0x00, - 0xff, 0xff, 0x61, 0x01, 0xff, 0xff, 0xa9, 0x00, 0xff, 0xff, 0xaa, 0x00, - 0xff, 0xff, 0xab, 0x00, 0xff, 0xff, 0xac, 0x00, 0xff, 0xff, 0xa4, 0x00, - 0xff, 0xff, 0xae, 0x00, 0xff, 0xff, 0xaf, 0x00, 0xc9, 0x02, 0xff, 0xff, - 0xb0, 0x00, 0xda, 0x02, 0xff, 0xff, 0xb1, 0x00, 0xff, 0xff, 0xb2, 0x00, - 0xff, 0xff, 0xb3, 0x00, 0xff, 0xff, 0x7d, 0x01, 0xff, 0xff, 0xb5, 0x00, - 0xbc, 0x03, 0xff, 0xff, 0xb6, 0x00, 0xff, 0xff, 0xb7, 0x00, 0x27, 0x20, - 0xc5, 0x22, 0x31, 0x2e, 0xff, 0xff, 0x7e, 0x01, 0xff, 0xff, 0xb9, 0x00, - 0xff, 0xff, 0xba, 0x00, 0xff, 0xff, 0xbb, 0x00, 0xff, 0xff, 0x52, 0x01, - 0xff, 0xff, 0x53, 0x01, 0xff, 0xff, 0x78, 0x01, 0xff, 0xff, 0xbf, 0x00, - 0xff, 0xff, 0x00, 0x25, 0x14, 0x20, 0x15, 0x20, 0xaf, 0x23, 0xff, 0xff, - 0x02, 0x25, 0xff, 0xff, 0x0c, 0x25, 0x6d, 0x25, 0xff, 0xff, 0x10, 0x25, - 0x6e, 0x25, 0xff, 0xff, 0x14, 0x25, 0x70, 0x25, 0xff, 0xff, 0x18, 0x25, - 0x6f, 0x25, 0xff, 0xff, 0x1c, 0x25, 0xff, 0xff, 0x24, 0x25, 0xff, 0xff, - 0x2c, 0x25, 0xff, 0xff, 0x34, 0x25, 0xff, 0xff, 0x3c, 0x25, 0xff, 0xff, - 0x91, 0x25, 0xff, 0xff, 0xba, 0x23, 0x3e, 0x20, 0xff, 0xff, 0xbb, 0x23, - 0xff, 0xff, 0xbc, 0x23, 0xff, 0xff, 0xbd, 0x23, 0xff, 0xff, 0x50, 0x25, - 0x01, 0x25, 0xff, 0xff, 0x51, 0x25, 0x03, 0x25, 0xff, 0xff, 0x54, 0x25, - 0x0f, 0x25, 0xff, 0xff, 0x57, 0x25, 0x13, 0x25, 0xff, 0xff, 0x5a, 0x25, - 0x17, 0x25, 0xff, 0xff, 0x5d, 0x25, 0x1b, 0x25, 0xff, 0xff, 0x60, 0x25, - 0x23, 0x25, 0xff, 0xff, 0x63, 0x25, 0x2b, 0x25, 0xff, 0xff, 0x66, 0x25, - 0x33, 0x25, 0xff, 0xff, 0x69, 0x25, 0x3b, 0x25, 0xff, 0xff, 0x6c, 0x25, - 0x4b, 0x25, 0xff, 0xff, 0x88, 0x25, 0xff, 0xff, 0x91, 0x21, 0xff, 0xff, - 0x93, 0x21, 0xff, 0xff, 0x90, 0x21, 0xff, 0xff, 0x92, 0x21, 0xff, 0xff, - 0xe0, 0x00, 0xff, 0xff, 0xe1, 0x00, 0xff, 0xff, 0xe2, 0x00, 0xff, 0xff, - 0xe3, 0x00, 0xff, 0xff, 0xe4, 0x00, 0xff, 0xff, 0xe5, 0x00, 0xff, 0xff, - 0xe6, 0x00, 0xff, 0xff, 0xe7, 0x00, 0xff, 0xff, 0xe8, 0x00, 0xff, 0xff, - 0xe9, 0x00, 0xff, 0xff, 0xea, 0x00, 0xff, 0xff, 0xeb, 0x00, 0xff, 0xff, - 0xec, 0x00, 0xff, 0xff, 0xed, 0x00, 0xff, 0xff, 0xee, 0x00, 0xff, 0xff, - 0xef, 0x00, 0xff, 0xff, 0xf0, 0x00, 0xff, 0xff, 0xf1, 0x00, 0xff, 0xff, - 0xf2, 0x00, 0xff, 0xff, 0xf3, 0x00, 0xff, 0xff, 0xf4, 0x00, 0xff, 0xff, - 0xf5, 0x00, 0xff, 0xff, 0xf6, 0x00, 0xff, 0xff, 0xf7, 0x00, 0xff, 0xff, - 0xf8, 0x00, 0xff, 0xff, 0xf9, 0x00, 0xff, 0xff, 0xfa, 0x00, 0xff, 0xff, - 0xfb, 0x00, 0xff, 0xff, 0xfc, 0x00, 0xff, 0xff, 0xfd, 0x00, 0xff, 0xff, - 0xfe, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff -}; -unsigned int zap_light16_psf_len = 5312; - -typedef struct { - uint8_t magic[2]; // 0x36, 0x04 - uint8_t mode; - uint8_t charsize; // bytes per glyph -} PSF1_Header; - diff --git a/src/drivers/video/render.c b/src/drivers/video/render.c deleted file mode 100644 index a1755a8..0000000 --- a/src/drivers/video/render.c +++ /dev/null @@ -1,342 +0,0 @@ -#include -#include "font.h" -#include - -#include "render.h" -#include "mm/memory.h" - -uint32_t *fb; -uint32_t fb_width; -uint32_t fb_height; -uint32_t fb_pitch; - - - - -static int cursor_x = 0; -static int cursor_y = 0; - -void put_pixel(int x, int y, uint32_t color) { - fb[y * fb_pitch + x] = color; -} - -static void scroll_up(void) { - uint32_t line_pixels = FONT_HEIGHT * fb_pitch; - uint32_t total_pixels = fb_height * fb_pitch; - - // Shift everything up by one line - memmove(fb, fb + line_pixels, (total_pixels - line_pixels) * sizeof(uint32_t)); - - // Clear the last line - memset(fb + (total_pixels - line_pixels), 0, line_pixels * sizeof(uint32_t)); - - cursor_y -= FONT_HEIGHT; -} - -void draw_char_colored(int x, int y, char c, uint32_t color) { - PSF1_Header *font = (PSF1_Header*)(uintptr_t)zap_light16_psf; - uint8_t *glyphs = zap_light16_psf + sizeof(PSF1_Header); - int charsize = font->charsize; - uint8_t *glyph = glyphs + ((unsigned char)c * charsize); - - for (int row = 0; row < charsize; row++) { - for (int col = 0; col < 8; col++) { - if (glyph[row] & (0x80 >> col)) { - fb[(y + row) * fb_pitch + (x + col)] = color; - } - } - } -} - -void putchar(char c) { - if (c == '\n') { - cursor_x = 0; - cursor_y += FONT_HEIGHT; - if (cursor_y + FONT_HEIGHT > fb_height) scroll_up(); - return; - } - - draw_char_colored(cursor_x, cursor_y, c, 0xFFFFFF); - cursor_x += FONT_WIDTH; - - if (cursor_x >= (int)fb_width) { - cursor_x = 0; - cursor_y += FONT_HEIGHT; - if (cursor_y + FONT_HEIGHT > fb_height) scroll_up(); - } -} - - -void putchar_colored(char c, uint32_t color) { - if (c == '\n') { - cursor_x = 0; - cursor_y += FONT_HEIGHT; - if (cursor_y + FONT_HEIGHT > fb_height) scroll_up(); - return; - } - - draw_char_colored(cursor_x, cursor_y, c, color); - cursor_x += FONT_WIDTH; - - if (cursor_x >= (int)fb_width) { - cursor_x = 0; - cursor_y += FONT_HEIGHT; - if (cursor_y + FONT_HEIGHT > fb_height) scroll_up(); - } -} - -void print(const char *str) { - while (*str) { - putchar(*str++); - } -} - -void itoa(int value, char *buf, int base) { - char *ptr = buf, *ptr1 = buf, tmp_char; - int tmp_value; - - do { - tmp_value = value; - value /= base; - *ptr++ = "0123456789ABCDEF"[tmp_value - value * base]; - } while (value); - - *ptr-- = '\0'; - - while (ptr1 < ptr) { - tmp_char = *ptr; - *ptr-- = *ptr1; - *ptr1++ = tmp_char; - } -} - -static inline uint32_t sample(uint32_t *src, int w, int h, int x, int y) -{ - if (x < 0) x = 0; - if (y < 0) y = 0; - if (x >= w) x = w - 1; - if (y >= h) y = h - 1; - - return src[y * w + x]; -} - -void clear_screen(uint32_t color) -{ - uint32_t total = fb_width * fb_height; - - for (uint32_t i = 0; i < total; i++) - { - fb[i] = color; - } - cursor_y = 0; -} - -void draw_image_bilinear(uint32_t *img, int x0, int y0, int new_w, int new_h) -{ - int src_w = img[0]; - int src_h = img[1]; - uint32_t *src = &img[2]; - - // fixed-point scale factors (16.16) - uint32_t x_scale = (src_w << 16) / new_w; - uint32_t y_scale = (src_h << 16) / new_h; - - for (int y = 0; y < new_h; y++) - { - int sy_fp = y * y_scale; - int sy = sy_fp >> 16; - int fy = (sy_fp >> 8) & 0xFF; - - for (int x = 0; x < new_w; x++) - { - int sx_fp = x * x_scale; - int sx = sx_fp >> 16; - int fx = (sx_fp >> 8) & 0xFF; - - int x1 = sx + 1; - int y1 = sy + 1; - - uint32_t c00 = sample(src, src_w, src_h, sx, sy); - uint32_t c10 = sample(src, src_w, src_h, x1, sy); - uint32_t c01 = sample(src, src_w, src_h, sx, y1); - uint32_t c11 = sample(src, src_w, src_h, x1, y1); - - // unpack - uint32_t a00 = (c00 >> 24) & 0xFF; - uint32_t r00 = (c00 >> 16) & 0xFF; - uint32_t g00 = (c00 >> 8) & 0xFF; - uint32_t b00 = (c00) & 0xFF; - - uint32_t a10 = (c10 >> 24) & 0xFF; - uint32_t r10 = (c10 >> 16) & 0xFF; - uint32_t g10 = (c10 >> 8) & 0xFF; - uint32_t b10 = (c10) & 0xFF; - - uint32_t a01 = (c01 >> 24) & 0xFF; - uint32_t r01 = (c01 >> 16) & 0xFF; - uint32_t g01 = (c01 >> 8) & 0xFF; - uint32_t b01 = (c01) & 0xFF; - - uint32_t a11 = (c11 >> 24) & 0xFF; - uint32_t r11 = (c11 >> 16) & 0xFF; - uint32_t g11 = (c11 >> 8) & 0xFF; - uint32_t b11 = (c11) & 0xFF; - - // bilinear interpolation weights - uint32_t w00 = (256 - fx) * (256 - fy); - uint32_t w10 = fx * (256 - fy); - uint32_t w01 = (256 - fx) * fy; - uint32_t w11 = fx * fy; - - uint32_t a = (a00*w00 + a10*w10 + a01*w01 + a11*w11) >> 16; - uint32_t r = (r00*w00 + r10*w10 + r01*w01 + r11*w11) >> 16; - uint32_t g = (g00*w00 + g10*w10 + g01*w01 + g11*w11) >> 16; - uint32_t b = (b00*w00 + b10*w10 + b01*w01 + b11*w11) >> 16; - - int dst_x = x0 + x; - int dst_y = y0 + y; - - if (dst_x < 0 || dst_y < 0 || - dst_x >= (int)fb_width || - dst_y >= (int)fb_height) - continue; - - uint32_t color = (a << 24) | (r << 16) | (g << 8) | b; - - // alpha blend with framebuffer - if (a < 255) - { - uint32_t dst = fb[dst_y * fb_pitch + dst_x]; - - uint32_t dr = (dst >> 16) & 0xFF; - uint32_t dg = (dst >> 8) & 0xFF; - uint32_t db = dst & 0xFF; - - uint32_t inv = 255 - a; - - r = (r * a + dr * inv) / 255; - g = (g * a + dg * inv) / 255; - b = (b * a + db * inv) / 255; - - fb[dst_y * fb_pitch + dst_x] = - (0xFF << 24) | (r << 16) | (g << 8) | b; - } - else - { - fb[dst_y * fb_pitch + dst_x] = - (0xFF << 24) | (r << 16) | (g << 8) | b; - } - } - } -} - -void backspace() { - if (cursor_x >= FONT_WIDTH) { - cursor_x -= FONT_WIDTH; - - // clear the character cell - for (int y = 0; y < FONT_HEIGHT; y++) { - for (int x = 0; x < FONT_WIDTH; x++) { - fb[(cursor_y + y) * fb_pitch + (cursor_x + x)] = 0; - } - } - } -} - -void draw_image(uint32_t *img, int x0, int y0) -{ - uint32_t w = img[0]; - uint32_t h = img[1]; - uint32_t *pixels = &img[2]; - - for (uint32_t y = 0; y < h; y++) { - for (uint32_t x = 0; x < w; x++) { - - int dst_x = x0 + x; - int dst_y = y0 + y; - - // bounds check (VERY important in kernel) - if (dst_x < 0 || dst_y < 0 || - dst_x >= (int)fb_width || - dst_y >= (int)fb_height) - continue; - - uint32_t color = pixels[y * w + x]; - - // alpha handling - uint8_t a = color >> 24; - - // fully transparent → skip - if (a == 0) - continue; - - // optional: simple alpha blend - if (a < 255) { - uint32_t dst = fb[dst_y * fb_pitch + dst_x]; - - uint8_t sr = (color >> 16) & 0xFF; - uint8_t sg = (color >> 8) & 0xFF; - uint8_t sb = (color) & 0xFF; - - uint8_t dr = (dst >> 16) & 0xFF; - uint8_t dg = (dst >> 8) & 0xFF; - uint8_t db = (dst) & 0xFF; - - uint8_t inv = 255 - a; - - uint32_t out = - ((sr * a + dr * inv) / 255) << 16 | - ((sg * a + dg * inv) / 255) << 8 | - ((sb * a + db * inv) / 255); - - fb[dst_y * fb_pitch + dst_x] = out; - } - else { - // opaque fast path - fb[dst_y * fb_pitch + dst_x] = color; - } - } - } -} - -/*void printf(const char *fmt, ...) { - va_list args; - va_start(args, fmt); - - char buffer[32]; - - for (int i = 0; fmt[i] != 0; i++) { - if (fmt[i] == '%') { - i++; - - switch (fmt[i]) { - case 'd': { - int val = va_arg(args, int); - itoa(val, buffer, 10); - print(buffer); - break; - } - case 'x': { - int val = va_arg(args, int); - itoa(val, buffer, 16); - print(buffer); - break; - } - case 's': { - char *str = va_arg(args, char*); - print(str); - break; - } - case 'c': { - char c = (char)va_arg(args, int); - putchar(c); - break; - } - } - } else { - putchar(fmt[i]); - } - } - - va_end(args); -}*/ \ No newline at end of file diff --git a/src/drivers/video/render.h b/src/drivers/video/render.h deleted file mode 100644 index 2836bc2..0000000 --- a/src/drivers/video/render.h +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once -#include - -#define FONT_WIDTH 8 -#define FONT_HEIGHT 16 - -extern uint32_t *fb; -extern uint32_t fb_width; -extern uint32_t fb_height; -extern uint32_t fb_pitch; - - -void putchar(char c); -void putchar_colored(char c, uint32_t color); -void draw_image(uint32_t *img, int x0, int y0); -void draw_image_bilinear(uint32_t *img, int x0, int y0, int new_w, int new_h); -void clear_screen(uint32_t color); -void backspace(); \ No newline at end of file diff --git a/src/drivers/video/tga.c b/src/drivers/video/tga.c deleted file mode 100644 index 4f62e8c..0000000 --- a/src/drivers/video/tga.c +++ /dev/null @@ -1,253 +0,0 @@ -#include "mm/memory.h" -#include -#include - -static inline int ok(int m, int need, int size) -{ - return (m >= 0 && need >= 0 && m <= size && size - m >= need); -} - -static inline uint32_t read_color(unsigned char *pal, int idx, int bpp) -{ - // palette entry size = bpp/8 - int step = bpp >> 3; - - unsigned char b = pal[idx * step + 0]; - unsigned char g = pal[idx * step + 1]; - unsigned char r = pal[idx * step + 2]; - unsigned char a = (step == 4) ? pal[idx * step + 3] : 0xFF; - - return ((uint32_t)a << 24) | - ((uint32_t)r << 16) | - ((uint32_t)g << 8) | - (uint32_t)b; -} - -unsigned int *tga_parse(unsigned char *ptr, int size) -{ - if (!ptr || size < 18) return NULL; - - int m = 0; - - int id_len = ptr[0]; - int cmap_type = ptr[1]; - int img_type = ptr[2]; - - int cmap_start = ptr[3] | (ptr[4] << 8); - int cmap_len = ptr[5] | (ptr[6] << 8); - int cmap_bpp = ptr[7]; - - int w = ptr[12] | (ptr[13] << 8); - int h = ptr[14] | (ptr[15] << 8); - int bpp = ptr[16]; - int desc = ptr[17]; - - if (w <= 0 || h <= 0 || w > 4096 || h > 4096) - return NULL; - - if (!ok(18, id_len, size)) - return NULL; - - m = 18 + id_len; - if (m > size) return NULL; - - int flip_y = !(desc & 0x20); - - size_t pixels = (size_t)w * (size_t)h; - if (pixels == 0 || pixels > (SIZE_MAX / sizeof(unsigned int))) - return NULL; - - unsigned int *out = (unsigned int *)kmalloc((pixels + 2) * sizeof(unsigned int)); - if (!out) return NULL; - - out[0] = w; - out[1] = h; - - size_t i = 0; - - unsigned char *palette = NULL; - if (cmap_type == 1) - { - // palette starts immediately after header+id - palette = ptr + m; - - size_t pal_size = (size_t)cmap_len * (cmap_bpp >> 3); - if (!ok(m, pal_size, size)) { - kfree(out); - return NULL; - } - - m += pal_size; - } - - // ========================= - // TYPE 2 (TRUECOLOR) - // ========================= - if (img_type == 2) - { - int bytespp = bpp >> 3; - if (bytespp != 3 && bytespp != 4) goto fail; - - for (int y = 0; y < h; y++) - { - int row = flip_y ? (h - 1 - y) : y; - - for (int x = 0; x < w; x++) - { - if (!ok(m, bytespp, size)) goto fail; - - unsigned char b = ptr[m + 0]; - unsigned char g = ptr[m + 1]; - unsigned char r = ptr[m + 2]; - unsigned char a = (bytespp == 4) ? ptr[m + 3] : 0xFF; - - m += bytespp; - - out[2 + (size_t)row * w + x] = - ((uint32_t)a << 24) | - ((uint32_t)r << 16) | - ((uint32_t)g << 8) | - (uint32_t)b; - } - } - } - - // ========================= - // TYPE 3 (GRAYSCALE) - // ========================= - else if (img_type == 3) - { - for (size_t idx = 0; idx < pixels; idx++) - { - if (!ok(m, 1, size)) goto fail; - - unsigned char v = ptr[m++]; - - out[2 + idx] = - 0xFF000000 | - (v << 16) | - (v << 8) | - v; - } - } - - // ========================= - // TYPE 1 (PALETTED) - // ========================= - else if (img_type == 1) - { - if (!palette) goto fail; - - for (size_t idx = 0; idx < pixels; idx++) - { - if (!ok(m, 1, size)) goto fail; - - unsigned char index = ptr[m++]; - - out[2 + idx] = read_color(palette, index, cmap_bpp); - } - } - - // ========================= - // TYPE 9/10/11 (RLE) - // ========================= - else if (img_type == 9 || img_type == 10 || img_type == 11) - { - int bytespp = (img_type == 9) ? 1 : (bpp >> 3); - if (img_type != 9 && bytespp != 3 && bytespp != 4) goto fail; - - while (i < pixels) - { - if (!ok(m, 1, size)) goto fail; - - unsigned char packet = ptr[m++]; - int count = (packet & 0x7F) + 1; - - if (count > (int)(pixels - i)) - count = (int)(pixels - i); - - if (packet & 0x80) - { - // RLE - if (!ok(m, bytespp, size)) goto fail; - - uint32_t color; - - if (img_type == 9) - { - color = read_color(palette, ptr[m], cmap_bpp); - m += 1; - } - else if (img_type == 11) - { - unsigned char v = ptr[m++]; - color = 0xFF000000 | (v << 16) | (v << 8) | v; - } - else - { - unsigned char b = ptr[m + 0]; - unsigned char g = ptr[m + 1]; - unsigned char r = ptr[m + 2]; - unsigned char a = (bytespp == 4) ? ptr[m + 3] : 0xFF; - m += bytespp; - - color = - ((uint32_t)a << 24) | - ((uint32_t)r << 16) | - ((uint32_t)g << 8) | - (uint32_t)b; - } - - for (int k = 0; k < count; k++) - out[2 + i++] = color; - } - else - { - // RAW - for (int k = 0; k < count; k++) - { - if (!ok(m, bytespp, size)) goto fail; - - uint32_t color; - - if (img_type == 9) - { - color = read_color(palette, ptr[m++], cmap_bpp); - } - else if (img_type == 11) - { - unsigned char v = ptr[m++]; - color = 0xFF000000 | (v << 16) | (v << 8) | v; - } - else - { - unsigned char b = ptr[m + 0]; - unsigned char g = ptr[m + 1]; - unsigned char r = ptr[m + 2]; - unsigned char a = (bytespp == 4) ? ptr[m + 3] : 0xFF; - - m += bytespp; - - color = - ((uint32_t)a << 24) | - ((uint32_t)r << 16) | - ((uint32_t)g << 8) | - (uint32_t)b; - } - - out[2 + i++] = color; - } - } - } - } - else - { - goto fail; - } - - return out; - -fail: - kfree(out); - return NULL; -} \ No newline at end of file diff --git a/src/drivers/video/tga.h b/src/drivers/video/tga.h deleted file mode 100644 index e1acfd2..0000000 --- a/src/drivers/video/tga.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -typedef struct { - unsigned char magic1; // must be zero - unsigned char colormap; // must be zero - unsigned char encoding; // must be 2 - unsigned short cmaporig, cmaplen; // must be zero - unsigned char cmapent; // must be zero - unsigned short x; // must be zero - unsigned short y; // image's height - unsigned short h; // image's height - unsigned short w; // image's width - unsigned char bpp; // must be 32 - unsigned char pixeltype; // must be 40 -} __attribute__((packed)) tga_header_t; -unsigned int *tga_parse(unsigned char *ptr, int size); \ No newline at end of file diff --git a/src/entry.c b/src/entry.c new file mode 100644 index 0000000..df4010f --- /dev/null +++ b/src/entry.c @@ -0,0 +1,227 @@ +#include +#include +#include +#include +#include "arch/x86_64/boot/gdt.h" +#include "arch/x86_64/boot/idt.h" +#include "arch/x86_64/boot/isr.h" +#include "mm/memory.h" +#include "mm/pmm.h" +#include "mm/vmm.h" +#include "arch/x86_64/bus/ata.h" +#include "string.h" +#include "arch/x86_64/cpu/io.h" +#include "arch/x86_64/sys/pit.h" +#include "arch/x86_64/bus/pci.h" +#include "arch/x86_64/sys/apic.h" +#include "mp/mp.h" +#include "drivers/fb/fb.h" +#include "libk/debug.h" +#include "fs/elf.h" +#include "sched/sched_types.h" +#include "arch/x86_64/sys/halt.h" +#include +#include "libk/random.h" +#include "libk/kargs.h" +#include "sched/sched_types.h" +#include "sched/sched.h" +#include "arch/x86_64/serial/serial.h" +#include "arch/x86_64/sys/tsc.h" +#include "arch/x86_64/fw/acpi.h" + +extern bool print_now; + + +static volatile struct limine_framebuffer_request framebuffer_request = { + .id = LIMINE_FRAMEBUFFER_REQUEST, + .revision = 6 +}; + + +static volatile struct limine_kernel_file_request limine_kernel_file_request = { + .id = LIMINE_KERNEL_FILE_REQUEST, .revision = 0}; + + +static volatile struct limine_memmap_request memmap_request = { + .id = LIMINE_MEMMAP_REQUEST, + .revision = 6 +}; + + +static volatile struct limine_rsdp_request rsdp_request = { + .id = LIMINE_RSDP_REQUEST, + .revision = 6 +}; + + +volatile struct limine_stack_size_request stack_size_request = { + .id = LIMINE_STACK_SIZE_REQUEST, + .revision = 0, + .stack_size = CPU_STACK_SIZE, +}; + + +volatile struct limine_smp_request mp_request = { + .id = LIMINE_SMP_REQUEST, + .revision = 0 +}; + + +static volatile struct limine_module_request module_request = { + .id = LIMINE_MODULE_REQUEST, .revision = 0}; + +extern bool is_halting; +extern uint8_t is_pausing; + +void nmi_vector(registers_t *reg) { + if (is_halting) { + for (;;) { + cli(); + halt(); + } + } else if (is_pausing) { + while (is_pausing & PAUSING) { + pause(); + } + apic_eoi(); + } else { + panic_((void *)(reg->rip), (void *)(reg->rbp), "Unexpected NMI\n"); + } +} + +void breakpoint_handler(registers_t *reg); + +static uint64_t rdseed(void) { + uint64_t r = 0; + asm volatile("rdseed %0" : "=r"(r)); + return r; +} + +static uint64_t rdrand(void) { + uint64_t r = 0; + asm volatile("rdrand %0" : "=r"(r)); + return r; +} + + +void random_setup_seed_source(void) { + uint32_t a = 0, b = 0, c = 0, d = 0; + __get_cpuid(7, &a, &b, &c, &d); + if (b & bit_RDSEED) { + kprintf("Using RDSEED as seed source\n"); + random_get_seed = rdseed; + } + if (c & bit_RDRND) { + kprintf("Using RDRAND as seed source\n"); + random_get_seed = rdrand; + } +} + + + +uint64_t g_rsdp_phys = 0; + +void _entry(void) { + cli(); + + struct limine_memmap_entry **memmap = memmap_request.response->entries; + size_t memmap_entries = memmap_request.response->entry_count; + + struct limine_rsdp_response *rsdp_response = rsdp_request.response; + + if (rsdp_response) { + g_rsdp_phys = (uint64_t)rsdp_response->address - MEM_PHYS_OFFSET; + } + + + + pmm_init(memmap, memmap_entries); + slab_init(); + vmm_init(memmap, memmap_entries); + + struct limine_framebuffer *framebuffer = framebuffer_request.response->framebuffers[0]; + struct framebuffer fb = {0}; + fb.address = (uint32_t *)framebuffer->address; + fb.pitch = framebuffer->pitch; + fb.bpp = framebuffer->bpp; + fb.width = framebuffer->width; + fb.height = framebuffer->height; + fb.tex_color = 0x00eee8d5; + fb.tex_x = 0; + fb.tex_y = 0; + fb.bg_color = 0x00124560; + fb.color_masks[0].offset = framebuffer->red_mask_shift; + fb.color_masks[0].length = framebuffer->red_mask_size; + fb.color_masks[1].offset = framebuffer->green_mask_shift; + fb.color_masks[1].length = framebuffer->green_mask_size; + fb.color_masks[2].offset = framebuffer->blue_mask_shift; + fb.color_masks[2].length = framebuffer->blue_mask_size; + framebuffer_init(&fb); + + print_now = true; + serial_init(); + + struct limine_file *kernel_file = + limine_kernel_file_request.response->kernel_file; + + kargs_init(kernel_file->cmdline); + uint16_t kernel_args_num = kernel_arguments.kernel_args; + uint32_t cpu_count = kernel_arguments.cpu_count; + + if ((kernel_args_num & KERNEL_ARGS_KPRINTF_LOGS)) { + put_to_fb = true; + } + + + kprintf("Hello x86_64!\n"); + kprintf("Kernel base: %p Mem phys base: %p\n", KERNEL_BASE, + MEM_PHYS_OFFSET); + + kprintf("KirkOS Ver. %s\n", KIRKOS_VERSION); + + kprintf("Got kernel cmdline as \"%s\"\n", kernel_file->cmdline); + if ((kernel_args_num & KERNEL_ARGS_CPU_COUNT_GIVEN)) + kprintf("CPU count is %u\n", cpu_count); + + gdt_init(); + isr_install(); + + isr_register_handler(2, nmi_vector); + isr_register_handler(3, breakpoint_handler); + isr_register_handler(48, resched); + + elf_init_function_table(kernel_file->address); + + acpi_init(); + + apic_init(); + + mp_init(mp_request.response); + + // The NSA has also forced hardware manufacturers to backdoor their 'Random + // Number Generators' to allow them to break RSA encryption + + if (!(kernel_arguments.kernel_args & + KERNEL_ARGS_DONT_TRUST_CPU_RANDOM_SEED)) { + random_setup_seed_source(); + } + random_set_seed(random_get_seed()); + + size_t module_info[2] = {0}; + if (module_request.response && module_request.response->module_count > 0) { + struct limine_file *module = module_request.response->modules[0]; + module_info[0] = (size_t)module->address; + module_info[1] = (size_t)module->size; + } + + + //time_init(); on hold for the time being, heh get it? + sched_init((uint64_t)module_info); + + timer_sched_oneshot(48, 20000); + sti(); + + for (;;) { + halt(); + } +} \ No newline at end of file diff --git a/src/fs/devtmpfs.c b/src/fs/devtmpfs.c new file mode 100644 index 0000000..caf4d61 --- /dev/null +++ b/src/fs/devtmpfs.c @@ -0,0 +1,341 @@ +#include "libk/debug.h" +#include "libk/errno.h" +#include "devtmpfs.h" +#include "vfs.h" +#include "libk/misc.h" +#include "libk/types.h" +#include "libk/resource.h" +#include "libk/time.h" +#include "mp/spinlock.h" +#include "mm/mmap.h" +#include "mm/pmm.h" +#include "mm/slab.h" +#include "mm/vmm.h" +#include +#include + + +struct devtmpfs_resource { + struct resource res; + + void *data; + size_t capacity; +}; + +struct devtmpfs { + struct vfs_filesystem fs; + + uint64_t dev_id; + uint64_t inode_counter; +}; + +static struct vfs_filesystem *devtmpfs = NULL; +static struct vfs_node *devtmpfs_root = NULL; + +static ssize_t devtmpfs_resource_read(struct resource *_this, + struct f_description *description, + void *buf, off_t offset, size_t count) { + (void)description; + + struct devtmpfs_resource *this = (struct devtmpfs_resource *)_this; + + spinlock_acquire_or_wait(&this->res.lock); + + size_t actual_count = count; + + if ((off_t)(offset + count) >= this->res.stat.st_size) { + actual_count = count - ((offset + count) - this->res.stat.st_size); + } + + memcpy(buf, this->data + offset, actual_count); + spinlock_drop(&this->res.lock); + + return actual_count; +} + +static ssize_t devtmpfs_resource_write(struct resource *_this, + struct f_description *description, + const void *buf, off_t offset, + size_t count) { + (void)description; + + ssize_t ret = -1; + struct devtmpfs_resource *this = (struct devtmpfs_resource *)_this; + + spinlock_acquire_or_wait(&this->res.lock); + + if (offset + count >= this->capacity) { + size_t new_capacity = this->capacity; + while (offset + count >= new_capacity) { + new_capacity *= 2; + } + + void *new_data = kmalloc(new_capacity); + if (new_data == NULL) { + errno = ENOMEM; + goto fail; + } + + memcpy(new_data, this->data, this->capacity); + kfree(this->data); + + this->data = new_data; + this->capacity = new_capacity; + } + + memcpy(this->data + offset, buf, count); + + if ((off_t)(offset + count) >= this->res.stat.st_size) { + this->res.stat.st_size = (off_t)(offset + count); + this->res.stat.st_blocks = + DIV_ROUNDUP(this->res.stat.st_size, this->res.stat.st_blksize); + } + + ret = count; + +fail: + spinlock_drop(&this->res.lock); + return ret; +} + +static void *devtmpfs_resource_mmap(struct resource *_this, size_t file_page, + int flags) { + struct devtmpfs_resource *this = (struct devtmpfs_resource *)_this; + + spinlock_acquire_or_wait(&this->res.lock); + + void *ret = NULL; + if ((flags & MAP_SHARED) != 0) { + ret = (this->data + file_page * PAGE_SIZE) - MEM_PHYS_OFFSET; + } else { + ret = pmm_alloc(1); + if (ret == NULL) { + goto cleanup; + } + + memcpy(ret + MEM_PHYS_OFFSET, this->data + file_page * PAGE_SIZE, + PAGE_SIZE); + } + +cleanup: + spinlock_drop(&this->res.lock); + return ret; +} + +static bool devtmpfs_truncate(struct resource *this_, + struct f_description *description, + size_t length) { + (void)description; + + struct devtmpfs_resource *this = (struct devtmpfs_resource *)this_; + + if (length > this->capacity) { + size_t new_capacity = this->capacity; + while (new_capacity < length) { + new_capacity *= 2; + } + + void *new_data = kmalloc(new_capacity); + if (new_data == NULL) { + errno = ENOMEM; + goto fail; + } + + memcpy(new_data, this->data, this->capacity); + kfree(this->data); + + this->data = new_data; + this->capacity = new_capacity; + } + + this->res.stat.st_size = (off_t)length; + this->res.stat.st_blocks = + DIV_ROUNDUP(this->res.stat.st_size, this->res.stat.st_blksize); + + return true; + +fail: + return false; +} + +static inline struct devtmpfs_resource * +create_devtmpfs_resource(struct devtmpfs *this, int mode) { + struct devtmpfs_resource *resource = + resource_create(sizeof(struct devtmpfs_resource)); + if (resource == NULL) { + return resource; + } + + if (S_ISREG(mode)) { + resource->capacity = 4096; + resource->data = kmalloc(resource->capacity); + resource->res.can_mmap = true; + } + + resource->res.read = devtmpfs_resource_read; + resource->res.write = devtmpfs_resource_write; + resource->res.mmap = devtmpfs_resource_mmap; + resource->res.truncate = devtmpfs_truncate; + + resource->res.stat.st_size = 0; + resource->res.stat.st_blocks = 0; + resource->res.stat.st_blksize = 512; + resource->res.stat.st_dev = this->dev_id; + resource->res.stat.st_ino = this->inode_counter++; + resource->res.stat.st_mode = mode; + resource->res.stat.st_nlink = 1; + + resource->res.stat.st_atim = time_realtime; + resource->res.stat.st_ctim = time_realtime; + resource->res.stat.st_mtim = time_realtime; + + return resource; +} + +static inline struct vfs_filesystem *devtmpfs_instantiate(void); + +static struct vfs_node *devtmpfs_mount(struct vfs_node *parent, + const char *name, + struct vfs_node *source) { + (void)parent; + (void)name; + (void)source; + return devtmpfs_root; +} + +static struct vfs_node *devtmpfs_create(struct vfs_filesystem *_this, + struct vfs_node *parent, + const char *name, int mode) { + struct devtmpfs *this = (struct devtmpfs *)_this; + struct vfs_node *new_node = NULL; + struct devtmpfs_resource *resource = NULL; + + new_node = vfs_create_node(_this, parent, name, S_ISDIR(mode)); + if (new_node == NULL) { + goto fail; + } + + resource = create_devtmpfs_resource(this, mode); + if (resource == NULL) { + goto fail; + } + + new_node->resource = (struct resource *)resource; + return new_node; + +fail: + if (new_node != NULL) { + kfree(new_node); // TODO: Use vfs_destroy_node + } + if (resource != NULL) { + kfree(resource); + } + + return NULL; +} + +static struct vfs_node *devtmpfs_symlink(struct vfs_filesystem *_this, + struct vfs_node *parent, + const char *name, const char *target) { + struct devtmpfs *this = (struct devtmpfs *)_this; + struct vfs_node *new_node = NULL; + struct devtmpfs_resource *resource = NULL; + + new_node = vfs_create_node(_this, parent, name, false); + if (new_node == NULL) { + goto fail; + } + + resource = create_devtmpfs_resource(this, 0777 | S_IFLNK); + if (resource == NULL) { + goto fail; + } + + new_node->resource = (struct resource *)resource; + new_node->symlink_target = strdup(target); + return new_node; + +fail: + if (new_node != NULL) { + kfree(new_node); // TODO: Use vfs_destroy_node + } + if (resource != NULL) { + kfree(resource); + } + + return NULL; +} + +static struct vfs_node *devtmpfs_link(struct vfs_filesystem *_this, + struct vfs_node *parent, const char *name, + struct vfs_node *node) { + if (S_ISDIR(node->resource->stat.st_mode)) { + errno = EISDIR; + return NULL; + } + + struct vfs_node *new_node = vfs_create_node(_this, parent, name, false); + if (new_node == NULL) { + return NULL; + } + + new_node->resource = node->resource; + return new_node; +} + +static inline struct vfs_filesystem *devtmpfs_instantiate(void) { + struct devtmpfs *new_fs = kmalloc(sizeof(struct devtmpfs)); + if (new_fs == NULL) { + return NULL; + } + + new_fs->fs.create = devtmpfs_create; + new_fs->fs.symlink = devtmpfs_symlink; + new_fs->fs.link = devtmpfs_link; + + return (struct vfs_filesystem *)new_fs; +} + +void devtmpfs_init(void) { + devtmpfs = devtmpfs_instantiate(); + if (devtmpfs == NULL) { + panic("Failed to instantiate devtmpfs\n"); + } + + devtmpfs_root = devtmpfs->create(devtmpfs, NULL, "", 0755 | S_IFDIR); + if (devtmpfs_root == NULL) { + panic("Failed to create root devtmpfs node\n"); + } + + vfs_add_filesystem(devtmpfs_mount, "devtmpfs"); +} + +bool devtmpfs_add_device(struct resource *device, const char *name) { + struct vfs_node *node = vfs_get_node(devtmpfs_root, name, false); + if (node != NULL) { + errno = EEXIST; + return false; + } + + struct vfs_node *new_node = + vfs_create_node(devtmpfs, devtmpfs_root, name, false); + if (new_node == NULL) { + return false; + } + + new_node->resource = device; + + struct devtmpfs *fs = (struct devtmpfs *)devtmpfs; + device->stat.st_dev = fs->dev_id; + device->stat.st_ino = fs->inode_counter++; + device->stat.st_nlink = 1; + + device->stat.st_atim = time_realtime; + device->stat.st_ctim = time_realtime; + device->stat.st_mtim = time_realtime; + + spinlock_acquire_or_wait(&vfs_lock); + HASHMAP_SINSERT(&devtmpfs_root->children, name, new_node); + spinlock_drop(&vfs_lock); + return new_node; +} diff --git a/src/fs/devtmpfs.h b/src/fs/devtmpfs.h new file mode 100644 index 0000000..dced6ed --- /dev/null +++ b/src/fs/devtmpfs.h @@ -0,0 +1,6 @@ +#pragma once +#include "libk/resource.h" +#include + +void devtmpfs_init(void); +bool devtmpfs_add_device(struct resource *device, const char *name); diff --git a/src/fs/elf.c b/src/fs/elf.c index e845ee9..74ae785 100644 --- a/src/fs/elf.c +++ b/src/fs/elf.c @@ -1,231 +1,489 @@ -// elf.c (now extracts AT_PHDR / AT_PHENT / AT_PHNUM + minor cleanups) #include "elf.h" -#include "libk/stdio.h" #include "libk/string.h" #include "mm/pmm.h" #include "mm/vmm.h" #include "mm/memory.h" -#include "fs/ext2.h" +#include "libk/debug.h" +#include "libk/module.h" +#include "fs/vfs.h" +#include "mm/mmap.h" +#include "libk/resource.h" +#include "libk/misc.h" -extern uintptr_t g_hhdm_offset; +// static struct function_symbol *name_to_function = NULL; +static struct function_symbol *function_to_name = NULL; +static size_t function_table_size = 0; +bool symbol_table_initialised = false; -bool ELF_Read(const char* path, - void** entryPoint, - struct pagemap *target_pagemap, - uint64_t *out_tls_fs_base, - uint64_t *out_phdr_va, - uint16_t *out_phent, - uint16_t *out_phnum) -{ - *out_tls_fs_base = 0; - *out_phdr_va = 0; - *out_phent = 0; - *out_phnum = 0; +module_list_t modules_list = {0}; +/* +static void simple_append_name(const char *string, uint64_t address) { + size_t index = hash(string, strlen(string)) % function_table_size; + name_to_function[index].address = address; + name_to_function[index].name = string; +} +*/ +const char *elf_get_name_from_function(uint64_t address) { + if (!symbol_table_initialised) + return ""; - uint32_t inum = ext2_resolve_path(path); - if (!inum) { - printf("ELF: file not found: %s\n", path); - return false; - } + address -= KERNEL_BASE; + address += 0xffffffff80000000; - ext2_inode_t inode; - if (!ext2_read_inode(inum, &inode)) { - printf("ELF: failed to read inode\n"); - return false; - } + // I HATE HASH COLLISIONS + // I HATE HASH COLLISIONS + for (size_t i = 0; i < function_table_size; i++) { + if (function_to_name[i].address == address) + return function_to_name[i].name; + } - uint64_t file_size = inode.i_size; - if (file_size < sizeof(ELFHeader)) { - printf("ELF: file too small\n"); - return false; - } + return "UNKNOWN"; +} - uint64_t buf_pages = ALIGN_UP(file_size, PAGE_SIZE) / PAGE_SIZE; - void* buffer_phys = pmm_allocz(buf_pages); - if (!buffer_phys) { - printf("ELF: failed to allocate %lu pages for file buffer\n", buf_pages); - return false; - } +uint64_t elf_get_function_from_name(const char *string) { + if (!symbol_table_initialised || !string) + return (uint64_t)NULL; - uint8_t* elf_buffer = (uint8_t*)((uintptr_t)buffer_phys + MEM_PHYS_OFFSET); + // size_t index = hash(string, strlen(string)) % function_table_size; + // uint64_t address = name_to_function[index].address; - if (!ext2_read_file(&inode, elf_buffer)) { - pmm_free(buffer_phys, buf_pages); - return false; - } + // I HATE HASH COLLISIONS + // I HATE HASH COLLISIONS + uint64_t address = 0; + for (size_t i = 0; i < function_table_size; i++) { + if (!strcmp(function_to_name[i].name, string)) { + address = function_to_name[i].address; + break; + } + } - ELFHeader* header = (ELFHeader*)elf_buffer; + if (address) { + address -= 0xffffffff80000000; + address += KERNEL_BASE; + } - printf("=== ELF DEBUG ===\n" - "Entry=0x%lx PHDR@0x%lx count=%u type=0x%x arch=0x%x\n" - "=== END ===\n", - header->ProgramEntryPosition, - header->ProgramHeaderTablePosition, - header->ProgramHeaderTableEntryCount, - header->Type, - header->InstructionSet); + return address; +} - if (memcmp(header->Magic, ELF_MAGIC, 4) != 0 || - header->Bitness != ELF_BITNESS_64BIT || - header->Endianness != ELF_ENDIANNESS_LITTLE || - (header->Type != ELF_TYPE_EXECUTABLE && header->Type != ELF_TYPE_SHARED) || - header->InstructionSet != ELF_INSTRUCTION_SET_X64) { +void elf_init_function_table(uint8_t *binary) { + vec_init(&modules_list); - printf("ELF: unsupported/invalid header\n"); - goto cleanup; - } + Elf64_Ehdr *ehdr = (Elf64_Ehdr *)binary; - *entryPoint = (void*)header->ProgramEntryPosition; + Elf64_Shdr *elf_section_headers = (Elf64_Shdr *)(binary + ehdr->e_shoff); - // ------------------------------------------------------------------ - // Parse program headers – LOAD, TLS, and PHDR - // ------------------------------------------------------------------ - uint64_t tls_vaddr = 0, tls_filesz = 0, tls_memsz = 0, tls_align = 8; - uint8_t* tls_src = NULL; - uint64_t phdr_vaddr = 0; + Elf64_Shdr *symtab = NULL; + char *strtab = NULL; - uint8_t* ph_table = elf_buffer + header->ProgramHeaderTablePosition; - uint64_t phdr_table_end = header->ProgramHeaderTablePosition + - (uint64_t)header->ProgramHeaderTableEntryCount * - header->ProgramHeaderTableEntrySize; + for (int i = 0; i < ehdr->e_shnum; i++) { + if (elf_section_headers[i].sh_type == SHT_SYMTAB) { + symtab = &elf_section_headers[i]; + strtab = (char *)((uintptr_t)binary + + elf_section_headers[symtab->sh_link].sh_offset); + break; + } + } - if (phdr_table_end > file_size) { - printf("ELF: program header table extends beyond file\n"); - goto cleanup; - } + if (symtab != NULL) { + Elf64_Sym *sym_entries = (Elf64_Sym *)(binary + symtab->sh_offset); + size_t num_symbols = symtab->sh_size / symtab->sh_entsize; - for (uint32_t i = 0; i < header->ProgramHeaderTableEntryCount; i++) { - ELFProgramHeader* ph = (ELFProgramHeader*)(ph_table + i * header->ProgramHeaderTableEntrySize); + function_table_size = 0; + // name_to_function = + // kmalloc(sizeof(struct function_symbol) * num_symbols); + function_to_name = + kmalloc(sizeof(struct function_symbol) * num_symbols); - // PT_PHDR - if (ph->Type == ELF_PROGRAM_TYPE_PHDR) { - phdr_vaddr = ph->VirtualAddress; - printf("ELF: Found PT_PHDR VA=0x%lx\n", phdr_vaddr); - // fall through - } + for (size_t i = 0; i < num_symbols; i++) { + if ((ELF64_ST_TYPE(sym_entries[i].st_info) == STT_OBJECT) && + ELF64_ST_BIND(sym_entries[i].st_info) == STB_GLOBAL) { + if (((uint64_t)strtab + sym_entries[i].st_name)) { + char *dupped = strdup( + (char *)((uint64_t)strtab + sym_entries[i].st_name)); - // PT_TLS - if (ph->Type == ELF_PROGRAM_TYPE_TLS) { - tls_vaddr = ph->VirtualAddress; - tls_filesz = ph->FileSize; - tls_memsz = ph->MemorySize; - tls_align = ph->Align ? ph->Align : 8; - tls_src = elf_buffer + ph->Offset; + function_to_name[function_table_size].address = + sym_entries[i].st_value; + function_to_name[function_table_size++].name = dupped; + } + } + if ((ELF64_ST_TYPE(sym_entries[i].st_info) == STT_FUNC) && + ELF64_ST_BIND(sym_entries[i].st_info) == STB_GLOBAL) { - if (ph->Offset + ph->FileSize > file_size) { - printf("ELF: PT_TLS segment data out of file bounds\n"); - goto cleanup; - } + // We need a better hash function for the addresses since there + // are hash collisions. For now we are using this slow way. + if (((uint64_t)strtab + sym_entries[i].st_name)) { + char *dupped = strdup( + (char *)((uint64_t)strtab + sym_entries[i].st_name)); - printf("ELF: Found PT_TLS VA=0x%lx FileSz=0x%lx MemSz=0x%lx Align=0x%lx\n", - tls_vaddr, tls_filesz, tls_memsz, tls_align); - continue; - } + function_to_name[function_table_size].address = + sym_entries[i].st_value; + function_to_name[function_table_size++].name = dupped; - if (ph->Type != ELF_PROGRAM_TYPE_LOAD || ph->MemorySize == 0) - continue; + // simple_append_name(dupped, sym_entries[i].st_value); + } + } + } + symbol_table_initialised = true; + } +} - if (ph->Offset + ph->FileSize > file_size) { - printf("ELF: PT_LOAD segment data out of file bounds\n"); - goto cleanup; - } +uint64_t module_load(const char *path) { + struct vfs_node *node = vfs_get_node(vfs_root, path, true); + if (!node) { + kprintf("Module not found\n"); + return 1; + } - // Map with exact permissions - uint64_t map_flags = PAGE_USER; - if (ph->Flags & PF_R) map_flags |= PAGE_READ; - if (ph->Flags & PF_W) map_flags |= PAGE_WRITE; - if (!(ph->Flags & PF_X)) map_flags |= PAGE_NO_EXECUTE; + struct resource *res = node->resource; - uint64_t virt = ph->VirtualAddress; - uint64_t aligned_virt = ALIGN_DOWN(virt, PAGE_SIZE); - uint64_t page_offset = virt & (PAGE_SIZE - 1); - uint64_t aligned_memsz = ALIGN_UP(ph->MemorySize + page_offset, PAGE_SIZE); - uint64_t pages = aligned_memsz / PAGE_SIZE; + struct module *m = kmalloc(sizeof(struct module)); + memzero(m, sizeof(struct module)); + strncpy(m->name, path, sizeof(m->name)); - void* seg_phys = pmm_allocz(pages); - if (!seg_phys) { - printf("ELF: failed to allocate physical pages for LOAD segment\n"); - goto cleanup; - } + Elf64_Ehdr *ehdr = kmalloc(sizeof(Elf64_Ehdr)); - for (uint64_t p = 0; p < pages; p++) { - if (!vmm_map_page(target_pagemap, - aligned_virt + p * PAGE_SIZE, - (uintptr_t)seg_phys + p * PAGE_SIZE, - map_flags, - Size4KiB)) { - pmm_free(seg_phys, pages); - goto cleanup; - } - } + res->read(res, NULL, ehdr, 0, sizeof(Elf64_Ehdr)); - uint8_t* dst = (uint8_t*)((uintptr_t)seg_phys + MEM_PHYS_OFFSET); - memcpy(dst + page_offset, elf_buffer + ph->Offset, ph->FileSize); - if (ph->MemorySize > ph->FileSize) { - memset(dst + page_offset + ph->FileSize, 0, - ph->MemorySize - ph->FileSize); - } - } + if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG)) { + kprintf("ELF header check fail\n"); + return 1; + } + if (ehdr->e_ident[EI_CLASS] != ELFCLASS64 || + ehdr->e_ident[EI_DATA] != ELFDATA2LSB || ehdr->e_ident[EI_OSABI] != 0 || + ehdr->e_machine != EM_X86_64) { + kprintf("This ELF isn't for us\n"); + return 1; + } - uint64_t tls_size = tls_memsz ? ALIGN_UP(tls_memsz, tls_align) : 0ULL; + if (ehdr->e_type != 1) { + kprintf("This ELF isn't executable\n"); + return 1; + } - uint64_t tcb_va = TLS_BASE_VA + PAGE_SIZE; - uint64_t tls_va = tcb_va - tls_size; + if (ehdr->e_shentsize != sizeof(Elf64_Shdr)) { + kprintf("Malformed ELF?\n"); + return 1; + } - uint64_t page_va = ALIGN_DOWN(tls_va, PAGE_SIZE); - uint64_t tcb_size = sizeof(TCB); - uint64_t block_end_va = tcb_va + tcb_size; - uint64_t block_end_page = ALIGN_UP(block_end_va, PAGE_SIZE); - uint64_t map_pages = ((block_end_page - page_va) / PAGE_SIZE) + 8; - if (map_pages == 0) map_pages = 1; + // Setup variable for the section headers + size_t section_count = ehdr->e_shnum; + size_t section_header_size = sizeof(Elf64_Shdr); + Elf64_Shdr *elf_section_headers = + kmalloc(section_header_size * section_count); + res->read(res, NULL, elf_section_headers, ehdr->e_shoff, + section_header_size * section_count); - void* tls_phys = pmm_allocz(map_pages); - if (!tls_phys) { - printf("ELF: failed to allocate TLS/TCB pages\n"); - goto cleanup; - } + for (size_t index = 0; index < section_count; index++) { + Elf64_Shdr *section = (elf_section_headers + index); - uint64_t tls_map_flags = PAGE_USER | PAGE_READ | PAGE_WRITE; - for (uint64_t p = 0; p < map_pages; p++) { - if (!vmm_map_page(target_pagemap, - page_va + p * PAGE_SIZE, - (uintptr_t)tls_phys + p * PAGE_SIZE, - tls_map_flags, - Size4KiB)) { - pmm_free(tls_phys, map_pages); - goto cleanup; - } - } + // If this section should allocate memory and is bigger than 0 + if ((section->sh_flags & SHF_ALLOC) && section->sh_size > 0) { + // Allocate memory, currenty RW so we can write to it + char *mem = (char *)((uint64_t)pmm_allocz( + 1 + (section->sh_size / PAGE_SIZE)) + + MEM_PHYS_OFFSET); - uint8_t* base_hhdm = (uint8_t*)((uintptr_t)tls_phys + MEM_PHYS_OFFSET); + m->mappings[m->mappings_count].addr = (uint64_t)(size_t)mem; + m->mappings[m->mappings_count++].size = section->sh_size; - if (tls_size > 0) { - uint8_t* tls_dst = base_hhdm + (tls_va - page_va); - if (tls_filesz) memcpy(tls_dst, tls_src, tls_filesz); - if (tls_memsz > tls_filesz) - memset(tls_dst + tls_filesz, 0, tls_memsz - tls_filesz); - } + if (section->sh_type == SHT_PROGBITS) { + // Read data from the file + res->read(res, NULL, mem, section->sh_offset, section->sh_size); + } else if (section->sh_type == SHT_NOBITS) { + // Section is empty, so fill with zeros + memzero(mem, section->sh_size); + } + section->sh_addr = (uintptr_t)mem; + } - TCB* tcb = (TCB*)(base_hhdm + (tcb_va - page_va)); - memset(tcb, 0, sizeof(TCB)); - tcb->self = (void*)tcb_va; - tcb->tid = 1; + // Load symbol and string tables from the file + if (section->sh_type == SHT_SYMTAB || section->sh_type == SHT_STRTAB) { + Elf64_Sym *table = kmalloc(section->sh_size); - *out_tls_fs_base = tcb_va; + res->read(res, NULL, table, section->sh_offset, section->sh_size); - *out_phdr_va = tls_vaddr ? tls_vaddr : phdr_vaddr; - *out_phent = header->ProgramHeaderTableEntrySize; - *out_phnum = header->ProgramHeaderTableEntryCount; + section->sh_addr = (uintptr_t)table; + } + } - printf("ELF: TLS/TCB setup complete TCB@0x%lx TLS@0x%lx FS=0x%lx\n" - " PHDR@0x%lx PHENT=0x%x PHNUM=%u\n", - tcb_va, tls_va, tcb_va, *out_phdr_va, *out_phent, *out_phnum); + for (size_t index = 0; index < section_count; index++) { + Elf64_Shdr *section = (elf_section_headers + index); - pmm_free(buffer_phys, buf_pages); - return true; + if (section->sh_type == SHT_RELA) { + size_t entry_count = section->sh_size / section->sh_entsize; + if (section->sh_entsize != sizeof(Elf64_Rela)) { + return 1; + } -cleanup: - pmm_free(buffer_phys, buf_pages); - return false; -} \ No newline at end of file + // Load relocation entries from the file + Elf64_Rela *entries = kmalloc(section->sh_size); + res->read(res, NULL, entries, section->sh_offset, section->sh_size); + section->sh_addr = (uintptr_t)entries; + + // Locate the section we are relocating + Elf64_Shdr *relocation_section = + (Elf64_Shdr *)(elf_section_headers + section->sh_info); + char *relocation_section_data = (char *)relocation_section->sh_addr; + + // Locate the symbol table for this relocation table + Elf64_Shdr *section_symbol_table = + (elf_section_headers + section->sh_link); + Elf64_Sym *symbol_table = + (Elf64_Sym *)(section_symbol_table->sh_addr); + + // Locate the string table for the symbol table + Elf64_Shdr *section_string_table = + (elf_section_headers + section_symbol_table->sh_link); + char *string_table = (char *)(section_string_table->sh_addr); + + // Relocate all the entries + for (size_t entry_ind = 0; entry_ind < entry_count; entry_ind++) { + Elf64_Rela *entry = (entries + entry_ind); + + // Find the symbol for this entry + Elf64_Sym *symbol = (symbol_table + ELF64_R_SYM(entry->r_info)); + char *symbol_name = (string_table + symbol->st_name); + + if (ELF64_R_TYPE(entry->r_info) == 1) { + // Determine the offset in the section + uint64_t *location = + (uint64_t *)((uint64_t)relocation_section_data + + entry->r_offset); + + // Check that the symbol is defined in this file + if (symbol->st_shndx > 0) { + // Calculate the location of the symbol + Elf64_Shdr *symbol_section = + (elf_section_headers + symbol->st_shndx); + uint64_t symbol_value = symbol_section->sh_addr + + symbol->st_value + + entry->r_addend; + + // Store the location + *location = symbol_value; + } else { + // The symbol is not defined inside the object file + // resolve using other rules + // kprintffos(0, "%s %p %s\n", symbol_name, + // elf_get_function_from_name(symbol_name), + // elf_get_name_from_function(elf_get_function_from_name(symbol_name))); + *location = elf_get_function_from_name(symbol_name); + if (*location == 0) { + // We need to add some exemptions + kprintf("Failed to find symbol %s\n", symbol_name); + return 1; + } + } + } else { + kprintf("What the fuck?\n"); + return 1; + } + } + } + } + + for (size_t index = 0; index < section_count; index++) { + Elf64_Shdr *section = (elf_section_headers + index); + + if ((section->sh_flags & SHF_ALLOC) && section->sh_size > 0) { + // Calculate the correct permissions + int prot = PAGE_READ; + if (section->sh_flags & SHF_WRITE) + prot |= PAGE_WRITE; + + m->mappings[index].prot = prot; + + for (size_t i = 0; i < section->sh_size; i += PAGE_SIZE) { + vmm_map_page(kernel_pagemap, section->sh_addr, + section->sh_addr - MEM_PHYS_OFFSET, prot, + Size4KiB); + } + } + } + + module_entry_t run_func = NULL; + for (size_t index = 0; index < section_count; index++) { + Elf64_Shdr *section = (elf_section_headers + index); + + // Look in all symbol tables for the run function + if (section->sh_type == SHT_SYMTAB) { + Elf64_Sym *symbol_table = (Elf64_Sym *)section->sh_addr; + + // Find the string table for this symbol table + Elf64_Shdr *section_string_table = + (elf_section_headers + section->sh_link); + char *string_table = (char *)(section_string_table->sh_addr); + + size_t symbol_count = section->sh_size / section->sh_entsize; + for (size_t i = 0; i < symbol_count; i++) { + // Get the symbol from the table + Elf64_Sym *symbol = (symbol_table + i); + char *symbol_name = (string_table + symbol->st_name); + Elf64_Shdr *run_section = + (elf_section_headers + symbol->st_shndx); + + // Check if it is the run symbol + if (strcmp("driver_entry", symbol_name) == 0 && + ((symbol->st_info >> 4) & 0xf) == 1 && + (run_section->sh_flags & 4)) { + // Calculate the symbol location + run_func = (module_entry_t)(run_section->sh_addr + + symbol->st_value); + + break; + } + } + } + + if (run_func) + break; + } + + kfree(ehdr); + kfree(elf_section_headers); + + if (run_func != NULL) { + m->entry_point = run_func; + vec_push(&modules_list, m); + return run_func(m); + } else { + kprintf("Failed to find driver_entry\n"); + return 1; + } +} + +bool module_unload(const char *name) { + for (int i = 0; i < modules_list.length; i++) { + if (!strncmp(name, modules_list.data[i]->name, 128)) { + struct module *m = modules_list.data[i]; + if (!m->exit) + return false; + + m->exit(); + for (size_t j = 0; j < m->mappings_count; j++) { + pmm_free((void *)(m->mappings[j].addr - MEM_PHYS_OFFSET), + (m->mappings[j].size / PAGE_SIZE) + 1); + for (size_t k = 0; k < m->mappings[j].size; k += PAGE_SIZE) { + vmm_unmap_page(kernel_pagemap, m->mappings[j].addr, false); + } + } + vec_remove(&modules_list, m); + kfree(m); + return true; + } + } + return false; +} + +void module_dump(void) { + kprintf("Loaded drivers\n"); + for (int i = 0; i < modules_list.length; i++) { + struct module *mod = modules_list.data[i]; + kprintf("\t%s with entry point at %p\n", mod->name, mod->entry_point); + for (size_t j = 0; j < mod->mappings_count; j++) { + kprintf("\t\t%p - %p with protections 0b%b\n", + mod->mappings[j].addr, + mod->mappings[j].addr + mod->mappings[j].size, + mod->mappings[j].prot); + } + } +} + +bool elf_load(struct pagemap *pagemap, struct resource *res, uint64_t load_base, + struct auxval *auxv, const char **ld_path) { + Elf64_Ehdr header; + + if (res->read(res, NULL, &header, 0, sizeof(header)) < 0) { + return false; + } + + if (memcmp(header.e_ident, ELFMAG, SELFMAG)) { + return false; + } + + if (header.e_ident[EI_CLASS] != ELFCLASS64 || + header.e_ident[EI_DATA] != ELFDATA2LSB || + header.e_ident[EI_OSABI] != 0 || header.e_machine != EM_X86_64) { + return false; + } + + for (size_t i = 0; i < header.e_phnum; i++) { + Elf64_Phdr phdr; + if (res->read(res, NULL, &phdr, header.e_phoff + i * header.e_phentsize, + sizeof(phdr)) < 0) { + goto fail; + } + + switch (phdr.p_type) { + case PT_LOAD: { + int prot = PROT_READ; + if (phdr.p_flags & PF_W) { + prot |= PROT_WRITE; + } + if (phdr.p_flags & PF_X) { + prot |= PROT_EXEC; + } + + size_t misalign = phdr.p_vaddr & (PAGE_SIZE - 1); + size_t page_count = + DIV_ROUNDUP(phdr.p_memsz + misalign, PAGE_SIZE); + + void *phys = pmm_allocz(page_count); + if (phys == NULL) { + goto fail; + } + + if (!mmap_range(pagemap, phdr.p_vaddr + load_base, + (uintptr_t)phys, page_count * PAGE_SIZE, prot, + MAP_ANONYMOUS)) { + pmm_free(phys, page_count); + goto fail; + } + + if (res->read( + res, NULL, + (void *)((uintptr_t)phys + misalign + MEM_PHYS_OFFSET), + phdr.p_offset, phdr.p_filesz) < 0) { + goto fail; + } + + break; + } + case PT_PHDR: + auxv->at_phdr = phdr.p_vaddr + load_base; + break; + case PT_INTERP: { + void *path = kmalloc(phdr.p_filesz + 1); + if (path == NULL) { + goto fail; + } + + if (res->read(res, NULL, path, phdr.p_offset, phdr.p_filesz) < + 0) { + kfree(path); + goto fail; + } + + if (ld_path != NULL) { + *ld_path = path; + } else { + kfree(path); + path = NULL; + } + break; + } + } + } + + auxv->at_entry = header.e_entry + load_base; + auxv->at_phent = header.e_phentsize; + auxv->at_phnum = header.e_phnum; + return true; + +fail: + if (ld_path != NULL && *ld_path != NULL) { + kfree((void *)*ld_path); + } + return false; +} diff --git a/src/fs/elf.h b/src/fs/elf.h index 56f6cb1..5653287 100644 --- a/src/fs/elf.h +++ b/src/fs/elf.h @@ -1,130 +1,445 @@ -// elf.h #pragma once #include #include #include // size_t for TCB #include "mm/vmm.h" +#include "libk/resource.h" -// ELF magic and basic constants -#define ELF_MAGIC ("\x7F" "ELF") - -// Standard ELF program header flags (bitfield) -#define PF_X 0x00000001 // Execute -#define PF_W 0x00000002 // Write -#define PF_R 0x00000004 // Read - -// Fixed canonical address for the initial thread's TLS + TCB block. -// This lives in the upper half of the 47-bit user address space and -// will never overlap with normal LOAD segments (which are usually low). -#define TLS_BASE_VA 0x00007FFF00000000ULL - -typedef struct -{ - uint8_t Magic[4]; - uint8_t Bitness; // 1 = 32-bit, 2 = 64-bit - uint8_t Endianness; // 1 = little, 2 = big - uint8_t ELFHeaderVersion; - uint8_t ABI; - uint8_t ABIVersion; - uint8_t _Padding[7]; - - uint16_t Type; // relocatable, executable, shared, core - uint16_t InstructionSet; // architecture - - uint32_t ELFVersion; - - uint64_t ProgramEntryPosition; - uint64_t ProgramHeaderTablePosition; - uint64_t SectionHeaderTablePosition; - - uint32_t Flags; - - uint16_t HeaderSize; - uint16_t ProgramHeaderTableEntrySize; - uint16_t ProgramHeaderTableEntryCount; - uint16_t SectionHeaderTableEntrySize; - uint16_t SectionHeaderTableEntryCount; - uint16_t SectionNamesIndex; - -} __attribute__((packed)) ELFHeader; - -enum ELFBitness -{ - ELF_BITNESS_32BIT = 1, - ELF_BITNESS_64BIT = 2, +struct auxval { + uint64_t at_entry; + uint64_t at_phdr; + uint64_t at_phent; + uint64_t at_phnum; }; -enum ELFEndianness -{ - ELF_ENDIANNESS_LITTLE = 1, - ELF_ENDIANNESS_BIG = 2, -}; +/* These constants define the various ELF target machines */ +#define EM_NONE 0 +#define EM_M32 1 +#define EM_SPARC 2 +#define EM_386 3 +#define EM_68K 4 +#define EM_88K 5 +#define EM_486 6 /* Perhaps disused */ +#define EM_860 7 +#define EM_MIPS 8 /* MIPS R3000 (officially, big-endian only) */ +#define EM_MIPS_RS3_LE 10 /* MIPS R3000 little-endian */ +#define EM_MIPS_RS4_BE 10 /* MIPS R4000 big-endian */ -enum ELFInstructionSet -{ - ELF_INSTRUCTION_SET_NONE = 0, - ELF_INSTRUCTION_SET_X86 = 3, - ELF_INSTRUCTION_SET_ARM = 0x28, - ELF_INSTRUCTION_SET_X64 = 0x3E, - ELF_INSTRUCTION_SET_ARM64 = 0xB7, - ELF_INSTRUCTION_SET_RISCV = 0xF3, -}; +#define EM_PARISC 15 /* HPPA */ +#define EM_SPARC32PLUS 18 /* Sun's "v8plus" */ +#define EM_PPC 20 /* PowerPC */ +#define EM_PPC64 21 /* PowerPC64 */ +#define EM_SPU 23 /* Cell BE SPU */ +#define EM_ARM 40 /* ARM 32 bit */ +#define EM_SH 42 /* SuperH */ +#define EM_SPARCV9 43 /* SPARC v9 64-bit */ +#define EM_H8_300 46 /* Renesas H8/300 */ +#define EM_IA_64 50 /* HP/Intel IA-64 */ +#define EM_X86_64 62 /* AMD x86-64 */ +#define EM_S390 22 /* IBM S/390 */ +#define EM_CRIS 76 /* Axis Communications 32-bit embedded processor */ +#define EM_M32R 88 /* Renesas M32R */ +#define EM_MN10300 89 /* Panasonic/MEI MN10300, AM33 */ +#define EM_OPENRISC 92 /* OpenRISC 32-bit embedded processor */ +#define EM_ARCOMPACT 93 /* ARCompact processor */ +#define EM_XTENSA 94 /* Tensilica Xtensa Architecture */ +#define EM_BLACKFIN 106 /* ADI Blackfin Processor */ +#define EM_UNICORE 110 /* UniCore-32 */ +#define EM_ALTERA_NIOS2 113 /* Altera Nios II soft-core processor */ +#define EM_TI_C6000 140 /* TI C6X DSPs */ +#define EM_HEXAGON 164 /* QUALCOMM Hexagon */ +#define EM_NDS32 \ + 167 /* Andes Technology compact code size \ + embedded RISC processor family */ +#define EM_AARCH64 183 /* ARM 64 bit */ +#define EM_TILEPRO 188 /* Tilera TILEPro */ +#define EM_MICROBLAZE 189 /* Xilinx MicroBlaze */ +#define EM_TILEGX 191 /* Tilera TILE-Gx */ +#define EM_ARCV2 195 /* ARCv2 Cores */ +#define EM_RISCV 243 /* RISC-V */ +#define EM_BPF 247 /* Linux BPF - in-kernel virtual machine */ +#define EM_CSKY 252 /* C-SKY */ +#define EM_LOONGARCH 258 /* LoongArch */ +#define EM_FRV 0x5441 /* Fujitsu FR-V */ -enum ELFType -{ - ELF_TYPE_RELOCATABLE = 1, - ELF_TYPE_EXECUTABLE = 2, - ELF_TYPE_SHARED = 3, - ELF_TYPE_CORE = 4, -}; +/* + * This is an interim value that we will use until the committee comes + * up with a final number. + */ +#define EM_ALPHA 0x9026 -typedef struct -{ - uint32_t Type; - uint32_t Flags; - uint64_t Offset; - uint64_t VirtualAddress; - uint64_t PhysicalAddress; - uint64_t FileSize; - uint64_t MemorySize; - uint64_t Align; +/* Bogus old m32r magic number, used by old tools. */ +#define EM_CYGNUS_M32R 0x9041 +/* This is the old interim value for S/390 architecture */ +#define EM_S390_OLD 0xA390 +/* Also Panasonic/MEI MN10300, AM33 */ +#define EM_CYGNUS_MN10300 0xbeef -} __attribute__((packed)) ELFProgramHeader; +/* 32-bit ELF base types. */ +typedef uint32_t Elf32_Addr; +typedef uint16_t Elf32_Half; +typedef uint32_t Elf32_Off; +typedef int32_t Elf32_Sword; +typedef uint32_t Elf32_Word; -enum ELFProgramType { - ELF_PROGRAM_TYPE_NULL = 0, - ELF_PROGRAM_TYPE_LOAD = 1, - ELF_PROGRAM_TYPE_DYNAMIC = 2, - ELF_PROGRAM_TYPE_INTERP = 3, - ELF_PROGRAM_TYPE_NOTE = 4, - ELF_PROGRAM_TYPE_SHLIB = 5, - ELF_PROGRAM_TYPE_PHDR = 6, - ELF_PROGRAM_TYPE_TLS = 7, +/* 64-bit ELF base types. */ +typedef uint64_t Elf64_Addr; +typedef uint16_t Elf64_Half; +typedef int16_t Elf64_SHalf; +typedef uint64_t Elf64_Off; +typedef int32_t Elf64_Sword; +typedef uint32_t Elf64_Word; +typedef uint64_t Elf64_Xword; +typedef int64_t Elf64_Sxword; - // OS/processor reserved ranges (we ignore them) - ELF_PROGRAM_TYPE_LOOS = 0x60000000, - ELF_PROGRAM_TYPE_HIOS = 0x6FFFFFFF, - ELF_PROGRAM_TYPE_LOPROC = 0x70000000, - ELF_PROGRAM_TYPE_HIPROC = 0x7FFFFFFF, -}; +/* These constants are for the segment types stored in the image headers */ +#define PT_NULL 0 +#define PT_LOAD 1 +#define PT_DYNAMIC 2 +#define PT_INTERP 3 +#define PT_NOTE 4 +#define PT_SHLIB 5 +#define PT_PHDR 6 +#define PT_TLS 7 /* Thread local storage segment */ +#define PT_LOOS 0x60000000 /* OS-specific */ +#define PT_HIOS 0x6fffffff /* OS-specific */ +#define PT_LOPROC 0x70000000 +#define PT_HIPROC 0x7fffffff +#define PT_GNU_EH_FRAME 0x6474e550 + +#define PT_GNU_STACK (PT_LOOS + 0x474e551) + +/* These constants define the different elf file types */ +#define ET_NONE 0 +#define ET_REL 1 +#define ET_EXEC 2 +#define ET_DYN 3 +#define ET_CORE 4 +#define ET_LOPROC 0xff00 +#define ET_HIPROC 0xffff + +/* This is the info that is needed to parse the dynamic section of the file */ +#define DT_NULL 0 +#define DT_NEEDED 1 +#define DT_PLTRELSZ 2 +#define DT_PLTGOT 3 +#define DT_HASH 4 +#define DT_STRTAB 5 +#define DT_SYMTAB 6 +#define DT_RELA 7 +#define DT_RELASZ 8 +#define DT_RELAENT 9 +#define DT_STRSZ 10 +#define DT_SYMENT 11 +#define DT_INIT 12 +#define DT_FINI 13 +#define DT_SONAME 14 +#define DT_RPATH 15 +#define DT_SYMBOLIC 16 +#define DT_REL 17 +#define DT_RELSZ 18 +#define DT_RELENT 19 +#define DT_PLTREL 20 +#define DT_DEBUG 21 +#define DT_TEXTREL 22 +#define DT_JMPREL 23 +#define DT_ENCODING 32 +#define OLD_DT_LOOS 0x60000000 +#define DT_LOOS 0x6000000d +#define DT_HIOS 0x6ffff000 +#define DT_VALRNGLO 0x6ffffd00 +#define DT_VALRNGHI 0x6ffffdff +#define DT_ADDRRNGLO 0x6ffffe00 +#define DT_ADDRRNGHI 0x6ffffeff +#define DT_VERSYM 0x6ffffff0 +#define DT_RELACOUNT 0x6ffffff9 +#define DT_RELCOUNT 0x6ffffffa +#define DT_FLAGS_1 0x6ffffffb +#define DT_VERDEF 0x6ffffffc +#define DT_VERDEFNUM 0x6ffffffd +#define DT_VERNEED 0x6ffffffe +#define DT_VERNEEDNUM 0x6fffffff +#define OLD_DT_HIOS 0x6fffffff +#define DT_LOPROC 0x70000000 +#define DT_HIPROC 0x7fffffff + +/* This info is needed when parsing the symbol table */ +#define STB_LOCAL 0 +#define STB_GLOBAL 1 +#define STB_WEAK 2 + +#define STT_NOTYPE 0 +#define STT_OBJECT 1 +#define STT_FUNC 2 +#define STT_SECTION 3 +#define STT_FILE 4 +#define STT_COMMON 5 +#define STT_TLS 6 + +#define ELF_ST_BIND(x) ((x) >> 4) +#define ELF_ST_TYPE(x) (((unsigned int)x) & 0xf) +#define ELF32_ST_BIND(x) ELF_ST_BIND(x) +#define ELF32_ST_TYPE(x) ELF_ST_TYPE(x) +#define ELF64_ST_BIND(x) ELF_ST_BIND(x) +#define ELF64_ST_TYPE(x) ELF_ST_TYPE(x) + +typedef struct dynamic { + Elf32_Sword d_tag; + union { + Elf32_Sword d_val; + Elf32_Addr d_ptr; + } d_un; +} Elf32_Dyn; -// Thread Control Block layout expected by mlibc. -// Only the fields mlibc actually reads are populated; the rest stay zero. typedef struct { - void* self; // 0x00 fs:0 (TCB self-pointer) - size_t dtvSize; // 0x08 - void** dtvPointers; // 0x10 - int tid; // 0x18 - int didExit; // 0x1C - uint8_t padding[8]; // 0x20 - uintptr_t stackCanary; // 0x28 - int cancelBits; // 0x30 -} TCB; + Elf64_Sxword d_tag; /* entry tag value */ + union { + Elf64_Xword d_val; + Elf64_Addr d_ptr; + } d_un; +} Elf64_Dyn; -bool ELF_Read(const char* path, - void** entryPoint, - struct pagemap *target_pagemap, - uint64_t *out_tls_fs_base, - uint64_t *out_phdr_va, // AT_PHDR - uint16_t *out_phent, // AT_PHENT - uint16_t *out_phnum); // AT_PHNUM \ No newline at end of file +/* The following are used with relocations */ +#define ELF32_R_SYM(x) ((x) >> 8) +#define ELF32_R_TYPE(x) ((x) & 0xff) + +#define ELF64_R_SYM(i) ((i) >> 32) +#define ELF64_R_TYPE(i) ((i) & 0xffffffff) + +typedef struct elf32_rel { + Elf32_Addr r_offset; + Elf32_Word r_info; +} Elf32_Rel; + +typedef struct elf64_rel { + Elf64_Addr r_offset; /* Location at which to apply the action */ + Elf64_Xword r_info; /* index and type of relocation */ +} Elf64_Rel; + +typedef struct elf32_rela { + Elf32_Addr r_offset; + Elf32_Word r_info; + Elf32_Sword r_addend; +} Elf32_Rela; + +typedef struct elf64_rela { + Elf64_Addr r_offset; /* Location at which to apply the action */ + Elf64_Xword r_info; /* index and type of relocation */ + Elf64_Sxword r_addend; /* Constant addend used to compute value */ +} Elf64_Rela; + +typedef struct elf32_sym { + Elf32_Word st_name; + Elf32_Addr st_value; + Elf32_Word st_size; + unsigned char st_info; + unsigned char st_other; + Elf32_Half st_shndx; +} Elf32_Sym; + +typedef struct elf64_sym { + Elf64_Word st_name; /* Symbol name, index in string tbl */ + unsigned char st_info; /* Type and binding attributes */ + unsigned char st_other; /* No defined meaning, 0 */ + Elf64_Half st_shndx; /* Associated section index */ + Elf64_Addr st_value; /* Value of the symbol */ + Elf64_Xword st_size; /* Associated symbol size */ +} Elf64_Sym; + +#define EI_NIDENT 16 + +typedef struct elf32_hdr { + unsigned char e_ident[EI_NIDENT]; + Elf32_Half e_type; + Elf32_Half e_machine; + Elf32_Word e_version; + Elf32_Addr e_entry; /* Entry point */ + Elf32_Off e_phoff; + Elf32_Off e_shoff; + Elf32_Word e_flags; + Elf32_Half e_ehsize; + Elf32_Half e_phentsize; + Elf32_Half e_phnum; + Elf32_Half e_shentsize; + Elf32_Half e_shnum; + Elf32_Half e_shstrndx; +} Elf32_Ehdr; + +typedef struct elf64_hdr { + unsigned char e_ident[EI_NIDENT]; /* ELF "magic number" */ + Elf64_Half e_type; + Elf64_Half e_machine; + Elf64_Word e_version; + Elf64_Addr e_entry; /* Entry point virtual address */ + Elf64_Off e_phoff; /* Program header table file offset */ + Elf64_Off e_shoff; /* Section header table file offset */ + Elf64_Word e_flags; + Elf64_Half e_ehsize; + Elf64_Half e_phentsize; + Elf64_Half e_phnum; + Elf64_Half e_shentsize; + Elf64_Half e_shnum; + Elf64_Half e_shstrndx; +} Elf64_Ehdr; + +/* These constants define the permissions on sections in the program + header, p_flags. */ +#define PF_R 0x4 +#define PF_W 0x2 +#define PF_X 0x1 + +typedef struct elf32_phdr { + Elf32_Word p_type; + Elf32_Off p_offset; + Elf32_Addr p_vaddr; + Elf32_Addr p_paddr; + Elf32_Word p_filesz; + Elf32_Word p_memsz; + Elf32_Word p_flags; + Elf32_Word p_align; +} Elf32_Phdr; + +typedef struct elf64_phdr { + Elf64_Word p_type; + Elf64_Word p_flags; + Elf64_Off p_offset; /* Segment file offset */ + Elf64_Addr p_vaddr; /* Segment virtual address */ + Elf64_Addr p_paddr; /* Segment physical address */ + Elf64_Xword p_filesz; /* Segment size in file */ + Elf64_Xword p_memsz; /* Segment size in memory */ + Elf64_Xword p_align; /* Segment alignment, file & memory */ +} Elf64_Phdr; + +/* sh_type */ +#define SHT_NULL 0 +#define SHT_PROGBITS 1 +#define SHT_SYMTAB 2 +#define SHT_STRTAB 3 +#define SHT_RELA 4 +#define SHT_HASH 5 +#define SHT_DYNAMIC 6 +#define SHT_NOTE 7 +#define SHT_NOBITS 8 +#define SHT_REL 9 +#define SHT_SHLIB 10 +#define SHT_DYNSYM 11 +#define SHT_NUM 12 +#define SHT_LOPROC 0x70000000 +#define SHT_HIPROC 0x7fffffff +#define SHT_LOUSER 0x80000000 +#define SHT_HIUSER 0xffffffff + +/* sh_flags */ +#define SHF_WRITE 0x1 +#define SHF_ALLOC 0x2 +#define SHF_EXECINSTR 0x4 +#define SHF_MASKPROC 0xf0000000 + +/* special section indexes */ +#define SHN_UNDEF 0 +#define SHN_LORESERVE 0xff00 +#define SHN_LOPROC 0xff00 +#define SHN_HIPROC 0xff1f +#define SHN_ABS 0xfff1 +#define SHN_COMMON 0xfff2 +#define SHN_HIRESERVE 0xffff + +typedef struct { + Elf32_Word sh_name; + Elf32_Word sh_type; + Elf32_Word sh_flags; + Elf32_Addr sh_addr; + Elf32_Off sh_offset; + Elf32_Word sh_size; + Elf32_Word sh_link; + Elf32_Word sh_info; + Elf32_Word sh_addralign; + Elf32_Word sh_entsize; +} Elf32_Shdr; + +typedef struct elf64_shdr { + Elf64_Word sh_name; /* Section name, index in string tbl */ + Elf64_Word sh_type; /* Type of section */ + Elf64_Xword sh_flags; /* Miscellaneous section attributes */ + Elf64_Addr sh_addr; /* Section virtual addr at execution */ + Elf64_Off sh_offset; /* Section file offset */ + Elf64_Xword sh_size; /* Size of section in bytes */ + Elf64_Word sh_link; /* Index of another section */ + Elf64_Word sh_info; /* Additional section information */ + Elf64_Xword sh_addralign; /* Section alignment */ + Elf64_Xword sh_entsize; /* Entry size if section holds table */ +} Elf64_Shdr; + +#define EI_MAG0 0 /* e_ident[] indexes */ +#define EI_MAG1 1 +#define EI_MAG2 2 +#define EI_MAG3 3 +#define EI_CLASS 4 +#define EI_DATA 5 +#define EI_VERSION 6 +#define EI_OSABI 7 +#define EI_PAD 8 + +#define ELFMAG0 0x7f /* EI_MAG */ +#define ELFMAG1 'E' +#define ELFMAG2 'L' +#define ELFMAG3 'F' +#define ELFMAG "\177ELF" +#define SELFMAG 4 + +#define ELFCLASSNONE 0 /* EI_CLASS */ +#define ELFCLASS32 1 +#define ELFCLASS64 2 +#define ELFCLASSNUM 3 + +#define ELFDATANONE 0 /* e_ident[EI_DATA] */ +#define ELFDATA2LSB 1 +#define ELFDATA2MSB 2 + +#define EV_NONE 0 /* e_version, EI_VERSION */ +#define EV_CURRENT 1 +#define EV_NUM 2 + +#define ELFOSABI_NONE 0 +#define ELFOSABI_LINUX 3 + +#ifndef ELF_OSABI +#define ELF_OSABI ELFOSABI_NONE +#endif + +/* Notes used in ET_CORE */ +#define NT_PRSTATUS 1 +#define NT_PRFPREG 2 +#define NT_PRPSINFO 3 +#define NT_TASKSTRUCT 4 +#define NT_AUXV 6 +#define NT_PRXFPREG 0x46e62b7f /* copied from gdb5.1/include/elf/common.h */ +#define NT_PPC_VMX 0x100 /* PowerPC Altivec/VMX registers */ +#define NT_PPC_SPE 0x101 /* PowerPC SPE/EVR registers */ +#define NT_PPC_VSX 0x102 /* PowerPC VSX registers */ +#define NT_386_TLS 0x200 /* i386 TLS slots (struct user_desc) */ +#define NT_386_IOPERM 0x201 /* x86 io permission bitmap (1=deny) */ +#define NT_PRXSTATUS 0x300 /* s390 upper register halves */ + +/* Note header in a PT_NOTE section */ +typedef struct elf32_note { + Elf32_Word n_namesz; /* Name size */ + Elf32_Word n_descsz; /* Content size */ + Elf32_Word n_type; /* Content type */ +} Elf32_Nhdr; + +/* Note header in a PT_NOTE section */ +typedef struct elf64_note { + Elf64_Word n_namesz; /* Name size */ + Elf64_Word n_descsz; /* Content size */ + Elf64_Word n_type; /* Content type */ +} Elf64_Nhdr; + + + +bool elf_load(struct pagemap *pagemap, struct resource *res, uint64_t load_base, + struct auxval *auxv, const char **ld_path); + +void elf_init_function_table(uint8_t *binary); +const char *elf_get_name_from_function(uint64_t address); +uint64_t elf_get_function_from_name(const char *string); \ No newline at end of file diff --git a/src/fs/partition.c b/src/fs/partition.c new file mode 100644 index 0000000..6ff44cb --- /dev/null +++ b/src/fs/partition.c @@ -0,0 +1,156 @@ +#include "libk/types.h" +#include "libk/errno.h" +#include "vfs.h" +#include "partition.h" +#include "libk/debug.h" +#include "devtmpfs.h" + +static ssize_t partition_read(struct resource *_this, + struct f_description *description, void *buf, + off_t loc, size_t count) { + struct partition_device *this = (struct partition_device *)_this; + if ((size_t)loc >= (this->sectors * this->lba_size)) { + errno = ESPIPE; + return -1; + } + return this->root->read(this->root, description, buf, + loc + this->start * this->lba_size, count); +} + +static ssize_t partition_write(struct resource *_this, + struct f_description *description, + const void *buf, off_t loc, size_t count) { + struct partition_device *this = (struct partition_device *)_this; + if ((size_t)loc >= (this->sectors * this->lba_size)) { + errno = ESPIPE; + return -1; + } + return this->root->write(this->root, description, buf, + loc + this->start * this->lba_size, count); +} + +static bool partition_enumerate_gpt(struct resource *res, char *root_name) { + uint16_t block_size = res->stat.st_blksize; + struct gpt_header header = {0}; + off_t loc = 512; + res->read(res, NULL, &header, loc, sizeof(struct gpt_header)); + loc += 512; + if (strncmp(header.signature, "EFI PART", 8)) { + return false; + } + if (header.size < 92) { + return false; + } + if (header.size > res->stat.st_size) { + return false; + } + if (header.header_lba != 1) { + return false; + } + if (header.first_usable > header.last_usable) { + kprintf("wtf?\n"); + return false; + } + struct gpt_entry entry = {0}; + loc = header.entry_array_lba_start * 512; + for (uint32_t i = 0; i < header.entry_count; i++) { + res->read(res, NULL, &entry, loc, sizeof(struct gpt_entry)); + loc += sizeof(struct gpt_entry); + if (entry.uni_low == 0 && entry.uni_hi == 0) { + continue; + } + if (entry.attr & (GPT_DONT_MOUNT | GPT_LEGACY)) { + continue; + } + struct partition_device *part_dev = + resource_create(sizeof(struct partition_device)); + part_dev->root = res; + part_dev->lba_size = block_size; + part_dev->start = entry.start; + part_dev->sectors = entry.end - entry.start; + part_dev->res.stat.st_blksize = block_size; + part_dev->res.stat.st_size = part_dev->sectors * block_size; + part_dev->res.stat.st_blocks = part_dev->sectors; + part_dev->res.stat.st_mode = 0666 | S_IFBLK; + part_dev->res.stat.st_rdev = resource_create_dev_id(); + part_dev->res.can_mmap = false; + part_dev->res.ioctl = resource_default_ioctl; + part_dev->res.read = partition_read; + part_dev->res.write = partition_write; + + char name[32] = {0}; + char num[21] = {0}; + strncpy(name, root_name, 32); + strcat(name, "p"); + ultoa(i + 1, num, 10); + strcat(name, num); + kprintf("%s: Starting from %u to %u\n", name, entry.start, entry.end); + devtmpfs_add_device((struct resource *)part_dev, name); + } + + return true; +} + +static bool partition_enumerate_mbr(struct resource *res, char *root_name) { + uint16_t mbr_magic = 0; + uint16_t block_size = res->stat.st_blksize; + + res->read(res, NULL, &mbr_magic, 510, 2); + if (mbr_magic != MBR_MAGIC) { + return false; + } + struct mbr_entry entries[4] = {0}; + res->read(res, NULL, entries, MBR_ENTRY_OFFSET, + sizeof(struct mbr_entry) * 4); + for (int i = 0; i < 4; i++) { + if (entries[i].type == 0) { + continue; + } + + struct partition_device *part_dev = + resource_create(sizeof(struct partition_device)); + part_dev->root = res; + part_dev->lba_size = block_size; + part_dev->start = entries[i].lba_start; + part_dev->sectors = entries[i].lba_size; + part_dev->res.stat.st_blksize = block_size; + part_dev->res.stat.st_size = entries[i].lba_size * block_size; + part_dev->res.stat.st_blocks = entries[i].lba_size; + part_dev->res.stat.st_rdev = resource_create_dev_id(); + part_dev->res.can_mmap = false; + part_dev->res.ioctl = resource_default_ioctl; + part_dev->res.read = partition_read; + part_dev->res.write = partition_write; + part_dev->res.stat.st_mode = 0666 | S_IFBLK; + + char name[32] = {0}; + char num[21] = {0}; + strncpy(name, root_name, 32); + strcat(name, "p"); + ultoa(i, num, 10); + strcat(name, num); + kprintf("%s: Starting from %u to %u\n", name, part_dev->start, + part_dev->start + part_dev->sectors); + devtmpfs_add_device((struct resource *)part_dev, name); + } + return true; +} + +void partition_enumerate(struct resource *res, char *root_name) { + if (!res) + return; + if (!root_name) + return; + + if (partition_enumerate_gpt(res, root_name)) { + kprintf("%s is a GPT drive!\n", root_name); + return; + } + if (partition_enumerate_mbr(res, root_name)) { + kprintf("%s is a MBR drive!\n", root_name); + kprintf("Expect it to not work since I haven't tested this properly " + ":upside_down:\n"); + return; + } + kprintf("Lost cause: %s\n", root_name); +} \ No newline at end of file diff --git a/src/fs/partition.h b/src/fs/partition.h new file mode 100644 index 0000000..82113c3 --- /dev/null +++ b/src/fs/partition.h @@ -0,0 +1,61 @@ +#pragma once +#include +#include "libk/resource.h" + +struct gpt_header { + char signature[8]; + uint32_t revision; + uint32_t size; + uint32_t crc32; + uint32_t reserved; + uint64_t header_lba; + uint64_t alternate_lba; + uint64_t first_usable; + uint64_t last_usable; + uint64_t guid[2]; + uint64_t entry_array_lba_start; + uint32_t entry_count; + uint32_t entry_byte_size; + uint32_t entry_array_crc32; +} __attribute__((packed)); + +struct gpt_entry { + uint64_t type_low; + uint64_t type_hi; + + uint64_t uni_low; + uint64_t uni_hi; + + uint64_t start; + uint64_t end; + + uint64_t attr; + + uint16_t name[36]; // the name is WCHAR +} __attribute__((packed)); + +struct mbr_entry { + uint8_t status; + uint8_t start[3]; + uint8_t type; + uint8_t end[3]; + uint32_t lba_start; + uint32_t lba_size; +} __attribute__((packed)); + +struct partition_device { + struct resource res; + uint64_t start; + uint64_t sectors; + uint16_t lba_size; + struct resource *root; +}; + +#define MBR_MAGIC 0xaa55 +#define MBR_ENTRY_OFFSET 510 + +#define GPT_IMPORTANT 1 +#define GPT_DONT_MOUNT 2 +#define GPT_LEGACY 4 + +void partition_enumerate(struct resource *res, char *root_name); \ No newline at end of file diff --git a/src/fs/ramdisk.c b/src/fs/ramdisk.c new file mode 100644 index 0000000..2f8d90d --- /dev/null +++ b/src/fs/ramdisk.c @@ -0,0 +1,97 @@ +#include "libk/debug.h" +#include "ramdisk.h" +#include "vfs.h" +#include "libk/misc.h" + +struct ustar_header { + char name[100]; + char mode[8]; + char uid[8]; + char gid[8]; + char size[12]; + char mtime[12]; + char checksum[8]; + char type; + char linkname[100]; + char signature[6]; + char version[2]; + char owner[32]; + char group[32]; + char devmajor[8]; + char devminor[8]; + char prefix[155]; +}; + +enum { + USTAR_REGULAR = 0, + USTAR_NORMAL = '0', + USTAR_HARD_LINK = '1', + USTAR_SYM_LINK = '2', + USTAR_CHAR_DEV = '3', + USTAR_BLOCK_DEV = '4', + USTAR_DIRECTORY = '5', + USTAR_FIFO = '6', + USTAR_CONTIGOUS = '7', + USTAR_GNU_LONG_PATH = 'L' +}; + +static uint64_t octal_to_int(const char *s) { + uint64_t ret = 0; + while (*s) { + ret *= 8; + ret += *s - '0'; + s++; + } + return ret; +} + +void ramdisk_install(uintptr_t ramdisk_address, uint64_t ramdisk_size) { + struct ustar_header *current_file = (void *)ramdisk_address; + char *name_override = NULL; + + while (strncmp(current_file->signature, "ustar", 5) == 0) { + char *name = current_file->name; + if (name_override != NULL) { + name = name_override; + name_override = NULL; + } + + if (strcmp(name, "./") == 0) + continue; + + size_t mode = octal_to_int(current_file->mode); + size_t size = octal_to_int(current_file->size); + + struct vfs_node *node = NULL; + switch (current_file->type) { + case USTAR_REGULAR: + case USTAR_NORMAL: + case USTAR_CONTIGOUS: { + node = vfs_create(vfs_root, name, mode | S_IFREG); + if (node) { + struct resource *resource = node->resource; + resource->write(resource, NULL, (void *)current_file + 512, + 0, size); + } + break; + } + case USTAR_SYM_LINK: { + vfs_symlink(vfs_root, current_file->linkname, name); + break; + } + case USTAR_DIRECTORY: { + vfs_create(vfs_root, name, mode | S_IFDIR); + break; + } + case USTAR_GNU_LONG_PATH: { + name_override = (void *)current_file + 512; + name_override[size] = 0; + break; + } + } + + current_file = (void *)current_file + 512 + ALIGN_UP(size, 512); + } + + kprintf("VFS: Loaded ramdisk of size %u\n", ramdisk_size); +} diff --git a/src/fs/ramdisk.h b/src/fs/ramdisk.h new file mode 100644 index 0000000..3ed5984 --- /dev/null +++ b/src/fs/ramdisk.h @@ -0,0 +1,5 @@ +#pragma once +#include +#include + +void ramdisk_install(uintptr_t ramdisk_address, uint64_t ramdisk_size); \ No newline at end of file diff --git a/src/fs/streams.c b/src/fs/streams.c new file mode 100644 index 0000000..47b3e79 --- /dev/null +++ b/src/fs/streams.c @@ -0,0 +1,107 @@ +#include +#include "libk/types.h" +#include "libk/resource.h" +#include +#include "libk/errno.h" +#include "streams.h" +#include "devtmpfs.h" + + + +// It all returns to nothing +static ssize_t null_read(struct resource *this, + struct f_description *description, void *buf, + off_t offset, size_t count) { + (void)this; + (void)description; + (void)buf; + (void)offset; + (void)count; + return 0; +} + +static ssize_t null_write(struct resource *this, + struct f_description *description, const void *buf, + off_t offset, size_t count) { + (void)this; + (void)description; + (void)buf; + (void)offset; + return count; +} + +// Letting me down, letting me down, letting me down +static ssize_t full_read(struct resource *this, + struct f_description *description, void *buf, + off_t offset, size_t count) { + (void)this; + (void)description; + (void)offset; + memset(buf, 0, count); + return count; +} + +static ssize_t full_write(struct resource *this, + struct f_description *description, const void *buf, + off_t offset, size_t count) { + (void)this; + (void)description; + (void)buf; + (void)offset; + (void)count; + errno = ENOSPC; + return -1; +} + +// Tumbling down, tumbling down, tumbling down +static ssize_t zero_read(struct resource *this, + struct f_description *description, void *buf, + off_t offset, size_t count) { + (void)this; + (void)description; + (void)offset; + memset(buf, 0, count); + return count; +} + +static ssize_t zero_write(struct resource *this, + struct f_description *description, const void *buf, + off_t offset, size_t count) { + (void)this; + (void)description; + (void)buf; + (void)offset; + return count; +} + +void streams_init(void) { + struct resource *null = resource_create(sizeof(struct resource)); + null->read = null_read; + null->write = null_write; + null->stat.st_size = 0; + null->stat.st_blocks = 0; + null->stat.st_blksize = 4096; + null->stat.st_rdev = resource_create_dev_id(); + null->stat.st_mode = 0666 | S_IFCHR; + devtmpfs_add_device(null, "null"); + + struct resource *full = resource_create(sizeof(struct resource)); + full->read = full_read; + full->write = full_write; + full->stat.st_size = 0; + full->stat.st_blocks = 0; + full->stat.st_blksize = 4096; + full->stat.st_rdev = resource_create_dev_id(); + full->stat.st_mode = 0666 | S_IFCHR; + devtmpfs_add_device(full, "full"); + + struct resource *zero = resource_create(sizeof(struct resource)); + zero->read = zero_read; + zero->write = zero_write; + zero->stat.st_size = 0; + zero->stat.st_blocks = 0; + zero->stat.st_blksize = 4096; + zero->stat.st_rdev = resource_create_dev_id(); + zero->stat.st_mode = 0666 | S_IFCHR; + devtmpfs_add_device(zero, "zero"); +} \ No newline at end of file diff --git a/src/fs/streams.h b/src/fs/streams.h new file mode 100644 index 0000000..5894937 --- /dev/null +++ b/src/fs/streams.h @@ -0,0 +1,4 @@ +#pragma once + + +void streams_init(void); \ No newline at end of file diff --git a/src/fs/tmpfs.c b/src/fs/tmpfs.c new file mode 100644 index 0000000..6a6dcff --- /dev/null +++ b/src/fs/tmpfs.c @@ -0,0 +1,296 @@ +#include +#include +#include "libk/types.h" +#include "libk/resource.h" +#include "mp/spinlock.h" +#include "libk/errno.h" +#include "mm/mmap.h" +#include "mm/pmm.h" +#include "mm/slab.h" +#include "mm/vmm.h" +#include "arch/x86_64/sys/prcb.h" +#include "vfs.h" +#include "libk/time.h" +#include "libk/misc.h" + + +struct tmpfs_resource { + struct resource res; + + void *data; + size_t capacity; +}; + +struct tmpfs { + struct vfs_filesystem fs; + + uint64_t dev_id; + uint64_t inode_counter; +}; + + +static ssize_t tmpfs_resource_read(struct resource *_this, + struct f_description *description, void *buf, + off_t offset, size_t count) { + (void)description; + + struct tmpfs_resource *this = (struct tmpfs_resource *)_this; + + spinlock_acquire_or_wait(&this->res.lock); + + size_t actual_count = count; + + if ((off_t)(offset + count) >= this->res.stat.st_size) { + actual_count = count - ((offset + count) - this->res.stat.st_size); + } + + memcpy(buf, this->data + offset, actual_count); + spinlock_drop(&this->res.lock); + + return actual_count; +} + +static ssize_t tmpfs_resource_write(struct resource *_this, + struct f_description *description, + const void *buf, off_t offset, + size_t count) { + (void)description; + + ssize_t ret = -1; + struct tmpfs_resource *this = (struct tmpfs_resource *)_this; + + spinlock_acquire_or_wait(&this->res.lock); + + if (offset + count >= this->capacity) { + size_t new_capacity = this->capacity; + while (offset + count >= new_capacity) { + new_capacity *= 2; + } + + void *new_data = krealloc(this->data, new_capacity); + if (new_data == NULL) { + errno = ENOMEM; + goto fail; + } + + this->data = new_data; + this->capacity = new_capacity; + } + + memcpy(this->data + offset, buf, count); + + if ((off_t)(offset + count) >= this->res.stat.st_size) { + this->res.stat.st_size = (off_t)(offset + count); + this->res.stat.st_blocks = + DIV_ROUNDUP(this->res.stat.st_size, this->res.stat.st_blksize); + } + + ret = count; + +fail: + spinlock_drop(&this->res.lock); + return ret; +} + +static void *tmpfs_resource_mmap(struct resource *_this, size_t file_page, + int flags) { + struct tmpfs_resource *this = (struct tmpfs_resource *)_this; + + spinlock_acquire_or_wait(&this->res.lock); + + void *ret = NULL; + if ((flags & MAP_SHARED) != 0) { + ret = (void *)(((uintptr_t)this->data + file_page * PAGE_SIZE) - + MEM_PHYS_OFFSET); + } else { + ret = pmm_alloc(1); + if (ret == NULL) { + goto cleanup; + } + + memcpy((void *)((uintptr_t)ret + MEM_PHYS_OFFSET), + (void *)((uintptr_t)this->data + file_page * PAGE_SIZE), + PAGE_SIZE); + } + +cleanup: + spinlock_drop(&this->res.lock); + return ret; +} + +static bool tmpfs_truncate(struct resource *this_, + struct f_description *description, size_t length) { + (void)description; + + struct tmpfs_resource *this = (struct tmpfs_resource *)this_; + + if (length > this->capacity) { + size_t new_capacity = this->capacity; + while (new_capacity < length) { + new_capacity *= 2; + } + + void *new_data = kmalloc(new_capacity); + if (new_data == NULL) { + errno = ENOMEM; + goto fail; + } + + memcpy(new_data, this->data, this->capacity); + kfree(this->data); + + this->data = new_data; + this->capacity = new_capacity; + } + + this->res.stat.st_size = (off_t)length; + this->res.stat.st_blocks = + DIV_ROUNDUP(this->res.stat.st_size, this->res.stat.st_blksize); + + return true; + +fail: + return false; +} + +static inline struct tmpfs_resource *create_tmpfs_resource(struct tmpfs *this, + int mode) { + struct tmpfs_resource *resource = + resource_create(sizeof(struct tmpfs_resource)); + if (resource == NULL) { + return resource; + } + + if (S_ISREG(mode)) { + resource->capacity = 4096; + resource->data = kmalloc(resource->capacity); + resource->res.can_mmap = true; + } + + resource->res.read = tmpfs_resource_read; + resource->res.write = tmpfs_resource_write; + resource->res.mmap = tmpfs_resource_mmap; + resource->res.truncate = tmpfs_truncate; + + resource->res.stat.st_size = 0; + resource->res.stat.st_blocks = 0; + resource->res.stat.st_blksize = 512; + resource->res.stat.st_dev = this->dev_id; + resource->res.stat.st_ino = this->inode_counter++; + resource->res.stat.st_mode = mode; + resource->res.stat.st_nlink = 1; + + resource->res.stat.st_atim = time_realtime; + resource->res.stat.st_ctim = time_realtime; + resource->res.stat.st_mtim = time_realtime; + + return resource; +} + +static inline struct vfs_filesystem *tmpfs_instantiate(void); + +static struct vfs_node *tmpfs_mount(struct vfs_node *parent, const char *name, + struct vfs_node *source) { + (void)source; + + struct vfs_filesystem *new_fs = tmpfs_instantiate(); + struct vfs_node *ret = new_fs->create(new_fs, parent, name, 0644 | S_IFDIR); + return ret; +} + +static struct vfs_node *tmpfs_create(struct vfs_filesystem *_this, + struct vfs_node *parent, const char *name, + int mode) { + struct tmpfs *this = (struct tmpfs *)_this; + struct vfs_node *new_node = NULL; + struct tmpfs_resource *resource = NULL; + + new_node = vfs_create_node(_this, parent, name, S_ISDIR(mode)); + if (new_node == NULL) { + goto fail; + } + + resource = create_tmpfs_resource(this, mode); + if (resource == NULL) { + goto fail; + } + + new_node->resource = (struct resource *)resource; + return new_node; + +fail: + if (new_node != NULL) { + kfree(new_node); // TODO: Use vfs_destroy_node + } + if (resource != NULL) { + kfree(resource); + } + + return NULL; +} + +static struct vfs_node *tmpfs_symlink(struct vfs_filesystem *_this, + struct vfs_node *parent, const char *name, + const char *target) { + struct tmpfs *this = (struct tmpfs *)_this; + struct vfs_node *new_node = NULL; + struct tmpfs_resource *resource = NULL; + + new_node = vfs_create_node(_this, parent, name, false); + if (new_node == NULL) { + goto fail; + } + + resource = create_tmpfs_resource(this, 0777 | S_IFLNK); + if (resource == NULL) { + goto fail; + } + + new_node->resource = (struct resource *)resource; + new_node->symlink_target = strdup(target); + return new_node; + +fail: + if (new_node != NULL) { + kfree(new_node); // TODO: Use vfs_destroy_node + } + if (resource != NULL) { + kfree(resource); + } + + return NULL; +} + +static struct vfs_node *tmpfs_link(struct vfs_filesystem *_this, + struct vfs_node *parent, const char *name, + struct vfs_node *node) { + if (S_ISDIR(node->resource->stat.st_mode)) { + errno = EISDIR; + return NULL; + } + + struct vfs_node *new_node = vfs_create_node(_this, parent, name, false); + if (new_node == NULL) { + return NULL; + } + + new_node->resource = node->resource; + return new_node; +} + +static inline struct vfs_filesystem *tmpfs_instantiate(void) { + struct tmpfs *new_fs = kmalloc(sizeof(struct tmpfs)); + if (new_fs == NULL) { + return NULL; + } + + new_fs->fs.create = tmpfs_create; + new_fs->fs.symlink = tmpfs_symlink; + new_fs->fs.link = tmpfs_link; + + return (struct vfs_filesystem *)new_fs; +} + +void tmpfs_init(void) { + vfs_add_filesystem(tmpfs_mount, "tmpfs"); +} diff --git a/src/fs/tmpfs.h b/src/fs/tmpfs.h new file mode 100644 index 0000000..864afb2 --- /dev/null +++ b/src/fs/tmpfs.h @@ -0,0 +1,7 @@ +#pragma once + + +#include +#include "libk/resource.h" + +void tmpfs_init(void); \ No newline at end of file diff --git a/src/fs/vfs.c b/src/fs/vfs.c index 89a1be3..c253cef 100644 --- a/src/fs/vfs.c +++ b/src/fs/vfs.c @@ -1,275 +1,1001 @@ -#include "vfs.h" -#include "drivers/video/render.h" -#include "arch/x86_64/sys/e9.h" -#include "mp/spinlock.h" -#include "libk/stdio.h" -#include "mm/memory.h" -#include "libk/string.h" +#include "libk/debug.h" #include "libk/errno.h" -#include "drivers/input/input.h" +#include "vfs.h" +#include "libk/hashmap.h" +#include "libk/resource.h" +#include "mp/spinlock.h" +#include "mm/slab.h" +#include "sched/sched.h" +#include +#include "arch/x86_64/sys/prcb.h" +#include "libk/types.h" +#include "libk/string.h" +#include "sched/syscall.h" -static spinlock_t s_vfs_lock = SPINLOCK_INIT; -static vfs_file_t vfs_fd_table[VFS_MAX_FDS]; -vfs_dir_t vfs_dir_table[VFS_MAX_DIRS]; +#define PATH_MAX 4096 -typedef struct { - const char* path; - int (*read)(void* buf, size_t len); - int (*write)(const void* buf, size_t len); -} vfs_chardev_t; +#define DT_FIFO 1 +#define DT_CHR 2 +#define DT_DIR 4 +#define DT_BLK 6 +#define DT_REG 8 +#define DT_LNK 10 +#define DT_SOCK 12 -#define VFS_MAX_CHARDEVS 8 -static vfs_chardev_t s_chardevs[VFS_MAX_CHARDEVS]; -static int s_num_chardevs = 0; +struct dirent { + ino_t d_ino; + off_t d_off; + unsigned short d_reclen; + unsigned char d_type; + char d_name[1024]; +}; -int VFS_RegisterCharDev(const char* path, - int (*read)(void* buf, size_t len), - int (*write)(const void* buf, size_t len)) -{ - if (s_num_chardevs >= VFS_MAX_CHARDEVS) { - printf("VFS: too many char devices!\n"); - return -ENFILE; - } - s_chardevs[s_num_chardevs].path = path; - s_chardevs[s_num_chardevs].read = read; - s_chardevs[s_num_chardevs].write = write; - printf("VFS: registered chardev %s\n", path); - s_num_chardevs++; - return 0; +spinlock_t vfs_lock = {0}; + +struct vfs_node *vfs_create_node(struct vfs_filesystem *fs, + struct vfs_node *parent, const char *name, + bool dir) { + struct vfs_node *node = kmalloc(sizeof(struct vfs_node)); + + node->name = kmalloc(strlen(name) + 1); + strcpy(node->name, name); + + node->parent = parent; + node->filesystem = fs; + + if (dir) { + node->children = (typeof(node->children))HASHMAP_INIT(256); + } + + return node; } -static fd_t VFS_Open_internal(const char* path, int flags, unsigned int mode) -{ - const char* p = path; +void vfs_create_dotentries(struct vfs_node *node, struct vfs_node *parent) { + struct vfs_node *dot = vfs_create_node(node->filesystem, node, ".", false); + struct vfs_node *dotdot = + vfs_create_node(node->filesystem, node, "..", false); - // ── chardevs (flags/mode ignored for now) ─────────────────────────────── - for (int i = 0; i < s_num_chardevs; i++) { - if (strcmp(s_chardevs[i].path, p) == 0) { - for (int fd = 4; fd < VFS_MAX_FDS; fd++) { - if (!vfs_fd_table[fd].used) { - vfs_fd_table[fd].used = true; - vfs_fd_table[fd].type = VFS_NODE_CHARDEV; - vfs_fd_table[fd].offset = 0; - vfs_fd_table[fd].chardev.dev_idx = i; - return fd; - } - } - return -EMFILE; - } - } + dot->redir = node; + dotdot->redir = parent; - // ── EXT2 files ────────────────────────────────────────────────────────── - uint32_t ino = ext2_resolve_path(p); - - if (ino != 0) { - // file exists - if ((flags & O_CREAT) && (flags & O_EXCL)) { - return -EEXIST; - } - - // O_TRUNC ? - if (flags & O_TRUNC) { - ext2_inode_t inode; - if (!ext2_read_inode(ino, &inode)) { - return -EIO; - } - if (!ext2_truncate(&inode, ino, 0)) { - return -EIO; - } - } - } else { - // does not exist - if (!(flags & O_CREAT)) { - return -ENOENT; - } - - // Create new regular file in root (simple filename only for now) - ext2_inode_t root; - if (!ext2_read_inode(2, &root)) { - return -EIO; - } - - uint32_t new_ino; - uint16_t file_mode = EXT2_S_IFREG | (mode & 0777); // respect user-supplied perms - - if (!ext2_create_file(&root, 2, p, file_mode, &new_ino)) { - return -EIO; - } - ino = new_ino; - } - - // ── allocate fd and fill table ────────────────────────────────────────── - for (int i = 4; i < VFS_MAX_FDS; i++) { - if (!vfs_fd_table[i].used) { - vfs_fd_table[i].used = true; - vfs_fd_table[i].type = VFS_NODE_FILE; - vfs_fd_table[i].offset = 0; - vfs_fd_table[i].ext2.inode_num = ino; - - if (!ext2_read_inode(ino, &vfs_fd_table[i].ext2.inode)) { - vfs_fd_table[i].used = false; - return -EIO; - } - return i; - } - } - - return -EMFILE; + HASHMAP_SINSERT(&node->children, ".", dot); + HASHMAP_SINSERT(&node->children, "..", dotdot); } -fd_t VFS_Open(const char* path, int flags, unsigned int mode) { - uint64_t spin_flags; - spinlock_acquire_irqsave(&s_vfs_lock, &spin_flags); - fd_t fd = VFS_Open_internal(path, flags, mode); - spinlock_release_irqrestore(&s_vfs_lock, spin_flags); - return fd; +static HASHMAP_TYPE(fs_mount_t) filesystems; + +void vfs_add_filesystem(fs_mount_t fs_mount, const char *identifier) { + spinlock_acquire_or_wait(&vfs_lock); + + HASHMAP_SINSERT(&filesystems, identifier, fs_mount); + + spinlock_drop(&vfs_lock); } -int VFS_Close_internal(fd_t fd) -{ - if (fd < 0 || fd >= VFS_MAX_FDS) - return -EBADF; +struct vfs_node *vfs_root = NULL; - vfs_fd_table[fd].used = false; - return 0; +void vfs_init(void) { + vfs_root = vfs_create_node(NULL, NULL, "", false); + + filesystems = (typeof(filesystems))HASHMAP_INIT(256); } -int VFS_Close(fd_t fd) { - uint64_t flags; - spinlock_acquire_irqsave(&s_vfs_lock, &flags); - int resp = VFS_Close_internal(fd); - spinlock_release_irqrestore(&s_vfs_lock, flags); - return resp; +struct path2node_res { + struct vfs_node *target_parent; + struct vfs_node *target; + char *basename; +}; + +static bool populate(struct vfs_node *node) { + if (node->filesystem && node->filesystem->populate && + node->populated == false && node->resource && + S_ISDIR(node->resource->stat.st_mode)) { + node->filesystem->populate(node->filesystem, node); + return node->populated; + } + return true; } -int VFS_Read_internal(fd_t fd, uint8_t* buf, size_t size) -{ - if (fd < 0 || fd >= VFS_MAX_FDS) - return -EBADF; +static struct vfs_node *reduce_node(struct vfs_node *node, + bool follow_symlinks); - if (fd == VFS_FD_STDIN) { - return input_read_console(buf, size); - } +static struct path2node_res path2node(struct vfs_node *parent, + const char *path) { + if (path == NULL || strlen(path) == 0) { + errno = ENOENT; + return (struct path2node_res){NULL, NULL, NULL}; + } - vfs_file_t* file = &vfs_fd_table[fd]; + size_t path_len = strlen(path); - if (!file->used) - return -EBADF; + bool ask_for_dir = path[path_len - 1] == '/'; - switch (file->type) - { - case VFS_NODE_FILE: - { - uint32_t file_size = file->ext2.inode.i_size; + size_t index = 0; + struct vfs_node *current_node = reduce_node(parent, false); + if (!populate(current_node)) { + return (struct path2node_res){NULL, NULL, NULL}; + } - if (file->offset >= file_size) - return 0; + if (path[index] == '/') { + current_node = reduce_node(vfs_root, false); + while (path[index] == '/') { + if (index == path_len - 1) { + return (struct path2node_res){current_node, current_node, + strdup("/")}; + } + index++; + } + } - // clamp read - if (file->offset + size > file_size) - size = file_size - file->offset; + for (;;) { + const char *elem = &path[index]; + size_t elem_len = 0; - // naive: read whole file then slice - uint8_t* tmp = kmalloc(file_size); - if (!tmp) { - return -ENOMEM; - } - if (!ext2_read_file(&file->ext2.inode, tmp)) { - kfree(tmp); - return -EIO; - } - - for (size_t i = 0; i < size; i++) - buf[i] = tmp[file->offset + i]; + while (index < path_len && path[index] != '/') { + elem_len++, index++; + } - file->offset += size; - kfree(tmp); - return size; - } + while (index < path_len && path[index] == '/') { + index++; + } - case VFS_NODE_CHARDEV: - { - int idx = file->chardev.dev_idx; - if (idx < 0 || idx >= s_num_chardevs || !s_chardevs[idx].read) - return -ENXIO; - return s_chardevs[idx].read(buf, size); - } + bool last = index == path_len; - default: - return -EINVAL; - } + char *elem_str = kmalloc(elem_len + 1); + memcpy(elem_str, elem, elem_len); + + current_node = reduce_node(current_node, false); + + struct vfs_node *new_node; + + // XXX put a lock around this guy + if (!HASHMAP_SGET(¤t_node->children, new_node, elem_str)) { + errno = ENOENT; + if (last) { + return (struct path2node_res){current_node, NULL, elem_str}; + } + return (struct path2node_res){NULL, NULL, NULL}; + } + + new_node = reduce_node(new_node, false); + if (!populate(new_node)) { + return (struct path2node_res){NULL, NULL, NULL}; + } + + if (last) { + if (ask_for_dir && !S_ISDIR(new_node->resource->stat.st_mode)) { + errno = ENOTDIR; + return (struct path2node_res){current_node, NULL, elem_str}; + } + return (struct path2node_res){current_node, new_node, elem_str}; + } + + current_node = new_node; + + if (S_ISLNK(current_node->resource->stat.st_mode)) { + struct path2node_res r = + path2node(current_node->parent, current_node->symlink_target); + if (r.target == NULL) { + return (struct path2node_res){NULL, NULL, NULL}; + } + current_node = r.target; + } + + if (!S_ISDIR(current_node->resource->stat.st_mode)) { + errno = ENOTDIR; + return (struct path2node_res){NULL, NULL, NULL}; + } + } + + errno = ENOENT; + return (struct path2node_res){NULL, NULL, NULL}; } -int VFS_Read(fd_t fd, uint8_t* buf, size_t size) -{ - uint64_t flags; - spinlock_acquire_irqsave(&s_vfs_lock, &flags); - int resp = VFS_Read_internal(fd, buf, size); - spinlock_release_irqrestore(&s_vfs_lock, flags); - return resp; +static struct vfs_node *get_parent_dir(int dir_fdnum, const char *path) { + struct process *proc = sched_get_running_thread()->mother_proc; + + if (path != NULL && *path == '/') { + return vfs_root; + } + + if (dir_fdnum == AT_FDCWD) { + return proc->cwd; + } + + struct f_descriptor *fd = fd_from_fdnum(proc, dir_fdnum); + if (fd == NULL) { + return NULL; + } + + struct f_description *description = fd->description; + if (!S_ISDIR(description->res->stat.st_mode)) { + errno = ENOTDIR; + return NULL; + } + + return description->node; } -int VFS_Write_internal(fd_t file, uint8_t* data, size_t size) -{ - switch (file) - { - case VFS_FD_STDIN: - return -EINVAL; - - case VFS_FD_STDOUT: - case VFS_FD_STDERR: - for (size_t i = 0; i < size; i++) { - putchar(data[i]); - e9_putc(data[i]); - } - return size; - - case VFS_FD_DEBUG: - for (size_t i = 0; i < size; i++) - e9_putc(data[i]); - return size; - - default: - break; - } - - if (file < 0 || file >= VFS_MAX_FDS) - return -EBADF; - - vfs_file_t* f = &vfs_fd_table[file]; - - if (!f->used) - return -EBADF; - - if (f->type == VFS_NODE_FILE) - { - if (!ext2_write_file( - &f->ext2.inode, - f->ext2.inode_num, - data, - size, - EXT2_WRITE_APPEND)) - return -EIO; - - return size; - } - - if (f->type == VFS_NODE_CHARDEV) - { - int idx = f->chardev.dev_idx; - if (idx >= 0 && idx < s_num_chardevs && s_chardevs[idx].write) - return s_chardevs[idx].write(data, size); - return -ENXIO; - } - - return -EINVAL; +static struct vfs_node *reduce_node(struct vfs_node *node, + bool follow_symlinks) { + if (node->redir != NULL) { + return reduce_node(node->redir, follow_symlinks); + } + if (node->mountpoint != NULL) { + return reduce_node(node->mountpoint, follow_symlinks); + } + if (node->symlink_target != NULL && follow_symlinks == true) { + struct path2node_res r = path2node(node->parent, node->symlink_target); + if (r.target == NULL) { + return NULL; + } + return reduce_node(r.target, follow_symlinks); + } + return node; } -int VFS_Write(fd_t file, uint8_t* data, size_t size) -{ - uint64_t flags; - spinlock_acquire_irqsave(&s_vfs_lock, &flags); - int resp = VFS_Write_internal(file, data, size); - spinlock_release_irqrestore(&s_vfs_lock, flags); - return resp; -} \ No newline at end of file +struct vfs_node *vfs_get_node(struct vfs_node *parent, const char *path, + bool follow_links) { + spinlock_acquire_or_wait(&vfs_lock); + + struct vfs_node *ret = NULL; + + struct path2node_res r = path2node(parent, path); + if (r.target == NULL) { + goto cleanup; + } + + if (follow_links) { + ret = reduce_node(r.target, true); + goto cleanup; + } + + ret = r.target; + +cleanup: + if (r.basename != NULL) { + kfree(r.basename); + } + spinlock_drop(&vfs_lock); + return ret; +} + +bool vfs_mount(struct vfs_node *parent, const char *source, const char *target, + const char *fs_name) { + spinlock_acquire_or_wait(&vfs_lock); + + bool ret = false; + struct path2node_res r = {0}; + + fs_mount_t fs_mount = {0}; + if (!HASHMAP_SGET(&filesystems, fs_mount, fs_name)) { + errno = ENODEV; + goto cleanup; + } + + struct vfs_node *source_node = NULL; + if (source != NULL && strlen(source) != 0) { + struct path2node_res rr = path2node(parent, source); + source_node = rr.target; + if (rr.basename != NULL) { + kfree(rr.basename); + } + if (source_node == NULL) { + goto cleanup; + } + if (S_ISDIR(source_node->resource->stat.st_mode)) { + errno = EISDIR; + goto cleanup; + } + } + + r = path2node(parent, target); + + bool mounting_root = r.target == vfs_root; + + if (r.target == NULL) { + goto cleanup; + } + + if (!mounting_root && !S_ISDIR(r.target->resource->stat.st_mode)) { + errno = EISDIR; + goto cleanup; + } + + struct vfs_node *mount_node = + fs_mount(r.target_parent, r.basename, source_node); + if (mount_node == NULL) { + goto cleanup; + } + r.target->mountpoint = mount_node; + + vfs_create_dotentries(mount_node, r.target_parent); + + if (source != NULL && strlen(source) != 0) { + kprintf("VFS: Mounted '%s' on '%s' with filesystem '%s'\n", source, + target, fs_name); + } else { + kprintf("VFS: Mounted %s on '%s'\n", fs_name, target); + } + + ret = true; + +cleanup: + if (r.basename != NULL) { + kfree(r.basename); + } + spinlock_drop(&vfs_lock); + return ret; +} + +struct vfs_node *vfs_symlink(struct vfs_node *parent, const char *dest, + const char *target) { + spinlock_acquire_or_wait(&vfs_lock); + + struct vfs_node *ret = NULL; + + struct path2node_res r = path2node(parent, target); + + if (r.target_parent == NULL) { + goto cleanup; + } + + if (r.target != NULL) { + errno = EEXIST; + goto cleanup; + } + + struct vfs_filesystem *target_fs = r.target_parent->filesystem; + struct vfs_node *target_node = + target_fs->symlink(target_fs, r.target_parent, r.basename, dest); + + HASHMAP_SINSERT(&r.target_parent->children, r.basename, target_node); + + ret = target_node; + +cleanup: + if (r.basename != NULL) { + kfree(r.basename); + } + spinlock_drop(&vfs_lock); + return ret; +} + +bool vfs_unlink(struct vfs_node *parent, const char *path) { + bool ret = false; + + spinlock_acquire_or_wait(&vfs_lock); + + struct path2node_res r = path2node(parent, path); + + if (r.target_parent == NULL) { + goto cleanup; + } + + if (r.target == NULL) { + goto cleanup; + } + + if (r.target->mountpoint != NULL) { + errno = EBUSY; + goto cleanup; + } + + if (!HASHMAP_SREMOVE(&r.target_parent->children, r.basename)) { + goto cleanup; + } + + if (!r.target->resource->unref( + r.target->resource, + &((struct f_description){.node = r.target, + .res = r.target->resource}))) { + goto cleanup; + } + + kfree(r.target->name); + if (r.target->symlink_target != NULL) { + kfree(r.target->symlink_target); + } + + if (S_ISDIR(r.target->resource->stat.st_mode)) { + HASHMAP_DELETE(&r.target->children); + } + + ret = true; + +cleanup: + if (r.basename != NULL) { + kfree(r.basename); + } + spinlock_drop(&vfs_lock); + return ret; +} + +struct vfs_node *vfs_create(struct vfs_node *parent, const char *name, + int mode) { + spinlock_acquire_or_wait(&vfs_lock); + + struct vfs_node *ret = NULL; + + struct path2node_res r = path2node(parent, name); + + if (r.target_parent == NULL) { + goto cleanup; + } + + if (r.target != NULL) { + errno = EEXIST; + goto cleanup; + } + + struct vfs_filesystem *target_fs = r.target_parent->filesystem; + struct vfs_node *target_node = + target_fs->create(target_fs, r.target_parent, r.basename, mode); + + HASHMAP_SINSERT(&r.target_parent->children, r.basename, target_node); + + if (S_ISDIR(target_node->resource->stat.st_mode)) { + vfs_create_dotentries(target_node, r.target_parent); + } + + ret = target_node; + +cleanup: + if (r.basename != NULL) { + kfree(r.basename); + } + spinlock_drop(&vfs_lock); + return ret; +} + +size_t vfs_pathname(struct vfs_node *node, char *buffer, size_t len) { + size_t offset = 0; + if (node->parent != vfs_root && node->parent != NULL) { + struct vfs_node *parent = reduce_node(node->parent, false); + + if (parent != vfs_root && parent != NULL) { + offset += vfs_pathname(parent, buffer, len - offset - 1); + buffer[offset++] = '/'; + } + } + + if (strcmp(node->name, "/") != 0) { + strncpy(buffer + offset, node->name, len - offset); + return strlen(node->name) + offset; + } else { + return offset; + } +} + +bool vfs_fdnum_path_to_node(int dir_fdnum, const char *path, bool empty_path, + bool enoent_error, struct vfs_node **parent, + struct vfs_node **node, char **basename) { + if (!empty_path && (path == NULL || strlen(path) == 0)) { + errno = ENOENT; + return false; + } + + struct vfs_node *parent_node = get_parent_dir(dir_fdnum, path); + if (parent == NULL) { + return false; + } + + struct path2node_res res = path2node(parent_node, path); + if (res.target == NULL && (errno == ENOENT && enoent_error)) { + return false; + } + + if (parent != NULL) { + *parent = res.target_parent; + } + + if (node != NULL) { + *node = res.target; + } + + if (basename != NULL) { + *basename = res.basename; + } else { + if (res.basename != NULL) { + kfree(res.basename); + } + } + + return true; +} + +void syscall_openat(struct syscall_arguments *args) { + struct process *proc = sched_get_running_thread()->mother_proc; + + int dir_fdnum = args->args0; + const char *path = (char *)args->args1; + int flags = args->args2; + int mode = args->args3; + struct vfs_node *parent = NULL; + char *basename = NULL; + if (!vfs_fdnum_path_to_node(dir_fdnum, path, false, false, &parent, NULL, + &basename)) { + args->ret = -1; + return; + } + + if (parent == NULL) { + errno = ENOENT; + args->ret = -1; + return; + } + + int create_flags = flags & FILE_CREATION_FLAGS_MASK; + int follow_links = (flags & O_NOFOLLOW) == 0; + + struct vfs_node *node = vfs_get_node(parent, basename, follow_links); + if (node == NULL) { + if ((create_flags & O_CREAT) != 0) { + node = + vfs_create(parent, basename, (mode & ~proc->umask) | S_IFREG); + } else { + errno = ENOENT; + args->ret = -1; + return; + } + } + + if (node == NULL) { + args->ret = -1; + return; + } + + if (S_ISLNK(node->resource->stat.st_mode)) { + errno = ELOOP; + args->ret = -1; + return; + } + + node = reduce_node(node, true); + if (node == NULL) { + args->ret = -1; + return; + } + + if (!S_ISDIR(node->resource->stat.st_mode) && (flags & O_DIRECTORY) != 0) { + errno = ENOTDIR; + args->ret = -1; + return; + } + + struct f_descriptor *fd = fd_create_from_resource(node->resource, flags); + if (fd == NULL) { + args->ret = -1; + return; + } + + if ((flags & O_TRUNC) != 0 && S_ISREG(node->resource->stat.st_mode)) { + node->resource->truncate(node->resource, fd->description, 0); + } + + fd->description->node = node; + args->ret = fdnum_create_from_fd(proc, fd, 0, false); + + if (basename != NULL) { + kfree(basename); + } +} + +void syscall_open(struct syscall_arguments *args) { + struct syscall_arguments pass_args = { + 0, AT_FDCWD, args->args0, args->args1, + args->args2, args->args3, args->args4, 0}; + syscall_openat(&pass_args); + args->ret = pass_args.ret; +} + +void syscall_fstatat(struct syscall_arguments *args) { + struct process *proc = sched_get_running_thread()->mother_proc; + struct stat *stat_src = NULL; + + int dir_fdnum = args->args0; + const char *path = (char *)args->args1; + struct stat *stat_buf = (void *)args->args2; + int flags = args->args3; + + if (stat_buf == NULL) { + errno = EINVAL; + args->ret = -1; + return; + } + + if (strlen(path) == 0) { + if ((flags & AT_EMPTY_PATH) == 0) { + errno = ENOENT; + args->ret = -1; + return; + } + + if (dir_fdnum == AT_FDCWD) { + stat_src = &proc->cwd->resource->stat; + } else { + struct f_descriptor *fd = fd_from_fdnum(proc, dir_fdnum); + if (fd == NULL) { + args->ret = -1; + return; + } + + stat_src = &fd->description->res->stat; + } + } else { + struct vfs_node *parent = get_parent_dir(dir_fdnum, path); + if (parent == NULL) { + args->ret = -1; + return; + } + + struct vfs_node *node = + vfs_get_node(parent, path, (flags & AT_SYMLINK_NOFOLLOW) == 0); + if (node == NULL) { + args->ret = -1; + return; + } + + stat_src = &node->resource->stat; + } + + *stat_buf = *stat_src; + args->ret = 0; +} + +void syscall_getcwd(struct syscall_arguments *args) { + struct process *proc = sched_get_running_thread()->mother_proc; + + char *buffer = (char *)args->args0; + size_t len = args->args1; + + char path_buffer[PATH_MAX] = {0}; + if (vfs_pathname(proc->cwd, path_buffer, PATH_MAX) >= len) { + errno = ERANGE; + args->ret = -1; + return; + } + + strncpy(buffer, path_buffer, len); + args->ret = 0; +} + +void syscall_chdir(struct syscall_arguments *args) { + struct process *proc = sched_get_running_thread()->mother_proc; + + const char *path = (char *)args->args0; + + if (path == NULL) { + errno = EINVAL; + args->ret = -1; + return; + } + + if (strlen(path) == 0) { + errno = ENOENT; + args->ret = -1; + return; + } + + struct vfs_node *node = vfs_get_node(proc->cwd, path, true); + if (node == NULL) { + errno = ENOENT; + args->ret = -1; + return; + } + + if (!S_ISDIR(node->resource->stat.st_mode)) { + errno = ENOTDIR; + args->ret = -1; + return; + } + + proc->cwd = node; + args->ret = 0; +} + +void syscall_readdir(struct syscall_arguments *args) { + struct process *proc = sched_get_running_thread()->mother_proc; + + int dir_fdnum = (int)args->args0; + void *buffer = (void *)args->args1; + size_t *size = (size_t *)args->args2; + + int ret = -1; + spinlock_acquire_or_wait(&vfs_lock); + + struct f_descriptor *dir_fd = fd_from_fdnum(proc, dir_fdnum); + if (dir_fd == NULL) { + errno = EBADF; + goto cleanup; + } + + struct vfs_node *dir_node = dir_fd->description->node; + if (!S_ISDIR(dir_fd->description->res->stat.st_mode)) { + errno = ENOTDIR; + goto cleanup; + } + + size_t entries_length = 0; + if (dir_node->children.buckets != NULL) { + for (size_t i = 0; i < dir_node->children.cap; i++) { + __auto_type bucket = &dir_node->children.buckets[i]; + + for (size_t j = 0; j < bucket->filled; j++) { + struct vfs_node *child = bucket->items[j].item; + entries_length += + sizeof(struct dirent) - 1024 + strlen(child->name) + 1; + } + } + } + + // We need space for a null entry that marks the end of directory + entries_length += sizeof(struct dirent) - 1024; + + if (entries_length > *size) { + *size = entries_length; + errno = ENOBUFS; + goto cleanup; + } + + size_t offset = 0; + if (dir_node->children.buckets != NULL) { + for (size_t i = 0; i < dir_node->children.cap; i++) { + __auto_type bucket = &dir_node->children.buckets[i]; + + for (size_t j = 0; j < bucket->filled; j++) { + struct vfs_node *child = bucket->items[j].item; + struct vfs_node *reduced = reduce_node(child, false); + struct dirent *ent = (struct dirent *)((size_t)buffer + offset); + + ent->d_ino = reduced->resource->stat.st_ino; + ent->d_reclen = + sizeof(struct dirent) - 1024 + strlen(child->name) + 1; + ent->d_off = 0; + + switch (reduced->resource->stat.st_mode & S_IFMT) { + case S_IFBLK: + ent->d_type = DT_BLK; + break; + case S_IFCHR: + ent->d_type = DT_CHR; + break; + case S_IFIFO: + ent->d_type = DT_FIFO; + break; + case S_IFREG: + ent->d_type = DT_REG; + break; + case S_IFDIR: + ent->d_type = DT_DIR; + break; + case S_IFLNK: + ent->d_type = DT_LNK; + break; + case S_IFSOCK: + ent->d_type = DT_SOCK; + break; + } + + memcpy(ent->d_name, child->name, strlen(child->name) + 1); + offset += ent->d_reclen; + } + } + } + + struct dirent *terminator = buffer + offset; + terminator->d_reclen = 0; + ret = 0; + +cleanup: + spinlock_drop(&vfs_lock); + args->ret = ret; +} + +void syscall_readlinkat(struct syscall_arguments *args) { + int dir_fdnum = (int)args->args0; + const char *path = (char *)args->args1; + char *buffer = (char *)args->args2; + size_t limit = args->args3; + + struct vfs_node *parent = NULL; + char *basename = NULL; + if (!vfs_fdnum_path_to_node(dir_fdnum, path, false, false, &parent, NULL, + &basename)) { + args->ret = -1; + return; + } + + if (parent == NULL) { + errno = ENOENT; + args->ret = -1; + return; + } + + struct vfs_node *node = vfs_get_node(parent, basename, false); + if (node == NULL) { + args->ret = -1; + return; + } + + if (!S_ISLNK(node->resource->stat.st_mode)) { + errno = EINVAL; + args->ret = -1; + return; + } + + size_t to_copy = strlen(node->symlink_target) + 1; + if (to_copy > limit) { + to_copy = limit; + } + + memcpy(buffer, node->symlink_target, to_copy); + + if (basename != NULL) { + kfree(basename); + } + args->ret = to_copy; +} + +void syscall_linkat(struct syscall_arguments *args) { + int olddir_fdnum = args->args0; + const char *old_path = (char *)args->args1; + int newdir_fdnum = args->args2; + const char *new_path = (char *)args->args3; + int flags = args->args4; + + if (old_path == NULL || strlen(old_path) == 0) { + errno = ENOENT; + args->ret = -1; + return; + } + + struct vfs_node *old_parent = NULL; + + if (!vfs_fdnum_path_to_node(olddir_fdnum, old_path, false, false, + &old_parent, NULL, NULL)) { + args->ret = -1; + return; + } + + struct vfs_node *new_parent = NULL; + char *basename = NULL; + + if (!vfs_fdnum_path_to_node(newdir_fdnum, new_path, false, false, + &new_parent, NULL, &basename)) { + args->ret = -1; + return; + } + + if (old_parent->filesystem != new_parent->filesystem) { + errno = EXDEV; + args->ret = -1; + return; + } + + struct vfs_node *old_node = + vfs_get_node(old_parent, old_path, (flags & AT_SYMLINK_NOFOLLOW) == 0); + if (old_node == NULL) { + args->ret = -1; + return; + } + + struct vfs_filesystem *fs = new_parent->filesystem; + struct vfs_node *node = fs->link(fs, new_parent, basename, old_node); + if (node == NULL) { + args->ret = -1; + return; + } + + HASHMAP_SINSERT(&new_parent->children, basename, node); + args->ret = 0; +} + +void syscall_unlinkat(struct syscall_arguments *args) { + int dir_fdnum = args->args0; + const char *path = (char *)args->args1; + int flags = args->args2; + + struct vfs_node *parent = NULL, *node = NULL; + char *basename = NULL; + if (!vfs_fdnum_path_to_node(dir_fdnum, path, false, true, &parent, &node, + &basename)) { + args->ret = -1; + return; + } + + if (S_ISDIR(node->resource->stat.st_mode) && (flags & AT_REMOVEDIR) == 0) { + errno = EISDIR; + args->ret = -1; + return; + } + + vfs_unlink(parent, basename); + + if (basename != NULL) { + kfree(basename); + } + + args->ret = 0; +} + +void syscall_mkdirat(struct syscall_arguments *args) { + struct process *proc = sched_get_running_thread()->mother_proc; + + int dir_fdnum = args->args0; + const char *path = (char *)args->args1; + mode_t mode = args->args2; + + if (path == NULL || strlen(path) == 0) { + errno = ENOENT; + args->ret = -1; + return; + } + + struct vfs_node *parent = NULL; + char *basename = NULL; + if (!vfs_fdnum_path_to_node(dir_fdnum, path, false, false, &parent, NULL, + &basename)) { + args->ret = -1; + return; + } + + if (parent == NULL) { + errno = ENOENT; + args->ret = -1; + return; + } + + struct vfs_node *node = + vfs_create(parent, basename, (mode & ~proc->umask) | S_IFDIR); + if (node == NULL) { + args->ret = -1; + return; + } + + if (basename != NULL) { + kfree(basename); + } + + args->ret = 0; +} + +void syscall_mknodat(struct syscall_arguments *args) { + struct process *proc = sched_get_running_thread()->mother_proc; + + int dir_fdnum = args->args0; + const char *path = (char *)args->args1; + mode_t mode = args->args2; + + if (path == NULL || strlen(path) == 0) { + errno = ENOENT; + args->ret = -1; + return; + } + + struct vfs_node *parent = NULL; + char *basename = NULL; + if (!vfs_fdnum_path_to_node(dir_fdnum, path, false, false, &parent, NULL, + &basename)) { + args->ret = -1; + return; + } + + if (parent == NULL) { + errno = ENOENT; + args->ret = -1; + return; + } + + struct vfs_node *node = vfs_create(parent, basename, (mode & ~proc->umask)); + if (node == NULL) { + args->ret = -1; + return; + } + + if (basename != NULL) { + kfree(basename); + } + + args->ret = 0; +} diff --git a/src/fs/vfs.h b/src/fs/vfs.h index e2d970f..3607f91 100644 --- a/src/fs/vfs.h +++ b/src/fs/vfs.h @@ -2,90 +2,73 @@ #include #include #include -#include "ext2.h" -#define O_RDONLY 0 -#define O_WRONLY 1 -#define O_RDWR 2 -#define O_ACCMODE 3 -#define O_CREAT 0100 -#define O_EXCL 0200 -#define O_NOCTTY 0400 -#define O_TRUNC 01000 -#define O_APPEND 02000 -#define O_NONBLOCK 04000 -#define O_DSYNC 010000 +#include "libk/hashmap.h" +#include "libk/resource.h" +#include "mp/spinlock.h" +#include +#include "sched/syscall.h" -typedef int fd_t; +extern spinlock_t vfs_lock; -#define VFS_MAX_FDS 32 +struct vfs_filesystem; -#define VFS_MAX_DIRS 64 - - - -#define VFS_FD_STDIN 0 -#define VFS_FD_STDOUT 1 -#define VFS_FD_STDERR 2 -#define VFS_FD_DEBUG 3 - -typedef enum { - VFS_NODE_NONE, - VFS_NODE_FILE, - VFS_NODE_DIR, - VFS_NODE_CHARDEV -} vfs_node_type_t; - -typedef struct { - vfs_node_type_t type; - - union { - struct { - uint32_t inode_num; - ext2_inode_t inode; - } ext2; - - struct { - int dev_idx; - } chardev; - }; - - size_t offset; - bool used; -} vfs_file_t; - -typedef struct { - bool used; - uint32_t inode; - size_t offset; - vfs_node_type_t type; -} vfs_dir_t; - -typedef uint64_t ino_t; -typedef int64_t off_t; - - -#define IFTODT(mode) (((mode) & 0170000) >> 12) - -#define __MLIBC_DIRENT_BODY ino_t d_ino; \ - off_t d_off; \ - unsigned short d_reclen; \ - unsigned char d_type; \ - char d_name[1024]; - -struct dirent { - __MLIBC_DIRENT_BODY +struct vfs_node { + struct vfs_node *mountpoint; + struct vfs_node *redir; + struct resource *resource; + struct vfs_filesystem *filesystem; + char *name; + struct vfs_node *parent; + HASHMAP_TYPE(struct vfs_node *) children; + char *symlink_target; + bool populated; }; -extern vfs_dir_t vfs_dir_table[VFS_MAX_DIRS]; +typedef struct vfs_node *(*fs_mount_t)(struct vfs_node *, const char *, + struct vfs_node *); -// API -fd_t VFS_Open(const char* path, int flags, unsigned int mode); -int VFS_Read(fd_t fd, uint8_t* buf, size_t size); -int VFS_Write(fd_t fd, uint8_t* data, size_t size); -int VFS_Close(fd_t fd); +struct vfs_filesystem { + void (*populate)(struct vfs_filesystem *, struct vfs_node *); + struct vfs_node *(*create)(struct vfs_filesystem *, struct vfs_node *, + const char *, int); + struct vfs_node *(*symlink)(struct vfs_filesystem *, struct vfs_node *, + const char *, const char *); + struct vfs_node *(*link)(struct vfs_filesystem *, struct vfs_node *, + const char *, struct vfs_node *); +}; -int VFS_RegisterCharDev(const char* path, - int (*read)(void* buf, size_t len), - int (*write)(const void* buf, size_t len)); \ No newline at end of file +extern struct vfs_node *vfs_root; + +void vfs_init(void); +struct vfs_node *vfs_create_node(struct vfs_filesystem *fs, + struct vfs_node *parent, const char *name, + bool dir); +void vfs_create_dotentries(struct vfs_node *node, struct vfs_node *parent); +void vfs_add_filesystem(fs_mount_t fs_mount, const char *identifier); +struct vfs_node *vfs_get_node(struct vfs_node *parent, const char *path, + bool follow_links); +bool vfs_mount(struct vfs_node *parent, const char *source, const char *target, + const char *fs_name); +struct vfs_node *vfs_symlink(struct vfs_node *parent, const char *dest, + const char *target); +struct vfs_node *vfs_create(struct vfs_node *parent, const char *name, + int mode); +bool vfs_unlink(struct vfs_node *parent, const char *path); +size_t vfs_pathname(struct vfs_node *node, char *buffer, size_t len); +bool vfs_fdnum_path_to_node(int dir_fdnum, const char *path, bool empty_path, + bool enoent_error, struct vfs_node **parent, + struct vfs_node **node, char **basename); + +void syscall_openat(struct syscall_arguments *args); +void syscall_open(struct syscall_arguments *args); +void syscall_fstatat(struct syscall_arguments *args); +void syscall_getcwd(struct syscall_arguments *args); +void syscall_chdir(struct syscall_arguments *args); +void syscall_readdir(struct syscall_arguments *args); +void syscall_readlinkat(struct syscall_arguments *args); +void syscall_linkat(struct syscall_arguments *args); +void syscall_unlinkat(struct syscall_arguments *args); +void syscall_mkdirat(struct syscall_arguments *args); +void syscall_mknodat(struct syscall_arguments *args); \ No newline at end of file diff --git a/src/ipc/pipe.c b/src/ipc/pipe.c new file mode 100644 index 0000000..c2e395f --- /dev/null +++ b/src/ipc/pipe.c @@ -0,0 +1,135 @@ +#include "libk/debug.h" +#include "libk/errno.h" +#include "pipe.h" +#include "libk/event.h" +#include "sched/syscall.h" +#include "arch/x86_64/sys/prcb.h" + + +static bool pipe_unref(struct resource *this, + struct f_description *description) { + (void)description; + event_trigger(&this->event, false); + this->refcount--; + struct pipe *p = (struct pipe *)this; + if (!this->refcount) { + kfree(p->data); + } + return true; +} + +static ssize_t pipe_read(struct resource *this, + struct f_description *description, void *buf, + off_t offset, size_t count) { + + (void)description; + (void)offset; + struct pipe *p = (struct pipe *)this; + uint8_t *d = buf; + + if (p->read_ptr == p->write_ptr) { + event_trigger(&this->event, false); + return 0; + } + + spinlock_acquire_or_wait(&this->lock); + + size_t i = 0; + for (i = 0; i < count; i++) { + if (p->write_ptr == p->read_ptr) { + this->status &= ~POLLIN; + break; + } + d[i] = p->data[p->read_ptr++ % p->data_length]; + } + + if (p->read_ptr != p->write_ptr) { + this->status |= POLLOUT; + } + + event_trigger(&this->event, false); + spinlock_drop(&this->lock); + return i; +} + +static ssize_t pipe_write(struct resource *this, + struct f_description *description, const void *buf, + off_t offset, size_t count) { + + spinlock_acquire_or_wait(&this->lock); + (void)description; + (void)offset; + + struct pipe *p = (struct pipe *)this; + const uint8_t *d = buf; + + for (size_t i = 0; i < count; i++) { + while (p->write_ptr == p->read_ptr + p->data_length) { + event_trigger(&this->event, false); + struct event *events[] = {&this->event}; + spinlock_drop(&this->lock); + if (event_await(events, 1, true) < 0) { + errno = EINTR; + return -1; + } + spinlock_acquire_or_wait(&this->lock); + } + p->data[p->write_ptr++ % p->data_length] = d[i]; + } + + if (p->write_ptr == p->read_ptr + p->data_length) { + this->status &= ~POLLOUT; + } + + this->status |= POLLIN; + + event_trigger(&this->event, false); + spinlock_drop(&this->lock); + return count; +} + +struct pipe *pipe_create(void) { + struct pipe *p = resource_create(sizeof(struct pipe)); + p->data = kmalloc(PAGE_SIZE * 32); + memzero(p->data, PAGE_SIZE * 32); + p->data_length = PAGE_SIZE * 32; + + p->read_ptr = 0; + p->write_ptr = 0; + + p->res.stat.st_mode = S_IFIFO; + p->res.write = pipe_write; + p->res.read = pipe_read; + p->res.unref = pipe_unref; + return p; +} + +void syscall_pipe(struct syscall_arguments *args) { + struct process *proc = sched_get_running_thread()->mother_proc; + + int *fds = (int *)(args->args0); + int flags = (int)(args->args1); + + args->ret = 0; + + if (fds == NULL) { + errno = EFAULT; + args->ret = -1; + return; + } + + struct resource *p = (struct resource *)pipe_create(); + + if (p == NULL) { + errno = ENOMEM; + args->ret = -1; + return; + } + + fds[0] = fdnum_create_from_resource(proc, p, flags | O_RDONLY, 0, false); + fds[1] = fdnum_create_from_resource(proc, p, flags | O_WRONLY, 0, false); + + if (fds[0] == -1 || fds[1] == -1) { + args->ret = -1; + } +} diff --git a/src/ipc/pipe.h b/src/ipc/pipe.h new file mode 100644 index 0000000..9ab051a --- /dev/null +++ b/src/ipc/pipe.h @@ -0,0 +1,18 @@ +#pragma once + +#include "fs/vfs.h" +#include "sched/sched.h" +#include +#include + +struct pipe { + struct resource res; + uint8_t *data; + size_t data_length; + uintptr_t read_ptr; + uintptr_t write_ptr; + struct event read_event; + struct event write_event; +}; + +void syscall_pipe(struct syscall_arguments *args); \ No newline at end of file diff --git a/src/libk/debug.c b/src/libk/debug.c index 158ce3f..3719b28 100644 --- a/src/libk/debug.c +++ b/src/libk/debug.c @@ -2,6 +2,42 @@ #include #include "stdio.h" #include +#include "mp/spinlock.h" +#include "libk/string.h" +#include "drivers/fb/fb.h" +#include "arch/x86_64/sys/timer.h" +#include "arch/x86_64/sys/halt.h" +#include "mp/mp.h" +#include "arch/x86_64/debug/backtrace.h" +#include "fs/elf.h" +#include "sched/sched_types.h" +#include "kargs.h" + +#if PRINTF_ALIAS_STANDARD_FUNCTION_NAMES_HARD +# define printf_ printf +# define sprintf_ sprintf +# define vsprintf_ vsprintf +# define snprintf_ snprintf +# define vsnprintf_ vsnprintf +# define vprintf_ vprintf +#endif + +#if PRINTF_SUPPORT_LONG_LONG +typedef unsigned long long printf_unsigned_value_t; +typedef long long printf_signed_value_t; +#else +typedef unsigned long printf_unsigned_value_t; +typedef long printf_signed_value_t; +#endif + + +#define ABS_FOR_PRINTING(_x) ((printf_unsigned_value_t) ( (_x) > 0 ? (_x) : -((printf_signed_value_t)_x) )) + +// 'ntoa' conversion buffer size, this must be big enough to hold one converted +// numeric number including padded zeros (dynamically created on stack) +#ifndef PRINTF_INTEGER_BUFFER_SIZE +#define PRINTF_INTEGER_BUFFER_SIZE 32 +#endif static const uint32_t g_LogSeverityColors[] = { @@ -13,22 +49,907 @@ static const uint32_t g_LogSeverityColors[] = }; -static const char* const g_ColorReset = "\033[0m"; +static printf_flags_t parse_flags(const char** format) +{ + printf_flags_t flags = 0U; + do { + switch (**format) { + case '0': flags |= FLAGS_ZEROPAD; (*format)++; break; + case '-': flags |= FLAGS_LEFT; (*format)++; break; + case '+': flags |= FLAGS_PLUS; (*format)++; break; + case ' ': flags |= FLAGS_SPACE; (*format)++; break; + case '#': flags |= FLAGS_HASH; (*format)++; break; + default : return flags; + } + } while (true); +} + +// Note: This function currently assumes it is not passed a '\0' c, +// or alternatively, that '\0' can be passed to the function in the output +// gadget. The former assumption holds within the printf library. It also +// assumes that the output gadget has been properly initialized. +static inline void putchar_via_gadget(output_gadget_t* gadget, char c) +{ + printf_size_t write_pos = gadget->pos++; + // We're _always_ increasing pos, so as to count how may characters + // _would_ have been written if not for the max_chars limitation + if (write_pos >= gadget->max_chars) { + return; + } + if (gadget->function != NULL) { + // No check for c == '\0' . + gadget->function(c, gadget->extra_function_arg); + } + else { + // it must be the case that gadget->buffer != NULL , due to the constraint + // on output_gadget_t ; and note we're relying on write_pos being non-negative. + gadget->buffer[write_pos] = c; + } +} + +// internal test if char is a digit (0-9) +// @return true if char is a digit +static inline bool is_digit_(char ch) +{ + return (ch >= '0') && (ch <= '9'); +} + +// internal ASCII string to printf_size_t conversion +static printf_size_t atou_(const char** str) +{ + printf_size_t i = 0U; + while (is_digit_(**str)) { + i = i * 10U + (printf_size_t)(*((*str)++) - '0'); + } + return i; +} + +// output the specified string in reverse, taking care of any zero-padding +static void out_rev_(output_gadget_t* output, const char* buf, printf_size_t len, printf_size_t width, printf_flags_t flags) +{ + const printf_size_t start_pos = output->pos; + + // pad spaces up to given width + if (!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD)) { + for (printf_size_t i = len; i < width; i++) { + putchar_via_gadget(output, ' '); + } + } + + // reverse string + while (len) { + putchar_via_gadget(output, buf[--len]); + } + + // append pad spaces up to given width + if (flags & FLAGS_LEFT) { + while (output->pos - start_pos < width) { + putchar_via_gadget(output, ' '); + } + } +} + +// Invoked by print_integer after the actual number has been printed, performing necessary +// work on the number's prefix (as the number is initially printed in reverse order) +static void print_integer_finalization(output_gadget_t* output, char* buf, printf_size_t len, bool negative, numeric_base_t base, printf_size_t precision, printf_size_t width, printf_flags_t flags) +{ + printf_size_t unpadded_len = len; + + // pad with leading zeros + { + if (!(flags & FLAGS_LEFT)) { + if (width && (flags & FLAGS_ZEROPAD) && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) { + width--; + } + while ((flags & FLAGS_ZEROPAD) && (len < width) && (len < PRINTF_INTEGER_BUFFER_SIZE)) { + buf[len++] = '0'; + } + } + + while ((len < precision) && (len < PRINTF_INTEGER_BUFFER_SIZE)) { + buf[len++] = '0'; + } + + if (base == BASE_OCTAL && (len > unpadded_len)) { + // Since we've written some zeros, we've satisfied the alternative format leading space requirement + flags &= ~FLAGS_HASH; + } + } + + // handle hash + if (flags & (FLAGS_HASH | FLAGS_POINTER)) { + if (!(flags & FLAGS_PRECISION) && len && ((len == precision) || (len == width))) { + // Let's take back some padding digits to fit in what will eventually + // be the format-specific prefix + if (unpadded_len < len) { + len--; // This should suffice for BASE_OCTAL + } + if (len && (base == BASE_HEX || base == BASE_BINARY) && (unpadded_len < len)) { + len--; // ... and an extra one for 0x or 0b + } + } + if ((base == BASE_HEX) && !(flags & FLAGS_UPPERCASE) && (len < PRINTF_INTEGER_BUFFER_SIZE)) { + buf[len++] = 'x'; + } + else if ((base == BASE_HEX) && (flags & FLAGS_UPPERCASE) && (len < PRINTF_INTEGER_BUFFER_SIZE)) { + buf[len++] = 'X'; + } + else if ((base == BASE_BINARY) && (len < PRINTF_INTEGER_BUFFER_SIZE)) { + buf[len++] = 'b'; + } + if (len < PRINTF_INTEGER_BUFFER_SIZE) { + buf[len++] = '0'; + } + } + + if (len < PRINTF_INTEGER_BUFFER_SIZE) { + if (negative) { + buf[len++] = '-'; + } + else if (flags & FLAGS_PLUS) { + buf[len++] = '+'; // ignore the space if the '+' exists + } + else if (flags & FLAGS_SPACE) { + buf[len++] = ' '; + } + } + + out_rev_(output, buf, len, width, flags); +} + +// An internal itoa-like function +static void print_integer(output_gadget_t* output, printf_unsigned_value_t value, bool negative, numeric_base_t base, printf_size_t precision, printf_size_t width, printf_flags_t flags) +{ + char buf[PRINTF_INTEGER_BUFFER_SIZE]; + printf_size_t len = 0U; + + if (!value) { + if ( !(flags & FLAGS_PRECISION) ) { + buf[len++] = '0'; + flags &= ~FLAGS_HASH; + // We drop this flag this since either the alternative and regular modes of the specifier + // don't differ on 0 values, or (in the case of octal) we've already provided the special + // handling for this mode. + } + else if (base == BASE_HEX) { + flags &= ~FLAGS_HASH; + // We drop this flag this since either the alternative and regular modes of the specifier + // don't differ on 0 values + } + } + else { + do { + const char digit = (char)(value % base); + buf[len++] = (char)(digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10); + value /= base; + } while (value && (len < PRINTF_INTEGER_BUFFER_SIZE)); + } + + print_integer_finalization(output, buf, len, negative, base, precision, width, flags); +} + +static inline printf_size_t strnlen_s_(const char* str, printf_size_t maxsize) +{ + const char* s; + for (s = str; *s && maxsize--; ++s); + return (printf_size_t)(s - str); +} + + +static inline void format_string_loop(output_gadget_t* output, const char* format, va_list args) +{ +#if PRINTF_CHECK_FOR_NUL_IN_FORMAT_SPECIFIER +#define ADVANCE_IN_FORMAT_STRING(cptr_) do { (cptr_)++; if (!*(cptr_)) return; } while(0) +#else +#define ADVANCE_IN_FORMAT_STRING(cptr_) (cptr_)++ +#endif + + + while (*format) + { + if (*format != '%') { + // A regular content character + putchar_via_gadget(output, *format); + format++; + continue; + } + // We're parsing a format specifier: %[flags][width][.precision][length] + ADVANCE_IN_FORMAT_STRING(format); + + printf_flags_t flags = parse_flags(&format); + + // evaluate width field + printf_size_t width = 0U; + if (is_digit_(*format)) { + width = (printf_size_t) atou_(&format); + } + else if (*format == '*') { + const int w = va_arg(args, int); + if (w < 0) { + flags |= FLAGS_LEFT; // reverse padding + width = (printf_size_t)-w; + } + else { + width = (printf_size_t)w; + } + ADVANCE_IN_FORMAT_STRING(format); + } + + // evaluate precision field + printf_size_t precision = 0U; + if (*format == '.') { + flags |= FLAGS_PRECISION; + ADVANCE_IN_FORMAT_STRING(format); + if (is_digit_(*format)) { + precision = (printf_size_t) atou_(&format); + } + else if (*format == '*') { + const int precision_ = va_arg(args, int); + precision = precision_ > 0 ? (printf_size_t) precision_ : 0U; + ADVANCE_IN_FORMAT_STRING(format); + } + } + + // evaluate length field + switch (*format) { +#ifdef PRINTF_SUPPORT_MSVC_STYLE_INTEGER_SPECIFIERS + case 'I' : { + ADVANCE_IN_FORMAT_STRING(format); + // Greedily parse for size in bits: 8, 16, 32 or 64 + switch(*format) { + case '8': flags |= FLAGS_INT8; + ADVANCE_IN_FORMAT_STRING(format); + break; + case '1': + ADVANCE_IN_FORMAT_STRING(format); + if (*format == '6') { format++; flags |= FLAGS_INT16; } + break; + case '3': + ADVANCE_IN_FORMAT_STRING(format); + if (*format == '2') { ADVANCE_IN_FORMAT_STRING(format); flags |= FLAGS_INT32; } + break; + case '6': + ADVANCE_IN_FORMAT_STRING(format); + if (*format == '4') { ADVANCE_IN_FORMAT_STRING(format); flags |= FLAGS_INT64; } + break; + default: break; + } + break; + } +#endif + case 'l' : + flags |= FLAGS_LONG; + ADVANCE_IN_FORMAT_STRING(format); + if (*format == 'l') { + flags |= FLAGS_LONG_LONG; + ADVANCE_IN_FORMAT_STRING(format); + } + break; + case 'L' : + flags |= FLAGS_LONG_DOUBLE; + ADVANCE_IN_FORMAT_STRING(format); + break; + case 'h' : + flags |= FLAGS_SHORT; + ADVANCE_IN_FORMAT_STRING(format); + if (*format == 'h') { + flags |= FLAGS_CHAR; + ADVANCE_IN_FORMAT_STRING(format); + } + break; + case 't' : + flags |= (sizeof(ptrdiff_t) <= sizeof(int) ) ? FLAGS_INT : (sizeof(ptrdiff_t) == sizeof(long)) ? FLAGS_LONG : FLAGS_LONG_LONG; + ADVANCE_IN_FORMAT_STRING(format); + break; + case 'j' : + flags |= (sizeof(intmax_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); + ADVANCE_IN_FORMAT_STRING(format); + break; + case 'z' : + flags |= (sizeof(size_t) <= sizeof(int) ) ? FLAGS_INT : (sizeof(size_t) == sizeof(long)) ? FLAGS_LONG : FLAGS_LONG_LONG; + ADVANCE_IN_FORMAT_STRING(format); + break; + default: + break; + } + + // evaluate specifier + switch (*format) { + case 'd' : + case 'i' : + case 'u' : + case 'x' : + case 'X' : + case 'o' : + case 'b' : { + + if (*format == 'd' || *format == 'i') { + flags |= FLAGS_SIGNED; + } + + numeric_base_t base; + if (*format == 'x' || *format == 'X') { + base = BASE_HEX; + } + else if (*format == 'o') { + base = BASE_OCTAL; + } + else if (*format == 'b') { + base = BASE_BINARY; + } + else { + base = BASE_DECIMAL; + flags &= ~FLAGS_HASH; // decimal integers have no alternative presentation + } + + if (*format == 'X') { + flags |= FLAGS_UPPERCASE; + } + + format++; + // ignore '0' flag when precision is given + if (flags & FLAGS_PRECISION) { + flags &= ~FLAGS_ZEROPAD; + } + + if (flags & FLAGS_SIGNED) { + // A signed specifier: d, i or possibly I + bit size if enabled + + if (flags & FLAGS_LONG_LONG) { +#if PRINTF_SUPPORT_LONG_LONG + const long long value = va_arg(args, long long); + print_integer(output, ABS_FOR_PRINTING(value), value < 0, base, precision, width, flags); +#endif + } + else if (flags & FLAGS_LONG) { + const long value = va_arg(args, long); + print_integer(output, ABS_FOR_PRINTING(value), value < 0, base, precision, width, flags); + } + else { + // We never try to interpret the argument as something potentially-smaller than int, + // due to integer promotion rules: Even if the user passed a short int, short unsigned + // etc. - these will come in after promotion, as int's (or unsigned for the case of + // short unsigned when it has the same size as int) + const int value = + (flags & FLAGS_CHAR) ? (signed char) va_arg(args, int) : + (flags & FLAGS_SHORT) ? (short int) va_arg(args, int) : + va_arg(args, int); + print_integer(output, ABS_FOR_PRINTING(value), value < 0, base, precision, width, flags); + } + } + else { + // An unsigned specifier: u, x, X, o, b + + flags &= ~(FLAGS_PLUS | FLAGS_SPACE); + + if (flags & FLAGS_LONG_LONG) { +#if PRINTF_SUPPORT_LONG_LONG + print_integer(output, (printf_unsigned_value_t) va_arg(args, unsigned long long), false, base, precision, width, flags); +#endif + } + else if (flags & FLAGS_LONG) { + print_integer(output, (printf_unsigned_value_t) va_arg(args, unsigned long), false, base, precision, width, flags); + } + else { + const unsigned int value = + (flags & FLAGS_CHAR) ? (unsigned char)va_arg(args, unsigned int) : + (flags & FLAGS_SHORT) ? (unsigned short int)va_arg(args, unsigned int) : + va_arg(args, unsigned int); + print_integer(output, (printf_unsigned_value_t) value, false, base, precision, width, flags); + } + } + break; + } +#if PRINTF_SUPPORT_DECIMAL_SPECIFIERS + case 'f' : + case 'F' : { + floating_point_t value = (floating_point_t) (flags & FLAGS_LONG_DOUBLE ? va_arg(args, long double) : va_arg(args, double)); + if (*format == 'F') flags |= FLAGS_UPPERCASE; + print_floating_point(output, value, precision, width, flags, PRINTF_PREFER_DECIMAL); + format++; + break; + } +#endif +#if PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS + case 'e': + case 'E': + case 'g': + case 'G': { + floating_point_t value = (floating_point_t) (flags & FLAGS_LONG_DOUBLE ? va_arg(args, long double) : va_arg(args, double)); + if ((*format == 'g')||(*format == 'G')) flags |= FLAGS_ADAPT_EXP; + if ((*format == 'E')||(*format == 'G')) flags |= FLAGS_UPPERCASE; + print_floating_point(output, value, precision, width, flags, PRINTF_PREFER_EXPONENTIAL); + format++; + break; + } +#endif // PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS + case 'c' : { + printf_size_t l = 1U; + // pre padding + if (!(flags & FLAGS_LEFT)) { + while (l++ < width) { + putchar_via_gadget(output, ' '); + } + } + // char output + putchar_via_gadget(output, (char) va_arg(args, int) ); + // post padding + if (flags & FLAGS_LEFT) { + while (l++ < width) { + putchar_via_gadget(output, ' '); + } + } + format++; + break; + } + + case 's' : { + const char* p = va_arg(args, char*); + if (p == NULL) { + out_rev_(output, ")llun(", 6, width, flags); + } + else { + printf_size_t l = strnlen_s_(p, precision ? precision : PRINTF_MAX_POSSIBLE_BUFFER_SIZE); + // pre padding + if (flags & FLAGS_PRECISION) { + l = (l < precision ? l : precision); + } + if (!(flags & FLAGS_LEFT)) { + while (l++ < width) { + putchar_via_gadget(output, ' '); + } + } + // string output + while ((*p != 0) && (!(flags & FLAGS_PRECISION) || precision)) { + putchar_via_gadget(output, *(p++)); + --precision; + } + // post padding + if (flags & FLAGS_LEFT) { + while (l++ < width) { + putchar_via_gadget(output, ' '); + } + } + } + format++; + break; + } + + case 'p' : { + width = sizeof(void*) * 2U + 2; // 2 hex chars per byte + the "0x" prefix + flags |= FLAGS_ZEROPAD | FLAGS_POINTER; + uintptr_t value = (uintptr_t)va_arg(args, void*); + (value == (uintptr_t) NULL) ? + out_rev_(output, ")lin(", 5, width, flags) : + print_integer(output, (printf_unsigned_value_t) value, false, BASE_HEX, precision, width, flags); + format++; + break; + } + + case '%' : + putchar_via_gadget(output, '%'); + format++; + break; + + // Many people prefer to disable support for %n, as it lets the caller + // engineer a write to an arbitrary location, of a value the caller + // effectively controls - which could be a security concern in some cases. +#if PRINTF_SUPPORT_WRITEBACK_SPECIFIER + case 'n' : { + if (flags & FLAGS_CHAR) *(va_arg(args, char*)) = (char) output->pos; + else if (flags & FLAGS_SHORT) *(va_arg(args, short*)) = (short) output->pos; + else if (flags & FLAGS_LONG) *(va_arg(args, long*)) = (long) output->pos; +#if PRINTF_SUPPORT_LONG_LONG + else if (flags & FLAGS_LONG_LONG) *(va_arg(args, long long*)) = (long long int) output->pos; +#endif // PRINTF_SUPPORT_LONG_LONG + else *(va_arg(args, int*)) = (int) output->pos; + format++; + break; + } +#endif // PRINTF_SUPPORT_WRITEBACK_SPECIFIER + + default : + putchar_via_gadget(output, *format); + format++; + break; + } + } +} + +// Possibly-write the string-terminating '\0' character +static inline void append_termination_with_gadget(output_gadget_t* gadget) +{ + if (gadget->function != NULL || gadget->max_chars == 0) { + return; + } + if (gadget->buffer == NULL) { + return; + } + printf_size_t null_char_pos = gadget->pos < gadget->max_chars ? gadget->pos : gadget->max_chars - 1; + gadget->buffer[null_char_pos] = '\0'; +} + + +static int vsnprintf_impl(output_gadget_t* output, const char* format, va_list args) +{ + // Note: The library only calls vsnprintf_impl() with output->pos being 0. However, it is + // possible to call this function with a non-zero pos value for some "remedial printing". + format_string_loop(output, format, args); + + // termination + append_termination_with_gadget(output); + + // return written chars without terminating \0 + return (int)output->pos; +} + + +static inline output_gadget_t discarding_gadget(void) +{ + output_gadget_t gadget; + gadget.function = NULL; + gadget.extra_function_arg = NULL; + gadget.buffer = NULL; + gadget.pos = 0; + gadget.max_chars = 0; + return gadget; +} + +static inline output_gadget_t buffer_gadget(char* buffer, size_t buffer_size) +{ + printf_size_t usable_buffer_size = (buffer_size > PRINTF_MAX_POSSIBLE_BUFFER_SIZE) ? + PRINTF_MAX_POSSIBLE_BUFFER_SIZE : (printf_size_t) buffer_size; + output_gadget_t result = discarding_gadget(); + if (buffer != NULL) { + result.buffer = buffer; + result.max_chars = usable_buffer_size; + } + return result; +} + +static inline output_gadget_t function_gadget(void (*function)(char, void*), void* extra_arg) +{ + output_gadget_t result = discarding_gadget(); + result.function = function; + result.extra_function_arg = extra_arg; + result.max_chars = PRINTF_MAX_POSSIBLE_BUFFER_SIZE; + return result; +} + +static inline void putchar_wrapper(char c, void* unused) +{ + (void) unused; + putchar_(c); +} + +static inline output_gadget_t extern_putchar_gadget(void) +{ + return function_gadget(putchar_wrapper, NULL); +} + +int vprintf_(const char* format, va_list arg) +{ + output_gadget_t gadget = extern_putchar_gadget(); + return vsnprintf_impl(&gadget, format, arg); +} void logf(const char* module, DebugLevel level, const char* fmt, ...) { va_list args; va_start(args, fmt); - uint32_t color = g_LogSeverityColors[level]; - - fputs_colored("[", color); - fputs_colored(module, color); - fputs_colored("] ", color); - - // print formatted text in same color - vfprintf(fmt, args); // or adapt vfprintf to take a color - - fputc('\n'); + kprintf("WARN: logf called!"); va_end(args); -} \ No newline at end of file +} + +static spinlock_t write_lock = {0}; +bool in_panic = false; +bool put_to_fb = true; +bool print_now = false; + +void kputchar(char c) { + if (c == '\n') + kputchar('\r'); + //kputchar_(c); + + if (put_to_fb) + framebuffer_putchar(c); +} + +void kputs(char *string) { + //kputs_(string); + + if (put_to_fb) + framebuffer_puts(string); +} + +bool disable_prefix = false; + +void kprintffos(bool fos, char *fmt, ...) { + spinlock_acquire_or_wait(&write_lock); + if (!fos) + put_to_fb = 0; + va_list args; + va_start(args, fmt); + if (!print_now) { + goto end; + } + if (!disable_prefix) { + if (in_panic) { + kputs("*** PANIC:\t"); + } else { + uint64_t timer_tick = 0; + if (timer_installed()) { + timer_tick = timer_count(); + } + char string[21] = {0}; + ltoa(timer_tick, string, 10); + kputs("["); + kputs(string); + kputs("] "); + } + } + vprintf_(fmt, args); +end: + va_end(args); + spinlock_drop(&write_lock); +} + + +void debug_hex_dump(const void *data, size_t size) { + disable_prefix = true; + char ascii[17]; + size_t i, j; + ascii[16] = '\0'; + for (i = 0; i < size; ++i) { + kprintffos(0, "%02X ", ((unsigned char *)data)[i]); + if (((unsigned char *)data)[i] >= ' ' && + ((unsigned char *)data)[i] <= '~') { + ascii[i % 16] = ((unsigned char *)data)[i]; + } else { + ascii[i % 16] = '.'; + } + if ((i + 1) % 8 == 0 || i + 1 == size) { + kprintffos(0, " "); + if ((i + 1) % 16 == 0) { + kprintffos(0, "| %s \n", ascii); + } else if (i + 1 == size) { + ascii[(i + 1) % 16] = '\0'; + if ((i + 1) % 16 <= 8) { + kprintffos(0, " "); + } + for (j = (i + 1) % 16; j < 16; ++j) { + kprintffos(0, " "); + } + kprintffos(0, "| %s \n", ascii); + } + } + } + disable_prefix = false; +} + +void panic_(size_t *ip, size_t *bp, char *fmt, ...) { + cli(); + spinlock_drop(&write_lock); + halt_other_cpus(); + put_to_fb = 1; + extern bool is_smp; + extern bool sched_runit; + if (in_panic) { + kprintf("Pretty bad kernel panic here\n"); + va_list args; + va_start(args, fmt); + kputs("*** PANIC:\t"); + vprintf_(fmt, args); + va_end(args); + halt_current_cpu(); + } + in_panic = true; + + if (is_smp) { + kprintf("Panic called on CPU%d\n", + prcb_return_current_cpu()->cpu_number); + } + + if (sched_runit && prcb_return_current_cpu()->running_thread) { + kprintf("Current thread id: %ld\n", + prcb_return_current_cpu()->running_thread->tid); + kprintf("Process name corresponding to the current thread: %s\n", + prcb_return_current_cpu()->running_thread->mother_proc->name); + } + + va_list args; + va_start(args, fmt); + kputs("*** PANIC:\t"); + vprintf_(fmt, args); + va_end(args); + kprintf("Crashed at %p\n", ip); + backtrace(bp); + halt_current_cpu(); + __builtin_unreachable(); +} + + +typedef struct { + char* buf; + size_t size; + size_t pos; +} snprintf_ctx_t; + +static void buf_putc(snprintf_ctx_t* ctx, char c) +{ + if (ctx->pos + 1 < ctx->size) { + ctx->buf[ctx->pos] = c; + } + ctx->pos++; +} + +static void buf_puts(snprintf_ctx_t* ctx, const char* str) +{ + while (*str) { + buf_putc(ctx, *str++); + } +} + +const char g_HexChars[] = "0123456789abcdef"; + +static void buf_print_unsigned(snprintf_ctx_t* ctx, uint64_t number, int radix, int width, int zero_pad) +{ + char buffer[32]; + int pos = 0; + + do { + buffer[pos++] = g_HexChars[number % radix]; + number /= radix; + } while (number > 0); + + // Apply zero-padding / width + if (zero_pad) { + while (pos < width) + buffer[pos++] = '0'; + } + + while (--pos >= 0) + buf_putc(ctx, buffer[pos]); +} + +static void buf_print_signed(snprintf_ctx_t* ctx, int64_t number, int radix, int width, int zero_pad) +{ + if (number < 0) { + buf_putc(ctx, '-'); + buf_print_unsigned(ctx, (uint64_t)(-number), radix, width, zero_pad); + } else { + buf_print_unsigned(ctx, (uint64_t)number, radix, width, zero_pad); + } +} + +int vsnprintf(char* buf, size_t size, const char* fmt, va_list args) +{ + snprintf_ctx_t ctx = { .buf = buf, .size = size, .pos = 0 }; + + while (*fmt) { + if (*fmt != '%') { + buf_putc(&ctx, *fmt++); + continue; + } + fmt++; + + int zero_pad = 0; + if (*fmt == '0') { zero_pad = 1; fmt++; } + + int width = 0; + while (*fmt >= '0' && *fmt <= '9') { + width = width * 10 + (*fmt++ - '0'); + } + + int precision = -1; + if (*fmt == '.') { + fmt++; + precision = 0; + while (*fmt >= '0' && *fmt <= '9') { + precision = precision * 10 + (*fmt++ - '0'); + } + } + + int is_long_long = 0; + if (*fmt == 'l') { + fmt++; + if (*fmt == 'l') { is_long_long = 1; fmt++; } + } else if (*fmt == 'z') { + is_long_long = 1; + fmt++; + } else if (*fmt == 'h') { + fmt++; + } + + switch (*fmt++) { + case 'c': + buf_putc(&ctx, (char)va_arg(args, int)); + break; + + case 's': { + const char* s = va_arg(args, const char*); + if (!s) s = "(null)"; + int max = (precision >= 0) ? precision : INT_MAX; + while (*s && max--) buf_putc(&ctx, *s++); + break; + } + + case 'd': + case 'i': + if (is_long_long) + buf_print_signed(&ctx, va_arg(args, long long), 10, width, zero_pad); + else + buf_print_signed(&ctx, va_arg(args, long), 10, width, zero_pad); + break; + + case 'u': + if (is_long_long) + buf_print_unsigned(&ctx, va_arg(args, unsigned long long), 10, width, zero_pad); + else + buf_print_unsigned(&ctx, va_arg(args, unsigned long), 10, width, zero_pad); + break; + + case 'x': + case 'X': + if (is_long_long) + buf_print_unsigned(&ctx, va_arg(args, unsigned long long), 16, width, zero_pad); + else + buf_print_unsigned(&ctx, va_arg(args, unsigned long), 16, width, zero_pad); + break; + + case 'p': + buf_puts(&ctx, "0x"); + buf_print_unsigned(&ctx, va_arg(args, uint64_t), 16, 16, 1); + break; + + case 'o': + if (is_long_long) + buf_print_unsigned(&ctx, va_arg(args, unsigned long long), 8, width, zero_pad); + else + buf_print_unsigned(&ctx, va_arg(args, unsigned long), 8, width, zero_pad); + break; + + case '%': + buf_putc(&ctx, '%'); + break; + + default: + buf_putc(&ctx, '%'); + buf_putc(&ctx, *(fmt - 1)); + break; + } + } + + if (ctx.size > 0) { + if (ctx.pos < ctx.size) + ctx.buf[ctx.pos] = '\0'; + else + ctx.buf[ctx.size - 1] = '\0'; + } + + return (int)ctx.pos; +} + + +int snprintf(char* buf, size_t size, const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + int ret = vsnprintf(buf, size, fmt, args); + va_end(args); + return ret; +} + +void syscall_puts(struct syscall_arguments *args) { + if (kernel_arguments.kernel_args & + KERNEL_ARGS_SUPPRESS_USER_DEBUG_MESSAGES) { + return; + } + char *str = (char *)(args->args0); + spinlock_acquire_or_wait(&write_lock); + if (str) { + kputs(str); + } + spinlock_drop(&write_lock); +} diff --git a/src/libk/debug.h b/src/libk/debug.h index dbf4cb5..cb25c22 100644 --- a/src/libk/debug.h +++ b/src/libk/debug.h @@ -1,4 +1,11 @@ -#include "libk/stdio.h" +#pragma once + +#include +#include +#include +#include +#include +#include "sched/syscall.h" #define MIN_LOG_LEVEL LVL_DEBUG @@ -10,11 +17,128 @@ typedef enum { LVL_CRITICAL = 4 } DebugLevel; +// The following will convert the number-of-digits into an exponential-notation literal +#define PRINTF_CONCATENATE(s1, s2) s1##s2 +#define PRINTF_EXPAND_THEN_CONCATENATE(s1, s2) PRINTF_CONCATENATE(s1, s2) +#define PRINTF_FLOAT_NOTATION_THRESHOLD ((floating_point_t) PRINTF_EXPAND_THEN_CONCATENATE(1e,PRINTF_MAX_INTEGRAL_DIGITS_FOR_DECIMAL)) +// internal flag definitions +#define FLAGS_ZEROPAD (1U << 0U) +#define FLAGS_LEFT (1U << 1U) +#define FLAGS_PLUS (1U << 2U) +#define FLAGS_SPACE (1U << 3U) +#define FLAGS_HASH (1U << 4U) +#define FLAGS_UPPERCASE (1U << 5U) +#define FLAGS_CHAR (1U << 6U) +#define FLAGS_SHORT (1U << 7U) +#define FLAGS_INT (1U << 8U) + // Only used with PRINTF_SUPPORT_MSVC_STYLE_INTEGER_SPECIFIERS +#define FLAGS_LONG (1U << 9U) +#define FLAGS_LONG_LONG (1U << 10U) +#define FLAGS_PRECISION (1U << 11U) +#define FLAGS_ADAPT_EXP (1U << 12U) +#define FLAGS_POINTER (1U << 13U) + // Note: Similar, but not identical, effect as FLAGS_HASH +#define FLAGS_SIGNED (1U << 14U) +#define FLAGS_LONG_DOUBLE (1U << 15U) + // Only used with PRINTF_SUPPORT_MSVC_STYLE_INTEGER_SPECIFIERS + +#ifdef PRINTF_SUPPORT_MSVC_STYLE_INTEGER_SPECIFIERS + +#define FLAGS_INT8 FLAGS_CHAR + + +#if (SHRT_MAX == 32767LL) +#define FLAGS_INT16 FLAGS_SHORT +#elif (INT_MAX == 32767LL) +#define FLAGS_INT16 FLAGS_INT +#elif (LONG_MAX == 32767LL) +#define FLAGS_INT16 FLAGS_LONG +#elif (LLONG_MAX == 32767LL) +#define FLAGS_INT16 FLAGS_LONG_LONG +#else +#error "No basic integer type has a size of 16 bits exactly" +#endif + +#if (SHRT_MAX == 2147483647LL) +#define FLAGS_INT32 FLAGS_SHORT +#elif (INT_MAX == 2147483647LL) +#define FLAGS_INT32 FLAGS_INT +#elif (LONG_MAX == 2147483647LL) +#define FLAGS_INT32 FLAGS_LONG +#elif (LLONG_MAX == 2147483647LL) +#define FLAGS_INT32 FLAGS_LONG_LONG +#else +#error "No basic integer type has a size of 32 bits exactly" +#endif + +#if (SHRT_MAX == 9223372036854775807LL) +#define FLAGS_INT64 FLAGS_SHORT +#elif (INT_MAX == 9223372036854775807LL) +#define FLAGS_INT64 FLAGS_INT +#elif (LONG_MAX == 9223372036854775807LL) +#define FLAGS_INT64 FLAGS_LONG +#elif (LLONG_MAX == 9223372036854775807LL) +#define FLAGS_INT64 FLAGS_LONG_LONG +#else +#error "No basic integer type has a size of 64 bits exactly" +#endif + +#endif // PRINTF_SUPPORT_MSVC_STYLE_INTEGER_SPECIFIERS + +typedef unsigned int printf_flags_t; + +#define BASE_BINARY 2 +#define BASE_OCTAL 8 +#define BASE_DECIMAL 10 +#define BASE_HEX 16 + +typedef uint8_t numeric_base_t; + +typedef unsigned int printf_size_t; +#define PRINTF_MAX_POSSIBLE_BUFFER_SIZE INT_MAX + +typedef struct { + void (*function)(char c, void* extra_arg); + void* extra_function_arg; + char* buffer; + printf_size_t pos; + printf_size_t max_chars; +} output_gadget_t; + +struct function_symbol { + uint64_t address; + const char *name; +}; + + +extern bool put_to_fb; +extern bool print_now; + +#define panic(...) \ + panic_(__builtin_return_address(0), __builtin_frame_address(0), __VA_ARGS__) void logf(const char* module, DebugLevel level, const char* fmt, ...); +void kprintffos(bool fos, char *fmt, ...); +void kputchar(char c); +void panic_(size_t *ip, size_t *bp, char *fmt, ...); +void backtrace(uintptr_t *bp); +void backtrace_unsafe(uintptr_t *bp); +void debug_hex_dump(const void *data, size_t size); + + #define log_debug(module, ...) logf(module, LVL_DEBUG, __VA_ARGS__) #define log_info(module, ...) logf(module, LVL_INFO, __VA_ARGS__) #define log_warn(module, ...) logf(module, LVL_WARN, __VA_ARGS__) #define log_err(module, ...) logf(module, LVL_ERROR, __VA_ARGS__) -#define log_crit(module, ...) logf(module, LVL_CRITICAL, __VA_ARGS__) \ No newline at end of file +#define log_crit(module, ...) logf(module, LVL_CRITICAL, __VA_ARGS__) +#define kprintf(...) kprintffos(put_to_fb, __VA_ARGS__) + + +#define putchar_ kputchar + +int snprintf(char* buf, size_t size, const char* fmt, ...); +int vsnprintf(char* buf, size_t size, const char* fmt, va_list args); + +void syscall_puts(struct syscall_arguments *args); + diff --git a/src/libk/errno.h b/src/libk/errno.h index c63fcab..4461127 100644 --- a/src/libk/errno.h +++ b/src/libk/errno.h @@ -1,143 +1,130 @@ -#define EPERM 1 /* Operation not permitted */ -#define ENOENT 2 /* No such file or directory */ -#define ESRCH 3 /* No such process */ -#define EINTR 4 /* Interrupted system call */ -#define EIO 5 /* I/O error */ -#define ENXIO 6 /* No such device or address */ -#define E2BIG 7 /* Argument list too long */ -#define ENOEXEC 8 /* Exec format error */ -#define EBADF 9 /* Bad file descriptor */ -#define ECHILD 10 /* No child processes */ -#define EAGAIN 11 /* Try again / resource temporarily unavailable */ -#define ENOMEM 12 /* Out of memory */ -#define EACCES 13 /* Permission denied */ -#define EFAULT 14 /* Bad address */ -#define ENOTBLK 15 /* Block device required */ -#define EBUSY 16 /* Device or resource busy */ -#define EEXIST 17 /* File exists */ -#define EXDEV 18 /* Cross-device link */ -#define ENODEV 19 /* No such device */ -#define ENOTDIR 20 /* Not a directory */ -#define EISDIR 21 /* Is a directory */ -#define EINVAL 22 /* Invalid argument */ -#define ENFILE 23 /* File table overflow */ -#define EMFILE 24 /* Too many open files */ -#define ENOTTY 25 /* Not a typewriter / inappropriate ioctl */ -#define ETXTBSY 26 /* Text file busy */ -#define EFBIG 27 /* File too large */ -#define ENOSPC 28 /* No space left on device */ -#define ESPIPE 29 /* Illegal seek */ -#define EROFS 30 /* Read-only file system */ -#define EMLINK 31 /* Too many links */ -#define EPIPE 32 /* Broken pipe */ -#define EDOM 33 /* Math argument out of domain of func */ -#define ERANGE 34 /* Math result not representable */ -#define EDEADLK 35 /* Resource deadlock would occur */ -#define ENAMETOOLONG 36 /* File name too long */ -#define ENOLCK 37 /* No record locks available */ -#define ENOSYS 38 /* Function not implemented */ -#define ENOTEMPTY 39 /* Directory not empty */ -#define ELOOP 40 /* Too many symbolic links encountered */ +#pragma once +#include +#include "sched/sched.h" -#define EWOULDBLOCK EAGAIN /* Operation would block */ +#define errno sched_get_running_thread()->errno -#define ENOMSG 42 /* No message of desired type */ -#define EIDRM 43 /* Identifier removed */ -#define ECHRNG 44 /* Channel number out of range */ -#define EL2NSYNC 45 /* Level 2 not synchronized */ -#define EL3HLT 46 /* Level 3 halted */ -#define EL3RST 47 /* Level 3 reset */ -#define ELNRNG 48 /* Link number out of range */ -#define EUNATCH 49 /* Protocol driver not attached */ -#define ENOCSI 50 /* No CSI structure available */ -#define EL2HLT 51 /* Level 2 halted */ -#define EBADE 52 /* Invalid exchange */ -#define EBADR 53 /* Invalid request descriptor */ -#define EXFULL 54 /* Exchange full */ -#define ENOANO 55 /* No anode */ -#define EBADRQC 56 /* Invalid request code */ -#define EBADSLT 57 /* Invalid slot */ +#define EPERM 1 /* Not super-user */ +#define ENOENT 2 /* No such file or directory */ +#define ESRCH 3 /* No such process */ +#define EINTR 4 /* Interrupted system call */ +#define EIO 5 /* I/O error */ +#define ENXIO 6 /* No such device or address */ +#define E2BIG 7 /* Arg list too long */ +#define ENOEXEC 8 /* Exec format error */ +#define EBADF 9 /* Bad file number */ +#define ECHILD 10 /* No children */ +#define EAGAIN 11 /* No more processes */ +#define ENOMEM 12 /* Not enough core */ +#define EACCES 13 /* Permission denied */ +#define EFAULT 14 /* Bad address */ +#define ENOTBLK 15 /* Block device required */ +#define EBUSY 16 /* Mount device busy */ +#define EEXIST 17 /* File exists */ +#define EXDEV 18 /* Cross-device link */ +#define ENODEV 19 /* No such device */ +#define ENOTDIR 20 /* Not a directory */ +#define EISDIR 21 /* Is a directory */ +#define EINVAL 22 /* Invalid argument */ +#define ENFILE 23 /* Too many open files in system */ +#define EMFILE 24 /* Too many open files */ +#define ENOTTY 25 /* Not a typewriter */ +#define ETXTBSY 26 /* Text file busy */ +#define EFBIG 27 /* File too large */ +#define ENOSPC 28 /* No space left on device */ +#define ESPIPE 29 /* Illegal seek */ +#define EROFS 30 /* Read only file system */ +#define EMLINK 31 /* Too many links */ +#define EPIPE 32 /* Broken pipe */ +#define EDOM 33 /* Math arg out of domain of func */ +#define ERANGE 34 /* Math result not representable */ +#define ENOMSG 35 /* No message of desired type */ +#define EIDRM 36 /* Identifier removed */ +#define ECHRNG 37 /* Channel number out of range */ +#define EL2NSYNC 38 /* Level 2 not synchronized */ +#define EL3HLT 39 /* Level 3 halted */ +#define EL3RST 40 /* Level 3 reset */ +#define ELNRNG 41 /* Link number out of range */ +#define EUNATCH 42 /* Protocol driver not attached */ +#define ENOCSI 43 /* No CSI structure available */ +#define EL2HLT 44 /* Level 2 halted */ +#define EDEADLK 45 /* Deadlock condition */ +#define ENOLCK 46 /* No record locks available */ +#define EBADE 50 /* Invalid exchange */ +#define EBADR 51 /* Invalid request descriptor */ +#define EXFULL 52 /* Exchange full */ +#define ENOANO 53 /* No anode */ +#define EBADRQC 54 /* Invalid request code */ +#define EBADSLT 55 /* Invalid slot */ +#define EDEADLOCK 56 /* File locking deadlock error */ +#define EBFONT 57 /* Bad font file fmt */ +#define ENOSTR 60 /* Device not a stream */ +#define ENODATA 61 /* No data (for no delay io) */ +#define ETIME 62 /* Timer expired */ +#define ENOSR 63 /* Out of streams resources */ +#define ENONET 64 /* Machine is not on the network */ +#define ENOPKG 65 /* Package not installed */ +#define EREMOTE 66 /* The object is remote */ +#define ENOLINK 67 /* The link has been severed */ +#define EADV 68 /* Advertise error */ +#define ESRMNT 69 /* Srmount error */ +#define ECOMM 70 /* Communication error on send */ +#define EPROTO 71 /* Protocol error */ +#define EMULTIHOP 74 /* Multihop attempted */ +#define ELBIN 75 /* Inode is remote (not really error) */ +#define EDOTDOT 76 /* Cross mount point (not really error) */ +#define EBADMSG 77 /* Trying to read unreadable message */ +#define EFTYPE 79 /* Inappropriate file type or format */ +#define ENOTUNIQ 80 /* Given log. name not unique */ +#define EBADFD 81 /* f.d. invalid for this operation */ +#define EREMCHG 82 /* Remote address changed */ +#define ELIBACC 83 /* Can't access a needed shared lib */ +#define ELIBBAD 84 /* Accessing a corrupted shared lib */ +#define ELIBSCN 85 /* .lib section in a.out corrupted */ +#define ELIBMAX 86 /* Attempting to link in too many libs */ +#define ELIBEXEC 87 /* Attempting to exec a shared library */ +#define ENOSYS 88 /* Function not implemented */ +#define ENMFILE 89 /* No more files */ +#define ENOTEMPTY 90 /* Directory not empty */ +#define ENAMETOOLONG 91 /* File or path name too long */ +#define ELOOP 92 /* Too many symbolic links */ +#define EOPNOTSUPP 95 /* Operation not supported on transport endpoint */ +#define EPFNOSUPPORT 96 /* Protocol family not supported */ +#define ECONNRESET 104 /* Connection reset by peer */ +#define ENOBUFS 105 /* No buffer space available */ +#define EAFNOSUPPORT 106 /* Address family not supported by protocol family */ +#define EPROTOTYPE 107 /* Protocol wrong type for socket */ +#define ENOTSOCK 108 /* Socket operation on non-socket */ +#define ENOPROTOOPT 109 /* Protocol not available */ +#define ESHUTDOWN 110 /* Can't send after socket shutdown */ +#define ECONNREFUSED 111 /* Connection refused */ +#define EADDRINUSE 112 /* Address already in use */ +#define ECONNABORTED 113 /* Connection aborted */ +#define ENETUNREACH 114 /* Network is unreachable */ +#define ENETDOWN 115 /* Network interface is not configured */ +#define ETIMEDOUT 116 /* Connection timed out */ +#define EHOSTDOWN 117 /* Host is down */ +#define EHOSTUNREACH 118 /* Host is unreachable */ +#define EINPROGRESS 119 /* Connection already in progress */ +#define EALREADY 120 /* Socket already connected */ +#define EDESTADDRREQ 121 /* Destination address required */ +#define EMSGSIZE 122 /* Message too long */ +#define EPROTONOSUPPORT 123 /* Unknown protocol */ +#define ESOCKTNOSUPPORT 124 /* Socket type not supported */ +#define EADDRNOTAVAIL 125 /* Address not available */ +#define ENETRESET 126 +#define EISCONN 127 /* Socket is already connected */ +#define ENOTCONN 128 /* Socket is not connected */ +#define ETOOMANYREFS 129 +#define EPROCLIM 130 +#define EUSERS 131 +#define EDQUOT 132 +#define ESTALE 133 +#define ENOTSUP 134 /* Not supported */ +#define ENOMEDIUM 135 /* No medium (in tape drive) */ +#define ENOSHARE 136 /* No such host or network path */ +#define ECASECLASH 137 /* Filename exists with different case */ +#define EILSEQ 138 +#define EOVERFLOW 139 /* Value too large for defined data type */ -#define EDEADLOCK EDEADLK /* Alias for deadlock */ - -#define EBFONT 59 /* Bad font file format */ -#define ENOSTR 60 /* Device not a stream */ -#define ENODATA 61 /* No data available */ -#define ETIME 62 /* Timer expired */ -#define ENOSR 63 /* Out of streams resources */ -#define ENONET 64 /* Machine is not on the network */ -#define ENOPKG 65 /* Package not installed */ -#define EREMOTE 66 /* Object is remote */ -#define ENOLINK 67 /* Link has been severed */ -#define EADV 68 /* Advertise error */ -#define ESRMNT 69 /* Srmount error */ -#define ECOMM 70 /* Communication error on send */ -#define EPROTO 71 /* Protocol error */ -#define EMULTIHOP 72 /* Multihop attempted */ -#define EDOTDOT 73 /* RFS specific error */ -#define EBADMSG 74 /* Not a data message */ -#define EOVERFLOW 75 /* Value too large for defined data type */ -#define ENOTUNIQ 76 /* Name not unique on network */ -#define EBADFD 77 /* File descriptor in bad state */ -#define EREMCHG 78 /* Remote address changed */ - -#define ELIBACC 79 /* Can not access a needed shared library */ -#define ELIBBAD 80 /* Accessing a corrupted shared library */ -#define ELIBSCN 81 /* lib section in a.out corrupted */ -#define ELIBMAX 82 /* Attempting to link in too many libs */ -#define ELIBEXEC 83 /* Cannot exec a shared library directly */ - -#define EILSEQ 84 /* Illegal byte sequence */ -#define ERESTART 85 /* Interrupted system call should be restarted */ -#define ESTRPIPE 86 /* Streams pipe error */ -#define EUSERS 87 /* Too many users */ - -#define ENOTSOCK 88 /* Socket operation on non-socket */ -#define EDESTADDRREQ 89 /* Destination address required */ -#define EMSGSIZE 90 /* Message too long */ -#define EPROTOTYPE 91 /* Protocol wrong type for socket */ -#define ENOPROTOOPT 92 /* Protocol not available */ -#define EPROTONOSUPPORT 93 /* Protocol not supported */ -#define ESOCKTNOSUPPORT 94 /* Socket type not supported */ -#define EOPNOTSUPP 95 /* Operation not supported */ -#define ENOTSUP EOPNOTSUPP - -#define EPFNOSUPPORT 96 /* Protocol family not supported */ -#define EAFNOSUPPORT 97 /* Address family not supported */ -#define EADDRINUSE 98 /* Address already in use */ -#define EADDRNOTAVAIL 99 /* Cannot assign requested address */ -#define ENETDOWN 100 /* Network is down */ -#define ENETUNREACH 101 /* Network is unreachable */ -#define ENETRESET 102 /* Network dropped connection */ -#define ECONNABORTED 103 /* Software caused connection abort */ -#define ECONNRESET 104 /* Connection reset by peer */ -#define ENOBUFS 105 /* No buffer space available */ -#define EISCONN 106 /* Transport endpoint is already connected */ -#define ENOTCONN 107 /* Transport endpoint is not connected */ -#define ESHUTDOWN 108 /* Cannot send after transport endpoint shutdown */ -#define ETOOMANYREFS 109 /* Too many references */ -#define ETIMEDOUT 110 /* Connection timed out */ -#define ECONNREFUSED 111 /* Connection refused */ -#define EHOSTDOWN 112 /* Host is down */ -#define EHOSTUNREACH 113 /* No route to host */ -#define EALREADY 114 /* Operation already in progress */ -#define EINPROGRESS 115 /* Operation now in progress */ -#define ESTALE 116 /* Stale file handle */ -#define EUCLEAN 117 /* Structure needs cleaning */ -#define ENOTNAM 118 /* Not a XENIX named type file */ -#define ENAVAIL 119 /* No XENIX semaphores available */ -#define EISNAM 120 /* Is a named type file */ -#define EREMOTEIO 121 /* Remote I/O error */ -#define EDQUOT 122 /* Quota exceeded */ -#define ENOMEDIUM 123 /* No medium found */ -#define EMEDIUMTYPE 124 /* Wrong medium type */ -#define ECANCELED 125 /* Operation canceled */ -#define ENOKEY 126 /* Required key not available */ -#define EKEYEXPIRED 127 /* Key has expired */ -#define EKEYREVOKED 128 /* Key has been revoked */ -#define EKEYREJECTED 129 /* Key was rejected by service */ - -#define EOWNERDEAD 130 /* Owner died */ -#define ENOTRECOVERABLE 131 /* State not recoverable */ -#define ERFKILL 132 /* Operation not possible due to RF-kill */ -#define EHWPOISON 133 /* Memory page has hardware error */ \ No newline at end of file +#define EWOULDBLOCK EAGAIN diff --git a/src/libk/event.c b/src/libk/event.c new file mode 100644 index 0000000..bca1d2d --- /dev/null +++ b/src/libk/event.c @@ -0,0 +1,147 @@ +#include "arch/x86_64/asm/asm.h" +#include "debug.h" +#include "event.h" +#include "mp/spinlock.h" +#include "sched/sched.h" +#include +#include +#include +#include "arch/x86_64/sys/prcb.h" +#include "types.h" + + +extern spinlock_t thread_lock; + +static ssize_t check_for_pending(struct event **events, size_t num_events) { + for (size_t i = 0; i < num_events; i++) { + if (events[i]->pending > 0) { + events[i]->pending--; + return i; + } + } + return -1; +} + +static void attach_listeners(struct event **events, size_t num_events, + struct thread *thread) { + thread->attached_events_i = 0; + + for (size_t i = 0; i < num_events; i++) { + struct event *event = events[i]; + + if (event->listeners_i == EVENT_MAX_LISTENERS) { + panic("Event listeners exhausted\n"); + } + + struct event_listener *listener = + &event->listeners[event->listeners_i++]; + listener->thread = thread; + listener->which = i; + + if (thread->attached_events_i == MAX_EVENTS) { + panic("Listening on too many events\n"); + } + + thread->attached_events[thread->attached_events_i++] = event; + } +} + +static void detach_listeners(struct thread *thread) { + for (size_t i = 0; i < thread->attached_events_i; i++) { + struct event *event = thread->attached_events[i]; + + for (size_t j = 0; j < event->listeners_i; j++) { + struct event_listener *listener = &event->listeners[j]; + if (listener->thread != thread) { + continue; + } + + event->listeners[j] = event->listeners[--event->listeners_i]; + break; + } + } + + thread->attached_events_i = 0; +} + +static void lock_events(struct event **events, size_t num_events) { + for (size_t i = 0; i < num_events; i++) { + spinlock_acquire_or_wait(&events[i]->lock); + } +} + +static void unlock_events(struct event **events, size_t num_events) { + for (size_t i = 0; i < num_events; i++) { + spinlock_drop(&events[i]->lock); + } +} + +ssize_t event_await(struct event **events, size_t num_events, bool block) { + ssize_t ret = -1; + + struct thread *thread = sched_get_running_thread(); + bool old_state = int_toggle(false); + + lock_events(events, num_events); + + ssize_t i = check_for_pending(events, num_events); + if (i != -1) { + ret = i; + unlock_events(events, num_events); + goto cleanup; + } + + if (!block) { + unlock_events(events, num_events); + goto cleanup; + } + + attach_listeners(events, num_events, thread); + unlock_events(events, num_events); + + thread->state = THREAD_WAITING_FOR_EVENT; + sched_yield(true); + + int_toggle(false); + ret = thread->which_event; + + lock_events(events, num_events); + detach_listeners(thread); + unlock_events(events, num_events); + +cleanup: + int_toggle(old_state); + return ret; +} + +size_t event_trigger(struct event *event, bool drop) { + if (!event) { + return 0; + } + + bool old_state = int_toggle(false); + + spinlock_acquire_or_wait(&event->lock); + + size_t ret = 0; + if (event->listeners_i == 0) { + if (!drop) { + event->pending++; + } + ret = 0; + goto cleanup; + } + for (size_t i = 0; i < event->listeners_i; i++) { + struct event_listener *listener = &event->listeners[i]; + struct thread *thread = listener->thread; + thread->which_event = listener->which; + thread->state = THREAD_READY_TO_RUN; + } + ret = event->listeners_i; + event->listeners_i = 0; + +cleanup: + spinlock_drop(&event->lock); + int_toggle(old_state); + return ret; +} diff --git a/src/libk/event.h b/src/libk/event.h new file mode 100644 index 0000000..f9ffa6d --- /dev/null +++ b/src/libk/event.h @@ -0,0 +1,23 @@ +#pragma once +#include "mp/spinlock.h" +#include +#include +#include +#include "types.h" + +#define EVENT_MAX_LISTENERS 32 + +struct event_listener { + struct thread *thread; + size_t which; +}; + +struct event { + spinlock_t lock; + size_t pending; + size_t listeners_i; + struct event_listener listeners[EVENT_MAX_LISTENERS]; +}; + +ssize_t event_await(struct event **events, size_t num_events, bool block); +size_t event_trigger(struct event *event, bool drop); \ No newline at end of file diff --git a/src/libk/hashmap.h b/src/libk/hashmap.h new file mode 100644 index 0000000..609740a --- /dev/null +++ b/src/libk/hashmap.h @@ -0,0 +1,216 @@ +#pragma once +#include +#include +#include +#include "mm/slab.h" +#include "mm/memory.h" +#include "libk/string.h" + +static inline uint32_t hash(const void *data, size_t length) { + const uint8_t *data_u8 = data; + uint32_t hash = 0; + + for (size_t i = 0; i < length; i++) { + uint32_t c = data_u8[i]; + hash = c + (hash << 6) + (hash << 16) - hash; + } + + return hash; +} + +#define HASHMAP_KEY_DATA_MAX 256 + +#define HASHMAP_INIT(CAP) \ + { .cap = (CAP), .buckets = NULL } + +#define HASHMAP_DELETE(HASHMAP) \ + do { \ + __auto_type HASHMAP_DELETE_hashmap = HASHMAP; \ + \ + if (HASHMAP_DELETE_hashmap->buckets == NULL) { \ + break; \ + } \ + \ + for (size_t HASHMAP_DELETE_i = 0; \ + HASHMAP_DELETE_i < HASHMAP_DELETE_hashmap->cap; \ + HASHMAP_DELETE_i++) { \ + __auto_type HASHMAP_DELETE_bucket = \ + &HASHMAP_DELETE_hashmap->buckets[HASHMAP_DELETE_i]; \ + \ + kfree(HASHMAP_DELETE_bucket->items); \ + } \ + \ + kfree(HASHMAP_DELETE_hashmap->buckets); \ + } while (0) + +#define HASHMAP_TYPE(TYPE) \ + struct { \ + size_t cap; \ + struct { \ + size_t cap; \ + size_t filled; \ + struct { \ + uint8_t key_data[HASHMAP_KEY_DATA_MAX]; \ + size_t key_length; \ + TYPE item; \ + } *items; \ + } *buckets; \ + } + +#define HASHMAP_GET(HASHMAP, RET, KEY_DATA, KEY_LENGTH) \ + ({ \ + __label__ out; \ + bool HASHMAP_GET_ok = false; \ + \ + __auto_type HASHMAP_GET_key_data = KEY_DATA; \ + __auto_type HASHMAP_GET_key_length = KEY_LENGTH; \ + \ + __auto_type HASHMAP_GET_hashmap = HASHMAP; \ + if (HASHMAP_GET_hashmap->buckets == NULL) { \ + goto out; \ + } \ + \ + size_t HASHMAP_GET_hash = \ + hash(HASHMAP_GET_key_data, HASHMAP_GET_key_length); \ + size_t HASHMAP_GET_index = \ + HASHMAP_GET_hash % HASHMAP_GET_hashmap->cap; \ + \ + __auto_type HASHMAP_GET_bucket = \ + &HASHMAP_GET_hashmap->buckets[HASHMAP_GET_index]; \ + \ + for (size_t HASHMAP_GET_i = 0; \ + HASHMAP_GET_i < HASHMAP_GET_bucket->filled; HASHMAP_GET_i++) { \ + if (HASHMAP_GET_key_length != \ + HASHMAP_GET_bucket->items[HASHMAP_GET_i].key_length) { \ + continue; \ + } \ + if (memcmp(HASHMAP_GET_key_data, \ + HASHMAP_GET_bucket->items[HASHMAP_GET_i].key_data, \ + HASHMAP_GET_key_length) == 0) { \ + RET = HASHMAP_GET_bucket->items[HASHMAP_GET_i].item; \ + HASHMAP_GET_ok = true; \ + break; \ + } \ + } \ + \ + out: \ + HASHMAP_GET_ok; \ + }) + +#define HASHMAP_SGET(HASHMAP, RET, STRING) \ + ({ \ + const char *HASHMAP_SGET_string = (STRING); \ + HASHMAP_GET(HASHMAP, RET, HASHMAP_SGET_string, \ + strlen(HASHMAP_SGET_string)); \ + }) + +#define HASHMAP_REMOVE(HASHMAP, KEY_DATA, KEY_LENGTH) \ + ({ \ + __label__ out; \ + \ + bool HASHMAP_REMOVE_ok = false; \ + \ + __auto_type HASHMAP_REMOVE_key_data = KEY_DATA; \ + __auto_type HASHMAP_REMOVE_key_length = KEY_LENGTH; \ + \ + __auto_type HASHMAP_REMOVE_hashmap = HASHMAP; \ + \ + if (HASHMAP_REMOVE_hashmap->buckets == NULL) { \ + goto out; \ + } \ + \ + size_t HASHMAP_REMOVE_hash = \ + hash(HASHMAP_REMOVE_key_data, HASHMAP_REMOVE_key_length); \ + size_t HASHMAP_REMOVE_index = \ + HASHMAP_REMOVE_hash % HASHMAP_REMOVE_hashmap->cap; \ + \ + __auto_type HASHMAP_REMOVE_bucket = \ + &HASHMAP_REMOVE_hashmap->buckets[HASHMAP_REMOVE_index]; \ + \ + for (size_t HASHMAP_REMOVE_i = 0; \ + HASHMAP_REMOVE_i < HASHMAP_REMOVE_bucket->filled; \ + HASHMAP_REMOVE_i++) { \ + if (HASHMAP_REMOVE_key_length != \ + HASHMAP_REMOVE_bucket->items[HASHMAP_REMOVE_i].key_length) { \ + continue; \ + } \ + if (memcmp( \ + HASHMAP_REMOVE_key_data, \ + HASHMAP_REMOVE_bucket->items[HASHMAP_REMOVE_i].key_data, \ + HASHMAP_REMOVE_key_length) == 0) { \ + if (HASHMAP_REMOVE_i != HASHMAP_REMOVE_bucket->filled - 1) { \ + memcpy(&HASHMAP_REMOVE_bucket->items[HASHMAP_REMOVE_i], \ + &HASHMAP_REMOVE_bucket \ + ->items[HASHMAP_REMOVE_bucket->filled - 1], \ + sizeof(*HASHMAP_REMOVE_bucket->items)); \ + } \ + HASHMAP_REMOVE_bucket->filled--; \ + HASHMAP_REMOVE_ok = true; \ + break; \ + } \ + } \ + \ + out: \ + HASHMAP_REMOVE_ok; \ + }) + +#define HASHMAP_SREMOVE(HASHMAP, STRING) \ + ({ \ + const char *HASHMAP_SREMOVE_string = (STRING); \ + HASHMAP_REMOVE(HASHMAP, HASHMAP_SREMOVE_string, \ + strlen(HASHMAP_SREMOVE_string)); \ + }) + +#define HASHMAP_INSERT(HASHMAP, KEY_DATA, KEY_LENGTH, ITEM) \ + do { \ + __auto_type HASHMAP_INSERT_key_data = KEY_DATA; \ + __auto_type HASHMAP_INSERT_key_length = KEY_LENGTH; \ + \ + __auto_type HASHMAP_INSERT_hashmap = HASHMAP; \ + if (HASHMAP_INSERT_hashmap->buckets == NULL) { \ + HASHMAP_INSERT_hashmap->buckets = \ + kmalloc(HASHMAP_INSERT_hashmap->cap * \ + sizeof(*HASHMAP_INSERT_hashmap->buckets)); \ + } \ + \ + size_t HASHMAP_INSERT_hash = \ + hash(HASHMAP_INSERT_key_data, HASHMAP_INSERT_key_length); \ + size_t HASHMAP_INSERT_index = \ + HASHMAP_INSERT_hash % HASHMAP_INSERT_hashmap->cap; \ + \ + __auto_type HASHMAP_INSERT_bucket = \ + &HASHMAP_INSERT_hashmap->buckets[HASHMAP_INSERT_index]; \ + \ + if (HASHMAP_INSERT_bucket->cap == 0) { \ + HASHMAP_INSERT_bucket->cap = 16; \ + HASHMAP_INSERT_bucket->items = \ + kmalloc(HASHMAP_INSERT_bucket->cap * \ + sizeof(*HASHMAP_INSERT_bucket->items)); \ + } \ + \ + if (HASHMAP_INSERT_bucket->filled == HASHMAP_INSERT_bucket->cap) { \ + HASHMAP_INSERT_bucket->cap *= 2; \ + HASHMAP_INSERT_bucket->items = \ + krealloc(HASHMAP_INSERT_bucket->items, \ + HASHMAP_INSERT_bucket->cap * \ + sizeof(*HASHMAP_INSERT_bucket->items)); \ + } \ + \ + __auto_type HASHMAP_INSERT_item = \ + &HASHMAP_INSERT_bucket->items[HASHMAP_INSERT_bucket->filled]; \ + \ + memcpy(HASHMAP_INSERT_item->key_data, HASHMAP_INSERT_key_data, \ + HASHMAP_INSERT_key_length); \ + HASHMAP_INSERT_item->key_length = HASHMAP_INSERT_key_length; \ + HASHMAP_INSERT_item->item = ITEM; \ + \ + HASHMAP_INSERT_bucket->filled++; \ + } while (0) + +#define HASHMAP_SINSERT(HASHMAP, STRING, ITEM) \ + do { \ + const char *HASHMAP_SINSERT_string = (STRING); \ + HASHMAP_INSERT(HASHMAP, HASHMAP_SINSERT_string, \ + strlen(HASHMAP_SINSERT_string), ITEM); \ + } while (0) + diff --git a/src/libk/kargs.c b/src/libk/kargs.c new file mode 100644 index 0000000..5ffea80 --- /dev/null +++ b/src/libk/kargs.c @@ -0,0 +1,66 @@ +#include "kargs.h" +#include "mm/slab.h" +#include "libk/string.h" + +struct kernel_args kernel_arguments = {0}; + +void kargs_init(char *args) { + char **tokens = NULL; + size_t count = strsplit(args, ' ', &tokens); + + for (size_t i = 0; i < count; i++) { + if (!strncmp(tokens[i], "cpus", 4)) { + char *cpu_count = &tokens[i][5]; + kernel_arguments.kernel_args |= KERNEL_ARGS_CPU_COUNT_GIVEN; + kernel_arguments.cpu_count = atol(cpu_count); + } + if (!strncmp(tokens[i], "no-lai", 6)) { + kernel_arguments.kernel_args |= KERNEL_ARGS_NO_LAI; + } + if (!strncmp(tokens[i], "kprintf", 7)) { + kernel_arguments.kernel_args |= KERNEL_ARGS_KPRINTF_LOGS; + } + if (!strncmp(tokens[i], "init", 4)) { + kernel_arguments.kernel_args |= KERNEL_ARGS_INIT_PATH_GIVEN; + char *init_path_from_args = &tokens[i][5]; + size_t length = strlen(init_path_from_args); + kernel_arguments.init_binary_path = kmalloc(length + 1); + strncpy(kernel_arguments.init_binary_path, init_path_from_args, + length + 1); + } + if (!strncmp(tokens[i], "suppress-ubsan", 14)) { + kernel_arguments.kernel_args |= KERNEL_ARGS_SUPPRESS_UBSAN; + } + if (!strncmp(tokens[i], "allow-writes-to-disks", 21)) { + kernel_arguments.kernel_args |= KERNEL_ARGS_ALLOW_WRITES_TO_DISKS; + } + if (!strncmp(tokens[i], "suppress-user-debug-messages", 29)) { + kernel_arguments.kernel_args |= + KERNEL_ARGS_SUPPRESS_USER_DEBUG_MESSAGES; + } + if (!strncmp(tokens[i], "dont-trust-cpu-random-seed", 27)) { + kernel_arguments.kernel_args |= + KERNEL_ARGS_DONT_TRUST_CPU_RANDOM_SEED; + } + if (!strncmp(tokens[i], "panic-on-deadlock", 18)) { + kernel_arguments.kernel_args |= KERNEL_ARGS_PANIC_ON_DEADLOCK; + } + if (!strncmp(tokens[i], "netconf", 7)) { + char *addrs = &tokens[i][8]; + char **a_tokens = NULL; + size_t a_count = strsplit(addrs, ',', &a_tokens); + if (a_count == 3) { + kernel_arguments.kernel_args |= KERNEL_ARGS_IP_GIVEN; + kernel_arguments.ip = a_tokens[0]; + kernel_arguments.mask = a_tokens[1]; + kernel_arguments.gateway = a_tokens[2]; + } + kfree(a_tokens); + } + } + + for (size_t i = 0; i < count; i++) { + kfree(tokens[i]); + } + kfree(tokens); +} diff --git a/src/libk/kargs.h b/src/libk/kargs.h new file mode 100644 index 0000000..2177e42 --- /dev/null +++ b/src/libk/kargs.h @@ -0,0 +1,28 @@ +#pragma once +#include + +enum kernel_args_enum_t { + KERNEL_ARGS_NO_LAI = 1, + KERNEL_ARGS_CPU_COUNT_GIVEN = 1 << 1, + KERNEL_ARGS_KPRINTF_LOGS = 1 << 2, + KERNEL_ARGS_INIT_PATH_GIVEN = 1 << 3, + KERNEL_ARGS_SUPPRESS_UBSAN = 1 << 4, + KERNEL_ARGS_ALLOW_WRITES_TO_DISKS = 1 << 5, + KERNEL_ARGS_SUPPRESS_USER_DEBUG_MESSAGES = 1 << 6, + KERNEL_ARGS_DONT_TRUST_CPU_RANDOM_SEED = 1 << 7, + KERNEL_ARGS_PANIC_ON_DEADLOCK = 1 << 8, + KERNEL_ARGS_IP_GIVEN = 1 << 9, +}; + +struct kernel_args { + uint32_t kernel_args; + uint32_t cpu_count; + char *init_binary_path; + char *ip; + char *mask; + char *gateway; +}; + +extern struct kernel_args kernel_arguments; + +void kargs_init(char *args); \ No newline at end of file diff --git a/src/libk/list.h b/src/libk/list.h new file mode 100644 index 0000000..37b98ba --- /dev/null +++ b/src/libk/list.h @@ -0,0 +1,39 @@ +#pragma once + +struct list_head { + struct list_head *next, *prev; +}; + +#define LIST_HEAD_INIT(name) { &(name), &(name) } +#define LIST_HEAD(name) struct list_head name = LIST_HEAD_INIT(name) + +static inline void list_init(struct list_head *list) { + list->next = list; + list->prev = list; +} + +static inline void list_add(struct list_head *new, struct list_head *head) { + new->next = head->next; + new->prev = head; + head->next->prev = new; + head->next = new; +} + +static inline void list_add_tail(struct list_head *new, struct list_head *head) { + new->next = head; + new->prev = head->prev; + head->prev->next = new; + head->prev = new; +} + +static inline void list_del(struct list_head *entry) { + entry->next->prev = entry->prev; + entry->prev->next = entry->next; +} + +static inline int list_empty(const struct list_head *head) { + return head->next == head; +} + +#define list_entry(ptr, type, member) \ + ((type *)((char *)(ptr) - offsetof(type, member))) \ No newline at end of file diff --git a/src/libk/misc.h b/src/libk/misc.h new file mode 100644 index 0000000..e060dbd --- /dev/null +++ b/src/libk/misc.h @@ -0,0 +1,38 @@ +#pragma once + +#define MIN(A, B) \ + ({ \ + __auto_type MIN_a = A; \ + __auto_type MIN_b = B; \ + MIN_a < MIN_b ? MIN_a : MIN_b; \ + }) + +#define MAX(A, B) \ + ({ \ + __auto_type MAX_a = A; \ + __auto_type MAX_b = B; \ + MAX_a > MAX_b ? MAX_a : MAX_b; \ + }) + +#define DIV_ROUNDUP(A, B) \ + ({ \ + __auto_type _a_ = A; \ + __auto_type _b_ = B; \ + (_a_ + (_b_ - 1)) / _b_; \ + }) + +#define ALIGN_UP(A, B) \ + ({ \ + __auto_type _a__ = A; \ + __auto_type _b__ = B; \ + DIV_ROUNDUP(_a__, _b__) * _b__; \ + }) + +#define ALIGN_DOWN(A, B) \ + ({ \ + __auto_type _a_ = A; \ + __auto_type _b_ = B; \ + (_a_ / _b_) * _b_; \ + }) + +#define SIZEOF_ARRAY(ARRAY) (sizeof(ARRAY) / sizeof(ARRAY[0])) \ No newline at end of file diff --git a/src/libk/module.h b/src/libk/module.h new file mode 100644 index 0000000..65edc38 --- /dev/null +++ b/src/libk/module.h @@ -0,0 +1,33 @@ +#include "vec.h" +#include +#include +#include + + +struct module; + +struct mapping { + uintptr_t addr; + size_t size; + uint64_t prot; +}; + +typedef uint64_t (*module_entry_t)(struct module *); +typedef void (*void_func_t)(void); + +struct module { + char name[128]; + module_entry_t entry_point; + void_func_t exit; + size_t mappings_count; + struct mapping mappings[16]; +}; + +typedef vec_t(struct module *) module_list_t; + +extern module_list_t modules_list; + +uint64_t module_load(const char *path); +bool module_unload(const char *name); + +void module_dump(void); diff --git a/src/libk/random.c b/src/libk/random.c new file mode 100644 index 0000000..d443e7d --- /dev/null +++ b/src/libk/random.c @@ -0,0 +1,117 @@ +#include +#include +#include "random.h" +#include "types.h" +#include "resource.h" +#include "fs/vfs.h" +#include "arch/x86_64/sys/timer.h" +#include "fs/devtmpfs.h" + + +#define W 64 +#define N 312 +#define M 156 +#define R 31 + +#define A 0xB5026F5AA96619E9ULL +#define U 29 +#define D 0x5555555555555555ULL +#define S 17 +#define B 0x71D67FFFEDA60000ULL +#define T 37 +#define C 0xFFF7EEE000000000ULL +#define L 43 +#define F 6364136223846793005 + +#define MASK_LOW ((1ULL << R) - 1) +#define MASK_UPP (~MASK_LOW) + +static uint64_t state[N] = {0}; +static uint32_t index = N + 1; + +// :god: +static uint64_t _random_get_seed(void) { + int *k = kmalloc(sizeof(int)); + int dk = *k; + kfree(k); + return (timer_count() ^ dk); +} + +uint64_t (*random_get_seed)(void) = _random_get_seed; + +static ssize_t randdev_read(struct resource *this, + struct f_description *description, void *buf, + off_t offset, size_t count) { + (void)this; + (void)description; + (void)offset; + + uint64_t *buffer = buf; + + for (size_t i = 0; i < count / sizeof(uint64_t); i++) { + buffer[i] = random(); + } + + return count; +} + +static ssize_t randdev_write(struct resource *this, + struct f_description *description, const void *buf, + off_t offset, size_t count) { + (void)this; + (void)description; + (void)buf; + (void)offset; + return count; +} + +static void twist(void) { + for (uint32_t i = 0; i < N; ++i) { + uint64_t x = (state[i] & MASK_UPP) + (state[(i + 1) % N] & MASK_LOW); + uint64_t xa = x >> 1; + if (x & 1) { + xa ^= A; + } + state[i] = state[(i + M) % N] ^ xa; + } + index = 0; +} + +void randdev_init(void) { + struct resource *randdev = resource_create(sizeof(struct resource)); + randdev->read = randdev_read; + randdev->write = randdev_write; + randdev->stat.st_size = 0; + randdev->stat.st_blocks = 0; + randdev->stat.st_blksize = 4096; + randdev->stat.st_rdev = resource_create_dev_id(); + randdev->stat.st_mode = 0666 | S_IFCHR; + devtmpfs_add_device(randdev, "urandom"); + vfs_symlink(vfs_root, "/dev/urandom", "/dev/random"); +} + +uint64_t random(void) { + if (index >= N) { + if (index > N) { + random_set_seed((*random_get_seed)()); + } + twist(); + } + + uint64_t y = state[index]; + y ^= (y >> U) & D; + y ^= (y << S) & B; + y ^= (y << T) & C; + y ^= y >> L; + + ++index; + return y; +} + +void random_set_seed(uint64_t seed) { + state[0] = seed; + index = N; + for (uint32_t i = 1; i < N; ++i) { + state[i] = F * (state[i - 1] ^ (state[i - 1] >> (W - 2))) + i; + } +} \ No newline at end of file diff --git a/src/libk/random.h b/src/libk/random.h new file mode 100644 index 0000000..51c9a8e --- /dev/null +++ b/src/libk/random.h @@ -0,0 +1,9 @@ +#pragma once +#include +#include + +extern uint64_t (*random_get_seed)(void); + +void randdev_init(void); +uint64_t random(void); +void random_set_seed(uint64_t seed); \ No newline at end of file diff --git a/src/libk/resource.c b/src/libk/resource.c new file mode 100644 index 0000000..db3323f --- /dev/null +++ b/src/libk/resource.c @@ -0,0 +1,638 @@ +#include "resource.h" +#include +#include +#include +#include "types.h" +#include "libk/errno.h" +#include "fs/elf.h" +#include "sched/sched_types.h" +#include "sched/sched.h" +#include "mp/mp.h" +#include "mp/spinlock.h" +#include "sched/syscall.h" +#include "fs/vfs.h" +#include "time.h" + +#define FIONCLEX 0x5450 +#define FIOCLEX 0x5451 +#define FIOASYNC 0x5452 + +int resource_default_ioctl(struct resource *this, + struct f_description *description, uint64_t request, + uint64_t arg) { + (void)this; + (void)description; + (void)arg; + + switch (request) { + case TCGETS: + case TCSETS: + case TIOCSCTTY: + case TIOCGWINSZ: + errno = ENOTTY; + return -1; + case FIONCLEX: + case FIOCLEX: + case FIOASYNC: + return 0; + } + + errno = EINVAL; + return -1; +} + +static ssize_t stub_read(struct resource *this, + struct f_description *description, void *buf, + off_t offset, size_t count) { + (void)this; + (void)description; + (void)buf; + (void)offset; + (void)count; + errno = ENOSYS; + return -1; +} + +static ssize_t stub_write(struct resource *this, + struct f_description *description, const void *buf, + off_t offset, size_t count) { + (void)this; + (void)description; + (void)buf; + (void)offset; + (void)count; + errno = ENOSYS; + return -1; +} + +static void *stub_mmap(struct resource *this, size_t file_page, int flags) { + (void)this; + (void)file_page; + (void)flags; + return NULL; +} + +static bool stub_ref(struct resource *this, struct f_description *description) { + (void)this; + (void)description; + this->refcount++; + return true; +} + +static bool stub_unref(struct resource *this, + struct f_description *description) { + (void)this; + (void)description; + this->refcount--; + return true; +} + +static bool stub_truncate(struct resource *this, + struct f_description *description, size_t length) { + (void)this; + (void)description; + (void)length; + errno = ENOSYS; + return false; +} + +void *resource_create(size_t size) { + struct resource *res = kmalloc(size); + if (res == NULL) { + return NULL; + } + + memzero(res, size); + + res->read = stub_read; + res->write = stub_write; + res->ioctl = resource_default_ioctl; + res->mmap = stub_mmap; + res->ref = stub_ref; + res->unref = stub_unref; + res->truncate = stub_truncate; + return res; +} + +dev_t resource_create_dev_id(void) { + static dev_t dev_id_counter = 1; + static spinlock_t lock = {0}; + spinlock_acquire_or_wait(&lock); + dev_t ret = dev_id_counter++; + spinlock_drop(&lock); + return ret; +} + +bool fdnum_close(struct process *proc, int fdnum) { + if (proc == NULL) { + proc = sched_get_running_thread()->mother_proc; + } + + bool ok = false; + spinlock_acquire_or_wait(&proc->fds_lock); + + if (fdnum < 0 || fdnum >= MAX_FDS) { + errno = EBADF; + goto cleanup; + } + + struct f_descriptor *fd = proc->fds[fdnum]; + if (fd == NULL) { + errno = EBADF; + goto cleanup; + } + + fd->description->res->unref(fd->description->res, fd->description); + + if (fd->description->refcount < 1) { + kfree(fd->description); + } + + kfree(fd); + + ok = true; + proc->fds[fdnum] = NULL; + +cleanup: + spinlock_drop(&proc->fds_lock); + return ok; +} + +int fdnum_create_from_fd(struct process *proc, struct f_descriptor *fd, + int old_fdnum, bool specific) { + if (proc == NULL) { + proc = sched_get_running_thread()->mother_proc; + } + + int res = -1; + spinlock_acquire_or_wait(&proc->fds_lock); + + if (old_fdnum < 0 || old_fdnum >= MAX_FDS) { + errno = EBADF; + goto cleanup; + } + + if (!specific) { + for (int i = old_fdnum; i < MAX_FDS; i++) { + if (proc->fds[i] == NULL) { + proc->fds[i] = fd; + res = i; + goto cleanup; + } + } + } else { + // TODO: Close an existing descriptor without deadlocking :^) + // fdnum_close(proc, old_fdnum); + proc->fds[old_fdnum] = fd; + res = old_fdnum; + } + +cleanup: + spinlock_drop(&proc->fds_lock); + return res; +} + +int fdnum_create_from_resource(struct process *proc, struct resource *res, + int flags, int old_fdnum, bool specific) { + struct f_descriptor *fd = fd_create_from_resource(res, flags); + if (fd == NULL) { + errno = ENOMEM; + return -1; + } + + return fdnum_create_from_fd(proc, fd, old_fdnum, specific); +} + +int fdnum_dup(struct process *old_proc, int old_fdnum, struct process *new_proc, + int new_fdnum, int flags, bool specific, bool cloexec) { + if (old_proc == NULL) { + old_proc = sched_get_running_thread()->mother_proc; + } + + if (new_proc == NULL) { + new_proc = sched_get_running_thread()->mother_proc; + } + + if (specific && old_fdnum == new_fdnum && old_proc == new_proc) { + errno = EINVAL; + return -1; + } + + struct f_descriptor *old_fd = fd_from_fdnum(old_proc, old_fdnum); + if (old_fd == NULL) { + return -1; + } + + struct f_descriptor *new_fd = kmalloc(sizeof(struct f_descriptor)); + if (new_fd == NULL) { + errno = ENOMEM; + return -1; + } + + memcpy(new_fd, old_fd, sizeof(struct f_descriptor)); + + new_fdnum = fdnum_create_from_fd(new_proc, new_fd, new_fdnum, specific); + if (new_fdnum < 0) { + kfree(new_fd); + return -1; + } + + new_fd->flags = flags & FILE_DESCRIPTOR_FLAGS_MASK; + if (cloexec) { + new_fd->flags &= O_CLOEXEC; + } + + old_fd->description->refcount++; + old_fd->description->res->refcount++; + + return new_fdnum; +} + +struct f_descriptor *fd_create_from_resource(struct resource *res, int flags) { + struct f_description *description = kmalloc(sizeof(struct f_description)); + if (description == NULL) { + goto fail; + } + + memzero(description, sizeof(struct f_description)); + + description->refcount = 1; + description->flags = flags & FILE_STATUS_FLAGS_MASK; + spinlock_init(description->lock); + description->res = res; + + struct f_descriptor *fd = kmalloc(sizeof(struct f_descriptor)); + if (fd == NULL) { + goto fail; + } + + res->ref(res, description); + fd->description = description; + fd->flags = flags & FILE_DESCRIPTOR_FLAGS_MASK; + return fd; + +fail: + if (description != NULL) { + kfree(description); + } + return NULL; +} + +struct f_descriptor *fd_from_fdnum(struct process *proc, int fdnum) { + if (proc == NULL) { + proc = sched_get_running_thread()->mother_proc; + } + + struct f_descriptor *ret = NULL; + spinlock_acquire_or_wait(&proc->fds_lock); + + if (fdnum < 0 || fdnum >= MAX_FDS) { + errno = EBADF; + goto cleanup; + } + + ret = proc->fds[fdnum]; + if (ret == NULL) { + errno = EBADF; + goto cleanup; + } + + // ret->description->refcount++; + +cleanup: + spinlock_drop(&proc->fds_lock); + return ret; +} + +void syscall_close(struct syscall_arguments *args) { + struct process *proc = sched_get_running_thread()->mother_proc; + + args->ret = fdnum_close(proc, args->args0) ? 0 : -1; +} + +void syscall_read(struct syscall_arguments *args) { + struct process *proc = sched_get_running_thread()->mother_proc; + + int fdnum = args->args0; + void *buf = (void *)args->args1; + size_t count = args->args2; + + struct f_descriptor *fd = fd_from_fdnum(proc, fdnum); + if (fd == NULL) { + args->ret = -1; + return; + } + + struct f_description *description = fd->description; + struct resource *res = description->res; + + ssize_t read = res->read(res, description, buf, description->offset, count); + if (read < 0) { + args->ret = -1; + return; + } + + description->offset += read; + args->ret = read; +} + +void syscall_write(struct syscall_arguments *args) { + struct process *proc = sched_get_running_thread()->mother_proc; + + int fdnum = args->args0; + void *buf = (void *)args->args1; + size_t count = args->args2; + + struct f_descriptor *fd = fd_from_fdnum(proc, fdnum); + if (fd == NULL) { + args->ret = -1; + return; + } + + struct f_description *description = fd->description; + struct resource *res = description->res; + + ssize_t written = + res->write(res, description, buf, description->offset, count); + if (written < 0) { + args->ret = -1; + return; + } + + description->offset += written; + args->ret = written; +} + +void syscall_seek(struct syscall_arguments *args) { + struct process *proc = sched_get_running_thread()->mother_proc; + + int fdnum = args->args0; + off_t offset = args->args1; + int whence = args->args2; + + struct f_descriptor *fd = fd_from_fdnum(proc, fdnum); + if (fd == NULL) { + args->ret = -1; + return; + } + + struct f_description *description = fd->description; + switch (description->res->stat.st_mode & S_IFMT) { + case S_IFCHR: + case S_IFIFO: + case S_IFSOCK: + errno = ESPIPE; + args->ret = -1; + return; + } + + off_t curr_offset = description->offset; + off_t new_offset = 0; + + switch (whence) { + case SEEK_CUR: + new_offset = curr_offset + offset; + break; + case SEEK_END: + new_offset = offset + description->res->stat.st_size; + break; + case SEEK_SET: + new_offset = offset; + break; + default: + errno = EINVAL; + args->ret = -1; + return; + } + + if (new_offset < 0) { + errno = EINVAL; + args->ret = -1; + return; + } + + // TODO: Implement res->grow + // if (new_offset >= fd->description->res->stat.st_size) { + // description->res->grow(description->res, new_offset); + // } + + description->offset = new_offset; + args->ret = new_offset; +} + +void syscall_fcntl(struct syscall_arguments *args) { + struct process *proc = sched_get_running_thread()->mother_proc; + + int fdnum = args->args0; + uint64_t request = args->args1; + uint64_t arg = args->args2; + + struct f_descriptor *fd = fd_from_fdnum(proc, fdnum); + if (fd == NULL) { + args->ret = -1; + return; + } + + switch (request) { + case F_DUPFD: + args->ret = fdnum_dup(proc, fdnum, proc, (int)arg, 0, false, false); + return; + case F_DUPFD_CLOEXEC: + args->ret = fdnum_dup(proc, fdnum, proc, (int)arg, 0, false, true); + return; + case F_GETFD: + if ((fd->flags & O_CLOEXEC) != 0) { + args->ret = O_CLOEXEC; + return; + } else { + args->ret = 0; + return; + } + case F_SETFD: + if ((arg & O_CLOEXEC) != 0) { + fd->flags = O_CLOEXEC; + } else { + fd->flags = 0; + } + args->ret = 0; + return; + case F_GETFL: + args->ret = fd->description->flags; + return; + case F_SETFL: + fd->description->flags = (int)arg; + args->ret = 0; + return; + default: + errno = EINVAL; + args->ret = -1; + return; + } +} + +void syscall_ioctl(struct syscall_arguments *args) { + struct process *proc = sched_get_running_thread()->mother_proc; + + int fdnum = args->args0; + uint64_t request = args->args1; + uint64_t arg = args->args2; + + struct f_descriptor *fd = fd_from_fdnum(proc, fdnum); + if (fd == NULL) { + args->ret = -1; + return; + } + + struct f_description *description = fd->description; + struct resource *res = description->res; + args->ret = res->ioctl(res, description, request, arg); +} + +void syscall_dup3(struct syscall_arguments *args) { + struct process *proc = sched_get_running_thread()->mother_proc; + + int old_fdnum = args->args0; + int new_fdnum = args->args1; + int flags = args->args2; + + args->ret = fdnum_dup(proc, old_fdnum, proc, new_fdnum, flags, true, false); +} + +void syscall_fchmodat(struct syscall_arguments *args) { + int dir_fdnum = args->args0; + const char *path = (char *)args->args1; + mode_t mode = args->args2; + + struct vfs_node *parent = NULL, *node = NULL; + if (!vfs_fdnum_path_to_node(dir_fdnum, path, true, true, &parent, &node, + NULL)) { + args->ret = -1; + return; + } + + struct vfs_node *target = node; + if (target == NULL) { + target = parent; + } + + target->resource->stat.st_mode &= ~0777; + target->resource->stat.st_mode |= mode & 0777; + args->ret = 0; +} + +void syscall_ppoll(struct syscall_arguments *args) { + struct process *proc = sched_get_running_thread()->mother_proc; + struct pollfd *pfds = (struct pollfd *)(args->args0); + uint32_t nfds = (uint32_t)(args->args1); + struct timespec *timeout = (struct timespec *)(args->args2); + // sigmask is unused rn since we dont implement signals + + int ret = 0; + int fd_count = 0; + int event_count = 0; + struct timer *timer = NULL; + + if (nfds == 0) { + goto end; + } + + if (nfds > EVENT_MAX_LISTENERS) { + errno = EINVAL; + ret = -1; + goto end; + } + + int fd_num_list[MAX_EVENTS] = {0}; + struct f_description *fd_list[MAX_EVENTS] = {0}; + struct event *events[MAX_EVENTS] = {0}; + + for (uint32_t i = 0; i < nfds; i++) { + struct pollfd *pfd = &pfds[i]; + pfd->revents = 0; + if (pfd->fd < 0) { + continue; + } + struct f_descriptor *fd = fd_from_fdnum(proc, pfd->fd); + if (fd == NULL) { + pfd->revents = POLLNVAL; + ret++; + continue; + } + struct f_description *description = fd->description; + struct resource *res = description->res; + int status = res->status; + if (((uint16_t)status & pfd->events) != 0) { + pfd->revents = (uint16_t)status & pfd->events; + ret++; + description->refcount--; + continue; + } + fd_list[fd_count] = description; + fd_num_list[fd_count++] = i; + events[event_count++] = &res->event; + } + + if (ret) { + goto end; + } + + if (timeout != NULL) { + timer = timer_new(*timeout); + if (timer == NULL) { + errno = ENOMEM; + ret = -1; + goto end; + } + + events[event_count++] = &timer->event; + } + + for (;;) { + ssize_t which = event_await(events, event_count, true); + if (which == -1) { + ret = -1; + errno = EINTR; + goto end; + } + + if (timer != NULL && which == event_count - 1) { + ret = 0; + goto end; + } + + struct pollfd *pollfd = &pfds[fd_num_list[which]]; + struct f_description *fd = fd_list[which]; + struct resource *res = fd->res; + + int status = res->status; + if (((uint16_t)status & pollfd->events) != 0) { + pollfd->revents = (uint16_t)status & pollfd->events; + ret++; + break; + } + } + +end: + for (int i = 0; i < fd_count; i++) { + fd_list[i]->refcount--; + } + + if (timer != NULL) { + timer_disarm(timer); + kfree(timer); + } + + args->ret = ret; +} + +void syscall_rmdir(struct syscall_arguments *args) { + const char *path = (const char *)args->args0; + + struct syscall_arguments unlink_args = {0}; + unlink_args.args0 = AT_FDCWD; + unlink_args.args1 = (uint64_t)path; + unlink_args.args2 = AT_REMOVEDIR; + + syscall_unlinkat(&unlink_args); + args->ret = unlink_args.ret; +} diff --git a/src/libk/resource.h b/src/libk/resource.h new file mode 100644 index 0000000..0df5e46 --- /dev/null +++ b/src/libk/resource.h @@ -0,0 +1,87 @@ +#pragma once +#include +#include +#include +#include "mp/spinlock.h" +#include "types.h" +#include "event.h" +#include "sched/syscall.h" + +struct process; +struct f_description; + +struct resource { + int status; + struct event event; + size_t refcount; + spinlock_t lock; + struct stat stat; + bool can_mmap; + + ssize_t (*read)(struct resource *this, struct f_description *description, + void *buf, off_t offset, size_t count); + ssize_t (*write)(struct resource *this, struct f_description *description, + const void *buf, off_t offset, size_t count); + int (*ioctl)(struct resource *this, struct f_description *description, + uint64_t request, uint64_t arg); + void *(*mmap)(struct resource *this, size_t file_page, int flags); + bool (*ref)(struct resource *this, struct f_description *description); + bool (*unref)(struct resource *this, struct f_description *description); + bool (*truncate)(struct resource *this, struct f_description *description, + size_t length); +}; + +struct f_description { + size_t refcount; + off_t offset; + bool is_dir; + int flags; + spinlock_t lock; + struct resource *res; + struct vfs_node *node; +}; + +struct f_descriptor { + struct f_description *description; + int flags; +}; + +struct pollfd { + int fd; + short events; + short revents; +}; + +void *resource_create(size_t size); +dev_t resource_create_dev_id(void); + +bool fdnum_close(struct process *proc, int fdnum); +int fdnum_create_from_fd(struct process *proc, struct f_descriptor *fd, + int old_fdnum, bool specific); +int fdnum_create_from_resource(struct process *proc, struct resource *res, + int flags, int old_fdnum, bool specific); +int fdnum_dup(struct process *old_proc, int old_fdnum, struct process *new_proc, + int new_fdnum, int flags, bool specific, bool cloexec); +struct f_descriptor *fd_create_from_resource(struct resource *res, int flags); +struct f_descriptor *fd_from_fdnum(struct process *proc, int fdnum); + +int resource_default_ioctl(struct resource *this, + struct f_description *description, uint64_t request, + uint64_t arg); + +#define FILE_CREATION_FLAGS_MASK \ + (O_CREAT | O_DIRECTORY | O_EXCL | O_NOCTTY | O_NOFOLLOW | O_TRUNC) +#define FILE_DESCRIPTOR_FLAGS_MASK (O_CLOEXEC) +#define FILE_STATUS_FLAGS_MASK \ + (~(FILE_CREATION_FLAGS_MASK | FILE_DESCRIPTOR_FLAGS_MASK)) + +void syscall_close(struct syscall_arguments *args); +void syscall_read(struct syscall_arguments *args); +void syscall_write(struct syscall_arguments *args); +void syscall_seek(struct syscall_arguments *args); +void syscall_fcntl(struct syscall_arguments *args); +void syscall_ioctl(struct syscall_arguments *args); +void syscall_dup3(struct syscall_arguments *args); +void syscall_fchmodat(struct syscall_arguments *args); +void syscall_ppoll(struct syscall_arguments *args); +void syscall_rmdir(struct syscall_arguments *args); \ No newline at end of file diff --git a/src/libk/stdio.c b/src/libk/stdio.c deleted file mode 100644 index 8fd3e62..0000000 --- a/src/libk/stdio.c +++ /dev/null @@ -1,373 +0,0 @@ -#include -#include -#include "stdio.h" -#include "debug.h" -#include "drivers/video/render.h" -#include "arch/x86_64/sys/e9.h" -#include "mp/spinlock.h" -#include -#include - -static const uint32_t g_LogSeverityColors[] = -{ - [LVL_DEBUG] = 0xAAAAAA, // light gray - [LVL_INFO] = 0xFFFFFF, // white - [LVL_WARN] = 0xFFFF00, // yellow - [LVL_ERROR] = 0xFF0000, // red - [LVL_CRITICAL] = 0xFFFFFF, // white -}; - -static spinlock_t s_printf_lock = SPINLOCK_INIT; - -typedef struct { - char* buf; - size_t size; - size_t pos; -} snprintf_ctx_t; - -static void buf_putc(snprintf_ctx_t* ctx, char c) -{ - if (ctx->pos + 1 < ctx->size) { - ctx->buf[ctx->pos] = c; - } - ctx->pos++; -} - -static void buf_puts(snprintf_ctx_t* ctx, const char* str) -{ - while (*str) { - buf_putc(ctx, *str++); - } -} - -void fputc(char c) -{ - putchar(c); - e9_putc(c); -} - -void fputs(const char* str) -{ - const char* s = str; - while (*s) fputc(*s++); -} - -void fputs_colored(const char* str, uint32_t color) { - while (*str) putchar_colored(*str++, color); - while (*str) fputc(*str++); -} - -const char g_HexChars[] = "0123456789abcdef"; - -static void buf_print_unsigned(snprintf_ctx_t* ctx, uint64_t number, int radix, int width, int zero_pad) -{ - char buffer[32]; - int pos = 0; - - do { - buffer[pos++] = g_HexChars[number % radix]; - number /= radix; - } while (number > 0); - - // Apply zero-padding / width - if (zero_pad) { - while (pos < width) - buffer[pos++] = '0'; - } - - while (--pos >= 0) - buf_putc(ctx, buffer[pos]); -} - -static void buf_print_signed(snprintf_ctx_t* ctx, int64_t number, int radix, int width, int zero_pad) -{ - if (number < 0) { - buf_putc(ctx, '-'); - buf_print_unsigned(ctx, (uint64_t)(-number), radix, width, zero_pad); - } else { - buf_print_unsigned(ctx, (uint64_t)number, radix, width, zero_pad); - } -} - - - -int snprintf(char* buf, size_t size, const char* fmt, ...) -{ - va_list args; - va_start(args, fmt); - int ret = vsnprintf(buf, size, fmt, args); - va_end(args); - return ret; -} - -static void fprintf_unsigned(uint64_t number, int radix, int width, int zero_pad) -{ - char buffer[32]; - int pos = 0; - - do { - buffer[pos++] = g_HexChars[number % radix]; - number /= radix; - } while (number > 0); - - if (zero_pad) { - while (pos < width) - buffer[pos++] = '0'; - } - - while (--pos >= 0) - fputc(buffer[pos]); -} - -static void fprintf_signed(int64_t number, int radix, int width, int zero_pad) -{ - if (number < 0) { - fputc('-'); - fprintf_unsigned((uint64_t)(-number), radix, width, zero_pad); - } else { - fprintf_unsigned((uint64_t)number, radix, width, zero_pad); - } -} - - -void vfprintf(const char* fmt, va_list args) -{ - while (*fmt) { - if (*fmt != '%') { - fputc(*fmt++); - continue; - } - fmt++; // skip % - - /* flags */ - int zero_pad = 0; - if (*fmt == '0') { - zero_pad = 1; - fmt++; - } - - /* width */ - int width = 0; - while (*fmt >= '0' && *fmt <= '9') { - width = width * 10 + (*fmt++ - '0'); - } - - /* precision */ - int precision = -1; - if (*fmt == '.') { - fmt++; - precision = 0; - while (*fmt >= '0' && *fmt <= '9') { - precision = precision * 10 + (*fmt++ - '0'); - } - } - - /* length modifier */ - int is_long_long = 0; - if (*fmt == 'l') { - fmt++; - if (*fmt == 'l') { - is_long_long = 1; - fmt++; - } - } else if (*fmt == 'z') { // %zu, %zx - is_long_long = 1; - fmt++; - } else if (*fmt == 'h') { - fmt++; // ignore h/hh - } - - /* specifier */ - switch (*fmt++) { - case 'c': - fputc((char)va_arg(args, int)); - break; - - case 's': { - const char* s = va_arg(args, const char*); - if (!s) s = "(null)"; - if (precision >= 0) { - while (*s && precision--) fputc(*s++); - } else { - fputs(s); - } - break; - } - - case 'd': - case 'i': - if (is_long_long) - fprintf_signed(va_arg(args, long long), 10, width, zero_pad); - else - fprintf_signed(va_arg(args, long), 10, width, zero_pad); - break; - - case 'u': - if (is_long_long) - fprintf_unsigned(va_arg(args, unsigned long long), 10, width, zero_pad); - else - fprintf_unsigned(va_arg(args, unsigned long), 10, width, zero_pad); - break; - - case 'x': - case 'X': - if (is_long_long) - fprintf_unsigned(va_arg(args, unsigned long long), 16, width, zero_pad); - else - fprintf_unsigned(va_arg(args, unsigned long), 16, width, zero_pad); - break; - - case 'p': - fputs("0x"); - fprintf_unsigned(va_arg(args, uint64_t), 16, 16, 1); // always full 64-bit - break; - - case 'o': - if (is_long_long) - fprintf_unsigned(va_arg(args, unsigned long long), 8, width, zero_pad); - else - fprintf_unsigned(va_arg(args, unsigned long), 8, width, zero_pad); - break; - - case '%': - fputc('%'); - break; - - default: - fputc('%'); - fputc(*(fmt - 1)); - break; - } - } -} - -int vsnprintf(char* buf, size_t size, const char* fmt, va_list args) -{ - snprintf_ctx_t ctx = { .buf = buf, .size = size, .pos = 0 }; - - while (*fmt) { - if (*fmt != '%') { - buf_putc(&ctx, *fmt++); - continue; - } - fmt++; - - int zero_pad = 0; - if (*fmt == '0') { zero_pad = 1; fmt++; } - - int width = 0; - while (*fmt >= '0' && *fmt <= '9') { - width = width * 10 + (*fmt++ - '0'); - } - - int precision = -1; - if (*fmt == '.') { - fmt++; - precision = 0; - while (*fmt >= '0' && *fmt <= '9') { - precision = precision * 10 + (*fmt++ - '0'); - } - } - - int is_long_long = 0; - if (*fmt == 'l') { - fmt++; - if (*fmt == 'l') { is_long_long = 1; fmt++; } - } else if (*fmt == 'z') { - is_long_long = 1; - fmt++; - } else if (*fmt == 'h') { - fmt++; - } - - switch (*fmt++) { - case 'c': - buf_putc(&ctx, (char)va_arg(args, int)); - break; - - case 's': { - const char* s = va_arg(args, const char*); - if (!s) s = "(null)"; - int max = (precision >= 0) ? precision : INT_MAX; - while (*s && max--) buf_putc(&ctx, *s++); - break; - } - - case 'd': - case 'i': - if (is_long_long) - buf_print_signed(&ctx, va_arg(args, long long), 10, width, zero_pad); - else - buf_print_signed(&ctx, va_arg(args, long), 10, width, zero_pad); - break; - - case 'u': - if (is_long_long) - buf_print_unsigned(&ctx, va_arg(args, unsigned long long), 10, width, zero_pad); - else - buf_print_unsigned(&ctx, va_arg(args, unsigned long), 10, width, zero_pad); - break; - - case 'x': - case 'X': - if (is_long_long) - buf_print_unsigned(&ctx, va_arg(args, unsigned long long), 16, width, zero_pad); - else - buf_print_unsigned(&ctx, va_arg(args, unsigned long), 16, width, zero_pad); - break; - - case 'p': - buf_puts(&ctx, "0x"); - buf_print_unsigned(&ctx, va_arg(args, uint64_t), 16, 16, 1); - break; - - case 'o': - if (is_long_long) - buf_print_unsigned(&ctx, va_arg(args, unsigned long long), 8, width, zero_pad); - else - buf_print_unsigned(&ctx, va_arg(args, unsigned long), 8, width, zero_pad); - break; - - case '%': - buf_putc(&ctx, '%'); - break; - - default: - buf_putc(&ctx, '%'); - buf_putc(&ctx, *(fmt - 1)); - break; - } - } - - if (ctx.size > 0) { - if (ctx.pos < ctx.size) - ctx.buf[ctx.pos] = '\0'; - else - ctx.buf[ctx.size - 1] = '\0'; - } - - return (int)ctx.pos; -} - -void printf(const char* fmt, ...) -{ - uint64_t flags; - spinlock_acquire_irqsave(&s_printf_lock, &flags); - - va_list args; - va_start(args, fmt); - - vfprintf(fmt, args); - - va_end(args); - - spinlock_release_irqrestore(&s_printf_lock, flags); -} - -void fprintf(const char* fmt, ...) -{ - va_list args; - va_start(args, fmt); - vfprintf(fmt, args); - va_end(args); -} diff --git a/src/libk/stdio.h b/src/libk/stdio.h deleted file mode 100644 index c089248..0000000 --- a/src/libk/stdio.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once -#include -#include -#include - -void printf(const char* fmt, ...); -void fputs(const char* str); -void fputc(char c); -void vfprintf(const char* fmt, va_list args); -void fprintf(const char* fmt, ...); -void fputs_colored(const char* str, uint32_t color); -int snprintf(char* buf, size_t size, const char* fmt, ...); -int vsnprintf(char* buf, size_t size, const char* fmt, va_list args); \ No newline at end of file diff --git a/src/libk/string.c b/src/libk/string.c index 863b2dd..5be1012 100644 --- a/src/libk/string.c +++ b/src/libk/string.c @@ -1,6 +1,8 @@ -#include "string.h" +#include "libk/string.h" #include #include +#include "mm/slab.h" +#include "mm/memory.h" const char* strchr(const char* str, char chr) { @@ -243,4 +245,128 @@ char* codepoint_to_utf8(int codepoint, char* stringOutput) *stringOutput++ = 0x80 | (codepoint & 0x3F); } return stringOutput; -} \ No newline at end of file +} + +char *ltoa(int64_t value, char *str, int base) { + char *rc; + char *ptr; + char *low; + // Check for supported base. + if (base < 2 || base > 36) { + *str = '\0'; + return str; + } + rc = ptr = str; + // Set '-' for negative decimals. + if (value < 0 && base == 10) { + *ptr++ = '-'; + } + // Remember where the numbers start. + low = ptr; + // The actual conversion. + do { + // Modulo is negative for negative value. This trick makes abs() + // unnecessary. + *ptr++ = "zyxwvutsrqponmlkjihgfedcba9876543210123456789abcdefghijklmnop" + "qrstuvwxyz"[35 + value % base]; + value /= base; + } while (value); + // Terminating the string. + *ptr-- = '\0'; + // Invert the numbers. + while (low < ptr) { + char tmp = *low; + *low++ = *ptr; + *ptr-- = tmp; + } + return rc; +} + +char *ultoa(uint64_t value, char *str, int base) { + char *rc; + char *ptr; + char *low; + // Check for supported base. + if (base < 2 || base > 36) { + *str = '\0'; + return str; + } + rc = ptr = str; + // Remember where the numbers start. + low = ptr; + // The actual conversion. + do { + *ptr++ = "0123456789abcdefghijklmnop" + "qrstuvwxyz"[value % base]; + value /= base; + } while (value); + // Terminating the string. + *ptr-- = '\0'; + // Invert the numbers. + while (low < ptr) { + char tmp = *low; + *low++ = *ptr; + *ptr-- = tmp; + } + return rc; +} + + +int64_t atol(const char *string) { + long num = 0; + int i = 0, sign = 1; + + // skip white space characters + while (string[i] == ' ' || string[i] == '\n' || string[i] == '\t') { + i++; + } + + // note sign of the number + if (string[i] == '+' || string[i] == '-') { + if (string[i] == '-') { + sign = -1; + } + i++; + } + + // run till the end of the string is reached, or the + // current character is non-numeric + while (string[i] && (string[i] >= '0' && string[i] <= '9')) { + num = num * 10 + (string[i] - '0'); + i++; + } + + return sign * num; +} + +size_t strsplit(const char *txt, char delim, char ***tokens) { + size_t *tklen, *t, count = 1; + char **arr, *p = (char *)txt; + + while (*p != '\0') + if (*p++ == delim) + count += 1; + t = tklen = kcalloc(count, sizeof(size_t)); + for (p = (char *)txt; *p != '\0'; p++) + *p == delim ? *t++ : (*t)++; + *tokens = arr = kmalloc(count * sizeof(char *)); + t = tklen; + p = *arr++ = kcalloc(*(t++) + 1, sizeof(char *)); + while (*txt != '\0') { + if (*txt == delim) { + p = *arr++ = kcalloc(*(t++) + 1, sizeof(char *)); + txt++; + } else + *p++ = *txt++; + } + kfree(tklen); + return count; +} + +char *strdup(const char *s) { + size_t l = strlen(s); + char *d = kmalloc(l + 1); + if (!d) + return NULL; + return memcpy(d, s, l + 1); +} diff --git a/src/libk/string.h b/src/libk/string.h index 0db6f7b..0422ffd 100644 --- a/src/libk/string.h +++ b/src/libk/string.h @@ -1,5 +1,6 @@ #pragma once #include +#include const char* strchr(const char* str, char chr); char* strcpy(char* dst, const char* src); @@ -10,4 +11,10 @@ char* strcat(char* dst, const char* src); int strncmp(const char* a, const char* b, size_t n); wchar_t* utf16_to_codepoint(wchar_t* string, int* codepoint); char* codepoint_to_utf8(int codepoint, char* stringOutput); -size_t strnlen(const char* str, size_t maxlen); \ No newline at end of file +size_t strnlen(const char* str, size_t maxlen); +char *ltoa(int64_t value, char *str, int base); +char *ultoa(uint64_t value, char *str, int base); +size_t strsplit(const char *txt, char delim, char ***tokens); +int64_t atol(const char *string); +char *strdup(const char *s); +size_t strsplit(const char *txt, char delim, char ***tokens); \ No newline at end of file diff --git a/src/libk/time.c b/src/libk/time.c new file mode 100644 index 0000000..02f2975 --- /dev/null +++ b/src/libk/time.c @@ -0,0 +1,178 @@ +#include "time.h" +#include +#include +#include +#include "mp/spinlock.h" +#include "vec.h" +#include "mm/slab.h" +#include "mm/vmm.h" +#include "sched/syscall.h" +#include "errno.h" + + + +struct timespec time_monotonic = {0, 0}; +struct timespec time_realtime = {0, 0}; + +static spinlock_t timers_lock = {0}; +static vec_t(struct timer *) armed_timers = {0}; + +struct timer *timer_new(struct timespec when) { + struct timer *timer = kmalloc(sizeof(struct timer)); + if (timer == NULL) { + return NULL; + } + + timer->when = when; + timer->fired = false; + timer->index = -1; + + timer_arm(timer); + return timer; +} + +void timer_arm(struct timer *timer) { + spinlock_acquire_or_wait(&timers_lock); + + timer->index = armed_timers.length; + timer->fired = false; + + vec_push(&armed_timers, timer); + spinlock_drop(&timers_lock); +} + +void timer_disarm(struct timer *timer) { + spinlock_acquire_or_wait(&timers_lock); + + if (armed_timers.length == 0 || timer->index == -1 || + timer->index >= armed_timers.length) { + goto cleanup; + } + + armed_timers.data[timer->index] = + armed_timers.data[armed_timers.length - 1]; + armed_timers.data[timer->index]->index = timer->index; + vec_splice(&armed_timers, armed_timers.length - 1, 1); + + timer->index = -1; + +cleanup: + spinlock_drop(&timers_lock); +} + +bool timer_handler(uint64_t ns) { + if (spinlock_acquire(&timers_lock)) { + struct timespec interval = {.tv_sec = ns / 1000000000, + .tv_nsec = ns % 1000000000}; + + time_monotonic = timespec_add(time_monotonic, interval); + time_realtime = timespec_add(time_realtime, interval); + + for (int i = 0; i < armed_timers.length; i++) { + struct timer *timer = armed_timers.data[i]; + if (timer->fired) { + continue; + } + + timer->when = timespec_sub(timer->when, interval); + if (timer->when.tv_sec == 0 && timer->when.tv_nsec == 0) { + event_trigger(&timer->event, false); + timer->fired = true; + } + } + spinlock_drop(&timers_lock); + return true; + } + return false; +} + +void time_nsleep(uint64_t ns) { + struct timespec duration = {.tv_sec = ns / 1000000000, + .tv_nsec = ns % 1000000000}; + struct timer *timer = NULL; + + timer = timer_new(duration); + if (timer == NULL) { + goto cleanup; + } + + struct event *events[] = {&timer->event}; + event_await(events, 1, true); + + timer_disarm(timer); + + kfree(timer); +cleanup: + return; +} + +void syscall_getclock(struct syscall_arguments *args) { + int which = args->args0; + struct timespec *out = (void *)args->args1; + int ret = -1; + + switch (which) { + case CLOCK_REALTIME: + case CLOCK_REALTIME_COARSE: + *out = time_realtime; + ret = 0; + goto cleanup; + case CLOCK_BOOTTIME: + case CLOCK_MONOTONIC: + case CLOCK_MONOTONIC_RAW: + case CLOCK_MONOTONIC_COARSE: + *out = time_monotonic; + ret = 0; + goto cleanup; + default: + break; + } + + errno = EINVAL; + +cleanup: + args->ret = ret; +} + +void syscall_nanosleep(struct syscall_arguments *args) { + struct timespec *duration = (struct timespec *)args->args0; + struct timespec *remaining = (struct timespec *)args->args1; + if (duration->tv_sec == 0 && duration->tv_nsec == 0) { + args->ret = 0; + return; + } + + if (duration->tv_nsec < 0 || duration->tv_nsec < 0 || + duration->tv_nsec > 1000000000) { + errno = EINVAL; + args->ret = -1; + return; + } + + struct timer *timer = timer_new(*duration); + if (timer == NULL) { + errno = ENOMEM; + args->ret = -1; + return; + } + + struct event *event = &timer->event; + + ssize_t which = event_await(&event, 1, true); + if (which == -1) { + if (remaining != NULL) { + *remaining = timer->when; + } + errno = EINTR; + timer_disarm(timer); + kfree(timer); + args->ret = -1; + return; + } + + timer_disarm(timer); + kfree(timer); + + args->ret = 0; + return; +} \ No newline at end of file diff --git a/src/libk/time.h b/src/libk/time.h new file mode 100644 index 0000000..f266b11 --- /dev/null +++ b/src/libk/time.h @@ -0,0 +1,72 @@ + +#include +#include +#include +#include "types.h" +#include "event.h" +#include "sched/syscall.h" + +#define TIMER_FREQ 1000 + +#define CLOCK_REALTIME 0 +#define CLOCK_MONOTONIC 1 +#define CLOCK_PROCESS_CPUTIME_ID 2 +#define CLOCK_THREAD_CPUTIME_ID 3 +#define CLOCK_MONOTONIC_RAW 4 +#define CLOCK_REALTIME_COARSE 5 +#define CLOCK_MONOTONIC_COARSE 6 +#define CLOCK_BOOTTIME 7 + +struct timer { + int index; + bool fired; + struct timespec when; + struct event event; +}; + +extern struct timespec time_monotonic; +extern struct timespec time_realtime; + +static inline struct timespec timespec_add(struct timespec a, + struct timespec b) { + if (a.tv_nsec + b.tv_nsec > 999999999) { + a.tv_nsec = (a.tv_nsec + b.tv_nsec) - 1000000000; + a.tv_sec++; + } else { + a.tv_nsec += b.tv_nsec; + } + a.tv_sec += b.tv_sec; + return a; +} + +static inline struct timespec timespec_sub(struct timespec a, + struct timespec b) { + if (b.tv_nsec > a.tv_nsec) { + a.tv_nsec = 999999999 - (b.tv_nsec - a.tv_nsec); + if (a.tv_sec == 0) { + a.tv_sec = a.tv_nsec = 0; + return a; + } + a.tv_sec--; + } else { + a.tv_nsec -= b.tv_nsec; + } + + if (b.tv_sec > a.tv_sec) { + a.tv_sec = a.tv_nsec = 0; + return a; + } + a.tv_sec -= b.tv_sec; + + return a; +} + +struct timer *timer_new(struct timespec when); +void timer_arm(struct timer *timer); +void timer_disarm(struct timer *timer); +bool timer_handler(uint64_t ns); +void time_nsleep(uint64_t ns); + +void time_init(void); +void syscall_getclock(struct syscall_arguments *args); +void syscall_nanosleep(struct syscall_arguments *args); \ No newline at end of file diff --git a/src/libk/types.h b/src/libk/types.h new file mode 100644 index 0000000..52b28a4 --- /dev/null +++ b/src/libk/types.h @@ -0,0 +1,254 @@ +#pragma once +#include + +#define NAME_MAX 255 + +typedef int64_t ssize_t; +typedef int64_t off_t; + +typedef uint64_t dev_t; +typedef uint64_t ino_t; +typedef unsigned int mode_t; +typedef unsigned long nlink_t; +typedef int64_t blksize_t; +typedef int64_t blkcnt_t; + +typedef int32_t pid_t; +typedef int32_t uid_t; +typedef int32_t gid_t; + + +typedef int64_t time_t; +typedef int64_t clockid_t; + +typedef uint32_t socklen_t; + +struct timespec { + time_t tv_sec; + long tv_nsec; +}; + + +#define O_PATH 010000000 + +#define O_ACCMODE (03 | O_PATH) +#define O_RDONLY 00 +#define O_WRONLY 01 +#define O_RDWR 02 + +#define O_CREAT 0100 +#define O_EXCL 0200 +#define O_NOCTTY 0400 +#define O_TRUNC 01000 +#define O_APPEND 02000 +#define O_NONBLOCK 04000 +#define O_DSYNC 010000 +#define O_ASYNC 020000 +#define O_DIRECT 040000 +#define O_DIRECTORY 0200000 +#define O_NOFOLLOW 0400000 +#define O_CLOEXEC 02000000 +#define O_SYNC 04010000 +#define O_RSYNC 04010000 +#define O_LARGEFILE 0100000 +#define O_NOATIME 01000000 +#define O_TMPFILE 020000000 + +#define O_EXEC O_PATH +#define O_SEARCH O_PATH + +#define F_DUPFD 0 +#define F_GETFD 1 +#define F_SETFD 2 +#define F_GETFL 3 +#define F_SETFL 4 + +#define F_SETOWN 8 +#define F_GETOWN 9 +#define F_SETSIG 10 +#define F_GETSIG 11 + +#define F_GETLK 5 +#define F_SETLK 6 +#define F_SETLKW 7 + +#define F_SETOWN_EX 15 +#define F_GETOWN_EX 16 + +#define F_GETOWNER_UIDS 17 + +#define F_DUPFD_CLOEXEC 1030 +#define F_ADD_SEALS 1033 +#define F_GET_SEALS 1034 + +#define F_SEAL_SEAL 0x0001 +#define F_SEAL_SHRINK 0x0002 +#define F_SEAL_GROW 0x0004 +#define F_SEAL_WRITE 0x0008 + +#define F_RDLCK 0 +#define F_WRLCK 1 +#define F_UNLCK 2 + +#define FD_CLOEXEC 1 + +#define AT_FDCWD -100 +#define AT_SYMLINK_NOFOLLOW 0x100 +#define AT_REMOVEDIR 0x200 +#define AT_SYMLINK_FOLLOW 0x400 +#define AT_EACCESS 0x200 +#define AT_EMPTY_PATH 0x1000 + +#define S_IFMT 0x0F000 +#define S_IFBLK 0x06000 +#define S_IFCHR 0x02000 +#define S_IFIFO 0x01000 +#define S_IFREG 0x08000 +#define S_IFDIR 0x04000 +#define S_IFLNK 0x0A000 +#define S_IFSOCK 0x0C000 + +#define S_IRWXU 0700 +#define S_IRUSR 0400 +#define S_IWUSR 0200 +#define S_IXUSR 0100 +#define S_IRWXG 070 +#define S_IRGRP 040 +#define S_IWGRP 020 +#define S_IXGRP 010 +#define S_IRWXO 07 +#define S_IROTH 04 +#define S_IWOTH 02 +#define S_IXOTH 01 +#define S_ISUID 04000 +#define S_ISGID 02000 +#define S_ISVTX 01000 + +#define S_IREAD S_IRUSR +#define S_IWRITE S_IWUSR +#define S_IEXEC S_IXUSR + +#define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK) +#define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR) +#define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO) +#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) +#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) +#define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK) + +#define SEEK_SET 0 +#define SEEK_CUR 1 +#define SEEK_END 2 + +struct stat { + dev_t st_dev; + ino_t st_ino; + nlink_t st_nlink; + mode_t st_mode; + uid_t st_uid; + gid_t st_gid; + unsigned int __pad0; + dev_t st_rdev; + off_t st_size; + blksize_t st_blksize; + blkcnt_t st_blocks; + struct timespec st_atim; + struct timespec st_mtim; + struct timespec st_ctim; + long __unused[3]; +}; + +#define TCGETS 0x5401 +#define TCSETS 0x5402 +#define TCSETSW 0x5403 +#define TCSETSF 0x5404 +#define TCSBRK 0x5409 +#define TCXONC 0x540A +#define TIOCSCTTY 0x540E +#define TIOCSTI 0x5412 +#define TIOCGWINSZ 0x5413 +#define TIOCMGET 0x5415 +#define TIOCMSET 0x5418 +#define TIOCINQ 0x541B +#define TIOCNOTTY 0x5422 +#define TIOCGSID 0x5429 + +// Start ioctl stuff +#define _IOC_NRBITS 8 +#define _IOC_TYPEBITS 8 + +#ifndef _IOC_SIZEBITS +#define _IOC_SIZEBITS 14 +#endif + +#ifndef _IOC_DIRBITS +#define _IOC_DIRBITS 2 +#endif + +#define _IOC_NRMASK ((1 << _IOC_NRBITS) - 1) +#define _IOC_TYPEMASK ((1 << _IOC_TYPEBITS) - 1) +#define _IOC_SIZEMASK ((1 << _IOC_SIZEBITS) - 1) +#define _IOC_DIRMASK ((1 << _IOC_DIRBITS) - 1) + +#define _IOC_NRSHIFT 0 +#define _IOC_TYPESHIFT (_IOC_NRSHIFT + _IOC_NRBITS) +#define _IOC_SIZESHIFT (_IOC_TYPESHIFT + _IOC_TYPEBITS) +#define _IOC_DIRSHIFT (_IOC_SIZESHIFT + _IOC_SIZEBITS) + +#ifndef _IOC_NONE +#define _IOC_NONE 0U +#endif + +#ifndef _IOC_WRITE +#define _IOC_WRITE 1U +#endif + +#ifndef _IOC_READ +#define _IOC_READ 2U +#endif + +#define _IOC(dir, type, nr, size) \ + (((dir) << _IOC_DIRSHIFT) | ((type) << _IOC_TYPESHIFT) | \ + ((nr) << _IOC_NRSHIFT) | ((size) << _IOC_SIZESHIFT)) + +#define _IOC_TYPECHECK(t) (sizeof(t)) + +#define _IO(type, nr) _IOC(_IOC_NONE, (type), (nr), 0) +#define _IOR(type, nr, argtype) \ + _IOC(_IOC_READ, (type), (nr), (_IOC_TYPECHECK(argtype))) +#define _IOW(type, nr, argtype) \ + _IOC(_IOC_WRITE, (type), (nr), (_IOC_TYPECHECK(argtype))) + +#define TIOCGPTN \ + _IOR('T', 0x30, unsigned int) /* Get Pty Number (of pty-mux device) */ +#define TIOCSPTLCK _IOW('T', 0x31, int) /* Lock/unlock Pty */ +#define TIOCGDEV \ + _IOR('T', 0x32, unsigned int) /* Get primary device node of /dev/console \ + */ +#define TCGETX 0x5432 /* SYS5 TCGETX compatibility */ +#define TCSETX 0x5433 +#define TCSETXF 0x5434 +#define TCSETXW 0x5435 +#define TIOCSIG _IOW('T', 0x36, int) /* pty: generate signal */ +#define TIOCVHANGUP 0x5437 +#define TIOCGPKT _IOR('T', 0x38, int) /* Get packet mode state */ +#define TIOCGPTLCK _IOR('T', 0x39, int) /* Get Pty lock state */ +#define TIOCGEXCL _IOR('T', 0x40, int) /* Get exclusive mode state */ +#define TIOCGPTPEER _IO('T', 0x41) /* Safely open the slave */ + +#define POLLIN 0x0001 +#define POLLPRI 0x0002 +#define POLLOUT 0x0004 +#define POLLERR 0x0008 +#define POLLHUP 0x0010 +#define POLLNVAL 0x0020 +#define POLLRDNORM 0x0040 +#define POLLRDBAND 0x0080 +#define POLLWRNORM 0x0100 +#define POLLWRBAND 0x0200 +#define POLLRDHUP 0x2000 + +#define SOCK_CLOEXEC O_CLOEXEC +#define SOCK_NONBLOCK O_NONBLOCK + +typedef unsigned int nfds_t; \ No newline at end of file diff --git a/src/libk/vec.c b/src/libk/vec.c new file mode 100644 index 0000000..95c8eb9 --- /dev/null +++ b/src/libk/vec.c @@ -0,0 +1,96 @@ +#include "vec.h" + +int vec_expand_(char **data, int *length, int *capacity, int memsz) { + if (*length + 1 > *capacity) { + void *ptr; + int n = (*capacity == 0) ? 1 : *capacity << 1; + ptr = krealloc(*data, n * memsz); + if (ptr == NULL) + return -1; + *data = ptr; + *capacity = n; + } + return 0; +} + +int vec_reserve_(char **data, int *length, int *capacity, int memsz, int n) { + (void)length; + if (n > *capacity) { + void *ptr = krealloc(*data, n * memsz); + if (ptr == NULL) + return -1; + *data = ptr; + *capacity = n; + } + return 0; +} + +int vec_reserve_po2_(char **data, int *length, int *capacity, int memsz, + int n) { + int n2 = 1; + if (n == 0) + return 0; + while (n2 < n) + n2 <<= 1; + return vec_reserve_(data, length, capacity, memsz, n2); +} + +int vec_compact_(char **data, int *length, int *capacity, int memsz) { + if (*length == 0) { + kfree(*data); + *data = NULL; + *capacity = 0; + return 0; + } else { + void *ptr; + int n = *length; + ptr = krealloc(*data, n * memsz); + if (ptr == NULL) + return -1; + *capacity = n; + *data = ptr; + } + return 0; +} + +int vec_insert_(char **data, int *length, int *capacity, int memsz, int idx) { + int err = vec_expand_(data, length, capacity, memsz); + if (err) + return err; + memmove(*data + (idx + 1) * memsz, *data + idx * memsz, + (*length - idx) * memsz); + return 0; +} + +void vec_splice_(char **data, int *length, int *capacity, int memsz, int start, + int count) { + (void)capacity; + memmove(*data + start * memsz, *data + (start + count) * memsz, + (*length - start - count) * memsz); +} + +void vec_swapsplice_(char **data, int *length, int *capacity, int memsz, + int start, int count) { + (void)capacity; + memmove(*data + start * memsz, *data + (*length - count) * memsz, + count * memsz); +} + +void vec_swap_(char **data, int *length, int *capacity, int memsz, int idx1, + int idx2) { + unsigned char *a, *b, tmp; + int count; + (void)length; + (void)capacity; + if (idx1 == idx2) + return; + a = (unsigned char *)*data + idx1 * memsz; + b = (unsigned char *)*data + idx2 * memsz; + count = memsz; + while (count--) { + tmp = *a; + *a = *b; + *b = tmp; + a++, b++; + } +} diff --git a/src/libk/vec.h b/src/libk/vec.h new file mode 100644 index 0000000..a13b8a7 --- /dev/null +++ b/src/libk/vec.h @@ -0,0 +1,130 @@ +#pragma once +#include "mm/memory.h" +#include "mm/slab.h" + + +#define VEC_VERSION "0.2.1" + +#define vec_unpack_(v) \ + (char **)&(v)->data, &(v)->length, &(v)->capacity, sizeof(*(v)->data) + +#define vec_t(T) \ + struct { \ + T *data; \ + int length, capacity; \ + } + +#define vec_init(v) memset((v), 0, sizeof(*(v))) + +#define vec_deinit(v) (kfree((v)->data), vec_init(v)) + +#define vec_push(v, val) \ + (vec_expand_(vec_unpack_(v)) ? -1 : ((v)->data[(v)->length++] = (val), 0)) + +#define vec_pop(v) (v)->data[--(v)->length] + +#define vec_splice(v, start, count) \ + (vec_splice_(vec_unpack_(v), start, count), (v)->length -= (count)) + +#define vec_swapsplice(v, start, count) \ + (vec_swapsplice_(vec_unpack_(v), start, count), (v)->length -= (count)) + +#define vec_insert(v, idx, val) \ + (vec_insert_(vec_unpack_(v), idx) ? -1 : ((v)->data[idx] = (val), 0), \ + (v)->length++, 0) + +#define vec_sort(v, fn) qsort((v)->data, (v)->length, sizeof(*(v)->data), fn) + +#define vec_swap(v, idx1, idx2) vec_swap_(vec_unpack_(v), idx1, idx2) + +#define vec_truncate(v, len) \ + ((v)->length = (len) < (v)->length ? (len) : (v)->length) + +#define vec_clear(v) ((v)->length = 0) + +#define vec_first(v) (v)->data[0] + +#define vec_last(v) (v)->data[(v)->length - 1] + +#define vec_reserve(v, n) vec_reserve_(vec_unpack_(v), n) + +#define vec_compact(v) vec_compact_(vec_unpack_(v)) + +#define vec_pusharr(v, arr, count) \ + do { \ + int i__, n__ = (count); \ + if (vec_reserve_po2_(vec_unpack_(v), (v)->length + n__) != 0) \ + break; \ + for (i__ = 0; i__ < n__; i__++) { \ + (v)->data[(v)->length++] = (arr)[i__]; \ + } \ + } while (0) + +#define vec_extend(v, v2) vec_pusharr((v), (v2)->data, (v2)->length) + +#define vec_find(v, val, idx) \ + do { \ + for ((idx) = 0; (idx) < (v)->length; (idx)++) { \ + if ((v)->data[(idx)] == (val)) \ + break; \ + } \ + if ((idx) == (v)->length) \ + (idx) = -1; \ + } while (0) + +#define vec_remove(v, val) \ + do { \ + int idx__; \ + vec_find(v, val, idx__); \ + if (idx__ != -1) \ + vec_splice(v, idx__, 1); \ + } while (0) + +#define vec_reverse(v) \ + do { \ + int i__ = (v)->length / 2; \ + while (i__--) { \ + vec_swap((v), i__, (v)->length - (i__ + 1)); \ + } \ + } while (0) + +#define vec_foreach(v, var, iter) \ + if ((v)->length > 0) \ + for ((iter) = 0; \ + (iter) < (v)->length && (((var) = (v)->data[(iter)]), 1); \ + ++(iter)) + +#define vec_foreach_rev(v, var, iter) \ + if ((v)->length > 0) \ + for ((iter) = (v)->length - 1; \ + (iter) >= 0 && (((var) = (v)->data[(iter)]), 1); --(iter)) + +#define vec_foreach_ptr(v, var, iter) \ + if ((v)->length > 0) \ + for ((iter) = 0; \ + (iter) < (v)->length && (((var) = &(v)->data[(iter)]), 1); \ + ++(iter)) + +#define vec_foreach_ptr_rev(v, var, iter) \ + if ((v)->length > 0) \ + for ((iter) = (v)->length - 1; \ + (iter) >= 0 && (((var) = &(v)->data[(iter)]), 1); --(iter)) + +int vec_expand_(char **data, int *length, int *capacity, int memsz); +int vec_reserve_(char **data, int *length, int *capacity, int memsz, int n); +int vec_reserve_po2_(char **data, int *length, int *capacity, int memsz, int n); +int vec_compact_(char **data, int *length, int *capacity, int memsz); +int vec_insert_(char **data, int *length, int *capacity, int memsz, int idx); +void vec_splice_(char **data, int *length, int *capacity, int memsz, int start, + int count); +void vec_swapsplice_(char **data, int *length, int *capacity, int memsz, + int start, int count); +void vec_swap_(char **data, int *length, int *capacity, int memsz, int idx1, + int idx2); + +typedef vec_t(void *) vec_void_t; +typedef vec_t(char *) vec_str_t; +typedef vec_t(int) vec_int_t; +typedef vec_t(char) vec_char_t; +typedef vec_t(float) vec_float_t; +typedef vec_t(double) vec_double_t; diff --git a/src/limine.h b/src/limine.h index 5280b46..c053291 100644 --- a/src/limine.h +++ b/src/limine.h @@ -14,15 +14,16 @@ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ + #ifndef LIMINE_H #define LIMINE_H 1 -#include - #ifdef __cplusplus extern "C" { #endif +#include + /* Misc */ #ifdef LIMINE_NO_POINTERS @@ -31,16 +32,42 @@ extern "C" { # define LIMINE_PTR(TYPE) TYPE #endif -#define LIMINE_REQUESTS_START_MARKER { 0xf6b8f4b39de7d1ae, 0xfab91a6940fcb9cf, \ - 0x785c6ed015d3e316, 0x181e920a7852b9d9 } -#define LIMINE_REQUESTS_END_MARKER { 0xadc0e0531bb10d03, 0x9572709f31764c62 } +#ifndef LIMINE_API_REVISION +# define LIMINE_API_REVISION 0 +#endif -#define LIMINE_BASE_REVISION(N) { 0xf9562b2d5c95a6c8, 0x6a7b384944536bdc, (N) } +#if LIMINE_API_REVISION > 3 +# error "limine.h API revision unsupported" +#endif -#define LIMINE_BASE_REVISION_SUPPORTED(VAR) ((VAR)[2] == 0) +#ifdef __GNUC__ +# define LIMINE_DEPRECATED __attribute__((__deprecated__)) +# define LIMINE_DEPRECATED_IGNORE_START \ + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") +# define LIMINE_DEPRECATED_IGNORE_END \ + _Pragma("GCC diagnostic pop") +#else +# define LIMINE_DEPRECATED +# define LIMINE_DEPRECATED_IGNORE_START +# define LIMINE_DEPRECATED_IGNORE_END +#endif -#define LIMINE_LOADED_BASE_REVISION_VALID(VAR) ((VAR)[1] != 0x6a7b384944536bdc) -#define LIMINE_LOADED_BASE_REVISION(VAR) ((VAR)[1]) +#define LIMINE_REQUESTS_START_MARKER \ + uint64_t limine_requests_start_marker[4] = { 0xf6b8f4b39de7d1ae, 0xfab91a6940fcb9cf, \ + 0x785c6ed015d3e316, 0x181e920a7852b9d9 }; +#define LIMINE_REQUESTS_END_MARKER \ + uint64_t limine_requests_end_marker[2] = { 0xadc0e0531bb10d03, 0x9572709f31764c62 }; + +#define LIMINE_REQUESTS_DELIMITER LIMINE_REQUESTS_END_MARKER + +#define LIMINE_BASE_REVISION(N) \ + uint64_t limine_base_revision[3] = { 0xf9562b2d5c95a6c8, 0x6a7b384944536bdc, (N) }; + +#define LIMINE_BASE_REVISION_SUPPORTED (limine_base_revision[2] == 0) + +#define LIMINE_LOADED_BASE_REV_VALID (limine_base_revision[1] != 0x6a7b384944536bdc) +#define LIMINE_LOADED_BASE_REVISION (limine_base_revision[1]) #define LIMINE_COMMON_MAGIC 0xc7b1dd30df4c8b88, 0x0a82e883a194f07b @@ -60,7 +87,11 @@ struct limine_file { LIMINE_PTR(void *) address; uint64_t size; LIMINE_PTR(char *) path; +#if LIMINE_API_REVISION >= 3 LIMINE_PTR(char *) string; +#else + LIMINE_PTR(char *) cmdline; +#endif uint32_t media_type; uint32_t unused; uint32_t tftp_ip; @@ -74,7 +105,7 @@ struct limine_file { /* Boot info */ -#define LIMINE_BOOTLOADER_INFO_REQUEST_ID { LIMINE_COMMON_MAGIC, 0xf55038d8e2a1202f, 0x279426fcf5f59740 } +#define LIMINE_BOOTLOADER_INFO_REQUEST { LIMINE_COMMON_MAGIC, 0xf55038d8e2a1202f, 0x279426fcf5f59740 } struct limine_bootloader_info_response { uint64_t revision; @@ -90,7 +121,7 @@ struct limine_bootloader_info_request { /* Executable command line */ -#define LIMINE_EXECUTABLE_CMDLINE_REQUEST_ID { LIMINE_COMMON_MAGIC, 0x4b161536e598651e, 0xb390ad4a2f1f303a } +#define LIMINE_EXECUTABLE_CMDLINE_REQUEST { LIMINE_COMMON_MAGIC, 0x4b161536e598651e, 0xb390ad4a2f1f303a } struct limine_executable_cmdline_response { uint64_t revision; @@ -105,11 +136,11 @@ struct limine_executable_cmdline_request { /* Firmware type */ -#define LIMINE_FIRMWARE_TYPE_REQUEST_ID { LIMINE_COMMON_MAGIC, 0x8c2f75d90bef28a8, 0x7045a4688eac00c3 } +#define LIMINE_FIRMWARE_TYPE_REQUEST { LIMINE_COMMON_MAGIC, 0x8c2f75d90bef28a8, 0x7045a4688eac00c3 } #define LIMINE_FIRMWARE_TYPE_X86BIOS 0 -#define LIMINE_FIRMWARE_TYPE_EFI32 1 -#define LIMINE_FIRMWARE_TYPE_EFI64 2 +#define LIMINE_FIRMWARE_TYPE_UEFI32 1 +#define LIMINE_FIRMWARE_TYPE_UEFI64 2 #define LIMINE_FIRMWARE_TYPE_SBI 3 struct limine_firmware_type_response { @@ -125,7 +156,7 @@ struct limine_firmware_type_request { /* Stack size */ -#define LIMINE_STACK_SIZE_REQUEST_ID { LIMINE_COMMON_MAGIC, 0x224ef0460a8e8926, 0xe1cb0fc25f46ea3d } +#define LIMINE_STACK_SIZE_REQUEST { LIMINE_COMMON_MAGIC, 0x224ef0460a8e8926, 0xe1cb0fc25f46ea3d } struct limine_stack_size_response { uint64_t revision; @@ -140,7 +171,7 @@ struct limine_stack_size_request { /* HHDM */ -#define LIMINE_HHDM_REQUEST_ID { LIMINE_COMMON_MAGIC, 0x48dcf1cb8ad2b852, 0x63984e959a98244b } +#define LIMINE_HHDM_REQUEST { LIMINE_COMMON_MAGIC, 0x48dcf1cb8ad2b852, 0x63984e959a98244b } struct limine_hhdm_response { uint64_t revision; @@ -155,7 +186,7 @@ struct limine_hhdm_request { /* Framebuffer */ -#define LIMINE_FRAMEBUFFER_REQUEST_ID { LIMINE_COMMON_MAGIC, 0x9d5827dcd881dd75, 0xa3148604f6fab11b } +#define LIMINE_FRAMEBUFFER_REQUEST { LIMINE_COMMON_MAGIC, 0x9d5827dcd881dd75, 0xa3148604f6fab11b } #define LIMINE_FRAMEBUFFER_RGB 1 @@ -206,69 +237,93 @@ struct limine_framebuffer_request { LIMINE_PTR(struct limine_framebuffer_response *) response; }; -/* Flanterm FB init params */ +/* Terminal */ -#define LIMINE_FLANTERM_FB_INIT_PARAMS_REQUEST_ID { LIMINE_COMMON_MAGIC, 0x3259399fe7c5f126, 0xe01c1c8c5db9d1a9 } +#define LIMINE_TERMINAL_REQUEST { LIMINE_COMMON_MAGIC, 0xc8ac59310c2b0844, 0xa68d0c7265d38878 } -#define LIMINE_FLANTERM_FB_ROTATE_0 0 -#define LIMINE_FLANTERM_FB_ROTATE_90 1 -#define LIMINE_FLANTERM_FB_ROTATE_180 2 -#define LIMINE_FLANTERM_FB_ROTATE_270 3 +#define LIMINE_TERMINAL_CB_DEC 10 +#define LIMINE_TERMINAL_CB_BELL 20 +#define LIMINE_TERMINAL_CB_PRIVATE_ID 30 +#define LIMINE_TERMINAL_CB_STATUS_REPORT 40 +#define LIMINE_TERMINAL_CB_POS_REPORT 50 +#define LIMINE_TERMINAL_CB_KBD_LEDS 60 +#define LIMINE_TERMINAL_CB_MODE 70 +#define LIMINE_TERMINAL_CB_LINUX 80 -struct limine_flanterm_fb_init_params { - LIMINE_PTR(uint32_t *) canvas; - uint64_t canvas_size; - uint32_t ansi_colours[8]; - uint32_t ansi_bright_colours[8]; - uint32_t default_bg; - uint32_t default_fg; - uint32_t default_bg_bright; - uint32_t default_fg_bright; - LIMINE_PTR(void *) font; - uint64_t font_width; - uint64_t font_height; - uint64_t font_spacing; - uint64_t font_scale_x; - uint64_t font_scale_y; - uint64_t margin; - uint64_t rotation; +#define LIMINE_TERMINAL_CTX_SIZE ((uint64_t)(-1)) +#define LIMINE_TERMINAL_CTX_SAVE ((uint64_t)(-2)) +#define LIMINE_TERMINAL_CTX_RESTORE ((uint64_t)(-3)) +#define LIMINE_TERMINAL_FULL_REFRESH ((uint64_t)(-4)) + +/* Response revision 1 */ +#define LIMINE_TERMINAL_OOB_OUTPUT_GET ((uint64_t)(-10)) +#define LIMINE_TERMINAL_OOB_OUTPUT_SET ((uint64_t)(-11)) + +#define LIMINE_TERMINAL_OOB_OUTPUT_OCRNL (1 << 0) +#define LIMINE_TERMINAL_OOB_OUTPUT_OFDEL (1 << 1) +#define LIMINE_TERMINAL_OOB_OUTPUT_OFILL (1 << 2) +#define LIMINE_TERMINAL_OOB_OUTPUT_OLCUC (1 << 3) +#define LIMINE_TERMINAL_OOB_OUTPUT_ONLCR (1 << 4) +#define LIMINE_TERMINAL_OOB_OUTPUT_ONLRET (1 << 5) +#define LIMINE_TERMINAL_OOB_OUTPUT_ONOCR (1 << 6) +#define LIMINE_TERMINAL_OOB_OUTPUT_OPOST (1 << 7) + +LIMINE_DEPRECATED_IGNORE_START + +struct LIMINE_DEPRECATED limine_terminal; + +typedef void (*limine_terminal_write)(struct limine_terminal *, const char *, uint64_t); +typedef void (*limine_terminal_callback)(struct limine_terminal *, uint64_t, uint64_t, uint64_t, uint64_t); + +struct LIMINE_DEPRECATED limine_terminal { + uint64_t columns; + uint64_t rows; + LIMINE_PTR(struct limine_framebuffer *) framebuffer; }; -struct limine_flanterm_fb_init_params_response { +struct LIMINE_DEPRECATED limine_terminal_response { uint64_t revision; - uint64_t entry_count; - LIMINE_PTR(struct limine_flanterm_fb_init_params **) entries; + uint64_t terminal_count; + LIMINE_PTR(struct limine_terminal **) terminals; + LIMINE_PTR(limine_terminal_write) write; }; -struct limine_flanterm_fb_init_params_request { +struct LIMINE_DEPRECATED limine_terminal_request { uint64_t id[4]; uint64_t revision; - LIMINE_PTR(struct limine_flanterm_fb_init_params_response *) response; + LIMINE_PTR(struct limine_terminal_response *) response; + LIMINE_PTR(limine_terminal_callback) callback; }; +LIMINE_DEPRECATED_IGNORE_END + /* Paging mode */ -#define LIMINE_PAGING_MODE_REQUEST_ID { LIMINE_COMMON_MAGIC, 0x95c1a0edab0944cb, 0xa4e5cb3842f7488a } +#define LIMINE_PAGING_MODE_REQUEST { LIMINE_COMMON_MAGIC, 0x95c1a0edab0944cb, 0xa4e5cb3842f7488a } +#if defined (__x86_64__) || defined (__i386__) #define LIMINE_PAGING_MODE_X86_64_4LVL 0 #define LIMINE_PAGING_MODE_X86_64_5LVL 1 -#define LIMINE_PAGING_MODE_X86_64_MIN LIMINE_PAGING_MODE_X86_64_4LVL -#define LIMINE_PAGING_MODE_X86_64_DEFAULT LIMINE_PAGING_MODE_X86_64_4LVL - +#define LIMINE_PAGING_MODE_MIN LIMINE_PAGING_MODE_X86_64_4LVL +#define LIMINE_PAGING_MODE_DEFAULT LIMINE_PAGING_MODE_X86_64_4LVL +#elif defined (__aarch64__) #define LIMINE_PAGING_MODE_AARCH64_4LVL 0 #define LIMINE_PAGING_MODE_AARCH64_5LVL 1 -#define LIMINE_PAGING_MODE_AARCH64_MIN LIMINE_PAGING_MODE_AARCH64_4LVL -#define LIMINE_PAGING_MODE_AARCH64_DEFAULT LIMINE_PAGING_MODE_AARCH64_4LVL - +#define LIMINE_PAGING_MODE_MIN LIMINE_PAGING_MODE_AARCH64_4LVL +#define LIMINE_PAGING_MODE_DEFAULT LIMINE_PAGING_MODE_AARCH64_4LVL +#elif defined (__riscv) && (__riscv_xlen == 64) #define LIMINE_PAGING_MODE_RISCV_SV39 0 #define LIMINE_PAGING_MODE_RISCV_SV48 1 #define LIMINE_PAGING_MODE_RISCV_SV57 2 -#define LIMINE_PAGING_MODE_RISCV_MIN LIMINE_PAGING_MODE_RISCV_SV39 -#define LIMINE_PAGING_MODE_RISCV_DEFAULT LIMINE_PAGING_MODE_RISCV_SV48 - -#define LIMINE_PAGING_MODE_LOONGARCH_4LVL 0 -#define LIMINE_PAGING_MODE_LOONGARCH_MIN LIMINE_PAGING_MODE_LOONGARCH_4LVL -#define LIMINE_PAGING_MODE_LOONGARCH_DEFAULT LIMINE_PAGING_MODE_LOONGARCH_4LVL +#define LIMINE_PAGING_MODE_MIN LIMINE_PAGING_MODE_RISCV_SV39 +#define LIMINE_PAGING_MODE_DEFAULT LIMINE_PAGING_MODE_RISCV_SV48 +#elif defined (__loongarch__) && (__loongarch_grlen == 64) +#define LIMINE_PAGING_MODE_LOONGARCH64_4LVL 0 +#define LIMINE_PAGING_MODE_MIN LIMINE_PAGING_MODE_LOONGARCH64_4LVL +#define LIMINE_PAGING_MODE_DEFAULT LIMINE_PAGING_MODE_LOONGARCH64_4LVL +#else +#error Unknown architecture +#endif struct limine_paging_mode_response { uint64_t revision; @@ -284,19 +339,47 @@ struct limine_paging_mode_request { uint64_t min_mode; }; +/* 5-level paging */ + +#define LIMINE_5_LEVEL_PAGING_REQUEST { LIMINE_COMMON_MAGIC, 0x94469551da9b3192, 0xebe5e86db7382888 } + +LIMINE_DEPRECATED_IGNORE_START + +struct LIMINE_DEPRECATED limine_5_level_paging_response { + uint64_t revision; +}; + +struct LIMINE_DEPRECATED limine_5_level_paging_request { + uint64_t id[4]; + uint64_t revision; + LIMINE_PTR(struct limine_5_level_paging_response *) response; +}; + +LIMINE_DEPRECATED_IGNORE_END + /* MP */ -#define LIMINE_MP_REQUEST_ID { LIMINE_COMMON_MAGIC, 0x95a67b819a1b857e, 0xa0b61b723b6a73e0 } +#if LIMINE_API_REVISION >= 1 +# define LIMINE_MP_REQUEST { LIMINE_COMMON_MAGIC, 0x95a67b819a1b857e, 0xa0b61b723b6a73e0 } +# define LIMINE_MP(TEXT) limine_mp_##TEXT +#else +# define LIMINE_SMP_REQUEST { LIMINE_COMMON_MAGIC, 0x95a67b819a1b857e, 0xa0b61b723b6a73e0 } +# define LIMINE_MP(TEXT) limine_smp_##TEXT +#endif -struct limine_mp_info; +struct LIMINE_MP(info); -typedef void (*limine_goto_address)(struct limine_mp_info *); +typedef void (*limine_goto_address)(struct LIMINE_MP(info) *); #if defined (__x86_64__) || defined (__i386__) -#define LIMINE_MP_RESPONSE_X86_64_X2APIC (1 << 0) +#if LIMINE_API_REVISION >= 1 +# define LIMINE_MP_X2APIC (1 << 0) +#else +# define LIMINE_SMP_X2APIC (1 << 0) +#endif -struct limine_mp_info { +struct LIMINE_MP(info) { uint32_t processor_id; uint32_t lapic_id; uint64_t reserved; @@ -304,17 +387,17 @@ struct limine_mp_info { uint64_t extra_argument; }; -struct limine_mp_response { +struct LIMINE_MP(response) { uint64_t revision; uint32_t flags; uint32_t bsp_lapic_id; uint64_t cpu_count; - LIMINE_PTR(struct limine_mp_info **) cpus; + LIMINE_PTR(struct LIMINE_MP(info) **) cpus; }; #elif defined (__aarch64__) -struct limine_mp_info { +struct LIMINE_MP(info) { uint32_t processor_id; uint32_t reserved1; uint64_t mpidr; @@ -323,17 +406,17 @@ struct limine_mp_info { uint64_t extra_argument; }; -struct limine_mp_response { +struct LIMINE_MP(response) { uint64_t revision; uint64_t flags; uint64_t bsp_mpidr; uint64_t cpu_count; - LIMINE_PTR(struct limine_mp_info **) cpus; + LIMINE_PTR(struct LIMINE_MP(info) **) cpus; }; #elif defined (__riscv) && (__riscv_xlen == 64) -struct limine_mp_info { +struct LIMINE_MP(info) { uint64_t processor_id; uint64_t hartid; uint64_t reserved; @@ -341,48 +424,39 @@ struct limine_mp_info { uint64_t extra_argument; }; -struct limine_mp_response { +struct LIMINE_MP(response) { uint64_t revision; uint64_t flags; uint64_t bsp_hartid; uint64_t cpu_count; - LIMINE_PTR(struct limine_mp_info **) cpus; + LIMINE_PTR(struct LIMINE_MP(info) **) cpus; }; #elif defined (__loongarch__) && (__loongarch_grlen == 64) -struct limine_mp_info { - uint64_t processor_id; - uint64_t phys_id; +struct LIMINE_MP(info) { uint64_t reserved; - LIMINE_PTR(limine_goto_address) goto_address; - uint64_t extra_argument; }; -struct limine_mp_response { - uint64_t revision; - uint64_t flags; - uint64_t bsp_phys_id; +struct LIMINE_MP(response) { uint64_t cpu_count; - LIMINE_PTR(struct limine_mp_info **) cpus; + LIMINE_PTR(struct LIMINE_MP(info) **) cpus; }; #else #error Unknown architecture #endif -#define LIMINE_MP_REQUEST_X86_64_X2APIC (1 << 0) - -struct limine_mp_request { +struct LIMINE_MP(request) { uint64_t id[4]; uint64_t revision; - LIMINE_PTR(struct limine_mp_response *) response; + LIMINE_PTR(struct LIMINE_MP(response) *) response; uint64_t flags; }; /* Memory map */ -#define LIMINE_MEMMAP_REQUEST_ID { LIMINE_COMMON_MAGIC, 0x67cf3d9d378a806f, 0xe304acdfc50c3c62 } +#define LIMINE_MEMMAP_REQUEST { LIMINE_COMMON_MAGIC, 0x67cf3d9d378a806f, 0xe304acdfc50c3c62 } #define LIMINE_MEMMAP_USABLE 0 #define LIMINE_MEMMAP_RESERVED 1 @@ -390,9 +464,12 @@ struct limine_mp_request { #define LIMINE_MEMMAP_ACPI_NVS 3 #define LIMINE_MEMMAP_BAD_MEMORY 4 #define LIMINE_MEMMAP_BOOTLOADER_RECLAIMABLE 5 -#define LIMINE_MEMMAP_EXECUTABLE_AND_MODULES 6 +#if LIMINE_API_REVISION >= 2 +# define LIMINE_MEMMAP_EXECUTABLE_AND_MODULES 6 +#else +# define LIMINE_MEMMAP_KERNEL_AND_MODULES 6 +#endif #define LIMINE_MEMMAP_FRAMEBUFFER 7 -#define LIMINE_MEMMAP_RESERVED_MAPPED 8 struct limine_memmap_entry { uint64_t base; @@ -414,7 +491,7 @@ struct limine_memmap_request { /* Entry point */ -#define LIMINE_ENTRY_POINT_REQUEST_ID { LIMINE_COMMON_MAGIC, 0x13d86c035a1cd3e1, 0x2b0caa89d8f3026a } +#define LIMINE_ENTRY_POINT_REQUEST { LIMINE_COMMON_MAGIC, 0x13d86c035a1cd3e1, 0x2b0caa89d8f3026a } typedef void (*limine_entry_point)(void); @@ -431,29 +508,53 @@ struct limine_entry_point_request { /* Executable File */ -#define LIMINE_EXECUTABLE_FILE_REQUEST_ID { LIMINE_COMMON_MAGIC, 0xad97e90e83f1ed67, 0x31eb5d1c5ff23b69 } +#if LIMINE_API_REVISION >= 2 +# define LIMINE_EXECUTABLE_FILE_REQUEST { LIMINE_COMMON_MAGIC, 0xad97e90e83f1ed67, 0x31eb5d1c5ff23b69 } +#else +# define LIMINE_KERNEL_FILE_REQUEST { LIMINE_COMMON_MAGIC, 0xad97e90e83f1ed67, 0x31eb5d1c5ff23b69 } +#endif +#if LIMINE_API_REVISION >= 2 struct limine_executable_file_response { +#else +struct limine_kernel_file_response { +#endif uint64_t revision; +#if LIMINE_API_REVISION >= 2 LIMINE_PTR(struct limine_file *) executable_file; +#else + LIMINE_PTR(struct limine_file *) kernel_file; +#endif }; +#if LIMINE_API_REVISION >= 2 struct limine_executable_file_request { +#else +struct limine_kernel_file_request { +#endif uint64_t id[4]; uint64_t revision; +#if LIMINE_API_REVISION >= 2 LIMINE_PTR(struct limine_executable_file_response *) response; +#else + LIMINE_PTR(struct limine_kernel_file_response *) response; +#endif }; /* Module */ -#define LIMINE_MODULE_REQUEST_ID { LIMINE_COMMON_MAGIC, 0x3e7e279702be32af, 0xca1c4f3bd1280cee } +#define LIMINE_MODULE_REQUEST { LIMINE_COMMON_MAGIC, 0x3e7e279702be32af, 0xca1c4f3bd1280cee } #define LIMINE_INTERNAL_MODULE_REQUIRED (1 << 0) #define LIMINE_INTERNAL_MODULE_COMPRESSED (1 << 1) struct limine_internal_module { LIMINE_PTR(const char *) path; +#if LIMINE_API_REVISION >= 3 LIMINE_PTR(const char *) string; +#else + LIMINE_PTR(const char *) cmdline; +#endif uint64_t flags; }; @@ -475,11 +576,15 @@ struct limine_module_request { /* RSDP */ -#define LIMINE_RSDP_REQUEST_ID { LIMINE_COMMON_MAGIC, 0xc5e77b6b397e7b43, 0x27637845accdcf3c } +#define LIMINE_RSDP_REQUEST { LIMINE_COMMON_MAGIC, 0xc5e77b6b397e7b43, 0x27637845accdcf3c } struct limine_rsdp_response { uint64_t revision; +#if LIMINE_API_REVISION >= 1 + uint64_t address; +#else LIMINE_PTR(void *) address; +#endif }; struct limine_rsdp_request { @@ -490,12 +595,17 @@ struct limine_rsdp_request { /* SMBIOS */ -#define LIMINE_SMBIOS_REQUEST_ID { LIMINE_COMMON_MAGIC, 0x9e9046f11e095391, 0xaa4a520fefbde5ee } +#define LIMINE_SMBIOS_REQUEST { LIMINE_COMMON_MAGIC, 0x9e9046f11e095391, 0xaa4a520fefbde5ee } struct limine_smbios_response { uint64_t revision; +#if LIMINE_API_REVISION >= 1 + uint64_t entry_32; + uint64_t entry_64; +#else LIMINE_PTR(void *) entry_32; LIMINE_PTR(void *) entry_64; +#endif }; struct limine_smbios_request { @@ -506,11 +616,15 @@ struct limine_smbios_request { /* EFI system table */ -#define LIMINE_EFI_SYSTEM_TABLE_REQUEST_ID { LIMINE_COMMON_MAGIC, 0x5ceba5163eaaf6d6, 0x0a6981610cf65fcc } +#define LIMINE_EFI_SYSTEM_TABLE_REQUEST { LIMINE_COMMON_MAGIC, 0x5ceba5163eaaf6d6, 0x0a6981610cf65fcc } struct limine_efi_system_table_response { uint64_t revision; +#if LIMINE_API_REVISION >= 1 + uint64_t address; +#else LIMINE_PTR(void *) address; +#endif }; struct limine_efi_system_table_request { @@ -521,7 +635,7 @@ struct limine_efi_system_table_request { /* EFI memory map */ -#define LIMINE_EFI_MEMMAP_REQUEST_ID { LIMINE_COMMON_MAGIC, 0x7df62a431d6872d5, 0xa4fcdfb3e57306c8 } +#define LIMINE_EFI_MEMMAP_REQUEST { LIMINE_COMMON_MAGIC, 0x7df62a431d6872d5, 0xa4fcdfb3e57306c8 } struct limine_efi_memmap_response { uint64_t revision; @@ -539,38 +653,74 @@ struct limine_efi_memmap_request { /* Date at boot */ -#define LIMINE_DATE_AT_BOOT_REQUEST_ID { LIMINE_COMMON_MAGIC, 0x502746e184c088aa, 0xfbc5ec83e6327893 } +#if LIMINE_API_REVISION >= 3 +# define LIMINE_DATE_AT_BOOT_REQUEST { LIMINE_COMMON_MAGIC, 0x502746e184c088aa, 0xfbc5ec83e6327893 } +#else +# define LIMINE_BOOT_TIME_REQUEST { LIMINE_COMMON_MAGIC, 0x502746e184c088aa, 0xfbc5ec83e6327893 } +#endif +#if LIMINE_API_REVISION >= 3 struct limine_date_at_boot_response { +#else +struct limine_boot_time_response { +#endif uint64_t revision; +#if LIMINE_API_REVISION >= 3 int64_t timestamp; +#else + int64_t boot_time; +#endif }; +#if LIMINE_API_REVISION >= 3 struct limine_date_at_boot_request { +#else +struct limine_boot_time_request { +#endif uint64_t id[4]; uint64_t revision; +#if LIMINE_API_REVISION >= 3 LIMINE_PTR(struct limine_date_at_boot_response *) response; +#else + LIMINE_PTR(struct limine_boot_time_response *) response; +#endif }; /* Executable address */ -#define LIMINE_EXECUTABLE_ADDRESS_REQUEST_ID { LIMINE_COMMON_MAGIC, 0x71ba76863cc55f63, 0xb2644a48c516a487 } +#if LIMINE_API_REVISION >= 2 +# define LIMINE_EXECUTABLE_ADDRESS_REQUEST { LIMINE_COMMON_MAGIC, 0x71ba76863cc55f63, 0xb2644a48c516a487 } +#else +# define LIMINE_KERNEL_ADDRESS_REQUEST { LIMINE_COMMON_MAGIC, 0x71ba76863cc55f63, 0xb2644a48c516a487 } +#endif +#if LIMINE_API_REVISION >= 2 struct limine_executable_address_response { +#else +struct limine_kernel_address_response { +#endif uint64_t revision; uint64_t physical_base; uint64_t virtual_base; }; +#if LIMINE_API_REVISION >= 2 struct limine_executable_address_request { +#else +struct limine_kernel_address_request { +#endif uint64_t id[4]; uint64_t revision; +#if LIMINE_API_REVISION >= 2 LIMINE_PTR(struct limine_executable_address_response *) response; +#else + LIMINE_PTR(struct limine_kernel_address_response *) response; +#endif }; /* Device Tree Blob */ -#define LIMINE_DTB_REQUEST_ID { LIMINE_COMMON_MAGIC, 0xb40ddb48fb54bac7, 0x545081493f81ffb7 } +#define LIMINE_DTB_REQUEST { LIMINE_COMMON_MAGIC, 0xb40ddb48fb54bac7, 0x545081493f81ffb7 } struct limine_dtb_response { uint64_t revision; @@ -585,7 +735,7 @@ struct limine_dtb_request { /* RISC-V Boot Hart ID */ -#define LIMINE_RISCV_BSP_HARTID_REQUEST_ID { LIMINE_COMMON_MAGIC, 0x1369359f025525f9, 0x2ff2a56178391bb6 } +#define LIMINE_RISCV_BSP_HARTID_REQUEST { LIMINE_COMMON_MAGIC, 0x1369359f025525f9, 0x2ff2a56178391bb6 } struct limine_riscv_bsp_hartid_response { uint64_t revision; @@ -598,35 +748,6 @@ struct limine_riscv_bsp_hartid_request { LIMINE_PTR(struct limine_riscv_bsp_hartid_response *) response; }; -/* Bootloader Performance */ - -#define LIMINE_BOOTLOADER_PERFORMANCE_REQUEST_ID { LIMINE_COMMON_MAGIC, 0x6b50ad9bf36d13ad, 0xdc4c7e88fc759e17 } - -struct limine_bootloader_performance_response { - uint64_t revision; - uint64_t reset_usec; - uint64_t init_usec; - uint64_t exec_usec; -}; - -struct limine_bootloader_performance_request { - uint64_t id[4]; - uint64_t revision; - LIMINE_PTR(struct limine_bootloader_performance_response *) response; -}; - -#define LIMINE_KEEP_IOMMU_REQUEST_ID { LIMINE_COMMON_MAGIC, 0x8ebaabe51f490179, 0x2aa86a59ffb4ab0f } - -struct limine_keep_iommu_response { - uint64_t revision; -}; - -struct limine_keep_iommu_request { - uint64_t id[4]; - uint64_t revision; - LIMINE_PTR(struct limine_keep_iommu_response *) response; -}; - #ifdef __cplusplus } #endif diff --git a/src/main.c b/src/main.c index f2045b2..f5c61a6 100644 --- a/src/main.c +++ b/src/main.c @@ -1,465 +1,113 @@ -#include -#include -#include -#include -#include "drivers/video/render.h" -#include "drivers/video/tga.h" -#include "libk/stdio.h" -#include "arch/x86_64/boot/gdt.h" -#include "arch/x86_64/boot/idt.h" -#include "arch/x86_64/boot/isr.h" -#include "arch/x86_64/sys/irq.h" -#include "mm/memory.h" -#include "mm/pmm.h" -#include "mm/vmm.h" -#include "arch/x86_64/bus/ata.h" -#include "fs/ext2.h" -#include "string.h" -#include "arch/x86_64/cpu/io.h" -#include "arch/x86_64/cpu/usermode.h" -#include "syscall/syscall.h" +#include "libk/debug.h" #include "fs/vfs.h" -#include "arch/x86_64/sys/tsc.h" -#include "arch/x86_64/sys/pit.h" -#include -#include -#include -#include "arch/x86_64/bus/pci.h" -#include "drivers/audio/hda.h" -#include "drivers/audio/pcm.h" -#include "drivers/input/input.h" -#include "arch/x86_64/sys/apic.h" -#include "arch/x86_64/sys/ioapic.h" -#include "drivers/input/ps2.h" -#include "sched/scheduler.h" -#include "drivers/rand/random.h" +#include "sched/sched_types.h" +#include "sched/sched.h" +#include "fs/tmpfs.h" +#include "fs/devtmpfs.h" +#include "libk/random.h" +#include "fs/streams.h" +#include "fs/partition.h" +#include "drivers/fb/fb.h" +#include "drivers/tty/console.h" +#include "arch/x86_64/sys/timer.h" +#include "libk/kargs.h" +#include "fs/ramdisk.h" +#include "sched/syscall.h" +#include "mm/mmap.h" +#include "drivers/tty/pty.h" +#include "ipc/pipe.h" -uintptr_t g_hhdm_offset; -#define CPU_STACK_SIZE (64 * 1024) +const char *module_list[] = {}; -// Set the base revision to 6, this is recommended as this is the latest -// base revision described by the Limine boot protocol specification. -// See specification for further info. +#define MODULE_LIST_SIZE (sizeof(module_list) / sizeof(module_list[0])) +#define ONE_SECOND (uint64_t)(1000 * 1000 * 1000) -__attribute__((used, section(".limine_requests"))) -static volatile uint64_t limine_base_revision[] = LIMINE_BASE_REVISION(6); - -// The Limine requests can be placed anywhere, but it is important that -// the compiler does not optimise them away, so, usually, they should -// be made volatile or equivalent, _and_ they should be accessed at least -// once or marked as used with the "used" attribute as done here. - -__attribute__((used, section(".limine_requests"))) -static volatile struct limine_framebuffer_request framebuffer_request = { - .id = LIMINE_FRAMEBUFFER_REQUEST_ID, - .revision = 6 -}; - -__attribute__((used, section(".limine_requests"))) -static volatile struct limine_memmap_request memmap_request = { - .id = LIMINE_MEMMAP_REQUEST_ID, - .revision = 6 -}; - -__attribute__((used, section(".limine_requests"))) -static volatile struct limine_rsdp_request rsdp_request = { - .id = LIMINE_RSDP_REQUEST_ID, - .revision = 6 -}; - - - - -__attribute__((used, section(".limine_requests"))) -volatile struct limine_stack_size_request stack_size_request = { - .id = LIMINE_STACK_SIZE_REQUEST_ID, - .revision = 0, - .stack_size = CPU_STACK_SIZE, -}; - - - -// Finally, define the start and end markers for the Limine requests. -// These can also be moved anywhere, to any .c file, as seen fit. - -__attribute__((used, section(".limine_requests_start"))) -static volatile uint64_t limine_requests_start_marker[] = LIMINE_REQUESTS_START_MARKER; - -__attribute__((used, section(".limine_requests_end"))) -static volatile uint64_t limine_requests_end_marker[] = LIMINE_REQUESTS_END_MARKER; - - -uint8_t g_clean_fxstate[512] __attribute__((aligned(16))); - - -// Halt and catch fire function. static void hcf(void) { for (;;) { asm volatile ("pause"); } } -static inline void cpuid(uint32_t leaf, uint32_t subleaf, - 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), "c"(subleaf)); -} - -int cpu_has_leaf7() { - uint32_t a, b, c, d; - cpuid(0, 0, &a, &b, &c, &d); - return a >= 7; -} - -int cpu_has_fsgsbase() { - if (!cpu_has_leaf7()) - return 0; - - uint32_t a, b, c, d; - cpuid(7, 0, &a, &b, &c, &d); - - return (b & (1u << 0)) != 0; -} - -static inline uint64_t read_cr4(void) { - uint64_t val; - __asm__ volatile ("mov %%cr4, %0" : "=r"(val)); - return val; -} - - -static inline void write_cr4(uint64_t val) { - asm volatile ("mov %0, %%cr4" :: "r"(val)); -} - -static inline uint64_t read_cr0(void) { - uint64_t val; - __asm__ volatile ("mov %%cr0, %0" : "=r"(val)); - return val; -} - -static inline void write_cr0(uint64_t val) { - __asm__ volatile ("mov %0, %%cr0" :: "r"(val)); -} - - -#define CR4_FSGSBASE (1ULL << 16) - - - -void enable_fsgsbase_if_supported() { - if (!cpu_has_fsgsbase()) { - // fallback: don't use wrfsbase - printf("FSGSBASE not supported, skipping wrfsbase/wrgsbase\n"); - return; - } - - uint64_t cr4 = read_cr4(); - cr4 |= CR4_FSGSBASE; - write_cr4(cr4); -} - -extern struct kernel_pagemap; -uint64_t g_rsdp_phys; - -void shutdown(void) { - uacpi_status ret2 = uacpi_prepare_for_sleep_state(UACPI_SLEEP_STATE_S5); - if (uacpi_unlikely_error(ret2)) { - printf("failed to prepare for sleep: %s", uacpi_status_to_string(ret2)); - } - - ret2 = uacpi_enter_sleep_state(UACPI_SLEEP_STATE_S5); - if (uacpi_unlikely_error(ret2)) { - printf("failed to enter sleep: %s", uacpi_status_to_string(ret2)); - } -} - -static uacpi_interrupt_ret handle_power_button(uacpi_handle ctx) { - /* - * Shut down right here using the helper we have defined above. - * - * Note that it's generally terrible practice to run any AML from - * an interrupt handler, as it's allowed to allocate, map, sleep, - * stall, acquire mutexes, etc. So, if possible in your kernel, - * instead schedule the shutdown callback to be run in a normal - * preemptible context later. - */ - shutdown(); - return UACPI_INTERRUPT_HANDLED; -} - - - - - -void init_simd(void) { - uint64_t cr0 = read_cr0(); - uint64_t cr4 = read_cr4(); - - // --- CR0 setup --- - cr0 &= ~(1 << 2); // Clear EM (Emulation) → allow FPU/SSE - cr0 |= (1 << 1); // Set MP (Monitor Coprocessor) - cr0 &= ~(1 << 3); // Clear TS (Task Switched) → no #NM - - // --- CR4 setup --- - cr4 |= (1 << 9); // OSFXSR → enable FXSAVE/FXRSTOR + SSE - cr4 |= (1 << 10); // OSXMMEXCPT → enable SSE exceptions - - write_cr0(cr0); - write_cr4(cr4); - - // Initialize FPU/SSE state - __asm__ volatile ("fninit"); - - __asm__ volatile ("fxsave %0" : "=m"(g_clean_fxstate)); - -} - - -void kmain(void) { - if (LIMINE_BASE_REVISION_SUPPORTED(limine_base_revision) == false) { - hcf(); - } - - uint64_t rsp; - asm volatile("mov %%rsp, %0" : "=r"(rsp)); - - // Ensure we got a framebuffer. - if (framebuffer_request.response == NULL - || framebuffer_request.response->framebuffer_count < 1) { - hcf(); - } - - // Fetch the first framebuffer. - struct limine_framebuffer *framebuffer = framebuffer_request.response->framebuffers[0]; - - struct limine_memmap_response *memmap_response = memmap_request.response; - - struct limine_rsdp_response *rsdp_response = rsdp_request.response; - - g_rsdp_phys = (uint64_t)rsdp_response->address - MEM_PHYS_OFFSET; - - - - - - if (!memmap_response) { - hcf(); - } - - - fb = framebuffer->address; - fb_width = framebuffer->width; - fb_height = framebuffer->height; - fb_pitch = framebuffer->pitch / 4; - - - - printf("Hello, Kernel!\n"); - printf("Number: %d\n", 1234); - printf("Hex: %x\n", 0xBEEF); - - for (size_t i = 0; i < memmap_response->entry_count; i++) { - struct limine_memmap_entry *entry = memmap_response->entries[i]; - - printf("Base: 0x%x%x, Length: 0x%x%x, Type: %d\n", - (uint32_t)(entry->base >> 32), (uint32_t)entry->base, - (uint32_t)(entry->length >> 32), (uint32_t)entry->length, - entry->type); - } - x86_64_DisableInterrupts(); - - uint32_t msr_lo, msr_hi; - asm volatile("rdmsr" : "=a"(msr_lo), "=d"(msr_hi) : "c"(0x1B)); - msr_lo &= ~(1 << 11); - asm volatile("wrmsr" : : "a"(msr_lo), "d"(msr_hi), "c"(0x1B)); - printf("rsdp: 0x%x\n", g_rsdp_phys); - printf("init pmm\n"); - pmm_init(memmap_response->entries, memmap_response->entry_count); - printf("init slab\n"); - slab_init(); - printf("init vmm\n"); - vmm_init(memmap_response->entries, memmap_response->entry_count); - - printf("init gdt"); - x86_64_GDT_Initialize(); - x86_64_IDT_Initialize(); - x86_64_ISR_Initialize(); - x86_64_IRQ_Initialize(); - x86_64_PIT_Initialize(1000); - asm volatile("sti"); - calibrate_tsc(); - - - //while (1) asm volatile("hlt"); - - ata_init(); - ata_identify(); - - if (!ext2_read_superblock()) { - printf("EXT2 failed\n"); - hcf(); - } - - if (!ext2_read_group_desc_table()) { - printf("GDT failed\n"); - hcf(); - } - - - ext2_read_root_dir(); - - uint32_t inum = ext2_resolve_path("charlie.tga"); - if (!inum) { - printf("file not found\n"); - hcf(); - } - - ext2_inode_t inode; - if (!ext2_read_inode(inum, &inode)) { - printf("inode read failed\n"); - hcf(); - } - - uint32_t tga_size = inode.i_size; - if (!tga_size) { - printf("tga file is empty\n"); - hcf(); - } - - uint8_t *file_buf = kmalloc(tga_size); - if (!file_buf) { - printf("OOM allocating tga buffer (%u bytes)\n", tga_size); - hcf(); - } - - if (!ext2_read_file(&inode, file_buf)) { - printf("read failed\n"); - kfree(file_buf); - hcf(); - } - - uint32_t *img = tga_parse(file_buf, tga_size); - kfree(file_buf); // free the raw file buffer as soon as parse is done - - if (!img) { - printf("tga parse failed\n"); - hcf(); - } - - int x = 0; - int y = 0; - int new_w = img[0] * 5; - int new_h = img[1] * 5; - clear_screen(0xFF1E1E1E); - draw_image_bilinear(img, x, y, new_w, new_h); - kfree(img); - - //clear_screen(0xFF1E1E1E); - - - printf("\nKirkOS %s\n", KIRKOS_VERSION); - - x86_64_EnableInterrupts(); - - - - /* - * Start with this as the first step of the initialization. This loads all - * tables, brings the event subsystem online, and enters ACPI mode. We pass - * in 0 as the flags as we don't want to override any default behavior for now. - */ - uacpi_status ret = uacpi_initialize(0); - if (uacpi_unlikely_error(ret)) { - printf("uacpi_initialize error: %s", uacpi_status_to_string(ret)); - } - - /* - * Load the AML namespace. This feeds DSDT and all SSDTs to the interpreter - * for execution. - */ - ret = uacpi_namespace_load(); - if (uacpi_unlikely_error(ret)) { - printf("uacpi_namespace_load error: %s", uacpi_status_to_string(ret)); - } - - /* - * Initialize the namespace. This calls all necessary _STA/_INI AML methods, - * as well as _REG for registered operation region handlers. - */ - ret = uacpi_namespace_initialize(); - if (uacpi_unlikely_error(ret)) { - printf("uacpi_namespace_initialize error: %s", uacpi_status_to_string(ret)); - } - - /* - * Tell uACPI that we have marked all GPEs we wanted for wake (even though we haven't - * actually marked any, as we have no power management support right now). This is - * needed to let uACPI enable all unmarked GPEs that have a corresponding AML handler. - * These handlers are used by the firmware to dynamically execute AML code at runtime - * to e.g. react to thermal events or device hotplug. - */ - ret = uacpi_finalize_gpe_initialization(); - if (uacpi_unlikely_error(ret)) { - printf("uACPI GPE initialization error: %s", uacpi_status_to_string(ret)); - } - - - - /*pci_init(); - - pci_device_t hda; - if (pci_find_hda(&hda)) { - - printf("[PCI] Found HDA controller at %02x:%02x.%x %04x:%04x\n", - hda.addr.bus, hda.addr.device, hda.addr.function, - hda.vendor_id, hda.device_id); - if (hda_init(&hda)) { - pcm_play_file("kirky.wav"); - } - } else { - printf("[PCI] No HDA controller found!\n"); - }*/ - - - - - lapic_init(); - ioapic_init(); - - - irq_redirect_to_apic(0, 0x20, lapic_id(), false); - printf("tst"); - - input_init(); - ps2_kbd_init(); - random_init(); - - gdt_load_tss((size_t)&kernel_tss); - kernel_tss.rsp0 = rsp; - - - syscall_init(); - enable_fsgsbase_if_supported(); - init_simd(); - - sched_init(); - - - - start_userspace(); - - sched_yield(); - - - for (;;) { - sched_yield(); - } - - // We're done, just hang... - //hcf(); -} - +void kernel_main(void *args) { + vfs_init(); + tmpfs_init(); + devtmpfs_init(); + vfs_mount(vfs_root, NULL, "/", "tmpfs"); + vfs_create(vfs_root, "/tmp", 0755 | S_IFDIR); + vfs_mount(vfs_root, NULL, "/tmp", "tmpfs"); + vfs_create(vfs_root, "/dev", 0755 | S_IFDIR); + vfs_mount(vfs_root, NULL, "/dev", "devtmpfs"); + streams_init(); + randdev_init(); + + kprintf("Hello I am %s\n", sched_get_running_thread()->mother_proc->name); + + if (args != NULL) { + uint64_t *module_info = (uint64_t *)args; + kprintf("Ramdisk located at %p\n", module_info[0]); + ramdisk_install(module_info[0], module_info[1]); + } + + partition_enumerate(NULL, NULL); + + fbdev_init(); + + + + syscall_register_handler(0x0, syscall_read); + syscall_register_handler(0x1, syscall_write); + syscall_register_handler(0x2, syscall_open); + syscall_register_handler(0x3, syscall_close); + syscall_register_handler(0x8, syscall_seek); + syscall_register_handler(0x9, syscall_mmap); + syscall_register_handler(0xa, syscall_mprotect); + syscall_register_handler(0xb, syscall_munmap); + syscall_register_handler(0x10, syscall_ioctl); + syscall_register_handler(0x48, syscall_fcntl); + syscall_register_handler(0x4f, syscall_getcwd); + syscall_register_handler(0x50, syscall_chdir); + syscall_register_handler(0x59, syscall_readdir); + syscall_register_handler(0x101, syscall_openat); + syscall_register_handler(0x102, syscall_mkdirat); + syscall_register_handler(0x103, syscall_mknodat); + syscall_register_handler(0x106, syscall_fstatat); + syscall_register_handler(0x107, syscall_unlinkat); + syscall_register_handler(0x109, syscall_linkat); + syscall_register_handler(0x10b, syscall_readlinkat); + syscall_register_handler(0x10c, syscall_fchmodat); + syscall_register_handler(0x124, syscall_dup3); + syscall_register_handler(0x125, syscall_pipe); + syscall_register_handler(0xff, syscall_openpty); + syscall_register_handler(0x10f, syscall_ppoll); + syscall_register_handler(0x54, syscall_rmdir); + + kprintf("Halting for 5 seconds..."); + timer_sleep(5000); + + console_init(); + + std_console_device = (vfs_get_node(vfs_root, "/dev/console", true))->resource; + + char *argv[] = {"init", NULL}; + char* envp[] = { + "HOME=/", + "TERM=linux", + NULL, + }; + + char *init_path = "/bin/oksh"; + if (kernel_arguments.kernel_args & KERNEL_ARGS_INIT_PATH_GIVEN) { + init_path = kernel_arguments.init_binary_path; + } + + kprintf("Running init binary %s\n", init_path); + + if (!process_run_init(init_path, argv, envp, sched_get_running_thread()->mother_proc)) { + panic("Failed to run the init binary o algo\n"); + } + + for (;;) { + sched_yield(true); + } +} \ No newline at end of file diff --git a/src/main.h b/src/main.h new file mode 100644 index 0000000..625ee36 --- /dev/null +++ b/src/main.h @@ -0,0 +1,2 @@ + +_Noreturn void kernel_main(void *args); diff --git a/src/mm/memory.c b/src/mm/memory.c deleted file mode 100644 index c202f26..0000000 --- a/src/mm/memory.c +++ /dev/null @@ -1,68 +0,0 @@ -#include -#include "stddef.h" - -#include "pmm.h" -#include "libk/stdio.h" -#include "vmm.h" -#include -#include "mp/spinlock.h" - - - -void *memcpy(void *restrict dest, const void *restrict src, size_t n) { - uint8_t *restrict pdest = (uint8_t *restrict)dest; - const uint8_t *restrict psrc = (const uint8_t *restrict)src; - - for (size_t i = 0; i < n; i++) { - pdest[i] = psrc[i]; - } - - return dest; -} - -void *memset(void *s, int c, size_t n) { - uint8_t *p = (uint8_t *)s; - - for (size_t i = 0; i < n; i++) { - p[i] = (uint8_t)c; - } - - return s; -} - -void *memmove(void *dest, const void *src, size_t n) { - uint8_t *pdest = (uint8_t *)dest; - const uint8_t *psrc = (const uint8_t *)src; - - if ((uintptr_t)src > (uintptr_t)dest) { - for (size_t i = 0; i < n; i++) { - pdest[i] = psrc[i]; - } - } else if ((uintptr_t)src < (uintptr_t)dest) { - for (size_t i = n; i > 0; i--) { - pdest[i-1] = psrc[i-1]; - } - } - - return dest; -} - -int memcmp(const void *s1, const void *s2, size_t n) { - const uint8_t *p1 = (const uint8_t *)s1; - const uint8_t *p2 = (const uint8_t *)s2; - - for (size_t i = 0; i < n; i++) { - if (p1[i] != p2[i]) { - return p1[i] < p2[i] ? -1 : 1; - } - } - - return 0; -} - - - - - - - diff --git a/src/mm/memory.h b/src/mm/memory.h index 1aebd88..f6ffefc 100644 --- a/src/mm/memory.h +++ b/src/mm/memory.h @@ -3,22 +3,12 @@ #include "stddef.h" #include "slab.h" -#define ALIGN_DOWN(x, a) ((x) & ~((a)-1)) -#define ALIGN_UP(x, a) (((x) + (a)-1) & ~((a)-1)) -#define SIZEOF_ARRAY(ARRAY) (sizeof(ARRAY) / sizeof(ARRAY[0])) -#define DIV_ROUNDUP(A, B) \ - ({ \ - __auto_type _a_ = A; \ - __auto_type _b_ = B; \ - (_a_ + (_b_ - 1)) / _b_; \ - }) - - -void *memcpy(void *restrict dest, const void *restrict src, size_t n); +void *memcpy(void *d, const void *s, size_t n); void *memset(void *s, int c, size_t n); -int memcmp(const void *s1, const void *s2, size_t n); -void *memmove(void *dest, const void *src, size_t n); +void *memset32(void *d, uint32_t c, size_t n); +int memcmp(const void *l, const void *r, size_t n); +void *memmove(void *d, const void *s, size_t n); #define memzero(a, b) memset(a, 0, b) diff --git a/src/mm/mmap.c b/src/mm/mmap.c new file mode 100644 index 0000000..edc3777 --- /dev/null +++ b/src/mm/mmap.c @@ -0,0 +1,539 @@ +#include +#include +#include +#include "vmm.h" +#include "pmm.h" +#include "slab.h" +#include "libk/vec.h" +#include "mp/spinlock.h" +#include "arch/x86_64/cpu/cr.h" +#include "mmap.h" +#include "libk/errno.h" +#include "libk/debug.h" +#include "sched/sched.h" +#include "libk/misc.h" + +struct addr2range { + struct mmap_range_local *range; + size_t memory_page; + size_t file_page; +}; + +struct addr2range addr2range(struct pagemap *pagemap, uintptr_t virt) { + struct mmap_range_local *local_range = NULL; + int i = 0; + vec_foreach(&pagemap->mmap_ranges, local_range, i) { + if (virt < local_range->base || + virt >= local_range->base + local_range->length) { + continue; + } + + size_t memory_page = virt / PAGE_SIZE; + size_t file_page = local_range->offset / PAGE_SIZE + + (memory_page - local_range->base / PAGE_SIZE); + return (struct addr2range){.range = local_range, + .memory_page = memory_page, + .file_page = file_page}; + } + + return (struct addr2range){.range = NULL, .memory_page = 0, .file_page = 0}; +} + +static void dump_ranges(struct pagemap *pagemap) { + for (int i = 0; i < pagemap->mmap_ranges.length; i++) { + kprintf("%p - %p 0b%b\n", pagemap->mmap_ranges.data[i]->base, + pagemap->mmap_ranges.data[i]->base + + pagemap->mmap_ranges.data[i]->length, + pagemap->mmap_ranges.data[i]->prot); + } +} + +bool mmap_handle_pf(registers_t *reg) { + if ((reg->errorCode & 0x1) != 0) { + return false; + } + + // TODO: mmap can be expensive, consider enabling interrupts + // temporarily + uint64_t cr2 = read_cr("2"); + + struct thread *thread = sched_get_running_thread(); + if (thread == NULL) { + return false; + } + struct process *process = thread->mother_proc; + struct pagemap *pagemap = process->process_pagemap; + + spinlock_acquire_or_wait(&pagemap->lock); + + struct addr2range range = addr2range(pagemap, cr2); + struct mmap_range_local *local_range = range.range; + + spinlock_drop(&pagemap->lock); + + if (local_range == NULL) { + return false; + } + + void *page = NULL; + if ((local_range->flags & MAP_ANONYMOUS) != 0) { + page = pmm_allocz(1); + } else { + struct resource *res = page = local_range->global->res; + page = res->mmap(res, range.file_page, local_range->flags); + } + + if (page == NULL) { + return false; + } + + return mmap_page_in_range(local_range->global, + range.memory_page * PAGE_SIZE, (uintptr_t)page, + local_range->prot); +} + +bool mmap_range(struct pagemap *pagemap, uintptr_t virt, uintptr_t phys, + size_t length, int prot, int flags) { + flags |= MAP_ANONYMOUS; + + uintptr_t aligned_virt = ALIGN_DOWN(virt, PAGE_SIZE); + size_t aligned_length = ALIGN_UP(length + (virt - aligned_virt), PAGE_SIZE); + + struct mmap_range_global *global_range = NULL; + struct mmap_range_local *local_range = NULL; + + global_range = kmalloc(sizeof(struct mmap_range_global)); + if (global_range == NULL) { + errno = ENOMEM; + goto cleanup; + } + + global_range->shadow_pagemap = vmm_new_pagemap(); + if (global_range->shadow_pagemap == NULL) { + goto cleanup; + } + + global_range->base = aligned_virt; + global_range->length = aligned_length; + vec_init(&global_range->locals); + + local_range = kmalloc(sizeof(struct mmap_range_local)); + if (local_range == NULL) { + errno = ENOMEM; + goto cleanup; + } + + local_range->pagemap = pagemap; + local_range->global = global_range; + local_range->base = aligned_virt; + local_range->length = aligned_length; + local_range->prot = prot; + local_range->flags = flags; + + vec_push(&global_range->locals, local_range); + + spinlock_acquire_or_wait(&pagemap->lock); + + vec_push(&pagemap->mmap_ranges, local_range); + + spinlock_drop(&pagemap->lock); + + for (size_t i = 0; i < aligned_length; i += PAGE_SIZE) { + if (!mmap_page_in_range(global_range, aligned_virt + i, phys + i, + prot)) { + // FIXME: Page map is in inconsistent state at this point! + goto cleanup; + } + } + + return true; + +cleanup: + if (local_range != NULL) { + kfree(local_range); + } + if (global_range != NULL) { + if (global_range->shadow_pagemap != NULL) { + vmm_destroy_pagemap(global_range->shadow_pagemap); + } + + kfree(global_range); + } + return false; +} + +bool mmap_page_in_range(struct mmap_range_global *global, uintptr_t virt, + uintptr_t phys, int prot) { + uint64_t pt_flags = PAGE_READ | PAGE_USER; + + if ((prot & PROT_WRITE) != 0) { + pt_flags |= PAGE_WRITE; + } + if ((prot & PROT_EXEC) == 0) { + pt_flags |= PAGE_NO_EXECUTE; + } + + if (!vmm_map_page(global->shadow_pagemap, virt, phys, pt_flags, Size4KiB)) { + return false; + } + + struct mmap_range_local *local_range = NULL; + int i = 0; + vec_foreach(&global->locals, local_range, i) { + if (virt < local_range->base || + virt >= local_range->base + local_range->length) { + continue; + } + + if (!vmm_map_page(local_range->pagemap, virt, phys, pt_flags, + Size4KiB)) { + return false; + } + } + + return true; +} + +void *mmap(struct pagemap *pagemap, uintptr_t addr, size_t length, int prot, + int flags, struct resource *res, off_t offset) { + struct mmap_range_global *global_range = NULL; + struct mmap_range_local *local_range = NULL; + + if (length == 0) { + errno = EINVAL; + goto cleanup; + } + length = ALIGN_UP(length, PAGE_SIZE); + + if ((flags & MAP_ANONYMOUS) == 0 && res != NULL && !res->can_mmap) { + errno = ENODEV; + return MAP_FAILED; + } + + struct thread *thread = sched_get_running_thread(); + struct process *process = thread->mother_proc; + + uint64_t base = 0; + if ((flags & MAP_FIXED) != 0) { + if (!munmap(pagemap, addr, length)) { + goto cleanup; + } + base = addr; + } else { + base = process->mmap_anon_base; + process->mmap_anon_base += length + PAGE_SIZE; + } + + global_range = kmalloc(sizeof(struct mmap_range_global)); + if (global_range == NULL) { + errno = ENOMEM; + goto cleanup; + } + + global_range->shadow_pagemap = vmm_new_pagemap(); + if (global_range->shadow_pagemap == NULL) { + goto cleanup; + } + + global_range->base = base; + global_range->length = length; + global_range->res = res; + global_range->offset = offset; + + local_range = kmalloc(sizeof(struct mmap_range_local)); + if (local_range == NULL) { + goto cleanup; + } + + local_range->pagemap = pagemap; + local_range->global = global_range; + local_range->base = base; + local_range->length = length; + local_range->prot = prot; + local_range->flags = flags; + local_range->offset = offset; + + vec_push(&global_range->locals, local_range); + + spinlock_acquire_or_wait(&pagemap->lock); + + vec_push(&pagemap->mmap_ranges, local_range); + + spinlock_drop(&pagemap->lock); + + if (res != NULL) { + res->refcount++; + } + + return (void *)base; + +cleanup: + if (local_range != NULL) { + kfree(local_range); + } + if (global_range != NULL) { + if (global_range->shadow_pagemap != NULL) { + vmm_destroy_pagemap(global_range->shadow_pagemap); + } + + kfree(global_range); + } + return MAP_FAILED; +} + +bool munmap(struct pagemap *pagemap, uintptr_t addr, size_t length) { + if (length == 0) { + if (sched_get_running_thread()) { + errno = EINVAL; + } + return false; + } + length = ALIGN_UP(length, PAGE_SIZE); + + for (uintptr_t i = addr; i < addr + length; i += PAGE_SIZE) { + struct addr2range range = addr2range(pagemap, i); + if (range.range == NULL) { + continue; + } + + struct mmap_range_local *local_range = range.range; + struct mmap_range_global *global_range = local_range->global; + + uintptr_t snip_begin = i; + for (;;) { + i += PAGE_SIZE; + if (i >= local_range->base + local_range->length || + i >= addr + length) { + break; + } + } + + uintptr_t snip_end = i; + size_t snip_length = snip_end - snip_begin; + + spinlock_acquire_or_wait(&pagemap->lock); + + if (snip_begin > local_range->base && + snip_end < local_range->base + local_range->length) { + struct mmap_range_local *postsplit_range = + kmalloc(sizeof(struct mmap_range_local)); + if (postsplit_range == NULL) { + // FIXME: Page map is in inconsistent state at this point! + if (sched_get_running_thread()) { + errno = ENOMEM; + } + spinlock_drop(&pagemap->lock); + return false; + } + + postsplit_range->pagemap = local_range->pagemap; + postsplit_range->global = global_range; + postsplit_range->base = snip_end; + postsplit_range->length = + (local_range->base + local_range->length) - snip_end; + postsplit_range->offset = + local_range->offset + (off_t)(snip_end - local_range->base); + postsplit_range->prot = local_range->prot; + postsplit_range->flags = local_range->flags; + + vec_push(&pagemap->mmap_ranges, postsplit_range); + + local_range->length -= postsplit_range->length; + } + + spinlock_drop(&pagemap->lock); + + for (uintptr_t j = snip_begin; j < snip_end; j += PAGE_SIZE) { + vmm_unmap_page(pagemap, j, true); + } + + if (snip_length == local_range->length) { + if (global_range->locals.length == 1) { + if ((local_range->flags & MAP_ANONYMOUS) != 0) { + for (uintptr_t j = global_range->base; + j < global_range->base + global_range->length; + j += PAGE_SIZE) { + uintptr_t phys = + vmm_virt_to_phys(global_range->shadow_pagemap, j); + if (phys == INVALID_PHYS) { + continue; + } + + if (!vmm_unmap_page(global_range->shadow_pagemap, j, + true)) { + // FIXME: Page map is in inconsistent state at this + // point! + errno = EINVAL; + return false; + } + pmm_free((void *)phys, 1); + } + } else { + // TODO: res->unmap(); + } + } else { + vec_remove(&global_range->locals, local_range); + } + vec_remove(&pagemap->mmap_ranges, local_range); + kfree(local_range); + } else { + if (snip_begin == local_range->base) { + local_range->offset += snip_length; + local_range->base = snip_end; + } + local_range->length -= snip_length; + } + } + return true; +} + +bool mprotect(struct pagemap *pagemap, uintptr_t addr, size_t length, + int prot) { + if (length == 0) { + errno = EINVAL; + return false; + } + + length = ALIGN_UP(length, PAGE_SIZE); + + for (uintptr_t i = addr; i < addr + length; i += PAGE_SIZE) { + bool remove_local_range = false; + struct mmap_range_local *local_range = addr2range(pagemap, i).range; + + if (local_range->prot == prot) { + continue; + } + + uintptr_t snip_begin = i; + for (;;) { + i += PAGE_SIZE; + if (i >= local_range->base + local_range->length || + i >= addr + length) { + break; + } + } + uintptr_t snip_end = i; + uintptr_t snip_size = snip_end - snip_begin; + + spinlock_acquire_or_wait(&pagemap->lock); + if (snip_begin > local_range->base && + snip_end < local_range->base + local_range->length) { + struct mmap_range_local *postsplit_range = + kmalloc(sizeof(struct mmap_range_local)); + + postsplit_range->pagemap = local_range->pagemap; + postsplit_range->global = local_range->global; + postsplit_range->base = snip_end; + postsplit_range->length = + (local_range->base + local_range->length) - snip_end; + postsplit_range->offset = + local_range->offset + (off_t)(snip_end - local_range->base); + postsplit_range->prot = local_range->prot; + postsplit_range->flags = local_range->flags; + + vec_push(&pagemap->mmap_ranges, postsplit_range); + + local_range->length -= postsplit_range->length; + if (local_range->length == 0) { + remove_local_range = true; + } + } + + for (uintptr_t j = snip_begin; j < snip_end; j += PAGE_SIZE) { + uint64_t pt_flags = PAGE_READ | PAGE_USER; + + if ((prot & PROT_WRITE) != 0) { + pt_flags |= PAGE_WRITE; + } + if ((prot & PROT_EXEC) == 0) { + pt_flags |= PAGE_NO_EXECUTE; + } + vmm_remap_page(pagemap, j, pt_flags, true); + } + + uintptr_t new_offset = + local_range->offset + (snip_begin - local_range->base); + + if (snip_begin == local_range->base) { + local_range->offset += snip_size; + local_range->base = snip_end; + } + local_range->length -= snip_size; + if (local_range->length == 0) { + remove_local_range = true; + } + + struct mmap_range_local *new_range = + kmalloc(sizeof(struct mmap_range_local)); + + new_range->pagemap = local_range->pagemap; + new_range->global = local_range->global; + new_range->base = snip_begin; + new_range->length = snip_size; + new_range->offset = new_offset; + new_range->prot = prot; + new_range->flags = local_range->flags; + + vec_push(&pagemap->mmap_ranges, new_range); + + if (remove_local_range) { + vec_remove(&pagemap->mmap_ranges, local_range); + } + + spinlock_drop(&pagemap->lock); + } + + return true; +} + +void syscall_mmap(struct syscall_arguments *args) { + uintptr_t hint = args->args0; + size_t length = args->args1; + int prot = args->args2; + int flags = args->args3; + int fdnum = args->args4; + off_t offset = args->args5; + + void *ret = MAP_FAILED; + + struct thread *thread = sched_get_running_thread(); + struct process *proc = thread->mother_proc; + + struct resource *res = NULL; + if (fdnum != -1) { + struct f_descriptor *fd = fd_from_fdnum(proc, fdnum); + if (fd == NULL) { + goto cleanup; + } + + res = fd->description->res; + } else if (offset != 0) { + errno = EINVAL; + goto cleanup; + } + + ret = mmap(proc->process_pagemap, hint, length, prot, flags, res, offset); +cleanup: + args->ret = (uint64_t)ret; +} + +void syscall_munmap(struct syscall_arguments *args) { + uintptr_t addr = args->args0; + size_t length = args->args1; + + struct thread *thread = sched_get_running_thread(); + struct process *proc = thread->mother_proc; + + args->ret = munmap(proc->process_pagemap, addr, length) ? 0 : -1; +} + +void syscall_mprotect(struct syscall_arguments *args) { + uintptr_t addr = args->args0; + size_t length = args->args1; + int prot = args->args2; + + struct thread *thread = sched_get_running_thread(); + struct process *proc = thread->mother_proc; + + args->ret = mprotect(proc->process_pagemap, addr, length, prot) ? 0 : -1; +} diff --git a/src/mm/mmap.h b/src/mm/mmap.h new file mode 100644 index 0000000..7b5a752 --- /dev/null +++ b/src/mm/mmap.h @@ -0,0 +1,77 @@ +#pragma once +#include "libk/resource.h" +#include "libk/vec.h" +#include "mm/vmm.h" +#include "sched/syscall.h" + +#define PROT_NONE 0x00 +#define PROT_READ 0x01 +#define PROT_WRITE 0x02 +#define PROT_EXEC 0x04 + +#define MAP_FAILED ((void *)(-1)) +#define MAP_FILE 0x00 +#define MAP_SHARED 0x01 +#define MAP_PRIVATE 0x02 +#define MAP_FIXED 0x10 +#define MAP_ANON 0x20 +#define MAP_ANONYMOUS 0x20 +#define MAP_NORESERVE 0x4000 + +#define MS_ASYNC 0x01 +#define MS_INVALIDATE 0x02 +#define MS_SYNC 0x04 + +#define MCL_CURRENT 0x01 +#define MCL_FUTURE 0x02 + +#define POSIX_MADV_NORMAL 0 +#define POSIX_MADV_RANDOM 1 +#define POSIX_MADV_SEQUENTIAL 2 +#define POSIX_MADV_WILLNEED 3 +#define POSIX_MADV_DONTNEED 4 + +#define MADV_NORMAL 0 +#define MADV_RANDOM 1 +#define MADV_SEQUENTIAL 2 +#define MADV_WILLNEED 3 +#define MADV_DONTNEED 4 +#define MADV_FREE 8 + +#define MREMAP_MAYMOVE 1 +#define MREMAP_FIXED 2 + +#define MFD_CLOEXEC 1U +#define MFD_ALLOW_SEALING 2U + +struct mmap_range_global { + struct pagemap *shadow_pagemap; + vec_t(struct mmap_range_local *) locals; + struct resource *res; + uintptr_t base; + size_t length; + off_t offset; +}; + +struct mmap_range_local { + struct pagemap *pagemap; + struct mmap_range_global *global; + uintptr_t base; + size_t length; + off_t offset; + int prot; + int flags; +}; + +void mmap_list_ranges(struct pagemap *pagemap); +bool mmap_page_in_range(struct mmap_range_global *global, uintptr_t virt, + uintptr_t phys, int prot); +bool mmap_range(struct pagemap *pagemap, uintptr_t virt, uintptr_t phys, + size_t length, int prot, int flags); +void *mmap(struct pagemap *pagemap, uintptr_t addr, size_t length, int prot, + int flags, struct resource *res, off_t offset); +bool munmap(struct pagemap *pagemap, uintptr_t addr, size_t length); + +void syscall_mmap(struct syscall_arguments *args); +void syscall_munmap(struct syscall_arguments *args); +void syscall_mprotect(struct syscall_arguments *args); \ No newline at end of file diff --git a/src/mm/pmm.c b/src/mm/pmm.c index 4c8f641..83f6d5a 100644 --- a/src/mm/pmm.c +++ b/src/mm/pmm.c @@ -1,110 +1,144 @@ #include "pmm.h" #include "vmm.h" // for MEM_PHYS_OFFSET #include "memory.h" // memset -#include "libk/stdio.h" +#include "libk/debug.h" +#include +#include "mp/spinlock.h" +#include "libk/misc.h" +#include "arch/x86_64/asm/asm.h" -static spinlock_t memory_lock = SPINLOCK_INIT; +static spinlock_t memory_lock = {0}; static uint8_t *bitmap = NULL; static uint64_t total_page_count = 0; static uint64_t last_used_index = 0; static uint64_t free_pages = 0; -void pmm_init(struct limine_memmap_entry **memmap, size_t memmapentries) { - uint64_t highest_addr = 0; - size_t memmap_entries = memmapentries; - for (size_t i = 0; i < memmap_entries; i++) { - if (memmap[i]->type != LIMINE_MEMMAP_USABLE && - memmap[i]->type != LIMINE_MEMMAP_BOOTLOADER_RECLAIMABLE) - continue; - uint64_t top = memmap[i]->base + memmap[i]->length; - if (top > highest_addr) highest_addr = top; - } +static inline bool bitmap_test(void *bitmap, size_t bit) { + uint8_t *bitmap_u8 = bitmap; + return bitmap_u8[bit / 8] & (1 << (bit % 8)); +} - total_page_count = highest_addr / PAGE_SIZE; - size_t bitmap_size = ALIGN_UP(total_page_count / 8, PAGE_SIZE); - // Find place for bitmap - for (size_t i = 0; i < memmap_entries; i++) { +static inline void bitmap_set(void *bitmap, size_t bit) { + uint8_t *bitmap_u8 = bitmap; + bitmap_u8[bit / 8] |= (1 << (bit % 8)); +} - if (memmap[i]->type != LIMINE_MEMMAP_USABLE) continue; - if (memmap[i]->length >= bitmap_size) { - bitmap = (uint8_t *)(memmap[i]->base + MEM_PHYS_OFFSET); - memset(bitmap, 0xFF, bitmap_size); +static inline void bitmap_reset(void *bitmap, size_t bit) { + uint8_t *bitmap_u8 = bitmap; + bitmap_u8[bit / 8] &= ~(1 << (bit % 8)); +} - memmap[i]->base += bitmap_size; - memmap[i]->length -= bitmap_size; - break; - } - - } +void pmm_init(struct limine_memmap_entry **memmap, size_t memmap_entries) { + uint64_t highest_addr = 0; - // Mark usable pages free - for (size_t i = 0; i < memmap_entries; i++) { - if (memmap[i]->type != LIMINE_MEMMAP_USABLE) continue; - for (uint64_t j = 0; j < memmap[i]->length; j += PAGE_SIZE) { - size_t bit = (memmap[i]->base + j) / PAGE_SIZE; - bitmap[bit / 8] &= ~(1u << (bit % 8)); - free_pages++; - } - } - printf("done"); + // First, calculate how big the bitmap needs to be + for (size_t i = 0; i < memmap_entries; i++) { + if (memmap[i]->type != LIMINE_MEMMAP_USABLE && + memmap[i]->type != LIMINE_MEMMAP_BOOTLOADER_RECLAIMABLE) + continue; + + uint64_t top = memmap[i]->base + memmap[i]->length; + if (top > highest_addr) + highest_addr = top; + } + + total_page_count = highest_addr / PAGE_SIZE; + size_t bitmap_size = ALIGN_UP(total_page_count / 8, PAGE_SIZE); + + // Second, find a location with enough free pages to host the bitmap + for (size_t i = 0; i < memmap_entries; i++) { + if (memmap[i]->type != LIMINE_MEMMAP_USABLE) + continue; + + if (memmap[i]->length >= bitmap_size) { + bitmap = (void *)(memmap[i]->base + MEM_PHYS_OFFSET); + + // Initialise entire bitmap to 1 (non-free) + memset(bitmap, 0xFF, bitmap_size); + + memmap[i]->length -= bitmap_size; + memmap[i]->base += bitmap_size; + + break; + } + } + + // Third, populate free bitmap entries according to memory map + for (size_t i = 0; i < memmap_entries; i++) { + if (memmap[i]->type != LIMINE_MEMMAP_USABLE) + continue; + + for (uint64_t j = 0; j < memmap[i]->length; j += PAGE_SIZE) { + bitmap_reset(bitmap, (memmap[i]->base + j) / PAGE_SIZE); + free_pages++; + } + } + // spinlock_drop(&memory_lock); } static void *inner_alloc(size_t pages, size_t limit) { - size_t p = 0; - while (last_used_index < limit) { - if (!(bitmap[last_used_index / 8] & (1u << (last_used_index % 8)))) { - if (++p == pages) { - size_t page = last_used_index - pages + 1; - for (size_t i = page; i < page + pages; i++) - bitmap[i / 8] |= (1u << (i % 8)); - last_used_index = page + pages; - return (void *)(page * PAGE_SIZE); - } - } else { - p = 0; - } - last_used_index++; - } - return NULL; + size_t p = 0; + + while (last_used_index < limit) { + if (!bitmap_test(bitmap, last_used_index++)) { + if (++p == pages) { + size_t page = last_used_index - pages; + for (size_t i = page; i < last_used_index; i++) { + bitmap_set(bitmap, i); + } + return (void *)(page * PAGE_SIZE); + } + } else { + p = 0; + } + } + + return NULL; } +// Disabling interrupts here because if something locks this and then gets +// scheduled away it will deadlock others. +// TODO: Go lockless? void *pmm_alloc(size_t pages) { - spinlock_acquire_or_wait(&memory_lock); - size_t last = last_used_index; - void *ret = inner_alloc(pages, total_page_count); - if (!ret) { - last_used_index = 0; - ret = inner_alloc(pages, last); - } - if (ret) free_pages -= pages; - spinlock_drop(&memory_lock); - return ret; + bool state = int_toggle(false); + spinlock_acquire_or_wait(&memory_lock); + + size_t last = last_used_index; + void *ret = inner_alloc(pages, total_page_count); + if (ret == NULL) { + last_used_index = 0; + ret = inner_alloc(pages, last); + } + free_pages -= pages; + if (ret == NULL) { + panic("Out of memory!\n"); + } + spinlock_drop(&memory_lock); + int_toggle(state); + return ret; } void *pmm_allocz(size_t pages) { - void *ret = pmm_alloc(pages); - if (!ret) return NULL; + void *ret = pmm_alloc(pages); - uintptr_t vaddr = (uintptr_t)ret + MEM_PHYS_OFFSET; - // Sanity: make sure we're not zeroing something ridiculous - if (vaddr < MEM_PHYS_OFFSET || vaddr > MEM_PHYS_OFFSET + 0x8000000000ULL) { - printf("PMM: allocz addr 0x%lx looks wrong!\n", vaddr); - pmm_free(ret, pages); - return NULL; - } + if (ret != NULL) + memset((void *)((uint64_t)ret + MEM_PHYS_OFFSET), 0, pages * PAGE_SIZE); - uint64_t *p = (uint64_t *)vaddr; - for (size_t i = 0; i < (pages * PAGE_SIZE) / 8; i++) - p[i] = 0; - - return ret; + return ret; } void pmm_free(void *addr, size_t pages) { - spinlock_acquire_or_wait(&memory_lock); - size_t page = (uintptr_t)addr / PAGE_SIZE; - for (size_t i = 0; i < pages; i++) - bitmap[(page + i) / 8] &= ~(1u << ((page + i) % 8)); - free_pages += pages; - spinlock_drop(&memory_lock); -} \ No newline at end of file + bool state = int_toggle(false); + spinlock_acquire_or_wait(&memory_lock); + size_t page = (size_t)addr; + page /= PAGE_SIZE; + for (size_t i = page; i < page + pages; i++) + bitmap_reset(bitmap, i); + spinlock_drop(&memory_lock); + int_toggle(state); +} + +void pmm_get_memory_info(uint64_t *info) { + info[0] = total_page_count; + info[1] = free_pages; +} diff --git a/src/mm/slab.c b/src/mm/slab.c index adf8c69..e738aa4 100644 --- a/src/mm/slab.c +++ b/src/mm/slab.c @@ -5,7 +5,7 @@ #include "vmm.h" #include #include -#include "libk/stdio.h" +#include "libk/misc.h" struct slab { spinlock_t lock; @@ -35,8 +35,8 @@ static inline struct slab *slab_for(size_t size) { } static void create_slab(struct slab *slab, size_t ent_size) { - spinlock_init(&slab->lock); - slab->first_free = (void **)((uint64_t)pmm_allocz(1) + MEM_PHYS_OFFSET); + spinlock_init(slab->lock); + slab->first_free = (void **)((uint64_t)pmm_alloc(1) + MEM_PHYS_OFFSET); slab->ent_size = ent_size; size_t header_offset = ALIGN_UP(sizeof(struct slab_header), ent_size); @@ -101,19 +101,17 @@ void slab_init(void) { } void *slab_alloc(size_t size) { - struct slab *slab = slab_for(size); if (slab != NULL) { return alloc_from_slab(slab); } - + size_t page_count = DIV_ROUNDUP(size, PAGE_SIZE); - uint64_t ret = (uint64_t)pmm_allocz(page_count + 1); if ((void *)ret == NULL) { return NULL; } - + ret += MEM_PHYS_OFFSET; struct alloc_metadata *metadata = (struct alloc_metadata *)ret; diff --git a/src/mm/vmm.c b/src/mm/vmm.c index 70dff93..6412254 100644 --- a/src/mm/vmm.c +++ b/src/mm/vmm.c @@ -1,136 +1,165 @@ #include "vmm.h" #include "pmm.h" #include "memory.h" -#include "libk/stdio.h" +#include "libk/errno.h" +#include "mp/mp.h" +#include "libk/debug.h" +#include "arch/x86_64/cpu/reg.h" +#include +#include "mp/spinlock.h" +#include "fs/elf.h" +#include "sched/sched_types.h" +#include "mmap.h" +#include "arch/x86_64/sys/halt.h" +#include "sched/sched.h" +#include "arch/x86_64/cpu/cr.h" +#include "arch/x86_64/boot/isr.h" +#include "libk/misc.h" +bool mmap_handle_pf(registers_t *reg); + struct pagemap *kernel_pagemap = NULL; -__attribute__((used, section(".limine_requests"))) -volatile struct limine_hhdm_request hhdm_request = { - .id = LIMINE_HHDM_REQUEST_ID, .revision = 0 -}; +extern char text_start_addr[], text_end_addr[]; +extern char rodata_start_addr[], rodata_end_addr[]; +extern char data_start_addr[], data_end_addr[]; -__attribute__((used, section(".limine_requests"))) -volatile struct limine_executable_address_request kernel_address_request = { - .id = LIMINE_EXECUTABLE_ADDRESS_REQUEST_ID, .revision = 0 -}; +volatile struct limine_hhdm_request hhdm_request = {.id = LIMINE_HHDM_REQUEST, + .revision = 0}; -__attribute__((used, section(".limine_requests"))) -volatile struct limine_paging_mode_request paging_mode_request = { - .id = LIMINE_PAGING_MODE_REQUEST_ID, .revision = 0, .mode = LIMINE_PAGING_MODE_X86_64_4LVL -}; -static uint64_t *get_next_level(uint64_t *top_level, size_t idx, bool allocate) { - if (top_level[idx] & 1) - return (uint64_t *)((top_level[idx] & ~0xFFFULL) + MEM_PHYS_OFFSET); +volatile struct limine_kernel_address_request kernel_address_request = { + .id = LIMINE_KERNEL_ADDRESS_REQUEST, .revision = 0}; - if (!allocate) return NULL; +static volatile struct limine_paging_mode_request paging_mode_request = { + .id = LIMINE_PAGING_MODE_REQUEST, + .revision = 0, + .response = NULL, + .mode = LIMINE_PAGING_MODE_X86_64_5LVL}; - void *next = pmm_allocz(1); - if (!next) return NULL; +static uint64_t *get_next_level(uint64_t *top_level, size_t idx, + bool allocate) { + if (top_level[idx] & 1) { + return (uint64_t *)((size_t)(top_level[idx] & ~((uint64_t)0xFFF)) + + MEM_PHYS_OFFSET); + } - top_level[idx] = (uint64_t)next | 0b111; - return (uint64_t *)((uintptr_t)next + MEM_PHYS_OFFSET); + if (!allocate) { + return NULL; + } + + void *next_level = pmm_allocz(1); + if (next_level == NULL) { + return NULL; + } + top_level[idx] = (uint64_t)next_level | 0b111; + + return (uint64_t *)((uintptr_t)next_level + MEM_PHYS_OFFSET); } -void vmm_init(struct limine_memmap_entry **memmap, size_t memmapentries) { - if (!hhdm_request.response) { - printf("PRAISE BE TO LIMINE WE DON'T HAE HHDM RESP"); - } - size_t memmap_entries = memmapentries - 1; - kernel_pagemap = kmalloc(sizeof(struct pagemap)); - if (!kernel_pagemap) { - printf("VMM: OOm allocating kernel_pagemap\n"); - while(1) asm volatile ("hlt"); - } - spinlock_init(&kernel_pagemap->lock); - kernel_pagemap->top_level = (uint64_t *)((uintptr_t)pmm_allocz(1) + MEM_PHYS_OFFSET); - if (!kernel_pagemap->top_level) { - printf("VMM: OOM allocating PML4\n"); - while (1) asm volatile ("hlt"); - } +void vmm_init(struct limine_memmap_entry **memmap, size_t memmap_entries) { + kernel_pagemap = kmalloc(sizeof(struct pagemap)); + spinlock_init(kernel_pagemap->lock); + kernel_pagemap->top_level = + (uint64_t *)((uintptr_t)pmm_allocz(1) + MEM_PHYS_OFFSET); - // Identity map lower half + HHDM - for (uint64_t p = 256; p < 512; p++) - get_next_level(kernel_pagemap->top_level, p, true); + for (uint64_t p = 256; p < 512; p++) + get_next_level(kernel_pagemap->top_level, p, true); - // Map all physical memory (HHDM) - for (uint64_t p = 0; p < 0x8000000000ULL; p += 0x200000) // 2 MiB pages up to 256 GiB - vmm_map_page(kernel_pagemap, p + MEM_PHYS_OFFSET, p, PAGE_READ | PAGE_WRITE, Size2MiB); - + for (uint64_t p = 0; p < 4096UL * 1024 * 1024; p += 0x200000) { + vmm_map_page(kernel_pagemap, p + MEM_PHYS_OFFSET, p, 0b11, Size2MiB); + } + for (size_t i = 0; i < (memmap_entries - 1); i++) { + uint64_t base = memmap[i]->base; + uint64_t length = memmap[i]->length; + uint64_t top = base + length; - + if (base < 0x100000000) + base = 0x100000000; - // Map kernel sections with correct permissions - extern char __text_start_addr[], __text_end_addr[]; - extern char __rodata_start_addr[], __rodata_end_addr[]; - extern char __data_start_addr[], __data_end_addr[]; - extern char __bss_start_addr[], __bss_end_addr[]; + if (base >= top) + continue; - uintptr_t text_start = ALIGN_DOWN((uintptr_t)__text_start_addr, PAGE_SIZE); - uintptr_t text_end = ALIGN_UP((uintptr_t)__text_end_addr, PAGE_SIZE); - uintptr_t rodata_start = ALIGN_DOWN((uintptr_t)__rodata_start_addr, PAGE_SIZE); - uintptr_t rodata_end = ALIGN_UP((uintptr_t)__rodata_end_addr, PAGE_SIZE); - uintptr_t data_start = ALIGN_DOWN((uintptr_t)__data_start_addr, PAGE_SIZE); - uintptr_t data_end = ALIGN_UP((uintptr_t)__data_end_addr, PAGE_SIZE); - uintptr_t bss_start = ALIGN_DOWN((uintptr_t)__bss_start_addr, PAGE_SIZE); - uintptr_t bss_end = ALIGN_UP((uintptr_t)__bss_end_addr, PAGE_SIZE); + uint64_t aligned_base = ALIGN_DOWN(base, PAGE_SIZE); + uint64_t aligned_top = ALIGN_UP(top, PAGE_SIZE); + uint64_t aligned_length = aligned_top - aligned_base; - uint64_t paddr = kernel_address_request.response->physical_base; - uint64_t vaddr = kernel_address_request.response->virtual_base; + for (uint64_t j = 0; j < aligned_length; j += PAGE_SIZE) { + uint64_t page = aligned_base + j; + vmm_map_page(kernel_pagemap, page + MEM_PHYS_OFFSET, page, 0b11, + Size4KiB); + } + } + uintptr_t text_start = ALIGN_DOWN((uintptr_t)text_start_addr, PAGE_SIZE), + rodata_start = + ALIGN_DOWN((uintptr_t)rodata_start_addr, PAGE_SIZE), + data_start = ALIGN_DOWN((uintptr_t)data_start_addr, PAGE_SIZE), + text_end = ALIGN_UP((uintptr_t)text_end_addr, PAGE_SIZE), + rodata_end = ALIGN_UP((uintptr_t)rodata_end_addr, PAGE_SIZE), + data_end = ALIGN_UP((uintptr_t)data_end_addr, PAGE_SIZE); - // Map everything from kernel load address up to .text (this is the requests section) - for (uintptr_t va = vaddr; va < text_start; va += PAGE_SIZE) { - uint64_t pa = va - vaddr + paddr; - vmm_map_page(kernel_pagemap, va, pa, - PAGE_READ | PAGE_WRITE | PAGE_NO_EXECUTE, Size4KiB); - } - for (uintptr_t va = text_start; va < text_end; va += PAGE_SIZE) { - uint64_t pa = va - vaddr + paddr; - vmm_map_page(kernel_pagemap, va, pa, PAGE_READ, Size4KiB); - } - // .rodata : R-- + NX - for (uintptr_t va = rodata_start; va < rodata_end; va += PAGE_SIZE) { - uint64_t pa = va - vaddr + paddr; - vmm_map_page(kernel_pagemap, va, pa, PAGE_READ | PAGE_NO_EXECUTE, Size4KiB); - } + uint64_t paddr = kernel_address_request.response->physical_base; + uint64_t vaddr = kernel_address_request.response->virtual_base; - // .data : RW- + NX - for (uintptr_t va = data_start; va < data_end; va += PAGE_SIZE) { - uint64_t pa = va - vaddr + paddr; - vmm_map_page(kernel_pagemap, va, pa, PAGE_READ | PAGE_WRITE | PAGE_NO_EXECUTE, Size4KiB); - } + for (uintptr_t text_addr = text_start; text_addr < text_end; + text_addr += PAGE_SIZE) { + uintptr_t phys = text_addr - vaddr + paddr; + vmm_map_page(kernel_pagemap, text_addr, phys, 1, Size4KiB); + } - // .bss : RW- + NX (stack lives here) - for (uintptr_t va = bss_start; va < bss_end; va += PAGE_SIZE) { - uint64_t pa = va - vaddr + paddr; - vmm_map_page(kernel_pagemap, va, pa, PAGE_READ | PAGE_WRITE | PAGE_NO_EXECUTE, Size4KiB); - } + for (uintptr_t rodata_addr = rodata_start; rodata_addr < rodata_end; + rodata_addr += PAGE_SIZE) { + uintptr_t phys = rodata_addr - vaddr + paddr; + vmm_map_page(kernel_pagemap, rodata_addr, phys, 1 | 1ull << 63ull, + Size4KiB); + } - vmm_switch_pagemap(kernel_pagemap); - printf("VMM: switched to new kernel pagemap\n"); + for (uintptr_t data_addr = data_start; data_addr < data_end; + data_addr += PAGE_SIZE) { + uintptr_t phys = data_addr - vaddr + paddr; + vmm_map_page(kernel_pagemap, data_addr, phys, 0b11 | 1ull << 63ull, + Size4KiB); + } + // Switch to the new page map, dropping Limine's default one + kprintf("Switching to our pagemaps now!"); + vmm_switch_pagemap(kernel_pagemap); + + isr_register_handler(0xe, vmm_page_fault_handler); } void vmm_switch_pagemap(struct pagemap *pagemap) { - uint64_t phys_cr3 = (uintptr_t)pagemap->top_level - MEM_PHYS_OFFSET; - printf("VMM: loading CR3 with PML4 @ 0x%lx\n", phys_cr3); - asm volatile("mov %0, %%cr3" : : "r"((uint64_t)pagemap->top_level - MEM_PHYS_OFFSET) : "memory"); - printf("rah"); + asm volatile("mov cr3, %0" + : + : "r"((void *)((uint64_t)pagemap->top_level - MEM_PHYS_OFFSET)) + : "memory"); } -bool vmm_map_page(struct pagemap *pagemap, uint64_t virt, uint64_t phys, - uint64_t flags, enum page_size pg_size) { - spinlock_acquire_or_wait(&pagemap->lock); +// Creates a new dynamically allocated page map +struct pagemap *vmm_new_pagemap(void) { + struct pagemap *pagemap = kmalloc(sizeof(struct pagemap)); + spinlock_init(pagemap->lock); + pagemap->top_level = + (uint64_t *)((uintptr_t)pmm_allocz(1) + MEM_PHYS_OFFSET); + for (size_t i = 256; i < 512; i++) + pagemap->top_level[i] = kernel_pagemap->top_level[i]; + + vec_init(&pagemap->mmap_ranges); + return pagemap; +} + +bool vmm_map_page(struct pagemap *pagemap, uint64_t virt_addr, + uint64_t phys_addr, uint64_t flags, enum page_size pg_size) { + spinlock_acquire_or_wait(&pagemap->lock); // Calculate the indices in the various tables using the virtual address - size_t pml5_entry = (virt & ((uint64_t)0x1FF << 48)) >> 48; - size_t pml4_entry = (virt & ((uint64_t)0x1FF << 39)) >> 39; - size_t pml3_entry = (virt & ((uint64_t)0x1FF << 30)) >> 30; - size_t pml2_entry = (virt & ((uint64_t)0x1FF << 21)) >> 21; - size_t pml1_entry = (virt & ((uint64_t)0x1FF << 12)) >> 12; + size_t pml5_entry = (virt_addr & ((uint64_t)0x1FF << 48)) >> 48; + size_t pml4_entry = (virt_addr & ((uint64_t)0x1FF << 39)) >> 39; + size_t pml3_entry = (virt_addr & ((uint64_t)0x1FF << 30)) >> 30; + size_t pml2_entry = (virt_addr & ((uint64_t)0x1FF << 21)) >> 21; + size_t pml1_entry = (virt_addr & ((uint64_t)0x1FF << 12)) >> 12; uint64_t *pml5, *pml4, *pml3, *pml2, *pml1; @@ -159,7 +188,7 @@ level4: } if (pg_size == Size2MiB) { - pml2[pml2_entry] = phys | flags | (1 << 7); + pml2[pml2_entry] = phys_addr | flags | (1 << 7); spinlock_drop(&pagemap->lock); return true; } @@ -171,10 +200,69 @@ level4: return false; } - pml1[pml1_entry] = phys | flags; + pml1[pml1_entry] = phys_addr | flags; + spinlock_drop(&pagemap->lock); + return true; +} - spinlock_drop(&pagemap->lock); - return true; // simplified for this response +bool vmm_remap_page(struct pagemap *pagemap, uintptr_t virt, uint64_t flags, + bool locked) { + if (!locked) { + spinlock_acquire_or_wait(&pagemap->lock); + } + size_t pml5_entry = (virt & ((uint64_t)0x1FF << 48)) >> 48; + size_t pml4_entry = (virt & ((uint64_t)0x1FF << 39)) >> 39; + size_t pml3_entry = (virt & ((uint64_t)0x1FF << 30)) >> 30; + size_t pml2_entry = (virt & ((uint64_t)0x1FF << 21)) >> 21; + size_t pml1_entry = (virt & ((uint64_t)0x1FF << 12)) >> 12; + + uint64_t *pml5, *pml4, *pml3, *pml2, *pml1; + + if (paging_mode_request.response->mode == LIMINE_PAGING_MODE_X86_64_5LVL) { + pml5 = pagemap->top_level; + goto level5; + } else { + pml4 = pagemap->top_level; + goto level4; + } + +level5: + pml4 = get_next_level(pml5, pml5_entry, false); + if (pml4 == NULL) { + goto die; + } +level4: + pml3 = get_next_level(pml4, pml4_entry, false); + if (pml3 == NULL) { + goto die; + } + + pml2 = get_next_level(pml3, pml3_entry, false); + if (pml2 == NULL) { + goto die; + } + + pml1 = get_next_level(pml2, pml2_entry, false); + if (pml1 == NULL) { + goto die; + } + + if ((pml1[pml1_entry] & 1) == 0) { + die: + if (!locked) { + spinlock_drop(&pagemap->lock); + } + return false; + } + + pml1[pml1_entry] = (((pml1[pml1_entry]) & 0xffffffffff000)) | flags; + + asm volatile("invlpg [%0]" : : "r"(virt) : "memory"); + + if (!locked) { + spinlock_drop(&pagemap->lock); + } + return true; } bool vmm_unmap_page(struct pagemap *pagemap, uintptr_t virt, bool locked) { @@ -229,7 +317,7 @@ level4: pml1[pml1_entry] = 0; - asm volatile("invlpg (%0)" : : "r"(virt) : "memory"); + asm volatile("invlpg [%0]" : : "r"(virt) : "memory"); if (!locked) { spinlock_drop(&pagemap->lock); @@ -279,54 +367,237 @@ level4: return NULL; } - spinlock_drop(&pagemap->lock); + // spinlock_drop(&pagemap->lock); return &pml1[pml1_entry]; } +uint64_t vmm_virt_to_phys(struct pagemap *pagemap, uint64_t virt_addr) { + spinlock_acquire_or_wait(&pagemap->lock); + uint64_t *pte = vmm_virt_to_pte(pagemap, virt_addr, false); + spinlock_drop(&pagemap->lock); + if (pte == NULL || (((*pte) & ~0xffffffffff000) & 1) == 0) + return INVALID_PHYS; -uint64_t vmm_virt_to_phys(struct pagemap *pagemap, uint64_t virt) { - spinlock_acquire_or_wait(&pagemap->lock); - - size_t pml4_entry = (virt & ((uint64_t)0x1FF << 39)) >> 39; - size_t pml3_entry = (virt & ((uint64_t)0x1FF << 30)) >> 30; - size_t pml2_entry = (virt & ((uint64_t)0x1FF << 21)) >> 21; - size_t pml1_entry = (virt & ((uint64_t)0x1FF << 12)) >> 12; - - uint64_t *pml4 = pagemap->top_level; - - uint64_t *pml3 = get_next_level(pml4, pml4_entry, false); - if (!pml3) goto fail; - - uint64_t *pml2 = get_next_level(pml3, pml3_entry, false); - if (!pml2) goto fail; - - // Check for 2MiB huge page (PS bit = bit 7) - if (pml2[pml2_entry] & (1 << 7)) { - uint64_t phys = (pml2[pml2_entry] & ~0x1FFFFFULL) // 2MiB-aligned base - + (virt & 0x1FFFFFULL); // + offset within 2MiB - spinlock_drop(&pagemap->lock); - return phys; - } - - uint64_t *pml1 = get_next_level(pml2, pml2_entry, false); - if (!pml1 || !(pml1[pml1_entry] & 1)) goto fail; - - uint64_t phys = (pml1[pml1_entry] & 0x000ffffffffff000ULL) - + (virt & 0xFFFULL); - spinlock_drop(&pagemap->lock); - return phys; - -fail: - spinlock_drop(&pagemap->lock); - printf("Invalid Phys!\n"); - return INVALID_PHYS; + return ((*pte) & 0xffffffffff000); } -uintptr_t find_free_vaddr(struct pagemap *pm, size_t len) { - // Very naive for now - start from a high address - static uintptr_t next = 0x700050000000ULL; - - uintptr_t addr = next; - next += ALIGN_UP(len, 0x1000000ULL); // 16 MiB alignment for simplicity - return addr; +uint64_t vmm_virt_to_kernel(struct pagemap *pagemap, uint64_t virt_addr) { + uint64_t aligned_virtual_address = ALIGN_DOWN(virt_addr, PAGE_SIZE); + uint64_t phys_addr = vmm_virt_to_phys(pagemap, virt_addr); + if (phys_addr == INVALID_PHYS) { + return 0; + } + return (phys_addr + MEM_PHYS_OFFSET + virt_addr - aligned_virtual_address); +} + +void vmm_page_fault_handler(registers_t *reg) { + if (mmap_handle_pf(reg)) { + return; + } + + uint64_t faulting_address = read_cr("2"); + bool present = reg->errorCode & 0x1; + bool read_write = reg->errorCode & 0x2; + bool user_supervisor = reg->errorCode & 0x4; + bool reserved = reg->errorCode & 0x8; + bool execute = reg->errorCode & 0x10; + + if (reg->cs & 0x3) { + struct thread *thrd = sched_get_running_thread(); + kprintf("Killing user thread tid %d under process %s for Page Fault\n", + thrd->tid, thrd->mother_proc->name); + kprintf("User thread crashed at address: %p\n", reg->rip); + // backtrace_unsafe((void *)reg->rbp); +#if 0 + kprintf("RIP: %p RBP: %p RSP: %p\n", reg->rip, reg->rbp, reg->rsp); + kprintf("RAX: %p RBX: %p RCX: %p\n", reg->rax, reg->rbx, reg->rcx); + kprintf("RDX: %p RDI: %p RSI: %p\n", reg->rdx, reg->rdi, reg->rsi); + kprintf("R8 : %p R9 : %p R10: %p\n", reg->r8, reg->r9, reg->r10); + kprintf("R11: %p R12: %p R13: %p\n", reg->r11, reg->r12, reg->r13); + kprintf("R14: %p R15: %p ERR: 0b%b\n", reg->r14, reg->r15, + reg->errorCode); + kprintf("CS : %p SS : %p RFLAGS: %p\n", reg->cs, reg->ss, reg->rflags); + kprintf("FS: %p UGS: %p KGS: %p\n", read_fs_base(), read_user_gs(), + read_kernel_gs()); + kprintf("Page fault at %p present: %s, read/write: %s, " + "user/supervisor: %s, reserved: %s, execute: %s\n", + faulting_address, present ? "P" : "NP", read_write ? "R" : "RW", + user_supervisor ? "U" : "S", reserved ? "R" : "NR", + execute ? "X" : "NX"); +#endif + thread_kill(thrd, true); + } else { + halt_other_cpus(); + kprintffos(0, "AH! UNHANDLED EXCEPTION!\n"); + kprintffos(0, "RIP: %p RBP: %p RSP: %p\n", reg->rip, reg->rbp, + reg->rsp); + kprintffos(0, "RAX: %p RBX: %p RCX: %p\n", reg->rax, reg->rbx, + reg->rcx); + kprintffos(0, "RDX: %p RDI: %p RSI: %p\n", reg->rdx, reg->rdi, + reg->rsi); + kprintffos(0, "R8 : %p R9 : %p R10: %p\n", reg->r8, reg->r9, reg->r10); + kprintffos(0, "R11: %p R12: %p R13: %p\n", reg->r11, reg->r12, + reg->r13); + kprintffos(0, "R14: %p R15: %p ERR: 0b%b\n", reg->r14, reg->r15, + reg->errorCode); + kprintffos(0, "CS : %p SS : %p RFLAGS: %p\n", reg->cs, reg->ss, + reg->rflags); + kprintffos(0, "FS : %p UGS: %p KGS: %p\n", read_fs_base(), + read_user_gs(), read_kernel_gs()); + put_to_fb = true; + panic_((void *)reg->rip, (void *)reg->rbp, + "Page fault at %p present: %s, read/write: %s, " + "user/supervisor: %s, reserved: %s, execute: %s\n", + faulting_address, present ? "P" : "NP", read_write ? "R" : "RW", + user_supervisor ? "U" : "S", reserved ? "R" : "NR", + execute ? "X" : "NX"); + } +} + +struct pagemap *vmm_fork_pagemap(struct pagemap *pagemap) { + spinlock_acquire_or_wait(&pagemap->lock); + + struct pagemap *new_pagemap = vmm_new_pagemap(); + if (new_pagemap == NULL) { + goto cleanup; + } + + struct mmap_range_local *local_range = NULL; + int idxn = 0; + vec_foreach(&pagemap->mmap_ranges, local_range, idxn) { + struct mmap_range_global *global_range = local_range->global; + + struct mmap_range_local *new_local_range = + kmalloc(sizeof(struct mmap_range_local)); + if (new_local_range == NULL) { + goto cleanup; + } + + *new_local_range = *local_range; + new_local_range->pagemap = new_pagemap; + + if (global_range->res != NULL) { + global_range->res->refcount++; + } + + if ((local_range->flags & MAP_SHARED) != 0) { + vec_push(&global_range->locals, new_local_range); + for (uintptr_t i = local_range->base; + i < local_range->base + local_range->length; i += PAGE_SIZE) { + uint64_t *old_pte = vmm_virt_to_pte(pagemap, i, false); + if (old_pte == NULL) { + continue; + } + + uint64_t *new_pte = vmm_virt_to_pte(new_pagemap, i, true); + if (new_pte == NULL) { + goto cleanup; + } + *new_pte = *old_pte; + } + } else { + struct mmap_range_global *new_global_range = + kmalloc(sizeof(struct mmap_range_global)); + if (new_global_range == NULL) { + goto cleanup; + } + + new_global_range->shadow_pagemap = vmm_new_pagemap(); + if (new_global_range->shadow_pagemap == NULL) { + goto cleanup; + } + + new_global_range->base = global_range->base; + new_global_range->length = global_range->length; + new_global_range->res = global_range->res; + new_global_range->offset = global_range->offset; + + vec_push(&new_global_range->locals, new_local_range); + + // TODO: CoW for MAP_PRIVATE? + // if ((local_range->flags & MAP_ANONYMOUS) != 0) { + for (uintptr_t i = local_range->base; + i < local_range->base + local_range->length; i += PAGE_SIZE) { + uint64_t *old_pte = vmm_virt_to_pte(pagemap, i, false); + if (old_pte == NULL || (((*old_pte) & 0xfff) & 1) == 0) { + continue; + } + spinlock_acquire_or_wait(&new_pagemap->lock); + uint64_t *new_pte = vmm_virt_to_pte(new_pagemap, i, true); + spinlock_drop(&new_pagemap->lock); + if (new_pte == NULL) { + goto cleanup; + } + spinlock_acquire_or_wait( + &new_global_range->shadow_pagemap->lock); + uint64_t *new_spte = + vmm_virt_to_pte(new_global_range->shadow_pagemap, i, true); + spinlock_drop(&new_global_range->shadow_pagemap->lock); + if (new_spte == NULL) { + goto cleanup; + } + + void *old_page = (void *)((*old_pte) & 0xffffffffff000); + void *page = pmm_alloc(1); + + if (page == NULL) { + goto cleanup; + } + + memcpy((void *)((uintptr_t)page + MEM_PHYS_OFFSET), + (void *)((uintptr_t)old_page + MEM_PHYS_OFFSET), + PAGE_SIZE); + + *new_pte = ((*old_pte) & 0xfff) | (uint64_t)page; + *new_spte = *new_pte; + } + // } else { + // kprintf("WARNING: Non anon fork\n"); + // } + } + + vec_push(&new_pagemap->mmap_ranges, new_local_range); + } + + spinlock_drop(&pagemap->lock); + return new_pagemap; + +cleanup: + spinlock_drop(&pagemap->lock); + if (new_pagemap != NULL) { + vmm_destroy_pagemap(new_pagemap); + } + return NULL; +} + +static void destroy_level(uint64_t *pml, size_t start, size_t end, int level) { + if (level == 0) { + return; + } + + for (size_t i = start; i < end; i++) { + uint64_t *next_level = get_next_level(pml, i, false); + if (next_level == NULL) { + continue; + } + + destroy_level(next_level, 0, 512, level - 1); + } + + pmm_free((void *)((uintptr_t)pml - MEM_PHYS_OFFSET), 1); +} + +void vmm_destroy_pagemap(struct pagemap *pagemap) { + while (pagemap->mmap_ranges.length > 0) { + struct mmap_range_local *local_range = pagemap->mmap_ranges.data[0]; + munmap(pagemap, local_range->base, local_range->length); + } + + destroy_level( + pagemap->top_level, 0, 256, + (paging_mode_request.response->mode == LIMINE_PAGING_MODE_X86_64_5LVL) + ? 5 + : 4); + + kfree(pagemap); } diff --git a/src/mm/vmm.h b/src/mm/vmm.h index fe1850a..265ba6c 100644 --- a/src/mm/vmm.h +++ b/src/mm/vmm.h @@ -6,42 +6,42 @@ #include #include #include - -#define PAGE_SIZE 4096ULL +#include "libk/vec.h" +#include "arch/x86_64/cpu/reg.h" extern volatile struct limine_hhdm_request hhdm_request; +extern volatile struct limine_kernel_address_request kernel_address_request; +#define PAGE_SIZE ((size_t)4096) #define MEM_PHYS_OFFSET (hhdm_request.response->offset) +#define KERNEL_BASE (kernel_address_request.response->virtual_base) +#define INVALID_PHYS ((uint64_t)0xffffffffffffffff) - -extern volatile struct limine_executable_address_request kernel_address_request; - -#define INVALID_PHYS 0xffffffffffffffffULL - -#define PAGE_READ (1ULL << 0) -#define PAGE_WRITE (1ULL << 1) -#define PAGE_USER (1ULL << 2) -#define PAGE_NO_EXECUTE (1ULL << 63) - -#define PTE_PRESENT (1ULL << 0) -#define PTE_WRITABLE (1ULL << 1) -#define PTE_USER (1ULL << 2) +#define PAGE_READ (0b1) +#define PAGE_WRITE (0b10) +#define PAGE_USER (0b100) +#define PAGE_NO_EXECUTE (1ull << 63ull) struct pagemap { - spinlock_t lock; - uint64_t *top_level; + spinlock_t lock; + uint64_t *top_level; + vec_t(struct mmap_range_local *) mmap_ranges; }; extern struct pagemap *kernel_pagemap; -enum page_size { Size4KiB, Size2MiB }; +enum page_size { Size4KiB, Size2MiB, Size1GiB }; void vmm_init(struct limine_memmap_entry **memmap, size_t memmap_entries); void vmm_switch_pagemap(struct pagemap *pagemap); -bool vmm_map_page(struct pagemap *pagemap, uint64_t virt, uint64_t phys, - uint64_t flags, enum page_size pg_size); -uint64_t vmm_virt_to_phys(struct pagemap *pagemap, uint64_t virt); -uint64_t *vmm_virt_to_pte(struct pagemap *pagemap, uintptr_t virt_addr, - bool allocate); +struct pagemap *vmm_new_pagemap(void); +bool vmm_map_page(struct pagemap *pagemap, uint64_t virt_addr, + uint64_t phys_addr, uint64_t flags, enum page_size pg_size); +bool vmm_remap_page(struct pagemap *pagemap, uintptr_t virt, uint64_t flags, + bool locked); bool vmm_unmap_page(struct pagemap *pagemap, uintptr_t virt, bool locked); -uintptr_t find_free_vaddr(struct pagemap *pm, size_t len); \ No newline at end of file +uint64_t vmm_virt_to_phys(struct pagemap *pagemap, uint64_t virt_addr); +uint64_t vmm_virt_to_kernel(struct pagemap *pagemap, uint64_t virt_addr); +void vmm_page_fault_handler(registers_t *reg); +struct pagemap *vmm_fork_pagemap(struct pagemap *pagemap); +void vmm_destroy_pagemap(struct pagemap *pagemap); diff --git a/src/mp/futex.c b/src/mp/futex.c deleted file mode 100644 index 2194166..0000000 --- a/src/mp/futex.c +++ /dev/null @@ -1,84 +0,0 @@ -#include "futex.h" -#include "mm/memory.h" -#include "string.h" -#include "sched/scheduler.h" -#include "libk/stdio.h" - -#define FUTEX_BUCKETS 256 - -struct futex_waiter { - task_t *task; - int *uaddr; - struct futex_waiter *next; -}; - -static struct futex_waiter *g_futex_table[FUTEX_BUCKETS]; - -static inline uint32_t futex_hash(int *uaddr) { - return ((uintptr_t)uaddr >> 3) & (FUTEX_BUCKETS - 1); -} - -int futex_wait(int *uaddr, int expected) -{ - if (!uaddr) return -1; - - /* 1. check value in user memory */ - if (*uaddr != expected) - return -1; - - uint32_t h = futex_hash(uaddr); - - struct futex_waiter *w = kmalloc(sizeof(*w)); - if (!w) return -1; - - w->task = sched_current(); - w->uaddr = uaddr; - - /* 2. insert into bucket */ - w->next = g_futex_table[h]; - g_futex_table[h] = w; - - /* 3. block task */ - sched_block(TASK_INTERRUPTIBLE); - - return 0; -} - -int futex_wake(int *uaddr, int count) -{ - if (!uaddr || count <= 0) - return 0; - - uint32_t h = futex_hash(uaddr); - - struct futex_waiter **prev = &g_futex_table[h]; - struct futex_waiter *cur = g_futex_table[h]; - - int woken = 0; - - while (cur && woken < count) { - if (cur->uaddr == uaddr) { - - task_t *task = cur->task; - - /* remove from list */ - *prev = cur->next; - - struct futex_waiter *tmp = cur; - cur = cur->next; - - kfree(tmp); - - /* wake task */ - sched_wake(task); - - woken++; - continue; - } - - prev = &cur->next; - cur = cur->next; - } - - return woken; -} diff --git a/src/mp/futex.h b/src/mp/futex.h deleted file mode 100644 index c673b1c..0000000 --- a/src/mp/futex.h +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once -#include -#include -#include "sched/scheduler.h" - -#define FUTEX_WAIT 0 -#define FUTEX_WAKE 1 - -int futex_wait(int *uaddr, int expected); -int futex_wake(int *uaddr, int count); \ No newline at end of file diff --git a/src/mp/mp.c b/src/mp/mp.c new file mode 100644 index 0000000..08b3c89 --- /dev/null +++ b/src/mp/mp.c @@ -0,0 +1,211 @@ +#include +#include +#include "mp.h" +#include "arch/x86_64/sys/apic.h" +#include "arch/x86_64/boot/gdt.h" +#include "arch/x86_64/boot/idt.h" +#include "libk/debug.h" +#include "arch/x86_64/sys/prcb.h" +#include "mm/slab.h" +#include "arch/x86_64/cpu/io.h" +#include "arch/x86_64/cpu/cr.h" +#include "arch/x86_64/cpu/features.h" +#include +#include "mm/memory.h" +#include "mm/pmm.h" +#include "mm/vmm.h" +#include "libk/kargs.h" +#include "arch/x86_64/asm/asm.h" +#include "fs/elf.h" +#include "sched/sched_types.h" + + + + +bool is_smp = false; +static spinlock_t smp_lock = {0}; +struct prcb *prcbs = NULL; +uint32_t smp_bsp_lapic_id = 0; +static size_t cpu_count = 0; +static size_t initialized_cpus = 0; + +extern void amd_syscall_entry(void); + +static void smp_cpu_init(struct limine_smp_info *smp_info) { + cli(); + struct prcb *prcb_local = (void *)smp_info->extra_argument; + gdt_reload(); + idt_reload(); + + prcb_local->sched_ticks = 0; + + spinlock_acquire_or_wait(&smp_lock); + + gdt_load_tss((size_t)(&prcb_local->cpu_tss)); + + set_kernel_gs((uint64_t)prcb_local); + set_user_gs((uint64_t)prcb_local); + + // SSE/SSE2 + uint64_t cr0 = 0; + cr0 = read_cr("0"); + cr0 &= ~(1 << 2); + cr0 |= (1 << 1); + write_cr("0", cr0); + + uint64_t cr4 = 0; + cr4 = read_cr("4"); + cr4 |= (3 << 9); + write_cr("4", cr4); + + // Enable syscall in EFER + wrmsr(0xc0000080, rdmsr(0xc0000080) | 1); + + // Set up syscall + wrmsr(0xc0000081, 0x13000800000000); + // Syscall entry address + wrmsr(0xc0000082, (uint64_t)amd_syscall_entry); + // Flags mask + wrmsr(0xc0000084, (uint64_t) ~((uint32_t)0x2)); + + // Security features + uint32_t a = 0, b = 0, c = 0, d = 0; + __get_cpuid(7, &a, &b, &c, &d); + if (b & CPUID_SMEP) { + cr4 = read_cr("4"); + cr4 |= (1 << 20); // Enable SMEP + write_cr("4", cr4); + } + + if (b & CPUID_SMAP) { + cr4 = read_cr("4"); + cr4 |= (1 << 21); // Enable SMAP + write_cr("4", cr4); + asm("clac"); + } + + if (c & CPUID_UMIP) { + cr4 = read_cr("4"); + cr4 |= (1 << 11); // Enable UMIP + write_cr("4", cr4); + } + + __get_cpuid(1, &a, &b, &c, &d); + if (c & bit_XSAVE) { + // Enable XSAVE and x{get,set}bv + cr4 = read_cr("4"); + cr4 |= (uint64_t)1 << 18; + write_cr("4", cr4); + + uint64_t xcr0 = 0; + xcr0 |= (uint64_t)1 << 0; + xcr0 |= (uint64_t)1 << 1; + + if (c & bit_AVX) { + xcr0 |= (uint64_t)1 << 2; + } + + __get_cpuid(7, &a, &b, &c, &d); + if (b & bit_AVX512F) { + xcr0 |= (uint64_t)1 << 5; + xcr0 |= (uint64_t)1 << 6; + xcr0 |= (uint64_t)1 << 7; + } + + wrxcr(0, xcr0); + + __cpuid(13, a, b, c, d); + + prcb_local->fpu_storage_size = c; + prcb_local->fpu_save = xsave; + prcb_local->fpu_restore = xrstor; + } else { + prcb_local->fpu_storage_size = 512; + prcb_local->fpu_save = fxsave; + prcb_local->fpu_restore = fxrstor; + } + + prcb_local->running_thread = NULL; + lapic_init(smp_info->lapic_id); + + if (prcb_local->lapic_id != smp_bsp_lapic_id) { + kprintf("CPU%u: I am alive!\n", prcb_return_current_cpu()->cpu_number); + initialized_cpus++; + spinlock_drop(&smp_lock); + timer_sched_oneshot(48, 20000); + for (;;) { + sti(); + halt(); + } + } + kprintf("CPU%u: I am alive and I am the BSP!\n", + prcb_return_current_cpu()->cpu_number); + initialized_cpus++; + spinlock_drop(&smp_lock); +} + +void mp_init(struct limine_smp_response *smp_info) { + kprintf("SMP: Total number of Processors Installed %u\n", + smp_info->cpu_count); + smp_bsp_lapic_id = smp_info->bsp_lapic_id; + + if (kernel_arguments.kernel_args & KERNEL_ARGS_CPU_COUNT_GIVEN) { + cpu_count = kernel_arguments.cpu_count; + if (cpu_count > smp_info->cpu_count || cpu_count < 1) { + cpu_count = smp_info->cpu_count; + kprintf("SMP: Download more CPU today!\n"); + } + kprintf("SMP: Setting up only %u Processors\n", cpu_count); + } else { + cpu_count = smp_info->cpu_count; + } + + prcbs = kcalloc(cpu_count, struct prcb); + + memzero(prcbs, sizeof(struct prcb) * cpu_count); + + for (size_t i = 0; i < cpu_count; i++) { + struct limine_smp_info *cpu = smp_info->cpus[i]; + cpu->extra_argument = (uint64_t)&prcbs[i]; + prcbs[i].cpu_number = i; + prcbs[i].lapic_id = cpu->lapic_id; + prcbs[i].cpu_tss.rsp0 = + (uint64_t)pmm_allocz(CPU_STACK_SIZE / PAGE_SIZE) + MEM_PHYS_OFFSET + + CPU_STACK_SIZE; + prcbs[i].cpu_tss.ist1 = + (uint64_t)pmm_allocz(CPU_STACK_SIZE / PAGE_SIZE) + MEM_PHYS_OFFSET + + CPU_STACK_SIZE; + + if (cpu->lapic_id != smp_bsp_lapic_id) { + cpu->goto_address = smp_cpu_init; + } else { + smp_cpu_init(cpu); + } + } + + while (initialized_cpus != cpu_count) { + pause(); + } + + kprintf("SMP: %u CPUs installed in the system\n", + prcb_return_installed_cpus()); + is_smp = true; +} + + +size_t prcb_return_installed_cpus(void) { + return initialized_cpus; +} + +struct prcb *prcb_return_current_cpu(void) { + uint64_t flags = 0; + asm volatile("pushfq; pop %0" : "=rm"(flags)); + bool interrupts_enabled = flags & (1 << 9); + if (interrupts_enabled && is_smp) { + panic("Calling prcb_return_current_cpu with interrupts enabled is a " + "bug\n"); + } + uint64_t cpu_number; + asm volatile("mov %0, qword ptr gs:[0]" : "=r"(cpu_number) : : "memory"); + return &prcbs[cpu_number]; +} diff --git a/src/mp/mp.h b/src/mp/mp.h new file mode 100644 index 0000000..b4c4828 --- /dev/null +++ b/src/mp/mp.h @@ -0,0 +1,70 @@ +#pragma once +#include "spinlock.h" +#include "arch/x86_64/sys/prcb.h" +#include "arch/x86_64/cpu/msr.h" +#include +#include "arch/x86_64/asm/asm.h" + +static inline void set_kernel_gs(uint64_t address) { + wrmsr(0xc0000102, address); +} + +static inline uintptr_t read_kernel_gs(void) { + return rdmsr(0xc0000102); +} + +static inline void set_user_gs(uint64_t address) { + wrmsr(0xc0000101, address); +} + +static inline uintptr_t read_user_gs(void) { + return rdmsr(0xc0000101); +} + +static inline void set_fs_base(uint64_t address) { + wrmsr(0xc0000100, address); +} + +static inline uintptr_t read_fs_base(void) { + return rdmsr(0xc0000100); +} + +static inline void swapgs(void) { + asm volatile("swapgs" ::: "memory"); +} + +extern size_t fpu_storage_size; +extern void (*fpu_save)(void *ctx); +extern void (*fpu_restore)(void *ctx); + +static inline void wrxcr(uint32_t reg, uint64_t value) { + uint32_t a = value; + uint32_t d = value >> 32; + asm volatile("xsetbv" ::"a"(a), "d"(d), "c"(reg) : "memory"); +} + +static inline void xsave(void *ctx) { + asm volatile("xsave %0" + : "+m"(FLAT_PTR(ctx)) + : "a"(0xffffffff), "d"(0xffffffff) + : "memory"); +} + +static inline void xrstor(void *ctx) { + asm volatile("xrstor %0" + : + : "m"(FLAT_PTR(ctx)), "a"(0xffffffff), "d"(0xffffffff) + : "memory"); +} + +static inline void fxsave(void *ctx) { + asm volatile("fxsave %0" : "+m"(FLAT_PTR(ctx)) : : "memory"); +} + +static inline void fxrstor(void *ctx) { + asm volatile("fxrstor %0" : : "m"(FLAT_PTR(ctx)) : "memory"); +} + +void mp_init(struct limine_smp_response *smp_info); +struct prcb *prcb_return_current_cpu(void); +size_t prcb_return_installed_cpus(void); \ No newline at end of file diff --git a/src/mp/percpu.h b/src/mp/percpu.h deleted file mode 100644 index b312718..0000000 --- a/src/mp/percpu.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include - -struct cpu_local { - uint64_t self; /* +0x00 (GS:0x00) — points to this struct */ - uint64_t user_rsp; /* +0x08 (GS:0x08) — saved user RSP on SYSCALL */ - uint64_t kernel_rsp; /* +0x10 (GS:0x10) — kernel stack for syscall */ - /* Future SMP fields: */ - uint32_t cpu_id; /* +0x18 logical CPU index */ - uint32_t _pad; - void *current; /* +0x20 pointer to current task_t */ -}; \ No newline at end of file diff --git a/src/mp/spinlock.c b/src/mp/spinlock.c index 686752c..a1ac14b 100644 --- a/src/mp/spinlock.c +++ b/src/mp/spinlock.c @@ -1,26 +1,47 @@ #include "spinlock.h" +#include +#include +#include +#include "libk/kargs.h" +#include "libk/debug.h" +#include "arch/x86_64/asm/asm.h" -void spinlock_init(spinlock_t *lock) { - atomic_flag_clear(&lock->flag); +extern bool sched_runit; +extern bool is_smp; + +static void spinlock_spinning_for_too_long(void *last_addr, spinlock_t *spin) { + panic("Deadlocked at %p. Last owner %p\n", last_addr, spin->last_owner); } -void spinlock_acquire_or_wait(spinlock_t *lock) { - while (atomic_flag_test_and_set_explicit(&lock->flag, memory_order_acquire)) - asm volatile("pause" ::: "memory"); +bool spinlock_acquire(spinlock_t *spin) { + if (!spin) + return false; + bool ret = __sync_bool_compare_and_swap(&spin->lock, 0, 1); + if (ret) + spin->last_owner = __builtin_return_address(0); + return ret; } -void spinlock_drop(spinlock_t *lock) { - atomic_flag_clear_explicit(&lock->flag, memory_order_release); +void spinlock_acquire_or_wait(spinlock_t *spin) { + if (!spin) + return; + volatile size_t deadlock_counter = 0; + void *caller = __builtin_return_address(0); + for (;;) { + if (spinlock_acquire(spin)) + break; + if ((kernel_arguments.kernel_args & KERNEL_ARGS_PANIC_ON_DEADLOCK)) { + if (++deadlock_counter >= 100000000) + spinlock_spinning_for_too_long(caller, spin); + } + pause(); + } + spin->last_owner = __builtin_return_address(0); } -void spinlock_acquire_irqsave(spinlock_t *lock, uint64_t *flags) { - uint64_t f; - asm volatile("pushfq; pop %0; cli" : "=r"(f) :: "memory"); - *flags = f; - spinlock_acquire_or_wait(lock); +void spinlock_drop(spinlock_t *spin) { + if (!spin) + return; + __atomic_store_n(&spin->lock, 0, __ATOMIC_SEQ_CST); + spin->last_owner = NULL; } - -void spinlock_release_irqrestore(spinlock_t *lock, uint64_t flags) { - spinlock_drop(lock); - asm volatile("push %0; popfq" :: "r"(flags) : "memory", "cc"); -} \ No newline at end of file diff --git a/src/mp/spinlock.h b/src/mp/spinlock.h index cb245c6..56b3440 100644 --- a/src/mp/spinlock.h +++ b/src/mp/spinlock.h @@ -1,17 +1,17 @@ #pragma once #include #include +#include typedef struct { - atomic_flag flag; + bool lock; + void *last_owner; } spinlock_t; -#define SPINLOCK_INIT ((spinlock_t){ .flag = ATOMIC_FLAG_INIT }) +#define spinlock_init(s) \ + s.lock = 0; \ + s.last_owner = NULL; -void spinlock_init(spinlock_t *lock); -void spinlock_acquire_or_wait(spinlock_t *lock); -void spinlock_drop(spinlock_t *lock); - -// Old IRQ-safe API still available for compatibility -void spinlock_acquire_irqsave(spinlock_t *lock, uint64_t *flags); -void spinlock_release_irqrestore(spinlock_t *lock, uint64_t flags); \ No newline at end of file +bool spinlock_acquire(spinlock_t *spin); +void spinlock_acquire_or_wait(spinlock_t *spin); +void spinlock_drop(spinlock_t *spin); \ No newline at end of file diff --git a/src/sched/context_switch.S b/src/sched/context_switch.S deleted file mode 100644 index 12518b6..0000000 --- a/src/sched/context_switch.S +++ /dev/null @@ -1,79 +0,0 @@ - -# void sched_context_switch(struct cpu_context *from, -# struct cpu_context *to); -# -# struct cpu_context layout (from sched.h): -# offset 0 : rsp (uint64_t) -# offset 8 : cr3 (uint64_t) -# -# Strategy -# -------- -# Only callee-saved registers need explicit saving; caller-saved registers -# (rax, rcx, rdx, rsi, rdi, r8–r11) are already on the caller's stack per -# the System V ABI. -# -# 1. Push all callee-saved GPRs onto the *current* kernel stack. -# 2. Save RSP → from->rsp. -# 3. If to->cr3 is non-zero and differs from the current CR3, load it -# (switches address space for user processes). -# 4. Load RSP ← to->rsp (switch to next task's kernel stack). -# 5. Pop all callee-saved GPRs from the *new* stack. -# 6. ret — pops the return address placed there during task creation -# (kthread_trampoline / user_task_trampoline) for a brand-new task, -# or returns into the schedule() call-site for a resumed task. -# -# NOTE: This function is called with IF=0 (interrupts disabled) and must -# NOT modify IF itself. The trampolines re-enable interrupts after the -# first-ever schedule-in of a task. - -.section .text -.global sched_context_switch -.type sched_context_switch, @function - -sched_context_switch: - # ── Save outgoing task ───────────────────────────────────────────── - pushq %rbx - pushq %rbp - pushq %r12 - pushq %r13 - pushq %r14 - pushq %r15 - - # from->rsp = RSP (rdi = struct cpu_context *from, offset 0 = rsp) - movq %rsp, 0(%rdi) - - fxsave 16(%rdi) - - # ── Switch address space (CR3) if needed ─────────────────────────── - # to->cr3 is at offset 8 in struct cpu_context - movq 8(%rsi), %rax - testq %rax, %rax # 0 means "keep current CR3" (kernel thread) - jz .Lno_cr3 - - movq %cr3, %rcx - cmpq %rax, %rcx # Same CR3? Don't flush the TLB needlessly. - je .Lno_cr3 - - movq %rax, %cr3 # Load new page table root (flushes TLB) - -.Lno_cr3: - # ── Switch to incoming task's kernel stack ───────────────────────── - # to->rsp is at offset 0 (rsi = struct cpu_context *to) - movq 0(%rsi), %rsp - - fxrstor 16(%rsi) - - # ── Restore incoming task's callee-saved registers ───────────────── - popq %r15 - popq %r14 - popq %r13 - popq %r12 - popq %rbp - popq %rbx - - # ret pops the "return address" from the new task's stack. - # • Brand-new task → jumps to kthread_trampoline or user_task_trampoline - # • Resumed task → returns into schedule(), then back up the call chain - ret - -.size sched_context_switch, . - sched_context_switch \ No newline at end of file diff --git a/src/sched/futex.c b/src/sched/futex.c new file mode 100644 index 0000000..69f3442 --- /dev/null +++ b/src/sched/futex.c @@ -0,0 +1,99 @@ +#include +#include +#include +#include "mm/vmm.h" +#include "mm/memory.h" +#include "mm/pmm.h" +#include "mm/slab.h" +#include "mp/mp.h" +#include "mp/spinlock.h" +#include "libk/event.h" +#include "futex.h" +#include "libk/hashmap.h" +#include "libk/errno.h" +#include "sched/syscall.h" + + +static spinlock_t futex_lock = {0}; +static HASHMAP_TYPE(struct futex_entry *) futex_hashmap = HASHMAP_INIT(256); + +int futex_wait(uint32_t value, uint32_t *futex, struct thread *thrd) { + if (*futex != value) { + errno = EAGAIN; + return -1; + } + + struct futex_entry *entry = NULL; + + if (HASHMAP_GET(&futex_hashmap, entry, &futex, sizeof(uint32_t *))) { + goto wait; + } + + entry = kmalloc(sizeof(struct futex_entry)); + struct event *futex_event = kmalloc(sizeof(struct event)); + memzero(futex_event, sizeof(struct event)); + + entry->value = value; + entry->address = futex; + entry->thrd = thrd; + entry->event = futex_event; + + spinlock_acquire_or_wait(&futex_lock); + HASHMAP_INSERT(&futex_hashmap, &futex, sizeof(uint32_t *), entry); + spinlock_drop(&futex_lock); + +wait: + if (event_await(&entry->event, 1, true) < 0) { + errno = EINTR; + return -1; + } + return 0; +} + +int futex_wake(uint32_t *futex) { + spinlock_acquire_or_wait(&futex_lock); + struct futex_entry *entry = NULL; + if (HASHMAP_GET(&futex_hashmap, entry, &futex, sizeof(uint32_t *))) { + event_trigger(entry->event, false); + } + spinlock_drop(&futex_lock); + return 0; +} + +void syscall_futex(struct syscall_arguments *args) { + uint32_t *raw_user_addr = (uint32_t *)args->args0; + int opcode = args->args1; + uint32_t value = args->args2; + *(volatile uint32_t *)raw_user_addr; // Ensure the page isn't demand paged + uint32_t *raw_kernel_addr = + (uint32_t *)syscall_helper_user_to_kernel_address( + (uintptr_t)raw_user_addr); + struct thread *thrd = sched_get_running_thread(); + + if (!raw_kernel_addr) { + errno = EFAULT; + args->ret = -1; + return; + } + + switch (opcode) { + case FUTEX_WAIT: + case FUTEX_WAIT_BITSET: + args->ret = futex_wait(value, raw_kernel_addr, thrd); + break; + case FUTEX_WAKE: + case FUTEX_WAKE_BITSET: + args->ret = futex_wake(raw_kernel_addr); + break; + default: + args->ret = -1; + errno = EINVAL; + break; + } +} + +void futex_init(void) { + uintptr_t phys = 0; + HASHMAP_INSERT(&futex_hashmap, &phys, sizeof(uintptr_t), NULL); + syscall_register_handler(0xca, syscall_futex); +} diff --git a/src/sched/futex.h b/src/sched/futex.h new file mode 100644 index 0000000..30b4294 --- /dev/null +++ b/src/sched/futex.h @@ -0,0 +1,19 @@ +#pragma once +#include "sched_types.h" +#include "libk/event.h" +#include + +struct futex_entry +{ + uint32_t value; + uint32_t *address; + struct thread *thrd; + struct event *event; +}; + +#define FUTEX_WAIT 0 +#define FUTEX_WAKE 1 +#define FUTEX_WAIT_BITSET 9 +#define FUTEX_WAKE_BITSET 10 + +void futex_init(void); diff --git a/src/sched/sched.c b/src/sched/sched.c new file mode 100644 index 0000000..d5acce6 --- /dev/null +++ b/src/sched/sched.c @@ -0,0 +1,825 @@ +#include +#include +#include +#include "mm/vmm.h" +#include "mm/memory.h" +#include "mm/pmm.h" +#include "mm/slab.h" +#include "mp/mp.h" +#include "mp/spinlock.h" +#include "fs/elf.h" +#include "sched.h" +#include "sched_types.h" +#include "arch/x86_64/cpu/reg.h" +#include "fs/vfs.h" +#include "libk/errno.h" +#include "libk/debug.h" +#include "futex.h" +#include "libk/string.h" +#include "main.h" +#include "sched/syscall.h" + + + +#define VIRTUAL_STACK_ADDR 0x70000000000 +#define MMAP_ANON_BASE 0x80000000000 + +spinlock_t sched_lock = {0}; +bool sched_runit = false; + +struct thread *thread_list = NULL; +struct process *process_list = NULL; + +struct process *kernel_proc = NULL; +struct process *init_proc = NULL; + +int64_t tid = 0; +int64_t pid = 0; + +spinlock_t thread_lock = {0}; +spinlock_t process_lock = {0}; + +struct resource *std_console_device = NULL; + +struct utsname system_uname = { + .sysname = "KirkOS", + .nodename = "localhost", + .release = "0.0.0", + .version = "Built on " __DATE__ " " __TIME__, +#if defined(__x86_64__) + .machine = "x86_64", +#endif + .domainname = "", +}; + +struct thread *sched_get_next_thread(struct thread *thrd) { + spinlock_acquire_or_wait(&thread_lock); + + struct thread *this = NULL; + if (thrd) { + this = thrd->next; + } else { + this = thread_list; + } + + while (this) { + if (spinlock_acquire(&this->lock)) { + if (this->state == THREAD_READY_TO_RUN) { + spinlock_drop(&thread_lock); + return this; + } + if (this->state == THREAD_KILLED) { + struct thread *next = this->next; + thread_destroy_context(this); + if (this->mother_proc->state == PROCESS_KILLED && + this->mother_proc->clean_up) { + process_destroy_context(this->mother_proc); + this->mother_proc->clean_up = false; + } + sched_remove_thread_from_list(&thread_list, this); + kfree(this); + this = next; + continue; + } + spinlock_drop(&this->lock); + this = this->next; + continue; + } + + this = this->next; + } + + spinlock_drop(&thread_lock); + return NULL; +} + +static struct thread *sched_tid_to_thread(int64_t t) { + struct thread *this = thread_list; + while (this) { + if (this->tid == t) + return this; + this = this->next; + } + return NULL; +} + +static struct process *sched_pid_to_process(int64_t p) { + struct process *this = process_list; + while (this) { + if (this->pid == p) + return this; + this = this->next; + } + return NULL; +} + +void sched_add_thread_to_list(struct thread **thrd_list, struct thread *thrd) { + if (!thrd) { + return; + } + + thrd->next = *thrd_list; + *thrd_list = thrd; +} + +void sched_remove_thread_from_list(struct thread **thrd_list, + struct thread *thrd) { + if (!thrd || !thrd_list) { + return; + } + struct thread *this = *thrd_list; + struct thread *next = NULL; + + if (this == thrd) { + *thrd_list = this->next; + return; + } + + while (this) { + next = this->next; + if (next == thrd) { + this->next = next->next; + next->next = NULL; + return; + } + this = next; + } +} + +void sched_add_process_to_list(struct process **proc_list, + struct process *proc) { + if (!proc) { + return; + } + + proc->next = *proc_list; + *proc_list = proc; +} + +void sched_remove_process_from_list(struct process **proc_list, + struct process *proc) { + if (!proc || !proc_list) { + return; + } + struct process *this = *proc_list; + struct process *next = NULL; + + if (this == proc) { + *proc_list = this->next; + return; + } + + while (this) { + next = this->next; + if (next == proc) { + this->next = next->next; + next->next = NULL; + return; + } + this = next; + } +} + +void syscall_kill(struct syscall_arguments *args) { + struct process *proc = sched_pid_to_process((int64_t)args->args0); + args->ret = 0; + if (!proc) + args->ret = -1; + else + process_kill(proc, false); +} + +void syscall_exit(struct syscall_arguments *args) { + sched_get_running_thread()->mother_proc->status = (uint8_t)args->args0; + process_kill(sched_get_running_thread()->mother_proc, false); +} + +void syscall_getpid(struct syscall_arguments *args) { + args->ret = sched_get_running_thread()->mother_proc->pid; +} + +void syscall_getppid(struct syscall_arguments *args) { + args->ret = 0; + if (sched_get_running_thread()->mother_proc->parent_process) { + args->ret = + sched_get_running_thread()->mother_proc->parent_process->pid; + } +} + +void syscall_fork(struct syscall_arguments *args) { + struct thread *running_thread = sched_get_running_thread(); + struct process *running_process = running_thread->mother_proc; + args->ret = process_fork(running_process, running_thread); +} + +void syscall_execve(struct syscall_arguments *args) { + if (!process_execve((char *)args->args0, (char **)args->args1, + (char **)args->args2)) { + args->ret = -1; + } +} + +void syscall_uname(struct syscall_arguments *args) { + args->ret = 0; + memcpy((struct utsname *)args->args0, &system_uname, + sizeof(struct utsname)); +} + +void syscall_sethostname(struct syscall_arguments *args) { + size_t count = args->args1; + if (count > 64) { + count = 64; + } + memzero(&system_uname.nodename, sizeof(system_uname.nodename)); + memcpy(&system_uname.nodename, (char *)args->args0, count); + args->ret = 0; +} + +void syscall_waitpid(struct syscall_arguments *args) { +#define WNOHANG 1 + int pid_to_wait_on = (int)args->args0; + int *status = (int *)(args->args1); + int mode = (int)args->args2; + args->ret = -1; + + struct process *waiter_proc = sched_get_running_thread()->mother_proc; + + spinlock_acquire_or_wait(&waiter_proc->lock); + + if (!waiter_proc->child_processes.length) { + errno = ECHILD; + return; + } + + if (pid_to_wait_on < -1 || pid_to_wait_on == 0) { + errno = EINVAL; + return; + } + + struct process *waitee_process = NULL; + struct event **events = NULL; + size_t event_count = 0; + + if (pid_to_wait_on == -1) { + events = kmalloc(sizeof(struct event *) * + waiter_proc->child_processes.length); + event_count = waiter_proc->child_processes.length; + for (int i = 0; i < waiter_proc->child_processes.length; i++) { + events[i] = &waiter_proc->child_processes.data[i]->death_event; + } + } + + else { + events = kmalloc(sizeof(struct event *)); + for (int i = 0; i < waiter_proc->child_processes.length; i++) { + if (waiter_proc->child_processes.data[i]->pid == pid_to_wait_on) { + waitee_process = waiter_proc->child_processes.data[i]; + break; + } + } + + if (waitee_process == NULL) { + errno = ECHILD; + kfree(events); + args->ret = -1; + return; + } + + event_count = 1; + events[0] = &waitee_process->death_event; + } + + spinlock_drop(&waiter_proc->lock); + + bool block = (mode & WNOHANG) == 0; + ssize_t which = event_await(events, event_count, block); + + if (which == -1) { + kfree(events); + if (block) { + args->ret = 0; + return; + } else { + errno = EINTR; + return; + } + } + + spinlock_acquire_or_wait(&waiter_proc->lock); + if (waitee_process == NULL) { + waitee_process = waiter_proc->child_processes.data[which]; + } + + *status = waitee_process->status; + args->ret = waitee_process->pid; + + vec_remove(&waiter_proc->child_processes, waitee_process); + spinlock_drop(&waiter_proc->lock); + + spinlock_acquire_or_wait(&process_lock); + sched_remove_process_from_list(&process_list, waitee_process); + spinlock_drop(&process_lock); + + kfree(events); + kfree(waitee_process); +} + +void syscall_thread_new(struct syscall_arguments *args) { + struct process *proc = sched_get_running_thread()->mother_proc; + + uintptr_t pc = (uintptr_t)args->args0; + uintptr_t sp = (uintptr_t)args->args1; + + struct thread *thrd = kmalloc(sizeof(struct thread)); + memzero(thrd, sizeof(struct thread)); + thrd->runtime = proc->runtime; + thrd->mother_proc = proc; + + thread_setup_context_from_user(thrd, pc, sp); + + thrd->next = NULL; + spinlock_init(thrd->lock); + spinlock_init(thrd->yield_lock); + + spinlock_acquire_or_wait(&proc->lock); + vec_push(&proc->process_threads, thrd); + spinlock_drop(&proc->lock); + + cli(); + + spinlock_acquire_or_wait(&thread_lock); + thrd->tid = tid++; + sched_add_thread_to_list(&thread_list, thrd); + thrd->state = THREAD_READY_TO_RUN; + spinlock_drop(&thread_lock); + + args->ret = thrd->tid; +} + +void syscall_thread_exit(struct syscall_arguments *args) { + (void)args; + cli(); + thread_kill(sched_get_running_thread(), true); +} + +void syscall_umask(struct syscall_arguments *args) { + mode_t old = sched_get_running_thread()->mother_proc->umask; + sched_get_running_thread()->mother_proc->umask = ((mode_t)args->args0); + args->ret = old; +} + +void sched_init(uint64_t args) { + syscall_register_handler(0x27, syscall_getpid); + syscall_register_handler(0x67, syscall_puts); + syscall_register_handler(0x6e, syscall_getppid); + syscall_register_handler(0x3c, syscall_exit); + syscall_register_handler(0x3e, syscall_kill); + syscall_register_handler(0x9d, syscall_prctl); + syscall_register_handler(0x39, syscall_fork); + syscall_register_handler(0x3b, syscall_execve); + syscall_register_handler(0x3f, syscall_uname); + syscall_register_handler(0xaa, syscall_sethostname); + syscall_register_handler(0x72, syscall_waitpid); + + syscall_register_handler(0x38, syscall_thread_new); + syscall_register_handler(0x3d, syscall_thread_exit); + + syscall_register_handler(0x5f, syscall_umask); + + futex_init(); + + process_create("kernel_tasks", PROCESS_READY_TO_RUN, 5000, + (uintptr_t)kernel_main, args, false, NULL); + + kernel_proc = process_list; + sched_runit = true; +} + +void process_create(char *name, uint8_t state, uint64_t runtime, + uintptr_t pc_address, uint64_t arguments, bool user, + struct process *parent_process) { + struct process *proc = kmalloc(sizeof(struct process)); + memzero(proc, sizeof(struct process)); + + strncpy(proc->name, name, 256); + + proc->runtime = runtime; + proc->state = state; + + process_setup_context(proc, user); + + proc->cwd = vfs_root; + proc->stack_top = VIRTUAL_STACK_ADDR; + + if (parent_process) { + proc->parent_process = parent_process; + if (proc->parent_process->cwd) { + proc->cwd = parent_process->cwd; + } + proc->umask = parent_process->umask; + proc->mmap_anon_base = parent_process->mmap_anon_base; + spinlock_acquire_or_wait(&parent_process->lock); + vec_push(&parent_process->child_processes, proc); + spinlock_drop(&parent_process->lock); + } else { + proc->umask = S_IWGRP | S_IWOTH; + proc->mmap_anon_base = MMAP_ANON_BASE; + } + proc->next = NULL; + + vec_init(&proc->process_threads); + vec_init(&proc->child_processes); + + spinlock_acquire_or_wait(&process_lock); + proc->pid = pid++; + sched_add_process_to_list(&process_list, proc); + spinlock_drop(&process_lock); + + thread_create(pc_address, arguments, user, proc); +} + +bool process_run_init(char *path, char **argv, char **envp, + struct process *parent_process) { + struct process *proc = kmalloc(sizeof(struct process)); + memzero(proc, sizeof(struct process)); + + strncpy(proc->name, "init", 5); + + proc->runtime = 20000; + proc->state = PROCESS_READY_TO_RUN; + + process_setup_context(proc, true); + + proc->cwd = vfs_root; + proc->stack_top = VIRTUAL_STACK_ADDR; + + if (parent_process) { + proc->parent_process = parent_process; + if (proc->parent_process->cwd) { + proc->cwd = parent_process->cwd; + } + proc->umask = parent_process->umask; + proc->mmap_anon_base = parent_process->mmap_anon_base; + spinlock_acquire_or_wait(&parent_process->lock); + vec_push(&parent_process->child_processes, proc); + spinlock_drop(&parent_process->lock); + } else { + proc->umask = S_IWGRP | S_IWOTH; + proc->mmap_anon_base = MMAP_ANON_BASE; + } + + struct auxval auxv, ld_aux; + struct vfs_node *node = vfs_get_node(vfs_root, path, true); + const char *ld_path = NULL; + + if (!node || !elf_load(proc->process_pagemap, node->resource, 0, &auxv, &ld_path)) { + return false; + } + + // HACK: ld_path for processes that don't depend on ld.so points to + // kmalloced memory which is not null checking the first letter is '/' or + // not to know if a program needs ld + + uint64_t entry = auxv.at_entry; + + if (ld_path && ld_path[0] == '/') { + struct vfs_node *ld_node = vfs_get_node(vfs_root, ld_path, true); + + if (!ld_node || !elf_load(proc->process_pagemap, ld_node->resource, + 0x40000000, &ld_aux, NULL)) { + return false; + } + entry = ld_aux.at_entry; + } + + proc->auxv = auxv; + + for (int i = 0; i < 3; i++) + fdnum_create_from_resource(proc, std_console_device, 0, i, true); + + proc->next = NULL; + + vec_init(&proc->process_threads); + vec_init(&proc->child_processes); + + spinlock_acquire_or_wait(&process_lock); + proc->pid = pid++; + sched_add_process_to_list(&process_list, proc); + spinlock_drop(&process_lock); + + thread_setup_for_init(entry, argv, envp, proc); + + init_proc = proc; + return true; +} + +int64_t process_fork(struct process *proc, struct thread *thrd) { + struct process *fproc = kmalloc(sizeof(struct process)); + memzero(fproc, sizeof(struct process)); + strncpy(fproc->name, proc->name, 256); + + for (int i = 0; i < MAX_FDS; i++) { + if (proc->fds[i] == NULL) { + continue; + } + + if (fdnum_dup(proc, i, fproc, i, 0, true, false) != i) { + kfree(fproc); + return -1; + } + } + + // No fucking clue why this works but disabling interrupts here stops all + // the random crashes. I am suspecting its a similar issue to execve but + // then the new thread and process isn't even "running"? + cli(); + process_fork_context(proc, fproc); + + fproc->mmap_anon_base = proc->mmap_anon_base; + fproc->stack_top = proc->stack_top; + fproc->cwd = proc->cwd; + fproc->umask = proc->umask; + fproc->parent_process = proc; + fproc->next = NULL; + + vec_init(&fproc->child_processes); + vec_init(&fproc->process_threads); + + spinlock_acquire_or_wait(&proc->lock); + vec_push(&proc->child_processes, fproc); + spinlock_drop(&proc->lock); + + spinlock_acquire_or_wait(&process_lock); + fproc->pid = pid++; + sched_add_process_to_list(&process_list, fproc); + spinlock_drop(&process_lock); + + thread_fork(thrd, fproc); + + fproc->state = PROCESS_READY_TO_RUN; + + return fproc->pid; +} + +// So a funny bug. If we reschedule in middle of the execve and we get +// scheduled again then the process pagemap loaded is the new one. Then we +// will try to read from the existing addresses which aren't mapped at all lol. +// Easy fix here disable interrupts for a while. + +static size_t get_argv_length(char **a) { + size_t count = 0; + while (a[count++] != NULL) { + pause(); + } + return count; +} + +bool process_execve(char *path, char **argv, char **envp) { + cli(); + + struct thread *thread = sched_get_running_thread(); + struct process *proc = thread->mother_proc; + + struct auxval auxv, ld_aux; + struct vfs_node *node = vfs_get_node(proc->cwd, path, true); + const char *ld_path = NULL; + + if (!node) { + return false; + } + + char shebang[2] = {0}; + node->resource->read(node->resource, NULL, shebang, 0, 2); + + // This is hard coded to 256 + 4 character. + // TODO: Make this dynamic in the future. + if (shebang[0] == '#' && shebang[1] == '!') { + char shebang_line[256 + 4 + 1] = {0}; + ssize_t read_size = node->resource->read(node->resource, NULL, + shebang_line, 2, 256 + 4); + char *c = shebang_line; + int counter = 0; + while (*c++ != '\n' && counter++ < read_size) { + pause(); + } + c--; + *c = '\0'; + char **arg_list = NULL; + size_t arg_count = strsplit(shebang_line, ' ', &arg_list); + size_t old_arg_count = get_argv_length(argv); + char *new_path = arg_list[1]; + char **new_argv = + kmalloc(sizeof(char *) * (arg_count + old_arg_count) + 2); + new_argv[0] = new_path; + new_argv[1] = path; + for (size_t i = 2; i < arg_count; i++) { + new_argv[i + 1] = arg_list[i]; + kfree(arg_list[i]); + } + for (size_t i = 0; i < old_arg_count; i++) { + new_argv[i + arg_count + 1] = argv[i]; + } + new_argv[arg_count + old_arg_count + 1] = NULL; + kfree(arg_list); + return process_execve(new_path, new_argv, envp); + } + + struct pagemap *old_pagemap = proc->process_pagemap; + process_setup_context(proc, true); + + if (!elf_load(proc->process_pagemap, node->resource, 0, &auxv, &ld_path)) { + proc->process_pagemap = old_pagemap; + errno = ENOENT; + return false; + } + + // HACK: ld_path for processes that don't depend on ld.so points to + // kmalloced memory which is not null checking the first letter is '/' or + // not to know if a program needs ld + + uint64_t entry = auxv.at_entry; + + if (ld_path && ld_path[0] == '/') { + struct vfs_node *ld_node = vfs_get_node(vfs_root, ld_path, true); + + if (!ld_node || !elf_load(proc->process_pagemap, ld_node->resource, + 0x40000000, &ld_aux, NULL)) { + proc->process_pagemap = old_pagemap; + errno = ENOENT; + return false; + } + entry = ld_aux.at_entry; + } + + strncpy(proc->name, path, 256); + + for (int i = 0; i < proc->process_threads.length; i++) { + if (proc->process_threads.data[i] != thread) { + proc->process_threads.data[i]->state = THREAD_KILLED; + } + } + + vec_deinit(&proc->process_threads); + vec_push(&proc->process_threads, thread); + + proc->mmap_anon_base = MMAP_ANON_BASE; + proc->stack_top = VIRTUAL_STACK_ADDR; + proc->state = PROCESS_READY_TO_RUN; + + proc->auxv = auxv; + + thread_execve(proc, thread, entry, argv, envp); + + vmm_switch_pagemap(kernel_pagemap); + + sched_yield(false); + return false; +} + +void process_kill(struct process *proc, bool crash) { + if (proc->pid < 2) { + panic("Attempted to kill init!\n"); + } + + bool are_we_killing_ourselves = false; + + for (int i = 0; i < MAX_FDS; i++) { + if (proc->fds[i] == NULL) { + continue; + } + fdnum_close(proc, i); + } + + if (sched_get_running_thread()->mother_proc == proc) { + cli(); + are_we_killing_ourselves = true; + vmm_switch_pagemap(kernel_pagemap); + } + + for (int i = 0; i < proc->process_threads.length; i++) { + if (proc->process_threads.data[i]->state == THREAD_NORMAL) { + sched_trigger_yield(proc->process_threads.data[i]->running_on_cpu); + } + proc->process_threads.data[i]->state = THREAD_KILLED; + } + + spinlock_acquire_or_wait(&init_proc->lock); + for (int i = 0; i < proc->child_processes.length; i++) { + struct process *child_proc = proc->child_processes.data[i]; + child_proc->parent_process = init_proc; + vec_push(&init_proc->child_processes, child_proc); + } + spinlock_drop(&init_proc->lock); + + vec_deinit(&proc->child_processes); + vec_deinit(&proc->process_threads); + + event_trigger(&proc->death_event, false); + proc->state = PROCESS_KILLED; + + if (!are_we_killing_ourselves) { + process_destroy_context(proc); + } + + if (are_we_killing_ourselves || crash) { + proc->clean_up = true; + sched_yield(false); + } +} + +void thread_setup_for_init(uintptr_t pc_address, char **argv, char **envp, + struct process *proc) { + struct thread *thrd = kmalloc(sizeof(struct thread)); + memzero(thrd, sizeof(struct thread)); + thrd->runtime = proc->runtime; + thrd->mother_proc = proc; + + thrd->next = NULL; + spinlock_init(thrd->lock); + spinlock_init(thrd->yield_lock); + + vec_push(&proc->process_threads, thrd); + + // thread_setup_context_from_user sets up a minimal thread for userspace we + // only have to pass the stack. thread_setup_context_for_execve anyways + // creates a new stack. + thread_setup_context_from_user(thrd, 0, 0); + thread_setup_context_for_execve(thrd, pc_address, argv, envp); + + spinlock_acquire_or_wait(&thread_lock); + thrd->tid = tid++; + sched_add_thread_to_list(&thread_list, thrd); + thrd->state = THREAD_READY_TO_RUN; + spinlock_drop(&thread_lock); +} + +void thread_create(uintptr_t pc_address, uint64_t arguments, bool user, + struct process *proc) { + struct thread *thrd = kmalloc(sizeof(struct thread)); + memzero(thrd, sizeof(struct thread)); + thrd->runtime = proc->runtime; + thrd->mother_proc = proc; + + thread_setup_context(thrd, pc_address, arguments, user); + + thrd->next = NULL; + spinlock_init(thrd->lock); + spinlock_init(thrd->yield_lock); + + spinlock_acquire_or_wait(&proc->lock); + vec_push(&proc->process_threads, thrd); + spinlock_drop(&proc->lock); + + spinlock_acquire_or_wait(&thread_lock); + thrd->tid = tid++; + sched_add_thread_to_list(&thread_list, thrd); + thrd->state = THREAD_READY_TO_RUN; + spinlock_drop(&thread_lock); +} + +void thread_fork(struct thread *pthrd, struct process *fproc) { + struct thread *thrd = kmalloc(sizeof(struct thread)); + memzero(thrd, sizeof(struct thread)); + + thrd->runtime = pthrd->runtime; + thrd->mother_proc = fproc; + thrd->next = NULL; + spinlock_init(thrd->lock); + spinlock_init(thrd->yield_lock); + + vec_push(&fproc->process_threads, thrd); + + thread_fork_context(pthrd, thrd); + + thrd->last_scheduled = 0; + + spinlock_acquire_or_wait(&thread_lock); + thrd->tid = tid++; + sched_add_thread_to_list(&thread_list, thrd); + thrd->state = THREAD_READY_TO_RUN; + spinlock_drop(&thread_lock); +} + +void thread_execve(struct process *proc, struct thread *thrd, + uintptr_t pc_address, char **argv, char **envp) { + thrd->runtime = proc->runtime; + thrd->mother_proc = proc; + + thread_setup_context_for_execve(thrd, pc_address, argv, envp); + + thrd->state = THREAD_READY_TO_RUN; +} + +void thread_kill(struct thread *thrd, bool reschedule) { + struct process *mother_proc = thrd->mother_proc; + + spinlock_acquire_or_wait(&mother_proc->lock); + vec_remove(&mother_proc->process_threads, thrd); + spinlock_drop(&mother_proc->lock); + + thrd->state = THREAD_KILLED; + + if (reschedule) { + sched_yield(false); + } +} diff --git a/src/sched/sched.h b/src/sched/sched.h new file mode 100644 index 0000000..5fed7c8 --- /dev/null +++ b/src/sched/sched.h @@ -0,0 +1,79 @@ +#pragma once +#include +#include +#include +#include "arch/x86_64/cpu/reg.h" +#include "libk/vec.h" +#include "sched_types.h" +#include "sched/syscall.h" + +void resched(registers_t *reg); +void thread_setup_context(struct thread *thrd, uintptr_t pc_address, + uint64_t arguments, bool user); +void thread_setup_context_from_user(struct thread *thrd, uintptr_t pc_address, + uintptr_t sp); +void thread_setup_context_for_execve(struct thread *thrd, uintptr_t pc_address, + char **argv, char **envp); +void thread_fork_context(struct thread *thrd, struct thread *fthrd); +void thread_destroy_context(struct thread *thrd); +void process_setup_context(struct process *proc, bool user); +void process_fork_context(struct process *proc, struct process *fproc); +void process_destroy_context(struct process *proc); + + +struct utsname { + char sysname[65]; + char nodename[65]; + char release[65]; + char version[65]; + char machine[65]; + char domainname[65]; +}; + +extern struct thread *thread_list; +extern struct process *process_list; +extern struct thread *sleeping_threads; +extern struct resource *std_console_device; +extern struct process *kernel_proc; +extern struct process *init_proc; + +void sched_yield(bool save); +void sched_trigger_yield(uint64_t cpu_number); +struct thread *sched_get_next_thread(struct thread *thrd); +void sched_init(uint64_t args); +void sched_add_thread_to_list(struct thread **thrd_list, struct thread *thrd); +void sched_remove_thread_from_list(struct thread **thrd_list, + struct thread *thrd); +void sched_add_process_to_list(struct process **proc_list, + struct process *proc); +void sched_remove_process_from_list(struct process **proc_list, + struct process *proc); +void process_create(char *name, uint8_t state, uint64_t runtime, + uintptr_t pc_address, uint64_t arguments, bool user, + struct process *parent_process); +bool process_run_init(char *path, char **argv, char **envp, + struct process *parent_process); +void process_kill(struct process *proc, bool crash); +int64_t process_fork(struct process *proc, struct thread *thrd); +bool process_execve(char *path, char **argv, char **envp); +void thread_setup_for_init(uintptr_t pc_address, char **argv, char **envp, + struct process *proc); +void thread_create(uintptr_t pc_address, uint64_t arguments, bool user, + struct process *proc); +void thread_execve(struct process *proc, struct thread *thrd, + uintptr_t pc_address, char **argv, char **envp); +void thread_kill(struct thread *thrd, bool reschedule); +void thread_fork(struct thread *pthrd, struct process *fproc); +void syscall_prctl(struct syscall_arguments *args); + +void process_wait_on_another_process(struct process *waiter, + struct process *waitee); + +void process_wait_on_processes(struct process *waiter, process_vec_t *waitees); + + +static inline struct thread *sched_get_running_thread(void) { + struct thread *ret; + asm volatile("mov %0, qword ptr gs:[24]" : "=r"(ret)::"memory"); + return ret; +} \ No newline at end of file diff --git a/src/sched/sched_types.h b/src/sched/sched_types.h new file mode 100644 index 0000000..328c6a5 --- /dev/null +++ b/src/sched/sched_types.h @@ -0,0 +1,79 @@ +#pragma once +#include +#include +#include "mp/spinlock.h" +#include "libk/vec.h" +#include "libk/event.h" +#include "arch/x86_64/cpu/reg.h" +#include "fs/elf.h" + + +enum thread_states { + THREAD_NORMAL = 0, + THREAD_READY_TO_RUN, + THREAD_KILLED, + THREAD_WAITING_FOR_EVENT +}; + +enum process_states { + PROCESS_NORMAL = 0, + PROCESS_READY_TO_RUN, + PROCESS_KILLED +}; + +struct process; + +#define MAX_FDS 256 +#define MAX_EVENTS 32 + +struct thread { + uint64_t errno; + int64_t tid; + registers_t reg; + spinlock_t lock; + spinlock_t yield_lock; + enum thread_states state; + uint64_t runtime; + uint64_t stack; + uint64_t pf_stack; + uint64_t kernel_stack; + uint64_t last_scheduled; + void *fpu_storage; + size_t which_event; + size_t attached_events_i; + struct event *attached_events[MAX_EVENTS]; + struct process *mother_proc; + uint64_t gs_base; + uint64_t fs_base; + int64_t running_on_cpu; + struct thread *next; +}; + +typedef vec_t(struct thread *) thread_vec_t; +typedef vec_t(struct process *) process_vec_t; + +struct process { + int64_t pid; + enum process_states state; + struct pagemap *process_pagemap; + spinlock_t lock; + uintptr_t mmap_anon_base; + uint64_t runtime; + uintptr_t stack_top; + thread_vec_t process_threads; + struct vfs_node *cwd; + spinlock_t fds_lock; + mode_t umask; + struct f_descriptor *fds[MAX_FDS]; + struct process *parent_process; + process_vec_t child_processes; + struct auxval auxv; + struct event death_event; + uint8_t status; + bool clean_up; + char name[256]; + struct process *next; +}; + +#define CPU_STACK_SIZE (64 * 1024) +#define STACK_SIZE (1024 * 1024 * 8) diff --git a/src/sched/scheduler.c b/src/sched/scheduler.c deleted file mode 100644 index bec6622..0000000 --- a/src/sched/scheduler.c +++ /dev/null @@ -1,933 +0,0 @@ -#include "scheduler.h" -#include "mm/memory.h" -#include "mm/pmm.h" -#include "libk/stdio.h" -#include "arch/x86_64/cpu/io.h" -#include "arch/x86_64/sys/pit.h" -#include "string.h" - -#define IA32_FS_BASE 0xC0000100 - -/* ===================================================================== - * Forward declarations for GDT/TSS (defined in gdt.c) - * ===================================================================== */ -typedef struct { - 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_offset; -} __attribute__((packed)) TSS; - -extern TSS kernel_tss; - -/* ===================================================================== - * Globals - * ===================================================================== */ -struct runqueue g_runqueue = {0}; -task_t *g_current_task = NULL; - -/* PIT tick counter (defined in pit.c) */ -extern volatile uint64_t g_Ticks; - -/* ===================================================================== - * Linux-compatible nice → CPU weight table (NICE_0_LOAD = 1024) - * - * vruntime_delta = actual_ticks * NICE_0_LOAD / weight[nice + 20] - * timeslice = BASE * weight[nice + 20] / NICE_0_LOAD - * - * Low weight (high nice) → vruntime accumulates faster → scheduled less. - * ===================================================================== */ -#define NICE_0_LOAD 1024u - -static const uint32_t nice_to_weight[40] = { - /* nice -20 */ 88761, 71755, 56483, 46273, 36291, - /* nice -15 */ 29154, 23254, 18705, 14949, 11916, - /* nice -10 */ 9548, 7620, 6100, 4904, 3906, - /* nice -5 */ 3121, 2501, 1991, 1586, 1277, - /* nice 0 */ 1024, 820, 655, 526, 423, - /* nice +5 */ 335, 272, 215, 172, 137, - /* nice +10 */ 110, 87, 70, 56, 45, - /* nice +15 */ 36, 29, 23, 18, 15, -}; - -static inline uint32_t weight_for_nice(int nice) { - int idx = nice + 20; - if (idx < 0) idx = 0; - if (idx > 39) idx = 39; - return nice_to_weight[idx]; -} - -/* ===================================================================== - * PID allocator (simple monotonic counter, single-CPU safe) - * ===================================================================== */ -static pid_t g_next_pid = 1; - -static pid_t alloc_pid(void) { - return g_next_pid++; -} - -/* ===================================================================== - * RT bitmap helpers (O(1) find-first-set) - * ===================================================================== */ -static inline void rt_bitmap_set(struct rt_prio_array *arr, int prio) { - /* prio is 1..99; store at bit (prio-1) */ - int bit = prio - 1; - arr->bitmap[bit / 64] |= (1ULL << (bit % 64)); -} - -static inline void rt_bitmap_clear(struct rt_prio_array *arr, int prio) { - int bit = prio - 1; - arr->bitmap[bit / 64] &= ~(1ULL << (bit % 64)); -} - -/* Returns the highest-priority (lowest numeric) non-empty RT queue, - * or -1 if all are empty. */ -static inline int rt_bitmap_first(const struct rt_prio_array *arr) { - if (arr->bitmap[0]) return __builtin_ctzll(arr->bitmap[0]) + 1; - if (arr->bitmap[1]) return __builtin_ctzll(arr->bitmap[1]) + 65; - return -1; -} - -/* ===================================================================== - * RT FIFO queue operations - * ===================================================================== */ -static void rt_enqueue(struct runqueue *rq, task_t *task) { - int prio = task->static_prio; - struct rt_prio_array *arr = &rq->rt; - - task->rq_next = NULL; - task->rq_prev = arr->tail[prio]; - - if (arr->tail[prio]) - arr->tail[prio]->rq_next = task; - else - arr->head[prio] = task; - - arr->tail[prio] = task; - rt_bitmap_set(arr, prio); - arr->total++; - rq->nr_running++; -} - -/* Dequeue the head of the given priority's FIFO */ -static task_t *rt_dequeue_head(struct runqueue *rq, int prio) { - struct rt_prio_array *arr = &rq->rt; - task_t *task = arr->head[prio]; - if (!task) return NULL; - - arr->head[prio] = task->rq_next; - if (arr->head[prio]) - arr->head[prio]->rq_prev = NULL; - else { - arr->tail[prio] = NULL; - rt_bitmap_clear(arr, prio); - } - - task->rq_next = task->rq_prev = NULL; - arr->total--; - rq->nr_running--; - return task; -} - -/* Remove a specific task from an RT queue (O(1) with doubly-linked list) */ -static void rt_remove(struct runqueue *rq, task_t *task) { - int prio = task->static_prio; - struct rt_prio_array *arr = &rq->rt; - - if (task->rq_prev) task->rq_prev->rq_next = task->rq_next; - else arr->head[prio] = task->rq_next; - - if (task->rq_next) task->rq_next->rq_prev = task->rq_prev; - else arr->tail[prio] = task->rq_prev; - - if (!arr->head[prio]) - rt_bitmap_clear(arr, prio); - - task->rq_next = task->rq_prev = NULL; - arr->total--; - rq->nr_running--; -} - -/* ===================================================================== - * CFS (normal) queue operations — sorted ascending by vruntime - * ===================================================================== */ - -/* - * Insert task into the CFS list, keeping it sorted by vruntime. - * O(n) — acceptable for hobby-kernel task counts; replace with - * red-black tree if you hit performance issues. - */ -static void cfs_enqueue(struct runqueue *rq, task_t *task) { - /* New tasks start at min_vruntime so they don't starve incumbents - * but also don't get a massive head-start. */ - if (task->vruntime < rq->min_vruntime) - task->vruntime = rq->min_vruntime; - - task_t **pp = &rq->cfs_head; - task_t *prev = NULL; - - while (*pp && (*pp)->vruntime <= task->vruntime) { - prev = *pp; - pp = &(*pp)->rq_next; - } - - task->rq_next = *pp; - task->rq_prev = prev; - if (*pp) (*pp)->rq_prev = task; - *pp = task; - - rq->cfs_count++; - rq->nr_running++; -} - -/* Remove the task with the smallest vruntime (head of list) */ -static task_t *cfs_dequeue_min(struct runqueue *rq) { - task_t *task = rq->cfs_head; - if (!task) return NULL; - - rq->cfs_head = task->rq_next; - if (rq->cfs_head) { - rq->cfs_head->rq_prev = NULL; - rq->min_vruntime = rq->cfs_head->vruntime; - } - - task->rq_next = task->rq_prev = NULL; - rq->cfs_count--; - rq->nr_running--; - return task; -} - -/* Remove a specific task from the CFS queue */ -static void cfs_remove(struct runqueue *rq, task_t *task) { - if (task->rq_prev) task->rq_prev->rq_next = task->rq_next; - else rq->cfs_head = task->rq_next; - - if (task->rq_next) task->rq_next->rq_prev = task->rq_prev; - - task->rq_next = task->rq_prev = NULL; - rq->cfs_count--; - rq->nr_running--; -} - -/* ===================================================================== - * Timeslice calculation - * ===================================================================== */ -static uint64_t calc_timeslice(const task_t *task) { - switch (task->policy) { - case SCHED_FIFO: - return UINT64_MAX; /* Runs until it yields or blocks */ - - case SCHED_RR: - return SCHED_RR_SLICE_MS; - - case SCHED_IDLE: - return SCHED_BASE_SLICE_MS; - - default: /* SCHED_NORMAL / SCHED_BATCH */ - { - uint32_t w = weight_for_nice(task->nice); - uint64_t ms = (uint64_t)SCHED_BASE_SLICE_MS * w / NICE_0_LOAD; - if (ms < SCHED_MIN_SLICE_MS) ms = SCHED_MIN_SLICE_MS; - if (ms > SCHED_MAX_SLICE_MS) ms = SCHED_MAX_SLICE_MS; - return ms; - } - } -} - -/* Update a task's vruntime based on how many ticks it actually ran */ -static void update_vruntime(task_t *task, uint64_t elapsed_ticks) { - task->sum_exec_runtime += elapsed_ticks; - - if (task->policy == SCHED_NORMAL || task->policy == SCHED_BATCH) { - /* vruntime_delta = ticks * NICE_0_LOAD / weight - * High-weight (low nice) tasks accumulate vruntime slowly → more CPU. */ - uint32_t w = weight_for_nice(task->nice); - uint64_t delta = elapsed_ticks * NICE_0_LOAD / w; - task->vruntime += delta; - } else { - /* RT and idle: track raw ticks for accounting; vruntime unused */ - task->vruntime += elapsed_ticks; - } -} - -/* ===================================================================== - * sched_enqueue / sched_dequeue (public, uses run-queue lock) - * ===================================================================== */ -void sched_enqueue(task_t *task) { - uint64_t flags; - spinlock_acquire_irqsave(&g_runqueue.lock, &flags); - - task->state = TASK_RUNNING; - - if (task->policy == SCHED_FIFO || task->policy == SCHED_RR) { - rt_enqueue(&g_runqueue, task); - } else if (task->policy == SCHED_IDLE) { - /* SCHED_IDLE: only one idle task, stored separately */ - g_runqueue.idle = task; - } else { - cfs_enqueue(&g_runqueue, task); - } - - spinlock_release_irqrestore(&g_runqueue.lock, flags); -} - -void sched_dequeue(task_t *task) { - uint64_t flags; - spinlock_acquire_irqsave(&g_runqueue.lock, &flags); - - if (task->policy == SCHED_FIFO || task->policy == SCHED_RR) { - rt_remove(&g_runqueue, task); - } else if (task->policy != SCHED_IDLE) { - cfs_remove(&g_runqueue, task); - } - - spinlock_release_irqrestore(&g_runqueue.lock, flags); -} - -/* ===================================================================== - * pick_next_task (called with interrupts OFF and lock held) - * - * Priority order: - * 1. Highest RT priority with a runnable task - * 2. Normal task with smallest vruntime - * 3. Idle task (always exists, never NULL) - * ===================================================================== */ -static task_t *pick_next_task(struct runqueue *rq) { - /* 1. Real-time */ - int rt_prio = rt_bitmap_first(&rq->rt); - if (rt_prio > 0) { - return rt_dequeue_head(rq, rt_prio); - } - - /* 2. CFS normal */ - if (rq->cfs_count > 0) { - return cfs_dequeue_min(rq); - } - - /* 3. Idle fallback */ - return rq->idle; -} - -/* ===================================================================== - * Trampolines - * These are the "return addresses" pushed onto a new task's kernel - * stack. When the task is scheduled for the first time, ret inside - * sched_context_switch() jumps here. - * ===================================================================== */ -static void kthread_trampoline(void) { - /* We arrive with interrupts disabled (just after a context switch). - * Re-enable them before entering user code. */ - x86_64_EnableInterrupts(); - - task_t *self = g_current_task; - self->kthread_entry(self->kthread_arg); - - /* Thread returned — treat it as a clean exit. */ - sched_exit(0); -} - -void set_fs_base(uint64_t base) { - /* ake sure the address is canonical (bits 63..48 all 0 or all 1). - * Non-canonical FS base + any fs: access from user code = #GP. */ - uint64_t high = base >> 48; - if (high != 0 && high != 0xFFFFULL) { - /* Simple sign-extension from bit 47 (common for user-space TLS) */ - if (base & (1ULL << 47)) - base |= 0xFFFFULL << 48; /* negative canonical */ - else - base &= (1ULL << 48) - 1; /* positive canonical */ - } - - asm volatile("wrfsbase %0" : : "r"(base) : "memory"); -} - - -/* ===================================================================== - * Kernel stack setup for a new task - * - * Lay out: [trampoline_addr] [r15=0] [r14=0] [r13=0] [r12=0] - * [rbp=0] [rbx=0] ← ctx.rsp points here - * - * sched_context_switch pops in order: rbx, rbp, r12, r13, r14, r15, - * then ret → trampoline. - * ===================================================================== */ -static void setup_initial_kstack(task_t *task, void *trampoline) { - uint64_t *sp = (uint64_t *)((uint8_t *)task->kernel_stack - + task->kernel_stack_size); - - *--sp = (uint64_t)trampoline; /* "return address" → trampoline */ - *--sp = 0; /* r15 */ - *--sp = 0; /* r14 */ - *--sp = 0; /* r13 */ - *--sp = 0; /* r12 */ - *--sp = 0; /* rbp */ - *--sp = 0; /* rbx */ - - task->ctx.rsp = (uint64_t)sp; -} - -/* ===================================================================== - * Task allocation helper - * ===================================================================== */ -static task_t *alloc_task(const char *name, bool is_user) { - task_t *task = kmalloc(sizeof(task_t)); - if (!task) return NULL; - memset(task, 0, sizeof(task_t)); - - memcpy(task->ctx.fxstate, g_clean_fxstate, 512); - - strncpy(task->name, name, sizeof(task->name) - 1); - task->is_user = is_user; - task->state = TASK_RUNNING; - task->policy = SCHED_NORMAL; - task->nice = NICE_DEFAULT; - task->static_prio = NICE_TO_PRIO(NICE_DEFAULT); - task->prio = task->static_prio; - task->pid = alloc_pid(); - task->ppid = g_current_task ? g_current_task->pid : 0; - task->parent = g_current_task; - - /* Default: all signals use SIG_DFL */ - for (int i = 0; i < _NSIG; i++) - task->sigactions[i].sa_handler = SIG_DFL; - - /* Allocate kernel stack */ - task->kernel_stack_size = KSTACK_SIZE; - task->kernel_stack = kmalloc(KSTACK_SIZE); - if (!task->kernel_stack) { - kfree(task); - return NULL; - } - memset(task->kernel_stack, 0xCC, KSTACK_SIZE); /* poison */ - - return task; -} - -/* ===================================================================== - * Public task-creation API - * ===================================================================== */ -task_t *sched_create_kthread(const char *name, - void (*entry)(void *), void *arg) -{ - task_t *task = alloc_task(name, false); - if (!task) return NULL; - - task->kthread_entry = entry; - task->kthread_arg = arg; - task->ctx.cr3 = 0; /* Kernel threads share kernel_pagemap */ - - setup_initial_kstack(task, kthread_trampoline); - - task->time_slice = calc_timeslice(task); - task->vruntime = g_runqueue.min_vruntime; - - sched_enqueue(task); - //printf("[sched] kthread '%s' pid=%d created\n", task->name, task->pid); - return task; -} - -task_t *sched_create_user_task(const char *name, - uint64_t entry_rip, - uint64_t user_rsp, - struct pagemap *pm, - uint64_t tls_fs_base, - uint64_t phdr_va, - uint16_t phent, - uint16_t phnum) -{ - task_t *task = alloc_task(name, true); - if (!task) return NULL; - - task->pagemap = pm; - task->user_entry = entry_rip; - task->user_stack_top= user_rsp; - task->tls_fs_base = tls_fs_base; - task->phdr_va = phdr_va; - task->phent = phent; - task->phnum = phnum; - - task->ctx.cr3 = (uint64_t)pm->top_level - MEM_PHYS_OFFSET; - - setup_initial_kstack(task, user_task_trampoline); - - task->time_slice = calc_timeslice(task); - task->vruntime = g_runqueue.min_vruntime; - - for (size_t i = 256; i < 512; i++) { - pm->top_level[i] = kernel_pagemap->top_level[i]; - } - - sched_enqueue(task); - return task; -} - -/* ===================================================================== - * Idle task - * ===================================================================== */ -static void idle_entry(void *arg) { - (void)arg; - for (;;) { - asm volatile("sti; hlt; cli" ::: "memory"); - /* If we get here, an interrupt fired; schedule() will be - * called by sched_tick if a real task became runnable. */ - } -} - -/* ===================================================================== - * sched_init — set up the idle task and initialise the run queue - * ===================================================================== */ -void sched_init(void) { - spinlock_init(&g_runqueue.lock); - g_runqueue.min_vruntime = 0; - - /* Synthesise a "current" descriptor for the boot thread so that - * the first schedule() call has something valid in g_current_task. */ - task_t *boot = kmalloc(sizeof(task_t)); - if (!boot) { - printf("[sched] FATAL: cannot allocate boot task\n"); - while (1) asm volatile("hlt"); - } - memset(boot, 0, sizeof(task_t)); - memcpy(boot->ctx.fxstate, g_clean_fxstate, 512); - strncpy(boot->name, "boot", 63); - boot->pid = alloc_pid(); /* pid 1 */ - boot->state = TASK_RUNNING; - boot->policy = SCHED_NORMAL; - boot->nice = NICE_DEFAULT; - boot->static_prio = NICE_TO_PRIO(NICE_DEFAULT); - boot->prio = boot->static_prio; - boot->time_slice = calc_timeslice(boot); - boot->slice_start = g_Ticks; - /* kernel_stack: we're already running on it; RSP will be saved by - * the first sched_context_switch() call, so no setup needed. */ - boot->is_user = false; - - g_current_task = boot; - g_runqueue.current = boot; - - /* Create the idle task but do NOT go through sched_create_kthread - * because we store it separately (not on the CFS/RT queues). */ - task_t *idle = alloc_task("idle", false); - if (!idle) { - printf("[sched] FATAL: cannot allocate idle task\n"); - while (1) asm volatile("hlt"); - } - idle->policy = SCHED_IDLE; - idle->static_prio = IDLE_PRIO; - idle->prio = IDLE_PRIO; - idle->kthread_entry = idle_entry; - idle->kthread_arg = NULL; - idle->ctx.cr3 = 0; - setup_initial_kstack(idle, kthread_trampoline); - - g_runqueue.idle = idle; - - printf("[sched] initialised; boot pid=%d\n", boot->pid); -} - -/* ===================================================================== - * schedule() — the heart of the scheduler - * - * Must be called with interrupts disabled (IF=0). Restores IF when - * the scheduled-in task next runs (via its own stack context or via - * the kthread_trampoline which calls x86_64_EnableInterrupts). - * ===================================================================== */ -void schedule(void) { - /* - * We deliberately do NOT use spinlock_acquire_irqsave here because - * we're already called with IF=0 (either from an ISR or from - * sched_yield/sched_block which do cli first). - * We use a plain spinlock_acquire_or_wait so that on SMP (future) - * another CPU spinning on the lock eventually gets it. - */ - spinlock_acquire_or_wait(&g_runqueue.lock); - - task_t *prev = g_runqueue.current; - task_t *next = pick_next_task(&g_runqueue); - - if (next->is_user) { - if (next->tls_fs_base != 0) { - set_fs_base(next->tls_fs_base); - } else { - printf("Warning: user task '%s' has no TLS FS base set; leaving FS at 0\n", next->name); - } - - } - - if (next == prev || next == NULL) { - /* Nothing to switch to; keep running current task. */ - spinlock_drop(&g_runqueue.lock); - return; - } - - /* Account for time the current task actually ran */ - uint64_t now = g_Ticks; - uint64_t elapsed = (prev->slice_start <= now) - ? (now - prev->slice_start) - : 0; - update_vruntime(prev, elapsed); - - /* Re-enqueue the outgoing task if it is still runnable (preempted). - * If it blocked/exited, its state is no longer TASK_RUNNING. */ - if (prev->state == TASK_RUNNING && prev != g_runqueue.idle) { - prev->time_slice = calc_timeslice(prev); /* refresh slice */ - - if (prev->policy == SCHED_FIFO || prev->policy == SCHED_RR) { - rt_enqueue(&g_runqueue, prev); - } else if (prev->policy != SCHED_IDLE) { - cfs_enqueue(&g_runqueue, prev); - } - } - - /* Set up the incoming task */ - next->state = TASK_RUNNING; - next->need_reschedule = false; - next->slice_start = now; - - g_runqueue.current = next; - g_current_task = next; - g_runqueue.nr_switches++; - - /* Update TSS.RSP0 so that user-mode interrupts for this task use - * the correct kernel stack. */ - if (next->is_user && next->kernel_stack) { - kernel_tss.rsp0 = (uint64_t)next->kernel_stack - + next->kernel_stack_size; - } - - spinlock_drop(&g_runqueue.lock); - - /* ---- Context switch -------------------------------------------- */ - //printf("[sched] switching from '%s' (pid=%d) to '%s' (pid=%d)\n", - // prev->name, prev->pid, next->name, next->pid); - if (next->is_user) { - //printf("switching to user task, fs_base=0x%lx\n", next->tls_fs_base); - } - sched_context_switch(&prev->ctx, &next->ctx); - - /* - * When we return here we are BACK in the context of `prev` - * (which has just been rescheduled). Process any pending signals - * before returning to user space. - */ - task_handle_pending_signals(); -} - -/* ===================================================================== - * sched_tick() — called from the PIT/LAPIC IRQ every millisecond - * ===================================================================== */ -void sched_tick(void) { - task_t *cur = g_current_task; - if (!cur) return; - - /* Decrement remaining timeslice */ - if (cur->time_slice > 0) - cur->time_slice--; - - /* - * Trigger a reschedule if: - * (a) The timeslice ran out, or - * (b) need_reschedule was set by a wakeup of a higher-prio task. - */ - if (cur->time_slice == 0 || cur->need_reschedule) { - /* schedule() expects IF=0 — guaranteed here because we are - * inside an IRQ handler; the CPU cleared IF on entry. */ - schedule(); - } -} - -/* ===================================================================== - * sched_yield() — voluntary CPU release - * ===================================================================== */ -void sched_yield(void) { - x86_64_DisableInterrupts(); - g_current_task->time_slice = 0; /* Force preemption */ - schedule(); - x86_64_EnableInterrupts(); -} - -/* ===================================================================== - * sched_block() — put current task to sleep - * - * Caller must set the task state BEFORE calling (the function - * honours whatever state is already set). Alternatively pass the - * desired reason and we set it here. - * ===================================================================== */ -void sched_block(task_state_t reason) { - x86_64_DisableInterrupts(); - g_current_task->state = reason; - schedule(); - x86_64_EnableInterrupts(); - /* When we return here the task has been woken up. */ -} - -/* ===================================================================== - * sched_wake() — wake a sleeping task - * Safe to call from interrupt context. - * ===================================================================== */ -void sched_wake(task_t *task) { - if (!task) return; - - uint64_t flags; - spinlock_acquire_irqsave(&g_runqueue.lock, &flags); - - if (task->state != TASK_RUNNING) { - task->state = TASK_RUNNING; - task->time_slice = calc_timeslice(task); - - if (task->policy == SCHED_FIFO || task->policy == SCHED_RR) { - rt_enqueue(&g_runqueue, task); - } else if (task->policy != SCHED_IDLE) { - cfs_enqueue(&g_runqueue, task); - } - - /* - * Preempt the current task if the woken task has strictly - * higher priority (lower numeric priority value). - */ - task_t *cur = g_runqueue.current; - if (cur && task->prio < cur->prio) { - cur->need_reschedule = true; - } - } - - spinlock_release_irqrestore(&g_runqueue.lock, flags); -} - -/* ===================================================================== - * sched_exit() — terminate the current task (noreturn) - * ===================================================================== */ -void sched_exit(int exit_code) { - x86_64_DisableInterrupts(); - - task_t *self = g_current_task; - self->exit_code = exit_code; - self->state = TASK_ZOMBIE; - - /* Notify parent (send SIGCHLD) */ - if (self->parent) - task_send_signal(self->parent, SIGCHLD); - - //printf("[sched] task '%s' pid=%d exited with code %d\n", - // self->name, self->pid, exit_code); - - /* Hand off to someone else; we will never return. */ - schedule(); - - /* schedule() should never return to a ZOMBIE task, but just in case: */ - for (;;) asm volatile("hlt"); - __builtin_unreachable(); -} - -/* ===================================================================== - * Signal delivery - * ===================================================================== */ - -/* Default signal actions */ -typedef enum { SIG_ACTION_TERM, SIG_ACTION_CORE, SIG_ACTION_IGN, - SIG_ACTION_STOP, SIG_ACTION_CONT } sig_default_action_t; - -static sig_default_action_t default_action(int signum) { - switch (signum) { - case SIGHUP: case SIGINT: case SIGKILL: case SIGPIPE: - case SIGALRM: case SIGTERM: case SIGUSR1: case SIGUSR2: - case SIGPROF: case SIGVTALRM: case SIGSTKFLT: - return SIG_ACTION_TERM; - case SIGQUIT: case SIGILL: case SIGABRT: case SIGFPE: - case SIGSEGV: case SIGBUS: case SIGSYS: case SIGTRAP: - case SIGXCPU: case SIGXFSZ: - return SIG_ACTION_CORE; /* we treat CORE same as TERM for now */ - case SIGCHLD: case SIGURG: case SIGWINCH: case SIGIO: case SIGPWR: - return SIG_ACTION_IGN; - case SIGSTOP: case SIGTSTP: case SIGTTIN: case SIGTTOU: - return SIG_ACTION_STOP; - case SIGCONT: - return SIG_ACTION_CONT; - default: - return SIG_ACTION_TERM; - } -} - -int task_send_signal(task_t *task, int signum) { - if (!task) return -1; - if (signum <= 0 || signum >= _NSIG) return -1; - - uint64_t flags; - spinlock_acquire_irqsave(&g_runqueue.lock, &flags); - - /* Set pending bit */ - task->pending_signals |= (1ULL << signum); - - /* SIGKILL and SIGCONT always wake the target */ - if (signum == SIGKILL || signum == SIGCONT) { - if (task->state == TASK_INTERRUPTIBLE || - task->state == TASK_STOPPED || - task->state == TASK_UNINTERRUPTIBLE) { - task->state = TASK_RUNNING; - /* Re-enqueue (simplified: call rt/cfs directly since lock held) */ - if (task->policy == SCHED_FIFO || task->policy == SCHED_RR) - rt_enqueue(&g_runqueue, task); - else if (task->policy != SCHED_IDLE) - cfs_enqueue(&g_runqueue, task); - } - } else if (!(task->signal_mask & (1ULL << signum))) { - /* Unblocked signal: wake an interruptible sleeper */ - if (task->state == TASK_INTERRUPTIBLE) { - task->state = TASK_RUNNING; - if (task->policy == SCHED_FIFO || task->policy == SCHED_RR) - rt_enqueue(&g_runqueue, task); - else if (task->policy != SCHED_IDLE) - cfs_enqueue(&g_runqueue, task); - } - } - - spinlock_release_irqrestore(&g_runqueue.lock, flags); - return 0; -} - -/* - * Handle pending signals for the current task. - * Called just before returning to user space (end of schedule(), syscall - * return path, or end of IRQ handler for user-mode tasks). - */ -void task_handle_pending_signals(void) { - task_t *self = g_current_task; - if (!self) return; - - /* Only run signal handling when we are about to return to user mode. - * Kernel threads can still get synchronous handlers, but we avoid - * unnecessary work / possible recursion on kernel tasks. */ - if (!self->is_user && !(self->pending_signals & ~self->signal_mask)) - return; - - while (self->pending_signals & ~self->signal_mask) { - uint64_t deliverable = self->pending_signals & ~self->signal_mask; - int signum = __builtin_ctzll(deliverable) + 1; - if (signum >= _NSIG) break; - - self->pending_signals &= ~(1ULL << (signum - 1)); - - sighandler_t handler = self->sigactions[signum].sa_handler; - - if (handler == SIG_IGN) { - if (signum == SIGCHLD) continue; - continue; - } - - if (handler != SIG_DFL) { - if (!self->is_user) { - handler(signum); /* kernel thread */ - } else { - /* TODO: proper sigframe + adjust trap frame on kernel stack */ - printf("[signal] TODO: deliver signal %d to user task '%s' (pid=%d)\n", - signum, self->name, self->pid); - /* For now fall through to default action so we don't silently ignore */ - } - } - - /* Default action (also used for user tasks when no handler is installed) */ - if (handler == SIG_DFL || self->is_user) { - switch (default_action(signum)) { - case SIG_ACTION_TERM: - case SIG_ACTION_CORE: - printf("[signal] task '%s' pid=%d killed by signal %d\n", - self->name, self->pid, signum); - sched_exit(128 + signum); /* does not return */ - break; - - case SIG_ACTION_STOP: - self->state = TASK_STOPPED; - if (self->parent) - task_send_signal(self->parent, SIGCHLD); - sched_block(TASK_STOPPED); /* does not return until CONT */ - break; - - case SIG_ACTION_CONT: - case SIG_ACTION_IGN: - break; - } - } - } -} - -/* ===================================================================== - * sched_find_task (linear scan — O(n), suitable for small task counts) - * ===================================================================== */ -task_t *sched_find_task(pid_t pid) { - /* - * Walk the CFS list and RT queues. In a production kernel this - * would be a hash table. For KirkOS this is fine. - */ - task_t *t = g_runqueue.cfs_head; - while (t) { - if (t->pid == pid) return t; - t = t->rq_next; - } - for (int p = RT_PRIO_MIN; p <= RT_PRIO_MAX; p++) { - t = g_runqueue.rt.head[p]; - while (t) { - if (t->pid == pid) return t; - t = t->rq_next; - } - } - if (g_runqueue.current && g_runqueue.current->pid == pid) - return g_runqueue.current; - return NULL; -} - -/* ===================================================================== - * Priority / scheduler controls - * ===================================================================== */ -int task_set_nice(task_t *task, int nice) { - if (nice < NICE_MIN) nice = NICE_MIN; - if (nice > NICE_MAX) nice = NICE_MAX; - - int old_nice = task->nice; - - task->nice = nice; - task->static_prio = NICE_TO_PRIO(nice); - task->prio = task->static_prio; - - /* - * Recompute the timeslice. If the task is currently on a queue we - * would need to re-sort it (out of scope here — next schedule() will - * pick the right slot when it re-enqueues). - */ - task->time_slice = calc_timeslice(task); - - return old_nice; -} - -int task_set_scheduler(task_t *task, int policy, int rt_prio) { - if (policy != SCHED_NORMAL && policy != SCHED_FIFO && - policy != SCHED_RR && policy != SCHED_BATCH && - policy != SCHED_IDLE) - return -1; - - if ((policy == SCHED_FIFO || policy == SCHED_RR) && - (rt_prio < RT_PRIO_MIN || rt_prio > RT_PRIO_MAX)) - return -1; - - /* Remove from current queue, change policy, re-enqueue */ - bool was_queued = (task->state == TASK_RUNNING && - task != g_runqueue.current); - if (was_queued) - sched_dequeue(task); - - task->policy = policy; - task->static_prio = (policy == SCHED_FIFO || policy == SCHED_RR) - ? rt_prio - : NICE_TO_PRIO(task->nice); - task->prio = task->static_prio; - task->time_slice = calc_timeslice(task); - - if (was_queued) - sched_enqueue(task); - - return 0; -} \ No newline at end of file diff --git a/src/sched/scheduler.h b/src/sched/scheduler.h deleted file mode 100644 index b2f6973..0000000 --- a/src/sched/scheduler.h +++ /dev/null @@ -1,330 +0,0 @@ -#pragma once -#include -#include -#include -#include "mm/vmm.h" -#include "mp/spinlock.h" - -/* ===================================================================== - * POSIX signal numbers - * ===================================================================== */ -#define SIGHUP 1 -#define SIGINT 2 -#define SIGQUIT 3 -#define SIGILL 4 -#define SIGTRAP 5 -#define SIGABRT 6 -#define SIGBUS 7 -#define SIGFPE 8 -#define SIGKILL 9 /* cannot be caught or ignored */ -#define SIGUSR1 10 -#define SIGSEGV 11 -#define SIGUSR2 12 -#define SIGPIPE 13 -#define SIGALRM 14 -#define SIGTERM 15 -#define SIGSTKFLT 16 -#define SIGCHLD 17 -#define SIGCONT 18 -#define SIGSTOP 19 /* cannot be caught or ignored */ -#define SIGTSTP 20 -#define SIGTTIN 21 -#define SIGTTOU 22 -#define SIGURG 23 -#define SIGXCPU 24 -#define SIGXFSZ 25 -#define SIGVTALRM 26 -#define SIGPROF 27 -#define SIGWINCH 28 -#define SIGIO 29 -#define SIGPWR 30 -#define SIGSYS 31 -#define _NSIG 32 - -/* Clean FXSAVE state used to initialize every new task */ -extern uint8_t g_clean_fxstate[512] __attribute__((aligned(16))); - -typedef void (*sighandler_t)(int signum); -#define SIG_DFL ((sighandler_t)0) /* default action */ -#define SIG_IGN ((sighandler_t)1) /* ignore signal */ -#define SIG_ERR ((sighandler_t)-1) /* error return */ - -#define SA_NOCLDSTOP 0x00000001 -#define SA_NOCLDWAIT 0x00000002 -#define SA_SIGINFO 0x00000004 -#define SA_RESTORER 0x04000000 -#define SA_ONSTACK 0x08000000 -#define SA_RESTART 0x10000000 -#define SA_NODEFER 0x40000000 -#define SA_RESETHAND 0x80000000 - -struct sigaction { - sighandler_t sa_handler; - uint64_t sa_mask; /* signals blocked while handler runs */ - int sa_flags; -}; - -/* ===================================================================== - * Scheduling policies (POSIX) - * ===================================================================== */ -#define SCHED_NORMAL 0 /* Fair time-sharing (CFS-like, nice values) */ -#define SCHED_FIFO 1 /* Real-time FIFO – runs until yield or block */ -#define SCHED_RR 2 /* Real-time round-robin with fixed timeslice */ -#define SCHED_BATCH 3 /* CPU-bound variant of NORMAL, no preemption boost */ -#define SCHED_IDLE 5 /* Only runs when nothing else is runnable */ - -/* Priority ranges: - * RT tasks: static_prio 1 .. 99 (1 = highest) - * Normal tasks: static_prio 100 .. 139 (maps from nice -20 .. +19) - * Idle: static_prio 140 - */ -#define MAX_RT_PRIO 100 -#define MAX_PRIO 140 -#define RT_PRIO_MIN 1 -#define RT_PRIO_MAX 99 -#define NICE_MIN (-20) -#define NICE_MAX 19 -#define NICE_DEFAULT 0 -#define IDLE_PRIO 140 - -/* nice ↔ static_prio conversions for SCHED_NORMAL */ -#define NICE_TO_PRIO(n) (MAX_RT_PRIO + (n) + 20) -#define PRIO_TO_NICE(p) ((p) - MAX_RT_PRIO - 20) - -/* Timeslice constants (ticks, PIT at 1000 Hz → 1 tick = 1 ms) */ -#define SCHED_BASE_SLICE_MS 10 /* base timeslice for NICE_DEFAULT */ -#define SCHED_MIN_SLICE_MS 1 /* minimum timeslice (1 ms) */ -#define SCHED_MAX_SLICE_MS 100 /* maximum timeslice (100 ms) */ -#define SCHED_RR_SLICE_MS 10 /* fixed timeslice for SCHED_RR */ - -/* ===================================================================== - * Task states - * ===================================================================== */ -typedef enum task_state { - TASK_RUNNING = 0, /* Runnable – on a run queue or executing */ - TASK_INTERRUPTIBLE = 1, /* Sleeping, can be woken by signal */ - TASK_UNINTERRUPTIBLE = 2, /* Sleeping, ignores signals (D state) */ - TASK_STOPPED = 4, /* Halted by SIGSTOP / SIGTSTP */ - TASK_ZOMBIE = 8, /* Exited, waiting for parent to wait() */ - TASK_DEAD = 16, /* Fully reaped, memory can be freed */ -} task_state_t; - -/* ===================================================================== - * Minimal CPU context - * - * Only RSP and CR3 live here; all callee-saved GPRs are pushed onto - * the kernel stack by sched_context_switch() before RSP is saved. - * This keeps the struct tiny and the assembly dead simple. - * ===================================================================== */ -struct cpu_context { - uint64_t rsp; /* Saved kernel stack pointer */ - uint64_t cr3; /* Physical address of PML4 (0 = stay on kernel map) */ - uint8_t fxstate[512] __attribute__((aligned(16))); /* FPU/SSE state (FXSAVE area) */ -}; - -/* ===================================================================== - * Task / Process descriptor - * ===================================================================== */ -typedef int pid_t; -typedef struct task task_t; - -struct task { - /* ---- CPU context (must stay first – asm references it at offset 0) */ - struct cpu_context ctx; - - /* ---- Identity ---------------------------------------------------- */ - pid_t pid; - pid_t ppid; - char name[64]; - bool is_user; /* true = user process, false = kernel thread */ - - /* ---- Scheduling policy and priority ------------------------------ */ - int policy; /* SCHED_NORMAL / SCHED_FIFO / SCHED_RR / … */ - int static_prio; /* Immutable base priority */ - int nice; /* -20 .. +19, SCHED_NORMAL only */ - int prio; /* Effective priority (may be boosted) */ - - /* ---- State ------------------------------------------------------- */ - volatile task_state_t state; - bool need_reschedule; /* Set when a higher-priority task wakes up */ - - /* ---- Time accounting (ticks, 1 tick = 1 ms at 1000 Hz PIT) ------- */ - uint64_t vruntime; /* Virtual runtime (tick-equivalents, weighted) */ - uint64_t sum_exec_runtime; /* Total CPU time consumed (raw ticks) */ - uint64_t time_slice; /* Remaining timeslice (ticks) */ - uint64_t slice_start; /* Tick when the current slice began */ - - /* ---- Memory ------------------------------------------------------ */ - struct pagemap *pagemap; /* NULL → use kernel_pagemap */ - void *kernel_stack; /* Pointer to bottom of kernel-stack alloc */ - size_t kernel_stack_size; - - /* ---- Entry points ------------------------------------------------ */ - uint64_t user_entry; /* User-space RIP for user tasks */ - uint64_t user_stack_top; /* User-space RSP for user tasks */ - void (*kthread_entry)(void *arg); /* Kernel thread entry point */ - void *kthread_arg; - uint64_t tls_fs_base; /* FS base for user tasks (TLS support) */ - uint64_t phdr_va; - uint16_t phent; - uint16_t phnum; - - /* ---- Signals ----------------------------------------------------- */ - uint64_t pending_signals; /* Bitmask of unhandled signals */ - uint64_t signal_mask; /* Blocked (SIG_BLOCK) signals */ - struct sigaction sigactions[_NSIG]; - - /* ---- Exit status ------------------------------------------------- */ - int exit_code; - - /* ---- Run-queue linkage (doubly-linked, intrusive) ---------------- */ - task_t *rq_next; - task_t *rq_prev; - - /* ---- Process tree ------------------------------------------------ */ - task_t *parent; - task_t *first_child; - task_t *next_sibling; -}; - -/* ===================================================================== - * Run queue - * - * Two sub-queues per CPU (single CPU for now, MP-ready by design): - * - * 1. RT array – 99 FIFO lists indexed by RT priority. Highest - * priority with a runnable task is O(1) via bitmap. - * - * 2. CFS list – Tasks sorted by vruntime (ascending). Pick-next - * is O(1) (front of list); insert is O(n) – good - * enough for now, swap in an rb-tree later. - * - * Idle task is stored separately and is returned only when both - * sub-queues are empty. - * ===================================================================== */ -#define RT_QUEUE_LEVELS MAX_RT_PRIO /* 100 levels (index 0 = unused, 1–99 used) */ - -struct rt_prio_array { - /* - * Bitmap: bit N is set ↔ rt_queue[N] is non-empty. - * Two 64-bit words cover 128 bits, enough for 100 levels. - */ - uint64_t bitmap[2]; - task_t *head[RT_QUEUE_LEVELS]; /* FIFO queue heads */ - task_t *tail[RT_QUEUE_LEVELS]; /* FIFO queue tails */ - int total; /* Total RT tasks enqueued */ -}; - -struct runqueue { - spinlock_t lock; - - /* Real-time (SCHED_FIFO / SCHED_RR) */ - struct rt_prio_array rt; - - /* Normal (SCHED_NORMAL / SCHED_BATCH) — sorted ascending by vruntime */ - task_t *cfs_head; - int cfs_count; - uint64_t min_vruntime; /* Lower bound; new tasks start from here */ - - /* Idle fallback (SCHED_IDLE) */ - task_t *idle; - - /* Currently executing task on this CPU */ - task_t *current; - - /* Statistics */ - uint64_t nr_switches; - uint64_t nr_running; /* Total runnable tasks (all classes) */ -}; - -/* ===================================================================== - * Globals (single-CPU; extend to per-cpu array for SMP) - * ===================================================================== */ -extern struct runqueue g_runqueue; -extern task_t *g_current_task; /* Pointer to currently-running task */ - -/* ===================================================================== - * Public scheduler API - * ===================================================================== */ - -/* Initialise the scheduler (call after PMM + VMM + PIT are ready) */ -void sched_init(void); - -/* Create a kernel thread and enqueue it immediately */ -task_t *sched_create_kthread(const char *name, - void (*entry)(void *), void *arg); - -/* Create a user-space task and enqueue it immediately */ -task_t *sched_create_user_task(const char *name, - uint64_t entry_rip, - uint64_t user_rsp, - struct pagemap *pm, - uint64_t tls_fs_base, - uint64_t phdr_va, - uint16_t phent, - uint16_t phnum); - - -/* Add a task to the appropriate run queue */ -void sched_enqueue(task_t *task); - -/* Remove a task from its run queue (does NOT free it) */ -void sched_dequeue(task_t *task); - -/* Pick next task and perform context switch (call with IF=0) */ -void schedule(void); - -/* Called from the PIT/LAPIC timer IRQ every tick (1 ms) */ -void sched_tick(void); - -/* Voluntarily give up the CPU */ -void sched_yield(void); - -/* Block current task (IF must be 0 on entry; IF restored by schedule) */ -void sched_block(task_state_t reason); - -/* Wake a sleeping task (safe to call from IRQ context) */ -void sched_wake(task_t *task); - -/* Terminate the current task (noreturn) */ -void sched_exit(int exit_code) __attribute__((noreturn)); - -/* ---- Signal API ------------------------------------------------------- */ - -/* Send signal signum to task (safe from any context) */ -int task_send_signal(task_t *task, int signum); - -/* Find a task by PID (NULL if not found) */ -task_t *sched_find_task(pid_t pid); - -/* Process pending signals for the current task (call before returning to user) */ -void task_handle_pending_signals(void); - -/* ---- Priority / policy control --------------------------------------- */ - -/* Set nice value [-20, +19] for a SCHED_NORMAL task; returns old nice */ -int task_set_nice(task_t *task, int nice); - -/* Change scheduling policy + RT priority; returns 0 on success */ -int task_set_scheduler(task_t *task, int policy, int rt_prio); - -/* ---- Convenience ----------------------------------------------------- */ -static inline task_t *sched_current(void) { return g_current_task; } - -/* ---- Assembly context switch (defined in sched_switch.S) ------------- */ -/* - * Save callee-saved registers of the current context onto its kernel - * stack and record RSP in *from. Then switch to *to's stack, restore - * its callee-saved registers, and return — which resumes wherever *to - * last called schedule(). For first-time tasks the "return" jumps to - * the appropriate trampoline. - */ -void sched_context_switch(struct cpu_context *from, - struct cpu_context *to); - -void set_fs_base(uint64_t base); - -extern void user_task_trampoline(void); /* Defined in user_task_trampoline.S */ - -/* Kernel stack size for each task */ -#define KSTACK_SIZE (32 * 1024) /* 32 KiB — comfortable headroom */ \ No newline at end of file diff --git a/src/sched/syscall.c b/src/sched/syscall.c new file mode 100644 index 0000000..9cbd0be --- /dev/null +++ b/src/sched/syscall.c @@ -0,0 +1,24 @@ +#include "libk/debug.h" +#include "libk/errno.h" +#include "syscall.h" +#include "arch/x86_64/sys/prcb.h" + +syscall_handler_t syscalls[512] = {NULL}; +char *syscalls_name[512] = {NULL}; + +void syscall_handle(struct syscall_arguments *args) { + if (args->syscall_nr >= 512 || syscalls[args->syscall_nr] == NULL) { + args->ret = -1; + errno = ENOSYS; + return; + } +#if 0 + kprintf("%s called by process %s\n", syscalls_name[args->syscall_nr], sched_get_running_thread()->mother_proc->name); +#endif + syscalls[args->syscall_nr](args); +#if 0 + if ((int)args->ret < 0) { + kprintf("%s failed. Called by process %s with errno %d\n", syscalls_name[args->syscall_nr], sched_get_running_thread()->mother_proc->name, errno); + } +#endif +} diff --git a/src/sched/syscall.h b/src/sched/syscall.h new file mode 100644 index 0000000..b5b9397 --- /dev/null +++ b/src/sched/syscall.h @@ -0,0 +1,32 @@ +#pragma once +#include +#include +#include + +struct syscall_arguments { + uint64_t syscall_nr; + uint64_t args0; + uint64_t args1; + uint64_t args2; + uint64_t args3; + uint64_t args4; + uint64_t args5; + uint64_t ret; +}; + +typedef void (*syscall_handler_t)(struct syscall_arguments *); + +extern syscall_handler_t syscalls[]; +extern char *syscalls_name[]; + +void syscall_install_handler(void); +#define syscall_register_handler(A, B) \ + syscalls[A] = B; \ + syscalls_name[A] = #B +void syscall_handle(struct syscall_arguments *args); +uint64_t syscall_helper_user_to_kernel_address(uintptr_t user_addr); +bool syscall_helper_copy_to_user(uintptr_t user_addr, void *buffer, + size_t count); +bool syscall_helper_copy_from_user(uintptr_t user_addr, void *buffer, + size_t count); + diff --git a/src/sched/user_task_trampoline.S b/src/sched/user_task_trampoline.S deleted file mode 100644 index 8dbbc48..0000000 --- a/src/sched/user_task_trampoline.S +++ /dev/null @@ -1,140 +0,0 @@ - -/* ── struct task offsets ─────────────────────────────────────────────────── */ - -.equ TASK_CTX, 0 -.equ TASK_CTX_RSP, 0 -.equ TASK_CTX_CR3, 8 -.equ TASK_CTX_FXSTATE, 16 - -.equ TASK_PID, 528 /* update if` struct changes */ -.equ TASK_PPID, 532 -.equ TASK_NAME, 536 -.equ TASK_IS_USER, 600 -.equ TASK_POLICY, 604 -.equ TASK_STATIC_PRIO, 608 -.equ TASK_NICE, 612 -.equ TASK_PRIO, 616 -.equ TASK_STATE, 620 # enum (4 bytes) -.equ TASK_NEED_RESCHED, 624 # bool (1 byte) -.equ TASK_VRUNTIME, 632 -.equ TASK_SUM_EXEC_RUNTIME,640 -.equ TASK_TIME_SLICE, 648 -.equ TASK_SLICE_START, 656 -.equ TASK_PAGEMAP, 664 -.equ TASK_KERNEL_STACK, 672 -.equ TASK_KERNEL_STACK_SIZE,680 -.equ TASK_USER_ENTRY, 688 -.equ TASK_USER_STACK_TOP, 696 -.equ TASK_KTHREAD_ENTRY, 704 -.equ TASK_KTHREAD_ARG, 712 -.equ TASK_TLS_FS_BASE, 720 -.equ TASK_PHDR_VA, 728 -.equ TASK_PHENT, 736 # uint16_t -.equ TASK_PHNUM, 738 # uint16_t - -/* ── GDT selectors ───────────────────────────────────────────────────────── */ -.equ SEL_USER_DS, 0x1B /* ring-3 data (index 3, RPL 3) */ -.equ SEL_USER_CS, 0x23 /* ring-3 code (index 4, RPL 3) */ - -/* ── ELF auxiliary-vector types ──────────────────────────────────────────── */ -.equ AT_NULL, 0 -.equ AT_PAGESZ, 6 -.equ AT_ENTRY, 9 -.equ AT_PHDR, 3 -.equ AT_PHENT, 4 -.equ AT_PHNUM, 5 -.equ AT_BASE, 7 - -/* ═══════════════════════════════════════════════════════════════════════════ - * user_task_trampoline - * ═══════════════════════════════════════════════════════════════════════════ */ -.section .text -.global user_task_trampoline -.type user_task_trampoline, @function - -user_task_trampoline: - movq g_current_task(%rip), %rbx - - /* ── TLS FS base ───────────────────────────────────────────────────── */ - movq TASK_TLS_FS_BASE(%rbx), %rdi - testq %rdi, %rdi - jz .Lno_tls - call set_fs_base - movq g_current_task(%rip), %rbx - -.Lno_tls: -/* ── Stash values we need after we switch stacks ───────────────────── */ - movq TASK_USER_STACK_TOP(%rbx), %r15 - movq TASK_USER_ENTRY(%rbx), %r14 - movq TASK_KERNEL_STACK(%rbx), %r13 - addq TASK_KERNEL_STACK_SIZE(%rbx), %r13 - - /* ── Load auxv values ──────── */ - movq TASK_PHDR_VA(%rbx), %r11 - movzwq TASK_PHENT(%rbx), %r10 - movzwq TASK_PHNUM(%rbx), %r9 - - /* ── Build initial user stack ─────────────────────── */ - /* program name string */ - movabsq $0x726f776f6c6c6568, %rax - movq %rax, -0x20(%r15) - movabsq $0x000000000000646c, %rax - movq %rax, -0x18(%r15) - - /* argc / argv / envp */ - movq $1, -0xB0(%r15) /* argc = 1 */ - leaq -0x20(%r15), %rax - movq %rax, -0xA8(%r15) /* argv[0] */ - movq $0, -0xA0(%r15) - movq $0, -0x98(%r15) /* envp[0] = NULL */ - - /* auxv */ - movq $AT_PAGESZ, -0x90(%r15) - movq $4096, -0x88(%r15) - - movq $AT_ENTRY, -0x80(%r15) - movq %r14, -0x78(%r15) - - movq $AT_PHDR, -0x70(%r15) - movq %r11, -0x68(%r15) - - movq $AT_PHENT, -0x60(%r15) - movq %r10, -0x58(%r15) - - movq $AT_PHNUM, -0x50(%r15) - movq %r9, -0x48(%r15) - - movq $AT_BASE, -0x40(%r15) - movq $0, -0x38(%r15) - - movq $AT_NULL, -0x30(%r15) - movq $0, -0x28(%r15) - - leaq -0xB0(%r15), %r12 /* user RSP */ - - /* ── Pivot to kernel stack top and build iretq frame ──────────────── */ - movq %r13, %rsp - - pushq $SEL_USER_DS - pushq %r12 - pushfq - orq $0x200, (%rsp) - pushq $SEL_USER_CS - pushq %r14 - - /* Zero GPRs */ - xorq %rax, %rax - xorq %rbx, %rbx - xorq %rcx, %rcx - xorq %rdx, %rdx - xorq %rsi, %rsi - xorq %rdi, %rdi - xorq %rbp, %rbp - xorq %r8, %r8 - xorq %r9, %r9 - xorq %r10, %r10 - xorq %r11, %r11 - - iretq - -.size user_task_trampoline, . - user_task_trampoline \ No newline at end of file diff --git a/src/syscall/syscall.c b/src/syscall/syscall.c deleted file mode 100644 index 438e98d..0000000 --- a/src/syscall/syscall.c +++ /dev/null @@ -1,444 +0,0 @@ -#include -#include "libk/stdio.h" -#include "mp/percpu.h" -#include "fs/vfs.h" -#include "syscall.h" -#include "sched/scheduler.h" -#include "mm/vmm.h" -#include "mm/pmm.h" -#include "mm/memory.h" -#include "libk/errno.h" -#include "mp/futex.h" -#include "drivers/rand/random.h" - -#define MSR_EFER 0xC0000080 -#define MSR_STAR 0xC0000081 -#define MSR_LSTAR 0xC0000082 -#define MSR_SFMASK 0xC0000084 -#define MSR_KERNEL_GSBASE 0xC0000102 -#define MSR_KERNEL_FSBASE 0xC0000100 - -#define EFER_SCE (1 << 0) - -static struct cpu_local g_cpu_local; - -static uint8_t g_syscall_kstack[16384] __attribute__((aligned(16))); - -static inline void wrmsr(uint32_t msr, uint64_t value) -{ - asm volatile("wrmsr" :: "c"(msr), "a"((uint32_t)value), "d"((uint32_t)(value >> 32))); -} - -static inline uint64_t rdmsr(uint32_t msr) -{ - uint32_t lo, hi; - asm volatile("rdmsr" : "=a"(lo), "=d"(hi) : "c"(msr)); - return ((uint64_t)hi << 32) | lo; -} - -uint64_t syscall_handler(uint64_t num, - uint64_t arg1, uint64_t arg2, uint64_t arg3, - uint64_t arg4, uint64_t arg5, uint64_t arg6) -{ - (void)arg4; (void)arg5; (void)arg6; - switch (num) - { - case SYS_READ: - { - int fd = (int)arg1; - uint8_t* buf = (uint8_t*)arg2; - size_t len = (size_t)arg3; - - return VFS_Read(fd, buf, len); - } - - case SYS_WRITE: - { - int fd = (int)arg1; - uint8_t* buf = (uint8_t*)arg2; - size_t len = (size_t)arg3; - - return (uint64_t)VFS_Write(fd, buf, len); - } - - case SYS_OPEN: - { - const char* path = (const char*)arg1; - int flags = (int)arg2; - unsigned int mode = (unsigned int)arg3; - return (uint64_t)VFS_Open(path, flags, mode); - } - case SYS_OPEN_DIR: - { - const char* path = (const char*)arg1; - int* out_handle = (int*)arg2; - - if (!path || !out_handle) - return -EINVAL; - - uint32_t ino = ext2_resolve_path(path); - if (ino == 0) - return -ENOENT; - - ext2_inode_t inode; - if (!ext2_read_inode(ino, &inode)) - return -EIO; - - if (!(inode.i_mode & EXT2_S_IFDIR)) - return -ENOTDIR; - - // allocate dir handle (safe offset so it never collides with real fds) - for (int i = 0; i < VFS_MAX_DIRS; i++) - { - if (!vfs_dir_table[i].used) - { - vfs_dir_table[i].used = true; - vfs_dir_table[i].inode = ino; - vfs_dir_table[i].offset = 0; - vfs_dir_table[i].type = 1; - - *out_handle = VFS_MAX_FDS + i; // e.g. 32 + i - return 0; - } - } - - return -EMFILE; - } - - case SYS_READ_ENTRIES: - { - int handle = (int)arg1; - void *buf = (void*)arg2; - size_t max = (size_t)arg3; - size_t *out = (size_t*)arg4; - - if (!buf || !out) - return -EINVAL; - - // Directory handles are at VFS_MAX_FDS + idx - int idx = handle - VFS_MAX_FDS; - if (idx < 0 || idx >= VFS_MAX_DIRS) { - return -EBADF; - } - - vfs_dir_t *d = &vfs_dir_table[idx]; - if (!d->used) { - return -EBADF; - } - - ext2_inode_t dir_inode; - if (!ext2_read_inode(d->inode, &dir_inode)) { - return -EIO; - } - - if (dir_inode.i_size == 0) { - *out = 0; - return 0; - } - - uint8_t *tmp = kmalloc(dir_inode.i_size); - if (!tmp) { - return -ENOMEM; - } - - // Read the raw directory blocks (same format as regular files) - if (!ext2_read_file(&dir_inode, tmp)) { - kfree(tmp); - return -EIO; - } - - size_t pos = 0; - size_t out_pos = 0; - size_t index = 0; - - while (pos < dir_inode.i_size && out_pos + sizeof(struct dirent) < max) - { - ext2_dir_entry_t *e = (ext2_dir_entry_t*)(tmp + pos); - - if (e->inode != 0) - { - if (index++ >= d->offset) - { - struct dirent *out_ent = (struct dirent*)((uint8_t*)buf + out_pos); - - memset(out_ent, 0, sizeof(struct dirent)); - - out_ent->d_ino = e->inode; - out_ent->d_off = index; - out_ent->d_reclen = sizeof(struct dirent); - out_ent->d_type = IFTODT(e->file_type); - - size_t n = e->name_len; - if (n >= sizeof(out_ent->d_name)) - n = sizeof(out_ent->d_name) - 1; - - memcpy(out_ent->d_name, e->name, n); - out_ent->d_name[n] = '\0'; - - out_pos += sizeof(struct dirent); - } - } - - pos += e->rec_len; - } - - d->offset = index; - - *out = out_pos; - - kfree(tmp); - return 0; - } - - case SYS_CLOSE: - { - int fd = (int)arg1; - - if (fd >= VFS_MAX_FDS && fd < VFS_MAX_FDS + VFS_MAX_DIRS) { - int idx = fd - VFS_MAX_FDS; - if (idx >= 0 && idx < VFS_MAX_DIRS && vfs_dir_table[idx].used) { - vfs_dir_table[idx].used = false; - return 0; - } - return -EBADF; - } - - return (uint64_t)VFS_Close(fd); - } - - case SYS_GETPID: - return (uint64_t)sched_current()->pid; - - case SYS_GETPPID: - return (uint64_t)sched_current()->ppid; - - case SYS_EXIT: - case SYS_EXIT_GROUP: - sched_exit((int)arg1); - //noreturn - - case SYS_MMAP: - { - uintptr_t addr = (uintptr_t)arg1; - size_t len = (size_t)arg2; - int prot = (int)arg3; - int flags = (int)arg4; - int fd = (int)arg5; - off_t offset = (off_t)arg6; - - (void)fd; (void)offset; // we only support anonymous for now - - if (len == 0) - return (uint64_t)MAP_FAILED; - - len = ALIGN_UP(len, PAGE_SIZE); - - if (!(flags & MAP_ANONYMOUS)) { - return (uint64_t)MAP_FAILED; // todo: file backed later - } - - struct pagemap *pm = sched_current()->pagemap; - if (!pm) pm = kernel_pagemap; - - if (!(flags & MAP_FIXED)) { - addr = find_free_vaddr(pm, len); - } - - uint64_t vmm_flags = PAGE_USER | PAGE_READ; - if (prot & PROT_WRITE) vmm_flags |= PAGE_WRITE; - if (prot & PROT_EXEC) vmm_flags |= PAGE_NO_EXECUTE; - - size_t page_count = len / PAGE_SIZE; - void *phys = pmm_allocz(page_count); - if (!phys) { - return (uint64_t)MAP_FAILED; - } - - // Map them - for (size_t i = 0; i < page_count; i++) { - uint64_t va = addr + i * PAGE_SIZE; - uint64_t pa = (uint64_t)phys + i * PAGE_SIZE; - if (!vmm_map_page(pm, va, pa, vmm_flags | PAGE_USER, Size4KiB)) { - pmm_free(phys, page_count); - return (uint64_t)MAP_FAILED; - } - } - - return addr; - } - - case SYS_MUNMAP: - { - uintptr_t addr = (uintptr_t)arg1; - size_t len = (size_t)arg2; - - if (len == 0 || addr == 0) - return 0; - - len = ALIGN_UP(len, PAGE_SIZE); - - struct pagemap *pm = sched_current()->pagemap ?: kernel_pagemap; - - for (size_t i = 0; i < len; i += PAGE_SIZE) { - vmm_unmap_page(pm, addr + i, false); - // TODO: also free the physical page (will need page refcounting or virt_to_phys + pmm_free) - } - - return 0; - } - - case SYS_SCHED_YIELD: - sched_yield(); - return 0; - - case SYS_NICE: - { - int increment = (int)arg1; - int old_nice = sched_current()->nice; - int new_nice = old_nice + increment; - return (uint64_t)task_set_nice(sched_current(), new_nice); - } - - case SYS_KILL: - { - pid_t target = (pid_t)arg1; - int sig = (int)arg2; - task_t *t = sched_find_task(target); - if (!t) return (uint64_t)-1; - return (uint64_t)task_send_signal(t, sig); - } - - case SYS_SIGACTION: - { - int signum = (int)arg1; - const struct sigaction *act = (const struct sigaction *)arg2; - struct sigaction *oact = (struct sigaction *)arg3; - - if (signum <= 0 || signum >= _NSIG) - return (uint64_t)-1; - if (signum == SIGKILL || signum == SIGSTOP) - return (uint64_t)-1; // cannot override - - task_t *cur = sched_current(); - if (oact) - *oact = cur->sigactions[signum]; - if (act) - cur->sigactions[signum] = *act; - return 0; - } - - case SYS_SIGPROCMASK: - { - // how: 0=SIG_BLOCK, 1=SIG_UNBLOCK, 2=SIG_SETMASK - int how = (int)arg1; - uint64_t new_set = arg2; - uint64_t *old = (uint64_t *)arg3; - - task_t *cur = sched_current(); - if (old) *old = cur->signal_mask; - - // SIGKILL and SIGSTOP can never be blocked - new_set &= ~((1ULL << SIGKILL) | (1ULL << SIGSTOP)); - - switch (how) { - case 0: cur->signal_mask |= new_set; break; // SIG_BLOCK - case 1: cur->signal_mask &= ~new_set; break; // SIG_UNBLOCK - case 2: cur->signal_mask = new_set; break; // SIG_SETMASK - default: return (uint64_t)-1; - } - return 0; - } - - case SYS_SCHED_GETSCHEDULER: - { - pid_t target = (pid_t)arg1; - task_t *t = target ? sched_find_task(target) - : sched_current(); - if (!t) return (uint64_t)-1; - return (uint64_t)t->policy; - } - - case SYS_SCHED_SETSCHEDULER: - { - pid_t target = (pid_t)arg1; - int policy = (int)arg2; - int rt_prio = (int)arg3; - task_t *t = target ? sched_find_task(target) - : sched_current(); - if (!t) return (uint64_t)-1; - return (uint64_t)task_set_scheduler(t, policy, rt_prio); - } - - case SYS_TCB_SET: - { - void *pointer = (void*)arg1; - if (pointer == NULL) { - return (uint64_t)-EINVAL; - } - - set_fs_base((uint64_t)pointer); - return 0; - } - - case SYS_FUTEX: - { - int *uaddr = (int*)arg1; - int op = (int)arg2; - int val = (int)arg3; - - switch (op) { - case FUTEX_WAIT: - return (uint64_t)futex_wait(uaddr, val); - case FUTEX_WAKE: - return (uint64_t)futex_wake(uaddr, val); - default: - return -EINVAL; - } - } - - case SYS_GETRANDOM: - { - void *buf = (void*)arg1; - size_t buflen = (size_t)arg2; - unsigned int flags = (unsigned int)arg3; - - return (int64_t)getrandom(buf, buflen, flags); - } - - - default: - { - printf("Unknown syscall: %lu\n", num); - return (uint64_t)-ENOSYS; - } - - } -} - -void syscall_init(void) -{ - extern void syscall_entry(void); - - // ── per-CPU local block ───────────────────────────────────────────── - g_cpu_local.self = (uint64_t)&g_cpu_local; - g_cpu_local.kernel_rsp = (uint64_t)(g_syscall_kstack + sizeof(g_syscall_kstack)); - - // After swapgs in syscall_entry, GS points here. - wrmsr(MSR_KERNEL_GSBASE, (uint64_t)&g_cpu_local); - - // ── STAR ──────────────────────────────────────────────────────────── - // Bits [47:32]: SYSCALL loads CS = 0x08 (kernel code), SS = 0x10 (kernel data) - // Bits [63:48]: SYSRET loads CS = (base+16)|3 = 0x23, SS = (base+8)|3 = 0x1B - // → base must be 0x10 - uint64_t star = ((uint64_t)0x08 << 32) | ((uint64_t)0x10 << 48); - wrmsr(MSR_STAR, star); - - wrmsr(MSR_LSTAR, (uint64_t)syscall_entry); - - uint64_t efer = rdmsr(MSR_EFER); - efer |= EFER_SCE; - wrmsr(MSR_EFER, efer); - - // Mask IF so interrupts are off during the syscall handler - wrmsr(MSR_SFMASK, (1 << 9)); -} diff --git a/src/syscall/syscall.h b/src/syscall/syscall.h deleted file mode 100644 index af0f8dc..0000000 --- a/src/syscall/syscall.h +++ /dev/null @@ -1,50 +0,0 @@ -#pragma once - - -#define SYS_READ 0 -#define SYS_WRITE 1 -#define SYS_OPEN 2 -#define SYS_CLOSE 3 -#define SYS_MMAP 9 -#define SYS_MUNMAP 11 -#define SYS_BRK 12 -#define SYS_SIGACTION 13 /* rt_sigaction on Linux */ -#define SYS_SIGPROCMASK 14 /* rt_sigprocmask on Linux */ -#define SYS_SCHED_YIELD 24 -#define SYS_GETPID 39 - -#define SYS_NICE 34 -#define SYS_FORK 57 -#define SYS_EXECVE 59 -#define SYS_EXIT 60 -#define SYS_KILL 62 - -#define SYS_GETPPID 110 -#define SYS_SCHED_GETSCHEDULER 138 -#define SYS_SCHED_SETSCHEDULER 139 -#define SYS_FUTEX 202 -#define SYS_EXIT_GROUP 231 -#define SYS_TCB_SET 300 -#define SYS_GETRANDOM 318 -#define SYS_OPEN_DIR 319 -#define SYS_READ_ENTRIES 320 - - - - - -// Memory protection flags (Linux compatible) -#define PROT_NONE 0x0 -#define PROT_READ 0x1 -#define PROT_WRITE 0x2 -#define PROT_EXEC 0x4 - -// mmap flags -#define MAP_PRIVATE 0x02 -#define MAP_SHARED 0x01 -#define MAP_ANONYMOUS 0x20 -#define MAP_FIXED 0x10 -#define MAP_FAILED ((void*)-1) - - -void syscall_init(void); \ No newline at end of file diff --git a/src/syscall/syscall_entry.S b/src/syscall/syscall_entry.S deleted file mode 100644 index 0763dda..0000000 --- a/src/syscall/syscall_entry.S +++ /dev/null @@ -1,41 +0,0 @@ -.global syscall_entry - -syscall_entry: - swapgs - - # Save user RSP, switch to kernel stack - mov %rsp, %gs:0x08 # cpu_local.user_rsp = user RSP - mov %gs:0x10, %rsp # RSP = cpu_local.kernel_rsp - - push %rcx # save user RIP (syscall puts it in RCX) - push %r11 # save user RFLAGS - - # ── Shuffle syscall regs → SysV args for syscall_handler ────────── - # On entry: rax=num rdi=a1 rsi=a2 rdx=a3 r10=a4 r8=a5 r9=a6 - # We want: rdi=num rsi=a1 rdx=a2 rcx=a3 r8=a4 r9=a5 - # - # Move r8/r10 FIRST before the push/pop clobbers them - mov %r8, %r9 # r9 = orig r8 (a5) - mov %r10, %r8 # r8 = orig r10 (a4) - - push %rdi # save orig rdi (a1) - push %rsi # save orig rsi (a2) - push %rdx # save orig rdx (a3) - - pop %rcx # rcx = a3 (4th param) - pop %rdx # rdx = a2 (3rd param) - pop %rsi # rsi = a1 (2nd param) - mov %rax, %rdi # rdi = num (1st param) - - sub $8, %rsp - call syscall_handler - add $8, %rsp - - # ── Restore user context ─────────────────────────────────────────── - pop %r11 # user RFLAGS - pop %rcx # user RIP - - mov %gs:0x08, %rsp # restore user RSP ← this was the missing piece - - swapgs - sysretq \ No newline at end of file diff --git a/test.c b/test.c new file mode 100644 index 0000000..4b953d5 --- /dev/null +++ b/test.c @@ -0,0 +1,6 @@ +#include + +void main() { + printf("test"); + return; +} diff --git a/test.o b/test.o new file mode 100644 index 0000000..a084a0e Binary files /dev/null and b/test.o differ diff --git a/user/include/mlibc/helloworld.c b/user/include/mlibc/helloworld.c index 711832c..492e2cf 100644 --- a/user/include/mlibc/helloworld.c +++ b/user/include/mlibc/helloworld.c @@ -1,151 +1,34 @@ #include -#include -#include -#include -#include -#include -#include +#include +#include -#define STDIN 0 -#define STDOUT 1 +int main() { + printf("\nforking!"); + pid_t pid = fork(); -static void print(const char* s) -{ - write(STDOUT, s, strlen(s)); -} - -static void touch(const char* path) -{ - int fd = open(path, O_CREAT | O_WRONLY, 0644); - if (fd < 0) { - print("touch: cannot create file\n"); - return; + if (pid < 0) { + perror("fork failed"); + return 1; } - close(fd); -} + if (pid == 0) { + // Child process + printf("child processed!"); + char *argv[] = {"/bin/pwd", NULL}; + char *envp[] = {NULL}; -static void ls(const char* path) -{ - struct dirent* entry; + execve("/bin/pwd", argv, envp); - DIR* dir = opendir(path ? path : "."); - if (!dir) { - print("ls: cannot open directory\n"); - return; + // If execve returns, it failed + perror("execve failed"); + return 1; + } else { + // Parent process + waitpid(pid, NULL, 0); + printf("Child process finished\n"); } - while ((entry = readdir(dir)) != NULL) - { - // skip . and .. - if (entry->d_name[0] == '.' && - (entry->d_name[1] == '\0' || - (entry->d_name[1] == '.' && entry->d_name[2] == '\0'))) - continue; - - print(entry->d_name); - print("\n"); - } - - closedir(dir); -} - -static void readline(char* buf, size_t max) -{ - size_t i = 0; - - while (i < max - 1) - { - char c; - ssize_t n = read(STDIN, &c, 1); - - if (n <= 0) - continue; - - if (c == '\n') - break; - - if (c == '\b') { - if (i > 0) i--; - continue; - } - - buf[i++] = c; - } - - buf[i] = '\0'; -} - -static void cat(const char* path) -{ - char buf[256]; - - int fd = open(path, O_RDONLY); - if (fd < 0) { - print("cat: cannot open file\n"); - return; - } - - ssize_t n = read(fd, buf, sizeof(buf)); - close(fd); - - if (n > 0) - write(STDOUT, buf, (size_t)n); -} - -int main(void) -{ - const char* path = "/qwerty.txt"; - const char* msg = "Suki Suki Daisuki Kekkon Shiyo, my honey!"; - - char buf[128]; - - int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644); - write(fd, msg, strlen(msg)); - close(fd); - - fd = open(path, O_RDONLY); - ssize_t n = read(fd, buf, sizeof(buf)); - close(fd); - - if (n > 0) - write(STDOUT, buf, (size_t)n); - - char line[128]; - - while (1) - { - print("> "); - readline(line, sizeof(line)); - - if (strcmp(line, "exit") == 0) - break; - - if (line[0] == 'c' && line[1] == 'a' && line[2] == 't' && line[3] == ' ') - { - cat(&line[4]); - continue; - } - - if (line[0] == 't' && line[1] == 'o' && line[2] == 'u' && line[3] == 'c' && line[4] == 'h' && line[5] == ' ') - { - touch(&line[6]); - continue; - } - - if (line[0] == 'l' && line[1] == 's') - { - if (line[2] == ' ') - ls(&line[3]); - else - ls(NULL); - - continue; - } - - if (line[0] != '\0') - print("Unknown command\n"); - } + printf("comp"); return 0; } \ No newline at end of file diff --git a/user/include/mlibc/options/posix/generic/unistd.cpp b/user/include/mlibc/options/posix/generic/unistd.cpp index d6cd0c3..7cbbe36 100644 --- a/user/include/mlibc/options/posix/generic/unistd.cpp +++ b/user/include/mlibc/options/posix/generic/unistd.cpp @@ -65,7 +65,7 @@ int chown(const char *path, uid_t uid, gid_t gid) { return 0; } -ssize_t confstr(int name, char *buf, size_t len) { +size_t confstr(int name, char *buf, size_t len) { const char *str = ""; if (name == _CS_PATH) { str = "/bin:/usr/bin"; diff --git a/user/include/mlibc/options/posix/include/unistd.h b/user/include/mlibc/options/posix/include/unistd.h index 8fcaeab..f90b20d 100644 --- a/user/include/mlibc/options/posix/include/unistd.h +++ b/user/include/mlibc/options/posix/include/unistd.h @@ -264,7 +264,7 @@ unsigned int alarm(unsigned int __seconds); int chdir(const char *__path); int chown(const char *__path, uid_t __uid, gid_t __gid); int close(int __fd); -ssize_t confstr(int __name, char *__buf, size_t __size); +size_t confstr(int __name, char *__buf, size_t __size); char *ctermid(char *__s); int dup(int __fd); int dup2(int __src_fd, int __dest_fd); diff --git a/user/include/mlibc/rebuild.sh b/user/include/mlibc/rebuild.sh new file mode 100755 index 0000000..e7d4898 --- /dev/null +++ b/user/include/mlibc/rebuild.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash +set -euo pipefail + +SYSROOT_DIR="/home/kaguya/KirkOS/ext2_root/" + +rm -rf headers-build build +rm -f "$SYSROOT_DIR/usr/lib/libc.a" "$SYSROOT_DIR/usr/lib/libm.a" "$SYSROOT_DIR/usr/lib/libdl.a" + +# 1. Headers +meson setup \ + --cross-file=x86_64-kirkos-gcc.txt \ + --prefix=/usr \ + -Dheaders_only=true \ + -Dlinux_kernel_headers=/home/kaguya/KirkOS/linux-headers/include \ + headers-build --reconfigure + +DESTDIR="$SYSROOT_DIR" ninja -C headers-build install + +# 2. Static libraries +meson setup \ + --cross-file=/home/kaguya/KirkOS/user/include/mlibc/x86_64-kirkos-gcc.txt \ + --prefix=/usr \ + -Ddefault_library=static \ + -Dno_headers=true \ + -Dlinux_kernel_headers=/home/kaguya/KirkOS/linux-headers/include \ + build --reconfigure + +DESTDIR="$SYSROOT_DIR" ninja -C build install \ No newline at end of file diff --git a/user/include/mlibc/sysdeps/kirkos/include/abi-bits/limits.h b/user/include/mlibc/sysdeps/kirkos/include/abi-bits/limits.h index 472b3ec..aa753b1 100755 --- a/user/include/mlibc/sysdeps/kirkos/include/abi-bits/limits.h +++ b/user/include/mlibc/sysdeps/kirkos/include/abi-bits/limits.h @@ -6,5 +6,6 @@ #define HOST_NAME_MAX 64 #define NAME_MAX 255 #define OPEN_MAX 256 +#define _POSIX_PATH_MAX 256 #endif /*_ABIBITS_LIMITS_H */ \ No newline at end of file diff --git a/user/include/mlibc/sysdeps/kirkos/include/mlibc/sysdeps.hpp b/user/include/mlibc/sysdeps/kirkos/include/mlibc/sysdeps.hpp index fe6e50e..6a3f7a2 100644 --- a/user/include/mlibc/sysdeps/kirkos/include/mlibc/sysdeps.hpp +++ b/user/include/mlibc/sysdeps/kirkos/include/mlibc/sysdeps.hpp @@ -21,7 +21,9 @@ struct KirkSysdepTags public Open, public VmMap, public VmUnmap, - public ClockGet + public ClockGet, + public Sigaction, + public GetPid { }; template diff --git a/user/include/mlibc/sysdeps/kirkos/include/syscall.h b/user/include/mlibc/sysdeps/kirkos/include/syscall.h index 3ad0b87..bc44ca6 100644 --- a/user/include/mlibc/sysdeps/kirkos/include/syscall.h +++ b/user/include/mlibc/sysdeps/kirkos/include/syscall.h @@ -1,29 +1,50 @@ #pragma once -#define SYS_READ 0 -#define SYS_WRITE 1 -#define SYS_OPEN 2 -#define SYS_CLOSE 3 -#define SYS_MMAP 9 -#define SYS_MUNMAP 11 -#define SYS_BRK 12 -#define SYS_SIGACTION 13 /* rt_sigaction on Linux */ -#define SYS_SIGPROCMASK 14 /* rt_sigprocmask on Linux */ -#define SYS_SCHED_YIELD 24 -#define SYS_GETPID 39 -#define SYS_NICE 34 -#define SYS_FORK 57 -#define SYS_EXECVE 59 -#define SYS_EXIT 60 -#define SYS_KILL 62 - -#define SYS_GETPPID 110 -#define SYS_SCHED_GETSCHEDULER 138 -#define SYS_SCHED_SETSCHEDULER 139 -#define SYS_FUTEX 202 -#define SYS_EXIT_GROUP 231 -#define SYS_TCB_SET 300 -#define SYS_GETRANDOM 318 -#define SYS_OPEN_DIR 319 -#define SYS_READ_ENTRIES 320 +#define SYS_read 0 +#define SYS_write 1 +#define SYS_open 2 +#define SYS_close 3 +#define SYS_seek 8 +#define SYS_mmap 9 +#define SYS_mprotect 10 +#define SYS_munmap 11 +#define SYS_ioctl 16 +#define SYS_nanosleep 35 +#define SYS_getpid 39 +#define SYS_fork 57 +#define SYS_execve 59 +#define SYS_exit 60 +#define SYS_kill 62 +#define SYS_fcntl 72 +#define SYS_getcwd 79 +#define SYS_chdir 80 +#define SYS_rmdir 84 +#define SYS_readdir 89 +#define SYS_puts 103 +#define SYS_getppid 110 +#define SYS_setsid 112 +#define SYS_waitpid 114 +#define SYS_prctl 157 +#define SYS_futex 202 +#define SYS_openat 257 +#define SYS_mkdirat 258 +#define SYS_fstatat 262 +#define SYS_unlinkat 263 +#define SYS_linkat 265 +#define SYS_readlinkat 267 +#define SYS_fchmodat 268 +#define SYS_dup3 292 +#define SYS_pipe 293 +#define SYS_getclock 314 +#define SYS_ppoll 271 +#define SYS_socket 41 +#define SYS_connect 42 +#define SYS_accept 43 +#define SYS_recvmsg 47 +#define SYS_bind 49 +#define SYS_listen 50 +#define SYS_getpeername 52 +#define SYS_socketpair 83 +#define SYS_threadnew 56 +#define SYS_threadexit 61 diff --git a/user/include/mlibc/sysdeps/kirkos/meson.build b/user/include/mlibc/sysdeps/kirkos/meson.build index e4095c3..a05f9cf 100644 --- a/user/include/mlibc/sysdeps/kirkos/meson.build +++ b/user/include/mlibc/sysdeps/kirkos/meson.build @@ -86,7 +86,7 @@ if not headers_only endif -rtld_sources += files( +rtld_dso_sources += files( 'sysdeps.cpp', 'syscall.cpp', 'dso.c', diff --git a/user/include/mlibc/sysdeps/kirkos/sysdeps.cpp b/user/include/mlibc/sysdeps/kirkos/sysdeps.cpp index d2a41f5..ddc6cb3 100644 --- a/user/include/mlibc/sysdeps/kirkos/sysdeps.cpp +++ b/user/include/mlibc/sysdeps/kirkos/sysdeps.cpp @@ -1,17 +1,19 @@ -#include "mlibc/tcb.hpp" -#include +#include +#include #include -#include +#include +#include +#include +#include +#include +#include #include -#include - -#include +#include +#include +#include +#include #include "syscall.h" -#define SYS_EXIT 60 -#define SYS_READ 0 -#define SYS_WRITE 1 - #define STUB() \ ({ \ __ensure(!"STUB function was called"); \ @@ -20,140 +22,638 @@ namespace mlibc { -void sys_libc_log(const char *msg) { - ssize_t dummy; - sys_write(1, msg, strlen(msg), &dummy); + +int fcntl_helper(int fd, int request, int *result, ...) { + va_list args; + va_start(args, result); + if (!mlibc::sys_fcntl) { + return ENOSYS; + } + int ret = mlibc::sys_fcntl(fd, request, args, result); + va_end(args); + return ret; } -void sys_libc_panic() { - sys_libc_log("!!! mlibc panic !!!\n"); - sys_exit(1); - __builtin_trap(); -} - -int sys_isatty(int fd) { - return 0; // everything is a tty for now -} - -int sys_write(int fd, const void *buf, size_t size, ssize_t *ret) { - long r = syscall(SYS_WRITE, fd, (uintptr_t)buf, size); - if (ret) *ret = r; - return r >= 0 ? 0 : -r; -} - -int sys_read(int fd, void *buf, size_t size, ssize_t *ret) { - long r = syscall(SYS_READ, fd, (uintptr_t)buf, size); - if (ret) *ret = r; - return r >= 0 ? 0 : -r; +void sys_libc_log(const char *message) { + syscall(SYS_puts, message); + syscall(SYS_puts, "\n"); } void sys_exit(int status) { - syscall(SYS_EXIT, status); - __builtin_unreachable(); + syscall(SYS_exit, status); + __builtin_unreachable(); } -int sys_close(int fd) { - long r = syscall(SYS_CLOSE, fd); - return r >= 0 ? 0 : -r; -} - -int sys_getrandom(void *buffer, size_t length, int flags) { - long r = syscall(SYS_GETRANDOM, buffer, length, flags); - return r; -} - -// ───────────────────────────────────────────────────────────────────────────── -// Memory -// ───────────────────────────────────────────────────────────────────────────── - -int sys_anon_allocate(size_t size, void **pointer) { - long r = syscall(SYS_MMAP, 0, size, - PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - - *pointer = (void*)r; - return (r == (long)MAP_FAILED) ? ENOMEM : 0; -} - -int sys_anon_free(void *ptr, size_t size) { - if (!ptr || size == 0) return 0; - long r = syscall(SYS_MUNMAP, (uintptr_t)ptr, size); - return r == 0 ? 0 : ENOMEM; -} - -int sys_vm_map(void *hint, size_t size, int prot, int flags, - int fd, off_t offset, void **out) { - long r = syscall(SYS_MMAP, - (uintptr_t)hint, - size, - prot, - flags, - fd, - offset); - - if (r < 0) - return -r; - - *out = (void *)r; - return 0; -} - -int sys_vm_unmap(void *addr, size_t size) { - long r = syscall(SYS_MUNMAP, (uintptr_t)addr, size); - return (r == 0) ? 0 : -r; -} - -// ───────────────────────────────────────────────────────────────────────────── -// Others -// ───────────────────────────────────────────────────────────────────────────── - -int sys_seek(int, off_t, int, off_t *) { - return ESPIPE; // no real files yet +void sys_libc_panic() { + sys_libc_log("\nmlibc: panic!\n"); + sys_exit(1); + __builtin_unreachable(); } int sys_tcb_set(void *pointer) { - sys_libc_log("[sysdeps] sys_tcb_set called"); - - long ret = syscall(SYS_TCB_SET, (uintptr_t)pointer); - if (ret < 0) - return -ret; - - sys_libc_log("[sysdeps] TCB set via syscall.\n"); - return 0; + // ARCH_SET_FS + syscall(SYS_prctl, 0x1002, pointer); + return 0; } -int sys_open(const char *path, int flags, unsigned int mode, int *fd) { - long r = syscall(SYS_OPEN, (uintptr_t)path, (long)flags, (long)mode); - if (r < 0) return -r; - *fd = (int)r; - return 0; +int sys_futex_wait(int *pointer, int expected, const struct timespec *time) { + (void)time; + return -(syscall(SYS_futex, pointer, 0, expected)); } -// Futexes -int sys_futex_wait(int *ptr, int expected, const timespec *time) { - long r = syscall(SYS_FUTEX, ptr, 0, expected, time); - return (r < 0) ? -r : 0; +int sys_futex_wake(int *pointer) { return -(syscall(SYS_futex, pointer, 1, 0)); } + +int sys_ioctl(int fd, unsigned long request, void *arg, int *result) { + int ret = syscall(SYS_ioctl, fd, request, arg); + if (ret < 0) { + return -ret; + } + *result = ret; + return 0; } -int sys_futex_wake(int *ptr) { - long r = syscall(SYS_FUTEX, ptr, 1, 1, 0); - return (r < 0) ? -r : 0; +int sys_isatty(int fd) { + struct winsize ws; + int ret; + + if (!sys_ioctl(fd, TIOCGWINSZ, &ws, &ret)) + return 0; + + return ENOTTY; +} + +int sys_getcwd(char *buffer, size_t size) { + syscall(SYS_getcwd, buffer, size); + return 0; +} + +int sys_openat(int dirfd, const char *path, int flags, mode_t mode, int *fd) { + int ret = syscall(SYS_openat, dirfd, path, flags, mode); + if (ret < 0) { + *fd = -1; + return -ret; + } + *fd = (int)ret; + return 0; +} + +int sys_open(const char *path, int flags, mode_t mode, int *fd) { + return sys_openat(AT_FDCWD, path, flags, mode, fd); +} + +#ifndef MLIBC_BUILDING_RTLD + +void sys_thread_exit() { + syscall(SYS_threadexit); + __builtin_unreachable(); +} + +extern "C" void __mlibc_thread_entry(); + +int sys_clone(void *tcb, pid_t *pid_out, void *stack) { + (void)tcb; + + int ret = syscall(SYS_threadnew, (uintptr_t)__mlibc_thread_entry, (uintptr_t)stack); + if (ret < 0) { + return -ret; + } + + *pid_out = ret; + return 0; +} + +extern "C" void __mlibc_thread_entry() { + /* Minimal stub so linking succeeds. + If you need real pthread support later, replace this with the + proper mlibc thread startup logic (pop start_routine + arg from stack, + call it, then sys_thread_exit). */ + mlibc::sys_libc_panic(); + __builtin_unreachable(); +} + +int sys_tcgetattr(int fd, struct termios *attr) { + int ret; + if (int r = sys_ioctl(fd, TCGETS, attr, &ret) != 0) { + return r; + } + return ret; +} + +int sys_tcsetattr(int fd, int optional_action, const struct termios *attr) { + int ret; + switch (optional_action) { + case TCSANOW: + optional_action = TCSETS; + break; + case TCSADRAIN: + optional_action = TCSETSW; + break; + case TCSAFLUSH: + optional_action = TCSETSF; + break; + default: + __ensure(!"Unsupported tcsetattr"); + } + + if (int r = sys_ioctl(fd, optional_action, (void *)attr, &ret) != 0) { + return r; + } + + return ret; } int sys_open_dir(const char *path, int *handle) { - long r = syscall(SYS_OPEN_DIR, (uintptr_t)path, (uintptr_t)handle); - return (r < 0) ? -r : 0; + return sys_openat(AT_FDCWD, path, O_DIRECTORY, 0, handle); } -int sys_read_entries(int handle, void *buf, size_t bufsize, size_t *out) { - long r = syscall(SYS_READ_ENTRIES, handle, (uintptr_t)buf, bufsize, (uintptr_t)out); - if (r < 0) return -r; +struct ReadDirState { + size_t offset; + size_t capacity; + void *buffer; +}; - // Kernel already wrote the byte count into *out via the pointer. - // Do NOT overwrite it with r (which is always 0 on success). - return 0; +static frg::hash_map, MemoryAllocator> open_dirs{ + frg::hash{}, getAllocator() +}; + +static ReadDirState *get_dir_state(int fdnum) { + ReadDirState *result; + if (auto value = open_dirs.get(fdnum)) { + result = *value; + } else { + result = (ReadDirState *)malloc(sizeof(ReadDirState)); + result->offset = 0; + result->capacity = 1024; + result->buffer = malloc(result->capacity); + open_dirs.insert(fdnum, result); + } + return result; } -int sys_clock_get(int, time_t *, long *) { return ENOSYS; } +int sys_read_entries(int fdnum, void *buffer, size_t max_size, size_t *bytes_read) { + ReadDirState *state = get_dir_state(fdnum); + +retry: + uint64_t ret = syscall(SYS_readdir, fdnum, state->buffer, &state->capacity); + int ret_but_int = (int)ret; + if (ret_but_int < 0) { + if (-ret_but_int == ENOBUFS) { + state->buffer = realloc(state->buffer, state->capacity); + goto retry; + } else { + return -ret_but_int; + } + } + + size_t offset = 0; + while (offset < max_size) { + struct dirent *ent = (struct dirent *)((char *)state->buffer + state->offset); + if (ent->d_reclen == 0) { + break; + } + + if (offset + ent->d_reclen >= max_size) { + break; + } + + memcpy((char *)buffer + offset, ent, ent->d_reclen); + offset += ent->d_reclen; + state->offset += ent->d_reclen; + } + + *bytes_read = offset; + return ret; +} + +#endif + +int sys_close(int fd) { + syscall(SYS_close, fd); +#ifndef MLIBC_BUILDING_RTLD + open_dirs.remove(fd); +#endif + return 0; +} + +int sys_seek(int fd, off_t offset, int whence, off_t *new_offset) { + uint64_t ret = syscall(SYS_seek, fd, offset, whence); + + int ret_but_int = (int)ret; + if (ret_but_int < 0) { + *new_offset = -1; + return -ret_but_int; + } + + *new_offset = (off_t)ret; + return 0; +} + +int sys_read(int fd, void *buf, size_t count, ssize_t *bytes_read) { + *bytes_read = (ssize_t)syscall(SYS_read, fd, buf, count); + int ret = *bytes_read; + if (ret < 0) { + *bytes_read = -1; + return -ret; + } + return 0; +} + +int sys_write(int fd, const void *buf, size_t count, ssize_t *bytes_written) { + *bytes_written = (ssize_t)syscall(SYS_write, fd, buf, count); + int ret = *bytes_written; + if (ret < 0) { + *bytes_written = -1; + return -ret; + } + return 0; +} + +int sys_readlink(const char *path, void *data, size_t max_size, ssize_t *length) { + *length = (ssize_t)syscall(SYS_readlinkat, AT_FDCWD, path, data, max_size); + int ret = *length; + if (ret < 0) { + *length = -1; + return -ret; + } + return 0; +} + +int sys_linkat(int olddirfd, const char *old_path, int newdirfd, const char *new_path, int flags) { + int ret = syscall(SYS_linkat, olddirfd, old_path, newdirfd, new_path, flags); + ret = ret < 0 ? -ret : ret; + return ret; +} + +int sys_link(const char *old_path, const char *new_path) { + return sys_linkat(AT_FDCWD, old_path, AT_FDCWD, new_path, 0); +} + +int sys_unlinkat(int fd, const char *path, int flags) { + int ret = syscall(SYS_unlinkat, fd, path, flags); + ret = ret < 0 ? -ret : ret; + return ret; +} + +int sys_fchmodat(int fd, const char *pathname, mode_t mode, int flags) { + int ret = syscall(SYS_fchmodat, fd, pathname, mode, flags); + ret = ret < 0 ? -ret : ret; + return ret; +} + +int sys_fchmod(int fd, mode_t mode) { return sys_fchmodat(fd, "", mode, AT_EMPTY_PATH); } + +int sys_chmod(const char *pathname, mode_t mode) { + int ret = sys_fchmodat(AT_FDCWD, pathname, mode, 0); + ret = ret < 0 ? -ret : ret; + return ret; +} + +int sys_vm_map(void *hint, size_t size, int prot, int flags, int fd, off_t offset, void **window) { + uint64_t ret = syscall(SYS_mmap, hint, size, prot, flags, fd, offset); + int64_t ret_but_int = (int64_t)ret; + if ((ret_but_int < 0) && (ret_but_int > -4096)) { + *window = ((void *)-1); + return -ret_but_int; + } + *window = (void *)ret; + return 0; +} + +int sys_vm_protect(void *pointer, size_t size, int prot) { + return -(syscall(SYS_mprotect, pointer, size, prot)); +} + +int sys_vm_unmap(void *pointer, size_t size) { return -(syscall(SYS_munmap, pointer, size)); } + +int sys_anon_allocate(size_t size, void **pointer) { + return sys_vm_map(NULL, size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS, -1, 0, pointer); +} + +int sys_anon_free(void *pointer, size_t size) { return sys_vm_unmap(pointer, size); } + +#ifndef MLIBC_BUILDING_RTLD + +pid_t sys_getpid() { return (pid_t)syscall(SYS_getpid); } + +pid_t sys_getppid() { return (pid_t)syscall(SYS_getppid); } + +uid_t sys_getuid() { return 0; } + +uid_t sys_geteuid() { return 0; } + +gid_t sys_getgid() { return 0; } + +int sys_setgid(gid_t) { return 0; } + +int sys_getpgid(pid_t, pid_t *) { return 0; } + +gid_t sys_getegid() { return 0; } + +int sys_setpgid(pid_t, pid_t) { return 0; } + +int sys_kill(pid_t p, int sig) { + (void)sig; + syscall(SYS_kill, p); + return 0; +} + +int sys_clock_get(int clock, time_t *secs, long *nanos) { + struct timespec buf; + syscall(SYS_getclock, clock, &buf); + *secs = buf.tv_sec; + *nanos = buf.tv_nsec; + return 0; +} + +int sys_sleep(time_t *secs, long *nanos) { + struct timespec req = {.tv_sec = *secs, .tv_nsec = *nanos}; + struct timespec rem = {0, 0}; + + int ret = syscall(0x23, &req, &rem); + + if (ret < 0) { + return -ret; + } + + *secs = rem.tv_sec; + *nanos = rem.tv_nsec; + return 0; +} + +int sys_stat(fsfd_target fsfdt, int fd, const char *path, int flags, struct stat *statbuf) { + int ret = 0; + switch (fsfdt) { + case fsfd_target::fd: + ret = syscall(SYS_fstatat, fd, "", statbuf, flags | AT_EMPTY_PATH); + break; + case fsfd_target::path: + ret = syscall(SYS_fstatat, AT_FDCWD, path, statbuf, flags); + break; + case fsfd_target::fd_path: + ret = syscall(SYS_fstatat, fd, path, statbuf, flags); + break; + default: + __ensure(!"sys_stat: Invalid fsfdt"); + __builtin_unreachable(); + } + return -ret; +} + +int sys_faccessat(int dirfd, const char *pathname, int mode, int flags) { + (void)flags; + struct stat buf; + if (int r = sys_stat(fsfd_target::fd_path, dirfd, pathname, mode & AT_SYMLINK_FOLLOW, &buf)) { + return r; + } + return 0; +} + +int sys_access(const char *path, int mode) { return sys_faccessat(AT_FDCWD, path, mode, 0); } + +int sys_pipe(int *fds, int flags) { + int ret = syscall(SYS_pipe, fds, flags); + ret = ret < 0 ? -ret : ret; + return ret; +} + +int sys_chdir(const char *path) { + int ret = syscall(SYS_chdir, path); + ret = ret < 0 ? -ret : ret; + return ret; +} + +int sys_mkdirat(int dirfd, const char *path, mode_t mode) { + int ret = syscall(SYS_mkdirat, dirfd, path, mode); + ret = ret < 0 ? -ret : ret; + return ret; +} + +int sys_mkdir(const char *path, mode_t mode) { return sys_mkdirat(AT_FDCWD, path, mode); } + +int sys_inotify_create(int, int *) { + mlibc::infoLogger() << "mlibc: sys_inotify_create() is unimplemented" << frg::endlog; + return ENOSYS; +} + +int sys_fork(pid_t *child) { + *child = (pid_t)syscall(SYS_fork); + return 0; +} + +int sys_execve(const char *path, char *const argv[], char *const envp[]) { + int ret = syscall(SYS_execve, path, argv, envp); + ret = ret < 0 ? -ret : 0; + return ret; +} + +int sys_fcntl(int fd, int request, va_list args, int *result) { + *result = (int)syscall(SYS_fcntl, fd, request, va_arg(args, uint64_t)); + int ret = *result; + if (ret < 0) { + *result = ret; + return -ret; + } + return 0; +} + +int sys_dup(int fd, int flags, int *newfd) { + (void)flags; + *newfd = (int)syscall(SYS_fcntl, fd, F_DUPFD, 0); + int ret = *newfd; + if (ret < 0) { + *newfd = -1; + return -ret; + } + return 0; +} + +int sys_dup2(int fd, int flags, int newfd) { + int ret = syscall(SYS_dup3, fd, newfd, flags); + ret = ret < 0 ? -ret : ret; + return ret; +} + +int sys_rmdir(const char *path) { + int ret = syscall(SYS_rmdir, path); + ret = ret < 0 ? -ret : ret; + return ret; +} + +int sys_setsid(pid_t *sid) { + (void)sid; + int ret = syscall(SYS_setsid); + ret = ret < 0 ? -ret : ret; + return ret; +} + +int sys_sigprocmask(int, const sigset_t *__restrict, sigset_t *__restrict) { + mlibc::infoLogger() << "mlibc: sys_sigprocmask() is a stub\n" << frg::endlog; + return 0; +} + +int sys_sigaction(int, const struct sigaction *, struct sigaction *) { + mlibc::infoLogger() << "mlibc: sys_sigaction() is a stub\n" << frg::endlog; + return 0; +} + +int sys_waitpid(pid_t pid, int *status, int flags, struct rusage *ru, pid_t *ret_pid) { + if (ru) { + mlibc::infoLogger() << "mlibc: struct rusage in sys_waitpid is unsupported\n" + << frg::endlog; + return ENOSYS; + } + + int ret = syscall(SYS_waitpid, pid, status, flags); + if (ret < 0) { + *ret_pid = -1; + return -ret; + } + + *ret_pid = ret; + return 0; +} + +int sys_getgroups(size_t, const gid_t *, int *) { + mlibc::infoLogger() << "mlibc: sys_getgroups() is unimplemented" << frg::endlog; + return ENOSYS; +} + +int sys_gethostname(char *buffer, size_t bufsize) { + struct utsname utsname; + if (int err = sys_uname(&utsname)) { + return err; + } + if (strlen(utsname.nodename) >= bufsize) { + return ENAMETOOLONG; + } + strncpy(buffer, utsname.nodename, bufsize); + return 0; +} + +int sys_getitimer(int, struct itimerval *) { + mlibc::infoLogger() << "mlibc: sys_getitimer() is unimplemented" << frg::endlog; + return ENOSYS; +} + +int sys_setitimer(int, const struct itimerval *, struct itimerval *) { + mlibc::infoLogger() << "mlibc: sys_setitimer() is unimplemented" << frg::endlog; + return ENOSYS; +} + +int sys_umask(mode_t mode, mode_t *old) { + int ret = syscall(0x5f, mode); + if (ret < 0) { + return -ret; + } + *old = ret; + return 0; +} + +int sys_uname(struct utsname *buf) { return syscall(0x3f, buf); } + +int sys_fsync(int) { + mlibc::infoLogger() << "sys_fsync is a stub" << frg::endlog; + return 0; +} + +int sys_ppoll( + struct pollfd *fds, + int nfds, + const struct timespec *timeout, + const sigset_t *sigmask, + int *num_events +) { + int ret = syscall(SYS_ppoll, fds, nfds, timeout, sigmask); + + if (ret < 0) + return -ret; + + *num_events = ret; + return 0; +} + +int sys_poll(struct pollfd *fds, nfds_t count, int timeout, int *num_events) { + struct timespec ts; + ts.tv_sec = timeout / 1000; + ts.tv_nsec = (timeout % 1000) * 1000000; + return sys_ppoll(fds, count, timeout < 0 ? NULL : &ts, NULL, num_events); +} + +int sys_pselect( + int nfds, + fd_set *read_set, + fd_set *write_set, + fd_set *except_set, + const struct timespec *timeout, + const sigset_t *sigmask, + int *num_events +) { + struct pollfd *fds = (struct pollfd *)calloc(nfds, sizeof(struct pollfd)); + if (fds == NULL) { + return ENOMEM; + } + + for (int i = 0; i < nfds; i++) { + struct pollfd *fd = &fds[i]; + + if (read_set && FD_ISSET(i, read_set)) { + fd->events |= POLLIN; + } + if (write_set && FD_ISSET(i, write_set)) { + fd->events |= POLLOUT; + } + if (except_set && FD_ISSET(i, except_set)) { + fd->events |= POLLPRI; + } + + if (!fd->events) { + fd->fd = -1; + continue; + } + fd->fd = i; + } + + int ret = sys_ppoll(fds, nfds, timeout, sigmask, num_events); + if (ret < 0) { + free(fds); + return -ret; + } + + fd_set res_read_set, res_write_set, res_except_set; + FD_ZERO(&res_read_set); + FD_ZERO(&res_write_set); + FD_ZERO(&res_except_set); + + for (int i = 0; i < nfds; i++) { + struct pollfd *fd = &fds[i]; + + if (read_set && FD_ISSET(i, read_set) + && (fd->revents & (POLLIN | POLLERR | POLLHUP)) != 0) { + FD_SET(i, &res_read_set); + } + if (write_set && FD_ISSET(i, write_set) + && (fd->revents & (POLLOUT | POLLERR | POLLHUP)) != 0) { + FD_SET(i, &res_write_set); + } + if (except_set && FD_ISSET(i, except_set) && (fd->revents & POLLPRI) != 0) { + FD_SET(i, &res_except_set); + } + } + + free(fds); + if (read_set) { + *read_set = res_read_set; + } + if (write_set) { + *write_set = res_write_set; + } + if (except_set) { + *except_set = res_except_set; + } + + return 0; +} + +#endif } // namespace mlibc diff --git a/user/include/mlibc/sysdeps/linux/include/bits/syscall.h b/user/include/mlibc/sysdeps/linux/include/bits/syscall.h index 680bb8c..2d3de68 100644 --- a/user/include/mlibc/sysdeps/linux/include/bits/syscall.h +++ b/user/include/mlibc/sysdeps/linux/include/bits/syscall.h @@ -19,7 +19,7 @@ __sc_word_t __do_syscall6(long, __sc_word_t, __sc_word_t, __sc_word_t, __sc_word __sc_word_t, __sc_word_t); __sc_word_t __do_syscall7(long, __sc_word_t, __sc_word_t, __sc_word_t, __sc_word_t, __sc_word_t, __sc_word_t, __sc_word_t); -long __do_syscall_ret(unsigned long); +long __do_syscall_ret(unsigned long ret); #ifdef __cplusplus extern "C++" {