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