@@ -21,7 +21,7 @@ use std::sync::Arc;
2121use std:: sync:: Mutex ;
2222use std:: sync:: atomic:: { AtomicBool , AtomicU64 , Ordering } ;
2323
24- use kvm_bindings:: { KVM_MEM_READONLY , kvm_fpu, kvm_regs, kvm_userspace_memory_region} ;
24+ use kvm_bindings:: { KVM_MEM_READONLY , kvm_fpu, kvm_regs, kvm_userspace_memory_region, kvm_xcrs } ;
2525use kvm_ioctls:: Cap :: UserMemory ;
2626use kvm_ioctls:: { Kvm , VcpuExit , VcpuFd , VmFd } ;
2727use log:: LevelFilter ;
@@ -37,8 +37,8 @@ use super::handlers::DbgMemAccessHandlerWrapper;
3737use super :: handlers:: { MemAccessHandlerWrapper , OutBHandlerWrapper } ;
3838#[ cfg( feature = "init-paging" ) ]
3939use super :: {
40- CR0_AM , CR0_ET , CR0_MP , CR0_NE , CR0_PE , CR0_PG , CR0_WP , CR4_OSFXSR , CR4_OSXMMEXCPT , CR4_PAE ,
41- EFER_LMA , EFER_LME , EFER_NX , EFER_SCE ,
40+ CR0_AM , CR0_ET , CR0_MP , CR0_NE , CR0_PE , CR0_PG , CR0_WP , CR4_OSFXSR , CR4_OSXMMEXCPT ,
41+ CR4_OSXSAVE , CR4_PAE , EFER_LMA , EFER_LME , EFER_NX , EFER_SCE , XCR0_AVX , XCR0_SSE , XCR0_X87 ,
4242} ;
4343use super :: { HyperlightExit , Hypervisor , InterruptHandle , LinuxInterruptHandle , VirtualCPU } ;
4444#[ cfg( gdb) ]
@@ -336,6 +336,10 @@ impl KVMDriver {
336336 } ) ?;
337337
338338 let mut vcpu_fd = vm_fd. create_vcpu ( 0 ) ?;
339+ let now = std:: time:: SystemTime :: now ( ) ;
340+ Self :: setup_cpuid ( & kvm, & mut vcpu_fd) ?;
341+ let elapsed = now. elapsed ( ) . unwrap ( ) ;
342+ println ! ( "CPUID setup took: {:?}" , elapsed) ;
339343 Self :: setup_initial_sregs ( & mut vcpu_fd, pml4_addr) ?;
340344
341345 #[ cfg( gdb) ]
@@ -409,7 +413,7 @@ impl KVMDriver {
409413 cfg_if:: cfg_if! {
410414 if #[ cfg( feature = "init-paging" ) ] {
411415 sregs. cr3 = _pml4_addr;
412- sregs. cr4 = CR4_PAE | CR4_OSFXSR | CR4_OSXMMEXCPT ;
416+ sregs. cr4 = CR4_PAE | CR4_OSFXSR | CR4_OSXMMEXCPT | CR4_OSXSAVE ;
413417 sregs. cr0 = CR0_PE | CR0_MP | CR0_ET | CR0_NE | CR0_AM | CR0_PG | CR0_WP ;
414418 sregs. efer = EFER_LME | EFER_LMA | EFER_SCE | EFER_NX ;
415419 sregs. cs. l = 1 ; // required for 64-bit mode
@@ -419,6 +423,116 @@ impl KVMDriver {
419423 }
420424 }
421425 vcpu_fd. set_sregs ( & sregs) ?;
426+
427+ // Setup XCR0 (Extended Control Register 0) to enable SIMD features
428+ // This is required for AVX and other SIMD instruction support
429+ // Only set XCR0 if the init-paging feature is enabled
430+ cfg_if:: cfg_if! {
431+ if #[ cfg( feature = "init-paging" ) ] {
432+ // Create a properly initialized kvm_xcrs structure
433+ let mut xcrs: kvm_xcrs = unsafe { std:: mem:: zeroed( ) } ;
434+
435+ // Set XCR0 to enable x87 FPU (required), SSE, and AVX
436+ // XCR0 bit 0 (x87) must always be set for any XSAVE features
437+ xcrs. xcrs[ 0 ] . xcr = 0 ; // XCR0 register number
438+ xcrs. xcrs[ 0 ] . value = XCR0_X87 | XCR0_SSE | XCR0_AVX ;
439+ xcrs. nr_xcrs = 1 ;
440+
441+ println!( "Setting XCRs: XCR0={:#x}, nr_xcrs={}" , xcrs. xcrs[ 0 ] . value, xcrs. nr_xcrs) ;
442+
443+ match vcpu_fd. set_xcrs( & xcrs) {
444+ Ok ( _) => {
445+ println!( "Successfully set XCR0 to enable SIMD features: {:#x}" , xcrs. xcrs[ 0 ] . value) ;
446+ } ,
447+ Err ( e) => {
448+ println!( "Failed to set XCRs (XCR0) for SIMD support: {:?}" , e) ;
449+ }
450+ }
451+ }
452+ }
453+
454+ Ok ( ( ) )
455+ }
456+
457+ /// Setup the CPUID for the vCPU to enable SIMD features.
458+ /// This is done by just mirroring the host's CPUID in the guest.
459+ #[ instrument( err( Debug ) , skip_all, parent = Span :: current( ) , level = "Trace" ) ]
460+ fn setup_cpuid ( kvm : & Kvm , vcpu_fd : & mut VcpuFd ) -> Result < ( ) > {
461+ // Get the supported CPUID from the host machine
462+ let cpuid = kvm. get_supported_cpuid ( kvm_bindings:: KVM_MAX_CPUID_ENTRIES ) ?;
463+
464+ let entries = cpuid. as_slice ( ) ;
465+
466+ // https://en.wikipedia.org/wiki/CPUID
467+ // sse: EAX=1, EDX bit 25
468+ assert ! (
469+ entries
470+ . get( 1 )
471+ . map( |entry| entry. edx & ( 1 << 25 ) != 0 )
472+ . unwrap_or( false ) ,
473+ "SSE support not detected on the host machine"
474+ ) ;
475+ // sse2 is EAX=1, EDX bit 26
476+ assert ! (
477+ entries
478+ . get( 1 )
479+ . map( |entry| entry. edx & ( 1 << 26 ) != 0 )
480+ . unwrap_or( false ) ,
481+ "SSE2 support not detected on the host machine"
482+ ) ;
483+ // sse3 is EAX=1, ECX bit 0
484+ assert ! (
485+ entries
486+ . get( 1 )
487+ . map( |entry| entry. ecx & ( 1 << 0 ) != 0 )
488+ . unwrap_or( false ) ,
489+ "SSE3 support not detected on the host machine"
490+ ) ;
491+ // ssse3 is EAX=1, ECX bit 9
492+ assert ! (
493+ entries
494+ . get( 1 )
495+ . map( |entry| entry. ecx & ( 1 << 9 ) != 0 )
496+ . unwrap_or( false ) ,
497+ "SSSE3 support not detected on the host machine"
498+ ) ;
499+ // sse4.1 is EAX=1, ECX bit 19
500+ assert ! (
501+ entries
502+ . get( 1 )
503+ . map( |entry| entry. ecx & ( 1 << 19 ) != 0 )
504+ . unwrap_or( false ) ,
505+ "SSE4.1 support not detected on the host machine"
506+ ) ;
507+ // sse4.2 is EAX=1, ECX bit 20
508+ assert ! (
509+ entries
510+ . get( 1 )
511+ . map( |entry| entry. ecx & ( 1 << 20 ) != 0 )
512+ . unwrap_or( false ) ,
513+ "SSE4.2 support not detected on the host machine"
514+ ) ;
515+ // avx is EAX=1, ECX bit 28
516+ assert ! (
517+ entries
518+ . get( 1 )
519+ . map( |entry| entry. ecx & ( 1 << 28 ) != 0 )
520+ . unwrap_or( false ) ,
521+ "AVX support not detected on the host machine"
522+ ) ;
523+ // avx2 is EAX=7, EBX bit 5
524+ assert ! (
525+ entries
526+ . get( 7 )
527+ . map( |entry| entry. ebx & ( 1 << 5 ) != 0 )
528+ . unwrap_or( false ) ,
529+ "AVX2 support not detected on the host machine"
530+ ) ;
531+
532+ // Set the CPUID for the guest's vCPU to be the same as the host's
533+ vcpu_fd. set_cpuid2 ( & cpuid) ?;
534+ println ! ( "CPUID set successfully for SIMD support" ) ;
535+
422536 Ok ( ( ) )
423537 }
424538}
0 commit comments