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 7bd069c8..8cdf5c72 Binary files a/kernel.fsys and b/kernel.fsys differ diff --git a/kernel.h b/kernel.h new file mode 100644 index 00000000..d875dbea --- /dev/null +++ b/kernel.h @@ -0,0 +1,11 @@ +#ifndef __FENNIX_KERNEL_KERNEL_H__ +#define __FENNIX_KERNEL_KERNEL_H__ + +#include +#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