Skip to main content

machina_virtio_device/
bell.rs

1// Copyright 2021 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use fuchsia_async::{self as fasync, PacketReceiver, ReceiverRegistration};
6
7use futures::channel::mpsc;
8use futures::{Stream, StreamExt, TryStreamExt};
9use std::pin::Pin;
10use std::task::{Context, Poll};
11use thiserror::Error;
12
13// Virtio 1.0 Section 4.1.4.4: notify_off_multiplier is combined with the
14// queue_notify_off to derive the Queue Notify address within a BAR for a
15// virtqueue:
16//
17//      cap.offset + queue_notify_off * notify_off_multiplier
18//
19// Virtio 1.0 Section 4.1.4.4.1: The device MUST either present
20// notify_off_multiplier as an even power of 2, or present
21// notify_off_multiplier as 0.
22//
23// By using a multiplier of 4, we use sequential 4b words to notify, ex:
24//
25//      cap.offset + 0  -> Notify Queue 0
26//      cap.offset + 4  -> Notify Queue 1
27//      ...
28//      cap.offset + 4n -> Notify Queue n
29const QUEUE_NOTIFY_MULTIPLIER: usize = 4;
30
31#[derive(Error, Debug, PartialEq, Eq)]
32pub enum BellError {
33    #[error("Received unexpected packet {0:?}")]
34    UnexpectedPacket(zx::Packet),
35    #[error("Trap address {0:?} did not map to a queue")]
36    BadAddress(zx::GPAddr),
37}
38
39#[derive(Debug, Eq, PartialEq)]
40enum Packet {
41    Bell(zx::GPAddr),
42    Other(zx::Packet),
43}
44
45// Forwards incoming port packets into a channel.
46#[derive(Debug)]
47pub struct PortForwarder {
48    channel: mpsc::UnboundedSender<Packet>,
49}
50
51impl PacketReceiver for PortForwarder {
52    fn receive_packet(&self, packet: zx::Packet) {
53        let packet = if let zx::PacketContents::GuestBell(bell) = packet.contents() {
54            Packet::Bell(bell.addr())
55        } else {
56            Packet::Other(packet)
57        };
58        // An unbounded channel should never be full and this PacketReceiver should have been
59        // de-registered if the receiver side of this channel were to have gone away, and so no
60        // errors should be possible. Even if an error does occur we have no mechanism to return it,
61        // so we just unwrap.
62        self.channel.unbounded_send(packet).unwrap();
63    }
64}
65
66/// Wrapper for receiving bell traps from the guest.
67///
68/// A bell trap from the guest is a signal that a particular virtqueue needs to be processed. Bell
69/// traps are delivered by the kernel as packets on a port. This wrapper registers itself on the
70/// current executors port and provides an asynchronous [`Stream`] of queues that have been
71/// notified.
72#[derive(Debug)]
73pub struct GuestBellTrap<T = ReceiverRegistration<PortForwarder>> {
74    _registration: T,
75    channel: mpsc::UnboundedReceiver<Packet>,
76    base: zx::GPAddr,
77    num_queues: u16,
78}
79
80impl GuestBellTrap {
81    /// Construct a [`GuestBellTrap`] for the provided guest range.
82    ///
83    /// If a device is using bell traps then the trap information is in the [`StartInfo`]
84    /// (fidl_fuchsia_virtualization_hardware::StartInfo). A reference to the [`zx::Guest`] is only
85    /// needed temporarily to register the trap range.
86    ///
87    /// Note that traps cannot be unregistered and creating a second [`GuestBellTrap`] for the same
88    /// range, even after dropping the first one, will fail.
89    pub fn new(guest: &zx::Guest, base: zx::GPAddr, len: usize) -> Result<Self, zx::Status> {
90        let (tx, rx) = mpsc::unbounded();
91        let registration =
92            fasync::EHandle::local().register_receiver(PortForwarder { channel: tx });
93        guest.set_trap_bell(base, len, registration.port(), registration.key())?;
94        Self::with_registration(base, len, rx, registration)
95    }
96}
97
98impl<T> GuestBellTrap<T> {
99    fn with_registration(
100        base: zx::GPAddr,
101        len: usize,
102        rx: mpsc::UnboundedReceiver<Packet>,
103        registration: T,
104    ) -> Result<Self, zx::Status> {
105        // Ensure base is aligned to the queue multiplier.
106        if (base.0 % QUEUE_NOTIFY_MULTIPLIER) != 0 {
107            return Err(zx::Status::INVALID_ARGS);
108        }
109        let num_queues = (len / QUEUE_NOTIFY_MULTIPLIER) as u16;
110        if num_queues as usize * QUEUE_NOTIFY_MULTIPLIER != len {
111            return Err(zx::Status::INVALID_ARGS);
112        }
113        // Needs to be at least one queue.
114        if num_queues == 0 {
115            return Err(zx::Status::INVALID_ARGS);
116        }
117        Ok(GuestBellTrap { _registration: registration, channel: rx, base, num_queues })
118    }
119
120    /// Convert a guest address to a queue.
121    ///
122    /// Returns a none if the provided `addr` is not within the trap range. Otherwise if a queue is
123    /// returned the caller still needs to validate that it is for a queue that was actually
124    /// configured and exists.
125    pub fn queue_for_addr(&self, addr: zx::GPAddr) -> Option<u16> {
126        let queue =
127            ((addr.0.checked_sub(self.base.0)?) / QUEUE_NOTIFY_MULTIPLIER).try_into().ok()?;
128        if queue >= self.num_queues { None } else { Some(queue) }
129    }
130}
131
132impl<T: Unpin> GuestBellTrap<T> {
133    /// Consume all traps by notifying the provided [`Device`](crate::Device).
134    ///
135    /// This method will only yield a value if there is an error, either due to the stream ending or
136    /// an invalid queue.
137    pub async fn complete<'a, N>(
138        self,
139        device: &crate::Device<'a, N>,
140    ) -> Result<(), crate::DeviceError> {
141        self.err_into()
142            .try_for_each(|queue| futures::future::ready(device.notify_queue(queue as u16)))
143            .await
144    }
145
146    /// [`complete`] a [`GuestBellTrap`] or block forever.
147    ///
148    /// Bell traps are not always provided to a device and this provides a unified way of
149    /// interacting with them. It will either run [`complete`] on the provided bell trap, or block
150    /// permanently. In this way it is similar to [`complete`] in that it only resolves to a value
151    /// on error, and the absence of a bell trap is not considered an error.
152    pub async fn complete_or_pending<'a, N>(
153        maybe_trap: Option<Self>,
154        device: &crate::Device<'a, N>,
155    ) -> Result<(), crate::DeviceError> {
156        match maybe_trap {
157            Some(bell) => bell.complete(device).await,
158            None => futures::future::pending().await,
159        }
160    }
161}
162
163impl<T: Unpin> Stream for GuestBellTrap<T> {
164    type Item = Result<u16, BellError>;
165
166    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
167        self.channel.poll_next_unpin(cx).map(|maybe_packet| {
168            // We do not expect our channel to get closed and for this to be a None, but if it is
169            // there is no choice but to propagate it up to have this stream get closed.
170            let packet = maybe_packet?;
171            match packet {
172                Packet::Bell(addr) => {
173                    Some(self.queue_for_addr(addr).ok_or(BellError::BadAddress(addr)))
174                }
175                Packet::Other(packet) => Some(Err(BellError::UnexpectedPacket(packet))),
176            }
177        })
178    }
179}
180
181#[cfg(test)]
182mod tests {
183    use super::*;
184    use futures::FutureExt;
185    #[test]
186    fn trap_size() {
187        // Base must be aligned.
188        assert_eq!(
189            GuestBellTrap::with_registration(zx::GPAddr(3), 4, mpsc::unbounded().1, ()).err(),
190            Some(zx::Status::INVALID_ARGS)
191        );
192        assert_eq!(
193            GuestBellTrap::with_registration(zx::GPAddr(1), 4, mpsc::unbounded().1, ()).err(),
194            Some(zx::Status::INVALID_ARGS)
195        );
196
197        // Length may not be zero.
198        assert_eq!(
199            GuestBellTrap::with_registration(zx::GPAddr(8), 0, mpsc::unbounded().1, ()).err(),
200            Some(zx::Status::INVALID_ARGS)
201        );
202
203        // Length must be a multiple.
204        assert_eq!(
205            GuestBellTrap::with_registration(zx::GPAddr(8), 1, mpsc::unbounded().1, ()).err(),
206            Some(zx::Status::INVALID_ARGS)
207        );
208        assert_eq!(
209            GuestBellTrap::with_registration(zx::GPAddr(8), 3, mpsc::unbounded().1, ()).err(),
210            Some(zx::Status::INVALID_ARGS)
211        );
212        assert_eq!(
213            GuestBellTrap::with_registration(zx::GPAddr(8), 9, mpsc::unbounded().1, ()).err(),
214            Some(zx::Status::INVALID_ARGS)
215        );
216        assert_eq!(
217            GuestBellTrap::with_registration(zx::GPAddr(8), 42, mpsc::unbounded().1, ()).err(),
218            Some(zx::Status::INVALID_ARGS)
219        );
220
221        assert!(
222            GuestBellTrap::with_registration(zx::GPAddr(64), 12, mpsc::unbounded().1, ()).is_ok()
223        );
224    }
225
226    #[test]
227    fn queue_conversion() {
228        let bell =
229            GuestBellTrap::with_registration(zx::GPAddr(80), 12, mpsc::unbounded().1, ()).unwrap();
230
231        // Too low to be in the range.
232        assert_eq!(bell.queue_for_addr(zx::GPAddr(79)), None);
233        assert_eq!(bell.queue_for_addr(zx::GPAddr(76)), None);
234
235        // Any access in the range should map to the queue.
236        assert_eq!(bell.queue_for_addr(zx::GPAddr(80)), Some(0));
237        assert_eq!(bell.queue_for_addr(zx::GPAddr(81)), Some(0));
238        assert_eq!(bell.queue_for_addr(zx::GPAddr(83)), Some(0));
239
240        // All queues should map.
241        assert_eq!(bell.queue_for_addr(zx::GPAddr(84)), Some(1));
242        assert_eq!(bell.queue_for_addr(zx::GPAddr(88)), Some(2));
243        assert_eq!(bell.queue_for_addr(zx::GPAddr(91)), Some(2));
244
245        // Too high to be in the range.
246        assert_eq!(bell.queue_for_addr(zx::GPAddr(92)), None);
247        assert_eq!(bell.queue_for_addr(zx::GPAddr(94)), None);
248        assert_eq!(bell.queue_for_addr(zx::GPAddr(128)), None);
249    }
250
251    #[fuchsia::test(allow_stalls = false)]
252    async fn packet_stream() {
253        let (tx, rx) = mpsc::unbounded();
254
255        let bell = GuestBellTrap::with_registration(zx::GPAddr(64), 12, rx, ()).unwrap();
256
257        // Put some valid and invalid packets in.
258        tx.unbounded_send(Packet::Bell(zx::GPAddr(64))).unwrap();
259        tx.unbounded_send(Packet::Bell(zx::GPAddr(68))).unwrap();
260        tx.unbounded_send(Packet::Bell(zx::GPAddr(100))).unwrap();
261
262        let mut stream = bell.peekable();
263        // There should be items waiting.
264        assert!(Pin::new(&mut stream).peek().now_or_never().is_some());
265
266        // Read off the valid and invalid items.
267        assert_eq!(stream.next().await, Some(Ok(0)));
268        assert_eq!(stream.next().await, Some(Ok(1)));
269        assert_eq!(stream.next().await, Some(Err(BellError::BadAddress(zx::GPAddr(100)))));
270
271        // Should be nothing else waiting.
272        assert!(Pin::new(&mut stream).peek().now_or_never().is_none());
273    }
274}