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