Skip to main content

get_protocol/
dps_json.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! The schema defined in this file must match the one defined in
5//! `onecore/vm/schema/mars/Config/Config.Devices.Chipset.mars`.
6
7use bitfield_struct::bitfield;
8use guid::Guid;
9use open_enum::open_enum;
10use serde::Deserialize;
11use serde::Serialize;
12
13/// A type-alias to mark fields as _temporarily_ optional to preserve
14/// build-to-compat compatibility during internal testing.
15///
16/// i.e: a newly added field should be marked as `DevLoopCompatOption` until
17/// we're sure that all hosts that we expect this new underhill version to run
18/// on are updated to send the new field.
19///
20/// It would be **very bad form** to ship a library/binary that includes
21/// `DevLoopCompatOption` fields!
22pub type DevLoopCompatOption<T> = Option<T>;
23
24#[derive(Debug, Default, Deserialize, Serialize)]
25#[serde(rename_all = "PascalCase")]
26pub struct DevicePlatformSettingsV2Json {
27    pub v1: HclDevicePlatformSettings,
28    pub v2: HclDevicePlatformSettingsV2,
29}
30
31// The legacy DPS response's mars schema specifies all fields as [OmitEmpty],
32// which we handle by setting `serde(default)` at the struct level.
33//
34// This is _not_ the case in the newer DPS packet, whereby all fields must be
35// present, specifying "empty values" if the data is not set.
36#[derive(Debug, Default, Deserialize, Serialize)]
37#[serde(default, rename_all = "PascalCase")]
38pub struct HclDevicePlatformSettings {
39    pub secure_boot_enabled: bool,
40    pub secure_boot_template_id: HclSecureBootTemplateId,
41    pub enable_battery: bool,
42    pub enable_processor_idle: bool,
43    pub enable_tpm: bool,
44    pub com1: HclUartSettings,
45    pub com2: HclUartSettings,
46    #[serde(with = "serde_helpers::as_string")]
47    pub bios_guid: Guid,
48    pub console_mode: u8,
49    pub enable_firmware_debugging: bool,
50    pub enable_hibernation: bool,
51    pub serial_number: String,
52    pub base_board_serial_number: String,
53    pub chassis_serial_number: String,
54    pub chassis_asset_tag: String,
55}
56
57// requires a `Default` derive, due to [OmitEmpty] used in parent struct
58#[derive(Debug, Default, Deserialize, Serialize)]
59#[serde(rename_all = "PascalCase")]
60pub enum HclSecureBootTemplateId {
61    #[serde(rename = "None")]
62    #[default]
63    None,
64    #[serde(rename = "MicrosoftWindows")]
65    MicrosoftWindows,
66    #[serde(rename = "MicrosoftUEFICertificateAuthority")]
67    MicrosoftUEFICertificateAuthority,
68}
69
70// requires a `Default` derive, due to [OmitEmpty] used in parent struct
71#[derive(Debug, Default, Deserialize, Serialize)]
72#[serde(default, rename_all = "PascalCase")]
73pub struct HclUartSettings {
74    pub enable_port: bool,
75    pub debugger_mode: bool,
76    pub enable_vmbus_redirector: bool,
77}
78
79#[derive(Debug, Default, Deserialize, Serialize)]
80#[serde(rename_all = "PascalCase")]
81pub struct HclDevicePlatformSettingsV2 {
82    pub r#static: HclDevicePlatformSettingsV2Static,
83    pub dynamic: HclDevicePlatformSettingsV2Dynamic,
84}
85
86/// Boot device order entry used by the PCAT Bios.
87#[derive(Debug, Copy, Clone, Deserialize, Serialize)]
88pub enum PcatBootDevice {
89    Floppy,
90    Optical,
91    HardDrive,
92    Network,
93}
94
95/// Guest state lifetime
96#[derive(Debug, Copy, Clone, Deserialize, Serialize, Default)]
97pub enum GuestStateLifetime {
98    #[default]
99    Default,
100    ReprovisionOnFailure,
101    Reprovision,
102    Ephemeral,
103}
104
105/// Guest state encryption policy
106#[derive(Debug, Copy, Clone, Deserialize, Serialize, Default)]
107pub enum GuestStateEncryptionPolicy {
108    /// Use the best encryption available, allowing fallback.
109    ///
110    /// VMs will be created using the best encryption available,
111    /// attempting GspKey, then GspById, and finally leaving the data
112    /// unencrypted if neither are available. VMs will not be migrated
113    /// to a different encryption method.
114    #[default]
115    Auto,
116    /// Prefer (or require, if strict) no encryption.
117    ///
118    /// Do not encrypt the guest state unless it is already encrypted and
119    /// strict encryption policy is disabled.
120    None,
121    /// Prefer (or require, if strict) GspById.
122    ///
123    /// This prevents a VM from being created as or migrated to GspKey even
124    /// if it is available. Existing GspKey encryption will be used unless
125    /// strict encryption policy is enabled. Fails if the data cannot be
126    /// encrypted.
127    GspById,
128    /// Prefer (or require, if strict) GspKey.
129    ///
130    /// VMs will be created as or migrated to GspKey. GspById encryption will
131    /// be used if GspKey is unavailable unless strict encryption policy is
132    /// enabled. Fails if the data cannot be encrypted.
133    GspKey,
134    /// Use hardware sealing exclusively.
135    ///
136    /// Expected to be set only when `no_persistent_secrets` is true on CVMs.
137    HardwareSealing,
138}
139
140open_enum! {
141    /// EFI Diagnostics Log Level Filter
142    #[derive(Default, Deserialize, Serialize)]
143    pub enum EfiDiagnosticsLogLevelType: u32 {
144        /// Default log level
145        DEFAULT = 0,
146        /// Include INFO logs
147        INFO = 1,
148        /// All logs
149        FULL = 2,
150    }
151}
152
153/// Hardware sealing policy
154///
155/// Selects how the hardware-derived key used to seal the VMGS DEK is computed
156/// (e.g. whether the OpenHCL measurement is mixed into the derivation).
157///
158/// On CVMs the policy governs the hardware-sealing-based VMGS DEK backup by
159/// default. When [`GuestStateEncryptionPolicy::HardwareSealing`] is selected
160/// (stateless mode, i.e. `no_persistent_secrets` is true), the same policy
161/// governs the exclusive hardware sealing that becomes the sole source of the
162/// VMGS DEK.
163#[derive(Debug, Copy, Clone, Deserialize, Serialize, Default)]
164pub enum HardwareSealingPolicy {
165    /// No hardware sealing
166    #[default]
167    None,
168    /// Hash-based hardware sealing
169    Hash,
170    /// Signer-based hardware sealing
171    Signer,
172}
173
174/// Version of the Microsoft TPM reference implementation to expose to the
175/// guest.
176#[derive(Debug, Copy, Clone, Deserialize, Serialize)]
177pub enum GetTpmVersion {
178    /// TPM reference implementation version 1.38
179    V138,
180    /// TPM reference implementation version 1.85
181    V185,
182}
183
184/// Management VTL Feature Flags
185#[bitfield(u64)]
186#[derive(Deserialize, Serialize)]
187#[serde(transparent)]
188pub struct ManagementVtlFeatures {
189    pub strict_encryption_policy: bool,
190    /// The host supports the `LOAD_FIRMWARE` host request (VTL0 firmware
191    /// overload). Bit 1 (`0x00000002`).
192    pub load_firmware_supported: bool,
193    pub control_ak_cert_provisioning: bool,
194    pub attempt_ak_cert_callback: bool,
195    pub tx_only_serial_port: bool,
196    #[bits(59)]
197    pub _reserved2: u64,
198}
199
200#[derive(Debug, Default, Deserialize, Serialize)]
201#[serde(rename_all = "PascalCase")]
202pub struct HclDevicePlatformSettingsV2Static {
203    // UEFI flags
204    pub legacy_memory_map: bool,
205    pub pause_after_boot_failure: bool,
206    pub pxe_ip_v6: bool,
207    pub measure_additional_pcrs: bool,
208    pub disable_frontpage: bool,
209    pub disable_sha384_pcr: bool,
210    pub media_present_enabled_by_default: bool,
211    pub memory_protection_mode: u8,
212    #[serde(default)]
213    pub default_boot_always_attempt: bool,
214
215    // UEFI info
216    pub vpci_boot_enabled: bool,
217    #[serde(default)]
218    #[serde(with = "serde_helpers::opt_guid_str")]
219    pub vpci_instance_filter: Option<Guid>,
220
221    // PCAT info
222    pub num_lock_enabled: bool,
223    pub pcat_boot_device_order: Option<[PcatBootDevice; 4]>,
224
225    pub smbios: HclDevicePlatformSettingsV2StaticSmbios,
226
227    // Per field serde(default) is required here because that
228    // we can't reply on serde's normal behavior for optional
229    // fields (put None if not present in json) because we're
230    // using custom serialize/deserialize methods
231    #[serde(default)]
232    #[serde(with = "serde_helpers::opt_base64_vec")]
233    pub vtl2_settings: Option<Vec<u8>>,
234
235    pub vmbus_redirection_enabled: bool,
236    pub no_persistent_secrets: bool,
237    pub watchdog_enabled: bool,
238    // this `#[serde(default)]` shouldn't have been necessary, but we let a
239    // `[OmitEmpty]` marker slip past in code review...
240    #[serde(default)]
241    pub firmware_mode_is_pcat: bool,
242    #[serde(default)]
243    pub always_relay_host_mmio: bool,
244    #[serde(default)]
245    pub imc_enabled: bool,
246    #[serde(default)]
247    pub cxl_memory_enabled: bool,
248    #[serde(default)]
249    pub guest_state_lifetime: GuestStateLifetime,
250    #[serde(default)]
251    pub guest_state_encryption_policy: GuestStateEncryptionPolicy,
252    #[serde(default)]
253    pub efi_diagnostics_log_level: EfiDiagnosticsLogLevelType,
254    #[serde(default)]
255    pub management_vtl_features: ManagementVtlFeatures,
256    #[serde(default)]
257    pub force_dma_bounce_enabled: bool,
258    #[serde(default)]
259    pub hardware_sealing_policy_id: HardwareSealingPolicy,
260    #[serde(default)]
261    pub tpm_version: Option<GetTpmVersion>,
262}
263
264#[derive(Debug, Default, Deserialize, Serialize)]
265#[serde(rename_all = "PascalCase")]
266pub struct HclDevicePlatformSettingsV2StaticSmbios {
267    pub system_manufacturer: String,
268    pub system_product_name: String,
269    pub system_version: String,
270    #[serde(rename = "SystemSKUNumber")]
271    pub system_sku_number: String,
272    pub system_family: String,
273    pub bios_lock_string: String,
274    pub memory_device_serial_number: String,
275}
276
277#[derive(Debug, Default, Deserialize, Serialize)]
278#[serde(rename_all = "PascalCase")]
279pub struct HclDevicePlatformSettingsV2Dynamic {
280    pub nvdimm_count: u16,
281    pub enable_psp: bool,
282    pub generation_id_low: u64,
283    pub generation_id_high: u64,
284    pub smbios: HclDevicePlatformSettingsV2DynamicSmbios,
285    pub is_servicing_scenario: bool,
286
287    #[serde(default)]
288    #[serde(with = "serde_helpers::vec_base64_vec")]
289    pub acpi_tables: Vec<Vec<u8>>,
290}
291
292#[derive(Debug, Default, Deserialize, Serialize)]
293#[serde(rename_all = "PascalCase")]
294pub struct HclDevicePlatformSettingsV2DynamicSmbios {
295    #[serde(with = "serde_helpers::base64_vec")]
296    pub processor_manufacturer: Vec<u8>,
297    #[serde(with = "serde_helpers::base64_vec")]
298    pub processor_version: Vec<u8>,
299
300    #[serde(rename = "ProcessorID")]
301    pub processor_id: u64,
302    pub external_clock: u16,
303    pub max_speed: u16,
304    pub current_speed: u16,
305    pub processor_characteristics: u16,
306    pub processor_family2: u16,
307    pub processor_type: u8,
308    pub voltage: u8,
309    pub status: u8,
310    pub processor_upgrade: u8,
311}
312
313#[cfg(test)]
314mod test {
315    use super::*;
316
317    #[test]
318    fn smoke_test_sample() {
319        serde_json::from_slice::<DevicePlatformSettingsV2Json>(include_bytes!(
320            "dps_test_json.json"
321        ))
322        .unwrap();
323    }
324
325    #[test]
326    fn smoke_test_sample_with_vtl2settings() {
327        serde_json::from_slice::<DevicePlatformSettingsV2Json>(include_bytes!(
328            "dps_test_json_with_vtl2settings.json"
329        ))
330        .unwrap();
331    }
332}