mirror of
https://github.com/DragonOS-Community/DragonOS.git
synced 2025-06-18 08:06:32 +00:00
feat: 允许通过multiboot引导(直到acpi初始化报错) (#914)
This commit is contained in:
8
kernel/crates/multiboot/Cargo.toml
Normal file
8
kernel/crates/multiboot/Cargo.toml
Normal 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]
|
555
kernel/crates/multiboot/src/lib.rs
Normal file
555
kernel/crates/multiboot/src/lib.rs
Normal 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)
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user