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