diff --git a/.gitignore b/.gitignore index e42c5ee4..a612f011 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ *.o efi-loader.bin loader.bin +FLDR.elf UEFI/gnu-efi UEFI/include UEFI/BOOTX64.EFI diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index a89a7998..b4802d55 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -199,6 +199,145 @@ "-fstack-check", "-fsanitize=undefined", + // VSCode flags + "-ffreestanding", + "-nostdinc", + "-nostdinc++" + ] + }, + { + "name": "FennixLoader x64 (Linux, GCC, debug)", + "includePath": [ + "${workspaceFolder}/FennixLoader/include" + ], + "defines": [ + "__debug_vscode__", + "KERNEL_NAME=\"Fennix\"", + "KERNEL_VERSION=\"1.0\"", + "GIT_COMMIT=\"0000000000000000000000000000000000000000\"", + "GIT_COMMIT_SHORT=\"0000000\"", + "DEBUG=\"1\"" + ], + "compilerPath": "${workspaceFolder}/../tools/cross/bin/amd64-elf-gcc", + "cStandard": "c17", + "cppStandard": "c++20", + "intelliSenseMode": "gcc-x64", + "configurationProvider": "ms-vscode.makefile-tools", + "compilerArgs": [ + // Compiler flags + "-fno-pic", + "-fno-pie", + "-mno-red-zone", + "-march=core2", + "-pipe", + "-mcmodel=kernel", + "-fno-builtin", + + // Warnings + "-Wall", + "-Wextra", + "-Wfloat-equal", + "-Wpointer-arith", + "-Wcast-align", + "-Wredundant-decls", + "-Winit-self", + "-Wswitch-default", + "-Wstrict-overflow=5", + "-Wconversion", + + // C++ flags + "-fno-rtti", + "-fexceptions", + + // Linker flags + "-T${workspaceFolder}/Architecture/amd64/linker.ld", + "-Wl,-static,--no-dynamic-linker,-ztext", + "-nostdlib", + "-nodefaultlibs", + "-nolibc", + "-zmax-page-size=0x1000", + "-shared", + + // Debug flags + "-ggdb3", + "-O0", + "-fdiagnostics-color=always", + "-fverbose-asm", + "-fstack-usage", + "-fstack-check", + "-fsanitize=undefined", + + // VSCode flags + "-ffreestanding", + "-nostdinc", + "-nostdinc++" + ] + }, + { + "name": "FennixLoader x32 (Linux, GCC, debug)", + "includePath": [ + "${workspaceFolder}/FennixLoader/include" + ], + "defines": [ + "__debug_vscode__", + "KERNEL_NAME=\"Fennix\"", + "KERNEL_VERSION=\"1.0\"", + "GIT_COMMIT=\"0000000000000000000000000000000000000000\"", + "GIT_COMMIT_SHORT=\"0000000\"", + "DEBUG=\"1\"" + ], + "compilerPath": "${workspaceFolder}/../tools/cross/bin/i386-elf-gcc", + "cStandard": "c17", + "cppStandard": "c++20", + "intelliSenseMode": "gcc-x86", + "configurationProvider": "ms-vscode.makefile-tools", + "compilerArgs": [ + // Compiler flags + "-fno-pic", + "-fno-pie", + "-mno-80387", + "-mno-mmx", + "-mno-3dnow", + "-mno-red-zone", + "-march=pentium", + "-pipe", + "-msoft-float", + "-fno-builtin", + + // Warnings + "-Wall", + "-Wextra", + "-Wfloat-equal", + "-Wpointer-arith", + "-Wcast-align", + "-Wredundant-decls", + "-Winit-self", + "-Wswitch-default", + "-Wstrict-overflow=5", + "-Wconversion", + + // C++ flags + "-fno-rtti", + "-fexceptions", + + // Linker flags + "-T${workspaceFolder}/Architecture/i386/linker.ld", + "-Wl,-static,--no-dynamic-linker,-ztext", + "-nostdlib", + "-nodefaultlibs", + "-nolibc", + "-zmax-page-size=0x1000", + "-shared", + + // Debug flags + "-ggdb3", + "-O0", + "-fdiagnostics-color=always", + "-fverbose-asm", + "-fstack-usage", + "-fstack-check", + "-fsanitize=undefined", + // VSCode flags "-ffreestanding", "-nostdinc", diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 00000000..24dcbc46 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,40 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "FLDR.elf", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceRoot}/FLDR.elf", + "cwd": "${workspaceRoot}", + "args": [], + "targetArchitecture": "x86", + "MIMode": "gdb", + "miDebuggerPath": "/usr/bin/gdb", + "miDebuggerArgs": "", + "externalConsole": false, + "additionalSOLibSearchPath": "${workspaceRoot}", + "customLaunchSetupCommands": [ + { + "text": "target remote localhost:1234", + "description": "Connect to QEMU remote debugger" + } + ], + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + }, + { + "text": "set breakpoint pending on", + "description": "Make breakpoint pending on future shared library load." + }, + { + "text": "file ${workspaceRoot}/FLDR.elf", + "description": "Load binary." + }, + ] + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..2d09fa10 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,19 @@ +{ + "C_Cpp.errorSquiggles": "Enabled", + "C_Cpp.autocompleteAddParentheses": true, + "C_Cpp.codeAnalysis.clangTidy.enabled": true, + "C_Cpp.clang_format_style": "Visual Studio", + "C_Cpp.default.intelliSenseMode": "gcc-x64", + "C_Cpp.default.cStandard": "c17", + "C_Cpp.default.cppStandard": "c++20", + "C_Cpp.intelliSenseMemoryLimit": 16384, + "editor.smoothScrolling": true, + "editor.cursorSmoothCaretAnimation": "on", + "C_Cpp.codeAnalysis.clangTidy.checks.disabled": [ + "clang-analyzer-security.insecureAPI.strcpy", + "clang-diagnostic-unknown-warning-option", + "clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling", + "clang-diagnostic-implicit-exception-spec-mismatch", + "clang-diagnostic-unknown-attributes" + ] +} \ No newline at end of file diff --git a/FennixLoader/ArithmeticOperations.c b/FennixLoader/ArithmeticOperations.c new file mode 100644 index 00000000..91a9db63 --- /dev/null +++ b/FennixLoader/ArithmeticOperations.c @@ -0,0 +1,327 @@ +/* Source: https://github.com/glitchub/arith64 */ +#define arith64_u64 unsigned long long int +#define arith64_s64 signed long long int +#define arith64_u32 unsigned int +#define arith64_s32 int + +typedef union +{ + arith64_u64 u64; + arith64_s64 s64; + struct + { +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + arith64_u32 hi; + arith64_u32 lo; +#else + arith64_u32 lo; + arith64_u32 hi; +#endif + } u32; + struct + { +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + arith64_s32 hi; + arith64_s32 lo; +#else + arith64_s32 lo; + arith64_s32 hi; +#endif + } s32; +} arith64_word; + +#define arith64_hi(n) (arith64_word){.u64 = n}.u32.hi +#define arith64_lo(n) (arith64_word){.u64 = n}.u32.lo +#define arith64_neg(a, b) (((a) ^ ((((arith64_s64)(b)) >= 0) - 1)) + (((arith64_s64)(b)) < 0)) +#define arith64_abs(a) arith64_neg(a, a) + +arith64_s64 __absvdi2(arith64_s64 a) +{ + return arith64_abs(a); +} + +arith64_s64 __ashldi3(arith64_s64 a, int b) +{ + arith64_word w = {.s64 = a}; + + b &= 63; + + if (b >= 32) + { + w.u32.hi = w.u32.lo << (b - 32); + w.u32.lo = 0; + } + else if (b) + { + w.u32.hi = (w.u32.lo >> (32 - b)) | (w.u32.hi << b); + w.u32.lo <<= b; + } + return w.s64; +} + +arith64_s64 __ashrdi3(arith64_s64 a, int b) +{ + arith64_word w = {.s64 = a}; + + b &= 63; + + if (b >= 32) + { + w.s32.lo = w.s32.hi >> (b - 32); + w.s32.hi >>= 31; // 0xFFFFFFFF or 0 + } + else if (b) + { + w.u32.lo = (w.u32.hi << (32 - b)) | (w.u32.lo >> b); + w.s32.hi >>= b; + } + return w.s64; +} + +int __clzsi2(arith64_u32 a) +{ + int b, n = 0; + b = !(a & 0xffff0000) << 4; + n += b; + a <<= b; + b = !(a & 0xff000000) << 3; + n += b; + a <<= b; + b = !(a & 0xf0000000) << 2; + n += b; + a <<= b; + b = !(a & 0xc0000000) << 1; + n += b; + a <<= b; + return n + !(a & 0x80000000); +} + +int __clzdi2(arith64_u64 a) +{ + int b, n = 0; + b = !(a & 0xffffffff00000000ULL) << 5; + n += b; + a <<= b; + b = !(a & 0xffff000000000000ULL) << 4; + n += b; + a <<= b; + b = !(a & 0xff00000000000000ULL) << 3; + n += b; + a <<= b; + b = !(a & 0xf000000000000000ULL) << 2; + n += b; + a <<= b; + b = !(a & 0xc000000000000000ULL) << 1; + n += b; + a <<= b; + return n + !(a & 0x8000000000000000ULL); +} + +int __ctzsi2(arith64_u32 a) +{ + int b, n = 0; + b = !(a & 0x0000ffff) << 4; + n += b; + a >>= b; + b = !(a & 0x000000ff) << 3; + n += b; + a >>= b; + b = !(a & 0x0000000f) << 2; + n += b; + a >>= b; + b = !(a & 0x00000003) << 1; + n += b; + a >>= b; + return n + !(a & 0x00000001); +} + +int __ctzdi2(arith64_u64 a) +{ + int b, n = 0; + b = !(a & 0x00000000ffffffffULL) << 5; + n += b; + a >>= b; + b = !(a & 0x000000000000ffffULL) << 4; + n += b; + a >>= b; + b = !(a & 0x00000000000000ffULL) << 3; + n += b; + a >>= b; + b = !(a & 0x000000000000000fULL) << 2; + n += b; + a >>= b; + b = !(a & 0x0000000000000003ULL) << 1; + n += b; + a >>= b; + return n + !(a & 0x0000000000000001ULL); +} + +arith64_u64 __divmoddi4(arith64_u64 a, arith64_u64 b, arith64_u64 *c) +{ + if (b > a) + { + if (c) + *c = a; + return 0; + } + if (!arith64_hi(b)) + { + if (b == 0) + { + volatile char x = 0; + x = 1 / x; + } + if (b == 1) + { + if (c) + *c = 0; + return a; + } + if (!arith64_hi(a)) + { + if (c) + *c = arith64_lo(a) % arith64_lo(b); + return arith64_lo(a) / arith64_lo(b); + } + } + + char bits = __clzdi2(b) - __clzdi2(a) + 1; + arith64_u64 rem = a >> bits; + a <<= 64 - bits; + arith64_u64 wrap = 0; + while (bits-- > 0) + { + rem = (rem << 1) | (a >> 63); + a = (a << 1) | (wrap & 1); + wrap = ((arith64_s64)(b - rem - 1) >> 63); + rem -= b & wrap; + } + if (c) + *c = rem; + return (a << 1) | (wrap & 1); +} + +arith64_s64 __divdi3(arith64_s64 a, arith64_s64 b) +{ + arith64_u64 q = __divmoddi4(arith64_abs(a), arith64_abs(b), (void *)0); + return arith64_neg(q, a ^ b); +} + +int __ffsdi2(arith64_u64 a) { return a ? __ctzdi2(a) + 1 : 0; } + +arith64_u64 __lshrdi3(arith64_u64 a, int b) +{ + arith64_word w = {.u64 = a}; + + b &= 63; + + if (b >= 32) + { + w.u32.lo = w.u32.hi >> (b - 32); + w.u32.hi = 0; + } + else if (b) + { + w.u32.lo = (w.u32.hi << (32 - b)) | (w.u32.lo >> b); + w.u32.hi >>= b; + } + return w.u64; +} + +arith64_s64 __moddi3(arith64_s64 a, arith64_s64 b) +{ + arith64_u64 r; + __divmoddi4(arith64_abs(a), arith64_abs(b), &r); + return arith64_neg(r, a); +} + +int __popcountsi2(arith64_u32 a) +{ + + a = a - ((a >> 1) & 0x55555555); + a = ((a >> 2) & 0x33333333) + (a & 0x33333333); + a = (a + (a >> 4)) & 0x0F0F0F0F; + a = (a + (a >> 16)); + + return (a + (a >> 8)) & 63; +} + +int __popcountdi2(arith64_u64 a) +{ + + a = a - ((a >> 1) & 0x5555555555555555ULL); + a = ((a >> 2) & 0x3333333333333333ULL) + (a & 0x3333333333333333ULL); + a = (a + (a >> 4)) & 0x0F0F0F0F0F0F0F0FULL; + a = (a + (a >> 32)); + a = (a + (a >> 16)); + + return (a + (a >> 8)) & 127; +} + +arith64_u64 __udivdi3(arith64_u64 a, arith64_u64 b) { return __divmoddi4(a, b, (void *)0); } + +arith64_u64 __umoddi3(arith64_u64 a, arith64_u64 b) +{ + arith64_u64 r; + __divmoddi4(a, b, &r); + return r; +} + +/* Good documentation: https://splichal.eu/scripts/sphinx/gccint/_build/html/the-gcc-low-level-runtime-library/routines-for-floating-point-emulation.html */ + +double __adddf3(double a, double b) { return a + b; } +double __muldf3(double a, double b) { return a * b; } +double __floatsidf(int i) { return (double)i; } +int __ltdf2(double a, double b) { return a < b; } +int __gtdf2(double a, double b) { return a > b; } +int __nedf2(double a, double b) { return a != b; } +int __eqdf2(double a, double b) { return a == b; } +double __floatdidf(long i) { return (double)i; } +double __divdf3(double a, double b) { return a / b; } +double __subdf3(double a, double b) { return a - b; } +int __gedf2(double a, double b) { return a >= b; } +int __fixdfsi(double a) { return (int)a; } +long __fixdfdi(double a) { return (long)a; } +int __ledf2(double a, double b) { return a <= b; } + +/* FIXME: Check if these functions are implemented correctly */ + +typedef long long int64_t; +typedef unsigned long long uint64_t; +typedef unsigned int uint32_t; +typedef struct +{ + uint64_t value; +} atomic_uint64_t; + +uint64_t __atomic_load_8(const atomic_uint64_t *p) +{ + uint64_t value; + __asm__ volatile("lock cmpxchg8b %1" + : "=A"(value) + : "m"(*p) + : "memory"); + return value; +} + +void __atomic_store_8(atomic_uint64_t *p, uint64_t value) +{ + __asm__ volatile("lock cmpxchg8b %0" + : "=m"(p->value) + : "a"((uint32_t)value), "d"((uint32_t)(value >> 32)), "m"(*p) + : "memory"); +} + +/* FIXME: __fixsfsi is not implemented correctly(?) */ +int __fixsfsi(float a) { return (int)a; } + +int __ltsf2(float a, float b) { return a < b; } +int __eqsf2(float a, float b) { return a == b; } +float __divsf3(float a, float b) { return a / b; } +double __extendsfdf2(float a) { return (double)a; } +float __truncdfsf2(double a) { return (float)a; } +float __subsf3(float a, float b) { return a - b; } +float __floatsisf(int a) { return (float)a; } +int __fixunssfsi(float a) { return (int)a; } +float __mulsf3(float a, float b) { return a * b; } +float __addsf3(float a, float b) { return a + b; } diff --git a/FennixLoader/Bootstrap.asm b/FennixLoader/Bootstrap.asm new file mode 100644 index 00000000..7b4957e4 --- /dev/null +++ b/FennixLoader/Bootstrap.asm @@ -0,0 +1,34 @@ +[bits 32] + +extern loader_main + +section .text + +MB_HeaderMagic: + dq 0 + +MB_HeaderInfo: + dq 0 + +global _start +_start: + cli + mov [MB_HeaderMagic], eax + mov [MB_HeaderInfo], ebx + mov esp, KernelStack + mov eax, [MB_HeaderMagic] + mov ebx, [MB_HeaderInfo] + push ebx + push eax + call loader_main +.hang: + cli + hlt + jmp .hang + +STACK_SIZE equ 0x4000 ; 16KB + +section .bss +align 16 +KernelStack: + resb STACK_SIZE diff --git a/FennixLoader/Debugger.cpp b/FennixLoader/Debugger.cpp new file mode 100644 index 00000000..40a63ab3 --- /dev/null +++ b/FennixLoader/Debugger.cpp @@ -0,0 +1,115 @@ +#include +#include + +enum vga_color +{ + VGA_COLOR_BLACK = 0, + VGA_COLOR_BLUE = 1, + VGA_COLOR_GREEN = 2, + VGA_COLOR_CYAN = 3, + VGA_COLOR_RED = 4, + VGA_COLOR_MAGENTA = 5, + VGA_COLOR_BROWN = 6, + VGA_COLOR_LIGHT_GREY = 7, + VGA_COLOR_DARK_GREY = 8, + VGA_COLOR_LIGHT_BLUE = 9, + VGA_COLOR_LIGHT_GREEN = 10, + VGA_COLOR_LIGHT_CYAN = 11, + VGA_COLOR_LIGHT_RED = 12, + VGA_COLOR_LIGHT_MAGENTA = 13, + VGA_COLOR_LIGHT_BROWN = 14, + VGA_COLOR_WHITE = 15, +}; + +extern "C" void terminal_setcolor(uint8_t color); +extern "C" void putchar(char c); +extern bool DoNotWriteOnScreen; + +static inline void WritePrefix(DebugLevel Level, const char *File, int Line, const char *Function) +{ + const char *DbgLvlString; + switch (Level) + { + case DebugLevelError: + DbgLvlString = "ERROR"; + terminal_setcolor(VGA_COLOR_RED); + break; + case DebugLevelWarning: + DbgLvlString = "WARN "; + terminal_setcolor(VGA_COLOR_BROWN); + break; + case DebugLevelInfo: + DbgLvlString = "INFO "; + terminal_setcolor(VGA_COLOR_LIGHT_GREY); + break; + case DebugLevelDebug: + DbgLvlString = "DEBUG"; + terminal_setcolor(VGA_COLOR_GREEN); + break; + case DebugLevelTrace: + DbgLvlString = "TRACE"; + terminal_setcolor(VGA_COLOR_DARK_GREY); + break; + case DebugLevelFixme: + DbgLvlString = "FIXME"; + terminal_setcolor(VGA_COLOR_LIGHT_RED); + break; + case DebugLevelUbsan: + { + DbgLvlString = "UBSAN"; + printf("%s|%s: ", DbgLvlString, Function); + terminal_setcolor(VGA_COLOR_LIGHT_RED); + return; + } + default: + DbgLvlString = "UNKNW"; + terminal_setcolor(VGA_COLOR_WHITE); + break; + } + DoNotWriteOnScreen = true; + printf("%s|%s->%s:%d: ", DbgLvlString, File, Function, Line); + DoNotWriteOnScreen = false; +} + +namespace SysDbg +{ + void Write(DebugLevel Level, const char *File, int Line, const char *Function, const char *Format, ...) + { + WritePrefix(Level, File, Line, Function); + va_list args; + va_start(args, Format); + vprintf(Format, args); + va_end(args); + } + + void WriteLine(DebugLevel Level, const char *File, int Line, const char *Function, const char *Format, ...) + { + WritePrefix(Level, File, Line, Function); + va_list args; + va_start(args, Format); + vprintf(Format, args); + va_end(args); + putchar('\n'); + } +} + +// C compatibility +extern "C" void SysDbgWrite(enum DebugLevel Level, const char *File, int Line, const char *Function, const char *Format, ...) +{ + WritePrefix(Level, File, Line, Function); + va_list args; + va_start(args, Format); + vprintf(Format, args); + va_end(args); +} + +// C compatibility +extern "C" void SysDbgWriteLine(enum DebugLevel Level, const char *File, int Line, const char *Function, const char *Format, ...) +{ + WritePrefix(Level, File, Line, Function); + va_list args; + va_start(args, Format); + vprintf(Format, args); + va_end(args); + putchar('\n'); +} diff --git a/FennixLoader/Detect/Detect64.asm b/FennixLoader/Detect/Detect64.asm new file mode 100644 index 00000000..9c6273e5 --- /dev/null +++ b/FennixLoader/Detect/Detect64.asm @@ -0,0 +1,16 @@ +[bits 32] +global Detect64Bit +Detect64Bit: + mov eax, 0x80000000 + cpuid + cmp eax, 0x80000001 + jb .NoLongMode + mov eax, 0x80000001 + cpuid + test edx, 1 << 29 + jz .NoLongMode + mov eax, 1 + ret +.NoLongMode: + xor eax, eax + ret diff --git a/FennixLoader/Detect/DetectCPUID.asm b/FennixLoader/Detect/DetectCPUID.asm new file mode 100644 index 00000000..eaa23e9c --- /dev/null +++ b/FennixLoader/Detect/DetectCPUID.asm @@ -0,0 +1,20 @@ +[bits 32] +global DetectCPUID +DetectCPUID: + pushfd + pop eax + mov ecx, eax + xor eax, 1 << 21 + push eax + popfd + pushfd + pop eax + push ecx + popfd + xor eax, ecx + jz .NoCPUID + mov eax, 1 + ret +.NoCPUID: + xor eax, eax + ret diff --git a/FennixLoader/LoadElf.cpp b/FennixLoader/LoadElf.cpp new file mode 100644 index 00000000..90cc0c21 --- /dev/null +++ b/FennixLoader/LoadElf.cpp @@ -0,0 +1,136 @@ +#include "loadelf.hpp" + +#include +#include +#include + +static uint64_t KernelJumpAddress = 0x0; + +bool LoadElfInMemory(void *Address, size_t Length, bool Allow64) +{ + Elf32_Ehdr *Header32 = (Elf32_Ehdr *)Address; + Elf64_Ehdr *Header64 = (Elf64_Ehdr *)Address; + + bool Is64Bit = false; + + if (Header32->e_ident[EI_MAG0] == ELFMAG0 && + Header32->e_ident[EI_MAG1] == ELFMAG1 && + Header32->e_ident[EI_MAG2] == ELFMAG2 && + Header32->e_ident[EI_MAG3] == ELFMAG3) + { + debug("Elf magic is correct"); + if (Header32->e_machine == EM_X86_64) + { + if (!Allow64) + { + error("64 bit elf not allowed"); + return false; + } + Is64Bit = true; + debug("Elf is 64 bit"); + } + else if (Header32->e_machine == EM_386) + { + if (Allow64) + { + error("32 bit elf not allowed"); + return false; + } + debug("Elf is 32 bit"); + } + else + { + error("Elf is neither 32 nor 64 bit"); + return false; + } + + if (Is64Bit) + { + fixme("64 bit elf not implemented"); + } + else + { + if (Header32->e_phentsize != sizeof(Elf32_Phdr)) + { + error("Elf program header size is not correct"); + return false; + } + + Elf32_Phdr *ProgramHeader = (Elf32_Phdr *)((uintptr_t)Address + Header32->e_phoff); + for (size_t i = 0; i < Header32->e_phnum; i++) + { + if (ProgramHeader[i].p_type != PT_LOAD) + continue; + + if (ProgramHeader[i].p_filesz > ProgramHeader[i].p_memsz) + { + error("File size is greater than memory size"); + return false; + } + + size_t SegmentSize = ProgramHeader[i].p_memsz; + void *VirtualAddress = (void *)ProgramHeader[i].p_vaddr; + void *PhysicalAddress = KernelAllocator32.RequestPages(TO_PAGES(SegmentSize)); + debug("- Segment: %p (physical allocated at: %p) (%#x bytes)", VirtualAddress, PhysicalAddress, SegmentSize); + + debug(" Mapping %d pages at %p (%p-%p)", TO_PAGES(SegmentSize), VirtualAddress, PhysicalAddress, (void *)((uintptr_t)PhysicalAddress + TO_PAGES(SegmentSize) * PAGE_SIZE)); + Memory32::Virtual().Map(VirtualAddress, PhysicalAddress, SegmentSize, P | RW); + + debug(" Copying %#x bytes to %p", ProgramHeader[i].p_filesz, PhysicalAddress); + memcpy(PhysicalAddress, (void *)((uintptr_t)Address + ProgramHeader[i].p_offset), ProgramHeader[i].p_filesz); + + if (ProgramHeader[i].p_filesz < ProgramHeader[i].p_memsz) + { + debug(" Zeroing %#x bytes at %p", ProgramHeader[i].p_memsz - ProgramHeader[i].p_filesz, (void *)((uintptr_t)PhysicalAddress + ProgramHeader[i].p_filesz)); + memset((void *)((uintptr_t)PhysicalAddress + ProgramHeader[i].p_filesz), 0, ProgramHeader[i].p_memsz - ProgramHeader[i].p_filesz); + } + } + + void *Stack = KernelAllocator32.RequestPage(); + memset(Stack, 0, PAGE_SIZE); + debug("Stack allocated at: %p", Stack); + + void *StackTop = (void *)((uintptr_t)Stack + PAGE_SIZE - 1); + debug("Stack top at: %p", StackTop); + + debug("Kernel Entry Point: %p", (void *)Header32->e_entry); + + KernelJumpAddress = (uint64_t)Header32->e_entry; + + asmv("cli"); + asmv("mov %0, %%esp" + : + : "r"(StackTop)); + asmv("pushf\n" + "orl $0x46, (%esp)\n" + "popf"); + asmv("mov $0x80000011, %eax"); + asmv("mov %eax, %cr0"); + asmv("mov $0, %eax"); + asmv("mov %eax, %cr4"); + asmv("mov $0, %eax\n" + "mov $0, %ebx\n" + "mov $0, %ecx\n" + "mov $0, %edx\n" + "mov $0, %esi\n" + "mov $0, %edi\n" + "mov $0, %ebp\n"); + asmv("jmp %0" + : + : "r"(KernelJumpAddress)); + + // asmv("call %0" + // : + // : "r"(KernelJumpAddress)); + + // asmv("call *%0" + // : + // : "r"(KernelJumpAddress)); + + // void (*EntryPoint)() = (void (*)())KernelJumpAddress; + // EntryPoint(); + } + } + + return false; +} diff --git a/FennixLoader/Loader.cpp b/FennixLoader/Loader.cpp new file mode 100644 index 00000000..c68a1e70 --- /dev/null +++ b/FennixLoader/Loader.cpp @@ -0,0 +1,371 @@ +#include +#include +#include +#include +#include + +#include "loadelf.hpp" + +using namespace UniversalAsynchronousReceiverTransmitter; + +BootInfo mb2binfo; + +bool DoNotWriteOnScreen = false; +extern "C" void terminal_initialize(void); +extern "C" void terminal_putchar(char c); + +extern "C" int DetectCPUID(); +extern "C" int Detect64Bit(); +extern "C" void EnablePaging(); + +char *strncpy(char *destination, const char *source, unsigned long num) +{ + if (destination == NULL) + return NULL; + char *ptr = destination; + while (*source && num--) + { + *destination = *source; + destination++; + source++; + } + *destination = '\0'; + return ptr; +} + +long unsigned strlen(const char s[]) +{ + long unsigned i = 0; + if (s) + while (s[i] != '\0') + ++i; + return i; +} + +int strcmp(const char *l, const char *r) +{ + for (; *l == *r && *l; l++, r++) + ; + return *(unsigned char *)l - *(unsigned char *)r; +} + +extern "C" void putchar(char c) +{ + if (mb2binfo.Framebuffer[0].Type == FramebufferType::EGA && !DoNotWriteOnScreen) + terminal_putchar(c); + UART(COM1).Write(c); +} + +void ProcessMB2(uint32_t Info) +{ + auto InfoAddress = Info; + for (auto Tag = (struct multiboot_tag *)((uint8_t *)(uint64_t)InfoAddress + 8); + ; + Tag = (struct multiboot_tag *)((multiboot_uint8_t *)Tag + ((Tag->size + 7) & ~7))) + { + if (Tag->type == MULTIBOOT_TAG_TYPE_END) + { + debug("End of multiboot2 tags"); + break; + } + + switch (Tag->type) + { + case MULTIBOOT_TAG_TYPE_CMDLINE: + { + strncpy(mb2binfo.Kernel.CommandLine, + ((multiboot_tag_string *)Tag)->string, + strlen(((multiboot_tag_string *)Tag)->string)); + debug("Kernel command line: %s", mb2binfo.Kernel.CommandLine); + break; + } + case MULTIBOOT_TAG_TYPE_BOOT_LOADER_NAME: + { + strncpy(mb2binfo.Bootloader.Name, + ((multiboot_tag_string *)Tag)->string, + strlen(((multiboot_tag_string *)Tag)->string)); + debug("Bootloader name: %s", mb2binfo.Bootloader.Name); + break; + } + case MULTIBOOT_TAG_TYPE_MODULE: + { + multiboot_tag_module *module = (multiboot_tag_module *)Tag; + static int module_count = 0; + mb2binfo.Modules[module_count].Address = (void *)(uint64_t)module->mod_start; + mb2binfo.Modules[module_count].Size = (uint64_t)module->size; + strncpy(mb2binfo.Modules[module_count].Path, "(null)", 6); + strncpy(mb2binfo.Modules[module_count].CommandLine, module->cmdline, + strlen(module->cmdline)); + debug("Module: %s %s %p %lld", mb2binfo.Modules[module_count].Path, + mb2binfo.Modules[module_count].CommandLine, + mb2binfo.Modules[module_count].Address, + mb2binfo.Modules[module_count].Size); + module_count++; + break; + } + case MULTIBOOT_TAG_TYPE_BASIC_MEMINFO: + { + multiboot_tag_basic_meminfo *meminfo = (multiboot_tag_basic_meminfo *)Tag; + fixme("basic_meminfo->[mem_lower: %#x, mem_upper: %#x]", + meminfo->mem_lower, meminfo->mem_upper); + break; + } + case MULTIBOOT_TAG_TYPE_BOOTDEV: + { + multiboot_tag_bootdev *bootdev = (multiboot_tag_bootdev *)Tag; + fixme("bootdev->[biosdev: %#x, slice: %#x, part: %#x]", + bootdev->biosdev, bootdev->slice, bootdev->part); + break; + } + case MULTIBOOT_TAG_TYPE_MMAP: + { + multiboot_tag_mmap *mmap = (multiboot_tag_mmap *)Tag; + uint32_t EntryCount = mmap->size / sizeof(multiboot_mmap_entry); + mb2binfo.Memory.Entries = EntryCount; + for (uint32_t i = 0; i < EntryCount; i++) + { + if (EntryCount > MAX_MEMORY_ENTRIES) + { + warn("Too many memory entries, skipping the rest..."); + break; + } + multiboot_mmap_entry entry = mmap->entries[i]; + mb2binfo.Memory.Size += entry.len; + switch (entry.type) + { + case MULTIBOOT_MEMORY_AVAILABLE: + mb2binfo.Memory.Entry[i].BaseAddress = (void *)entry.addr; + mb2binfo.Memory.Entry[i].Length = entry.len; + mb2binfo.Memory.Entry[i].Type = Usable; + debug("Available memory: %#llx - %#llx", entry.addr, entry.addr + entry.len); + break; + case MULTIBOOT_MEMORY_RESERVED: + mb2binfo.Memory.Entry[i].BaseAddress = (void *)entry.addr; + mb2binfo.Memory.Entry[i].Length = entry.len; + mb2binfo.Memory.Entry[i].Type = Reserved; + debug("Reserved memory: %#llx - %#llx", entry.addr, entry.addr + entry.len); + break; + case MULTIBOOT_MEMORY_ACPI_RECLAIMABLE: + mb2binfo.Memory.Entry[i].BaseAddress = (void *)entry.addr; + mb2binfo.Memory.Entry[i].Length = entry.len; + mb2binfo.Memory.Entry[i].Type = ACPIReclaimable; + debug("ACPI reclaimable memory: %#llx - %#llx", entry.addr, entry.addr + entry.len); + break; + case MULTIBOOT_MEMORY_NVS: + mb2binfo.Memory.Entry[i].BaseAddress = (void *)entry.addr; + mb2binfo.Memory.Entry[i].Length = entry.len; + mb2binfo.Memory.Entry[i].Type = ACPINVS; + debug("ACPI NVS memory: %#llx - %#llx", entry.addr, entry.addr + entry.len); + break; + case MULTIBOOT_MEMORY_BADRAM: + mb2binfo.Memory.Entry[i].BaseAddress = (void *)entry.addr; + mb2binfo.Memory.Entry[i].Length = entry.len; + mb2binfo.Memory.Entry[i].Type = BadMemory; + debug("Bad memory: %#llx - %#llx", entry.addr, entry.addr + entry.len); + break; + default: + mb2binfo.Memory.Entry[i].BaseAddress = (void *)entry.addr; + mb2binfo.Memory.Entry[i].Length = entry.len; + mb2binfo.Memory.Entry[i].Type = Unknown; + debug("Unknown memory: %#llx - %#llx", entry.addr, entry.addr + entry.len); + break; + } + } + break; + } + case MULTIBOOT_TAG_TYPE_VBE: + { + multiboot_tag_vbe *vbe = (multiboot_tag_vbe *)Tag; + fixme("vbe->[vbe_mode: %#x, vbe_interface_seg: %#x, vbe_interface_off: %#x, vbe_interface_len: %#x]", + vbe->vbe_mode, vbe->vbe_interface_seg, vbe->vbe_interface_off, vbe->vbe_interface_len); + break; + } + case MULTIBOOT_TAG_TYPE_FRAMEBUFFER: + { + multiboot_tag_framebuffer *fb = (multiboot_tag_framebuffer *)Tag; + static int fb_count = 0; + mb2binfo.Framebuffer[fb_count].BaseAddress = (void *)fb->common.framebuffer_addr; + mb2binfo.Framebuffer[fb_count].Width = fb->common.framebuffer_width; + mb2binfo.Framebuffer[fb_count].Height = fb->common.framebuffer_height; + mb2binfo.Framebuffer[fb_count].Pitch = fb->common.framebuffer_pitch; + mb2binfo.Framebuffer[fb_count].BitsPerPixel = fb->common.framebuffer_bpp; + switch (fb->common.framebuffer_type) + { + case MULTIBOOT_FRAMEBUFFER_TYPE_INDEXED: + { + mb2binfo.Framebuffer[fb_count].Type = FramebufferType::Indexed; + break; + } + case MULTIBOOT_FRAMEBUFFER_TYPE_RGB: + { + mb2binfo.Framebuffer[fb_count].Type = FramebufferType::RGB; + mb2binfo.Framebuffer[fb_count].RedMaskSize = fb->framebuffer_red_mask_size; + mb2binfo.Framebuffer[fb_count].RedMaskShift = fb->framebuffer_red_field_position; + mb2binfo.Framebuffer[fb_count].GreenMaskSize = fb->framebuffer_green_mask_size; + mb2binfo.Framebuffer[fb_count].GreenMaskShift = fb->framebuffer_green_field_position; + mb2binfo.Framebuffer[fb_count].BlueMaskSize = fb->framebuffer_blue_mask_size; + mb2binfo.Framebuffer[fb_count].BlueMaskShift = fb->framebuffer_blue_field_position; + break; + } + case MULTIBOOT_FRAMEBUFFER_TYPE_EGA_TEXT: + { + mb2binfo.Framebuffer[fb_count].Type = FramebufferType::EGA; + break; + } + } + debug("Framebuffer %d: %dx%d %d bpp", Tag, fb->common.framebuffer_width, fb->common.framebuffer_height, fb->common.framebuffer_bpp); + debug("More info:\nAddress: %p\nPitch: %lld\nMemoryModel: %d\nRedMaskSize: %d\nRedMaskShift: %d\nGreenMaskSize: %d\nGreenMaskShift: %d\nBlueMaskSize: %d\nBlueMaskShift: %d", + fb->common.framebuffer_addr, fb->common.framebuffer_pitch, fb->common.framebuffer_type, + fb->framebuffer_red_mask_size, fb->framebuffer_red_field_position, fb->framebuffer_green_mask_size, + fb->framebuffer_green_field_position, fb->framebuffer_blue_mask_size, fb->framebuffer_blue_field_position); + fb_count++; + break; + } + case MULTIBOOT_TAG_TYPE_ELF_SECTIONS: + { + multiboot_tag_elf_sections *elf = (multiboot_tag_elf_sections *)Tag; + fixme("elf_sections->[num=%d, size=%d, entsize=%d, shndx=%d]", + elf->num, elf->size, elf->entsize, elf->shndx); + break; + } + case MULTIBOOT_TAG_TYPE_APM: + { + multiboot_tag_apm *apm = (multiboot_tag_apm *)Tag; + fixme("apm->[version: %d, cseg: %d, offset: %d, cseg_16: %d, dseg: %d, flags: %d, cseg_len: %d, cseg_16_len: %d, dseg_len: %d]", + apm->version, apm->cseg, apm->offset, apm->cseg_16, apm->dseg, apm->flags, apm->cseg_len, apm->cseg_16_len, apm->dseg_len); + break; + } + case MULTIBOOT_TAG_TYPE_EFI32: + { + multiboot_tag_efi32 *efi32 = (multiboot_tag_efi32 *)Tag; + fixme("efi32->[pointer: %p, size: %d]", efi32->pointer, efi32->size); + break; + } + case MULTIBOOT_TAG_TYPE_EFI64: + { + multiboot_tag_efi64 *efi64 = (multiboot_tag_efi64 *)Tag; + fixme("efi64->[pointer: %p, size: %d]", efi64->pointer, efi64->size); + break; + } + case MULTIBOOT_TAG_TYPE_SMBIOS: + { + multiboot_tag_smbios *smbios = (multiboot_tag_smbios *)Tag; + fixme("smbios->[major: %d, minor: %d]", smbios->major, smbios->minor); + break; + } + case MULTIBOOT_TAG_TYPE_ACPI_OLD: + { + mb2binfo.RSDP = (BootInfo::RSDPInfo *)((multiboot_tag_old_acpi *)Tag)->rsdp; + debug("OLD ACPI RSDP: %p", mb2binfo.RSDP); + break; + } + case MULTIBOOT_TAG_TYPE_ACPI_NEW: + { + mb2binfo.RSDP = (BootInfo::RSDPInfo *)((multiboot_tag_new_acpi *)Tag)->rsdp; + debug("NEW ACPI RSDP: %p", mb2binfo.RSDP); + break; + } + case MULTIBOOT_TAG_TYPE_NETWORK: + { + multiboot_tag_network *net = (multiboot_tag_network *)Tag; + fixme("network->[dhcpack: %p]", net->dhcpack); + break; + } + case MULTIBOOT_TAG_TYPE_EFI_MMAP: + { + multiboot_tag_efi_mmap *efi_mmap = (multiboot_tag_efi_mmap *)Tag; + fixme("efi_mmap->[descr_size: %d, descr_vers: %d, efi_mmap: %p]", + efi_mmap->descr_size, efi_mmap->descr_vers, efi_mmap->efi_mmap); + break; + } + case MULTIBOOT_TAG_TYPE_EFI_BS: + { + fixme("efi_bs->[%p] (unknown structure)", Tag); + break; + } + case MULTIBOOT_TAG_TYPE_EFI32_IH: + { + multiboot_tag_efi32_ih *efi32_ih = (multiboot_tag_efi32_ih *)Tag; + fixme("efi32_ih->[pointer: %p]", efi32_ih->pointer); + break; + } + case MULTIBOOT_TAG_TYPE_EFI64_IH: + { + multiboot_tag_efi64_ih *efi64_ih = (multiboot_tag_efi64_ih *)Tag; + fixme("efi64_ih->[pointer: %p]", efi64_ih->pointer); + break; + } + case MULTIBOOT_TAG_TYPE_LOAD_BASE_ADDR: + { + multiboot_tag_load_base_addr *load_base_addr = (multiboot_tag_load_base_addr *)Tag; + mb2binfo.Kernel.PhysicalBase = (void *)load_base_addr->load_base_addr; + mb2binfo.Kernel.VirtualBase = (void *)(load_base_addr->load_base_addr + 0xC0000000); + debug("Kernel base: %p (physical) %p (virtual)", mb2binfo.Kernel.PhysicalBase, mb2binfo.Kernel.VirtualBase); + break; + } + default: + { + error("Unknown multiboot2 tag type: %d", Tag->type); + break; + } + } + } +} + +typedef void (*CallPtr)(void); +extern CallPtr __init_array_start[0], __init_array_end[0]; +extern CallPtr __fini_array_start[0], __fini_array_end[0]; + +extern "C" int loader_main(uint32_t magic, uint32_t addr) +{ + if (magic != MULTIBOOT2_BOOTLOADER_MAGIC) + { + error("Multiboot magic is invalid (%#x != %#x)", magic, MULTIBOOT2_BOOTLOADER_MAGIC); + while (1) + asmv("cli; hlt"); + } + + for (CallPtr *func = __init_array_start; func != __init_array_end; func++) + (*func)(); + + ProcessMB2(addr); + if (mb2binfo.Framebuffer[0].Type == FramebufferType::EGA) + terminal_initialize(); + debug("Multiboot2 tags processed"); + InitializeMemoryManagement(&mb2binfo, true); + EnablePaging(); + + bool Load64Bit = false; + + if (DetectCPUID()) + { + debug("CPUID supported"); + if (Detect64Bit()) + { + info("64-bit CPU detected"); + if (strcmp(mb2binfo.Kernel.CommandLine, "no64bit") == 0) + info("64-bit kernel disabled"); + else + Load64Bit = true; + } + else + { + info("32-bit CPU detected"); + } + } + else + { + error("CPUID not supported"); + while (1) + asmv("cli; hlt"); + } + if (!LoadElfInMemory(mb2binfo.Modules[0].Address, mb2binfo.Modules[0].Size, Load64Bit)) + { + error("Kernel not loaded"); + while (1) + asmv("cli; hlt"); + } + error("Unwanted reach"); + while (1) + asmv("cli; hlt"); +} diff --git a/FennixLoader/MB2Header.asm b/FennixLoader/MB2Header.asm new file mode 100644 index 00000000..a0a9a71e --- /dev/null +++ b/FennixLoader/MB2Header.asm @@ -0,0 +1,41 @@ +section .multiboot2 +align 4096 +HEADER_START: + dd 0xE85250D6 + dd 0 + dd (HEADER_END - HEADER_START) + dd 0x100000000 - (HEADER_END - HEADER_START) - 0 - 0xE85250D6 +align 8 +MB2_INFO_REQUEST_TAG_START: + dw 1 + dw 0 + dd MB2_INFO_REQUEST_TAG_END - MB2_INFO_REQUEST_TAG_START + dd 1 ; Command Line + dd 2 ; Boot Loader Name + dd 3 ; Module + dd 4 ; Basic Memory Information + dd 5 ; BIOS Boot Device + dd 6 ; Memory Map + dd 7 ; VBE + dd 8 ; Framebuffer + dd 9 ; ELF Sections + dd 10 ; APM Table + dd 11 ; EFI 32-bit System Table Pointer + dd 12 ; EFI 64-bit System Table Pointer + ; dd 13 ; SMBIOS + dd 14 ; ACPI Old + dd 15 ; ACPI New + dd 16 ; Network + dd 17 ; EFI Memory Map + dd 18 ; EFI Boot Services Notifier + dd 19 ; EFI 32-bit Image Handle Pointer + dd 20 ; EFI 64-bit Image Handle Pointer + dd 21 ; Load Base Address +MB2_INFO_REQUEST_TAG_END: +align 8 +MB2_TAG_START: + dw 0 + dw 0 + dd MB2_TAG_END - MB2_TAG_START +MB2_TAG_END: +HEADER_END: diff --git a/FennixLoader/MBHeader.asm b/FennixLoader/MBHeader.asm new file mode 100644 index 00000000..71a6efe8 --- /dev/null +++ b/FennixLoader/MBHeader.asm @@ -0,0 +1,5 @@ +section .multiboot +align 4 + dd 0x1BADB002 + dd 1 << 0 | 1 << 1 + dd -(0x1BADB002 + (1 << 0 | 1 << 1)) diff --git a/FennixLoader/Makefile b/FennixLoader/Makefile new file mode 100644 index 00000000..dff21074 --- /dev/null +++ b/FennixLoader/Makefile @@ -0,0 +1,50 @@ +# Config file +include ../../Makefile.conf + +NASM_ARCH = + +CC = ../../$(COMPILER_PATH)/i386-elf-gcc +CPP = ../../$(COMPILER_PATH)/i386-elf-g++ +NASM = /usr/bin/nasm + +ASM_SOURCES = $(shell find ./ -type f -name '*.asm') +C_SOURCES = $(shell find ./ -type f -name '*.c') +CPP_SOURCES = $(shell find ./ -type f -name '*.cpp') + +OBJ = $(C_SOURCES:.c=.o) $(CPP_SOURCES:.cpp=.o) $(ASM_SOURCES:.asm=.o) + +LDFLAGS = -shared -nostdlib -nodefaultlibs -nolibc -Tlinker.ld + +CFLAGS = -fno-pic -fno-pie \ + -mno-red-zone -pipe \ + -fno-builtin -Wno-main \ + -march=pentium -I./include -Wall -Wextra + +ifeq ($(DEBUG), 1) + CFLAGS += -DDEBUG -ggdb3 -O0 -fdiagnostics-color=always -fsanitize=undefined + NASM_ARCH += -F dwarf -g +endif + +build: $(OBJ) + $(info Linking $@) + $(CC) $(LDFLAGS) $(OBJ) -o FLDR.elf + mv FLDR.elf ../FLDR.elf + +%.o: %.c + $(info Compiling $<) + $(CC) $(CFLAGS) -std=c17 -c $< -o $@ + +%.o: %.cpp + $(info Compiling $<) + $(CPP) $(CFLAGS) -std=c++20 -fexceptions -c $< -o $@ -fno-rtti + +%.o: %.asm + $(info Compiling $<) + $(NASM) $< $(NASM_ARCH) -f elf32 -o $@ + +clean_only_obj: + $(info Removing objects $(OBJ)) + rm -f $(OBJ) + +clean: + rm -f $(OBJ) diff --git a/FennixLoader/Memory/Bitmap.cpp b/FennixLoader/Memory/Bitmap.cpp new file mode 100644 index 00000000..32ce0479 --- /dev/null +++ b/FennixLoader/Memory/Bitmap.cpp @@ -0,0 +1,28 @@ +#include + +bool Bitmap::operator[](uint64_t index) { return Get(index); } + +bool Bitmap::Get(uint64_t index) +{ + if (index > Size * 8) + return false; + uint64_t byteIndex = index / 8; + uint8_t bitIndex = index % 8; + uint8_t bitIndexer = 0b10000000 >> bitIndex; + if ((Buffer[byteIndex] & bitIndexer) > 0) + return true; + return false; +} + +bool Bitmap::Set(uint64_t index, bool value) +{ + if (index > Size * 8) + return false; + uint64_t byteIndex = index / 8; + uint8_t bitIndex = index % 8; + uint8_t bitIndexer = 0b10000000 >> bitIndex; + Buffer[byteIndex] &= ~bitIndexer; + if (value) + Buffer[byteIndex] |= bitIndexer; + return true; +} diff --git a/FennixLoader/Memory/Memory.cpp b/FennixLoader/Memory/Memory.cpp new file mode 100644 index 00000000..b1b34587 --- /dev/null +++ b/FennixLoader/Memory/Memory.cpp @@ -0,0 +1,50 @@ +#include + +Memory32::Physical KernelAllocator32{}; +Memory32::PageTable *KernelPageTable32 = nullptr; + +Memory64::Physical KernelAllocator64{}; +Memory64::PageTable *KernelPageTable64 = nullptr; + +void InitializeMemoryManagement(BootInfo *Info, bool is32) +{ + if (is32) + { + trace("Initializing Physical Memory Manager"); + // KernelAllocator32 = Physical(); <- Already called in the constructor + KernelAllocator32.Init(Info); + info("Memory Info: %lldMB / %lldMB (%lldMB reserved)", + TO_MB(KernelAllocator32.UsedMemory), + TO_MB(KernelAllocator32.TotalMemory), + TO_MB(KernelAllocator32.ReservedMemory)); + + trace("Initializing Virtual Memory Manager"); + + KernelPageTable32 = (Memory32::PageTable *)KernelAllocator32.RequestPages(TO_PAGES(PAGE_SIZE + 1)); + memset(KernelPageTable32, 0, PAGE_SIZE); + + { + Memory32::Virtual va = Memory32::Virtual(KernelPageTable32); + debug("Mapping from 0x0 to %#llx", Info->Memory.Size); + size_t MemSize = Info->Memory.Size; + va.Map((void *)0, (void *)0, MemSize, PTFlag::RW); + + debug("Mapping Framebuffer"); + int itrfb = 0; + while (true) + { + if (!Info->Framebuffer[itrfb].BaseAddress) + break; + + va.Map((void *)Info->Framebuffer[itrfb].BaseAddress, + (void *)Info->Framebuffer[itrfb].BaseAddress, + Info->Framebuffer[itrfb].Pitch * Info->Framebuffer[itrfb].Height, + PTFlag::RW | PTFlag::US | PTFlag::G); + itrfb++; + } + } + + trace("Applying new page table from address %#lx", KernelPageTable32); + asmv("mov %0, %%cr3" ::"r"(KernelPageTable32)); + } +} diff --git a/FennixLoader/Memory/PhysicalMemoryManager.cpp b/FennixLoader/Memory/PhysicalMemoryManager.cpp new file mode 100644 index 00000000..89c9d037 --- /dev/null +++ b/FennixLoader/Memory/PhysicalMemoryManager.cpp @@ -0,0 +1,583 @@ +#include +namespace Memory32 +{ + void *Physical::RequestPage() + { + for (; PageBitmapIndex < PageBitmap.Size * 8; PageBitmapIndex++) + { + if (PageBitmap[PageBitmapIndex] == true) + continue; + + this->LockPage((void *)(PageBitmapIndex * PAGE_SIZE)); + return (void *)(PageBitmapIndex * PAGE_SIZE); + } + + error("Out of memory! (Free: %ldMB; Used: %ldMB; Reserved: %ldMB)", TO_MB(FreeMemory), TO_MB(UsedMemory), TO_MB(ReservedMemory)); + while (1) + asmv("cli; hlt"); + __builtin_unreachable(); + } + + void *Physical::RequestPages(size_t Count) + { + for (; PageBitmapIndex < PageBitmap.Size * 8; PageBitmapIndex++) + { + if (PageBitmap[PageBitmapIndex] == true) + continue; + + for (uint64_t Index = PageBitmapIndex; Index < PageBitmap.Size * 8; Index++) + { + if (PageBitmap[Index] == true) + continue; + + for (size_t i = 0; i < Count; i++) + { + if (PageBitmap[Index + i] == true) + goto NextPage; + } + + this->LockPages((void *)(Index * PAGE_SIZE), Count); + return (void *)(Index * PAGE_SIZE); + + NextPage: + Index += Count; + continue; + } + } + + error("Out of memory! (Free: %ldMB; Used: %ldMB; Reserved: %ldMB)", TO_MB(FreeMemory), TO_MB(UsedMemory), TO_MB(ReservedMemory)); + while (1) + asmv("cli; hlt"); + __builtin_unreachable(); + } + + void Physical::FreePage(void *Address) + { + if (unlikely(Address == nullptr)) + { + warn("Null pointer passed to FreePage."); + return; + } + + size_t Index = (size_t)Address / PAGE_SIZE; + + if (unlikely(PageBitmap[Index] == false)) + { + warn("Tried to free an already free page. (%p)", Address); + return; + } + + if (PageBitmap.Set(Index, false)) + { + FreeMemory += PAGE_SIZE; + UsedMemory -= PAGE_SIZE; + if (PageBitmapIndex > Index) + PageBitmapIndex = Index; + } + } + + void Physical::FreePages(void *Address, size_t Count) + { + if (unlikely(Address == nullptr || Count == 0)) + { + warn("%s%s%s passed to FreePages.", Address == nullptr ? "Null pointer " : "", Address == nullptr && Count == 0 ? "and " : "", Count == 0 ? "Zero count" : ""); + return; + } + for (size_t t = 0; t < Count; t++) + this->FreePage((void *)((uintptr_t)Address + (t * PAGE_SIZE))); + } + + void Physical::LockPage(void *Address) + { + if (unlikely(Address == nullptr)) + warn("Trying to lock null address."); + + uintptr_t Index = (uintptr_t)Address / PAGE_SIZE; + + if (unlikely(PageBitmap[Index] == true)) + return; + + if (PageBitmap.Set(Index, true)) + { + FreeMemory -= PAGE_SIZE; + UsedMemory += PAGE_SIZE; + } + } + + void Physical::LockPages(void *Address, size_t PageCount) + { + if (unlikely(Address == nullptr || PageCount == 0)) + warn("Trying to lock %s%s.", Address ? "null address" : "", PageCount ? "0 pages" : ""); + + for (size_t i = 0; i < PageCount; i++) + this->LockPage((void *)((uintptr_t)Address + (i * PAGE_SIZE))); + } + + void Physical::ReservePage(void *Address) + { + if (unlikely(Address == nullptr)) + warn("Trying to reserve null address."); + + uintptr_t Index = (Address == NULL) ? 0 : (uintptr_t)Address / PAGE_SIZE; + + if (unlikely(PageBitmap[Index] == true)) + return; + + if (PageBitmap.Set(Index, true)) + { + FreeMemory -= PAGE_SIZE; + ReservedMemory += PAGE_SIZE; + } + } + + void Physical::ReservePages(void *Address, size_t PageCount) + { + if (unlikely(Address == nullptr || PageCount == 0)) + warn("Trying to reserve %s%s.", Address ? "null address" : "", PageCount ? "0 pages" : ""); + + for (size_t t = 0; t < PageCount; t++) + { + uintptr_t Index = ((uintptr_t)Address + (t * PAGE_SIZE)) / PAGE_SIZE; + + if (unlikely(PageBitmap[Index] == true)) + return; + + if (PageBitmap.Set(Index, true)) + { + FreeMemory -= PAGE_SIZE; + ReservedMemory += PAGE_SIZE; + } + } + } + + void Physical::UnreservePage(void *Address) + { + if (unlikely(Address == nullptr)) + warn("Trying to unreserve null address."); + + uintptr_t Index = (Address == NULL) ? 0 : (uintptr_t)Address / PAGE_SIZE; + + if (unlikely(PageBitmap[Index] == false)) + return; + + if (PageBitmap.Set(Index, false)) + { + FreeMemory += PAGE_SIZE; + ReservedMemory -= PAGE_SIZE; + if (PageBitmapIndex > Index) + PageBitmapIndex = Index; + } + } + + void Physical::UnreservePages(void *Address, size_t PageCount) + { + if (unlikely(Address == nullptr || PageCount == 0)) + warn("Trying to unreserve %s%s.", Address ? "null address" : "", PageCount ? "0 pages" : ""); + + for (size_t t = 0; t < PageCount; t++) + { + uintptr_t Index = ((uintptr_t)Address + (t * PAGE_SIZE)) / PAGE_SIZE; + + if (unlikely(PageBitmap[Index] == false)) + return; + + if (PageBitmap.Set(Index, false)) + { + FreeMemory += PAGE_SIZE; + ReservedMemory -= PAGE_SIZE; + if (PageBitmapIndex > Index) + PageBitmapIndex = Index; + } + } + } + + void Physical::Init(BootInfo *Info) + { + uint64_t MemorySize = Info->Memory.Size; + debug("Memory size: %lld bytes (%ld pages)", MemorySize, TO_PAGES(MemorySize)); + UsedMemory = 0; + ReservedMemory = 0; + TotalMemory = MemorySize; + FreeMemory = MemorySize; + + void *LargestFreeMemorySegment = nullptr; + uint64_t LargestFreeMemorySegmentSize = 0; + + for (uint64_t i = 0; i < Info->Memory.Entries; i++) + { + if (Info->Memory.Entry[i].Type == Usable) + { + if (Info->Memory.Entry[i].Length > LargestFreeMemorySegmentSize) + { + /* We don't want to use 0 as a memory address. */ + if (Info->Memory.Entry[i].BaseAddress == 0x0) + continue; + + LargestFreeMemorySegment = (void *)Info->Memory.Entry[i].BaseAddress; + LargestFreeMemorySegmentSize = Info->Memory.Entry[i].Length; + + debug("Largest free memory segment: %llp (%lldMB)", + (void *)Info->Memory.Entry[i].BaseAddress, + TO_MB(Info->Memory.Entry[i].Length)); + } + } + } + + if (LargestFreeMemorySegment == nullptr) + { + error("No free memory found!"); + while (1) + asmv("cli; hlt"); + } + + size_t BitmapSize = (MemorySize / PAGE_SIZE) / 8 + 1; + void *LargestFreeMemorySegmentNoKernel = nullptr; + + if ((uintptr_t)LargestFreeMemorySegment >= (uintptr_t)&_kernel_start && + (uintptr_t)LargestFreeMemorySegment <= (uintptr_t)&_kernel_end) + { + LargestFreeMemorySegmentNoKernel = (void *)((uintptr_t)LargestFreeMemorySegment + (uintptr_t)&_kernel_end); + if ((uintptr_t)LargestFreeMemorySegmentNoKernel > ((uintptr_t)LargestFreeMemorySegment + LargestFreeMemorySegmentSize)) + { + error("No free memory found!"); + while (1) + asmv("cli; hlt"); + } + } + else + { + LargestFreeMemorySegmentNoKernel = LargestFreeMemorySegment; + } + + debug("Initializing Bitmap at %llp-%llp (%lld Bytes)", + LargestFreeMemorySegmentNoKernel, + (void *)((uintptr_t)LargestFreeMemorySegmentNoKernel + BitmapSize), + BitmapSize); + + PageBitmap.Size = BitmapSize; + PageBitmap.Buffer = (uint8_t *)LargestFreeMemorySegmentNoKernel; + for (size_t i = 0; i < BitmapSize; i++) + *(uint8_t *)(PageBitmap.Buffer + i) = 0; + + debug("Reserving pages..."); + this->ReservePages(0, TO_PAGES(Info->Memory.Size)); + debug("Unreserving usable pages..."); + + for (uint64_t i = 0; i < Info->Memory.Entries; i++) + { + if (Info->Memory.Entry[i].Type == Usable) + this->UnreservePages(Info->Memory.Entry[i].BaseAddress, TO_PAGES(Info->Memory.Entry[i].Length)); + } + + debug("Reserving bitmap pages..."); + this->ReservePages(PageBitmap.Buffer, TO_PAGES(PageBitmap.Size)); + debug("Reserving kernel..."); + this->ReservePages(&_kernel_start, TO_PAGES((uintptr_t)&_kernel_end - (uintptr_t)&_kernel_start)); + debug("Reserving kernel and its modules..."); + + BootInfo::ModuleInfo *Module = &Info->Modules[0]; + int ModuleIndex = 0; + do + { + debug("Reserving %s (%p-%p)", Module->CommandLine, Module->Address, (void *)((uintptr_t)Module->Address + Module->Size)); + this->ReservePages(Module->Address, TO_PAGES(Module->Size)); + Module = &Info->Modules[++ModuleIndex]; + } while (Module->Address); + + this->ReservePages(0, 16); + } + + Physical::Physical() {} + Physical::~Physical() {} +} + +namespace Memory64 +{ + void *Physical::RequestPage() + { + for (; PageBitmapIndex < PageBitmap.Size * 8; PageBitmapIndex++) + { + if (PageBitmap[PageBitmapIndex] == true) + continue; + + this->LockPage((void *)(PageBitmapIndex * PAGE_SIZE)); + return (void *)(PageBitmapIndex * PAGE_SIZE); + } + + error("Out of memory! (Free: %ldMB; Used: %ldMB; Reserved: %ldMB)", TO_MB(FreeMemory), TO_MB(UsedMemory), TO_MB(ReservedMemory)); + while (1) + asmv("cli; hlt"); + __builtin_unreachable(); + } + + void *Physical::RequestPages(size_t Count) + { + for (; PageBitmapIndex < PageBitmap.Size * 8; PageBitmapIndex++) + { + if (PageBitmap[PageBitmapIndex] == true) + continue; + + for (uint64_t Index = PageBitmapIndex; Index < PageBitmap.Size * 8; Index++) + { + if (PageBitmap[Index] == true) + continue; + + for (size_t i = 0; i < Count; i++) + { + if (PageBitmap[Index + i] == true) + goto NextPage; + } + + this->LockPages((void *)(Index * PAGE_SIZE), Count); + return (void *)(Index * PAGE_SIZE); + + NextPage: + Index += Count; + continue; + } + } + + error("Out of memory! (Free: %ldMB; Used: %ldMB; Reserved: %ldMB)", TO_MB(FreeMemory), TO_MB(UsedMemory), TO_MB(ReservedMemory)); + while (1) + asmv("cli; hlt"); + __builtin_unreachable(); + } + + void Physical::FreePage(void *Address) + { + if (unlikely(Address == nullptr)) + { + warn("Null pointer passed to FreePage."); + return; + } + + size_t Index = (size_t)Address / PAGE_SIZE; + + if (unlikely(PageBitmap[Index] == false)) + { + warn("Tried to free an already free page. (%p)", Address); + return; + } + + if (PageBitmap.Set(Index, false)) + { + FreeMemory += PAGE_SIZE; + UsedMemory -= PAGE_SIZE; + if (PageBitmapIndex > Index) + PageBitmapIndex = Index; + } + } + + void Physical::FreePages(void *Address, size_t Count) + { + if (unlikely(Address == nullptr || Count == 0)) + { + warn("%s%s%s passed to FreePages.", Address == nullptr ? "Null pointer " : "", Address == nullptr && Count == 0 ? "and " : "", Count == 0 ? "Zero count" : ""); + return; + } + for (size_t t = 0; t < Count; t++) + this->FreePage((void *)((uintptr_t)Address + (t * PAGE_SIZE))); + } + + void Physical::LockPage(void *Address) + { + if (unlikely(Address == nullptr)) + warn("Trying to lock null address."); + + uintptr_t Index = (uintptr_t)Address / PAGE_SIZE; + + if (unlikely(PageBitmap[Index] == true)) + return; + + if (PageBitmap.Set(Index, true)) + { + FreeMemory -= PAGE_SIZE; + UsedMemory += PAGE_SIZE; + } + } + + void Physical::LockPages(void *Address, size_t PageCount) + { + if (unlikely(Address == nullptr || PageCount == 0)) + warn("Trying to lock %s%s.", Address ? "null address" : "", PageCount ? "0 pages" : ""); + + for (size_t i = 0; i < PageCount; i++) + this->LockPage((void *)((uintptr_t)Address + (i * PAGE_SIZE))); + } + + void Physical::ReservePage(void *Address) + { + if (unlikely(Address == nullptr)) + warn("Trying to reserve null address."); + + uintptr_t Index = (Address == NULL) ? 0 : (uintptr_t)Address / PAGE_SIZE; + + if (unlikely(PageBitmap[Index] == true)) + return; + + if (PageBitmap.Set(Index, true)) + { + FreeMemory -= PAGE_SIZE; + ReservedMemory += PAGE_SIZE; + } + } + + void Physical::ReservePages(void *Address, size_t PageCount) + { + if (unlikely(Address == nullptr || PageCount == 0)) + warn("Trying to reserve %s%s.", Address ? "null address" : "", PageCount ? "0 pages" : ""); + + for (size_t t = 0; t < PageCount; t++) + { + uintptr_t Index = ((uintptr_t)Address + (t * PAGE_SIZE)) / PAGE_SIZE; + + if (unlikely(PageBitmap[Index] == true)) + return; + + if (PageBitmap.Set(Index, true)) + { + FreeMemory -= PAGE_SIZE; + ReservedMemory += PAGE_SIZE; + } + } + } + + void Physical::UnreservePage(void *Address) + { + if (unlikely(Address == nullptr)) + warn("Trying to unreserve null address."); + + uintptr_t Index = (Address == NULL) ? 0 : (uintptr_t)Address / PAGE_SIZE; + + if (unlikely(PageBitmap[Index] == false)) + return; + + if (PageBitmap.Set(Index, false)) + { + FreeMemory += PAGE_SIZE; + ReservedMemory -= PAGE_SIZE; + if (PageBitmapIndex > Index) + PageBitmapIndex = Index; + } + } + + void Physical::UnreservePages(void *Address, size_t PageCount) + { + if (unlikely(Address == nullptr || PageCount == 0)) + warn("Trying to unreserve %s%s.", Address ? "null address" : "", PageCount ? "0 pages" : ""); + + for (size_t t = 0; t < PageCount; t++) + { + uintptr_t Index = ((uintptr_t)Address + (t * PAGE_SIZE)) / PAGE_SIZE; + + if (unlikely(PageBitmap[Index] == false)) + return; + + if (PageBitmap.Set(Index, false)) + { + FreeMemory += PAGE_SIZE; + ReservedMemory -= PAGE_SIZE; + if (PageBitmapIndex > Index) + PageBitmapIndex = Index; + } + } + } + + void Physical::Init(BootInfo *Info) + { + uint64_t MemorySize = Info->Memory.Size; + debug("Memory size: %lld bytes (%ld pages)", MemorySize, TO_PAGES(MemorySize)); + UsedMemory = 0; + ReservedMemory = 0; + TotalMemory = MemorySize; + FreeMemory = MemorySize; + + void *LargestFreeMemorySegment = nullptr; + uint64_t LargestFreeMemorySegmentSize = 0; + + for (uint64_t i = 0; i < Info->Memory.Entries; i++) + { + if (Info->Memory.Entry[i].Type == Usable) + { + if (Info->Memory.Entry[i].Length > LargestFreeMemorySegmentSize) + { + /* We don't want to use 0 as a memory address. */ + if (Info->Memory.Entry[i].BaseAddress == 0x0) + continue; + + LargestFreeMemorySegment = (void *)Info->Memory.Entry[i].BaseAddress; + LargestFreeMemorySegmentSize = Info->Memory.Entry[i].Length; + + debug("Largest free memory segment: %llp (%lldMB)", + (void *)Info->Memory.Entry[i].BaseAddress, + TO_MB(Info->Memory.Entry[i].Length)); + } + } + } + + if (LargestFreeMemorySegment == nullptr) + { + error("No free memory found!"); + while (1) + asmv("cli; hlt"); + } + + size_t BitmapSize = (MemorySize / PAGE_SIZE) / 8 + 1; + void *LargestFreeMemorySegmentNoKernel = nullptr; + + if ((uintptr_t)LargestFreeMemorySegment >= (uintptr_t)&_kernel_start && + (uintptr_t)LargestFreeMemorySegment <= (uintptr_t)&_kernel_end) + { + LargestFreeMemorySegmentNoKernel = (void *)((uintptr_t)LargestFreeMemorySegment + (uintptr_t)&_kernel_end); + if ((uintptr_t)LargestFreeMemorySegmentNoKernel > ((uintptr_t)LargestFreeMemorySegment + LargestFreeMemorySegmentSize)) + { + error("No free memory found!"); + while (1) + asmv("cli; hlt"); + } + } + else + { + LargestFreeMemorySegmentNoKernel = LargestFreeMemorySegment; + } + + debug("Initializing Bitmap at %llp-%llp (%lld Bytes)", + LargestFreeMemorySegmentNoKernel, + (void *)((uintptr_t)LargestFreeMemorySegmentNoKernel + BitmapSize), + BitmapSize); + + PageBitmap.Size = BitmapSize; + PageBitmap.Buffer = (uint8_t *)LargestFreeMemorySegmentNoKernel; + for (size_t i = 0; i < BitmapSize; i++) + *(uint8_t *)(PageBitmap.Buffer + i) = 0; + + debug("Reserving pages..."); + this->ReservePages(0, TO_PAGES(Info->Memory.Size)); + debug("Unreserving usable pages..."); + + for (uint64_t i = 0; i < Info->Memory.Entries; i++) + { + if (Info->Memory.Entry[i].Type == Usable) + this->UnreservePages(Info->Memory.Entry[i].BaseAddress, TO_PAGES(Info->Memory.Entry[i].Length)); + } + + debug("Reserving bitmap pages..."); + this->ReservePages(PageBitmap.Buffer, TO_PAGES(PageBitmap.Size)); + debug("Reserving fennix loader..."); + this->ReservePages(&_kernel_start, TO_PAGES((uintptr_t)&_kernel_end - (uintptr_t)&_kernel_start)); + debug("Reserving kernel and its modules..."); + + BootInfo::ModuleInfo *Module = &Info->Modules[0]; + int ModuleIndex = 0; + do + { + debug("Reserving %s (%p-%p)", Module->CommandLine, Module->Address, (void *)((uintptr_t)Module->Address + Module->Size)); + this->ReservePages(Module->Address, TO_PAGES(Module->Size)); + Module = &Info->Modules[++ModuleIndex]; + } while (Module->Address); + this->ReservePages(0, 16); + } + + Physical::Physical() {} + Physical::~Physical() {} +} diff --git a/FennixLoader/Memory/Setup.asm b/FennixLoader/Memory/Setup.asm new file mode 100644 index 00000000..5390768c --- /dev/null +++ b/FennixLoader/Memory/Setup.asm @@ -0,0 +1,8 @@ +[bits 32] +section .text +global EnablePaging +EnablePaging: + mov ecx, cr0 + or ecx, 0x80000000 ; Set PG in CR0 + mov cr0, ecx + ret diff --git a/FennixLoader/Memory/VirtualMemoryManager.cpp b/FennixLoader/Memory/VirtualMemoryManager.cpp new file mode 100644 index 00000000..c990c9e8 --- /dev/null +++ b/FennixLoader/Memory/VirtualMemoryManager.cpp @@ -0,0 +1,316 @@ +#include +#include + +namespace Memory32 +{ + void Virtual::Map(void *VirtualAddress, void *PhysicalAddress, uint64_t Flags) + { + if (unlikely(!this->Table)) + { + error("No page table"); + return; + } + + Flags |= PTFlag::P; + + PageMapIndexer Index = PageMapIndexer((uintptr_t)VirtualAddress); + // Clear any flags that are not 1 << 0 (Present) - 1 << 5 (Accessed) because rest are for page table entries only + uint64_t DirectoryFlags = Flags & 0x3F; + + PageDirectoryEntry *PDE = &this->Table->Entries[Index.PDEIndex]; + PageTableEntryPtr *PTEPtr = nullptr; + if (!PDE->Present) + { + PTEPtr = (PageTableEntryPtr *)KernelAllocator32.RequestPages(TO_PAGES(sizeof(PageTableEntryPtr) + 1)); + memset(PTEPtr, 0, sizeof(PageTableEntryPtr)); + PDE->Present = true; + PDE->SetAddress((uintptr_t)PTEPtr >> 12); + } + else + PTEPtr = (PageTableEntryPtr *)(PDE->GetAddress() << 12); + PDE->raw |= DirectoryFlags; + + PageTableEntry *PTE = &PTEPtr->Entries[Index.PTEIndex]; + PTE->Present = true; + PTE->raw |= Flags; + PTE->SetAddress((uintptr_t)PhysicalAddress >> 12); + + asmv("invlpg (%0)" ::"r"(VirtualAddress) + : "memory"); + } + + Virtual::Virtual(PageTable *Table) + { + if (Table) + this->Table = Table; + else + { + asmv("movl %%cr3, %0" + : "=r"(this->Table)); + } + } + + Virtual::~Virtual() {} +} + +namespace Memory64 +{ + bool Virtual::Check(void *VirtualAddress, PTFlag Flag, MapType Type) + { + // 0x1000 aligned + uintptr_t Address = (uintptr_t)VirtualAddress; + Address &= 0xFFFFFFFFFFFFF000; + + PageMapIndexer Index = PageMapIndexer(Address); + PageMapLevel4 PML4 = this->Table->Entries[Index.PMLIndex]; + + PageDirectoryPointerTableEntryPtr *PDPTE = nullptr; + PageDirectoryEntryPtr *PDE = nullptr; + PageTableEntryPtr *PTE = nullptr; + + if ((PML4.raw & Flag) > 0) + { + PDPTE = (PageDirectoryPointerTableEntryPtr *)((uintptr_t)PML4.GetAddress() << 12); + if (PDPTE) + { + if ((PDPTE->Entries[Index.PDPTEIndex].Present)) + { + if (Type == MapType::OneGB && PDPTE->Entries[Index.PDPTEIndex].PageSize) + return true; + + PDE = (PageDirectoryEntryPtr *)((uintptr_t)PDPTE->Entries[Index.PDPTEIndex].GetAddress() << 12); + if (PDE) + { + if (Type == MapType::TwoMB && PDE->Entries[Index.PDEIndex].PageSize) + return true; + + if ((PDE->Entries[Index.PDEIndex].Present)) + { + PTE = (PageTableEntryPtr *)((uintptr_t)PDE->Entries[Index.PDEIndex].GetAddress() << 12); + if (PTE) + { + if ((PTE->Entries[Index.PTEIndex].Present)) + return true; + } + } + } + } + } + } + return false; + } + + void *Virtual::GetPhysical(void *VirtualAddress) + { + // 0x1000 aligned + uintptr_t Address = (uintptr_t)VirtualAddress; + Address &= 0xFFFFFFFFFFFFF000; + + PageMapIndexer Index = PageMapIndexer(Address); + PageMapLevel4 PML4 = this->Table->Entries[Index.PMLIndex]; + + PageDirectoryPointerTableEntryPtr *PDPTE = nullptr; + PageDirectoryEntryPtr *PDE = nullptr; + PageTableEntryPtr *PTE = nullptr; + + if (PML4.Present) + { + PDPTE = (PageDirectoryPointerTableEntryPtr *)((uintptr_t)PML4.GetAddress() << 12); + if (PDPTE) + { + if (PDPTE->Entries[Index.PDPTEIndex].Present) + { + if (PDPTE->Entries[Index.PDPTEIndex].PageSize) + return (void *)((uintptr_t)PDPTE->Entries[Index.PDPTEIndex].GetAddress() << 12); + + PDE = (PageDirectoryEntryPtr *)((uintptr_t)PDPTE->Entries[Index.PDPTEIndex].GetAddress() << 12); + if (PDE) + { + if (PDE->Entries[Index.PDEIndex].Present) + { + if (PDE->Entries[Index.PDEIndex].PageSize) + return (void *)((uintptr_t)PDE->Entries[Index.PDEIndex].GetAddress() << 12); + + PTE = (PageTableEntryPtr *)((uintptr_t)PDE->Entries[Index.PDEIndex].GetAddress() << 12); + if (PTE) + { + if (PTE->Entries[Index.PTEIndex].Present) + return (void *)((uintptr_t)PTE->Entries[Index.PTEIndex].GetAddress() << 12); + } + } + } + } + } + } + return nullptr; + } + + void Virtual::Map(void *VirtualAddress, void *PhysicalAddress, uint64_t Flags, MapType Type) + { + if (unlikely(!this->Table)) + { + error("No page table"); + return; + } + + Flags |= PTFlag::P; + + PageMapIndexer Index = PageMapIndexer((uintptr_t)VirtualAddress); + // Clear any flags that are not 1 << 0 (Present) - 1 << 5 (Accessed) because rest are for page table entries only + uint64_t DirectoryFlags = Flags & 0x3F; + + PageMapLevel4 *PML4 = &this->Table->Entries[Index.PMLIndex]; + PageDirectoryPointerTableEntryPtr *PDPTEPtr = nullptr; + if (!PML4->Present) + { + PDPTEPtr = (PageDirectoryPointerTableEntryPtr *)KernelAllocator64.RequestPages(TO_PAGES(sizeof(PageDirectoryPointerTableEntryPtr) + 1)); + memset(PDPTEPtr, 0, sizeof(PageDirectoryPointerTableEntryPtr)); + PML4->Present = true; + PML4->SetAddress((uintptr_t)PDPTEPtr >> 12); + } + else + PDPTEPtr = (PageDirectoryPointerTableEntryPtr *)(PML4->GetAddress() << 12); + PML4->raw |= DirectoryFlags; + + PageDirectoryPointerTableEntry *PDPTE = &PDPTEPtr->Entries[Index.PDPTEIndex]; + if (Type == MapType::OneGB) + { + PDPTE->raw |= Flags; + PDPTE->PageSize = true; + PDPTE->SetAddress((uintptr_t)PhysicalAddress >> 12); + debug("Mapped 1GB page at %p to %p", VirtualAddress, PhysicalAddress); + return; + } + + PageDirectoryEntryPtr *PDEPtr = nullptr; + if (!PDPTE->Present) + { + PDEPtr = (PageDirectoryEntryPtr *)KernelAllocator64.RequestPages(TO_PAGES(sizeof(PageDirectoryEntryPtr) + 1)); + memset(PDEPtr, 0, sizeof(PageDirectoryEntryPtr)); + PDPTE->Present = true; + PDPTE->SetAddress((uintptr_t)PDEPtr >> 12); + } + else + PDEPtr = (PageDirectoryEntryPtr *)(PDPTE->GetAddress() << 12); + PDPTE->raw |= DirectoryFlags; + + PageDirectoryEntry *PDE = &PDEPtr->Entries[Index.PDEIndex]; + if (Type == MapType::TwoMB) + { + PDE->raw |= Flags; + PDE->PageSize = true; + PDE->SetAddress((uintptr_t)PhysicalAddress >> 12); + debug("Mapped 2MB page at %p to %p", VirtualAddress, PhysicalAddress); + return; + } + + PageTableEntryPtr *PTEPtr = nullptr; + if (!PDE->Present) + { + PTEPtr = (PageTableEntryPtr *)KernelAllocator64.RequestPages(TO_PAGES(sizeof(PageTableEntryPtr) + 1)); + memset(PTEPtr, 0, sizeof(PageTableEntryPtr)); + PDE->Present = true; + PDE->SetAddress((uintptr_t)PTEPtr >> 12); + } + else + PTEPtr = (PageTableEntryPtr *)(PDE->GetAddress() << 12); + PDE->raw |= DirectoryFlags; + + PageTableEntry *PTE = &PTEPtr->Entries[Index.PTEIndex]; + PTE->Present = true; + PTE->raw |= Flags; + PTE->SetAddress((uintptr_t)PhysicalAddress >> 12); + + asmv("invlpg (%0)" ::"r"(VirtualAddress) + : "memory"); + +#ifdef DEBUG +/* https://stackoverflow.com/a/3208376/9352057 */ +#define BYTE_TO_BINARY_PATTERN "%c%c%c%c%c%c%c%c" +#define BYTE_TO_BINARY(byte) \ + (byte & 0x80 ? '1' : '0'), \ + (byte & 0x40 ? '1' : '0'), \ + (byte & 0x20 ? '1' : '0'), \ + (byte & 0x10 ? '1' : '0'), \ + (byte & 0x08 ? '1' : '0'), \ + (byte & 0x04 ? '1' : '0'), \ + (byte & 0x02 ? '1' : '0'), \ + (byte & 0x01 ? '1' : '0') + + if (!this->Check(VirtualAddress, (PTFlag)Flags, Type)) // quick workaround just to see where it fails + warn("Failed to map v:%#lx p:%#lx with flags: " BYTE_TO_BINARY_PATTERN, VirtualAddress, PhysicalAddress, BYTE_TO_BINARY(Flags)); +#endif + } + + void Virtual::Unmap(void *VirtualAddress, MapType Type) + { + if (!this->Table) + { + error("No page table"); + return; + } + + PageMapIndexer Index = PageMapIndexer((uintptr_t)VirtualAddress); + PageMapLevel4 *PML4 = &this->Table->Entries[Index.PMLIndex]; + if (!PML4->Present) + { + error("Page %#lx not present", PML4->GetAddress()); + return; + } + + PageDirectoryPointerTableEntryPtr *PDPTEPtr = (PageDirectoryPointerTableEntryPtr *)((uintptr_t)PML4->Address << 12); + PageDirectoryPointerTableEntry *PDPTE = &PDPTEPtr->Entries[Index.PDPTEIndex]; + if (!PDPTE->Present) + { + error("Page %#lx not present", PDPTE->GetAddress()); + return; + } + + if (Type == MapType::OneGB && PDPTE->PageSize) + { + PDPTE->Present = false; + return; + } + + PageDirectoryEntryPtr *PDEPtr = (PageDirectoryEntryPtr *)((uintptr_t)PDPTE->Address << 12); + PageDirectoryEntry *PDE = &PDEPtr->Entries[Index.PDEIndex]; + if (!PDE->Present) + { + error("Page %#lx not present", PDE->GetAddress()); + return; + } + + if (Type == MapType::TwoMB && PDE->PageSize) + { + PDE->Present = false; + return; + } + + PageTableEntryPtr *PTEPtr = (PageTableEntryPtr *)((uintptr_t)PDE->Address << 12); + PageTableEntry PTE = PTEPtr->Entries[Index.PTEIndex]; + if (!PTE.Present) + { + error("Page %#lx not present", PTE.GetAddress()); + return; + } + + PTE.Present = false; + PTEPtr->Entries[Index.PTEIndex] = PTE; + + asmv("invlpg (%0)" ::"r"(VirtualAddress) + : "memory"); + } + + Virtual::Virtual(PageTable *Table) + { + if (Table) + this->Table = Table; + else + { + asmv("movl %%cr3, %0" // FIXME: movq + : "=r"(this->Table)); + } + } + + Virtual::~Virtual() {} +} diff --git a/FennixLoader/Memory/memop.cpp b/FennixLoader/Memory/memop.cpp new file mode 100644 index 00000000..393fa85d --- /dev/null +++ b/FennixLoader/Memory/memop.cpp @@ -0,0 +1,18 @@ +#include + +void *memcpy(void *dest, const void *src, size_t n) +{ + unsigned char *d = (unsigned char *)dest; + const unsigned char *s = (const unsigned char *)src; + while (n--) + *d++ = *s++; + return dest; +} + +void *memset(void *s, int c, size_t n) +{ + unsigned char *p = (unsigned char *)s; + while (n--) + *p++ = (unsigned char)c; + return s; +} diff --git a/FennixLoader/UndefinedBehaviorSanitization.c b/FennixLoader/UndefinedBehaviorSanitization.c new file mode 100644 index 00000000..8052522f --- /dev/null +++ b/FennixLoader/UndefinedBehaviorSanitization.c @@ -0,0 +1,513 @@ +#include "ubsan.h" +#include + +extern void __asan_report_load1(void *unknown) +{ + ubsan("load1"); + UNUSED(unknown); +} + +extern void __asan_report_load2(void *unknown) +{ + ubsan("load2"); + UNUSED(unknown); +} + +extern void __asan_report_load4(void *unknown) +{ + ubsan("load4"); + UNUSED(unknown); +} + +extern void __asan_report_load8(void *unknown) +{ + ubsan("load8"); + UNUSED(unknown); +} + +extern void __asan_report_load16(void *unknown) +{ + ubsan("load16"); + UNUSED(unknown); +} + +extern void __asan_report_load_n(void *unknown, uintptr_t size) +{ + ubsan("loadn"); + UNUSED(unknown); + UNUSED(size); +} + +extern void __asan_report_store1(void *unknown) +{ + ubsan("store1"); + UNUSED(unknown); +} + +extern void __asan_report_store2(void *unknown) +{ + ubsan("store2"); + UNUSED(unknown); +} + +extern void __asan_report_store4(void *unknown) +{ + ubsan("store4"); + UNUSED(unknown); +} + +extern void __asan_report_store8(void *unknown) +{ + ubsan("store8"); + UNUSED(unknown); +} + +extern void __asan_report_store16(void *unknown) +{ + ubsan("store16"); + UNUSED(unknown); +} + +extern void __asan_report_store_n(void *unknown, uintptr_t size) +{ + ubsan("storen"); + UNUSED(unknown); + UNUSED(size); +} + +extern void __asan_report_load1_noabort(void *unknown) +{ + ubsan("load1"); + UNUSED(unknown); +} + +extern void __asan_report_load2_noabort(void *unknown) +{ + ubsan("load2"); + UNUSED(unknown); +} + +extern void __asan_report_load4_noabort(void *unknown) +{ + ubsan("load4"); + UNUSED(unknown); +} + +extern void __asan_report_load8_noabort(void *unknown) +{ + ubsan("load8"); + UNUSED(unknown); +} + +extern void __asan_report_load16_noabort(void *unknown) +{ + ubsan("load16"); + UNUSED(unknown); +} + +extern void __asan_report_load_n_noabort(void *unknown, uintptr_t size) +{ + ubsan("loadn"); + UNUSED(unknown); + UNUSED(size); +} + +extern void __asan_report_store1_noabort(void *unknown) +{ + ubsan("store1"); + UNUSED(unknown); +} + +extern void __asan_report_store2_noabort(void *unknown) +{ + ubsan("store2"); + UNUSED(unknown); +} + +extern void __asan_report_store4_noabort(void *unknown) +{ + ubsan("store4"); + UNUSED(unknown); +} + +extern void __asan_report_store8_noabort(void *unknown) +{ + ubsan("store8"); + UNUSED(unknown); +} + +extern void __asan_report_store16_noabort(void *unknown) +{ + ubsan("store16"); + UNUSED(unknown); +} + +extern void __asan_report_store_n_noabort(void *unknown, uintptr_t size) +{ + ubsan("storen"); + UNUSED(unknown); + UNUSED(size); +} + +extern void __asan_stack_malloc_0(uintptr_t size) +{ + ubsan("stack malloc 0"); + UNUSED(size); +} + +extern void __asan_stack_malloc_1(uintptr_t size) +{ + ubsan("stack malloc 1"); + UNUSED(size); +} + +extern void __asan_stack_malloc_2(uintptr_t size) +{ + ubsan("stack malloc 2"); + UNUSED(size); +} + +extern void __asan_stack_malloc_3(uintptr_t size) +{ + ubsan("stack malloc 3"); + UNUSED(size); +} + +extern void __asan_stack_malloc_4(uintptr_t size) +{ + ubsan("stack malloc 4"); + UNUSED(size); +} + +extern void __asan_stack_malloc_5(uintptr_t size) +{ + ubsan("stack malloc 5"); + UNUSED(size); +} + +extern void __asan_stack_malloc_6(uintptr_t size) +{ + ubsan("stack malloc 6"); + UNUSED(size); +} + +extern void __asan_stack_malloc_7(uintptr_t size) +{ + ubsan("stack malloc 7"); + UNUSED(size); +} + +extern void __asan_stack_malloc_8(uintptr_t size) +{ + ubsan("stack malloc 8"); + UNUSED(size); +} + +extern void __asan_stack_malloc_9(uintptr_t size) +{ + ubsan("stack malloc 9"); + UNUSED(size); +} + +extern void __asan_stack_free_0(void *ptr, uintptr_t size) +{ + ubsan("stack free 0"); + UNUSED(ptr); + UNUSED(size); +} + +extern void __asan_stack_free_1(void *ptr, uintptr_t size) +{ + ubsan("stack free 1"); + UNUSED(ptr); + UNUSED(size); +} + +extern void __asan_stack_free_2(void *ptr, uintptr_t size) +{ + ubsan("stack free 2"); + UNUSED(ptr); + UNUSED(size); +} + +extern void __asan_stack_free_3(void *ptr, uintptr_t size) +{ + ubsan("stack free 3"); + UNUSED(ptr); + UNUSED(size); +} + +extern void __asan_stack_free_4(void *ptr, uintptr_t size) +{ + ubsan("stack free 4"); + UNUSED(ptr); + UNUSED(size); +} + +extern void __asan_stack_free_5(void *ptr, uintptr_t size) +{ + ubsan("stack free 5"); + UNUSED(ptr); + UNUSED(size); +} + +extern void __asan_stack_free_6(void *ptr, uintptr_t size) +{ + ubsan("stack free 6"); + UNUSED(ptr); + UNUSED(size); +} + +extern void __asan_stack_free_7(void *ptr, uintptr_t size) +{ + ubsan("stack free 7"); + UNUSED(ptr); + UNUSED(size); +} + +extern void __asan_stack_free_8(void *ptr, uintptr_t size) +{ + ubsan("stack free 8"); + UNUSED(ptr); + UNUSED(size); +} + +extern void __asan_stack_free_9(void *ptr, uintptr_t size) +{ + ubsan("stack free 9"); + UNUSED(ptr); + UNUSED(size); +} + +extern void __asan_poison_stack_memory(void *addr, uintptr_t size) +{ + ubsan("poison stack memory"); + UNUSED(addr); + UNUSED(size); +} + +extern void __asan_unpoison_stack_memory(void *addr, uintptr_t size) +{ + ubsan("unpoison stack memory"); + UNUSED(addr); + UNUSED(size); +} + +extern void __asan_before_dynamic_init(const char *module_name) +{ + ubsan("before dynamic init"); + UNUSED(module_name); +} + +extern void __asan_after_dynamic_init(void) { ubsan("after dynamic init"); } + +extern void __asan_register_globals(void *unknown, size_t size) +{ + ubsan("register_globals"); + UNUSED(unknown); + UNUSED(size); +} + +extern void __asan_unregister_globals(void) { ubsan("unregister_globals"); } + +extern void __asan_init(void) { ubsan("init"); } +extern void __asan_version_mismatch_check_v8(void) { ubsan("version_mismatch_check_v8"); } +extern void __asan_option_detect_stack_use_after_return(void) { ubsan("stack use after return"); } + +extern __noreturn void __asan_handle_no_return(void) +{ + ubsan("no_return"); + while (1) + asmv("cli; hlt"); +} + +#define is_aligned(value, alignment) !(value & (alignment - 1)) + +const char *Type_Check_Kinds[] = { + "Load of", + "Store to", + "Reference binding to", + "Member access within", + "Member call on", + "Constructor call on", + "Downcast of", + "Downcast of", + "Upcast of", + "Cast to virtual base of", +}; + +bool UBSANMsg(const char *file, uint32_t line, uint32_t column) +{ + ubsan("\t\tIn File: %s:%i:%i", file, line, column); + return true; +} + +void __ubsan_handle_type_mismatch_v1(struct type_mismatch_v1_data *type_mismatch, uintptr_t pointer) +{ + struct source_location *location = &type_mismatch->location; + if (pointer == 0) + { + if (UBSANMsg(location->file, location->line, location->column)) + { + ubsan("Null pointer access."); + } + } + else if (type_mismatch->alignment != 0 && is_aligned(pointer, type_mismatch->alignment)) + { + if (UBSANMsg(location->file, location->line, location->column)) + { + ubsan("Unaligned memory access %#llx.", pointer); + } + } + else + { + if (UBSANMsg(location->file, location->line, location->column)) + { + ubsan("%s address %#llx with insufficient space for object of type %s", + Type_Check_Kinds[type_mismatch->type_check_kind], (void *)pointer, type_mismatch->type->name); + } + } +} + +void __ubsan_handle_add_overflow(struct overflow_data *data) +{ + if (UBSANMsg(data->location.file, data->location.line, data->location.column)) + { + ubsan("Addition overflow."); + } +} + +void __ubsan_handle_sub_overflow(struct overflow_data *data) +{ + if (UBSANMsg(data->location.file, data->location.line, data->location.column)) + { + ubsan("Subtraction overflow."); + } +} + +void __ubsan_handle_mul_overflow(struct overflow_data *data) +{ + if (UBSANMsg(data->location.file, data->location.line, data->location.column)) + { + ubsan("Multiplication overflow."); + } +} + +void __ubsan_handle_divrem_overflow(struct overflow_data *data) +{ + if (UBSANMsg(data->location.file, data->location.line, data->location.column)) + { + ubsan("Division overflow."); + } +} + +void __ubsan_handle_negate_overflow(struct overflow_data *data) +{ + if (UBSANMsg(data->location.file, data->location.line, data->location.column)) + { + ubsan("Negation overflow."); + } +} + +void __ubsan_handle_pointer_overflow(struct overflow_data *data) +{ + if (UBSANMsg(data->location.file, data->location.line, data->location.column)) + { + ubsan("Pointer overflow."); + } +} + +void __ubsan_handle_shift_out_of_bounds(struct shift_out_of_bounds_data *data) +{ + if (UBSANMsg(data->location.file, data->location.line, data->location.column)) + { + ubsan("Shift out of bounds."); + } +} + +void __ubsan_handle_load_invalid_value(struct invalid_value_data *data) +{ + if (UBSANMsg(data->location.file, data->location.line, data->location.column)) + { + ubsan("Invalid load value."); + } +} + +void __ubsan_handle_out_of_bounds(struct array_out_of_bounds_data *data) +{ + if (UBSANMsg(data->location.file, data->location.line, data->location.column)) + { + ubsan("Array out of bounds."); + } +} + +void __ubsan_handle_vla_bound_not_positive(struct negative_vla_data *data) +{ + if (UBSANMsg(data->location.file, data->location.line, data->location.column)) + { + ubsan("Variable-length argument is negative."); + } +} + +void __ubsan_handle_nonnull_return(struct nonnull_return_data *data) +{ + if (UBSANMsg(data->location.file, data->location.line, data->location.column)) + { + ubsan("Non-null return is null."); + } +} + +void __ubsan_handle_nonnull_return_v1(struct nonnull_return_data *data) +{ + if (UBSANMsg(data->location.file, data->location.line, data->location.column)) + { + ubsan("Non-null return is null."); + } +} + +void __ubsan_handle_nonnull_arg(struct nonnull_arg_data *data) +{ + if (UBSANMsg(data->location.file, data->location.line, data->location.column)) + { + ubsan("Non-null argument is null."); + } +} + +void __ubsan_handle_builtin_unreachable(struct unreachable_data *data) +{ + if (UBSANMsg(data->location.file, data->location.line, data->location.column)) + { + ubsan("Unreachable code reached."); + } +} + +void __ubsan_handle_invalid_builtin(struct invalid_builtin_data *data) +{ + if (UBSANMsg(data->location.file, data->location.line, data->location.column)) + { + ubsan("Invalid builtin."); + } +} + +void __ubsan_handle_missing_return(struct unreachable_data *data) +{ + if (UBSANMsg(data->location.file, data->location.line, data->location.column)) + { + ubsan("Missing return."); + } +} + +void __ubsan_vptr_type_cache(uintptr_t *cache, uintptr_t ptr) +{ + ubsan("Vptr type cache."); + *cache = ptr; +} + +void __ubsan_handle_dynamic_type_cache_miss(struct dynamic_type_cache_miss_data *data, uintptr_t ptr) +{ + if (UBSANMsg(data->location.file, data->location.line, data->location.column)) + { + ubsan("Dynamic type cache miss."); + } + UNUSED(ptr); +} diff --git a/FennixLoader/UniversalAsynchronousReceiverTransmitter.cpp b/FennixLoader/UniversalAsynchronousReceiverTransmitter.cpp new file mode 100644 index 00000000..7010ed8f --- /dev/null +++ b/FennixLoader/UniversalAsynchronousReceiverTransmitter.cpp @@ -0,0 +1,116 @@ +#include +#include + +volatile bool serialports[8] = {false, false, false, false, false, false, false, false}; + + +__always_inline inline uint8_t inportb(uint16_t Port) +{ + uint8_t Result; + asm("in %%dx, %%al" + : "=a"(Result) + : "d"(Port)); + return Result; +} + +__always_inline inline void outportb(uint16_t Port, uint8_t Data) +{ + asmv("out %%al, %%dx" + : + : "a"(Data), "d"(Port)); +} + +namespace UniversalAsynchronousReceiverTransmitter +{ +#define SERIAL_ENABLE_DLAB 0x80 +#define SERIAL_RATE_115200_LO 0x01 +#define SERIAL_RATE_115200_HI 0x00 +#define SERIAL_RATE_57600_LO 0x02 +#define SERIAL_RATE_57600_HI 0x00 +#define SERIAL_RATE_38400_LO 0x03 +#define SERIAL_RATE_38400_HI 0x00 +#define SERIAL_BUFFER_EMPTY 0x20 + + /* TODO: Serial Port implementation needs reword. https://wiki.osdev.org/Serial_Ports */ + + SafeFunction UART::UART(SerialPorts Port) + { + if (Port == COMNULL) + return; + + this->Port = Port; + int PortNumber = 0; + + switch (Port) + { + case COM1: + PortNumber = 0; + break; + case COM2: + PortNumber = 1; + break; + case COM3: + PortNumber = 2; + break; + case COM4: + PortNumber = 3; + break; + case COM5: + PortNumber = 4; + break; + case COM6: + PortNumber = 5; + break; + case COM7: + PortNumber = 6; + break; + case COM8: + PortNumber = 7; + break; + default: + return; + } + + if (serialports[PortNumber]) + return; + + // Initialize the serial port + outportb(s_cst(uint16_t, Port + 1), 0x00); // Disable all interrupts + outportb(s_cst(uint16_t, Port + 3), SERIAL_ENABLE_DLAB); // Enable DLAB (set baud rate divisor) + outportb(s_cst(uint16_t, Port + 0), SERIAL_RATE_115200_LO); // Set divisor to 1 (lo byte) 115200 baud + outportb(s_cst(uint16_t, Port + 1), SERIAL_RATE_115200_HI); // (hi byte) + outportb(s_cst(uint16_t, Port + 3), 0x03); // 8 bits, no parity, one stop bit + outportb(s_cst(uint16_t, Port + 2), 0xC7); // Enable FIFO, clear them, with 14-byte threshold + outportb(s_cst(uint16_t, Port + 4), 0x0B); // IRQs enabled, RTS/DSR set + + // Check if the serial port is faulty. + if (inportb(s_cst(uint16_t, Port + 0)) != 0xAE) + { + static int once = 0; + if (!once++) + warn("Serial port %#llx is faulty.", Port); + // serialports[Port] = false; // ignore for now + // return; + } + + // Set to normal operation mode. + outportb(s_cst(uint16_t, Port + 4), 0x0F); + serialports[PortNumber] = true; + } + + SafeFunction UART::~UART() {} + + SafeFunction void UART::Write(uint8_t Char) + { + while ((inportb(s_cst(uint16_t, Port + 5)) & SERIAL_BUFFER_EMPTY) == 0) + ; + outportb(Port, Char); + } + + SafeFunction uint8_t UART::Read() + { + while ((inportb(s_cst(uint16_t, Port + 5)) & 1) == 0) + ; + return inportb(Port); + } +} diff --git a/FennixLoader/cxxabi.cpp b/FennixLoader/cxxabi.cpp new file mode 100644 index 00000000..7591c164 --- /dev/null +++ b/FennixLoader/cxxabi.cpp @@ -0,0 +1,277 @@ +#include + +#include +#include + +// TODO: complete implementation for everything +// TODO: https://wiki.osdev.org/C%2B%2B + +#define ATEXIT_MAX_FUNCS 128 + +typedef unsigned uarch_t; + +struct atexit_func_entry_t +{ + /* + * Each member is at least 4 bytes large. Such that each entry is 12bytes. + * 128 * 12 = 1.5KB exact. + **/ + void (*destructor_func)(void *); + void *obj_ptr; + void *dso_handle; +}; + +typedef enum +{ + _URC_NO_REASON = 0, + _URC_FOREIGN_EXCEPTION_CAUGHT = 1, + _URC_FATAL_PHASE2_ERROR = 2, + _URC_FATAL_PHASE1_ERROR = 3, + _URC_NORMAL_STOP = 4, + _URC_END_OF_STACK = 5, + _URC_HANDLER_FOUND = 6, + _URC_INSTALL_CONTEXT = 7, + _URC_CONTINUE_UNWIND = 8 +} _Unwind_Reason_Code; + +struct _Unwind_Context; +typedef unsigned _Unwind_Ptr __attribute__((__mode__(__pointer__))); +typedef unsigned _Unwind_Exception_Class __attribute__((__mode__(__DI__))); +typedef unsigned _Unwind_Word __attribute__((__mode__(__unwind_word__))); +typedef void (*_Unwind_Exception_Cleanup_Fn)(_Unwind_Reason_Code, struct _Unwind_Exception *); +typedef int _Unwind_Action; + +struct type_info +{ + const char *name; +}; + +struct unexpected_handler +{ + void (*unexpected)(); +}; + +struct terminate_handler +{ + void (*handler)(); +}; + +struct _Unwind_Exception +{ + _Unwind_Exception_Class exception_class; + _Unwind_Exception_Cleanup_Fn exception_cleanup; +#if !defined(__USING_SJLJ_EXCEPTIONS__) && defined(__SEH__) + _Unwind_Word private_[6]; +#else + _Unwind_Word private_1; + _Unwind_Word private_2; +#endif +} __attribute__((__aligned__)); + +struct __cxa_exception +{ +#if __LP64__ + size_t referenceCount; +#endif + type_info *exceptionType; + void (*exceptionDestructor)(void *); + unexpected_handler unexpectedHandler; + terminate_handler terminateHandler; + __cxa_exception *nextException; + int handlerCount; + +#ifdef __ARM_EABI_UNWINDER__ + __cxa_exception *nextPropagatingException; + int propagationCount; +#else + int handlerSwitchValue; + const unsigned char *actionRecord; + const unsigned char *languageSpecificData; + _Unwind_Ptr catchTemp; + void *adjustedPtr; +#endif +#if !__LP64__ + size_t referenceCount; +#endif + _Unwind_Exception unwindHeader; +}; + +/* extern */ void *__dso_handle = 0; +atexit_func_entry_t __atexit_funcs[ATEXIT_MAX_FUNCS]; +uarch_t __atexit_func_count = 0; + +extern "C" int __cxa_atexit(void (*f)(void *), void *objptr, void *dso) +{ + fixme("__cxa_atexit( %p %p %p ) triggered.", f, objptr, dso); + if (__atexit_func_count >= ATEXIT_MAX_FUNCS) + return -1; + __atexit_funcs[__atexit_func_count].destructor_func = f; + __atexit_funcs[__atexit_func_count].obj_ptr = objptr; + __atexit_funcs[__atexit_func_count].dso_handle = dso; + __atexit_func_count++; + return 0; +} + +extern "C" void __cxa_finalize(void *f) +{ + fixme("__cxa_finalize( %p ) triggered.", f); + uarch_t i = __atexit_func_count; + if (!f) + { + while (i--) + if (__atexit_funcs[i].destructor_func) + (*__atexit_funcs[i].destructor_func)(__atexit_funcs[i].obj_ptr); + + return; + } + + while (i--) + if (__atexit_funcs[i].destructor_func == f) + { + (*__atexit_funcs[i].destructor_func)(__atexit_funcs[i].obj_ptr); + __atexit_funcs[i].destructor_func = 0; + } +} + +extern "C" _Unwind_Reason_Code __gxx_personality_v0(int version, _Unwind_Action actions, _Unwind_Exception_Class exception_class, _Unwind_Exception *ue_header, _Unwind_Context *context) +{ + fixme("__gxx_personality_v0( %d %p %p %p %p ) triggered.", version, actions, exception_class, ue_header, context); + return _URC_NO_REASON; +} + +extern "C" void _Unwind_Resume(struct _Unwind_Exception *exc) { fixme("_Unwind_Resume( %p ) triggered.", exc); } + +static inline size_t align_exception_allocation_size(size_t s, size_t a) { return (s + a - 1) & ~(a - 1); } + +void unexpected_header_stub() { fixme("unexpected() called."); } +void terminate_header_stub() { fixme("terminate() called."); } + +extern "C" void *__cxa_allocate_exception(size_t thrown_size) throw() +{ + fixme("__cxa_allocate_exception( %d ) triggered.", thrown_size); + + size_t real_size = align_exception_allocation_size(thrown_size + sizeof(__cxa_exception), alignof(__cxa_exception)); + + __cxa_exception *header = (__cxa_exception *)KernelAllocator32.RequestPages(TO_PAGES(real_size)); + if (!header) + { + error("Failed to allocate exception."); + return nullptr; + } + + header->referenceCount = 1; + header->exceptionType = nullptr; + header->exceptionDestructor = nullptr; + header->unexpectedHandler = {.unexpected = unexpected_header_stub}; + header->terminateHandler = {.handler = terminate_header_stub}; + header->nextException = nullptr; + header->handlerCount = -1; + header->handlerSwitchValue = 0; + header->actionRecord = nullptr; + header->languageSpecificData = nullptr; + header->catchTemp = 0; + header->adjustedPtr = nullptr; + + return header + 1; +} + +extern "C" void _Unwind_RaiseException(_Unwind_Exception *exc) +{ + fixme("_Unwind_RaiseException( %p ) triggered.", exc); + + __cxa_exception *header = ((__cxa_exception *)exc) - 1; + if (header->terminateHandler.handler) + { + debug("Calling terminate handler."); + header->terminateHandler.handler(); + } + else + { + error("Unhandled exception."); + } + + while (1) + asmv("cli; hlt"); +} + +extern "C" void __cxa_throw(void *thrown_object, void *tinfo, void (*dest)(void *)) +{ + fixme("__cxa_throw( %p %p %p ) triggered.", thrown_object, tinfo, dest); + + __cxa_exception *header = ((__cxa_exception *)thrown_object) - 1; + header->exceptionType = (type_info *)tinfo; + header->exceptionDestructor = dest; + + _Unwind_RaiseException(&header->unwindHeader); +} + +extern "C" void __cxa_rethrow() { fixme("__cxa_rethrow() triggered."); } + +extern "C" void __cxa_pure_virtual() { fixme("__cxa_pure_virtual() triggered."); } + +extern "C" void __cxa_throw_bad_array_new_length() { fixme("__cxa_throw_bad_array_new_length() triggered."); } + +extern "C" void __cxa_free_exception(void *thrown_exception) { fixme("__cxa_free_exception( %p ) triggered.", thrown_exception); } + +#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 4) +extern "C" void *__cxa_begin_catch(void *e) throw() +#else +extern "C" void *__cxa_begin_catch(void *e) +#endif +{ + fixme("__cxa_begin_catch( %p ) triggered.", e); + return (void *)0; +} + +extern "C" void __cxa_end_catch() { fixme("__cxa_end_catch() triggered."); } + +__extension__ typedef int __guard __attribute__((mode(__DI__))); + +extern "C" int __cxa_guard_acquire(__guard *g) +{ + fixme("__cxa_guard_acquire( %p ) triggered.", g); + return !*(char *)(g); +} + +extern "C" void __cxa_guard_release(__guard *g) +{ + fixme("__cxa_guard_release( %p ) triggered.", g); + *(char *)g = 1; +} + +extern "C" void __cxa_guard_abort(__guard *g) { fixme("__cxa_guard_abort( %p ) triggered.", g); } + +// vtable for __cxxabiv1::__class_type_info +extern "C" void *_ZTVN10__cxxabiv117__class_type_infoE(void) +{ + fixme("_ZTVN10__cxxabiv117__class_type_infoE() triggered."); + return (void *)0; +} + +// vtable for __cxxabiv1::__si_class_type_info +extern "C" void *_ZTVN10__cxxabiv120__si_class_type_infoE(void) +{ + fixme("_ZTVN10__cxxabiv120__si_class_type_infoE() triggered."); + return (void *)0; +} + +// typeinfo for int +extern "C" void *_ZTIi(void) +{ + fixme("_ZTIi() triggered."); + return (void *)0; +} + +// typeinfo for unsigned char* +extern "C" void *_ZTIPh(void) +{ + fixme("_ZTIPh() triggered."); + return (void *)0; +} + +// typeinfo for char const* +extern "C" void *_ZTIPKc(void) +{ + fixme("_ZTIPKc() triggered."); + return (void *)0; +} diff --git a/FennixLoader/ega.c b/FennixLoader/ega.c new file mode 100644 index 00000000..91af8884 --- /dev/null +++ b/FennixLoader/ega.c @@ -0,0 +1,166 @@ +/* https://wiki.osdev.org/Bare_Bones */ +#include + +enum vga_color +{ + VGA_COLOR_BLACK = 0, + VGA_COLOR_BLUE = 1, + VGA_COLOR_GREEN = 2, + VGA_COLOR_CYAN = 3, + VGA_COLOR_RED = 4, + VGA_COLOR_MAGENTA = 5, + VGA_COLOR_BROWN = 6, + VGA_COLOR_LIGHT_GREY = 7, + VGA_COLOR_DARK_GREY = 8, + VGA_COLOR_LIGHT_BLUE = 9, + VGA_COLOR_LIGHT_GREEN = 10, + VGA_COLOR_LIGHT_CYAN = 11, + VGA_COLOR_LIGHT_RED = 12, + VGA_COLOR_LIGHT_MAGENTA = 13, + VGA_COLOR_LIGHT_BROWN = 14, + VGA_COLOR_WHITE = 15, +}; + +void outb(uint16_t port, uint8_t val) +{ + asm volatile("outb %0, %1" + : + : "a"(val), "Nd"(port)); +} + +static inline uint8_t vga_entry_color(enum vga_color fg, enum vga_color bg) +{ + return fg | bg << 4; +} + +static inline uint16_t vga_entry(unsigned char uc, uint8_t color) +{ + return (uint16_t)uc | (uint16_t)color << 8; +} + +size_t ega_strlen(const char *str) +{ + size_t len = 0; + while (str[len]) + len++; + return len; +} + +static const size_t VGA_WIDTH = 80; +static const size_t VGA_HEIGHT = 25; + +int terminal_initialized; +size_t terminal_row; +size_t terminal_column; +uint8_t terminal_color; +uint16_t *terminal_buffer; + +void terminal_initialize(void) +{ + terminal_initialized = 1; + terminal_row = 0; + terminal_column = 0; + terminal_color = vga_entry_color(VGA_COLOR_LIGHT_GREY, VGA_COLOR_BLACK); + terminal_buffer = (uint16_t *)0xB8000; + for (size_t y = 0; y < VGA_HEIGHT; y++) + { + for (size_t x = 0; x < VGA_WIDTH; x++) + { + const size_t index = y * VGA_WIDTH + x; + terminal_buffer[index] = vga_entry(' ', terminal_color); + } + } +} + +void terminal_setcolor(uint8_t color) +{ + terminal_color = color; +} + +void terminal_putentryat(char c, uint8_t color, size_t x, size_t y) +{ + if (!terminal_initialized) + return; + const size_t index = y * VGA_WIDTH + x; + terminal_buffer[index] = vga_entry(c, color); +} + +void terminal_putchar(char c) +{ + if (!terminal_initialized) + return; + + switch (c) + { + case '\n': + case '\r': + terminal_column = 0; + if (++terminal_row >= VGA_HEIGHT) + { + // terminal_row = 0; + // scroll the screen + for (size_t y = 0; y < VGA_HEIGHT - 1; y++) + { + for (size_t x = 0; x < VGA_WIDTH; x++) + { + const size_t index = y * VGA_WIDTH + x; + terminal_buffer[index] = terminal_buffer[index + VGA_WIDTH]; + } + } + for (size_t x = 0; x < VGA_WIDTH; x++) + { + const size_t index = (VGA_HEIGHT - 1) * VGA_WIDTH + x; + terminal_buffer[index] = vga_entry(' ', terminal_color); + } + terminal_row = VGA_HEIGHT - 1; + } + // Update the cursor + uint16_t cursorLocation = terminal_row * 80 + terminal_column; + outb(0x3D4, 14); // Tell the VGA board we are setting the high cursor byte. + outb(0x3D5, cursorLocation >> 8); // Send the high cursor byte. + outb(0x3D4, 15); // Tell the VGA board we are setting the low cursor byte. + outb(0x3D5, cursorLocation); // Send the low cursor byte. + return; + case '\t': + terminal_column += 4; + if (terminal_column >= VGA_WIDTH) + { + terminal_column = 0; + if (++terminal_row == VGA_HEIGHT) + terminal_row = 0; + } + return; + } + + terminal_putentryat(c, terminal_color, terminal_column, terminal_row); + if (++terminal_column >= VGA_WIDTH) + { + terminal_column = 0; + if (++terminal_row >= VGA_HEIGHT) + { + // terminal_row = 0; + // scroll the screen + for (size_t y = 0; y < VGA_HEIGHT - 1; y++) + { + for (size_t x = 0; x < VGA_WIDTH; x++) + { + const size_t index = y * VGA_WIDTH + x; + terminal_buffer[index] = terminal_buffer[index + VGA_WIDTH]; + } + } + for (size_t x = 0; x < VGA_WIDTH; x++) + { + const size_t index = (VGA_HEIGHT - 1) * VGA_WIDTH + x; + terminal_buffer[index] = vga_entry(' ', terminal_color); + } + terminal_row = VGA_HEIGHT - 1; + } + } + + // Update the cursor + uint16_t cursorLocation = terminal_row * 80 + terminal_column; + outb(0x3D4, 14); // Tell the VGA board we are setting the high cursor byte. + outb(0x3D5, cursorLocation >> 8); // Send the high cursor byte. + outb(0x3D4, 15); // Tell the VGA board we are setting the low cursor byte. + outb(0x3D5, cursorLocation); // Send the low cursor byte. +} diff --git a/FennixLoader/include/binfo.h b/FennixLoader/include/binfo.h new file mode 100644 index 00000000..eb40b0f5 --- /dev/null +++ b/FennixLoader/include/binfo.h @@ -0,0 +1,130 @@ +#ifndef __FENNIX_KERNEL_BOOT_INFO_H__ +#define __FENNIX_KERNEL_BOOT_INFO_H__ + +enum MemoryType +{ + Unknown_Memory_Type, + Usable, + Reserved, + ACPIReclaimable, + ACPINVS, + BadMemory, + BootloaderReclaimable, + KernelAndModules, + Framebuffer, + Unknown +}; + +enum FramebufferType +{ + Unknown_Framebuffer_Type, + Indexed, + RGB, + EGA +}; + +#define MAX_FRAMEBUFFERS 16 +#define MAX_MEMORY_ENTRIES 256 +#define MAX_MODULES 16 + +struct BootInfo +{ + struct FramebufferInfo + { + enum FramebufferType Type; + void *BaseAddress; + __UINT32_TYPE__ Width; + __UINT32_TYPE__ Height; + __UINT64_TYPE__ Pitch; + __UINT16_TYPE__ BitsPerPixel; + __UINT8_TYPE__ RedMaskSize; + __UINT8_TYPE__ RedMaskShift; + __UINT8_TYPE__ GreenMaskSize; + __UINT8_TYPE__ GreenMaskShift; + __UINT8_TYPE__ BlueMaskSize; + __UINT8_TYPE__ BlueMaskShift; + void *ExtendedDisplayIdentificationData; + __UINT64_TYPE__ EDIDSize; + } Framebuffer[MAX_FRAMEBUFFERS]; + + struct MemoryInfo + { + struct MemoryEntryInfo + { + void *BaseAddress; + __UINT64_TYPE__ Length; + enum MemoryType Type; + } Entry[MAX_MEMORY_ENTRIES]; + __UINT64_TYPE__ Entries; + __UINT64_TYPE__ Size; + } Memory; + + struct ModuleInfo + { + void *Address; + char Path[256]; + char CommandLine[256]; + __UINT64_TYPE__ Size; + } Modules[MAX_MODULES]; + + struct RSDPInfo + { + /** + * @brief Signature + */ + __UINT8_TYPE__ Signature[8]; + /** + * @brief Checksum + */ + __UINT8_TYPE__ Checksum; + /** + * @brief OEM ID + */ + __UINT8_TYPE__ OEMID[6]; + /** + * @brief Revision + */ + __UINT8_TYPE__ Revision; + /** + * @brief Address of the Root System Description Table + */ + __UINT32_TYPE__ RSDTAddress; + /* END OF RSDP 1.0 */ + + /** + * @brief Length + */ + __UINT32_TYPE__ Length; + /** + * @brief Extended System Descriptor Table + */ + __UINT64_TYPE__ XSDTAddress; + /** + * @brief Extended checksum + */ + __UINT8_TYPE__ ExtendedChecksum; + /** + * @brief Reserved + */ + __UINT8_TYPE__ Reserved[3]; + } __attribute__((packed)) * RSDP; + + struct KernelInfo + { + void *PhysicalBase; + void *VirtualBase; + void *FileBase; + char CommandLine[256]; + __UINT64_TYPE__ Size; + } Kernel; + + struct BootloaderInfo + { + char Name[256]; + char Version[64]; + } Bootloader; + + void *SMBIOSPtr; +}; + +#endif // !__FENNIX_KERNEL_BOOT_INFO_H__ diff --git a/FennixLoader/include/bitmap.hpp b/FennixLoader/include/bitmap.hpp new file mode 100644 index 00000000..b34d5be8 --- /dev/null +++ b/FennixLoader/include/bitmap.hpp @@ -0,0 +1,12 @@ +#pragma once +#include + +class Bitmap +{ +public: + size_t Size; + uint8_t *Buffer; + bool operator[](uint64_t index); + bool Set(uint64_t index, bool value); + bool Get(uint64_t index); +}; diff --git a/FennixLoader/include/debug.h b/FennixLoader/include/debug.h new file mode 100644 index 00000000..d68ef3cc --- /dev/null +++ b/FennixLoader/include/debug.h @@ -0,0 +1,89 @@ +#ifndef __FENNIX_KERNEL_DEBUGGER_H__ +#define __FENNIX_KERNEL_DEBUGGER_H__ + +#include + +enum DebugLevel +{ + DebugLevelNone = 0, + DebugLevelError = 1, + DebugLevelWarning = 2, + DebugLevelInfo = 3, + DebugLevelDebug = 4, + DebugLevelTrace = 5, + DebugLevelFixme = 6, + DebugLevelUbsan = 7 +}; + +#ifdef __cplusplus + +namespace SysDbg +{ + void Write(DebugLevel Level, const char *File, int Line, const char *Function, const char *Format, ...); + void WriteLine(DebugLevel Level, const char *File, int Line, const char *Function, const char *Format, ...); + void LockedWrite(DebugLevel Level, const char *File, int Line, const char *Function, const char *Format, ...); + void LockedWriteLine(DebugLevel Level, const char *File, int Line, const char *Function, const char *Format, ...); +} + +#define error(Format, ...) SysDbg::WriteLine(DebugLevelError, __FILE__, __LINE__, __FUNCTION__, Format, ##__VA_ARGS__) +#define warn(Format, ...) SysDbg::WriteLine(DebugLevelWarning, __FILE__, __LINE__, __FUNCTION__, Format, ##__VA_ARGS__) +#define info(Format, ...) SysDbg::WriteLine(DebugLevelInfo, __FILE__, __LINE__, __FUNCTION__, Format, ##__VA_ARGS__) +#ifdef DEBUG +#define debug(Format, ...) SysDbg::WriteLine(DebugLevelDebug, __FILE__, __LINE__, __FUNCTION__, Format, ##__VA_ARGS__) +#define ubsan(Format, ...) SysDbg::WriteLine(DebugLevelUbsan, __FILE__, __LINE__, __FUNCTION__, Format, ##__VA_ARGS__) +#else +#define debug(Format, ...) +#define ubsan(Format, ...) +#endif +#define trace(Format, ...) SysDbg::WriteLine(DebugLevelTrace, __FILE__, __LINE__, __FUNCTION__, Format, ##__VA_ARGS__) +#define fixme(Format, ...) SysDbg::WriteLine(DebugLevelFixme, __FILE__, __LINE__, __FUNCTION__, Format, ##__VA_ARGS__) + +#define locked_error(Format, ...) SysDbg::LockedWriteLine(DebugLevelError, __FILE__, __LINE__, __FUNCTION__, Format, ##__VA_ARGS__) +#define locked_warn(Format, ...) SysDbg::LockedWriteLine(DebugLevelWarning, __FILE__, __LINE__, __FUNCTION__, Format, ##__VA_ARGS__) +#define locked_info(Format, ...) SysDbg::LockedWriteLine(DebugLevelInfo, __FILE__, __LINE__, __FUNCTION__, Format, ##__VA_ARGS__) +#ifdef DEBUG +#define locked_debug(Format, ...) SysDbg::LockedWriteLine(DebugLevelDebug, __FILE__, __LINE__, __FUNCTION__, Format, ##__VA_ARGS__) +#define locked_ubsan(Format, ...) SysDbg::LockedWriteLine(DebugLevelUbsan, __FILE__, __LINE__, __FUNCTION__, Format, ##__VA_ARGS__) +#else +#define locked_debug(Format, ...) +#define locked_ubsan(Format, ...) +#endif +#define locked_trace(Format, ...) SysDbg::LockedWriteLine(DebugLevelTrace, __FILE__, __LINE__, __FUNCTION__, Format, ##__VA_ARGS__) +#define locked_fixme(Format, ...) SysDbg::LockedWriteLine(DebugLevelFixme, __FILE__, __LINE__, __FUNCTION__, Format, ##__VA_ARGS__) + +#else + +void SysDbgWrite(enum DebugLevel Level, const char *File, int Line, const char *Function, const char *Format, ...); +void SysDbgWriteLine(enum DebugLevel Level, const char *File, int Line, const char *Function, const char *Format, ...); +void SysDbgLockedWrite(enum DebugLevel Level, const char *File, int Line, const char *Function, const char *Format, ...); +void SysDbgLockedWriteLine(enum DebugLevel Level, const char *File, int Line, const char *Function, const char *Format, ...); + +#define error(Format, ...) SysDbgWriteLine(DebugLevelError, __FILE__, __LINE__, __FUNCTION__, Format, ##__VA_ARGS__) +#define warn(Format, ...) SysDbgWriteLine(DebugLevelWarning, __FILE__, __LINE__, __FUNCTION__, Format, ##__VA_ARGS__) +#define info(Format, ...) SysDbgWriteLine(DebugLevelInfo, __FILE__, __LINE__, __FUNCTION__, Format, ##__VA_ARGS__) +#ifdef DEBUG +#define debug(Format, ...) SysDbgWriteLine(DebugLevelDebug, __FILE__, __LINE__, __FUNCTION__, Format, ##__VA_ARGS__) +#define ubsan(Format, ...) SysDbgWriteLine(DebugLevelUbsan, __FILE__, __LINE__, __FUNCTION__, Format, ##__VA_ARGS__) +#else +#define debug(Format, ...) +#define ubsan(Format, ...) +#endif +#define trace(Format, ...) SysDbgWriteLine(DebugLevelTrace, __FILE__, __LINE__, __FUNCTION__, Format, ##__VA_ARGS__) +#define fixme(Format, ...) SysDbgWriteLine(DebugLevelFixme, __FILE__, __LINE__, __FUNCTION__, Format, ##__VA_ARGS__) + +#define locked_error(Format, ...) SysDbgLockedWriteLine(DebugLevelError, __FILE__, __LINE__, __FUNCTION__, Format, ##__VA_ARGS__) +#define locked_warn(Format, ...) SysDbgLockedWriteLine(DebugLevelWarning, __FILE__, __LINE__, __FUNCTION__, Format, ##__VA_ARGS__) +#define locked_info(Format, ...) SysDbgLockedWriteLine(DebugLevelInfo, __FILE__, __LINE__, __FUNCTION__, Format, ##__VA_ARGS__) +#ifdef DEBUG +#define locked_debug(Format, ...) SysDbgLockedWriteLine(DebugLevelDebug, __FILE__, __LINE__, __FUNCTION__, Format, ##__VA_ARGS__) +#define locked_ubsan(Format, ...) SysDbgLockedWriteLine(DebugLevelUbsan, __FILE__, __LINE__, __FUNCTION__, Format, ##__VA_ARGS__) +#else +#define locked_debug(Format, ...) +#define locked_ubsan(Format, ...) +#endif +#define locked_trace(Format, ...) SysDbgLockedWriteLine(DebugLevelTrace, __FILE__, __LINE__, __FUNCTION__, Format, ##__VA_ARGS__) +#define locked_fixme(Format, ...) SysDbgLockedWriteLine(DebugLevelFixme, __FILE__, __LINE__, __FUNCTION__, Format, ##__VA_ARGS__) + +#endif // __cplusplus + +#endif // !__FENNIX_KERNEL_DEBUGGER_H__ diff --git a/FennixLoader/include/elf.h b/FennixLoader/include/elf.h new file mode 100644 index 00000000..24818eb7 --- /dev/null +++ b/FennixLoader/include/elf.h @@ -0,0 +1,476 @@ +#ifndef __FENNIX_KERNEL_ELF_H__ +#define __FENNIX_KERNEL_ELF_H__ + +#include + +// https://wiki.osdev.org/ELF_Tutorial +// https://github.com/torvalds/linux/blob/master/include/uapi/linux/elf.h + +/* 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; + +/* 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; + +#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; + 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; + +typedef struct elf32_shdr +{ + 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; + +typedef struct { + 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_Word p_type; + Elf64_Word p_flags; + Elf64_Off p_offset; + Elf64_Addr p_vaddr; + Elf64_Addr p_paddr; + Elf64_Xword p_filesz; + Elf64_Xword p_memsz; + Elf64_Xword p_align; +} Elf64_Phdr; + +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_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; + +struct Elf32_Dyn +{ + Elf32_Sword d_tag; /* Type of dynamic table entry. */ + union + { + Elf32_Word d_val; /* Integer value of entry. */ + Elf32_Addr d_ptr; /* Pointer value of entry. */ + } d_un; +}; + +struct Elf64_Dyn +{ + Elf64_Sxword d_tag; /* Type of dynamic table entry. */ + union + { + Elf64_Xword d_val; /* Integer value of entry. */ + Elf64_Addr d_ptr; /* Pointer value of entry. */ + } d_un; +}; + +typedef struct +{ + Elf64_Addr r_offset; + Elf64_Xword r_info; + Elf64_Sxword r_addend; +} Elf64_Rela; + +enum Elf_Ident +{ + EI_MAG0 = 0, // 0x7F + EI_MAG1 = 1, // 'E' + EI_MAG2 = 2, // 'L' + EI_MAG3 = 3, // 'F' + EI_CLASS = 4, // Architecture (32/64) + EI_DATA = 5, // Byte Order + EI_VERSION = 6, // ELF Version + EI_OSABI = 7, // OS Specific + EI_ABIVERSION = 8, // OS Specific + EI_PAD = 9 // Padding +}; + +enum Elf_OSABI +{ + ELFOSABI_NONE = 0, + ELFOSABI_HPUX = 1, + ELFOSABI_NETBSD = 2, + ELFOSABI_LINUX = 3, + ELFOSABI_HURD = 4, + ELFOSABI_SOLARIS = 6, + ELFOSABI_AIX = 7, + ELFOSABI_IRIX = 8, + ELFOSABI_FREEBSD = 9, + ELFOSABI_TRU64 = 10, + ELFOSABI_MODESTO = 11, + ELFOSABI_OPENBSD = 12, + ELFOSABI_OPENVMS = 13, + ELFOSABI_NSK = 14, + ELFOSABI_AROS = 15, + ELFOSABI_FENIXOS = 16, + ELFOSABI_CLOUDABI = 17, + ELFOSABI_OPENVOS = 18, + ELFOSABI_C6000_ELFABI = 64, + ELFOSABI_C6000_LINUX = 65, + ELFOSABI_ARM = 97, + ELFOSABI_STANDALONE = 255 +}; + +enum Elf_Type +{ + ET_NONE = 0, // Unknown Type + ET_REL = 1, // Relocatable File + ET_EXEC = 2, // Executable File + ET_DYN = 3, // Shared Object File + ET_CORE = 4, // Core File + ET_LOPROC = 0xff00, // Processor Specific + ET_HIPROC = 0xffff // Processor Specific +}; + +enum RtT_Types +{ + R_386_NONE = 0, // No relocation + R_386_32 = 1, // Symbol + Offset + R_386_PC32 = 2, // Symbol + Offset - Section Offset + + R_X86_64_NONE = 0, + R_X86_64_64 = 1, + R_X86_64_PC32 = 2, + R_X86_64_GOT32 = 3, + R_X86_64_PLT32 = 4, + R_X86_64_COPY = 5, + R_X86_64_GLOB_DAT = 6, + R_X86_64_JUMP_SLOT = 7, + R_X86_64_RELATIVE = 8, + R_X86_64_GOTPCREL = 9, + R_X86_64_32 = 10, + R_X86_64_32S = 11, + R_X86_64_16 = 12, +}; + +enum ProgFlags_Types +{ + PF_X = 1, + PF_W = 2, + PF_R = 4 +}; + +enum StT_Bindings +{ + /** + * @brief Local symbol. Symbol is not visible outside the object file. + */ + STB_LOCAL = 0, + /** + * @brief Global symbol. These symbols are visible to all object files being combined. + */ + STB_GLOBAL = 1, + /** + * @brief Weak symbols. These symbols are like global symbols, but their definitions are not required. Weak symbols are not visible outside the object file containing their definition. + */ + STB_WEAK = 2, + /** + * @brief Values in this inclusive range are reserved for operating system-specific semantics. + */ + STB_LOOS = 10, + /** + * @brief Values in this inclusive range are reserved for operating system-specific semantics. + */ + STB_HIOS = 12, + /** + * @brief Values in this inclusive range are reserved for processor-specific semantics. + */ + STB_LOPROC = 13, + /** + * @brief Values in this inclusive range are reserved for processor-specific semantics. + */ + STB_HIPROC = 15 +}; + +enum StT_Types +{ + STT_NOTYPE = 0, // No type + STT_OBJECT = 1, // Variables, arrays, etc. + STT_FUNC = 2 // Methods or functions +}; + +enum SegmentTypes +{ + PT_NULL = 0, + PT_LOAD = 1, + PT_DYNAMIC = 2, + PT_INTERP = 3, + PT_NOTE = 4, + PT_SHLIB = 5, + PT_PHDR = 6, + PT_TLS = 7, + PT_LOSUNW = 0x6ffffffa, + PT_SUNWBSS = 0x6ffffffb, + PT_SUNWSTACK = 0x6ffffffa, + PT_HISUNW = 0x6fffffff, + PT_LOPROC = 0x70000000, + PT_HIPROC = 0x7fffffff +}; + +/* https://docs.oracle.com/cd/E19683-01/817-3677/chapter6-42444/index.html */ +enum DynamicArrayTags +{ + DT_NULL = 0, + DT_NEEDED = 1, + DT_PLTRELSZ = 2, + DT_PLTGOT = 3, + DT_HASH = 4, + DT_STRTAB = 5, + DT_SYMTAB = 6, + DT_RELA = 7, + DT_RELASZ = 8, + DT_RELAENT = 9, + DT_STRSZ = 10, + DT_SYMENT = 11, + DT_INIT = 12, + DT_FINI = 13, + DT_SONAME = 14, + DT_RPATH = 15, + DT_SYMBOLIC = 16, + DT_REL = 17, + DT_RELSZ = 18, + DT_RELENT = 19, + DT_PLTREL = 20, + DT_DEBUG = 21, + DT_TEXTREL = 22, + DT_JMPREL = 23, + DT_BIND_NOW = 24, + DT_INIT_ARRAY = 25, + DT_FINI_ARRAY = 26, + DT_INIT_ARRAYSZ = 27, + DT_FINI_ARRAYSZ = 28, + DT_RUNPATH = 29, + DT_FLAGS = 30, + DT_ENCODING = 32, + DT_PREINIT_ARRAY = 32, + DT_PREINIT_ARRAYSZ = 33, + DT_LOOS = 0x6000000d, + DT_SUNW_RTLDINF = 0x6000000e, + DT_HIOS = 0x6ffff000, + DT_VALRNGLO = 0x6ffffd00, + DT_CHECKSUM = 0x6ffffdf8, + DT_PLTPADSZ = 0x6ffffdf9, + DT_MOVEENT = 0x6ffffdfa, + DT_MOVESZ = 0x6ffffdfb, + DT_FEATURE_1 = 0x6ffffdfc, + DT_POSFLAG_1 = 0x6ffffdfd, + DT_SYMINSZ = 0x6ffffdfe, + DT_SYMINENT = 0x6ffffdff, + DT_VALRNGHI = 0x6ffffdff, + DT_ADDRRNGLO = 0x6ffffe00, + DT_CONFIG = 0x6ffffefa, + DT_DEPAUDIT = 0x6ffffefb, + DT_AUDIT = 0x6ffffefc, + DT_PLTPAD = 0x6ffffefd, + DT_MOVETAB = 0x6ffffefe, + DT_SYMINFO = 0x6ffffeff, + DT_ADDRRNGHI = 0x6ffffeff, + DT_RELACOUNT = 0x6ffffff9, + DT_RELCOUNT = 0x6ffffffa, + DT_FLAGS_1 = 0x6ffffffb, + DT_VERDEF = 0x6ffffffc, + DT_VERDEFNUM = 0x6ffffffd, + DT_VERNEED = 0x6ffffffe, + DT_VERNEEDNUM = 0x6fffffff, + DT_LOPROC = 0x70000000, + DT_SPARC_REGISTER = 0x70000001, + DT_AUXILIARY = 0x7ffffffd, + DT_USED = 0x7ffffffe, + DT_FILTER = 0x7fffffff, + DT_HIPROC = 0x7fffffff +}; + +// used for Elf64_Sym st_info +#define ELF32_ST_BIND(info) ((info) >> 4) +#define ELF32_ST_TYPE(info) ((info)&0xf) +#define ELF32_ST_INFO(bind, type) (((bind) << 4) + ((type)&0xf)) +#define ELF64_ST_BIND(info) ((info) >> 4) +#define ELF64_ST_TYPE(info) ((info)&0xf) +#define ELF64_ST_INFO(bind, type) (((bind) << 4) + ((type)&0xf)) + +// used for Elf64_Sym st_other +#define ELF32_ST_VISIBILITY(o) ((o)&0x3) +#define ELF64_ST_VISIBILITY(o) ((o)&0x3) + +#define DO_386_32(S, A) ((S) + (A)) +#define DO_386_PC32(S, A, P) ((S) + (A) - (P)) + +#define DO_64_64(S, A) ((S) + (A)) +#define DO_64_PC32(S, A, P) ((S) + (A) - (P)) + +#define ELF32_R_SYM(i) ((i) >> 8) +#define ELF32_R_TYPE(i) ((unsigned char)(i)) +#define ELF32_R_INFO(s, t) (((s) << 8) + (unsigned char)(t)) + +#define ELF64_R_SYM(i) ((i) >> 32) +#define ELF64_R_TYPE(i) ((i)&0xffffffffL) +#define ELF64_R_INFO(s, t) (((s) << 32) + ((t)&0xffffffffL)) + +#define SHN_UNDEF 0 +#define SHN_ABS 0xfff1 + +#define SHT_NOBITS 8 +#define SHT_REL 9 + +#define SHF_WRITE 0x1 +#define SHF_ALLOC 0x2 + +#define EM_386 0x3 // x86 Machine Type +#define EM_X86_64 0x3E // 64bit +#define EM_ARM 0x28 // ARM +#define EM_AARCH64 0xb7 // ARM64 + +#define EV_CURRENT 0x1 // ELF Current Version + +#define ELFMAG0 0x7F // e_ident[EI_MAG0] +#define ELFMAG1 'E' // e_ident[EI_MAG1] +#define ELFMAG2 'L' // e_ident[EI_MAG2] +#define ELFMAG3 'F' // e_ident[EI_MAG3] + +#define ELFDATANONE 0 /* e_ident[EI_DATA] */ +#define ELFDATA2LSB 1 +#define ELFDATA2MSB 2 + +#define ELFCLASSNONE 0 /* EI_CLASS */ +#define ELFCLASS32 1 +#define ELFCLASS64 2 +#define ELFCLASSNUM 3 + +#define SHT_NULL 0 /* Section header table entry unused */ +#define SHT_PROGBITS 1 /* Program data */ +#define SHT_SYMTAB 2 /* Symbol table */ +#define SHT_STRTAB 3 /* String table */ +#define SHT_RELA 4 /* Relocation entries with addends */ +#define SHT_HASH 5 /* Symbol hash table */ +#define SHT_DYNAMIC 6 /* Dynamic linking information */ +#define SHT_NOTE 7 /* Notes */ +#define SHT_NOBITS 8 /* Program space with no data (bss) */ +#define SHT_REL 9 /* Relocation entries, no addends */ +#define SHT_SHLIB 10 /* Reserved */ +#define SHT_DYNSYM 11 /* Dynamic linker symbol table */ +#define SHT_INIT_ARRAY 14 /* Array of constructors */ +#define SHT_FINI_ARRAY 15 /* Array of destructors */ +#define SHT_PREINIT_ARRAY 16 /* Array of pre-constructors */ +#define SHT_GROUP 17 /* Section group */ +#define SHT_SYMTAB_SHNDX 18 /* Extended section indeces */ +#define SHT_NUM 19 /* Number of defined types. */ +#define SHT_LOOS 0x60000000 /* Start OS-specific. */ +#define SHT_GNU_ATTRIBUTES 0x6ffffff5 /* Object attributes. */ +#define SHT_GNU_HASH 0x6ffffff6 /* GNU-style hash table. */ +#define SHT_GNU_LIBLIST 0x6ffffff7 /* Prelink library list */ +#define SHT_CHECKSUM 0x6ffffff8 /* Checksum for DSO content. */ +#define SHT_LOSUNW 0x6ffffffa /* Sun-specific low bound. */ +#define SHT_SUNW_move 0x6ffffffa +#define SHT_SUNW_COMDAT 0x6ffffffb +#define SHT_SUNW_syminfo 0x6ffffffc +#define SHT_GNU_verdef 0x6ffffffd /* Version definition section. */ +#define SHT_GNU_verneed 0x6ffffffe /* Version needs section. */ +#define SHT_GNU_versym 0x6fffffff /* Version symbol table. */ +#define SHT_HISUNW 0x6fffffff /* Sun-specific high bound. */ +#define SHT_HIOS 0x6fffffff /* End OS-specific type */ +#define SHT_LOPROC 0x70000000 /* Start of processor-specific */ +#define SHT_HIPROC 0x7fffffff /* End of processor-specific */ +#define SHT_LOUSER 0x80000000 /* Start of application-specific */ +#define SHT_HIUSER 0x8fffffff /* End of application-specific */ + +#endif // !__FENNIX_KERNEL_ELF_H__ diff --git a/FennixLoader/include/memory.hpp b/FennixLoader/include/memory.hpp new file mode 100644 index 00000000..a0f5aa74 --- /dev/null +++ b/FennixLoader/include/memory.hpp @@ -0,0 +1,618 @@ +#pragma once +#include +#include +#include +#include + +extern uintptr_t _kernel_start, _kernel_end; + +// kilobyte +#define TO_KB(d) ((d) / 1024) +// megabyte +#define TO_MB(d) ((d) / 1024 / 1024) +// gigabyte +#define TO_GB(d) ((d) / 1024 / 1024 / 1024) + +#define PAGE_SIZE 0x1000 // 4KB +#define PAGE_SIZE_4K PAGE_SIZE // 4KB +#define PAGE_SIZE_2M 0x200000 // 2MB +#define PAGE_SIZE_4M 0x400000 // 4MB +#define PAGE_SIZE_1G 0x40000000 // 1GB + +// To pages +#define TO_PAGES(d) (((d) + PAGE_SIZE - 1) / PAGE_SIZE) +// From pages +#define FROM_PAGES(d) ((d)*PAGE_SIZE) + +enum PTFlag +{ + /** @brief Present */ + P = 1 << 0, + + /** @brief Read/Write */ + RW = 1 << 1, + + /** @brief User/Supervisor */ + US = 1 << 2, + + /** @brief Write-Through */ + PWT = 1 << 3, + + /** @brief Cache Disable */ + PCD = 1 << 4, + + /** @brief Accessed */ + A = 1 << 5, + + /** @brief Dirty */ + D = 1 << 6, + + /** @brief Page Size */ + PS = 1 << 7, + + /** @brief Global */ + G = 1 << 8, + + /** @brief Available 0 */ + AVL0 = 1 << 9, + + /** @brief Available 1 */ + AVL1 = 1 << 10, + + /** @brief Available 2 */ + AVL2 = 1 << 11, + + /** @brief Page Attribute Table */ + PAT = 1 << 12, + + /** @brief Available 3 */ + AVL3 = (uint64_t)1 << 52, + + /** @brief Available 4 */ + AVL4 = (uint64_t)1 << 53, + + /** @brief Available 5 */ + AVL5 = (uint64_t)1 << 54, + + /** @brief Available 6 */ + AVL6 = (uint64_t)1 << 55, + + /** @brief Available 7 */ + AVL7 = (uint64_t)1 << 56, + + /** @brief Available 8 */ + AVL8 = (uint64_t)1 << 57, + + /** @brief Available 9 */ + AVL9 = (uint64_t)1 << 58, + + /** @brief Protection Key 0 */ + PK0 = (uint64_t)1 << 59, + + /** @brief Protection Key 1 */ + PK1 = (uint64_t)1 << 60, + + /** @brief Protection Key 2 */ + PK2 = (uint64_t)1 << 61, + + /** @brief Protection Key 3 */ + PK3 = (uint64_t)1 << 62, + + /** @brief Execute Disable */ + XD = (uint64_t)1 << 63 +}; + +namespace Memory32 +{ + union __packed PageTableEntry + { + struct + { + uint32_t Present : 1; // 0 + uint32_t ReadWrite : 1; // 1 + uint32_t UserSupervisor : 1; // 2 + uint32_t WriteThrough : 1; // 3 + uint32_t CacheDisable : 1; // 4 + uint32_t Accessed : 1; // 5 + uint32_t Dirty : 1; // 6 + uint32_t PageAttributeTable : 1; // 7 + uint32_t Global : 1; // 8 + uint32_t Available0 : 3; // 9-11 + uint32_t Address : 20; // 12-31 + }; + uint32_t raw; + + void SetAddress(uintptr_t _Address) + { + _Address &= 0x000FFFFF; + this->raw &= 0xFFC00003; + this->raw |= (_Address << 12); + } + + uintptr_t GetAddress() + { + return (this->raw & 0x003FFFFF000) >> 12; + } + }; + + struct __packed PageTableEntryPtr + { + PageTableEntry Entries[1024]; + }; + + union __packed PageDirectoryEntry + { + struct + { + uint32_t Present : 1; // 0 + uint32_t ReadWrite : 1; // 1 + uint32_t UserSupervisor : 1; // 2 + uint32_t WriteThrough : 1; // 3 + uint32_t CacheDisable : 1; // 4 + uint32_t Accessed : 1; // 5 + uint32_t Available0 : 1; // 6 + uint32_t PageSize : 1; // 7 + uint32_t Available1 : 4; // 8-11 + uint32_t Address : 20; // 12-31 + }; + uint32_t raw; + + void SetAddress(uintptr_t _Address) + { + _Address &= 0x000FFFFF; + this->raw &= 0xFFC00003; + this->raw |= (_Address << 12); + } + + uintptr_t GetAddress() + { + return (this->raw & 0x003FFFFF000) >> 12; + } + }; + + struct PageTable + { + PageDirectoryEntry Entries[1024]; + } __aligned(0x1000); + + class Physical + { + private: + uint64_t PageBitmapIndex = 0; + Bitmap PageBitmap; + + public: + uint64_t TotalMemory = 0; + uint64_t FreeMemory = 0; + uint64_t ReservedMemory = 0; + uint64_t UsedMemory = 0; + void LockPage(void *Address); + void LockPages(void *Address, size_t PageCount); + void ReservePage(void *Address); + void ReservePages(void *Address, size_t PageCount); + void UnreservePage(void *Address); + void UnreservePages(void *Address, size_t PageCount); + void *RequestPage(); + void *RequestPages(size_t Count); + void FreePage(void *Address); + void FreePages(void *Address, size_t Count); + void Init(BootInfo *Info); + Physical(); + ~Physical(); + }; + + class Virtual + { + private: + PageTable *Table = nullptr; + + public: + class PageMapIndexer + { + public: + uintptr_t PDEIndex = 0; + uintptr_t PTEIndex = 0; + PageMapIndexer(uintptr_t VirtualAddress) + { + uintptr_t Address = VirtualAddress; + Address >>= 12; + this->PTEIndex = Address & 0x3FF; + Address >>= 10; + this->PDEIndex = Address & 0x3FF; + } + }; + + void Map(void *VirtualAddress, void *PhysicalAddress, uint64_t Flag = PTFlag::P); + + __always_inline inline void Map(void *VirtualAddress, void *PhysicalAddress, size_t Length, uint64_t Flags) + { + for (uintptr_t i = 0; i < Length; i += PAGE_SIZE_4K) + this->Map((void *)((uintptr_t)VirtualAddress + i), (void *)((uintptr_t)PhysicalAddress + i), Flags); + } + + Virtual(PageTable *Table = nullptr); + ~Virtual(); + }; +} + +namespace Memory64 +{ + union __packed PageTableEntry + { + struct + { + uint64_t Present : 1; // 0 + uint64_t ReadWrite : 1; // 1 + uint64_t UserSupervisor : 1; // 2 + uint64_t WriteThrough : 1; // 3 + uint64_t CacheDisable : 1; // 4 + uint64_t Accessed : 1; // 5 + uint64_t Dirty : 1; // 6 + uint64_t PageAttributeTable : 1; // 7 + uint64_t Global : 1; // 8 + uint64_t Available0 : 3; // 9-11 + uint64_t Address : 40; // 12-51 + uint64_t Available1 : 7; // 52-58 + uint64_t ProtectionKey : 4; // 59-62 + uint64_t ExecuteDisable : 1; // 63 + }; + uint64_t raw; + + /** @brief Set Address */ + void SetAddress(uintptr_t _Address) + { + _Address &= 0x000000FFFFFFFFFF; + this->raw &= 0xFFF0000000000FFF; + this->raw |= (_Address << 12); + } + + /** @brief Get Address */ + uintptr_t GetAddress() + { + return (this->raw & 0x000FFFFFFFFFF000) >> 12; + } + }; + + struct __packed PageTableEntryPtr + { + PageTableEntry Entries[511]; + }; + + union __packed PageDirectoryEntry + { + struct + { + uint64_t Present : 1; // 0 + uint64_t ReadWrite : 1; // 1 + uint64_t UserSupervisor : 1; // 2 + uint64_t WriteThrough : 1; // 3 + uint64_t CacheDisable : 1; // 4 + uint64_t Accessed : 1; // 5 + uint64_t Available0 : 1; // 6 + uint64_t PageSize : 1; // 7 + uint64_t Available1 : 4; // 8-11 + uint64_t Address : 40; // 12-51 + uint64_t Available2 : 11; // 52-62 + uint64_t ExecuteDisable : 1; // 63 + }; + + struct + { + uint64_t Present : 1; // 0 + uint64_t ReadWrite : 1; // 1 + uint64_t UserSupervisor : 1; // 2 + uint64_t WriteThrough : 1; // 3 + uint64_t CacheDisable : 1; // 4 + uint64_t Accessed : 1; // 5 + uint64_t Dirty : 1; // 6 + uint64_t PageSize : 1; // 7 + uint64_t Global : 1; // 8 + uint64_t Available0 : 3; // 9-11 + uint64_t PageAttributeTable : 1; // 12 + uint64_t Reserved0 : 8; // 13-20 + uint64_t Address : 31; // 21-51 + uint64_t Available1 : 7; // 52-58 + uint64_t ProtectionKey : 4; // 59-62 + uint64_t ExecuteDisable : 1; // 63 + } TwoMB; + + uint64_t raw; + + /** @brief Set PageTableEntryPtr address */ + void SetAddress(uintptr_t _Address) + { + _Address &= 0x000000FFFFFFFFFF; + this->raw &= 0xFFF0000000000FFF; + this->raw |= (_Address << 12); + } + + /** @brief Get PageTableEntryPtr address */ + uintptr_t GetAddress() + { + return (this->raw & 0x000FFFFFFFFFF000) >> 12; + } + }; + + struct __packed PageDirectoryEntryPtr + { + PageDirectoryEntry Entries[511]; + }; + + union __packed PageDirectoryPointerTableEntry + { + struct + { + uint64_t Present : 1; // 0 + uint64_t ReadWrite : 1; // 1 + uint64_t UserSupervisor : 1; // 2 + uint64_t WriteThrough : 1; // 3 + uint64_t CacheDisable : 1; // 4 + uint64_t Accessed : 1; // 5 + uint64_t Available0 : 1; // 6 + uint64_t PageSize : 1; // 7 + uint64_t Available1 : 4; // 8-11 + uint64_t Address : 40; // 12-51 + uint64_t Available2 : 11; // 52-62 + uint64_t ExecuteDisable : 1; // 63 + }; + + struct + { + uint64_t Present : 1; // 0 + uint64_t ReadWrite : 1; // 1 + uint64_t UserSupervisor : 1; // 2 + uint64_t WriteThrough : 1; // 3 + uint64_t CacheDisable : 1; // 4 + uint64_t Accessed : 1; // 5 + uint64_t Dirty : 1; // 6 + uint64_t PageSize : 1; // 7 + uint64_t Global : 1; // 8 + uint64_t Available0 : 3; // 9-11 + uint64_t PageAttributeTable : 1; // 12 + uint64_t Reserved0 : 17; // 13-29 + uint64_t Address : 22; // 30-51 + uint64_t Available1 : 7; // 52-58 + uint64_t ProtectionKey : 4; // 59-62 + uint64_t ExecuteDisable : 1; // 63 + } OneGB; + + uint64_t raw; + + /** @brief Set PageDirectoryEntryPtr address */ + void SetAddress(uintptr_t _Address) + { + _Address &= 0x000000FFFFFFFFFF; + this->raw &= 0xFFF0000000000FFF; + this->raw |= (_Address << 12); + } + + /** @brief Get PageDirectoryEntryPtr address */ + uintptr_t GetAddress() + { + return (this->raw & 0x000FFFFFFFFFF000) >> 12; + } + }; + + struct __packed PageDirectoryPointerTableEntryPtr + { + PageDirectoryPointerTableEntry Entries[511]; + }; + + union __packed PageMapLevel4 + { + struct + { + uint64_t Present : 1; // 0 + uint64_t ReadWrite : 1; // 1 + uint64_t UserSupervisor : 1; // 2 + uint64_t WriteThrough : 1; // 3 + uint64_t CacheDisable : 1; // 4 + uint64_t Accessed : 1; // 5 + uint64_t Available0 : 1; // 6 + uint64_t Reserved0 : 1; // 7 + uint64_t Available1 : 4; // 8-11 + uint64_t Address : 40; // 12-51 + uint64_t Available2 : 11; // 52-62 + uint64_t ExecuteDisable : 1; // 63 + }; + uint64_t raw; + + /** @brief Set PageDirectoryPointerTableEntryPtr address */ + void SetAddress(uintptr_t _Address) + { + _Address &= 0x000000FFFFFFFFFF; + this->raw &= 0xFFF0000000000FFF; + this->raw |= (_Address << 12); + } + + /** @brief Get PageDirectoryPointerTableEntryPtr address */ + uintptr_t GetAddress() + { + return (this->raw & 0x000FFFFFFFFFF000) >> 12; + } + }; + + struct PageTable + { + PageMapLevel4 Entries[511]; + + /** + * @brief Update CR3 with this PageTable + */ + void Update() + { + asmv("mov %0, %%cr3" ::"r"(this)); + } + } __aligned(0x1000); + + class Physical + { + private: + uint64_t PageBitmapIndex = 0; + Bitmap PageBitmap; + + public: + uint64_t TotalMemory = 0; + uint64_t FreeMemory = 0; + uint64_t ReservedMemory = 0; + uint64_t UsedMemory = 0; + void LockPage(void *Address); + void LockPages(void *Address, size_t PageCount); + void ReservePage(void *Address); + void ReservePages(void *Address, size_t PageCount); + void UnreservePage(void *Address); + void UnreservePages(void *Address, size_t PageCount); + void *RequestPage(); + void *RequestPages(size_t Count); + void FreePage(void *Address); + void FreePages(void *Address, size_t Count); + void Init(BootInfo *Info); + Physical(); + ~Physical(); + }; + + class Virtual + { + private: + PageTable *Table = nullptr; + + public: + enum MapType + { + NoMapType, + FourKB, + TwoMB, + OneGB + }; + + class PageMapIndexer + { + public: + uintptr_t PMLIndex = 0; + uintptr_t PDPTEIndex = 0; + uintptr_t PDEIndex = 0; + uintptr_t PTEIndex = 0; + PageMapIndexer(uintptr_t VirtualAddress) + { + uintptr_t Address = VirtualAddress; + Address >>= 12; + this->PTEIndex = Address & 0x1FF; + Address >>= 9; + this->PDEIndex = Address & 0x1FF; + Address >>= 9; + this->PDPTEIndex = Address & 0x1FF; + Address >>= 9; + this->PMLIndex = Address & 0x1FF; + } + }; + + bool Check(void *VirtualAddress, PTFlag Flag = PTFlag::P, MapType Type = MapType::FourKB); + void *GetPhysical(void *VirtualAddress); + void Map(void *VirtualAddress, void *PhysicalAddress, uint64_t Flag = PTFlag::P, MapType Type = MapType::FourKB); + + __always_inline inline void Map(void *VirtualAddress, void *PhysicalAddress, size_t Length, uint64_t Flags, MapType Type = MapType::FourKB) + { + int PageSize = PAGE_SIZE_4K; + + if (Type == MapType::TwoMB) + PageSize = PAGE_SIZE_2M; + else if (Type == MapType::OneGB) + PageSize = PAGE_SIZE_1G; + + for (uintptr_t i = 0; i < Length; i += PageSize) + this->Map((void *)((uintptr_t)VirtualAddress + i), (void *)((uintptr_t)PhysicalAddress + i), Flags, Type); + } + + __always_inline inline MapType OptimizedMap(void *VirtualAddress, void *PhysicalAddress, size_t Length, uint64_t Flags, bool Fit = false, bool FailOnModulo = false) + { + if (unlikely(Fit)) + { + while (Length >= PAGE_SIZE_1G) + { + this->Map(VirtualAddress, PhysicalAddress, Length, Flags, Virtual::MapType::OneGB); + VirtualAddress = (void *)((uintptr_t)VirtualAddress + PAGE_SIZE_1G); + PhysicalAddress = (void *)((uintptr_t)PhysicalAddress + PAGE_SIZE_1G); + Length -= PAGE_SIZE_1G; + } + + while (Length >= PAGE_SIZE_2M) + { + this->Map(VirtualAddress, PhysicalAddress, Length, Flags, Virtual::MapType::TwoMB); + VirtualAddress = (void *)((uintptr_t)VirtualAddress + PAGE_SIZE_2M); + PhysicalAddress = (void *)((uintptr_t)PhysicalAddress + PAGE_SIZE_2M); + Length -= PAGE_SIZE_2M; + } + + while (Length >= PAGE_SIZE_4K) + { + this->Map(VirtualAddress, PhysicalAddress, Length, Flags, Virtual::MapType::FourKB); + VirtualAddress = (void *)((uintptr_t)VirtualAddress + PAGE_SIZE_4K); + PhysicalAddress = (void *)((uintptr_t)PhysicalAddress + PAGE_SIZE_4K); + Length -= PAGE_SIZE_4K; + } + + return Virtual::MapType::FourKB; + } + + Virtual::MapType Type = Virtual::MapType::FourKB; + + if (Length >= PAGE_SIZE_1G) + { + Type = Virtual::MapType::OneGB; + if (Length % PAGE_SIZE_1G != 0) + { + warn("Length is not a multiple of 1GB."); + if (FailOnModulo) + return Virtual::MapType::NoMapType; + } + } + else if (Length >= PAGE_SIZE_2M) + { + Type = Virtual::MapType::TwoMB; + if (Length % PAGE_SIZE_2M != 0) + { + warn("Length is not a multiple of 2MB."); + if (FailOnModulo) + return Virtual::MapType::NoMapType; + } + } + + this->Map(VirtualAddress, PhysicalAddress, Length, Flags, Type); + return Type; + } + + void Unmap(void *VirtualAddress, MapType Type = MapType::FourKB); + + __always_inline inline void Unmap(void *VirtualAddress, size_t Length, MapType Type = MapType::FourKB) + { + int PageSize = PAGE_SIZE_4K; + + if (Type == MapType::TwoMB) + PageSize = PAGE_SIZE_2M; + else if (Type == MapType::OneGB) + PageSize = PAGE_SIZE_1G; + + for (uintptr_t i = 0; i < Length; i += PageSize) + this->Unmap((void *)((uintptr_t)VirtualAddress + i), Type); + } + + __always_inline inline void Remap(void *VirtualAddress, void *PhysicalAddress, uint64_t Flags, MapType Type = MapType::FourKB) + { + this->Unmap(VirtualAddress, Type); + this->Map(VirtualAddress, PhysicalAddress, Flags, Type); + } + + Virtual(PageTable *Table = nullptr); + ~Virtual(); + }; +} + +extern Memory32::Physical KernelAllocator32; +extern Memory32::PageTable *KernelPageTable32; + +extern Memory64::Physical KernelAllocator64; +extern Memory64::PageTable *KernelPageTable64; + +void *memcpy(void *dest, const void *src, size_t n); +void *memset(void *s, int c, size_t n); +void InitializeMemoryManagement(BootInfo *Info, bool is32); diff --git a/FennixLoader/include/multiboot.h b/FennixLoader/include/multiboot.h new file mode 100644 index 00000000..683ae4b4 --- /dev/null +++ b/FennixLoader/include/multiboot.h @@ -0,0 +1,274 @@ +/* multiboot.h - Multiboot header file. */ +/* Copyright (C) 1999,2003,2007,2008,2009,2010 Free Software Foundation, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ANY + * DEVELOPER OR DISTRIBUTOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR + * IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef MULTIBOOT_HEADER +#define MULTIBOOT_HEADER 1 + +/* How many bytes from the start of the file we search for the header. */ +#define MULTIBOOT_SEARCH 8192 +#define MULTIBOOT_HEADER_ALIGN 4 + +/* The magic field should contain this. */ +#define MULTIBOOT_HEADER_MAGIC 0x1BADB002 + +/* This should be in %eax. */ +#define MULTIBOOT_BOOTLOADER_MAGIC 0x2BADB002 + +/* Alignment of multiboot modules. */ +#define MULTIBOOT_MOD_ALIGN 0x00001000 + +/* Alignment of the multiboot info structure. */ +#define MULTIBOOT_INFO_ALIGN 0x00000004 + +/* Flags set in the ’flags’ member of the multiboot header. */ + +/* Align all boot modules on i386 page (4KB) boundaries. */ +#define MULTIBOOT_PAGE_ALIGN 0x00000001 + +/* Must pass memory information to OS. */ +#define MULTIBOOT_MEMORY_INFO 0x00000002 + +/* Must pass video information to OS. */ +#define MULTIBOOT_VIDEO_MODE 0x00000004 + +/* This flag indicates the use of the address fields in the header. */ +#define MULTIBOOT_AOUT_KLUDGE 0x00010000 + +/* Flags to be set in the ’flags’ member of the multiboot info structure. */ + +/* is there basic lower/upper memory information? */ +#define MULTIBOOT_INFO_MEMORY 0x00000001 +/* is there a boot device set? */ +#define MULTIBOOT_INFO_BOOTDEV 0x00000002 +/* is the command-line defined? */ +#define MULTIBOOT_INFO_CMDLINE 0x00000004 +/* are there modules to do something with? */ +#define MULTIBOOT_INFO_MODS 0x00000008 + +/* These next two are mutually exclusive */ + +/* is there a symbol table loaded? */ +#define MULTIBOOT_INFO_AOUT_SYMS 0x00000010 +/* is there an ELF section header table? */ +#define MULTIBOOT_INFO_ELF_SHDR 0X00000020 + +/* is there a full memory map? */ +#define MULTIBOOT_INFO_MEM_MAP 0x00000040 + +/* Is there drive info? */ +#define MULTIBOOT_INFO_DRIVE_INFO 0x00000080 + +/* Is there a config table? */ +#define MULTIBOOT_INFO_CONFIG_TABLE 0x00000100 + +/* Is there a boot loader name? */ +#define MULTIBOOT_INFO_BOOT_LOADER_NAME 0x00000200 + +/* Is there a APM table? */ +#define MULTIBOOT_INFO_APM_TABLE 0x00000400 + +/* Is there video information? */ +#define MULTIBOOT_INFO_VBE_INFO 0x00000800 +#define MULTIBOOT_INFO_FRAMEBUFFER_INFO 0x00001000 + +#ifndef ASM_FILE + +typedef unsigned char multiboot_uint8_t; +typedef unsigned short multiboot_uint16_t; +typedef unsigned int multiboot_uint32_t; +typedef unsigned long long multiboot_uint64_t; + +struct multiboot_header +{ + /* Must be MULTIBOOT_MAGIC - see above. */ + multiboot_uint32_t magic; + + /* Feature flags. */ + multiboot_uint32_t flags; + + /* The above fields plus this one must equal 0 mod 2^32. */ + multiboot_uint32_t checksum; + + /* These are only valid if MULTIBOOT_AOUT_KLUDGE is set. */ + multiboot_uint32_t header_addr; + multiboot_uint32_t load_addr; + multiboot_uint32_t load_end_addr; + multiboot_uint32_t bss_end_addr; + multiboot_uint32_t entry_addr; + + /* These are only valid if MULTIBOOT_VIDEO_MODE is set. */ + multiboot_uint32_t mode_type; + multiboot_uint32_t width; + multiboot_uint32_t height; + multiboot_uint32_t depth; +}; + +/* The symbol table for a.out. */ +struct multiboot_aout_symbol_table +{ + multiboot_uint32_t tabsize; + multiboot_uint32_t strsize; + multiboot_uint32_t addr; + multiboot_uint32_t reserved; +}; +typedef struct multiboot_aout_symbol_table multiboot_aout_symbol_table_t; + +/* The section header table for ELF. */ +struct multiboot_elf_section_header_table +{ + multiboot_uint32_t num; + multiboot_uint32_t size; + multiboot_uint32_t addr; + multiboot_uint32_t shndx; +}; +typedef struct multiboot_elf_section_header_table multiboot_elf_section_header_table_t; + +struct multiboot_info +{ + /* Multiboot info version number */ + multiboot_uint32_t flags; + + /* Available memory from BIOS */ + multiboot_uint32_t mem_lower; + multiboot_uint32_t mem_upper; + + /* "root" partition */ + multiboot_uint32_t boot_device; + + /* Kernel command line */ + multiboot_uint32_t cmdline; + + /* Boot-Module list */ + multiboot_uint32_t mods_count; + multiboot_uint32_t mods_addr; + + union + { + multiboot_aout_symbol_table_t aout_sym; + multiboot_elf_section_header_table_t elf_sec; + } u; + + /* Memory Mapping buffer */ + multiboot_uint32_t mmap_length; + multiboot_uint32_t mmap_addr; + + /* Drive Info buffer */ + multiboot_uint32_t drives_length; + multiboot_uint32_t drives_addr; + + /* ROM configuration table */ + multiboot_uint32_t config_table; + + /* Boot Loader Name */ + multiboot_uint32_t boot_loader_name; + + /* APM table */ + multiboot_uint32_t apm_table; + + /* Video */ + multiboot_uint32_t vbe_control_info; + multiboot_uint32_t vbe_mode_info; + multiboot_uint16_t vbe_mode; + multiboot_uint16_t vbe_interface_seg; + multiboot_uint16_t vbe_interface_off; + multiboot_uint16_t vbe_interface_len; + + multiboot_uint64_t framebuffer_addr; + multiboot_uint32_t framebuffer_pitch; + multiboot_uint32_t framebuffer_width; + multiboot_uint32_t framebuffer_height; + multiboot_uint8_t framebuffer_bpp; +#define MULTIBOOT_FRAMEBUFFER_TYPE_INDEXED 0 +#define MULTIBOOT_FRAMEBUFFER_TYPE_RGB 1 +#define MULTIBOOT_FRAMEBUFFER_TYPE_EGA_TEXT 2 + multiboot_uint8_t framebuffer_type; + union + { + struct + { + multiboot_uint32_t framebuffer_palette_addr; + multiboot_uint16_t framebuffer_palette_num_colors; + }; + struct + { + multiboot_uint8_t framebuffer_red_field_position; + multiboot_uint8_t framebuffer_red_mask_size; + multiboot_uint8_t framebuffer_green_field_position; + multiboot_uint8_t framebuffer_green_mask_size; + multiboot_uint8_t framebuffer_blue_field_position; + multiboot_uint8_t framebuffer_blue_mask_size; + }; + }; +}; +typedef struct multiboot_info multiboot_info_t; + +struct multiboot_color +{ + multiboot_uint8_t red; + multiboot_uint8_t green; + multiboot_uint8_t blue; +}; + +struct multiboot_mmap_entry +{ + multiboot_uint32_t size; + multiboot_uint64_t addr; + multiboot_uint64_t len; +#define MULTIBOOT_MEMORY_AVAILABLE 1 +#define MULTIBOOT_MEMORY_RESERVED 2 +#define MULTIBOOT_MEMORY_ACPI_RECLAIMABLE 3 +#define MULTIBOOT_MEMORY_NVS 4 +#define MULTIBOOT_MEMORY_BADRAM 5 + multiboot_uint32_t type; +} __attribute__((packed)); +typedef struct multiboot_mmap_entry multiboot_memory_map_t; + +struct multiboot_mod_list +{ + /* the memory used goes from bytes ’mod_start’ to ’mod_end-1’ inclusive */ + multiboot_uint32_t mod_start; + multiboot_uint32_t mod_end; + + /* Module command line */ + multiboot_uint32_t cmdline; + + /* padding to take it to 16 bytes (must be zero) */ + multiboot_uint32_t pad; +}; +typedef struct multiboot_mod_list multiboot_module_t; + +/* APM BIOS info. */ +struct multiboot_apm_info +{ + multiboot_uint16_t version; + multiboot_uint16_t cseg; + multiboot_uint32_t offset; + multiboot_uint16_t cseg_16; + multiboot_uint16_t dseg; + multiboot_uint16_t flags; + multiboot_uint16_t cseg_len; + multiboot_uint16_t cseg_16_len; + multiboot_uint16_t dseg_len; +}; + +#endif /* ! ASM_FILE */ + +#endif /* ! MULTIBOOT_HEADER */ \ No newline at end of file diff --git a/FennixLoader/include/multiboot2.h b/FennixLoader/include/multiboot2.h new file mode 100644 index 00000000..bcc4d257 --- /dev/null +++ b/FennixLoader/include/multiboot2.h @@ -0,0 +1,417 @@ +/* multiboot2.h - Multiboot 2 header file. */ +/* Copyright (C) 1999,2003,2007,2008,2009,2010 Free Software Foundation, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ANY + * DEVELOPER OR DISTRIBUTOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR + * IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef MULTIBOOT_HEADER +#define MULTIBOOT_HEADER 1 + +/* How many bytes from the start of the file we search for the header. */ +#define MULTIBOOT_SEARCH 32768 +#define MULTIBOOT_HEADER_ALIGN 8 + +/* The magic field should contain this. */ +#define MULTIBOOT2_HEADER_MAGIC 0xe85250d6 + +/* This should be in %eax. */ +#define MULTIBOOT2_BOOTLOADER_MAGIC 0x36d76289 + +/* Alignment of multiboot modules. */ +#define MULTIBOOT_MOD_ALIGN 0x00001000 + +/* Alignment of the multiboot info structure. */ +#define MULTIBOOT_INFO_ALIGN 0x00000008 + +/* Flags set in the 'flags' member of the multiboot header. */ + +#define MULTIBOOT_TAG_ALIGN 8 +#define MULTIBOOT_TAG_TYPE_END 0 +#define MULTIBOOT_TAG_TYPE_CMDLINE 1 +#define MULTIBOOT_TAG_TYPE_BOOT_LOADER_NAME 2 +#define MULTIBOOT_TAG_TYPE_MODULE 3 +#define MULTIBOOT_TAG_TYPE_BASIC_MEMINFO 4 +#define MULTIBOOT_TAG_TYPE_BOOTDEV 5 +#define MULTIBOOT_TAG_TYPE_MMAP 6 +#define MULTIBOOT_TAG_TYPE_VBE 7 +#define MULTIBOOT_TAG_TYPE_FRAMEBUFFER 8 +#define MULTIBOOT_TAG_TYPE_ELF_SECTIONS 9 +#define MULTIBOOT_TAG_TYPE_APM 10 +#define MULTIBOOT_TAG_TYPE_EFI32 11 +#define MULTIBOOT_TAG_TYPE_EFI64 12 +#define MULTIBOOT_TAG_TYPE_SMBIOS 13 +#define MULTIBOOT_TAG_TYPE_ACPI_OLD 14 +#define MULTIBOOT_TAG_TYPE_ACPI_NEW 15 +#define MULTIBOOT_TAG_TYPE_NETWORK 16 +#define MULTIBOOT_TAG_TYPE_EFI_MMAP 17 +#define MULTIBOOT_TAG_TYPE_EFI_BS 18 +#define MULTIBOOT_TAG_TYPE_EFI32_IH 19 +#define MULTIBOOT_TAG_TYPE_EFI64_IH 20 +#define MULTIBOOT_TAG_TYPE_LOAD_BASE_ADDR 21 + +#define MULTIBOOT_HEADER_TAG_END 0 +#define MULTIBOOT_HEADER_TAG_INFORMATION_REQUEST 1 +#define MULTIBOOT_HEADER_TAG_ADDRESS 2 +#define MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS 3 +#define MULTIBOOT_HEADER_TAG_CONSOLE_FLAGS 4 +#define MULTIBOOT_HEADER_TAG_FRAMEBUFFER 5 +#define MULTIBOOT_HEADER_TAG_MODULE_ALIGN 6 +#define MULTIBOOT_HEADER_TAG_EFI_BS 7 +#define MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS_EFI32 8 +#define MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS_EFI64 9 +#define MULTIBOOT_HEADER_TAG_RELOCATABLE 10 + +#define MULTIBOOT_ARCHITECTURE_I386 0 +#define MULTIBOOT_ARCHITECTURE_MIPS32 4 +#define MULTIBOOT_HEADER_TAG_OPTIONAL 1 + +#define MULTIBOOT_LOAD_PREFERENCE_NONE 0 +#define MULTIBOOT_LOAD_PREFERENCE_LOW 1 +#define MULTIBOOT_LOAD_PREFERENCE_HIGH 2 + +#define MULTIBOOT_CONSOLE_FLAGS_CONSOLE_REQUIRED 1 +#define MULTIBOOT_CONSOLE_FLAGS_EGA_TEXT_SUPPORTED 2 + +#ifndef ASM_FILE + +typedef unsigned char multiboot_uint8_t; +typedef unsigned short multiboot_uint16_t; +typedef unsigned int multiboot_uint32_t; +typedef unsigned long long multiboot_uint64_t; + +struct multiboot_header +{ + /* Must be MULTIBOOT_MAGIC - see above. */ + multiboot_uint32_t magic; + + /* ISA */ + multiboot_uint32_t architecture; + + /* Total header length. */ + multiboot_uint32_t header_length; + + /* The above fields plus this one must equal 0 mod 2^32. */ + multiboot_uint32_t checksum; +}; + +struct multiboot_header_tag +{ + multiboot_uint16_t type; + multiboot_uint16_t flags; + multiboot_uint32_t size; +}; + +struct multiboot_header_tag_information_request +{ + multiboot_uint16_t type; + multiboot_uint16_t flags; + multiboot_uint32_t size; + multiboot_uint32_t requests[0]; +}; + +struct multiboot_header_tag_address +{ + multiboot_uint16_t type; + multiboot_uint16_t flags; + multiboot_uint32_t size; + multiboot_uint32_t header_addr; + multiboot_uint32_t load_addr; + multiboot_uint32_t load_end_addr; + multiboot_uint32_t bss_end_addr; +}; + +struct multiboot_header_tag_entry_address +{ + multiboot_uint16_t type; + multiboot_uint16_t flags; + multiboot_uint32_t size; + multiboot_uint32_t entry_addr; +}; + +struct multiboot_header_tag_console_flags +{ + multiboot_uint16_t type; + multiboot_uint16_t flags; + multiboot_uint32_t size; + multiboot_uint32_t console_flags; +}; + +struct multiboot_header_tag_framebuffer +{ + multiboot_uint16_t type; + multiboot_uint16_t flags; + multiboot_uint32_t size; + multiboot_uint32_t width; + multiboot_uint32_t height; + multiboot_uint32_t depth; +}; + +struct multiboot_header_tag_module_align +{ + multiboot_uint16_t type; + multiboot_uint16_t flags; + multiboot_uint32_t size; +}; + +struct multiboot_header_tag_relocatable +{ + multiboot_uint16_t type; + multiboot_uint16_t flags; + multiboot_uint32_t size; + multiboot_uint32_t min_addr; + multiboot_uint32_t max_addr; + multiboot_uint32_t align; + multiboot_uint32_t preference; +}; + +struct multiboot_color +{ + multiboot_uint8_t red; + multiboot_uint8_t green; + multiboot_uint8_t blue; +}; + +struct multiboot_mmap_entry +{ + multiboot_uint64_t addr; + multiboot_uint64_t len; +#define MULTIBOOT_MEMORY_AVAILABLE 1 +#define MULTIBOOT_MEMORY_RESERVED 2 +#define MULTIBOOT_MEMORY_ACPI_RECLAIMABLE 3 +#define MULTIBOOT_MEMORY_NVS 4 +#define MULTIBOOT_MEMORY_BADRAM 5 + multiboot_uint32_t type; + multiboot_uint32_t zero; +}; +typedef struct multiboot_mmap_entry multiboot_memory_map_t; + +struct multiboot_tag +{ + multiboot_uint32_t type; + multiboot_uint32_t size; +}; + +struct multiboot_tag_string +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + char string[0]; +}; + +struct multiboot_tag_module +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint32_t mod_start; + multiboot_uint32_t mod_end; + char cmdline[0]; +}; + +struct multiboot_tag_basic_meminfo +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint32_t mem_lower; + multiboot_uint32_t mem_upper; +}; + +struct multiboot_tag_bootdev +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint32_t biosdev; + multiboot_uint32_t slice; + multiboot_uint32_t part; +}; + +struct multiboot_tag_mmap +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint32_t entry_size; + multiboot_uint32_t entry_version; + struct multiboot_mmap_entry entries[0]; +}; + +struct multiboot_vbe_info_block +{ + multiboot_uint8_t external_specification[512]; +}; + +struct multiboot_vbe_mode_info_block +{ + multiboot_uint8_t external_specification[256]; +}; + +struct multiboot_tag_vbe +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + + multiboot_uint16_t vbe_mode; + multiboot_uint16_t vbe_interface_seg; + multiboot_uint16_t vbe_interface_off; + multiboot_uint16_t vbe_interface_len; + + struct multiboot_vbe_info_block vbe_control_info; + struct multiboot_vbe_mode_info_block vbe_mode_info; +}; + +struct multiboot_tag_framebuffer_common +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + + multiboot_uint64_t framebuffer_addr; + multiboot_uint32_t framebuffer_pitch; + multiboot_uint32_t framebuffer_width; + multiboot_uint32_t framebuffer_height; + multiboot_uint8_t framebuffer_bpp; +#define MULTIBOOT_FRAMEBUFFER_TYPE_INDEXED 0 +#define MULTIBOOT_FRAMEBUFFER_TYPE_RGB 1 +#define MULTIBOOT_FRAMEBUFFER_TYPE_EGA_TEXT 2 + multiboot_uint8_t framebuffer_type; + multiboot_uint16_t reserved; +}; + +struct multiboot_tag_framebuffer +{ + struct multiboot_tag_framebuffer_common common; + + union + { + struct + { + multiboot_uint16_t framebuffer_palette_num_colors; + struct multiboot_color framebuffer_palette[0]; + }; + struct + { + multiboot_uint8_t framebuffer_red_field_position; + multiboot_uint8_t framebuffer_red_mask_size; + multiboot_uint8_t framebuffer_green_field_position; + multiboot_uint8_t framebuffer_green_mask_size; + multiboot_uint8_t framebuffer_blue_field_position; + multiboot_uint8_t framebuffer_blue_mask_size; + }; + }; +}; + +struct multiboot_tag_elf_sections +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint32_t num; + multiboot_uint32_t entsize; + multiboot_uint32_t shndx; + char sections[0]; +}; + +struct multiboot_tag_apm +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint16_t version; + multiboot_uint16_t cseg; + multiboot_uint32_t offset; + multiboot_uint16_t cseg_16; + multiboot_uint16_t dseg; + multiboot_uint16_t flags; + multiboot_uint16_t cseg_len; + multiboot_uint16_t cseg_16_len; + multiboot_uint16_t dseg_len; +}; + +struct multiboot_tag_efi32 +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint32_t pointer; +}; + +struct multiboot_tag_efi64 +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint64_t pointer; +}; + +struct multiboot_tag_smbios +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint8_t major; + multiboot_uint8_t minor; + multiboot_uint8_t reserved[6]; + multiboot_uint8_t tables[0]; +}; + +struct multiboot_tag_old_acpi +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint8_t rsdp[0]; +}; + +struct multiboot_tag_new_acpi +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint8_t rsdp[0]; +}; + +struct multiboot_tag_network +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint8_t dhcpack[0]; +}; + +struct multiboot_tag_efi_mmap +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint32_t descr_size; + multiboot_uint32_t descr_vers; + multiboot_uint8_t efi_mmap[0]; +}; + +struct multiboot_tag_efi32_ih +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint32_t pointer; +}; + +struct multiboot_tag_efi64_ih +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint64_t pointer; +}; + +struct multiboot_tag_load_base_addr +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint32_t load_base_addr; +}; + +#endif /* ! ASM_FILE */ + +#endif /* ! MULTIBOOT_HEADER */ \ No newline at end of file diff --git a/FennixLoader/include/printf.h b/FennixLoader/include/printf.h new file mode 100644 index 00000000..52866c68 --- /dev/null +++ b/FennixLoader/include/printf.h @@ -0,0 +1,194 @@ +/** + * @author (c) Eyal Rozenberg + * 2021-2022, Haifa, Palestine/Israel + * @author (c) Marco Paland (info@paland.com) + * 2014-2019, PALANDesign Hannover, Germany + * + * @note Others have made smaller contributions to this file: see the + * contributors page at https://github.com/eyalroz/printf/graphs/contributors + * or ask one of the authors. + * + * @brief Small stand-alone implementation of the printf family of functions + * (`(v)printf`, `(v)s(n)printf` etc., geared towards use on embedded systems with + * a very limited resources. + * + * @note the implementations are thread-safe; re-entrant; use no functions from + * the standard library; and do not dynamically allocate any memory. + * + * @license The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef PRINTF_H_ +#define PRINTF_H_ + +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +#ifdef __GNUC__ +#define ATTR_PRINTF(one_based_format_index, first_arg) \ + __attribute__((format(__printf__, (one_based_format_index), (first_arg)))) +#define ATTR_VPRINTF(one_based_format_index) ATTR_PRINTF((one_based_format_index), 0) +#else +#define ATTR_PRINTF((one_based_format_index), (first_arg)) +#define ATTR_VPRINTF(one_based_format_index) +#endif + +#ifndef PRINTF_ALIAS_STANDARD_FUNCTION_NAMES +#define PRINTF_ALIAS_STANDARD_FUNCTION_NAMES 0 +#endif + +#if PRINTF_ALIAS_STANDARD_FUNCTION_NAMES +#define printf printf +#define sprintf sprintf +#define vsprintf vsprintf +#define snprintf_ snprintf +#define vsnprintf vsnprintf +#define vprintf vprintf +#endif + +// If you want to include this implementation file directly rather than +// link against, this will let you control the functions' visibility, +// e.g. make them static so as not to clash with other objects also +// using them. +#ifndef PRINTF_VISIBILITY +#define PRINTF_VISIBILITY +#endif + + /** + * Prints/send a single character to some opaque output entity + * + * @note This function is not implemented by the library, only declared; you must provide an + * implementation if you wish to use the @ref printf / @ref vprintf function (and possibly + * for linking against the library, if your toolchain does not support discarding unused functions) + * + * @note The output could be as simple as a wrapper for the `write()` system call on a Unix-like + * system, or even libc's @ref putchar , for replicating actual functionality of libc's @ref printf + * function; but on an embedded system it may involve interaction with a special output device, + * like a UART, etc. + * + * @note in libc's @ref putchar, the parameter type is an int; this was intended to support the + * representation of either a proper character or EOF in a variable - but this is really not + * meaningful to pass into @ref putchar and is discouraged today. See further discussion in: + * @link https://stackoverflow.com/q/17452847/1593077 + * + * @param c the single character to print + */ + // PRINTF_VISIBILITY + // void putchar(char c); + + /** + * An implementation of the C standard's printf/vprintf + * + * @note you must implement a @ref putchar_ function for using this function - it invokes @ref putchar_ + * rather than directly performing any I/O (which insulates it from any dependence on the operating system + * and external libraries). + * + * @param format A string specifying the format of the output, with %-marked specifiers of how to interpret + * additional arguments. + * @param arg Additional arguments to the function, one for each %-specifier in @p format string + * @return The number of characters written into @p s, not counting the terminating null character + */ + ///@{ + PRINTF_VISIBILITY + int printf(const char *format, ...) ATTR_PRINTF(1, 2); + PRINTF_VISIBILITY + int vprintf(const char *format, va_list arg) ATTR_VPRINTF(1); + ///@} + + /** + * An implementation of the C standard's sprintf/vsprintf + * + * @note For security considerations (the potential for exceeding the buffer bounds), please consider using + * the size-constrained variant, @ref snprintf / @ref vsnprintf , instead. + * + * @param s An array in which to store the formatted string. It must be large enough to fit the formatted + * output! + * @param format A string specifying the format of the output, with %-marked specifiers of how to interpret + * additional arguments. + * @param arg Additional arguments to the function, one for each specifier in @p format + * @return The number of characters written into @p s, not counting the terminating null character + */ + ///@{ + PRINTF_VISIBILITY + int sprintf(char *s, const char *format, ...) ATTR_PRINTF(2, 3); + PRINTF_VISIBILITY + int vsprintf(char *s, const char *format, va_list arg) ATTR_VPRINTF(2); + ///@} + + /** + * An implementation of the C standard's snprintf/vsnprintf + * + * @param s An array in which to store the formatted string. It must be large enough to fit either the + * entire formatted output, or at least @p n characters. Alternatively, it can be NULL, in which case + * nothing will be printed, and only the number of characters which _could_ have been printed is + * tallied and returned. + * @param n The maximum number of characters to write to the array, including a terminating null character + * @param format A string specifying the format of the output, with %-marked specifiers of how to interpret + * additional arguments. + * @param arg Additional arguments to the function, one for each specifier in @p format + * @return The number of characters that COULD have been written into @p s, not counting the terminating + * null character. A value equal or larger than @p n indicates truncation. Only when the returned value + * is non-negative and less than @p n, the null-terminated string has been fully and successfully printed. + */ + ///@{ + PRINTF_VISIBILITY + int snprintf(char *s, size_t count, const char *format, ...) ATTR_PRINTF(3, 4); + PRINTF_VISIBILITY + int vsnprintf(char *s, size_t count, const char *format, va_list arg) ATTR_VPRINTF(3); + ///@} + + /** + * printf/vprintf with user-specified output function + * + * An alternative to @ref printf, in which the output function is specified dynamically + * (rather than @ref putchar_ being used) + * + * @param out An output function which takes one character and a type-erased additional parameters + * @param extra_arg The type-erased argument to pass to the output function @p out with each call + * @param format A string specifying the format of the output, with %-marked specifiers of how to interpret + * additional arguments. + * @param arg Additional arguments to the function, one for each specifier in @p format + * @return The number of characters for which the output f unction was invoked, not counting the terminating null character + * + */ + PRINTF_VISIBILITY + int fctprintf(void (*out)(char c, void *extra_arg), void *extra_arg, const char *format, ...) ATTR_PRINTF(3, 4); + PRINTF_VISIBILITY + int vfctprintf(void (*out)(char c, void *extra_arg), void *extra_arg, const char *format, va_list arg) ATTR_VPRINTF(3); + +#if PRINTF_ALIAS_STANDARD_FUNCTION_NAMES +#undef printf +#undef sprintf +#undef vsprintf +#undef snprintf_ +#undef vsnprintf +#undef vprintf +#endif + +#ifdef __cplusplus +} +#endif + +#endif // PRINTF_H_ \ No newline at end of file diff --git a/FennixLoader/include/types.h b/FennixLoader/include/types.h new file mode 100644 index 00000000..b5df5ee4 --- /dev/null +++ b/FennixLoader/include/types.h @@ -0,0 +1,343 @@ +#ifndef __FENNIX_KERNEL_TYPES_H__ +#define __FENNIX_KERNEL_TYPES_H__ + +#ifdef __cplusplus +#define EXTERNC extern "C" +#define START_EXTERNC \ + EXTERNC \ + { +#define END_EXTERNC \ + } +#else // __cplusplus +#define EXTERNC +#define START_EXTERNC +#define END_EXTERNC +#endif // __cplusplus + +#ifdef __cplusplus +#define NULL 0 +#else // __cplusplus +#define NULL ((void *)0) +#define bool _Bool +#endif // __cplusplus + +#define asm __asm__ +#define asmv __asm__ volatile + +#define true 1 +#define false 0 + +#define inf_loop while (1) +#define ilp inf_loop; /* Used for debugging */ + +#ifdef __cplusplus +#define foreach for +#define in : + +#define r_cst(t, v) reinterpret_cast(v) +#define c_cst(t, v) const_cast(v) +#define s_cst(t, v) static_cast(v) +#define d_cst(t, v) dynamic_cast(v) +#endif // __cplusplus + +#define UNUSED(x) (void)(x) +#define CONCAT(x, y) x##y + +#ifndef __cplusplus /* This conflicts with std */ +#define toupper(c) ((c)-0x20 * (((c) >= 'a') && ((c) <= 'z'))) +#define tolower(c) ((c) + 0x20 * (((c) >= 'A') && ((c) <= 'Z'))) +#endif + +#ifndef __va_list__ +typedef __builtin_va_list va_list; +#endif + +#define va_start(v, l) __builtin_va_start(v, l) +#define va_end(v) __builtin_va_end(v) +#define va_arg(v, l) __builtin_va_arg(v, l) + +#define ALIGN_UP(x, align) ((__typeof__(x))(((uintptr_t)(x) + ((align)-1)) & (~((align)-1)))) +#define ALIGN_DOWN(x, align) ((__typeof__(x))((x) & (~((align)-1)))) + +#define offsetof(type, member) __builtin_offsetof(type, member) + +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) + +#define VPOKE(type, address) (*((volatile type *)(address))) +#define POKE(type, address) (*((type *)(address))) + +#ifndef __cplusplus + +#ifdef __STDC__ +#ifdef __STDC_VERSION__ +#if (__STDC_VERSION__ >= 201710L) +#define C_LANGUAGE_STANDARD 2018 +#elif (__STDC_VERSION__ >= 201112L) +#define C_LANGUAGE_STANDARD 2011 +#elif (__STDC_VERSION__ >= 199901L) +#define C_LANGUAGE_STANDARD 1999 +#elif (__STDC_VERSION__ >= 199409L) +#define C_LANGUAGE_STANDARD 1995 +#endif +#else +#define C_LANGUAGE_STANDARD 1990 +#endif +#else +#define C_LANGUAGE_STANDARD 1972 +#endif + +#else + +#ifdef __STDC__ +#ifdef __cplusplus +#if (__cplusplus >= 202100L) +#define CPP_LANGUAGE_STANDARD 2023 +#elif (__cplusplus >= 202002L) +#define CPP_LANGUAGE_STANDARD 2020 +#elif (__cplusplus >= 201703L) +#define CPP_LANGUAGE_STANDARD 2017 +#elif (__cplusplus >= 201402L) +#define CPP_LANGUAGE_STANDARD 2014 +#elif (__cplusplus >= 201103L) +#define CPP_LANGUAGE_STANDARD 2011 +#elif (__cplusplus >= 199711L) +#define CPP_LANGUAGE_STANDARD 1998 +#endif +#else +#define CPP_LANGUAGE_STANDARD __cplusplus +#endif +#else +#define CPP_LANGUAGE_STANDARD __cplusplus +#endif + +#endif // __cplusplus + +#ifndef __SIG_ATOMIC_TYPE__ +#define __SIG_ATOMIC_TYPE__ int +#endif + +typedef __INT8_TYPE__ int8_t; +typedef __INT16_TYPE__ int16_t; +typedef __INT32_TYPE__ int32_t; +typedef __INT64_TYPE__ int64_t; + +typedef __UINT8_TYPE__ uint8_t; +typedef __UINT16_TYPE__ uint16_t; +typedef __UINT32_TYPE__ uint32_t; +typedef __UINT64_TYPE__ uint64_t; + +typedef __INT_LEAST8_TYPE__ int_least8_t; +typedef __INT_LEAST16_TYPE__ int_least16_t; +typedef __INT_LEAST32_TYPE__ int_least32_t; +typedef __INT_LEAST64_TYPE__ int_least64_t; + +typedef __UINT_LEAST8_TYPE__ uint_least8_t; +typedef __UINT_LEAST16_TYPE__ uint_least16_t; +typedef __UINT_LEAST32_TYPE__ uint_least32_t; +typedef __UINT_LEAST64_TYPE__ uint_least64_t; + +typedef __INT_FAST8_TYPE__ int_fast8_t; +typedef __INT_FAST16_TYPE__ int_fast16_t; +typedef __INT_FAST32_TYPE__ int_fast32_t; +typedef __INT_FAST64_TYPE__ int_fast64_t; + +typedef __UINT_FAST8_TYPE__ uint_fast8_t; +typedef __UINT_FAST16_TYPE__ uint_fast16_t; +typedef __UINT_FAST32_TYPE__ uint_fast32_t; +typedef __UINT_FAST64_TYPE__ uint_fast64_t; + +typedef __INTPTR_TYPE__ intptr_t; +typedef __UINTPTR_TYPE__ uintptr_t; + +typedef __INTMAX_TYPE__ intmax_t; +typedef __UINTMAX_TYPE__ uintmax_t; + +typedef __PTRDIFF_TYPE__ ptrdiff_t; +typedef __SIZE_TYPE__ size_t; +#ifndef __cplusplus +typedef __WCHAR_TYPE__ wchar_t; +#endif +typedef __WINT_TYPE__ wint_t; +typedef __SIG_ATOMIC_TYPE__ sig_atomic_t; +// TODO: ssize_t +typedef intptr_t ssize_t; + +#define INT8_MAX __INT8_MAX__ +#define INT8_MIN (-INT8_MAX - 1) +#define UINT8_MAX __UINT8_MAX__ +#define INT16_MAX __INT16_MAX__ +#define INT16_MIN (-INT16_MAX - 1) +#define UINT16_MAX __UINT16_MAX__ +#define INT32_MAX __INT32_MAX__ +#define INT32_MIN (-INT32_MAX - 1) +#define UINT32_MAX __UINT32_MAX__ +#define INT64_MAX __INT64_MAX__ +#define INT64_MIN (-INT64_MAX - 1) +#define UINT64_MAX __UINT64_MAX__ + +#define INT_LEAST8_MAX __INT_LEAST8_MAX__ +#define INT_LEAST8_MIN (-INT_LEAST8_MAX - 1) +#define UINT_LEAST8_MAX __UINT_LEAST8_MAX__ +#define INT_LEAST16_MAX __INT_LEAST16_MAX__ +#define INT_LEAST16_MIN (-INT_LEAST16_MAX - 1) +#define UINT_LEAST16_MAX __UINT_LEAST16_MAX__ +#define INT_LEAST32_MAX __INT_LEAST32_MAX__ +#define INT_LEAST32_MIN (-INT_LEAST32_MAX - 1) +#define UINT_LEAST32_MAX __UINT_LEAST32_MAX__ +#define INT_LEAST64_MAX __INT_LEAST64_MAX__ +#define INT_LEAST64_MIN (-INT_LEAST64_MAX - 1) +#define UINT_LEAST64_MAX __UINT_LEAST64_MAX__ + +#define INT_FAST8_MAX __INT_FAST8_MAX__ +#define INT_FAST8_MIN (-INT_FAST8_MAX - 1) +#define UINT_FAST8_MAX __UINT_FAST8_MAX__ +#define INT_FAST16_MAX __INT_FAST16_MAX__ +#define INT_FAST16_MIN (-INT_FAST16_MAX - 1) +#define UINT_FAST16_MAX __UINT_FAST16_MAX__ +#define INT_FAST32_MAX __INT_FAST32_MAX__ +#define INT_FAST32_MIN (-INT_FAST32_MAX - 1) +#define UINT_FAST32_MAX __UINT_FAST32_MAX__ +#define INT_FAST64_MAX __INT_FAST64_MAX__ +#define INT_FAST64_MIN (-INT_FAST64_MAX - 1) +#define UINT_FAST64_MAX __UINT_FAST64_MAX__ + +#define INTPTR_MAX __INTPTR_MAX__ +#define INTPTR_MIN (-INTPTR_MAX - 1) +#define UINTPTR_MAX __UINTPTR_MAX__ + +#define INTMAX_MAX __INTMAX_MAX__ +#define INTMAX_MIN (-INTMAX_MAX - 1) +#define UINTMAX_MAX __UINTMAX_MAX__ + +#define PTRDIFF_MAX __PTRDIFF_MAX__ +#define PTRDIFF_MIN (-PTRDIFF_MAX - 1) + +#define SIG_ATOMIC_MAX __SIG_ATOMIC_MAX__ +#define SIG_ATOMIC_MIN __SIG_ATOMIC_MIN__ + +#define SIZE_MAX __SIZE_MAX__ + +#define WCHAR_MAX __WCHAR_MAX__ +#define WCHAR_MIN __WCHAR_MIN__ + +#define WINT_MAX __WINT_MAX__ +#define WINT_MIN __WINT_MIN__ + +#define BREAK __asm__ __volatile__("int $0x3" \ + : \ + : \ + : "memory"); + +#ifdef __INT48_TYPE__ +typedef __INT48_TYPE__ int48_t; +typedef __UINT48_TYPE__ uint48_t; +typedef int48_t int_least48_t; +typedef uint48_t uint_least48_t; +typedef int48_t int_fast48_t; +typedef uint48_t uint_fast48_t; +#else // __INT48_TYPE__ +typedef __INT64_TYPE__ int48_t; +typedef __UINT64_TYPE__ uint48_t; +typedef int48_t int_least48_t; +typedef uint48_t uint_least48_t; +typedef int48_t int_fast48_t; +typedef uint48_t uint_fast48_t; +#endif // __INT48_TYPE__ + +#define b4(x) ((x & 0x0F) << 4 | (x & 0xF0) >> 4) +#define b8(x) ((x)&0xFF) +#define b16(x) __builtin_bswap16(x) +#define b32(x) __builtin_bswap32(x) +#define b48(x) (((((x)&0x0000000000ff) << 40) | \ + (((x)&0x00000000ff00) << 24) | \ + (((x)&0x000000ff0000) << 8) | \ + (((x)&0x0000ff000000) >> 8) | \ + (((x)&0x00ff00000000) >> 24) | \ + (((x)&0xff0000000000) >> 40))) +#define b64(x) __builtin_bswap64(x) + +/* https://gcc.gnu.org/onlinedocs/gcc-9.5.0/gnat_ugn/Optimization-Levels.html */ + +/** @brief No optimization (the default); generates unoptimized code but has the fastest compilation time. */ +#define O0 __attribute__((optimize("O0"))) +/** @brief Moderate optimization; optimizes reasonably well but does not degrade compilation time significantly. */ +#define O1 __attribute__((optimize("O1"))) +/** @brief Full optimization; generates highly optimized code and has the slowest compilation time. */ +#define O2 __attribute__((optimize("O2"))) +/** @brief Full optimization as in -O2; also uses more aggressive automatic inlining of subprograms within a unit (Inlining of Subprograms) and attempts to vectorize loops. */ +#define O3 __attribute__((optimize("O3"))) +/** @brief Optimize space usage (code and data) of resulting program. */ +#define Os __attribute__((optimize("Os"))) +/** @brief Disregard strict standards compliance. -Ofast enables all -O3 optimizations. It also enables optimizations that are not valid for all standard-compliant programs. */ +#define Ofast __attribute__((optimize("Ofast"))) + +#define __unused __attribute__((unused)) +#define __packed __attribute__((packed)) +#define __naked __attribute__((naked)) +#define __aligned(x) __attribute__((aligned(x))) +#define __section(x) __attribute__((section(x))) +#define __noreturn __attribute__((noreturn)) +#define __weak __attribute__((weak)) +#define __alias(x) __attribute__((alias(x))) +#define __always_inline __attribute__((always_inline)) +#define __noinline __attribute__((noinline)) +#define __pure __attribute__((pure)) +#define __const __attribute__((const)) +#define __malloc __attribute__((malloc)) +#define __returns_twice __attribute__((returns_twice)) +#define __used __attribute__((used)) +#define __deprecated __attribute__((deprecated)) +#define __deprecated_msg(x) __attribute__((deprecated(x))) +#define __weakref(x) __attribute__((weakref(x))) +#define __weakrefalias(x) __attribute__((weakref(#x))) +#define __visibility(x) __attribute__((visibility(x))) +#define __constructor __attribute__((constructor)) +#define __destructor __attribute__((destructor)) +#define __cleanup(x) __attribute__((cleanup(x))) +#define __fallthrough __attribute__((fallthrough)) +#define __nonnull(x) __attribute__((nonnull x)) +#define __nonnull_all __attribute__((nonnull)) +#define __returns_nonnull __attribute__((returns_nonnull)) +#define __sentinel __attribute__((sentinel)) +#define __sentinel_all __attribute__((sentinel(0))) +#define __format(x, y, z) __attribute__((format(x, y, z))) +#define __format_arg(x) __attribute__((format_arg(x))) +#define __nonnull_params(x) __attribute__((nonnull x)) +#define __nonnull_all __attribute__((nonnull)) +#define __warn_unused_result __attribute__((warn_unused_result)) +#define __no_stack_protector __attribute__((no_stack_protector)) +#define __no_instrument_function __attribute__((no_instrument_function)) +#define __no_debug __attribute__((no_debug)) +#define __target(x) __attribute__((target(x))) +#define __min_vector_width(x) __attribute__((min_vector_width(x))) + +// sanitizer +#define __no_sanitize_address __attribute__((no_sanitize_address)) +#define __no_sanitize_undefined __attribute__((no_sanitize_undefined)) +#define __no_address_safety_analysis __attribute__((no_address_safety_analysis)) +#define __no_sanitize_thread __attribute__((no_sanitize_thread)) + +#define __synchronize __sync_synchronize() +#define __sync __synchronize + +#define likely(x) __builtin_expect(!!(x), 1) +#define unlikely(x) __builtin_expect(!!(x), 0) + +#define SafeFunction __no_stack_protector __no_sanitize_address __no_sanitize_undefined __no_address_safety_analysis __no_sanitize_thread + +#define NIF __no_instrument_function + +#define int1 \ + __asm__ __volatile__("int $0x1" \ + : \ + : \ + : "memory") + +#define int3 \ + __asm__ __volatile__("int3" \ + : \ + : \ + : "memory") + +#endif // !__FENNIX_KERNEL_TYPES_H__ diff --git a/FennixLoader/include/uart.hpp b/FennixLoader/include/uart.hpp new file mode 100644 index 00000000..0b1cee3c --- /dev/null +++ b/FennixLoader/include/uart.hpp @@ -0,0 +1,34 @@ +#ifndef __FENNIX_KERNEL_UART_H__ +#define __FENNIX_KERNEL_UART_H__ + +#include + +namespace UniversalAsynchronousReceiverTransmitter +{ + enum SerialPorts + { + COMNULL = 0, + COM1 = 0x3F8, + COM2 = 0x2F8, + COM3 = 0x3E8, + COM4 = 0x2E8, + COM5 = 0x5F8, + COM6 = 0x4F8, + COM7 = 0x5E8, + COM8 = 0x4E8 + }; + + class UART + { + private: + SerialPorts Port; + + public: + UART(SerialPorts Port = COMNULL); + ~UART(); + void Write(uint8_t Char); + uint8_t Read(); + }; +} + +#endif // !__FENNIX_KERNEL_UART_H__ diff --git a/FennixLoader/linker.ld b/FennixLoader/linker.ld new file mode 100644 index 00000000..5cc8c02d --- /dev/null +++ b/FennixLoader/linker.ld @@ -0,0 +1,54 @@ +ENTRY(_start) + +SECTIONS +{ + . = 0x100000; + _kernel_start = .; + + .text BLOCK(4K) : ALIGN(4K) + { + *(.multiboot) + *(.multiboot2) + *(.text) + } + + .rodata BLOCK(4K) : ALIGN(4K) + { + *(.rodata) + } + + .data BLOCK(4K) : ALIGN(4K) + { + *(.data) + } + + .init_array : + { + PROVIDE_HIDDEN(__init_array_start = .); + KEEP(*(.init_array .ctors)) + KEEP(*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*))) + PROVIDE_HIDDEN (__init_array_end = .); + } + + .fini_array : + { + PROVIDE_HIDDEN(__fini_array_start = .); + KEEP(*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*))) + KEEP(*(.fini_array .dtors)) + PROVIDE_HIDDEN (__fini_array_end = .); + } + + .bss BLOCK(4K) : ALIGN(4K) + { + *(COMMON) + *(.bss) + } + + /DISCARD/ : + { + *(.comment*) + *(.note*) + } + . += CONSTANT(MAXPAGESIZE); + _kernel_end = ALIGN(CONSTANT(MAXPAGESIZE)); +} diff --git a/FennixLoader/loadelf.hpp b/FennixLoader/loadelf.hpp new file mode 100644 index 00000000..8c35b0ae --- /dev/null +++ b/FennixLoader/loadelf.hpp @@ -0,0 +1,4 @@ +#pragma once +#include + +bool LoadElfInMemory(void *Address, size_t Length, bool Allow64); diff --git a/FennixLoader/printf.c b/FennixLoader/printf.c new file mode 100644 index 00000000..7032a34d --- /dev/null +++ b/FennixLoader/printf.c @@ -0,0 +1,1592 @@ +/** + * @author (c) Eyal Rozenberg + * 2021-2022, Haifa, Palestine/Israel + * @author (c) Marco Paland (info@paland.com) + * 2014-2019, PALANDesign Hannover, Germany + * + * @note Others have made smaller contributions to this file: see the + * contributors page at https://github.com/eyalroz/printf/graphs/contributors + * or ask one of the authors. The original code for exponential specifiers was + * contributed by Martijn Jasperse . + * + * @brief Small stand-alone implementation of the printf family of functions + * (`(v)printf`, `(v)s(n)printf` etc., geared towards use on embedded systems with + * a very limited resources. + * + * @note the implementations are thread-safe; re-entrant; use no functions from + * the standard library; and do not dynamically allocate any memory. + * + * @license The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#pragma GCC diagnostic ignored "-Wfloat-equal" + +// Define this globally (e.g. gcc -DPRINTF_INCLUDE_CONFIG_H=1 ...) to include the +// printf_config.h header file +#if PRINTF_INCLUDE_CONFIG_H +#include "printf_config.h" +#endif + +#include + +#ifdef __cplusplus +#include +#include +#else +#include +#include +#endif // __cplusplus + +#if PRINTF_ALIAS_STANDARD_FUNCTION_NAMES +#define printf printf +#define sprintf sprintf +#define vsprintf vsprintf +#define snprintf_ snprintf +#define vsnprintf vsnprintf +#define vprintf vprintf +#endif + +// '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 + +// size of the fixed (on-stack) buffer for printing individual decimal numbers. +// this must be big enough to hold one converted floating-point value including +// padded zeros. +#ifndef PRINTF_DECIMAL_BUFFER_SIZE +#define PRINTF_DECIMAL_BUFFER_SIZE 32 +#endif + +// Support for the decimal notation floating point conversion specifiers (%f, %F) +#ifndef PRINTF_SUPPORT_DECIMAL_SPECIFIERS +#define PRINTF_SUPPORT_DECIMAL_SPECIFIERS 1 +#endif + +// Support for the exponential notation floating point conversion specifiers (%e, %g, %E, %G) +#ifndef PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS +#define PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS 1 +#endif + +// Support for the length write-back specifier (%n) +#ifndef PRINTF_SUPPORT_WRITEBACK_SPECIFIER +#define PRINTF_SUPPORT_WRITEBACK_SPECIFIER 1 +#endif + +// Default precision for the floating point conversion specifiers (the C standard sets this at 6) +#ifndef PRINTF_DEFAULT_FLOAT_PRECISION +#define PRINTF_DEFAULT_FLOAT_PRECISION 6 +#endif + +// According to the C languages standard, printf() and related functions must be able to print any +// integral number in floating-point notation, regardless of length, when using the %f specifier - +// possibly hundreds of characters, potentially overflowing your buffers. In this implementation, +// all values beyond this threshold are switched to exponential notation. +#ifndef PRINTF_MAX_INTEGRAL_DIGITS_FOR_DECIMAL +#define PRINTF_MAX_INTEGRAL_DIGITS_FOR_DECIMAL 9 +#endif + +// Support for the long long integral types (with the ll, z and t length modifiers for specifiers +// %d,%i,%o,%x,%X,%u, and with the %p specifier). Note: 'L' (long double) is not supported. +#ifndef PRINTF_SUPPORT_LONG_LONG +#define PRINTF_SUPPORT_LONG_LONG 1 +#endif + +// The number of terms in a Taylor series expansion of log_10(x) to +// use for approximation - including the power-zero term (i.e. the +// value at the point of expansion). +#ifndef PRINTF_LOG10_TAYLOR_TERMS +#define PRINTF_LOG10_TAYLOR_TERMS 4 +#endif + +#if PRINTF_LOG10_TAYLOR_TERMS <= 1 +#error "At least one non-constant Taylor expansion is necessary for the log10() calculation" +#endif + +// Be extra-safe, and don't assume format specifiers are completed correctly +// before the format string end. +#ifndef PRINTF_CHECK_FOR_NUL_IN_FORMAT_SPECIFIER +#define PRINTF_CHECK_FOR_NUL_IN_FORMAT_SPECIFIER 1 +#endif + +#define PRINTF_PREFER_DECIMAL false +#define PRINTF_PREFER_EXPONENTIAL true + +/////////////////////////////////////////////////////////////////////////////// + +// 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 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) +// 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; + +#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 + +// The printf()-family functions return an `int`; it is therefore +// unnecessary/inappropriate to use size_t - often larger than int +// in practice - for non-negative related values, such as widths, +// precisions, offsets into buffers used for printing and the sizes +// of these buffers. instead, we use: +typedef unsigned int printf_size_t; +#define PRINTF_MAX_POSSIBLE_BUFFER_SIZE INT_MAX +// If we were to nitpick, this would actually be INT_MAX + 1, +// since INT_MAX is the maximum return value, which excludes the +// trailing '\0'. + +#if (PRINTF_SUPPORT_DECIMAL_SPECIFIERS || PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS) +#include +#if FLT_RADIX != 2 +#error "Non-binary-radix floating-point types are unsupported." +#endif + +#if DBL_MANT_DIG == 24 + +#define DOUBLE_SIZE_IN_BITS 32 +typedef uint32_t double_uint_t; +#define DOUBLE_EXPONENT_MASK 0xFFU +#define DOUBLE_BASE_EXPONENT 127 +#define DOUBLE_MAX_SUBNORMAL_EXPONENT_OF_10 -38 +#define DOUBLE_MAX_SUBNORMAL_POWER_OF_10 1e-38 + +#elif DBL_MANT_DIG == 53 + +#define DOUBLE_SIZE_IN_BITS 64 +typedef uint64_t double_uint_t; +#define DOUBLE_EXPONENT_MASK 0x7FFU +#define DOUBLE_BASE_EXPONENT 1023 +#define DOUBLE_MAX_SUBNORMAL_EXPONENT_OF_10 -308 +#define DOUBLE_MAX_SUBNORMAL_POWER_OF_10 1e-308 + +#else +#error "Unsupported double type configuration" +#endif +#define DOUBLE_STORED_MANTISSA_BITS (DBL_MANT_DIG - 1) + +typedef union +{ + double_uint_t U; + double F; +} double_with_bit_access; + +// This is unnecessary in C99, since compound initializers can be used, +// but: +// 1. Some compilers are finicky about this; +// 2. Some people may want to convert this to C89; +// 3. If you try to use it as C++, only C++20 supports compound literals +static inline NIF double_with_bit_access get_bit_access(double x) +{ + double_with_bit_access dwba; + dwba.F = x; + return dwba; +} + +static inline NIF int get_sign_bit(double x) +{ + // The sign is stored in the highest bit + return (int)(get_bit_access(x).U >> (DOUBLE_SIZE_IN_BITS - 1)); +} + +static inline int get_exp2(double_with_bit_access x) +{ + // The exponent in an IEEE-754 floating-point number occupies a contiguous + // sequence of bits (e.g. 52..62 for 64-bit doubles), but with a non-trivial representation: An + // unsigned offset from some negative value (with the extremal offset values reserved for + // special use). + return (int)((x.U >> DOUBLE_STORED_MANTISSA_BITS) & DOUBLE_EXPONENT_MASK) - DOUBLE_BASE_EXPONENT; +} +#define PRINTF_ABS(_x) ((_x) > 0 ? (_x) : -(_x)) + +#endif // (PRINTF_SUPPORT_DECIMAL_SPECIFIERS || PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS) + +// Note in particular the behavior here on LONG_MIN or LLONG_MIN; it is valid +// and well-defined, but if you're not careful you can easily trigger undefined +// behavior with -LONG_MIN or -LLONG_MIN +#define ABS_FOR_PRINTING(_x) ((printf_unsigned_value_t)((_x) > 0 ? (_x) : -((printf_signed_value_t)_x))) + +// wrapper (used as buffer) for output function type +// +// One of the following must hold: +// 1. max_chars is 0 +// 2. buffer is non-null +// 3. function is non-null +// +// ... otherwise bad things will happen. +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; + +// 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 NIF 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; + } +} + +// Possibly-write the string-terminating '\0' character +static inline NIF 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'; +} + +extern void putchar(char c); + +// We can't use putchar_ as is, since our output gadget +// only takes pointers to functions with an extra argument +static inline NIF void putchar_wrapper(char c, void *unused) +{ + putchar(c); + UNUSED(unused); +} + +static inline NIF 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 NIF 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 NIF 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 NIF output_gadget_t extern_putchar_gadget(void) +{ + return function_gadget(putchar_wrapper, NULL); +} + +// internal secure strlen +// @return The length of the string (excluding the terminating 0) limited by 'maxsize' +// @note strlen uses size_t, but wes only use this function with printf_size_t +// variables - hence the signature. +static inline NIF 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); +} + +// internal test if char is a digit (0-9) +// @return true if char is a digit +static inline NIF bool is_digit_(char ch) +{ + return (ch >= '0') && (ch <= '9'); +} + +// internal ASCII string to printf_size_t conversion +static NIF 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 NIF 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 NIF 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 NIF 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); +} + +#if (PRINTF_SUPPORT_DECIMAL_SPECIFIERS || PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS) + +// Stores a fixed-precision representation of a double relative +// to a fixed precision (which cannot be determined by examining this structure) +struct double_components +{ + int_fast64_t integral; + int_fast64_t fractional; + // ... truncation of the actual fractional part of the double value, scaled + // by the precision value + bool is_negative; +}; + +#define NUM_DECIMAL_DIGITS_IN_INT64_T 18 +#define PRINTF_MAX_PRECOMPUTED_POWER_OF_10 NUM_DECIMAL_DIGITS_IN_INT64_T +static const double powers_of_10[NUM_DECIMAL_DIGITS_IN_INT64_T] = { + 1e00, 1e01, 1e02, 1e03, 1e04, 1e05, 1e06, 1e07, 1e08, + 1e09, 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17}; + +#define PRINTF_MAX_SUPPORTED_PRECISION NUM_DECIMAL_DIGITS_IN_INT64_T - 1 + +// Break up a double number - which is known to be a finite non-negative number - +// into its base-10 parts: integral - before the decimal point, and fractional - after it. +// Taken the precision into account, but does not change it even internally. +static struct NIF double_components get_components(double number, printf_size_t precision) +{ + struct double_components number_; + number_.is_negative = get_sign_bit(number); + double abs_number = (number_.is_negative) ? -number : number; + number_.integral = (int_fast64_t)abs_number; + double remainder = (abs_number - (double)number_.integral) * powers_of_10[precision]; + number_.fractional = (int_fast64_t)remainder; + + remainder -= (double)number_.fractional; + + if (remainder > 0.5) + { + ++number_.fractional; + // handle rollover, e.g. case 0.99 with precision 1 is 1.0 + if ((double)number_.fractional >= powers_of_10[precision]) + { + number_.fractional = 0; + ++number_.integral; + } + } + else if ((remainder == 0.5) && ((number_.fractional == 0U) || (number_.fractional & 1U))) + { + // if halfway, round up if odd OR if last digit is 0 + ++number_.fractional; + } + + if (precision == 0U) + { + remainder = abs_number - (double)number_.integral; + if ((!(remainder < 0.5) || (remainder > 0.5)) && (number_.integral & 1)) + { + // exactly 0.5 and ODD, then round up + // 1.5 -> 2, but 2.5 -> 2 + ++number_.integral; + } + } + return number_; +} + +#if PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS +struct scaling_factor +{ + double raw_factor; + bool multiply; // if true, need to multiply by raw_factor; otherwise need to divide by it +}; + +static double apply_scaling(double num, struct scaling_factor normalization) +{ + return normalization.multiply ? num * normalization.raw_factor : num / normalization.raw_factor; +} + +static double unapply_scaling(double normalized, struct scaling_factor normalization) +{ +#ifdef __GNUC__ +// accounting for a static analysis bug in GCC 6.x and earlier +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#endif + return normalization.multiply ? normalized / normalization.raw_factor : normalized * normalization.raw_factor; +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif +} + +static struct scaling_factor update_normalization(struct scaling_factor sf, double extra_multiplicative_factor) +{ + struct scaling_factor result; + if (sf.multiply) + { + result.multiply = true; + result.raw_factor = sf.raw_factor * extra_multiplicative_factor; + } + else + { + int factor_exp2 = get_exp2(get_bit_access(sf.raw_factor)); + int extra_factor_exp2 = get_exp2(get_bit_access(extra_multiplicative_factor)); + + // Divide the larger-exponent raw raw_factor by the smaller + if (PRINTF_ABS(factor_exp2) > PRINTF_ABS(extra_factor_exp2)) + { + result.multiply = false; + result.raw_factor = sf.raw_factor / extra_multiplicative_factor; + } + else + { + result.multiply = true; + result.raw_factor = extra_multiplicative_factor / sf.raw_factor; + } + } + return result; +} + +static struct double_components get_normalized_components(bool negative, printf_size_t precision, double non_normalized, struct scaling_factor normalization, int floored_exp10) +{ + struct double_components components; + components.is_negative = negative; + double scaled = apply_scaling(non_normalized, normalization); + + bool close_to_representation_extremum = ((-floored_exp10 + (int)precision) >= DBL_MAX_10_EXP - 1); + if (close_to_representation_extremum) + { + // We can't have a normalization factor which also accounts for the precision, i.e. moves + // some decimal digits into the mantissa, since it's unrepresentable, or nearly unrepresentable. + // So, we'll give up early on getting extra precision... + return get_components(negative ? -scaled : scaled, precision); + } + components.integral = (int_fast64_t)scaled; + double remainder = non_normalized - unapply_scaling((double)components.integral, normalization); + double prec_power_of_10 = powers_of_10[precision]; + struct scaling_factor account_for_precision = update_normalization(normalization, prec_power_of_10); + double scaled_remainder = apply_scaling(remainder, account_for_precision); + double rounding_threshold = 0.5; + + components.fractional = (int_fast64_t)scaled_remainder; // when precision == 0, the assigned value should be 0 + scaled_remainder -= (double)components.fractional; // when precision == 0, this will not change scaled_remainder + + components.fractional += (scaled_remainder >= rounding_threshold); + if (scaled_remainder == rounding_threshold) + { + // banker's rounding: Round towards the even number (making the mean error 0) + components.fractional &= ~((int_fast64_t)0x1); + } + // handle rollover, e.g. the case of 0.99 with precision 1 becoming (0,100), + // and must then be corrected into (1, 0). + // Note: for precision = 0, this will "translate" the rounding effect from + // the fractional part to the integral part where it should actually be + // felt (as prec_power_of_10 is 1) + if ((double)components.fractional >= prec_power_of_10) + { + components.fractional = 0; + ++components.integral; + } + return components; +} +#endif // PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS + +static NIF void print_broken_up_decimal( + struct double_components number_, output_gadget_t *output, printf_size_t precision, + printf_size_t width, printf_flags_t flags, char *buf, printf_size_t len) +{ + if (precision != 0U) + { + // do fractional part, as an unsigned number + + printf_size_t count = precision; + + // %g/%G mandates we skip the trailing 0 digits... + if ((flags & FLAGS_ADAPT_EXP) && !(flags & FLAGS_HASH) && (number_.fractional > 0)) + { + while (true) + { + int_fast64_t digit = number_.fractional % 10U; + if (digit != 0) + { + break; + } + --count; + number_.fractional /= 10U; + } + // ... and even the decimal point if there are no + // non-zero fractional part digits (see below) + } + + if (number_.fractional > 0 || !(flags & FLAGS_ADAPT_EXP) || (flags & FLAGS_HASH)) + { + while (len < PRINTF_DECIMAL_BUFFER_SIZE) + { + --count; + buf[len++] = (char)('0' + number_.fractional % 10U); + if (!(number_.fractional /= 10U)) + { + break; + } + } + // add extra 0s + while ((len < PRINTF_DECIMAL_BUFFER_SIZE) && (count > 0U)) + { + buf[len++] = '0'; + --count; + } + if (len < PRINTF_DECIMAL_BUFFER_SIZE) + { + buf[len++] = '.'; + } + } + } + else + { + if ((flags & FLAGS_HASH) && (len < PRINTF_DECIMAL_BUFFER_SIZE)) + { + buf[len++] = '.'; + } + } + + // Write the integer part of the number (it comes after the fractional + // since the character order is reversed) + while (len < PRINTF_DECIMAL_BUFFER_SIZE) + { + buf[len++] = (char)('0' + (number_.integral % 10)); + if (!(number_.integral /= 10)) + { + break; + } + } + + // pad leading zeros + if (!(flags & FLAGS_LEFT) && (flags & FLAGS_ZEROPAD)) + { + if (width && (number_.is_negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) + { + width--; + } + while ((len < width) && (len < PRINTF_DECIMAL_BUFFER_SIZE)) + { + buf[len++] = '0'; + } + } + + if (len < PRINTF_DECIMAL_BUFFER_SIZE) + { + if (number_.is_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); +} + +// internal ftoa for fixed decimal floating point +static NIF void print_decimal_number(output_gadget_t *output, double number, printf_size_t precision, printf_size_t width, printf_flags_t flags, char *buf, printf_size_t len) +{ + struct double_components value_ = get_components(number, precision); + print_broken_up_decimal(value_, output, precision, width, flags, buf, len); +} + +#if PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS + +// A floor function - but one which only works for numbers whose +// floor value is representable by an int. +static int bastardized_floor(double x) +{ + if (x >= 0) + { + return (int)x; + } + int n = (int)x; + return (((double)n) == x) ? n : n - 1; +} + +// Computes the base-10 logarithm of the input number - which must be an actual +// positive number (not infinity or NaN, nor a sub-normal) +static double log10_of_positive(double positive_number) +{ + // The implementation follows David Gay (https://www.ampl.com/netlib/fp/dtoa.c). + // + // Since log_10 ( M * 2^x ) = log_10(M) + x , we can separate the components of + // our input number, and need only solve log_10(M) for M between 1 and 2 (as + // the base-2 mantissa is always 1-point-something). In that limited range, a + // Taylor series expansion of log10(x) should serve us well enough; and we'll + // take the mid-point, 1.5, as the point of expansion. + + double_with_bit_access dwba = get_bit_access(positive_number); + // based on the algorithm by David Gay (https://www.ampl.com/netlib/fp/dtoa.c) + int exp2 = get_exp2(dwba); + // drop the exponent, so dwba.F comes into the range [1,2) + dwba.U = (dwba.U & (((double_uint_t)(1) << DOUBLE_STORED_MANTISSA_BITS) - 1U)) | + ((double_uint_t)DOUBLE_BASE_EXPONENT << DOUBLE_STORED_MANTISSA_BITS); + double z = (dwba.F - 1.5); + return ( + // Taylor expansion around 1.5: + 0.1760912590556812420 // Expansion term 0: ln(1.5) / ln(10) + + z * 0.2895296546021678851 // Expansion term 1: (M - 1.5) * 2/3 / ln(10) +#if PRINTF_LOG10_TAYLOR_TERMS > 2 + - z * z * 0.0965098848673892950 // Expansion term 2: (M - 1.5)^2 * 2/9 / ln(10) +#if PRINTF_LOG10_TAYLOR_TERMS > 3 + + z * z * z * 0.0428932821632841311 // Expansion term 2: (M - 1.5)^3 * 8/81 / ln(10) +#endif +#endif + // exact log_2 of the exponent x, with logarithm base change + + exp2 * 0.30102999566398119521 // = exp2 * log_10(2) = exp2 * ln(2)/ln(10) + ); +} + +static double pow10_of_int(int floored_exp10) +{ + // A crude hack for avoiding undesired behavior with barely-normal or slightly-subnormal values. + if (floored_exp10 == DOUBLE_MAX_SUBNORMAL_EXPONENT_OF_10) + { + return DOUBLE_MAX_SUBNORMAL_POWER_OF_10; + } + // Compute 10^(floored_exp10) but (try to) make sure that doesn't overflow + double_with_bit_access dwba; + int exp2 = bastardized_floor(floored_exp10 * 3.321928094887362 + 0.5); + const double z = floored_exp10 * 2.302585092994046 - exp2 * 0.6931471805599453; + const double z2 = z * z; + dwba.U = ((double_uint_t)(exp2) + DOUBLE_BASE_EXPONENT) << DOUBLE_STORED_MANTISSA_BITS; + // compute exp(z) using continued fractions, + // see https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex + dwba.F *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14))))); + return dwba.F; +} + +static NIF void print_exponential_number(output_gadget_t *output, double number, printf_size_t precision, printf_size_t width, printf_flags_t flags, char *buf, printf_size_t len) +{ + const bool negative = get_sign_bit(number); + // This number will decrease gradually (by factors of 10) as we "extract" the exponent out of it + double abs_number = negative ? -number : number; + + int floored_exp10; + bool abs_exp10_covered_by_powers_table; + struct scaling_factor normalization; + + // Determine the decimal exponent + if (abs_number == 0.0) + { + // TODO: This is a special-case for 0.0 (and -0.0); but proper handling is required for denormals more generally. + floored_exp10 = 0; // ... and no need to set a normalization factor or check the powers table + } + else + { + double exp10 = log10_of_positive(abs_number); + floored_exp10 = bastardized_floor(exp10); + double p10 = pow10_of_int(floored_exp10); + // correct for rounding errors + if (abs_number < p10) + { + floored_exp10--; + p10 /= 10; + } + abs_exp10_covered_by_powers_table = PRINTF_ABS(floored_exp10) < PRINTF_MAX_PRECOMPUTED_POWER_OF_10; + normalization.raw_factor = abs_exp10_covered_by_powers_table ? powers_of_10[PRINTF_ABS(floored_exp10)] : p10; + } + + // We now begin accounting for the widths of the two parts of our printed field: + // the decimal part after decimal exponent extraction, and the base-10 exponent part. + // For both of these, the value of 0 has a special meaning, but not the same one: + // a 0 exponent-part width means "don't print the exponent"; a 0 decimal-part width + // means "use as many characters as necessary". + + bool fall_back_to_decimal_only_mode = false; + if (flags & FLAGS_ADAPT_EXP) + { + int required_significant_digits = (precision == 0) ? 1 : (int)precision; + // Should we want to fall-back to "%f" mode, and only print the decimal part? + fall_back_to_decimal_only_mode = (floored_exp10 >= -4 && floored_exp10 < required_significant_digits); + // Now, let's adjust the precision + // This also decided how we adjust the precision value - as in "%g" mode, + // "precision" is the number of _significant digits_, and this is when we "translate" + // the precision value to an actual number of decimal digits. + int precision_ = fall_back_to_decimal_only_mode ? (int)precision - 1 - floored_exp10 : (int)precision - 1; // the presence of the exponent ensures only one significant digit comes before the decimal point + precision = (precision_ > 0 ? (unsigned)precision_ : 0U); + flags |= FLAGS_PRECISION; // make sure print_broken_up_decimal respects our choice above + } + + normalization.multiply = (floored_exp10 < 0 && abs_exp10_covered_by_powers_table); + bool should_skip_normalization = (fall_back_to_decimal_only_mode || floored_exp10 == 0); + struct double_components decimal_part_components = + should_skip_normalization ? get_components(negative ? -abs_number : abs_number, precision) : get_normalized_components(negative, precision, abs_number, normalization, floored_exp10); + + // Account for roll-over, e.g. rounding from 9.99 to 100.0 - which effects + // the exponent and may require additional tweaking of the parts + if (fall_back_to_decimal_only_mode) + { + if ((flags & FLAGS_ADAPT_EXP) && floored_exp10 >= -1 && decimal_part_components.integral == powers_of_10[floored_exp10 + 1]) + { + floored_exp10++; // Not strictly necessary, since floored_exp10 is no longer really used + precision--; + // ... and it should already be the case that decimal_part_components.fractional == 0 + } + // TODO: What about rollover strictly within the fractional part? + } + else + { + if (decimal_part_components.integral >= 10) + { + floored_exp10++; + decimal_part_components.integral = 1; + decimal_part_components.fractional = 0; + } + } + + // the floored_exp10 format is "E%+03d" and largest possible floored_exp10 value for a 64-bit double + // is "307" (for 2^1023), so we set aside 4-5 characters overall + printf_size_t exp10_part_width = fall_back_to_decimal_only_mode ? 0U : (PRINTF_ABS(floored_exp10) < 100) ? 4U + : 5U; + + printf_size_t decimal_part_width = + ((flags & FLAGS_LEFT) && exp10_part_width) ? + // We're padding on the right, so the width constraint is the exponent part's + // problem, not the decimal part's, so we'll use as many characters as we need: + 0U + : + // We're padding on the left; so the width constraint is the decimal part's + // problem. Well, can both the decimal part and the exponent part fit within our overall width? + ((width > exp10_part_width) ? + // Yes, so we limit our decimal part's width. + // (Note this is trivially valid even if we've fallen back to "%f" mode) + width - exp10_part_width + : + // No; we just give up on any restriction on the decimal part and use as many + // characters as we need + 0U); + + const printf_size_t printed_exponential_start_pos = output->pos; + print_broken_up_decimal(decimal_part_components, output, precision, decimal_part_width, flags, buf, len); + + if (!fall_back_to_decimal_only_mode) + { + putchar_via_gadget(output, (flags & FLAGS_UPPERCASE) ? 'E' : 'e'); + print_integer(output, + ABS_FOR_PRINTING(floored_exp10), + floored_exp10 < 0, 10, 0, exp10_part_width - 1, + FLAGS_ZEROPAD | FLAGS_PLUS); + if (flags & FLAGS_LEFT) + { + // We need to right-pad with spaces to meet the width requirement + while (output->pos - printed_exponential_start_pos < width) + { + putchar_via_gadget(output, ' '); + } + } + } +} +#endif // PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS + +static NIF void print_floating_point(output_gadget_t *output, double value, printf_size_t precision, printf_size_t width, printf_flags_t flags, bool prefer_exponential) +{ + char buf[PRINTF_DECIMAL_BUFFER_SIZE]; + printf_size_t len = 0U; + + // test for special values + if (value != value) + { + out_rev_(output, "nan", 3, width, flags); + return; + } + if (value < -DBL_MAX) + { + out_rev_(output, "fni-", 4, width, flags); + return; + } + if (value > DBL_MAX) + { + out_rev_(output, (flags & FLAGS_PLUS) ? "fni+" : "fni", (flags & FLAGS_PLUS) ? 4U : 3U, width, flags); + return; + } + + if (!prefer_exponential && + ((value > PRINTF_FLOAT_NOTATION_THRESHOLD) || (value < -PRINTF_FLOAT_NOTATION_THRESHOLD))) + { + // The required behavior of standard printf is to print _every_ integral-part digit -- which could mean + // printing hundreds of characters, overflowing any fixed internal buffer and necessitating a more complicated + // implementation. +#if PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS + print_exponential_number(output, value, precision, width, flags, buf, len); +#endif + return; + } + + // set default precision, if not set explicitly + if (!(flags & FLAGS_PRECISION)) + { + precision = PRINTF_DEFAULT_FLOAT_PRECISION; + } + + // limit precision so that our integer holding the fractional part does not overflow + while ((len < PRINTF_DECIMAL_BUFFER_SIZE) && (precision > PRINTF_MAX_SUPPORTED_PRECISION)) + { + buf[len++] = '0'; // This respects the precision in terms of result length only + precision--; + } + +#if PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS + if (prefer_exponential) + print_exponential_number(output, value, precision, width, flags, buf, len); + else +#endif + print_decimal_number(output, value, precision, width, flags, buf, len); +} + +#endif // (PRINTF_SUPPORT_DECIMAL_SPECIFIERS || PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS) + +// Advances the format pointer past the flags, and returns the parsed flags +// due to the characters passed +static NIF 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); +} + +static inline NIF 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 '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(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(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': + if (*format == 'F') + flags |= FLAGS_UPPERCASE; + print_floating_point(output, va_arg(args, double), precision, width, flags, PRINTF_PREFER_DECIMAL); + format++; + break; +#endif +#if PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS + case 'e': + case 'E': + case 'g': + case 'G': + if ((*format == 'g') || (*format == 'G')) + flags |= FLAGS_ADAPT_EXP; + if ((*format == 'E') || (*format == 'G')) + flags |= FLAGS_UPPERCASE; + print_floating_point(output, va_arg(args, double), 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; + } + } +} + +// internal vsnprintf - used for implementing _all library functions +static NIF 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; +} + +/////////////////////////////////////////////////////////////////////////////// + +NIF int vprintf(const char *format, va_list arg) +{ + output_gadget_t gadget = extern_putchar_gadget(); + return vsnprintf_impl(&gadget, format, arg); +} + +NIF int vsnprintf(char *s, size_t n, const char *format, va_list arg) +{ + output_gadget_t gadget = buffer_gadget(s, n); + return vsnprintf_impl(&gadget, format, arg); +} + +NIF int vsprintf(char *s, const char *format, va_list arg) +{ + return vsnprintf(s, PRINTF_MAX_POSSIBLE_BUFFER_SIZE, format, arg); +} + +NIF int vfctprintf(void (*out)(char c, void *extra_arg), void *extra_arg, const char *format, va_list arg) +{ + output_gadget_t gadget = function_gadget(out, extra_arg); + return vsnprintf_impl(&gadget, format, arg); +} + +NIF int printf(const char *format, ...) +{ + va_list args; + va_start(args, format); + const int ret = vprintf(format, args); + va_end(args); + return ret; +} + +NIF int sprintf(char *s, const char *format, ...) +{ + va_list args; + va_start(args, format); + const int ret = vsprintf(s, format, args); + va_end(args); + return ret; +} + +NIF int snprintf(char *s, size_t n, const char *format, ...) +{ + va_list args; + va_start(args, format); + const int ret = vsnprintf(s, n, format, args); + va_end(args); + return ret; +} + +NIF int fctprintf(void (*out)(char c, void *extra_arg), void *extra_arg, const char *format, ...) +{ + va_list args; + va_start(args, format); + const int ret = vfctprintf(out, extra_arg, format, args); + va_end(args); + return ret; +} diff --git a/FennixLoader/ubsan.h b/FennixLoader/ubsan.h new file mode 100644 index 00000000..9f0fc7ca --- /dev/null +++ b/FennixLoader/ubsan.h @@ -0,0 +1,94 @@ +#ifndef __FENNIX_KERNEL_UBSAN_H__ +#define __FENNIX_KERNEL_UBSAN_H__ + +#include + +struct source_location +{ + const char *file; + uint32_t line; + uint32_t column; +}; + +struct type_descriptor +{ + uint16_t kind; + uint16_t info; + char name[]; +}; + +struct type_mismatch_v1_data +{ + struct source_location location; + struct type_descriptor *type; + uint8_t alignment; + uint8_t type_check_kind; +}; + +struct out_of_bounds_info +{ + struct source_location location; + struct type_descriptor left_type; + struct type_descriptor right_type; +}; + +struct overflow_data +{ + struct source_location location; + struct type_descriptor *type; +}; + +struct negative_vla_data +{ + struct source_location location; + struct type_descriptor *type; +}; + +struct invalid_value_data +{ + struct source_location location; + struct type_descriptor *type; +}; + +struct nonnull_return_data +{ + struct source_location location; +}; + +struct nonnull_arg_data +{ + struct source_location location; +}; + +struct unreachable_data +{ + struct source_location location; +}; + +struct invalid_builtin_data +{ + struct source_location location; + uint8_t kind; +}; + +struct array_out_of_bounds_data +{ + struct source_location location; + struct type_descriptor *array_type; + struct type_descriptor *index_type; +}; + +struct shift_out_of_bounds_data +{ + struct source_location location; + struct type_descriptor *left_type; + struct type_descriptor *right_type; +}; + +struct dynamic_type_cache_miss_data +{ + struct source_location location; + struct type_descriptor *type; +}; + +#endif // !__FENNIX_KERNEL_UBSAN_H__ diff --git a/Makefile b/Makefile index 23d8924a..0352b356 100644 --- a/Makefile +++ b/Makefile @@ -2,6 +2,9 @@ prepare: make -C BIOS prepare make -C UEFI prepare +loader: + make -C FennixLoader build + build: make -C BIOS build make -C UEFI build @@ -11,4 +14,5 @@ build: clean: make -C BIOS clean make -C UEFI clean - rm -f loader.bin efi-loader.bin + make -C FennixLoader clean + rm -f loader.bin efi-loader.bin FLDR.elf