1 //! Multiboot v1 library 2 //! 3 //! This crate is partitially modified from `https://github.com/gz/rust-multiboot` && asterinas 4 //! 5 //! The main structs to interact with are [`Multiboot`] for the Multiboot information 6 //! passed from the bootloader to the kernel at runtime and [`Header`] for the static 7 //! information passed from the kernel to the bootloader in the kernel image. 8 //! 9 //! 10 //! # Additional documentation 11 //! * https://www.gnu.org/software/grub/manual/multiboot/multiboot.html 12 //! * http://git.savannah.gnu.org/cgit/grub.git/tree/doc/multiboot.texi?h=multiboot 13 //! 14 //! [`Multiboot`]: information/struct.Multiboot.html 15 //! [`Header`]: header/struct.Header.html 16 #![no_std] 17 18 use core::ffi::CStr; 19 20 pub const MAGIC: u32 = 0x2BADB002; 21 22 /// The ‘boot_device’ field. 23 /// 24 /// Partition numbers always start from zero. Unused partition 25 /// bytes must be set to 0xFF. For example, if the disk is partitioned 26 /// using a simple one-level DOS partitioning scheme, then 27 /// ‘part’ contains the DOS partition number, and ‘part2’ and ‘part3’ 28 /// are both 0xFF. As another example, if a disk is partitioned first into 29 /// DOS partitions, and then one of those DOS partitions is subdivided 30 /// into several BSD partitions using BSD's disklabel strategy, then ‘part1’ 31 /// contains the DOS partition number, ‘part2’ contains the BSD sub-partition 32 /// within that DOS partition, and ‘part3’ is 0xFF. 33 /// 34 #[derive(Debug, Clone, Copy)] 35 #[repr(C)] 36 pub struct BootDevice { 37 /// Contains the bios drive number as understood by 38 /// the bios INT 0x13 low-level disk interface: e.g. 0x00 for the 39 /// first floppy disk or 0x80 for the first hard disk. 40 pub drive: u8, 41 /// Specifies the top-level partition number. 42 pub partition1: u8, 43 /// Specifies a sub-partition in the top-level partition 44 pub partition2: u8, 45 /// Specifies a sub-partition in the 2nd-level partition 46 pub partition3: u8, 47 } 48 49 impl BootDevice { 50 /// Is partition1 a valid partition? partition1_is_valid(&self) -> bool51 pub fn partition1_is_valid(&self) -> bool { 52 self.partition1 != 0xff 53 } 54 55 /// Is partition2 a valid partition? partition2_is_valid(&self) -> bool56 pub fn partition2_is_valid(&self) -> bool { 57 self.partition2 != 0xff 58 } 59 60 /// Is partition3 a valid partition? partition3_is_valid(&self) -> bool61 pub fn partition3_is_valid(&self) -> bool { 62 self.partition3 != 0xff 63 } 64 } 65 66 impl Default for BootDevice { default() -> Self67 fn default() -> Self { 68 Self { 69 drive: 0xff, 70 partition1: 0xff, 71 partition2: 0xff, 72 partition3: 0xff, 73 } 74 } 75 } 76 77 /// Representation of Multiboot Information according to specification. 78 /// 79 /// Reference: https://www.gnu.org/software/grub/manual/multiboot/multiboot.html#Boot-information-format 80 /// 81 ///```text 82 /// +-------------------+ 83 /// 0 | flags | (required) 84 /// +-------------------+ 85 /// 4 | mem_lower | (present if flags[0] is set) 86 /// 8 | mem_upper | (present if flags[0] is set) 87 /// +-------------------+ 88 /// 12 | boot_device | (present if flags[1] is set) 89 /// +-------------------+ 90 /// 16 | cmdline | (present if flags[2] is set) 91 /// +-------------------+ 92 /// 20 | mods_count | (present if flags[3] is set) 93 /// 24 | mods_addr | (present if flags[3] is set) 94 /// +-------------------+ 95 /// 28 - 40 | syms | (present if flags[4] or 96 /// | | flags[5] is set) 97 /// +-------------------+ 98 /// 44 | mmap_length | (present if flags[6] is set) 99 /// 48 | mmap_addr | (present if flags[6] is set) 100 /// +-------------------+ 101 /// 52 | drives_length | (present if flags[7] is set) 102 /// 56 | drives_addr | (present if flags[7] is set) 103 /// +-------------------+ 104 /// 60 | config_table | (present if flags[8] is set) 105 /// +-------------------+ 106 /// 64 | boot_loader_name | (present if flags[9] is set) 107 /// +-------------------+ 108 /// 68 | apm_table | (present if flags[10] is set) 109 /// +-------------------+ 110 /// 72 | vbe_control_info | (present if flags[11] is set) 111 /// 76 | vbe_mode_info | 112 /// 80 | vbe_mode | 113 /// 82 | vbe_interface_seg | 114 /// 84 | vbe_interface_off | 115 /// 86 | vbe_interface_len | 116 /// +-------------------+ 117 /// 88 | framebuffer_addr | (present if flags[12] is set) 118 /// 96 | framebuffer_pitch | 119 /// 100 | framebuffer_width | 120 /// 104 | framebuffer_height| 121 /// 108 | framebuffer_bpp | 122 /// 109 | framebuffer_type | 123 /// 110-115 | color_info | 124 /// +-------------------+ 125 ///``` 126 /// 127 #[allow(dead_code)] 128 #[derive(Debug, Copy, Clone)] 129 #[repr(C, packed)] 130 pub struct MultibootInfo { 131 /// Indicate whether the below field exists. 132 flags: u32, 133 134 /// Physical memory low. 135 mem_lower: u32, 136 /// Physical memory high. 137 mem_upper: u32, 138 139 /// Indicates which BIOS disk device the boot loader loaded the OS image from. 140 boot_device: BootDevice, 141 142 /// Command line passed to kernel. 143 cmdline: u32, 144 145 /// Modules count. 146 pub mods_count: u32, 147 /// The start address of modules list, each module structure format: 148 /// ```text 149 /// +-------------------+ 150 /// 0 | mod_start | 151 /// 4 | mod_end | 152 /// +-------------------+ 153 /// 8 | string | 154 /// +-------------------+ 155 /// 12 | reserved (0) | 156 /// +-------------------+ 157 /// ``` 158 mods_paddr: u32, 159 160 /// If flags[4] = 1, then the field starting at byte 28 are valid: 161 /// ```text 162 /// +-------------------+ 163 /// 28 | tabsize | 164 /// 32 | strsize | 165 /// 36 | addr | 166 /// 40 | reserved (0) | 167 /// +-------------------+ 168 /// ``` 169 /// These indicate where the symbol table from kernel image can be found. 170 /// 171 /// If flags[5] = 1, then the field starting at byte 28 are valid: 172 /// ```text 173 /// +-------------------+ 174 /// 28 | num | 175 /// 32 | size | 176 /// 36 | addr | 177 /// 40 | shndx | 178 /// +-------------------+ 179 /// ``` 180 /// These indicate where the section header table from an ELF kernel is, 181 /// the size of each entry, number of entries, and the string table used as the index of names. 182 symbols: [u8; 16], 183 184 memory_map_len: u32, 185 memory_map_paddr: u32, 186 187 drives_length: u32, 188 drives_addr: u32, 189 190 config_table: u32, 191 192 /// bootloader name paddr 193 pub boot_loader_name: u32, 194 195 apm_table: u32, 196 197 vbe_table: VbeInfo, 198 199 pub framebuffer_table: FramebufferTable, 200 } 201 202 impl MultibootInfo { 203 /// If true, then the `mem_upper` and `mem_lower` fields are valid. 204 pub const FLAG_MEMORY_BOUNDS: u32 = 1 << 0; 205 /// If true, then the `boot_device` field is valid. 206 pub const FLAG_BOOT_DEVICE: u32 = 1 << 1; 207 /// If true, then the `cmdline` field is valid. 208 pub const FLAG_CMDLINE: u32 = 1 << 2; 209 /// If true, then the `mods_count` and `mods_addr` fields are valid. 210 pub const FLAG_MODULES: u32 = 1 << 3; 211 /// If true, then the `symbols` field is valid. 212 pub const FLAG_SYMBOLS: u32 = 1 << 4; 213 memory_map(&self, ops: &'static dyn MultibootOps) -> MemoryEntryIter214 pub unsafe fn memory_map(&self, ops: &'static dyn MultibootOps) -> MemoryEntryIter { 215 let mmap_addr = ops.phys_2_virt(self.memory_map_paddr as usize); 216 let mmap_len = self.memory_map_len as usize; 217 MemoryEntryIter { 218 cur_ptr: mmap_addr, 219 region_end_vaddr: mmap_addr + mmap_len, 220 } 221 } 222 modules(&self, ops: &'static dyn MultibootOps) -> Option<ModulesIter>223 pub unsafe fn modules(&self, ops: &'static dyn MultibootOps) -> Option<ModulesIter> { 224 if !self.has_modules() { 225 return None; 226 } 227 228 let mods_addr = ops.phys_2_virt(self.mods_paddr as usize); 229 let end = mods_addr + (self.mods_count as usize) * core::mem::size_of::<MBModule>(); 230 Some(ModulesIter { 231 cur_ptr: mods_addr, 232 region_end_vaddr: end, 233 }) 234 } 235 cmdline(&self, ops: &'static dyn MultibootOps) -> Option<&str>236 pub unsafe fn cmdline(&self, ops: &'static dyn MultibootOps) -> Option<&str> { 237 if !self.has_cmdline() { 238 return None; 239 } 240 241 let cmdline_vaddr = ops.phys_2_virt(self.cmdline as usize); 242 243 let cstr = CStr::from_ptr(cmdline_vaddr as *const i8); 244 cstr.to_str().ok() 245 } 246 247 #[inline] has_memory_bounds(&self) -> bool248 pub fn has_memory_bounds(&self) -> bool { 249 self.flags & Self::FLAG_MEMORY_BOUNDS != 0 250 } 251 252 #[inline] has_boot_device(&self) -> bool253 pub fn has_boot_device(&self) -> bool { 254 self.flags & Self::FLAG_BOOT_DEVICE != 0 255 } 256 257 #[inline] has_cmdline(&self) -> bool258 pub fn has_cmdline(&self) -> bool { 259 self.flags & Self::FLAG_CMDLINE != 0 260 } 261 262 #[inline] has_modules(&self) -> bool263 pub fn has_modules(&self) -> bool { 264 self.flags & Self::FLAG_MODULES != 0 265 } 266 267 #[inline] has_symbols(&self) -> bool268 pub fn has_symbols(&self) -> bool { 269 self.flags & Self::FLAG_SYMBOLS != 0 270 } 271 } 272 273 pub trait MultibootOps { phys_2_virt(&self, paddr: usize) -> usize274 fn phys_2_virt(&self, paddr: usize) -> usize; 275 } 276 277 #[derive(Debug, Copy, Clone)] 278 #[repr(C, packed)] 279 pub struct VbeInfo { 280 pub control_info: u32, 281 pub mode_info: u32, 282 pub mode: u16, 283 pub interface_seg: u16, 284 pub interface_off: u16, 285 pub interface_len: u16, 286 } 287 288 #[derive(Debug, Copy, Clone)] 289 #[repr(C, packed)] 290 pub struct FramebufferTable { 291 pub paddr: u64, 292 pub pitch: u32, 293 pub width: u32, 294 pub height: u32, 295 pub bpp: u8, 296 pub typ: u8, 297 color_info: ColorInfo, 298 } 299 300 impl FramebufferTable { 301 /// Get the color info from this table. color_info(&self) -> Option<ColorInfoType>302 pub fn color_info(&self) -> Option<ColorInfoType> { 303 unsafe { 304 match self.typ { 305 0 => Some(ColorInfoType::Palette(self.color_info.palette)), 306 1 => Some(ColorInfoType::Rgb(self.color_info.rgb)), 307 2 => Some(ColorInfoType::Text), 308 _ => None, 309 } 310 } 311 } 312 } 313 314 /// Safe wrapper for `ColorInfo` 315 #[derive(Debug)] 316 pub enum ColorInfoType { 317 Palette(ColorInfoPalette), 318 Rgb(ColorInfoRgb), 319 Text, 320 } 321 322 /// Multiboot format for the frambuffer color info 323 /// 324 /// According to the spec, if type == 0, it's indexed color and 325 ///<rawtext> 326 /// +----------------------------------+ 327 /// 110 | framebuffer_palette_addr | 328 /// 114 | framebuffer_palette_num_colors | 329 /// +----------------------------------+ 330 ///</rawtext> 331 /// The address points to an array of `ColorDescriptor`s. 332 /// If type == 1, it's RGB and 333 ///<rawtext> 334 /// +----------------------------------+ 335 ///110 | framebuffer_red_field_position | 336 ///111 | framebuffer_red_mask_size | 337 ///112 | framebuffer_green_field_position | 338 ///113 | framebuffer_green_mask_size | 339 ///114 | framebuffer_blue_field_position | 340 ///115 | framebuffer_blue_mask_size | 341 /// +----------------------------------+ 342 ///</rawtext> 343 /// (If type == 2, it's just text.) 344 #[repr(C)] 345 #[derive(Clone, Copy)] 346 union ColorInfo { 347 palette: ColorInfoPalette, 348 rgb: ColorInfoRgb, 349 _union_align: [u32; 2usize], 350 } 351 352 impl core::fmt::Debug for ColorInfo { fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result353 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 354 unsafe { 355 f.debug_struct("ColorInfo") 356 .field("palette", &self.palette) 357 .field("rgb", &self.rgb) 358 .finish() 359 } 360 } 361 } 362 363 // default type is 0, so indexed color 364 impl Default for ColorInfo { default() -> Self365 fn default() -> Self { 366 Self { 367 palette: ColorInfoPalette { 368 palette_addr: 0, 369 palette_num_colors: 0, 370 }, 371 } 372 } 373 } 374 375 /// Information for indexed color mode 376 #[repr(C)] 377 #[derive(Debug, Clone, Copy)] 378 pub struct ColorInfoPalette { 379 palette_addr: u32, 380 palette_num_colors: u16, 381 } 382 383 /// Information for direct RGB color mode 384 #[repr(C)] 385 #[derive(Debug, Clone, Copy)] 386 pub struct ColorInfoRgb { 387 pub red_field_position: u8, 388 pub red_mask_size: u8, 389 pub green_field_position: u8, 390 pub green_mask_size: u8, 391 pub blue_field_position: u8, 392 pub blue_mask_size: u8, 393 } 394 395 /// Types that define if the memory is usable or not. 396 #[derive(Debug, PartialEq, Eq)] 397 pub enum MemoryType { 398 /// memory, available to OS 399 Available = 1, 400 /// reserved, not available (rom, mem map dev) 401 Reserved = 2, 402 /// ACPI Reclaim Memory 403 ACPI = 3, 404 /// ACPI NVS Memory 405 NVS = 4, 406 /// defective RAM modules 407 Defect = 5, 408 } 409 410 /// A memory entry in the memory map header info region. 411 /// 412 /// The memory layout of the entry structure doesn't fit in any scheme 413 /// provided by Rust: 414 /// 415 /// ```text 416 /// +-------------------+ <- start of the struct pointer 417 /// -4 | size | 418 /// +-------------------+ 419 /// 0 | base_addr | 420 /// 8 | length | 421 /// 16 | type | 422 /// +-------------------+ 423 /// ``` 424 /// 425 /// The start of a entry is not 64-bit aligned. Although the boot 426 /// protocol may provide the `mmap_addr` 64-bit aligned when added with 427 /// 4, it is not guaranteed. So we need to use pointer arithmetic to 428 /// access the fields. 429 pub struct MemoryEntry { 430 ptr: usize, 431 } 432 433 impl MemoryEntry { size(&self) -> u32434 pub fn size(&self) -> u32 { 435 // SAFETY: the entry can only be contructed from a valid address. 436 unsafe { (self.ptr as *const u32).read_unaligned() } 437 } 438 base_addr(&self) -> u64439 pub fn base_addr(&self) -> u64 { 440 // SAFETY: the entry can only be contructed from a valid address. 441 unsafe { ((self.ptr + 4) as *const u64).read_unaligned() } 442 } 443 length(&self) -> u64444 pub fn length(&self) -> u64 { 445 // SAFETY: the entry can only be contructed from a valid address. 446 unsafe { ((self.ptr + 12) as *const u64).read_unaligned() } 447 } 448 memory_type(&self) -> MemoryType449 pub fn memory_type(&self) -> MemoryType { 450 let typ_val = unsafe { ((self.ptr + 20) as *const u8).read_unaligned() }; 451 // The meaning of the values are however documented clearly by the manual. 452 match typ_val { 453 1 => MemoryType::Available, 454 2 => MemoryType::Reserved, 455 3 => MemoryType::ACPI, 456 4 => MemoryType::NVS, 457 5 => MemoryType::Defect, 458 _ => MemoryType::Reserved, 459 } 460 } 461 } 462 463 /// A memory entry iterator in the memory map header info region. 464 #[derive(Debug, Copy, Clone)] 465 pub struct MemoryEntryIter { 466 cur_ptr: usize, 467 region_end_vaddr: usize, 468 } 469 470 impl Iterator for MemoryEntryIter { 471 type Item = MemoryEntry; 472 next(&mut self) -> Option<Self::Item>473 fn next(&mut self) -> Option<Self::Item> { 474 if self.cur_ptr >= self.region_end_vaddr { 475 return None; 476 } 477 let entry = MemoryEntry { ptr: self.cur_ptr }; 478 self.cur_ptr += entry.size() as usize + 4; 479 Some(entry) 480 } 481 } 482 483 /// Multiboot format to information about module 484 #[repr(C)] 485 pub struct MBModule { 486 /// Start address of module in memory. 487 start: u32, 488 489 /// End address of module in memory. 490 end: u32, 491 492 /// The `string` field provides an arbitrary string to be associated 493 /// with that particular boot module. 494 /// 495 /// It is a zero-terminated ASCII string, just like the kernel command line. 496 /// The `string` field may be 0 if there is no string associated with the module. 497 /// Typically the string might be a command line (e.g. if the operating system 498 /// treats boot modules as executable programs), or a pathname 499 /// (e.g. if the operating system treats boot modules as files in a file system), 500 /// but its exact use is specific to the operating system. 501 string: u32, 502 503 /// Must be zero. 504 reserved: u32, 505 } 506 507 impl MBModule { 508 #[inline] start(&self) -> u32509 pub fn start(&self) -> u32 { 510 self.start 511 } 512 513 #[inline] end(&self) -> u32514 pub fn end(&self) -> u32 { 515 self.end 516 } 517 string(&self) -> u32518 pub fn string(&self) -> u32 { 519 self.string 520 } 521 reserved(&self) -> u32522 pub fn reserved(&self) -> u32 { 523 self.reserved 524 } 525 } 526 527 impl core::fmt::Debug for MBModule { fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result528 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { 529 write!( 530 f, 531 "MBModule {{ start: {}, end: {}, string: {}, reserved: {} }}", 532 self.start, self.end, self.string, self.reserved 533 ) 534 } 535 } 536 537 #[derive(Debug, Copy, Clone)] 538 pub struct ModulesIter { 539 cur_ptr: usize, 540 region_end_vaddr: usize, 541 } 542 543 impl Iterator for ModulesIter { 544 type Item = MBModule; 545 next(&mut self) -> Option<Self::Item>546 fn next(&mut self) -> Option<Self::Item> { 547 if self.cur_ptr >= self.region_end_vaddr { 548 return None; 549 } 550 let mb_module = unsafe { (self.cur_ptr as *const MBModule).read() }; 551 552 self.cur_ptr += core::mem::size_of::<MBModule>(); 553 Some(mb_module) 554 } 555 } 556