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