aboutsummaryrefslogtreecommitdiff
path: root/arch/x86_64/src/memory/multiboot/reader.cpp
blob: e35baf3a9d561d1e26b5ef76e4ba649005f3eec6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#include "arch/memory/multiboot/reader.hpp"

#include "arch/boot/pointers.hpp"
#include "arch/exception_handling/assert.hpp"
#include "arch/memory/multiboot/elf_symbols_section.hpp"
#include "arch/memory/multiboot/info.hpp"

#include <algorithm>

namespace teachos::arch::memory::multiboot
{
  namespace
  {
    template<typename T>
      requires std::is_pointer<T>::value
    auto align_to_8_byte_boundary(T ptr, uint32_t size) -> T
    {
      return reinterpret_cast<T>(reinterpret_cast<uint8_t *>(ptr) + ((size + 7) & ~7));
    }

    auto process_memory_map(memory_map_header * mminfo, memory_area_container::iterator & begin_area,
                            memory_area_container::iterator & end_area) -> void
    {
      auto const expected_entry_size = mminfo->entry_size;
      auto constexpr actual_entry_size = sizeof(memory_area);
      exception_handling::assert(expected_entry_size == actual_entry_size,
                                 "[Multiboot Reader] Unexpected memory area entry size");

      auto const total_size = mminfo->info.size;
      auto const total_entries_size = total_size - sizeof(memory_map_header) + actual_entry_size;
      auto const number_of_entries = total_entries_size / actual_entry_size;

      begin_area = memory_area_container::iterator{&mminfo->entries};
      end_area = begin_area + number_of_entries;
    }

    auto process_elf_sections(elf_symbols_section_header * symbol, std::size_t & kernel_start, std::size_t & kernel_end,
                              elf_section_header_container::iterator & begin_kernel,
                              elf_section_header_container::iterator & end_kernel) -> void
    {
      auto const expected_entry_size = symbol->entry_size;
      auto constexpr actual_entry_size = sizeof(elf_section_header);
      exception_handling::assert(expected_entry_size == actual_entry_size,
                                 "[Multiboot Reader] Unexpected elf section header entry size");

      auto const expected_total_size = symbol->info.size;
      auto const actual_total_entry_size = actual_entry_size * symbol->number_of_sections;
      auto constexpr actual_total_section_size = sizeof(elf_symbols_section_header) - sizeof(uint32_t);
      auto const actual_total_size = actual_total_entry_size + actual_total_section_size;
      exception_handling::assert(expected_total_size == actual_total_size,
                                 "[Multiboot Reader] Unexpected elf symbols section header total size");

      begin_kernel = elf_section_header_container::iterator{reinterpret_cast<elf_section_header *>(&symbol->end)};
      end_kernel = begin_kernel + symbol->number_of_sections;
      exception_handling::assert(begin_kernel->is_null(),
                                 "[Multiboot Reader] Elf symbols section not starting with SHT_NULL section");

      elf_section_header_container container{begin_kernel, end_kernel};

      auto const elf_section_with_lowest_virtual_address =
          std::ranges::min_element(container, [](elf_section_header const & a, elf_section_header const & b) {
            return a.virtual_address < b.virtual_address;
          });

      auto const elf_section_with_highest_virtual_address =
          std::ranges::max_element(container, [](elf_section_header const & a, elf_section_header const & b) {
            auto a_virtual_address_end = a.virtual_address + a.section_size;
            auto b_virtual_address_end = b.virtual_address + b.section_size;
            return a_virtual_address_end < b_virtual_address_end;
          });

      auto const symbol_table_section_count = std::ranges::count_if(container, [](elf_section_header const & section) {
        return section.type == elf_section_type::DYNAMIC_SYMBOL_TABLE || section.type == elf_section_type::SYMBOL_TABLE;
      });
      auto const dynamic_section_count = std::ranges::count_if(
          container, [](elf_section_header const & section) { return section.type == elf_section_type::DYNAMIC; });

      exception_handling::assert(
          symbol_table_section_count == 1U,
          "[Multiboot Reader] ELF Specifications allows only (1) symbol table section, but got more");
      exception_handling::assert(
          dynamic_section_count <= 1U,
          "[Multiboot Reader] ELF Specifications allows only (1) or less dynamic sections, but got more");

      auto const lowest_elf_section = *elf_section_with_lowest_virtual_address;
      kernel_start = lowest_elf_section.virtual_address;

      auto const highest_elf_section = *elf_section_with_highest_virtual_address;
      kernel_end = highest_elf_section.virtual_address + highest_elf_section.section_size;
    }
  }  // namespace

  auto read_multiboot2() -> memory_information
  {
    memory_information mem_info{UINT64_MAX,
                                0U,
                                elf_section_header_container::iterator{},
                                elf_section_header_container::iterator{},
                                boot::multiboot_information_pointer,
                                0U,
                                memory_area_container::iterator{},
                                memory_area_container::iterator{}};

    auto const multiboot_information_pointer = reinterpret_cast<info_header *>(boot::multiboot_information_pointer);
    auto const multiboot_tag = &multiboot_information_pointer->tags;
    mem_info.multiboot_end = mem_info.multiboot_start + multiboot_information_pointer->total_size;

    for (auto tag = multiboot_tag; tag->type != tag_type::END; tag = align_to_8_byte_boundary(tag, tag->size))
    {
      switch (tag->type)
      {
        case tag_type::ELF_SECTIONS: {
          auto const symbol = reinterpret_cast<elf_symbols_section_header *>(tag);
          process_elf_sections(symbol, mem_info.kernel_start, mem_info.kernel_end, mem_info.begin_kernel,
                               mem_info.end_kernel);
          break;
        }
        case tag_type::MEMORY_MAP: {
          auto const mminfo = reinterpret_cast<memory_map_header *>(tag);
          process_memory_map(mminfo, mem_info.begin_area, mem_info.end_area);
          break;
        }
        default:
          // All other cases are not important and can be ignored.
          break;
      }
    }
    return mem_info;
  }
}  // namespace teachos::arch::memory::multiboot