feat: 允许通过multiboot引导(直到acpi初始化报错) (#914)

This commit is contained in:
LoGin
2024-09-06 20:04:36 +08:00
committed by GitHub
parent 886ce28516
commit db7c782a9a
12 changed files with 847 additions and 63 deletions

View File

@ -0,0 +1,8 @@
[package]
name = "multiboot"
version = "0.1.0"
edition = "2021"
authors = ["longjin <longjin@dragonos.org>", "Dan Schatzberg <schatzberg.dan@gmail.com>", "Adrian Danis <Adrian.Danis@nicta.com.au>", "Stephane Duverger <stephane.duverger@gmail.com>", "Niklas Sombert <niklas@ytvwld.de>", "Paul Cacheux <paulcacheux@gmail.com>"]
license = "GPL-2.0"
[dependencies]

View File

@ -0,0 +1,555 @@
//! 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)
}
}