mirror of
https://github.com/DragonOS-Community/DragonOS.git
synced 2025-06-08 14:16:47 +00:00
feat: 允许通过multiboot引导(直到acpi初始化报错) (#914)
This commit is contained in:
parent
886ce28516
commit
db7c782a9a
@ -63,6 +63,7 @@ lru = "0.12.3"
|
||||
# target为x86_64时,使用下面的依赖
|
||||
[target.'cfg(target_arch = "x86_64")'.dependencies]
|
||||
mini-backtrace = { git = "https://git.mirrors.dragonos.org.cn/DragonOS-Community/mini-backtrace.git", rev = "e0b1d90940" }
|
||||
multiboot = { path = "crates/multiboot" }
|
||||
multiboot2 = { git = "https://git.mirrors.dragonos.org.cn/DragonOS-Community/multiboot2", rev = "05739aab40" }
|
||||
raw-cpuid = "11.0.1"
|
||||
x86 = "=0.52.0"
|
||||
@ -90,4 +91,4 @@ debug = true # Controls whether the compiler passes `-g`
|
||||
|
||||
# The release profile, used for `cargo build --release`
|
||||
[profile.release]
|
||||
debug = false
|
||||
debug = true
|
||||
|
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)
|
||||
}
|
||||
}
|
@ -7,8 +7,8 @@
|
||||
|
||||
// 以下是来自 multiboot2 规范的定义
|
||||
// How many bytes from the start of the file we search for the header.
|
||||
#define MULTIBOOT_SEARCH 32768
|
||||
#define MULTIBOOT_HEADER_ALIGN 8
|
||||
#define MULTIBOOT2_SEARCH 32768
|
||||
#define MULTIBOOT2_HEADER_ALIGN 8
|
||||
|
||||
// The magic field should contain this.
|
||||
#define MULTIBOOT2_HEADER_MAGIC 0xe85250d6
|
||||
@ -16,59 +16,59 @@
|
||||
|
||||
|
||||
// Alignment of multiboot modules.
|
||||
#define MULTIBOOT_MOD_ALIGN 0x00001000
|
||||
#define MULTIBOOT2_MOD_ALIGN 0x00001000
|
||||
|
||||
// Alignment of the multiboot info structure.
|
||||
#define MULTIBOOT_INFO_ALIGN 0x00000008
|
||||
#define MULTIBOOT2_INFO_ALIGN 0x00000008
|
||||
|
||||
// Flags set in the 'flags' member of the multiboot header.
|
||||
|
||||
#define MULTIBOOT_TAG_ALIGN 8
|
||||
#define MULTIBOOT_TAG_TYPE_END 0
|
||||
#define MULTIBOOT_TAG_TYPE_CMDLINE 1
|
||||
#define MULTIBOOT_TAG_TYPE_BOOT_LOADER_NAME 2
|
||||
#define MULTIBOOT_TAG_TYPE_MODULE 3
|
||||
#define MULTIBOOT_TAG_TYPE_BASIC_MEMINFO 4
|
||||
#define MULTIBOOT_TAG_TYPE_BOOTDEV 5
|
||||
#define MULTIBOOT_TAG_TYPE_MMAP 6
|
||||
#define MULTIBOOT_TAG_TYPE_VBE 7
|
||||
#define MULTIBOOT_TAG_TYPE_FRAMEBUFFER 8
|
||||
#define MULTIBOOT_TAG_TYPE_ELF_SECTIONS 9
|
||||
#define MULTIBOOT_TAG_TYPE_APM 10
|
||||
#define MULTIBOOT_TAG_TYPE_EFI32 11
|
||||
#define MULTIBOOT_TAG_TYPE_EFI64 12
|
||||
#define MULTIBOOT_TAG_TYPE_SMBIOS 13
|
||||
#define MULTIBOOT_TAG_TYPE_ACPI_OLD 14
|
||||
#define MULTIBOOT_TAG_TYPE_ACPI_NEW 15
|
||||
#define MULTIBOOT_TAG_TYPE_NETWORK 16
|
||||
#define MULTIBOOT_TAG_TYPE_EFI_MMAP 17
|
||||
#define MULTIBOOT_TAG_TYPE_EFI_BS 18
|
||||
#define MULTIBOOT_TAG_TYPE_EFI32_IH 19
|
||||
#define MULTIBOOT_TAG_TYPE_EFI64_IH 20
|
||||
#define MULTIBOOT_TAG_TYPE_LOAD_BASE_ADDR 21
|
||||
#define MULTIBOOT2_TAG_ALIGN 8
|
||||
#define MULTIBOOT2_TAG_TYPE_END 0
|
||||
#define MULTIBOOT2_TAG_TYPE_CMDLINE 1
|
||||
#define MULTIBOOT2_TAG_TYPE_BOOT_LOADER_NAME 2
|
||||
#define MULTIBOOT2_TAG_TYPE_MODULE 3
|
||||
#define MULTIBOOT2_TAG_TYPE_BASIC_MEMINFO 4
|
||||
#define MULTIBOOT2_TAG_TYPE_BOOTDEV 5
|
||||
#define MULTIBOOT2_TAG_TYPE_MMAP 6
|
||||
#define MULTIBOOT2_TAG_TYPE_VBE 7
|
||||
#define MULTIBOOT2_TAG_TYPE_FRAMEBUFFER 8
|
||||
#define MULTIBOOT2_TAG_TYPE_ELF_SECTIONS 9
|
||||
#define MULTIBOOT2_TAG_TYPE_APM 10
|
||||
#define MULTIBOOT2_TAG_TYPE_EFI32 11
|
||||
#define MULTIBOOT2_TAG_TYPE_EFI64 12
|
||||
#define MULTIBOOT2_TAG_TYPE_SMBIOS 13
|
||||
#define MULTIBOOT2_TAG_TYPE_ACPI_OLD 14
|
||||
#define MULTIBOOT2_TAG_TYPE_ACPI_NEW 15
|
||||
#define MULTIBOOT2_TAG_TYPE_NETWORK 16
|
||||
#define MULTIBOOT2_TAG_TYPE_EFI_MMAP 17
|
||||
#define MULTIBOOT2_TAG_TYPE_EFI_BS 18
|
||||
#define MULTIBOOT2_TAG_TYPE_EFI32_IH 19
|
||||
#define MULTIBOOT2_TAG_TYPE_EFI64_IH 20
|
||||
#define MULTIBOOT2_TAG_TYPE_LOAD_BASE_ADDR 21
|
||||
|
||||
#define MULTIBOOT_HEADER_TAG_END 0
|
||||
#define MULTIBOOT_HEADER_TAG_INFORMATION_REQUEST 1
|
||||
#define MULTIBOOT_HEADER_TAG_ADDRESS 2
|
||||
#define MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS 3
|
||||
#define MULTIBOOT_HEADER_TAG_CONSOLE_FLAGS 4
|
||||
#define MULTIBOOT_HEADER_TAG_FRAMEBUFFER 5
|
||||
#define MULTIBOOT_HEADER_TAG_MODULE_ALIGN 6
|
||||
#define MULTIBOOT_HEADER_TAG_EFI_BS 7
|
||||
#define MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS_EFI32 8
|
||||
#define MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS_EFI64 9
|
||||
#define MULTIBOOT_HEADER_TAG_RELOCATABLE 10
|
||||
#define MULTIBOOT2_HEADER_TAG_END 0
|
||||
#define MULTIBOOT2_HEADER_TAG_INFORMATION_REQUEST 1
|
||||
#define MULTIBOOT2_HEADER_TAG_ADDRESS 2
|
||||
#define MULTIBOOT2_HEADER_TAG_ENTRY_ADDRESS 3
|
||||
#define MULTIBOOT2_HEADER_TAG_CONSOLE_FLAGS 4
|
||||
#define MULTIBOOT2_HEADER_TAG_FRAMEBUFFER 5
|
||||
#define MULTIBOOT2_HEADER_TAG_MODULE_ALIGN 6
|
||||
#define MULTIBOOT2_HEADER_TAG_EFI_BS 7
|
||||
#define MULTIBOOT2_HEADER_TAG_ENTRY_ADDRESS_EFI32 8
|
||||
#define MULTIBOOT2_HEADER_TAG_ENTRY_ADDRESS_EFI64 9
|
||||
#define MULTIBOOT2_HEADER_TAG_RELOCATABLE 10
|
||||
|
||||
#define MULTIBOOT_ARCHITECTURE_I386 0
|
||||
#define MULTIBOOT_ARCHITECTURE_MIPS32 4
|
||||
#define MULTIBOOT_HEADER_TAG_OPTIONAL 1
|
||||
#define MULTIBOOT2_ARCHITECTURE_I386 0
|
||||
#define MULTIBOOT2_ARCHITECTURE_MIPS32 4
|
||||
#define MULTIBOOT2_HEADER_TAG_OPTIONAL 1
|
||||
|
||||
#define MULTIBOOT_LOAD_PREFERENCE_NONE 0
|
||||
#define MULTIBOOT_LOAD_PREFERENCE_LOW 1
|
||||
#define MULTIBOOT_LOAD_PREFERENCE_HIGH 2
|
||||
#define MULTIBOOT2_LOAD_PREFERENCE_NONE 0
|
||||
#define MULTIBOOT2_LOAD_PREFERENCE_LOW 1
|
||||
#define MULTIBOOT2_LOAD_PREFERENCE_HIGH 2
|
||||
|
||||
#define MULTIBOOT_CONSOLE_FLAGS_CONSOLE_REQUIRED 1
|
||||
#define MULTIBOOT_CONSOLE_FLAGS_EGA_TEXT_SUPPORTED 2
|
||||
#define MULTIBOOT2_CONSOLE_FLAGS_CONSOLE_REQUIRED 1
|
||||
#define MULTIBOOT2_CONSOLE_FLAGS_EGA_TEXT_SUPPORTED 2
|
||||
|
||||
// This should be in %eax.
|
||||
#define MULTIBOOT_BOOTLOADER_MAGIC 0x2badb002
|
||||
@ -94,8 +94,14 @@
|
||||
|
||||
.section ".multiboot_header", "a"
|
||||
|
||||
#define MB_FLAGS_FB 0x4
|
||||
|
||||
// reference: https://www.gnu.org/software/grub/manual/multiboot/multiboot.html#Header-graphics-fields
|
||||
#define MB_HEADER_GRAPHIC_MODE_LINEAR 0
|
||||
#define MB_HEADER_GRAPHIC_MODE_TEXT 1
|
||||
|
||||
MB_MAGIC = 0x1BADB002
|
||||
MB_FLAGS = 0
|
||||
MB_FLAGS = MB_FLAGS_FB
|
||||
MB_CHECKSUM = -(MB_MAGIC + MB_FLAGS)
|
||||
|
||||
.code32
|
||||
@ -104,24 +110,42 @@ multiboot_header:
|
||||
.long MB_MAGIC
|
||||
.long MB_FLAGS
|
||||
.long MB_CHECKSUM
|
||||
// header_addr if flags[16] is set
|
||||
.long 0
|
||||
// load_addr if flags[16] is set
|
||||
.long 0
|
||||
// load_end_addr if flags[16] is set
|
||||
.long 0
|
||||
// bss_end_addr if flags[16] is set
|
||||
.long 0
|
||||
// entry_addr if flags[16] is set
|
||||
.long 0
|
||||
// mode_type if flags[2] is set
|
||||
.long MB_HEADER_GRAPHIC_MODE_LINEAR
|
||||
// width if flags[2] is set
|
||||
.long 1440
|
||||
// height if flags[2] is set
|
||||
.long 900
|
||||
// depth if flags[2] is set
|
||||
.long 32
|
||||
|
||||
|
||||
// multiboot2 文件头
|
||||
// 计算头长度
|
||||
.SET MB2_HEADER_LENGTH, multiboot2_header_end - multiboot2_header
|
||||
// 计算校验和
|
||||
.SET MB2_CHECKSUM, -(MULTIBOOT2_HEADER_MAGIC + MULTIBOOT_ARCHITECTURE_I386 + MB2_HEADER_LENGTH)
|
||||
.SET MB2_CHECKSUM, -(MULTIBOOT2_HEADER_MAGIC + MULTIBOOT2_ARCHITECTURE_I386 + MB2_HEADER_LENGTH)
|
||||
// 8 字节对齐
|
||||
.code32
|
||||
.section .multiboot2_header
|
||||
.align MULTIBOOT_HEADER_ALIGN
|
||||
.align MULTIBOOT2_HEADER_ALIGN
|
||||
// 声明所属段
|
||||
|
||||
multiboot2_header:
|
||||
// 魔数
|
||||
.long MULTIBOOT2_HEADER_MAGIC
|
||||
// 架构
|
||||
.long MULTIBOOT_ARCHITECTURE_I386
|
||||
.long MULTIBOOT2_ARCHITECTURE_I386
|
||||
// 头长度
|
||||
.long MB2_HEADER_LENGTH
|
||||
// 校验和
|
||||
@ -131,15 +155,15 @@ multiboot2_header:
|
||||
// 设置帧缓冲区(同时在这里设置qemu的分辨率, 默认为: 1440*900, 还支持: 640*480, 等)
|
||||
.align 8
|
||||
framebuffer_tag_start:
|
||||
.short MULTIBOOT_HEADER_TAG_FRAMEBUFFER
|
||||
.short MULTIBOOT_HEADER_TAG_OPTIONAL
|
||||
.short MULTIBOOT2_HEADER_TAG_FRAMEBUFFER
|
||||
.short MULTIBOOT2_HEADER_TAG_OPTIONAL
|
||||
.long framebuffer_tag_end - framebuffer_tag_start
|
||||
.long 1440 // 宽
|
||||
.long 900 // 高
|
||||
.long 32
|
||||
framebuffer_tag_end:
|
||||
.align 8
|
||||
.short MULTIBOOT_HEADER_TAG_END
|
||||
.short MULTIBOOT2_HEADER_TAG_END
|
||||
// 结束标记
|
||||
.short 0
|
||||
.long 8
|
||||
|
@ -1,5 +1,7 @@
|
||||
use system_error::SystemError;
|
||||
|
||||
use crate::arch::init::multiboot::early_multiboot_init;
|
||||
|
||||
use super::multiboot2::early_multiboot2_init;
|
||||
|
||||
const BOOT_ENTRY_TYPE_MULTIBOOT: u64 = 1;
|
||||
@ -38,10 +40,7 @@ pub(super) fn early_boot_init(
|
||||
) -> Result<(), SystemError> {
|
||||
let boot_protocol = BootProtocol::try_from(boot_entry_type)?;
|
||||
match boot_protocol {
|
||||
BootProtocol::Multiboot => {
|
||||
// early_multiboot_init(arg1, arg2);
|
||||
unimplemented!();
|
||||
}
|
||||
BootProtocol::Multiboot => early_multiboot_init(arg1 as u32, arg2),
|
||||
BootProtocol::Multiboot2 => early_multiboot2_init(arg1 as u32, arg2),
|
||||
BootProtocol::Linux32 => {
|
||||
// linux32_init(arg1, arg2);
|
||||
|
@ -25,6 +25,7 @@ use super::{
|
||||
};
|
||||
|
||||
mod boot;
|
||||
mod multiboot;
|
||||
mod multiboot2;
|
||||
|
||||
#[derive(Debug)]
|
||||
|
191
kernel/src/arch/x86_64/init/multiboot.rs
Normal file
191
kernel/src/arch/x86_64/init/multiboot.rs
Normal file
@ -0,0 +1,191 @@
|
||||
use core::ffi::CStr;
|
||||
|
||||
use alloc::string::{String, ToString};
|
||||
|
||||
use multiboot::MultibootInfo;
|
||||
use system_error::SystemError;
|
||||
|
||||
use crate::{
|
||||
arch::MMArch,
|
||||
driver::{
|
||||
serial::serial8250::send_to_default_serial8250_port,
|
||||
video::fbdev::{
|
||||
base::{BootTimeScreenInfo, BootTimeVideoType},
|
||||
vesafb::vesafb_early_map,
|
||||
},
|
||||
},
|
||||
init::{
|
||||
boot::{register_boot_callbacks, BootCallbacks, BootloaderAcpiArg},
|
||||
boot_params,
|
||||
},
|
||||
libs::lazy_init::Lazy,
|
||||
mm::{memblock::mem_block_manager, MemoryManagementArch, PhysAddr},
|
||||
};
|
||||
|
||||
static MB1_INFO: Lazy<MultibootInfo> = Lazy::new();
|
||||
|
||||
struct Mb1Ops;
|
||||
|
||||
impl multiboot::MultibootOps for Mb1Ops {
|
||||
fn phys_2_virt(&self, paddr: usize) -> usize {
|
||||
unsafe { MMArch::phys_2_virt(PhysAddr::new(paddr)).unwrap().data() }
|
||||
}
|
||||
}
|
||||
struct Mb1Callback;
|
||||
|
||||
impl BootCallbacks for Mb1Callback {
|
||||
fn init_bootloader_name(&self) -> Result<Option<String>, SystemError> {
|
||||
let info = MB1_INFO.get();
|
||||
if info.boot_loader_name != 0 {
|
||||
// SAFETY: the bootloader name is C-style zero-terminated string.
|
||||
unsafe {
|
||||
let cstr_ptr =
|
||||
MMArch::phys_2_virt(PhysAddr::new(info.boot_loader_name as usize)).unwrap();
|
||||
let cstr = CStr::from_ptr(cstr_ptr.data() as *const i8);
|
||||
|
||||
let result = cstr.to_str().unwrap_or("unknown").to_string();
|
||||
return Ok(Some(result));
|
||||
}
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn init_acpi_args(&self) -> Result<BootloaderAcpiArg, SystemError> {
|
||||
// MB1不提供rsdp信息。因此,将来需要让内核支持从UEFI获取RSDP表。
|
||||
Ok(BootloaderAcpiArg::NotProvided)
|
||||
}
|
||||
|
||||
fn init_kernel_cmdline(&self) -> Result<(), SystemError> {
|
||||
let info = MB1_INFO.get();
|
||||
|
||||
if !info.has_cmdline() {
|
||||
log::debug!("No kernel command line found in multiboot1 info");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if let Some(cmdline) = unsafe { info.cmdline(&Mb1Ops) } {
|
||||
let mut guard = boot_params().write_irqsave();
|
||||
guard.boot_cmdline_append(cmdline.as_bytes());
|
||||
|
||||
log::info!("Kernel command line: {}\n", cmdline);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn early_init_framebuffer_info(
|
||||
&self,
|
||||
scinfo: &mut BootTimeScreenInfo,
|
||||
) -> Result<(), SystemError> {
|
||||
let info = MB1_INFO.get();
|
||||
let fb_table = info.framebuffer_table;
|
||||
let width = fb_table.width;
|
||||
let height = fb_table.height;
|
||||
scinfo.is_vga = true;
|
||||
scinfo.lfb_base = PhysAddr::new(fb_table.paddr as usize);
|
||||
let fb_type = fb_table.color_info().unwrap();
|
||||
|
||||
match fb_type {
|
||||
multiboot::ColorInfoType::Palette(_) => todo!(),
|
||||
multiboot::ColorInfoType::Rgb(rgb) => {
|
||||
scinfo.lfb_width = width;
|
||||
scinfo.lfb_height = height;
|
||||
scinfo.video_type = BootTimeVideoType::Vlfb;
|
||||
scinfo.lfb_depth = fb_table.bpp;
|
||||
scinfo.red_pos = rgb.red_field_position;
|
||||
scinfo.red_size = rgb.red_mask_size;
|
||||
scinfo.green_pos = rgb.green_field_position;
|
||||
scinfo.green_size = rgb.green_mask_size;
|
||||
scinfo.blue_pos = rgb.blue_field_position;
|
||||
scinfo.blue_size = rgb.blue_mask_size;
|
||||
}
|
||||
multiboot::ColorInfoType::Text => {
|
||||
scinfo.origin_video_cols = width as u8;
|
||||
scinfo.origin_video_lines = height as u8;
|
||||
scinfo.video_type = BootTimeVideoType::Mda;
|
||||
scinfo.lfb_depth = 8;
|
||||
}
|
||||
}
|
||||
scinfo.lfb_size = (width * height * ((scinfo.lfb_depth as u32 + 7) / 8)) as usize;
|
||||
|
||||
scinfo.lfb_virt_base = Some(vesafb_early_map(scinfo.lfb_base, scinfo.lfb_size)?);
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
fn early_init_memory_blocks(&self) -> Result<(), SystemError> {
|
||||
let info = MB1_INFO.get();
|
||||
let mut total_mem_size = 0usize;
|
||||
let mut usable_mem_size = 0usize;
|
||||
for entry in unsafe { info.memory_map(&Mb1Ops) } {
|
||||
let start = PhysAddr::new(entry.base_addr() as usize);
|
||||
let size = entry.length() as usize;
|
||||
let area_typ = entry.memory_type();
|
||||
total_mem_size += size;
|
||||
|
||||
match area_typ {
|
||||
multiboot::MemoryType::Available => {
|
||||
usable_mem_size += size;
|
||||
mem_block_manager()
|
||||
.add_block(start, size)
|
||||
.unwrap_or_else(|e| {
|
||||
log::warn!(
|
||||
"Failed to add memory block: base={:?}, size={:#x}, error={:?}",
|
||||
start,
|
||||
size,
|
||||
e
|
||||
);
|
||||
});
|
||||
}
|
||||
_ => {
|
||||
mem_block_manager()
|
||||
.reserve_block(start, size)
|
||||
.unwrap_or_else(|e| {
|
||||
log::warn!(
|
||||
"Failed to reserve memory block: base={:?}, size={:#x}, error={:?}",
|
||||
start,
|
||||
size,
|
||||
e
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
send_to_default_serial8250_port("init_memory_area_from_multiboot1 end\n\0".as_bytes());
|
||||
log::info!(
|
||||
"Total memory size: {:#x}, Usable memory size: {:#x}",
|
||||
total_mem_size,
|
||||
usable_mem_size
|
||||
);
|
||||
|
||||
if let Some(modules_iter) = unsafe { info.modules(&Mb1Ops) } {
|
||||
for m in modules_iter {
|
||||
let base = PhysAddr::new(m.start() as usize);
|
||||
let size = m.end() as usize - m.start() as usize;
|
||||
mem_block_manager()
|
||||
.reserve_block(base, size)
|
||||
.unwrap_or_else(|e| {
|
||||
log::warn!(
|
||||
"Failed to reserve modules memory block: base={:?}, size={:#x}, error={:?}",
|
||||
base,
|
||||
size,
|
||||
e
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn early_multiboot_init(boot_magic: u32, boot_info: u64) -> Result<(), SystemError> {
|
||||
assert_eq!(boot_magic, multiboot::MAGIC);
|
||||
let boot_info = unsafe { MMArch::phys_2_virt(PhysAddr::new(boot_info as usize)).unwrap() };
|
||||
let mb1_info = unsafe { (boot_info.data() as *const MultibootInfo).as_ref().unwrap() };
|
||||
MB1_INFO.init(*mb1_info);
|
||||
|
||||
register_boot_callbacks(&Mb1Callback);
|
||||
|
||||
Ok(())
|
||||
}
|
@ -131,7 +131,7 @@ impl BootCallbacks for Mb2Callback {
|
||||
}
|
||||
};
|
||||
|
||||
scinfo.lfb_size = (width * height * ((fb_tag.bpp() as u32 + 7) / 8)) as usize;
|
||||
scinfo.lfb_size = (width * height * ((scinfo.lfb_depth as u32 + 7) / 8)) as usize;
|
||||
|
||||
scinfo.lfb_virt_base = Some(vesafb_early_map(scinfo.lfb_base, scinfo.lfb_size)?);
|
||||
|
||||
|
@ -9,7 +9,7 @@ SECTIONS
|
||||
//KERNEL_VMA = 0;
|
||||
. = 0;
|
||||
. = 0x100000;
|
||||
|
||||
_default_kernel_load_base = .;
|
||||
.boot.text :
|
||||
{
|
||||
KEEP(*(.multiboot_header))
|
||||
|
@ -141,12 +141,13 @@ impl MemoryManagementArch for X86_64MMArch {
|
||||
fn _edata();
|
||||
fn _erodata();
|
||||
fn _end();
|
||||
fn _default_kernel_load_base();
|
||||
}
|
||||
|
||||
Self::init_xd_rsvd();
|
||||
|
||||
let bootstrap_info = X86_64MMBootstrapInfo {
|
||||
kernel_load_base_paddr: 0,
|
||||
kernel_load_base_paddr: _default_kernel_load_base as usize,
|
||||
kernel_code_start: _text as usize,
|
||||
kernel_code_end: _etext as usize,
|
||||
kernel_data_end: _edata as usize,
|
||||
|
@ -882,7 +882,7 @@ impl FixedScreenInfo {
|
||||
///
|
||||
/// 长度为16的字符数组
|
||||
pub const fn name2id(name: &str) -> [char; 16] {
|
||||
let mut id = [0 as char; 16];
|
||||
let mut id = [0u8 as char; 16];
|
||||
let mut i = 0;
|
||||
|
||||
while i < 15 && i < name.len() {
|
||||
|
@ -151,7 +151,11 @@ pub fn boot_callbacks() -> &'static dyn BootCallbacks {
|
||||
pub(super) fn boot_callback_except_early() {
|
||||
boot_callbacks()
|
||||
.init_kernel_cmdline()
|
||||
.expect("Failed to init kernel cmdline");
|
||||
.inspect_err(|e| {
|
||||
log::error!("Failed to init kernel cmdline: {:?}", e);
|
||||
})
|
||||
.ok();
|
||||
|
||||
let mut boot_params = boot_params().write();
|
||||
boot_params.bootloader_name = boot_callbacks()
|
||||
.init_bootloader_name()
|
||||
|
Loading…
x
Reference in New Issue
Block a user