@@ -40,17 +40,19 @@ limitations under the License.
4040//!
4141//! ## Skipping
4242//!
43- //! At test time we detect the locally available hypervisor. If no
44- //! fixture is committed for that HV (e.g. KVM-only fixtures
45- //! present, test runs on WHP), the test silently skips with a
46- //! message. CI matrices ensure each HV is exercised by at least
47- //! one job.
43+ //! At test time we detect the locally available hypervisor, the
44+ //! CPU vendor, and the build profile (`debug` vs `release`) and
45+ //! resolve the fixture file `{name}_{hv}_{cpu}_{profile}.hls`. If
46+ //! no matching fixture is committed (e.g. only Intel KVM fixtures
47+ //! checked in, test runs on AMD MSHV), the test silently skips
48+ //! with a message. CI matrices ensure every (hv, cpu, profile)
49+ //! combination is exercised by at least one job.
4850//!
4951//! ## Regeneration
5052//!
5153//! Set `HYPERLIGHT_REGEN_GOLDENS=1` and run `cargo test
52- //! golden_regen` to overwrite every fixture for the locally
53- //! available hypervisor . Always overwrites; the test is
54+ //! golden_regen` to overwrite every fixture matching the local
55+ //! `{hv}_{cpu}_{profile}` triple . Always overwrites; the test is
5456//! deliberate. See `tests/snapshot_goldens/fixtures/README.md`
5557//! for when this is needed.
5658
@@ -116,16 +118,83 @@ impl LocalHypervisor {
116118 }
117119}
118120
121+ /// Detected CPU vendor. Used as a fixture-name suffix because the
122+ /// snapshot byte stream depends on the underlying CPU's CPUID
123+ /// (folded into sregs and the page tables we relocate on load).
124+ #[ derive( Copy , Clone , Debug , PartialEq , Eq ) ]
125+ enum LocalCpu {
126+ Intel ,
127+ Amd ,
128+ }
129+
130+ impl LocalCpu {
131+ fn short_name ( self ) -> & ' static str {
132+ match self {
133+ Self :: Intel => "intel" ,
134+ Self :: Amd => "amd" ,
135+ }
136+ }
137+
138+ /// Detect the running CPU vendor via CPUID leaf 0. Returns
139+ /// `None` for any vendor string we do not recognize so the
140+ /// test simply skips on exotic hardware rather than guessing.
141+ fn detect ( ) -> Option < Self > {
142+ #[ cfg( target_arch = "x86_64" ) ]
143+ unsafe {
144+ let r = std:: arch:: x86_64:: __cpuid ( 0 ) ;
145+ let mut vendor = [ 0u8 ; 12 ] ;
146+ vendor[ 0 ..4 ] . copy_from_slice ( & r. ebx . to_le_bytes ( ) ) ;
147+ vendor[ 4 ..8 ] . copy_from_slice ( & r. edx . to_le_bytes ( ) ) ;
148+ vendor[ 8 ..12 ] . copy_from_slice ( & r. ecx . to_le_bytes ( ) ) ;
149+ match & vendor {
150+ b"GenuineIntel" => Some ( Self :: Intel ) ,
151+ b"AuthenticAMD" => Some ( Self :: Amd ) ,
152+ _ => None ,
153+ }
154+ }
155+ #[ cfg( not( target_arch = "x86_64" ) ) ]
156+ {
157+ None
158+ }
159+ }
160+ }
161+
162+ /// Locally selected build profile, detected at test-time. The
163+ /// profile picks which guest binary `simple_guest_as_string`
164+ /// resolves to (debug vs release simpleguest), which changes the
165+ /// memory blob, so it is part of the fixture name.
166+ fn local_profile ( ) -> & ' static str {
167+ if cfg ! ( debug_assertions) {
168+ "debug"
169+ } else {
170+ "release"
171+ }
172+ }
173+
119174fn fixtures_dir ( ) -> PathBuf {
120175 PathBuf :: from ( env ! ( "CARGO_MANIFEST_DIR" ) ) . join ( "tests/snapshot_goldens/fixtures" )
121176}
122177
123- /// Resolve the path to a fixture file for the current hypervisor.
124- /// Returns `None` if no hypervisor is available, or if the fixture
125- /// is missing from the checked-in set .
126- fn fixture_path ( name : & str ) -> Option < PathBuf > {
178+ /// Filename suffix used for fixtures matching the current host:
179+ /// `{hv}_{cpu}_{profile}`. Returns `None` if any dimension is not
180+ /// detectable .
181+ fn fixture_suffix ( ) -> Option < String > {
127182 let hv = LocalHypervisor :: detect ( ) ?;
128- let path = fixtures_dir ( ) . join ( format ! ( "{}_{}.hls" , name, hv. short_name( ) ) ) ;
183+ let cpu = LocalCpu :: detect ( ) ?;
184+ Some ( format ! (
185+ "{}_{}_{}" ,
186+ hv. short_name( ) ,
187+ cpu. short_name( ) ,
188+ local_profile( )
189+ ) )
190+ }
191+
192+ /// Resolve the path to a fixture file for the current hypervisor,
193+ /// CPU vendor and build profile. Returns `None` if any dimension is
194+ /// missing or the fixture is not checked in.
195+ fn fixture_path ( name : & str ) -> Option < PathBuf > {
196+ let suffix = fixture_suffix ( ) ?;
197+ let path = fixtures_dir ( ) . join ( format ! ( "{}_{}.hls" , name, suffix) ) ;
129198 path. exists ( ) . then_some ( path)
130199}
131200
@@ -141,7 +210,7 @@ fn load_golden(
141210 Some ( p) => p,
142211 None => {
143212 eprintln ! (
144- "snapshot_goldens: skipping {} (no fixture for local hypervisor )" ,
213+ "snapshot_goldens: skipping {} (no fixture matching local hv/cpu/profile )" ,
145214 name,
146215 ) ;
147216 return None ;
@@ -267,10 +336,8 @@ fn register_host_echo_fns<R: Registerable>(r: &mut R) {
267336type FixtureBuilder = fn ( ) -> Arc < Snapshot > ;
268337
269338/// Master list of fixtures regenerated by the regen test.
270- const FIXTURES : & [ ( & str , FixtureBuilder ) ] = & [
271- ( INIT_FIXTURE , build_init) ,
272- ( CALL_FIXTURE , build_call) ,
273- ] ;
339+ const FIXTURES : & [ ( & str , FixtureBuilder ) ] =
340+ & [ ( INIT_FIXTURE , build_init) , ( CALL_FIXTURE , build_call) ] ;
274341
275342// ============================================================================
276343// Regeneration test (env-var-gated, not run in CI)
@@ -293,21 +360,21 @@ fn golden_regen() {
293360 }
294361 }
295362
296- let hv = match LocalHypervisor :: detect ( ) {
297- Some ( h ) => h ,
363+ let suffix = match fixture_suffix ( ) {
364+ Some ( s ) => s ,
298365 None => {
299- eprintln ! ( "golden_regen: no hypervisor available, nothing to write" ) ;
366+ eprintln ! ( "golden_regen: skipping ( no detected hypervisor / cpu vendor on this host)" , ) ;
300367 return ;
301368 }
302369 } ;
303- eprintln ! ( "golden_regen: using hypervisor {}" , hv . short_name ( ) ) ;
370+ eprintln ! ( "golden_regen: writing fixtures for suffix {}" , suffix ) ;
304371
305372 let dir = fixtures_dir ( ) ;
306373 std:: fs:: create_dir_all ( & dir) . expect ( "create fixtures dir" ) ;
307374
308375 let mut wrote = 0usize ;
309376 for ( name, build) in FIXTURES {
310- let path = dir. join ( format ! ( "{}_{}.hls" , name, hv . short_name ( ) ) ) ;
377+ let path = dir. join ( format ! ( "{}_{}.hls" , name, suffix ) ) ;
311378 let snap = build ( ) ;
312379 snap. to_file ( & path) . unwrap_or_else ( |e| {
313380 panic ! (
0 commit comments