@@ -59,6 +59,22 @@ use vmgs_resources::GuestStateEncryptionPolicy;
5959/// The Hyper-V Petri backend 
6060pub  struct  HyperVPetriBackend  { } 
6161
62+ /// Represents a SCSI Controller addeded to a VM. 
63+ #[ derive( Debug ) ]  
64+ pub  struct  HyperVScsiController  { 
65+     /// An identifier provided by the test to identify this controller. 
66+ pub  test_id :  String , 
67+ 
68+     /// The controller number assigned by Hyper-V. 
69+ pub  controller_number :  u32 , 
70+ 
71+     /// The target VTL this controller is mapped to (supplied by test). 
72+ pub  target_vtl :  u32 , 
73+ 
74+     /// The VSID assigned by Hyper-V for this controller. 
75+ pub  vsid :  guid:: Guid , 
76+ } 
77+ 
6278/// Resources needed at runtime for a Hyper-V Petri VM 
6379pub  struct  HyperVPetriRuntime  { 
6480    vm :  HyperVVM , 
@@ -68,11 +84,63 @@ pub struct HyperVPetriRuntime {
6884
6985    is_openhcl :  bool , 
7086    is_isolated :  bool , 
87+ 
88+     /// Last VTL2 settings set on this VM. 
89+ vtl2_settings :  Option < vtl2_settings_proto:: Vtl2Settings > , 
90+ 
91+     /// Test-added SCSI controllers. 
92+ /// TODO (future PR): push this into `PetriVmConfig` and use in 
93+ /// openvmm as well. 
94+ additional_scsi_controllers :  Vec < HyperVScsiController > , 
95+ } 
96+ 
97+ /// Additional configuration for a Hyper-V VM. 
98+ #[ derive( Default ,  Debug ) ]  
99+ pub  struct  HyperVPetriConfig  { 
100+     /// VTL2 settings to configure on the VM before petri powers it on. 
101+ initial_vtl2_settings :  Option < vtl2_settings_proto:: Vtl2Settings > , 
102+ 
103+     /// Test-added SCSI controllers (targeting specific VTLs). 
104+ /// A tuple if test-identifier and targetvtl. Test-identifier 
105+ /// is used so that the test can find a specific controller, if that 
106+ /// is important to the test. These are resolved into a list of 
107+ /// [`HyperVScsiController`] objects stored in the runtime. 
108+ additional_scsi_controllers :  Vec < ( String ,  u32 ) > , 
109+ } 
110+ 
111+ impl  HyperVPetriConfig  { 
112+     /// Add custom VTL 2 settings. 
113+ // TODO: At some point we want to replace uses of this with nicer with_disk, 
114+     // with_nic, etc. methods. And unify this with the same function definition 
115+     // as the openvmm backend. 
116+     pub  fn  with_custom_vtl2_settings ( 
117+         mut  self , 
118+         f :  impl  FnOnce ( & mut  vtl2_settings_proto:: Vtl2Settings ) , 
119+     )  -> Self  { 
120+         if  self . initial_vtl2_settings . is_none ( )  { 
121+             self . initial_vtl2_settings  = Some ( vtl2_settings_proto:: Vtl2Settings  { 
122+                 version :  vtl2_settings_proto:: vtl2_settings_base:: Version :: V1 . into ( ) , 
123+                 fixed :  None , 
124+                 dynamic :  Some ( Default :: default ( ) ) , 
125+                 namespace_settings :  Default :: default ( ) , 
126+             } ) ; 
127+         } 
128+ 
129+         f ( self . initial_vtl2_settings . as_mut ( ) . unwrap ( ) ) ; 
130+         self 
131+     } 
132+ 
133+     /// Add an additional SCSI controller to the VM. 
134+ /// Will be added before the VM starts. 
135+ pub  fn  with_additional_scsi_controller ( mut  self ,  test_id :  String ,  target_vtl :  u32 )  -> Self  { 
136+         self . additional_scsi_controllers . push ( ( test_id,  target_vtl) ) ; 
137+         self 
138+     } 
71139} 
72140
73141#[ async_trait]  
74142impl  PetriVmmBackend  for  HyperVPetriBackend  { 
75-     type  VmmConfig  = ( ) ; 
143+     type  VmmConfig  = HyperVPetriConfig ; 
76144    type  VmRuntime  = HyperVPetriRuntime ; 
77145
78146    fn  check_compat ( firmware :  & Firmware ,  arch :  MachineArch )  -> bool  { 
@@ -95,10 +163,6 @@ impl PetriVmmBackend for HyperVPetriBackend {
95163        modify_vmm_config :  Option < impl  FnOnce ( Self :: VmmConfig )  -> Self :: VmmConfig  + Send > , 
96164        resources :  & PetriVmResources , 
97165    )  -> anyhow:: Result < Self :: VmRuntime >  { 
98-         if  modify_vmm_config. is_some ( )  { 
99-             panic ! ( "specified modify_vmm_config, but that is not supported for hyperv" ) ; 
100-         } 
101- 
102166        let  PetriVmConfig  { 
103167            name, 
104168            arch, 
@@ -327,7 +391,7 @@ impl PetriVmmBackend for HyperVPetriBackend {
327391            BootDeviceType :: Ide  => Some ( ( powershell:: ControllerType :: Ide ,  0 ) ) , 
328392            BootDeviceType :: Scsi  => Some ( ( 
329393                powershell:: ControllerType :: Scsi , 
330-                 vm. add_scsi_controller ( 0 ) . await ?, 
394+                 vm. add_scsi_controller ( 0 ) . await ?. 0 , 
331395            ) ) , 
332396            BootDeviceType :: Nvme  => todo ! ( "NVMe boot device not yet supported for Hyper-V" ) , 
333397        }  { 
@@ -384,7 +448,7 @@ impl PetriVmmBackend for HyperVPetriBackend {
384448                    vm. set_imc ( & imc_hive) . await ?; 
385449                } 
386450
387-                 let  controller_number = vm. add_scsi_controller ( 0 ) . await ?; 
451+                 let  controller_number = vm. add_scsi_controller ( 0 ) . await ?. 0 ; 
388452                vm. add_vhd ( 
389453                    & agent_disk_path, 
390454                    powershell:: ControllerType :: Scsi , 
@@ -437,7 +501,7 @@ impl PetriVmmBackend for HyperVPetriBackend {
437501                if  build_and_persist_agent_image ( & agent_image,  & agent_disk_path) 
438502                    . context ( "vtl2 agent disk" ) ?
439503                { 
440-                     let  controller_number = vm. add_scsi_controller ( 2 ) . await ?; 
504+                     let  controller_number = vm. add_scsi_controller ( 2 ) . await ?. 0 ; 
441505                    vm. add_vhd ( 
442506                        & agent_disk_path, 
443507                        powershell:: ControllerType :: Scsi , 
@@ -507,6 +571,32 @@ impl PetriVmmBackend for HyperVPetriBackend {
507571            hyperv_serial_log_task ( driver. clone ( ) ,  serial_pipe_path,  serial_log_file) , 
508572        ) ) ; 
509573
574+         let  mut  added_controllers = Vec :: new ( ) ; 
575+         let  mut  vtl2_settings = None ; 
576+ 
577+         // TODO: If OpenHCL is being used, then translate storage through it. 
578+         // (requires changes above where VHDs are added) 
579+         if  let  Some ( modify_vmm_config)  = modify_vmm_config { 
580+             let  config = modify_vmm_config ( HyperVPetriConfig :: default ( ) ) ; 
581+ 
582+             tracing:: debug!( ?config,  "additional hyper-v config" ) ; 
583+ 
584+             for  ( test_id,  target_vtl)  in  config. additional_scsi_controllers  { 
585+                 let  ( controller_number,  vsid)  = vm. add_scsi_controller ( target_vtl) . await ?; 
586+                 added_controllers. push ( HyperVScsiController  { 
587+                     test_id, 
588+                     controller_number, 
589+                     target_vtl, 
590+                     vsid, 
591+                 } ) ; 
592+             } 
593+ 
594+             if  let  Some ( settings)  = & config. initial_vtl2_settings  { 
595+                 vm. set_base_vtl2_settings ( settings) . await ?; 
596+                 vtl2_settings = Some ( settings. clone ( ) ) ; 
597+             } 
598+         } 
599+ 
510600        vm. start ( ) . await ?; 
511601
512602        Ok ( HyperVPetriRuntime  { 
@@ -516,10 +606,63 @@ impl PetriVmmBackend for HyperVPetriBackend {
516606            driver :  driver. clone ( ) , 
517607            is_openhcl :  openhcl_config. is_some ( ) , 
518608            is_isolated :  firmware. isolation ( ) . is_some ( ) , 
609+             additional_scsi_controllers :  added_controllers, 
610+             vtl2_settings, 
519611        } ) 
520612    } 
521613} 
522614
615+ impl  HyperVPetriRuntime  { 
616+     /// Gets the VTL2 settings in the `Base` namespace. 
617+ /// 
618+ /// TODO: For now, this is just a copy of whatever was last set via petri. 
619+ /// The function signature (`async`, return `Result`) is to allow for 
620+ /// future changes to actually query the VM for the current settings. 
621+ pub  async  fn  get_base_vtl2_settings ( 
622+         & self , 
623+     )  -> anyhow:: Result < Option < vtl2_settings_proto:: Vtl2Settings > >  { 
624+         Ok ( self . vtl2_settings . clone ( ) ) 
625+     } 
626+ 
627+     /// Get the list of additional SCSI controllers added to this VM (those 
628+ /// configured to be added by the test, as opposed to the petri framework). 
629+ pub  fn  get_additional_scsi_controllers ( & self )  -> & [ HyperVScsiController ]  { 
630+         & self . additional_scsi_controllers 
631+     } 
632+ 
633+     /// Set the VTL2 settings in the `Base` namespace (fixed settings, storage 
634+ /// settings, etc). 
635+ pub  async  fn  set_base_vtl2_settings ( 
636+         & mut  self , 
637+         settings :  & vtl2_settings_proto:: Vtl2Settings , 
638+     )  -> anyhow:: Result < ( ) >  { 
639+         let  r = self . vm . set_base_vtl2_settings ( settings) . await ; 
640+         if  r. is_ok ( )  { 
641+             self . vtl2_settings  = Some ( settings. clone ( ) ) ; 
642+         } 
643+         r
644+     } 
645+ 
646+     /// Adds a VHD with the optionally specified location (a.k.a LUN) to the 
647+ /// optionally specified controller. 
648+ pub  async  fn  add_vhd ( 
649+         & mut  self , 
650+         vhd :  impl  AsRef < Path > , 
651+         controller_type :  powershell:: ControllerType , 
652+         controller_location :  Option < u32 > , 
653+         controller_number :  Option < u32 > , 
654+     )  -> anyhow:: Result < ( ) >  { 
655+         self . vm 
656+             . add_vhd ( 
657+                 vhd. as_ref ( ) , 
658+                 controller_type, 
659+                 controller_location, 
660+                 controller_number, 
661+             ) 
662+             . await 
663+     } 
664+ } 
665+ 
523666#[ async_trait]  
524667impl  PetriVmRuntime  for  HyperVPetriRuntime  { 
525668    type  VmInspector  = NoPetriVmInspector ; 
0 commit comments