1 //! x86/HVM启动
2 //!
3 //! 初始化代码可参考:https://code.dragonos.org.cn/xref/linux-6.6.21/arch/x86/platform/pvh/enlighten.c#45
4 use alloc::string::{String, ToString};
5 use core::{ffi::CStr, hint::spin_loop};
6 use param::{E820Type, HvmMemmapTableEntry, HvmStartInfo};
7 use system_error::SystemError;
8
9 use crate::{
10 arch::MMArch,
11 driver::{
12 serial::serial8250::send_to_default_serial8250_port, video::fbdev::base::BootTimeScreenInfo,
13 },
14 init::{
15 boot::{register_boot_callbacks, BootCallbacks, BootloaderAcpiArg},
16 boot_params,
17 },
18 libs::lazy_init::Lazy,
19 mm::{memblock::mem_block_manager, MemoryManagementArch, PhysAddr},
20 };
21
22 mod param;
23
24 static START_INFO: Lazy<HvmStartInfo> = Lazy::new();
25
26 struct PvhBootCallback;
27
28 impl BootCallbacks for PvhBootCallback {
init_bootloader_name(&self) -> Result<Option<String>, SystemError>29 fn init_bootloader_name(&self) -> Result<Option<String>, SystemError> {
30 return Ok(Some("x86 PVH".to_string()));
31 }
32
init_acpi_args(&self) -> Result<BootloaderAcpiArg, SystemError>33 fn init_acpi_args(&self) -> Result<BootloaderAcpiArg, SystemError> {
34 let rsdp_paddr = PhysAddr::new(START_INFO.get().rsdp_paddr as usize);
35 if rsdp_paddr.data() != 0 {
36 Ok(BootloaderAcpiArg::Rsdp(rsdp_paddr))
37 } else {
38 Ok(BootloaderAcpiArg::NotProvided)
39 }
40 }
41
init_kernel_cmdline(&self) -> Result<(), SystemError>42 fn init_kernel_cmdline(&self) -> Result<(), SystemError> {
43 let cmdline_c_str: &CStr = unsafe {
44 CStr::from_ptr(
45 MMArch::phys_2_virt(PhysAddr::new(START_INFO.get().cmdline_paddr as usize))
46 .unwrap()
47 .data() as *const i8,
48 )
49 };
50 let cmdline = cmdline_c_str.to_str().unwrap();
51 boot_params()
52 .write_irqsave()
53 .boot_cmdline_append(cmdline.as_bytes());
54 log::info!("pvh boot cmdline: {:?}", cmdline_c_str);
55 Ok(())
56 }
57
early_init_framebuffer_info( &self, _scinfo: &mut BootTimeScreenInfo, ) -> Result<(), SystemError>58 fn early_init_framebuffer_info(
59 &self,
60 _scinfo: &mut BootTimeScreenInfo,
61 ) -> Result<(), SystemError> {
62 return Err(SystemError::ENODEV);
63 }
64
early_init_memory_blocks(&self) -> Result<(), SystemError>65 fn early_init_memory_blocks(&self) -> Result<(), SystemError> {
66 let start_info = START_INFO.get();
67 let mut total_mem_size = 0usize;
68 let mut usable_mem_size = 0usize;
69 send_to_default_serial8250_port("init_memory_area by pvh boot\n\0".as_bytes());
70
71 if (start_info.version > 0) && start_info.memmap_entries > 0 {
72 let mut ep = unsafe {
73 MMArch::phys_2_virt(PhysAddr::new(start_info.memmap_paddr as usize)).unwrap()
74 }
75 .data() as *const HvmMemmapTableEntry;
76
77 for _ in 0..start_info.memmap_entries {
78 let entry = unsafe { *ep };
79 let start = PhysAddr::new(entry.addr as usize);
80 let size = entry.size as usize;
81 let typ = E820Type::from(entry.type_);
82
83 total_mem_size += size;
84 match typ {
85 param::E820Type::Ram => {
86 usable_mem_size += size;
87 mem_block_manager()
88 .add_block(start, size)
89 .unwrap_or_else(|e| {
90 log::warn!(
91 "Failed to add memory block: base={:?}, size={:#x}, error={:?}",
92 start,
93 size,
94 e
95 );
96 });
97 }
98 _ => {
99 mem_block_manager()
100 .reserve_block(start, size)
101 .unwrap_or_else(|e| {
102 log::warn!(
103 "Failed to reserve memory block: base={:?}, size={:#x}, error={:?}",
104 start,
105 size,
106 e
107 );
108 });
109 }
110 }
111 ep = unsafe { ep.add(1) };
112 }
113 }
114 send_to_default_serial8250_port("init_memory_area_from pvh boot end\n\0".as_bytes());
115 log::info!(
116 "Total memory size: {:#x}, Usable memory size: {:#x}",
117 total_mem_size,
118 usable_mem_size
119 );
120 Ok(())
121 }
122 }
123
124 #[inline(never)]
early_linux32_pvh_init(params_ptr: usize) -> Result<(), SystemError>125 pub(super) fn early_linux32_pvh_init(params_ptr: usize) -> Result<(), SystemError> {
126 let start_info = unsafe { *(params_ptr as *const HvmStartInfo) };
127 if start_info.magic != HvmStartInfo::XEN_HVM_START_MAGIC_VALUE {
128 send_to_default_serial8250_port(
129 "early_linux32_pvh_init failed: Magic number not matched.\n\0".as_bytes(),
130 );
131
132 loop {
133 spin_loop();
134 }
135 }
136
137 START_INFO.init(start_info);
138
139 register_boot_callbacks(&PvhBootCallback);
140 send_to_default_serial8250_port("early_linux32_pvh_init done.\n\0".as_bytes());
141 Ok(())
142 }
143