From 59a6bcf6aee15a11a16431bdf875905c5ecf9157 Mon Sep 17 00:00:00 2001 From: LoGin Date: Wed, 9 Oct 2024 21:36:18 +0800 Subject: [PATCH] feat: Add HVM boot support for x86_64 (#953) Signed-off-by: longjin --- build-scripts/Cargo.toml | 2 +- docs/kernel/boot/bootloader.md | 5 + kernel/Cargo.toml | 3 +- kernel/crates/multiboot/Cargo.toml | 8 - kernel/crates/multiboot/src/lib.rs | 555 ------------------ kernel/src/Makefile | 2 +- kernel/src/arch/x86_64/asm/head.S | 63 +- kernel/src/arch/x86_64/init/boot.rs | 22 +- kernel/src/arch/x86_64/init/mod.rs | 2 +- kernel/src/arch/x86_64/init/multiboot.rs | 191 ------ kernel/src/arch/x86_64/init/pvh/mod.rs | 142 +++++ kernel/src/arch/x86_64/init/pvh/param.rs | 425 ++++++++++++++ kernel/src/arch/x86_64/link.lds | 8 +- kernel/src/driver/acpi/mod.rs | 1 + kernel/src/driver/base/device/mod.rs | 2 +- kernel/src/driver/tty/tty_driver.rs | 5 + kernel/src/driver/tty/virtual_terminal/mod.rs | 9 +- .../fbdev/base/fbcon/framebuffer_console.rs | 6 +- kernel/src/driver/video/fbdev/base/mod.rs | 24 + kernel/src/driver/video/mod.rs | 15 +- kernel/src/init/init.rs | 9 +- kernel/src/init/initial_kthread.rs | 7 +- kernel/src/libs/lib_ui/screen_manager.rs | 12 +- tools/run-qemu.sh | 4 +- 24 files changed, 696 insertions(+), 826 deletions(-) delete mode 100644 kernel/crates/multiboot/Cargo.toml delete mode 100644 kernel/crates/multiboot/src/lib.rs delete mode 100644 kernel/src/arch/x86_64/init/multiboot.rs create mode 100644 kernel/src/arch/x86_64/init/pvh/mod.rs create mode 100644 kernel/src/arch/x86_64/init/pvh/param.rs diff --git a/build-scripts/Cargo.toml b/build-scripts/Cargo.toml index 94a1493f..56299a19 100644 --- a/build-scripts/Cargo.toml +++ b/build-scripts/Cargo.toml @@ -2,4 +2,4 @@ members = [ "kernel_build", ] -resolver = "2" \ No newline at end of file +resolver = "2" diff --git a/docs/kernel/boot/bootloader.md b/docs/kernel/boot/bootloader.md index 7d9e1d18..facd8940 100644 --- a/docs/kernel/boot/bootloader.md +++ b/docs/kernel/boot/bootloader.md @@ -3,6 +3,11 @@ ## X86_64 - [x] multiboot2 +- [x] HVM/PVH + +### x86_64下的HVM/PVH启动 + +在DragonOS的note段,有一段PVH header,允许qemu使用`-kernel`参数启动DragonOS内核。 ## RISC-V 64 diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index ed54a0d8..3156e114 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -62,7 +62,6 @@ 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 +89,4 @@ debug = true # Controls whether the compiler passes `-g` # The release profile, used for `cargo build --release` [profile.release] -debug = true +debug = false diff --git a/kernel/crates/multiboot/Cargo.toml b/kernel/crates/multiboot/Cargo.toml deleted file mode 100644 index f89ab4d8..00000000 --- a/kernel/crates/multiboot/Cargo.toml +++ /dev/null @@ -1,8 +0,0 @@ -[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 deleted file mode 100644 index 904f754f..00000000 --- a/kernel/crates/multiboot/src/lib.rs +++ /dev/null @@ -1,555 +0,0 @@ -//! 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/Makefile b/kernel/src/Makefile index e9da4b02..3ee688cc 100644 --- a/kernel/src/Makefile +++ b/kernel/src/Makefile @@ -17,7 +17,7 @@ ifeq ($(UNWIND_ENABLE), yes) CFLAGS_UNWIND = -funwind-tables ifeq ($(ARCH), x86_64) LDFLAGS_UNWIND = --eh-frame-hdr - RUSTFLAGS_UNWIND = -Cforce-unwind-tables -Clink-arg=-Wl,eh_frame.ld + RUSTFLAGS_UNWIND = -Cforce-unwind-tables -Clink-arg=-Wl,eh_frame.ld endif endif diff --git a/kernel/src/arch/x86_64/asm/head.S b/kernel/src/arch/x86_64/asm/head.S index b3b2727b..d7b19234 100644 --- a/kernel/src/arch/x86_64/asm/head.S +++ b/kernel/src/arch/x86_64/asm/head.S @@ -79,6 +79,7 @@ #define BOOT_ENTRY_TYPE_MULTIBOOT2 2 #define BOOT_ENTRY_TYPE_LINUX_32 3 #define BOOT_ENTRY_TYPE_LINUX_64 4 +#define BOOT_ENTRY_TYPE_LINUX_32_PVH 5 // 直接用 -m64 编译出来的是 64 位代码, // 但是启动后的机器是 32 位的,相当于在 32 位机器上跑 64 位程序。 @@ -92,6 +93,19 @@ // 声明这一段代码以 32 位模式编译 .code32 +/* PVH Header with pvh_start_addr = __linux32_pvh_boot */ + + .pushsection .note.dragonos, "a", @note + .align 4 + .long 2f - 1f + .long 4f - 3f + .long 18 + 1:.asciz "Xen" + 2:.align 4 + 3:.long __linux32_pvh_boot + 4:.align 4 + .popsection + .section ".multiboot_header", "a" #define MB_FLAGS_FB 0x4 @@ -104,31 +118,6 @@ MB_MAGIC = 0x1BADB002 MB_FLAGS = MB_FLAGS_FB MB_CHECKSUM = -(MB_MAGIC + MB_FLAGS) -.code32 -multiboot_header: - .align 8 - .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 文件头 // 计算头长度 @@ -169,7 +158,20 @@ framebuffer_tag_end: .long 8 multiboot2_header_end: -.section .bootstrap +// direct_linux32_boot启动的内核入口 +.section .bootstrap, "a" +.code32 +.global __linux32_pvh_boot +__linux32_pvh_boot: + cli + cld + // start info 指针 + mov %ebx, mb_entry_info + mov $BOOT_ENTRY_TYPE_LINUX_32_PVH, %ebx + mov %ebx, boot_entry_type + jmp protected_mode_setup + +.code32 .global _start @@ -181,7 +183,7 @@ multiboot2_header_end: ENTRY(_start) // 关中断 cli - + // multiboot2_info/ multiboot_info 结构体指针 mov %ebx, mb_entry_info //mov %ebx, %e8 @@ -299,7 +301,6 @@ halt: .extern Start_Kernel ENTRY(_start64) - // 初始化寄存器 mov $0x10, %ax mov %ax, %ds @@ -372,7 +373,7 @@ ENTRY(_start64) add $8, %eax loop .fill_pt_64_2 - + // ==== 加载CR3寄存器 @@ -381,6 +382,7 @@ load_cr3: movq $__PML4E, %rax //设置页目录基地址 movq %rax, %cr3 + jmp to_switch_seg load_apu_cr3: @@ -394,11 +396,12 @@ load_apu_cr3: jmp to_switch_seg to_switch_seg: - + movq switch_seg(%rip), %rax // 由于ljmp和lcall在GAS中不受支持,因此我们需要先伪造函数调用现场,通过lret的方式,给它跳转过去。才能更新cs寄存器 // 实在是太妙了!Amazing! pushq $0x08 //段选择子 + pushq %rax lretq diff --git a/kernel/src/arch/x86_64/init/boot.rs b/kernel/src/arch/x86_64/init/boot.rs index 01a52e2d..1bf96557 100644 --- a/kernel/src/arch/x86_64/init/boot.rs +++ b/kernel/src/arch/x86_64/init/boot.rs @@ -1,13 +1,14 @@ +use core::hint::spin_loop; + use system_error::SystemError; -use crate::arch::init::multiboot::early_multiboot_init; - -use super::multiboot2::early_multiboot2_init; +use super::{multiboot2::early_multiboot2_init, pvh::early_linux32_pvh_init}; const BOOT_ENTRY_TYPE_MULTIBOOT: u64 = 1; const BOOT_ENTRY_TYPE_MULTIBOOT2: u64 = 2; const BOOT_ENTRY_TYPE_LINUX_32: u64 = 3; const BOOT_ENTRY_TYPE_LINUX_64: u64 = 4; +const BOOT_ENTRY_TYPE_LINUX_32_PVH: u64 = 5; #[derive(Debug)] #[repr(u64)] @@ -16,6 +17,7 @@ enum BootProtocol { Multiboot2, Linux32, Linux64, + Linux32Pvh, } impl TryFrom for BootProtocol { @@ -27,6 +29,7 @@ impl TryFrom for BootProtocol { BOOT_ENTRY_TYPE_MULTIBOOT2 => Ok(BootProtocol::Multiboot2), BOOT_ENTRY_TYPE_LINUX_32 => Ok(BootProtocol::Linux32), BOOT_ENTRY_TYPE_LINUX_64 => Ok(BootProtocol::Linux64), + BOOT_ENTRY_TYPE_LINUX_32_PVH => Ok(BootProtocol::Linux32Pvh), _ => Err(SystemError::EINVAL), } } @@ -40,15 +43,10 @@ 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 as u32, arg2), BootProtocol::Multiboot2 => early_multiboot2_init(arg1 as u32, arg2), - BootProtocol::Linux32 => { - // linux32_init(arg1, arg2); - unimplemented!(); - } - BootProtocol::Linux64 => { - // linux64_init(arg1, arg2); - unimplemented!(); - } + BootProtocol::Linux32 | BootProtocol::Linux64 | BootProtocol::Multiboot => loop { + spin_loop(); + }, + BootProtocol::Linux32Pvh => early_linux32_pvh_init(arg2 as usize), } } diff --git a/kernel/src/arch/x86_64/init/mod.rs b/kernel/src/arch/x86_64/init/mod.rs index 12c0d950..50bd2e33 100644 --- a/kernel/src/arch/x86_64/init/mod.rs +++ b/kernel/src/arch/x86_64/init/mod.rs @@ -25,8 +25,8 @@ use super::{ }; mod boot; -mod multiboot; mod multiboot2; +mod pvh; #[derive(Debug)] pub struct ArchBootParams {} diff --git a/kernel/src/arch/x86_64/init/multiboot.rs b/kernel/src/arch/x86_64/init/multiboot.rs deleted file mode 100644 index 3c7790b9..00000000 --- a/kernel/src/arch/x86_64/init/multiboot.rs +++ /dev/null @@ -1,191 +0,0 @@ -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/pvh/mod.rs b/kernel/src/arch/x86_64/init/pvh/mod.rs new file mode 100644 index 00000000..6ad1b9ad --- /dev/null +++ b/kernel/src/arch/x86_64/init/pvh/mod.rs @@ -0,0 +1,142 @@ +//! x86/HVM启动 +//! +//! 初始化代码可参考:https://code.dragonos.org.cn/xref/linux-6.6.21/arch/x86/platform/pvh/enlighten.c#45 +use alloc::string::{String, ToString}; +use core::{ffi::CStr, hint::spin_loop}; +use param::{E820Type, HvmMemmapTableEntry, HvmStartInfo}; +use system_error::SystemError; + +use crate::{ + arch::MMArch, + driver::{ + serial::serial8250::send_to_default_serial8250_port, video::fbdev::base::BootTimeScreenInfo, + }, + init::{ + boot::{register_boot_callbacks, BootCallbacks, BootloaderAcpiArg}, + boot_params, + }, + libs::lazy_init::Lazy, + mm::{memblock::mem_block_manager, MemoryManagementArch, PhysAddr}, +}; + +mod param; + +static START_INFO: Lazy = Lazy::new(); + +struct PvhBootCallback; + +impl BootCallbacks for PvhBootCallback { + fn init_bootloader_name(&self) -> Result, SystemError> { + return Ok(Some("x86 PVH".to_string())); + } + + fn init_acpi_args(&self) -> Result { + let rsdp_paddr = PhysAddr::new(START_INFO.get().rsdp_paddr as usize); + if rsdp_paddr.data() != 0 { + Ok(BootloaderAcpiArg::Rsdp(rsdp_paddr)) + } else { + Ok(BootloaderAcpiArg::NotProvided) + } + } + + fn init_kernel_cmdline(&self) -> Result<(), SystemError> { + let cmdline_c_str: &CStr = unsafe { + CStr::from_ptr( + MMArch::phys_2_virt(PhysAddr::new(START_INFO.get().cmdline_paddr as usize)) + .unwrap() + .data() as *const i8, + ) + }; + let cmdline = cmdline_c_str.to_str().unwrap(); + boot_params() + .write_irqsave() + .boot_cmdline_append(cmdline.as_bytes()); + log::info!("pvh boot cmdline: {:?}", cmdline_c_str); + Ok(()) + } + + fn early_init_framebuffer_info( + &self, + _scinfo: &mut BootTimeScreenInfo, + ) -> Result<(), SystemError> { + return Err(SystemError::ENODEV); + } + + fn early_init_memory_blocks(&self) -> Result<(), SystemError> { + let start_info = START_INFO.get(); + let mut total_mem_size = 0usize; + let mut usable_mem_size = 0usize; + send_to_default_serial8250_port("init_memory_area by pvh boot\n\0".as_bytes()); + + if (start_info.version > 0) && start_info.memmap_entries > 0 { + let mut ep = unsafe { + MMArch::phys_2_virt(PhysAddr::new(start_info.memmap_paddr as usize)).unwrap() + } + .data() as *const HvmMemmapTableEntry; + + for _ in 0..start_info.memmap_entries { + let entry = unsafe { *ep }; + let start = PhysAddr::new(entry.addr as usize); + let size = entry.size as usize; + let typ = E820Type::from(entry.type_); + + total_mem_size += size; + match typ { + param::E820Type::Ram => { + 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 + ); + }); + } + } + ep = unsafe { ep.add(1) }; + } + } + send_to_default_serial8250_port("init_memory_area_from pvh boot end\n\0".as_bytes()); + log::info!( + "Total memory size: {:#x}, Usable memory size: {:#x}", + total_mem_size, + usable_mem_size + ); + Ok(()) + } +} + +#[inline(never)] +pub(super) fn early_linux32_pvh_init(params_ptr: usize) -> Result<(), SystemError> { + let start_info = unsafe { *(params_ptr as *const HvmStartInfo) }; + if start_info.magic != HvmStartInfo::XEN_HVM_START_MAGIC_VALUE { + send_to_default_serial8250_port( + "early_linux32_pvh_init failed: Magic number not matched.\n\0".as_bytes(), + ); + + loop { + spin_loop(); + } + } + + START_INFO.init(start_info); + + register_boot_callbacks(&PvhBootCallback); + send_to_default_serial8250_port("early_linux32_pvh_init done.\n\0".as_bytes()); + Ok(()) +} diff --git a/kernel/src/arch/x86_64/init/pvh/param.rs b/kernel/src/arch/x86_64/init/pvh/param.rs new file mode 100644 index 00000000..e99eb3b1 --- /dev/null +++ b/kernel/src/arch/x86_64/init/pvh/param.rs @@ -0,0 +1,425 @@ +/* + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Copyright (c) 2016, Citrix Systems, Inc. + */ + +/* + * automatically generated by rust-bindgen using: + * + * # bindgen start_info.h -- -include stdint.h > start_info.rs + * + * From the canonical version in upstream Xen repository + * xen/include/public/arch-x86/hvm/start_info.h + * at commit: + * b4642c32c4d079916d5607ddda0232aae5e1690e + * + * The generated file has been edited to eliminate unnecessary + * definitions, add comments, and relocate definitions and tests for clarity. + * Added Default to the list of traits that are automatically derived. + * + * The definitions in this file are intended to be exported and used by a particular + * VMM implementation in order to boot a Linux guest using the PVH entry point as + * specified in the x86/HVM direct boot ABI. + * These structures contain all the required information (cmdline address, ACPI RSDP, + * memory maps, etc) that must be written to guest memory before starting guest + * execution by jumping to the PVH entry point address. + * A comparable set of definitions to hvm_start_info and hvm_memmap_table_entry in this + * file would be the boot_params and boot_e820_entry definitions used by the Linux + * 64-bit boot protocol. + * + * Start of day structure passed to PVH guests and to HVM guests in %ebx. + * + * NOTE: nothing will be loaded at physical address 0, so a 0 value in any + * of the address fields should be treated as not present. + * + * 0 +----------------+ + * | magic | Contains the magic value XEN_HVM_START_MAGIC_VALUE + * | | ("xEn3" with the 0x80 bit of the "E" set). + * 4 +----------------+ + * | version | Version of this structure. Current version is 1. New + * | | versions are guaranteed to be backwards-compatible. + * 8 +----------------+ + * | flags | SIF_xxx flags. + * 12 +----------------+ + * | nr_modules | Number of modules passed to the kernel. + * 16 +----------------+ + * | modlist_paddr | Physical address of an array of modules + * | | (layout of the structure below). + * 24 +----------------+ + * | cmdline_paddr | Physical address of the command line, + * | | a zero-terminated ASCII string. + * 32 +----------------+ + * | rsdp_paddr | Physical address of the RSDP ACPI data structure. + * 40 +----------------+ + * | memmap_paddr | Physical address of the (optional) memory map. Only + * | | present in version 1 and newer of the structure. + * 48 +----------------+ + * | memmap_entries | Number of entries in the memory map table. Zero + * | | if there is no memory map being provided. Only + * | | present in version 1 and newer of the structure. + * 52 +----------------+ + * | reserved | Version 1 and newer only. + * 56 +----------------+ + * + * The layout of each entry in the module structure is the following: + * + * 0 +----------------+ + * | paddr | Physical address of the module. + * 8 +----------------+ + * | size | Size of the module in bytes. + * 16 +----------------+ + * | cmdline_paddr | Physical address of the command line, + * | | a zero-terminated ASCII string. + * 24 +----------------+ + * | reserved | + * 32 +----------------+ + * + * The layout of each entry in the memory map table is as follows: + * + * 0 +----------------+ + * | addr | Base address + * 8 +----------------+ + * | size | Size of mapping in bytes + * 16 +----------------+ + * | type | Type of mapping as defined between the hypervisor + * | | and guest. See XEN_HVM_MEMMAP_TYPE_* values below. + * 20 +----------------| + * | reserved | + * 24 +----------------+ + * + * The address and sizes are always a 64bit little endian unsigned integer. + * + * NB: Xen on x86 will always try to place all the data below the 4GiB + * boundary. + * + * Version numbers of the hvm_start_info structure have evolved like this: + * + * Version 0: Initial implementation. + * + * Version 1: Added the memmap_paddr/memmap_entries fields (plus 4 bytes of + * padding) to the end of the hvm_start_info struct. These new + * fields can be used to pass a memory map to the guest. The + * memory map is optional and so guests that understand version 1 + * of the structure must check that memmap_entries is non-zero + * before trying to read the memory map. + */ + +#[repr(C)] +#[derive(Debug, Copy, Clone, Default)] +pub struct HvmStartInfo { + pub magic: u32, + pub version: u32, + pub flags: u32, + pub nr_modules: u32, + pub modlist_paddr: u64, + pub cmdline_paddr: u64, + pub rsdp_paddr: u64, + pub memmap_paddr: u64, + pub memmap_entries: u32, + pub reserved: u32, +} + +impl HvmStartInfo { + pub const XEN_HVM_START_MAGIC_VALUE: u32 = 0x336ec578; +} + +#[repr(C)] +#[derive(Debug, Copy, Clone, Default)] +pub struct HvmModlistEntry { + pub paddr: u64, + pub size: u64, + pub cmdline_paddr: u64, + pub reserved: u64, +} + +#[repr(C)] +#[derive(Debug, Copy, Clone, Default)] +pub struct HvmMemmapTableEntry { + pub addr: u64, + pub size: u64, + pub type_: u32, + pub reserved: u32, +} + +/// The E820 types known to the kernel. +#[derive(Copy, Clone, Debug)] +#[repr(u32)] +pub enum E820Type { + Ram = 1, + Reserved = 2, + Acpi = 3, + Nvs = 4, + Unusable = 5, + Pmem = 7, + Pram = 12, + SoftReserved = 0xefffffff, + ReservedKern = 128, +} + +impl From for E820Type { + fn from(val: u32) -> Self { + match val { + 1 => E820Type::Ram, + 2 => E820Type::Reserved, + 3 => E820Type::Acpi, + 4 => E820Type::Nvs, + 5 => E820Type::Unusable, + 7 => E820Type::Pmem, + 12 => E820Type::Pram, + 0xefffffff => E820Type::SoftReserved, + 128 => E820Type::ReservedKern, + _ => E820Type::Reserved, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn bindgen_test_layout_hvm_start_info() { + const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); + let ptr = UNINIT.as_ptr(); + assert_eq!( + ::std::mem::size_of::(), + 56usize, + concat!("Size of: ", stringify!(hvm_start_info)) + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + concat!("Alignment of ", stringify!(hvm_start_info)) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).magic) as usize - ptr as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(hvm_start_info), + "::", + stringify!(magic) + ) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).version) as usize - ptr as usize }, + 4usize, + concat!( + "Offset of field: ", + stringify!(hvm_start_info), + "::", + stringify!(version) + ) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).flags) as usize - ptr as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(hvm_start_info), + "::", + stringify!(flags) + ) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).nr_modules) as usize - ptr as usize }, + 12usize, + concat!( + "Offset of field: ", + stringify!(hvm_start_info), + "::", + stringify!(nr_modules) + ) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).modlist_paddr) as usize - ptr as usize }, + 16usize, + concat!( + "Offset of field: ", + stringify!(hvm_start_info), + "::", + stringify!(modlist_paddr) + ) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).cmdline_paddr) as usize - ptr as usize }, + 24usize, + concat!( + "Offset of field: ", + stringify!(hvm_start_info), + "::", + stringify!(cmdline_paddr) + ) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).rsdp_paddr) as usize - ptr as usize }, + 32usize, + concat!( + "Offset of field: ", + stringify!(hvm_start_info), + "::", + stringify!(rsdp_paddr) + ) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).memmap_paddr) as usize - ptr as usize }, + 40usize, + concat!( + "Offset of field: ", + stringify!(hvm_start_info), + "::", + stringify!(memmap_paddr) + ) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).memmap_entries) as usize - ptr as usize }, + 48usize, + concat!( + "Offset of field: ", + stringify!(hvm_start_info), + "::", + stringify!(memmap_entries) + ) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).reserved) as usize - ptr as usize }, + 52usize, + concat!( + "Offset of field: ", + stringify!(hvm_start_info), + "::", + stringify!(reserved) + ) + ); + } + + #[test] + fn bindgen_test_layout_hvm_modlist_entry() { + const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); + let ptr = UNINIT.as_ptr(); + assert_eq!( + ::std::mem::size_of::(), + 32usize, + concat!("Size of: ", stringify!(hvm_modlist_entry)) + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + concat!("Alignment of ", stringify!(hvm_modlist_entry)) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).paddr) as usize - ptr as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(hvm_modlist_entry), + "::", + stringify!(paddr) + ) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).size) as usize - ptr as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(hvm_modlist_entry), + "::", + stringify!(size) + ) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).cmdline_paddr) as usize - ptr as usize }, + 16usize, + concat!( + "Offset of field: ", + stringify!(hvm_modlist_entry), + "::", + stringify!(cmdline_paddr) + ) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).reserved) as usize - ptr as usize }, + 24usize, + concat!( + "Offset of field: ", + stringify!(hvm_modlist_entry), + "::", + stringify!(reserved) + ) + ); + } + + #[test] + fn bindgen_test_layout_hvm_memmap_table_entry() { + const UNINIT: ::std::mem::MaybeUninit = + ::std::mem::MaybeUninit::uninit(); + let ptr = UNINIT.as_ptr(); + assert_eq!( + ::std::mem::size_of::(), + 24usize, + concat!("Size of: ", stringify!(hvm_memmap_table_entry)) + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + concat!("Alignment of ", stringify!(hvm_memmap_table_entry)) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).addr) as usize - ptr as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(hvm_memmap_table_entry), + "::", + stringify!(addr) + ) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).size) as usize - ptr as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(hvm_memmap_table_entry), + "::", + stringify!(size) + ) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).type_) as usize - ptr as usize }, + 16usize, + concat!( + "Offset of field: ", + stringify!(hvm_memmap_table_entry), + "::", + stringify!(type_) + ) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).reserved) as usize - ptr as usize }, + 20usize, + concat!( + "Offset of field: ", + stringify!(hvm_memmap_table_entry), + "::", + stringify!(reserved) + ) + ); + } +} diff --git a/kernel/src/arch/x86_64/link.lds b/kernel/src/arch/x86_64/link.lds index 256f1cb2..9c729ed9 100644 --- a/kernel/src/arch/x86_64/link.lds +++ b/kernel/src/arch/x86_64/link.lds @@ -14,15 +14,17 @@ SECTIONS { KEEP(*(.multiboot_header)) KEEP(*(.multiboot2_header)) + *(.bootstrap) *(.bootstrap.code64) *(.bootstrap.data) + . = ALIGN(4096); } - . = 0x1000000; - . += KERNEL_VMA; + . = ALIGN(32768); + . += KERNEL_VMA; text_start_pa = .; .text (text_start_pa): AT(text_start_pa - KERNEL_VMA) { @@ -55,6 +57,8 @@ SECTIONS _rodata = .; *(.rodata) *(.rodata.*) + *(.note.gnu.*) + *(.fixup) _erodata = .; } diff --git a/kernel/src/driver/acpi/mod.rs b/kernel/src/driver/acpi/mod.rs index e7fb8fe1..04db5761 100644 --- a/kernel/src/driver/acpi/mod.rs +++ b/kernel/src/driver/acpi/mod.rs @@ -81,6 +81,7 @@ impl AcpiManager { let table_paddr: PhysAddr = match acpi_args { BootloaderAcpiArg::Rsdt(rsdpv1) => Self::rsdp_paddr(&rsdpv1), BootloaderAcpiArg::Xsdt(rsdpv2) => Self::rsdp_paddr(&rsdpv2), + BootloaderAcpiArg::Rsdp(rsdp) => rsdp, _ => { error!( "AcpiManager::map_tables(): unsupported acpi_args: {:?}", diff --git a/kernel/src/driver/base/device/mod.rs b/kernel/src/driver/base/device/mod.rs index 0930223b..b1b76a39 100644 --- a/kernel/src/driver/base/device/mod.rs +++ b/kernel/src/driver/base/device/mod.rs @@ -506,7 +506,7 @@ impl DeviceManager { } let kobject_parent = self.get_device_parent(&device, deivce_parent)?; if let Some(ref kobj) = kobject_parent { - log::info!("kobject parent: {:?}", kobj.name()); + log::debug!("kobject parent: {:?}", kobj.name()); } if let Some(kobject_parent) = kobject_parent { // debug!( diff --git a/kernel/src/driver/tty/tty_driver.rs b/kernel/src/driver/tty/tty_driver.rs index fc86e63f..a586599f 100644 --- a/kernel/src/driver/tty/tty_driver.rs +++ b/kernel/src/driver/tty/tty_driver.rs @@ -284,6 +284,11 @@ impl TtyDriver { if err == SystemError::ENOSYS { return self.standard_install(tty); } else { + log::error!( + "driver_install_tty: Failed to install. name: {}, err: {:?}", + tty.core().name(), + err + ); return Err(err); } } diff --git a/kernel/src/driver/tty/virtual_terminal/mod.rs b/kernel/src/driver/tty/virtual_terminal/mod.rs index 62401b85..8336591a 100644 --- a/kernel/src/driver/tty/virtual_terminal/mod.rs +++ b/kernel/src/driver/tty/virtual_terminal/mod.rs @@ -495,9 +495,10 @@ pub struct DrawRegion { #[inline(never)] pub fn vty_init() -> Result<(), SystemError> { if let Ok(tty_console_driver_inner) = TtyConsoleDriverInner::new() { + const NAME: &str = "tty"; let console_driver = TtyDriver::new( MAX_NR_CONSOLES, - "tty", + NAME, 0, Major::TTY_MAJOR, 0, @@ -507,8 +508,8 @@ pub fn vty_init() -> Result<(), SystemError> { None, ); - TtyDriverManager::tty_register_driver(console_driver).inspect(|_| { - log::error!("tty console: register driver failed"); + TtyDriverManager::tty_register_driver(console_driver).inspect_err(|e| { + log::error!("tty console: register driver {} failed: {:?}", NAME, e); })?; } @@ -520,7 +521,7 @@ fn vty_late_init() -> Result<(), SystemError> { let (_, console_driver) = TtyDriverManager::lookup_tty_driver(DeviceNumber::new(Major::TTY_MAJOR, 0)) .ok_or(SystemError::ENODEV)?; - console_driver.init_tty_device(None)?; + console_driver.init_tty_device(None).ok(); vc_manager().setup_default_vc(); Ok(()) diff --git a/kernel/src/driver/video/fbdev/base/fbcon/framebuffer_console.rs b/kernel/src/driver/video/fbdev/base/fbcon/framebuffer_console.rs index 730e5590..3c61ba30 100644 --- a/kernel/src/driver/video/fbdev/base/fbcon/framebuffer_console.rs +++ b/kernel/src/driver/video/fbdev/base/fbcon/framebuffer_console.rs @@ -185,10 +185,12 @@ impl ConsoleSwitch for BlittingFbConsole { } let fb = fb.unwrap(); if fb.is_none() { - panic!( + log::warn!( "The Framebuffer with FbID {} has not been initialized yet.", vc_data.index - ) + ); + + return Err(SystemError::ENODEV); } let fb = fb.as_ref().unwrap().clone(); diff --git a/kernel/src/driver/video/fbdev/base/mod.rs b/kernel/src/driver/video/fbdev/base/mod.rs index 79bb9a24..fb9921a6 100644 --- a/kernel/src/driver/video/fbdev/base/mod.rs +++ b/kernel/src/driver/video/fbdev/base/mod.rs @@ -1169,6 +1169,30 @@ pub enum BootTimeVideoType { Efi, } +impl From for BootTimeVideoType { + fn from(value: u8) -> Self { + match value { + 0 => BootTimeVideoType::UnDefined, + 0x10 => BootTimeVideoType::Mda, + 0x11 => BootTimeVideoType::Cga, + 0x20 => BootTimeVideoType::EgaM, + 0x21 => BootTimeVideoType::EgaC, + 0x22 => BootTimeVideoType::VgaC, + 0x23 => BootTimeVideoType::Vlfb, + 0x30 => BootTimeVideoType::PicaS3, + 0x31 => BootTimeVideoType::MipsG364, + 0x33 => BootTimeVideoType::Sgi, + 0x40 => BootTimeVideoType::TgaC, + 0x50 => BootTimeVideoType::Sun, + 0x51 => BootTimeVideoType::SunPci, + 0x60 => BootTimeVideoType::Pmac, + 0x70 => BootTimeVideoType::Efi, + + _ => BootTimeVideoType::UnDefined, + } + } +} + #[derive(Debug, Default)] pub struct FbCursor { /// 设置选项 diff --git a/kernel/src/driver/video/mod.rs b/kernel/src/driver/video/mod.rs index 726dd389..42c4249a 100644 --- a/kernel/src/driver/video/mod.rs +++ b/kernel/src/driver/video/mod.rs @@ -21,6 +21,10 @@ pub mod fbdev; static mut __MAMAGER: Option = None; +#[inline] +pub fn has_video_refresh_manager() -> bool { + return unsafe { __MAMAGER.is_some() }; +} pub fn video_refresh_manager() -> &'static VideoRefreshManager { return unsafe { __MAMAGER @@ -73,9 +77,13 @@ impl VideoRefreshManager { /** * VBE帧缓存区的地址重新映射 */ - fn init_frame_buffer(&self) { - info!("Re-mapping VBE frame buffer..."); + fn init_frame_buffer(&self) -> Result<(), SystemError> { let mut bp = boot_params().write_irqsave(); + // 没有VBE + if bp.screen_info.lfb_base.data() == 0 { + return Err(SystemError::ENODEV); + } + info!("Re-mapping VBE frame buffer..."); let buf_size = bp.screen_info.lfb_size; let mmio_guard = mmio_pool().create_mmio(page_align_up(buf_size)).unwrap(); @@ -100,6 +108,7 @@ impl VideoRefreshManager { }; info!("VBE frame buffer successfully Re-mapped!"); + Ok(()) } /** @@ -111,7 +120,7 @@ impl VideoRefreshManager { */ pub fn video_reinitialize(&self, level: bool) -> Result<(), SystemError> { if !level { - self.init_frame_buffer(); + self.init_frame_buffer()?; } else { // 开启屏幕计时刷新 assert!(self.run_video_refresh()); diff --git a/kernel/src/init/init.rs b/kernel/src/init/init.rs index 8f2d6fa6..a1d8aee8 100644 --- a/kernel/src/init/init.rs +++ b/kernel/src/init/init.rs @@ -1,3 +1,5 @@ +use log::warn; + use crate::{ arch::{ init::{early_setup_arch, setup_arch, setup_arch_post}, @@ -55,8 +57,11 @@ fn do_start_kernel() { early_setup_arch().expect("setup_arch failed"); unsafe { mm_init() }; - scm_reinit().unwrap(); - textui_init().unwrap(); + if scm_reinit().is_ok() { + if let Err(e) = textui_init() { + warn!("Failed to init textui: {:?}", e); + } + } boot_callback_except_early(); init_intertrait(); diff --git a/kernel/src/init/initial_kthread.rs b/kernel/src/init/initial_kthread.rs index 0cc88de3..31e2547c 100644 --- a/kernel/src/init/initial_kthread.rs +++ b/kernel/src/init/initial_kthread.rs @@ -20,7 +20,8 @@ use super::initcall::do_initcalls; pub fn initial_kernel_thread() -> i32 { kernel_init().unwrap_or_else(|err| { - panic!("Failed to initialize kernel: {:?}", err); + log::error!("Failed to initialize kernel: {:?}", err); + panic!() }); switch_to_user(); @@ -29,10 +30,6 @@ pub fn initial_kernel_thread() -> i32 { fn kernel_init() -> Result<(), SystemError> { KernelThreadMechanism::init_stage2(); kenrel_init_freeable()?; - - // 由于目前加锁,速度过慢,所以先不开启双缓冲 - // scm_enable_double_buffer().expect("Failed to enable double buffer"); - #[cfg(target_arch = "x86_64")] crate::driver::disk::ahci::ahci_init() .inspect_err(|e| log::error!("ahci_init failed: {:?}", e)) diff --git a/kernel/src/libs/lib_ui/screen_manager.rs b/kernel/src/libs/lib_ui/screen_manager.rs index 3db719ab..839ce360 100644 --- a/kernel/src/libs/lib_ui/screen_manager.rs +++ b/kernel/src/libs/lib_ui/screen_manager.rs @@ -8,7 +8,10 @@ use alloc::{boxed::Box, collections::LinkedList, string::String, sync::Arc}; use system_error::SystemError; use crate::{ - driver::{serial::serial8250::send_to_default_serial8250_port, video::video_refresh_manager}, + driver::{ + serial::serial8250::send_to_default_serial8250_port, + video::{has_video_refresh_manager, video_refresh_manager}, + }, libs::{lib_ui::textui::textui_is_enable_put_to_window, rwlock::RwLock, spinlock::SpinLock}, mm::{mmio_buddy::MMIOSpaceGuard, VirtAddr}, }; @@ -430,9 +433,10 @@ pub fn scm_reinit() -> Result<(), SystemError> { #[allow(dead_code)] fn true_scm_reinit() -> Result<(), SystemError> { - video_refresh_manager() - .video_reinitialize(false) - .expect("video reinitialize failed"); + if !has_video_refresh_manager() { + return Err(SystemError::ENODEV); + } + video_refresh_manager().video_reinitialize(false)?; // 遍历当前所有使用帧缓冲区的框架,更新地址 let device_buffer = video_refresh_manager().device_buffer().clone(); diff --git a/tools/run-qemu.sh b/tools/run-qemu.sh index e21c9d8a..8f201c8d 100644 --- a/tools/run-qemu.sh +++ b/tools/run-qemu.sh @@ -94,8 +94,7 @@ if [ ${ARCH} == "i386" ] || [ ${ARCH} == "x86_64" ]; then else QEMU_DEVICES_DISK="-device virtio-blk-pci,drive=disk -device pci-bridge,chassis_nr=1,id=pci.1 -device pcie-root-port " fi - - + else QEMU_MACHINE=" -machine virt,memory-backend=${QEMU_MEMORY_BACKEND} -cpu sifive-u54 " QEMU_DEVICES_DISK="-device virtio-blk-device,drive=disk " @@ -132,6 +131,7 @@ while true;do QEMU_SERIAL=" -serial mon:stdio " QEMU_MONITOR="" QEMU_ARGUMENT+=" --nographic " + QEMU_ARGUMENT+=" -kernel ../bin/kernel/kernel.elf " ;; esac;shift 2;;