123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555 |
- //! Multiboot v1 library
- //!
- //! This crate is partitially modified from `https://github.com/gz/rust-multiboot` && asterinas
- //!
- //! The main structs to interact with are [`Multiboot`] for the Multiboot information
- //! passed from the bootloader to the kernel at runtime and [`Header`] for the static
- //! information passed from the kernel to the bootloader in the kernel image.
- //!
- //!
- //! # Additional documentation
- //! * https://www.gnu.org/software/grub/manual/multiboot/multiboot.html
- //! * http://git.savannah.gnu.org/cgit/grub.git/tree/doc/multiboot.texi?h=multiboot
- //!
- //! [`Multiboot`]: information/struct.Multiboot.html
- //! [`Header`]: header/struct.Header.html
- #![no_std]
- use core::ffi::CStr;
- pub const MAGIC: u32 = 0x2BADB002;
- /// The ‘boot_device’ field.
- ///
- /// Partition numbers always start from zero. Unused partition
- /// bytes must be set to 0xFF. For example, if the disk is partitioned
- /// using a simple one-level DOS partitioning scheme, then
- /// ‘part’ contains the DOS partition number, and ‘part2’ and ‘part3’
- /// are both 0xFF. As another example, if a disk is partitioned first into
- /// DOS partitions, and then one of those DOS partitions is subdivided
- /// into several BSD partitions using BSD's disklabel strategy, then ‘part1’
- /// contains the DOS partition number, ‘part2’ contains the BSD sub-partition
- /// within that DOS partition, and ‘part3’ is 0xFF.
- ///
- #[derive(Debug, Clone, Copy)]
- #[repr(C)]
- pub struct BootDevice {
- /// Contains the bios drive number as understood by
- /// the bios INT 0x13 low-level disk interface: e.g. 0x00 for the
- /// first floppy disk or 0x80 for the first hard disk.
- pub drive: u8,
- /// Specifies the top-level partition number.
- pub partition1: u8,
- /// Specifies a sub-partition in the top-level partition
- pub partition2: u8,
- /// Specifies a sub-partition in the 2nd-level partition
- pub partition3: u8,
- }
- impl BootDevice {
- /// Is partition1 a valid partition?
- pub fn partition1_is_valid(&self) -> bool {
- self.partition1 != 0xff
- }
- /// Is partition2 a valid partition?
- pub fn partition2_is_valid(&self) -> bool {
- self.partition2 != 0xff
- }
- /// Is partition3 a valid partition?
- pub fn partition3_is_valid(&self) -> bool {
- self.partition3 != 0xff
- }
- }
- impl Default for BootDevice {
- fn default() -> Self {
- Self {
- drive: 0xff,
- partition1: 0xff,
- partition2: 0xff,
- partition3: 0xff,
- }
- }
- }
- /// Representation of Multiboot Information according to specification.
- ///
- /// Reference: https://www.gnu.org/software/grub/manual/multiboot/multiboot.html#Boot-information-format
- ///
- ///```text
- /// +-------------------+
- /// 0 | flags | (required)
- /// +-------------------+
- /// 4 | mem_lower | (present if flags[0] is set)
- /// 8 | mem_upper | (present if flags[0] is set)
- /// +-------------------+
- /// 12 | boot_device | (present if flags[1] is set)
- /// +-------------------+
- /// 16 | cmdline | (present if flags[2] is set)
- /// +-------------------+
- /// 20 | mods_count | (present if flags[3] is set)
- /// 24 | mods_addr | (present if flags[3] is set)
- /// +-------------------+
- /// 28 - 40 | syms | (present if flags[4] or
- /// | | flags[5] is set)
- /// +-------------------+
- /// 44 | mmap_length | (present if flags[6] is set)
- /// 48 | mmap_addr | (present if flags[6] is set)
- /// +-------------------+
- /// 52 | drives_length | (present if flags[7] is set)
- /// 56 | drives_addr | (present if flags[7] is set)
- /// +-------------------+
- /// 60 | config_table | (present if flags[8] is set)
- /// +-------------------+
- /// 64 | boot_loader_name | (present if flags[9] is set)
- /// +-------------------+
- /// 68 | apm_table | (present if flags[10] is set)
- /// +-------------------+
- /// 72 | vbe_control_info | (present if flags[11] is set)
- /// 76 | vbe_mode_info |
- /// 80 | vbe_mode |
- /// 82 | vbe_interface_seg |
- /// 84 | vbe_interface_off |
- /// 86 | vbe_interface_len |
- /// +-------------------+
- /// 88 | framebuffer_addr | (present if flags[12] is set)
- /// 96 | framebuffer_pitch |
- /// 100 | framebuffer_width |
- /// 104 | framebuffer_height|
- /// 108 | framebuffer_bpp |
- /// 109 | framebuffer_type |
- /// 110-115 | color_info |
- /// +-------------------+
- ///```
- ///
- #[allow(dead_code)]
- #[derive(Debug, Copy, Clone)]
- #[repr(C, packed)]
- pub struct MultibootInfo {
- /// Indicate whether the below field exists.
- flags: u32,
- /// Physical memory low.
- mem_lower: u32,
- /// Physical memory high.
- mem_upper: u32,
- /// Indicates which BIOS disk device the boot loader loaded the OS image from.
- boot_device: BootDevice,
- /// Command line passed to kernel.
- cmdline: u32,
- /// Modules count.
- pub mods_count: u32,
- /// The start address of modules list, each module structure format:
- /// ```text
- /// +-------------------+
- /// 0 | mod_start |
- /// 4 | mod_end |
- /// +-------------------+
- /// 8 | string |
- /// +-------------------+
- /// 12 | reserved (0) |
- /// +-------------------+
- /// ```
- mods_paddr: u32,
- /// If flags[4] = 1, then the field starting at byte 28 are valid:
- /// ```text
- /// +-------------------+
- /// 28 | tabsize |
- /// 32 | strsize |
- /// 36 | addr |
- /// 40 | reserved (0) |
- /// +-------------------+
- /// ```
- /// These indicate where the symbol table from kernel image can be found.
- ///
- /// If flags[5] = 1, then the field starting at byte 28 are valid:
- /// ```text
- /// +-------------------+
- /// 28 | num |
- /// 32 | size |
- /// 36 | addr |
- /// 40 | shndx |
- /// +-------------------+
- /// ```
- /// These indicate where the section header table from an ELF kernel is,
- /// the size of each entry, number of entries, and the string table used as the index of names.
- symbols: [u8; 16],
- memory_map_len: u32,
- memory_map_paddr: u32,
- drives_length: u32,
- drives_addr: u32,
- config_table: u32,
- /// bootloader name paddr
- pub boot_loader_name: u32,
- apm_table: u32,
- vbe_table: VbeInfo,
- pub framebuffer_table: FramebufferTable,
- }
- impl MultibootInfo {
- /// If true, then the `mem_upper` and `mem_lower` fields are valid.
- pub const FLAG_MEMORY_BOUNDS: u32 = 1 << 0;
- /// If true, then the `boot_device` field is valid.
- pub const FLAG_BOOT_DEVICE: u32 = 1 << 1;
- /// If true, then the `cmdline` field is valid.
- pub const FLAG_CMDLINE: u32 = 1 << 2;
- /// If true, then the `mods_count` and `mods_addr` fields are valid.
- pub const FLAG_MODULES: u32 = 1 << 3;
- /// If true, then the `symbols` field is valid.
- pub const FLAG_SYMBOLS: u32 = 1 << 4;
- pub unsafe fn memory_map(&self, ops: &'static dyn MultibootOps) -> MemoryEntryIter {
- let mmap_addr = ops.phys_2_virt(self.memory_map_paddr as usize);
- let mmap_len = self.memory_map_len as usize;
- MemoryEntryIter {
- cur_ptr: mmap_addr,
- region_end_vaddr: mmap_addr + mmap_len,
- }
- }
- pub unsafe fn modules(&self, ops: &'static dyn MultibootOps) -> Option<ModulesIter> {
- if !self.has_modules() {
- return None;
- }
- let mods_addr = ops.phys_2_virt(self.mods_paddr as usize);
- let end = mods_addr + (self.mods_count as usize) * core::mem::size_of::<MBModule>();
- Some(ModulesIter {
- cur_ptr: mods_addr,
- region_end_vaddr: end,
- })
- }
- pub unsafe fn cmdline(&self, ops: &'static dyn MultibootOps) -> Option<&str> {
- if !self.has_cmdline() {
- return None;
- }
- let cmdline_vaddr = ops.phys_2_virt(self.cmdline as usize);
- let cstr = CStr::from_ptr(cmdline_vaddr as *const i8);
- cstr.to_str().ok()
- }
- #[inline]
- pub fn has_memory_bounds(&self) -> bool {
- self.flags & Self::FLAG_MEMORY_BOUNDS != 0
- }
- #[inline]
- pub fn has_boot_device(&self) -> bool {
- self.flags & Self::FLAG_BOOT_DEVICE != 0
- }
- #[inline]
- pub fn has_cmdline(&self) -> bool {
- self.flags & Self::FLAG_CMDLINE != 0
- }
- #[inline]
- pub fn has_modules(&self) -> bool {
- self.flags & Self::FLAG_MODULES != 0
- }
- #[inline]
- pub fn has_symbols(&self) -> bool {
- self.flags & Self::FLAG_SYMBOLS != 0
- }
- }
- pub trait MultibootOps {
- fn phys_2_virt(&self, paddr: usize) -> usize;
- }
- #[derive(Debug, Copy, Clone)]
- #[repr(C, packed)]
- pub struct VbeInfo {
- pub control_info: u32,
- pub mode_info: u32,
- pub mode: u16,
- pub interface_seg: u16,
- pub interface_off: u16,
- pub interface_len: u16,
- }
- #[derive(Debug, Copy, Clone)]
- #[repr(C, packed)]
- pub struct FramebufferTable {
- pub paddr: u64,
- pub pitch: u32,
- pub width: u32,
- pub height: u32,
- pub bpp: u8,
- pub typ: u8,
- color_info: ColorInfo,
- }
- impl FramebufferTable {
- /// Get the color info from this table.
- pub fn color_info(&self) -> Option<ColorInfoType> {
- unsafe {
- match self.typ {
- 0 => Some(ColorInfoType::Palette(self.color_info.palette)),
- 1 => Some(ColorInfoType::Rgb(self.color_info.rgb)),
- 2 => Some(ColorInfoType::Text),
- _ => None,
- }
- }
- }
- }
- /// Safe wrapper for `ColorInfo`
- #[derive(Debug)]
- pub enum ColorInfoType {
- Palette(ColorInfoPalette),
- Rgb(ColorInfoRgb),
- Text,
- }
- /// Multiboot format for the frambuffer color info
- ///
- /// According to the spec, if type == 0, it's indexed color and
- ///<rawtext>
- /// +----------------------------------+
- /// 110 | framebuffer_palette_addr |
- /// 114 | framebuffer_palette_num_colors |
- /// +----------------------------------+
- ///</rawtext>
- /// The address points to an array of `ColorDescriptor`s.
- /// If type == 1, it's RGB and
- ///<rawtext>
- /// +----------------------------------+
- ///110 | framebuffer_red_field_position |
- ///111 | framebuffer_red_mask_size |
- ///112 | framebuffer_green_field_position |
- ///113 | framebuffer_green_mask_size |
- ///114 | framebuffer_blue_field_position |
- ///115 | framebuffer_blue_mask_size |
- /// +----------------------------------+
- ///</rawtext>
- /// (If type == 2, it's just text.)
- #[repr(C)]
- #[derive(Clone, Copy)]
- union ColorInfo {
- palette: ColorInfoPalette,
- rgb: ColorInfoRgb,
- _union_align: [u32; 2usize],
- }
- impl core::fmt::Debug for ColorInfo {
- fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
- unsafe {
- f.debug_struct("ColorInfo")
- .field("palette", &self.palette)
- .field("rgb", &self.rgb)
- .finish()
- }
- }
- }
- // default type is 0, so indexed color
- impl Default for ColorInfo {
- fn default() -> Self {
- Self {
- palette: ColorInfoPalette {
- palette_addr: 0,
- palette_num_colors: 0,
- },
- }
- }
- }
- /// Information for indexed color mode
- #[repr(C)]
- #[derive(Debug, Clone, Copy)]
- pub struct ColorInfoPalette {
- palette_addr: u32,
- palette_num_colors: u16,
- }
- /// Information for direct RGB color mode
- #[repr(C)]
- #[derive(Debug, Clone, Copy)]
- pub struct ColorInfoRgb {
- pub red_field_position: u8,
- pub red_mask_size: u8,
- pub green_field_position: u8,
- pub green_mask_size: u8,
- pub blue_field_position: u8,
- pub blue_mask_size: u8,
- }
- /// Types that define if the memory is usable or not.
- #[derive(Debug, PartialEq, Eq)]
- pub enum MemoryType {
- /// memory, available to OS
- Available = 1,
- /// reserved, not available (rom, mem map dev)
- Reserved = 2,
- /// ACPI Reclaim Memory
- ACPI = 3,
- /// ACPI NVS Memory
- NVS = 4,
- /// defective RAM modules
- Defect = 5,
- }
- /// A memory entry in the memory map header info region.
- ///
- /// The memory layout of the entry structure doesn't fit in any scheme
- /// provided by Rust:
- ///
- /// ```text
- /// +-------------------+ <- start of the struct pointer
- /// -4 | size |
- /// +-------------------+
- /// 0 | base_addr |
- /// 8 | length |
- /// 16 | type |
- /// +-------------------+
- /// ```
- ///
- /// The start of a entry is not 64-bit aligned. Although the boot
- /// protocol may provide the `mmap_addr` 64-bit aligned when added with
- /// 4, it is not guaranteed. So we need to use pointer arithmetic to
- /// access the fields.
- pub struct MemoryEntry {
- ptr: usize,
- }
- impl MemoryEntry {
- pub fn size(&self) -> u32 {
- // SAFETY: the entry can only be contructed from a valid address.
- unsafe { (self.ptr as *const u32).read_unaligned() }
- }
- pub fn base_addr(&self) -> u64 {
- // SAFETY: the entry can only be contructed from a valid address.
- unsafe { ((self.ptr + 4) as *const u64).read_unaligned() }
- }
- pub fn length(&self) -> u64 {
- // SAFETY: the entry can only be contructed from a valid address.
- unsafe { ((self.ptr + 12) as *const u64).read_unaligned() }
- }
- pub fn memory_type(&self) -> MemoryType {
- let typ_val = unsafe { ((self.ptr + 20) as *const u8).read_unaligned() };
- // The meaning of the values are however documented clearly by the manual.
- match typ_val {
- 1 => MemoryType::Available,
- 2 => MemoryType::Reserved,
- 3 => MemoryType::ACPI,
- 4 => MemoryType::NVS,
- 5 => MemoryType::Defect,
- _ => MemoryType::Reserved,
- }
- }
- }
- /// A memory entry iterator in the memory map header info region.
- #[derive(Debug, Copy, Clone)]
- pub struct MemoryEntryIter {
- cur_ptr: usize,
- region_end_vaddr: usize,
- }
- impl Iterator for MemoryEntryIter {
- type Item = MemoryEntry;
- fn next(&mut self) -> Option<Self::Item> {
- if self.cur_ptr >= self.region_end_vaddr {
- return None;
- }
- let entry = MemoryEntry { ptr: self.cur_ptr };
- self.cur_ptr += entry.size() as usize + 4;
- Some(entry)
- }
- }
- /// Multiboot format to information about module
- #[repr(C)]
- pub struct MBModule {
- /// Start address of module in memory.
- start: u32,
- /// End address of module in memory.
- end: u32,
- /// The `string` field provides an arbitrary string to be associated
- /// with that particular boot module.
- ///
- /// It is a zero-terminated ASCII string, just like the kernel command line.
- /// The `string` field may be 0 if there is no string associated with the module.
- /// Typically the string might be a command line (e.g. if the operating system
- /// treats boot modules as executable programs), or a pathname
- /// (e.g. if the operating system treats boot modules as files in a file system),
- /// but its exact use is specific to the operating system.
- string: u32,
- /// Must be zero.
- reserved: u32,
- }
- impl MBModule {
- #[inline]
- pub fn start(&self) -> u32 {
- self.start
- }
- #[inline]
- pub fn end(&self) -> u32 {
- self.end
- }
- pub fn string(&self) -> u32 {
- self.string
- }
- pub fn reserved(&self) -> u32 {
- self.reserved
- }
- }
- impl core::fmt::Debug for MBModule {
- fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
- write!(
- f,
- "MBModule {{ start: {}, end: {}, string: {}, reserved: {} }}",
- self.start, self.end, self.string, self.reserved
- )
- }
- }
- #[derive(Debug, Copy, Clone)]
- pub struct ModulesIter {
- cur_ptr: usize,
- region_end_vaddr: usize,
- }
- impl Iterator for ModulesIter {
- type Item = MBModule;
- fn next(&mut self) -> Option<Self::Item> {
- if self.cur_ptr >= self.region_end_vaddr {
- return None;
- }
- let mb_module = unsafe { (self.cur_ptr as *const MBModule).read() };
- self.cur_ptr += core::mem::size_of::<MBModule>();
- Some(mb_module)
- }
- }
|