From 636d0c6078a1cb7a2ff32cf84d62fa001fca7630 Mon Sep 17 00:00:00 2001 From: longjin Date: Thu, 5 Sep 2024 17:16:28 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=85=81=E8=AE=B8=E9=80=9A=E8=BF=87mul?= =?UTF-8?q?tiboot=E5=BC=95=E5=AF=BC=EF=BC=88=E7=9B=B4=E5=88=B0acpi?= =?UTF-8?q?=E5=88=9D=E5=A7=8B=E5=8C=96=E6=8A=A5=E9=94=99=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kernel/Cargo.toml | 3 +- kernel/crates/multiboot/Cargo.toml | 8 + kernel/crates/multiboot/src/lib.rs | 555 ++++++++++++++++++++++ kernel/src/arch/x86_64/asm/head.S | 138 +++--- kernel/src/arch/x86_64/init/boot.rs | 7 +- kernel/src/arch/x86_64/init/mod.rs | 1 + kernel/src/arch/x86_64/init/multiboot.rs | 191 ++++++++ kernel/src/arch/x86_64/init/multiboot2.rs | 2 +- kernel/src/arch/x86_64/link.lds | 2 +- kernel/src/arch/x86_64/mm/mod.rs | 3 +- kernel/src/driver/video/fbdev/base/mod.rs | 2 +- kernel/src/init/boot.rs | 6 +- 12 files changed, 851 insertions(+), 67 deletions(-) create mode 100644 kernel/crates/multiboot/Cargo.toml create mode 100644 kernel/crates/multiboot/src/lib.rs create mode 100644 kernel/src/arch/x86_64/init/multiboot.rs diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index fad66f6b9..77a9cb2ec 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -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 diff --git a/kernel/crates/multiboot/Cargo.toml b/kernel/crates/multiboot/Cargo.toml new file mode 100644 index 000000000..f89ab4d8b --- /dev/null +++ b/kernel/crates/multiboot/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "multiboot" +version = "0.1.0" +edition = "2021" +authors = ["longjin ", "Dan Schatzberg ", "Adrian Danis ", "Stephane Duverger ", "Niklas Sombert ", "Paul Cacheux "] +license = "GPL-2.0" + +[dependencies] diff --git a/kernel/crates/multiboot/src/lib.rs b/kernel/crates/multiboot/src/lib.rs new file mode 100644 index 000000000..904f754f4 --- /dev/null +++ b/kernel/crates/multiboot/src/lib.rs @@ -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 { + 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::(); + 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 { + 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 +/// +/// +----------------------------------+ +/// 110 | framebuffer_palette_addr | +/// 114 | framebuffer_palette_num_colors | +/// +----------------------------------+ +/// +/// The address points to an array of `ColorDescriptor`s. +/// If type == 1, it's RGB and +/// +/// +----------------------------------+ +///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 | +/// +----------------------------------+ +/// +/// (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 { + 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 { + 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::(); + Some(mb_module) + } +} diff --git a/kernel/src/arch/x86_64/asm/head.S b/kernel/src/arch/x86_64/asm/head.S index 05244594c..b3b2727bb 100644 --- a/kernel/src/arch/x86_64/asm/head.S +++ b/kernel/src/arch/x86_64/asm/head.S @@ -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 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 MULTIBOOT_ARCHITECTURE_I386 0 -#define MULTIBOOT_ARCHITECTURE_MIPS32 4 -#define MULTIBOOT_HEADER_TAG_OPTIONAL 1 - -#define MULTIBOOT_LOAD_PREFERENCE_NONE 0 -#define MULTIBOOT_LOAD_PREFERENCE_LOW 1 -#define MULTIBOOT_LOAD_PREFERENCE_HIGH 2 - -#define MULTIBOOT_CONSOLE_FLAGS_CONSOLE_REQUIRED 1 -#define MULTIBOOT_CONSOLE_FLAGS_EGA_TEXT_SUPPORTED 2 +#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 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 MULTIBOOT2_ARCHITECTURE_I386 0 +#define MULTIBOOT2_ARCHITECTURE_MIPS32 4 +#define MULTIBOOT2_HEADER_TAG_OPTIONAL 1 + +#define MULTIBOOT2_LOAD_PREFERENCE_NONE 0 +#define MULTIBOOT2_LOAD_PREFERENCE_LOW 1 +#define MULTIBOOT2_LOAD_PREFERENCE_HIGH 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 diff --git a/kernel/src/arch/x86_64/init/boot.rs b/kernel/src/arch/x86_64/init/boot.rs index 12b587c59..01a52e2dd 100644 --- a/kernel/src/arch/x86_64/init/boot.rs +++ b/kernel/src/arch/x86_64/init/boot.rs @@ -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); diff --git a/kernel/src/arch/x86_64/init/mod.rs b/kernel/src/arch/x86_64/init/mod.rs index d51d73abf..12c0d9503 100644 --- a/kernel/src/arch/x86_64/init/mod.rs +++ b/kernel/src/arch/x86_64/init/mod.rs @@ -25,6 +25,7 @@ use super::{ }; mod boot; +mod multiboot; mod multiboot2; #[derive(Debug)] diff --git a/kernel/src/arch/x86_64/init/multiboot.rs b/kernel/src/arch/x86_64/init/multiboot.rs new file mode 100644 index 000000000..3c7790b95 --- /dev/null +++ b/kernel/src/arch/x86_64/init/multiboot.rs @@ -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 = 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, 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 { + // 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(()) +} diff --git a/kernel/src/arch/x86_64/init/multiboot2.rs b/kernel/src/arch/x86_64/init/multiboot2.rs index c302c0a94..3d1078fd7 100644 --- a/kernel/src/arch/x86_64/init/multiboot2.rs +++ b/kernel/src/arch/x86_64/init/multiboot2.rs @@ -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)?); diff --git a/kernel/src/arch/x86_64/link.lds b/kernel/src/arch/x86_64/link.lds index ca264e15f..256f1cb2f 100644 --- a/kernel/src/arch/x86_64/link.lds +++ b/kernel/src/arch/x86_64/link.lds @@ -9,7 +9,7 @@ SECTIONS //KERNEL_VMA = 0; . = 0; . = 0x100000; - + _default_kernel_load_base = .; .boot.text : { KEEP(*(.multiboot_header)) diff --git a/kernel/src/arch/x86_64/mm/mod.rs b/kernel/src/arch/x86_64/mm/mod.rs index 9d9531aa4..f96c96f4d 100644 --- a/kernel/src/arch/x86_64/mm/mod.rs +++ b/kernel/src/arch/x86_64/mm/mod.rs @@ -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, diff --git a/kernel/src/driver/video/fbdev/base/mod.rs b/kernel/src/driver/video/fbdev/base/mod.rs index a266b3635..79bb9a24c 100644 --- a/kernel/src/driver/video/fbdev/base/mod.rs +++ b/kernel/src/driver/video/fbdev/base/mod.rs @@ -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() { diff --git a/kernel/src/init/boot.rs b/kernel/src/init/boot.rs index 8779f0cdc..426ea1ccb 100644 --- a/kernel/src/init/boot.rs +++ b/kernel/src/init/boot.rs @@ -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()