@@ -12,6 +12,8 @@ use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
12
12
use cap:: Cap ;
13
13
use ioctls:: vm:: { new_vmfd, VmFd } ;
14
14
use ioctls:: Result ;
15
+ #[ cfg( any( target_arch = "aarch64" ) ) ]
16
+ use kvm_bindings:: KVM_VM_TYPE_ARM_IPA_SIZE_MASK ;
15
17
#[ cfg( any( target_arch = "x86" , target_arch = "x86_64" ) ) ]
16
18
use kvm_bindings:: { CpuId , MsrList , KVM_MAX_MSR_ENTRIES } ;
17
19
use kvm_ioctls:: * ;
@@ -374,6 +376,58 @@ impl Kvm {
374
376
}
375
377
}
376
378
379
+ /// AArch64 specific function to create a VM fd using the KVM fd with flexible IPA size.
380
+ ///
381
+ /// See the arm64 section of KVM documentation for `KVM_CREATE_VM`.
382
+ /// A call to this function will also initialize the size of the vcpu mmap area using the
383
+ /// `KVM_GET_VCPU_MMAP_SIZE` ioctl.
384
+ ///
385
+ /// Note: `Cap::ArmVmIPASize` should be checked using `check_extension` before calling
386
+ /// this function to determine if the host machine supports the IPA size capability.
387
+ ///
388
+ /// # Arguments
389
+ ///
390
+ /// * `ipa_size` - Guest VM IPA size, 32 <= ipa_size <= Host_IPA_Limit.
391
+ /// The value of `Host_IPA_Limit` may be different between hardware
392
+ /// implementations and can be extracted by calling `get_host_ipa_limit`.
393
+ /// Possible values can be found in documentation of registers `TCR_EL2`
394
+ /// and `VTCR_EL2`.
395
+ ///
396
+ /// # Example
397
+ ///
398
+ /// ```
399
+ /// # use kvm_ioctls::{Kvm, Cap};
400
+ /// let kvm = Kvm::new().unwrap();
401
+ /// // Check if the ArmVmIPASize cap is supported.
402
+ /// if kvm.check_extension(Cap::ArmVmIPASize) {
403
+ /// let host_ipa_limit = kvm.get_host_ipa_limit();
404
+ /// let vm = kvm.create_vm_with_ipa_size(host_ipa_limit as u32).unwrap();
405
+ /// // Check that the VM mmap size is the same reported by `KVM_GET_VCPU_MMAP_SIZE`.
406
+ /// assert!(vm.run_size() == kvm.get_vcpu_mmap_size().unwrap());
407
+ /// }
408
+ /// ```
409
+ ///
410
+ #[ cfg( any( target_arch = "aarch64" ) ) ]
411
+ pub fn create_vm_with_ipa_size ( & self , ipa_size : u32 ) -> Result < VmFd > {
412
+ // Safe because we know `self.kvm` is a real KVM fd as this module is the only one that
413
+ // create Kvm objects.
414
+ let ret = unsafe {
415
+ ioctl_with_val (
416
+ & self . kvm ,
417
+ KVM_CREATE_VM ( ) ,
418
+ ( ipa_size & KVM_VM_TYPE_ARM_IPA_SIZE_MASK ) . into ( ) ,
419
+ )
420
+ } ;
421
+ if ret >= 0 {
422
+ // Safe because we verify the value of ret and we are the owners of the fd.
423
+ let vm_file = unsafe { File :: from_raw_fd ( ret) } ;
424
+ let run_mmap_size = self . get_vcpu_mmap_size ( ) ?;
425
+ Ok ( new_vmfd ( vm_file, run_mmap_size) )
426
+ } else {
427
+ Err ( errno:: Error :: last ( ) )
428
+ }
429
+ }
430
+
377
431
/// Creates a VmFd object from a VM RawFd.
378
432
///
379
433
/// This function is unsafe as the primitives currently returned have the contract that
@@ -462,6 +516,28 @@ mod tests {
462
516
assert_eq ! ( vm. run_size( ) , kvm. get_vcpu_mmap_size( ) . unwrap( ) ) ;
463
517
}
464
518
519
+ #[ test]
520
+ #[ cfg( any( target_arch = "aarch64" ) ) ]
521
+ fn test_create_vm_with_ipa_size ( ) {
522
+ let kvm = Kvm :: new ( ) . unwrap ( ) ;
523
+ if kvm. check_extension ( Cap :: ArmVmIPASize ) {
524
+ let host_ipa_limit = kvm. get_host_ipa_limit ( ) ;
525
+ // Here we test with the maximum value that the host supports to both test the
526
+ // discoverability of supported IPA sizes and likely some other values than 40.
527
+ kvm. create_vm_with_ipa_size ( host_ipa_limit as u32 ) . unwrap ( ) ;
528
+ // Test invalid input values
529
+ // Case 1: IPA size is smaller than 32.
530
+ assert ! ( kvm. create_vm_with_ipa_size( 31 ) . is_err( ) ) ;
531
+ // Case 2: IPA size is bigger than Host_IPA_Limit.
532
+ assert ! ( kvm
533
+ . create_vm_with_ipa_size( ( host_ipa_limit + 1 ) as u32 )
534
+ . is_err( ) ) ;
535
+ } else {
536
+ // Unsupported, here we can test with the default value 40.
537
+ assert ! ( kvm. create_vm_with_ipa_size( 40 ) . is_err( ) ) ;
538
+ }
539
+ }
540
+
465
541
#[ cfg( any( target_arch = "x86" , target_arch = "x86_64" ) ) ]
466
542
#[ test]
467
543
fn test_get_supported_cpuid ( ) {
0 commit comments