xref: /cloud-hypervisor/vmm/src/vm_config.rs (revision f6cd3bd86ded632da437b6dd6077f4237d2f71fe)
1 // Copyright © 2022 Intel Corporation
2 //
3 // SPDX-License-Identifier: Apache-2.0
4 //
5 use net_util::MacAddr;
6 use serde::{Deserialize, Serialize};
7 use std::{net::Ipv4Addr, path::PathBuf};
8 use virtio_devices::RateLimiterConfig;
9 
10 #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
11 pub struct CpuAffinity {
12     pub vcpu: u8,
13     pub host_cpus: Vec<usize>,
14 }
15 
16 #[derive(Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
17 pub struct CpuFeatures {
18     #[cfg(target_arch = "x86_64")]
19     #[serde(default)]
20     pub amx: bool,
21 }
22 
23 #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
24 pub struct CpuTopology {
25     pub threads_per_core: u8,
26     pub cores_per_die: u8,
27     pub dies_per_package: u8,
28     pub packages: u8,
29 }
30 
31 // When booting with PVH boot the maximum physical addressable size
32 // is a 46 bit address space even when the host supports with 5-level
33 // paging.
34 pub const DEFAULT_MAX_PHYS_BITS: u8 = 46;
35 
36 pub fn default_cpuconfig_max_phys_bits() -> u8 {
37     DEFAULT_MAX_PHYS_BITS
38 }
39 
40 #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
41 pub struct CpusConfig {
42     pub boot_vcpus: u8,
43     pub max_vcpus: u8,
44     #[serde(default)]
45     pub topology: Option<CpuTopology>,
46     #[serde(default)]
47     pub kvm_hyperv: bool,
48     #[serde(default = "default_cpuconfig_max_phys_bits")]
49     pub max_phys_bits: u8,
50     #[serde(default)]
51     pub affinity: Option<Vec<CpuAffinity>>,
52     #[serde(default)]
53     pub features: CpuFeatures,
54 }
55 
56 pub const DEFAULT_VCPUS: u8 = 1;
57 
58 impl Default for CpusConfig {
59     fn default() -> Self {
60         CpusConfig {
61             boot_vcpus: DEFAULT_VCPUS,
62             max_vcpus: DEFAULT_VCPUS,
63             topology: None,
64             kvm_hyperv: false,
65             max_phys_bits: DEFAULT_MAX_PHYS_BITS,
66             affinity: None,
67             features: CpuFeatures::default(),
68         }
69     }
70 }
71 
72 pub const DEFAULT_NUM_PCI_SEGMENTS: u16 = 1;
73 pub fn default_platformconfig_num_pci_segments() -> u16 {
74     DEFAULT_NUM_PCI_SEGMENTS
75 }
76 
77 #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
78 pub struct PlatformConfig {
79     #[serde(default = "default_platformconfig_num_pci_segments")]
80     pub num_pci_segments: u16,
81     #[serde(default)]
82     pub iommu_segments: Option<Vec<u16>>,
83     #[serde(default)]
84     pub serial_number: Option<String>,
85     #[serde(default)]
86     pub uuid: Option<String>,
87     #[serde(default)]
88     pub oem_strings: Option<Vec<String>>,
89     #[cfg(feature = "tdx")]
90     #[serde(default)]
91     pub tdx: bool,
92     #[cfg(feature = "sev_snp")]
93     #[serde(default)]
94     pub sev_snp: bool,
95 }
96 
97 pub const DEFAULT_PCI_SEGMENT_APERTURE_WEIGHT: u32 = 1;
98 
99 fn default_pci_segment_aperture_weight() -> u32 {
100     DEFAULT_PCI_SEGMENT_APERTURE_WEIGHT
101 }
102 
103 #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
104 pub struct PciSegmentConfig {
105     #[serde(default)]
106     pub pci_segment: u16,
107     #[serde(default = "default_pci_segment_aperture_weight")]
108     pub mmio32_aperture_weight: u32,
109     #[serde(default = "default_pci_segment_aperture_weight")]
110     pub mmio64_aperture_weight: u32,
111 }
112 
113 #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
114 pub struct MemoryZoneConfig {
115     pub id: String,
116     pub size: u64,
117     #[serde(default)]
118     pub file: Option<PathBuf>,
119     #[serde(default)]
120     pub shared: bool,
121     #[serde(default)]
122     pub hugepages: bool,
123     #[serde(default)]
124     pub hugepage_size: Option<u64>,
125     #[serde(default)]
126     pub host_numa_node: Option<u32>,
127     #[serde(default)]
128     pub hotplug_size: Option<u64>,
129     #[serde(default)]
130     pub hotplugged_size: Option<u64>,
131     #[serde(default)]
132     pub prefault: bool,
133 }
134 
135 #[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize, Default)]
136 pub enum HotplugMethod {
137     #[default]
138     Acpi,
139     VirtioMem,
140 }
141 
142 fn default_memoryconfig_thp() -> bool {
143     true
144 }
145 
146 #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
147 pub struct MemoryConfig {
148     pub size: u64,
149     #[serde(default)]
150     pub mergeable: bool,
151     #[serde(default)]
152     pub hotplug_method: HotplugMethod,
153     #[serde(default)]
154     pub hotplug_size: Option<u64>,
155     #[serde(default)]
156     pub hotplugged_size: Option<u64>,
157     #[serde(default)]
158     pub shared: bool,
159     #[serde(default)]
160     pub hugepages: bool,
161     #[serde(default)]
162     pub hugepage_size: Option<u64>,
163     #[serde(default)]
164     pub prefault: bool,
165     #[serde(default)]
166     pub zones: Option<Vec<MemoryZoneConfig>>,
167     #[serde(default = "default_memoryconfig_thp")]
168     pub thp: bool,
169 }
170 
171 pub const DEFAULT_MEMORY_MB: u64 = 512;
172 
173 impl Default for MemoryConfig {
174     fn default() -> Self {
175         MemoryConfig {
176             size: DEFAULT_MEMORY_MB << 20,
177             mergeable: false,
178             hotplug_method: HotplugMethod::Acpi,
179             hotplug_size: None,
180             hotplugged_size: None,
181             shared: false,
182             hugepages: false,
183             hugepage_size: None,
184             prefault: false,
185             zones: None,
186             thp: true,
187         }
188     }
189 }
190 
191 #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize, Default)]
192 pub enum VhostMode {
193     #[default]
194     Client,
195     Server,
196 }
197 
198 #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
199 pub struct RateLimiterGroupConfig {
200     #[serde(default)]
201     pub id: String,
202     #[serde(default)]
203     pub rate_limiter_config: RateLimiterConfig,
204 }
205 
206 #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
207 pub struct VirtQueueAffinity {
208     pub queue_index: u16,
209     pub host_cpus: Vec<usize>,
210 }
211 
212 #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
213 pub struct DiskConfig {
214     pub path: Option<PathBuf>,
215     #[serde(default)]
216     pub readonly: bool,
217     #[serde(default)]
218     pub direct: bool,
219     #[serde(default)]
220     pub iommu: bool,
221     #[serde(default = "default_diskconfig_num_queues")]
222     pub num_queues: usize,
223     #[serde(default = "default_diskconfig_queue_size")]
224     pub queue_size: u16,
225     #[serde(default)]
226     pub vhost_user: bool,
227     pub vhost_socket: Option<String>,
228     #[serde(default)]
229     pub rate_limit_group: Option<String>,
230     #[serde(default)]
231     pub rate_limiter_config: Option<RateLimiterConfig>,
232     #[serde(default)]
233     pub id: Option<String>,
234     // For testing use only. Not exposed in API.
235     #[serde(default)]
236     pub disable_io_uring: bool,
237     // For testing use only. Not exposed in API.
238     #[serde(default)]
239     pub disable_aio: bool,
240     #[serde(default)]
241     pub pci_segment: u16,
242     #[serde(default)]
243     pub serial: Option<String>,
244     #[serde(default)]
245     pub queue_affinity: Option<Vec<VirtQueueAffinity>>,
246 }
247 
248 pub const DEFAULT_DISK_NUM_QUEUES: usize = 1;
249 
250 pub fn default_diskconfig_num_queues() -> usize {
251     DEFAULT_DISK_NUM_QUEUES
252 }
253 
254 pub const DEFAULT_DISK_QUEUE_SIZE: u16 = 128;
255 
256 pub fn default_diskconfig_queue_size() -> u16 {
257     DEFAULT_DISK_QUEUE_SIZE
258 }
259 
260 #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
261 pub struct NetConfig {
262     #[serde(default = "default_netconfig_tap")]
263     pub tap: Option<String>,
264     #[serde(default = "default_netconfig_ip")]
265     pub ip: Ipv4Addr,
266     #[serde(default = "default_netconfig_mask")]
267     pub mask: Ipv4Addr,
268     #[serde(default = "default_netconfig_mac")]
269     pub mac: MacAddr,
270     #[serde(default)]
271     pub host_mac: Option<MacAddr>,
272     #[serde(default)]
273     pub mtu: Option<u16>,
274     #[serde(default)]
275     pub iommu: bool,
276     #[serde(default = "default_netconfig_num_queues")]
277     pub num_queues: usize,
278     #[serde(default = "default_netconfig_queue_size")]
279     pub queue_size: u16,
280     #[serde(default)]
281     pub vhost_user: bool,
282     pub vhost_socket: Option<String>,
283     #[serde(default)]
284     pub vhost_mode: VhostMode,
285     #[serde(default)]
286     pub id: Option<String>,
287     #[serde(
288         default,
289         serialize_with = "serialize_netconfig_fds",
290         deserialize_with = "deserialize_netconfig_fds"
291     )]
292     pub fds: Option<Vec<i32>>,
293     #[serde(default)]
294     pub rate_limiter_config: Option<RateLimiterConfig>,
295     #[serde(default)]
296     pub pci_segment: u16,
297     #[serde(default = "default_netconfig_true")]
298     pub offload_tso: bool,
299     #[serde(default = "default_netconfig_true")]
300     pub offload_ufo: bool,
301     #[serde(default = "default_netconfig_true")]
302     pub offload_csum: bool,
303 }
304 
305 pub fn default_netconfig_true() -> bool {
306     true
307 }
308 
309 pub fn default_netconfig_tap() -> Option<String> {
310     None
311 }
312 
313 pub fn default_netconfig_ip() -> Ipv4Addr {
314     Ipv4Addr::new(192, 168, 249, 1)
315 }
316 
317 pub fn default_netconfig_mask() -> Ipv4Addr {
318     Ipv4Addr::new(255, 255, 255, 0)
319 }
320 
321 pub fn default_netconfig_mac() -> MacAddr {
322     MacAddr::local_random()
323 }
324 
325 pub const DEFAULT_NET_NUM_QUEUES: usize = 2;
326 
327 pub fn default_netconfig_num_queues() -> usize {
328     DEFAULT_NET_NUM_QUEUES
329 }
330 
331 pub const DEFAULT_NET_QUEUE_SIZE: u16 = 256;
332 
333 pub fn default_netconfig_queue_size() -> u16 {
334     DEFAULT_NET_QUEUE_SIZE
335 }
336 
337 fn serialize_netconfig_fds<S>(x: &Option<Vec<i32>>, s: S) -> Result<S::Ok, S::Error>
338 where
339     S: serde::Serializer,
340 {
341     if let Some(x) = x {
342         warn!("'NetConfig' contains FDs that can't be serialized correctly. Serializing them as invalid FDs.");
343         let invalid_fds = vec![-1; x.len()];
344         s.serialize_some(&invalid_fds)
345     } else {
346         s.serialize_none()
347     }
348 }
349 
350 fn deserialize_netconfig_fds<'de, D>(d: D) -> Result<Option<Vec<i32>>, D::Error>
351 where
352     D: serde::Deserializer<'de>,
353 {
354     let invalid_fds: Option<Vec<i32>> = Option::deserialize(d)?;
355     if let Some(invalid_fds) = invalid_fds {
356         warn!("'NetConfig' contains FDs that can't be deserialized correctly. Deserializing them as invalid FDs.");
357         Ok(Some(vec![-1; invalid_fds.len()]))
358     } else {
359         Ok(None)
360     }
361 }
362 
363 #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
364 pub struct RngConfig {
365     pub src: PathBuf,
366     #[serde(default)]
367     pub iommu: bool,
368 }
369 
370 pub const DEFAULT_RNG_SOURCE: &str = "/dev/urandom";
371 
372 impl Default for RngConfig {
373     fn default() -> Self {
374         RngConfig {
375             src: PathBuf::from(DEFAULT_RNG_SOURCE),
376             iommu: false,
377         }
378     }
379 }
380 
381 #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
382 pub struct BalloonConfig {
383     pub size: u64,
384     /// Option to deflate the balloon in case the guest is out of memory.
385     #[serde(default)]
386     pub deflate_on_oom: bool,
387     /// Option to enable free page reporting from the guest.
388     #[serde(default)]
389     pub free_page_reporting: bool,
390 }
391 
392 #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
393 pub struct FsConfig {
394     pub tag: String,
395     pub socket: PathBuf,
396     #[serde(default = "default_fsconfig_num_queues")]
397     pub num_queues: usize,
398     #[serde(default = "default_fsconfig_queue_size")]
399     pub queue_size: u16,
400     #[serde(default)]
401     pub id: Option<String>,
402     #[serde(default)]
403     pub pci_segment: u16,
404 }
405 
406 pub fn default_fsconfig_num_queues() -> usize {
407     1
408 }
409 
410 pub fn default_fsconfig_queue_size() -> u16 {
411     1024
412 }
413 
414 #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
415 pub struct PmemConfig {
416     pub file: PathBuf,
417     #[serde(default)]
418     pub size: Option<u64>,
419     #[serde(default)]
420     pub iommu: bool,
421     #[serde(default)]
422     pub discard_writes: bool,
423     #[serde(default)]
424     pub id: Option<String>,
425     #[serde(default)]
426     pub pci_segment: u16,
427 }
428 
429 #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
430 pub enum ConsoleOutputMode {
431     Off,
432     Pty,
433     Tty,
434     File,
435     Socket,
436     Null,
437 }
438 
439 #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
440 pub struct ConsoleConfig {
441     #[serde(default = "default_consoleconfig_file")]
442     pub file: Option<PathBuf>,
443     pub mode: ConsoleOutputMode,
444     #[serde(default)]
445     pub iommu: bool,
446     pub socket: Option<PathBuf>,
447 }
448 
449 pub fn default_consoleconfig_file() -> Option<PathBuf> {
450     None
451 }
452 
453 #[cfg(target_arch = "x86_64")]
454 #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
455 pub struct DebugConsoleConfig {
456     #[serde(default)]
457     pub file: Option<PathBuf>,
458     pub mode: ConsoleOutputMode,
459     /// Optionally dedicated I/O-port, if the default port should not be used.
460     pub iobase: Option<u16>,
461 }
462 
463 #[cfg(target_arch = "x86_64")]
464 impl Default for DebugConsoleConfig {
465     fn default() -> Self {
466         Self {
467             file: None,
468             mode: ConsoleOutputMode::Off,
469             iobase: Some(devices::debug_console::DEFAULT_PORT as u16),
470         }
471     }
472 }
473 
474 #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
475 pub struct DeviceConfig {
476     pub path: PathBuf,
477     #[serde(default)]
478     pub iommu: bool,
479     #[serde(default)]
480     pub id: Option<String>,
481     #[serde(default)]
482     pub pci_segment: u16,
483     #[serde(default)]
484     pub x_nv_gpudirect_clique: Option<u8>,
485 }
486 
487 #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
488 pub struct UserDeviceConfig {
489     pub socket: PathBuf,
490     #[serde(default)]
491     pub id: Option<String>,
492     #[serde(default)]
493     pub pci_segment: u16,
494 }
495 
496 #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
497 pub struct VdpaConfig {
498     pub path: PathBuf,
499     #[serde(default = "default_vdpaconfig_num_queues")]
500     pub num_queues: usize,
501     #[serde(default)]
502     pub iommu: bool,
503     #[serde(default)]
504     pub id: Option<String>,
505     #[serde(default)]
506     pub pci_segment: u16,
507 }
508 
509 pub fn default_vdpaconfig_num_queues() -> usize {
510     1
511 }
512 
513 #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
514 pub struct VsockConfig {
515     pub cid: u32,
516     pub socket: PathBuf,
517     #[serde(default)]
518     pub iommu: bool,
519     #[serde(default)]
520     pub id: Option<String>,
521     #[serde(default)]
522     pub pci_segment: u16,
523 }
524 
525 #[cfg(target_arch = "x86_64")]
526 #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
527 pub struct SgxEpcConfig {
528     pub id: String,
529     #[serde(default)]
530     pub size: u64,
531     #[serde(default)]
532     pub prefault: bool,
533 }
534 
535 #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
536 pub struct NumaDistance {
537     #[serde(default)]
538     pub destination: u32,
539     #[serde(default)]
540     pub distance: u8,
541 }
542 
543 #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
544 pub struct NumaConfig {
545     #[serde(default)]
546     pub guest_numa_id: u32,
547     #[serde(default)]
548     pub cpus: Option<Vec<u8>>,
549     #[serde(default)]
550     pub distances: Option<Vec<NumaDistance>>,
551     #[serde(default)]
552     pub memory_zones: Option<Vec<String>>,
553     #[cfg(target_arch = "x86_64")]
554     #[serde(default)]
555     pub sgx_epc_sections: Option<Vec<String>>,
556     #[serde(default)]
557     pub pci_segments: Option<Vec<u16>>,
558 }
559 
560 #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
561 pub struct PayloadConfig {
562     #[serde(default)]
563     pub firmware: Option<PathBuf>,
564     #[serde(default)]
565     pub kernel: Option<PathBuf>,
566     #[serde(default)]
567     pub cmdline: Option<String>,
568     #[serde(default)]
569     pub initramfs: Option<PathBuf>,
570     #[cfg(feature = "igvm")]
571     #[serde(default)]
572     pub igvm: Option<PathBuf>,
573     #[cfg(feature = "sev_snp")]
574     #[serde(default)]
575     pub host_data: Option<String>,
576 }
577 
578 pub fn default_serial() -> ConsoleConfig {
579     ConsoleConfig {
580         file: None,
581         mode: ConsoleOutputMode::Null,
582         iommu: false,
583         socket: None,
584     }
585 }
586 
587 pub fn default_console() -> ConsoleConfig {
588     ConsoleConfig {
589         file: None,
590         mode: ConsoleOutputMode::Tty,
591         iommu: false,
592         socket: None,
593     }
594 }
595 
596 #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
597 pub struct TpmConfig {
598     pub socket: PathBuf,
599 }
600 
601 #[derive(Debug, PartialEq, Eq, Deserialize, Serialize)]
602 pub struct VmConfig {
603     #[serde(default)]
604     pub cpus: CpusConfig,
605     #[serde(default)]
606     pub memory: MemoryConfig,
607     pub payload: Option<PayloadConfig>,
608     pub rate_limit_groups: Option<Vec<RateLimiterGroupConfig>>,
609     pub disks: Option<Vec<DiskConfig>>,
610     pub net: Option<Vec<NetConfig>>,
611     #[serde(default)]
612     pub rng: RngConfig,
613     pub balloon: Option<BalloonConfig>,
614     pub fs: Option<Vec<FsConfig>>,
615     pub pmem: Option<Vec<PmemConfig>>,
616     #[serde(default = "default_serial")]
617     pub serial: ConsoleConfig,
618     #[serde(default = "default_console")]
619     pub console: ConsoleConfig,
620     #[cfg(target_arch = "x86_64")]
621     #[serde(default)]
622     pub debug_console: DebugConsoleConfig,
623     pub devices: Option<Vec<DeviceConfig>>,
624     pub user_devices: Option<Vec<UserDeviceConfig>>,
625     pub vdpa: Option<Vec<VdpaConfig>>,
626     pub vsock: Option<VsockConfig>,
627     #[serde(default)]
628     pub pvpanic: bool,
629     #[serde(default)]
630     pub iommu: bool,
631     #[cfg(target_arch = "x86_64")]
632     pub sgx_epc: Option<Vec<SgxEpcConfig>>,
633     pub numa: Option<Vec<NumaConfig>>,
634     #[serde(default)]
635     pub watchdog: bool,
636     #[cfg(feature = "guest_debug")]
637     #[serde(default)]
638     pub gdb: bool,
639     pub pci_segments: Option<Vec<PciSegmentConfig>>,
640     pub platform: Option<PlatformConfig>,
641     pub tpm: Option<TpmConfig>,
642     // Preserved FDs are the ones that share the same life-time as its holding
643     // VmConfig instance, such as FDs for creating TAP devices.
644     // Preserved FDs will stay open as long as the holding VmConfig instance is
645     // valid, and will be closed when the holding VmConfig instance is destroyed.
646     #[serde(skip)]
647     pub preserved_fds: Option<Vec<i32>>,
648 }
649