@@ -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,120 @@ 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+ if !entries
469+ . get ( 1 )
470+ . map ( |entry| entry. edx & ( 1 << 25 ) != 0 )
471+ . unwrap_or ( false )
472+ {
473+ return Err ( new_error ! ( "SSE support not detected on the host machine" ) ) ;
474+ }
475+ // sse2 is EAX=1, EDX bit 26
476+ if !entries
477+ . get ( 1 )
478+ . map ( |entry| entry. edx & ( 1 << 26 ) != 0 )
479+ . unwrap_or ( false )
480+ {
481+ return Err ( new_error ! ( "SSE2 support not detected on the host machine" ) ) ;
482+ }
483+ // sse3 is EAX=1, ECX bit 0
484+ if !entries
485+ . get ( 1 )
486+ . map ( |entry| entry. ecx & ( 1 << 0 ) != 0 )
487+ . unwrap_or ( false )
488+ {
489+ return Err ( new_error ! ( "SSE3 support not detected on the host machine" ) ) ;
490+ }
491+ // ssse3 is EAX=1, ECX bit 9
492+ if !entries
493+ . get ( 1 )
494+ . map ( |entry| entry. ecx & ( 1 << 9 ) != 0 )
495+ . unwrap_or ( false )
496+ {
497+ return Err ( new_error ! ( "SSSE3 support not detected on the host machine" ) ) ;
498+ }
499+ // sse4.1 is EAX=1, ECX bit 19
500+ if !entries
501+ . get ( 1 )
502+ . map ( |entry| entry. ecx & ( 1 << 19 ) != 0 )
503+ . unwrap_or ( false )
504+ {
505+ return Err ( new_error ! (
506+ "SSE4.1 support not detected on the host machine"
507+ ) ) ;
508+ }
509+ // sse4.2 is EAX=1, ECX bit 20
510+ if !entries
511+ . get ( 1 )
512+ . map ( |entry| entry. ecx & ( 1 << 20 ) != 0 )
513+ . unwrap_or ( false )
514+ {
515+ return Err ( new_error ! (
516+ "SSE4.2 support not detected on the host machine"
517+ ) ) ;
518+ }
519+ // avx is EAX=1, ECX bit 28
520+ if !entries
521+ . get ( 1 )
522+ . map ( |entry| entry. ecx & ( 1 << 28 ) != 0 )
523+ . unwrap_or ( false )
524+ {
525+ return Err ( new_error ! ( "AVX support not detected on the host machine" ) ) ;
526+ }
527+ // avx2 is EAX=7, EBX bit 5
528+ if !entries
529+ . get ( 7 )
530+ . map ( |entry| entry. ebx & ( 1 << 5 ) != 0 )
531+ . unwrap_or ( false )
532+ {
533+ return Err ( new_error ! ( "AVX2 support not detected on the host machine" ) ) ;
534+ }
535+
536+ // Set the CPUID for the guest's vCPU to be the same as the host's
537+ vcpu_fd. set_cpuid2 ( & cpuid) ?;
538+ println ! ( "CPUID set successfully for SIMD support" ) ;
539+
422540 Ok ( ( ) )
423541 }
424542}
0 commit comments