@@ -18,15 +18,19 @@ use kvm_bindings::{
1818    KVM_IRQ_ROUTING_IRQCHIP ,  KVM_IRQ_ROUTING_MSI ,  KVM_MEM_LOG_DIRTY_PAGES ,  KVM_MSI_VALID_DEVID , 
1919    KvmIrqRouting ,  kvm_irq_routing_entry,  kvm_userspace_memory_region, 
2020} ; 
21- use  kvm_ioctls:: VmFd ; 
21+ use  kvm_ioctls:: { IoEventAddress ,   NoDatamatch ,   VmFd } ; 
2222use  log:: debug; 
23- use  pci:: DeviceRelocation ; 
23+ #[ cfg( target_arch = "aarch64" ) ]  
24+ use  log:: error; 
25+ use  pci:: { DeviceRelocation ,  PciBarRegionType } ; 
2426use  serde:: { Deserialize ,  Serialize } ; 
27+ use  vm_allocator:: RangeInclusive ; 
2528use  vm_device:: interrupt:: { InterruptSourceGroup ,  MsiIrqSourceConfig } ; 
2629use  vmm_sys_util:: errno; 
2730use  vmm_sys_util:: eventfd:: EventFd ; 
2831
2932pub  use  crate :: arch:: { ArchVm  as  Vm ,  ArchVmError ,  VmState } ; 
33+ use  crate :: devices:: virtio:: transport:: pci:: device:: VirtioPciDevice ; 
3034use  crate :: logger:: info; 
3135use  crate :: persist:: CreateSnapshotError ; 
3236use  crate :: snapshot:: Persist ; 
@@ -622,23 +626,107 @@ impl Vm {
622626impl  DeviceRelocation  for  Vm  { 
623627    fn  move_bar ( 
624628        & self , 
625-         _old_base :  u64 , 
626-         _new_base :  u64 , 
627-         _len :  u64 , 
628-         _pci_dev :  & mut  dyn  pci:: PciDevice , 
629-         _region_type :  pci:: PciBarRegionType , 
629+         old_base :  u64 , 
630+         new_base :  u64 , 
631+         len :  u64 , 
632+         pci_dev :  & mut  dyn  pci:: PciDevice , 
633+         region_type :  pci:: PciBarRegionType , 
630634    )  -> Result < ( ) ,  std:: io:: Error >  { 
631-         todo ! ( ) 
635+         debug ! ( "pci: moving BAR from {old_base:#x}:{len:#x} to {new_base:#x}:{len:#x}" ) ; 
636+         match  region_type { 
637+             PciBarRegionType :: IoRegion  => { 
638+                 #[ cfg( target_arch = "x86_64" ) ]  
639+                 // We do not allocate IO addresses, we just hard-code them, no need to handle 
640+                 // (re)allocations. Just update PIO bus 
641+                 self . pio_bus 
642+                     . update_range ( old_base,  len,  new_base,  len) 
643+                     . map_err ( std:: io:: Error :: other) ?; 
644+ 
645+                 #[ cfg( target_arch = "aarch64" ) ]  
646+                 return  Err ( std:: io:: Error :: other ( 
647+                     "pci: IO regions not supported on Aarch64" , 
648+                 ) ) ; 
649+             } 
650+             PciBarRegionType :: Memory32BitRegion  | PciBarRegionType :: Memory64BitRegion  => { 
651+                 let  old_range =
652+                     RangeInclusive :: new ( old_base,  old_base + len - 1 ) . map_err ( |_| { 
653+                         std:: io:: Error :: other ( "pci: invalid old range for device relocation" ) 
654+                     } ) ?; 
655+                 let  allocator = if  region_type == PciBarRegionType :: Memory32BitRegion  { 
656+                     & self . common . resource_allocator . mmio32_memory 
657+                 }  else  { 
658+                     & self . common . resource_allocator . mmio64_memory 
659+                 } ; 
660+ 
661+                 allocator
662+                     . lock ( ) 
663+                     . expect ( "Poisoned lock" ) 
664+                     . free ( & old_range) 
665+                     . map_err ( |_| { 
666+                         std:: io:: Error :: other ( "pci: failed deallocating old MMIO range" ) 
667+                     } ) ?; 
668+ 
669+                 allocator
670+                     . lock ( ) 
671+                     . unwrap ( ) 
672+                     . allocate ( len,  len,  vm_allocator:: AllocPolicy :: ExactMatch ( new_base) ) 
673+                     . map_err ( |_| std:: io:: Error :: other ( "pci: failed allocating new MMIO range" ) ) ?; 
674+ 
675+                 // Update MMIO bus 
676+                 self . common 
677+                     . mmio_bus 
678+                     . update_range ( old_base,  len,  new_base,  len) 
679+                     . map_err ( std:: io:: Error :: other) ?; 
680+             } 
681+         } 
682+ 
683+         let  any_dev = pci_dev. as_any_mut ( ) ; 
684+         if  let  Some ( virtio_pci_dev)  = any_dev. downcast_ref :: < VirtioPciDevice > ( )  { 
685+             let  bar_addr = virtio_pci_dev. config_bar_addr ( ) ; 
686+             if  bar_addr == new_base { 
687+                 for  ( i,  queue_evt)  in  virtio_pci_dev
688+                     . virtio_device ( ) 
689+                     . lock ( ) 
690+                     . expect ( "Poisoned lock" ) 
691+                     . queue_events ( ) 
692+                     . iter ( ) 
693+                     . enumerate ( ) 
694+                 { 
695+                     const  NOTIFICATION_BAR_OFFSET :  u64  = 0x6000 ; 
696+                     const  NOTIFY_OFF_MULTIPLIER :  u64  = 4 ; 
697+                     let  notify_base = old_base + NOTIFICATION_BAR_OFFSET ; 
698+                     let  io_addr =
699+                         IoEventAddress :: Mmio ( notify_base + i as  u64  *  NOTIFY_OFF_MULTIPLIER ) ; 
700+                     self . common 
701+                         . fd 
702+                         . unregister_ioevent ( queue_evt,  & io_addr,  NoDatamatch ) ?; 
703+ 
704+                     let  notify_base = new_base + NOTIFICATION_BAR_OFFSET ; 
705+                     let  io_addr =
706+                         IoEventAddress :: Mmio ( notify_base + i as  u64  *  NOTIFY_OFF_MULTIPLIER ) ; 
707+                     self . common 
708+                         . fd 
709+                         . register_ioevent ( queue_evt,  & io_addr,  NoDatamatch ) ?; 
710+                 } 
711+             } 
712+         } 
713+ 
714+         pci_dev. move_bar ( old_base,  new_base) 
632715    } 
633716} 
634717
635718#[ cfg( test) ]  
636719pub ( crate )  mod  tests { 
720+     use  std:: ops:: DerefMut ; 
721+ 
722+     use  pci:: PciBdf ; 
723+     use  vm_allocator:: AllocPolicy ; 
637724    use  vm_device:: interrupt:: { InterruptSourceConfig ,  LegacyIrqSourceConfig } ; 
638725    use  vm_memory:: GuestAddress ; 
639726    use  vm_memory:: mmap:: MmapRegionBuilder ; 
640727
641728    use  super :: * ; 
729+     use  crate :: device_manager:: mmio:: tests:: DummyDevice ; 
642730    use  crate :: test_utils:: single_region_mem_raw; 
643731    use  crate :: utils:: mib_to_bytes; 
644732    use  crate :: vstate:: kvm:: Kvm ; 
@@ -969,4 +1057,195 @@ pub(crate) mod tests {
9691057            assert ! ( !new_vector. enabled. load( Ordering :: Acquire ) ) ; 
9701058        } 
9711059    } 
1060+ 
1061+     fn  new_virtio_pci_device ( vm :  & Arc < Vm > )  -> Arc < Mutex < VirtioPciDevice > >  { 
1062+         let  dummy = Arc :: new ( Mutex :: new ( DummyDevice :: new ( ) ) ) ; 
1063+         let  msi_vectors = Arc :: new ( Vm :: create_msix_group ( vm. clone ( ) ,  0 ,  2 ) . unwrap ( ) ) ; 
1064+         Arc :: new ( Mutex :: new ( 
1065+             VirtioPciDevice :: new ( 
1066+                 "dummy" . to_string ( ) , 
1067+                 vm. guest_memory ( ) . clone ( ) , 
1068+                 dummy, 
1069+                 msi_vectors, 
1070+                 PciBdf :: new ( 0 ,  0 ,  1 ,  0 ) . into ( ) , 
1071+                 true , 
1072+                 None , 
1073+             ) 
1074+             . unwrap ( ) , 
1075+         ) ) 
1076+     } 
1077+ 
1078+     #[ cfg( target_arch = "aarch64" ) ]  
1079+     #[ test]  
1080+     fn  test_device_relocation_no_io_on_arm ( )  { 
1081+         let  ( _,  vm)  = setup_vm_with_memory ( mib_to_bytes ( 128 ) ) ; 
1082+         let  vm = Arc :: new ( vm) ; 
1083+         let  old_base = 0x42 ; 
1084+         let  new_base = 0x84 ; 
1085+         let  len = 0x1312000 ; 
1086+         let  mut  pci_dev = new_virtio_pci_device ( & vm) ; 
1087+ 
1088+         vm. move_bar ( 
1089+             old_base, 
1090+             new_base, 
1091+             len, 
1092+             & mut  pci_dev, 
1093+             PciBarRegionType :: IoRegion , 
1094+         ) 
1095+         . unwrap_err ( ) ; 
1096+     } 
1097+ 
1098+     #[ test]  
1099+     fn  test_device_relocation_bad_ranges ( )  { 
1100+         let  ( _,  vm)  = setup_vm_with_memory ( mib_to_bytes ( 128 ) ) ; 
1101+         let  vm = Arc :: new ( vm) ; 
1102+         let  virtio_dev = new_virtio_pci_device ( & vm) ; 
1103+         let  mut  virtio_dev_locked = virtio_dev. lock ( ) . unwrap ( ) ; 
1104+         let  pci_dev = virtio_dev_locked. deref_mut ( ) ; 
1105+ 
1106+         // Old region would overflow 
1107+         vm. move_bar ( 0 ,  0x12 ,  u64:: MAX ,  pci_dev,  PciBarRegionType :: IoRegion ) 
1108+             . unwrap_err ( ) ; 
1109+         // New region would overflow 
1110+         vm. move_bar ( 0x13 ,  0 ,  u64:: MAX ,  pci_dev,  PciBarRegionType :: IoRegion ) 
1111+             . unwrap_err ( ) ; 
1112+     } 
1113+ 
1114+     #[ test]  
1115+     fn  test_device_relocation_old_region_not_allocated ( )  { 
1116+         let  ( _,  vm)  = setup_vm_with_memory ( mib_to_bytes ( 128 ) ) ; 
1117+         let  vm = Arc :: new ( vm) ; 
1118+         let  virtio_dev = new_virtio_pci_device ( & vm) ; 
1119+         let  mut  virtio_dev_locked = virtio_dev. lock ( ) . unwrap ( ) ; 
1120+         let  pci_dev = virtio_dev_locked. deref_mut ( ) ; 
1121+ 
1122+         let  err = vm
1123+             . move_bar ( 
1124+                 0x12 , 
1125+                 0x13 , 
1126+                 0x42 , 
1127+                 pci_dev, 
1128+                 PciBarRegionType :: Memory32BitRegion , 
1129+             ) 
1130+             . unwrap_err ( ) ; 
1131+         assert_eq ! ( format!( "{err}" ) ,  "pci: failed deallocating old MMIO range" ) ; 
1132+     } 
1133+ 
1134+     #[ test]  
1135+     fn  test_device_relocation_new_region_allocated ( )  { 
1136+         let  ( _,  vm)  = setup_vm_with_memory ( mib_to_bytes ( 128 ) ) ; 
1137+         let  vm = Arc :: new ( vm) ; 
1138+         let  virtio_dev = new_virtio_pci_device ( & vm) ; 
1139+         let  mut  virtio_dev_locked = virtio_dev. lock ( ) . unwrap ( ) ; 
1140+         let  pci_dev = virtio_dev_locked. deref_mut ( ) ; 
1141+ 
1142+         // Allocate old range and add it to bus 
1143+         let  old_base = vm
1144+             . common 
1145+             . resource_allocator 
1146+             . allocate_32bit_mmio_memory ( 0x1000 ,  0x1000 ,  AllocPolicy :: FirstMatch ) 
1147+             . unwrap ( ) ; 
1148+         vm. common 
1149+             . mmio_bus 
1150+             . insert ( virtio_dev. clone ( ) ,  0x1000 ,  0x1000 ) 
1151+             . unwrap ( ) ; 
1152+ 
1153+         // Also allocate new region. This should cause relocation to fail 
1154+         let  new_base = vm
1155+             . common 
1156+             . resource_allocator 
1157+             . allocate_32bit_mmio_memory ( 0x1000 ,  0x1000 ,  AllocPolicy :: FirstMatch ) 
1158+             . unwrap ( ) ; 
1159+ 
1160+         let  err = vm
1161+             . move_bar ( 
1162+                 old_base, 
1163+                 new_base, 
1164+                 0x1000 , 
1165+                 pci_dev, 
1166+                 PciBarRegionType :: Memory32BitRegion , 
1167+             ) 
1168+             . unwrap_err ( ) ; 
1169+         assert_eq ! ( format!( "{err}" ) ,  "pci: failed allocating new MMIO range" ) ; 
1170+     } 
1171+ 
1172+     #[ cfg( target_arch = "x86_64" ) ]  
1173+     #[ test]  
1174+     fn  test_device_relocation_io_device ( )  { 
1175+         let  ( _,  vm)  = setup_vm_with_memory ( mib_to_bytes ( 128 ) ) ; 
1176+         let  vm = Arc :: new ( vm) ; 
1177+         let  virtio_dev = new_virtio_pci_device ( & vm) ; 
1178+         let  mut  virtio_dev_locked = virtio_dev. lock ( ) . unwrap ( ) ; 
1179+         let  pci_dev = virtio_dev_locked. deref_mut ( ) ; 
1180+ 
1181+         let  err = vm
1182+             . move_bar ( 0x12 ,  0x13 ,  0x42000 ,  pci_dev,  PciBarRegionType :: IoRegion ) 
1183+             . unwrap_err ( ) ; 
1184+         assert_eq ! ( format!( "{err}" ) ,  "bus_error: MissingAddressRange" ) ; 
1185+ 
1186+         // If, instead, we add the device in the PIO bus, everything should work fine 
1187+         vm. pio_bus 
1188+             . insert ( virtio_dev. clone ( ) ,  0x12 ,  0x42000 ) 
1189+             . unwrap ( ) ; 
1190+ 
1191+         vm. move_bar ( 0x12 ,  0x13 ,  0x42000 ,  pci_dev,  PciBarRegionType :: IoRegion ) 
1192+             . unwrap ( ) 
1193+     } 
1194+ 
1195+     #[ test]  
1196+     fn  test_device_relocation_mmio_device ( )  { 
1197+         let  ( _,  vm)  = setup_vm_with_memory ( mib_to_bytes ( 128 ) ) ; 
1198+         let  vm = Arc :: new ( vm) ; 
1199+ 
1200+         let  virtio_dev = new_virtio_pci_device ( & vm) ; 
1201+         let  mut  virtio_dev_locked = virtio_dev. lock ( ) . unwrap ( ) ; 
1202+         let  pci_dev = virtio_dev_locked. deref_mut ( ) ; 
1203+ 
1204+         let  old_base = vm
1205+             . common 
1206+             . resource_allocator 
1207+             . allocate_64bit_mmio_memory ( 0x8000 ,  0x1000 ,  AllocPolicy :: FirstMatch ) 
1208+             . unwrap ( ) ; 
1209+ 
1210+         let  err = vm
1211+             . move_bar ( 
1212+                 old_base, 
1213+                 old_base + 0x8000 , 
1214+                 0x8000 , 
1215+                 pci_dev, 
1216+                 PciBarRegionType :: Memory64BitRegion , 
1217+             ) 
1218+             . unwrap_err ( ) ; 
1219+         assert_eq ! ( format!( "{err}" ) ,  "bus_error: MissingAddressRange" ) ; 
1220+ 
1221+         // Need to reset the allocator here. Erroring out left it to a limbo state (old range is 
1222+         // deallocated, new range is allocated). 
1223+         vm. common 
1224+             . resource_allocator 
1225+             . mmio64_memory 
1226+             . lock ( ) 
1227+             . unwrap ( ) 
1228+             . free ( & RangeInclusive :: new ( old_base + 0x8000 ,  old_base + 0x8000  + 0x8000  - 1 ) . unwrap ( ) ) 
1229+             . unwrap ( ) ; 
1230+         vm. common 
1231+             . resource_allocator 
1232+             . allocate_64bit_mmio_memory ( 0x8000 ,  0x1000 ,  AllocPolicy :: FirstMatch ) 
1233+             . unwrap ( ) ; 
1234+ 
1235+         // If we add the device to the MMIO bus, everything should work fine 
1236+         vm. common 
1237+             . mmio_bus 
1238+             . insert ( virtio_dev. clone ( ) ,  old_base,  0x8000 ) 
1239+             . unwrap ( ) ; 
1240+ 
1241+         println ! ( "old base: {old_base:#x}" ) ; 
1242+         vm. move_bar ( 
1243+             old_base, 
1244+             old_base + 0x8000 , 
1245+             0x8000 , 
1246+             pci_dev, 
1247+             PciBarRegionType :: Memory64BitRegion , 
1248+         ) 
1249+         . unwrap ( ) ; 
1250+     } 
9721251} 
0 commit comments