From 8652d781ce2a3de9845af50878f3f993b6c1e247 Mon Sep 17 00:00:00 2001 From: Alex Date: Sat, 8 Oct 2022 04:33:53 +0300 Subject: [PATCH] Update files --- .gitignore | 5 +- .vscode/c_boilerplates.code-snippets | 20 + .vscode/c_cpp_properties.json | 40 + .vscode/extensions.json | 16 + .vscode/launch.json | 40 + .vscode/settings.json | 12 + Fennix Kernel.code-workspace | 16 + Kernel.cpp | 28 +- Makefile | 155 +- arch/CPU.cpp | 63 + ...iversalAsynchronousReceiverTransmitter.cpp | 101 ++ arch/aarch64/boot.S | 17 + arch/aarch64/linker.ld | 42 + arch/amd64/Limine.c | 233 +++ arch/amd64/linker.ld | 12 +- arch/i686/.gitkeep | 0 arch/{aarch64/.gitkeep => i686/linker.ld} | 0 core/Debugger.cpp | 85 + core/Memory/HeapAllocators/Xalloc.cpp | 205 +++ core/Memory/HeapAllocators/Xalloc.hpp | 180 ++ core/Memory/Memory.cpp | 215 +++ core/Memory/PhysicalMemoryManager.cpp | 266 +++ core/Memory/VirtualMemoryManager.cpp | 123 ++ core/StackGuard.c | 58 + include/.gitkeep | 0 include/bitmap.hpp | 12 + include/boot/binfo.h | 120 ++ include/boot/protocols/limine.h | 420 +++++ include/boot/protocols/multiboot2.h | 417 +++++ include/cpu.hpp | 134 ++ include/cstring | 2 + include/debug.h | 46 + include/hashmap.hpp | 93 + include/io.h | 220 +++ include/limits.h | 119 ++ include/lock.hpp | 53 + include/memory.hpp | 432 +++++ include/printf.h | 199 +++ include/string.h | 33 + include/sys.h | 8 + include/types.h | 143 ++ include/uart.hpp | 71 + include/vector.hpp | 162 ++ kernel.fsys | Bin 4 -> 170528 bytes kernel.h | 11 + lib/.gitkeep | 0 lib/Bitmap.cpp | 28 + lib/String.c | 159 ++ lib/cxxabi.cpp | 140 ++ lib/liballoc_1_1.c | 790 ++++++++ lib/liballoc_1_1.h | 74 + lib/liballocimpl.cpp | 14 + lib/printf.c | 1591 +++++++++++++++++ 53 files changed, 7413 insertions(+), 10 deletions(-) create mode 100644 .vscode/c_boilerplates.code-snippets create mode 100644 .vscode/c_cpp_properties.json create mode 100644 .vscode/extensions.json create mode 100644 .vscode/launch.json create mode 100644 .vscode/settings.json create mode 100644 Fennix Kernel.code-workspace create mode 100644 arch/CPU.cpp create mode 100644 arch/UniversalAsynchronousReceiverTransmitter.cpp create mode 100644 arch/aarch64/boot.S create mode 100644 arch/aarch64/linker.ld create mode 100644 arch/amd64/Limine.c delete mode 100644 arch/i686/.gitkeep rename arch/{aarch64/.gitkeep => i686/linker.ld} (100%) create mode 100644 core/Debugger.cpp create mode 100644 core/Memory/HeapAllocators/Xalloc.cpp create mode 100644 core/Memory/HeapAllocators/Xalloc.hpp create mode 100644 core/Memory/Memory.cpp create mode 100644 core/Memory/PhysicalMemoryManager.cpp create mode 100644 core/Memory/VirtualMemoryManager.cpp create mode 100644 core/StackGuard.c delete mode 100644 include/.gitkeep create mode 100644 include/bitmap.hpp create mode 100644 include/boot/binfo.h create mode 100644 include/boot/protocols/limine.h create mode 100644 include/boot/protocols/multiboot2.h create mode 100644 include/cpu.hpp create mode 100644 include/cstring create mode 100644 include/debug.h create mode 100644 include/hashmap.hpp create mode 100644 include/io.h create mode 100644 include/limits.h create mode 100644 include/lock.hpp create mode 100644 include/memory.hpp create mode 100644 include/printf.h create mode 100644 include/string.h create mode 100644 include/sys.h create mode 100644 include/types.h create mode 100644 include/uart.hpp create mode 100644 include/vector.hpp mode change 100644 => 100755 kernel.fsys create mode 100644 kernel.h delete mode 100644 lib/.gitkeep create mode 100644 lib/Bitmap.cpp create mode 100644 lib/String.c create mode 100644 lib/cxxabi.cpp create mode 100644 lib/liballoc_1_1.c create mode 100644 lib/liballoc_1_1.h create mode 100644 lib/liballocimpl.cpp create mode 100644 lib/printf.c diff --git a/.gitignore b/.gitignore index 15309787..ab9d1dd8 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,4 @@ -*.o \ No newline at end of file +*.o +*.map +*.fsys +*.log diff --git a/.vscode/c_boilerplates.code-snippets b/.vscode/c_boilerplates.code-snippets new file mode 100644 index 00000000..1ac15e7c --- /dev/null +++ b/.vscode/c_boilerplates.code-snippets @@ -0,0 +1,20 @@ +{ + "Fennix Kernel Header": { + "scope": "c", + "prefix": [ + "head", + ], + "body": [ + "#ifndef __FENNIX_KERNEL_${2:header}_H__", + "#define __FENNIX_KERNEL_${2:header}_H__", + "", + "#include ", + "", + "$0", + "", + "#endif // !__FENNIX_KERNEL_${2:header}_H__", + "" + ], + "description": "Create kernel header." + } +} \ No newline at end of file diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 00000000..68d9df2f --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,40 @@ +{ + "configurations": [ + { + "name": "Linux", + "includePath": [ + "${workspaceFolder}/include/**" + ], + "defines": [ + "KERNEL_NAME=\"Fennix\"", + "KERNEL_VERSION=\"1.0\"", + "GIT_COMMIT=\"0000000000000000000000000000000000000000\"", + "GIT_COMMIT_SHORT=\"0000000\"", + "DEBUG=\"1\"" + ], + "compilerPath": "/usr/bin/gcc", + "cStandard": "c17", + "cppStandard": "c++20", + "intelliSenseMode": "gcc-x64", + "configurationProvider": "ms-vscode.makefile-tools", + "compilerArgs": [ + "-fno-rtti", + "-fexceptions", + "-fno-pic", + "-fno-pie", + "-mno-80387", + "-mno-mmx", + "-mno-3dnow", + "-mno-red-zone", + "-mno-sse", + "-mno-sse2", + "-march=nehalem", + "-pipe", + "-mcmodel=kernel", + "-msoft-float", + "-fno-builtin" + ] + } + ], + "version": 4 +} \ No newline at end of file diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 00000000..cb0c1719 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,16 @@ +{ + "recommendations": [ + "pejmannikram.vscode-auto-scroll", + "aaron-bond.better-comments", + "ms-vscode.cpptools", + "streetsidesoftware.code-spell-checker", + "naumovs.color-highlight", + "cschlosser.doxdocgen", + "ferrierbenjamin.fold-unfold-all-icone", + "ajshort.include-autocomplete", + "zixuanwang.linkerscript", + "ibm.output-colorizer", + "christian-kohler.path-intellisense", + "Gruntfuggly.todo-tree" + ] +} \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 00000000..fb5d3cae --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,40 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Attach to a running QEMU instance", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceRoot}/kernel.fsys", + "cwd": "${workspaceRoot}", + "args": [], + "targetArchitecture": "x64", + "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}/kernel.fsys", + "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..44109af3 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,12 @@ +{ + "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": true +} \ No newline at end of file diff --git a/Fennix Kernel.code-workspace b/Fennix Kernel.code-workspace new file mode 100644 index 00000000..dd030035 --- /dev/null +++ b/Fennix Kernel.code-workspace @@ -0,0 +1,16 @@ +{ + "folders": [ + { + "path": "." + } + ], + "settings": { + "debug.allowBreakpointsEverywhere": true, + "files.associations": { + "limine.h": "c", + "types.h": "c", + "binfo.h": "c", + "liballoc_1_1.h": "c" + } + } +} \ No newline at end of file diff --git a/Kernel.cpp b/Kernel.cpp index 8f79d4e0..66f52169 100644 --- a/Kernel.cpp +++ b/Kernel.cpp @@ -1,6 +1,30 @@ +#include "kernel.h" -extern "C" void kernel_entry(void *Data) +#include +#include +#include + +BootInfo *bInfo = nullptr; + +EXTERNC void kernel_aarch64_entry(uint64_t dtb_ptr32, uint64_t x1, uint64_t x2, uint64_t x3) { + trace("Hello, World!"); while (1) - ; + CPU::Halt(); +} + +EXTERNC void kernel_entry(BootInfo *Info) +{ + InitializeMemoryManagement(Info); + trace("Hello, World!"); + bInfo = (BootInfo *)KernelAllocator.RequestPages(TO_PAGES(sizeof(BootInfo))); + memcpy(bInfo, Info, sizeof(BootInfo)); + debug("BootInfo structure is at %p", bInfo); + while (1) + CPU::Halt(); +} + +// TODO: Implement screen printing +extern "C" void putchar(int a, int b) +{ } diff --git a/Makefile b/Makefile index a0c2073e..72d48a7d 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,156 @@ -prepare: +# Config file +include ../Makefile.conf -build: +KERNEL_FILENAME = kernel.fsys + +CC = ../$(COMPILER_PATH)/$(COMPILER_ARCH)gcc +CPP = ../$(COMPILER_PATH)/$(COMPILER_ARCH)g++ +LD = ../$(COMPILER_PATH)/$(COMPILER_ARCH)ld +AS = ../$(COMPILER_PATH)/$(COMPILER_ARCH)as +NM = ../$(COMPILER_PATH)/$(COMPILER_ARCH)nm +OBJCOPY = ../$(COMPILER_PATH)/$(COMPILER_ARCH)objcopy +OBJDUMP = ../$(COMPILER_PATH)/$(COMPILER_ARCH)objdump +GDB = ../$(COMPILER_PATH)/$(COMPILER_ARCH)gdb +NASM = /usr/bin/nasm + +GIT_COMMIT = $(shell git rev-parse HEAD) +GIT_COMMIT_SHORT = $(shell git rev-parse --short HEAD) + +BMP_SOURCES = $(shell find ./ -type f -name '*.bmp') +PSF_SOURCES = $(shell find ./ -type f -name '*.psf') +ifeq ($(OSARCH), amd64) +ASM_SOURCES = $(shell find ./ -type f -name '*.asm' -not -path "./arch/i686/*" -not -path "./arch/aarch64/*") +S_SOURCES = $(shell find ./ -type f -name '*.S' -not -path "./arch/i686/*" -not -path "./arch/aarch64/*") +C_SOURCES = $(shell find ./ -type f -name '*.c' -not -path "./arch/i686/*" -not -path "./arch/aarch64/*") +CPP_SOURCES = $(shell find ./ -type f -name '*.cpp' -not -path "./arch/i686/*" -not -path "./arch/aarch64/*") +else ifeq ($(OSARCH), i686) +ASM_SOURCES = $(shell find ./ -type f -name '*.asm' -not -path "./arch/amd64/*" -not -path "./arch/aarch64/*") +S_SOURCES = $(shell find ./ -type f -name '*.S' -not -path "./arch/amd64/*" -not -path "./arch/aarch64/*") +C_SOURCES = $(shell find ./ -type f -name '*.c' -not -path "./arch/amd64/*" -not -path "./arch/aarch64/*") +CPP_SOURCES = $(shell find ./ -type f -name '*.cpp' -not -path "./arch/amd64/*" -not -path "./arch/aarch64/*") +else ifeq ($(OSARCH), aarch64) +ASM_SOURCES = $(shell find ./ -type f -name '*.asm' -not -path "./arch/amd64/*" -not -path "./arch/i686/*") +S_SOURCES = $(shell find ./ -type f -name '*.S' -not -path "./arch/amd64/*" -not -path "./arch/i686/*") +C_SOURCES = $(shell find ./ -type f -name '*.c' -not -path "./arch/amd64/*" -not -path "./arch/i686/*") +CPP_SOURCES = $(shell find ./ -type f -name '*.cpp' -not -path "./arch/amd64/*" -not -path "./arch/i686/*") +endif +HEADERS = $(sort $(dir $(wildcard ./include/*))) +OBJ = $(C_SOURCES:.c=.o) $(CPP_SOURCES:.cpp=.o) $(ASM_SOURCES:.asm=.o) $(S_SOURCES:.S=.o) $(PSF_SOURCES:.psf=.o) $(BMP_SOURCES:.bmp=.o) +INCLUDE_DIR = ./include + +LDFLAGS := -Wl,-Map kernel.map -shared -nostdlib -nodefaultlibs -nolibc + +# Disable all warnings by adding "-w" in WARNCFLAG and if you want to treat the warnings as errors, add "-Werror" +WARNCFLAG = -Wall -Wextra + +# https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html +CFLAGS := \ + -I$(INCLUDE_DIR) \ + -DKERNEL_NAME='"$(OSNAME)"' \ + -DKERNEL_VERSION='"$(KERNEL_VERSION)"' \ + -DGIT_COMMIT='"$(GIT_COMMIT)"' \ + -DGIT_COMMIT_SHORT='"$(GIT_COMMIT_SHORT)"' + +ifeq ($(OSARCH), amd64) + +CFLAGS += -fno-pic -fno-pie -mno-80387 -mno-mmx -mno-3dnow \ + -mno-red-zone -mno-sse -mno-sse2 \ + -march=x86-64 -pipe \ + -mcmodel=kernel -msoft-float -fno-builtin +CFLAG_STACK_PROTECTOR := -fstack-protector-all +LDFLAGS += -Tarch/amd64/linker.ld \ + -fno-pic -fno-pie \ + -Wl,-static,--no-dynamic-linker,-ztext \ + -nostdlib -nodefaultlibs -nolibc \ + -zmax-page-size=0x1000 \ + -Wl,-Map kernel.map -shared + +else ifeq ($(OSARCH), i686) + +CFLAGS += -fno-pic -fno-pie -mno-80387 -mno-mmx -mno-3dnow \ + -mno-red-zone -mno-sse -mno-sse2 \ + -march=i686 -pipe -msoft-float -fno-builtin +CFLAG_STACK_PROTECTOR := -fstack-protector-all +LDFLAGS += -Tarch/i686/linker.ld \ + -fno-pic -fno-pie \ + -Wl,-static,--no-dynamic-linker,-ztext \ + -nostdlib -nodefaultlibs -nolibc \ + -zmax-page-size=0x1000 \ + -Wl,-Map kernel.map -shared + +else ifeq ($(OSARCH), aarch64) + +CFLAGS += -pipe -fno-builtin -fPIC -mgeneral-regs-only +CFLAG_STACK_PROTECTOR := -fstack-protector-all +LDFLAGS += -Tarch/aarch64/linker.ld -fPIC + +endif + +ifeq ($(DEBUG), 1) + CFLAGS += -DDEBUG -ggdb -O0 -fdiagnostics-color=always + LDFLAGS += -ggdb -O0 -g + NASMFLAGS += -F dwarf -g + WARNCFLAG += -Wno-unused-function -Wno-maybe-uninitialized -Wno-builtin-declaration-mismatch -Wno-unknown-pragmas -Wno-unused-parameter -Wno-unused-variable +ifeq ($(TESTING), 1) + CFLAGS += -DTESTING +endif +endif + +default: + $(error Please specify a target) + +# TODO: complete this +prepare: + $(info Nothing to prepare) + +build: $(KERNEL_FILENAME) + $(OBJDUMP) -D -d $(KERNEL_FILENAME) > kernel_dump.map + +$(KERNEL_FILENAME): $(OBJ) + $(CC) $(LDFLAGS) $(OBJ) -o $@ + +%.o: %.c $(HEADERS) + $(info Compiling $<) + $(CC) $(CFLAGS) $(CFLAG_STACK_PROTECTOR) $(WARNCFLAG) -std=c17 -c $< -o $@ + +# https://gcc.gnu.org/projects/cxx-status.html +%.o: %.cpp $(HEADERS) + $(info Compiling $<) + $(CPP) $(CFLAGS) $(CFLAG_STACK_PROTECTOR) $(WARNCFLAG) -std=c++20 -fexceptions -c $< -o $@ -fno-rtti + +%.o: %.asm + $(info Compiling $<) + $(NASM) $< $(NASMFLAGS) -o $@ + +%.o: %.S + $(info Compiling $<) +ifeq ($(OSARCH), amd64) + $(AS) -c $< -o $@ +else ifeq ($(OSARCH), i686) + $(AS) -c $< -o $@ +else ifeq ($(OSARCH), aarch64) + $(AS) -c $< -o $@ +endif + +%.o: %.psf +ifeq ($(OSARCH), amd64) + $(OBJCOPY) -O elf64-x86-64 -I binary $< $@ +else ifeq ($(OSARCH), i686) + $(OBJCOPY) -O elf32-i386 -I binary $< $@ +else ifeq ($(OSARCH), aarch64) + $(OBJCOPY) -O elf64-bigaarch64 -I binary $< $@ +endif + $(NM) $@ + +%.o: %.bmp +ifeq ($(OSARCH), amd64) + $(OBJCOPY) -O elf64-x86-64 -I binary $< $@ +else ifeq ($(OSARCH), i686) + $(OBJCOPY) -O elf32-i386 -I binary $< $@ +else ifeq ($(OSARCH), aarch64) + $(OBJCOPY) -O elf64-bigaarch64 -I binary $< $@ +endif + $(NM) $@ clean: + rm -f *.bin *.o *.elf *.sym kernel.map kernel_dump.map initrd.tar.gz $(OBJ) $(KERNEL_FILENAME) diff --git a/arch/CPU.cpp b/arch/CPU.cpp new file mode 100644 index 00000000..d3d7dc53 --- /dev/null +++ b/arch/CPU.cpp @@ -0,0 +1,63 @@ +#include + +namespace CPU +{ + void Pause() + { +#if defined(__amd64__) || defined(__i386__) + asmv("pause"); +#elif defined(__aarch64__) + asmv("yield"); +#endif + } + + void Halt() + { +#if defined(__amd64__) || defined(__i386__) + asmv("hlt"); +#elif defined(__aarch64__) + asmv("wfe"); +#endif + } + + bool Interrupts(InterruptsType Type) + { + switch (Type) + { + case Check: + { +#if defined(__amd64__) || defined(__i386__) + uint64_t rflags; + asmv("pushfq"); + asmv("popq %0" + : "=r"(rflags)); + return rflags & (1 << 9); +#elif defined(__aarch64__) + uint64_t daif; + asmv("mrs %0, daif" + : "=r"(daif)); + return !(daif & (1 << 2)); +#endif + } + case Enable: + { +#if defined(__amd64__) || defined(__i386__) + asmv("sti"); +#elif defined(__aarch64__) + asmv("msr daifclr, #2"); +#endif + return true; + } + case Disable: + { +#if defined(__amd64__) || defined(__i386__) + asmv("cli"); +#elif defined(__aarch64__) + asmv("msr daifset, #2"); +#endif + return true; + } + } + return false; + } +} diff --git a/arch/UniversalAsynchronousReceiverTransmitter.cpp b/arch/UniversalAsynchronousReceiverTransmitter.cpp new file mode 100644 index 00000000..7a239c14 --- /dev/null +++ b/arch/UniversalAsynchronousReceiverTransmitter.cpp @@ -0,0 +1,101 @@ +#include + +#include +#include +#include + +namespace UniversalAsynchronousReceiverTransmitter +{ +#define SERIAL_ENABLE_DLAB 0x80 +#define SERIAL_RATE_38400_LO 0x03 +#define SERIAL_RATE_38400_HI 0x00 +#define SERIAL_BUFFER_EMPTY 0x20 + + volatile bool serialports[8] = {false, false, false, false, false, false, false, false}; + + Vector RegisteredEvents; + + UART::UART(SerialPorts Port) + { +#if defined(__amd64__) || defined(__i386__) + if (Port == COMNULL) + return; + + this->Port = Port; + + if (serialports[Port]) + return; + + // Initialize the serial port + outb(Port + 1, 0x00); // Disable all interrupts + outb(Port + 3, SERIAL_ENABLE_DLAB); // Enable DLAB (set baud rate divisor) + outb(Port + 0, SERIAL_RATE_38400_LO); // Set divisor to 3 (lo byte) 38400 baud + outb(Port + 1, SERIAL_RATE_38400_HI); // (hi byte) + outb(Port + 3, 0x03); // 8 bits, no parity, one stop bit + outb(Port + 2, 0xC7); // Enable FIFO, clear them, with 14-byte threshold + outb(Port + 4, 0x0B); // IRQs enabled, RTS/DSR set + + // Check if the serial port is faulty. + if (inb(Port + 0) != 0xAE) + { + static int once = 0; + if (!once++) + warn("Serial port %#lx is faulty.", Port); + // serialports[Port] = false; // ignore for now + // return; + } + + // Set to normal operation mode. + outb(Port + 4, 0x0F); + serialports[Port] = true; +#endif + } + + UART::~UART() {} + + void UART::Write(uint8_t Char) + { +#if defined(__amd64__) || defined(__i386__) + while ((inb(Port + 5) & SERIAL_BUFFER_EMPTY) == 0) + ; + outb(Port, Char); +#endif + foreach (auto e in RegisteredEvents) + if (e->GetRegisteredPort() == Port || e->GetRegisteredPort() == COMNULL) + e->OnSent(Char); + } + + uint8_t UART::Read() + { +#if defined(__amd64__) || defined(__i386__) + while ((inb(Port + 5) & 1) == 0) + ; + return inb(Port); +#endif + foreach (auto e in RegisteredEvents) + { + if (e->GetRegisteredPort() == Port || e->GetRegisteredPort() == COMNULL) + { +#if defined(__amd64__) || defined(__i386__) + e->OnReceived(inb(Port)); +#endif + } + } + } + + Events::Events(SerialPorts Port) + { + this->Port = Port; + RegisteredEvents.push_back(this); + } + + Events::~Events() + { + for (uint64_t i = 0; i < RegisteredEvents.size(); i++) + if (RegisteredEvents[i] == this) + { + RegisteredEvents.remove(i); + return; + } + } +} diff --git a/arch/aarch64/boot.S b/arch/aarch64/boot.S new file mode 100644 index 00000000..ece3d735 --- /dev/null +++ b/arch/aarch64/boot.S @@ -0,0 +1,17 @@ +.section ".text.boot" + +.global _start + .org 0x80000 +_start: + ldr x5, =_start + mov sp, x5 + ldr x5, =_kernel_rodata_end + ldr w6, =_bss_size +1: cbz w6, 2f + str xzr, [x5], #8 + sub w6, w6, #1 + cbnz w6, 1b +2: bl kernel_aarch64_entry +Halt: + wfe + b Halt diff --git a/arch/aarch64/linker.ld b/arch/aarch64/linker.ld new file mode 100644 index 00000000..c6567c63 --- /dev/null +++ b/arch/aarch64/linker.ld @@ -0,0 +1,42 @@ +ENTRY(_start) + +SECTIONS +{ + . = 0x80000; + _kernel_start = .; + .text : + { + KEEP(*(.text.boot)) + *(.text .text.*) + } + . = ALIGN(4096); + _kernel_text_end = .; + + .data : + { + *(.data .data.*) + } + . = ALIGN(4096); + _kernel_data_end = .; + + .rodata : + { + *(.rodata .rodata.*) + } + . = ALIGN(4096); + _kernel_rodata_end = .; + + .bss : + { + *(.bss .bss.*) + } + . = ALIGN(4096); + _kernel_end = .; + _bss_size = _kernel_end - _kernel_rodata_end; + + /DISCARD/ : + { + *(.eh_frame) + *(.note .note.*) + } +} diff --git a/arch/amd64/Limine.c b/arch/amd64/Limine.c new file mode 100644 index 00000000..859a7053 --- /dev/null +++ b/arch/amd64/Limine.c @@ -0,0 +1,233 @@ +#include +#include +#include +#include +#include + +#include "../../kernel.h" + +void init_limine(); + +static volatile struct limine_entry_point_request EntryPointRequest = { + .id = LIMINE_ENTRY_POINT_REQUEST, + .revision = 0, + .response = NULL, + .entry = init_limine}; +static volatile struct limine_bootloader_info_request BootloaderInfoRequest = { + .id = LIMINE_BOOTLOADER_INFO_REQUEST, + .revision = 0}; +static volatile struct limine_terminal_request TerminalRequest = { + .id = LIMINE_TERMINAL_REQUEST, + .revision = 0}; +static volatile struct limine_framebuffer_request FramebufferRequest = { + .id = LIMINE_FRAMEBUFFER_REQUEST, + .revision = 0}; +static volatile struct limine_memmap_request MemmapRequest = { + .id = LIMINE_MEMMAP_REQUEST, + .revision = 0}; +static volatile struct limine_kernel_address_request KernelAddressRequest = { + .id = LIMINE_KERNEL_ADDRESS_REQUEST, + .revision = 0}; +static volatile struct limine_rsdp_request RsdpRequest = { + .id = LIMINE_RSDP_REQUEST, + .revision = 0}; +static volatile struct limine_kernel_file_request KernelFileRequest = { + .id = LIMINE_KERNEL_FILE_REQUEST, + .revision = 0}; +static volatile struct limine_module_request ModuleRequest = { + .id = LIMINE_MODULE_REQUEST, + .revision = 0}; + +void init_limine() +{ + struct BootInfo binfo; + struct limine_bootloader_info_response *BootloaderInfoResponse = BootloaderInfoRequest.response; + info("Bootloader: %s %s", BootloaderInfoResponse->name, BootloaderInfoResponse->version); + + struct limine_terminal_response *TerminalResponse = TerminalRequest.response; + + if (TerminalResponse == NULL || TerminalResponse->terminal_count < 1) + { + warn("No terminal available."); + while (1) + asmv("hlt"); + } + TerminalResponse->write(TerminalResponse->terminals[0], "Please wait... ", 15); + + struct limine_framebuffer_response *FrameBufferResponse = FramebufferRequest.response; + struct limine_memmap_response *MemmapResponse = MemmapRequest.response; + struct limine_kernel_address_response *KernelAddressResponse = KernelAddressRequest.response; + struct limine_rsdp_response *RsdpResponse = RsdpRequest.response; + struct limine_kernel_file_response *KernelFileResponse = KernelFileRequest.response; + struct limine_module_response *ModuleResponse = ModuleRequest.response; + + if (FrameBufferResponse == NULL || FrameBufferResponse->framebuffer_count < 1) + { + error("No framebuffer available [%p;%ld]", FrameBufferResponse, + (FrameBufferResponse == NULL) ? 0 : FrameBufferResponse->framebuffer_count); + TerminalResponse->write(TerminalResponse->terminals[0], "No framebuffer available", 24); + while (1) + asmv("hlt"); + } + + if (MemmapResponse == NULL || MemmapResponse->entry_count < 1) + { + error("No memory map available [%p;%ld]", MemmapResponse, + (MemmapResponse == NULL) ? 0 : MemmapResponse->entry_count); + TerminalResponse->write(TerminalResponse->terminals[0], "No memory map available", 23); + while (1) + asmv("hlt"); + } + + if (KernelAddressResponse == NULL) + { + error("No kernel address available [%p]", KernelAddressResponse); + TerminalResponse->write(TerminalResponse->terminals[0], "No kernel address available", 27); + while (1) + asmv("hlt"); + } + + if (RsdpResponse == NULL || RsdpResponse->address == 0) + { + error("No RSDP address available [%p;%p]", RsdpResponse, + (RsdpResponse == NULL) ? 0 : RsdpResponse->address); + TerminalResponse->write(TerminalResponse->terminals[0], "No RSDP address available", 25); + while (1) + asmv("hlt"); + } + + if (KernelFileResponse == NULL || KernelFileResponse->kernel_file == NULL) + { + error("No kernel file available [%p;%p]", KernelFileResponse, + (KernelFileResponse == NULL) ? 0 : KernelFileResponse->kernel_file); + TerminalResponse->write(TerminalResponse->terminals[0], "No kernel file available", 24); + while (1) + asmv("hlt"); + } + + if (ModuleResponse == NULL || ModuleResponse->module_count < 1) + { + error("No module information available [%p;%ld]", ModuleResponse, + (ModuleResponse == NULL) ? 0 : ModuleResponse->module_count); + TerminalResponse->write(TerminalResponse->terminals[0], "No module information available", 31); + while (1) + asmv("hlt"); + } + + for (uint64_t i = 0; i < FrameBufferResponse->framebuffer_count; i++) + { + struct limine_framebuffer *framebuffer = FrameBufferResponse->framebuffers[i]; + binfo.Framebuffer[i].BaseAddress = framebuffer->address - 0xffff800000000000; + binfo.Framebuffer[i].Width = framebuffer->width; + binfo.Framebuffer[i].Height = framebuffer->height; + binfo.Framebuffer[i].Pitch = framebuffer->pitch; + binfo.Framebuffer[i].BitsPerPixel = framebuffer->bpp; + binfo.Framebuffer[i].MemoryModel = framebuffer->memory_model; + binfo.Framebuffer[i].RedMaskSize = framebuffer->red_mask_size; + binfo.Framebuffer[i].RedMaskShift = framebuffer->red_mask_shift; + binfo.Framebuffer[i].GreenMaskSize = framebuffer->green_mask_size; + binfo.Framebuffer[i].GreenMaskShift = framebuffer->green_mask_shift; + binfo.Framebuffer[i].BlueMaskSize = framebuffer->blue_mask_size; + binfo.Framebuffer[i].BlueMaskShift = framebuffer->blue_mask_shift; + binfo.Framebuffer[i].ExtendedDisplayIdentificationData = framebuffer->edid; + binfo.Framebuffer[i].EDIDSize = framebuffer->edid_size; + debug("Framebuffer %d: %dx%d %d bpp", i, framebuffer->width, framebuffer->height, framebuffer->bpp); + debug("More info:\nAddress: %p\nPitch: %ld\nMemoryModel: %d\nRedMaskSize: %d\nRedMaskShift: %d\nGreenMaskSize: %d\nGreenMaskShift: %d\nBlueMaskSize: %d\nBlueMaskShift: %d\nEDID: %p\nEDIDSize: %d", + framebuffer->address - 0xffff800000000000, framebuffer->pitch, framebuffer->memory_model, framebuffer->red_mask_size, framebuffer->red_mask_shift, framebuffer->green_mask_size, framebuffer->green_mask_shift, framebuffer->blue_mask_size, framebuffer->blue_mask_shift, framebuffer->edid, framebuffer->edid_size); + } + + binfo.Memory.Entries = MemmapResponse->entry_count; + for (uint64_t i = 0; i < MemmapResponse->entry_count; i++) + { + if (MemmapResponse->entry_count > MAX_MEMORY_ENTRIES) + { + warn("Too many memory entries, skipping the rest..."); + break; + } + + struct limine_memmap_entry *entry = MemmapResponse->entries[i]; + binfo.Memory.Size += entry->length; + switch (entry->type) + { + case LIMINE_MEMMAP_USABLE: + binfo.Memory.Entry[i].BaseAddress = (void *)entry->base; + binfo.Memory.Entry[i].Length = entry->length; + binfo.Memory.Entry[i].Type = Usable; + break; + case LIMINE_MEMMAP_RESERVED: + binfo.Memory.Entry[i].BaseAddress = (void *)entry->base; + binfo.Memory.Entry[i].Length = entry->length; + binfo.Memory.Entry[i].Type = Reserved; + break; + case LIMINE_MEMMAP_ACPI_RECLAIMABLE: + binfo.Memory.Entry[i].BaseAddress = (void *)entry->base; + binfo.Memory.Entry[i].Length = entry->length; + binfo.Memory.Entry[i].Type = ACPIReclaimable; + break; + case LIMINE_MEMMAP_ACPI_NVS: + binfo.Memory.Entry[i].BaseAddress = (void *)entry->base; + binfo.Memory.Entry[i].Length = entry->length; + binfo.Memory.Entry[i].Type = ACPINVS; + break; + case LIMINE_MEMMAP_BAD_MEMORY: + binfo.Memory.Entry[i].BaseAddress = (void *)entry->base; + binfo.Memory.Entry[i].Length = entry->length; + binfo.Memory.Entry[i].Type = BadMemory; + break; + case LIMINE_MEMMAP_BOOTLOADER_RECLAIMABLE: + binfo.Memory.Entry[i].BaseAddress = (void *)entry->base; + binfo.Memory.Entry[i].Length = entry->length; + binfo.Memory.Entry[i].Type = BootloaderReclaimable; + break; + case LIMINE_MEMMAP_KERNEL_AND_MODULES: + binfo.Memory.Entry[i].BaseAddress = (void *)entry->base; + binfo.Memory.Entry[i].Length = entry->length; + binfo.Memory.Entry[i].Type = KernelAndModules; + break; + case LIMINE_MEMMAP_FRAMEBUFFER: + binfo.Memory.Entry[i].BaseAddress = (void *)entry->base; + binfo.Memory.Entry[i].Length = entry->length; + binfo.Memory.Entry[i].Type = Framebuffer; + break; + default: + binfo.Memory.Entry[i].BaseAddress = (void *)entry->base; + binfo.Memory.Entry[i].Length = entry->length; + binfo.Memory.Entry[i].Type = Unknown; + break; + } + } + + for (uint64_t i = 0; i < ModuleResponse->module_count; i++) + { + if (i > MAX_MODULES) + { + warn("Too many modules, skipping the rest..."); + break; + } + + binfo.Modules[i].Address = ModuleResponse->modules[i]->address - 0xffff800000000000; + strcpy(binfo.Modules[i].Path, ModuleResponse->modules[i]->path); + strcpy(binfo.Modules[i].CommandLine, ModuleResponse->modules[i]->cmdline); + binfo.Modules[i].Size = ModuleResponse->modules[i]->size; + debug("Module %d:\nAddress: %p\nPath: %s\nCommand Line: %s\nSize: %ld", i, + ModuleResponse->modules[i]->address - 0xffff800000000000, ModuleResponse->modules[i]->path, + ModuleResponse->modules[i]->cmdline, ModuleResponse->modules[i]->size); + } + + binfo.RSDP = (struct RSDPInfo *)(RsdpResponse->address - 0xffff800000000000); + trace("RSDP: %p(%p) [Signature: %.8s] [OEM: %.6s]", + RsdpResponse->address, binfo.RSDP, binfo.RSDP->Signature, binfo.RSDP->OEMID); + + binfo.Kernel.PhysicalBase = (void *)KernelAddressResponse->physical_base; + binfo.Kernel.VirtualBase = (void *)KernelAddressResponse->virtual_base; + strcpy(binfo.Kernel.CommandLine, KernelFileResponse->kernel_file->cmdline); + binfo.Kernel.Size = KernelFileResponse->kernel_file->size; + trace("Kernel physical address: %p", KernelAddressResponse->physical_base); + trace("Kernel virtual address: %p", KernelAddressResponse->virtual_base); + + strcpy(binfo.Bootloader.Name, BootloaderInfoResponse->name); + strcpy(binfo.Bootloader.Version, BootloaderInfoResponse->version); + + // Call kernel entry point + kernel_entry(&binfo); +} diff --git a/arch/amd64/linker.ld b/arch/amd64/linker.ld index 925d52ed..f56690ca 100644 --- a/arch/amd64/linker.ld +++ b/arch/amd64/linker.ld @@ -15,6 +15,13 @@ SECTIONS _kernel_text_end = ALIGN(CONSTANT(MAXPAGESIZE)); . += CONSTANT(MAXPAGESIZE); + .data : + { + *(.data .data.*) + } + _kernel_data_end = ALIGN(CONSTANT(MAXPAGESIZE)); + . += CONSTANT(MAXPAGESIZE); + .rodata : { *(.rodata .rodata.*) @@ -22,11 +29,6 @@ SECTIONS _kernel_rodata_end = ALIGN(CONSTANT(MAXPAGESIZE)); . += CONSTANT(MAXPAGESIZE); - .data : - { - *(.data .data.*) - } - .bss : { *(COMMON) diff --git a/arch/i686/.gitkeep b/arch/i686/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/arch/aarch64/.gitkeep b/arch/i686/linker.ld similarity index 100% rename from arch/aarch64/.gitkeep rename to arch/i686/linker.ld diff --git a/core/Debugger.cpp b/core/Debugger.cpp new file mode 100644 index 00000000..34d14d65 --- /dev/null +++ b/core/Debugger.cpp @@ -0,0 +1,85 @@ +#include + +#include +#include + +using namespace UniversalAsynchronousReceiverTransmitter; + +static inline void uart_wrapper(char c, void *unused) +{ + UART(COM1).Write(c); + (void)unused; +} + +static inline void WritePrefix(DebugLevel Level, const char *File, int Line, const char *Function) +{ + const char *DbgLvlString; + switch (Level) + { + case DebugLevelError: + DbgLvlString = "ERROR"; + break; + case DebugLevelWarning: + DbgLvlString = "WARN "; + break; + case DebugLevelInfo: + DbgLvlString = "INFO "; + break; + case DebugLevelDebug: + DbgLvlString = "DEBUG"; + break; + case DebugLevelTrace: + DbgLvlString = "TRACE"; + break; + case DebugLevelFixme: + DbgLvlString = "FIXME"; + break; + default: + DbgLvlString = "UNKNW"; + break; + } + fctprintf(uart_wrapper, nullptr, "%s|%s->%s:%d: ", DbgLvlString, File, Function, Line); +} + +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); + vfctprintf(uart_wrapper, nullptr, 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); + vfctprintf(uart_wrapper, nullptr, Format, args); + va_end(args); + uart_wrapper('\n', nullptr); + } +} + +// 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); + vfctprintf(uart_wrapper, nullptr, 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); + vfctprintf(uart_wrapper, nullptr, Format, args); + va_end(args); + uart_wrapper('\n', nullptr); +} diff --git a/core/Memory/HeapAllocators/Xalloc.cpp b/core/Memory/HeapAllocators/Xalloc.cpp new file mode 100644 index 00000000..1a065a81 --- /dev/null +++ b/core/Memory/HeapAllocators/Xalloc.cpp @@ -0,0 +1,205 @@ +#include "Xalloc.hpp" + +namespace Xalloc +{ + class SmartSMAPClass + { + private: + AllocatorV1 *allocator = nullptr; + + public: + SmartSMAPClass(AllocatorV1 *allocator) + { + this->allocator = allocator; + this->allocator->Xstac(); + } + ~SmartSMAPClass() { this->allocator->Xclac(); } + }; +#define SmartSMAP SmartSMAPClass XALLOC_CONCAT(SmartSMAP##_, __COUNTER__)(this) + + AllocatorV1::AllocatorV1(void *Address, bool UserMode, bool SMAPEnabled) + { + SmartSMAP; + void *Position = Address; + UserMapping = UserMode; + SMAPUsed = SMAPEnabled; + for (Xuint64_t i = 0; i < 0x20; i++) + { + void *Page = Xalloc_REQUEST_PAGE(); + if (UserMapping) + Xalloc_MAP_MEMORY(Position, Page, Xalloc_MAP_MEMORY_READ_WRITE | Xalloc_MAP_MEMORY_USER); + else + Xalloc_MAP_MEMORY(Position, Page, Xalloc_MAP_MEMORY_READ_WRITE); + Xalloc_trace("Preallocate Heap Memory (%#llx-%#llx [%#llx])...", Position, (Xuint64_t)Position + Xalloc_PAGE_SIZE, Page); + Position = (void *)((Xuint64_t)Position + Xalloc_PAGE_SIZE); + } + Xuint64_t HeapLength = 16 * Xalloc_PAGE_SIZE; + this->HeapStart = Address; + this->HeapEnd = (void *)((Xuint64_t)this->HeapStart + HeapLength); + HeapSegment *StartSegment = (HeapSegment *)Address; + StartSegment->Length = HeapLength - sizeof(HeapSegment); + StartSegment->Next = nullptr; + StartSegment->Last = nullptr; + StartSegment->IsFree = true; + this->LastSegment = StartSegment; + } + + AllocatorV1::~AllocatorV1() + { + SmartSMAP; + Xalloc_trace("Destructor not implemented yet."); + } + + void AllocatorV1::ExpandHeap(Xuint64_t Length) + { + if (Length % Xalloc_PAGE_SIZE) + { + Length -= Length % Xalloc_PAGE_SIZE; + Length += Xalloc_PAGE_SIZE; + } + Xuint64_t PageCount = Length / Xalloc_PAGE_SIZE; + HeapSegment *NewSegment = (HeapSegment *)this->HeapEnd; + for (Xuint64_t i = 0; i < PageCount; i++) + { + void *Page = Xalloc_REQUEST_PAGE(); + if (UserMapping) + Xalloc_MAP_MEMORY(this->HeapEnd, Page, Xalloc_MAP_MEMORY_READ_WRITE | Xalloc_MAP_MEMORY_USER); + else + Xalloc_MAP_MEMORY(this->HeapEnd, Page, Xalloc_MAP_MEMORY_READ_WRITE); + // Xalloc_trace("Expanding Heap Memory (%#llx-%#llx [%#llx])...", this->HeapEnd, (Xuint64_t)this->HeapEnd + Xalloc_PAGE_SIZE, Page); + this->HeapEnd = (void *)((Xuint64_t)this->HeapEnd + Xalloc_PAGE_SIZE); + } + NewSegment->IsFree = true; + NewSegment->Last = this->LastSegment; + this->LastSegment->Next = NewSegment; + this->LastSegment = NewSegment; + NewSegment->Next = nullptr; + NewSegment->Length = Length - sizeof(HeapSegment); + NewSegment->CombineBackward(this->LastSegment); + } + + void *AllocatorV1::Malloc(Xuint64_t Size) + { + SmartSMAP; + if (this->HeapStart == nullptr) + { + Xalloc_err("Memory allocation not initialized yet!"); + return 0; + } + + if (Size < 0x10) + { + // Xalloc_warn("Allocation size is too small, using 0x10 instead!"); + Size = 0x10; + } + + // #ifdef DEBUG + // if (Size < 1024) + // debug("Allocating %dB", Size); + // else if (TO_KB(Size) < 1024) + // debug("Allocating %dKB", TO_KB(Size)); + // else if (TO_MB(Size) < 1024) + // debug("Allocating %dMB", TO_MB(Size)); + // else if (TO_GB(Size) < 1024) + // debug("Allocating %dGB", TO_GB(Size)); + // #endif + + if (Size % 0x10 > 0) // it is not a multiple of 0x10 + { + Size -= (Size % 0x10); + Size += 0x10; + } + if (Size == 0) + { + return nullptr; + } + + HeapSegment *CurrentSegment = (HeapSegment *)this->HeapStart; + while (true) + { + if (CurrentSegment->IsFree) + { + if (CurrentSegment->Length > Size) + { + CurrentSegment->Split(Size, this->LastSegment); + CurrentSegment->IsFree = false; + return (void *)((Xuint64_t)CurrentSegment + sizeof(HeapSegment)); + } + if (CurrentSegment->Length == Size) + { + CurrentSegment->IsFree = false; + return (void *)((Xuint64_t)CurrentSegment + sizeof(HeapSegment)); + } + } + if (CurrentSegment->Next == nullptr) + break; + CurrentSegment = CurrentSegment->Next; + } + ExpandHeap(Size); + return this->Malloc(Size); + } + + void AllocatorV1::Free(void *Address) + { + SmartSMAP; + if (this->HeapStart == nullptr) + { + Xalloc_err("Memory allocation not initialized yet!"); + return; + } + HeapSegment *Segment = (HeapSegment *)Address - 1; + Segment->IsFree = true; + Segment->CombineForward(this->LastSegment); + Segment->CombineBackward(this->LastSegment); + } + + void *AllocatorV1::Calloc(Xuint64_t NumberOfBlocks, Xuint64_t Size) + { + SmartSMAP; + if (this->HeapStart == nullptr) + { + Xalloc_err("Memory allocation not initialized yet!"); + return 0; + } + + if (Size < 0x10) + { + // Xalloc_warn("Allocation size is too small, using 0x10 instead!"); + Size = 0x10; + } + + void *Block = this->Malloc(NumberOfBlocks * Size); + if (Block) + Xmemset(Block, 0, NumberOfBlocks * Size); + return Block; + } + + void *AllocatorV1::Realloc(void *Address, Xuint64_t Size) + { + SmartSMAP; + if (this->HeapStart == nullptr) + { + Xalloc_err("Memory allocation not initialized yet!"); + return 0; + } + if (!Address && Size == 0) + { + this->Free(Address); + return nullptr; + } + else if (!Address) + { + return this->Calloc(Size, sizeof(char)); + } + + if (Size < 0x10) + { + // Xalloc_warn("Allocation size is too small, using 0x10 instead!"); + Size = 0x10; + } + + void *newAddress = this->Calloc(Size, sizeof(char)); + Xmemcpy(newAddress, Address, Size); + return newAddress; + } +} diff --git a/core/Memory/HeapAllocators/Xalloc.hpp b/core/Memory/HeapAllocators/Xalloc.hpp new file mode 100644 index 00000000..fbc3a534 --- /dev/null +++ b/core/Memory/HeapAllocators/Xalloc.hpp @@ -0,0 +1,180 @@ +#pragma once +#include +#include + +// Functions defines + +// Page allocation functions +#define Xalloc_REQUEST_PAGE() KernelAllocator.RequestPage() +#define Xalloc_REQUEST_PAGES(Pages) KernelAllocator.RequestPages(Pages) +#define Xalloc_FREE_PAGE(Address) KernelAllocator.FreePage(Address) +#define Xalloc_FREE_PAGES(Address, Pages) KernelAllocator.FreePages(Address, Pages) + +#define Xalloc_MAP_MEMORY(VirtualAddress, PhysicalAddress, Flags) Memory::Virtual(KernelPageTable).Map(VirtualAddress, PhysicalAddress, Flags) +#define Xalloc_UNMAP_MEMORY(VirtualAddress) Memory::Virtual(KernelPageTable).Unmap(VirtualAddress) +#define Xalloc_MAP_MEMORY_READ_WRITE Memory::PTFlag::RW +#define Xalloc_MAP_MEMORY_USER Memory::PTFlag::US + +#define Xalloc_PAGE_SIZE PAGE_SIZE + +#define Xalloc_trace(m, ...) trace(m, ##__VA_ARGS__) +#define Xalloc_warn(m, ...) warn(m, ##__VA_ARGS__) +#define Xalloc_err(m, ...) error(m, ##__VA_ARGS__) + +#define XALLOC_CONCAT(x, y) x##y + +typedef long unsigned Xuint64_t; + +namespace Xalloc +{ + class AllocatorV1 + { + private: + struct HeapSegment + { + Xuint64_t Length; + HeapSegment *Next; + HeapSegment *Last; + bool IsFree; + + HeapSegment *Split(Xuint64_t SplitLength, HeapSegment *LastSegment) + { + if (SplitLength < 0x10) + return nullptr; + int64_t SplitSegmentLength = Length - SplitLength - (sizeof(HeapSegment)); + if (SplitSegmentLength < 0x10) + return nullptr; + HeapSegment *NewSplitHdr = (HeapSegment *)((Xuint64_t)this + SplitLength + sizeof(HeapSegment)); + Next->Last = NewSplitHdr; + NewSplitHdr->Next = Next; + Next = NewSplitHdr; + NewSplitHdr->Last = this; + NewSplitHdr->Length = SplitSegmentLength; + NewSplitHdr->IsFree = IsFree; + Length = SplitLength; + if (LastSegment == this) + LastSegment = NewSplitHdr; + return NewSplitHdr; + } + + void CombineForward(HeapSegment *LastSegment) + { + if (Next == nullptr) + return; + if (Next->IsFree == false) + return; + if (Next == LastSegment) + LastSegment = this; + if (Next->Next != nullptr) + Next->Next->Last = this; + + Length = Length + Next->Length + sizeof(HeapSegment); + Next = Next->Next; + } + + void CombineBackward(HeapSegment *LastSegment) + { + if (Last != nullptr && Last->IsFree) + Last->CombineForward(LastSegment); + } + } __attribute__((aligned(16))); + + void *HeapStart = nullptr; + void *HeapEnd = nullptr; + HeapSegment *LastSegment = nullptr; + bool UserMapping = false; + bool SMAPUsed = false; + + void ExpandHeap(Xuint64_t Length); + + // TODO: Change memcpy with an optimized version + static inline void *Xmemcpy(void *__restrict__ Destination, const void *__restrict__ Source, Xuint64_t Length) + { + unsigned char *dst = (unsigned char *)Destination; + const unsigned char *src = (const unsigned char *)Source; + for (Xuint64_t i = 0; i < Length; i++) + dst[i] = src[i]; + return Destination; + } + + // TODO: Change memset with an optimized version + static inline void *Xmemset(void *__restrict__ Destination, int Data, Xuint64_t Length) + { + unsigned char *Buffer = (unsigned char *)Destination; + for (Xuint64_t i = 0; i < Length; i++) + Buffer[i] = (unsigned char)Data; + return Destination; + } + + public: + inline void Xstac() + { + if (this->SMAPUsed) + { +#if defined(__amd64__) || defined(__i386__) + asm volatile("stac" :: + : "cc"); +#endif + } + } + + inline void Xclac() + { + if (this->SMAPUsed) + { +#if defined(__amd64__) || defined(__i386__) + asm volatile("clac" :: + : "cc"); +#endif + } + } + + /** + * @brief Construct a new Allocator V1 object + * + * @param Address Virtual address to allocate. + * @param UserMode Map the new pages with USER flag? + * @param SMAPEnabled Does the kernel has Supervisor Mode Access Prevention enabled? + */ + AllocatorV1(void *Address, bool UserMode, bool SMAPEnabled); + + /** + * @brief Destroy the Allocator V 1 object + * + */ + ~AllocatorV1(); + + /** + * @brief Allocate a new memory block + * + * @param Size Size of the block to allocate. + * @return void* Pointer to the allocated block. + */ + void *Malloc(Xuint64_t Size); + + /** + * @brief Free a previously allocated block + * + * @param Address Address of the block to free. + */ + void Free(void *Address); + + /** + * @brief Allocate a new memory block + * + * @param NumberOfBlocks Number of blocks to allocate. + * @param Size Size of the block to allocate. + * @return void* Pointer to the allocated block. + */ + void *Calloc(Xuint64_t NumberOfBlocks, Xuint64_t Size); + + /** + * @brief Reallocate a previously allocated block + * + * @param Address Address of the block to reallocate. + * @param Size New size of the block. + * @return void* Pointer to the reallocated block. + */ + void *Realloc(void *Address, Xuint64_t Size); + }; +} diff --git a/core/Memory/Memory.cpp b/core/Memory/Memory.cpp new file mode 100644 index 00000000..00f7a9e5 --- /dev/null +++ b/core/Memory/Memory.cpp @@ -0,0 +1,215 @@ +#include + +#include +#include + +#include "HeapAllocators/Xalloc.hpp" +#include "../lib/liballoc_1_1.h" + +using namespace Memory; + +Physical KernelAllocator; +PageTable *KernelPageTable = nullptr; + +enum MemoryAllocatorType +{ + None, + Pages, + XallocV1, + liballoc11 +}; + +static MemoryAllocatorType AllocatorType = MemoryAllocatorType::None; +Xalloc::AllocatorV1 *XallocV1Allocator = nullptr; + +#ifdef DEBUG +void tracepagetable(PageTable *pt) +{ + for (int i = 0; i < 512; i++) + { + if (pt->Entries[i].Value.Present) + debug("Entry %d: %x %x %x %x %x %x %x %x %x %x %x %p-%#lx", i, + pt->Entries[i].Value.Present, pt->Entries[i].Value.ReadWrite, + pt->Entries[i].Value.UserSupervisor, pt->Entries[i].Value.WriteThrough, + pt->Entries[i].Value.CacheDisable, pt->Entries[i].Value.Accessed, + pt->Entries[i].Value.Dirty, pt->Entries[i].Value.PageSize, + pt->Entries[i].Value.Global, pt->Entries[i].Value.PageAttributeTable, + pt->Entries[i].Value.ExecuteDisable, pt->Entries[i].GetAddress(), + pt->Entries[i].Value); + } +} +#endif + +void InitializeMemoryManagement(BootInfo *Info) +{ + trace("Initializing Physical Memory Manager"); + KernelAllocator = Physical(); + KernelAllocator.Init(Info); + debug("Memory Info: %dMB / %dMB (%dMB reserved)", + TO_MB(KernelAllocator.GetUsedMemory()), + TO_MB(KernelAllocator.GetTotalMemory()), + TO_MB(KernelAllocator.GetReservedMemory())); + + AllocatorType = MemoryAllocatorType::Pages; + + trace("Initializing Virtual Memory Manager"); + KernelPageTable = (PageTable *)KernelAllocator.RequestPage(); + memset(KernelPageTable, 0, PAGE_SIZE); + Virtual kva = Virtual(KernelPageTable); + + uint64_t KernelStart = (uint64_t)&_kernel_start; + uint64_t KernelTextEnd = (uint64_t)&_kernel_text_end; + uint64_t KernelDataEnd = (uint64_t)&_kernel_data_end; + uint64_t KernelRoDataEnd = (uint64_t)&_kernel_rodata_end; + uint64_t KernelEnd = (uint64_t)&_kernel_end; + + uint64_t VirtualOffsetNormalVMA = NORMAL_VMA_OFFSET; + uint64_t BaseKernelMapAddress = (uint64_t)Info->Kernel.PhysicalBase; + + for (uint64_t t = 0; t < Info->Memory.Size; t += PAGE_SIZE) + { + kva.Map((void *)t, (void *)t, PTFlag::RW); + kva.Map((void *)VirtualOffsetNormalVMA, (void *)t, PTFlag::RW); + VirtualOffsetNormalVMA += PAGE_SIZE; + } + + /* Mapping Framebuffer address */ + int itrfb = 0; + while (1) + { + if (!Info->Framebuffer[itrfb].BaseAddress) + break; + + for (uint64_t fb_base = (uint64_t)Info->Framebuffer[itrfb].BaseAddress; + fb_base < ((uint64_t)Info->Framebuffer[itrfb].BaseAddress + ((Info->Framebuffer[itrfb].Pitch * Info->Framebuffer[itrfb].Height) + PAGE_SIZE)); + fb_base += PAGE_SIZE) + kva.Map((void *)(fb_base + NORMAL_VMA_OFFSET), (void *)fb_base, PTFlag::RW | PTFlag::US); + itrfb++; + } + + /* Kernel mapping */ + for (uint64_t k = KernelStart; k < KernelTextEnd; k += PAGE_SIZE) + { + kva.Map((void *)k, (void *)BaseKernelMapAddress, PTFlag::RW); + KernelAllocator.LockPage((void *)BaseKernelMapAddress); + BaseKernelMapAddress += PAGE_SIZE; + } + + for (uint64_t k = KernelTextEnd; k < KernelDataEnd; k += PAGE_SIZE) + { + kva.Map((void *)k, (void *)BaseKernelMapAddress, PTFlag::RW); + KernelAllocator.LockPage((void *)BaseKernelMapAddress); + BaseKernelMapAddress += PAGE_SIZE; + } + + for (uint64_t k = KernelDataEnd; k < KernelRoDataEnd; k += PAGE_SIZE) + { + kva.Map((void *)k, (void *)BaseKernelMapAddress, PTFlag::P); + KernelAllocator.LockPage((void *)BaseKernelMapAddress); + BaseKernelMapAddress += PAGE_SIZE; + } + + for (uint64_t k = KernelRoDataEnd; k < KernelEnd; k += PAGE_SIZE) + { + kva.Map((void *)k, (void *)BaseKernelMapAddress, PTFlag::RW); + KernelAllocator.LockPage((void *)BaseKernelMapAddress); + BaseKernelMapAddress += PAGE_SIZE; + } + + debug("\nStart: %#llx - Text End: %#llx - RoEnd: %#llx - End: %#llx\nStart Physical: %#llx - End Physical: %#llx", + KernelStart, KernelTextEnd, KernelRoDataEnd, KernelEnd, Info->Kernel.PhysicalBase, BaseKernelMapAddress - PAGE_SIZE); + + /* KernelStart KernelTextEnd KernelRoDataEnd KernelEnd + Kernel Start & Text Start ------ Text End ------ Kernel Rodata End ------ Kernel Data End & Kernel End + */ + trace("Applying new page table from address %p", KernelPageTable); +#ifdef DEBUG + tracepagetable(KernelPageTable); +#endif +#if defined(__amd64__) || defined(__i386__) + asm volatile("mov %0, %%cr3" ::"r"(KernelPageTable)); +#elif defined(__aarch64__) + asm volatile("msr ttbr0_el1, %0" ::"r"(KernelPageTable)); +#endif + if (strstr(Info->Kernel.CommandLine, "xallocv1")) + { + XallocV1Allocator = new Xalloc::AllocatorV1((void *)KERNEL_HEAP_BASE, false, false); + AllocatorType = MemoryAllocatorType::XallocV1; + trace("XallocV1 Allocator initialized (%p)", XallocV1Allocator); + } + else if (strstr(Info->Kernel.CommandLine, "liballoc11")) + { + AllocatorType = MemoryAllocatorType::liballoc11; + } +} + +void *HeapMalloc(uint64_t Size) +{ + switch (AllocatorType) + { + case MemoryAllocatorType::Pages: + return KernelAllocator.RequestPages(TO_PAGES(Size)); + case MemoryAllocatorType::XallocV1: + return XallocV1Allocator->Malloc(Size); + case MemoryAllocatorType::liballoc11: + return PREFIX(malloc)(Size); + default: + throw; + } +} + +void *HeapCalloc(uint64_t n, uint64_t Size) +{ + switch (AllocatorType) + { + case MemoryAllocatorType::Pages: + return KernelAllocator.RequestPages(TO_PAGES(n * Size)); + case MemoryAllocatorType::XallocV1: + return XallocV1Allocator->Calloc(n, Size); + case MemoryAllocatorType::liballoc11: + return PREFIX(calloc)(n, Size); + default: + throw; + } +} + +void *HeapRealloc(void *Address, uint64_t Size) +{ + switch (AllocatorType) + { + case MemoryAllocatorType::Pages: + return KernelAllocator.RequestPages(TO_PAGES(Size)); // WARNING: Potential memory leak + case MemoryAllocatorType::XallocV1: + return XallocV1Allocator->Realloc(Address, Size); + case MemoryAllocatorType::liballoc11: + return PREFIX(realloc)(Address, Size); + default: + throw; + } +} + +void HeapFree(void *Address) +{ + switch (AllocatorType) + { + case MemoryAllocatorType::Pages: + KernelAllocator.FreePage(Address); // WARNING: Potential memory leak + break; + case MemoryAllocatorType::XallocV1: + XallocV1Allocator->Free(Address); + break; + case MemoryAllocatorType::liballoc11: + PREFIX(free) + (Address); + break; + default: + throw; + } +} + +void *operator new(uint64_t Size) { return HeapMalloc(Size); } +void *operator new[](uint64_t Size) { return HeapMalloc(Size); } +void operator delete(void *Pointer) { HeapFree(Pointer); } +void operator delete[](void *Pointer) { HeapFree(Pointer); } +void operator delete(void *Pointer, long unsigned int Size) { HeapFree(Pointer); } +void operator delete[](void *Pointer, long unsigned int Size) { HeapFree(Pointer); } diff --git a/core/Memory/PhysicalMemoryManager.cpp b/core/Memory/PhysicalMemoryManager.cpp new file mode 100644 index 00000000..46541312 --- /dev/null +++ b/core/Memory/PhysicalMemoryManager.cpp @@ -0,0 +1,266 @@ +#include + +#include + +namespace Memory +{ + uint64_t Physical::GetTotalMemory() + { + SMARTLOCK(this->MemoryLock); + return this->TotalMemory; + } + + uint64_t Physical::GetFreeMemory() + { + SMARTLOCK(this->MemoryLock); + return this->FreeMemory; + } + + uint64_t Physical::GetReservedMemory() + { + SMARTLOCK(this->MemoryLock); + return this->ReservedMemory; + } + + uint64_t Physical::GetUsedMemory() + { + SMARTLOCK(this->MemoryLock); + return this->UsedMemory; + } + + bool Physical::SwapPage(void *Address) + { + fixme("%p", Address); + return false; + } + + bool Physical::SwapPages(void *Address, uint64_t PageCount) + { + for (uint64_t i = 0; i < PageCount; i++) + if (!this->SwapPage((void *)((uint64_t)Address + (i * PAGE_SIZE)))) + return false; + return false; + } + + bool Physical::UnswapPage(void *Address) + { + fixme("%p", Address); + return false; + } + + bool Physical::UnswapPages(void *Address, uint64_t PageCount) + { + for (uint64_t i = 0; i < PageCount; i++) + if (!this->UnswapPage((void *)((uint64_t)Address + (i * PAGE_SIZE)))) + return false; + return false; + } + + void *Physical::RequestPage() + { + SMARTLOCK(this->MemoryLock); + for (; PageBitmapIndex < PageBitmap.Size * 8; PageBitmapIndex++) + { + if (PageBitmap[PageBitmapIndex] == true) + continue; + this->LockPage((void *)(PageBitmapIndex * PAGE_SIZE)); + return (void *)(PageBitmapIndex * PAGE_SIZE); + } + + if (this->SwapPage((void *)(PageBitmapIndex * PAGE_SIZE))) + { + 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) + CPU::Halt(); + return nullptr; + } + + void *Physical::RequestPages(uint64_t Count) + { + SMARTLOCK(this->MemoryLock); + 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 (uint64_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; + } + } + + if (this->SwapPages((void *)(PageBitmapIndex * PAGE_SIZE), Count)) + { + this->LockPages((void *)(PageBitmapIndex * PAGE_SIZE), Count); + 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) + CPU::Halt(); + return nullptr; + } + + void Physical::FreePage(void *Address) + { + SMARTLOCK(this->MemoryLock); + if (Address == nullptr) + { + warn("Null pointer passed to FreePage."); + return; + } + uint64_t Index = (uint64_t)Address / PAGE_SIZE; + if (PageBitmap[Index] == false) + return; + + if (PageBitmap.Set(Index, false)) + { + FreeMemory += PAGE_SIZE; + UsedMemory -= PAGE_SIZE; + if (PageBitmapIndex > Index) + PageBitmapIndex = Index; + } + } + + void Physical::FreePages(void *Address, uint64_t Count) + { + if (Address == nullptr || Count == 0) + { + warn("%s%s passed to FreePages.", Address == nullptr ? "Null pointer" : "", Count == 0 ? "Zero count" : ""); + return; + } + + for (uint64_t t = 0; t < Count; t++) + this->FreePage((void *)((uint64_t)Address + (t * PAGE_SIZE))); + } + + void Physical::LockPage(void *Address) + { + if (Address == nullptr) + warn("Trying to lock null address."); + + uint64_t Index = (uint64_t)Address / PAGE_SIZE; + if (PageBitmap[Index] == true) + return; + if (PageBitmap.Set(Index, true)) + { + FreeMemory -= PAGE_SIZE; + UsedMemory += PAGE_SIZE; + } + } + + void Physical::LockPages(void *Address, uint64_t PageCount) + { + if (Address == nullptr || PageCount == 0) + warn("Trying to lock %s%s.", Address ? "null address" : "", PageCount ? "0 pages" : ""); + + for (uint64_t i = 0; i < PageCount; i++) + this->LockPage((void *)((uint64_t)Address + (i * PAGE_SIZE))); + } + + void Physical::ReservePage(void *Address) + { + if (Address == nullptr) + warn("Trying to reserve null address."); + + uint64_t Index = (uint64_t)Address / PAGE_SIZE; + if (PageBitmap[Index] == true) + return; + + if (PageBitmap.Set(Index, true)) + { + FreeMemory -= PAGE_SIZE; + ReservedMemory += PAGE_SIZE; + } + } + + void Physical::ReservePages(void *Address, uint64_t PageCount) + { + if (Address == nullptr || PageCount == 0) + warn("Trying to reserve %s%s.", Address ? "null address" : "", PageCount ? "0 pages" : ""); + + for (uint64_t t = 0; t < PageCount; t++) + this->ReservePage((void *)((uint64_t)Address + (t * PAGE_SIZE))); + } + + void Physical::UnreservePage(void *Address) + { + if (Address == nullptr) + warn("Trying to unreserve null address."); + + uint64_t Index = (uint64_t)Address / PAGE_SIZE; + if (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, uint64_t PageCount) + { + if (Address == nullptr || PageCount == 0) + warn("Trying to unreserve %s%s.", Address ? "null address" : "", PageCount ? "0 pages" : ""); + + for (uint64_t t = 0; t < PageCount; t++) + this->UnreservePage((void *)((uint64_t)Address + (t * PAGE_SIZE))); + } + + void Physical::Init(BootInfo *Info) + { + SMARTLOCK(this->MemoryLock); + void *LargestFreeMemorySegment = nullptr; + uint64_t LargestFreeMemorySegmentSize = 0; + uint64_t MemorySize = Info->Memory.Size; + + for (uint64_t i = 0; i < Info->Memory.Entries; i++) + if (Info->Memory.Entry[i].Type == Usable) + if (Info->Memory.Entry[i].Length > LargestFreeMemorySegmentSize) + { + LargestFreeMemorySegment = (void *)Info->Memory.Entry[i].BaseAddress; + LargestFreeMemorySegmentSize = Info->Memory.Entry[i].Length; + debug("Largest free memory segment: %p (%dKB)", + (void *)Info->Memory.Entry[i].BaseAddress, + TO_KB(Info->Memory.Entry[i].Length)); + } + TotalMemory = MemorySize; + FreeMemory = MemorySize; + + uint64_t BitmapSize = ALIGN_UP((MemorySize / 0x1000) / 8, 0x1000); + trace("Initializing Bitmap (%p %dKB)", LargestFreeMemorySegment, TO_KB(BitmapSize)); + PageBitmap.Size = BitmapSize; + PageBitmap.Buffer = (uint8_t *)LargestFreeMemorySegment; + for (uint64_t i = 0; i < BitmapSize; i++) + *(uint8_t *)(PageBitmap.Buffer + i) = 0; + + this->ReservePages(0, MemorySize / PAGE_SIZE + 1); + for (uint64_t i = 0; i < Info->Memory.Entries; i++) + if (Info->Memory.Entry[i].Type == Usable) + this->UnreservePages((void *)Info->Memory.Entry[i].BaseAddress, Info->Memory.Entry[i].Length / PAGE_SIZE + 1); + this->ReservePages(0, 0x100); // Reserve between 0 and 0x100000 + this->LockPages(PageBitmap.Buffer, PageBitmap.Size / PAGE_SIZE + 1); + } + + Physical::Physical() {} + Physical::~Physical() {} +} diff --git a/core/Memory/VirtualMemoryManager.cpp b/core/Memory/VirtualMemoryManager.cpp new file mode 100644 index 00000000..5bdbb217 --- /dev/null +++ b/core/Memory/VirtualMemoryManager.cpp @@ -0,0 +1,123 @@ +#include + +#include +#include + +namespace Memory +{ + void Virtual::Map(void *VirtualAddress, void *PhysicalAddress, uint64_t Flags) + { + SMARTLOCK(this->MemoryLock); + if (!this->Table) + { + error("No page table"); + return; + } + PageMapIndexer Index = PageMapIndexer((uint64_t)VirtualAddress); + PageDirectoryEntry PDE = this->Table->Entries[Index.PDP_i]; + PageTable *PDP; + if (!PDE.GetFlag(PTFlag::P)) + { + PDP = (PageTable *)KernelAllocator.RequestPage(); + memset(PDP, 0, PAGE_SIZE); + PDE.SetAddress((uint64_t)PDP >> 12); + PDE.SetFlag(PTFlag::P, true); + PDE.AddFlag(Flags); + this->Table->Entries[Index.PDP_i] = PDE; + } + else + PDP = (PageTable *)((uint64_t)PDE.GetAddress() << 12); + + PDE = PDP->Entries[Index.PD_i]; + PageTable *PD; + if (!PDE.GetFlag(PTFlag::P)) + { + PD = (PageTable *)KernelAllocator.RequestPage(); + memset(PD, 0, PAGE_SIZE); + PDE.SetAddress((uint64_t)PD >> 12); + PDE.SetFlag(PTFlag::P, true); + PDE.AddFlag(Flags); + PDP->Entries[Index.PD_i] = PDE; + } + else + PD = (PageTable *)((uint64_t)PDE.GetAddress() << 12); + + PDE = PD->Entries[Index.PT_i]; + PageTable *PT; + if (!PDE.GetFlag(PTFlag::P)) + { + PT = (PageTable *)KernelAllocator.RequestPage(); + memset(PT, 0, PAGE_SIZE); + PDE.SetAddress((uint64_t)PT >> 12); + PDE.SetFlag(PTFlag::P, true); + PDE.AddFlag(Flags); + PD->Entries[Index.PT_i] = PDE; + } + else + PT = (PageTable *)((uint64_t)PDE.GetAddress() << 12); + + PDE = PT->Entries[Index.P_i]; + PDE.SetAddress((uint64_t)PhysicalAddress >> 12); + PDE.SetFlag(PTFlag::P, true); + PDE.AddFlag(Flags); + PT->Entries[Index.P_i] = PDE; +#if defined(__amd64__) || defined(__i386__) + asmv("invlpg (%0)" + : + : "r"(VirtualAddress) + : "memory"); +#elif defined(__aarch64__) + asmv("dsb sy"); + asmv("tlbi vae1is, %0" + : + : "r"(VirtualAddress) + : "memory"); + asmv("dsb sy"); + asmv("isb"); +#endif + } + + void Virtual::Map(void *VirtualAddress, void *PhysicalAddress, uint64_t PageCount, uint64_t Flags) + { + for (uint64_t i = 0; i < PageCount; i++) + this->Map((void *)((uint64_t)VirtualAddress + (i * PAGE_SIZE)), (void *)((uint64_t)PhysicalAddress + (i * PAGE_SIZE)), Flags); + } + + void Virtual::Unmap(void *VirtualAddress) + { + SMARTLOCK(this->MemoryLock); + if (!this->Table) + { + error("No page table"); + return; + } + + PageMapIndexer Index = PageMapIndexer((uint64_t)VirtualAddress); + PageDirectoryEntry PDE = this->Table->Entries[Index.PDP_i]; + PDE.ClearFlags(); + +#if defined(__amd64__) || defined(__i386__) + asmv("invlpg (%0)" + : + : "r"(VirtualAddress) + : "memory"); +#elif defined(__aarch64__) + asmv("dsb sy"); + asmv("tlbi vae1is, %0" + : + : "r"(VirtualAddress) + : "memory"); + asmv("dsb sy"); + asmv("isb"); +#endif + } + + void Virtual::Unmap(void *VirtualAddress, uint64_t PageCount) + { + for (uint64_t i = 0; i < PageCount; i++) + this->Unmap((void *)((uint64_t)VirtualAddress + (i * PAGE_SIZE))); + } + + Virtual::Virtual(PageTable *Table) { this->Table = Table; } + Virtual::~Virtual() {} +} diff --git a/core/StackGuard.c b/core/StackGuard.c new file mode 100644 index 00000000..3c9ec84a --- /dev/null +++ b/core/StackGuard.c @@ -0,0 +1,58 @@ +#include +#include + +#ifndef STACK_CHK_GUARD_VALUE +#if UINTPTR_MAX == UINT32_MAX +#define STACK_CHK_GUARD_VALUE 0x25F6CC8D +#else +#define STACK_CHK_GUARD_VALUE 0xBADFE2EC255A8572 +#endif +#endif + +__attribute__((weak)) uintptr_t __stack_chk_guard = 0; + +__attribute__((weak)) uintptr_t __stack_chk_guard_init(void) +{ + return STACK_CHK_GUARD_VALUE; +} + +static void __attribute__((constructor, no_stack_protector)) __construct_stk_chk_guard() +{ + if (__stack_chk_guard == 0) + __stack_chk_guard = __stack_chk_guard_init(); +} + +// https://opensource.apple.com/source/xnu/xnu-1504.7.4/libkern/stack_protector.c.auto.html +// static void __guard_setup(void) __attribute__((constructor)); + +// static void __guard_setup(void) +// { +// read_random(__stack_chk_guard, sizeof(__stack_chk_guard)); +// } + +__attribute__((weak, noreturn, no_stack_protector)) void __stack_chk_fail(void) +{ + error("Stack smashing detected!", false); + for (;;) + { +#if defined(__amd64__) || defined(__i386__) + asmv("hlt"); +#elif defined(__aarch64__) + asmv("wfe"); +#endif + } +} + +// https://github.com/gcc-mirror/gcc/blob/master/libssp/ssp.c +__attribute__((weak, noreturn, no_stack_protector)) void __chk_fail(void) +{ + error("Buffer overflow detected!", false); + for (;;) + { +#if defined(__amd64__) || defined(__i386__) + asmv("hlt"); +#elif defined(__aarch64__) + asmv("wfe"); +#endif + } +} diff --git a/include/.gitkeep b/include/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/include/bitmap.hpp b/include/bitmap.hpp new file mode 100644 index 00000000..b34d5be8 --- /dev/null +++ b/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/include/boot/binfo.h b/include/boot/binfo.h new file mode 100644 index 00000000..ba6315fd --- /dev/null +++ b/include/boot/binfo.h @@ -0,0 +1,120 @@ +#ifndef __FENNIX_KERNEL_BOOT_INFO_H__ +#define __FENNIX_KERNEL_BOOT_INFO_H__ + +#include + +enum MemoryType +{ + Usable, + Reserved, + ACPIReclaimable, + ACPINVS, + BadMemory, + BootloaderReclaimable, + KernelAndModules, + Framebuffer, + Unknown +}; + +#define MAX_FRAMEBUFFERS 16 +#define MAX_MEMORY_ENTRIES 256 +#define MAX_MODULES 16 + +struct BootInfo +{ + struct FramebufferInfo + { + void *BaseAddress; + uint64_t Width; + uint64_t Height; + uint64_t Pitch; + uint16_t BitsPerPixel; + uint8_t MemoryModel; + uint8_t RedMaskSize; + uint8_t RedMaskShift; + uint8_t GreenMaskSize; + uint8_t GreenMaskShift; + uint8_t BlueMaskSize; + uint8_t BlueMaskShift; + void *ExtendedDisplayIdentificationData; + uint64_t EDIDSize; + } Framebuffer[MAX_FRAMEBUFFERS]; + + struct MemoryInfo + { + struct MemoryEntryInfo + { + void *BaseAddress; + uint64_t Length; + enum MemoryType Type; + } Entry[MAX_MEMORY_ENTRIES]; + uint64_t Entries; + uint64_t Size; + } Memory; + + struct ModuleInfo + { + void *Address; + char Path[256]; + char CommandLine[256]; + uint64_t Size; + } Modules[MAX_MODULES]; + + struct RSDPInfo + { + /** + * @brief Signature + */ + unsigned char Signature[8]; + /** + * @brief Checksum + */ + uint8_t Checksum; + /** + * @brief OEM ID + */ + uint8_t OEMID[6]; + /** + * @brief Revision + */ + uint8_t Revision; + /** + * @brief Address of the Root System Description Table + */ + uint32_t RSDTAddress; + /* END OF RSDP 1.0 */ + + /** + * @brief Length + */ + uint32_t Length; + /** + * @brief Extended System Descriptor Table + */ + uint64_t XSDTAddress; + /** + * @brief Extended checksum + */ + uint8_t ExtendedChecksum; + /** + * @brief Reserved + */ + uint8_t Reserved[3]; + } __attribute__((packed)) * RSDP; + + struct KernelInfo + { + void *PhysicalBase; + void *VirtualBase; + char CommandLine[256]; + uint64_t Size; + } Kernel; + + struct BootloaderInfo + { + char Name[256]; + char Version[64]; + } Bootloader; +}; + +#endif // !__FENNIX_KERNEL_BOOT_INFO_H__ diff --git a/include/boot/protocols/limine.h b/include/boot/protocols/limine.h new file mode 100644 index 00000000..e772b961 --- /dev/null +++ b/include/boot/protocols/limine.h @@ -0,0 +1,420 @@ +#ifndef _LIMINE_H +#define _LIMINE_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/* Misc */ + +#ifdef LIMINE_NO_POINTERS +# define LIMINE_PTR(TYPE) uint64_t +#else +# define LIMINE_PTR(TYPE) TYPE +#endif + +#define LIMINE_COMMON_MAGIC 0xc7b1dd30df4c8b88, 0x0a82e883a194f07b + +struct limine_uuid { + uint32_t a; + uint16_t b; + uint16_t c; + uint8_t d[8]; +}; + +#define LIMINE_MEDIA_TYPE_GENERIC 0 +#define LIMINE_MEDIA_TYPE_OPTICAL 1 +#define LIMINE_MEDIA_TYPE_TFTP 2 + +struct limine_file { + uint64_t revision; + LIMINE_PTR(void *) address; + uint64_t size; + LIMINE_PTR(char *) path; + LIMINE_PTR(char *) cmdline; + uint32_t media_type; + uint32_t unused; + uint32_t tftp_ip; + uint32_t tftp_port; + uint32_t partition_index; + uint32_t mbr_disk_id; + struct limine_uuid gpt_disk_uuid; + struct limine_uuid gpt_part_uuid; + struct limine_uuid part_uuid; +}; + +/* Boot info */ + +#define LIMINE_BOOTLOADER_INFO_REQUEST { LIMINE_COMMON_MAGIC, 0xf55038d8e2a1202f, 0x279426fcf5f59740 } + +struct limine_bootloader_info_response { + uint64_t revision; + LIMINE_PTR(char *) name; + LIMINE_PTR(char *) version; +}; + +struct limine_bootloader_info_request { + uint64_t id[4]; + uint64_t revision; + LIMINE_PTR(struct limine_bootloader_info_response *) response; +}; + +/* Stack size */ + +#define LIMINE_STACK_SIZE_REQUEST { LIMINE_COMMON_MAGIC, 0x224ef0460a8e8926, 0xe1cb0fc25f46ea3d } + +struct limine_stack_size_response { + uint64_t revision; +}; + +struct limine_stack_size_request { + uint64_t id[4]; + uint64_t revision; + LIMINE_PTR(struct limine_stack_size_response *) response; + uint64_t stack_size; +}; + +/* HHDM */ + +#define LIMINE_HHDM_REQUEST { LIMINE_COMMON_MAGIC, 0x48dcf1cb8ad2b852, 0x63984e959a98244b } + +struct limine_hhdm_response { + uint64_t revision; + uint64_t offset; +}; + +struct limine_hhdm_request { + uint64_t id[4]; + uint64_t revision; + LIMINE_PTR(struct limine_hhdm_response *) response; +}; + +/* Framebuffer */ + +#define LIMINE_FRAMEBUFFER_REQUEST { LIMINE_COMMON_MAGIC, 0x9d5827dcd881dd75, 0xa3148604f6fab11b } + +#define LIMINE_FRAMEBUFFER_RGB 1 + +struct limine_framebuffer { + LIMINE_PTR(void *) address; + uint64_t width; + uint64_t height; + uint64_t pitch; + uint16_t bpp; + uint8_t memory_model; + uint8_t red_mask_size; + uint8_t red_mask_shift; + uint8_t green_mask_size; + uint8_t green_mask_shift; + uint8_t blue_mask_size; + uint8_t blue_mask_shift; + uint8_t unused[7]; + uint64_t edid_size; + LIMINE_PTR(void *) edid; +}; + +struct limine_framebuffer_response { + uint64_t revision; + uint64_t framebuffer_count; + LIMINE_PTR(struct limine_framebuffer **) framebuffers; +}; + +struct limine_framebuffer_request { + uint64_t id[4]; + uint64_t revision; + LIMINE_PTR(struct limine_framebuffer_response *) response; +}; + +/* Terminal */ + +#define LIMINE_TERMINAL_REQUEST { LIMINE_COMMON_MAGIC, 0xc8ac59310c2b0844, 0xa68d0c7265d38878 } + +#define LIMINE_TERMINAL_CB_DEC 10 +#define LIMINE_TERMINAL_CB_BELL 20 +#define LIMINE_TERMINAL_CB_PRIVATE_ID 30 +#define LIMINE_TERMINAL_CB_STATUS_REPORT 40 +#define LIMINE_TERMINAL_CB_POS_REPORT 50 +#define LIMINE_TERMINAL_CB_KBD_LEDS 60 +#define LIMINE_TERMINAL_CB_MODE 70 +#define LIMINE_TERMINAL_CB_LINUX 80 + +#define LIMINE_TERMINAL_CTX_SIZE ((uint64_t)(-1)) +#define LIMINE_TERMINAL_CTX_SAVE ((uint64_t)(-2)) +#define LIMINE_TERMINAL_CTX_RESTORE ((uint64_t)(-3)) +#define LIMINE_TERMINAL_FULL_REFRESH ((uint64_t)(-4)) + +struct limine_terminal; + +typedef void (*limine_terminal_write)(struct limine_terminal *, const char *, uint64_t); +typedef void (*limine_terminal_callback)(struct limine_terminal *, uint64_t, uint64_t, uint64_t, uint64_t); + +struct limine_terminal { + uint64_t columns; + uint64_t rows; + LIMINE_PTR(struct limine_framebuffer *) framebuffer; +}; + +struct limine_terminal_response { + uint64_t revision; + uint64_t terminal_count; + LIMINE_PTR(struct limine_terminal **) terminals; + LIMINE_PTR(limine_terminal_write) write; +}; + +struct limine_terminal_request { + uint64_t id[4]; + uint64_t revision; + LIMINE_PTR(struct limine_terminal_response *) response; + LIMINE_PTR(limine_terminal_callback) callback; +}; + +/* 5-level paging */ + +#define LIMINE_5_LEVEL_PAGING_REQUEST { LIMINE_COMMON_MAGIC, 0x94469551da9b3192, 0xebe5e86db7382888 } + +struct limine_5_level_paging_response { + uint64_t revision; +}; + +struct limine_5_level_paging_request { + uint64_t id[4]; + uint64_t revision; + LIMINE_PTR(struct limine_5_level_paging_response *) response; +}; + +/* SMP */ + +#define LIMINE_SMP_REQUEST { LIMINE_COMMON_MAGIC, 0x95a67b819a1b857e, 0xa0b61b723b6a73e0 } + +struct limine_smp_info; + +typedef void (*limine_goto_address)(struct limine_smp_info *); + +#if defined (__x86_64__) || defined (__i386__) + +#define LIMINE_SMP_X2APIC (1 << 0) + +struct limine_smp_info { + uint32_t processor_id; + uint32_t lapic_id; + uint64_t reserved; + LIMINE_PTR(limine_goto_address) goto_address; + uint64_t extra_argument; +}; + +struct limine_smp_response { + uint64_t revision; + uint32_t flags; + uint32_t bsp_lapic_id; + uint64_t cpu_count; + LIMINE_PTR(struct limine_smp_info **) cpus; +}; + +#elif defined (__aarch64__) + +struct limine_smp_info { + uint32_t processor_id; + uint32_t gic_iface_no; + uint64_t mpidr; + uint64_t reserved; + LIMINE_PTR(limine_goto_address) goto_address; + uint64_t extra_argument; +}; + +struct limine_smp_response { + uint64_t revision; + uint32_t flags; + uint64_t bsp_mpidr; + uint64_t cpu_count; + LIMINE_PTR(struct limine_smp_info **) cpus; +}; + +#else +#error Unknown architecture +#endif + +struct limine_smp_request { + uint64_t id[4]; + uint64_t revision; + LIMINE_PTR(struct limine_smp_response *) response; + uint64_t flags; +}; + +/* Memory map */ + +#define LIMINE_MEMMAP_REQUEST { LIMINE_COMMON_MAGIC, 0x67cf3d9d378a806f, 0xe304acdfc50c3c62 } + +#define LIMINE_MEMMAP_USABLE 0 +#define LIMINE_MEMMAP_RESERVED 1 +#define LIMINE_MEMMAP_ACPI_RECLAIMABLE 2 +#define LIMINE_MEMMAP_ACPI_NVS 3 +#define LIMINE_MEMMAP_BAD_MEMORY 4 +#define LIMINE_MEMMAP_BOOTLOADER_RECLAIMABLE 5 +#define LIMINE_MEMMAP_KERNEL_AND_MODULES 6 +#define LIMINE_MEMMAP_FRAMEBUFFER 7 + +struct limine_memmap_entry { + uint64_t base; + uint64_t length; + uint64_t type; +}; + +struct limine_memmap_response { + uint64_t revision; + uint64_t entry_count; + LIMINE_PTR(struct limine_memmap_entry **) entries; +}; + +struct limine_memmap_request { + uint64_t id[4]; + uint64_t revision; + LIMINE_PTR(struct limine_memmap_response *) response; +}; + +/* Entry point */ + +#define LIMINE_ENTRY_POINT_REQUEST { LIMINE_COMMON_MAGIC, 0x13d86c035a1cd3e1, 0x2b0caa89d8f3026a } + +typedef void (*limine_entry_point)(void); + +struct limine_entry_point_response { + uint64_t revision; +}; + +struct limine_entry_point_request { + uint64_t id[4]; + uint64_t revision; + LIMINE_PTR(struct limine_entry_point_response *) response; + LIMINE_PTR(limine_entry_point) entry; +}; + +/* Kernel File */ + +#define LIMINE_KERNEL_FILE_REQUEST { LIMINE_COMMON_MAGIC, 0xad97e90e83f1ed67, 0x31eb5d1c5ff23b69 } + +struct limine_kernel_file_response { + uint64_t revision; + LIMINE_PTR(struct limine_file *) kernel_file; +}; + +struct limine_kernel_file_request { + uint64_t id[4]; + uint64_t revision; + LIMINE_PTR(struct limine_kernel_file_response *) response; +}; + +/* Module */ + +#define LIMINE_MODULE_REQUEST { LIMINE_COMMON_MAGIC, 0x3e7e279702be32af, 0xca1c4f3bd1280cee } + +struct limine_module_response { + uint64_t revision; + uint64_t module_count; + LIMINE_PTR(struct limine_file **) modules; +}; + +struct limine_module_request { + uint64_t id[4]; + uint64_t revision; + LIMINE_PTR(struct limine_module_response *) response; +}; + +/* RSDP */ + +#define LIMINE_RSDP_REQUEST { LIMINE_COMMON_MAGIC, 0xc5e77b6b397e7b43, 0x27637845accdcf3c } + +struct limine_rsdp_response { + uint64_t revision; + LIMINE_PTR(void *) address; +}; + +struct limine_rsdp_request { + uint64_t id[4]; + uint64_t revision; + LIMINE_PTR(struct limine_rsdp_response *) response; +}; + +/* SMBIOS */ + +#define LIMINE_SMBIOS_REQUEST { LIMINE_COMMON_MAGIC, 0x9e9046f11e095391, 0xaa4a520fefbde5ee } + +struct limine_smbios_response { + uint64_t revision; + LIMINE_PTR(void *) entry_32; + LIMINE_PTR(void *) entry_64; +}; + +struct limine_smbios_request { + uint64_t id[4]; + uint64_t revision; + LIMINE_PTR(struct limine_smbios_response *) response; +}; + +/* EFI system table */ + +#define LIMINE_EFI_SYSTEM_TABLE_REQUEST { LIMINE_COMMON_MAGIC, 0x5ceba5163eaaf6d6, 0x0a6981610cf65fcc } + +struct limine_efi_system_table_response { + uint64_t revision; + LIMINE_PTR(void *) address; +}; + +struct limine_efi_system_table_request { + uint64_t id[4]; + uint64_t revision; + LIMINE_PTR(struct limine_efi_system_table_response *) response; +}; + +/* Boot time */ + +#define LIMINE_BOOT_TIME_REQUEST { LIMINE_COMMON_MAGIC, 0x502746e184c088aa, 0xfbc5ec83e6327893 } + +struct limine_boot_time_response { + uint64_t revision; + int64_t boot_time; +}; + +struct limine_boot_time_request { + uint64_t id[4]; + uint64_t revision; + LIMINE_PTR(struct limine_boot_time_response *) response; +}; + +/* Kernel address */ + +#define LIMINE_KERNEL_ADDRESS_REQUEST { LIMINE_COMMON_MAGIC, 0x71ba76863cc55f63, 0xb2644a48c516a487 } + +struct limine_kernel_address_response { + uint64_t revision; + uint64_t physical_base; + uint64_t virtual_base; +}; + +struct limine_kernel_address_request { + uint64_t id[4]; + uint64_t revision; + LIMINE_PTR(struct limine_kernel_address_response *) response; +}; + +/* Device Tree Blob */ + +#define LIMINE_DTB_REQUEST { LIMINE_COMMON_MAGIC, 0xb40ddb48fb54bac7, 0x545081493f81ffb7 } + +struct limine_dtb_response { + uint64_t revision; + LIMINE_PTR(void *) dtb_ptr; +}; + +struct limine_dtb_request { + uint64_t id[4]; + uint64_t revision; + LIMINE_PTR(struct limine_dtb_response *) response; +}; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/boot/protocols/multiboot2.h b/include/boot/protocols/multiboot2.h new file mode 100644 index 00000000..b1a8a245 --- /dev/null +++ b/include/boot/protocols/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/include/cpu.hpp b/include/cpu.hpp new file mode 100644 index 00000000..cb1c1bde --- /dev/null +++ b/include/cpu.hpp @@ -0,0 +1,134 @@ +#ifndef __FENNIX_KERNEL_CPU_H__ +#define __FENNIX_KERNEL_CPU_H__ + +#include + +/** + * @brief CPU related functions. + */ +namespace CPU +{ + /** + * @brief Enum for CPU::Interrupts() function. + */ + enum InterruptsType + { + /** + * @brief Check if interrupts are enabled. + */ + Check, + /** + * @brief Enable interrupts. + */ + Enable, + /** + * @brief Disable interrupts. + */ + Disable + }; + + /** + * @brief Pause the CPU + */ + void Pause(); + /** + * @brief Halt the CPU + */ + void Halt(); + /** + * @brief Check if interrupts are enabled + * + * @return true If InterruptsType::Check and interrupts are enabled, or if other InterruptsType were executed successfully + * @return false If InterruptsType::Check and interrupts are disabled, or if other InterruptsType failed + */ + bool Interrupts(InterruptsType Type = Check); + + namespace MemBar + { + static inline void Barrier() + { +#if defined(__amd64__) || defined(__i386__) + asmv("" :: + : "memory"); +#elif defined(__aarch64__) + asmv("dmb ish" :: + : "memory"); +#endif + } + + static inline void Fence() + { +#if defined(__amd64__) || defined(__i386__) + asmv("mfence" :: + : "memory"); +#elif defined(__aarch64__) + asmv("dmb ish" :: + : "memory"); +#endif + } + + static inline void StoreFence() + { +#if defined(__amd64__) || defined(__i386__) + asmv("sfence" :: + : "memory"); +#elif defined(__aarch64__) + asmv("dmb ishst" :: + : "memory"); +#endif + } + + static inline void LoadFence() + { +#if defined(__amd64__) || defined(__i386__) + asmv("lfence" :: + : "memory"); +#elif defined(__aarch64__) + asmv("dmb ishld" :: + : "memory"); +#endif + } + } + + namespace x86 + { + static inline void lgdt(void *gdt) + { +#if defined(__amd64__) || defined(__i386__) + asmv("lgdt (%0)" + : + : "r"(gdt)); +#endif + } + + static inline void lidt(void *idt) + { +#if defined(__amd64__) || defined(__i386__) + asmv("lidt (%0)" + : + : "r"(idt)); +#endif + } + + static inline void ltr(uint16_t Segment) + { +#if defined(__amd64__) || defined(__i386__) + asmv("ltr %0" + : + : "r"(Segment)); +#endif + } + + static inline void invlpg(void *Address) + { +#if defined(__amd64__) || defined(__i386__) + asmv("invlpg (%0)" + : + : "r"(Address) + : "memory"); +#endif + } + } +} + +#endif // !__FENNIX_KERNEL_CPU_H__ diff --git a/include/cstring b/include/cstring new file mode 100644 index 00000000..3e144e1f --- /dev/null +++ b/include/cstring @@ -0,0 +1,2 @@ +#pragma once +#include diff --git a/include/debug.h b/include/debug.h new file mode 100644 index 00000000..9f6468e5 --- /dev/null +++ b/include/debug.h @@ -0,0 +1,46 @@ +#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 +}; + +#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, ...); +} + +#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__) +#define debug(Format, ...) SysDbg::WriteLine(DebugLevelDebug, __FILE__, __LINE__, __FUNCTION__, Format, ##__VA_ARGS__) +#define trace(Format, ...) SysDbg::WriteLine(DebugLevelTrace, __FILE__, __LINE__, __FUNCTION__, Format, ##__VA_ARGS__) +#define fixme(Format, ...) SysDbg::WriteLine(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, ...); + +#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__) +#define debug(Format, ...) SysDbgWriteLine(DebugLevelDebug, __FILE__, __LINE__, __FUNCTION__, Format, ##__VA_ARGS__) +#define trace(Format, ...) SysDbgWriteLine(DebugLevelTrace, __FILE__, __LINE__, __FUNCTION__, Format, ##__VA_ARGS__) +#define fixme(Format, ...) SysDbgWriteLine(DebugLevelFixme, __FILE__, __LINE__, __FUNCTION__, Format, ##__VA_ARGS__) + +#endif // __cplusplus + +#endif // !__FENNIX_KERNEL_DEBUGGER_H__ diff --git a/include/hashmap.hpp b/include/hashmap.hpp new file mode 100644 index 00000000..4cbec763 --- /dev/null +++ b/include/hashmap.hpp @@ -0,0 +1,93 @@ +#pragma once + +template +class HashNode +{ +public: + V Value; + K Key; + + HashNode(K Key, V Value) + { + this->Value = Value; + this->Key = Key; + } +}; + +template +class HashMap +{ + int HashMapSize; + int HashMapCapacity; + HashNode **Nodes; + HashNode *DummyNode; + +public: + HashMap() + { + HashMapCapacity = 20; + HashMapSize = 0; + Nodes = new HashNode *[HashMapCapacity]; + for (int i = 0; i < HashMapCapacity; i++) + Nodes[i] = nullptr; + DummyNode = new HashNode(-1, -1); + } + + int HashCode(K Key) { return Key % HashMapCapacity; } + + void AddNode(K Key, V Value) + { + HashNode *tmp = new HashNode(Key, Value); + int Index = HashCode(Key); + + while (Nodes[Index] != nullptr && Nodes[Index]->Key != Key && Nodes[Index]->Key != -1) + { + Index++; + Index %= HashMapCapacity; + } + + if (Nodes[Index] == nullptr || Nodes[Index]->Key == -1) + HashMapSize++; + Nodes[Index] = tmp; + } + + V DeleteNode(int Key) + { + int Index = HashCode(Key); + + while (Nodes[Index] != nullptr) + { + if (Nodes[Index]->Key == Key) + { + HashNode *tmp = Nodes[Index]; + Nodes[Index] = DummyNode; + HashMapSize--; + return tmp->Value; + } + Index++; + Index %= HashMapCapacity; + } + return nullptr; + } + + V Get(int Key) + { + int Index = HashCode(Key); + int Iterate = 0; + + while (Nodes[Index] != nullptr) + { + if (Iterate++ > HashMapCapacity) + return 0; + + if (Nodes[Index]->Key == Key) + return Nodes[Index]->Value; + Index++; + Index %= HashMapCapacity; + } + return 0; + } + + int Size() { return HashMapSize; } + bool IsEmpty() { return HashMapSize == 0; } +}; diff --git a/include/io.h b/include/io.h new file mode 100644 index 00000000..9089e84a --- /dev/null +++ b/include/io.h @@ -0,0 +1,220 @@ +#ifndef __FENNIX_KERNEL_IO_H__ +#define __FENNIX_KERNEL_IO_H__ + +#include + +#if defined(__amd64__) || defined(__i386__) + +#ifdef __cplusplus +extern "C" +{ +#endif + static inline uint8_t inportb(uint16_t Port) + { + uint8_t Result; + asm("in %%dx, %%al" + : "=a"(Result) + : "d"(Port)); + return Result; + } + + static inline uint16_t inportw(uint16_t Port) + { + uint16_t Result; + asm("in %%dx, %%ax" + : "=a"(Result) + : "d"(Port)); + return Result; + } + + static inline uint32_t inportl(uint16_t Port) + { + uint32_t Result; + asmv("inl %1, %0" + : "=a"(Result) + : "dN"(Port)); + return Result; + } + + static inline void outportb(uint16_t Port, uint8_t Data) + { + asmv("out %%al, %%dx" + : + : "a"(Data), "d"(Port)); + } + + static inline void outportw(uint16_t Port, uint16_t Data) + { + asmv("out %%ax, %%dx" + : + : "a"(Data), "d"(Port)); + } + + static inline void outportl(uint16_t Port, uint32_t Data) + { + asmv("outl %1, %0" + : + : "dN"(Port), "a"(Data)); + } + + static inline uint8_t mmioin8(uint64_t Address) + { + asmv("" :: + : "memory"); + uint8_t Result = *(volatile uint8_t *)Address; + asmv("" :: + : "memory"); + return Result; + } + + static inline uint16_t mmioin16(uint64_t Address) + { + asmv("" :: + : "memory"); + uint16_t Result = *(volatile uint16_t *)Address; + asmv("" :: + : "memory"); + return Result; + } + + static inline uint32_t mmioin32(uint64_t Address) + { + asmv("" :: + : "memory"); + uint32_t Result = *(volatile uint32_t *)Address; + asmv("" :: + : "memory"); + return Result; + } + + static inline uint64_t mmioin64(uint64_t Address) + { + asmv("" :: + : "memory"); + uint64_t Result = *(volatile uint64_t *)Address; + asmv("" :: + : "memory"); + return Result; + } + + static inline void mmioout8(uint64_t Address, uint8_t Data) + { + asmv("" :: + : "memory"); + *(volatile uint8_t *)Address = Data; + asmv("" :: + : "memory"); + } + + static inline void mmioout16(uint64_t Address, uint16_t Data) + { + asmv("" :: + : "memory"); + *(volatile uint16_t *)Address = Data; + asmv("" :: + : "memory"); + } + + static inline void mmioout32(uint64_t Address, uint32_t Data) + { + asmv("" :: + : "memory"); + *(volatile uint32_t *)Address = Data; + asmv("" :: + : "memory"); + } + + static inline void mmioout64(uint64_t Address, uint64_t Data) + { + asmv("" :: + : "memory"); + *(volatile uint64_t *)Address = Data; + asmv("" :: + : "memory"); + } + + static inline void mmoutb(void *Address, uint8_t Value) + { + asmv("mov %1, %0" + : "=m"((*(uint8_t *)(Address))) + : "r"(Value) + : "memory"); + } + + static inline void mmoutw(void *Address, uint16_t Value) + { + asmv("mov %1, %0" + : "=m"((*(uint16_t *)(Address))) + : "r"(Value) + : "memory"); + } + + static inline void mmoutl(void *Address, uint32_t Value) + { + asmv("mov %1, %0" + : "=m"((*(uint32_t *)(Address))) + : "r"(Value) + : "memory"); + } + + static inline void mmoutq(void *Address, uint64_t Value) + { + asmv("mov %1, %0" + : "=m"((*(uint64_t *)(Address))) + : "r"(Value) + : "memory"); + } + + static inline uint8_t mminb(void *Address) + { + uint8_t Result; + asmv("mov %1, %0" + : "=r"(Result) + : "m"((*(uint8_t *)(Address))) + : "memory"); + return Result; + } + + static inline uint16_t mminw(void *Address) + { + uint16_t Result; + asmv("mov %1, %0" + : "=r"(Result) + : "m"((*(uint16_t *)(Address))) + : "memory"); + return Result; + } + + static inline uint32_t mminl(void *Address) + { + uint32_t Result; + asmv("mov %1, %0" + : "=r"(Result) + : "m"((*(uint32_t *)(Address))) + : "memory"); + return Result; + } + + static inline uint64_t mminq(void *Address) + { + uint64_t Result; + asmv("mov %1, %0" + : "=r"(Result) + : "m"((*(uint64_t *)(Address))) + : "memory"); + return Result; + } +#ifdef __cplusplus +} +#endif + +#define inb(Port) inportb(Port) +#define inw(Port) inportw(Port) +#define inl(Port) inportl(Port) +#define outb(Port, Data) outportb(Port, Data) +#define outw(Port, Data) outportw(Port, Data) +#define outl(Port, Data) outportl(Port, Data) + +#endif // defined(__amd64__) || defined(__i386__) + +#endif // !__FENNIX_KERNEL_IO_H__ diff --git a/include/limits.h b/include/limits.h new file mode 100644 index 00000000..f5e88c6b --- /dev/null +++ b/include/limits.h @@ -0,0 +1,119 @@ +#ifndef __FENNIX_KERNEL_LIMITS_H__ +#define __FENNIX_KERNEL_LIMITS_H__ + +#undef CHAR_BIT +#define CHAR_BIT __CHAR_BIT__ + +#ifndef MB_LEN_MAX +#define MB_LEN_MAX 1 +#endif + +#undef SCHAR_MIN +#define SCHAR_MIN (-SCHAR_MAX - 1) +#undef SCHAR_MAX +#define SCHAR_MAX __SCHAR_MAX__ + +#undef UCHAR_MAX +#if __SCHAR_MAX__ == __INT_MAX__ +#define UCHAR_MAX (SCHAR_MAX * 2U + 1U) +#else +#define UCHAR_MAX (SCHAR_MAX * 2 + 1) +#endif + +#ifdef __CHAR_UNSIGNED__ +#undef CHAR_MIN +#if __SCHAR_MAX__ == __INT_MAX__ +#define CHAR_MIN 0U +#else +#define CHAR_MIN 0 +#endif +#undef CHAR_MAX +#define CHAR_MAX UCHAR_MAX +#else +#undef CHAR_MIN +#define CHAR_MIN SCHAR_MIN +#undef CHAR_MAX +#define CHAR_MAX SCHAR_MAX +#endif + +#undef SHRT_MIN +#define SHRT_MIN (-SHRT_MAX - 1) +#undef SHRT_MAX +#define SHRT_MAX __SHRT_MAX__ + +#undef USHRT_MAX +#if __SHRT_MAX__ == __INT_MAX__ +#define USHRT_MAX (SHRT_MAX * 2U + 1U) +#else +#define USHRT_MAX (SHRT_MAX * 2 + 1) +#endif + +#undef INT_MIN +#define INT_MIN (-INT_MAX - 1) +#undef INT_MAX +#define INT_MAX __INT_MAX__ + +#undef UINT_MAX +#define UINT_MAX (INT_MAX * 2U + 1U) + +#undef LONG_MIN +#define LONG_MIN (-LONG_MAX - 1L) +#undef LONG_MAX +#define LONG_MAX __LONG_MAX__ + +#undef ULONG_MAX +#define ULONG_MAX (LONG_MAX * 2UL + 1UL) + +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +#undef LLONG_MIN +#define LLONG_MIN (-LLONG_MAX - 1LL) +#undef LLONG_MAX +#define LLONG_MAX __LONG_LONG_MAX__ + +#undef ULLONG_MAX +#define ULLONG_MAX (LLONG_MAX * 2ULL + 1ULL) +#endif + +#if defined(__GNU_LIBRARY__) ? defined(__USE_GNU) : !defined(__STRICT_ANSI__) +#undef LONG_LONG_MIN +#define LONG_LONG_MIN (-LONG_LONG_MAX - 1LL) +#undef LONG_LONG_MAX +#define LONG_LONG_MAX __LONG_LONG_MAX__ + +#undef ULONG_LONG_MAX +#define ULONG_LONG_MAX (LONG_LONG_MAX * 2ULL + 1ULL) +#endif + +#if (defined __STDC_WANT_IEC_60559_BFP_EXT__ || (defined(__STDC_VERSION__) && __STDC_VERSION__ > 201710L)) +#undef CHAR_WIDTH +#define CHAR_WIDTH __SCHAR_WIDTH__ +#undef SCHAR_WIDTH +#define SCHAR_WIDTH __SCHAR_WIDTH__ +#undef UCHAR_WIDTH +#define UCHAR_WIDTH __SCHAR_WIDTH__ +#undef SHRT_WIDTH +#define SHRT_WIDTH __SHRT_WIDTH__ +#undef USHRT_WIDTH +#define USHRT_WIDTH __SHRT_WIDTH__ +#undef INT_WIDTH +#define INT_WIDTH __INT_WIDTH__ +#undef UINT_WIDTH +#define UINT_WIDTH __INT_WIDTH__ +#undef LONG_WIDTH +#define LONG_WIDTH __LONG_WIDTH__ +#undef ULONG_WIDTH +#define ULONG_WIDTH __LONG_WIDTH__ +#undef LLONG_WIDTH +#define LLONG_WIDTH __LONG_LONG_WIDTH__ +#undef ULLONG_WIDTH +#define ULLONG_WIDTH __LONG_LONG_WIDTH__ +#endif + +#if defined(__STDC_VERSION__) && __STDC_VERSION__ > 201710L +#undef BOOL_MAX +#define BOOL_MAX 1 +#undef BOOL_WIDTH +#define BOOL_WIDTH 1 +#endif + +#endif // !__FENNIX_KERNEL_LIMITS_H__ diff --git a/include/lock.hpp b/include/lock.hpp new file mode 100644 index 00000000..5f8104f2 --- /dev/null +++ b/include/lock.hpp @@ -0,0 +1,53 @@ +#ifndef __FENNIX_KERNEL_LOCK_H__ +#define __FENNIX_KERNEL_LOCK_H__ + +#include +#include + +#ifdef __cplusplus + +class LockClass +{ +private: + bool IsLocked = false; + +public: + int Lock() + { + while (!__sync_bool_compare_and_swap(&IsLocked, false, true)) + CPU::Pause(); + __sync_synchronize(); + return 0; + } + + int Unlock() + { + __sync_synchronize(); + __atomic_store_n(&IsLocked, false, __ATOMIC_SEQ_CST); + IsLocked = false; + return 0; + } +}; + +#define NEWLOCK(Name) LockClass Name + +class SmartLock +{ +private: + LockClass *LockPointer = nullptr; + +public: + SmartLock(LockClass &Lock) + { + this->LockPointer = &Lock; + this->LockPointer->Lock(); + } + ~SmartLock() { this->LockPointer->Unlock(); } +}; + +#define SL_CONCAT(x, y) x##y +#define SMARTLOCK(LockClassName) SmartLock SL_CONCAT(lock##_, __COUNTER__)(LockClassName) + +#endif + +#endif // !__FENNIX_KERNEL_LOCK_H__ diff --git a/include/memory.hpp b/include/memory.hpp new file mode 100644 index 00000000..2bfdbe86 --- /dev/null +++ b/include/memory.hpp @@ -0,0 +1,432 @@ +#ifndef __FENNIX_KERNEL_INTERNAL_MEMORY_H__ +#define __FENNIX_KERNEL_INTERNAL_MEMORY_H__ + +#include +#include +#include +#include + +extern uint64_t _kernel_start, _kernel_end; +extern uint64_t _kernel_text_end, _kernel_data_end, _kernel_rodata_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) +// terabyte +#define TO_TB(d) (d / 1024 / 1024 / 1024 / 1024) +// petabyte +#define TO_PB(d) (d / 1024 / 1024 / 1024 / 1024 / 1024) +// exobyte +#define TO_EB(d) (d / 1024 / 1024 / 1024 / 1024 / 1024 / 1024) +// zettabyte +#define TO_ZB(d) (d / 1024 / 1024 / 1024 / 1024 / 1024 / 1024 / 1024) +// yottabyte +#define TO_YB(d) (d / 1024 / 1024 / 1024 / 1024 / 1024 / 1024 / 1024 / 1024) +// brontobyte +#define TO_BB(d) (d / 1024 / 1024 / 1024 / 1024 / 1024 / 1024 / 1024 / 1024 / 1024) +// geopbyte +#define TO_GPB(d) (d / 1024 / 1024 / 1024 / 1024 / 1024 / 1024 / 1024 / 1024 / 1024 / 1024) + +#define PAGE_SIZE 0x1000 + +// to pages +#define TO_PAGES(d) (d / PAGE_SIZE + 1) +// from pages +#define FROM_PAGES(d) (d * PAGE_SIZE - 1) + +#define NORMAL_VMA_OFFSET 0xFFFF800000000000 +#define KERNEL_VMA_OFFSET 0xFFFFFFFF80000000 + +/** + * @brief KERNEL_HEAP_BASE is the base address of the kernel heap + */ +#define KERNEL_HEAP_BASE 0xFFFFC00000000000 +/** + * @brief USER_HEAP_BASE is the base address of the user heap allocated by the kernel + */ +#define USER_HEAP_BASE 0xFFFFD00000000000 + +namespace Memory +{ + /** + * @brief https://wiki.osdev.org/images/4/41/64-bit_page_tables1.png + * @brief https://wiki.osdev.org/images/6/6b/64-bit_page_tables2.png + */ + 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 + }; + + typedef union __attribute__((packed)) + { + struct + { + bool Present : 1; + bool ReadWrite : 1; + bool UserSupervisor : 1; + bool WriteThrough : 1; + bool CacheDisable : 1; + bool Accessed : 1; + bool Dirty : 1; + bool PageSize : 1; + bool Global : 1; + uint8_t Available1 : 3; + bool PageAttributeTable : 1; + uint64_t Reserved : 39; + uint32_t Available2 : 7; + uint16_t ProtectionKey : 4; + bool ExecuteDisable : 1; + }; + uint64_t raw; + } PDEData; + + struct __attribute__((packed)) PageDirectoryEntry + { + PDEData Value; + void AddFlag(uint64_t Flag) { this->Value.raw |= Flag; } + void RemoveFlags(uint64_t Flag) { this->Value.raw &= ~Flag; } + void ClearFlags() { this->Value.raw = 0; } + void SetFlag(uint64_t Flag, bool Enabled) + { + this->Value.raw &= ~Flag; + if (Enabled) + this->Value.raw |= Flag; + } + bool GetFlag(uint64_t Flag) { return (this->Value.raw & Flag) > 0 ? true : false; } + uint64_t GetFlag() { return this->Value.raw; } + void SetAddress(uint64_t Address) + { +#if defined(__amd64__) + Address &= 0x000000FFFFFFFFFF; + this->Value.raw &= 0xFFF0000000000FFF; + this->Value.raw |= (Address << 12); +#elif defined(__i386__) + Address &= 0x000FFFFF; + this->Value.raw &= 0xFFC00003; + this->Value.raw |= (Address << 12); +#elif defined(__aarch64__) + Address &= 0x000000FFFFFFFFFF; + this->Value.raw &= 0xFFF0000000000FFF; + this->Value.raw |= (Address << 12); +#endif + } + uint64_t GetAddress() + { +#if defined(__amd64__) + return (this->Value.raw & 0x000FFFFFFFFFF000) >> 12; +#elif defined(__i386__) + return (this->Value.raw & 0x003FFFFF000) >> 12; +#elif defined(__aarch64__) + return (this->Value.raw & 0x000FFFFFFFFFF000) >> 12; +#endif + } + }; + + struct PageTable + { + PageDirectoryEntry Entries[512]; + } __attribute__((aligned(0x1000))); + + class Physical + { + private: + NEWLOCK(MemoryLock); + + uint64_t TotalMemory = 0; + uint64_t FreeMemory = 0; + uint64_t ReservedMemory = 0; + uint64_t UsedMemory = 0; + uint64_t PageBitmapIndex = 0; + Bitmap PageBitmap; + + void ReservePage(void *Address); + void ReservePages(void *Address, uint64_t PageCount); + void UnreservePage(void *Address); + void UnreservePages(void *Address, uint64_t PageCount); + + public: + /** + * @brief Get Total Memory + * + * @return uint64_t + */ + uint64_t GetTotalMemory(); + /** + * @brief Get Free Memory + * + * @return uint64_t + */ + uint64_t GetFreeMemory(); + /** + * @brief Get Reserved Memory + * + * @return uint64_t + */ + uint64_t GetReservedMemory(); + /** + * @brief Get Used Memory + * + * @return uint64_t + */ + uint64_t GetUsedMemory(); + + /** + * @brief Swap page + * + * @param Address Address of the page + * @return true if swap was successful + * @return false if swap was unsuccessful + */ + bool SwapPage(void *Address); + /** + * @brief Swap pages + * + * @param Address Address of the pages + * @param PageCount Number of pages + * @return true if swap was successful + * @return false if swap was unsuccessful + */ + bool SwapPages(void *Address, uint64_t PageCount); + /** + * @brief Unswap page + * + * @param Address Address of the page + * @return true if unswap was successful + * @return false if unswap was unsuccessful + */ + bool UnswapPage(void *Address); + /** + * @brief Unswap pages + * + * @param Address Address of the pages + * @param PageCount Number of pages + * @return true if unswap was successful + * @return false if unswap was unsuccessful + */ + bool UnswapPages(void *Address, uint64_t PageCount); + + /** + * @brief Lock page + * + * @param Address Address of the page + */ + void LockPage(void *Address); + /** + * @brief Lock pages + * + * @param Address Address of the pages + * @param PageCount Number of pages + */ + void LockPages(void *Address, uint64_t PageCount); + + /** + * @brief Request page + * + * @return void* Allocated page address + */ + void *RequestPage(); + /** + * @brief Request pages + * + * @param PageCount Number of pages + * @return void* Allocated pages address + */ + void *RequestPages(uint64_t Count); + /** + * @brief Free page + * + * @param Address Address of the page + */ + void FreePage(void *Address); + /** + * @brief Free pages + * + * @param Address Address of the pages + * @param PageCount Number of pages + */ + void FreePages(void *Address, uint64_t Count); + /** @brief Do not use. */ + void Init(BootInfo *Info); + /** @brief Do not use. */ + Physical(); + /** @brief Do not use. */ + ~Physical(); + }; + + class Virtual + { + private: + NEWLOCK(MemoryLock); + PageTable *Table = nullptr; + + class PageMapIndexer + { + public: + uint64_t PDP_i; + uint64_t PD_i; + uint64_t PT_i; + uint64_t P_i; + + PageMapIndexer(uint64_t VirtualAddress) + { +#if defined(__amd64__) + PDP_i = (VirtualAddress & ((uint64_t)0x1FF << 39)) >> 39; + PD_i = (VirtualAddress & ((uint64_t)0x1FF << 30)) >> 30; + PT_i = (VirtualAddress & ((uint64_t)0x1FF << 21)) >> 21; + P_i = (VirtualAddress & ((uint64_t)0x1FF << 12)) >> 12; +#elif defined(__i386__) + PD_i = (VirtualAddress & ((uint64_t)0x3FF << 22)) >> 22; + PT_i = (VirtualAddress & ((uint64_t)0x3FF << 12)) >> 12; + P_i = (VirtualAddress & ((uint64_t)0xFFF << 0)) >> 0; +#elif defined(__aarch64__) + PD_i = (VirtualAddress & ((uint64_t)0x1FF << 30)) >> 30; + PT_i = (VirtualAddress & ((uint64_t)0x1FF << 21)) >> 21; + P_i = (VirtualAddress & ((uint64_t)0x1FF << 12)) >> 12; +#endif + } + }; + + public: + /** + * @brief Map page. + * + * @param VirtualAddress Virtual address of the page. + * @param PhysicalAddress Physical address of the page. + * @param Flags Flags of the page. Check PTFlag enum. + */ + void Map(void *VirtualAddress, void *PhysicalAddress, uint64_t Flags); + /** + * @brief Map multiple pages. + * + * @param VirtualAddress First virtual address of the page. + * @param PhysicalAddress First physical address of the page. + * @param PageCount Number of pages. + * @param Flags Flags of the page. Check PTFlag enum. + */ + void Map(void *VirtualAddress, void *PhysicalAddress, uint64_t PageCount, uint64_t Flags); + /** + * @brief Unmap page. + * + * @param VirtualAddress Virtual address of the page. + */ + void Unmap(void *VirtualAddress); + /** + * @brief Unmap multiple pages. + * + * @param VirtualAddress First virtual address of the page. + * @param PageCount Number of pages. + */ + void Unmap(void *VirtualAddress, uint64_t PageCount); + /** + * @brief Construct a new Virtual object + * + * @param Table Page table + */ + Virtual(PageTable *Table); + /** + * @brief Destroy the Virtual object + * + */ + ~Virtual(); + }; +} + +void InitializeMemoryManagement(BootInfo *Info); + +void *operator new(uint64_t Size); +void *operator new[](uint64_t Size); +void operator delete(void *Pointer); +void operator delete[](void *Pointer); +void operator delete(void *Pointer, long unsigned int Size); +void operator delete[](void *Pointer, long unsigned int Size); + +void *HeapMalloc(uint64_t Size); +void *HeapCalloc(uint64_t n, uint64_t Size); +void *HeapRealloc(void *Address, uint64_t Size); +void HeapFree(void *Address); + +#define kmalloc(Size) HeapMalloc(Size) +#define kcalloc(n, Size) HeapCalloc(n, Size) +#define krealloc(Address, Size) HeapRealloc(Address, Size) +#define kfree(Address) HeapFree(Address) + +extern Memory::Physical KernelAllocator; +extern Memory::PageTable *KernelPageTable; + +#endif // !__FENNIX_KERNEL_INTERNAL_MEMORY_H__ diff --git a/include/printf.h b/include/printf.h new file mode 100644 index 00000000..c49d7dcb --- /dev/null +++ b/include/printf.h @@ -0,0 +1,199 @@ +/** + * @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 +# 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/include/string.h b/include/string.h new file mode 100644 index 00000000..4c22bfbb --- /dev/null +++ b/include/string.h @@ -0,0 +1,33 @@ +#pragma once +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + int isdigit(int c); + int isspace(int c); + int isempty(char *str); + unsigned int isdelim(char c, char *delim); + + void *memcpy(void *dest, const void *src, size_t n); + void *memset(void *dest, int data, size_t nbytes); + void *memmove(void *dest, const void *src, size_t n); + int memcmp(const void *vl, const void *vr, size_t n); + long unsigned strlen(const char s[]); + int strncmp(const char *s1, const char *s2, unsigned long n); + char *strcat(char *destination, const char *source); + char *strcpy(char *destination, const char *source); + char *strncpy(char *destination, const char *source, unsigned long num); + int strcmp(const char *l, const char *r); + char *strstr(const char *haystack, const char *needle); + char *strdup(const char *String); + char *strchr(const char *String, int Char); + char *strrchr(const char *String, int Char); + int strncasecmp(const char *lhs, const char *rhs, long unsigned int Count); + int strcasecmp(const char *lhs, const char *rhs); + char *strtok(char *src, const char *delim); + +#ifdef __cplusplus +} +#endif diff --git a/include/sys.h b/include/sys.h new file mode 100644 index 00000000..86116b83 --- /dev/null +++ b/include/sys.h @@ -0,0 +1,8 @@ +#ifndef __FENNIX_KERNEL_SYSTEM_H__ +#define __FENNIX_KERNEL_SYSTEM_H__ + +#include + + + +#endif // !__FENNIX_KERNEL_SYSTEM_H__ diff --git a/include/types.h b/include/types.h new file mode 100644 index 00000000..74c09231 --- /dev/null +++ b/include/types.h @@ -0,0 +1,143 @@ +#ifndef __FENNIX_KERNEL_TYPES_H__ +#define __FENNIX_KERNEL_TYPES_H__ + +#ifdef __cplusplus +#define EXTERNC extern "C" +#define START_EXTERNC \ + EXTERNC \ + { +#define END_EXTERNC \ + } +#else +#define EXTERNC +#define START_EXTERNC +#define END_EXTERNC +#endif + +#ifdef __cplusplus +#define NULL 0 +#else +#define NULL ((void *)0) +#define bool _Bool +#endif + +#define asm __asm__ +#define asmv __asm__ volatile + +#define true 1 +#define false 0 + +#ifdef __cplusplus +#define foreach for +#define in : +#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))(((uint64_t)(x) + ((align)-1)) & (~((align)-1)))) +#define ALIGN_DOWN(x, align) ((__typeof__(x))((x) & (~((align)-1)))) + +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; + +#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__ + +#endif // !__FENNIX_KERNEL_TYPES_H__ diff --git a/include/uart.hpp b/include/uart.hpp new file mode 100644 index 00000000..c6c3fb01 --- /dev/null +++ b/include/uart.hpp @@ -0,0 +1,71 @@ +#ifndef __FENNIX_KERNEL_UART_H__ +#define __FENNIX_KERNEL_UART_H__ + +#include + +namespace UniversalAsynchronousReceiverTransmitter +{ + /** + * @brief Serial ports. (if available) + */ + 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(); + }; + + class Events + { + private: + SerialPorts Port; + + protected: + /** + * @brief UART events. + * @param Port if none, all ports are registered for events. + */ + Events(SerialPorts Port = COMNULL); + ~Events(); + + public: + /** + * @brief Get the Registered Port object + * @return SerialPorts + */ + SerialPorts GetRegisteredPort() { return this->Port; } + + /** + * @brief Called when a character is sent. + * @param Char the sent character. + */ + + virtual void OnSent(uint8_t Char) {} + /** + * @brief Called when a character is received. + * @param Char the received character. + */ + virtual void OnReceived(uint8_t Char) {} + }; + +} + +#endif // !__FENNIX_KERNEL_UART_H__ diff --git a/include/vector.hpp b/include/vector.hpp new file mode 100644 index 00000000..b1feff6b --- /dev/null +++ b/include/vector.hpp @@ -0,0 +1,162 @@ +#pragma once +#include +#include + +template +class Vector +{ +private: + uint64_t VectorSize = 0; + uint64_t VectorCapacity = 0; + T *VectorBuffer = nullptr; + +public: + typedef T *iterator; + + Vector() + { + VectorCapacity = 0; + VectorSize = 0; + VectorBuffer = 0; + } + + Vector(uint64_t Size) + { + VectorCapacity = Size; + VectorSize = Size; +#ifdef DEBUG_MEM_ALLOCATION + debug("VECTOR ALLOCATION: Vector( %ld )", Size); +#endif + VectorBuffer = new T[Size]; + } + + Vector(uint64_t Size, const T &Initial) + { + VectorSize = Size; + VectorCapacity = Size; +#ifdef DEBUG_MEM_ALLOCATION + debug("VECTOR ALLOCATION: Vector( %ld %llx )", Size, Initial); +#endif + VectorBuffer = new T[Size]; + for (uint64_t i = 0; i < Size; i++) + VectorBuffer[i] = Initial; + } + + Vector(const Vector &Vector) + { + VectorSize = Vector.VectorSize; + VectorCapacity = Vector.VectorCapacity; +#ifdef DEBUG_MEM_ALLOCATION + debug("VECTOR ALLOCATION: Vector( )->Size: %ld", VectorSize); +#endif + VectorBuffer = new T[VectorSize]; + for (uint64_t i = 0; i < VectorSize; i++) + VectorBuffer[i] = Vector.VectorBuffer[i]; + } + + ~Vector() + { +#ifdef DEBUG_MEM_ALLOCATION + debug("VECTOR ALLOCATION: ~Vector( ~%lx )", VectorBuffer); +#endif + delete[] VectorBuffer; + } + + void remove(uint64_t Position) + { + if (Position >= VectorSize) + return; + memset(&*(VectorBuffer + Position), 0, sizeof(T)); + for (uint64_t i = 0; i < VectorSize - 1; i++) + { + *(VectorBuffer + Position + i) = *(VectorBuffer + Position + i + 1); + } + VectorSize--; + } + + uint64_t capacity() const { return VectorCapacity; } + + uint64_t size() const { return VectorSize; } + + bool empty() const; + + iterator begin() { return VectorBuffer; } + + iterator end() { return VectorBuffer + size(); } + + T &front() { return VectorBuffer[0]; } + + T &back() { return VectorBuffer[VectorSize - 1]; } + + void push_back(const T &Value) + { + if (VectorSize >= VectorCapacity) + reserve(VectorCapacity + 5); + VectorBuffer[VectorSize++] = Value; + } + + void pop_back() { VectorSize--; } + + void reverse() + { + if (VectorSize <= 1) + return; + for (uint64_t i = 0, j = VectorSize - 1; i < j; i++, j--) + { + T c = *(VectorBuffer + i); + *(VectorBuffer + i) = *(VectorBuffer + j); + *(VectorBuffer + j) = c; + } + } + + void reserve(uint64_t Capacity) + { + if (VectorBuffer == 0) + { + VectorSize = 0; + VectorCapacity = 0; + } +#ifdef DEBUG_MEM_ALLOCATION + debug("VECTOR ALLOCATION: reverse( %ld )", Capacity); +#endif + T *Newbuffer = new T[Capacity]; + uint64_t _Size = Capacity < VectorSize ? Capacity : VectorSize; + for (uint64_t i = 0; i < _Size; i++) + Newbuffer[i] = VectorBuffer[i]; + VectorCapacity = Capacity; +#ifdef DEBUG_MEM_ALLOCATION + debug("VECTOR ALLOCATION: reverse( )->Buffer:~%ld", VectorBuffer); +#endif + delete[] VectorBuffer; + VectorBuffer = Newbuffer; + } + + void resize(uint64_t Size) + { + reserve(Size); + VectorSize = Size; + } + + T &operator[](uint64_t Index) { return VectorBuffer[Index]; } + + Vector &operator=(const Vector &Vector) + { + delete[] VectorBuffer; + VectorSize = Vector.VectorSize; + VectorCapacity = Vector.VectorCapacity; + #ifdef DEBUG_MEM_ALLOCATION + debug("VECTOR ALLOCATION: operator=( )->Size:%ld", VectorSize); +#endif + VectorBuffer = new T[VectorSize]; + for (uint64_t i = 0; i < VectorSize; i++) + VectorBuffer[i] = Vector.VectorBuffer[i]; + return *this; + } + + void clear() + { + VectorCapacity = 0; + VectorSize = 0; + VectorBuffer = 0; + } +}; diff --git a/kernel.fsys b/kernel.fsys old mode 100644 new mode 100755 index 7bd069c80f0e4a3f9e8e12210e09a26019078b6c..8cdf5c722b2039943f54032811ea6a275adda915 GIT binary patch literal 170528 zcmeFa3w%_?*#~?!$tDW~Hh^eUltqmM5C~qmsR^>WDA5Q+sY)S2AW;Z0*#*2rf=eXZ zH5RN?+Iop?RjO8LMU1GBAd*zA#!B1RqQ*<>#zl>{g0`0L|9@uA*|VFC+P8k+`~H3( z{50px%yXY-o_S`@oU^OEGtNzMI1KY&igA{~(ujpd?Lqv@*A0yuQcMsd-xRQEO5vI zhb(Z&0*5Sc$O4BfaL59OEO5vIhb(Z&0*5T{A6TH+7y4U4al!0azSa98XB&nu>>Qrr zFnl3z$M|Lx)?j_^3)N*eA3W#_>q~x?FT6q3%yjvJ`&>oSL$6G4ZEpOtQR{1M+4xtZ zcH0=p;A`FeTU6~x#eEZHioAIPYJg%K;obU40H3v=t(cLzp zr`U*cJ3GU{mR1iWm~tD)Y#@$Xs?q}sP*70;66ri&lR`5Q+B@hHo43j9Q>Mb^=p@cc%Lq@;iNw zh;PhFhRhJ-mO_LNSwsPKS*Nezu+hHN?^g<3N(|1%+zwZk2!~i^3TcO%9|z}tBHxo7 zZVuoUg-pKgt@+0#hr0xD^)|5MlY3U~tz7$-#U8^I3)iTwZdx{5Uvvg!_p$tDB4BJ>zllAGrL*Gcjd;jL24NbNZX% zY37ufhIMeEF_romsm(yXOke7J6s3}x)#L|K$mSG?YOWoN5aB}O0o{usS?IIqDH7L zzB0=fJdojDdlh=n7fyR_4w9jRofZy!s>P%qYWF#sNus4QH5O1N122RqqM7(`tHenP zUTK1V4NMteT4^bx>k5%dlm6e6g^5g8y9&n2mj8RQ&=vHB(l2yf6p$A zKwT&O-;f34)97l1*Zd!_3m;l`9rk}imJG^Lk3ath?BaqL>k!BpIvL3GBw`{)PLwM5 zP2c-c!gdzw0cv#l=v%>OSk97YU2lulFH$MJeB=mKb73R$!meE9laf7#WhqJ$wrI^P zIhMCIWHO)&2qI`pr&C4TG%Mnk9S>F#aleKnmJ|(-mx)TrXgRZnf{3Hr7i@O!@aewra+hxhUBIZNf~XS+ABQG`DkPt<{Ij)q{7XmaF$?M3hfLV+S^IdPE=^wrmU8n zd1qO2?#UC<8YU~IOP=J&qRVE&G}MWb7$o;y%%BnWcKAj+AN#QNLCbw` z4quUS4r@f2(=X032BAjd*4(T zwxp{?$A*hP3$?d&ra0Oi&74~(5aOE}`@T?Li0cJ_I~9s?;j)R`1r&C^+C{>4qJ;iL_`Za%*=qzkOcws* zq66bdzQ_DeNiy2|8L4)fvFgz-QEF+v&8GQwrP`*2c1{Zww%2<*Lin>M?9B>!UkrPp z6MWUV4ES&fIGQK}HQSV$R|RWcH2kMY8Or)c*Vc3xfxc4CMEU68(ia`kA;4-Va?pA^ zwSK#eJPRoY&6B+^Rv!Zehb@1#S8xEeSEmcm>$Np|T!Bm=GNPviOWQsIxtuJRx31Z} z=51J+Wdlhwx*(sH^G^~s?S7YH7Z`<3pX}WWY*=0(vOzV=pN_vNq#bKzePDzL>;XLy zbj1;%(?hG3(F@~&{a*t-Cpuhs9Zj89B(>6&y*K4a(_C8IpH!AH(R>j(4IPmbV03ht z0Js27#h ztD$6J4__Lt*?cL@w4QrwcpPV%YQ*2gRDYXdGnF;hg^HhsUD5!e>_VkTXm&13SZBP)&QTnHi}2(Vc*y zl$a_RDDnSDZwFQcMSCT$E$YOR#nKc=(7Jm8s&?R3C*5`PrHeWo%O?t6u-ReJi03kK zXVqCq5F{}WaTI80G#B>ka0G@6K6)aPHw^sdJyv%@G+6Vr5ttyj5-^|&mFpnB5y%iS z%d^>T!|v4BGy!53^NuWBn0B~u?N;fBq#cd#N;iC;dwT%3yf#mT#8-SFK!52AJugf{ zAQg5l0S<}?f#`!y^g%zMcEF_GNECde?<3j=|DorS35w4@CfU0C>v*^lc??P<9A(cd zuA0Z5cSM)lEFQCv&HH>DrY*kEtG>{uzEC0jXfB*Va;aYc0g6OKkv1qYc()gQK!<`d zOjJ6e0V%@w-uM&_jOz?!E+O$U0JKJ0b@gddjoG3-v~&Mo+`e`UqEO5J*WA-vCholP z2oR=$&7p|_MdI0t-`r+2HED}MNT@~C*m2>)h;MZNFA>Et6E{s{FU>umdujE)b?`}a z)W%SEsCXTetDh07TZwsmMyRm1-g`SEOr2kHALkPw0m`b=ZpZlXg&PP8F16rRueTDu z;zBXx-4yn&8!f13p6ozuxCbNdf!dk}4F9Own%fN-b(_plw+T{i3{wECyIzir!$|Mq zz6;210xc+^2jT|B&|8aj1;!(RKbD1QalLOOip873)*7NA^gx*Xq~#>q%is|i&V-U( z5JF-dE^G%BuHMaBg-J?SG$WAeu0d9`*W`Og)**DqS=Kc|UST@ELfK&!#H2Q*_ z|F+O3dz-2bOE!{0Xd?)A_CCVSdQ1B5UuTFU8?6yGA05{m3Oo&03>9vf0h@Db0KF`5 zZNoKNbPRhvxI#bY=bdH`GAOv$Y=v?NX(8{vuy;!iUs~XNe;YJLIoO$nQr#M*e>>vJ zjn|8}zWwiGitx1@NZ|l)`SNTPT;Ljv5!}H_;SLs_9E`*OLI^;0u%_Dx zj0-N^hj9Ya_d>4ai?fL!v8p|KGddOB1%ZC}DsPHAXw96_{U&I=)fuhX)jz*r@c**_VzRe`w0V!dF@(^8*Q|Kn4MnuSPk(dUN3IY2uxbK!W&3=Yr-`CDQKU4#io#QR;t7~3-5pg`w zS1`LCru)ORSh^w!MeehaqPW33?5~Z71+Q-~P85F;La8lG{n$ddA(4GPh7-<A`I&J zOj)|*uhKQcPPV#c^}a@q*H8^+)Ie=$!wpnHX0T~$E}ju;C_|mh%?vHRuoGLUu}-X| z;txixMECC6n!DA6TWe0Z0D(8jA|bQYOMXVzPw@vjG!wgznjqUyZezI(Gh)cw81^=z zaJ6b|5?~HLPjG1?lb$Buw%@;~{%K3KL{h=2DOl6^(GC6acVPTRMZNdV+R{56wKaDd z{=-R}iO=+%=o6h~OHSSd679RacNt;HCv@cZuLwRv(->HT*6{|LADpa1-d}>zzix1Y z-em7DYvOiB>b-Z>mfi)1cVRb6p~3w|dmE#Dtdis|o1|wk-}rWhg!<|RMlP;_LEX3( zyDpG9jyBR~toTVffOyo1+}ge;Q}4Z}w)7rwzDF`3-66JhM?+y}So&HgL8A~)qGw>` zbhodehA!ET!GT%4uz8w>!`MeqXe_{9e>2WBP7D1Zaw3*DTqB8sljH4mK7=}W!!%Io z1eO{h=3-{z&cNuPx6u*&H7)6g9t(b0103jc<6*4k7A0+K<}X4%L&jY+8)Wa2&Ru}c zO#>7ET}-iM=^ij-3>A>?65CuI+4%d~Kv za`9)x<)sr4d&7@YPJ;Ck0bf&w@L)2yE7tBo;csJ0WEhT=us3haK+(4`>XHk_5n{10zsgw<`dxuY{$yWLLVkHpx9SFy9VR+| zj_feiTbovO;G^{%?cFk2%u@Bb8L`j*vxEiLSSu_9_i+ZnXv981@za<&)LuYaJvm_2Me00;fKvYh5fZ7T@1gLe*PNoGz8QA>0VNfrnM$5Rv_&G<<`h!m|n2W)#{ZoWszz|)5jPy zSY!`0K}U5Fv?+S6$$M0w0=FB(M)1J66({hP&v;Z7?2M&IxOiiDP{aAKPVvT2-zW}1 z69x}G8+dpLdWFt+g)xb=1#8-jYX<}ix1_)|_jiy@I__`G}M$)frYsJF7&{@^NY5*j2l=n>0?3#BW1w@gWPL> zYdK{0>vTwJl9E)DDNEdSYqhS36?U@GDxlS(&0eW_O(_dp3Zv%Ro1)$%Y@*|nRSr#7 znX%4#c&BX~jN}9l%yO??-_IOhn?)T{h=sD_B<@-s$RN#pB`qCzWvp>dAbq#@v8?DQ z6LFbTxS~gz6<10{O4J3ozQNKhADInv0%_3G!=mZou{h?(F8h{$OBsy0>aK^Cr3_oL ze1|gMf9nDaBuI3zwfYz&>E;pOz(-eq0bgp+mM!S4EJfc1|hU?V`#iRtX4{$FT}RI zL_aIsaQ~~qi=J|T5`#yXOLj;~jz2ez zSQN^Vlh)Gnk{x~LEbxe^>o<^ISV*4epZy(c0T$7VzhjzwbR2W&DBiEqOCj43TJh(2 zZ&;^+uyIrOi;txZOV8Fwr-Ivr-64#09&z&QmPPm*Lv1V7d~>J77j_zGM+&U=)jL2# zmX(ozi~AKfBVlR7auN-45PG2S62h&|3E?_BVQUKpOJHnE@@bp%9>Cccgf&e?Z)FE% z=feUVtcbCBkQ+ow{`rc{v353jvWb8*QtAPvb4rXwxUd6=9D|YV67OJ?dAQNk1}SRo zN87BCOz8J_eT~rHPcHQ5A6aZ30y-Wim)qG?LTD_X@$jAx`9p;fJdy0iBaQ>G(e;X5 z8@H!6BN$_ML!MI7ChagDo_WwlSc!vy(EV#{e%J(2Q}iSGLw| zs$QhH_W!nAhy72uzLm)JwVk5t9Y@*>oDW^mn*sC?Iw9Q6#Lje#o^^P_q&4+ZvU-V+mflV=1r zHh36Nct*ezXNT~dHt5AOC2ot65wj(Z83v0_TSUv>pmVGTVMWlgjoYf=)C4VG28^X8 z8o0a0ry|f&k?gKM9lSe)x2bPOOkdnu`gTZ805O`X{uJd|pC-`Z;T>8C$3JoHR=ZLt zp3gxHB~0w4oeniY;~M~co-Z1VR~meDgiV9|gnsI8*C4K+qHbSsX&cDWKyo0J+gyGe zjqzgfokelbRTFmlnTDYuTQwMZ&wz=~*5;8rv1v~2@hD6Qb!W4f^|G%MY9(N_S@j9$ z=yaf?XT${b5buYm0L@s%N7zFCaFX{UeuoXJg*|npGIOY*uIo!c+flPQ3-6aAkMIQqe;@&kqDKc1xoj;0E z4c0`AzyQ^OTGoqbP1vBvY2ZM2^ccttADGH#p`Zj4@Yrpr5dAR-9u0p{(uln{rIC~p z>R2Vz-XT5IR6_lGyQQiKrKLH;l7@%#L7rS9=IAlUs^a&2lR z!Z3k{$JqT36+;{sF?3M|XN&(SD4`>ZEe1z_se5LC67I#pc5{Sk_Q5|t#cnkm3=te> zO>lM`e-qA*YZ}i35DL~Oq4B7lhR1&_X!P;pDd4{T{z3TLm#1F#W%7C7zJBq~KsD_O zMf;>U+Im>dw1|rpklalB7qSlYd#-w#I$qlg+i_^T5}6O$wB?4V&b4}RkDzF-2c8m* zwqhY=g?{l@9@&?3V9q}RL|Ml?5kD9w;zbb7EbrE51-+l)l=)`}H62y&N`o4LKU2iN zXD6OGudJ}B12Jx1i4KR1=;Ntr;EwUd?)TxoFcKbwg&nndkST^ zh2nZECfIFBQBG7S|G+Mh5-i7t@}s0EpPnkZykMc^+fZ&#iqZ^}b+9U*1pW+ou;}-Y zX~vu@RI?Z6sYR1ADo&dOn{#p6F6iBt8}5J4jazF0Tl2GXV>liQC;u>lInSVMe9O@}hIgc_-1J-&VbnBt%&5C+5hv<_G>V}5dH0<{ zvDMqCubbp5CsKKc!Q1HpFuv!s6c5wYe0(`5v_HX8Goq~XIo^i7`nAMHWs`i`kd~AxH6i(5bmHx;~T#skO!XkMl%k6>0-=z6@{1stFkbY-D$Jj87dlTVPoN@X{+6b zPDgt~Q7)T9McKaakmZjn<&-m*sgj~3;$hcJcFvl!&>eCg>~!O{8BW=3_=h&hI(4+H zH6U{_L7Ih_Kx6+kOA&}<)n_T^c^S{=S=aSpnl=P2qmqKncpV{v86ZLtm0O_e+&3L> z+Dr>C#*{r8tu-ieF+LN7c-mi?(ibN4|HQigr8RT6*sXb#Dk%xS)|zdwCRQ$%H96C^ zX=}E{tl5^tnyW`?Yc{6FS+l9DEk!Dt#LMY;h1!T=6~tf|Mljf$OFWz1o5J}+aqxgO zah=d#;Y%}PWfSo&CvMV~SNwQ{UHV5A)d_?qTlj5?`8hy1q{LG?Wk5737p^x89(H4L z2Y&pwc9TD#N=-_v-kcXw*R39Ir;@jQY(%leQTUnV6lNU$6G8+ms8-YyYD%s?iI~AkdO&ljc!t6+5-7O=ubzeo`RTAdAfR6q| zKIpC+!%0aEZuxxSbP3v=RDQe}2(bw$I+;=nyI?3-bQV1QffcZ)S~&xDYV-tEJ{k@QcXb_bngl1 zg(uWNhc)xFVAXESz6X9ShK5mw@?|ac#-8(SRofl=B*4g>!S{0^3dVFcE)m28D#%+< zlY~aL!=%9dxbPqqeQ_tBIbd=m_tH2MMv+LOn*(AtTXgY|7l^5Z+Pi$8f-(yM|1DCvj1g7fht!U-#8o~NwZ zEa_T=FnL3I-|^U5!`6iJHUc2WI~(^m<6fnmH}FpKMzw#ZQc2KeCR&Ws_ISIv<3{eM zfS~h2K&!s+H-qa8h~_nJkxcD!<_7dacb%;}OB=Xi>L zpKn1snQa$Sp=qc*Uy*>#An~t>!$GW#EFQv(6e4<8 zHeFMq2!~!X8_U7JOP`JdG)BEl@%Fe70ZK&0g-&_DA5h8^ox{Zx2pkd@&|&NwGcHV0 zap6?TrGnK+-YS@cxInbHxF8kwxbXRd7Mb^XtH>me3zdw2?<5_EFEf;>xFDh?#s%n- z7#D=v2T7@|*3|5A!A?QP1)=bdq!iB96qG@Cd9;^BE0uyCdGBK{7BtOO*j@EDhZwv# zl`DiF0Ra_l(R@`7s!`%tD$@s0+}YSN^pq90>$!=-1# z2`w+nf_L4BdhK0JRdtu;?0fsoZF=@vpitloCQ8_O5y7!~%#83rgD>Pf3l;0s$7R&P z7wpcgLpNb}rjxyeLB%QaSGbq8s8VM*;v~MKE1yip76i6JTAQCzSOu^z*oC~K}+*0mfju5 zd4}S=5{T(qhd)%!wCxy5y~`814JwOUo@F!9d>c;8L^lIRDV3v? zVq{!LrJ}S&Yn{h5(J=T53vByhG7W%F0T3oA)wC0v#ZG$^$v+4f-&f$7h1SSMg7coQ z&d2O@xlPKelS=aB&f9E z#O(Ba;3%cOnnc~bjZmbKNG+6~R^#D@$zeRY5J z4`JmGQMsKmvG9?yBWci%yow#SD3(7^EH{DWCbBFdOWucz8*O&nWYgltq*`n|8i>k{ z+&JB&?f5F~_%7|ZD!Cn3*{H?rc)p_Mi5!PFW5nM+w@DDR;l%8CIB=9wk58oTTzTVv zD0MxZrL`{4wByJfwV)6gL0!3tz|VikS3|I;&>9J{Iw;Vz7_r1+&nx0{6!9hyZzA}y zB+h%T_+y@z{r6Z1S)WwiMMsg3c;04MtjT{N(aRQxeaK^jm8#p8jcxlh$85T ze2}S)c7;udAKP$ZMtkc>(`dC5K$APLI;B6Cp3z!2!8F=reDhNZ{#*=K-ckE^l>g|h zFD~KQS$#H)!cWKr1(0SWf|#*MnQ@?E*9HtpApcCW@}7srVrH}t;QNzmFbo=KGd9DF zZQ6|2(Si;9^FXg=WJ^yozLYHlJ(2tS(TqV!g&e_Wm>{{`x4y;KDe>b1+5p2u?mC|? zW~pM4E;>|kjxK%zr5OeB84_pp-I5=U$0m;LGbF)%T=_+C2B%Y=#NyjozOeH=z@l%k z0z^X4s5r`*cT0&bT%NIUA=xJUZ$vHz zm&hR4B$68^Kc3l#0ud0KLOx;&8j^|jDAXX)j$jG;yJxLzJ#=6Z7RW&8XCb(s;M2H$ z$kds$!_l}N-gl>9KPcFywL)Kpx)+6t0ZNv?1BhY?yomdW;(Z^QydP26OX7L62i4c_ zZnaQ@HSZekHDfSZX?Z+B4`t!AcfO78t+hodj#?h>9E?WPVKIR<>~@+1{ZkItk;+t7 z)~d?cs**Lxb_q7s1heCgF)IaELuQ+mDMW8o=&5F98Y|)Vr1>EZ0fIGZQ1~SZQy#(B zqHv`9JbXXpXNJK;;Y0S}22TNQXZ<)pt2INn>gde(;7+4K4Wld@_qOjol?~C#3P;SA z+{QL@>obZx?ZFm@B=yI!JM;0?S00vs=(2!aoJM%uY)jI)08(R*m=WY}8`8$*f? zv;2T6N1`RDK`k4*aobJcX;{gJWD_xLOx(%e&{6d~9xewt7};@@AAb!Pm#^6Y7w+TI z!gR|96@C^6M_^pS01qq!8%*CE1pQ#I_U>f%`urz$6*sG~6>^b%8vLdBzB z4<6{i@x3uH57lwE-cI;Lb_XoTXMf_b{C1)pU^)PB{7jC~X<@cQ7;aCxZ-a*i9x>&5 ze52@IjdQ@3cV$7QyHJl#=3bp9@CCXL2B@W*wC)yfX>I*+g;5)M3lZC-SE}e8pGdFJ zq8Irw#wg@v$U8$pe~t|pi@QgUgrhAC+M64pD$u1WoR%Iq=7(1 z4If+SVUK?CJh@A*4DtywB51c+kVHFv;w%^HVWc|upcNg7?G?y@ia9{T72_7Wu?D{8 zgs)?Ly-oMPqJnG*%_h;K=H92jQ8jTUF+QRasGIq#|9c1k(6GGaB0g#FO8hVi6t&R9txq z&irL09*E2QBLT!!VI&X~_2tlKClcoFs^=6cjvK3(Yzi+^k}5!~B@|0|z~}K)-89TY zI7J4(DStnSul*2hmQCpF@?mQ9itZhd9Rji@sric3v361bcKR$bpRO}uPgSs=;B9Qn z#R)sqg8j?{?V~_j?VuiY2=qg&q?CYoauPg{;va`Ut4_vKo{ZoDhyTdvh&$(?y&DpD z=g9L7Oo|(tqeK3wj$roxQeD+7tw+*NKol0a)XtBd0<-C?KI{xwjA@L6few7MO6ACv zoGDvdZfEK=nH|@<`(CC_=zX0dkHPYeE1t0P4y+4;2Uac5XrdcMYw(?Q)P&?)ZnmGO zS=Xp?6bO)lvvNs6Hd$#5K+aAIvRQ#32m)j%S>yDbb1-h2w1+V(_!(Ba(EfW7TgP}r zd&GaVDRWb6rSE2?Z#(rpFR8v~A|DMqA3`3_FD>IK5}u;J1;}VIkEG)9W=vJ5(o~+) zozYvXt7n7K9)h?ySL_VVwPM3rsd3!<0Wy| ztw1-!bd2$*tP?wbjCZrG0g$JcNg<4Td{7#19kpXf^Z}%H^5T651MSM&&;dmS^7D_K zss{!|&;wV1ZBLJT3XcTfaUOd=KlVf6e~q2LNB8p`XiMzpT#$!f`^aT}QZB{dVvgBj zMSXBG>JyVtpQEVPk@~`<)c0dHvCIGIts?(#@Va))ehH)WWx&Mru#eJ4#qU}2D@)36 zE%TazXChIEcDLR77wiLUshz_C)ul#lFb8IlVqME`Qf`Z6-YAMPV#^vncW4F zdebr?p1htBBl&uRSmkGlB%edQvIL~e11)sre3Gn4D&-GsB!8et`s^f4N2##L#sWgU zqW%b}S0<%?l8yRFiu(KS*lb`@&q_kQO;L}M`pTr#UqYm{JIeF7h)L@09BAd=qTVve zSBmUW?}O|ONy)ANS+nC-D3YhyNt!}mZjp_dBoAa^@M2QGDJk_MRdS%-*}&okD>ywH z^;5il_jVEkeH1oTi1Dgayf%@yA3Nlx*voP^cN;$@`EHyHY+Btk_qHX z5^{XD#SxMgQcoA^$#UxvHWmndkWC-nkcU`${iBGq;En9K@s8V6z32EXl|C zX?Xog1;3qwPo&Cc{JMD`?99M>T6`^QTP<`5wY>Ue2;T_*2yMXEXX@nDspGsKVFe%e z5j@>HOTiVYjkZgbk0>Nkk=l#!G+(cx=?^}lojvkDlrF^IAo*y2d+3$nEeW#oQ?IrB z6#t0{4Lt89Z>-7ZqnLj8^?p>JVt>ozHq4q(t*FyxaE_bnP_M=fmdL;4qu@V@__BhS ztB{UPMDq4P`YMK$tC0Twrj3(1oHApG;uhAU<9qwiX2LWAKZm6E_rzCApEk5>iJ4r- z({cBY4;MdE#J<;)81f&B5c2t>W#_jzBC+O4ZuWG$H}}QZmG8s_-t6`TGf#+%MEQ#J zzu%yq@#7%uJl>+M5Gf{ia*dl|ba`XV9@IWxD=I%Gq_}q@YhU?U7dRbX)_4^VP?`(i z2JgGu56D(lYa|;BAh^q3z7B?ZwM@MW3qF>(PFsL`#yReB|BzgUM~~vuD&0Jdpdq+7 z9`WH2Wn?@gh~NKaW)8dKjW6afkYu`&1S=~^($iP?)OX@?qx>uvRzTem#1Wj$#NCIy z8_&DXcsGJ~U-Ir4-WkA%4(44t@9+&jZ+9l{ATQq4mZc&`&{+D>D_WoKMx)QTx1S3L{aZBu;X&0^H=zi#ux%23V2P^U3eyb0=TL`t?!zX)Ch zXc!o;NP{yz`Mn83G~zXHSmtBwgW~P*Try1lcKAA92%nSRX_qOI-J&dyW0%e$N|aak zek)PltLs&iMgJpF8k+G4lJ?rs7k?r@!(J(Uf|6YiUKnf>znx@-y-W5Yi2mnB`gBK3 zlJPDtP1r zuO&nxkF^>IJD&p#%%XO8^h$j`f&#L#1-NV*&Y_BriQ?Y2QV*g_?NN7?;&diBSq_)D zGjMr~6C)P>MbCiz_+DLSraE-2j?MGQftlv;Cz;gOMRl$c_bPZt)egH5U#=E)n>r{& z{4AoTyVn5Fi>^PWuH%yGdWU&FR8OiuQfw|3Hu~v6qOQwgoc`a@b$}A$MSLnn>uUG) z?pjf_=Qw)O4XZB`uW2aCQ{I!G%SROaSwGC%d_Z!*ArBGc)iw+-v|Bxd=}pcKzlJ2% z!j{%&yZ#RAahNLzvxKG1X%zqV{4^x$z3Vh^9a^$szsrxeiz z8ax{b?P2PNT_})873o_nX0dp&KHt7cx%)eGff+UzaNu6ehqv=I3g*Me5=`>ZFU-;Q z>;Re>^klRAdYt%(DFhvj(|$^ z7`y<7x8!-MLcQ`%-L#kBV_P?!12<9o)kHWy>j=3*=+bFq);VpZ%59Z_R0Hl8ou(8c;-lUQ5~z26#{ zp9x^g#jPr(h$**e5f< za9p;5rzLL+?$wZ3jng7ep=5OZg6-_YmI&fF=H{4_Vh~7VIS$XN;h^T@^bA3aE>(~| z`dX)C^aB2J95a6k z*I00u=$VrKL%%4$$lM(Z)jcQEeF&)L^Pz4$AnB;$cQ&}?+I$_X;Ede(iZz=cGn*F#y`Vqfo z)Y{wtW#jvi)jK!n>coEhxiZlcG|@vzO*9F?l>PYad-3~>Y_{*=pcmIcdv@a&D@DgA zUbg9I@5R^9ZqrYCu}z5>p@>_Oig*XkrGUZL_2N`So^REQp91ikdl9L!y%)1%y?BJu z@Q*LqG)x?gpRS10^1tuJ&yPf4Mi26?L$#I zQ88t8wuWYS?ylx}N(^Ipoi;bty%Uy;>i>9=s`nOc2Ls6F#s7W(7AfOhMdQ7h)Ofce z`21)6JFWLoIx8Sb4iqKL9jY<}+l|NrF_fJr0{{xD?0qBVtxY=f`X`eS>UeOP3~aZ z*Mb@vRE{V2#@D1L@3(&wP)nq*B=9s!4<)*>kFGglTkB%=$hB7WFVSpY`j8gb*Ngs_P6O$HO*JzO;#gC5sm2Y*Q3RI6bYaKI?CSR3T_SLWc84S|X7@4E zQ^$1JHAw(f*SIK)FZ78{|4+iE0R(SPuzS1vrbTKYxN#d^D2W}68A{>BAWZDZQfcQ4 z`)CSKmQOm4I{+{lB(0J0;`Nf*?YjoU%-W*G5Wz+nK~i5jEy65RX^otZa|8`NLY}mIc z?4qRDfjDfqs>$;Tg>{srO2MTCmlPBi%r-YJnh^TVP}tZyZuRb>E?w(YkK)JJcKH_k z!M!yNPp2F>RJ_KI27JI7U%YQP>^ZHrd|?B+z9fsH7HDtbO>56+j%46BXuIQU9nFz6 zP(%X^We0bt+D?0IT03aCw{|4fr7PGT^-fjao(Qiwwk6i5D){Ea`V`c=w;qVEZjVT9 zoIYY5Ji&lr@bfARM>F_MXPT+t;Hj+_Yl|1mDwtVtaY50gm(1jk@gkXTSK%u4LqyJD z^r6<>gK#gu5G3tC4$q$W9EW3R$K?fA z6wEF7e!)EbR;%>%>U|v~9+KC?(BJ0)2fo%J=l}D-kPm|?!x)<(h47Gf z+PGfP?+5r8aP&QoX(2WpHa)baBcRPAQnt*uz(rnHnl)ZT}n^&t>o}dhFNkMH7m|P_p znkWrb8(Qm5A^@o?vBDczfnb8egz5ZH3nCmcp*4HI9wa2M!bB%QbRY4bBuP;^tX`5q zUIibatm&i#RetCa0SRssFd2U!_99%cIoro0wW3?NL5co5U&~t%of~v$!Uswo-RW~M zBceqnia73@W`S)`ly`JFaF21SerGLISA-%z{jgyIZhYa#Sr*C7PWWSnm`j>tj{gqR z;MMBQDP?;iYSn~>{)w~uz-lFFhU(_vVrRv29ILC6vRc`L6%jQnp)uK8TeZo@mq0`E zma?76e3J>s4ST|wl*!;8Oo*tN2o1bnDopCZqzr9#CM?Hk(ujVC;uBaOGuk6KFdlC- zB5Ec=W3qQ0nJ7eaTf_8a8A0KON)kmlpILM;u{@~*)Q8PXCJ4A&xTU5Z>5TiFS zcJD#9gJ6<}FmM+Z7b*tjeH06~dXG;+ek_>WMYOQ@&o?(B;z}bcyLkEs#Ai!RDr}IV}Iq-#! zaezas^Pu10!BobsZrm!N%WOi+;SjBBOy~$soN)U(Ub)Y!5DJVlYE8_o@ zn}5voM>`){jQMPG^i+g)@0t(%{kMAv2Pp$Um=Ss>nyQ}u%WQOIY}4>}cqS7$WjmB= zD2>Q0sAfQGOD*gy0%@OY7~;GsBnTcz^`DDBt1jZFAlCHL zf|(&Y8dmvBHD%>#0twuoV@To*fX-JpKLRLcWJw46Ob&zmb&<@^KY6B?T=FTjn?kJx zX$l2-QdiBhggPNQOX%bCk7^@9zFT5RHiD9!4$1PBWSv+^Ksw3N_v6VV&Py;Y6$wS; z$de*uD=emkJY5O-+0(rUdALo;x3QB>Adv$$x-BM1f~+jlCNZN z68Roi@{Nee_v2DA;_ROC;rtKUOaq>)Wjl+qooC5*gnVcVHnD7WX#_H{H#3z?W^cY6 z*P9uSi=pnu^CN_DSsG+2NuGbIhovxH7MhYMW8+O>?)@r1comJv)7B?Le4+>9)EMGV z72=#^h&T2?d{d`}T=dLWn8zl=oYn*LJ{#tV3iI7bz2Ne=Xcw=|7X1l#jk)M2ivy}uR{H)J=SBFDSFw7^rl+$ z?5{$dq@dr23-(-}x-AqD94UZ2e8I0TV^+ZI0=0&>>;8V{N-@H{Lde{2T50V!g^KHO zey0|CZ37^z5!-ia)R84f_0&RKJ|o8`L>pBgz8{xuh}xV1Hsjv*ahiF1%_l^UB8)?r zb-o5Z3Y3wbA`>zk&Xe*A9nSxGIQENi0**n#E+lv z#Tc=&*)zB0(L$iMpDt2fVvX>dQf<5-OAVN~H9|xHPu^ZnMv73O?6fgD}O6;bdVsEB2J;eSnRb;eV{ClcO z3zR5!Moet5mze`s?I`?qh<5r|51x^fF(+)`+ZlH%m+Yign5Cqfalg*(m|#9FY-Bdm ziR>-*v~c4*3?*w%NdR^*y;FNi?Jr~EpR|>IY2Q=Ix1OW%3*>n39Y^Kh@6HQA46e+l zhUOU&FGR3C!c$*=Y>lr}&-Jig;Lmk%Zwkld#d}|Qv9bS0j{VD%j{OBlJJdto zvmt0B=$Qk*kMspc4d{suv4JmZCZveq*J$eqh|(Il6mrBm!cB#&ju3#;DX8j*^Ik$n z{AP>U5xtzzd0}o3`+wsj)E16^n~iM`-);aoOskW?HZfkSsp|3=Kb@ns>=WC@?IXD4s?u93ZyKw_IVK8i$!@j)0Ipf?E_%LS7l|^zCo=4L2pJt=Vhfqc2}4WIx5~ zcfajn8Kr9!t{JcE@z7OPCU86qiy^-Q1iEZ(ZxntNqhOm?K}3tSjZXq_y7^#CZnKHK z*z$I!uaBva*+a#O=iuIWo)+V&lGqVB+-i&U5jZfnJvP2to>j1ZNJ4#&o%|x zb(t99rr%h8%MIC;W_}QAt1rH#93GOytGaBEUr+67sgbzUW8LN9rXbpaigPbpa5hXtxsll1p z!fNBHK+bnaa#ApGXKV*6$RHC*$g0N<3qyn$Ws%MF;hV8)<}yDxmtiA zy4rHV6C1pKrl0(Ds?2C$H=^Tkk$txZ_qNZS#{j3TgoLQJf6?Ni@m`jv4TmSIh?4LIDk322{ zb%5d!MFZk8G`$u_WYz(Z2W0M>1NS&5Iz_E@JJ`JZpRgRx*74}ORFtwpuZD%d1Rf?* zvG}Pr#NwPs*&*hsT-!qUJWz+wa{3etS@~Robm84tPh+O>!Eo3TE~70^PHM|9UXd>C zmL38U<>r+4!KLEX4`CUsp0m)a=h$Yv7Xe^-b7PJ?)WhqsS-#b!jq+@7wYO32(s&zH zq5->_6Cv$v`X{$CO#iIL0!{npCheaEaIxHl0EzkMn_tizoC7Oe;-_{Wy^M!uv|%&9 zgkdp*4tZ}u9D%!&5itGqNE?9{EI`b*+SBMU=p{r9aX2PG_pc#HN5NC%G) zCz315`6!F0Kx#^_y(ZqnFxlIk&qhW%_A4agrf*HZ1bu`Z7Mm*_MS+#)tlrIA%gKyb z%VM-dUU`_}nm(0ymh!`57|hIPc$N~5D*V0NlcmHMMo4G4lv$~q>>fH9BtmDZ0n5!Q z2|lgO(GlhWO)<_M75m7$P21}pW;~&1xptGWK9>tObP)HQMh{s#8$D>{VFEBk z10YcB*oA%?`Ow$()43p!w4d(cqAk`>OgnWy-Oqmd5iQ!IpZ5LUOgr%za17UA1a_j% zVj0Fdbb)wtr&jNmDjiL%z7!TW*8!r{B8U@9KiAFbp|<=evU1!uygd^+Sst!f>j3(v z577r^4K(}IkIk0RX;aDQ(()kNzE2! z&pyY_rgh}ZqI^Bq2GwaTtjCJ?;WK>N11_g@mIu_$>|u&Y8UP;f%ubkM#IF)PAkh-} zAd=J)cPP(KGFpF2OKeSQiNC*Kj@H}QE(38mZDItt0crQ{a;z|Ah(@nca4-XY#1np^ z&mPL&lYr(E2_kRQ`Il~4IwV8}Nf zFb!$u7D8gh^eFoc(*Gk`;YU|CX-4)<0`?z&Zu$M6A> zNsK30Isyt+WiKSb?0htIL3ZACzd3_jd4tOD`^9vXd|ZN@d(9f9E12iwds6cCe8fIK zSVeymp8SVa=uOTPP?G!ip%K+&moG@tG~2q>A@&~oX#%_ZQ9|D@%$sPK1g*tUNBA60RXtvj|JHrLnii9y|K7uGE9+k^t;%e;zc)OyJ`q3g`s$9oBj((7 z^l!fWN7~JY{lbLn?V5`xWzhK^TQ*K_q?V7c} zJp4J|`3rw*!u57d#6Rcy8z)|U{d><%dHMGbdsi;_kL|bkgO7s)y6*UO`Q%UL4V@bu z-y2`EK9T>UCvQ)=W5kVTeVCd1%H)fNK5xQVSLCqx&v;`F4>KG*xVKzg-@98~Ex1EB zhb(Z&0*5Sc$O4BfaL59OEO5vIhb(Z&0*5Sc$O4BfaL5Axzp=p7Wy}0amX$0lt(xS? zsm6asN!5a~aV1L^o_^}M8RbjMD@w;MFfLr?@t0PiP_o2Rvb>~xNy(Kn|0dsUr>g-B@6o&N_P}4TUfe;=zV9FE-WmmzIs;qb)|9TvhqcK zsW`8yw6r1)BvxYqOC-^4vM+B;cBbC5m%&ga6>t;fEu|Iys`Aq6 zlRVW|mseJnS1k7U%St_P0D2~nV(?|XW3G)UFeyCfgu%I zuq6wXWONM*otslR(sTK&^2HS;{y-H$$4;o8>$&_QZz1nbubyjMp!~G5?Aq$`1sqs0 z`geJvjQ6IIR_ldsV*jJVN_EXm%>+5lus{Dw)PU^49vVM4hop=`o^+v{upA%3csW5+}# z&+f_p|GbVmUFm)QY3gd0|F`W?p!?^$lEb9;C#|npHuddIe-rMTL;o{tCwXRFTkS7hYR)S{yaaIuQM=r8<+c9OY6*ogc+If9!V{=2UFgYO zDkvjL7ml=}#NaP2sjA@ov|w4Ff)i%Bh^3YAR#h#lO5Rkoth%}!X_sf&^3tlRKt;8} z?IHiA1v4+4e&KnOJSNL|LOYl8kHn0K>8QeUVeyO^33^y~8axlsn3k46r$trEmU?nl z62T-o$z#D6&Geo-eU8?9WE>yrNmgUc>Emng7a?r`Ho=o{j`8)*oO#jANgm4_QOeJ^^{H1{<{_;wgQ;V65CDmo@oQ0+S(gpDD;l=_ag5#w1yg*6SLX3>5Dut)JEn2c{g$>q5 zbKbmaQk=J-?CN=ourM&2<|WivgHYXEKHEEMHnOa_3H8RpW%H2H&BHMB>m+FG0%WSQ zAT0X|2J*}-y(WNW&uqe=RE?gT)Tht*F_-5Yzhvdy$)1z-9c9U>p2W15^gNVL1~S0J zF?h}O&MESqGuu0jH3jF)E-sic(f|vc4WDegA8!;|PSZn>7)oii9jUb1hIVfGN(`q$ z=@=!yy0@6c6>W&quB|9p3XisoRh3+;IVle@g(tgj!K5dbcs8beY>X1boR@?IX!uGodz|FCbXnCB z3<9+>n^(d$@ad<{rXMN4C^oCKsvHSBb5jr9 zk*$kL;5OHeHHr&n;(Vm~C#&q@igFCD>XIb|)z?-mD63jlu`EzMvvffzt7li0R8%i5 zM>wjIAz)(0U(dQq&7W2JigCBHWL}BCbY;08p}122H`0UU(c;CWRZN)eFft`BSh=#~ z%5veN+7@x8j|HAkNP=s&8=$p#<;r=L&|(=;{MXJ~o(C)}F-_LENubaZ19XZjR+LvP zoX6q3G#THxcJdWHIrT%?d8I2ClvZ-74Bp2h)lOuY92lllbo{XWimx}9fM{?`qJog1 z7n$fL4Oj{Pn5P$Uxk;ey?W$jG^31!kWZ}FLjvSdBmXua3_Ls#|iIYDmyy-2vUolt2 z*{Vn1PzTN^F(|uSSC%d=ub2mmFDUEHHEbZ-2&L#&g_6VqR-+bF$+?(Q(7cib*96L| zk~xEPejHG|o~o$|0S^09y-6AelnlS*N{r&K#}*LcN;Y6HC-NB6FFf}mk1@?VwfH<^ z_RNBFyaw{4La$ML;RP37iW%vKoa!-W>50kY>%Cr$<39R2SYMCP*AeT`SEP3qRI#w60zfL36jxN+k$`zo04dKRo%Ig)k|)quY*48? zS^WGI-uIT4lI zVkk-p=n-RpJtgX&z(ZR_*z|-|L~Srh=msj1Q!X~^6KKca74v zi-|ch^O4+&wdA`*WViDpzSUekPN}4wV;Y>82^aL@_Fwr@EZx}Q(Cyk%|5)Qj3sKS4 zVw5Ora1UM3Y$gHPaGe)R5HijYVC4JidJr#a>i;uGV-GXmSz;{56?N`hT)mr8us`|p zR9$rvt~36Sik}{THBD6wVAT~R52qL{DXJ!mH6NvPq`sYQ>`zy9$N0LiANp|GYZ)E= zjZK-(?fs2?nNHk)&|d+#PsR1B7gKkq8-LX}LitMP3l5{xp@5FR+cEm7G~;!L^CxM> z>J%sLf12WaFwJ;Z?%TCU6#l3(92$Pe;k+l+c$wJy9L^_GjV-CeM74e;wJE_=W2089 zZ;A5{4&yz|rElr!e{&dL>QcYbn;gGPF`lGyd-@xO({SKzEW z0w+r0B*%RKP8}hIJjY9T^(r-23a2{OlJrO^OmI}NFiHwj9AijebT+r!^BtpzbCMK_ z9AjA+BZc`!>I0w+LvMuv5958tBWTV}J=GOKK`C{bR_YGYKV77CIqo9;Go+AZWb^}4 zUg{**(Eu4xzR;LFz=J!DKSl68j@?xK%t?7@$u%-bzYnd&vRE|LNPUbFG^1Brr~0Duo=!1q7>*!ikP@nroR9MmX+fp;8LEkFM!YF-JN&h*Kp3 zS>^x@izYCrQYCLiyGRx#%}8y1zFpA)#jJ+3lbl|dRcC;l2u>b;uNG8TS5nowDeja1|pBi4g$ zwvkE&GA@P>Oh~=o^=kmoFb~)bvq^|(!~E)$-vTDr!DmmY503f=u=9+}rKn4#sIClX zk)7g*;yz{6$*6TGc~V+(5FAv&m2w2+ajHU=;bK(Fb~%PYh!l2e9}_1Prt53s{0=1h zImQtR);a-~)F;Ss0@TSH;JA$S5Z&$I_z^l;3e}Uc8$<^>#?J!?x^yH_Dd5m@-eo(o zuHlta(J`(F{<@BGtj9%Y9Ia(BT%C}4!m*B*(Lz4MVthPyKEoZS?*T||%8+c1n-dnI z01J^|>I(dEJqBU2Mmh|T6Aq&^hm?2V4x=3>vcYAfGFrMS@F#Dq<8)M!=eQWpJUh?v zj?*Ut1iCqKN#(E4xNc=fOmO&7A#5g^Y+gdmBvo*wyaNW49U}+@qYCSzftYue<3Uz| z=h-oy`F5TKj`5EI1U#?BB{d&^Tz6A7pX0B%7oO8ip6|oj=Q~tTH(c-1X$l>g=s0q? zFvj5`JBK32xPbrx2NF!(LJr5kM%i_a^HDDxZZyh8@>y zcDKaXHQ3qR>X=xo*pYhb;rMg-4X`9aM*7W=!;$_&>X`mB_G`~s!4p@TTGU5;sNIYtUu z4krkt9V>-w$8}IAZP*}AuO7#npqAztsG{-|5*RLp;~oEC%W+aT!NJe}rX4SZ9LFb+ zKJ5f4oakV8rR7Lrgkwk_6i$>vu45WoMu=>A4i}Zm6$vI78BV}ur;W*a*3x0zV5P%D zrvonUaHYesFjm_5W9q;!%NWSvnUy^{^8k^$5Dd~z?#Fwh-#`#QMQ9t@{W4KFRSK?b zqYtg*0yhcy2aSR6kygqXndhU!di3rkQpG?+wQ#EcnD4F2@zL`iy}eprwz)%|em1xE+jkX|o5_ z0wv2K`d=o6!yLoe@_pf)?YNNCt`r=PgI<%iXy8|%Hr(j52&g11B4wy~j!jZ_s&fCn zx}0JRya(9Xj`GZlQ0JIWCRb}C%w}PU6kHA(A#JHxAxn%~{?c%QC1;_L=?Wpb*HQ7I|kGeK@FyB3iqRKJ0d4 z>2fMN?eHjc;t+Hepe21O_!#N0f{Y{mWN49+-VZXRrmv)}(|xFSrfVY2byLjC$QsAVMqtfR!#GKK<12P%mqzdc|;Z! z=`&zRrqp(hIO3ncbsnF7DXU#Dl~g-t&~lQV@Xs*Ab0su# zj0CJ=Y%U@kckaZ3BkeWN!&eJvx%C~jtI~dr@=0TuW~QyLuU$2qKL+{-0QO1U_*X6Y zBs7jYkx4FHO(;|6DaSF; zr>)763bL3>yrmb&6`C3pu>jX|UY3rM7S7pDIrWo&{*7!FU;tVP`0=M8kZGHE_ z8wY=^A#T?YY0R@$oWL|IZGC&~DtO`(E=A-CFyp_UDrv$zbTK&b zF3U()D?%4xk9MW2WnsEn77kU*LRT1V>1u80x&!waoTO4u&VH2jYJr%p7KIsH2&U?V z;8ZY9J?9v~n8uNN{!tV&qbU>jMMv=d7!KZyG%|Olr;kFxEd$ss1K2GC*ewItEd$ss z1K6zwFhn{Nj_4vR@E5%Q@c-fLJ>cXhj<)gcnVrqM zx0j=Ice*>B;whpmBq0G3AR&Y#B#|T#0tpa8A{*{x6HPvHIE={HV3IN6wG9}94cI1_ zU~Dj8gAF!dFiyPKnE&%s^~~-{eDD8zzy1AocdDzps=B(mr@N+R1`W19Z&^dqWa7V| zO`?M6XRBE|aWCrHUrDtjPmGB>L1F(=^#jx$oV5Q=(a&p_ng3Lu$%Ea@7j0DE)OCQ) zSUzLFwat#UsQ_J_=4JaaHrvi_Pp^eWV{Jz4o_+0#iVYc8js&Z8Zfpr1ysk z6BKAM?X{rD$qy^S%Cd`0vZO#61s>H^qs~OjS1Z?d;ILs=Zv%?cKZg9>y{e~KGd%{) zoUcGKJqgO}>7_}9uD9M8m?cT5hBN3{IH+d3DoyeP9D4_%b&I{>EXVxh26X)*lornN zo*Qods^7Q*Z z?XC3Q06|YYCzboR^~G@2$c&9wFwEfh5A>NKTSsCV0(2hOixHqk9V&H*8mI1{#%EMx z3(&RfO)BAU1l0Q5QWZMlJ>@ENZKpzai3)i>n!QQHC7ih9!Hgd}us=Kp;+o<$p*U${ zQ@le3Z0*xFwrnS0aH)U>w5L)8TrpG4daUw?fV&_-SXRaxmE}2(W%YI}YfD)^ik`{{ z%Q24S;So!o$fTz-@p|p4hqR|M@fq4v_h?UL;`^$|+xvV(&%y9esf&*C&PGq>;amo%1!(CQuYZNKDjx5o2(N#!NQlQLQUW>VYhe{5 z%uaF4icMg249rZtPR;%hd)dU>w3nUwCO)Q2eG{KmroLBHKR*-A`7EGkw>x1gBhAO) z945+s7BneCh@(9|3q~lzd(xX*HEoD$nhC7jcjxz4d!akJPFn{2=`9yPdtX_xPLwzRufumXLXt)OQ_C#Cmi#03N z_dzws<=`ELZs6x}QZZ^!AG9nG@i+BlKXOE@#t#?i-tN$GX3)i@yw|_lWUm;fI)y}B zhzZf$ppCmy_=(Am9eLZZEbR#x=-=b6mO6P0L~BN8ragJGPZy#Cq7WWx-< zCs4xAAHXJdlsO}4R+HU>mW5e(2RSao(L1qC=DI)zAN(>??eg~+OXCL|ZVpQehVBm!PV%#<*|Bp-+;@Fea%&|1DDW4I9o1{jpZ@yApN}} zR>KNj|5{U^r=)h`2_}~N=Ll^*oHS`1uM_Z%eu_ib@4vXu`vCgf=0?=~km% zBPKKE0chuq!q4441~!Qv>Wgg}H`d%6!ngOCOn#WwI?>hglBn^dpF2-#T-GOJ`)471 z75q0i5)^0q9pw4fdL;6pI?PXd0bvR=`#(b%QR%A}STb z+tp2Rrlp(C^S`I`aKl?bzgy%tCbbZnzv1N)?W)^dSn%*;2NuD>>`wT3(-SYFM~JR( zdQ)^zxe@=Sw@A6W5&xFw+|Z5qx4hxuh!^eN@+PTvSE+Vyc?-1uw!X~1PK^iL#N$2G zjhWSxvyj(+Kqu^Fyu&LHHt_~O&13Woob1dzPAOhH2g9DN+K}%@Q`amYEl1~l-fW#} z&d{mqK5vBz#!~W!FvERbFME$mNz2iApLa7yqu-C&fKGa!_uzSgf2Y^G4EX!iFm%!j zq1XeuLgv}my9MMA=_1+hzp~HU2lyX`cnrrUVb{k)7$?^@u=|l04gNhq{zQmIr{zKZ zWC-KL8te`FE4;x}@94qWU`C4hu>+3*>2FyFf(5ESI_g4jxWYT42Ly69|2t@|3I)#3 zJm8pwOHuE6Xt2$bfz8?I8Gtv0;5=CTQ@ByTC&N1B>V_wIJ7Cd4_DNW@-&;h9T%OUO z{oW>>-sYR&GcY(`8Qd~RTTo=cn_c{k#~%%fy-FFJ?;Rd7xDxzVYM(c?D1-B&K7W!L zaK>Nh?MVjF>1n06CUjPh&b@!J4>@5Us77X!*<}ZZ;QTJeJYELZl)v~bm1b(Vi(#3Z zkn$!;si}{^X0Vt0s7a|tMmpVIfKFVd4faNrok9nDdo2T%=tw^u?5$8g#em{6Xs~yz zE`um7P-Cb$sHKnpxAVN*)Ol@gqZP zir2t~dHr+Ins32I&)D204N7<*5d7GI#g(o#&)BJK4fntR4RrtNyD&wCD5{+Vv1 zxq3-`3~#u@=6+DGdUAEdj~#d~)bkG8U8?6Db~;x)p`LeGnZ&3k7ru8`aTDrE$#>Wj zDH(cte+Kw%D<@J2&Unr+T`JGS)|>lmi?(7l#>rQq=Qdl%f}Ndi#Zyvu6?z@{7oZsK zmSw1aW3gGmu`vHNwuUzQd!06IfsiLw1d= zIl)oy98v!P)c&Es4R_|3q=PkfJevo{GO2Gu+CH`hgc8#$p7=Wu>a*PR?qlaUuk_jh z?jLFnm;MUiX|`sWtK1*SJvyvB&B~dWL+?gSdIS1uemGot^w(&z2;9+W}08Fm;fWD|BM8HDl6sZj+m zPgu9)S|LlWly$4S)yABJO&=qnGtjST$#8icH6gWx-y+zPPJODu$oFiBgXv^Rj9= zL@VQWk&g?}@z|dZ1{Vo~6CHyCP|Ode9sGKSnu{vQpCi*h4Akce^#n&fkEHn6fjSrt z)aMqduLSkU3Cg}=o+@{p(31KnP{#(7poxc=q+$~Zja6v2U1PpdsmCkqA?7P>dc4}i zzY8Azsi55~igY^Gn`^?0%Im!syPqSV!MRVlg@KX!oe%Gg{I z)H;jBjwaOOYI{7LA39HcX1?77+a8`#%GJi_6^*6Bxx;ZT_QgIS=cP5JoR^MvoQ2nl zBCmrWcCPTca2M6`a#a#qUKjG3TgGc{$Sbwq7(`Wzy!HmK=@nFZhU2v);`K}L^0t`i z?ao$qy_qR10WM1Y(^0cWtbEHH)q){%e(b;otR+0$ztYpFRp)!SwWVjc0<*o1rf1^O zAmoZW@ncu}K&gw`>&@6oVj(5*yhBEwbm%xUK2PWL`VX>)jyL0*6yQ0)zZfbF6_pCE zUePc=rAj9pWrjA$5%xwWY&JuODL|EmP6$=9N10(wFOVL~y3J--haSVdo=0ImzJ{_! zK~`;o*M=9(*5*HsV}2}FC2*Brk)W0YY^Jwm^P#4;o8n}%ywTJ;d06wT#r;V2HLL}R zsq;{=!O34;@pZ?2lJGWWHK<^s=2|t;e3N+8QA88cdei2xh@QkbJNPpvAp9uW{@zVG z>m5Hk@Oh|hu7=ujv~@#;++ZEq^J3p1G%~Ud;{+8>+trDjlRiA;x84+7_pLzarfW3R zjfv!Nx;rhT91t7DLaAb+*wA8+#KuQ5*P3`}AA6`tgw{!1?OHQ2yxTN%}hX`a1Ux(z(~yx!2dZ*Vk#c zno%3QE>z7KSSR4YdRb^Eu#d&#-^1Vk;<|3l60D|Y=Q&+X#|m$kMjje(4NB+AanhTO z@_7|xU+{i|vAU}q*Lb&}zjl-325%K;c5hsVx@{&w6>6Rab`cAN?_$9z-+kmb>Fos> zOGHM&8;kn;I$o5qRE}G`w?VT^j=`5N)V5tHSYeg8xr@MdHwZew6&@EBo&d@Nn_m*11y>!t#0y_`7H|hS95S&~-UldLUB#(7<+nu@ zi!MmjUZw*5Cu~=1e8Mv z^Wtd1)cybT#k8`>3eZpe-W5{ z-y^eKm#ep~Loq*g;2=yS4V!=-L_BAhN_Nlpy>80n6qDf@=n*eTJl1L%JcPwO>PZ)4 zW{uqm=8t*OgV+;fx%=CQFM8x{l~r}U6FL+O_=!D?#y{_QN8p&NB9o)Wtd!^Zqtc|Q zRQ+RMe3xkb>N4fTQOu7Wcn$=WlVG#Eyg}3xbB(Bdmp4Me*`n7yUYyp!sv@(=+#5Dw zwP{PEsvYOW+mZnz9SRkzZPg~y0FXC*1Q8BXyg!kN6aJvn@3RL4`LTaQ!RzQTg#!rU~6)dc~5#; zE{#y*KW1G&CE4shNvj!yPSOwF7dMcQ_1mERAoL1JME=4Ba_}HPpt5ET@rE8_mX9I@Ym*cohn5N_GRs9sTa$d#r;N z+g3oE3!?GTbpe;Jrf!MASox)ROv@jb;Z87Ua9WGPw$dr-l z@=Kx1x}Ds3POdjcI7_ZI<_EA0PryujNXZE}gA$&A?^vO>dxXu!XG1Fl>j@a^1*Z2~ zJ^fle{igNwi+nvM_gg*vT0Q+{c>4A9{F^PFbp3ezZgBQ?fkPNiaxpx~#qcB-!;@SL zPjWFl&Be%_$q6Rgz77>q{uFmU$a4bsJK`Up)aO>hm!mzRBZ3vCko<4_Lu=09u}^Ru ztOmO=6IN62YxMy6!yAWXfg!s z*nt~dv%ZbJG(TT}XGS|Pqa8RRnb9uHXcvyuF3i$vu(M@9Rxt->e8UEoXqQ&;9*qf& zr?NzQw`v{CuXkuvF)>H;a;l2!toks06&?OQ>cu31HFg%N*_b4-#w39?CJC%DNnnkQ zLnp;!%=Am(3u~F`FbD7k^h|?fvo>b=_7_QJ^-Ggk{g$c=S;x-9uRlltO=k563!urY zerwgSAkbu1zpe6M07ER3S^ahaG?~>OB7i2d`W*sjGOOPyfF`s0T>@w_t3Ol#O=k5= zlUe;?qQMkX{U92~ywpyN?gq(h9bNc$)SlABR)1`rwugzW{a6U0 zWWZEae_s91DOFNc{rS>XO;z>xs{9QwnyTtA7L2B<`b(=$r{0pP>aP+&Q&s(gT&FNq z)jve)XsW8euJS{0Yd6(l>gfo8l6vZwrk?txsi*$Y(ujhzVWY4{>S^_>U_KkEr%TA; zHBK2@gwDi2N3)L0e~xlvjbxYl!xK5WU0kzE; zQ(n|l;8`Z|Dd?%5z!fGz&!UD7PSF8IeGeIg$wcGMC{J-XHhx@>HpF5tp$9keJ4nNg#zw{&&Ah&3qKzH%#7X_xlXZkdJfo=#NLFT@08Ja zEH^#7lQ5Hh&GE8Nl@UQJHalaz<#jlo3*?x&i3ESSZ+MDF=G!w#h@$wbTu%ImyZG_o;nV+_HvZOvuRNyci#vA6{%6smowfH5Ujx0 zC#4y;FN)*!sq@foV(rlp%A}}AD?KAI@lwu@$AInZ#IqTD6m(0xAb4X35qMFDkbM~X zC0=sA7(lbMxpI32Maw$g z)V>TYU)v4jyrG4!WA-DYc24aFQ1SI|;AbkZOt3FfnA%fO{Wph0spUX$HZLNzdajA8 z{Jv^77_hT@L32h(XL1j)aqm~vfCI4uz}#5GdTi}qstkk>`(MC3R>Xdr&HrcB*$_bN ztH8VwVe=jAT$}$L>ax@R0gUAw5jD@`3lNSwb=H8AB#l51FC)<=`MreYG%!)$&s*xUxxgJo!^ zO$^8}w&Gd*asBulXkHHKO#X*ZWRk7;0)0;GhroOqVx0~Z7@RX~{!JJ+3rDEuNvmj; ziYLH$uFXFH^hltlm!Tbti-6qK=8pvF-oUSlXgD>P3VQYKHnkSE-w4!kA=*sX2uxyu zO+5=W?O9>D&vAlJ>I``3#IN>%2G^+w)N{?pF`J6V-vW6|R(!D+acE<*;)}_OFD5I# zn5_6>y5ieuJan-c>~P#-ukkMEV{K|DEGZ|+aW-f|3v3k%_@F_b7&H0XB+CVp`8PZ) zb}FX+0?Ap@O=0{ioy*Y52LqPzx%b$w|4_4ZYu zE(&X!rqe-loxR6(>+M9)txer?=*w+(9W3{FNbSKya5xV=Ibi#Mp%$+dtK19$x7y)o z!N*}0)4D(8rK&ryHk-zgkg7|kP}92wE%I3Eu^04+;B=5Yg)>IeVkmZ#J<~~SZ;*)33hkA!S4W$bl^k1!5`7Pqa65HZ}1tgqaFBEuc*PK z*pxr^9c5nFdzBvaI4J?IE%xMTU%&rvrxgxz_)LgzfR{{^6NBIXd~eVRaGC>O?F~MG z;&UDN7C?0Nd(4H(Ear zgL52%ZM}t=j-tKD;N0p#@Crl(u>Lyi5M>a$J)~djD&O8)*zB+qij}W-m1j$lj41k; zxOE2S1o|5nmF=7q>&60Hi0_~$;AhiNGU!co`@X00UQi^yjfQC|Py(BX4T7lr!)@`8S(14UP< z=$*-YDr&xq|8VPC6y3?X6#E1G{|DcpkKo626Fhx;u|$bj(~4f;?)VJ2)-c%}FU8aZ zpEVt-qWu162$p)fGj{i>4Bze+l@Yo#PN1X0TU6Z6cpXd1cE%^M#O;iAJ3Im%(QH7!^zb)WO(~XVM8bQHrH+gM}$Ryv4c}1GuN{c?%3Ygr=>V|p4e{vir@9bV ztNwEfACZA+a_k@|XFD^OfrL@`(JXil^O=1RC6BY>2Us+Cqp{dCJtKge$s?-`>Xrs2 zeOB_92T5@CK0=+(qI%mvZ2a%HDMJ`j(`-$d1nERHg=ILgT4C?kubj4X~avN+1<;%M-{Ku?>t(ytPxjrDQA z4L440f5UM*9RUerZtc|aEUeKGizj{u`xfRvqgX26hpMK~49AP51xOYiLTAJ>$#+TR z-%Fqne4U=L0;=;Og8c_jMp5;_m2CYM6!(TGPnascJ@^q#eJOA^mEk?ZZdIbu!RbRl z#Onn=iio(&G5$T2KQ-9e2K=kQy&dA6XHMz>eop$CcHp_x@MAcjeyT)rF&M`d<}ZQe zsZ}%3?IcN)dy--1*xvbHp>@Pg1!h(edjg_sv3=5$;RZD4e!v_UVaJ_C$@v1!WRqhF z?3+Iv+LGjJ_?|;jvDEm(Hm)_XW%-Vwj=>E;-4>yf&q91`zvQz(*FddDfqAlseTEiT zne0R75&Iip-j1;Tlf>?yE=c=P%m=(^`_;ZD!f>%e(hovE+W_3)5O4f8nr3bKq)wnF zpm=H#H35AZ+mwD1mF*%FuMAPHYj0+A&r18Ox)HdO%kfEx7o4Bm3v8%!KhOi)k-XFe z78e$B_-h2o{Xjn&k!asfrxpY0nbiFy;C>zA&4i_flY~idH061i=P*mi5z7)ZeC1#G zLvPRXC&50iBuOPn+RsIwru@}zbX0OOn#k#n^UV;G-vxg=B^RQJ9EOvwgcehPoy?2% ziF}5~#{=QO|Rw{>Buh=V4s)#KIof@Uh$>T$PL$Fp^e$5O2%NBlu=f0UUR{3BP@Py{-*<$Q~a%P|Z>DE?InrpA=8(|3b4;JgM(Zl8RrU{0%5F2!gNJ zsP*?G1|K`X%L+NEj|+tzDYBa6d|8p-`+}f_B&@ zwyBvz$m^e76Q@6)lI6(YYQ3M`_?0TfuC7fub#@hQSe&dku~QWZ>1ht|7a`!rqg3o` zj?50^l4W971jnFnVsn~Y-P*sq8V8p!-pxq`WBTYQusFz(&5IawQY~rEir`}OU+nyr zP^mO$jKe`R!c`4dn|6TPVyRle-hegJ9Gl#Uf8$8i|NKbI-61Xe3ber!7__nd2XUZs z8JXsc@hpfIyQ&MLs;`w-oo~_`K>Ha=n;b3o*ZkOl1eSNDtufVeVbWTZdad}0yv04dzGN5-g79;krOO1?MVFJgYWdC@MC5WY<8Y_fU7Z+C|nRmn)9!r!*584w<8pF z=-SYU&{f+u(pQlCL0xEW8fm3Y_F7`_f#0JvM-Gsk`-A*1HAA_WnU~ugxUsIrYN2P1 zlTj+hyq=@Be?@)gVP;QGAq_P!94D@Z5paIa`3*lq2@kM5<@#w#`WjU3Ib023QNoWM z_^gyAQv-sRU@U*i;QfL2`|t0oJ=4{>Txwv19A^Ak6bdsmhH~LCjiG3a!NyQJl*Gz- zIK)lj4fxIYN_VjVQFoaNYB3h)j-7!wPF`_>;hj}ERwuXa6qr@&4cY|#PEjsC{6h%xw#dm9nNf|khMz8B*?0QV!n#A>yPk%*;ReTGX2gIS3uTZU#3qVQqB#j2luY1NrsVbytD z@77}blXkHcje9*{Wnx zFUltMqHMKVHL2HQle*PR>i#lS@<6;AZX=u}Rwsr(nxd6v8+kx`Hz5t`g?dAAQ*jrJ8$MyY% z725qTP&xH4klM~FPEo0ySl*?_qru1yEu6%DJqT5E)8hpoAt;TU7`v}U%$uXV=bQ*EJesev95#Pppz3IA}J2hiEUu{cf~0V(HSdxARtL7?Zy4m=E8 z=5TZVC>~(4@i=8ai{q64;-8e*T4K0u8a2AT64RJ?IQD||T z3ED^O#<>Nn8_}7`Y`BV0CXQvIbH)T@ng1P_93nmX!DiK~*rVKE#y7xk4w-mFNuTO- zna(WZ%QE5HU;3VO51?O$JY?H>;;{x(VH62#Qnw{4$m-6P9@suv-3a zX#J`-PE~owcPq5w#}3>Gg7|f?->Rt`^Ege#--Y6<_TdBuDRItWt2T2E!$L4VK-?9o z!Tk6)pzEspZUYPK#>8~$cvYc3%>g z;%?k=*=x9Q2Ydd8L&2y#ku50ZHwDG~rko^DAT7NK(vn#^OZHSYJ*gQKQk-W*#Mcap zR?yieLGV1j@u7hHV@Y4Z6bfHcD1_#ZDE#j-8lUB+z!VB!Qz(RxY277^ObT0oa3PdB z8^@KDlBT>$uJ~R$QM>Rp6!2pQxav!AOFt3^7;v$E7`L*SdclWcT&P!eQ3MRXPOY2*L zj+C^%pt)iSMCdargO;i@aja>5!5{%NtuGiXfTr~YtyM>WOw;;;w#wxIG_5aa7eLec zf*}HET3^s1fTr~YodRfDU(h9hru79w1<!M#GSIX>Ikv)KhH~&sf!)|SE8B;Kj->Sk^Xd<#R7vX#=1W^O ztuNTC@)%$=tuI(C7)|R7mR6mDDw@_8tP()e`htT*kUoJjI7I5S$rC7pb(MYK)^74) zS|6_hN?KoVq-(=c5ClgFpih(xj+O=$qzxN|HPZU>ui{L=w7x%*1Jn9~Ekb7kP3yZz z8e}A`FBqP8a@4747R35Pd_Y9rg-{^>*+@(0u*NO^;Y4>Ka=FGJp07;>?89= zCr}g1Xnd*K#aKO^%o-O^!g6CP#5&E21>H-v+pE0$-1!eHn8VQJUO;fXH)o6d~0F6;Ya8 z8?YnGC`?6^Cdcao3(JsNJynt>N8Jts&5Ibv@BMiy{X z(WFY!T*{BUya40GjJ()BG&AYNcOI8y%2E=nM`yrpyVQMmGCUAh&o4g4Og zE02Zyr`y>oYz``)LNBJraTefp1-b1lB~vNhtsr;e%?64eJHPZjDa^-{48wY3ej+av_l^KFE|uu@oOaGgue|bNCkvG;1U%%Nhw9GJ_Zi51MPMz!N*tHmvhT;OFZA<=QG` zdx|_3<*iH>xfNyi;+r5caSJM%!ORs2T&~EEQT#%P_r61XD|1D#5@W6i3+x|4JOF7> z1)0H?eD12Rk6dkg5W}L~#w%&I0w3J@0EIDa!;>F&kGC(txJggO-;mvjk{_bb5fUP+ zU`curek$3fr@?hSfGzk|(LMi)LQ{Dnf8iZxpqM!t5voH>G`LBuP+s9mqq=oyOreoq7l<;E*ronIR#}Kt}+|N+J$EMk9A$8$Awu83~TH1c$B8re3 z2bU7NTCwN!*>ebgKFDh%)7jCY5j$Me49XK>_ z4bG1gqxK&l&XeDntTF40@8su;nem(ix=wzT;^rq01XQS0Xtn9yokEf|;e>jY%7Bnz zhoXF_j3PXk=y5bQIYvfW|7_$rW4v%?o`j`km(0xnfV*;L{=_lh-wmT6SZCb4PM$-P zdA-Du*GnATx_8d-gp=3n9Q8Vbu49Q_@~GDh^hqqy4Rn2R16^O-K-X(r2IJZc3OA^+ z-vdg@U1v$T>ntgEoh9Y2v!vX0mi!1)c90wgqsEhR*BM|`G7aJO^jh?Pv)i6BtR&l0 zyh1@165%Tp&Sf1t55IVY0)gli3Ix9V6$(7@>Gl+_P#_S!LV-Z^3IzhuD-;MsuTUTm zy+VP2Zcp))2?Q z1_j!~SDh_J(g&-vF6EVJu8lKDTvj~Ts)h|vL`;&1tGHNm;PM}g;# z42gS^6xF7?^tUnPjoE&aPJFaUm!TbQ(g{SHbOH_5ZPE!un{)!vCY^>J+Z1im_rQ2q zhOfMHNH?lY(I)+HAWkepst+_pn{@7;FDs)kP0=R(Rv?%`SLChLt4cQM)Q#^UdMTtc zRVABrVm|=plOncclTK_Js~g!x({!e)WRotf2DS%Z(ZJM{Y|`mAy8**AfTDh;reu>& zlJ!8JTt=czs43Z`ldc~$17&okreu>&5@zf@QAVPyYD+fhoT}a~qcOE5oAiGIl|Va- z3OjAK+oZRGrYoc~HQQ~{X9C021LZ<9nc9*~`T;;O(XJ@O)Rt`0zXH^GWoXxol1=*c zz<(>E`4C5@wq%q32nwDE(PnJfCjFJLJlv!gn01?1`xwAy z@Qt{cMv|=r6TS@S1jpnA0fO`P5M`}9F5`}6rqc{Uo%4=6J@Plj#fOAi9xQug}}b|zoEkVr$w zyD3g%+!rO@0r;Mri}?tZ#D`eU{!z}re1uBk4q*QzAg*i4(?9sk;go;dhblMGI2xDP z9KqrtJ71v1yFesA{|yx@)|(IN&jldQPZ^TBNJ$_^J}uw}lFuCp9-dOUk0ZIwkxX2V zq!%j$xSj^_>|W|2XUUk!u+4GKX0NBWu-F;p9arBD{2Zl`QP2|sLq4w$=L!VN{ovPV zl}H0BeXoS(Bl-_!Q`KZ#qB#X#Uq#pWj6m zn1j+C^tpFvsRbB0O8M`7pj8vJzyswLAcaWGm<$=lIR)RIJ`sm&&)MDrFQ9Y>iMMx_ zTHy9cr55-tDx~}eo&3swIF6A8)|+o{X93m_eNrYvadDnDu1+TqTlm`9+gpHlcAu${2 zN0iKQ6Q($IE1z@K_jJ{v`c5TX$(7US^rPT7-x02hcVYRi1h zGV#>3$&f{y3%IUy2SVbH_if*n4Ja)!jkcvL@>I^(U7@E|zU``)w57YGEnG&nrq2V% z0~}$oEf0U0oYpTHgNKG~iF3``nEy2@B3Dta2OJQY=I`a~v`3r69yM{UhO6^+Rd`uM z@Ah@ap|qsQTDP`Xm+S0YehH}N?@;&XFRNQotjo20B7Y&MK5&uLqR!v^vbw3LZYMWK z4}gYu@XK=L5fqfHp;*5e{qX_*H@kI9I6)v43Z-?d&gD-hgGwHjVGa03ye8i_0wZ=< zlkB}3S>bfBn+}pD$8NvKz5H^U8JDm8urIz2bN+u<5t=-MhZNC z^F99kzdaeMK8(@bfHhT{#OUyz(}$@ORONe4Ik1g?6Ck|j^c)-~{dvR;5}@D*n8@*- zQ(_wYQ(z>#=X5=QHvdgw@EJ72drk>-`o|G)IVAok7+!eKDKSI+ zlVJzE=afLV&x>t%&nbanJ|Dvp2I+9M1HdrRV2Y^^gLEBGrA>l#cM{r9#h((!atf)msT-luAog? zB|weO+ph2)Q|*>TrmrqhQ*)rVm^K6A-m%}Oqd#4iog`D_H><#Fdd(|WUTRDAanO{sHBGJ=w~LD)G*y3;0rBD4bzP#Ff*xP zI+~|pX0q(zP$^y>qnHeb1HyF7F5%a~?zQffEJ_^$5?W7i2-DGcY`?$=(-Eec0wYYv zN;zmCFv4_y2cGH)j4&O2iyAsOP7fI3!gL8{Q*>)C$z`Z#x;2-C#YstJba6U{=8nPl z6nwqs@nhLpW=ct%jwJR{{M=MVV_9ydmc;3V@S!pyoMPFmoW|+m4?E~GcH$$L16%n-97*G`)6$o~ zohue2F>lF!xLtkOz9v?&c)z74S+RKTJX5HcW7;a_nb{SK_cdFG?+y-~|1}ZZyv|4Y z7wde2f63vC=PZT8RXt5eJ_S?tYy`BKsuv?*wyF9i%_g6%rs~xQV{FxLihx)3yCM*4 zT)gMluu>90#GwFSe28fS0F&rvcH@e9;qg{8BWb@LOa^5y2J_6MJuh_>4uZE~w)3l` z|4r~$h`2xj8%zOkp#om;ZE(Fv0Y6BB%f$-Bf@fgieg%TyKmeC05D!`aT&h4KSO{*H zDUb{XLHo;V>0W*+_!%%)C?*?la=EgO{necdhJoy=I!;cBiNQmou@|c2nISekxCWSO zw9YKEGv}aLo7bF(g}ctQZLdSaq`kiLq9jD{R!@6FC6Dp8OknJd`9q=GooGLw5arP) zbnv8)f-?4|{1FiLQ=nei4t+Kp$KIU(FIeMGKz$sdP5OHDmAxhZ2{17Ts=>F&R|j0z zLKSTQ?LT2A&ElUXEV_1`S^UQnte$pai8g8_GJMYd`6<}D+09nC$i2j1sLY@scQDs z0;iI(X-1;$uk(Da+DB*_?K+FmX#0Ae&sIAL z_|wa1Oq%g%`$sPsGlOdhSGa_yE>1xFC?$x)8SVpAr z3Ryl~jo5zyW7AGelV?QQ>eJPTZ3Lz@!q$~Ur0EKif#E|xi%m3jB@tNF&G@q-F&A234KYk!Q{WjN4t{||JoDZ67F zpJDID1ic)DYs!dCHL>T}G=7f*hIhl3h!klfsCmErJ)~U;^oG0?|67^(Zp}TA| zrM2=FN!e~B;_*FmIF68ASQ6bqSK*4_!hD#2WQ%FPCB4UO$y$C@h~2@ZW^4nK$Br;;X){{lbr7kH`4OK>6_`%gG@CXM%HyA08>J_xf}yxXBd zZ!B%)Q@+L9y?ng8faAS*0*x^>6pvko8NJvFwA43QzSg0x<#X^)`2|;fk6I|_)z?up z9+d7%_Y5C`vKJrqpW3DJXfT#8G);URLTYxEG(0S|mqDaydW@(!g1U()mMUr?Z!v6o zpF(B{{*e%GnmJe8g|2!IMZdzgg;#_=L7{18?|%iTKcTn^nh5GCa2d)iY%_Hjx*DA^ zoP9rZ6#IimrirgLO4nybqYl4zeE4JH+%LuXV8Uk3W6RM+^MO1V-$?Kx&bN3Edy7n7 zO?(hcS|0?{M?TVf;>yzOW=`!($6}cZ3ePC;SxFzXQeJEkkwiQemR{JrwTE zON9x&RA?*EzpAcCh!kIpONyJp>D_N=G!BRIu><2)4#A}Bib3XzYxv;GU%m;wRyy3y z5pG|MfW%FsfFI1pAlj7RCh(V2Iq@=;xQ5TMd}SRvqIrkP7l)N!X(_Gz%H*grCBMuk zX8!g%Rd!k=`O&cQ%b{eI_0qfO#@luv|8q$GQYgy|;S)ryZ&+4XgZOP*|mgPvv}+59PdLK-+L`B;j(iRJ>=T%3Ev(-G=tx(2d?Ae%-Yy^OlH#kM@q= zXl`hB3QF}Gx})lR?&l?Z>?fJRsZ?j9$gc*6NQv2*?C~K zFg0HU4fGCv^>WqcBr1>35NKI@oY}QnRk^3v3_k4&C3#*c%C-=gJo>+B zhl4Q&y&iMX7>PljfH)Hh4Id*Iey*AXvug}Gt4X3`lUYp?&1#b9I2VJaoNP&wXl#7L z;Vdf25{*r0JcUIiX`(R=L`$A%Oasx9C>qm1bXydN#=VRmqVE4=Ai60GMBj>zYLP&6 z(3pJ$4V6H2cr&8}qH#0h1(2kHXxz+5Km*aZnUR17qH#0hMgSU!#?6dOThl-^Qs@b2 zAR0F_63{?2Ze}FVC4p$%%t$~3(YTqBfCi#*Ga~^FM29ysN+23HGg5;orYQ_W4@0N5 zNFX{GT`&N&L8XCc+|8&Ba(6Rwj=~KS3`FB@Mp^^`Gn`B&2yZ;ASp(5hr@=IZf#?ln z&Pg=9o6%<=8h0~301nBN2BPKI3WpiOF6o&T2}B38vLBKI1JSsh@fQ>;foR;$$krw$ z5RKaze+P^PqH#MTF&c=*?TlXqpn+)I&PYH5(YT#ayM=*h+|I~4Z4!va?Tnv*Tf1or z1JPpu`V2&ew=+s0I=r1x0@1jgk*z35BQ^?a1frY5K=gj(;BIFmok?jR+T$|D1)^~~ zV->-a2BPU_@LUZ<4*{akk~$Z|^ze2DI|?&X8i=NO8fK<65KTpqFPi=->Qao1@h?pJ z$2jKK!5E9o7tOIK&GAO-2`*vMGz8l(aG)^N6xdT<)KlPDCjA-csh+?UCQaX>h7L~9 z0|vQ!8WXaB(?ImqsD}r?;*_0~?MSk^zzlTX+deS>D^-UbO0Jiu|7{C@F3}6co2CxMP1K0wD0c-)n0JZ>O09$}CfGt26 zz@}S90c^p90c;L{D1gm*EDB)rj1mQ~rOqrf<8^f5Oa`#M0iB^+B!KN*-}x9O5eZ;> zH&pW2P93+PvK9(Ia%o_MXjrhP|Bxt-Tjg z95WiU_FmKzl?1K5mvq9Hptbk102;LRUJ*cp*4{4#(4e*VYJtAELMpS}s$yUHj`K1!cvy65v{NUdJYV6~^gYMSaj(EK^1Gt~@M zdm5}JHlD_uiLYpAsu`^IG+0e+Com(5SO%*-4OSD&Q1tu=+fWj$mi`Ck$PlY-Zzu^? zljL-uFD)a{CNz`;t4VhUXdWn|GYuudYLYw)^c!U)%Bu8LAfEvLc^OUFt3WE@747&I zJIiUaUATHGXl8|UcrrVT;$8(Z1xDEqm_tIW)1f4N`UIf98lhRil)eh&a-eQ0Lpv5F z>C+Db|3pM{OpYuxVlA%%c@-!=f>C>Ph=#1?O(1^`OWmnsw#2MG#H{DEVtL=@EwL4h zA9wr?BWjrn?zj)f%jG!P@gR=(ljFiJiw{P=EM5*zM`*A^$Y7Pteaf2LSsVwolmi~g zssV8sX(L=fychW7$YMbJLR7km6;EeH+C_iJ-Uyur#2*07^Zy4rl~85YMUV%6HhTHzDtO5HpK%VFxlof zqosMofgc9N6P%I~AA&<72BmVeQQ~JTrn&+TU??eCu)Tm;zMf}e<=TsrYjZWog` zvQiIxjISs7@y*I|xiBQlU!k->7GPPpO8ys{PA<)m2mdF+Oc)i`hoaClvI6tq>npx2 z555hh1*rxN2h)MJFtv1vle;C7`*oBixtpc|AUpnjwA$a&O8-SLF3uD}f6J5$1e`y5 zi(?RGu3;a4e7z!9C3X!#YBTz;j{i!>AEsGSVw$f& zkK_}Oc#w-`90_gtu>(g}mgvD-5!5tEQH1v*#6^>&Xu=o4s_dkw;`e{tE2c|3(_O5l zI}J=z{;j2^E0mkAGBO=3iI>{e&`;Sj08N{NwgFmddK`3npAjpQxmejZIcndvD_VCE z4sOL)ob+VynonQyR@pAv$TWTpxP37Ip2q(Nw`ynf6N8T(V5Vq+EMx%4|6)_eMbv)- z*f9hYuX09P3P~J?bzF#bq4Xrj`#5C{-gCC2jdSnseR~M?V#K+Qi^0_(n&hhP5>-8Q z`>Gz>bVqAcmFvrELG&{>OKga${`gC)-V#=gj~xw8w~XMqAMc+vbzH{&9(68poTf&c z_^uss)Tpg|kyj-~?H-O6lbd|(z-W|~(#A#M8$+q4i(9j4AXd7cAusTiN^)*yI3%~%r5wB6Q10B{RJHJL&V4~`y_}?*6^?8&Q$br3nk^G}O zxL^HS+ygSYHA|iL`B>!+H}dg*mJQq8wHNQ4Z@F05o)n9M+=%L^-Sk zq8wHNQ4T8s4ILtfl|YokN+8N%CGh1rtkhtNX$eDzJEIF*!yMK+0NS9^&>`+L*9N&9 z*6)FX3mxK4b6Uh?HFQWa(~#BBq10(GEn(`YUogot#Fu8B^*4{ z8s@P2APjR@`M?Ja9U_O7t<}(BY_Ce5{h}OJVxk;Yp1-0TRsvBDDJBl9kzs_!=uT;<*<^@WHofy071ru4w1t;j9^wn zhx9WO%WCLw3J`^stcDKf6PTIR&>_v!Ff*&6Ln?~UVe+@AOEGd|#1|&{I*$2uFb1dG zYR;l02O6y>xP(cHSs_8-Kw+vWu&2DJr@*st4+>;aJ%KAslD{61$DugeV z5n-f@kGLGxg+R__=rEPaw1wHJdtw#{vr~myfhT?WDxY{p5l% z@lU~j7<)A_@uDooYZRqROiM3D5TgoFx++HLsu-oKk|`eu8L8*@i=SQ z;TokIu2H(-8l@YrQM$U)C|zA?l&-EcN>^7JrK>BA($$ql>FP?Obc0HxbO6SoiSj7` z7#{*{049XM>_Mebx_QZjM(JkYRJ?04ab9W!4icJXDqV!gDVB&AR0&`cnr141m(Vm* z0sMrfnF-3~>Jpk}dOJ{eg=mvwtS+HxrjMia z7a>OY>X;b8Xk9|XbyS^+s(&w|GfJZIIvODdovZN`74dc&^ld`ZOu393A7WJb`Ha{l zG-AifyM<`XR*cvsu1nC_7laYJ#M9XmK_U^m#7|o;gB37SNW=~=?(N40St53Lc`wJ0 zM(h$V>iIz;c6fO&<=`}12ktKmpbnuj?5}IZ@3HUi>G$zNmT|(1LmjQKH8CqFX zmOS`?I`WZclC4Tnrjn66_Yz-XuLbsokc+7-dGGdrHD< zgF(p*)gpD6a4O-onLsTpLo18DsC%N{N_@?(1%){#BBSPJAtP*wE3Aar?AKT%GF;f7ls|MgY&GQ%)eV#E-A!5hsld8@!XtHzj^2mv2fr-BExo8IM=xuTXRlrd*L-O zmz-1EQBOg}XU?hZY+@bFIkjB^XwIn}D(9ev5zINoZHvq~CF#YeyA5BDfF&o(Ba>s! zsnwj*`%(I6L?h_M%sI81bNXC4%1F+sEzLQ#r8%c|X&qT;&Z%9Nm(eddr*^pjjXCC= zTFp5fi@4f!e7$!={mi7!(Iq*j2Lh?C_m?8Y4D@!CbJ`bCEJVYkji!k?r&g0rF9rVU zP=sk(0q$0_PQQ)P?-ViY3EaQJtWzecz8+#!JlmmX!Y1aNT1`6r7)1XH3Dw&wnRIH4 zNvGA&doaEwO>AP)snx90@j%Ut&~o~yWY(!IW}Pkrn#rj}4%*6bi(m&^%sM?eq9Gfl zG5Z`l86M*W8UH#&gS=!NEp3`u>Sa;|{N0a&M_KjX__2asvWe(I>ONGl&xg2ZG);m_ zWZ28`;XGoM_`mQohJCbeoUWW9vtjy>l&J+|O_n?)#lC{EWLavZ%jAeZ%fRPtAL;uX z-b@tT8-*+JtzpdcVHBFloFJIWxdFvrEk!NoREAliMP`JNG}yhg!!00s{ca6yEa0vP zAH3-YC-6MEegHZ~6k`&l(q}y|z!~E{!@GAX#|$H+sn2PW@1)o`+!o)?O-~J&r2P9n zR_3WoXyB(M|C({GB%4AK*~=tMhB8O8uLr2tOhCW5ORJ_My#`QPuGdz|9T={9gnA9k zE!XQCWqRESCMo}pP_KWI!yV;%kwo;0BZm>kQ|O`>zJQ}&e_M6>r&x-%YJ)?U>m;_i za0r`0Td8d>F4JKwDj^~o>X7e&4p+$35ZQ(#q5~e5LK!1DlJ+6fm4L?Ise0TV+2(eX zmg^yXv`=Ik)nnf>J=7N0hkCrgJ~}?qV@eA|?(`HGFa4H^dm|Dz2HsI3?y9~RZvyak z8l|ScxG%+Uy*Hye?3&cIH1teiPL~=r9h=v|xa_raL)crR_@L5MH8Nhj+49_YuYWe$ zJC&2on3)i`4qx$h`eh9-kt_v%8oliVpoTQl4aZ3fF|6rfEEX%`0Vk~8LA_^#iZ?L5 z@l9>j31GsH9e59=wUmT--t-6Y-?;)m^(*m%4C;S6`uT+L*^kKW3mZu6^{)ngD(BCd ze*iwVrbO{BI!KK?8KH|=+co1keReTzyJij#r9ods6>U(RG^qKQPG};2+I$;K-6@uB zJ^*d$7NZVX0b@3aF^4qMh%Nf;eJCQPAVV<)p1eYRI@s8UQT>|$dT&!xtVR#;V+Rhb zg(;{zuL9J5AqQLgYSSS0`~TfrTPp{j_D=Uf*vS`4)Q{lLPW+k8pFQ!@xtvG)@n{W? z4#5vUqxcfz9@g?8v4J%i?r&giKGdjz95{L!$c6sWLr%3MCO5e(LypX*Y9Lqj6=dFW zwr0xeY50n>SWbkYvzQJx>{T>-8~)9Awb|h{Bs=g;l-9C3Bf2xTf!6~7eu{6ztBdD~ zOqZMsI!ON(;6LO4_WPCFe$qr5@e7pJk^#8YJ}Uo<%}}0j2iHMMM_-ga5~b0oq6R~G z0^b9rqjsR(Z+qIATn=!8_|bP%kM)rrFM>99$|R_C*c617q0amm-~49}Tfmcy*FR5& zuRUy~j>U)iQj8wi!&_Jr-aPt!$8)&jiIHoMXdyM#H1U&AK9h?TDzKX)KFty1@vZft z0{ce_tVbu<{SW#FY>duR1@_+*DlmBMmg9^)&?&HD@RrSZIO{<}$cXx!=tN8`(dS^N z&tmAq2VV~5!tHESc}cF!?9Z*vMB%5~x6AgXg!pNXoz z@ugK~^n_Jco`kCZ4Wgm5S*5oo|&3iP(6j45h-Z*rw3t)Utu!s$psj>v56#Zw@U3baN%u; zhz|pTkMl}YTR)%4C_@WO(iI59_9Tl^)-I^#LAo84$8bNOGGk5)tO>F^3Ey0&$z*oU z`0$53Xp+;wCepN`u0(Y#hC_m2YI`1SX-A7IMS3u;U073Q6`BAxY(jhWf@-#*I%6ws z74{M}Lq%w9t?h0kZCwnF*||NDX^$26$5T`DnN?3hG(Hzd`F zsj|wYRnywp!d6`_6LvthXhK{AqhsY=lSPa6V0$$xLcc3SK3Yi=U@%a1Z#q4UUR@o! zT21JDc*|$#2=IJ#wlqWK_$;UoJ(rrQ=QfmdKzlsw_%G}2;xM2;G;8O#6gz*AxK<_u z(Z#VcxZaiyoz`jvVSX}c6NNLyaoHaF=@5&K(jnC$z37N=vdq|JaK5fm{}|e%!$x{O z>=>N#4mOr%1RlKcPw<(h(0JfRy^v{MCc%) zbFn>T^H6%n5tmV;XNyM9m3wM_DB4g_bh$!F@0Mxmnp!T=&692}q>uSU)uV|r8eTh0 zlu_f7J6JKbgB8;{s4^jG#5PMygd;x}`gT6__R`jIT;OV8G^BO%s|rn9t^V)n{EEp? zT)2Ihn=57Jt_?d=W?^=w&Q5iuYc20-ZA5W)g+ARF)Ftz4Ieqz5IK+()XjodOgWlCy#Fe(54pnp( zFPy|MDRbh9X=h{lm6HsthjZ1m?#8oinNso6u1|zrpA5S`wWD>?MYCkeheGzttdygG zaA`10&Mo0|EbE1E{;Qy8aRQckw9-v4CFa0<++#{lm_-B95;9kpu0YfbVvUJ7CB{Pg z1fr1G1EM(J#C4tz7X`e0x1%AVk(Ne%ckXxfGsRxWs?~F)UA+-QYka$%(YRLDD`D%~ z6P$#%Lso>{<&Y)y3gH;5EKW96+YPtsaHS$`;oMjg4v%OsM8og@>+W6P>$<8t;d9T` zlCCVt*3OYfgd~HK)3CqaDiSk+ zDS>UN^U4{{t2KRNkwEb@8gx4xQRG?boqg~rw&d|(H+t0o7@RHD7?n+#9w6ung6))V zVNG^l@QL7lKg)eXIy}e_TdP3*!P#ADj^nm4kTn1cJTr=`lAqqDkF{|ZthIwh_0^M6 z2EH)oh%m>}8%h&qHnPf}vzXD+(af_tC*S;lVu3>{3^d!*ClLAR1LQEo)0*rS6cyU= zNoAhE(?(U6NWT?^|4V}`$HOVJT4OGr2p3hBi`pw^F_^~{gl_ZgPp-EhBv=bGYk@^U z24j|3Y!HwDi#bu)X^GXRORZt4kkAY<$37!a%a{$8v(~To>=R_tAZ=P>tl@)-e5vCO7cX1wJ*2F%Znl$Q>_uXUF*y3z0)RK1!Y}uw2O_75P^mkt)K66 zSQXD6hYSpa0H?Rtl6P?4;u61f&CHJ>+CdY`jn$gc88t{e7`0r@wjzH8#!xja!t`HXmQh)@H*c(b_8wqiQR$dCFl`~+DUl;y05 ziNASn#56{&Ol4V_BGFgbA|o18a2q*_osgs`h)K++U~Z7rkXI4hF@%u2lP`ui5VI6` z%FJ_a^l7jw8Jcyx#1$+oGrR$m!l{m85z7)^EK4&HzRZu2#VQW&}w(!no;R?^8*A{~^hr^;|O#G~}z}Kgci6Fbp^O?rl#rYzD3w!}wn63yHO?N~W zPn!j`oJ)i)BxIh(HcUVXoToj)_S|$iT^1k;l;U!)$26Cg9S8QG0xqpdU!@14#(VJj zlhH74gAZbsohHH7!Sq$9A6!~TFPn7-Bw9q9voq<5pIn(qKerfQQc)0|k0W8VpFoD* zU`z`S&q`M zQ7iL#h*^-0s!4~k_fc+*)jVsg=KG49`^=EpPM_$-Va<`2;|@k8X?dAoG*odvW&sR! zAAA@LCy!4NUnT}8ICwsb09|m;tTpLQ2hvY>v6T#&g*!Jlm zRvket@w(HR@S^0Hx;7zHDih=^oaV^g~5BY6=H;}HY}L1GhFtNHR8mSo=`h#09m zT#$DPHY>0sVfqfrk@|fBvGWBsow7D>lxH=n5#ThT3@&`8S56Z~UjK@5gi)9t0a#zo zs19Vm0>*$>M?DL}9OFr`VNWCvt>WW{Wz*D!muoZ&ct%P4-j;alK>;>d3U8@KatiYe z)0Do95Dshm~Lbq zv%1HFqbCQa>zNdMTAaH#y%*_YUpi2fv8c`coTxnz^pTp(gsON++} z9OOwZ3Qyfj#m-81Tb7Eh2w-24QSDZm;bJ1NPAckFQCEi$x>a> z9q&)qjt!z6x29`Htns9W52CrYXV8v2JdQKkacA(B84xGA_?{p*BTa*oz497SO-PA&mW(PlBLlX8RlnH%u8 z#A#?JwF6FNL2!tyesyg+s0HWv8ZPV@rq@nWAH_yifV}4|P7mF9QQBB=s4uGk=bAx7 zpOnk80$e^>DT6)|iO&=trulwp&euZGEUN#lnhH9Viw&meaS4igW|`@SJyYrX4=X&5 z0{xkzyuES~EX#^=rYIA8r+5un*R4EkkT?kI=zB6MzSSXB#QxlD__Llr=NSh(htNAS zhl%SX=J6E{qsN=Osy7+KS|gldEuSdSP|W2`H$pE%LSR#8`O}C2tG{|PZBTzaZJ}TR ztIO=enhCW+XBU}G4n620LhbmTyot^<1`e4L6Q$`RKYqTCyAq9x*Oz1^3aiZl3xEaY zeb~bpi~_bz*Sp(A@9yyR?oJ(`HvxXAi-j$)YzDo%dyB_u@ZG{{5#C#H=iXjXi~B%Y zk2|4nR2;l+MJ;0E9#}TrX>9U}Vpe>;-0U$kz>eelZh=zb3LebVKn3a?vi|2`k(4xt zv(>9*Y^31#Ca+Tfao$pT8Ji3^%D(5Z^k*F6A7@RAzYS%QZHC48G&b-B&l(O#3JNe@ zNl6zjRK?35j=W2N$1U;sZ`HQ^XWFFvmohi6 zm?QHEpK|5blwUu_#dUT*W9!?a#C{CZ#GkvxKYW^0_#~?E8CT)svce~Cg~t;<_3QQM z^*qMpI^CbJ?K1$$|HJ-2$EmfTI_TcnK@ zchd%s{;mdDvYlt45zRzT(ow!##_oPxUZhQYxj;Mla-P=mWy(-B8oMB6E>kT-W7mjh z3N;xWzBMgerpSzj#k8NVO-4nyFB*m6x+_5}246P3 z$E8?^QHp2Ib=MS=>GO(VTa+?wWGGe5#jhA%K`GWL+}6@yTj**l-$zsK6y~3q%^yN- zv}lSNE>j@}GDh3nN6KjFW!gGMOTd6*)CdLEMEAzd(&o!l-h?vlrV`rXZYt$*HvmqT zV^2}!HFpV=?ryx8$~t*3^kqh!9A%x{19i$e+34ZyKjpIKYYM;2A3=1#!B34IHrm&^ z&e3_p4o}ki= zLQ($8;_(BS2w$?dW>00GzDgz6DQ}GC)X>@&sX9gXT&6ORqK(SCXl{z~8@^4Et+5@k zb2R@t6tbvl*s!{%B`W*O>|EK<({YfU(uX? zZeUIe71Yq&U(uozt!|*mn`4LFnqtPJ>`}_^rotAg1d3?9TrNg3o9F=UqTRGzE^}L` zq=n|yQ0ZlEXyJXdwOiz)NknFnyDP1s!pk%-Max)0N3tg=Kl_x}T+Gf>H0Ln$N7<8< zKZ&#(N;Hv?%5K`r-Aq#9JE`D7z0FzOE#?FxG^k3vbYTNYRF^bdcMHB;wmWXXus% zvE-(*nhhIgHPM^pj;9TGY)>Jh<9rdH2|g90opn~~H5uKEzq2<-DiB`|io~&YFCM4d zCaQUo3Yw_7fh}dlNtU&OrjN?}rG%q}&D=IRjs`#sGn)-m48_w#(3Xw8w5*9r(86<~ zsZ*v%G-b3rq_7V4q75|nVeTm(>NR#DIxCiny~+}_M@9lJ+k;}b7*&)qcj$~IldeH9 zC!~0Dli_xhYnI|YxWEkYoh^RC6u(J%nDHi>-$F~9>E0$<*!Cz@VnzyLeRL0m?>sGT zq2Ui)qmfS~EsFVmD}81I_9txHpBTxaVwU2l&~b zDVolX_0s$%8Z|9c#4cZ|yNKD*}(2F;5b^Oc?t5hiM5!$lXyw zMW<2ErC;YvaqLr*A*KqTmn$2$! zUtUZK;2gSu4)^k(@VFNW> zr&Vn|GzVUI8!KsGlY5ZLAX}5P1-hdS(|MKc#Imb|Hj3brffR5Qm;%kr-tNX?T6mob zuhNbu8K~^&X%1sok5Kspt@{$q{~4`)jq+cn1sAEPnfAq=^Qx#BG*rumB^<_+>@g$i z;4r?6oX3}&ebr1ME2UXc@addpie081%s`QBmIAO5M{;aU*&JoDmJqC>lY9@uh6{J` z2w%@gz}QVj?{ynsq=DXoXV5fU1aoK_1hsHy-H<8KW<3G8jUaD%QnzPjOG#;{841%ydNg8C8#m0awgq!^!74dZk-onJ?1GIE6 zt%>F_2Jo2-Fp?cTbIx$TyFp6918&iYijH1HQ=9u|rV3Y(1lJG;_z{ z)=}&s+7Ig?NE(}!G7bvQzG;@9kg;ZlIbw`QEIW4gA;xhrZXmC7(ZXZy+EOZlBRk2i zOWtWJg!gieipQwhDwA0D0Oj_$>&hT{YfECguTtrCDxRPsAOqQtP0(zp))W<7p^}1#OSPSA?|+ov%R#snabN9X8)r6OH=@#7|lF6MhmZTxx(|bu7&2JQW-q8 z7TV917EV$5Rx0{a_6tZQ5DA%YEt)gDR^BA88Xlb^90-yu9cd3c?HAHh8s3=-o z!3`Bf^FVVn!u~tRUcncGD9pq(@Z1(yq7-?TN2$D-iVhki;9ef2^5>~=62k;&k`}@h zoTQacQJLFRLifH(WiP_!taI;;P(}1Xq-ftK?y48y)Uub9>lrEG8pVlf;7I zo~6_Q>Tw(4`n&rJsf@KrBpc3>QS1;Kc003F+rW={mCB?2ZA?R;Qid6e&T+@N|CH&! ztc6{SGIwt=p3HLB;mJ`t37@%%PIA|s+~*{XNY_tC4$uhqz1nT!zL5v0E2E+}oAHC> z%~axcfeo%vae`s17&(cBnWoLmxtYL$iVGj6`QuawR~{a84b6wse~zjiK~KP@91Ib9 z1YSdT^ML}>xM-Y~yQ@ud5(mG9B?e9-Y~s{Frqu5AG(z+hE^EF_ zIv0jN*R@M%bD^f70ue43AzuOS0OXHW6I(W<+05N#n$33&M%bh4C7OYc1uT(jfGnZD zRA!}*%EeN-P->af;Ivw{S7b@bE>iBKX%%%%B?&#yKl2|tUcs03;-R%s8C2S&xv}M6 zmAh9>sDkp^t8@xvXG7YxK@ z9yBpO7KMR~>pZ?Y&krStldzw%t4+87g$V}_8U!}vGF2j|gVWVR#(&j?mYugR zjr2dctobt1_%D3x=kx4Rb@T^L1frFDgOmxaT%DIZ)Pus0aP{BMblip+EPUZC#hzrp zCHE@L2GOdBswwX{#U7)a&?C5c=27lrTzkhPxnDyRkEIUpf6#fJTNjD7BFp)g@1t1< z5wKKHUNeD&c|V}+o0R(~Q8#6upvq^6e!v-kXlxnHa;wWAa#vWL7G0qHtMFE?Q0enD z`y8C3TqG!%F|SfKre};G{)O+N*t^JmGtotD!>yT1vo28K4~dXrj3QfB097>|y%Lb20SaFXVOnNkE&B3Ux~64ycu zi86VElrw4k%g9N*`vX?GMmfkr@Gqo^_;N+yFv1VB7(*~~CZA!)0P*&$=q!w3lJ0$m z?rWkV#4rt%^CZokq#R?D^c(p`K#WBK1Re~&=YWHLj$pop)=a{!r7Vtn-0avCE?Ke5 z{GA<}C;8F~H2*{F&w}o;re-QT$$iEz(T*m{fk2_Xtuz~cQ3EYRJlxFR(-FB16ioC# z6AC#WqTDZ1-a|~VW$&l_b2JOt0MGy!K|*H17eFRxlHPKO)}5n=ny46R3OPwL_*ozy zIN=&*3##ur3l>CZ_RnEup$CxjX`<}Ry_&#`ra=*~l^gwSUzcf_}FejpU4-;xu|S-DReuizPOyQGI9 zB8X7av#u~O=Y$clNty-KfYIX?oCvwy%C7$$B(`r5vUf$XS}wilBvqZK<>y%-%eGP_ zAVxTIw$YrkOsLBgxxfZ5|ErY!b8_K><{}#=wv4_;(QzIQeU!jf*|9Qm&t{?I4a>wdfKC^{S2G18F6gdUsBxS?U_r_XgdJ-*&HNnik5QEnZ$9gvY(Ru6@aBEAck+%x$y-Jmc z65u$4IgDplZN?vseMG`fIQB1SsodB}cQ> zYQdtiwvW0ii)=&tU$EtJV;3{Dw4M>leTm~%G{OqXHWFKE+gSesN<8o4U|~jsT|`Br zYUavGTe)hc%GGI=w;{0s(}@hY@yR4l;>PybW~#q70}eH5%`C5>f|u+n7t_N1Pqu}c z3@z+qV#d>2z(P~yMI^ziZ7W6JG(>e5+lJ!*bvi2hu-3=DWk=ujcFFa?SET5_I8(2^ z_8LWAi#-LFhihrPNbWC!m9(YssISomp3A%#B4ff#7V_o{Qwm|#nHR6SWnlkzW(oV_ zWY}BC_u5hd8A@+oC z&;Y~Kb$KT!Js;i&A_lB$#|{D)bjVRpq(XuW^tq95u{`?>En>lQS0kGM5d3^2fx1iM zMXF(7x7kAh2cPfUs|+$h5z@3QBAljOAUey9Jt3Pb+K4X29)g=at!Iw)&@&R;6wSLC z+x9B0n4;2`X$5v#Aa;vYGv;N;ZN1D<;PPr(33Ln2(`qb~a`zB|P25DAFd)i)nrJn` z@~x9JH~N{kPeEt?wd1ae#F{4ERbY^;EHeXkPbDj{>okuOlcn$;-~(PWbBJQIF;RA_ zW%TO~CV%M^t)DcLuaE7{%jh?q$P_J}qMAusdzI~ZeFL_>o8D%j8M`Ah`fVq2lL~Io zLRewMiznEFSOIZDb`+iqEa(b&-Ly4`g5A9t{TibH>eLAT0fiWvwwpPl?~b79byKu? zl2-zDHds8lf4jpo`ep7PCK~;fT;xCui8y$W<*`b}$4qaHEWODB4}x-D0gPTXDQqm@ zuH!r4XjEyJEX}mhdY%GVIY&kdkwQU~6QXoa5DB}jH2M=I0(zWxk``T|C4Wlu*oZ{3 zUzMzS_RkD1Xwt-z?~psY(SS7oXWE%XvBJryAZx|Rd#Gn#DFB|F-&XO+IX3%_)9!)qf$hx!W3f}(`N#fR=fW-x)~Et{Fl>f&{;EKzYPua`rbjBCd&E) z)_H?lvbPrz4PFPt)PN4{LO)q zM6BG|>!Xym4JC`W)ke&huMLgixBGOXTOvHU$OX9f!F+d9(FtDY+=5j$Z1;-GrJh&C zuTee}yh(MScmX@b^geb+A;<*?Ay%HC4Y(A={@ggdOCQE%k}7*Cj!QLi6a}#_K$agt z$^!l@G6K9p8971QW81D0_GAq6PKOBZA;H5^ZXOShoACI-DY_VO?<>Z#ZObGrpJKP5 zm{#%4>M1(HH}!n8af$}{W*aM-9aA(Jkv6eR7ie>N3^KKHidwj6BzD+5G~+=g0ZW~n zDM`B>W&hWnWg^Vm$*I4ijCsTZYW59&8~sO)#T=9d|-5e9Zm zqh{pnkh&1zT-Rm=$p< zoC-l2R(Ua+wfn>oC2g9b`=RgIirha%nA&x*tyk$yQ*_eMdYfj3kqVX;763PvGu|S9 zn}I>jk9f%DYYMd-GvrJ7;Tp%pZ2U4}XSbU#VAFsZ#wfAc%N|u0esUTs>v^J|f(4%W zYz)a0XbWtZK(xdWIkH2q_dRo?l*#2tX#t00Hz%d04yr0APN+Z_rvAcO9 zc5sI3yQ^v2WnSKSo$8D>#&K^At;ft4U_(|5ZGbvxqQ!70)=$!AsFo*a(_+TSWZ15X zqu}jDsQ~Z7Us(Aet(OaYGc=O>5VmwYTuLlhdT|dJR{(Be%}3eoxd&l=EmjrYPUWk3HKQy#2Dg$6E;imR3UZnb z-hkQrfr0$VZ46{pwEP@xgpP#|1W8=gqBo1to6sXLls(>13dT9gSqEVRDMAsa2>~J0bI>$Ul`GX ze<4^~X379!V`%$Uh*3HIjyOW@UkgqL(}}kTB>W{@(l?Hi*(zCWI0AIy<)%BM6INQM zEkQ8G_!%+TGc+RQ>}d_&q1!Y`seNVr39w zQ+1JQd1H1Ne;MTweawA4B3r;T*20=hbk=zeo0dLL#fW2(y>@qECju64k#mHW8)))?rRb-xyJC@_qL)7 z5WUiDSX*qoMR<f*OrP^QYWpbFtV3FQJ@O3YOB!-L_(ye+?8l zfhG37atJbQ*Js$NycTJt*|C#XuADvlB=)c)A@?GcjPr)}!kXAihJ{>Qc^9VpDYJ_L zFG_Hv#d(Vl(LI-d%{kiheoP^h7S>+kv|dm>vF~61|QheJlRthOzoTD`1X3-3t5WvJOj4HF?VB~p(q)9mh?uzziI|ZtBTka1`#%vC{91fJ zP9oX&9ix{xMN>D$Z%^!a(Rznm!oC3+{TQo*^;1-PIr!MV5E*?o!kPg28#s51Vd$|z zxQAC&a$gD-x9>|vKh6Dd{F0($;NfnoT)0d75@qx<7b<&!=D);E&W~-SHE0~c9hAFm z^|n-20a~@MUPiy=qSZYwQ29;TJw|i!4R`k@?SiWX?v7%PIWWkI8Y0^lOcx;Phqq7) z7KJ$|ILUfy-$h!Ddjw+es^(+EIUBDAx*s9R&uAeu#k|=2Shu0fYUB)>Xv-z$R4mah z!;2B9^1Q8bU#Ln2s$gw!^S3zEE5vT%*D1dgpr0Slr-HqU;%VQm_z<1!vsC z`Nn>GhG0n^3w4vsMGKy!yt#Ly-~Vc#s~JTgdt7YvqRcM}FMX;S&%EzNuK?jbwPNEGtj zdP%KfTRt{m>3})wVc$-UzG}9hEW$fwXl_1NsTOmmOJ5IWXn@y8?CZ?Y|H%pJ@*7+o zyFPPw7!k*=oLtww;T-*^Ohu4>Vf}4idydZHFnck!vBAsyGGUsF zeFGegVDl+EB-r#MKKg0wJ`)5NMQ6Rp%z=y_R@y634!^+u#8)89AHH<(NeBnJlChZ6t|4-Jkc zEjX1N!Q=M+L}z<{e@A=gDQADOuctR(4cLGAKKp|qg~VSNN+@O9BxnbIwQ%h#6bJ#sl;e+Uw6tG80s49PkNnq zkF*aYJI1=ZlOxl{rbA8+rbf=#&ZWAC=aHj(S`Q3%4>>&}$>gB+)Av}?IcT~$qQl+m z_hL#bO~=Oix}1Gn^B@pB%Jp^+fy{$lEg<={E_GC??$mIiZ`j#2G?eNeYVS&paGOW$ z#2f*6Y`EF=Dj5g6ZG&Y#R z1D%Cr7Z4rq8%d2}raQnK7B@Tn&N1Z!4~-x}Ov8s84<6X#^bPi<5;A;epkpM_)rXlu zmR1vSMs;R1} zsI953t=UqurDApR^l);dZvgaa?_X1~eqeBD{pOmw&70)sz`$wwQP%~DlAqwj^^Xk= zCgpB))c;WnQ2R({@7B|sH?H4UkGoDVTC#tuur=;Shq_biyD{CViuFA`T^$wc57$(z zA5C>_?F4e`yV3h_9~$Hz9bxV~%QpwKL(8zkosHK2PkqkYN*n9lY z?qdfIA4(*g*1l9{Z-74x$Bw2(I)~3VZAbSUvx=c-I3?3(*jNc`8NwH)2ZU`{r8uoh zT5|*0@!GQDqAcEQw1#$M=XG4?r;+8cbqMonp9o(YJjVvdMw49*b8#P}Jis0f#2MV5 zFobnfAx#q^p-1DM1AB}(a$nreu%U^dwHLa`_N<5wcF6!n17tgdnYP3ZP(?aWYB8E; z62m+e70zR4hCwFQTNo4+t|w1Bcefrm^uST5<}Mko<8gRrZ~KT7#7(K_%cOKFg28=F z@7|xUfM|?@W?g;li4-FhniK8AS0fL$j|}$VwXMd}sU)PfYfs$oUpEAb z1$s|=s@-9&R96eFWXUq>?CKv&dO8qePL7OY7J1A^lAZnSeFN2zSc0c+t?(fqP_dgGa3w1Sn4M%B?wM^VXQ z48<^%VTjAsI2k(n*2gr-Q?nT}b9~eg{FGR4!=@G~h4~g;d$U%AwX8p$V(i6jW~+FaVUHX z8sst&FP8i>hD0838VYp{je!*t6Md=PL7 zB)Z#K`8vZx6VPCZq3%Sy#u+mNPN?T$OjFM=o^_8VVXfPtxJSA`$*u%+KG$s@8BOAM zdk?y5XXbGf5uP#h)ju@YQ(?Y5wSWgsPcoH&p{jKZc4!Va&`Xd=*f;J3Ts$-iRnk5@ z+54Rj<8HR3f)3@+R~HrUDP!D&wojbUgL9qn2xh_BY{>>mOxQY?$;WE6ujss%$! zjU)#kW@s_lGt%CVVGV;#fop$q&>0?sS%ac%?-=!P;3;DSl0Cqvf2c<=0!83`j3)(! zZbJk#B6LqVMi>p*z-I<+bj5^bVlhw14Cw$mMutwoGaMVX1aP`xh2dLtp5ifBstgT+ z{A`*W3o+Vd!f-n`-Z?ZNGdJoC!4hH?CKBx9#0YpD2r(ZBK{}bBK(>QTv60b~(S9Rvg1dM~sb26| z?@&Jz6m!SJF!P^)0_kTWgP{}7xOAOxM%pK=%#S2{5~JGBKKP z`bI^pAS8<3IP9$u8`H&O(781R6b)Hr5@g*(H-}(-kI9U&VZJ!*6neO4#g6T<`#js7i?!FPsKmxks z6y&3?pMN5ck{TK7gyBe?G7oyjz?pbrxNHW9Rt^;b;bC_U8r(H(O2nNZ=s8%_kqU&8 zNjQ)R6hpIM@zlWZP9{(tVtnIlw=z#9P9EBL&_qd_ZKM{j)li2Ej5flDooFAPv2Z-l zT4U=tazX9Gf|O&N;7mPiu`Q0G#( zG(C042cf?>Tw=U==&9a5gGk-(*5md2+xt`K(N1^V`&H|tRrKKXUMUv_>D9PPwlQZ1DeJ+TP!JMRvQP6MfE2s(_rd(#u@{Ylg`fxaC90Zjz*9gazNm{ z5K^Hpf`+7_MdsN6hkC=yjCnlnV=2g+6wtl**4F%gI0(WRrbj-{iwRngCwcinGa+o1A&3(K+jUR>Z%n#{PK8*)v&<9&Q zE-*?364!3D!ZbeJYzwmJ%&ak(E&(B)Q5^R0cr#H9YSfcb8Jw^yiwW{QJxL@$kvlkG z@|~F)r~?Mak=hDqkwY7$Qr~Ilsal?@s85>TGn)H*6@Hrz1 zKMo2L`EO{Yc-N4TepUl!lt##R_qUIZ)^k@nbU~`_DEHCS>`HYc5Ea+eB7pE3 zNuS$VU-ye{$)x^kjZNW6<0ht*X;zFFIO`k(d5GzN)pLONt@!|l$`efJ4{ z*KT`5V+Y|59ZkgJN0D)8?{7ugakTNy8j5c`f_WVUt0X}UGu7Nc^Ig$ob7wo^vcA+A zrtiL%!@G92z`-8tNE}Gq(L}}L(BBvs=TmuLv4(ec*WNAJ zRjf819y|(~H}*P*k`oe}s0~_Czrz7p8M>=kHyX7YswjoP14+NTW}|w} z02t9q=TNxr#;!Y^{YmsAB4;8#ba_`p5Uc)cX$sUZ4#Mrvgic|+=J23u@-8rje{*LH zA&ih z;m7taXJ`<4q{memJ;2KY+{0Z_aw9Ug$ZjzWOtueZ90>yCIAH0qOX2XrIDgghR~>)V z^VbIc+Q?s<_-nIcb+6`Q45JjAK+KGtPLb1Gz9>3*!Z3<3|VC*J(A`d@TFiZKn`f!@>VFBbwQ~Cpu4KRUWSP1oUPXa_A%NXG~HC>E<3Rj|N;7&!gXZ z*HpF!{jL;C9V8Odbh}O|BYpd3%NUvsCSwP4b+;3^!8i~=wzZwJ+J2PmVEK(#V$lI- z**2Kx!h3ulukni-=M0IhV1Nzp(M+I8_a@pqz#Ixe3j8!u9%g0-6G$r~NOBU#kL*qy zICS*b&X$(M?!$+UHMSi?c`2LV#R^?BmsW?dzaXJ|wWRL;orm_cG#*LpJ$(Go9;|a{ z+9!dH6d`esWXZ3nzTa6_QA1XMNk)-U|)hG=rpUv7HzKstmg7? z1VxxVY;{RF7!))D5F4Dp!eHVELKQ6BOLHShGjazDDA=uL#(n7Uk%REqj~+YRialQ- zBi0YhgS|UJk3{SKoktsM6OBiX96rJeuH)#EW9)>c(zTaHJ7(?3TNdzfwFr4d6DF}? z)^j*Xj}mZR9Dm|}{1mOv_ct3?KW2r_C` z4<7f*8^l9pVc9}SL=&_@r~3V%%n?5eCJQaRSq_|_C>!|+rpml#z0OTgzzE5T+)Tq} z90NTg4HW2Ea+-!RZ3D8{dGz4UR$0~%53|iEm3Z8&tMRJa1MzyMEoar*plUmH`&y%V zq%wf`X2xs}Hrud@In&5mGLA(G_9*wVk=?G3=Lp2auOVl zZu8w?j$;vr%<-0?nFh5{(zO_mbnB7CSvnzO&BA$hW?dMyO7coN-0rChIspdA#2+hWWnsTuswN?cA9Nz zN~27ZXK>XS-f(mnhQD#36%69E@zQbtP0K?bX_eVP?gJ=dyZ7{-%c6W|r-JYc*?*FV+HaP}Pw}PFa zjZLgHu+;&K**Adv8k`tmuOmZyFqyZg3H#DI-q8W+N_Ju#*i>s4Qg=SkvN1qeJ4e=t zZJY@Q?9xDctkRQ!|W(?lvy25-L&p$G~=16agh+^ zy7-0=i#Hnnn*py)Dpx>%Kp7)IfXr-xB`C`lvfh=hXJ4m2=Ua>{!c3{`u`ZK|v(v^B zf0Xy#I6B3M-bcDSjQ&)N1Krqp;#mzT+{aZ00;Mwz%0p^ET!>@vBgc9q*13g6soBlx zNiitnj!&1@>kTDE9d$UqjGI@kNL^s1A(aFFUaNtEw%hr$6xjQwSm7j@z-Yl+5-G}0|p!*xLS{U#s+$( z^%#%VQ8rc|!z*AGN4e7L8OES)jn}7JJfF$a#~(1!jz3vuelk#GgVEA#&h{kwfHuCA zf0W?^6YEi!9j?|rtz1Fh!MuYG%G@tD?hEt=i~ zPtMn>4B70)(E%}{B@ zJk5P;yj6K|nPx8UtIG%C#w|7yU=cYgd;i2k_E2^XnXROUyLYksJ?a=L=s=k$Itl~U zo9Sc-cFdzh^)IjrG+n=1st=PN&+u7E%pu$h(V^wbe(D z?y22SQ@uAiIM{c(S~R5N*L13?`vyDv#~}FLuWIZHNmcW!Nke#-Yc%vAP`SFQ3jaGC z(TrC0!jurZY?13 z7z%J{>!&8-iMW6a)0x{|+A0gVm0fhxN(phahFqpK#J18?rokD<3%>Sja+BW+C; z+bz4VPH($fRHngT-|pS_RjkGyt~IZ-eaqp$TFz(N@h9t*#V;?(`u39VEOG2tXgSB8;q!+Ja_pHu`u1r= zW;yhd2l~Tk)|aFI5_Kr{DO>6tZq{!{|06(U9_ZCb)@1ZqfIjtUTk5YPSueQ%<~sCw z5A^v+)|cFyu9IWm+5coojy>byr;!|c62py1jy*D=JI9`Y@V1<<{E;->mtzkg*qUR{ z8Q7ZhDf>6nbL@!$TQSC4ej<39<^03Hg$vCBAc8*-zvI71hnuy)B85Mp@EV2JD*Vf# z^1Br7{JFGa%O6+xju5<4;ZKC%r)~Qo_>;E%zesQYgSPz;{8TSOv-;);ky-nUExeG{v1{K!MRfYfPkGo zg?~`te7-(^QVRdaQ2ECdzO7u^dAk&Mo>cf375>)>f3L!ynJ4Ani~~{l^C^Y@WP!l% z#XWz%fNKOZ^JSecOP{Z3`2~xlJRcv*A3MLTuLyjOfSqq@`HwFac$dPzuJGI?>G1C> zd_dvf)$+ekc=J*z|2*#bQ-pEweC1RM{5c#0#h*0_|NIJpTl}{vym6(#<5JvdRrt8V zEj>FF{*Ma(A6kA$;jgTccFMEl!P5%=#ae-XT=D#f!he6Az`vvI{DHzJ)(ia8v*p1b z1D;7PzBmK?tAIxw9DV9Wp7=L>#DY4`iwcj0;IA0C>kO-$D*DdSWxR(D-EnlJKi?#g^DSU^*ixmDzg|{i(^1qdf;ZQqYK>1Ab z`xU@>yx(mTe3pjVej@uc8m_2DYO zdEQU!eBsu7Qd<5!eNw(v;U81@-zdCF;eQW!COrSw4DbkLE}uUGeDMtMwSe<@?Rm%V zRD8B8{OgYi{(q(L&KcTy+YIoh0cZLY&JlUUaLmWj=XcMi)91t5?++Dj>2r04_P;R$ z{JVe`qv!vWlJ+0bet)L$A9e}6M&Z9y`10KXA6K{wRk952cn-G0TL9>x1-?b`d5^-a3;p+s&mSrL&!3g{*^}qb%L=b-5ICPU&7bcn z{Ll`8|7QU^KLVWT=|u+$|CN@vf9J;HxeyAR>HKr0^M|zjF~BRqSDQn6vRli)dRqG3 zpykIE9>)K}fHOXx*RJKKwEW+ER@&i15&83Dg>U$Xz^y-aQ{f#S6ZlW1xHCIf@c-oj zfv-e4{w!7a@4r*vuL;=M33wqi0i?#*F9kct0B1bCh(jMduI)Vhc4?-wubF`_0)V@G*rygYrxtFAh`qHHG^zs66<( zw!i4((vFp*A1QoX<;&`U2n-7M``tf~@)o`r@FFOQCqjI<7I4PL3$qoUw<lb}g;oB7cL2c)c6h6@?>Ovlxu z<-e=#cv%UB|Ff2_e}}a5Ii>UB0^uKaEg_?ROX8jK7zI z(0+%seApiSiNYPVFP6`LqVRjv4t`nN`4_;M&KH%=tcmzjI9u?aJtFu#VW5t)5^%=H z&&0@sO$zrC23r1Mg?p(2g>P1TZmL{c`FfA``;GTYzib!z^Ld5;{d)xdF6cG>d`sc~ zewV<1N5IZ2fENN!=gR_Du=5XqGyYzRLm$k8AC`JZU{f+4~4UWNZk%X_IBg;y5}o`3%Dr2M-Te!s$NFAJRG zTmC!@IOAET`q}b-kCvaM^t5vQ+X`Q&?f(a9#(7@hxHWd_q6P+2fHVI%A^r2RmjB=$ zDgO~|=f5euNc}V`cQ+OO@tsosceVUKD}4SLf$vawE&>pquZ8M2TK~LA;lHPTou$uw zz?lwS@>AP+RLggT@Od}jJg&E>o}Z`XzpCx{c}98gzZLE!(X@O$=)`z-JuUcKIzOOr z`{#asrR6&TXFRioU}`^}&4E4)zYY31Ta3U}0huzWtd zMDU#bZo%`H(u`BD@QNb>{}Yuv{6#=Bu0!hg4r}?tfHVGKKL3Eiy^OP#->v@1Yuly$ zK863Swjb7GrTEJ>+`gZWmI@~TXF5L{@*gI(yqC?@_CKTWFrB}y?R+m}r@p1-y?m** zQ&Oh-Q~AV!J>btWz!@L=7pB$;*r^2^f2PB6{;P~v!OrKPc$wc?Lw?0>ZD)nb(TG-P z0-WjKCH)otUM(Mv7e20Vn+QLp^Y|5oPrgg=v2n!TDg2lwlE16`mOEF*wQ*d^C$-;i zsvZ1yA$`7B%XfcR%70wTZ_xQYp>lmt;Z<6`@c}8{t#Qf@g%5`O@V$UD9lWHpw$rU} zZ(T#-pV4-{-YM-LSNN9|{$U-L)!ScJ_&3z=8rSmQR(P5E4;>2svBJat@QX^H52}5C ztCr6#7kbud`+F2#r0}r+xnAKvR{LW6-3vJL&yEoPv;c1S$NHA90z?>%ZL5F?`rv`r=;IE>3AJ)q26pKO*<-+4?YPV z8Y{=}9rI3amHWN^d9`|8M#gjdbZ3Sw5&O2GnY(T(k zZ|01-@IhSw+ehig1M*Ni{S)89=ksMeSc~gqJ~vBnn0^wS!0qv1dc5BB!v`}Qj5jpl zAKoXeU}C(31+^u45IPtxc#>h@@W9|U41doBANVw251aB%c?ui?6+CJw{TL}ZiK;)5 ze#lesbSc!GcEFW)T9w(Z>Frx)B+LO<_PIH9D~*TiGn_S*R-g|1{QXmQtN10Az-`<~ z+gO4ZBxX1rN(9U7Y`CKxB=#jVKF?3zM!VxTtFQ@d+AIEz_2%u<#L4=&Ik@U}Uh>3- z%JgmD*q?O!?_Gr6#JBri0()N4_q!N&^7guC$f-`2pkZETimiN^!)XH0v? z0-K~~-V-C+RLuNvLv`MU7qfdXG;WzpuZZ2lH+`#1U_VR7cV|?;@!luq(>tQ)E#EJG zBQ0Uq*)%o*xAQxT_6;xIkS3$_Hib_+Y3z==p8n=vhFz21`xqIz_qI-l(Zb6Z*tP1P zuf=tR!Mt;KGIVA4ZQtfrSsx+0OESY2Vef#+0X=#UwfuKf0!Q#63prxJT zFzrw}yNe9xNAeywv&T?q=MCXjyFoK;ZjRUZJCm^|l!=mov}rrR?U8AgoowIU+X{g* z0SAmG2q5b5Uii2u*-MxRylUZ3P$tAT1@=k%CB5d;PS~+@Yf+-zzT(sQ@DJ(;5Gb^r zRk+!Rod3$^3`e+mFLT}2n`QRh4|z{UCMhg3e8QCfTA8q&ihD-tadCO+1tan~+CTNk zr>ULanZ(s{c_zk@fd=2D=XUSrd4)R#Uiul(v>TB9<23bP&cw;I z1GEC##h#7{Yk>{M`od`73|jQ$j|cgcrzJU1)06_x00IlcbAwls*^)1 zXQmzWw|Nk!PjZjhrkw}L-VGm*aa=k)jZ?ov$z4pRxe z_I{TQgXYGj2haMPff#A8UZuV87ktZjhRXh1k#OU@6Kp-bEkxeQswhu z?B9oJHh+DE)nR^H|*CyHfYlj`I9+QzSspBtDaE7J*C^6X>aw!#e=*3;mave znH6K`?8yN+7u(0e=Rh{7c+D*RoXpV3;?qyf^$k=vzdRb4SmQ6=jgw%N>)oAWXAdB| zBaRXyp^pDuvHpw>#0vHZ-)iWZ_N-8r1N*?_w9maF~h+v zYEit`r=c-epWG3#Z5*9`W|}`jk4Hnav?C9`0hpoIw9{NOQ6l}7eBt8svp=l=?!CM1 zbN+}PvM#48GYT2H@Lq@J=XWC(CVE!7xAXf={!z2Rvt`WbIQEQu8Bp3uA~OXw%=Gr8 z{TYT52$GDw;Im`=<4EK{Syc7CO)u!Un{j)Gm!j$0SB^Fo|Fy{S9cKbWHD^rgOfv)l zHa5{==46D8i)Qd8<^AYsZ&x0B;Lb_b$VoAri{-hEqvmyO=83Rbn0APKVD8~+g(8jH zJnG5o$I7{vnvmf6Zg?r}s6sj0Aj7yqC+r8cLc1Q_#I-UCJBZZ@NecLg=C5Pj5$_uA z@y@sngnFWx*yYQVl(EijAWX4b6PmcRL%f};(K7=$ngiF=h+KQ^FLQiU6>E8?iqBiY zONf%=ajJScJ5~5`R1OY#aH=|RI98Q;yA*3QIFCwyw|8{#pL(!&=3qpxp!qXo`Usuc zQVhnpagRkEK4Zr|7#Zy01ApuarY;1&!OKF_<0U}s1hOC5&x-v!{Mq_>x!P}C*0;A< zW4FBkk& z1aI)0_fLe|5=~DA0@R{JQ)rT$cAMhGAST{M||gh z!ex11MHt4V|BPRZ!l!+^xp-gUvXgkiuo|y~{8+Y3X0&8;`U6|vUf+wynd)yiDfR!% zGKMx|>)9*sZ_8Bwe?2S}*QR&xjn&rwgw_ugb1L+Owd*tVZ_9H#p(5tdPZI+DC*@xj y$CLPH`QOR~ZyWP?&(xH!|9(d5{|f8n;!m#} +#include + +extern struct BootInfo *bInfo; + +EXTERNC void kernel_entry(struct BootInfo *Info); + +#endif // !__FENNIX_KERNEL_KERNEL_H__ diff --git a/lib/.gitkeep b/lib/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/lib/Bitmap.cpp b/lib/Bitmap.cpp new file mode 100644 index 00000000..32ce0479 --- /dev/null +++ b/lib/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/lib/String.c b/lib/String.c new file mode 100644 index 00000000..eee45c3c --- /dev/null +++ b/lib/String.c @@ -0,0 +1,159 @@ +#include +#include + +void *memcpy(void *dest, const void *src, size_t n) +{ + unsigned char *d = (unsigned char *)dest; + const unsigned char *c = (const unsigned char *)src; + for (size_t i = 0; i < n; i++) + d[i] = c[i]; + return dest; +} + +void *memset(void *dest, int data, size_t nbytes) +{ + unsigned char *buf = (unsigned char *)dest; + for (size_t i = 0; i < nbytes; i++) + buf[i] = (unsigned char)data; + return dest; +} + +void *memmove(void *dest, const void *src, size_t n) +{ + unsigned char *dst = (unsigned char *)dest; + const unsigned char *srcc = (const unsigned char *)src; + if (dst < srcc) + { + for (size_t i = 0; i < n; i++) + dst[i] = srcc[i]; + } + else + { + for (size_t i = n; i != 0; i--) + dst[i - 1] = srcc[i - 1]; + } + return dest; +} + +int memcmp(const void *vl, const void *vr, size_t n) +{ + const unsigned char *l = vl, *r = vr; + for (; n && *l == *r; n--, l++, r++) + ; + return n ? *l - *r : 0; +} + +int strncmp(const char *s1, const char *s2, size_t n) +{ + for (size_t i = 0; i < n; i++) + { + char c1 = s1[i], c2 = s2[i]; + if (c1 != c2) + return c1 - c2; + if (!c1) + return 0; + } + return 0; +} + +long unsigned strlen(const char s[]) +{ + long unsigned i = 0; + while (s[i] != '\0') + ++i; + return i; +} + +char *strcat(char *destination, const char *source) +{ + if ((destination == NULL) && (source == NULL)) + return NULL; + char *start = destination; + while (*start != '\0') + { + start++; + } + while (*source != '\0') + { + *start++ = *source++; + } + *start = '\0'; + return destination; +} + +char *strcpy(char *destination, const char *source) +{ + if (destination == NULL) + return NULL; + char *ptr = destination; + while (*source != '\0') + { + *destination = *source; + destination++; + source++; + } + *destination = '\0'; + return ptr; +} + +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; +} + +int strcmp(const char *l, const char *r) +{ + for (; *l == *r && *l; l++, r++) + ; + return *(unsigned char *)l - *(unsigned char *)r; +} + +char *strstr(const char *haystack, const char *needle) +{ + const char *a = haystack, *b = needle; + while (1) + { + if (!*b) + return (char *)haystack; + if (!*a) + return NULL; + if (*a++ != *b++) + { + a = ++haystack; + b = needle; + } + } +} + +int isdigit(int c) +{ + return c >= '0' && c <= '9'; +} + +int isspace(int c) +{ + return c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '\f' || c == '\v'; +} + +int isempty(char *str) +{ + if (strlen(str) == 0) + return 1; + while (*str != '\0') + { + if (!isspace(*str)) + return 0; + str++; + } + return 1; +} diff --git a/lib/cxxabi.cpp b/lib/cxxabi.cpp new file mode 100644 index 00000000..08e8d723 --- /dev/null +++ b/lib/cxxabi.cpp @@ -0,0 +1,140 @@ +#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_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 _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__)); + +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); } + +extern "C" void *__cxa_allocate_exception(uint64_t thrown_size) throw() +{ + fixme("__cxa_allocate_exception( %#llu ) triggered.", thrown_size); + return (void *)0; +} + +extern "C" void __cxa_throw(void *thrown_object, void *tinfo, void (*dest)(void *)) { fixme("__cxa_throw( %p %p %p ) triggered.", thrown_object, tinfo, dest); } + +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); } diff --git a/lib/liballoc_1_1.c b/lib/liballoc_1_1.c new file mode 100644 index 00000000..689a679b --- /dev/null +++ b/lib/liballoc_1_1.c @@ -0,0 +1,790 @@ +#include "liballoc_1_1.h" + +/** Durand's Amazing Super Duper Memory functions. */ + +#define VERSION "1.1" +#define ALIGNMENT 16ul // 4ul ///< This is the byte alignment that memory must be allocated on. IMPORTANT for GTK and other stuff. + +#define ALIGN_TYPE char /// unsigned char[16] /// unsigned short +#define ALIGN_INFO sizeof(ALIGN_TYPE) * 16 ///< Alignment information is stored right before the pointer. This is the number of bytes of information stored there. + +#define USE_CASE1 +#define USE_CASE2 +#define USE_CASE3 +#define USE_CASE4 +#define USE_CASE5 + +/** This macro will conveniently align our pointer upwards */ +#define ALIGN(ptr) \ + if (ALIGNMENT > 1) \ + { \ + uintptr_t diff; \ + ptr = (void *)((uintptr_t)ptr + ALIGN_INFO); \ + diff = (uintptr_t)ptr & (ALIGNMENT - 1); \ + if (diff != 0) \ + { \ + diff = ALIGNMENT - diff; \ + ptr = (void *)((uintptr_t)ptr + diff); \ + } \ + *((ALIGN_TYPE *)((uintptr_t)ptr - ALIGN_INFO)) = \ + diff + ALIGN_INFO; \ + } + +#define UNALIGN(ptr) \ + if (ALIGNMENT > 1) \ + { \ + uintptr_t diff = *((ALIGN_TYPE *)((uintptr_t)ptr - ALIGN_INFO)); \ + if (diff < (ALIGNMENT + ALIGN_INFO)) \ + { \ + ptr = (void *)((uintptr_t)ptr - diff); \ + } \ + } + +#define LIBALLOC_MAGIC 0xc001c0de +#define LIBALLOC_DEAD 0xdeaddead + +// #define LIBALLOCDEBUG 1 +#define LIBALLOCINFO 1 + +#if defined LIBALLOCDEBUG || defined LIBALLOCINFO +// #include +// #include +#include + +// #define FLUSH() fflush(stdout) +#define FLUSH() +#define atexit(x) +#define printf(m, ...) trace(m, ##__VA_ARGS__) + +#endif + +/** A structure found at the top of all system allocated + * memory blocks. It details the usage of the memory block. + */ +struct liballoc_major +{ + struct liballoc_major *prev; ///< Linked list information. + struct liballoc_major *next; ///< Linked list information. + unsigned int pages; ///< The number of pages in the block. + unsigned int size; ///< The number of pages in the block. + unsigned int usage; ///< The number of bytes used in the block. + struct liballoc_minor *first; ///< A pointer to the first allocated memory in the block. +}; + +/** This is a structure found at the beginning of all + * sections in a major block which were allocated by a + * malloc, calloc, realloc call. + */ +struct liballoc_minor +{ + struct liballoc_minor *prev; ///< Linked list information. + struct liballoc_minor *next; ///< Linked list information. + struct liballoc_major *block; ///< The owning block. A pointer to the major structure. + unsigned int magic; ///< A magic number to idenfity correctness. + unsigned int size; ///< The size of the memory allocated. Could be 1 byte or more. + unsigned int req_size; ///< The size of memory requested. +}; + +static struct liballoc_major *l_memRoot = NULL; ///< The root memory block acquired from the system. +static struct liballoc_major *l_bestBet = NULL; ///< The major with the most free memory. + +static unsigned int l_pageSize = 4096; ///< The size of an individual page. Set up in liballoc_init. +static unsigned int l_pageCount = 16; ///< The number of pages to request per chunk. Set up in liballoc_init. +static unsigned long long l_allocated = 0; ///< Running total of allocated memory. +static unsigned long long l_inuse = 0; ///< Running total of used memory. + +static long long l_warningCount = 0; ///< Number of warnings encountered +static long long l_errorCount = 0; ///< Number of actual errors +static long long l_possibleOverruns = 0; ///< Number of possible overruns + +// *********** HELPER FUNCTIONS ******************************* + +static void *liballoc_memset(void *s, int c, size_t n) +{ + unsigned int i; + for (i = 0; i < n; i++) + ((char *)s)[i] = c; + + return s; +} +static void *liballoc_memcpy(void *s1, const void *s2, size_t n) +{ + char *cdest; + char *csrc; + unsigned int *ldest = (unsigned int *)s1; + unsigned int *lsrc = (unsigned int *)s2; + + while (n >= sizeof(unsigned int)) + { + *ldest++ = *lsrc++; + n -= sizeof(unsigned int); + } + + cdest = (char *)ldest; + csrc = (char *)lsrc; + + while (n > 0) + { + *cdest++ = *csrc++; + n -= 1; + } + + return s1; +} + +#if defined LIBALLOCDEBUG || defined LIBALLOCINFO +static void liballoc_dump() +{ +#ifdef LIBALLOCDEBUG + struct liballoc_major *maj = l_memRoot; + struct liballoc_minor *min = NULL; +#endif + + printf("liballoc: ------ Memory data ---------------\n"); + printf("liballoc: System memory allocated: %i bytes\n", l_allocated); + printf("liballoc: Memory in used (malloc'ed): %i bytes\n", l_inuse); + printf("liballoc: Warning count: %i\n", l_warningCount); + printf("liballoc: Error count: %i\n", l_errorCount); + printf("liballoc: Possible overruns: %i\n", l_possibleOverruns); + +#ifdef LIBALLOCDEBUG + while (maj != NULL) + { + printf("liballoc: %x: total = %i, used = %i\n", + maj, + maj->size, + maj->usage); + + min = maj->first; + while (min != NULL) + { + printf("liballoc: %x: %i bytes\n", + min, + min->size); + min = min->next; + } + + maj = maj->next; + } +#endif + + FLUSH(); +} +#endif + +// *************************************************************** + +static struct liballoc_major *allocate_new_page(unsigned int size) +{ + unsigned int st; + struct liballoc_major *maj; + + // This is how much space is required. + st = size + sizeof(struct liballoc_major); + st += sizeof(struct liballoc_minor); + + // Perfect amount of space? + if ((st % l_pageSize) == 0) + st = st / (l_pageSize); + else + st = st / (l_pageSize) + 1; + // No, add the buffer. + + // Make sure it's >= the minimum size. + if (st < l_pageCount) + st = l_pageCount; + + maj = (struct liballoc_major *)liballoc_alloc(st); + + if (maj == NULL) + { + l_warningCount += 1; +#if defined LIBALLOCDEBUG || defined LIBALLOCINFO + printf("liballoc: WARNING: liballoc_alloc( %i ) return NULL\n", st); + FLUSH(); +#endif + return NULL; // uh oh, we ran out of memory. + } + + maj->prev = NULL; + maj->next = NULL; + maj->pages = st; + maj->size = st * l_pageSize; + maj->usage = sizeof(struct liballoc_major); + maj->first = NULL; + + l_allocated += maj->size; + +#ifdef LIBALLOCDEBUG + printf("liballoc: Resource allocated %x of %i pages (%i bytes) for %i size.\n", maj, st, maj->size, size); + + printf("liballoc: Total memory usage = %i KB\n", (int)((l_allocated / (1024)))); + FLUSH(); +#endif + + return maj; +} + +void *PREFIX(malloc)(size_t req_size) +{ + int startedBet = 0; + unsigned long long bestSize = 0; + void *p = NULL; + uintptr_t diff; + struct liballoc_major *maj; + struct liballoc_minor *min; + struct liballoc_minor *new_min; + unsigned long size = req_size; + + // For alignment, we adjust size so there's enough space to align. + if (ALIGNMENT > 1) + { + size += ALIGNMENT + ALIGN_INFO; + } + // So, ideally, we really want an alignment of 0 or 1 in order + // to save space. + + liballoc_lock(); + + if (size == 0) + { + l_warningCount += 1; +#if defined LIBALLOCDEBUG || defined LIBALLOCINFO + printf("liballoc: WARNING: alloc( 0 ) called from %x\n", + __builtin_return_address(0)); + FLUSH(); +#endif + liballoc_unlock(); + return PREFIX(malloc)(1); + } + + if (l_memRoot == NULL) + { +#if defined LIBALLOCDEBUG || defined LIBALLOCINFO +#ifdef LIBALLOCDEBUG + printf("liballoc: initialization of liballoc " VERSION "\n"); +#endif + atexit(liballoc_dump); + FLUSH(); +#endif + + // This is the first time we are being used. + l_memRoot = allocate_new_page(size); + if (l_memRoot == NULL) + { + liballoc_unlock(); +#ifdef LIBALLOCDEBUG + printf("liballoc: initial l_memRoot initialization failed\n", p); + FLUSH(); +#endif + return NULL; + } + +#ifdef LIBALLOCDEBUG + printf("liballoc: set up first memory major %x\n", l_memRoot); + FLUSH(); +#endif + } + +#ifdef LIBALLOCDEBUG + printf("liballoc: %x PREFIX(malloc)( %i ): ", + __builtin_return_address(0), + size); + FLUSH(); +#endif + + // Now we need to bounce through every major and find enough space.... + + maj = l_memRoot; + startedBet = 0; + + // Start at the best bet.... + if (l_bestBet != NULL) + { + bestSize = l_bestBet->size - l_bestBet->usage; + + if (bestSize > (size + sizeof(struct liballoc_minor))) + { + maj = l_bestBet; + startedBet = 1; + } + } + + while (maj != NULL) + { + diff = maj->size - maj->usage; + // free memory in the block + + if (bestSize < diff) + { + // Hmm.. this one has more memory then our bestBet. Remember! + l_bestBet = maj; + bestSize = diff; + } + +#ifdef USE_CASE1 + + // CASE 1: There is not enough space in this major block. + if (diff < (size + sizeof(struct liballoc_minor))) + { +#ifdef LIBALLOCDEBUG + printf("CASE 1: Insufficient space in block %x\n", maj); + FLUSH(); +#endif + + // Another major block next to this one? + if (maj->next != NULL) + { + maj = maj->next; // Hop to that one. + continue; + } + + if (startedBet == 1) // If we started at the best bet, + { // let's start all over again. + maj = l_memRoot; + startedBet = 0; + continue; + } + + // Create a new major block next to this one and... + maj->next = allocate_new_page(size); // next one will be okay. + if (maj->next == NULL) + break; // no more memory. + maj->next->prev = maj; + maj = maj->next; + + // .. fall through to CASE 2 .. + } + +#endif + +#ifdef USE_CASE2 + + // CASE 2: It's a brand new block. + if (maj->first == NULL) + { + maj->first = (struct liballoc_minor *)((uintptr_t)maj + sizeof(struct liballoc_major)); + + maj->first->magic = LIBALLOC_MAGIC; + maj->first->prev = NULL; + maj->first->next = NULL; + maj->first->block = maj; + maj->first->size = size; + maj->first->req_size = req_size; + maj->usage += size + sizeof(struct liballoc_minor); + + l_inuse += size; + + p = (void *)((uintptr_t)(maj->first) + sizeof(struct liballoc_minor)); + + ALIGN(p); + +#ifdef LIBALLOCDEBUG + printf("CASE 2: returning %x\n", p); + FLUSH(); +#endif + liballoc_unlock(); // release the lock + return p; + } + +#endif + +#ifdef USE_CASE3 + + // CASE 3: Block in use and enough space at the start of the block. + diff = (uintptr_t)(maj->first); + diff -= (uintptr_t)maj; + diff -= sizeof(struct liballoc_major); + + if (diff >= (size + sizeof(struct liballoc_minor))) + { + // Yes, space in front. Squeeze in. + maj->first->prev = (struct liballoc_minor *)((uintptr_t)maj + sizeof(struct liballoc_major)); + maj->first->prev->next = maj->first; + maj->first = maj->first->prev; + + maj->first->magic = LIBALLOC_MAGIC; + maj->first->prev = NULL; + maj->first->block = maj; + maj->first->size = size; + maj->first->req_size = req_size; + maj->usage += size + sizeof(struct liballoc_minor); + + l_inuse += size; + + p = (void *)((uintptr_t)(maj->first) + sizeof(struct liballoc_minor)); + ALIGN(p); + +#ifdef LIBALLOCDEBUG + printf("CASE 3: returning %x\n", p); + FLUSH(); +#endif + liballoc_unlock(); // release the lock + return p; + } + +#endif + +#ifdef USE_CASE4 + + // CASE 4: There is enough space in this block. But is it contiguous? + min = maj->first; + + // Looping within the block now... + while (min != NULL) + { + // CASE 4.1: End of minors in a block. Space from last and end? + if (min->next == NULL) + { + // the rest of this block is free... is it big enough? + diff = (uintptr_t)(maj) + maj->size; + diff -= (uintptr_t)min; + diff -= sizeof(struct liballoc_minor); + diff -= min->size; + // minus already existing usage.. + + if (diff >= (size + sizeof(struct liballoc_minor))) + { + // yay.... + min->next = (struct liballoc_minor *)((uintptr_t)min + sizeof(struct liballoc_minor) + min->size); + min->next->prev = min; + min = min->next; + min->next = NULL; + min->magic = LIBALLOC_MAGIC; + min->block = maj; + min->size = size; + min->req_size = req_size; + maj->usage += size + sizeof(struct liballoc_minor); + + l_inuse += size; + + p = (void *)((uintptr_t)min + sizeof(struct liballoc_minor)); + ALIGN(p); + +#ifdef LIBALLOCDEBUG + printf("CASE 4.1: returning %x\n", p); + FLUSH(); +#endif + liballoc_unlock(); // release the lock + return p; + } + } + + // CASE 4.2: Is there space between two minors? + if (min->next != NULL) + { + // is the difference between here and next big enough? + diff = (uintptr_t)(min->next); + diff -= (uintptr_t)min; + diff -= sizeof(struct liballoc_minor); + diff -= min->size; + // minus our existing usage. + + if (diff >= (size + sizeof(struct liballoc_minor))) + { + // yay...... + new_min = (struct liballoc_minor *)((uintptr_t)min + sizeof(struct liballoc_minor) + min->size); + + new_min->magic = LIBALLOC_MAGIC; + new_min->next = min->next; + new_min->prev = min; + new_min->size = size; + new_min->req_size = req_size; + new_min->block = maj; + min->next->prev = new_min; + min->next = new_min; + maj->usage += size + sizeof(struct liballoc_minor); + + l_inuse += size; + + p = (void *)((uintptr_t)new_min + sizeof(struct liballoc_minor)); + ALIGN(p); + +#ifdef LIBALLOCDEBUG + printf("CASE 4.2: returning %x\n", p); + FLUSH(); +#endif + + liballoc_unlock(); // release the lock + return p; + } + } // min->next != NULL + + min = min->next; + } // while min != NULL ... + +#endif + +#ifdef USE_CASE5 + + // CASE 5: Block full! Ensure next block and loop. + if (maj->next == NULL) + { +#ifdef LIBALLOCDEBUG + printf("CASE 5: block full\n"); + FLUSH(); +#endif + + if (startedBet == 1) + { + maj = l_memRoot; + startedBet = 0; + continue; + } + + // we've run out. we need more... + maj->next = allocate_new_page(size); // next one guaranteed to be okay + if (maj->next == NULL) + break; // uh oh, no more memory..... + maj->next->prev = maj; + } + +#endif + + maj = maj->next; + } // while (maj != NULL) + + liballoc_unlock(); // release the lock + +#ifdef LIBALLOCDEBUG + printf("All cases exhausted. No memory available.\n"); + FLUSH(); +#endif +#if defined LIBALLOCDEBUG || defined LIBALLOCINFO + printf("liballoc: WARNING: PREFIX(malloc)( %i ) returning NULL.\n", size); + liballoc_dump(); + FLUSH(); +#endif + return NULL; +} + +void PREFIX(free)(void *ptr) +{ + struct liballoc_minor *min; + struct liballoc_major *maj; + + if (ptr == NULL) + { + l_warningCount += 1; +#if defined LIBALLOCDEBUG || defined LIBALLOCINFO + printf("liballoc: WARNING: PREFIX(free)( NULL ) called from %x\n", + __builtin_return_address(0)); + FLUSH(); +#endif + return; + } + + UNALIGN(ptr); + + liballoc_lock(); // lockit + + min = (struct liballoc_minor *)((uintptr_t)ptr - sizeof(struct liballoc_minor)); + + if (min->magic != LIBALLOC_MAGIC) + { + l_errorCount += 1; + + // Check for overrun errors. For all bytes of LIBALLOC_MAGIC + if ( + ((min->magic & 0xFFFFFF) == (LIBALLOC_MAGIC & 0xFFFFFF)) || + ((min->magic & 0xFFFF) == (LIBALLOC_MAGIC & 0xFFFF)) || + ((min->magic & 0xFF) == (LIBALLOC_MAGIC & 0xFF))) + { + l_possibleOverruns += 1; +#if defined LIBALLOCDEBUG || defined LIBALLOCINFO + printf("liballoc: ERROR: Possible 1-3 byte overrun for magic %x != %x\n", + min->magic, + LIBALLOC_MAGIC); + FLUSH(); +#endif + } + + if (min->magic == LIBALLOC_DEAD) + { +#if defined LIBALLOCDEBUG || defined LIBALLOCINFO + printf("liballoc: ERROR: multiple PREFIX(free)() attempt on %x from %x.\n", + ptr, + __builtin_return_address(0)); + FLUSH(); +#endif + } + else + { +#if defined LIBALLOCDEBUG || defined LIBALLOCINFO + printf("liballoc: ERROR: Bad PREFIX(free)( %x ) called from %x\n", + ptr, + __builtin_return_address(0)); + FLUSH(); +#endif + } + + // being lied to... + liballoc_unlock(); // release the lock + return; + } + +#ifdef LIBALLOCDEBUG + printf("liballoc: %x PREFIX(free)( %x ): ", + __builtin_return_address(0), + ptr); + FLUSH(); +#endif + + maj = min->block; + + l_inuse -= min->size; + + maj->usage -= (min->size + sizeof(struct liballoc_minor)); + min->magic = LIBALLOC_DEAD; // No mojo. + + if (min->next != NULL) + min->next->prev = min->prev; + if (min->prev != NULL) + min->prev->next = min->next; + + if (min->prev == NULL) + maj->first = min->next; + // Might empty the block. This was the first + // minor. + + // We need to clean up after the majors now.... + + if (maj->first == NULL) // Block completely unused. + { + if (l_memRoot == maj) + l_memRoot = maj->next; + if (l_bestBet == maj) + l_bestBet = NULL; + if (maj->prev != NULL) + maj->prev->next = maj->next; + if (maj->next != NULL) + maj->next->prev = maj->prev; + l_allocated -= maj->size; + + liballoc_free(maj, maj->pages); + } + else + { + if (l_bestBet != NULL) + { + int bestSize = l_bestBet->size - l_bestBet->usage; + int majSize = maj->size - maj->usage; + + if (majSize > bestSize) + l_bestBet = maj; + } + } + +#ifdef LIBALLOCDEBUG + printf("OK\n"); + FLUSH(); +#endif + + liballoc_unlock(); // release the lock +} + +void *PREFIX(calloc)(size_t nobj, size_t size) +{ + int real_size; + void *p; + + real_size = nobj * size; + + p = PREFIX(malloc)(real_size); + + liballoc_memset(p, 0, real_size); + + return p; +} + +void *PREFIX(realloc)(void *p, size_t size) +{ + void *ptr; + struct liballoc_minor *min; + unsigned int real_size; + + // Honour the case of size == 0 => free old and return NULL + if (size == 0) + { + PREFIX(free) + (p); + return NULL; + } + + // In the case of a NULL pointer, return a simple malloc. + if (p == NULL) + return PREFIX(malloc)(size); + + // Unalign the pointer if required. + ptr = p; + UNALIGN(ptr); + + liballoc_lock(); // lockit + + min = (struct liballoc_minor *)((uintptr_t)ptr - sizeof(struct liballoc_minor)); + + // Ensure it is a valid structure. + if (min->magic != LIBALLOC_MAGIC) + { + l_errorCount += 1; + + // Check for overrun errors. For all bytes of LIBALLOC_MAGIC + if ( + ((min->magic & 0xFFFFFF) == (LIBALLOC_MAGIC & 0xFFFFFF)) || + ((min->magic & 0xFFFF) == (LIBALLOC_MAGIC & 0xFFFF)) || + ((min->magic & 0xFF) == (LIBALLOC_MAGIC & 0xFF))) + { + l_possibleOverruns += 1; +#if defined LIBALLOCDEBUG || defined LIBALLOCINFO + printf("liballoc: ERROR: Possible 1-3 byte overrun for magic %x != %x\n", + min->magic, + LIBALLOC_MAGIC); + FLUSH(); +#endif + } + + if (min->magic == LIBALLOC_DEAD) + { +#if defined LIBALLOCDEBUG || defined LIBALLOCINFO + printf("liballoc: ERROR: multiple PREFIX(free)() attempt on %x from %x.\n", + ptr, + __builtin_return_address(0)); + FLUSH(); +#endif + } + else + { +#if defined LIBALLOCDEBUG || defined LIBALLOCINFO + printf("liballoc: ERROR: Bad PREFIX(free)( %x ) called from %x\n", + ptr, + __builtin_return_address(0)); + FLUSH(); +#endif + } + + // being lied to... + liballoc_unlock(); // release the lock + return NULL; + } + + // Definitely a memory block. + + real_size = min->req_size; + + if (real_size >= size) + { + min->req_size = size; + liballoc_unlock(); + return p; + } + + liballoc_unlock(); + + // If we got here then we're reallocating to a block bigger than us. + ptr = PREFIX(malloc)(size); // We need to allocate new memory + liballoc_memcpy(ptr, p, real_size); + PREFIX(free) + (p); + + return ptr; +} diff --git a/lib/liballoc_1_1.h b/lib/liballoc_1_1.h new file mode 100644 index 00000000..19552ad5 --- /dev/null +++ b/lib/liballoc_1_1.h @@ -0,0 +1,74 @@ +#ifndef _LIBALLOC_H +#define _LIBALLOC_H + +#include + +/** \defgroup ALLOCHOOKS liballoc hooks + * + * These are the OS specific functions which need to + * be implemented on any platform that the library + * is expected to work on. + */ + +/** @{ */ + +// If we are told to not define our own size_t, then we skip the define. +//#define _HAVE_UINTPTR_T +// typedef unsigned long uintptr_t; + +// This lets you prefix malloc and friends +#define PREFIX(func) kliballoc_##func + +#ifdef __cplusplus +extern "C" +{ +#endif + + /** This function is supposed to lock the memory data structures. It + * could be as simple as disabling interrupts or acquiring a spinlock. + * It's up to you to decide. + * + * \return 0 if the lock was acquired successfully. Anything else is + * failure. + */ + extern int liballoc_lock(); + + /** This function unlocks what was previously locked by the liballoc_lock + * function. If it disabled interrupts, it enables interrupts. If it + * had acquiried a spinlock, it releases the spinlock. etc. + * + * \return 0 if the lock was successfully released. + */ + extern int liballoc_unlock(); + + /** This is the hook into the local system which allocates pages. It + * accepts an integer parameter which is the number of pages + * required. The page size was set up in the liballoc_init function. + * + * \return NULL if the pages were not allocated. + * \return A pointer to the allocated memory. + */ + extern void *liballoc_alloc(size_t); + + /** This frees previously allocated memory. The void* parameter passed + * to the function is the exact same value returned from a previous + * liballoc_alloc call. + * + * The integer value is the number of pages to free. + * + * \return 0 if the memory was successfully freed. + */ + extern int liballoc_free(void *, size_t); + + extern void *PREFIX(malloc)(size_t); ///< The standard function. + extern void *PREFIX(realloc)(void *, size_t); ///< The standard function. + extern void *PREFIX(calloc)(size_t, size_t); ///< The standard function. + extern void PREFIX(free)(void *); ///< The standard function. + +#ifdef __cplusplus +} +#endif + +/** @} */ + +#endif diff --git a/lib/liballocimpl.cpp b/lib/liballocimpl.cpp new file mode 100644 index 00000000..f81c9045 --- /dev/null +++ b/lib/liballocimpl.cpp @@ -0,0 +1,14 @@ +#include +#include +#include + +NEWLOCK(liballocLock); + +EXTERNC int liballoc_lock() { return liballocLock.Lock(); } +EXTERNC int liballoc_unlock() { return liballocLock.Unlock(); } +EXTERNC void *liballoc_alloc(size_t Pages) { return KernelAllocator.RequestPages(Pages); } +EXTERNC int liballoc_free(void *Address, size_t Pages) +{ + KernelAllocator.FreePages(Address, Pages); + return 0; +} diff --git a/lib/printf.c b/lib/printf.c new file mode 100644 index 00000000..93d85ead --- /dev/null +++ b/lib/printf.c @@ -0,0 +1,1591 @@ +/** + * @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 "-Wtype-limits" +#pragma GCC diagnostic ignored "-Wsign-compare" +#pragma GCC diagnostic ignored "-Wtautological-compare" +#pragma GCC diagnostic ignored "-Wsign-compare" + +// Define this globally (e.g. gcc -DPRINTF_INCLUDE_CONFIG_H ...) to include the +// printf_config.h header file +#if PRINTF_INCLUDE_CONFIG_H +#include "printf_config.h" +#endif + +#include +#include +#include +#include + +#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 / *doubl e*/unsigned long) 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 + +#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 / *doubl e*/unsigned long type configuration" +#endif +#define DOUBLE_STORED_MANTISSA_BITS (DBL_MANT_DIG - 1) + +typedef union +{ + double_uint_t U; + /* double */ unsigned long 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 double_with_bit_access get_bit_access(/* double */ unsigned long x) +{ + double_with_bit_access dwba; + dwba.F = x; + return dwba; +} + +static inline int get_sign_bit(/* double */ unsigned long 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 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 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'; +} + +// We can't use putchar_ as is, since our output gadget +// only takes pointers to functions with an extra argument +static inline void putchar_wrapper(char c, void *unused) +{ + (void)unused; + putchar(c); +} + +static inline output_gadget_t discarding_gadget() +{ + output_gadget_t gadget; + gadget.function = NULL; + gadget.extra_function_arg = NULL; + gadget.buffer = NULL; + gadget.pos = 0; + gadget.max_chars = 0; + return gadget; +} + +static inline output_gadget_t buffer_gadget(char *buffer, size_t buffer_size) +{ + printf_size_t usable_buffer_size = (buffer_size > PRINTF_MAX_POSSIBLE_BUFFER_SIZE) ? PRINTF_MAX_POSSIBLE_BUFFER_SIZE : (printf_size_t)buffer_size; + output_gadget_t result = discarding_gadget(); + if (buffer != NULL) + { + result.buffer = buffer; + result.max_chars = usable_buffer_size; + } + return result; +} + +static inline output_gadget_t function_gadget(void (*function)(char, void *), void *extra_arg) +{ + output_gadget_t result = discarding_gadget(); + result.function = function; + result.extra_function_arg = extra_arg; + result.max_chars = PRINTF_MAX_POSSIBLE_BUFFER_SIZE; + return result; +} + +static inline output_gadget_t extern_putchar_gadget() +{ + 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 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 bool is_digit_(char ch) +{ + return (ch >= '0') && (ch <= '9'); +} + +// internal ASCII string to printf_size_t conversion +static printf_size_t atou_(const char **str) +{ + printf_size_t i = 0U; + while (is_digit_(**str)) + { + i = i * 10U + (printf_size_t)(*((*str)++) - '0'); + } + return i; +} + +// output the specified string in reverse, taking care of any zero-padding +static void out_rev_(output_gadget_t *output, const char *buf, printf_size_t len, printf_size_t width, printf_flags_t flags) +{ + const printf_size_t start_pos = output->pos; + + // pad spaces up to given width + if (!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD)) + { + for (printf_size_t i = len; i < width; i++) + { + putchar_via_gadget(output, ' '); + } + } + + // reverse string + while (len) + { + putchar_via_gadget(output, buf[--len]); + } + + // append pad spaces up to given width + if (flags & FLAGS_LEFT) + { + while (output->pos - start_pos < width) + { + putchar_via_gadget(output, ' '); + } + } +} + +// Invoked by print_integer after the actual number has been printed, performing necessary +// work on the number's prefix (as the number is initially printed in reverse order) +static void print_integer_finalization(output_gadget_t *output, char *buf, printf_size_t len, bool negative, numeric_base_t base, printf_size_t precision, printf_size_t width, printf_flags_t flags) +{ + printf_size_t unpadded_len = len; + + // pad with leading zeros + { + if (!(flags & FLAGS_LEFT)) + { + if (width && (flags & FLAGS_ZEROPAD) && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) + { + width--; + } + while ((flags & FLAGS_ZEROPAD) && (len < width) && (len < PRINTF_INTEGER_BUFFER_SIZE)) + { + buf[len++] = '0'; + } + } + + while ((len < precision) && (len < PRINTF_INTEGER_BUFFER_SIZE)) + { + buf[len++] = '0'; + } + + if (base == BASE_OCTAL && (len > unpadded_len)) + { + // Since we've written some zeros, we've satisfied the alternative format leading space requirement + flags &= ~FLAGS_HASH; + } + } + + // handle hash + if (flags & (FLAGS_HASH | FLAGS_POINTER)) + { + if (!(flags & FLAGS_PRECISION) && len && ((len == precision) || (len == width))) + { + // Let's take back some padding digits to fit in what will eventually + // be the format-specific prefix + if (unpadded_len < len) + { + len--; // This should suffice for BASE_OCTAL + } + if (len && (base == BASE_HEX || base == BASE_BINARY) && (unpadded_len < len)) + { + len--; // ... and an extra one for 0x or 0b + } + } + if ((base == BASE_HEX) && !(flags & FLAGS_UPPERCASE) && (len < PRINTF_INTEGER_BUFFER_SIZE)) + { + buf[len++] = 'x'; + } + else if ((base == BASE_HEX) && (flags & FLAGS_UPPERCASE) && (len < PRINTF_INTEGER_BUFFER_SIZE)) + { + buf[len++] = 'X'; + } + else if ((base == BASE_BINARY) && (len < PRINTF_INTEGER_BUFFER_SIZE)) + { + buf[len++] = 'b'; + } + if (len < PRINTF_INTEGER_BUFFER_SIZE) + { + buf[len++] = '0'; + } + } + + if (len < PRINTF_INTEGER_BUFFER_SIZE) + { + if (negative) + { + buf[len++] = '-'; + } + else if (flags & FLAGS_PLUS) + { + buf[len++] = '+'; // ignore the space if the '+' exists + } + else if (flags & FLAGS_SPACE) + { + buf[len++] = ' '; + } + } + + out_rev_(output, buf, len, width, flags); +} + +// An internal itoa-like function +static void print_integer(output_gadget_t *output, printf_unsigned_value_t value, bool negative, numeric_base_t base, printf_size_t precision, printf_size_t width, printf_flags_t flags) +{ + char buf[PRINTF_INTEGER_BUFFER_SIZE]; + printf_size_t len = 0U; + + if (!value) + { + if (!(flags & FLAGS_PRECISION)) + { + buf[len++] = '0'; + flags &= ~FLAGS_HASH; + // We drop this flag this since either the alternative and regular modes of the specifier + // don't differ on 0 values, or (in the case of octal) we've already provided the special + // handling for this mode. + } + else if (base == BASE_HEX) + { + flags &= ~FLAGS_HASH; + // We drop this flag this since either the alternative and regular modes of the specifier + // don't differ on 0 values + } + } + else + { + do + { + const char digit = (char)(value % base); + buf[len++] = (char)(digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10); + value /= base; + } while (value && (len < PRINTF_INTEGER_BUFFER_SIZE)); + } + + print_integer_finalization(output, buf, len, negative, base, precision, width, flags); +} + +#if (PRINTF_SUPPORT_DECIMAL_SPECIFIERS || PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS) + +// Stores a fixed-precision representation of a / *doubl e*/unsigned long 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 / *doubl e*/unsigned long 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 */ unsigned long 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 / *doubl e*/unsigned long 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 double_components get_components(/* double */ unsigned long number, printf_size_t precision) +{ + struct double_components number_; + number_.is_negative = get_sign_bit(number); + /* double */ unsigned long abs_number = (number_.is_negative) ? -number : number; + number_.integral = (int_fast64_t)abs_number; + /* double */ unsigned long remainder = (abs_number - (/* double */ unsigned long)number_.integral) * powers_of_10[precision]; + number_.fractional = (int_fast64_t)remainder; + + remainder -= (/* double */ unsigned long)number_.fractional; + + // if (remainder > 0.5) + warn("SSE not supported."); + if (remainder > 1) + { + ++number_.fractional; + // handle rollover, e.g. case 0.99 with precision 1 is 1.0 + if ((/* double */ unsigned long)number_.fractional >= powers_of_10[precision]) + { + number_.fractional = 0; + ++number_.integral; + } + } + // else if ((remainder == 0.5) && ((number_.fractional == 0U) || (number_.fractional & 1U))) + else if ((remainder == 1) && ((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 */ unsigned long)number_.integral; + // if ((!(remainder < 0.5) || (remainder > 0.5)) && (number_.integral & 1)) + if ((!(remainder < 1) || (remainder > 1)) && (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 */ unsigned long raw_factor; + bool multiply; // if true, need to multiply by raw_factor; otherwise need to divide by it +}; + +static /* double */ unsigned long apply_scaling(/* double */ unsigned long num, struct scaling_factor normalization) +{ + return normalization.multiply ? num * normalization.raw_factor : num / normalization.raw_factor; +} + +static /* double */ unsigned long unapply_scaling(/* double */ unsigned long normalized, struct scaling_factor normalization) +{ + return normalization.multiply ? normalized / normalization.raw_factor : normalized * normalization.raw_factor; +} + +static struct scaling_factor update_normalization(struct scaling_factor sf, /* double */ unsigned long 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 */ unsigned long non_normalized, struct scaling_factor normalization, int floored_exp10) +{ + struct double_components components; + components.is_negative = negative; + /* double */ unsigned long 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 */ unsigned long remainder = non_normalized - unapply_scaling((/* double */ unsigned long)components.integral, normalization); + /* double */ unsigned long prec_power_of_10 = powers_of_10[precision]; + struct scaling_factor account_for_precision = update_normalization(normalization, prec_power_of_10); + /* double */ unsigned long scaled_remainder = apply_scaling(remainder, account_for_precision); + /* double */ unsigned long rounding_threshold = 1; // 0.5; + + components.fractional = (int_fast64_t)scaled_remainder; // when precision == 0, the assigned value should be 0 + scaled_remainder -= (/* double */ unsigned long)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 */ unsigned long)components.fractional >= prec_power_of_10) + { + components.fractional = 0; + ++components.integral; + } + return components; +} +#endif // PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS + +static 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 void print_decimal_number(output_gadget_t *output, /* double */ unsigned long 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 */ unsigned long x) +{ + if (x >= 0) + { + return (int)x; + } + int n = (int)x; + return (((/* double */ unsigned long)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 */ unsigned long log10_of_positive(/* double */ unsigned long 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 */ unsigned long z = (dwba.F - 1 /* 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) + // ); + return ( + // Taylor expansion around 1.5: + 0 // Expansion term 0: ln(1.5) / ln(10) + + z * 0 // Expansion term 1: (M - 1.5) * 2/3 / ln(10) +#if PRINTF_LOG10_TAYLOR_TERMS > 2 + - z * z * 0 // Expansion term 2: (M - 1.5)^2 * 2/9 / ln(10) +#if PRINTF_LOG10_TAYLOR_TERMS > 3 + + z * z * z * 0 // 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 // = exp2 * log_10(2) = exp2 * ln(2)/ln(10) + ); +} + +static /* double */ unsigned long 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 */ unsigned long z = floored_exp10 * 2.302585092994046 - exp2 * 0.6931471805599453; + int exp2 = bastardized_floor(floored_exp10 * 3 + 1/* 0.5 */); + const /* double */ unsigned long z = floored_exp10 * 2 - exp2 * 0; + const /* double */ unsigned long 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 void print_exponential_number(output_gadget_t *output, /* double */ unsigned long 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 */ unsigned long 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) + if (abs_number == 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 */ unsigned long exp10 = log10_of_positive(abs_number); + floored_exp10 = bastardized_floor(exp10); + /* double */ unsigned long 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 / *doubl e*/unsigned long + // 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 void print_floating_point(output_gadget_t *output, /* double */ unsigned long 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) + if (value < -1) + { + out_rev_(output, "fni-", 4, width, flags); + return; + } + // if (value > DBL_MAX) + if (value > 1) + { + 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))) + ((value > 1) || (value < -1))) + { + // 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 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); +} + +// internal vsnprintf - used for implementing _all library functions +// Note: We don't like the C standard's parameter names, so using more informative parameter names +// here instead. +static int _vsnprintf(output_gadget_t *output, const char *format, va_list args) +{ + // Note: The library only calls _vsnprintf() with output->pos being 0. However, it is + // possible to call this function with a non-zero pos value for some "remedial printing". + + while (*format) + { + // format specifier? %[flags][width][.precision][length] + if (*format != '%') + { + // no + putchar_via_gadget(output, *format); + format++; + continue; + } + else + { + // yes, evaluate it + 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; + } + format++; + } + + // evaluate precision field + printf_size_t precision = 0U; + if (*format == '.') + { + flags |= FLAGS_PRECISION; + 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; + format++; + } + } + + // evaluate length field + switch (*format) + { +#ifdef PRINTF_SUPPORT_MSVC_STYLE_INTEGER_SPECIFIERS + case 'I': + { + format++; + // Greedily parse for size in bits: 8, 16, 32 or 64 + switch (*format) + { + case '8': + flags |= FLAGS_INT8; + format++; + break; + case '1': + format++; + if (*format == '6') + { + format++; + flags |= FLAGS_INT16; + } + break; + case '3': + format++; + if (*format == '2') + { + format++; + flags |= FLAGS_INT32; + } + break; + case '6': + format++; + if (*format == '4') + { + format++; + flags |= FLAGS_INT64; + } + break; + default: + break; + } + break; + } +#endif + case 'l': + flags |= FLAGS_LONG; + format++; + if (*format == 'l') + { + flags |= FLAGS_LONG_LONG; + format++; + } + break; + case 'h': + flags |= FLAGS_SHORT; + format++; + if (*format == 'h') + { + flags |= FLAGS_CHAR; + format++; + } + break; + case 't': + flags |= (sizeof(ptrdiff_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); + format++; + break; + case 'j': + flags |= (sizeof(intmax_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); + format++; + break; + case 'z': + flags |= (sizeof(size_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); + 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 */ unsigned long), 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 */ unsigned long), 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; + } + } + + // termination + append_termination_with_gadget(output); + + // return written chars without terminating \0 + return (int)output->pos; +} + +/////////////////////////////////////////////////////////////////////////////// + +int vprintf_(const char *format, va_list arg) +{ + output_gadget_t gadget = extern_putchar_gadget(); + return _vsnprintf(&gadget, format, arg); +} + +int vsnprintf_(char *s, size_t n, const char *format, va_list arg) +{ + output_gadget_t gadget = buffer_gadget(s, n); + return _vsnprintf(&gadget, format, arg); +} + +int vsprintf_(char *s, const char *format, va_list arg) +{ + return vsnprintf_(s, PRINTF_MAX_POSSIBLE_BUFFER_SIZE, format, arg); +} + +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(&gadget, format, arg); +} + +int printf_(const char *format, ...) +{ + va_list args; + va_start(args, format); + const int ret = vprintf_(format, args); + va_end(args); + return ret; +} + +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; +} + +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; +} + +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; +} + +#ifdef __cplusplus +} // extern "C" +#endif \ No newline at end of file