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