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