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