aboutsummaryrefslogtreecommitdiff
path: root/libs/multiboot2
diff options
context:
space:
mode:
authorFelix Morgner <felix.morgner@ost.ch>2025-07-14 15:21:32 +0000
committerFelix Morgner <felix.morgner@ost.ch>2025-07-14 15:21:32 +0000
commit22fbbf849497c32f5b237ab70e9ed8aef63e54cf (patch)
treece44e8cd25661f2818181bdbe3f0668d3c6e0837 /libs/multiboot2
parent9bfd5652e865505ae73f5ae3ba70f384d7418e84 (diff)
downloadteachos-22fbbf849497c32f5b237ab70e9ed8aef63e54cf.tar.xz
teachos-22fbbf849497c32f5b237ab70e9ed8aef63e54cf.zip
libs: extract multiboot library
Diffstat (limited to 'libs/multiboot2')
-rw-r--r--libs/multiboot2/CMakeLists.txt26
-rw-r--r--libs/multiboot2/include/multiboot2/constants.hpp20
-rw-r--r--libs/multiboot2/include/multiboot2/impl/data.hpp130
-rw-r--r--libs/multiboot2/include/multiboot2/impl/ids.hpp79
-rw-r--r--libs/multiboot2/include/multiboot2/impl/iterator.hpp63
-rw-r--r--libs/multiboot2/include/multiboot2/impl/tag.hpp113
-rw-r--r--libs/multiboot2/include/multiboot2/information.hpp160
7 files changed, 591 insertions, 0 deletions
diff --git a/libs/multiboot2/CMakeLists.txt b/libs/multiboot2/CMakeLists.txt
new file mode 100644
index 0000000..af56d5a
--- /dev/null
+++ b/libs/multiboot2/CMakeLists.txt
@@ -0,0 +1,26 @@
+cmake_minimum_required(VERSION "3.27")
+
+project("multiboot2"
+ LANGUAGES CXX
+ VERSION "1.0.0"
+)
+
+add_library("multiboot2" INTERFACE)
+add_library("multiboot2::multiboot2" ALIAS "multiboot2")
+
+target_sources("multiboot2" INTERFACE
+ FILE_SET HEADERS
+ BASE_DIRS "include"
+ FILES
+ "include/multiboot2/constants.hpp"
+ "include/multiboot2/information.hpp"
+
+ "include/multiboot2/impl/data.hpp"
+ "include/multiboot2/impl/ids.hpp"
+ "include/multiboot2/impl/iterator.hpp"
+ "include/multiboot2/impl/tag.hpp"
+)
+
+target_include_directories("multiboot2" INTERFACE
+ "include"
+)
diff --git a/libs/multiboot2/include/multiboot2/constants.hpp b/libs/multiboot2/include/multiboot2/constants.hpp
new file mode 100644
index 0000000..30d52d0
--- /dev/null
+++ b/libs/multiboot2/include/multiboot2/constants.hpp
@@ -0,0 +1,20 @@
+#ifndef MULTIBOOT2_CONSTANTS_HPP
+#define MULTIBOOT2_CONSTANTS_HPP
+
+#include "impl/ids.hpp" // IWYU pragma: export
+
+#include <cstdint>
+
+namespace multiboot2
+{
+
+ using impl::architecture_id;
+ using impl::information_id;
+ using impl::memory_type;
+ using impl::tag_id;
+
+ auto constexpr inline header_magic = std::uint32_t{0xe85250d6};
+
+} // namespace multiboot2
+
+#endif
diff --git a/libs/multiboot2/include/multiboot2/impl/data.hpp b/libs/multiboot2/include/multiboot2/impl/data.hpp
new file mode 100644
index 0000000..367e8cb
--- /dev/null
+++ b/libs/multiboot2/include/multiboot2/impl/data.hpp
@@ -0,0 +1,130 @@
+#ifndef MULTIBOOT2_IMPL_DATA_HPP
+#define MULTIBOOT2_IMPL_DATA_HPP
+
+#include "multiboot2/impl/ids.hpp"
+
+#include <cstdint>
+
+namespace multiboot2::impl
+{
+ template<auto Id>
+ struct tag_data
+ {
+ auto constexpr inline static id = Id;
+ };
+
+ /**
+ * @brief Basic system memory information
+ */
+ struct basic_memory_data : tag_data<information_id::BASIC_MEMORY_INFO>
+ {
+ /**
+ * @brief Amount of lower memory (below 1MiB) available to the system.
+ *
+ * The maximum possible value for this field is 640 KiB.
+ */
+ std::uint32_t const lower_KiB;
+
+ /**
+ * @brief Amount of upper memory (above 1MiB) available to the system.
+ *
+ * The maximum possible value for this field is the address of the first upper memory hole minus 1MiB.
+ */
+ std::uint32_t const upper_KiB;
+ };
+
+ /**
+ * @brief Device the image got loaded from
+ */
+ struct bios_boot_device_data : tag_data<information_id::BOOTDEV>
+ {
+ /**
+ * @brief BIOS device number as understood by INT 13h.
+ */
+ std::uint32_t device_number;
+
+ /**
+ * @brief Number of the primary partition.
+ */
+ std::uint32_t partition_number;
+
+ /**
+ * @brief Number the sub-partion on the primary partition.
+ */
+ std::uint32_t sub_partition_number;
+ };
+
+ /**
+ * @brief Supplied image command line
+ */
+ struct command_line_data : tag_data<information_id::CMDLINE>
+ {
+ /* This struct intentionally left blank. */
+ };
+
+ /**
+ * @brief ELF symbols of the image
+ */
+ struct elf_symbols_data : tag_data<information_id::ELF_SECTIONS>
+ {
+ std::uint32_t count;
+ std::uint32_t entry_size;
+ std::uint32_t string_table_index;
+ };
+
+ /**
+ * @brief Name of the boot loader
+ */
+ struct loader_name_data : tag_data<information_id::BOOT_LOADER_NAME>
+ {
+ /* This struct intentionally left blank. */
+ };
+
+ /**
+ * @brief Detailed map of the memory regions present in the system
+ *
+ */
+ struct memory_map_data : tag_data<information_id::MEMORY_MAP>
+ {
+ /**
+ * @brief A region of memory
+ */
+ struct region
+ {
+ /**
+ * @brief Check if the memory described by this region is available for use.
+ */
+ auto constexpr available() const noexcept { return type == memory_type::AVAILABLE; }
+
+ /**
+ * @brief Start address of this region
+ */
+ std::uint64_t const base;
+
+ /**
+ * @brief Size of this region in bytes.
+ */
+ std::uint64_t const size_in_B;
+
+ /**
+ * @brief Type of this region.
+ */
+ memory_type const type;
+
+ std::uint32_t : 0;
+ };
+
+ /**
+ * @brief Size of each entry present in the map
+ */
+ std::uint32_t const entry_size;
+
+ /**
+ * @brief Version of each entry present in the map
+ */
+ std::uint32_t const entry_version;
+ };
+
+} // namespace multiboot2::impl
+
+#endif \ No newline at end of file
diff --git a/libs/multiboot2/include/multiboot2/impl/ids.hpp b/libs/multiboot2/include/multiboot2/impl/ids.hpp
new file mode 100644
index 0000000..3a7215e
--- /dev/null
+++ b/libs/multiboot2/include/multiboot2/impl/ids.hpp
@@ -0,0 +1,79 @@
+#ifndef MULTIBOOT2_IMPL_IDS_HPP
+#define MULTIBOOT2_IMPL_IDS_HPP
+
+#include <cstdint>
+
+namespace multiboot2::impl
+{
+
+ /**
+ * @brief Information tag IDs.
+ */
+ enum struct information_id : std::uint32_t
+ {
+ END, ///< Signals final tag for the multiboot2 information structure.
+ CMDLINE, ///< Contains the command line string.
+ BOOT_LOADER_NAME, ///< Contains the name of the boot loader booting the kernel.
+ MODULE, ///< Indicates the boot module which was loaded along the kernel image.
+ BASIC_MEMORY_INFO, ///< Contains the amount of lower (0MB start address) and upper memory (1MB start address).
+ BOOTDEV, ///< Indicates which BIOS disk device the hoot loader has loaded the OS image from.
+ MEMORY_MAP, ///< Describes the memory layout of the system with individual areas and their flags.
+ VBE_INFO, ///< Includes information to access and utilize the device GPU.
+ FRAMEBUFFER, ///< VBE framebuffer information.
+ ELF_SECTIONS, ///< Includes list of all section headers from the loaded ELF kernel.
+ APM_INFO, ///< Advanced Power Management information.
+ EFI32, ///< EFI 32 bit system table pointer.
+ EFI64, ///< EFI 64 bit system table pointer.
+ SMBIOS, ///< Contains copy of all Sytem Management BIOS tables.
+ ACPI_OLD, ///< Contains copy of RSDP as defined per ACPI1.0 specification.
+ ACPI_NEW, ///< Contains copy of RSDP as defined per ACPI2.0 or later specification.
+ NETWORK, ///< Contains network information specified specified as DHCP.
+ EFI_MEMORY_MAP, ///< Contains EFI memory map.
+ EFI_BS_NOT_TERMINATED, ///< Indicated ExitBootServies wasn't called.
+ EFI32_IMAGE_HANDLE, ///< EFI 32 bit image handle pointer.
+ EFI64_IMAGE_HANDLE, ///< EFI 64 bit imae handle pointer.
+ LOAD_BASE_ADDRESS ///< Contains image load base physical address.
+ };
+
+ /**
+ * @brief Header tag IDs.
+ */
+ enum struct tag_id : std::uint32_t
+ {
+ END,
+ INFORMATION_REQUEST,
+ ADDRESSES,
+ ENTRY_ADDRESS,
+ CONSOLE_FLAGS,
+ PREFERRED_FRAMEBUFFER_MODE,
+ PAGE_ALIGN_MODULES,
+ EFI_BOOT_SERVICES_SUPPORTED,
+ EFI32_ENTRY_ADDRESS,
+ EFI64_ENTRY_ADDRESS,
+ RELOCATABLE,
+ };
+
+ /**
+ * @brief System architecture IDs.
+ */
+ enum struct architecture_id : std::uint32_t
+ {
+ I386 = 0,
+ MIPS32 = 4,
+ };
+
+ /**
+ * @brief Memory type IDs.
+ */
+ enum struct memory_type : std::uint32_t
+ {
+ AVAILABLE = 1,
+ RESERVED,
+ ACPI_RECLAIMABLE,
+ NON_VOLATILE_STORAGE,
+ BAD_RAM,
+ };
+
+} // namespace multiboot2::impl
+
+#endif \ No newline at end of file
diff --git a/libs/multiboot2/include/multiboot2/impl/iterator.hpp b/libs/multiboot2/include/multiboot2/impl/iterator.hpp
new file mode 100644
index 0000000..b84ef2c
--- /dev/null
+++ b/libs/multiboot2/include/multiboot2/impl/iterator.hpp
@@ -0,0 +1,63 @@
+#ifndef MULTIBOOT2_IMPL_INFORMATION_ITERATOR_HPP
+#define MULTIBOOT2_IMPL_INFORMATION_ITERATOR_HPP
+
+#include "multiboot2/impl/ids.hpp"
+#include "multiboot2/impl/tag.hpp"
+
+#include <cstddef>
+#include <iterator>
+#include <optional>
+
+namespace multiboot2::impl
+{
+
+ struct information_iterator
+ {
+ using iterator_category = std::forward_iterator_tag;
+ using value_type = impl::tag_header;
+ using pointer = value_type const *;
+ using reference = value_type const &;
+ using difference_type = std::ptrdiff_t;
+
+ constexpr information_iterator() = default;
+
+ constexpr explicit information_iterator(impl::tag_header const * offset)
+ : m_current(offset)
+ {
+ }
+
+ auto constexpr operator==(information_iterator const &) const noexcept -> bool = default;
+
+ auto constexpr operator*() const noexcept -> reference { return *(m_current.value()); }
+
+ auto constexpr operator->() const noexcept -> pointer { return m_current.value(); }
+
+ auto constexpr operator++() noexcept -> information_iterator &
+ {
+ if (m_current)
+ {
+ if (auto next = m_current.value()->next(); next->information_id() != information_id::END)
+ {
+ m_current = next;
+ }
+ m_current.reset();
+ }
+ return *this;
+ }
+
+ auto constexpr operator++(int) noexcept -> information_iterator
+ {
+ auto copy = *this;
+ ++(*this);
+ return copy;
+ }
+
+ private:
+ std::optional<impl::tag_header const *> m_current{};
+ };
+
+ static_assert(std::input_or_output_iterator<information_iterator>);
+
+} // namespace multiboot2::impl
+
+#endif \ No newline at end of file
diff --git a/libs/multiboot2/include/multiboot2/impl/tag.hpp b/libs/multiboot2/include/multiboot2/impl/tag.hpp
new file mode 100644
index 0000000..f7471a4
--- /dev/null
+++ b/libs/multiboot2/include/multiboot2/impl/tag.hpp
@@ -0,0 +1,113 @@
+#ifndef MULTIBOOT2_IMPL_TAG_HPP
+#define MULTIBOOT2_IMPL_TAG_HPP
+
+#include "multiboot2/impl/ids.hpp"
+
+#include <bit>
+#include <cstddef>
+#include <string_view>
+
+namespace multiboot2::impl
+{
+
+ /**
+ * @brief Header data and functionality shared by all tags.
+ */
+ struct tag_header
+ {
+ tag_header()
+ : m_id{}
+ , m_size{}
+ {
+ }
+
+ tag_header(tag_header const * data)
+ : tag_header{*data}
+ {
+ }
+
+ auto full_size() const noexcept -> std::size_t { return (m_size + 7) & (~7); }
+
+ auto information_id() const noexcept -> impl::information_id const & { return m_id; }
+
+ auto next() const noexcept -> tag_header const *
+ {
+ return std::bit_cast<tag_header const *>(std::bit_cast<std::byte const *>(this) + full_size());
+ }
+
+ auto unaligned_size() const noexcept -> std::uint32_t { return m_size; }
+
+ protected:
+ impl::information_id const m_id;
+ std::uint32_t const m_size;
+ };
+
+ /**
+ * @brief A tag containing no variable length array data.
+ */
+ template<typename Data>
+ struct tag : tag_header, Data
+ {
+ tag()
+ : tag_header{}
+ , Data{}
+ {
+ }
+
+ explicit tag(tag_header const * header)
+ requires(sizeof(tag) > sizeof(tag_header))
+ : tag_header{header}
+ , Data{*std::bit_cast<Data const *>(header + 1)}
+ {
+ }
+
+ explicit tag(tag_header const * header)
+ requires(sizeof(tag) == sizeof(tag_header))
+ : tag_header{header}
+ , Data{}
+ {
+ }
+ };
+
+ /**
+ * @brief A tag containing variable length array data.
+ */
+ template<typename Data, typename VlaData, template<typename> typename Range>
+ struct vla_tag : tag<Data>
+ {
+ using range_type = Range<VlaData>;
+
+ vla_tag()
+ : tag<Data>{}
+ , m_vla{}
+ {
+ }
+
+ explicit vla_tag(tag_header const * header)
+ : tag<Data>{header}
+ , m_vla{vla_start(header), vla_size(header)}
+ {
+ }
+
+ protected:
+ auto static vla_start(tag_header const * header) noexcept -> VlaData *
+ {
+ auto raw = std::bit_cast<std::byte const *>(header);
+ auto start = raw + sizeof(tag<Data>);
+ return std::bit_cast<VlaData *>(start);
+ }
+
+ auto static vla_size(tag_header const * header) noexcept -> std::size_t
+ {
+ auto size = (header->unaligned_size() - sizeof(tag<Data>) -
+ std::is_same_v<range_type, std::basic_string_view<VlaData>> * 1) /
+ sizeof(VlaData);
+ return size;
+ }
+
+ range_type const m_vla;
+ };
+
+} // namespace multiboot2::impl
+
+#endif \ No newline at end of file
diff --git a/libs/multiboot2/include/multiboot2/information.hpp b/libs/multiboot2/include/multiboot2/information.hpp
new file mode 100644
index 0000000..04ba183
--- /dev/null
+++ b/libs/multiboot2/include/multiboot2/information.hpp
@@ -0,0 +1,160 @@
+#ifndef JOS_MULTIBOOT2_INFORMATION_HPP
+#define JOS_MULTIBOOT2_INFORMATION_HPP
+
+#include "impl/data.hpp"
+#include "impl/iterator.hpp"
+#include "impl/tag.hpp"
+
+#include <algorithm>
+#include <cstddef>
+#include <cstdint>
+#include <optional>
+#include <span>
+#include <string_view>
+
+namespace multiboot2
+{
+
+ /**
+ * @copydoc multiboot2::impl::basic_memory_data
+ */
+ struct basic_memory : impl::tag<impl::basic_memory_data>
+ {
+ using tag::tag;
+ };
+
+ /**
+ * @copydoc multiboot2::impl::bios_boot_device_data
+ */
+ struct bios_boot_device : impl::tag<impl::bios_boot_device_data>
+ {
+ using tag::tag;
+ };
+
+ /**
+ * @copydoc multiboot2::impl::command_line_data
+ */
+ struct command_line : impl::vla_tag<impl::command_line_data, char, std::basic_string_view>
+ {
+ using vla_tag::vla_tag;
+
+ /**
+ * @brief The command line string
+ */
+ auto string() const noexcept -> range_type { return m_vla; }
+ };
+
+ /**
+ * @copydoc multiboot2::impl::elf_symbols_data
+ */
+ struct elf_symbols : impl::vla_tag<impl::elf_symbols_data, std::byte const, std::span>
+ {
+ using vla_tag::vla_tag;
+
+ using iterator = range_type::iterator;
+
+ auto data() const noexcept -> range_type { return m_vla; }
+ };
+
+ /**
+ * @copydoc multiboot2::impl::loader_name_data
+ */
+ struct loader_name : impl::vla_tag<impl::loader_name_data, char, std::basic_string_view>
+ {
+ using vla_tag::vla_tag;
+
+ /**
+ * @brief The name of the bootloader
+ */
+ auto string() const noexcept -> std::string_view { return m_vla; }
+ };
+
+ /**
+ * @copydoc multiboot2::impl::memory_map_data
+ */
+ struct memory_map : impl::vla_tag<impl::memory_map_data, impl::memory_map_data::region, std::span>
+ {
+ using vla_tag::vla_tag;
+
+ using iterator = range_type::iterator;
+
+ auto begin() const noexcept -> iterator { return regions().begin(); }
+
+ auto end() const noexcept -> iterator { return regions().end(); }
+
+ /**
+ * @brief The available memory regions
+ */
+ auto regions() const noexcept -> range_type { return m_vla; }
+ };
+
+ struct information_view
+ {
+ using iterator = impl::information_iterator;
+ using value_type = impl::information_iterator::value_type;
+ using pointer = impl::information_iterator::pointer;
+ using reference = impl::information_iterator::reference;
+
+ auto size_bytes() const noexcept -> std::size_t { return m_size; }
+
+ // Range access
+
+ auto begin() const noexcept -> iterator { return iterator{&m_tags}; }
+
+ auto end() const noexcept -> iterator { return iterator{}; }
+
+ // Tag access
+
+ template<typename Tag>
+ auto has() const noexcept -> bool
+ {
+ return get<Tag>().has_value();
+ }
+
+ auto maybe_basic_memory() const noexcept -> std::optional<basic_memory> { return get<multiboot2::basic_memory>(); }
+
+ auto basic_memory() const -> basic_memory { return maybe_basic_memory().value(); }
+
+ auto maybe_bios_boot_device() const noexcept -> std::optional<bios_boot_device>
+ {
+ return get<multiboot2::bios_boot_device>();
+ }
+
+ auto bios_boot_device() const -> bios_boot_device { return maybe_bios_boot_device().value(); }
+
+ auto maybe_command_line() const noexcept -> std::optional<command_line> { return get<multiboot2::command_line>(); }
+
+ auto command_line() const -> command_line { return maybe_command_line().value(); }
+
+ auto maybe_elf_symbols() const noexcept -> std::optional<elf_symbols> { return get<multiboot2::elf_symbols>(); }
+
+ auto elf_symbols() const -> elf_symbols { return maybe_elf_symbols().value(); }
+
+ auto maybe_loader_name() const noexcept -> std::optional<loader_name> { return get<multiboot2::loader_name>(); }
+
+ auto loader_name() const -> loader_name { return maybe_loader_name().value(); }
+
+ auto maybe_memory_map() const noexcept -> std::optional<memory_map> { return get<multiboot2::memory_map>(); }
+
+ auto memory_map() const -> memory_map { return maybe_memory_map().value(); }
+
+ private:
+ template<typename Tag>
+ auto constexpr get() const noexcept -> std::optional<Tag>
+ {
+ if (auto found = std::ranges::find_if(*this, [](auto tag) { return tag.information_id() == Tag::id; });
+ found != end())
+ {
+ return Tag{&*found};
+ }
+ return std::nullopt;
+ }
+
+ [[maybe_unused]] uint32_t const m_size{};
+ uint32_t : 32;
+ impl::tag_header const m_tags{};
+ };
+
+} // namespace multiboot2
+
+#endif \ No newline at end of file