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 serde::Deserialize;
10use serde::Serialize;
11
12/// A type-alias to mark fields as _temporarily_ optional to preserve
13/// build-to-compat compatibility during internal testing.
14///
15/// i.e: a newly added field should be marked as `DevLoopCompatOption` until
16/// we're sure that all hosts that we expect this new underhill version to run
17/// on are updated to send the new field.
18///
19/// It would be **very bad form** to ship a library/binary that includes
20/// `DevLoopCompatOption` fields!
21pub type DevLoopCompatOption<T> = Option<T>;
22
23#[derive(Debug, Default, Deserialize, Serialize)]
24#[serde(rename_all = "PascalCase")]
25pub struct DevicePlatformSettingsV2Json {
26    pub v1: HclDevicePlatformSettings,
27    pub v2: HclDevicePlatformSettingsV2,
28}
29
30// The legacy DPS response's mars schema specifies all fields as [OmitEmpty],
31// which we handle by setting `serde(default)` at the struct level.
32//
33// This is _not_ the case in the newer DPS packet, whereby all fields must be
34// present, specifying "empty values" if the data is not set.
35#[derive(Debug, Default, Deserialize, Serialize)]
36#[serde(default, rename_all = "PascalCase")]
37pub struct HclDevicePlatformSettings {
38    pub secure_boot_enabled: bool,
39    pub secure_boot_template_id: HclSecureBootTemplateId,
40    pub enable_battery: bool,
41    pub enable_processor_idle: bool,
42    pub enable_tpm: bool,
43    pub com1: HclUartSettings,
44    pub com2: HclUartSettings,
45    #[serde(with = "serde_helpers::as_string")]
46    pub bios_guid: Guid,
47    pub console_mode: u8,
48    pub enable_firmware_debugging: bool,
49    pub enable_hibernation: bool,
50    pub serial_number: String,
51    pub base_board_serial_number: String,
52    pub chassis_serial_number: String,
53    pub chassis_asset_tag: String,
54}
55
56// requires a `Default` derive, due to [OmitEmpty] used in parent struct
57#[derive(Debug, Default, Deserialize, Serialize)]
58#[serde(rename_all = "PascalCase")]
59pub enum HclSecureBootTemplateId {
60    #[serde(rename = "None")]
61    #[default]
62    None,
63    #[serde(rename = "MicrosoftWindows")]
64    MicrosoftWindows,
65    #[serde(rename = "MicrosoftUEFICertificateAuthority")]
66    MicrosoftUEFICertificateAuthority,
67}
68
69// requires a `Default` derive, due to [OmitEmpty] used in parent struct
70#[derive(Debug, Default, Deserialize, Serialize)]
71#[serde(default, rename_all = "PascalCase")]
72pub struct HclUartSettings {
73    pub enable_port: bool,
74    pub debugger_mode: bool,
75    pub enable_vmbus_redirector: bool,
76}
77
78#[derive(Debug, Default, Deserialize, Serialize)]
79#[serde(rename_all = "PascalCase")]
80pub struct HclDevicePlatformSettingsV2 {
81    pub r#static: HclDevicePlatformSettingsV2Static,
82    pub dynamic: HclDevicePlatformSettingsV2Dynamic,
83}
84
85/// Boot device order entry used by the PCAT Bios.
86#[derive(Debug, Copy, Clone, Deserialize, Serialize)]
87pub enum PcatBootDevice {
88    Floppy,
89    Optical,
90    HardDrive,
91    Network,
92}
93
94/// Guest state lifetime
95#[derive(Debug, Copy, Clone, Deserialize, Serialize, Default)]
96pub enum GuestStateLifetime {
97    #[default]
98    Default,
99    ReprovisionOnFailure,
100    Reprovision,
101    Ephemeral,
102}
103
104/// Guest state encryption policy
105#[derive(Debug, Copy, Clone, Deserialize, Serialize, Default)]
106pub enum GuestStateEncryptionPolicy {
107    /// Use the best encryption available, allowing fallback.
108    ///
109    /// VMs will be created as or migrated to the best encryption available,
110    /// attempting GspKey, then GspById, and finally leaving the data
111    /// unencrypted if neither are available.
112    #[default]
113    Auto,
114    /// Prefer (or require, if strict) no encryption.
115    ///
116    /// Do not encrypt the guest state unless it is already encrypted and
117    /// strict encryption policy is disabled.
118    None,
119    /// Prefer (or require, if strict) GspById.
120    ///
121    /// This prevents a VM from being created as or migrated to GspKey even
122    /// if it is available. Exisiting GspKey encryption will be used unless
123    /// strict encryption policy is enabled. Fails if the data cannot be
124    /// encrypted.
125    GspById,
126    /// Require GspKey.
127    ///
128    /// VMs will be created as or migrated to GspKey. Fails if GspKey is
129    /// not available. Strict encryption policy has no effect here since
130    /// GspKey is currently the most secure policy.
131    GspKey,
132    /// Use hardware sealing
133    // TODO: update this doc comment once hardware sealing is implemented
134    HardwareSealing,
135}
136
137/// Management VTL Feature Flags
138#[bitfield(u64)]
139#[derive(Deserialize, Serialize)]
140#[serde(transparent)]
141pub struct ManagementVtlFeatures {
142    pub strict_encryption_policy: bool,
143    pub _reserved1: bool,
144    pub control_ak_cert_provisioning: bool,
145    pub attempt_ak_cert_callback: bool,
146    #[bits(60)]
147    pub _reserved2: u64,
148}
149
150#[derive(Debug, Default, Deserialize, Serialize)]
151#[serde(rename_all = "PascalCase")]
152pub struct HclDevicePlatformSettingsV2Static {
153    //UEFI flags
154    pub legacy_memory_map: bool,
155    pub pause_after_boot_failure: bool,
156    pub pxe_ip_v6: bool,
157    pub measure_additional_pcrs: bool,
158    pub disable_frontpage: bool,
159    pub disable_sha384_pcr: bool,
160    pub media_present_enabled_by_default: bool,
161    pub memory_protection_mode: u8,
162    #[serde(default)]
163    pub default_boot_always_attempt: bool,
164
165    // UEFI info
166    pub vpci_boot_enabled: bool,
167    #[serde(default)]
168    #[serde(with = "serde_helpers::opt_guid_str")]
169    pub vpci_instance_filter: Option<Guid>,
170
171    // PCAT info
172    pub num_lock_enabled: bool,
173    pub pcat_boot_device_order: Option<[PcatBootDevice; 4]>,
174
175    pub smbios: HclDevicePlatformSettingsV2StaticSmbios,
176
177    // Per field serde(default) is required here because that
178    // we can't reply on serde's normal behavior for optional
179    // fields (put None if not present in json) because we're
180    // using custom serialize/deserialize methods
181    #[serde(default)]
182    #[serde(with = "serde_helpers::opt_base64_vec")]
183    pub vtl2_settings: Option<Vec<u8>>,
184
185    pub vmbus_redirection_enabled: bool,
186    pub no_persistent_secrets: bool,
187    pub watchdog_enabled: bool,
188    // this `#[serde(default)]` shouldn't have been necessary, but we let a
189    // `[OmitEmpty]` marker slip past in code review...
190    #[serde(default)]
191    pub firmware_mode_is_pcat: bool,
192    #[serde(default)]
193    pub always_relay_host_mmio: bool,
194    #[serde(default)]
195    pub imc_enabled: bool,
196    #[serde(default)]
197    pub cxl_memory_enabled: bool,
198    #[serde(default)]
199    pub guest_state_lifetime: GuestStateLifetime,
200    #[serde(default)]
201    pub guest_state_encryption_policy: GuestStateEncryptionPolicy,
202    #[serde(default)]
203    pub management_vtl_features: ManagementVtlFeatures,
204}
205
206#[derive(Debug, Default, Deserialize, Serialize)]
207#[serde(rename_all = "PascalCase")]
208pub struct HclDevicePlatformSettingsV2StaticSmbios {
209    pub system_manufacturer: String,
210    pub system_product_name: String,
211    pub system_version: String,
212    #[serde(rename = "SystemSKUNumber")]
213    pub system_sku_number: String,
214    pub system_family: String,
215    pub bios_lock_string: String,
216    pub memory_device_serial_number: String,
217}
218
219#[derive(Debug, Default, Deserialize, Serialize)]
220#[serde(rename_all = "PascalCase")]
221pub struct HclDevicePlatformSettingsV2Dynamic {
222    pub nvdimm_count: u16,
223    pub enable_psp: bool,
224    pub generation_id_low: u64,
225    pub generation_id_high: u64,
226    pub smbios: HclDevicePlatformSettingsV2DynamicSmbios,
227    pub is_servicing_scenario: bool,
228
229    #[serde(default)]
230    #[serde(with = "serde_helpers::vec_base64_vec")]
231    pub acpi_tables: Vec<Vec<u8>>,
232}
233
234#[derive(Debug, Default, Deserialize, Serialize)]
235#[serde(rename_all = "PascalCase")]
236pub struct HclDevicePlatformSettingsV2DynamicSmbios {
237    #[serde(with = "serde_helpers::base64_vec")]
238    pub processor_manufacturer: Vec<u8>,
239    #[serde(with = "serde_helpers::base64_vec")]
240    pub processor_version: Vec<u8>,
241
242    #[serde(rename = "ProcessorID")]
243    pub processor_id: u64,
244    pub external_clock: u16,
245    pub max_speed: u16,
246    pub current_speed: u16,
247    pub processor_characteristics: u16,
248    pub processor_family2: u16,
249    pub processor_type: u8,
250    pub voltage: u8,
251    pub status: u8,
252    pub processor_upgrade: u8,
253}
254
255#[cfg(test)]
256mod test {
257    use super::*;
258
259    #[test]
260    fn smoke_test_sample() {
261        serde_json::from_slice::<DevicePlatformSettingsV2Json>(include_bytes!(
262            "dps_test_json.json"
263        ))
264        .unwrap();
265    }
266
267    #[test]
268    fn smoke_test_sample_with_vtl2settings() {
269        serde_json::from_slice::<DevicePlatformSettingsV2Json>(include_bytes!(
270            "dps_test_json_with_vtl2settings.json"
271        ))
272        .unwrap();
273    }
274}