hvlite_defs/
config.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Configuration for the VM worker.
5
6use guid::Guid;
7use hvlite_pcat_locator::RomFileLocation;
8use input_core::InputData;
9use memory_range::MemoryRange;
10use mesh::MeshPayload;
11use mesh::payload::Protobuf;
12use net_backend_resources::mac_address::MacAddress;
13use std::fmt;
14use std::fs::File;
15use vm_resource::Resource;
16use vm_resource::kind::PciDeviceHandleKind;
17use vm_resource::kind::VirtioDeviceHandle;
18use vm_resource::kind::VmbusDeviceHandleKind;
19use vmgs_resources::VmgsResource;
20use vmotherboard::ChipsetDeviceHandle;
21use vmotherboard::options::BaseChipsetManifest;
22
23#[derive(MeshPayload, Debug)]
24pub struct Config {
25    pub load_mode: LoadMode,
26    pub floppy_disks: Vec<floppy_resources::FloppyDiskConfig>,
27    pub ide_disks: Vec<ide_resources::IdeDeviceConfig>,
28    pub pcie_root_complexes: Vec<PcieRootComplexConfig>,
29    pub pcie_devices: Vec<PcieDeviceConfig>,
30    pub pcie_switches: Vec<PcieSwitchConfig>,
31    pub vpci_devices: Vec<VpciDeviceConfig>,
32    pub memory: MemoryConfig,
33    pub processor_topology: ProcessorTopologyConfig,
34    pub hypervisor: HypervisorConfig,
35    pub chipset: BaseChipsetManifest,
36    pub vmbus: Option<VmbusConfig>,
37    pub vtl2_vmbus: Option<VmbusConfig>,
38    #[cfg(windows)]
39    pub kernel_vmnics: Vec<KernelVmNicConfig>,
40    pub input: mesh::Receiver<InputData>,
41    pub framebuffer: Option<framebuffer::Framebuffer>,
42    pub vga_firmware: Option<RomFileLocation>,
43    pub vtl2_gfx: bool,
44    pub virtio_console_pci: bool,
45    pub virtio_serial: Option<SerialPipes>,
46    pub virtio_devices: Vec<(VirtioBus, Resource<VirtioDeviceHandle>)>,
47    #[cfg(windows)]
48    pub vpci_resources: Vec<virt_whp::device::DeviceHandle>,
49    pub vmgs: Option<VmgsResource>,
50    pub secure_boot_enabled: bool,
51    pub custom_uefi_vars: firmware_uefi_custom_vars::CustomVars,
52    // TODO: move FirmwareEvent somewhere not GED-specific.
53    pub firmware_event_send: Option<mesh::Sender<get_resources::ged::FirmwareEvent>>,
54    pub debugger_rpc: Option<mesh::Receiver<vmm_core_defs::debug_rpc::DebugRequest>>,
55    pub vmbus_devices: Vec<(DeviceVtl, Resource<VmbusDeviceHandleKind>)>,
56    pub chipset_devices: Vec<ChipsetDeviceHandle>,
57    pub generation_id_recv: Option<mesh::Receiver<[u8; 16]>>,
58    // This is used for testing. TODO: resourcify, and also store this in VMGS.
59    pub rtc_delta_milliseconds: i64,
60    /// allow the guest to reset without notifying the client
61    pub automatic_guest_reset: bool,
62    pub efi_diagnostics_log_level: EfiDiagnosticsLogLevelType,
63}
64
65// ARM64 needs a larger low gap.
66const DEFAULT_LOW_MMAP_GAP_SIZE_X86: u64 = 1024 * 1024 * 128;
67const DEFAULT_LOW_MMAP_GAP_SIZE_AARCH64: u64 = 1024 * 1024 * 512;
68
69/// Default mmio gaps for an x86 partition.
70pub const DEFAULT_MMIO_GAPS_X86: [MemoryRange; 2] = [
71    MemoryRange::new(0x1_0000_0000 - DEFAULT_LOW_MMAP_GAP_SIZE_X86..0x1_0000_0000), // nMB just below 4GB
72    MemoryRange::new(0xF_E000_0000..0x10_0000_0000), // 512MB just below 64GB, then up to 64GB
73];
74
75/// Default mmio gaps for x86 if VTL2 is enabled.
76pub const DEFAULT_MMIO_GAPS_X86_WITH_VTL2: [MemoryRange; 3] = [
77    MemoryRange::new(0x1_0000_0000 - DEFAULT_LOW_MMAP_GAP_SIZE_X86..0x1_0000_0000), // nMB just below 4GB
78    MemoryRange::new(0xF_E000_0000..0x20_0000_0000), // 512MB just below 64GB, then up to 128GB
79    MemoryRange::new(0x20_0000_0000..0x20_4000_0000), // 128GB to 129 GB
80];
81
82/// Default mmio gaps for an aarch64 partition.
83pub const DEFAULT_MMIO_GAPS_AARCH64: [MemoryRange; 2] = [
84    MemoryRange::new(0x1_0000_0000 - DEFAULT_LOW_MMAP_GAP_SIZE_AARCH64..0x1_0000_0000), // nMB just below 4GB
85    MemoryRange::new(0xF_E000_0000..0x10_0000_0000), // 512MB just below 64GB, then up to 64GB
86];
87
88/// Default mmio gaps for aarch64 if VTL2 is enabled.
89pub const DEFAULT_MMIO_GAPS_AARCH64_WITH_VTL2: [MemoryRange; 3] = [
90    MemoryRange::new(0x1_0000_0000 - DEFAULT_LOW_MMAP_GAP_SIZE_AARCH64..0x1_0000_0000), // nMB just below 4GB
91    MemoryRange::new(0xF_E000_0000..0x20_0000_0000), // 512MB just below 64GB, then up to 128GB
92    MemoryRange::new(0x20_0000_0000..0x20_4000_0000), // 128GB to 129 GB
93];
94
95pub const DEFAULT_GIC_DISTRIBUTOR_BASE: u64 = 0xFFFF_0000;
96// The KVM in-kernel vGICv3 requires the distributor and redistributor bases be 64KiB aligned.
97pub const DEFAULT_GIC_REDISTRIBUTORS_BASE: u64 = if cfg!(target_os = "linux") {
98    0xEFFF_0000
99} else {
100    0xEFFE_E000
101};
102
103pub const DEFAULT_PCIE_ECAM_BASE: u64 = 0x8_0000_0000; // 32GB, size depends on configuration
104
105#[derive(MeshPayload, Debug)]
106pub enum LoadMode {
107    Linux {
108        kernel: File,
109        initrd: Option<File>,
110        cmdline: String,
111        enable_serial: bool,
112        custom_dsdt: Option<Vec<u8>>,
113    },
114    Uefi {
115        firmware: File,
116        enable_debugging: bool,
117        enable_memory_protections: bool,
118        disable_frontpage: bool,
119        enable_tpm: bool,
120        enable_battery: bool,
121        enable_serial: bool,
122        enable_vpci_boot: bool,
123        uefi_console_mode: Option<UefiConsoleMode>,
124        default_boot_always_attempt: bool,
125        bios_guid: Guid,
126    },
127    Pcat {
128        firmware: RomFileLocation,
129        boot_order: [PcatBootDevice; 4],
130    },
131    Igvm {
132        file: File,
133        cmdline: String,
134        vtl2_base_address: Vtl2BaseAddressType,
135        com_serial: Option<SerialInformation>,
136    },
137    None,
138}
139
140#[derive(Debug, Clone, Copy, MeshPayload)]
141pub struct SerialInformation {
142    pub io_port: u16,
143    pub irq: u32,
144}
145
146/// Different types to specify the base address for the VTL2 region of the IGVM
147/// file.
148#[derive(Debug, Clone, Copy, MeshPayload)]
149pub enum Vtl2BaseAddressType {
150    /// Use the addresses specified in the file. The IGVM file does not need to
151    /// support relocations.
152    File,
153    /// Put VTL2 at the specified address. The IGVM file must support
154    /// relocations.
155    Absolute(u64),
156    /// Use the specified range in the supplied MemoryLayout, as the caller has
157    /// created a specific range for VTL2. The IGVM file must support
158    /// relocations.
159    ///
160    /// An optional size may be specified to override the size describing VTL2
161    /// provided in the IGVM file. It must be larger than the IGVM file provided
162    /// size.
163    MemoryLayout { size: Option<u64> },
164    /// Tell VTL2 to allocate out it's own memory. This will load the file at
165    /// the base address specified in the file, and the host will tell VTL2 the
166    /// size of memory to allocate for itself.
167    ///
168    /// An optional size may be specified to override the size describing VTL2
169    /// provided in the IGVM file. It must be larger than the IGVM file provided
170    /// size.
171    Vtl2Allocate { size: Option<u64> },
172}
173
174#[derive(Debug, MeshPayload)]
175pub struct PcieRootComplexConfig {
176    pub index: u32,
177    pub name: String,
178    pub segment: u16,
179    pub start_bus: u8,
180    pub end_bus: u8,
181    pub low_mmio_size: u32,
182    pub high_mmio_size: u64,
183    pub ports: Vec<PcieRootPortConfig>,
184}
185
186#[derive(Debug, MeshPayload)]
187pub struct PcieRootPortConfig {
188    pub name: String,
189    pub hotplug: bool,
190}
191
192#[derive(Debug, MeshPayload)]
193pub struct PcieSwitchConfig {
194    pub name: String,
195    pub num_downstream_ports: u8,
196    pub parent_port: String,
197    pub hotplug: bool,
198}
199
200#[derive(Debug, MeshPayload)]
201pub struct PcieDeviceConfig {
202    pub port_name: String,
203    pub resource: Resource<PciDeviceHandleKind>,
204}
205
206#[derive(Debug, MeshPayload)]
207pub struct VpciDeviceConfig {
208    pub vtl: DeviceVtl,
209    /// The ID of the device. Vpci devices are identified by a portion of `data2` and `data3` of the
210    /// instance ID, which is used to generate the guest-visible device ID.
211    pub instance_id: Guid,
212    pub resource: Resource<PciDeviceHandleKind>,
213}
214
215#[derive(Debug, Protobuf)]
216pub struct ProcessorTopologyConfig {
217    pub proc_count: u32,
218    pub vps_per_socket: Option<u32>,
219    pub enable_smt: Option<bool>,
220    pub arch: Option<ArchTopologyConfig>,
221}
222
223#[derive(Debug, Protobuf, Default, Clone)]
224pub struct X86TopologyConfig {
225    pub apic_id_offset: u32,
226    pub x2apic: X2ApicConfig,
227}
228
229#[derive(Debug, Default, Copy, Clone, Protobuf)]
230pub enum X2ApicConfig {
231    #[default]
232    /// Support the X2APIC if recommended by the hypervisor or if needed by the
233    /// topology configuration.
234    Auto,
235    /// Support the X2APIC, and automatically enable it if needed to address all
236    /// processors.
237    Supported,
238    /// Do not support the X2APIC.
239    Unsupported,
240    /// Support and enable the X2APIC.
241    Enabled,
242}
243
244#[derive(Debug, Protobuf, Default, Clone)]
245pub enum PmuGsivConfig {
246    #[default]
247    /// Use the hypervisor's platform GSIV value for the PMU.
248    Platform,
249    /// Use the specified GSIV value for the PMU.
250    Gsiv(u32),
251}
252
253#[derive(Debug, Protobuf, Default, Clone)]
254pub struct Aarch64TopologyConfig {
255    pub gic_config: Option<GicConfig>,
256    pub pmu_gsiv: PmuGsivConfig,
257}
258
259#[derive(Debug, Protobuf, Clone)]
260pub struct GicConfig {
261    pub gic_distributor_base: u64,
262    pub gic_redistributors_base: u64,
263}
264
265#[derive(Debug, Protobuf, Clone)]
266pub enum ArchTopologyConfig {
267    X86(X86TopologyConfig),
268    Aarch64(Aarch64TopologyConfig),
269}
270
271#[derive(Debug, MeshPayload)]
272pub struct MemoryConfig {
273    pub mem_size: u64,
274    pub mmio_gaps: Vec<MemoryRange>,
275    pub prefetch_memory: bool,
276    pub pcie_ecam_base: u64,
277}
278
279#[derive(Debug, MeshPayload, Default)]
280pub struct VmbusConfig {
281    pub vsock_listener: Option<unix_socket::UnixListener>,
282    pub vsock_path: Option<String>,
283    pub vmbus_max_version: Option<u32>,
284    #[cfg(windows)]
285    pub vmbusproxy_handle: Option<vmbus_proxy::ProxyHandle>,
286    pub vtl2_redirect: bool,
287}
288
289#[derive(Debug, MeshPayload, Default)]
290pub struct HypervisorConfig {
291    pub with_hv: bool,
292    pub user_mode_hv_enlightenments: bool,
293    pub user_mode_apic: bool,
294    pub with_vtl2: Option<Vtl2Config>,
295    pub with_isolation: Option<IsolationType>,
296}
297
298#[derive(Debug, Copy, Clone, MeshPayload)]
299pub enum Hypervisor {
300    Kvm,
301    MsHv,
302    Whp,
303    Hvf,
304}
305
306impl fmt::Display for Hypervisor {
307    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
308        f.pad(match self {
309            Self::Kvm => "kvm",
310            Self::MsHv => "mshv",
311            Self::Whp => "whp",
312            Self::Hvf => "hvf",
313        })
314    }
315}
316
317/// Input and output for a connected serial port.
318#[derive(Debug, MeshPayload)]
319pub struct SerialPipes {
320    /// Input for a serial port.
321    ///
322    /// If the file reaches EOF, then the serial port will report carrier drop
323    /// to the guest. Use `None` when the port should remain connected
324    /// indefinitely.
325    pub input: Option<File>,
326    /// Output for a serial port.
327    ///
328    /// If the file write fails with [`std::io::ErrorKind::BrokenPipe`], then
329    /// the serial port will report carrier drop to the guest.
330    ///
331    /// `None` is equivalent to `/dev/null`--it will silently succeed all
332    /// writes.
333    pub output: Option<File>,
334}
335
336impl SerialPipes {
337    pub fn try_clone(&self) -> std::io::Result<Self> {
338        Ok(Self {
339            input: self.input.as_ref().map(File::try_clone).transpose()?,
340            output: self.output.as_ref().map(File::try_clone).transpose()?,
341        })
342    }
343}
344
345#[derive(Debug, MeshPayload)]
346pub struct KernelVmNicConfig {
347    pub instance_id: Guid,
348    pub mac_address: MacAddress,
349    pub switch_port_id: SwitchPortId,
350}
351
352#[derive(Clone, Debug, MeshPayload)]
353pub struct SwitchPortId {
354    pub switch: Guid,
355    pub port: Guid,
356}
357
358pub const DEFAULT_PCAT_BOOT_ORDER: [PcatBootDevice; 4] = [
359    PcatBootDevice::Optical,
360    PcatBootDevice::HardDrive,
361    PcatBootDevice::Network,
362    PcatBootDevice::Floppy,
363];
364
365#[derive(MeshPayload, Debug, Clone, Copy, PartialEq)]
366pub enum PcatBootDevice {
367    Floppy,
368    HardDrive,
369    Optical,
370    Network,
371}
372
373#[derive(Eq, PartialEq, Debug, Copy, Clone, MeshPayload)]
374pub enum VirtioBus {
375    Mmio,
376    Pci,
377}
378
379/// Policy for the partition when mapping VTL0 memory late.
380#[derive(Eq, PartialEq, Debug, Copy, Clone, MeshPayload)]
381pub enum LateMapVtl0MemoryPolicy {
382    /// Halt execution of the VP if VTL0 memory is accessed.
383    Halt,
384    /// Log the error but emulate the access with the instruction emulator.
385    Log,
386    /// Inject an exception into the guest.
387    InjectException,
388}
389
390impl From<LateMapVtl0MemoryPolicy> for virt::LateMapVtl0MemoryPolicy {
391    fn from(value: LateMapVtl0MemoryPolicy) -> Self {
392        match value {
393            LateMapVtl0MemoryPolicy::Halt => virt::LateMapVtl0MemoryPolicy::Halt,
394            LateMapVtl0MemoryPolicy::Log => virt::LateMapVtl0MemoryPolicy::Log,
395            LateMapVtl0MemoryPolicy::InjectException => {
396                virt::LateMapVtl0MemoryPolicy::InjectException
397            }
398        }
399    }
400}
401
402/// Configuration for VTL2.
403///
404/// NOTE: This is distinct from `virt::Vtl2Config` to keep an abstraction
405/// between the virt crate and this crate. Users should not be specifying
406/// virt crate configuration directly.
407#[derive(Debug, Clone, MeshPayload)]
408pub struct Vtl2Config {
409    /// Enable the VTL0 alias map. This maps VTL0's view of memory in VTL2 at
410    /// the highest legal physical address bit.
411    pub vtl0_alias_map: bool,
412    /// If set, map VTL0 memory late after VTL2 has started. The current
413    /// heuristic is to defer mapping VTL0 memory until the first
414    /// `HvModifyVtlProtectionMask` hypercall is made.
415    pub late_map_vtl0_memory: Option<LateMapVtl0MemoryPolicy>,
416}
417
418// Isolation type for a partition.
419#[derive(Eq, PartialEq, Debug, Copy, Clone, MeshPayload)]
420pub enum IsolationType {
421    Vbs,
422}
423
424impl From<IsolationType> for virt::IsolationType {
425    fn from(value: IsolationType) -> Self {
426        match value {
427            IsolationType::Vbs => Self::Vbs,
428        }
429    }
430}
431
432/// Which VTL to assign a particular device to.
433#[derive(Copy, Clone, Debug, PartialEq, Eq, MeshPayload)]
434pub enum DeviceVtl {
435    Vtl0,
436    Vtl1,
437    Vtl2,
438}
439
440#[derive(Copy, Clone, Debug, MeshPayload)]
441pub enum UefiConsoleMode {
442    Default,
443    Com1,
444    Com2,
445    None,
446}
447
448#[derive(Copy, Clone, Debug, MeshPayload, Default)]
449pub enum EfiDiagnosticsLogLevelType {
450    /// Default log level
451    #[default]
452    Default,
453    /// Include INFO logs
454    Info,
455    /// All logs
456    Full,
457}