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