xref: /DragonOS/kernel/crates/multiboot/src/lib.rs (revision db7c782a9aaacb320027167bda4f23751b8f36e1)
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