ostd/arch/x86/boot/linux_boot/
mod.rs1use linux_boot_params::{BootParams, E820Type, LINUX_BOOT_HEADER_MAGIC};
7
8#[cfg(feature = "cvm_guest")]
9use crate::arch::init_cvm_guest;
10use crate::{
11 arch::if_tdx_enabled,
12 boot::{
13 BootloaderAcpiArg, BootloaderFramebufferArg,
14 memory_region::{MemoryRegion, MemoryRegionArray, MemoryRegionType},
15 },
16 mm::kspace::paddr_to_vaddr,
17};
18
19fn parse_bootloader_name(boot_params: &BootParams) -> &str {
20 match boot_params.hdr.type_of_loader {
24 0x0 => "LILO", 0x1 => "Loadlin",
26 0x2 => "bootsect-loader", 0x3 => "Syslinux",
28 0x4 => "Etherboot/gPXE/iPXE",
29 0x5 => "ELILO",
30 0x7 => "GRUB",
31 0x8 => "U-Boot",
32 0x9 => "Xen",
33 0xA => "Gujin",
34 0xB => "Qemu",
35 0xC => "Arcturus Networks uCbootloader",
36 0xD => "kexec-tools",
37 0xE => "Extended loader",
38 0xF => "Special", 0x10 => "Reserved",
40 0x11 => "Minimal Linux Bootloader <http://sebastian-plotz.blogspot.de>",
41 0x12 => "OVMF UEFI virtualization stack",
42 _ => "Unknown Linux Loader",
43 }
44}
45
46fn parse_kernel_commandline(boot_params: &BootParams) -> Option<&str> {
47 if boot_params.ext_cmd_line_ptr != 0 {
48 return None;
52 }
53
54 if boot_params.hdr.cmd_line_ptr == 0 || boot_params.hdr.cmdline_size == 0 {
55 return None;
56 }
57
58 let cmdline_ptr = paddr_to_vaddr(boot_params.hdr.cmd_line_ptr as usize);
59 let cmdline_len = boot_params.hdr.cmdline_size as usize;
60 let cmdline = unsafe { core::slice::from_raw_parts(cmdline_ptr as *const u8, cmdline_len) };
62
63 core::ffi::CStr::from_bytes_until_nul(cmdline)
65 .ok()?
66 .to_str()
67 .ok()
68}
69
70fn parse_initramfs(boot_params: &BootParams) -> Option<&[u8]> {
71 if boot_params.ext_ramdisk_image != 0 || boot_params.ext_ramdisk_size != 0 {
72 return None;
74 }
75
76 if boot_params.hdr.ramdisk_image == 0 || boot_params.hdr.ramdisk_size == 0 {
77 return None;
78 }
79
80 let initramfs_ptr = paddr_to_vaddr(boot_params.hdr.ramdisk_image as usize);
81 let initramfs_len = boot_params.hdr.ramdisk_size as usize;
82 let initramfs =
87 unsafe { core::slice::from_raw_parts(initramfs_ptr as *const u8, initramfs_len) };
88
89 Some(initramfs)
90}
91
92fn parse_acpi_arg(boot_params: &BootParams) -> BootloaderAcpiArg {
93 let rsdp = boot_params.acpi_rsdp_addr;
94
95 if rsdp == 0 {
96 if is_efi_boot(boot_params) {
97 BootloaderAcpiArg::NotProvided
98 } else {
99 BootloaderAcpiArg::ScanBios
100 }
101 } else {
102 BootloaderAcpiArg::Rsdp(rsdp.try_into().expect("RSDP address overflowed!"))
103 }
104}
105
106fn is_efi_boot(boot_params: &BootParams) -> bool {
107 const EFI32_LOADER_SIGNATURE: u32 = u32::from_le_bytes(*b"EL32");
108 const EFI64_LOADER_SIGNATURE: u32 = u32::from_le_bytes(*b"EL64");
109
110 let efi_info = boot_params.efi_info;
111 matches!(
112 efi_info.efi_loader_signature,
113 EFI32_LOADER_SIGNATURE | EFI64_LOADER_SIGNATURE
114 )
115}
116
117fn parse_framebuffer_info(boot_params: &BootParams) -> Option<BootloaderFramebufferArg> {
118 let screen_info = boot_params.screen_info;
119
120 let address = screen_info.lfb_base as usize | ((screen_info.ext_lfb_base as usize) << 32);
121 if address == 0 {
122 return None;
123 }
124
125 Some(BootloaderFramebufferArg {
126 address,
127 width: screen_info.lfb_width as usize,
128 height: screen_info.lfb_height as usize,
129 bpp: screen_info.lfb_depth as usize,
130 })
131}
132
133impl From<E820Type> for MemoryRegionType {
134 fn from(value: E820Type) -> Self {
135 match value {
136 E820Type::Ram => Self::Usable,
137 E820Type::Reserved => Self::Reserved,
138 E820Type::Acpi => Self::Reclaimable,
139 E820Type::Nvs => Self::NonVolatileSleep,
140 E820Type::Unusable => Self::BadMemory,
141 _ => Self::Reserved,
145 }
146 }
147}
148
149fn parse_memory_regions(boot_params: &BootParams) -> MemoryRegionArray {
150 let mut regions = MemoryRegionArray::new();
151
152 let num_entries = boot_params.e820_entries as usize;
154 for e820_entry in &boot_params.e820_table[0..num_entries] {
155 regions
156 .push(MemoryRegion::new(
157 e820_entry.addr.try_into().unwrap(),
158 e820_entry.size.try_into().unwrap(),
159 e820_entry.typ.into(),
160 ))
161 .unwrap();
162 }
163
164 if let Some(fb) = parse_framebuffer_info(boot_params) {
166 regions.push(MemoryRegion::framebuffer(&fb)).unwrap();
167 }
168
169 regions.push(MemoryRegion::kernel()).unwrap();
171
172 if let Some(initramfs) = parse_initramfs(boot_params) {
174 regions.push(MemoryRegion::module(initramfs)).unwrap();
175 }
176
177 regions
179 .push(super::smp::reclaimable_memory_region())
180 .unwrap();
181
182 if let Some(kcmdline) = parse_kernel_commandline(boot_params) {
184 regions
185 .push(MemoryRegion::module(kcmdline.as_bytes()))
186 .unwrap();
187 }
188
189 if_tdx_enabled!({
195 regions
199 .push(MemoryRegion::new(
200 0x800000,
202 0x006000,
204 MemoryRegionType::NonVolatileSleep,
206 ))
207 .unwrap();
208 });
209
210 regions.into_non_overlapping()
211}
212
213#[unsafe(no_mangle)]
222unsafe extern "sysv64" fn __linux_boot(params_ptr: *const BootParams) -> ! {
223 let params = unsafe { &*params_ptr };
224 assert_eq!({ params.hdr.header }, LINUX_BOOT_HEADER_MAGIC);
225
226 use crate::boot::{EARLY_INFO, EarlyBootInfo, start_kernel};
227
228 #[cfg(feature = "cvm_guest")]
229 init_cvm_guest();
230
231 EARLY_INFO.call_once(|| EarlyBootInfo {
232 bootloader_name: parse_bootloader_name(params),
233 kernel_cmdline: parse_kernel_commandline(params).unwrap_or(""),
234 initramfs: parse_initramfs(params),
235 acpi_arg: parse_acpi_arg(params),
236 framebuffer_arg: parse_framebuffer_info(params),
237 memory_regions: parse_memory_regions(params),
238 });
239
240 unsafe { start_kernel() };
243}