mirror of
https://github.com/DragonOS-Community/DragonOS.git
synced 2025-06-08 14:16:47 +00:00
feat: Add HVM boot support for x86_64 (#953)
Signed-off-by: longjin <longjin@DragonOS.org>
This commit is contained in:
parent
a8753f8fff
commit
59a6bcf6ae
@ -2,4 +2,4 @@
|
|||||||
members = [
|
members = [
|
||||||
"kernel_build",
|
"kernel_build",
|
||||||
]
|
]
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
|
@ -3,6 +3,11 @@
|
|||||||
## X86_64
|
## X86_64
|
||||||
|
|
||||||
- [x] multiboot2
|
- [x] multiboot2
|
||||||
|
- [x] HVM/PVH
|
||||||
|
|
||||||
|
### x86_64下的HVM/PVH启动
|
||||||
|
|
||||||
|
在DragonOS的note段,有一段PVH header,允许qemu使用`-kernel`参数启动DragonOS内核。
|
||||||
|
|
||||||
## RISC-V 64
|
## RISC-V 64
|
||||||
|
|
||||||
|
@ -62,7 +62,6 @@ lru = "0.12.3"
|
|||||||
# target为x86_64时,使用下面的依赖
|
# target为x86_64时,使用下面的依赖
|
||||||
[target.'cfg(target_arch = "x86_64")'.dependencies]
|
[target.'cfg(target_arch = "x86_64")'.dependencies]
|
||||||
mini-backtrace = { git = "https://git.mirrors.dragonos.org.cn/DragonOS-Community/mini-backtrace.git", rev = "e0b1d90940" }
|
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" }
|
multiboot2 = { git = "https://git.mirrors.dragonos.org.cn/DragonOS-Community/multiboot2", rev = "05739aab40" }
|
||||||
raw-cpuid = "11.0.1"
|
raw-cpuid = "11.0.1"
|
||||||
x86 = "=0.52.0"
|
x86 = "=0.52.0"
|
||||||
@ -90,4 +89,4 @@ debug = true # Controls whether the compiler passes `-g`
|
|||||||
|
|
||||||
# The release profile, used for `cargo build --release`
|
# The release profile, used for `cargo build --release`
|
||||||
[profile.release]
|
[profile.release]
|
||||||
debug = true
|
debug = false
|
||||||
|
@ -1,8 +0,0 @@
|
|||||||
[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]
|
|
@ -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<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)
|
|
||||||
}
|
|
||||||
}
|
|
@ -17,7 +17,7 @@ ifeq ($(UNWIND_ENABLE), yes)
|
|||||||
CFLAGS_UNWIND = -funwind-tables
|
CFLAGS_UNWIND = -funwind-tables
|
||||||
ifeq ($(ARCH), x86_64)
|
ifeq ($(ARCH), x86_64)
|
||||||
LDFLAGS_UNWIND = --eh-frame-hdr
|
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
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
@ -79,6 +79,7 @@
|
|||||||
#define BOOT_ENTRY_TYPE_MULTIBOOT2 2
|
#define BOOT_ENTRY_TYPE_MULTIBOOT2 2
|
||||||
#define BOOT_ENTRY_TYPE_LINUX_32 3
|
#define BOOT_ENTRY_TYPE_LINUX_32 3
|
||||||
#define BOOT_ENTRY_TYPE_LINUX_64 4
|
#define BOOT_ENTRY_TYPE_LINUX_64 4
|
||||||
|
#define BOOT_ENTRY_TYPE_LINUX_32_PVH 5
|
||||||
|
|
||||||
// 直接用 -m64 编译出来的是 64 位代码,
|
// 直接用 -m64 编译出来的是 64 位代码,
|
||||||
// 但是启动后的机器是 32 位的,相当于在 32 位机器上跑 64 位程序。
|
// 但是启动后的机器是 32 位的,相当于在 32 位机器上跑 64 位程序。
|
||||||
@ -92,6 +93,19 @@
|
|||||||
// 声明这一段代码以 32 位模式编译
|
// 声明这一段代码以 32 位模式编译
|
||||||
.code32
|
.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"
|
.section ".multiboot_header", "a"
|
||||||
|
|
||||||
#define MB_FLAGS_FB 0x4
|
#define MB_FLAGS_FB 0x4
|
||||||
@ -104,31 +118,6 @@ MB_MAGIC = 0x1BADB002
|
|||||||
MB_FLAGS = MB_FLAGS_FB
|
MB_FLAGS = MB_FLAGS_FB
|
||||||
MB_CHECKSUM = -(MB_MAGIC + MB_FLAGS)
|
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 文件头
|
// multiboot2 文件头
|
||||||
// 计算头长度
|
// 计算头长度
|
||||||
@ -169,7 +158,20 @@ framebuffer_tag_end:
|
|||||||
.long 8
|
.long 8
|
||||||
multiboot2_header_end:
|
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
|
.global _start
|
||||||
@ -181,7 +183,7 @@ multiboot2_header_end:
|
|||||||
ENTRY(_start)
|
ENTRY(_start)
|
||||||
// 关中断
|
// 关中断
|
||||||
cli
|
cli
|
||||||
|
|
||||||
// multiboot2_info/ multiboot_info 结构体指针
|
// multiboot2_info/ multiboot_info 结构体指针
|
||||||
mov %ebx, mb_entry_info
|
mov %ebx, mb_entry_info
|
||||||
//mov %ebx, %e8
|
//mov %ebx, %e8
|
||||||
@ -299,7 +301,6 @@ halt:
|
|||||||
.extern Start_Kernel
|
.extern Start_Kernel
|
||||||
ENTRY(_start64)
|
ENTRY(_start64)
|
||||||
|
|
||||||
|
|
||||||
// 初始化寄存器
|
// 初始化寄存器
|
||||||
mov $0x10, %ax
|
mov $0x10, %ax
|
||||||
mov %ax, %ds
|
mov %ax, %ds
|
||||||
@ -372,7 +373,7 @@ ENTRY(_start64)
|
|||||||
add $8, %eax
|
add $8, %eax
|
||||||
loop .fill_pt_64_2
|
loop .fill_pt_64_2
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ==== 加载CR3寄存器
|
// ==== 加载CR3寄存器
|
||||||
|
|
||||||
@ -381,6 +382,7 @@ load_cr3:
|
|||||||
movq $__PML4E, %rax //设置页目录基地址
|
movq $__PML4E, %rax //设置页目录基地址
|
||||||
|
|
||||||
movq %rax, %cr3
|
movq %rax, %cr3
|
||||||
|
|
||||||
jmp to_switch_seg
|
jmp to_switch_seg
|
||||||
|
|
||||||
load_apu_cr3:
|
load_apu_cr3:
|
||||||
@ -394,11 +396,12 @@ load_apu_cr3:
|
|||||||
jmp to_switch_seg
|
jmp to_switch_seg
|
||||||
|
|
||||||
to_switch_seg:
|
to_switch_seg:
|
||||||
|
|
||||||
movq switch_seg(%rip), %rax
|
movq switch_seg(%rip), %rax
|
||||||
// 由于ljmp和lcall在GAS中不受支持,因此我们需要先伪造函数调用现场,通过lret的方式,给它跳转过去。才能更新cs寄存器
|
// 由于ljmp和lcall在GAS中不受支持,因此我们需要先伪造函数调用现场,通过lret的方式,给它跳转过去。才能更新cs寄存器
|
||||||
// 实在是太妙了!Amazing!
|
// 实在是太妙了!Amazing!
|
||||||
pushq $0x08 //段选择子
|
pushq $0x08 //段选择子
|
||||||
|
|
||||||
pushq %rax
|
pushq %rax
|
||||||
lretq
|
lretq
|
||||||
|
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
|
use core::hint::spin_loop;
|
||||||
|
|
||||||
use system_error::SystemError;
|
use system_error::SystemError;
|
||||||
|
|
||||||
use crate::arch::init::multiboot::early_multiboot_init;
|
use super::{multiboot2::early_multiboot2_init, pvh::early_linux32_pvh_init};
|
||||||
|
|
||||||
use super::multiboot2::early_multiboot2_init;
|
|
||||||
|
|
||||||
const BOOT_ENTRY_TYPE_MULTIBOOT: u64 = 1;
|
const BOOT_ENTRY_TYPE_MULTIBOOT: u64 = 1;
|
||||||
const BOOT_ENTRY_TYPE_MULTIBOOT2: u64 = 2;
|
const BOOT_ENTRY_TYPE_MULTIBOOT2: u64 = 2;
|
||||||
const BOOT_ENTRY_TYPE_LINUX_32: u64 = 3;
|
const BOOT_ENTRY_TYPE_LINUX_32: u64 = 3;
|
||||||
const BOOT_ENTRY_TYPE_LINUX_64: u64 = 4;
|
const BOOT_ENTRY_TYPE_LINUX_64: u64 = 4;
|
||||||
|
const BOOT_ENTRY_TYPE_LINUX_32_PVH: u64 = 5;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
#[repr(u64)]
|
#[repr(u64)]
|
||||||
@ -16,6 +17,7 @@ enum BootProtocol {
|
|||||||
Multiboot2,
|
Multiboot2,
|
||||||
Linux32,
|
Linux32,
|
||||||
Linux64,
|
Linux64,
|
||||||
|
Linux32Pvh,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<u64> for BootProtocol {
|
impl TryFrom<u64> for BootProtocol {
|
||||||
@ -27,6 +29,7 @@ impl TryFrom<u64> for BootProtocol {
|
|||||||
BOOT_ENTRY_TYPE_MULTIBOOT2 => Ok(BootProtocol::Multiboot2),
|
BOOT_ENTRY_TYPE_MULTIBOOT2 => Ok(BootProtocol::Multiboot2),
|
||||||
BOOT_ENTRY_TYPE_LINUX_32 => Ok(BootProtocol::Linux32),
|
BOOT_ENTRY_TYPE_LINUX_32 => Ok(BootProtocol::Linux32),
|
||||||
BOOT_ENTRY_TYPE_LINUX_64 => Ok(BootProtocol::Linux64),
|
BOOT_ENTRY_TYPE_LINUX_64 => Ok(BootProtocol::Linux64),
|
||||||
|
BOOT_ENTRY_TYPE_LINUX_32_PVH => Ok(BootProtocol::Linux32Pvh),
|
||||||
_ => Err(SystemError::EINVAL),
|
_ => Err(SystemError::EINVAL),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -40,15 +43,10 @@ pub(super) fn early_boot_init(
|
|||||||
) -> Result<(), SystemError> {
|
) -> Result<(), SystemError> {
|
||||||
let boot_protocol = BootProtocol::try_from(boot_entry_type)?;
|
let boot_protocol = BootProtocol::try_from(boot_entry_type)?;
|
||||||
match boot_protocol {
|
match boot_protocol {
|
||||||
BootProtocol::Multiboot => early_multiboot_init(arg1 as u32, arg2),
|
|
||||||
BootProtocol::Multiboot2 => early_multiboot2_init(arg1 as u32, arg2),
|
BootProtocol::Multiboot2 => early_multiboot2_init(arg1 as u32, arg2),
|
||||||
BootProtocol::Linux32 => {
|
BootProtocol::Linux32 | BootProtocol::Linux64 | BootProtocol::Multiboot => loop {
|
||||||
// linux32_init(arg1, arg2);
|
spin_loop();
|
||||||
unimplemented!();
|
},
|
||||||
}
|
BootProtocol::Linux32Pvh => early_linux32_pvh_init(arg2 as usize),
|
||||||
BootProtocol::Linux64 => {
|
|
||||||
// linux64_init(arg1, arg2);
|
|
||||||
unimplemented!();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,8 +25,8 @@ use super::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
mod boot;
|
mod boot;
|
||||||
mod multiboot;
|
|
||||||
mod multiboot2;
|
mod multiboot2;
|
||||||
|
mod pvh;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ArchBootParams {}
|
pub struct ArchBootParams {}
|
||||||
|
@ -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<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(())
|
|
||||||
}
|
|
142
kernel/src/arch/x86_64/init/pvh/mod.rs
Normal file
142
kernel/src/arch/x86_64/init/pvh/mod.rs
Normal file
@ -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<HvmStartInfo> = Lazy::new();
|
||||||
|
|
||||||
|
struct PvhBootCallback;
|
||||||
|
|
||||||
|
impl BootCallbacks for PvhBootCallback {
|
||||||
|
fn init_bootloader_name(&self) -> Result<Option<String>, SystemError> {
|
||||||
|
return Ok(Some("x86 PVH".to_string()));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init_acpi_args(&self) -> Result<BootloaderAcpiArg, SystemError> {
|
||||||
|
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(())
|
||||||
|
}
|
425
kernel/src/arch/x86_64/init/pvh/param.rs
Normal file
425
kernel/src/arch/x86_64/init/pvh/param.rs
Normal file
@ -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<u32> 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<HvmStartInfo> = ::std::mem::MaybeUninit::uninit();
|
||||||
|
let ptr = UNINIT.as_ptr();
|
||||||
|
assert_eq!(
|
||||||
|
::std::mem::size_of::<HvmStartInfo>(),
|
||||||
|
56usize,
|
||||||
|
concat!("Size of: ", stringify!(hvm_start_info))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
::std::mem::align_of::<HvmStartInfo>(),
|
||||||
|
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<HvmModlistEntry> = ::std::mem::MaybeUninit::uninit();
|
||||||
|
let ptr = UNINIT.as_ptr();
|
||||||
|
assert_eq!(
|
||||||
|
::std::mem::size_of::<HvmModlistEntry>(),
|
||||||
|
32usize,
|
||||||
|
concat!("Size of: ", stringify!(hvm_modlist_entry))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
::std::mem::align_of::<HvmModlistEntry>(),
|
||||||
|
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<HvmMemmapTableEntry> =
|
||||||
|
::std::mem::MaybeUninit::uninit();
|
||||||
|
let ptr = UNINIT.as_ptr();
|
||||||
|
assert_eq!(
|
||||||
|
::std::mem::size_of::<HvmMemmapTableEntry>(),
|
||||||
|
24usize,
|
||||||
|
concat!("Size of: ", stringify!(hvm_memmap_table_entry))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
::std::mem::align_of::<HvmMemmapTableEntry>(),
|
||||||
|
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)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -14,15 +14,17 @@ SECTIONS
|
|||||||
{
|
{
|
||||||
KEEP(*(.multiboot_header))
|
KEEP(*(.multiboot_header))
|
||||||
KEEP(*(.multiboot2_header))
|
KEEP(*(.multiboot2_header))
|
||||||
|
|
||||||
*(.bootstrap)
|
*(.bootstrap)
|
||||||
*(.bootstrap.code64)
|
*(.bootstrap.code64)
|
||||||
*(.bootstrap.data)
|
*(.bootstrap.data)
|
||||||
|
|
||||||
. = ALIGN(4096);
|
. = ALIGN(4096);
|
||||||
}
|
}
|
||||||
|
|
||||||
. = 0x1000000;
|
|
||||||
. += KERNEL_VMA;
|
|
||||||
. = ALIGN(32768);
|
. = ALIGN(32768);
|
||||||
|
. += KERNEL_VMA;
|
||||||
text_start_pa = .;
|
text_start_pa = .;
|
||||||
.text (text_start_pa): AT(text_start_pa - KERNEL_VMA)
|
.text (text_start_pa): AT(text_start_pa - KERNEL_VMA)
|
||||||
{
|
{
|
||||||
@ -55,6 +57,8 @@ SECTIONS
|
|||||||
_rodata = .;
|
_rodata = .;
|
||||||
*(.rodata)
|
*(.rodata)
|
||||||
*(.rodata.*)
|
*(.rodata.*)
|
||||||
|
*(.note.gnu.*)
|
||||||
|
*(.fixup)
|
||||||
_erodata = .;
|
_erodata = .;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,6 +81,7 @@ impl AcpiManager {
|
|||||||
let table_paddr: PhysAddr = match acpi_args {
|
let table_paddr: PhysAddr = match acpi_args {
|
||||||
BootloaderAcpiArg::Rsdt(rsdpv1) => Self::rsdp_paddr(&rsdpv1),
|
BootloaderAcpiArg::Rsdt(rsdpv1) => Self::rsdp_paddr(&rsdpv1),
|
||||||
BootloaderAcpiArg::Xsdt(rsdpv2) => Self::rsdp_paddr(&rsdpv2),
|
BootloaderAcpiArg::Xsdt(rsdpv2) => Self::rsdp_paddr(&rsdpv2),
|
||||||
|
BootloaderAcpiArg::Rsdp(rsdp) => rsdp,
|
||||||
_ => {
|
_ => {
|
||||||
error!(
|
error!(
|
||||||
"AcpiManager::map_tables(): unsupported acpi_args: {:?}",
|
"AcpiManager::map_tables(): unsupported acpi_args: {:?}",
|
||||||
|
@ -506,7 +506,7 @@ impl DeviceManager {
|
|||||||
}
|
}
|
||||||
let kobject_parent = self.get_device_parent(&device, deivce_parent)?;
|
let kobject_parent = self.get_device_parent(&device, deivce_parent)?;
|
||||||
if let Some(ref kobj) = kobject_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 {
|
if let Some(kobject_parent) = kobject_parent {
|
||||||
// debug!(
|
// debug!(
|
||||||
|
@ -284,6 +284,11 @@ impl TtyDriver {
|
|||||||
if err == SystemError::ENOSYS {
|
if err == SystemError::ENOSYS {
|
||||||
return self.standard_install(tty);
|
return self.standard_install(tty);
|
||||||
} else {
|
} else {
|
||||||
|
log::error!(
|
||||||
|
"driver_install_tty: Failed to install. name: {}, err: {:?}",
|
||||||
|
tty.core().name(),
|
||||||
|
err
|
||||||
|
);
|
||||||
return Err(err);
|
return Err(err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -495,9 +495,10 @@ pub struct DrawRegion {
|
|||||||
#[inline(never)]
|
#[inline(never)]
|
||||||
pub fn vty_init() -> Result<(), SystemError> {
|
pub fn vty_init() -> Result<(), SystemError> {
|
||||||
if let Ok(tty_console_driver_inner) = TtyConsoleDriverInner::new() {
|
if let Ok(tty_console_driver_inner) = TtyConsoleDriverInner::new() {
|
||||||
|
const NAME: &str = "tty";
|
||||||
let console_driver = TtyDriver::new(
|
let console_driver = TtyDriver::new(
|
||||||
MAX_NR_CONSOLES,
|
MAX_NR_CONSOLES,
|
||||||
"tty",
|
NAME,
|
||||||
0,
|
0,
|
||||||
Major::TTY_MAJOR,
|
Major::TTY_MAJOR,
|
||||||
0,
|
0,
|
||||||
@ -507,8 +508,8 @@ pub fn vty_init() -> Result<(), SystemError> {
|
|||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
|
|
||||||
TtyDriverManager::tty_register_driver(console_driver).inspect(|_| {
|
TtyDriverManager::tty_register_driver(console_driver).inspect_err(|e| {
|
||||||
log::error!("tty console: register driver failed");
|
log::error!("tty console: register driver {} failed: {:?}", NAME, e);
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -520,7 +521,7 @@ fn vty_late_init() -> Result<(), SystemError> {
|
|||||||
let (_, console_driver) =
|
let (_, console_driver) =
|
||||||
TtyDriverManager::lookup_tty_driver(DeviceNumber::new(Major::TTY_MAJOR, 0))
|
TtyDriverManager::lookup_tty_driver(DeviceNumber::new(Major::TTY_MAJOR, 0))
|
||||||
.ok_or(SystemError::ENODEV)?;
|
.ok_or(SystemError::ENODEV)?;
|
||||||
console_driver.init_tty_device(None)?;
|
console_driver.init_tty_device(None).ok();
|
||||||
|
|
||||||
vc_manager().setup_default_vc();
|
vc_manager().setup_default_vc();
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -185,10 +185,12 @@ impl ConsoleSwitch for BlittingFbConsole {
|
|||||||
}
|
}
|
||||||
let fb = fb.unwrap();
|
let fb = fb.unwrap();
|
||||||
if fb.is_none() {
|
if fb.is_none() {
|
||||||
panic!(
|
log::warn!(
|
||||||
"The Framebuffer with FbID {} has not been initialized yet.",
|
"The Framebuffer with FbID {} has not been initialized yet.",
|
||||||
vc_data.index
|
vc_data.index
|
||||||
)
|
);
|
||||||
|
|
||||||
|
return Err(SystemError::ENODEV);
|
||||||
}
|
}
|
||||||
|
|
||||||
let fb = fb.as_ref().unwrap().clone();
|
let fb = fb.as_ref().unwrap().clone();
|
||||||
|
@ -1169,6 +1169,30 @@ pub enum BootTimeVideoType {
|
|||||||
Efi,
|
Efi,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<u8> 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)]
|
#[derive(Debug, Default)]
|
||||||
pub struct FbCursor {
|
pub struct FbCursor {
|
||||||
/// 设置选项
|
/// 设置选项
|
||||||
|
@ -21,6 +21,10 @@ pub mod fbdev;
|
|||||||
|
|
||||||
static mut __MAMAGER: Option<VideoRefreshManager> = None;
|
static mut __MAMAGER: Option<VideoRefreshManager> = None;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn has_video_refresh_manager() -> bool {
|
||||||
|
return unsafe { __MAMAGER.is_some() };
|
||||||
|
}
|
||||||
pub fn video_refresh_manager() -> &'static VideoRefreshManager {
|
pub fn video_refresh_manager() -> &'static VideoRefreshManager {
|
||||||
return unsafe {
|
return unsafe {
|
||||||
__MAMAGER
|
__MAMAGER
|
||||||
@ -73,9 +77,13 @@ impl VideoRefreshManager {
|
|||||||
/**
|
/**
|
||||||
* VBE帧缓存区的地址重新映射
|
* VBE帧缓存区的地址重新映射
|
||||||
*/
|
*/
|
||||||
fn init_frame_buffer(&self) {
|
fn init_frame_buffer(&self) -> Result<(), SystemError> {
|
||||||
info!("Re-mapping VBE frame buffer...");
|
|
||||||
let mut bp = boot_params().write_irqsave();
|
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 buf_size = bp.screen_info.lfb_size;
|
||||||
|
|
||||||
let mmio_guard = mmio_pool().create_mmio(page_align_up(buf_size)).unwrap();
|
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!");
|
info!("VBE frame buffer successfully Re-mapped!");
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -111,7 +120,7 @@ impl VideoRefreshManager {
|
|||||||
*/
|
*/
|
||||||
pub fn video_reinitialize(&self, level: bool) -> Result<(), SystemError> {
|
pub fn video_reinitialize(&self, level: bool) -> Result<(), SystemError> {
|
||||||
if !level {
|
if !level {
|
||||||
self.init_frame_buffer();
|
self.init_frame_buffer()?;
|
||||||
} else {
|
} else {
|
||||||
// 开启屏幕计时刷新
|
// 开启屏幕计时刷新
|
||||||
assert!(self.run_video_refresh());
|
assert!(self.run_video_refresh());
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
use log::warn;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
arch::{
|
arch::{
|
||||||
init::{early_setup_arch, setup_arch, setup_arch_post},
|
init::{early_setup_arch, setup_arch, setup_arch_post},
|
||||||
@ -55,8 +57,11 @@ fn do_start_kernel() {
|
|||||||
early_setup_arch().expect("setup_arch failed");
|
early_setup_arch().expect("setup_arch failed");
|
||||||
unsafe { mm_init() };
|
unsafe { mm_init() };
|
||||||
|
|
||||||
scm_reinit().unwrap();
|
if scm_reinit().is_ok() {
|
||||||
textui_init().unwrap();
|
if let Err(e) = textui_init() {
|
||||||
|
warn!("Failed to init textui: {:?}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
boot_callback_except_early();
|
boot_callback_except_early();
|
||||||
init_intertrait();
|
init_intertrait();
|
||||||
|
@ -20,7 +20,8 @@ use super::initcall::do_initcalls;
|
|||||||
|
|
||||||
pub fn initial_kernel_thread() -> i32 {
|
pub fn initial_kernel_thread() -> i32 {
|
||||||
kernel_init().unwrap_or_else(|err| {
|
kernel_init().unwrap_or_else(|err| {
|
||||||
panic!("Failed to initialize kernel: {:?}", err);
|
log::error!("Failed to initialize kernel: {:?}", err);
|
||||||
|
panic!()
|
||||||
});
|
});
|
||||||
|
|
||||||
switch_to_user();
|
switch_to_user();
|
||||||
@ -29,10 +30,6 @@ pub fn initial_kernel_thread() -> i32 {
|
|||||||
fn kernel_init() -> Result<(), SystemError> {
|
fn kernel_init() -> Result<(), SystemError> {
|
||||||
KernelThreadMechanism::init_stage2();
|
KernelThreadMechanism::init_stage2();
|
||||||
kenrel_init_freeable()?;
|
kenrel_init_freeable()?;
|
||||||
|
|
||||||
// 由于目前加锁,速度过慢,所以先不开启双缓冲
|
|
||||||
// scm_enable_double_buffer().expect("Failed to enable double buffer");
|
|
||||||
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
crate::driver::disk::ahci::ahci_init()
|
crate::driver::disk::ahci::ahci_init()
|
||||||
.inspect_err(|e| log::error!("ahci_init failed: {:?}", e))
|
.inspect_err(|e| log::error!("ahci_init failed: {:?}", e))
|
||||||
|
@ -8,7 +8,10 @@ use alloc::{boxed::Box, collections::LinkedList, string::String, sync::Arc};
|
|||||||
use system_error::SystemError;
|
use system_error::SystemError;
|
||||||
|
|
||||||
use crate::{
|
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},
|
libs::{lib_ui::textui::textui_is_enable_put_to_window, rwlock::RwLock, spinlock::SpinLock},
|
||||||
mm::{mmio_buddy::MMIOSpaceGuard, VirtAddr},
|
mm::{mmio_buddy::MMIOSpaceGuard, VirtAddr},
|
||||||
};
|
};
|
||||||
@ -430,9 +433,10 @@ pub fn scm_reinit() -> Result<(), SystemError> {
|
|||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
fn true_scm_reinit() -> Result<(), SystemError> {
|
fn true_scm_reinit() -> Result<(), SystemError> {
|
||||||
video_refresh_manager()
|
if !has_video_refresh_manager() {
|
||||||
.video_reinitialize(false)
|
return Err(SystemError::ENODEV);
|
||||||
.expect("video reinitialize failed");
|
}
|
||||||
|
video_refresh_manager().video_reinitialize(false)?;
|
||||||
|
|
||||||
// 遍历当前所有使用帧缓冲区的框架,更新地址
|
// 遍历当前所有使用帧缓冲区的框架,更新地址
|
||||||
let device_buffer = video_refresh_manager().device_buffer().clone();
|
let device_buffer = video_refresh_manager().device_buffer().clone();
|
||||||
|
@ -94,8 +94,7 @@ if [ ${ARCH} == "i386" ] || [ ${ARCH} == "x86_64" ]; then
|
|||||||
else
|
else
|
||||||
QEMU_DEVICES_DISK="-device virtio-blk-pci,drive=disk -device pci-bridge,chassis_nr=1,id=pci.1 -device pcie-root-port "
|
QEMU_DEVICES_DISK="-device virtio-blk-pci,drive=disk -device pci-bridge,chassis_nr=1,id=pci.1 -device pcie-root-port "
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
||||||
else
|
else
|
||||||
QEMU_MACHINE=" -machine virt,memory-backend=${QEMU_MEMORY_BACKEND} -cpu sifive-u54 "
|
QEMU_MACHINE=" -machine virt,memory-backend=${QEMU_MEMORY_BACKEND} -cpu sifive-u54 "
|
||||||
QEMU_DEVICES_DISK="-device virtio-blk-device,drive=disk "
|
QEMU_DEVICES_DISK="-device virtio-blk-device,drive=disk "
|
||||||
@ -132,6 +131,7 @@ while true;do
|
|||||||
QEMU_SERIAL=" -serial mon:stdio "
|
QEMU_SERIAL=" -serial mon:stdio "
|
||||||
QEMU_MONITOR=""
|
QEMU_MONITOR=""
|
||||||
QEMU_ARGUMENT+=" --nographic "
|
QEMU_ARGUMENT+=" --nographic "
|
||||||
|
QEMU_ARGUMENT+=" -kernel ../bin/kernel/kernel.elf "
|
||||||
|
|
||||||
;;
|
;;
|
||||||
esac;shift 2;;
|
esac;shift 2;;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user