github.com/emc-advanced-dev/unik@v0.0.0-20190717152701-a58d3e8e33b7/containers/compilers/includeos/cpp/patches/src/kernel/os.cpp (about) 1 // This file is a part of the IncludeOS unikernel - www.includeos.org 2 // 3 // Copyright 2015 Oslo and Akershus University College of Applied Sciences 4 // and Alfred Bratterud 5 // 6 // Licensed under the Apache License, Version 2.0 (the "License"); 7 // you may not use this file except in compliance with the License. 8 // You may obtain a copy of the License at 9 // 10 // http://www.apache.org/licenses/LICENSE-2.0 11 // 12 // Unless required by applicable law or agreed to in writing, software 13 // distributed under the License is distributed on an "AS IS" BASIS, 14 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 // See the License for the specific language governing permissions and 16 // limitations under the License. 17 18 //#define DEBUG 19 #define MYINFO(X,...) INFO("Kernel", X, ##__VA_ARGS__) 20 21 #include <cstdio> 22 #include <os> 23 #include <boot/multiboot.h> 24 #include <kernel/elf.hpp> 25 #include <hw/acpi.hpp> 26 #include <hw/apic.hpp> 27 #include <hw/apic_timer.hpp> 28 #include <hw/cmos.hpp> 29 #include <kernel/irq_manager.hpp> 30 #include <kernel/pci_manager.hpp> 31 #include <kernel/timers.hpp> 32 #include <kernel/rtc.hpp> 33 #include <statman> 34 #include <vector> 35 36 extern "C" uint16_t _cpu_sampling_freq_divider_; 37 extern uintptr_t heap_begin; 38 extern uintptr_t heap_end; 39 extern uintptr_t _start; 40 extern uintptr_t _end; 41 extern uintptr_t _ELF_START_; 42 extern uintptr_t _TEXT_START_; 43 extern uintptr_t _LOAD_START_; 44 extern uintptr_t _ELF_END_; 45 extern uintptr_t _MAX_MEM_MIB_; 46 47 bool OS::power_ {true}; 48 bool OS::ready_ = true; 49 MHz OS::cpu_mhz_ {1000}; 50 RTC::timestamp_t OS::booted_at_ {0}; 51 uintptr_t OS::low_memory_size_ {0}; 52 uintptr_t OS::high_memory_size_ {0}; 53 uintptr_t OS::heap_max_ {0xfffffff}; 54 const uintptr_t OS::elf_binary_size_ {(uintptr_t)&_ELF_END_ - (uintptr_t)&_ELF_START_}; 55 // stdout redirection 56 static std::vector<OS::print_func> os_print_handlers; 57 extern void default_stdout_handlers(); 58 // custom init 59 std::vector<OS::Custom_init_struct> OS::custom_init_; 60 // OS version 61 #ifndef OS_VERSION 62 #define OS_VERSION "v?.?.?" 63 #endif 64 std::string OS::version_field = OS_VERSION; 65 66 // Multiboot command line for the service 67 static std::string os_cmdline = Service::binary_name(); 68 // sleep statistics 69 static uint64_t* os_cycles_hlt = nullptr; 70 static uint64_t* os_cycles_total = nullptr; 71 extern "C" uintptr_t get_cpu_esp(); 72 73 void OS::start(uint32_t boot_magic, uint32_t boot_addr) { 74 75 atexit(default_exit); 76 default_stdout_handlers(); 77 78 // Print a fancy header 79 FILLINE('='); 80 CAPTION("#include<os> // Literally\n"); 81 FILLINE('='); 82 83 auto esp = get_cpu_esp(); 84 MYINFO ("Stack: 0x%x", esp); 85 Expects (esp < 0xA0000 and esp > 0x0 and "Stack location OK"); 86 87 MYINFO("Boot args: 0x%x (multiboot magic), 0x%x (bootinfo addr)", 88 boot_magic, boot_addr); 89 90 MYINFO("Max mem (from linker): %i MiB", reinterpret_cast<size_t>(&_MAX_MEM_MIB_)); 91 92 if (boot_magic == MULTIBOOT_BOOTLOADER_MAGIC) { 93 OS::multiboot(boot_magic, boot_addr); 94 } else { 95 96 // Fetch CMOS memory info (unfortunately this is maximally 10^16 kb) 97 auto mem = cmos::meminfo(); 98 low_memory_size_ = mem.base.total * 1024; 99 INFO2("* Low memory: %i Kib", mem.base.total); 100 high_memory_size_ = mem.extended.total * 1024; 101 102 // Use memsize provided by Make / linker unless CMOS knows this is wrong 103 decltype(high_memory_size_) hardcoded_mem = reinterpret_cast<size_t>(&_MAX_MEM_MIB_ - 0x100000) << 20; 104 if (mem.extended.total == 0xffff or hardcoded_mem < mem.extended.total) { 105 high_memory_size_ = hardcoded_mem; 106 INFO2("* High memory (from linker): %i Kib", high_memory_size_ / 1024); 107 } else { 108 INFO2("* High memory (from cmos): %i Kib", mem.extended.total); 109 } 110 } 111 112 MYINFO("Assigning fixed memory ranges (Memory map)"); 113 auto& memmap = memory_map(); 114 115 // @ Todo: The first ~600k of memory is free for use. What can we put there? 116 memmap.assign_range({0x0009FC00, 0x0009FFFF, 117 "EBDA", "Extended BIOS data area"}); 118 memmap.assign_range({0x000A0000, 0x000FFFFF, 119 "VGA/ROM", "Memory mapped video memory"}); 120 memmap.assign_range({(uintptr_t)&_LOAD_START_, (uintptr_t)&_end, 121 "ELF", "Your service binary including OS"}); 122 123 // @note for security we don't want to expose this 124 memmap.assign_range({(uintptr_t)&_end + 1, heap_begin - 1, 125 "Pre-heap", "Heap randomization area (not for use))"}); 126 127 memmap.assign_range({0x4000, 0x5fff, "Statman", "Statistics"}); 128 memmap.assign_range({0xA000, 0x9fbff, "Kernel / service main stack"}); 129 130 // Create ranges for heap and the remaining address space 131 // @note : since the maximum size of a span is unsigned (ptrdiff_t) we may need more than one 132 uintptr_t addr_max = std::numeric_limits<std::size_t>::max(); 133 uintptr_t span_max = std::numeric_limits<std::ptrdiff_t>::max(); 134 135 // Give the rest of physical memory to heap 136 heap_max_ = ((0x100000 + high_memory_size_) & 0xffff0000) - 1; 137 138 // ...Unless it's more than the maximum for a range 139 // @note : this is a stupid way to limit the heap - we'll change it, but not until 140 // we have a good solution. 141 heap_max_ = std::min(span_max, heap_max_); 142 143 memmap.assign_range({heap_begin, heap_max_, 144 "Heap", "Dynamic memory", heap_usage }); 145 146 uintptr_t unavail_start = 0x100000 + high_memory_size_; 147 size_t interval = std::min(span_max, addr_max - unavail_start) - 1; 148 uintptr_t unavail_end = unavail_start + interval; 149 150 while (unavail_end < addr_max){ 151 INFO2("* Unavailable memory: 0x%x - 0x%x", unavail_start, unavail_end); 152 memmap.assign_range({unavail_start, unavail_end, 153 "N/A", "Reserved / outside physical range" }); 154 unavail_start = unavail_end + 1; 155 interval = std::min(span_max, addr_max - unavail_start); 156 // Increment might wrapped around 157 if (unavail_start > unavail_end + interval or unavail_start + interval == addr_max){ 158 INFO2("* Last chunk of memory: 0x%x - 0x%x", unavail_start, addr_max); 159 memmap.assign_range({unavail_start, addr_max, 160 "N/A", "Reserved / outside physical range" }); 161 break; 162 } 163 164 unavail_end += interval; 165 } 166 167 168 MYINFO("Printing memory map"); 169 170 for (const auto &i : memory_map()) 171 INFO2("* %s",i.second.to_string().c_str()); 172 173 // Set up interrupt and exception handlers 174 IRQ_manager::init(); 175 176 // read ACPI tables 177 hw::ACPI::init(); 178 179 // setup APIC, APIC timer, SMP etc. 180 hw::APIC::init(); 181 182 // enable interrupts 183 INFO("BSP", "Enabling interrupts"); 184 IRQ_manager::enable_interrupts(); 185 186 // Initialize the Interval Timer 187 hw::PIT::init(); 188 189 // Initialize PCI devices 190 PCI_manager::init(); 191 192 // Print registered devices 193 hw::Devices::print_devices(); 194 195 // Estimate CPU frequency 196 MYINFO("Estimating CPU-frequency"); 197 INFO2("|"); 198 INFO2("+--(10 samples, %f sec. interval)", 199 (hw::PIT::frequency() / _cpu_sampling_freq_divider_).count()); 200 INFO2("|"); 201 202 // TODO: Debug why actual measurments sometimes causes problems. Issue #246. 203 cpu_mhz_ = hw::PIT::CPU_frequency(); 204 INFO2("+--> %f MHz", cpu_mhz_.count()); 205 206 // cpu_mhz must be known before we can start timer system 207 /// initialize timers hooked up to APIC timer 208 Timers::init( 209 // timer start function 210 hw::APIC_Timer::oneshot, 211 // timer stop function 212 hw::APIC_Timer::stop); 213 214 // initialize BSP APIC timer 215 hw::APIC_Timer::init( 216 [] { 217 // set final interrupt handler 218 hw::APIC_Timer::set_handler(Timers::timers_handler); 219 // signal that kernel is done with everything 220 Service::ready(); 221 // signal ready 222 // NOTE: this executes the first timers, so we 223 // don't want to run this before calling Service ready 224 Timers::ready(); 225 }); 226 227 // Realtime/monotonic clock 228 RTC::init(); 229 booted_at_ = RTC::now(); 230 231 // sleep statistics 232 os_cycles_hlt = &Statman::get().create( 233 Stat::UINT64, std::string("cpu0.cycles_hlt")).get_uint64(); 234 os_cycles_total = &Statman::get().create( 235 Stat::UINT64, std::string("cpu0.cycles_total")).get_uint64(); 236 237 OS::ready_ = false; 238 239 // Trying custom initialization functions 240 MYINFO("Calling custom initialization functions"); 241 for (auto init : custom_init_) { 242 INFO2("* Calling %s", init.name_); 243 try{ 244 init.func_(); 245 } catch(std::exception& e){ 246 MYINFO("Exception thrown when calling custom init: %s", e.what()); 247 } catch(...){ 248 MYINFO("Unknown exception when calling custom initialization function"); 249 } 250 } 251 252 MYINFO("Waiting for ready: %d", OS::ready_); 253 while(!OS::ready_) { 254 OS::block(); 255 } 256 257 // Everything is ready 258 MYINFO("Starting %s", Service::name().c_str()); 259 FILLINE('='); 260 // initialize random seed based on cycles since start 261 srand(cycles_since_boot() & 0xFFFFFFFF); 262 263 // begin service start 264 Service::start(os_cmdline); 265 266 event_loop(); 267 } 268 269 void OS::register_custom_init(Custom_init delg, const char* name){ 270 MYINFO("Registering custom init function %s", name); 271 custom_init_.emplace_back(delg, name); 272 } 273 274 uintptr_t OS::heap_max() { 275 // Before the memory map is populated 276 if (UNLIKELY(memory_map().empty())) 277 return heap_max_; 278 279 // After memory map is populated 280 return memory_map().at(heap_begin).addr_end(); 281 } 282 283 uintptr_t OS::heap_usage() noexcept { 284 return (uintptr_t) (heap_end - heap_begin); 285 } 286 287 uint64_t OS::get_cycles_halt() noexcept { 288 return *os_cycles_hlt; 289 } 290 __attribute__((noinline)) 291 void OS::halt() { 292 *os_cycles_total = cycles_since_boot(); 293 asm volatile("hlt"); 294 295 // add a global symbol here so we can quickly discard 296 // event loop from stack sampling 297 asm volatile( 298 ".global _irq_cb_return_location;\n" 299 "_irq_cb_return_location:" ); 300 // Count sleep cycles 301 *os_cycles_hlt += cycles_since_boot() - *os_cycles_total; 302 } 303 304 uint64_t OS::get_cycles_total() noexcept { 305 return *os_cycles_total; 306 } 307 308 void OS::event_loop() { 309 FILLINE('='); 310 printf(" IncludeOS %s\n", version().c_str()); 311 printf(" +--> Running [ %s ]\n", Service::name().c_str()); 312 FILLINE('~'); 313 314 while (power_) { 315 IRQ_manager::get().process_interrupts(); 316 debug2("OS going to sleep.\n"); 317 OS::halt(); 318 } 319 320 // Cleanup 321 Service::stop(); 322 // ACPI shutdown sequence 323 hw::ACPI::shutdown(); 324 } 325 326 void OS::shutdown() 327 { 328 power_ = false; 329 } 330 331 void OS::add_stdout(OS::print_func func) 332 { 333 os_print_handlers.push_back(func); 334 } 335 size_t OS::print(const char* str, const size_t len) { 336 // Output callbacks 337 for (auto& func : os_print_handlers) 338 func(str, len); 339 return len; 340 } 341 342 void OS::multiboot(uint32_t boot_magic, uint32_t boot_addr){ 343 MYINFO("Booted with multiboot"); 344 INFO2("* magic value: 0x%x Multiboot info at 0x%x", boot_magic, boot_addr); 345 346 multiboot_info_t* bootinfo = (multiboot_info_t*) boot_addr; 347 348 if (! bootinfo->flags & MULTIBOOT_INFO_MEMORY) { 349 INFO2("* No memory info provided in multiboot info"); 350 return; 351 } 352 353 uint32_t mem_low_start = 0; 354 uint32_t mem_low_end = (bootinfo->mem_lower * 1024) - 1; 355 uint32_t mem_low_kb = bootinfo->mem_lower; 356 uint32_t mem_high_start = 0x100000; 357 uint32_t mem_high_end = mem_high_start + (bootinfo->mem_upper * 1024) - 1; 358 uint32_t mem_high_kb = bootinfo->mem_upper; 359 360 OS::low_memory_size_ = mem_low_kb * 1024; 361 OS::high_memory_size_ = mem_high_kb * 1024; 362 363 INFO2("* Valid memory (%i Kib):", mem_low_kb + mem_high_kb); 364 INFO2("\t 0x%08x - 0x%08x (%i Kib)", 365 mem_low_start, mem_low_end, mem_low_kb); 366 INFO2("\t 0x%08x - 0x%08x (%i Kib)", 367 mem_high_start, mem_high_end, mem_high_kb); 368 INFO2(""); 369 370 if (bootinfo->flags & MULTIBOOT_INFO_CMDLINE) { 371 INFO2("* Booted with parameters @ %p: %s",(void*)bootinfo->cmdline, (char*)bootinfo->cmdline); 372 os_cmdline = (char*) bootinfo->cmdline; 373 374 } 375 376 if (bootinfo->flags & MULTIBOOT_INFO_MEM_MAP) { 377 INFO2("* Multiboot provided memory map (%i entries @ %p)", 378 bootinfo->mmap_length / sizeof(multiboot_memory_map_t), (void*)bootinfo->mmap_addr); 379 gsl::span<multiboot_memory_map_t> mmap { reinterpret_cast<multiboot_memory_map_t*>(bootinfo->mmap_addr), 380 (int)(bootinfo->mmap_length / sizeof(multiboot_memory_map_t))}; 381 382 for (auto map : mmap) { 383 const char* str_type = map.type & MULTIBOOT_MEMORY_AVAILABLE ? "FREE" : "RESERVED"; 384 INFO2("\t 0x%08llx - 0x%08llx %s (%llu Kb.)", 385 map.addr, map.addr + map.len - 1, str_type, map.len / 1024 ); 386 } 387 printf("\n"); 388 } 389 } 390 391 /// SERVICE RELATED /// 392 393 // Moved to kernel/service_stub.cpp