void OS::start(uint32_t boot_magic, uint32_t boot_addr) { OS::cmdline = Service::binary_name(); // Initialize stdout handlers if(os_default_stdout) { OS::add_stdout(&OS::default_stdout); } PROFILE("OS::start"); // Print a fancy header CAPTION("#include<os> // Literally"); MYINFO("Stack: %p", get_cpu_esp()); MYINFO("Boot magic: 0x%x, addr: 0x%x", boot_magic, boot_addr); // Call global ctors PROFILE("Global constructors"); __libc_init_array(); // PAGING // PROFILE("Enable paging"); extern void __arch_init_paging(); __arch_init_paging(); // BOOT METHOD // PROFILE("Multiboot / legacy"); OS::memory_end_ = 0; // Detect memory limits etc. depending on boot type if (boot_magic == MULTIBOOT_BOOTLOADER_MAGIC) { OS::multiboot(boot_addr); } else { if (is_softreset_magic(boot_magic) && boot_addr != 0) OS::resume_softreset(boot_addr); OS::legacy_boot(); } assert(OS::memory_end_ != 0); // Give the rest of physical memory to heap OS::heap_max_ = OS::memory_end_; /// STATMAN /// PROFILE("Statman"); /// initialize on page 9, 8 pages in size Statman::get().init(0x8000, 0x8000); PROFILE("Memory map"); // Assign memory ranges used by the kernel auto& memmap = memory_map(); MYINFO("Assigning fixed memory ranges (Memory map)"); memmap.assign_range({0x8000, 0xffff, "Statman"}); #if defined(ARCH_x86_64) /** * TODO: Map binary parts using mem::map instead of assigning ranges directly * e.g. the .text segment is now mapped individually by __arch_init_paging */ memmap.assign_range({0x1000, 0x6fff, "Pagetables"}); memmap.assign_range({0x10000, 0x9d3ff, "Stack"}); #elif defined(ARCH_i686) memmap.assign_range({0x10000, 0x9d3ff, "Stack"}); #endif assert(::heap_begin != 0x0 and OS::heap_max_ != 0x0); // @note for security we don't want to expose this memmap.assign_range({(uintptr_t)&_end, ::heap_begin - 1, "Pre-heap"}); uintptr_t span_max = std::numeric_limits<std::ptrdiff_t>::max(); uintptr_t heap_range_max_ = std::min(span_max, OS::heap_max_); MYINFO("Assigning heap"); memmap.assign_range({::heap_begin, heap_range_max_, "Dynamic memory", heap_usage }); MYINFO("Virtual memory map"); for (const auto &i : memmap) INFO2("%s",i.second.to_string().c_str()); PROFILE("Platform init"); extern void __platform_init(); __platform_init(); PROFILE("RTC init"); // Realtime/monotonic clock RTC::init(); }
void OS::start(uint32_t boot_magic, uint32_t boot_addr) { atexit(default_exit); default_stdout_handlers(); // Print a fancy header FILLINE('='); CAPTION("#include<os> // Literally\n"); FILLINE('='); auto esp = get_cpu_esp(); MYINFO ("Stack: 0x%x", esp); Expects (esp < 0xA0000 and esp > 0x0 and "Stack location OK"); MYINFO("Boot args: 0x%x (multiboot magic), 0x%x (bootinfo addr)", boot_magic, boot_addr); MYINFO("Max mem (from linker): %i MiB", reinterpret_cast<size_t>(&_MAX_MEM_MIB_)); if (boot_magic == MULTIBOOT_BOOTLOADER_MAGIC) { OS::multiboot(boot_magic, boot_addr); } else { // Fetch CMOS memory info (unfortunately this is maximally 10^16 kb) auto mem = cmos::meminfo(); low_memory_size_ = mem.base.total * 1024; INFO2("* Low memory: %i Kib", mem.base.total); high_memory_size_ = mem.extended.total * 1024; // Use memsize provided by Make / linker unless CMOS knows this is wrong decltype(high_memory_size_) hardcoded_mem = reinterpret_cast<size_t>(&_MAX_MEM_MIB_ - 0x100000) << 20; if (mem.extended.total == 0xffff or hardcoded_mem < mem.extended.total) { high_memory_size_ = hardcoded_mem; INFO2("* High memory (from linker): %i Kib", high_memory_size_ / 1024); } else { INFO2("* High memory (from cmos): %i Kib", mem.extended.total); } } MYINFO("Assigning fixed memory ranges (Memory map)"); auto& memmap = memory_map(); // @ Todo: The first ~600k of memory is free for use. What can we put there? memmap.assign_range({0x0009FC00, 0x0009FFFF, "EBDA", "Extended BIOS data area"}); memmap.assign_range({0x000A0000, 0x000FFFFF, "VGA/ROM", "Memory mapped video memory"}); memmap.assign_range({(uintptr_t)&_LOAD_START_, (uintptr_t)&_end, "ELF", "Your service binary including OS"}); // @note for security we don't want to expose this memmap.assign_range({(uintptr_t)&_end + 1, heap_begin - 1, "Pre-heap", "Heap randomization area (not for use))"}); memmap.assign_range({0x8000, 0x9fff, "Statman", "Statistics"}); memmap.assign_range({0xA000, 0x9fbff, "Kernel / service main stack"}); // Create ranges for heap and the remaining address space // @note : since the maximum size of a span is unsigned (ptrdiff_t) we may need more than one uintptr_t addr_max = std::numeric_limits<std::size_t>::max(); uintptr_t span_max = std::numeric_limits<std::ptrdiff_t>::max(); // Give the rest of physical memory to heap heap_max_ = ((0x100000 + high_memory_size_) & 0xffff0000) - 1; // ...Unless it's more than the maximum for a range // @note : this is a stupid way to limit the heap - we'll change it, but not until // we have a good solution. heap_max_ = std::min(span_max, heap_max_); memmap.assign_range({heap_begin, heap_max_, "Heap", "Dynamic memory", heap_usage }); uintptr_t unavail_start = 0x100000 + high_memory_size_; size_t interval = std::min(span_max, addr_max - unavail_start) - 1; uintptr_t unavail_end = unavail_start + interval; while (unavail_end < addr_max){ INFO2("* Unavailable memory: 0x%x - 0x%x", unavail_start, unavail_end); memmap.assign_range({unavail_start, unavail_end, "N/A", "Reserved / outside physical range" }); unavail_start = unavail_end + 1; interval = std::min(span_max, addr_max - unavail_start); // Increment might wrapped around if (unavail_start > unavail_end + interval or unavail_start + interval == addr_max){ INFO2("* Last chunk of memory: 0x%x - 0x%x", unavail_start, addr_max); memmap.assign_range({unavail_start, addr_max, "N/A", "Reserved / outside physical range" }); break; } unavail_end += interval; } MYINFO("Printing memory map"); for (const auto &i : memory_map()) INFO2("* %s",i.second.to_string().c_str()); // Set up interrupt and exception handlers IRQ_manager::init(); // read ACPI tables hw::ACPI::init(); // setup APIC, APIC timer, SMP etc. hw::APIC::init(); // enable interrupts INFO("BSP", "Enabling interrupts"); IRQ_manager::enable_interrupts(); // Initialize the Interval Timer hw::PIT::init(); // Initialize PCI devices PCI_manager::init(); // Print registered devices hw::Devices::print_devices(); // Estimate CPU frequency MYINFO("Estimating CPU-frequency"); INFO2("|"); INFO2("+--(10 samples, %f sec. interval)", (hw::PIT::frequency() / _cpu_sampling_freq_divider_).count()); INFO2("|"); // TODO: Debug why actual measurments sometimes causes problems. Issue #246. cpu_mhz_ = hw::PIT::CPU_frequency(); INFO2("+--> %f MHz", cpu_mhz_.count()); // cpu_mhz must be known before we can start timer system /// initialize timers hooked up to APIC timer Timers::init( // timer start function hw::APIC_Timer::oneshot, // timer stop function hw::APIC_Timer::stop); // initialize BSP APIC timer hw::APIC_Timer::init( [] { // set final interrupt handler hw::APIC_Timer::set_handler(Timers::timers_handler); // signal that kernel is done with everything Service::ready(); // signal ready // NOTE: this executes the first timers, so we // don't want to run this before calling Service ready Timers::ready(); }); // Realtime/monotonic clock RTC::init(); booted_at_ = RTC::now(); // sleep statistics os_cycles_hlt = &Statman::get().create( Stat::UINT64, std::string("cpu0.cycles_hlt")).get_uint64(); os_cycles_total = &Statman::get().create( Stat::UINT64, std::string("cpu0.cycles_total")).get_uint64(); // Trying custom initialization functions MYINFO("Calling custom initialization functions"); for (auto init : custom_init_) { INFO2("* Calling %s", init.name_); try{ init.func_(); } catch(std::exception& e){ MYINFO("Exception thrown when calling custom init: %s", e.what()); } catch(...){ MYINFO("Unknown exception when calling custom initialization function"); } } // Everything is ready MYINFO("Starting %s", Service::name().c_str()); FILLINE('='); // initialize random seed based on cycles since start srand(cycles_since_boot() & 0xFFFFFFFF); // begin service start Service::start(Service::command_line()); event_loop(); }
void OS::start(char* _cmdline, uintptr_t mem_size) { // Initialize stdout handlers OS::add_stdout(&OS::default_stdout); PROFILE(""); // Print a fancy header CAPTION("#include<os> // Literally"); void* esp = get_cpu_esp(); MYINFO("Stack: %p", esp); /// STATMAN /// /// initialize on page 7, 2 pages in size Statman::get().init(0x6000, 0x3000); OS::cmdline = _cmdline; // setup memory and heap end OS::memory_end_ = mem_size; OS::heap_max_ = OS::memory_end_; // Call global ctors PROFILE("Global constructors"); __libc_init_array(); PROFILE("Memory map"); // Assign memory ranges used by the kernel auto& memmap = memory_map(); MYINFO("Assigning fixed memory ranges (Memory map)"); memmap.assign_range({0x500, 0x5fff, "solo5", "solo5"}); memmap.assign_range({0x6000, 0x8fff, "Statman", "Statistics"}); memmap.assign_range({0xA000, 0x9fbff, "Stack", "Kernel / service main stack"}); memmap.assign_range({(uintptr_t)&_LOAD_START_, (uintptr_t)&_end, "ELF", "Your service binary including OS"}); Expects(::heap_begin and heap_max_); // @note for security we don't want to expose this memmap.assign_range({(uintptr_t)&_end + 1, ::heap_begin - 1, "Pre-heap", "Heap randomization area"}); uintptr_t span_max = std::numeric_limits<std::ptrdiff_t>::max(); uintptr_t heap_range_max_ = std::min(span_max, heap_max_); MYINFO("Assigning heap"); memmap.assign_range({::heap_begin, heap_range_max_, "Heap", "Dynamic memory", heap_usage }); MYINFO("Printing memory map"); for (const auto &i : memmap) INFO2("* %s",i.second.to_string().c_str()); extern void __platform_init(); __platform_init(); MYINFO("Booted at monotonic_ns=%lld walltime_ns=%lld", solo5_clock_monotonic(), solo5_clock_wall()); Solo5_manager::init(); // We don't need a start or stop function in solo5. Timers::init( // timer start function [] (std::chrono::microseconds) {}, // timer stop function [] () {}); // Some tests are asserting there is at least one timer that is always ON // (the RTC calibration timer). Let's fake some timer so those tests pass. Timers::oneshot(std::chrono::hours(1000000), [] (auto) {}); Timers::ready(); }