xref: /cloud-hypervisor/vmm/src/vm_config.rs (revision f9c134471ac88ea307d0baae8a8d1434b56b81ea)
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
apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()>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 
default_cpuconfig_max_phys_bits() -> u850 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 {
default() -> Self73     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;
default_platformconfig_num_pci_segments() -> u1687 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;
default_platformconfig_iommu_address_width_bits() -> u892 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 
default_pci_segment_aperture_weight() -> u32120 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 {
apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()>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 
default_memoryconfig_thp() -> bool172 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 {
default() -> Self204     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 {
apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()>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 
default_diskconfig_num_queues() -> usize289 pub fn default_diskconfig_num_queues() -> usize {
290     DEFAULT_DISK_NUM_QUEUES
291 }
292 
293 pub const DEFAULT_DISK_QUEUE_SIZE: u16 = 128;
294 
default_diskconfig_queue_size() -> u16295 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 
default_netconfig_true() -> bool344 pub fn default_netconfig_true() -> bool {
345     true
346 }
347 
default_netconfig_tap() -> Option<String>348 pub fn default_netconfig_tap() -> Option<String> {
349     None
350 }
351 
default_netconfig_ip() -> IpAddr352 pub fn default_netconfig_ip() -> IpAddr {
353     warn!("Deprecation warning: No IP address provided. A default IP address is assigned. This behavior will be deprecated soon.");
354     IpAddr::V4(Ipv4Addr::new(192, 168, 249, 1))
355 }
356 
default_netconfig_mask() -> IpAddr357 pub fn default_netconfig_mask() -> IpAddr {
358     warn!("Deprecation warning: No network mask provided. A default network mask is assigned. This behavior will be deprecated soon.");
359     IpAddr::V4(Ipv4Addr::new(255, 255, 255, 0))
360 }
361 
default_netconfig_mac() -> MacAddr362 pub fn default_netconfig_mac() -> MacAddr {
363     MacAddr::local_random()
364 }
365 
366 pub const DEFAULT_NET_NUM_QUEUES: usize = 2;
367 
default_netconfig_num_queues() -> usize368 pub fn default_netconfig_num_queues() -> usize {
369     DEFAULT_NET_NUM_QUEUES
370 }
371 
372 pub const DEFAULT_NET_QUEUE_SIZE: u16 = 256;
373 
default_netconfig_queue_size() -> u16374 pub fn default_netconfig_queue_size() -> u16 {
375     DEFAULT_NET_QUEUE_SIZE
376 }
377 
serialize_netconfig_fds<S>(x: &Option<Vec<i32>>, s: S) -> Result<S::Ok, S::Error> where S: serde::Serializer,378 fn serialize_netconfig_fds<S>(x: &Option<Vec<i32>>, s: S) -> Result<S::Ok, S::Error>
379 where
380     S: serde::Serializer,
381 {
382     if let Some(x) = x {
383         warn!("'NetConfig' contains FDs that can't be serialized correctly. Serializing them as invalid FDs.");
384         let invalid_fds = vec![-1; x.len()];
385         s.serialize_some(&invalid_fds)
386     } else {
387         s.serialize_none()
388     }
389 }
390 
deserialize_netconfig_fds<'de, D>(d: D) -> Result<Option<Vec<i32>>, D::Error> where D: serde::Deserializer<'de>,391 fn deserialize_netconfig_fds<'de, D>(d: D) -> Result<Option<Vec<i32>>, D::Error>
392 where
393     D: serde::Deserializer<'de>,
394 {
395     let invalid_fds: Option<Vec<i32>> = Option::deserialize(d)?;
396     if let Some(invalid_fds) = invalid_fds {
397         warn!("'NetConfig' contains FDs that can't be deserialized correctly. Deserializing them as invalid FDs.");
398         Ok(Some(vec![-1; invalid_fds.len()]))
399     } else {
400         Ok(None)
401     }
402 }
403 
404 #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
405 pub struct RngConfig {
406     pub src: PathBuf,
407     #[serde(default)]
408     pub iommu: bool,
409 }
410 
411 pub const DEFAULT_RNG_SOURCE: &str = "/dev/urandom";
412 
413 impl Default for RngConfig {
default() -> Self414     fn default() -> Self {
415         RngConfig {
416             src: PathBuf::from(DEFAULT_RNG_SOURCE),
417             iommu: false,
418         }
419     }
420 }
421 
422 impl ApplyLandlock for RngConfig {
apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()>423     fn apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()> {
424         // Rng Path only need read access
425         landlock.add_rule_with_access(self.src.to_path_buf(), "r")?;
426         Ok(())
427     }
428 }
429 
430 #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
431 pub struct BalloonConfig {
432     pub size: u64,
433     /// Option to deflate the balloon in case the guest is out of memory.
434     #[serde(default)]
435     pub deflate_on_oom: bool,
436     /// Option to enable free page reporting from the guest.
437     #[serde(default)]
438     pub free_page_reporting: bool,
439 }
440 
441 #[cfg(feature = "pvmemcontrol")]
442 #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize, Default)]
443 pub struct PvmemcontrolConfig {}
444 
445 #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
446 pub struct FsConfig {
447     pub tag: String,
448     pub socket: PathBuf,
449     #[serde(default = "default_fsconfig_num_queues")]
450     pub num_queues: usize,
451     #[serde(default = "default_fsconfig_queue_size")]
452     pub queue_size: u16,
453     #[serde(default)]
454     pub id: Option<String>,
455     #[serde(default)]
456     pub pci_segment: u16,
457 }
458 
default_fsconfig_num_queues() -> usize459 pub fn default_fsconfig_num_queues() -> usize {
460     1
461 }
462 
default_fsconfig_queue_size() -> u16463 pub fn default_fsconfig_queue_size() -> u16 {
464     1024
465 }
466 
467 impl ApplyLandlock for FsConfig {
apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()>468     fn apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()> {
469         landlock.add_rule_with_access(self.socket.to_path_buf(), "rw")?;
470         Ok(())
471     }
472 }
473 
474 #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
475 pub struct PmemConfig {
476     pub file: PathBuf,
477     #[serde(default)]
478     pub size: Option<u64>,
479     #[serde(default)]
480     pub iommu: bool,
481     #[serde(default)]
482     pub discard_writes: bool,
483     #[serde(default)]
484     pub id: Option<String>,
485     #[serde(default)]
486     pub pci_segment: u16,
487 }
488 
489 impl ApplyLandlock for PmemConfig {
apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()>490     fn apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()> {
491         let access = if self.discard_writes { "r" } else { "rw" };
492         landlock.add_rule_with_access(self.file.to_path_buf(), access)?;
493         Ok(())
494     }
495 }
496 
497 #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
498 pub enum ConsoleOutputMode {
499     Off,
500     Pty,
501     Tty,
502     File,
503     Socket,
504     Null,
505 }
506 
507 #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
508 pub struct ConsoleConfig {
509     #[serde(default = "default_consoleconfig_file")]
510     pub file: Option<PathBuf>,
511     pub mode: ConsoleOutputMode,
512     #[serde(default)]
513     pub iommu: bool,
514     pub socket: Option<PathBuf>,
515 }
516 
default_consoleconfig_file() -> Option<PathBuf>517 pub fn default_consoleconfig_file() -> Option<PathBuf> {
518     None
519 }
520 
521 impl ApplyLandlock for ConsoleConfig {
apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()>522     fn apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()> {
523         if let Some(file) = &self.file {
524             landlock.add_rule_with_access(file.to_path_buf(), "rw")?;
525         }
526         if let Some(socket) = &self.socket {
527             landlock.add_rule_with_access(socket.to_path_buf(), "rw")?;
528         }
529         Ok(())
530     }
531 }
532 
533 #[cfg(target_arch = "x86_64")]
534 #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
535 pub struct DebugConsoleConfig {
536     #[serde(default)]
537     pub file: Option<PathBuf>,
538     pub mode: ConsoleOutputMode,
539     /// Optionally dedicated I/O-port, if the default port should not be used.
540     pub iobase: Option<u16>,
541 }
542 
543 #[cfg(target_arch = "x86_64")]
544 impl Default for DebugConsoleConfig {
default() -> Self545     fn default() -> Self {
546         Self {
547             file: None,
548             mode: ConsoleOutputMode::Off,
549             iobase: Some(devices::debug_console::DEFAULT_PORT as u16),
550         }
551     }
552 }
553 #[cfg(target_arch = "x86_64")]
554 impl ApplyLandlock for DebugConsoleConfig {
apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()>555     fn apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()> {
556         if let Some(file) = &self.file {
557             landlock.add_rule_with_access(file.to_path_buf(), "rw")?;
558         }
559         Ok(())
560     }
561 }
562 
563 #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
564 pub struct DeviceConfig {
565     pub path: PathBuf,
566     #[serde(default)]
567     pub iommu: bool,
568     #[serde(default)]
569     pub id: Option<String>,
570     #[serde(default)]
571     pub pci_segment: u16,
572     #[serde(default)]
573     pub x_nv_gpudirect_clique: Option<u8>,
574 }
575 
576 impl ApplyLandlock for DeviceConfig {
apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()>577     fn apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()> {
578         let device_path = fs::read_link(self.path.as_path()).map_err(LandlockError::OpenPath)?;
579         let iommu_group = device_path.file_name();
580         let iommu_group_str = iommu_group
581             .ok_or(LandlockError::InvalidPath)?
582             .to_str()
583             .ok_or(LandlockError::InvalidPath)?;
584 
585         let vfio_group_path = "/dev/vfio/".to_owned() + iommu_group_str;
586         landlock.add_rule_with_access(vfio_group_path.into(), "rw")?;
587 
588         Ok(())
589     }
590 }
591 
592 #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
593 pub struct UserDeviceConfig {
594     pub socket: PathBuf,
595     #[serde(default)]
596     pub id: Option<String>,
597     #[serde(default)]
598     pub pci_segment: u16,
599 }
600 
601 impl ApplyLandlock for UserDeviceConfig {
apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()>602     fn apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()> {
603         landlock.add_rule_with_access(self.socket.to_path_buf(), "rw")?;
604         Ok(())
605     }
606 }
607 
608 #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
609 pub struct VdpaConfig {
610     pub path: PathBuf,
611     #[serde(default = "default_vdpaconfig_num_queues")]
612     pub num_queues: usize,
613     #[serde(default)]
614     pub iommu: bool,
615     #[serde(default)]
616     pub id: Option<String>,
617     #[serde(default)]
618     pub pci_segment: u16,
619 }
620 
default_vdpaconfig_num_queues() -> usize621 pub fn default_vdpaconfig_num_queues() -> usize {
622     1
623 }
624 
625 impl ApplyLandlock for VdpaConfig {
apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()>626     fn apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()> {
627         landlock.add_rule_with_access(self.path.to_path_buf(), "rw")?;
628         Ok(())
629     }
630 }
631 
632 #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
633 pub struct VsockConfig {
634     pub cid: u32,
635     pub socket: PathBuf,
636     #[serde(default)]
637     pub iommu: bool,
638     #[serde(default)]
639     pub id: Option<String>,
640     #[serde(default)]
641     pub pci_segment: u16,
642 }
643 
644 impl ApplyLandlock for VsockConfig {
apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()>645     fn apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()> {
646         landlock.add_rule_with_access(self.socket.to_path_buf(), "rw")?;
647         Ok(())
648     }
649 }
650 
651 #[cfg(target_arch = "x86_64")]
652 #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
653 pub struct SgxEpcConfig {
654     pub id: String,
655     #[serde(default)]
656     pub size: u64,
657     #[serde(default)]
658     pub prefault: bool,
659 }
660 
661 #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
662 pub struct NumaDistance {
663     #[serde(default)]
664     pub destination: u32,
665     #[serde(default)]
666     pub distance: u8,
667 }
668 
669 #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
670 pub struct NumaConfig {
671     #[serde(default)]
672     pub guest_numa_id: u32,
673     #[serde(default)]
674     pub cpus: Option<Vec<u8>>,
675     #[serde(default)]
676     pub distances: Option<Vec<NumaDistance>>,
677     #[serde(default)]
678     pub memory_zones: Option<Vec<String>>,
679     #[cfg(target_arch = "x86_64")]
680     #[serde(default)]
681     pub sgx_epc_sections: Option<Vec<String>>,
682     #[serde(default)]
683     pub pci_segments: Option<Vec<u16>>,
684 }
685 
686 #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
687 pub struct PayloadConfig {
688     #[serde(default)]
689     pub firmware: Option<PathBuf>,
690     #[serde(default)]
691     pub kernel: Option<PathBuf>,
692     #[serde(default)]
693     pub cmdline: Option<String>,
694     #[serde(default)]
695     pub initramfs: Option<PathBuf>,
696     #[cfg(feature = "igvm")]
697     #[serde(default)]
698     pub igvm: Option<PathBuf>,
699     #[cfg(feature = "sev_snp")]
700     #[serde(default)]
701     pub host_data: Option<String>,
702 }
703 
704 impl ApplyLandlock for PayloadConfig {
apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()>705     fn apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()> {
706         // Payload only needs read access
707         if let Some(firmware) = &self.firmware {
708             landlock.add_rule_with_access(firmware.to_path_buf(), "r")?;
709         }
710 
711         if let Some(kernel) = &self.kernel {
712             landlock.add_rule_with_access(kernel.to_path_buf(), "r")?;
713         }
714 
715         if let Some(initramfs) = &self.initramfs {
716             landlock.add_rule_with_access(initramfs.to_path_buf(), "r")?;
717         }
718 
719         #[cfg(feature = "igvm")]
720         if let Some(igvm) = &self.igvm {
721             landlock.add_rule_with_access(igvm.to_path_buf(), "r")?;
722         }
723 
724         Ok(())
725     }
726 }
727 
default_serial() -> ConsoleConfig728 pub fn default_serial() -> ConsoleConfig {
729     ConsoleConfig {
730         file: None,
731         mode: ConsoleOutputMode::Null,
732         iommu: false,
733         socket: None,
734     }
735 }
736 
default_console() -> ConsoleConfig737 pub fn default_console() -> ConsoleConfig {
738     ConsoleConfig {
739         file: None,
740         mode: ConsoleOutputMode::Tty,
741         iommu: false,
742         socket: None,
743     }
744 }
745 
746 #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
747 pub struct TpmConfig {
748     pub socket: PathBuf,
749 }
750 
751 impl ApplyLandlock for TpmConfig {
apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()>752     fn apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()> {
753         landlock.add_rule_with_access(self.socket.to_path_buf(), "rw")?;
754         Ok(())
755     }
756 }
757 
758 #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
759 pub struct LandlockConfig {
760     pub path: PathBuf,
761     pub access: String,
762 }
763 
764 impl ApplyLandlock for LandlockConfig {
apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()>765     fn apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()> {
766         landlock.add_rule_with_access(self.path.to_path_buf(), self.access.clone().as_str())?;
767         Ok(())
768     }
769 }
770 
771 #[derive(Debug, PartialEq, Eq, Deserialize, Serialize)]
772 pub struct VmConfig {
773     #[serde(default)]
774     pub cpus: CpusConfig,
775     #[serde(default)]
776     pub memory: MemoryConfig,
777     pub payload: Option<PayloadConfig>,
778     pub rate_limit_groups: Option<Vec<RateLimiterGroupConfig>>,
779     pub disks: Option<Vec<DiskConfig>>,
780     pub net: Option<Vec<NetConfig>>,
781     #[serde(default)]
782     pub rng: RngConfig,
783     pub balloon: Option<BalloonConfig>,
784     pub fs: Option<Vec<FsConfig>>,
785     pub pmem: Option<Vec<PmemConfig>>,
786     #[serde(default = "default_serial")]
787     pub serial: ConsoleConfig,
788     #[serde(default = "default_console")]
789     pub console: ConsoleConfig,
790     #[cfg(target_arch = "x86_64")]
791     #[serde(default)]
792     pub debug_console: DebugConsoleConfig,
793     pub devices: Option<Vec<DeviceConfig>>,
794     pub user_devices: Option<Vec<UserDeviceConfig>>,
795     pub vdpa: Option<Vec<VdpaConfig>>,
796     pub vsock: Option<VsockConfig>,
797     #[cfg(feature = "pvmemcontrol")]
798     #[serde(default)]
799     pub pvmemcontrol: Option<PvmemcontrolConfig>,
800     #[serde(default)]
801     pub pvpanic: bool,
802     #[serde(default)]
803     pub iommu: bool,
804     #[cfg(target_arch = "x86_64")]
805     pub sgx_epc: Option<Vec<SgxEpcConfig>>,
806     pub numa: Option<Vec<NumaConfig>>,
807     #[serde(default)]
808     pub watchdog: bool,
809     #[cfg(feature = "guest_debug")]
810     #[serde(default)]
811     pub gdb: bool,
812     pub pci_segments: Option<Vec<PciSegmentConfig>>,
813     pub platform: Option<PlatformConfig>,
814     pub tpm: Option<TpmConfig>,
815     // Preserved FDs are the ones that share the same life-time as its holding
816     // VmConfig instance, such as FDs for creating TAP devices.
817     // Preserved FDs will stay open as long as the holding VmConfig instance is
818     // valid, and will be closed when the holding VmConfig instance is destroyed.
819     #[serde(skip)]
820     pub preserved_fds: Option<Vec<i32>>,
821     #[serde(default)]
822     pub landlock_enable: bool,
823     pub landlock_rules: Option<Vec<LandlockConfig>>,
824 }
825 
826 impl VmConfig {
apply_landlock(&self) -> LandlockResult<()>827     pub(crate) fn apply_landlock(&self) -> LandlockResult<()> {
828         let mut landlock = Landlock::new()?;
829 
830         if let Some(mem_zones) = &self.memory.zones {
831             for zone in mem_zones.iter() {
832                 zone.apply_landlock(&mut landlock)?;
833             }
834         }
835 
836         let disks = &self.disks;
837         if let Some(disks) = disks {
838             for disk in disks.iter() {
839                 disk.apply_landlock(&mut landlock)?;
840             }
841         }
842 
843         self.rng.apply_landlock(&mut landlock)?;
844 
845         if let Some(fs_configs) = &self.fs {
846             for fs_config in fs_configs.iter() {
847                 fs_config.apply_landlock(&mut landlock)?;
848             }
849         }
850 
851         if let Some(pmem_configs) = &self.pmem {
852             for pmem_config in pmem_configs.iter() {
853                 pmem_config.apply_landlock(&mut landlock)?;
854             }
855         }
856 
857         self.console.apply_landlock(&mut landlock)?;
858         self.serial.apply_landlock(&mut landlock)?;
859 
860         #[cfg(target_arch = "x86_64")]
861         {
862             self.debug_console.apply_landlock(&mut landlock)?;
863         }
864 
865         if let Some(devices) = &self.devices {
866             landlock.add_rule_with_access("/dev/vfio/vfio".into(), "rw")?;
867 
868             for device in devices.iter() {
869                 device.apply_landlock(&mut landlock)?;
870             }
871         }
872 
873         if let Some(user_devices) = &self.user_devices {
874             for user_devices in user_devices.iter() {
875                 user_devices.apply_landlock(&mut landlock)?;
876             }
877         }
878 
879         if let Some(vdpa_configs) = &self.vdpa {
880             for vdpa_config in vdpa_configs.iter() {
881                 vdpa_config.apply_landlock(&mut landlock)?;
882             }
883         }
884 
885         if let Some(vsock_config) = &self.vsock {
886             vsock_config.apply_landlock(&mut landlock)?;
887         }
888 
889         if let Some(payload) = &self.payload {
890             payload.apply_landlock(&mut landlock)?;
891         }
892 
893         if let Some(tpm_config) = &self.tpm {
894             tpm_config.apply_landlock(&mut landlock)?;
895         }
896 
897         if self.net.is_some() {
898             landlock.add_rule_with_access("/dev/net/tun".into(), "rw")?;
899         }
900 
901         if let Some(landlock_rules) = &self.landlock_rules {
902             for landlock_rule in landlock_rules.iter() {
903                 landlock_rule.apply_landlock(&mut landlock)?;
904             }
905         }
906 
907         landlock.restrict_self()?;
908 
909         Ok(())
910     }
911 }
912