xref: /cloud-hypervisor/vmm/src/vm_config.rs (revision 88a9f799449c04180c6b9a21d3b9c0c4b57e2bd6)
1 // Copyright © 2022 Intel Corporation
2 //
3 // SPDX-License-Identifier: Apache-2.0
4 //
5 use std::{fs, net::Ipv4Addr, path::PathBuf, result};
6 
7 use net_util::MacAddr;
8 use serde::{Deserialize, Serialize};
9 use virtio_devices::RateLimiterConfig;
10 
11 use crate::{landlock::LandlockError, Landlock};
12 
13 pub type LandlockResult<T> = result::Result<T, LandlockError>;
14 
15 /// Trait to apply Landlock on VmConfig elements
16 pub(crate) trait ApplyLandlock {
17     /// Apply Landlock rules to file paths
18     fn apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()>;
19 }
20 
21 #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
22 pub struct CpuAffinity {
23     pub vcpu: u8,
24     pub host_cpus: Vec<usize>,
25 }
26 
27 #[derive(Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
28 pub struct CpuFeatures {
29     #[cfg(target_arch = "x86_64")]
30     #[serde(default)]
31     pub amx: bool,
32 }
33 
34 #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
35 pub struct CpuTopology {
36     pub threads_per_core: u8,
37     pub cores_per_die: u8,
38     pub dies_per_package: u8,
39     pub packages: u8,
40 }
41 
42 // When booting with PVH boot the maximum physical addressable size
43 // is a 46 bit address space even when the host supports with 5-level
44 // paging.
45 pub const DEFAULT_MAX_PHYS_BITS: u8 = 46;
46 
47 pub fn default_cpuconfig_max_phys_bits() -> u8 {
48     DEFAULT_MAX_PHYS_BITS
49 }
50 
51 #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
52 pub struct CpusConfig {
53     pub boot_vcpus: u8,
54     pub max_vcpus: u8,
55     #[serde(default)]
56     pub topology: Option<CpuTopology>,
57     #[serde(default)]
58     pub kvm_hyperv: bool,
59     #[serde(default = "default_cpuconfig_max_phys_bits")]
60     pub max_phys_bits: u8,
61     #[serde(default)]
62     pub affinity: Option<Vec<CpuAffinity>>,
63     #[serde(default)]
64     pub features: CpuFeatures,
65 }
66 
67 pub const DEFAULT_VCPUS: u8 = 1;
68 
69 impl Default for CpusConfig {
70     fn default() -> Self {
71         CpusConfig {
72             boot_vcpus: DEFAULT_VCPUS,
73             max_vcpus: DEFAULT_VCPUS,
74             topology: None,
75             kvm_hyperv: false,
76             max_phys_bits: DEFAULT_MAX_PHYS_BITS,
77             affinity: None,
78             features: CpuFeatures::default(),
79         }
80     }
81 }
82 
83 pub const DEFAULT_NUM_PCI_SEGMENTS: u16 = 1;
84 pub fn default_platformconfig_num_pci_segments() -> u16 {
85     DEFAULT_NUM_PCI_SEGMENTS
86 }
87 
88 #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
89 pub struct PlatformConfig {
90     #[serde(default = "default_platformconfig_num_pci_segments")]
91     pub num_pci_segments: u16,
92     #[serde(default)]
93     pub iommu_segments: Option<Vec<u16>>,
94     #[serde(default)]
95     pub serial_number: Option<String>,
96     #[serde(default)]
97     pub uuid: Option<String>,
98     #[serde(default)]
99     pub oem_strings: Option<Vec<String>>,
100     #[cfg(feature = "tdx")]
101     #[serde(default)]
102     pub tdx: bool,
103     #[cfg(feature = "sev_snp")]
104     #[serde(default)]
105     pub sev_snp: bool,
106 }
107 
108 pub const DEFAULT_PCI_SEGMENT_APERTURE_WEIGHT: u32 = 1;
109 
110 fn default_pci_segment_aperture_weight() -> u32 {
111     DEFAULT_PCI_SEGMENT_APERTURE_WEIGHT
112 }
113 
114 #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
115 pub struct PciSegmentConfig {
116     #[serde(default)]
117     pub pci_segment: u16,
118     #[serde(default = "default_pci_segment_aperture_weight")]
119     pub mmio32_aperture_weight: u32,
120     #[serde(default = "default_pci_segment_aperture_weight")]
121     pub mmio64_aperture_weight: u32,
122 }
123 
124 #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
125 pub struct MemoryZoneConfig {
126     pub id: String,
127     pub size: u64,
128     #[serde(default)]
129     pub file: Option<PathBuf>,
130     #[serde(default)]
131     pub shared: bool,
132     #[serde(default)]
133     pub hugepages: bool,
134     #[serde(default)]
135     pub hugepage_size: Option<u64>,
136     #[serde(default)]
137     pub host_numa_node: Option<u32>,
138     #[serde(default)]
139     pub hotplug_size: Option<u64>,
140     #[serde(default)]
141     pub hotplugged_size: Option<u64>,
142     #[serde(default)]
143     pub prefault: bool,
144 }
145 
146 impl ApplyLandlock for MemoryZoneConfig {
147     fn apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()> {
148         if let Some(file) = &self.file {
149             landlock.add_rule_with_access(file.to_path_buf(), "rw")?;
150         }
151         Ok(())
152     }
153 }
154 
155 #[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize, Default)]
156 pub enum HotplugMethod {
157     #[default]
158     Acpi,
159     VirtioMem,
160 }
161 
162 fn default_memoryconfig_thp() -> bool {
163     true
164 }
165 
166 #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
167 pub struct MemoryConfig {
168     pub size: u64,
169     #[serde(default)]
170     pub mergeable: bool,
171     #[serde(default)]
172     pub hotplug_method: HotplugMethod,
173     #[serde(default)]
174     pub hotplug_size: Option<u64>,
175     #[serde(default)]
176     pub hotplugged_size: Option<u64>,
177     #[serde(default)]
178     pub shared: bool,
179     #[serde(default)]
180     pub hugepages: bool,
181     #[serde(default)]
182     pub hugepage_size: Option<u64>,
183     #[serde(default)]
184     pub prefault: bool,
185     #[serde(default)]
186     pub zones: Option<Vec<MemoryZoneConfig>>,
187     #[serde(default = "default_memoryconfig_thp")]
188     pub thp: bool,
189 }
190 
191 pub const DEFAULT_MEMORY_MB: u64 = 512;
192 
193 impl Default for MemoryConfig {
194     fn default() -> Self {
195         MemoryConfig {
196             size: DEFAULT_MEMORY_MB << 20,
197             mergeable: false,
198             hotplug_method: HotplugMethod::Acpi,
199             hotplug_size: None,
200             hotplugged_size: None,
201             shared: false,
202             hugepages: false,
203             hugepage_size: None,
204             prefault: false,
205             zones: None,
206             thp: true,
207         }
208     }
209 }
210 
211 #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize, Default)]
212 pub enum VhostMode {
213     #[default]
214     Client,
215     Server,
216 }
217 
218 #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
219 pub struct RateLimiterGroupConfig {
220     #[serde(default)]
221     pub id: String,
222     #[serde(default)]
223     pub rate_limiter_config: RateLimiterConfig,
224 }
225 
226 #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
227 pub struct VirtQueueAffinity {
228     pub queue_index: u16,
229     pub host_cpus: Vec<usize>,
230 }
231 
232 #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
233 pub struct DiskConfig {
234     pub path: Option<PathBuf>,
235     #[serde(default)]
236     pub readonly: bool,
237     #[serde(default)]
238     pub direct: bool,
239     #[serde(default)]
240     pub iommu: bool,
241     #[serde(default = "default_diskconfig_num_queues")]
242     pub num_queues: usize,
243     #[serde(default = "default_diskconfig_queue_size")]
244     pub queue_size: u16,
245     #[serde(default)]
246     pub vhost_user: bool,
247     pub vhost_socket: Option<String>,
248     #[serde(default)]
249     pub rate_limit_group: Option<String>,
250     #[serde(default)]
251     pub rate_limiter_config: Option<RateLimiterConfig>,
252     #[serde(default)]
253     pub id: Option<String>,
254     // For testing use only. Not exposed in API.
255     #[serde(default)]
256     pub disable_io_uring: bool,
257     // For testing use only. Not exposed in API.
258     #[serde(default)]
259     pub disable_aio: bool,
260     #[serde(default)]
261     pub pci_segment: u16,
262     #[serde(default)]
263     pub serial: Option<String>,
264     #[serde(default)]
265     pub queue_affinity: Option<Vec<VirtQueueAffinity>>,
266 }
267 
268 impl ApplyLandlock for DiskConfig {
269     fn apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()> {
270         if let Some(path) = &self.path {
271             landlock.add_rule_with_access(path.to_path_buf(), "rw")?;
272         }
273         Ok(())
274     }
275 }
276 
277 pub const DEFAULT_DISK_NUM_QUEUES: usize = 1;
278 
279 pub fn default_diskconfig_num_queues() -> usize {
280     DEFAULT_DISK_NUM_QUEUES
281 }
282 
283 pub const DEFAULT_DISK_QUEUE_SIZE: u16 = 128;
284 
285 pub fn default_diskconfig_queue_size() -> u16 {
286     DEFAULT_DISK_QUEUE_SIZE
287 }
288 
289 #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
290 pub struct NetConfig {
291     #[serde(default = "default_netconfig_tap")]
292     pub tap: Option<String>,
293     #[serde(default = "default_netconfig_ip")]
294     pub ip: Ipv4Addr,
295     #[serde(default = "default_netconfig_mask")]
296     pub mask: Ipv4Addr,
297     #[serde(default = "default_netconfig_mac")]
298     pub mac: MacAddr,
299     #[serde(default)]
300     pub host_mac: Option<MacAddr>,
301     #[serde(default)]
302     pub mtu: Option<u16>,
303     #[serde(default)]
304     pub iommu: bool,
305     #[serde(default = "default_netconfig_num_queues")]
306     pub num_queues: usize,
307     #[serde(default = "default_netconfig_queue_size")]
308     pub queue_size: u16,
309     #[serde(default)]
310     pub vhost_user: bool,
311     pub vhost_socket: Option<String>,
312     #[serde(default)]
313     pub vhost_mode: VhostMode,
314     #[serde(default)]
315     pub id: Option<String>,
316     #[serde(
317         default,
318         serialize_with = "serialize_netconfig_fds",
319         deserialize_with = "deserialize_netconfig_fds"
320     )]
321     pub fds: Option<Vec<i32>>,
322     #[serde(default)]
323     pub rate_limiter_config: Option<RateLimiterConfig>,
324     #[serde(default)]
325     pub pci_segment: u16,
326     #[serde(default = "default_netconfig_true")]
327     pub offload_tso: bool,
328     #[serde(default = "default_netconfig_true")]
329     pub offload_ufo: bool,
330     #[serde(default = "default_netconfig_true")]
331     pub offload_csum: bool,
332 }
333 
334 pub fn default_netconfig_true() -> bool {
335     true
336 }
337 
338 pub fn default_netconfig_tap() -> Option<String> {
339     None
340 }
341 
342 pub fn default_netconfig_ip() -> Ipv4Addr {
343     Ipv4Addr::new(192, 168, 249, 1)
344 }
345 
346 pub fn default_netconfig_mask() -> Ipv4Addr {
347     Ipv4Addr::new(255, 255, 255, 0)
348 }
349 
350 pub fn default_netconfig_mac() -> MacAddr {
351     MacAddr::local_random()
352 }
353 
354 pub const DEFAULT_NET_NUM_QUEUES: usize = 2;
355 
356 pub fn default_netconfig_num_queues() -> usize {
357     DEFAULT_NET_NUM_QUEUES
358 }
359 
360 pub const DEFAULT_NET_QUEUE_SIZE: u16 = 256;
361 
362 pub fn default_netconfig_queue_size() -> u16 {
363     DEFAULT_NET_QUEUE_SIZE
364 }
365 
366 fn serialize_netconfig_fds<S>(x: &Option<Vec<i32>>, s: S) -> Result<S::Ok, S::Error>
367 where
368     S: serde::Serializer,
369 {
370     if let Some(x) = x {
371         warn!("'NetConfig' contains FDs that can't be serialized correctly. Serializing them as invalid FDs.");
372         let invalid_fds = vec![-1; x.len()];
373         s.serialize_some(&invalid_fds)
374     } else {
375         s.serialize_none()
376     }
377 }
378 
379 fn deserialize_netconfig_fds<'de, D>(d: D) -> Result<Option<Vec<i32>>, D::Error>
380 where
381     D: serde::Deserializer<'de>,
382 {
383     let invalid_fds: Option<Vec<i32>> = Option::deserialize(d)?;
384     if let Some(invalid_fds) = invalid_fds {
385         warn!("'NetConfig' contains FDs that can't be deserialized correctly. Deserializing them as invalid FDs.");
386         Ok(Some(vec![-1; invalid_fds.len()]))
387     } else {
388         Ok(None)
389     }
390 }
391 
392 #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
393 pub struct RngConfig {
394     pub src: PathBuf,
395     #[serde(default)]
396     pub iommu: bool,
397 }
398 
399 pub const DEFAULT_RNG_SOURCE: &str = "/dev/urandom";
400 
401 impl Default for RngConfig {
402     fn default() -> Self {
403         RngConfig {
404             src: PathBuf::from(DEFAULT_RNG_SOURCE),
405             iommu: false,
406         }
407     }
408 }
409 
410 impl ApplyLandlock for RngConfig {
411     fn apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()> {
412         // Rng Path only need read access
413         landlock.add_rule_with_access(self.src.to_path_buf(), "r")?;
414         Ok(())
415     }
416 }
417 
418 #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
419 pub struct BalloonConfig {
420     pub size: u64,
421     /// Option to deflate the balloon in case the guest is out of memory.
422     #[serde(default)]
423     pub deflate_on_oom: bool,
424     /// Option to enable free page reporting from the guest.
425     #[serde(default)]
426     pub free_page_reporting: bool,
427 }
428 
429 #[cfg(feature = "pvmemcontrol")]
430 #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize, Default)]
431 pub struct PvmemcontrolConfig {}
432 
433 #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
434 pub struct FsConfig {
435     pub tag: String,
436     pub socket: PathBuf,
437     #[serde(default = "default_fsconfig_num_queues")]
438     pub num_queues: usize,
439     #[serde(default = "default_fsconfig_queue_size")]
440     pub queue_size: u16,
441     #[serde(default)]
442     pub id: Option<String>,
443     #[serde(default)]
444     pub pci_segment: u16,
445 }
446 
447 pub fn default_fsconfig_num_queues() -> usize {
448     1
449 }
450 
451 pub fn default_fsconfig_queue_size() -> u16 {
452     1024
453 }
454 
455 impl ApplyLandlock for FsConfig {
456     fn apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()> {
457         landlock.add_rule_with_access(self.socket.to_path_buf(), "rw")?;
458         Ok(())
459     }
460 }
461 
462 #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
463 pub struct PmemConfig {
464     pub file: PathBuf,
465     #[serde(default)]
466     pub size: Option<u64>,
467     #[serde(default)]
468     pub iommu: bool,
469     #[serde(default)]
470     pub discard_writes: bool,
471     #[serde(default)]
472     pub id: Option<String>,
473     #[serde(default)]
474     pub pci_segment: u16,
475 }
476 
477 impl ApplyLandlock for PmemConfig {
478     fn apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()> {
479         landlock.add_rule_with_access(self.file.to_path_buf(), "rw")?;
480         Ok(())
481     }
482 }
483 
484 #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
485 pub enum ConsoleOutputMode {
486     Off,
487     Pty,
488     Tty,
489     File,
490     Socket,
491     Null,
492 }
493 
494 #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
495 pub struct ConsoleConfig {
496     #[serde(default = "default_consoleconfig_file")]
497     pub file: Option<PathBuf>,
498     pub mode: ConsoleOutputMode,
499     #[serde(default)]
500     pub iommu: bool,
501     pub socket: Option<PathBuf>,
502 }
503 
504 pub fn default_consoleconfig_file() -> Option<PathBuf> {
505     None
506 }
507 
508 impl ApplyLandlock for ConsoleConfig {
509     fn apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()> {
510         if let Some(file) = &self.file {
511             landlock.add_rule_with_access(file.to_path_buf(), "rw")?;
512         }
513         if let Some(socket) = &self.socket {
514             landlock.add_rule_with_access(socket.to_path_buf(), "rw")?;
515         }
516         Ok(())
517     }
518 }
519 
520 #[cfg(target_arch = "x86_64")]
521 #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
522 pub struct DebugConsoleConfig {
523     #[serde(default)]
524     pub file: Option<PathBuf>,
525     pub mode: ConsoleOutputMode,
526     /// Optionally dedicated I/O-port, if the default port should not be used.
527     pub iobase: Option<u16>,
528 }
529 
530 #[cfg(target_arch = "x86_64")]
531 impl Default for DebugConsoleConfig {
532     fn default() -> Self {
533         Self {
534             file: None,
535             mode: ConsoleOutputMode::Off,
536             iobase: Some(devices::debug_console::DEFAULT_PORT as u16),
537         }
538     }
539 }
540 #[cfg(target_arch = "x86_64")]
541 impl ApplyLandlock for DebugConsoleConfig {
542     fn apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()> {
543         if let Some(file) = &self.file {
544             landlock.add_rule_with_access(file.to_path_buf(), "rw")?;
545         }
546         Ok(())
547     }
548 }
549 
550 #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
551 pub struct DeviceConfig {
552     pub path: PathBuf,
553     #[serde(default)]
554     pub iommu: bool,
555     #[serde(default)]
556     pub id: Option<String>,
557     #[serde(default)]
558     pub pci_segment: u16,
559     #[serde(default)]
560     pub x_nv_gpudirect_clique: Option<u8>,
561 }
562 
563 impl ApplyLandlock for DeviceConfig {
564     fn apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()> {
565         let device_path = fs::read_link(self.path.as_path()).map_err(LandlockError::OpenPath)?;
566         let iommu_group = device_path.file_name();
567         let iommu_group_str = iommu_group
568             .ok_or(LandlockError::InvalidPath)?
569             .to_str()
570             .ok_or(LandlockError::InvalidPath)?;
571 
572         let vfio_group_path = "/dev/vfio/".to_owned() + iommu_group_str;
573         landlock.add_rule_with_access(vfio_group_path.into(), "rw")?;
574 
575         Ok(())
576     }
577 }
578 
579 #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
580 pub struct UserDeviceConfig {
581     pub socket: PathBuf,
582     #[serde(default)]
583     pub id: Option<String>,
584     #[serde(default)]
585     pub pci_segment: u16,
586 }
587 
588 impl ApplyLandlock for UserDeviceConfig {
589     fn apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()> {
590         landlock.add_rule_with_access(self.socket.to_path_buf(), "rw")?;
591         Ok(())
592     }
593 }
594 
595 #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
596 pub struct VdpaConfig {
597     pub path: PathBuf,
598     #[serde(default = "default_vdpaconfig_num_queues")]
599     pub num_queues: usize,
600     #[serde(default)]
601     pub iommu: bool,
602     #[serde(default)]
603     pub id: Option<String>,
604     #[serde(default)]
605     pub pci_segment: u16,
606 }
607 
608 pub fn default_vdpaconfig_num_queues() -> usize {
609     1
610 }
611 
612 impl ApplyLandlock for VdpaConfig {
613     fn apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()> {
614         landlock.add_rule_with_access(self.path.to_path_buf(), "rw")?;
615         Ok(())
616     }
617 }
618 
619 #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
620 pub struct VsockConfig {
621     pub cid: u32,
622     pub socket: PathBuf,
623     #[serde(default)]
624     pub iommu: bool,
625     #[serde(default)]
626     pub id: Option<String>,
627     #[serde(default)]
628     pub pci_segment: u16,
629 }
630 
631 impl ApplyLandlock for VsockConfig {
632     fn apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()> {
633         landlock.add_rule_with_access(self.socket.to_path_buf(), "rw")?;
634         Ok(())
635     }
636 }
637 
638 #[cfg(target_arch = "x86_64")]
639 #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
640 pub struct SgxEpcConfig {
641     pub id: String,
642     #[serde(default)]
643     pub size: u64,
644     #[serde(default)]
645     pub prefault: bool,
646 }
647 
648 #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
649 pub struct NumaDistance {
650     #[serde(default)]
651     pub destination: u32,
652     #[serde(default)]
653     pub distance: u8,
654 }
655 
656 #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
657 pub struct NumaConfig {
658     #[serde(default)]
659     pub guest_numa_id: u32,
660     #[serde(default)]
661     pub cpus: Option<Vec<u8>>,
662     #[serde(default)]
663     pub distances: Option<Vec<NumaDistance>>,
664     #[serde(default)]
665     pub memory_zones: Option<Vec<String>>,
666     #[cfg(target_arch = "x86_64")]
667     #[serde(default)]
668     pub sgx_epc_sections: Option<Vec<String>>,
669     #[serde(default)]
670     pub pci_segments: Option<Vec<u16>>,
671 }
672 
673 #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
674 pub struct PayloadConfig {
675     #[serde(default)]
676     pub firmware: Option<PathBuf>,
677     #[serde(default)]
678     pub kernel: Option<PathBuf>,
679     #[serde(default)]
680     pub cmdline: Option<String>,
681     #[serde(default)]
682     pub initramfs: Option<PathBuf>,
683     #[cfg(feature = "igvm")]
684     #[serde(default)]
685     pub igvm: Option<PathBuf>,
686     #[cfg(feature = "sev_snp")]
687     #[serde(default)]
688     pub host_data: Option<String>,
689 }
690 
691 impl ApplyLandlock for PayloadConfig {
692     fn apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()> {
693         // Payload only needs read access
694         if let Some(firmware) = &self.firmware {
695             landlock.add_rule_with_access(firmware.to_path_buf(), "r")?;
696         }
697 
698         if let Some(kernel) = &self.kernel {
699             landlock.add_rule_with_access(kernel.to_path_buf(), "r")?;
700         }
701 
702         if let Some(initramfs) = &self.initramfs {
703             landlock.add_rule_with_access(initramfs.to_path_buf(), "r")?;
704         }
705 
706         #[cfg(feature = "igvm")]
707         if let Some(igvm) = &self.igvm {
708             landlock.add_rule_with_access(igvm.to_path_buf(), "r")?;
709         }
710 
711         Ok(())
712     }
713 }
714 
715 pub fn default_serial() -> ConsoleConfig {
716     ConsoleConfig {
717         file: None,
718         mode: ConsoleOutputMode::Null,
719         iommu: false,
720         socket: None,
721     }
722 }
723 
724 pub fn default_console() -> ConsoleConfig {
725     ConsoleConfig {
726         file: None,
727         mode: ConsoleOutputMode::Tty,
728         iommu: false,
729         socket: None,
730     }
731 }
732 
733 #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
734 pub struct TpmConfig {
735     pub socket: PathBuf,
736 }
737 
738 impl ApplyLandlock for TpmConfig {
739     fn apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()> {
740         landlock.add_rule_with_access(self.socket.to_path_buf(), "rw")?;
741         Ok(())
742     }
743 }
744 
745 #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
746 pub struct LandlockConfig {
747     pub path: PathBuf,
748     pub access: String,
749 }
750 
751 impl ApplyLandlock for LandlockConfig {
752     fn apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()> {
753         landlock.add_rule_with_access(self.path.to_path_buf(), self.access.clone().as_str())?;
754         Ok(())
755     }
756 }
757 
758 #[derive(Debug, PartialEq, Eq, Deserialize, Serialize)]
759 pub struct VmConfig {
760     #[serde(default)]
761     pub cpus: CpusConfig,
762     #[serde(default)]
763     pub memory: MemoryConfig,
764     pub payload: Option<PayloadConfig>,
765     pub rate_limit_groups: Option<Vec<RateLimiterGroupConfig>>,
766     pub disks: Option<Vec<DiskConfig>>,
767     pub net: Option<Vec<NetConfig>>,
768     #[serde(default)]
769     pub rng: RngConfig,
770     pub balloon: Option<BalloonConfig>,
771     pub fs: Option<Vec<FsConfig>>,
772     pub pmem: Option<Vec<PmemConfig>>,
773     #[serde(default = "default_serial")]
774     pub serial: ConsoleConfig,
775     #[serde(default = "default_console")]
776     pub console: ConsoleConfig,
777     #[cfg(target_arch = "x86_64")]
778     #[serde(default)]
779     pub debug_console: DebugConsoleConfig,
780     pub devices: Option<Vec<DeviceConfig>>,
781     pub user_devices: Option<Vec<UserDeviceConfig>>,
782     pub vdpa: Option<Vec<VdpaConfig>>,
783     pub vsock: Option<VsockConfig>,
784     #[cfg(feature = "pvmemcontrol")]
785     #[serde(default)]
786     pub pvmemcontrol: Option<PvmemcontrolConfig>,
787     #[serde(default)]
788     pub pvpanic: bool,
789     #[serde(default)]
790     pub iommu: bool,
791     #[cfg(target_arch = "x86_64")]
792     pub sgx_epc: Option<Vec<SgxEpcConfig>>,
793     pub numa: Option<Vec<NumaConfig>>,
794     #[serde(default)]
795     pub watchdog: bool,
796     #[cfg(feature = "guest_debug")]
797     #[serde(default)]
798     pub gdb: bool,
799     pub pci_segments: Option<Vec<PciSegmentConfig>>,
800     pub platform: Option<PlatformConfig>,
801     pub tpm: Option<TpmConfig>,
802     // Preserved FDs are the ones that share the same life-time as its holding
803     // VmConfig instance, such as FDs for creating TAP devices.
804     // Preserved FDs will stay open as long as the holding VmConfig instance is
805     // valid, and will be closed when the holding VmConfig instance is destroyed.
806     #[serde(skip)]
807     pub preserved_fds: Option<Vec<i32>>,
808     #[serde(default)]
809     pub landlock_enable: bool,
810     pub landlock_rules: Option<Vec<LandlockConfig>>,
811 }
812 
813 impl VmConfig {
814     pub(crate) fn apply_landlock(&self) -> LandlockResult<()> {
815         let mut landlock = Landlock::new()?;
816 
817         if let Some(mem_zones) = &self.memory.zones {
818             for zone in mem_zones.iter() {
819                 zone.apply_landlock(&mut landlock)?;
820             }
821         }
822 
823         let disks = &self.disks;
824         if let Some(disks) = disks {
825             for disk in disks.iter() {
826                 disk.apply_landlock(&mut landlock)?;
827             }
828         }
829 
830         self.rng.apply_landlock(&mut landlock)?;
831 
832         if let Some(fs_configs) = &self.fs {
833             for fs_config in fs_configs.iter() {
834                 fs_config.apply_landlock(&mut landlock)?;
835             }
836         }
837 
838         if let Some(pmem_configs) = &self.pmem {
839             for pmem_config in pmem_configs.iter() {
840                 pmem_config.apply_landlock(&mut landlock)?;
841             }
842         }
843 
844         self.console.apply_landlock(&mut landlock)?;
845         self.serial.apply_landlock(&mut landlock)?;
846 
847         #[cfg(target_arch = "x86_64")]
848         {
849             self.debug_console.apply_landlock(&mut landlock)?;
850         }
851 
852         if let Some(devices) = &self.devices {
853             landlock.add_rule_with_access("/dev/vfio/vfio".into(), "rw")?;
854 
855             for device in devices.iter() {
856                 device.apply_landlock(&mut landlock)?;
857             }
858         }
859 
860         if let Some(user_devices) = &self.user_devices {
861             for user_devices in user_devices.iter() {
862                 user_devices.apply_landlock(&mut landlock)?;
863             }
864         }
865 
866         if let Some(vdpa_configs) = &self.vdpa {
867             for vdpa_config in vdpa_configs.iter() {
868                 vdpa_config.apply_landlock(&mut landlock)?;
869             }
870         }
871 
872         if let Some(vsock_config) = &self.vsock {
873             vsock_config.apply_landlock(&mut landlock)?;
874         }
875 
876         if let Some(payload) = &self.payload {
877             payload.apply_landlock(&mut landlock)?;
878         }
879 
880         if let Some(tpm_config) = &self.tpm {
881             tpm_config.apply_landlock(&mut landlock)?;
882         }
883 
884         if self.net.is_some() {
885             landlock.add_rule_with_access("/dev/net/tun".into(), "rw")?;
886         }
887 
888         if let Some(landlock_rules) = &self.landlock_rules {
889             for landlock_rule in landlock_rules.iter() {
890                 landlock_rule.apply_landlock(&mut landlock)?;
891             }
892         }
893 
894         landlock.restrict_self()?;
895 
896         Ok(())
897     }
898 }
899