1#![expect(missing_docs)]
7#![forbid(unsafe_code)]
8#![no_std]
9
10extern crate alloc;
11
12use alloc::string::String;
13use bitfield_struct::bitfield;
14use core::fmt::Display;
15use core::ops::Index;
16use core::ops::IndexMut;
17#[cfg(feature = "inspect")]
18use inspect::Inspect;
19use open_enum::open_enum;
20use serde::Deserialize;
21use serde::Serialize;
22use static_assertions::const_assert;
23use zerocopy::FromBytes;
24use zerocopy::Immutable;
25use zerocopy::IntoBytes;
26use zerocopy::KnownLayout;
27
28pub const VMGS_DEFAULT_CAPACITY: u64 = 0x400000;
36
37open_enum! {
38 #[cfg_attr(feature = "inspect", derive(Inspect))]
40 #[cfg_attr(feature = "inspect", inspect(debug))]
41 pub enum FileId: u32 {
42 FILE_TABLE = 0,
43
44 BIOS_NVRAM = 1,
45 TPM_PPI = 2,
46 TPM_NVRAM = 3,
47 RTC_SKEW = 4,
48 ATTEST = 5,
49 KEY_PROTECTOR = 6,
50 VM_UNIQUE_ID = 7,
51 GUEST_FIRMWARE = 8,
52 CUSTOM_UEFI = 9,
53 GUEST_WATCHDOG = 10,
54 HW_KEY_PROTECTOR = 11,
55 GUEST_SECRET_KEY = 13,
56 HIBERNATION_FIRMWARE = 14,
57 PLATFORM_SEED = 15,
58 PROVENANCE_DOC = 16,
59 TPM_NVRAM_BACKUP = 17,
60 PROVISIONING_MARKER = 18,
61
62 EXTENDED_FILE_TABLE = 63,
63 }
64}
65
66impl Display for FileId {
67 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
68 f.write_fmt(format_args!("File ID {} ({:?})", self.0, self))
69 }
70}
71
72pub const VMGS_VERSION_2_0: u32 = 0x00020000;
73pub const VMGS_VERSION_3_0: u32 = 0x00030000;
74
75pub const VMGS_SIGNATURE: u64 = u64::from_le_bytes(*b"GUESTRTS"); pub const VMGS_BYTES_PER_BLOCK: u32 = 4096;
78
79const VMGS_MAX_CAPACITY_BLOCKS: u64 = 0x100000000;
80pub const VMGS_MAX_CAPACITY_BYTES: u64 = VMGS_MAX_CAPACITY_BLOCKS * VMGS_BYTES_PER_BLOCK as u64;
81
82pub const VMGS_MIN_FILE_BLOCK_OFFSET: u32 = 2;
83pub const VMGS_FILE_COUNT: usize = 64;
84pub const VMGS_MAX_FILE_SIZE_BLOCKS: u64 = 0xFFFFFFFF;
85pub const VMGS_MAX_FILE_SIZE_BYTES: u64 = VMGS_MAX_FILE_SIZE_BLOCKS * VMGS_BYTES_PER_BLOCK as u64;
86
87pub const VMGS_NONCE_SIZE: usize = 12; pub const VMGS_NONCE_RANDOM_SEED_SIZE: usize = 4;
89pub const VMGS_AUTHENTICATION_TAG_SIZE: usize = 16;
90pub const VMGS_ENCRYPTION_KEY_SIZE: usize = 32;
91
92pub type VmgsNonce = [u8; VMGS_NONCE_SIZE];
93pub type VmgsAuthTag = [u8; VMGS_AUTHENTICATION_TAG_SIZE];
94pub type VmgsDatastoreKey = [u8; VMGS_ENCRYPTION_KEY_SIZE];
95
96#[repr(C)]
97#[derive(Clone, Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
98pub struct VmgsFileEntry {
99 pub offset: u32,
101 pub allocation_size: u32,
102 pub valid_data_size: u64,
103
104 pub nonce: VmgsNonce,
106 pub authentication_tag: VmgsAuthTag,
107
108 pub attributes: FileAttribute,
111
112 pub reserved: [u8; 16],
113}
114
115const_assert!(size_of::<VmgsFileEntry>() == 64);
116
117impl Index<FileId> for [VmgsFileEntry] {
118 type Output = VmgsFileEntry;
119
120 fn index(&self, file_id: FileId) -> &Self::Output {
121 &self[file_id.0 as usize]
122 }
123}
124
125impl IndexMut<FileId> for [VmgsFileEntry] {
126 fn index_mut(&mut self, file_id: FileId) -> &mut Self::Output {
127 &mut self[file_id.0 as usize]
128 }
129}
130
131#[repr(C)]
132#[derive(Clone, Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
133pub struct VmgsExtendedFileEntry {
134 pub attributes: FileAttribute,
135 pub encryption_key: VmgsDatastoreKey,
136
137 pub reserved: [u8; 28],
138}
139
140const_assert!(size_of::<VmgsExtendedFileEntry>() == 64);
141
142impl Index<FileId> for [VmgsExtendedFileEntry] {
143 type Output = VmgsExtendedFileEntry;
144
145 fn index(&self, file_id: FileId) -> &Self::Output {
146 &self[file_id.0 as usize]
147 }
148}
149
150impl IndexMut<FileId> for [VmgsExtendedFileEntry] {
151 fn index_mut(&mut self, file_id: FileId) -> &mut Self::Output {
152 &mut self[file_id.0 as usize]
153 }
154}
155
156#[repr(C)]
157#[derive(Clone, Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
158#[cfg_attr(feature = "inspect", derive(Inspect))]
159pub struct VmgsEncryptionKey {
160 pub nonce: VmgsNonce,
161 pub reserved: u32,
162 pub authentication_tag: VmgsAuthTag,
163 pub encryption_key: VmgsDatastoreKey,
164}
165
166const_assert!(size_of::<VmgsEncryptionKey>() == 64);
167
168#[repr(C)]
169#[derive(Clone, Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
170pub struct VmgsHeader {
171 pub signature: u64,
173 pub version: u32,
174 pub checksum: u32,
175 pub sequence: u32,
176 pub header_size: u32,
177
178 pub file_table_offset: u32,
180 pub file_table_size: u32,
181
182 pub encryption_algorithm: EncryptionAlgorithm,
184 pub markers: VmgsMarkers,
185 pub metadata_keys: [VmgsEncryptionKey; 2],
186 pub reserved_1: u32,
187}
188
189const_assert!(size_of::<VmgsHeader>() == 168);
190
191#[repr(C)]
192#[derive(Clone, IntoBytes, Immutable, KnownLayout, FromBytes, Debug)]
193pub struct VmgsFileTable {
194 pub entries: [VmgsFileEntry; VMGS_FILE_COUNT],
195}
196
197const_assert!(size_of::<VmgsFileTable>() == 4096);
198const_assert!((size_of::<VmgsFileTable>() as u32).is_multiple_of(VMGS_BYTES_PER_BLOCK));
199pub const VMGS_FILE_TABLE_BLOCK_SIZE: u32 =
200 size_of::<VmgsFileTable>() as u32 / VMGS_BYTES_PER_BLOCK;
201
202#[repr(C)]
203#[derive(Clone, IntoBytes, Immutable, KnownLayout, FromBytes)]
204pub struct VmgsExtendedFileTable {
205 pub entries: [VmgsExtendedFileEntry; VMGS_FILE_COUNT],
206}
207
208const_assert!(size_of::<VmgsExtendedFileTable>() == 4096);
209const_assert!((size_of::<VmgsExtendedFileTable>() as u32).is_multiple_of(VMGS_BYTES_PER_BLOCK));
210pub const VMGS_EXTENDED_FILE_TABLE_BLOCK_SIZE: u32 =
211 size_of::<VmgsExtendedFileTable>() as u32 / VMGS_BYTES_PER_BLOCK;
212
213#[cfg_attr(feature = "inspect", derive(Inspect))]
215#[bitfield(u32)]
216#[derive(IntoBytes, Immutable, KnownLayout, FromBytes, PartialEq, Eq)]
217pub struct FileAttribute {
218 pub encrypted: bool,
219 pub authenticated: bool,
220 #[bits(30)]
221 _reserved: u32,
222}
223
224open_enum! {
225 #[cfg_attr(feature = "inspect", derive(Inspect))]
227 #[cfg_attr(feature = "inspect", inspect(debug))]
228 #[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
229 pub enum EncryptionAlgorithm: u16 {
230 NONE = 0,
232 AES_GCM = 1,
234 }
235}
236
237#[cfg_attr(feature = "inspect", derive(Inspect))]
239#[bitfield(u16)]
240#[derive(IntoBytes, Immutable, KnownLayout, FromBytes, PartialEq, Eq)]
241pub struct VmgsMarkers {
242 pub reprovisioned: bool,
243 #[bits(15)]
244 _reserved: u16,
245}
246
247#[cfg_attr(feature = "inspect", derive(Inspect))]
249#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
250#[serde(rename_all = "lowercase")]
251pub enum VmgsProvisioner {
252 Unknown,
253 Hcl,
254 OpenHcl,
255 CpsVmgstoolCvm,
256 HaVmgstoolTvm,
257 HclPostProvisioning,
258}
259
260#[cfg_attr(feature = "inspect", derive(Inspect))]
262#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
263#[serde(rename_all = "lowercase")]
264pub enum VmgsProvisioningReason {
265 Empty,
267 Failure,
269 Request,
271 Unknown,
273}
274
275#[cfg_attr(feature = "inspect", derive(Inspect))]
280#[derive(Debug, Serialize, Deserialize)]
281pub struct VmgsProvisioningMarker {
282 pub provisioner: VmgsProvisioner,
283 pub reason: VmgsProvisioningReason,
284 pub tpm_version: String,
285 pub tpm_nvram_size: usize,
286 pub akcert_size: usize,
287 pub akcert_attrs: String,
288 pub provisioner_version: String,
289}