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