@@ -104,6 +104,12 @@ pub fn functionally_same(base: CpuIdDump, target: CpuIdDump) -> bool {
104104 if base_info. has_fp256 ( ) != target_info. has_fp256 ( ) {
105105 return false ;
106106 }
107+
108+ // TODO: same as above: we probably just need to require "base" has
109+ // the same or wider FPU datapath than "target"
110+ if base_info. has_fp512 ( ) != target_info. has_fp512 ( ) {
111+ return false ;
112+ }
107113 }
108114 _ => {
109115 // Specific cases here may be acceptable, but for expediency (and
@@ -500,8 +506,8 @@ fn milan_ideal() -> CpuIdDump {
500506
501507 // Set up processor optimization info (leaf 8000_001Ah)
502508 let mut leaf = PerformanceOptimizationInfo :: empty ( ) ;
503- leaf. set_movu ( true ) ; // TODO: BREAKING
504- leaf. set_fp256 ( true ) ; // TODO: BREAKINGISH?
509+ leaf. set_movu ( true ) ;
510+ leaf. set_fp256 ( true ) ;
505511 cpuid
506512 . set_performance_optimization_info ( Some ( leaf) )
507513 . expect ( "can set leaf 8000_001Ah" ) ;
@@ -548,6 +554,249 @@ fn milan_ideal() -> CpuIdDump {
548554 dump
549555}
550556
557+ pub fn turin_v1 ( ) -> CpuIdDump {
558+ // For VMs, a Turin-like CPU is very much like Milan with AVX-512 features,
559+ // so start from Milan.
560+ let baseline = milan_ideal ( ) ;
561+
562+ let mut cpuid = CpuId :: with_cpuid_reader ( baseline) ;
563+
564+ let mut leaf =
565+ cpuid. get_feature_info ( ) . expect ( "baseline Milan defines leaf 1" ) ;
566+
567+ // Set up EAX: Family 1Ah model 2h stepping 1.
568+ //
569+ // This corresponds to processor revision C1, the production stepping of Turin processors.
570+ leaf. set_extended_family_id ( 0x0B ) ;
571+ leaf. set_base_family_id ( 0x0F ) ;
572+ leaf. set_base_model_id ( 0x02 ) ;
573+ leaf. set_stepping_id ( 0x01 ) ;
574+
575+ // EBX, ECX, EDX are all unchanged from Milan (same cache line flush size,
576+ // leaf 1 features are unchanged)
577+
578+ cpuid. set_feature_info ( Some ( leaf) ) . expect ( "can set leaf 1" ) ;
579+
580+ let mut leaf = cpuid
581+ . get_extended_feature_info ( )
582+ . expect ( "baseline Milan defines leaf 7" ) ;
583+
584+ // Same as with initial Milan profiles, `rdseed` is not supported by the
585+ // virt stack, so we should hide it from guests for now.
586+ leaf. set_rdseed ( false ) ;
587+
588+ // Turin supports the TSC_ADJUST MSR but guest plumbing is not present for
589+ // it and it's not clear what a guest would productively do with it anyway.
590+ leaf. set_tsc_adjust_msr ( false ) ;
591+
592+ // Turin supports MOVDIR64B and MOVDIRI, so pass them through.
593+ leaf. set_movdir64b ( true ) ;
594+ leaf. set_movdiri ( true ) ;
595+
596+ // These AVX512 features are present for all Turin processors.
597+ leaf. set_avx512f ( true ) ;
598+ leaf. set_avx512dq ( true ) ;
599+ leaf. set_avx512_ifma ( true ) ;
600+ leaf. set_avx512cd ( true ) ;
601+ leaf. set_avx512bw ( true ) ;
602+ leaf. set_avx512vl ( true ) ;
603+
604+ leaf. set_avx512vbmi ( true ) ;
605+ leaf. set_avx512vbmi2 ( true ) ;
606+ leaf. set_gfni ( true ) ;
607+ leaf. set_avx512vnni ( true ) ;
608+ leaf. set_avx512bitalg ( true ) ;
609+ leaf. set_avx512vpopcntdq ( true ) ;
610+ // While hardware supports 57-bit virtual addresses, the bhyve support is
611+ // not there yet.
612+ leaf. set_la57 ( false ) ;
613+
614+ leaf. set_avx512_vp2intersect ( true ) ;
615+
616+ leaf. set_avx512_bf16 ( true ) ;
617+ leaf. set_avx_vnni ( true ) ;
618+
619+ cpuid. set_extended_feature_info ( Some ( leaf) ) . expect ( "can set leaf 7h" ) ;
620+
621+ // This is the same information for leaf D as in Milan, but with the new
622+ // AVX-512 bits in Turin.
623+ // TODO: kind of gross to have to pass an empty `CpuIdDump` here...
624+ let mut state = ExtendedStateInfo :: empty ( CpuIdDump :: new ( ) ) ;
625+ state. set_xcr0_supports_legacy_x87 ( true ) ;
626+ state. set_xcr0_supports_sse_128 ( true ) ;
627+ state. set_xcr0_supports_avx_256 ( true ) ;
628+ // Update leaf D for the larger XCR0 set
629+ state. set_xcr0_supports_avx512_opmask ( true ) ;
630+ state. set_xcr0_supports_avx512_zmm_hi256 ( true ) ;
631+ state. set_xcr0_supports_avx512_zmm_hi16 ( true ) ;
632+ // Managed dynamically in practice.
633+ state. set_xsave_area_size_enabled_features ( 0x980 ) ;
634+ // `Core::X86::Cpuid::ProcExtStateEnumEcx00`, but minus the MPK support we
635+ // don't make available to guests.
636+ state. set_xsave_area_size_supported_features ( 0x980 ) ;
637+
638+ state. set_xsaveopt ( true ) ;
639+ state. set_xsavec ( true ) ;
640+ state. set_xgetbv ( true ) ;
641+ state. set_xsave_size ( 0x980 ) ;
642+
643+ let mut leaves = state. into_leaves ( ) . to_vec ( ) ;
644+ let mut ymm_state = ExtendedState :: empty ( ) ;
645+ ymm_state. set_size ( 0x100 ) ;
646+ ymm_state. set_offset ( 0x240 ) ;
647+ leaves. push ( Some ( ymm_state. into_leaf ( ) ) ) ;
648+ // level 3
649+ leaves. push ( None ) ;
650+ // level 4
651+ leaves. push ( None ) ;
652+ // levels 5, 6, and 7 are described in the PPR:
653+ // `Core::X86::Cpuid::ProcExtStateEnumEax06`
654+ //
655+ // level 5
656+ let mut kregs_state = ExtendedState :: empty ( ) ;
657+ kregs_state. set_size ( 0x040 ) ;
658+ kregs_state. set_offset ( 0x340 ) ;
659+ leaves. push ( Some ( kregs_state. into_leaf ( ) ) ) ;
660+ // level 6
661+ let mut zmmhi_state = ExtendedState :: empty ( ) ;
662+ zmmhi_state. set_size ( 0x200 ) ;
663+ zmmhi_state. set_offset ( 0x380 ) ;
664+ leaves. push ( Some ( zmmhi_state. into_leaf ( ) ) ) ;
665+ // level 7
666+ let mut zmmhi16_state = ExtendedState :: empty ( ) ;
667+ zmmhi16_state. set_size ( 0x400 ) ;
668+ zmmhi16_state. set_offset ( 0x580 ) ;
669+ leaves. push ( Some ( zmmhi16_state. into_leaf ( ) ) ) ;
670+
671+ cpuid. set_extended_state_info ( Some ( & leaves[ ..] ) ) . expect ( "can set leaf Dh" ) ;
672+
673+ let mut leaf = cpuid
674+ . get_extended_processor_and_feature_identifiers ( )
675+ . expect ( "baseline Milan defines leaf 8000_0001" ) ;
676+
677+ // This is the same as the leaf 1 EAX configured earlier.
678+ leaf. set_extended_signature ( 0x00B00F21 ) ;
679+
680+ // Hide topology extensions. We'd want to set this and set
681+ // ThreadsPerComputeUnit to indicate SMT is active, but we'd run afoul of
682+ // https://github.com/oxidecomputer/propolis/issues/940, which in turn
683+ // really needs us to disallow VM shapes with odd vCPU counts. For now, just
684+ // hide topology extensions and we'll get sockets into shape in a later CPU
685+ // platform rev.
686+ leaf. set_topology_extensions ( false ) ;
687+ // This is just strange. bhyve supports all six performance counters, so we
688+ // *should* be free to set this bit. Linux is fine with this. But
689+ // experimentally I've seen that with this bit set and TopologyExtensions
690+ // *not* set (and leaves 8000_001D,8000_001E zeroed), Windows Server 2022
691+ // gets into an infinite loop somewhere early in boot.
692+ //
693+ // We want to hide topology extensions for a bit still - we'd like to
694+ // indicate SMT there, but that wants some other changes (see above or
695+ // Propolis#940)
696+ //
697+ // So, if we don't have TopologyExtensions, apparently Windows can't have
698+ // six perf counters?
699+ leaf. set_perf_cntr_extensions ( false ) ;
700+ // RDTSCP requires some bhyve and Propolis work to support, so it is masked
701+ // off for now.
702+ leaf. set_rdtscp ( false ) ;
703+ cpuid
704+ . set_extended_processor_and_feature_identifiers ( Some ( leaf) )
705+ . expect ( "can set leaf 8000_0001h" ) ;
706+
707+ cpuid
708+ . set_processor_brand_string ( Some ( b"Oxide Virtual Turin-like Processor" ) )
709+ . expect ( "can set vCPU brand string" ) ;
710+
711+ let mut leaf = cpuid
712+ . get_processor_capacity_feature_info ( )
713+ . expect ( "can get leaf 8000_0008h" ) ;
714+
715+ // Support for `wbnoinvd` is hidden in bhyve for the time being. This would
716+ // probably be fine to pass through, but it is as-yet untested. Continue
717+ // hiding this instruction.
718+ leaf. set_wbnoinvd ( false ) ;
719+
720+ // "Processor is not vulnerable to Branch Type Confusion"
721+ // This is 1 for all Turin processors and does not require particular MSR
722+ // settings or hypervisor support, so pass it along.
723+ leaf. set_btc_no ( true ) ;
724+
725+ // BSFD, SSBD, STIBP, and IBRS, are all supported on Turin, but guests
726+ // are not yet allowed to access SPEC_CTRL to enable (or confirm they are
727+ // enabled).
728+ leaf. set_psfd ( false ) ;
729+ leaf. set_ssbd ( false ) ;
730+ leaf. set_stibp ( false ) ;
731+ leaf. set_ibrs ( false ) ;
732+
733+ cpuid
734+ . set_processor_capacity_feature_info ( Some ( leaf) )
735+ . expect ( "can set leaf 8000_0008h" ) ;
736+
737+ let mut leaf = cpuid
738+ . get_performance_optimization_info ( )
739+ . expect ( "baseline Milan defines 8000_001Ah" ) ;
740+ leaf. set_fp256 ( false ) ;
741+ leaf. set_fp512 ( true ) ;
742+ cpuid
743+ . set_performance_optimization_info ( Some ( leaf) )
744+ . expect ( "can set leaf 8000_001Ah" ) ;
745+
746+ let mut leaf = cpuid
747+ . get_extended_feature_identification_2 ( )
748+ . expect ( "can get leaf 8000_0021h" ) ;
749+
750+ // We don't support access to MSR `BP_CFG`, so SRSO_MSR_FIX stays hidden.
751+ leaf. set_srso_msr_fix ( false ) ;
752+ // SRSO_USER_KERNEL_NO is advice about vulnerabilities the processor is not
753+ // affected by; no bhyve/Propolis support needed.
754+ leaf. set_srso_user_kernel_no ( true ) ;
755+ // SRSO_NO, more generally, is clear on Turin.
756+ leaf. set_srso_no ( false ) ;
757+ // IBPB_BRTYPE and SBPB are hidden because PRED_CMD and SPEC_CTRL generally
758+ // aren't guest-accessible yet.
759+ leaf. set_ibpb_brtype ( false ) ;
760+ leaf. set_sbpb ( false ) ;
761+ // Enhanced return address predictor security is another "this is just how
762+ // the processor behaves" bit.
763+ leaf. set_eraps ( true ) ;
764+ leaf. set_prefetchi ( true ) ;
765+ // FP512 downgrade is configurable via MSR, but the MSR is not made
766+ // available to guests. The other bits are present on all Turin processors.
767+ leaf. set_fp512_downgrade ( false ) ;
768+ leaf. set_fast_rep_scasb ( true ) ;
769+ leaf. set_epsf ( true ) ;
770+ leaf. set_opcode_0f_017_reclaim ( true ) ;
771+ leaf. set_amd_ermsb ( true ) ;
772+ leaf. set_fast_short_repe_cmpsb ( true ) ;
773+ leaf. set_fast_short_rep_stosb ( true ) ;
774+ // The EFER write is permitted in bhyve, so this *should* work? But I'm not
775+ // very familiar with ohw this is used in practice or where guest OSes would
776+ // find it beneficial. Hide it for now and we'll come back to this for a
777+ // broader speculative controls enablement with SPEC_CTRL/PRED_CMD later.
778+ leaf. set_automatic_ibrs ( false ) ;
779+ // The EFER write is permitted in bhyve, so this *should* work? But the
780+ // forward utility of this bit is not as clear, so hide it.
781+ leaf. set_upper_address_ignore ( false ) ;
782+ // Architectural behavior, so we should pass this through.
783+ leaf. set_fs_gs_base_write_not_serializing ( true ) ;
784+
785+ cpuid
786+ . set_extended_feature_identification_2 ( Some ( leaf) )
787+ . expect ( "can set leaf 8000_0021h" ) ;
788+
789+ // Cache topology leaves are otherwise left zeroed; if we can avoid getting
790+ // into it, let's try!
791+
792+ let mut source = cpuid. into_source ( ) ;
793+ // We've cleared `topology_extensions` above, now remove the leaves so
794+ // Propolis doesn't try specializing these; we don't want them presented yet!
795+ source. set_leaf ( 0x8000_001D , None ) ;
796+ source. set_leaf ( 0x8000_001E , None ) ;
797+ source
798+ }
799+
551800pub fn milan_rfd314 ( ) -> CpuIdDump {
552801 // This is the Milan we'd "want" to expose, absent any other constraints.
553802 let baseline = milan_ideal ( ) ;
@@ -753,7 +1002,7 @@ pub fn dump_to_cpuid_entries(dump: CpuIdDump) -> Vec<CpuidEntry> {
7531002#[ cfg( test) ]
7541003mod test {
7551004 use crate :: app:: instance_platform:: cpu_platform:: {
756- dump_to_cpuid_entries, milan_rfd314,
1005+ dump_to_cpuid_entries, milan_rfd314, turin_v1 ,
7571006 } ;
7581007 use raw_cpuid:: {
7591008 CpuId , CpuIdReader , CpuIdResult , CpuIdWriter , L1CacheTlbInfo ,
@@ -846,6 +1095,76 @@ mod test {
8461095 cpuid_leaf ! ( 0x80000021 , 0x00000045 , 0x00000000 , 0x00000000 , 0x00000000 ) ,
8471096 ] ;
8481097
1098+ // This CPUID leaf blob is some small tweaks on top of the "ideal Milan",
1099+ // maintaining some details that are disabled due to needed bhyve support
1100+ // and including Turin-specific features as supported and relevant to
1101+ // guests.
1102+ const TURIN_V1_CPUID : [ CpuidEntry ; 25 ] = [
1103+ cpuid_leaf ! ( 0x0 , 0x0000000D , 0x68747541 , 0x444D4163 , 0x69746E65 ) ,
1104+ cpuid_leaf ! ( 0x1 , 0x00B00F21 , 0x00000800 , 0xF6D83203 , 0x078BFBFF ) ,
1105+ cpuid_leaf ! ( 0x5 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 ) ,
1106+ cpuid_leaf ! ( 0x6 , 0x00000004 , 0x00000000 , 0x00000000 , 0x00000000 ) ,
1107+ cpuid_subleaf ! (
1108+ 0x7 , 0x0 , 0x00000001 , 0xF1BB03A9 , 0x18005F42 , 0x00000110
1109+ ) ,
1110+ cpuid_subleaf ! (
1111+ 0x7 , 0x1 , 0x00000030 , 0x00000000 , 0x00000000 , 0x00000000
1112+ ) ,
1113+ cpuid_subleaf ! (
1114+ 0xD , 0x0 , 0x000000E7 , 0x00000980 , 0x00000980 , 0x00000000
1115+ ) ,
1116+ cpuid_subleaf ! (
1117+ 0xD , 0x1 , 0x00000007 , 0x00000980 , 0x00000000 , 0x00000000
1118+ ) ,
1119+ cpuid_subleaf ! (
1120+ 0xD , 0x2 , 0x00000100 , 0x00000240 , 0x00000000 , 0x00000000
1121+ ) ,
1122+ /*
1123+ * subleaves 3 and 4 are all-zero
1124+ */
1125+ cpuid_subleaf ! (
1126+ 0xD , 0x5 , 0x00000040 , 0x00000340 , 0x00000000 , 0x00000000
1127+ ) ,
1128+ cpuid_subleaf ! (
1129+ 0xD , 0x6 , 0x00000200 , 0x00000380 , 0x00000000 , 0x00000000
1130+ ) ,
1131+ cpuid_subleaf ! (
1132+ 0xD , 0x7 , 0x00000400 , 0x00000580 , 0x00000000 , 0x00000000
1133+ ) ,
1134+ cpuid_leaf ! ( 0x80000000 , 0x80000021 , 0x68747541 , 0x444D4163 , 0x69746E65 ) ,
1135+ cpuid_leaf ! ( 0x80000001 , 0x00B00F21 , 0x40000000 , 0x440001F1 , 0x25D3FBFF ) ,
1136+ cpuid_leaf ! ( 0x80000002 , 0x6469784F , 0x69562065 , 0x61757472 , 0x7554206C ) ,
1137+ cpuid_leaf ! ( 0x80000003 , 0x2D6E6972 , 0x656B696C , 0x6F725020 , 0x73736563 ) ,
1138+ cpuid_leaf ! ( 0x80000004 , 0x2020726F , 0x20202020 , 0x20202020 , 0x00202020 ) ,
1139+ cpuid_leaf ! ( 0x80000007 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000100 ) ,
1140+ cpuid_leaf ! ( 0x80000008 , 0x00003030 , 0x20000005 , 0x00000000 , 0x00000000 ) ,
1141+ cpuid_leaf ! ( 0x8000000A , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 ) ,
1142+ cpuid_leaf ! ( 0x8000001A , 0x0000000A , 0x00000000 , 0x00000000 , 0x00000000 ) ,
1143+ cpuid_leaf ! ( 0x8000001B , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 ) ,
1144+ cpuid_leaf ! ( 0x8000001C , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 ) ,
1145+ cpuid_leaf ! ( 0x8000001F , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 ) ,
1146+ cpuid_leaf ! ( 0x80000021 , 0x411D8C47 , 0x00000000 , 0x00000000 , 0x00000000 ) ,
1147+ ] ;
1148+
1149+ // Test that Turin V1 matches the predetermined CPUID leaves written above
1150+ // (e.g. that the collection of builders behind `turin_v1` produce this
1151+ // profile as used for testing and elsewhere).
1152+ //
1153+ // This is largely "baseline Milan" with Turin-specific additions.
1154+ #[ test]
1155+ fn turin_v1_is_as_described ( ) {
1156+ let computed = dump_to_cpuid_entries ( turin_v1 ( ) ) ;
1157+
1158+ for ( l, r) in TURIN_V1_CPUID . iter ( ) . zip ( computed. as_slice ( ) . iter ( ) ) {
1159+ eprintln ! ( "comparing {:#08x}.{:?}" , l. leaf, l. subleaf) ;
1160+ assert_eq ! (
1161+ l, r,
1162+ "leaf 0x{:08x} (subleaf? {:?}) did not match" ,
1163+ l. leaf, l. subleaf
1164+ ) ;
1165+ }
1166+ }
1167+
8491168 // Test that the initial RFD 314 definition matches what we compute as the
8501169 // CPUID profile with that configuration in `milan_rfd314()`.
8511170 #[ test]
0 commit comments