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