1 // Copyright © 2022 Intel Corporation 2 // 3 // SPDX-License-Identifier: Apache-2.0 4 // 5 use std::net::Ipv4Addr; 6 use std::path::PathBuf; 7 use std::{fs, result}; 8 9 use net_util::MacAddr; 10 use serde::{Deserialize, Serialize}; 11 use virtio_devices::RateLimiterConfig; 12 13 use crate::landlock::LandlockError; 14 use crate::Landlock; 15 16 pub type LandlockResult<T> = result::Result<T, LandlockError>; 17 18 /// Trait to apply Landlock on VmConfig elements 19 pub(crate) trait ApplyLandlock { 20 /// Apply Landlock rules to file paths 21 fn apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()>; 22 } 23 24 #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] 25 pub struct CpuAffinity { 26 pub vcpu: u8, 27 pub host_cpus: Vec<usize>, 28 } 29 30 #[derive(Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)] 31 pub struct CpuFeatures { 32 #[cfg(target_arch = "x86_64")] 33 #[serde(default)] 34 pub amx: bool, 35 } 36 37 #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] 38 pub struct CpuTopology { 39 pub threads_per_core: u8, 40 pub cores_per_die: u8, 41 pub dies_per_package: u8, 42 pub packages: u8, 43 } 44 45 // When booting with PVH boot the maximum physical addressable size 46 // is a 46 bit address space even when the host supports with 5-level 47 // paging. 48 pub const DEFAULT_MAX_PHYS_BITS: u8 = 46; 49 50 pub fn default_cpuconfig_max_phys_bits() -> u8 { 51 DEFAULT_MAX_PHYS_BITS 52 } 53 54 #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] 55 pub struct CpusConfig { 56 pub boot_vcpus: u8, 57 pub max_vcpus: u8, 58 #[serde(default)] 59 pub topology: Option<CpuTopology>, 60 #[serde(default)] 61 pub kvm_hyperv: bool, 62 #[serde(default = "default_cpuconfig_max_phys_bits")] 63 pub max_phys_bits: u8, 64 #[serde(default)] 65 pub affinity: Option<Vec<CpuAffinity>>, 66 #[serde(default)] 67 pub features: CpuFeatures, 68 } 69 70 pub const DEFAULT_VCPUS: u8 = 1; 71 72 impl Default for CpusConfig { 73 fn default() -> Self { 74 CpusConfig { 75 boot_vcpus: DEFAULT_VCPUS, 76 max_vcpus: DEFAULT_VCPUS, 77 topology: None, 78 kvm_hyperv: false, 79 max_phys_bits: DEFAULT_MAX_PHYS_BITS, 80 affinity: None, 81 features: CpuFeatures::default(), 82 } 83 } 84 } 85 86 pub const DEFAULT_NUM_PCI_SEGMENTS: u16 = 1; 87 pub fn default_platformconfig_num_pci_segments() -> u16 { 88 DEFAULT_NUM_PCI_SEGMENTS 89 } 90 91 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: Ipv4Addr, 305 #[serde(default = "default_netconfig_mask")] 306 pub mask: Ipv4Addr, 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() -> Ipv4Addr { 353 Ipv4Addr::new(192, 168, 249, 1) 354 } 355 356 pub fn default_netconfig_mask() -> Ipv4Addr { 357 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