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_TOKEN = 14,
57 PLATFORM_SEED = 15,
58 PROVENANCE_DOC = 16,
59 TPM_NVRAM_BACKUP = 17,
60 PROVISIONING_MARKER = 18,
61 TPM_185_NVRAM = 19,
62
63 EXTENDED_FILE_TABLE = 63,
64 }
65}
66
67impl Display for FileId {
68 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
69 f.write_fmt(format_args!("File ID {} ({:?})", self.0, self))
70 }
71}
72
73pub const VMGS_VERSION_2_0: u32 = 0x00020000;
74pub const VMGS_VERSION_3_0: u32 = 0x00030000;
75
76pub const VMGS_SIGNATURE: u64 = u64::from_le_bytes(*b"GUESTRTS"); pub const VMGS_BYTES_PER_BLOCK: u32 = 4096;
79
80const VMGS_MAX_CAPACITY_BLOCKS: u64 = 0x100000000;
81pub const VMGS_MAX_CAPACITY_BYTES: u64 = VMGS_MAX_CAPACITY_BLOCKS * VMGS_BYTES_PER_BLOCK as u64;
82
83pub const VMGS_MIN_FILE_BLOCK_OFFSET: u32 = 2;
84pub const VMGS_FILE_COUNT: usize = 64;
85pub const VMGS_MAX_FILE_SIZE_BLOCKS: u64 = 0xFFFFFFFF;
86pub const VMGS_MAX_FILE_SIZE_BYTES: u64 = VMGS_MAX_FILE_SIZE_BLOCKS * VMGS_BYTES_PER_BLOCK as u64;
87
88pub const VMGS_NONCE_SIZE: usize = 12; pub const VMGS_NONCE_RANDOM_SEED_SIZE: usize = 4;
90pub const VMGS_AUTHENTICATION_TAG_SIZE: usize = 16;
91pub const VMGS_ENCRYPTION_KEY_SIZE: usize = 32;
92
93pub type VmgsNonce = [u8; VMGS_NONCE_SIZE];
94pub type VmgsAuthTag = [u8; VMGS_AUTHENTICATION_TAG_SIZE];
95pub type VmgsDatastoreKey = [u8; VMGS_ENCRYPTION_KEY_SIZE];
96
97#[repr(C)]
98#[derive(Clone, Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
99pub struct VmgsFileEntry {
100 pub offset: u32,
102 pub allocation_size: u32,
103 pub valid_data_size: u64,
104
105 pub nonce: VmgsNonce,
107 pub authentication_tag: VmgsAuthTag,
108
109 pub attributes: FileAttribute,
112
113 pub reserved: [u8; 16],
114}
115
116const_assert!(size_of::<VmgsFileEntry>() == 64);
117
118impl Index<FileId> for [VmgsFileEntry] {
119 type Output = VmgsFileEntry;
120
121 fn index(&self, file_id: FileId) -> &Self::Output {
122 &self[file_id.0 as usize]
123 }
124}
125
126impl IndexMut<FileId> for [VmgsFileEntry] {
127 fn index_mut(&mut self, file_id: FileId) -> &mut Self::Output {
128 &mut self[file_id.0 as usize]
129 }
130}
131
132#[repr(C)]
133#[derive(Clone, Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
134pub struct VmgsExtendedFileEntry {
135 pub attributes: FileAttribute,
136 pub encryption_key: VmgsDatastoreKey,
137
138 pub reserved: [u8; 28],
139}
140
141const_assert!(size_of::<VmgsExtendedFileEntry>() == 64);
142
143impl Index<FileId> for [VmgsExtendedFileEntry] {
144 type Output = VmgsExtendedFileEntry;
145
146 fn index(&self, file_id: FileId) -> &Self::Output {
147 &self[file_id.0 as usize]
148 }
149}
150
151impl IndexMut<FileId> for [VmgsExtendedFileEntry] {
152 fn index_mut(&mut self, file_id: FileId) -> &mut Self::Output {
153 &mut self[file_id.0 as usize]
154 }
155}
156
157#[repr(C)]
158#[derive(Clone, Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
159#[cfg_attr(feature = "inspect", derive(Inspect))]
160pub struct VmgsEncryptionKey {
161 pub nonce: VmgsNonce,
162 pub reserved: u32,
163 pub authentication_tag: VmgsAuthTag,
164 pub encryption_key: VmgsDatastoreKey,
165}
166
167const_assert!(size_of::<VmgsEncryptionKey>() == 64);
168
169#[repr(C)]
170#[derive(Clone, Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
171pub struct VmgsHeader {
172 pub signature: u64,
174 pub version: u32,
175 pub checksum: u32,
176 pub sequence: u32,
177 pub header_size: u32,
178
179 pub file_table_offset: u32,
181 pub file_table_size: u32,
182
183 pub encryption_algorithm: EncryptionAlgorithm,
185 pub markers: VmgsMarkers,
186 pub metadata_keys: [VmgsEncryptionKey; 2],
187 pub reserved_1: u32,
188}
189
190const_assert!(size_of::<VmgsHeader>() == 168);
191
192#[repr(C)]
193#[derive(Clone, IntoBytes, Immutable, KnownLayout, FromBytes, Debug)]
194pub struct VmgsFileTable {
195 pub entries: [VmgsFileEntry; VMGS_FILE_COUNT],
196}
197
198const_assert!(size_of::<VmgsFileTable>() == 4096);
199const_assert!((size_of::<VmgsFileTable>() as u32).is_multiple_of(VMGS_BYTES_PER_BLOCK));
200pub const VMGS_FILE_TABLE_BLOCK_SIZE: u32 =
201 size_of::<VmgsFileTable>() as u32 / VMGS_BYTES_PER_BLOCK;
202
203#[repr(C)]
204#[derive(Clone, IntoBytes, Immutable, KnownLayout, FromBytes)]
205pub struct VmgsExtendedFileTable {
206 pub entries: [VmgsExtendedFileEntry; VMGS_FILE_COUNT],
207}
208
209const_assert!(size_of::<VmgsExtendedFileTable>() == 4096);
210const_assert!((size_of::<VmgsExtendedFileTable>() as u32).is_multiple_of(VMGS_BYTES_PER_BLOCK));
211pub const VMGS_EXTENDED_FILE_TABLE_BLOCK_SIZE: u32 =
212 size_of::<VmgsExtendedFileTable>() as u32 / VMGS_BYTES_PER_BLOCK;
213
214#[cfg_attr(feature = "inspect", derive(Inspect))]
216#[bitfield(u32)]
217#[derive(IntoBytes, Immutable, KnownLayout, FromBytes, PartialEq, Eq)]
218pub struct FileAttribute {
219 pub encrypted: bool,
220 pub authenticated: bool,
221 #[bits(30)]
222 _reserved: u32,
223}
224
225open_enum! {
226 #[cfg_attr(feature = "inspect", derive(Inspect))]
228 #[cfg_attr(feature = "inspect", inspect(debug))]
229 #[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
230 pub enum EncryptionAlgorithm: u16 {
231 NONE = 0,
233 AES_GCM = 1,
235 }
236}
237
238#[cfg_attr(feature = "inspect", derive(Inspect))]
240#[bitfield(u16)]
241#[derive(IntoBytes, Immutable, KnownLayout, FromBytes, PartialEq, Eq)]
242pub struct VmgsMarkers {
243 pub reprovisioned: bool,
244 #[bits(15)]
245 _reserved: u16,
246}
247
248#[cfg_attr(feature = "inspect", derive(Inspect))]
250#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
251#[serde(rename_all = "lowercase")]
252pub enum VmgsProvisioner {
253 Unknown,
254 Hcl,
255 OpenHcl,
256 CpsVmgstoolCvm,
257 HaVmgstoolTvm,
258 HclPostProvisioning,
259}
260
261#[cfg_attr(feature = "inspect", derive(Inspect))]
263#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
264#[serde(rename_all = "lowercase")]
265pub enum VmgsProvisioningReason {
266 Empty,
268 Failure,
270 Request,
272 Unknown,
274}
275
276#[cfg_attr(feature = "inspect", derive(Inspect))]
281#[derive(Debug, Serialize, Deserialize)]
282pub struct VmgsProvisioningMarker {
283 pub provisioner: VmgsProvisioner,
284 pub reason: VmgsProvisioningReason,
285 pub tpm_version: String,
286 pub tpm_nvram_size: usize,
287 pub akcert_size: usize,
288 pub akcert_attrs: String,
289 pub provisioner_version: String,
290}