#pragma once #include "util/number.h" #include "util/function.h" #include "memory/pointer.h" namespace multiboot { inline constexpr uint32_t magic = 0x36d76289; inline constexpr uint32_t alignment = 8; enum class tag_type : uint32_t { end = 0, cmdline = 1, boot_loader_name = 2, module = 3, basic_meminfo = 4, bootdev = 5, mmap = 6, vbe = 7, framebuffer = 8, elf_sections = 9, apm = 10, efi32 = 14, efi64 = 15, smbios = 16, acpi_old = 17, acpi_new = 18, network = 19, efi_mmap = 20, efi_bs = 21, efi32_ih = 22, efi64_ih = 23, load_base_addr = 24, }; inline constexpr const char* get_tag_name(tag_type type) { switch (type) { case tag_type::end: return "end"; case tag_type::cmdline: return "cmdline"; case tag_type::boot_loader_name: return "boot_loader_name"; case tag_type::module: return "module"; case tag_type::basic_meminfo: return "basic_meminfo"; case tag_type::bootdev: return "bootdev"; case tag_type::mmap: return "mmap"; case tag_type::vbe: return "vbe"; case tag_type::framebuffer: return "framebuffer"; case tag_type::elf_sections: return "elf_sections"; case tag_type::apm: return "apm"; case tag_type::efi32: return "efi32"; case tag_type::efi64: return "efi64"; case tag_type::smbios: return "smbios"; case tag_type::acpi_old: return "acpi_old"; case tag_type::acpi_new: return "acpi_new"; case tag_type::network: return "network"; case tag_type::efi_mmap: return "efi_mmap"; case tag_type::efi_bs: return "efi_bs"; case tag_type::efi32_ih: return "efi32_ih"; case tag_type::efi64_ih: return "efi64_ih"; case tag_type::load_base_addr: return "load_base_addr"; } return ""; } enum class framebuffer_type : uint8_t { indexed = 0, rgb = 1, ega_text = 2, }; enum class mem_type : uint32_t { available = 1, reserved = 2, acpi_reclaimable = 3, nvs = 4, badram = 5, }; inline constexpr const char* get_mem_type_name(mem_type type) { switch (type) { case mem_type::available: return "available"; case mem_type::reserved: return "reserved"; case mem_type::acpi_reclaimable: return "acpi_reclaimable"; case mem_type::nvs: return "nvs"; case mem_type::badram: return "badram"; default: return "unknown"; } } struct color { uint8_t red; uint8_t green; uint8_t blue; }; struct mem_entry { uint64_t addr; uint64_t len; mem_type type; uint32_t zero; }; struct tag { tag_type type; uint32_t size; }; struct tag_string { tag_type type; uint32_t size; char string[0]; }; struct tag_module { tag_type type; uint32_t size; uint32_t mod_start; uint32_t mod_end; char cmdline[0]; }; struct tag_basic_meminfo { tag_type type; uint32_t size; uint32_t mem_lower; uint32_t mem_upper; }; struct tag_bootdev { tag_type type; uint32_t size; uint32_t biosdev; uint32_t slice; uint32_t part; }; struct tag_mmap { tag_type type; uint32_t size; uint32_t entry_size; uint32_t entry_version; mem_entry entries[0]; }; struct vbe_info_block { uint8_t external_specification[512]; }; struct vbe_mode_info_block { uint8_t external_specification[256]; }; struct tag_vbe { tag_type type; uint32_t size; uint16_t vbe_mode; uint16_t vbe_interface_seg; uint16_t vbe_interface_off; uint16_t vbe_interface_len; vbe_info_block vbe_control_info; vbe_mode_info_block vbe_mode_info; }; struct tag_framebuffer { tag_type type; uint32_t size; uint64_t framebuffer_addr; uint32_t framebuffer_pitch; uint32_t framebuffer_width; uint32_t framebuffer_height; uint8_t framebuffer_bpp; framebuffer_type framebuffer_type; uint16_t reserved; union { struct { uint16_t framebuffer_palette_num_colors; color framebuffer_palette[0]; }; struct { uint8_t framebuffer_red_field_position; uint8_t framebuffer_red_mask_size; uint8_t framebuffer_green_field_position; uint8_t framebuffer_green_mask_size; uint8_t framebuffer_blue_field_position; uint8_t framebuffer_blue_mask_size; }; }; }; struct tag_elf_sections { tag_type type; uint32_t size; uint32_t num; uint32_t entsize; uint32_t shndx; char sections[0]; }; struct tag_apm { tag_type type; uint32_t size; uint16_t version; uint16_t cseg; uint32_t offset; uint16_t cseg_16; uint16_t dseg; uint16_t flags; uint16_t cseg_len; uint16_t cseg_16_len; uint16_t dseg_len; }; struct tag_efi32 { tag_type type; uint32_t size; uint32_t pointer; }; struct tag_efi64 { tag_type type; uint32_t size; uint64_t pointer; }; struct tag_smbios { tag_type type; uint32_t size; uint8_t major; uint8_t minor; uint8_t reserved[6]; uint8_t tables[0]; }; struct tag_old_acpi { tag_type type; uint32_t size; uint8_t rsdp[0]; }; struct tag_new_acpi { tag_type type; uint32_t size; uint8_t rsdp[0]; }; struct tag_network { tag_type type; uint32_t size; uint8_t dhcpack[0]; }; struct tag_efi_mmap { tag_type type; uint32_t size; uint32_t descr_size; uint32_t descr_vers; uint8_t efi_mmap[0]; }; struct tag_efi32_ih { tag_type type; uint32_t size; uint32_t pointer; }; struct tag_efi64_ih { tag_type type; uint32_t size; uint64_t pointer; }; struct tag_load_base_addr { tag_type type; uint32_t size; uint32_t load_base_addr; }; struct info { uint32_t total_size; uint32_t reserved; }; inline info* get_multiboot_info() { auto addr = read_symbol("__multiboot_info"); uint32_t phys = *paddr_t{addr}.access(); return paddr_t{phys}.access(); } template void visit(const tag* t, Fn&& fn) { #define MB_DISPATCH(tag_enum, TagType) \ case tag_type::tag_enum: \ if constexpr (requires { fn(static_cast(nullptr)); }) \ fn(reinterpret_cast(t)); \ break; switch (t->type) { MB_DISPATCH(cmdline, tag_string) MB_DISPATCH(boot_loader_name, tag_string) MB_DISPATCH(module, tag_module) MB_DISPATCH(basic_meminfo, tag_basic_meminfo) MB_DISPATCH(bootdev, tag_bootdev) MB_DISPATCH(mmap, tag_mmap) MB_DISPATCH(vbe, tag_vbe) MB_DISPATCH(framebuffer, tag_framebuffer) MB_DISPATCH(elf_sections, tag_elf_sections) MB_DISPATCH(apm, tag_apm) MB_DISPATCH(efi32, tag_efi32) MB_DISPATCH(efi64, tag_efi64) MB_DISPATCH(smbios, tag_smbios) MB_DISPATCH(acpi_old, tag_old_acpi) MB_DISPATCH(acpi_new, tag_new_acpi) MB_DISPATCH(network, tag_network) MB_DISPATCH(efi_mmap, tag_efi_mmap) MB_DISPATCH(efi32_ih, tag_efi32_ih) MB_DISPATCH(efi64_ih, tag_efi64_ih) MB_DISPATCH(load_base_addr, tag_load_base_addr) default: break; } #undef MB_DISPATCH } template void visit_all(const info* mb, Fn&& fn) { auto ptr = reinterpret_cast(mb) + sizeof(info); const auto end = reinterpret_cast(mb) + mb->total_size; while (ptr < end) { auto t = reinterpret_cast(ptr); if (t->type == tag_type::end) break; visit(t, fn); ptr += (t->size + alignment - 1) & ~(alignment - 1); } } template void visit_all(Fn&& fn) { visit_all(get_multiboot_info(), fn); } } // namespace multiboot