Skip to main content

loader_defs/
linux.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Linux loader definitions.
5//!
6//! These structures are defined in the Linux kernel and can be found in the kernel docs under
7//! [`The Linux/x86 Boot Protocol`](https://www.kernel.org/doc/html/latest/x86/boot.html).
8
9#![expect(missing_docs)]
10#![expect(non_camel_case_types)]
11
12use static_assertions::const_assert_eq;
13use zerocopy::FromBytes;
14use zerocopy::FromZeros;
15use zerocopy::Immutable;
16use zerocopy::IntoBytes;
17use zerocopy::KnownLayout;
18
19mod packed_nums {
20    pub type u16_ne = zerocopy::U16<zerocopy::NativeEndian>;
21    pub type u32_ne = zerocopy::U32<zerocopy::NativeEndian>;
22    pub type u64_ne = zerocopy::U64<zerocopy::NativeEndian>;
23}
24use self::packed_nums::*;
25
26#[repr(C)]
27#[derive(Debug, Copy, Clone, IntoBytes, Immutable, KnownLayout, FromBytes)]
28pub struct apm_bios_info {
29    pub version: u16,
30    pub cseg: u16,
31    pub offset: u32,
32    pub cseg_16: u16,
33    pub dseg: u16,
34    pub flags: u16,
35    pub cseg_len: u16,
36    pub cseg_16_len: u16,
37    pub dseg_len: u16,
38}
39
40#[repr(C)]
41#[derive(Debug, Copy, Clone, IntoBytes, Immutable, KnownLayout, FromBytes)]
42pub struct screen_info {
43    pub orig_x: u8,
44    pub orig_y: u8,
45    pub ext_mem_k: u16,
46    pub orig_video_page: u16,
47    pub orig_video_mode: u8,
48    pub orig_video_cols: u8,
49    pub flags: u8,
50    pub unused2: u8,
51    pub orig_video_ega_bx: u16,
52    pub unused3: u16,
53    pub orig_video_lines: u8,
54    pub orig_video_is_vga: u8,
55    pub orig_video_points: u16,
56    pub lfb_width: u16,
57    pub lfb_height: u16,
58    pub lfb_depth: u16,
59    pub lfb_base: u32,
60    pub lfb_size: u32,
61    pub cl_magic: u16,
62    pub cl_offset: u16,
63    pub lfb_linelength: u16,
64    pub red_size: u8,
65    pub red_pos: u8,
66    pub green_size: u8,
67    pub green_pos: u8,
68    pub blue_size: u8,
69    pub blue_pos: u8,
70    pub rsvd_size: u8,
71    pub rsvd_pos: u8,
72    pub vesapm_seg: u16,
73    pub vesapm_off: u16,
74    pub pages: u16,
75    pub vesa_attributes: u16,
76    pub capabilities: u32_ne,
77    pub _reserved: [u8; 6],
78}
79
80#[repr(C)]
81#[derive(Debug, Copy, Clone, IntoBytes, Immutable, KnownLayout, FromBytes)]
82pub struct sys_desc_table {
83    pub length: u16,
84    pub table: [u8; 14],
85}
86
87#[repr(C)]
88#[derive(Debug, Copy, Clone, IntoBytes, Immutable, KnownLayout, FromBytes)]
89pub struct olpc_ofw_header {
90    pub ofw_magic: u32,
91    pub ofw_version: u32,
92    pub cif_handler: u32,
93    pub irq_desc_table: u32,
94}
95
96#[repr(C)]
97#[derive(Copy, Clone, IntoBytes, Immutable, KnownLayout, FromBytes)]
98pub struct edid_info {
99    pub dummy: [u8; 128],
100}
101
102#[repr(C)]
103#[derive(Debug, Copy, Clone, IntoBytes, Immutable, KnownLayout, FromBytes)]
104pub struct efi_info {
105    pub efi_loader_signature: u32,
106    pub efi_systab: u32,
107    pub efi_memdesc_size: u32,
108    pub efi_memdesc_version: u32,
109    pub efi_memmap: u32,
110    pub efi_memmap_size: u32,
111    pub efi_systab_hi: u32,
112    pub efi_memmap_hi: u32,
113}
114
115#[repr(C)]
116#[derive(Debug, Copy, Clone, IntoBytes, Immutable, KnownLayout, FromBytes)]
117pub struct setup_header {
118    pub setup_sects: u8,
119    pub root_flags: u16_ne,
120    pub syssize: u32_ne,
121    pub ram_size: u16_ne,
122    pub vid_mode: u16_ne,
123    pub root_dev: u16_ne,
124    pub boot_flag: u16_ne,
125    pub jump: u16_ne,
126    pub header: u32_ne,
127    pub version: u16_ne,
128    pub realmode_swtch: u32_ne,
129    pub start_sys: u16_ne,
130    pub kernel_version: u16_ne,
131    pub type_of_loader: u8,
132    pub loadflags: u8,
133    pub setup_move_size: u16_ne,
134    pub code32_start: u32_ne,
135    pub ramdisk_image: u32_ne,
136    pub ramdisk_size: u32_ne,
137    pub bootsect_kludge: u32_ne,
138    pub heap_end_ptr: u16_ne,
139    pub ext_loader_ver: u8,
140    pub ext_loader_type: u8,
141    pub cmd_line_ptr: u32_ne,
142    pub initrd_addr_max: u32_ne,
143    pub kernel_alignment: u32_ne,
144    pub relocatable_kernel: u8,
145    pub min_alignment: u8,
146    pub xloadflags: u16_ne,
147    pub cmdline_size: u32_ne,
148    pub hardware_subarch: u32_ne,
149    pub hardware_subarch_data: u64_ne,
150    pub payload_offset: u32_ne,
151    pub payload_length: u32_ne,
152    pub setup_data: u64_ne,
153    pub pref_address: u64_ne,
154    pub init_size: u32_ne,
155    pub handover_offset: u32_ne,
156}
157
158// TODO: zerocopy doesn't support const new methods, so define them as u32 for now. (https://github.com/microsoft/openvmm/issues/759)
159pub const E820_RAM: u32 = 1;
160pub const E820_RESERVED: u32 = 2;
161pub const E820_ACPI: u32 = 3;
162pub const E820_NVS: u32 = 4;
163pub const E820_UNUSABLE: u32 = 5;
164
165#[repr(C)]
166#[derive(Debug, Copy, Clone, IntoBytes, Immutable, KnownLayout, FromBytes)]
167pub struct e820entry {
168    pub addr: u64_ne,
169    pub size: u64_ne,
170    pub typ: u32_ne,
171}
172
173#[repr(C)]
174#[derive(Debug, Copy, Clone, IntoBytes, Immutable, KnownLayout, FromBytes)]
175pub struct edd_info {
176    pub device: u8,
177    pub version: u8,
178    pub interface_support: u16,
179    pub legacy_max_cylinder: u16,
180    pub legacy_max_head: u8,
181    pub legacy_sectors_per_track: u8,
182    pub params: edd_device_params,
183}
184
185#[repr(C)]
186#[derive(Debug, Copy, Clone, IntoBytes, Immutable, KnownLayout, FromBytes)]
187pub struct edd_device_params {
188    pub length: u16,
189    pub info_flags: u16,
190    pub num_default_cylinders: u32_ne,
191    pub num_default_heads: u32_ne,
192    pub sectors_per_track: u32_ne,
193    pub number_of_sectors: u64_ne,
194    pub bytes_per_sector: u16,
195    pub dpte_ptr: u32_ne,
196    pub key: u16,
197    pub device_path_info_length: u8,
198    pub reserved2: u8,
199    pub reserved3: u16,
200    pub host_bus_type: [u8; 4],
201    pub interface_type: [u8; 8],
202    pub interface_path: [u8; 8],
203    pub device_path: [u8; 16],
204    pub reserved4: u8,
205    pub checksum: u8,
206}
207
208#[repr(C)]
209#[derive(Debug, Copy, Clone, IntoBytes, Immutable, KnownLayout, FromBytes)]
210pub struct ist_info {
211    pub signature: u32,
212    pub command: u32,
213    pub event: u32,
214    pub perf_level: u32,
215}
216
217#[repr(C)]
218#[derive(Copy, Clone, IntoBytes, Immutable, KnownLayout, FromBytes)]
219pub struct boot_params {
220    pub screen_info: screen_info,
221    pub apm_bios_info: apm_bios_info,
222    pub _pad2: [u8; 4],
223    pub tboot_addr: u64,
224    pub ist_info: ist_info,
225    pub acpi_rsdp_addr: u64,
226    pub _pad3: [u8; 8],
227    pub hd0_info: [u8; 16],
228    pub hd1_info: [u8; 16],
229    pub sys_desc_table: sys_desc_table,
230    pub olpc_ofw_header: olpc_ofw_header,
231    pub ext_ramdisk_image: u32,
232    pub ext_ramdisk_size: u32,
233    pub ext_cmd_line_ptr: u32,
234    pub _pad4: [[u8; 29]; 4],
235    pub edid_info: edid_info,
236    pub efi_info: efi_info,
237    pub alt_mem_k: u32,
238    pub scratch: u32,
239    pub e820_entries: u8,
240    pub eddbuf_entries: u8,
241    pub edd_mbr_sig_buf_entries: u8,
242    pub kbd_status: u8,
243    pub _pad5: [u8; 3],
244    pub sentinel: u8,
245    pub _pad6: [u8; 1],
246    pub hdr: setup_header,
247    pub _pad7: [u8; 40],
248    pub edd_mbr_sig_buffer: [u32; 16],
249    pub e820_map: [e820entry; 128],
250    pub _pad8: [u8; 48],
251    pub eddbuf: [edd_info; 6],
252    pub _pad9: [[u8; 23]; 12],
253}
254
255impl Default for boot_params {
256    fn default() -> Self {
257        FromZeros::new_zeroed()
258    }
259}
260
261const_assert_eq!(size_of::<boot_params>(), 4096);
262
263// This must be aligned so that it doesn't straddle a page boundary.
264#[repr(C, align(16))]
265#[derive(Copy, Clone, IntoBytes, Immutable, KnownLayout, FromBytes)]
266pub struct setup_data {
267    pub next: u64,
268    pub ty: u32,
269    pub len: u32,
270}
271
272pub const SETUP_E820_EXT: u32 = 1;
273pub const SETUP_DTB: u32 = 2;
274pub const SETUP_CC_BLOB: u32 = 7;
275
276#[repr(C)]
277#[derive(Copy, Clone, IntoBytes, Immutable, KnownLayout, FromBytes)]
278pub struct cc_blob_sev_info {
279    pub magic: u32,
280    pub version: u16,
281    pub _reserved: u16,
282    pub secrets_phys: u64,
283    pub secrets_len: u32,
284    pub _rsvd1: u32,
285    pub cpuid_phys: u64,
286    pub cpuid_len: u32,
287    pub _rsvd2: u32,
288}
289
290pub const CC_BLOB_SEV_INFO_MAGIC: u32 = 0x45444d41;
291
292#[repr(C, align(16))]
293#[derive(Copy, Clone, IntoBytes, Immutable, KnownLayout, FromBytes)]
294pub struct cc_setup_data {
295    pub header: setup_data,
296    pub cc_blob_address: u32,
297    pub _padding: [u32; 3],
298}
299
300/// Magic value for a measured [`SnpBootShimParams`] page.
301pub const SNP_BOOT_SHIM_PARAMS_MAGIC: u64 = u64::from_le_bytes(*b"SNPBSHIM");
302/// Version of the [`SnpBootShimParams`] handoff ABI.
303pub const SNP_BOOT_SHIM_PARAMS_VERSION: u32 = 3;
304/// Magic value for the measured platform extension.
305pub const SNP_BOOT_SHIM_PLATFORM_MAGIC: u64 = u64::from_le_bytes(*b"SNPACPI3");
306/// Version of the platform extension.
307pub const SNP_BOOT_SHIM_PLATFORM_VERSION: u32 = 2;
308/// Maximum host device-tree size accepted by the fixed SNP profile.
309pub const SNP_BOOT_SHIM_DT_SIZE: u64 = 64 * 1024;
310/// Reserved capacity for the complete generated ACPI table set.
311pub const SNP_BOOT_SHIM_ACPI_SIZE: u64 = 64 * 1024;
312/// Temporary allocator capacity for bounded full ACPI generation.
313pub const SNP_BOOT_SHIM_HEAP_SIZE: u64 = 1024 * 1024;
314/// Maximum number of PCIe host bridges supported by the SNP bootshim.
315pub const SNP_BOOT_SHIM_MAX_PCIE_BRIDGES: usize = 8;
316/// Maximum number of CPUs in the host device tree.
317pub const SNP_BOOT_SHIM_MAX_CPUS: usize = 255;
318/// Maximum number of RAM ranges in the host device tree.
319pub const SNP_BOOT_SHIM_MAX_RAM_RANGES: usize = 64;
320/// Exclusive upper bound of the RAM that the shim can accept.
321pub const SNP_BOOT_SHIM_MAX_RAM_END: u64 = 512 << 30;
322const SNP_BOOT_SHIM_PARAMS_HEADER_SIZE: usize = 48;
323/// Maximum number of RAM ranges that fit in one [`SnpBootShimParams`] page.
324pub const SNP_BOOT_SHIM_MAX_RANGES: usize = (hvdef::HV_PAGE_SIZE as usize
325    - SNP_BOOT_SHIM_PARAMS_HEADER_SIZE)
326    / size_of::<SnpBootShimRange>();
327
328/// A contiguous range of private RAM for the SNP bootshim to accept.
329#[repr(C)]
330#[derive(Debug, Copy, Clone, Eq, PartialEq, IntoBytes, Immutable, KnownLayout, FromBytes)]
331pub struct SnpBootShimRange {
332    /// The first 4-KiB guest page number in the range.
333    pub start_gpn: u64,
334    /// The number of 4-KiB pages in the range.
335    pub page_count: u64,
336}
337
338/// The measured handoff from the IGVM generator to the SNP Linux bootshim.
339///
340/// The generator imports one page with this layout and starts the bootshim
341/// with RSI pointing to it. The bootshim validates the complete header and the
342/// active prefix of `ranges` before it accepts any omitted RAM or enters Linux.
343#[repr(C, align(4096))]
344#[derive(Debug, Copy, Clone, Eq, PartialEq, IntoBytes, Immutable, KnownLayout, FromBytes)]
345pub struct SnpBootShimParams {
346    /// Must equal [`SNP_BOOT_SHIM_PARAMS_MAGIC`].
347    pub magic: u64,
348    /// Must equal [`SNP_BOOT_SHIM_PARAMS_VERSION`].
349    pub version: u32,
350    /// Number of valid entries at the start of `ranges`.
351    pub range_count: u32,
352    /// Identity-mapped address of the direct-boot Linux entry point.
353    pub linux_entry: u64,
354    /// Guest physical address of the Linux zero page passed in RSI.
355    pub linux_zero_page: u64,
356    /// Exclusive end of the RAM that the image measures. The host device tree
357    /// describes this RAM and any RAM above it.
358    pub ram_end: u64,
359    /// Page-aligned GPA of the mandatory [`SnpBootShimPlatformParams`].
360    pub platform_gpa: u64,
361    /// Sorted, non-overlapping RAM ranges omitted from measured page data.
362    pub ranges: [SnpBootShimRange; SNP_BOOT_SHIM_MAX_RANGES],
363}
364
365/// Measured locations and capacities for host-DT-based full ACPI generation.
366///
367/// The device-tree contents are unmeasured host input. These bounds, unlike
368/// those contents, are part of the measured image and cannot be supplied by DT.
369#[repr(C)]
370#[derive(Debug, Copy, Clone, Eq, PartialEq, IntoBytes, Immutable, KnownLayout, FromBytes)]
371pub struct SnpBootShimPlatformParams {
372    pub magic: u64,
373    pub version: u32,
374    pub reserved: u32,
375    pub dt_gpa: u64,
376    pub dt_size: u64,
377    pub heap_gpa: u64,
378    pub heap_size: u64,
379    pub acpi_output_gpa: u64,
380    pub acpi_output_size: u64,
381    pub rsdp_gpa: u64,
382    /// SNP page-table encryption bit; PCIe addresses must lie below this bit.
383    pub c_bit_mask: u64,
384}
385
386const_assert_eq!(size_of::<SnpBootShimPlatformParams>(), 80);
387const_assert_eq!(core::mem::offset_of!(boot_params, acpi_rsdp_addr), 0x70);
388
389const_assert_eq!(size_of::<SnpBootShimRange>(), 16);
390const_assert_eq!(align_of::<SnpBootShimRange>(), 8);
391const_assert_eq!(
392    core::mem::offset_of!(SnpBootShimParams, ranges),
393    SNP_BOOT_SHIM_PARAMS_HEADER_SIZE
394);
395const_assert_eq!(size_of::<SnpBootShimParams>(), hvdef::HV_PAGE_SIZE as usize);
396const_assert_eq!(
397    align_of::<SnpBootShimParams>(),
398    hvdef::HV_PAGE_SIZE as usize
399);