diff --git a/kernel/Makefile b/kernel/Makefile index eb468a8..e862d51 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -6,7 +6,7 @@ output_dir := $(root_dir)/output LD := ld CXX := clang++ -CXXFLAGS := -std=c++20 -fpie -fno-strict-aliasing -fno-stack-protector -fno-asynchronous-unwind-tables -fno-exceptions -fno-rtti -fno-common -mno-red-zone -mgeneral-regs-only -ffreestanding -O2 -g -Wall -Wextra -Wno-reserved-identifier -I$(root_dir)/include +CXXFLAGS := -std=c++20 -fpie -fno-strict-aliasing -fno-stack-protector -fno-asynchronous-unwind-tables -fno-exceptions -fno-rtti -fno-common -fno-c++-static-destructors -mno-red-zone -mgeneral-regs-only -ffreestanding -O2 -g -Wall -Wextra -Wno-reserved-identifier -I$(root_dir)/include source_files := $(shell find $(source_dir) -type f -name "*.cpp") object_files := $(patsubst $(source_dir)/%,$(object_dir)/%.o,$(source_files)) @@ -19,7 +19,7 @@ $(object_files): $(object_dir)%.o : $(source_dir)% kernel.bin: $(object_files) @mkdir -p $(output_dir) @echo "Linking kernel" - @$(LD) --build-id=none --no-relax -nostdlib -o $(output_dir)/kernel.bin -T $(linker_script) $(object_files) + @$(LD) --build-id=none --no-relax --no-warn-rwx-segments -nostdlib -o $(output_dir)/kernel.bin -T $(linker_script) $(object_files) @echo "Disassembling kernel" @objdump -dC $(output_dir)/kernel.bin > $(output_dir)/kernel.asm @objdump -DC $(output_dir)/kernel.bin > $(output_dir)/kernel_full.asm diff --git a/kernel/include/init/multiboot2.h b/kernel/include/init/multiboot2.h index 15305c5..0313fc7 100644 --- a/kernel/include/init/multiboot2.h +++ b/kernel/include/init/multiboot2.h @@ -34,6 +34,34 @@ enum class tag_type : uint32_t { load_base_addr = 24, }; +inline constexpr const char* get_tag_name(tag_type type) { + switch (type) { + case tag_type::end: return "end"; + case tag_type::cmdline: return "cmdline"; + case tag_type::boot_loader_name: return "boot_loader_name"; + case tag_type::module: return "module"; + case tag_type::basic_meminfo: return "basic_meminfo"; + case tag_type::bootdev: return "bootdev"; + case tag_type::mmap: return "mmap"; + case tag_type::vbe: return "vbe"; + case tag_type::framebuffer: return "framebuffer"; + case tag_type::elf_sections: return "elf_sections"; + case tag_type::apm: return "apm"; + case tag_type::efi32: return "efi32"; + case tag_type::efi64: return "efi64"; + case tag_type::smbios: return "smbios"; + case tag_type::acpi_old: return "acpi_old"; + case tag_type::acpi_new: return "acpi_new"; + case tag_type::network: return "network"; + case tag_type::efi_mmap: return "efi_mmap"; + case tag_type::efi_bs: return "efi_bs"; + case tag_type::efi32_ih: return "efi32_ih"; + case tag_type::efi64_ih: return "efi64_ih"; + case tag_type::load_base_addr: return "load_base_addr"; + } + return ""; +} + enum class framebuffer_type : uint8_t { indexed = 0, rgb = 1, @@ -48,6 +76,17 @@ enum class mem_type : uint32_t { badram = 5, }; +inline constexpr const char* get_mem_type_name(mem_type type) { + switch (type) { + case mem_type::available: return "available"; + case mem_type::reserved: return "reserved"; + case mem_type::acpi_reclaimable: return "acpi_reclaimable"; + case mem_type::nvs: return "nvs"; + case mem_type::badram: return "badram"; + default: return "unknown"; + } +} + struct color { uint8_t red; uint8_t green; diff --git a/kernel/include/memory/allocator.h b/kernel/include/memory/allocator.h new file mode 100644 index 0000000..b75130e --- /dev/null +++ b/kernel/include/memory/allocator.h @@ -0,0 +1,37 @@ +#pragma once + +#include "memory/pointer.h" +#include "process/process.h" +#include "util/optional.h" +#include "init/multiboot2.h" + +constexpr const size_t page_size = 4096; + +struct PhysicalAllocator { +public: + struct allocation_t { + paddr_t ptr; + size_t page_count; + }; + struct entry_t { + uint64_t page_count; + pid_t owner; + }; + static void init(const multiboot::tag_mmap* multiboot_info); + static PhysicalAllocator& getInstance(); + allocation_t allocPages(size_t page_count, pid_t owner); + void free(allocation_t ptr); + void force(allocation_t ptr, pid_t owner); + entry_t getOwner(paddr_t from); +private: + struct Page { + optional next; + entry_t entries[255]; + void forceEntry(entry_t entry); + void cleanup(); + void compact(); + }; + static_assert(sizeof(Page) == page_size); + Page* storage; +}; + diff --git a/kernel/include/memory/pointer.h b/kernel/include/memory/pointer.h index 82fab09..4bf28c9 100644 --- a/kernel/include/memory/pointer.h +++ b/kernel/include/memory/pointer.h @@ -7,13 +7,12 @@ constexpr uint64_t high_base = 0xFFFF800000000000; #define read_symbol(symbol) [](){uint64_t res; asm volatile("movabsq $" symbol ", %0": "=r"(res)); return res;}() struct paddr_t { -uint64_t address; - -template -T* access() { - return reinterpret_cast(address+high_base); -} + uint64_t address; + template + T* access() { + return reinterpret_cast(address+high_base); + } }; inline void* operator new(size_t, void* p) { diff --git a/kernel/include/memory/sections.h b/kernel/include/memory/sections.h index 8071bf8..62ce0bd 100644 --- a/kernel/include/memory/sections.h +++ b/kernel/include/memory/sections.h @@ -17,3 +17,14 @@ void for_each_reserved_section(Fn&& fn) { for (auto* s = __reserved_ranges_start__; s < __reserved_ranges_end__; ++s) fn(*s); } + +inline bool is_in_reserved_section(paddr_t ptr, size_t size = 1) { + bool res = false; + for_each_reserved_section([&](const KernelSection& section) { + if (ptr.address < section.phys_end().address && + section.phys_start().address < ptr.address + size) { + res = true; + } + }); + return res; +} diff --git a/kernel/include/process/process.h b/kernel/include/process/process.h new file mode 100644 index 0000000..359ce04 --- /dev/null +++ b/kernel/include/process/process.h @@ -0,0 +1,10 @@ +#pragma once + +#include "util/number.h" + +using pid_t = uint64_t; + +constexpr const pid_t pid_free = 0; +constexpr const pid_t pid_reserved = 1; +constexpr const pid_t pid_mmio = 2; +constexpr const pid_t pid_kernel = 3; diff --git a/kernel/include/util/optional.h b/kernel/include/util/optional.h new file mode 100644 index 0000000..ee788e7 --- /dev/null +++ b/kernel/include/util/optional.h @@ -0,0 +1,144 @@ +#pragma once + +#include "memory/pointer.h" +#include "util/number.h" +#include "util/utility.h" + +// Ported from legacy/CrackOS3 (include/features/optional.h), adapted to the +// PrOSess freestanding environment: uses the in-tree move/forward (util/utility.h) +// instead of . The CrackOS3 value_or_panic() helper is omitted because +// there is no global panic() in this tree yet. + +template +struct optional { +private: + char buffer[sizeof(T)]; + bool initialized; + + void destroy() { + if (initialized) { + ((T*) buffer)->~T(); + initialized = false; + } + } + +public: + using value_type = T; + + constexpr optional() : initialized(false) {} + optional(const T& value) : initialized(true) { + new (buffer) T(value); + } + optional(T&& value) : initialized(true) { + new (buffer) T(move(value)); + } + + optional(const optional& other) : initialized(other.initialized) { + if (initialized) { + new (buffer) T(*other); + } + } + + optional(optional&& other) : initialized(other.initialized) { + if (initialized) { + new (buffer) T(move(*other)); + } + other.initialized = false; + } + + ~optional() { + destroy(); + } + + T* operator->() { + return (T*) buffer; + } + + T& operator*() { + return *((T*) buffer); + } + + const T* operator->() const { + return (const T*) buffer; + } + + const T& operator*() const { + return *((const T*) buffer); + } + + optional& operator=(const optional& other) { + if (this == &other) return *this; + destroy(); + if (other.initialized) { + new (buffer) T(*other); + initialized = true; + } + return *this; + } + + optional& operator=(optional&& other) noexcept { + if (this == &other) return *this; + destroy(); + if (other.initialized) { + new (buffer) T(move(*other)); + initialized = true; + } + other.initialized = false; + return *this; + } + + [[nodiscard]] bool has_value() const { + return initialized; + } + + T& value() { + return *((T*) buffer); + } + + const T& value() const { + return *((const T*) buffer); + } + + template + T value_or_create(Func func, ArgT... args) { + if (initialized) { + return value(); + } else { + return func(args...); + } + } + + template + T value_or_create(Func func, ArgT... args) const { + if (initialized) { + return value(); + } else { + return func(args...); + } + } + + const T& value_or(const T& default_value) const { + if (initialized) { + return *((const T*) buffer); + } else { + return default_value; + } + } + + operator bool() const { + return initialized; + } + + bool operator!() const { + return !initialized; + } + + template + optional map(Func func, ArgT... args) { + if (initialized) { + return func(value(), forward(args)...); + } else { + return {}; + } + } +}; diff --git a/kernel/include/util/utility.h b/kernel/include/util/utility.h new file mode 100644 index 0000000..a4a249e --- /dev/null +++ b/kernel/include/util/utility.h @@ -0,0 +1,28 @@ +#pragma once + +// Minimal freestanding replacements for (no stdlib available). + +template +struct remove_reference { using type = T; }; +template +struct remove_reference { using type = T; }; +template +struct remove_reference { using type = T; }; + +template +using remove_reference_t = typename remove_reference::type; + +template +constexpr remove_reference_t&& move(T&& value) { + return static_cast&&>(value); +} + +template +constexpr T&& forward(remove_reference_t& value) { + return static_cast(value); +} + +template +constexpr T&& forward(remove_reference_t&& value) { + return static_cast(value); +} diff --git a/kernel/linker.ld b/kernel/linker.ld index 80535ae..15c3d23 100644 --- a/kernel/linker.ld +++ b/kernel/linker.ld @@ -39,6 +39,13 @@ SECTIONS .rodata : AT(ADDR(.rodata) - high_base) { __rodata_start__ = .; *(EXCLUDE_FILE(*trampoline*) .rodata) + __reserved_ranges_start__ = .; + KEEP(*(.reserved_ranges)) + __reserved_ranges_end__ = .; + __init_array_start__ = .; + KEEP(*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*))) + KEEP(*(.init_array .ctors)) + __init_array_end__ = .; __rodata_end__ = .; } > HIGH_KERNEL_V .data : AT(ADDR(.data) - high_base) { @@ -46,11 +53,6 @@ SECTIONS *(EXCLUDE_FILE(*trampoline*) .data) __data_end__ = .; } > HIGH_KERNEL_V - .reserved_ranges : AT(ADDR(.reserved_ranges) - high_base) { - __reserved_ranges_start__ = .; - KEEP(*(.reserved_ranges)) - __reserved_ranges_end__ = .; - } > HIGH_KERNEL_V .bss (NOLOAD) : AT(ADDR(.bss) - high_base) { __bss_start__ = .; *(EXCLUDE_FILE(*trampoline*) .bss) diff --git a/kernel/src/init/cxxabi.cpp b/kernel/src/init/cxxabi.cpp new file mode 100644 index 0000000..8fcb9a0 --- /dev/null +++ b/kernel/src/init/cxxabi.cpp @@ -0,0 +1,10 @@ + +extern "C" { + +void __cxa_pure_virtual() { + for (;;) { + __asm__ volatile("cli; hlt"); + } +} + +} diff --git a/kernel/src/init/init.cpp b/kernel/src/init/init.cpp index 703752b..7c12be7 100644 --- a/kernel/src/init/init.cpp +++ b/kernel/src/init/init.cpp @@ -3,9 +3,10 @@ #include "init/init.h" #include "init/multiboot2.h" #include "init/print.h" +#include "memory/allocator.h" [[maybe_unused]] [[noreturn]] __attribute__((used)) static void halt() { - while (true) __asm__ volatile (""); + while (true) __asm__ volatile ("hlt"); } [[maybe_unused]] [[noreturn]] __attribute__((used)) static void panic(const char* msg) { @@ -13,40 +14,116 @@ halt(); } +static int step_depth = 0; template static void step(const char* name, T&& fn) { - print("Starting ", {}); - print(name, {}); - print("\n", {}); + for(int i = 0; i < step_depth; ++i) { + print(" "); + } + print("Starting "); + print(name); + print("\n"); + ++step_depth; fn(); - print("Finished ", {}); - print(name, {}); - print("\n", {}); + --step_depth; + for(int i = 0; i < step_depth; ++i) { + print(" "); + } + print("Finished "); + print(name); + print("\n"); +} + +optional findInitialMemoryPage(const multiboot::tag_mmap* multiboot_info) { + const auto* base = reinterpret_cast(multiboot_info->entries); + const auto* end = reinterpret_cast(multiboot_info) + multiboot_info->size; + for (const auto* ptr = base; ptr < end; ptr += multiboot_info->entry_size) { + const auto* entry = reinterpret_cast(ptr); + if(entry->type != multiboot::mem_type::available) { + continue; + } + auto start_ptr = (entry->addr + page_size - 1) & (~page_size); + auto end_ptr = (entry->addr + entry->len) & (~page_size); + auto size = end_ptr - start_ptr; + if(size < page_size) { + continue; + } + for(auto ptr = start_ptr; ptr < end_ptr; ptr += page_size) { + if(!is_in_reserved_section(paddr_t{ptr}, page_size)) { + return paddr_t{ptr}; + } + } + } + return {}; +} + +void PhysicalAllocator::init(const multiboot::tag_mmap* multiboot_info) { + PhysicalAllocator alloc; + auto page_opt = findInitialMemoryPage(multiboot_info); + if(!page_opt.has_value()) { + panic("System could not find enough valid ram!"); + } + auto page = page_opt.value(); + memset(page.access(), 0, page_size); + alloc.storage = page.access(); + + alloc.storage->entries[0].page_count=68719476736; + alloc.storage->entries[0].owner=pid_reserved; + + step("multiboot", [&]() { + const auto* base = reinterpret_cast(multiboot_info->entries); + const auto* end = reinterpret_cast(multiboot_info) + multiboot_info->size; + for (const auto* ptr = base; ptr < end; ptr += multiboot_info->entry_size) { + const auto* entry = reinterpret_cast(ptr); + if(entry->type!=multiboot::mem_type::available) { + continue; + } + auto start_ptr = (entry->addr + page_size - 1) & (~page_size); + auto end_ptr = (entry->addr + entry->len) & (~page_size); + auto size = end_ptr - start_ptr; + alloc.storage->forceEntry({size/page_size, pid_free}); + } + }); + step("reserved_sections", [&]() { + for_each_reserved_section([&](const KernelSection& section){ + if(section.vma_start == section.vma_end) { + return; + } + auto start_ptr = (section.phys_start().address) & (~page_size); + auto end_ptr = (section.phys_end().address + page_size - 1) & (~page_size); + auto size = end_ptr - start_ptr; + alloc.storage->forceEntry({size/page_size, pid_kernel}); + }); + }); + alloc.storage->cleanup(); + alloc.storage->compact(); + PhysicalAllocator::getInstance() = alloc; } static void loadMultiboot() { multiboot::visit_all(overloaded{ [](const multiboot::tag_mmap* mem) { - print("Tag Memory\n", {}); + print("Tag Memory Map\n"); + step("memory setup", [mem](){PhysicalAllocator::init(mem);}); }, [](const multiboot::tag_string* str) { - print("Tag String (0x", {}); - print_hex(static_cast(str->type), {}); - print("): ", {}); - print(str->string, {}); - print("\n", {}); + print("Tag String ("); + print(multiboot::get_tag_name(str->type)); + print("): "); + print(str->string); + print("\n"); }, [](const auto* tag) { - print("Tag (0x", {}); - print_hex(static_cast(tag->type), {}); - print(")\n", {}); + print("Tag "); + print(multiboot::get_tag_name(tag->type)); + print("\n"); } }); } [[maybe_unused]] [[noreturn]] __attribute__((used)) void init() { initFromLow(); - print("Reached init\n", {}); + print("Reached init\n"); step("multiboot", loadMultiboot); halt(); } diff --git a/kernel/src/init/print.cpp b/kernel/src/init/print.cpp index 9c7e5b1..a892c41 100644 --- a/kernel/src/init/print.cpp +++ b/kernel/src/init/print.cpp @@ -6,6 +6,7 @@ static constexpr int VGA_COLS = 80; static constexpr int VGA_ROWS = 25; static constexpr int BYTES_PER_CELL = 2; static constexpr int BYTES_PER_ROW = VGA_COLS * BYTES_PER_CELL; +static constexpr int SCREEN_BYTES = BYTES_PER_ROW * VGA_ROWS; static constexpr int HEX_BITS = 4; static constexpr int HEX_TOP_SHIFT = (sizeof(uint64_t) * 8) - HEX_BITS; @@ -17,8 +18,23 @@ static volatile uint16_t* vga_cell(uint32_t offset) { return paddr_t{VGA_PHYS_BASE + offset}.access(); } +static void scroll() { + // Shift every row up by one. + for (int i = 0; i < (VGA_ROWS - 1) * VGA_COLS; i++) { + *vga_cell(i * BYTES_PER_CELL) = *vga_cell((i + VGA_COLS) * BYTES_PER_CELL); + } + // Clear the last row. + for (int i = (VGA_ROWS - 1) * VGA_COLS; i < VGA_ROWS * VGA_COLS; i++) { + *vga_cell(i * BYTES_PER_CELL) = 0; + } + cursor -= BYTES_PER_ROW; +} + static void next_line() { cursor = ((cursor + BYTES_PER_ROW - 1) / BYTES_PER_ROW) * BYTES_PER_ROW; + while (cursor >= SCREEN_BYTES) { + scroll(); + } } static void put_char(char ch, uint16_t attr_word) { @@ -26,6 +42,9 @@ static void put_char(char ch, uint16_t attr_word) { next_line(); return; } + while (cursor >= SCREEN_BYTES) { + scroll(); + } *vga_cell(cursor) = attr_word | (uint16_t)ch; cursor += BYTES_PER_CELL; } diff --git a/kernel/src/init/sections.cpp b/kernel/src/init/sections.cpp index c68fdee..d901ddb 100644 --- a/kernel/src/init/sections.cpp +++ b/kernel/src/init/sections.cpp @@ -13,15 +13,9 @@ RESERVE_SECTION(boot); RESERVE_SECTION(startup_text); RESERVE_SECTION(startup_data); RESERVE_SECTION(text); -RESERVE_SECTION(rodata); +RESERVE_SECTION(rodata); // covers the nested .reserved_ranges array RESERVE_SECTION(data); RESERVE_SECTION(bss); -[[maybe_unused]] static const KernelSection _rsv_reserved_ranges -__attribute__((section(".reserved_ranges"), used)) = { - (uint64_t)__reserved_ranges_start__, - (uint64_t)__reserved_ranges_end__, - "reserved_ranges" -}; RESERVE_SECTION(trampoline_text); RESERVE_SECTION(trampoline_data); RESERVE_SECTION(trampoline_bss); diff --git a/kernel/src/memory/allocator.cpp b/kernel/src/memory/allocator.cpp new file mode 100644 index 0000000..1685a49 --- /dev/null +++ b/kernel/src/memory/allocator.cpp @@ -0,0 +1,16 @@ +#include "memory/allocator.h" +#include "util/optional.h" + +static PhysicalAllocator allocator{}; + +PhysicalAllocator& PhysicalAllocator::getInstance() { + return allocator; +} + +PhysicalAllocator::allocation_t PhysicalAllocator::allocPages(size_t count, pid_t owner) { + +} + +void PhysicalAllocator::free(allocation_t ptr) { + +} \ No newline at end of file diff --git a/kernel/src/startup/entry.cpp b/kernel/src/startup/entry.cpp index e9a1ab8..02796c8 100644 --- a/kernel/src/startup/entry.cpp +++ b/kernel/src/startup/entry.cpp @@ -1,8 +1,24 @@ #include "init/init.h" +#include "memory/pointer.h" + +extern "C" { + // Linker-defined bounds of the (NOLOAD) .bss section and the .init_array of + // global constructors. See linker.ld. + extern char __bss_start__[]; + extern char __bss_end__[]; + + using constructor_t = void (*)(); + extern constructor_t __init_array_start__[]; + extern constructor_t __init_array_end__[]; +} extern "C" [[maybe_unused]] [[noreturn]] __attribute__((used)) void __entry64() { + memset(__bss_start__, 0, static_cast(__bss_end__ - __bss_start__)); + for (constructor_t* ctor = __init_array_start__; ctor != __init_array_end__; ++ctor) { + (*ctor)(); + } init(); - while (true) __asm__ volatile (""); + while (true) __asm__ volatile ("hlt"); } asm(R"(