From 88b090fe949e56ac03af7ecad6979362b68f9332 Mon Sep 17 00:00:00 2001 From: "Justin C. Miller" Date: Sun, 31 May 2020 23:58:01 -0700 Subject: [PATCH] [kernel] Run global constructors Look up the global constructor list that the linker outputs, and run them all. Required creation of the `kutil::no_construct` template for objects that are constructed before the global constructors are run. Also split the `memory_initialize` function into two - one for just those objects that need to happen before the global ctors, and one after. Tags: memory c++ --- src/arch/x86_64/kernel.ld | 6 +++ src/kernel/console.cpp | 5 +- src/kernel/console.h | 2 +- src/kernel/device_manager.cpp | 18 ++++--- src/kernel/device_manager.h | 9 ++-- src/kernel/frame_allocator.cpp | 2 - src/kernel/frame_allocator.h | 2 - src/kernel/log.cpp | 12 +++-- src/kernel/main.cpp | 48 ++++++++++++++----- src/kernel/memory_bootstrap.cpp | 32 ++++++++++--- src/kernel/objects/kobject.cpp | 3 -- src/kernel/page_manager.cpp | 4 +- src/kernel/page_manager.h | 11 +---- src/kernel/serial.cpp | 2 +- src/libraries/kutil/include/kutil/logger.h | 14 ++++-- .../kutil/include/kutil/no_construct.h | 15 ++++++ src/libraries/kutil/logger.cpp | 8 ++-- 17 files changed, 126 insertions(+), 67 deletions(-) create mode 100644 src/libraries/kutil/include/kutil/no_construct.h diff --git a/src/arch/x86_64/kernel.ld b/src/arch/x86_64/kernel.ld index e1dc196..5c57969 100755 --- a/src/arch/x86_64/kernel.ld +++ b/src/arch/x86_64/kernel.ld @@ -20,6 +20,12 @@ SECTIONS *(.rodata) } + .ctors : ALIGN(8) { + __ctors = .; + KEEP(*(.ctors)) + __ctors_end = .; + } + .bss ALIGN(4096) : { __bss_start = .; *(.bss) diff --git a/src/kernel/console.cpp b/src/kernel/console.cpp index 16d5019..67a322e 100644 --- a/src/kernel/console.cpp +++ b/src/kernel/console.cpp @@ -1,6 +1,7 @@ #include "kutil/coord.h" #include "kutil/guid.h" #include "kutil/memory.h" +#include "kutil/no_construct.h" #include "kutil/printf.h" #include "console.h" #include "font.h" @@ -9,7 +10,9 @@ const char digits[] = "0123456789abcdef"; -console g_console; + +static kutil::no_construct __g_console_storage; +console &g_console = __g_console_storage.value; class console::screen_out diff --git a/src/kernel/console.h b/src/kernel/console.h index 1d0cc06..7fc0538 100644 --- a/src/kernel/console.h +++ b/src/kernel/console.h @@ -46,7 +46,7 @@ private: serial_port *m_serial; }; -extern console g_console; +extern console &g_console; inline console * console::get() { return &g_console; } diff --git a/src/kernel/device_manager.cpp b/src/kernel/device_manager.cpp index 92e3ea2..beee7b8 100644 --- a/src/kernel/device_manager.cpp +++ b/src/kernel/device_manager.cpp @@ -14,7 +14,7 @@ static const char expected_signature[] = "RSD PTR "; -device_manager device_manager::s_instance(nullptr, kutil::allocator::invalid); +device_manager device_manager::s_instance; struct acpi1_rsdp { @@ -59,8 +59,17 @@ void irq4_callback(void *) } -device_manager::device_manager(const void *root_table, kutil::allocator &alloc) : +device_manager::device_manager() : m_lapic(nullptr) +{ + m_irqs.ensure_capacity(32); + m_irqs.set_size(16); + m_irqs[2] = {"Clock interrupt", irq2_callback, nullptr}; + m_irqs[4] = {"Serial interrupt", irq4_callback, nullptr}; +} + +void +device_manager::parse_acpi(const void *root_table) { kassert(root_table != 0, "ACPI root table pointer is null."); @@ -83,11 +92,6 @@ device_manager::device_manager(const void *root_table, kutil::allocator &alloc) kassert(sum == 0, "ACPI 2.0 RSDP checksum mismatch."); load_xsdt(reinterpret_cast(acpi2->xsdt_address)); - - m_irqs.ensure_capacity(32); - m_irqs.set_size(16); - m_irqs[2] = {"Clock interrupt", irq2_callback, nullptr}; - m_irqs[4] = {"Serial interrupt", irq4_callback, nullptr}; } ioapic * diff --git a/src/kernel/device_manager.h b/src/kernel/device_manager.h index 2a406e1..6701b17 100644 --- a/src/kernel/device_manager.h +++ b/src/kernel/device_manager.h @@ -18,9 +18,7 @@ class device_manager { public: /// Constructor. - /// \arg root_table Pointer to the ACPI RSDP - /// \arg alloc Allocator for device arrays - device_manager(const void *root_table, kutil::allocator &alloc); + device_manager(); /// Get the system global device manager. /// \returns A reference to the system device manager @@ -36,6 +34,10 @@ public: /// otherwise nullptr. ioapic * get_ioapic(int i); + /// Parse ACPI tables. + /// \arg root_table Pointer to the ACPI RSDP + void parse_acpi(const void *root_table); + /// Intialize drivers for the current device list. void init_drivers(); @@ -122,7 +124,6 @@ private: static device_manager s_instance; - device_manager() = delete; device_manager(const device_manager &) = delete; device_manager operator=(const device_manager &) = delete; }; diff --git a/src/kernel/frame_allocator.cpp b/src/kernel/frame_allocator.cpp index 24ce876..f4ecd1f 100644 --- a/src/kernel/frame_allocator.cpp +++ b/src/kernel/frame_allocator.cpp @@ -6,8 +6,6 @@ using memory::frame_size; using memory::page_offset; using frame_block_node = kutil::list_node; -frame_allocator g_frame_allocator; - int frame_block::compare(const frame_block *rhs) const { diff --git a/src/kernel/frame_allocator.h b/src/kernel/frame_allocator.h index 3e0a45a..412c4b0 100644 --- a/src/kernel/frame_allocator.h +++ b/src/kernel/frame_allocator.h @@ -66,5 +66,3 @@ struct frame_block int compare(const frame_block *rhs) const; }; - -extern frame_allocator g_frame_allocator; diff --git a/src/kernel/log.cpp b/src/kernel/log.cpp index 277ecea..a05efa7 100644 --- a/src/kernel/log.cpp +++ b/src/kernel/log.cpp @@ -1,11 +1,16 @@ #include "kutil/memory.h" +#include "kutil/no_construct.h" #include "console.h" #include "log.h" #include "scheduler.h" - static uint8_t log_buffer[0x10000]; -static log::logger g_logger(log_buffer, sizeof(log_buffer)); + +// The logger is initialized _before_ global constructors are called, +// so that we can start log output immediately. Keep its constructor +// from being called here so as to not overwrite the previous initialization. +static kutil::no_construct __g_logger_storage; +static log::logger &g_logger = __g_logger_storage.value; static const uint8_t level_colors[] = {0x07, 0x07, 0x0f, 0x0b, 0x09}; @@ -50,6 +55,5 @@ logger_task() void logger_init() { - new (&g_logger) log::logger(log_buffer, sizeof(log_buffer)); - g_logger.set_immediate(output_log); + new (&g_logger) log::logger(log_buffer, sizeof(log_buffer), output_log); } diff --git a/src/kernel/main.cpp b/src/kernel/main.cpp index 645d162..4cdfa82 100644 --- a/src/kernel/main.cpp +++ b/src/kernel/main.cpp @@ -5,7 +5,6 @@ #include "initrd/initrd.h" #include "kutil/assert.h" -#include "kutil/heap_allocator.h" #include "kutil/vm_space.h" #include "apic.h" #include "block_device.h" @@ -27,12 +26,35 @@ extern "C" { void kernel_main(kernel::args::header *header); - void *__bss_start, *__bss_end; + void (*__ctors)(void); + void (*__ctors_end)(void); } +void +run_constructors() +{ + void (**p)(void) = &__ctors; + while (p < &__ctors_end) { + void (*ctor)(void) = *p++; + ctor(); + } +} + +class test_ctor +{ +public: + test_ctor(int value) : value(value) {} + int value; +}; + +test_ctor ctor_tester(42); + extern void __kernel_assert(const char *, unsigned, const char *); -extern kutil::heap_allocator g_kernel_heap; +/// Bootstrap the memory managers. +void memory_initialize_pre_ctors(kernel::args::header *kargs); +void memory_initialize_post_ctors(kernel::args::header *kargs); + using namespace kernel; class test_observer : @@ -65,7 +87,6 @@ init_console() cons->puts("jsix OS "); cons->set_color(0x08, 0x00); cons->puts(GIT_VERSION " booting...\n"); - logger_init(); } @@ -78,13 +99,14 @@ kernel_main(args::header *header) gdt_init(); interrupts_init(); + memory_initialize_pre_ctors(header); + kutil::memset(&ctor_tester, 0, sizeof(ctor_tester)); + run_constructors(); + memory_initialize_post_ctors(header); + cpu_id cpu; cpu.validate(); - memory_initialize(header); - - kutil::allocator &heap = g_kernel_heap; - /* if (header->frame_buffer && header->frame_buffer_length) { page_manager::get()->map_offset_pointer( @@ -116,8 +138,8 @@ kernel_main(args::header *header) page_manager::get()->dump_blocks(true); */ - device_manager *devices = - new (&device_manager::get()) device_manager(header->acpi_table, heap); + device_manager &devices = device_manager::get(); + devices.parse_acpi(header->acpi_table); interrupts_enable(); @@ -130,7 +152,7 @@ kernel_main(args::header *header) log::info(logs::boot, "cr4: %016x", cr4); */ - devices->init_drivers(); + devices.init_drivers(); /* block_device *disk = devices->get_block_device(0); @@ -156,10 +178,10 @@ kernel_main(args::header *header) } */ - devices->get_lapic()->calibrate_timer(); + devices.get_lapic()->calibrate_timer(); syscall_enable(); - scheduler *sched = new (&scheduler::get()) scheduler(devices->get_lapic()); + scheduler *sched = new (&scheduler::get()) scheduler(devices.get_lapic()); sched->create_kernel_task(-1, logger_task); diff --git a/src/kernel/memory_bootstrap.cpp b/src/kernel/memory_bootstrap.cpp index 150596e..83f5de3 100644 --- a/src/kernel/memory_bootstrap.cpp +++ b/src/kernel/memory_bootstrap.cpp @@ -5,6 +5,7 @@ #include "kutil/assert.h" #include "kutil/heap_allocator.h" +#include "kutil/no_construct.h" #include "kutil/vm_space.h" #include "frame_allocator.h" @@ -24,7 +25,18 @@ using memory::table_entries; using namespace kernel; kutil::vm_space g_kernel_space; -kutil::heap_allocator g_kernel_heap; + +// These objects are initialized _before_ global constructors are called, +// so we don't want them to have global constructors at all, lest they +// overwrite the previous initialization. +static kutil::no_construct __g_kernel_heap_storage; +kutil::heap_allocator &g_kernel_heap = __g_kernel_heap_storage.value; + +static kutil::no_construct __g_page_manager_storage; +page_manager &g_page_manager = __g_page_manager_storage.value; + +static kutil::no_construct __g_frame_allocator_storage; +frame_allocator &g_frame_allocator = __g_frame_allocator_storage.value; void * operator new(size_t size) { return g_kernel_heap.allocate(size); } void * operator new [] (size_t size) { return g_kernel_heap.allocate(size); } @@ -74,7 +86,7 @@ void walk_page_table( } void -memory_initialize(args::header *kargs) +memory_initialize_pre_ctors(args::header *kargs) { args::mem_entry *entries = kargs->mem_map; size_t entry_count = kargs->num_map_entries; @@ -82,26 +94,30 @@ memory_initialize(args::header *kargs) new (&g_kernel_heap) kutil::heap_allocator {heap_start, kernel_max_heap}; - frame_allocator *fa = new (&g_frame_allocator) frame_allocator; + new (&g_frame_allocator) frame_allocator; for (unsigned i = 0; i < entry_count; ++i) { // TODO: use entry attributes args::mem_entry &e = entries[i]; if (e.type == args::mem_type::free) - fa->free(e.start, e.pages); + g_frame_allocator.free(e.start, e.pages); } // Create the page manager - page_manager *pm = new (&g_page_manager) page_manager(*fa, kpml4); + new (&g_page_manager) page_manager {g_frame_allocator, kpml4}; +} +void +memory_initialize_post_ctors(args::header *kargs) +{ new (&g_kernel_space) kutil::vm_space { kernel_offset, (page_offset-kernel_offset)}; - uintptr_t current_start = 0; size_t current_bytes = 0; // TODO: Should we exclude the top of this area? (eg, buffers, stacks, etc) + page_table *kpml4 = reinterpret_cast(kargs->pml4); for (unsigned i = pml4e_kernel; i < pml4e_offset; ++i) { page_table *pdp = kpml4->get(i); @@ -118,5 +134,7 @@ memory_initialize(args::header *kargs) g_kernel_space); } - fa->free(reinterpret_cast(kargs->page_table_cache), kargs->num_free_tables); + g_frame_allocator.free( + reinterpret_cast(kargs->page_table_cache), + kargs->num_free_tables); } diff --git a/src/kernel/objects/kobject.cpp b/src/kernel/objects/kobject.cpp index a8662a1..fff0e9d 100644 --- a/src/kernel/objects/kobject.cpp +++ b/src/kernel/objects/kobject.cpp @@ -1,11 +1,8 @@ #include "j6/errors.h" #include "j6/signals.h" #include "j6/types.h" -#include "kutil/heap_allocator.h" #include "objects/kobject.h" -extern kutil::heap_allocator g_kernel_heap; - // TODO: per-cpu this? static j6_koid_t next_koid; diff --git a/src/kernel/page_manager.cpp b/src/kernel/page_manager.cpp index 082693b..68c4b98 100644 --- a/src/kernel/page_manager.cpp +++ b/src/kernel/page_manager.cpp @@ -16,9 +16,6 @@ using memory::page_mappable; using memory::pml4e_kernel; using memory::table_entries; -page_manager g_page_manager(g_frame_allocator, 0); -extern kutil::vm_space g_kernel_space; - // NB: in 4KiB page table entries, bit 7 isn't pagesize but PAT. Currently this // doesn't matter, becasue in the default PAT table, both 000 and 100 are WB. constexpr uint64_t sys_page_flags = 0x183; // global, pagesize, write, present @@ -321,6 +318,7 @@ page_manager::fault_handler(uintptr_t addr) if (!addr) return false; + extern kutil::vm_space g_kernel_space; bool is_heap = addr >= ::memory::heap_start && addr < ::memory::heap_start + ::memory::kernel_max_heap; diff --git a/src/kernel/page_manager.h b/src/kernel/page_manager.h index 9393dea..8bd4b52 100644 --- a/src/kernel/page_manager.h +++ b/src/kernel/page_manager.h @@ -181,7 +181,7 @@ private: }; /// Global page manager. -extern page_manager g_page_manager; +extern page_manager &g_page_manager; inline page_manager * page_manager::get() { return &g_page_manager; } @@ -206,12 +206,3 @@ page_table_align(T p) { return ((p - 1) & ~0x1fffffull) + 0x200000; } - -namespace kernel { -namespace args { - struct header; -} -} - -/// Bootstrap the memory managers. -void memory_initialize(kernel::args::header *mem_map); diff --git a/src/kernel/serial.cpp b/src/kernel/serial.cpp index d12e449..2675d92 100644 --- a/src/kernel/serial.cpp +++ b/src/kernel/serial.cpp @@ -1,7 +1,7 @@ #include "io.h" #include "serial.h" -serial_port g_com1; +serial_port g_com1(COM1); serial_port::serial_port() : diff --git a/src/libraries/kutil/include/kutil/logger.h b/src/libraries/kutil/include/kutil/logger.h index bf54e04..15aeb9a 100644 --- a/src/libraries/kutil/include/kutil/logger.h +++ b/src/libraries/kutil/include/kutil/logger.h @@ -18,11 +18,18 @@ enum class level : uint8_t { class logger { public: + /// Callback type for immediate-mode logging + typedef void (*immediate)(area_t, level, const char *); + /// Default constructor. Creates a logger without a backing store. - logger(); + /// \arg output Immediate-mode logging output function + logger(immediate output = nullptr); /// Constructor. Logs are written to the given buffer. - logger(uint8_t *buffer, size_t size); + /// \arg buffer Buffer to which logs are written + /// \arg size Size of `buffer`, in bytes + /// \arg output Immediate-mode logging output function + logger(uint8_t *buffer, size_t size, immediate output = nullptr); /// Register a log area for future use. /// \arg area The key for the new area @@ -30,9 +37,6 @@ public: /// \arg verbosity What level of logs to print for this area void register_area(area_t area, const char *name, level verbosity); - /// Callback type for immediate-mode logging - typedef void (*immediate)(area_t, level, const char *); - /// Register an immediate-mode log callback inline void set_immediate(immediate cb) { m_immediate = cb; } diff --git a/src/libraries/kutil/include/kutil/no_construct.h b/src/libraries/kutil/include/kutil/no_construct.h new file mode 100644 index 0000000..2bd5ec0 --- /dev/null +++ b/src/libraries/kutil/include/kutil/no_construct.h @@ -0,0 +1,15 @@ +#pragma once +/// \file no_construct.h +/// Tools for creating objects witout running constructors + +namespace kutil { + +/// Helper template for creating objects witout running constructors +template +union no_construct +{ + T value; + no_construct() {} +}; + +} // namespace kutil diff --git a/src/libraries/kutil/logger.cpp b/src/libraries/kutil/logger.cpp index 428c3fd..ade8782 100644 --- a/src/libraries/kutil/logger.cpp +++ b/src/libraries/kutil/logger.cpp @@ -21,9 +21,9 @@ using kutil::memcpy; logger *logger::s_log = nullptr; const char *logger::s_level_names[] = {"", "debug", "info", "warn", "error", "fatal"}; -logger::logger() : +logger::logger(logger::immediate output) : m_buffer(nullptr, 0), - m_immediate(nullptr), + m_immediate(output), m_sequence(0) { memset(&m_levels, 0, sizeof(m_levels)); @@ -31,9 +31,9 @@ logger::logger() : s_log = this; } -logger::logger(uint8_t *buffer, size_t size) : +logger::logger(uint8_t *buffer, size_t size, logger::immediate output) : m_buffer(buffer, size), - m_immediate(nullptr), + m_immediate(output), m_sequence(0) { memset(&m_levels, 0, sizeof(m_levels));