@@ -874,6 +874,7 @@ impl PciDevice for VirtioPciDevice {
874874 . contains ( & o) =>
875875 {
876876 // Handled with ioeventfds.
877+ warn ! ( "pci: unexpected read to notification BAR. Offset {o:#x}" ) ;
877878 }
878879 o if ( MSIX_TABLE_BAR_OFFSET ..MSIX_TABLE_BAR_OFFSET + MSIX_TABLE_SIZE ) . contains ( & o) => {
879880 if let Some ( msix_config) = & self . msix_config {
@@ -915,7 +916,7 @@ impl PciDevice for VirtioPciDevice {
915916 . contains ( & o) =>
916917 {
917918 // Handled with ioeventfds.
918- error ! ( "Unexpected write to notification BAR: offset = 0x{: x}" , o ) ;
919+ warn ! ( "pci: unexpected write to notification BAR. Offset {o:# x}" ) ;
919920 }
920921 o if ( MSIX_TABLE_BAR_OFFSET ..MSIX_TABLE_BAR_OFFSET + MSIX_TABLE_SIZE ) . contains ( & o) => {
921922 if let Some ( msix_config) = & self . msix_config {
@@ -1024,6 +1025,7 @@ mod tests {
10241025 VirtioPciNotifyCap ,
10251026 } ;
10261027 use crate :: rate_limiter:: RateLimiter ;
1028+ use crate :: utils:: u64_to_usize;
10271029 use crate :: { Vm , Vmm } ;
10281030
10291031 fn create_vmm_with_virtio_pci_device ( ) -> Vmm {
@@ -1472,4 +1474,43 @@ mod tests {
14721474 isr_status_write ( & mut locked_virtio_pci_device, 0x1312 ) ;
14731475 assert_eq ! ( isr_status_read( & mut locked_virtio_pci_device) , 0 ) ;
14741476 }
1477+
1478+ #[ test]
1479+ fn test_notification_capability ( ) {
1480+ let mut vmm = create_vmm_with_virtio_pci_device ( ) ;
1481+ let device = get_virtio_device ( & vmm) ;
1482+ let mut locked_virtio_pci_device = device. lock ( ) . unwrap ( ) ;
1483+
1484+ let notification_cap_offset = ( capabilities_start ( & mut locked_virtio_pci_device) as usize
1485+ + 3 * ( size_of :: < VirtioPciCap > ( ) + 2 ) )
1486+ . try_into ( )
1487+ . unwrap ( ) ;
1488+
1489+ let ( _, _, notify_cap) =
1490+ read_virtio_notification_cap ( & mut locked_virtio_pci_device, notification_cap_offset) ;
1491+
1492+ // We do not offer `VIRTIO_F_NOTIFICATION_DATA` so:
1493+ // * `cap.offset` MUST by 2-byte aligned
1494+ assert_eq ! ( u32 :: from( notify_cap. cap. offset) & 0x3 , 0 ) ;
1495+ // * The device MUST either present notify_off_multiplier as an even power of 2, or present
1496+ // notify_off_multiplier as 0.
1497+ let multiplier = u32:: from ( notify_cap. notify_off_multiplier ) ;
1498+ assert ! ( multiplier. is_power_of_two( ) && multiplier. trailing_zeros( ) % 2 == 0 ) ;
1499+ // * For all queues, the value cap.length presented by the device MUST satisfy:
1500+ //
1501+ // `cap.length >= queue_notify_off * notify_off_multiplier + 2`
1502+ //
1503+ // The spec allows for up to 65536 queues, but in reality the device we are using with most
1504+ // queues is vsock (3). Let's check here for 16, projecting for future devices and
1505+ // use-cases such as multiple queue pairs in network devices
1506+ assert ! ( u32 :: from( notify_cap. cap. length) >= 15 * multiplier + 2 ) ;
1507+
1508+ // Reads and writes to the notification region of the BAR are handled by IoEvent file
1509+ // descriptors. Any such accesses should have no effects.
1510+ let data = [ 0x42u8 ; u64_to_usize ( NOTIFICATION_SIZE ) ] ;
1511+ locked_virtio_pci_device. write_bar ( 0 , NOTIFICATION_BAR_OFFSET , & data) ;
1512+ let mut buffer = [ 0x0 ; u64_to_usize ( NOTIFICATION_SIZE ) ] ;
1513+ locked_virtio_pci_device. read_bar ( 0 , NOTIFICATION_BAR_OFFSET , & mut buffer) ;
1514+ assert_eq ! ( buffer, [ 0u8 ; u64_to_usize( NOTIFICATION_SIZE ) ] ) ;
1515+ }
14751516}
0 commit comments