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