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