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