virtio_drivers/device/
console.rs

1//! Driver for VirtIO console devices.
2
3use crate::hal::Hal;
4use crate::queue::VirtQueue;
5use crate::transport::Transport;
6use crate::volatile::{volread, ReadOnly, WriteOnly};
7use crate::{Result, PAGE_SIZE};
8use alloc::boxed::Box;
9use bitflags::bitflags;
10use core::ptr::NonNull;
11
12const QUEUE_RECEIVEQ_PORT_0: u16 = 0;
13const QUEUE_TRANSMITQ_PORT_0: u16 = 1;
14const QUEUE_SIZE: usize = 2;
15const SUPPORTED_FEATURES: Features = Features::RING_EVENT_IDX.union(Features::RING_INDIRECT_DESC);
16
17/// Driver for a VirtIO console device.
18///
19/// Only a single port is allowed since `alloc` is disabled. Emergency write and cols/rows are not
20/// implemented.
21///
22/// # Example
23///
24/// ```
25/// # use virtio_drivers::{Error, Hal, transport::Transport};
26/// use virtio_drivers::device::console::VirtIOConsole;
27/// # fn example<HalImpl: Hal, T: Transport>(transport: T) -> Result<(), Error> {
28/// let mut console = VirtIOConsole::<HalImpl, _>::new(transport)?;
29///
30/// let info = console.info();
31/// println!("VirtIO console {}x{}", info.rows, info.columns);
32///
33/// for &c in b"Hello console!\n" {
34///   console.send(c)?;
35/// }
36///
37/// let c = console.recv(true)?;
38/// println!("Read {:?} from console.", c);
39/// # Ok(())
40/// # }
41/// ```
42pub struct VirtIOConsole<H: Hal, T: Transport> {
43    transport: T,
44    config_space: NonNull<Config>,
45    receiveq: VirtQueue<H, QUEUE_SIZE>,
46    transmitq: VirtQueue<H, QUEUE_SIZE>,
47    queue_buf_rx: Box<[u8; PAGE_SIZE]>,
48    cursor: usize,
49    pending_len: usize,
50    /// The token of the outstanding receive request, if there is one.
51    receive_token: Option<u16>,
52}
53
54// SAFETY: The config space can be accessed from any thread.
55unsafe impl<H: Hal, T: Transport + Send> Send for VirtIOConsole<H, T> where
56    VirtQueue<H, QUEUE_SIZE>: Send
57{
58}
59
60// SAFETY: A `&VirtIOConsole` only allows reading the config space.
61unsafe impl<H: Hal, T: Transport + Sync> Sync for VirtIOConsole<H, T> where
62    VirtQueue<H, QUEUE_SIZE>: Sync
63{
64}
65
66/// Information about a console device, read from its configuration space.
67#[derive(Clone, Debug, Eq, PartialEq)]
68pub struct ConsoleInfo {
69    /// The console height in characters.
70    pub rows: u16,
71    /// The console width in characters.
72    pub columns: u16,
73    /// The maxumum number of ports supported by the console device.
74    pub max_ports: u32,
75}
76
77impl<H: Hal, T: Transport> VirtIOConsole<H, T> {
78    /// Creates a new VirtIO console driver.
79    pub fn new(mut transport: T) -> Result<Self> {
80        let negotiated_features = transport.begin_init(SUPPORTED_FEATURES);
81        let config_space = transport.config_space::<Config>()?;
82        let receiveq = VirtQueue::new(
83            &mut transport,
84            QUEUE_RECEIVEQ_PORT_0,
85            negotiated_features.contains(Features::RING_INDIRECT_DESC),
86            negotiated_features.contains(Features::RING_EVENT_IDX),
87        )?;
88        let transmitq = VirtQueue::new(
89            &mut transport,
90            QUEUE_TRANSMITQ_PORT_0,
91            negotiated_features.contains(Features::RING_INDIRECT_DESC),
92            negotiated_features.contains(Features::RING_EVENT_IDX),
93        )?;
94
95        // Safe because no alignment or initialisation is required for [u8], the DMA buffer is
96        // dereferenceable, and the lifetime of the reference matches the lifetime of the DMA buffer
97        // (which we don't otherwise access).
98        let queue_buf_rx = Box::new([0; PAGE_SIZE]);
99
100        transport.finish_init();
101        let mut console = VirtIOConsole {
102            transport,
103            config_space,
104            receiveq,
105            transmitq,
106            queue_buf_rx,
107            cursor: 0,
108            pending_len: 0,
109            receive_token: None,
110        };
111        console.poll_retrieve()?;
112        Ok(console)
113    }
114
115    /// Returns a struct with information about the console device, such as the number of rows and columns.
116    pub fn info(&self) -> ConsoleInfo {
117        // Safe because config_space is a valid pointer to the device configuration space.
118        unsafe {
119            let columns = volread!(self.config_space, cols);
120            let rows = volread!(self.config_space, rows);
121            let max_ports = volread!(self.config_space, max_nr_ports);
122            ConsoleInfo {
123                rows,
124                columns,
125                max_ports,
126            }
127        }
128    }
129
130    /// Makes a request to the device to receive data, if there is not already an outstanding
131    /// receive request or some data already received and not yet returned.
132    fn poll_retrieve(&mut self) -> Result<()> {
133        if self.receive_token.is_none() && self.cursor == self.pending_len {
134            // Safe because the buffer lasts at least as long as the queue, and there are no other
135            // outstanding requests using the buffer.
136            self.receive_token = Some(unsafe {
137                self.receiveq
138                    .add(&[], &mut [self.queue_buf_rx.as_mut_slice()])
139            }?);
140            if self.receiveq.should_notify() {
141                self.transport.notify(QUEUE_RECEIVEQ_PORT_0);
142            }
143        }
144        Ok(())
145    }
146
147    /// Acknowledges a pending interrupt, if any, and completes the outstanding finished read
148    /// request if there is one.
149    ///
150    /// Returns true if new data has been received.
151    pub fn ack_interrupt(&mut self) -> Result<bool> {
152        if !self.transport.ack_interrupt() {
153            return Ok(false);
154        }
155
156        self.finish_receive()
157    }
158
159    /// If there is an outstanding receive request and it has finished, completes it.
160    ///
161    /// Returns true if new data has been received.
162    fn finish_receive(&mut self) -> Result<bool> {
163        let mut flag = false;
164        if let Some(receive_token) = self.receive_token {
165            if self.receive_token == self.receiveq.peek_used() {
166                // Safe because we are passing the same buffer as we passed to `VirtQueue::add` in
167                // `poll_retrieve` and it is still valid.
168                let len = unsafe {
169                    self.receiveq.pop_used(
170                        receive_token,
171                        &[],
172                        &mut [self.queue_buf_rx.as_mut_slice()],
173                    )?
174                };
175                flag = true;
176                assert_ne!(len, 0);
177                self.cursor = 0;
178                self.pending_len = len as usize;
179                // Clear `receive_token` so that when the buffer is used up the next call to
180                // `poll_retrieve` will add a new pending request.
181                self.receive_token.take();
182            }
183        }
184        Ok(flag)
185    }
186
187    /// Returns the next available character from the console, if any.
188    ///
189    /// If no data has been received this will not block but immediately return `Ok<None>`.
190    pub fn recv(&mut self, pop: bool) -> Result<Option<u8>> {
191        self.finish_receive()?;
192        if self.cursor == self.pending_len {
193            return Ok(None);
194        }
195        let ch = self.queue_buf_rx[self.cursor];
196        if pop {
197            self.cursor += 1;
198            self.poll_retrieve()?;
199        }
200        Ok(Some(ch))
201    }
202
203    /// Sends a character to the console.
204    pub fn send(&mut self, chr: u8) -> Result<()> {
205        let buf: [u8; 1] = [chr];
206        self.transmitq
207            .add_notify_wait_pop(&[&buf], &mut [], &mut self.transport)?;
208        Ok(())
209    }
210}
211
212impl<H: Hal, T: Transport> Drop for VirtIOConsole<H, T> {
213    fn drop(&mut self) {
214        // Clear any pointers pointing to DMA regions, so the device doesn't try to access them
215        // after they have been freed.
216        self.transport.queue_unset(QUEUE_RECEIVEQ_PORT_0);
217        self.transport.queue_unset(QUEUE_TRANSMITQ_PORT_0);
218    }
219}
220
221#[repr(C)]
222struct Config {
223    cols: ReadOnly<u16>,
224    rows: ReadOnly<u16>,
225    max_nr_ports: ReadOnly<u32>,
226    emerg_wr: WriteOnly<u32>,
227}
228
229bitflags! {
230    #[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
231    struct Features: u64 {
232        const SIZE                  = 1 << 0;
233        const MULTIPORT             = 1 << 1;
234        const EMERG_WRITE           = 1 << 2;
235
236        // device independent
237        const NOTIFY_ON_EMPTY       = 1 << 24; // legacy
238        const ANY_LAYOUT            = 1 << 27; // legacy
239        const RING_INDIRECT_DESC    = 1 << 28;
240        const RING_EVENT_IDX        = 1 << 29;
241        const UNUSED                = 1 << 30; // legacy
242        const VERSION_1             = 1 << 32; // detect legacy
243
244        // since virtio v1.1
245        const ACCESS_PLATFORM       = 1 << 33;
246        const RING_PACKED           = 1 << 34;
247        const IN_ORDER              = 1 << 35;
248        const ORDER_PLATFORM        = 1 << 36;
249        const SR_IOV                = 1 << 37;
250        const NOTIFICATION_DATA     = 1 << 38;
251    }
252}
253
254#[cfg(test)]
255mod tests {
256    use super::*;
257    use crate::{
258        hal::fake::FakeHal,
259        transport::{
260            fake::{FakeTransport, QueueStatus, State},
261            DeviceType,
262        },
263    };
264    use alloc::{sync::Arc, vec};
265    use core::ptr::NonNull;
266    use std::{sync::Mutex, thread};
267
268    #[test]
269    fn receive() {
270        let mut config_space = Config {
271            cols: ReadOnly::new(0),
272            rows: ReadOnly::new(0),
273            max_nr_ports: ReadOnly::new(0),
274            emerg_wr: WriteOnly::default(),
275        };
276        let state = Arc::new(Mutex::new(State {
277            queues: vec![QueueStatus::default(), QueueStatus::default()],
278            ..Default::default()
279        }));
280        let transport = FakeTransport {
281            device_type: DeviceType::Console,
282            max_queue_size: 2,
283            device_features: 0,
284            config_space: NonNull::from(&mut config_space),
285            state: state.clone(),
286        };
287        let mut console = VirtIOConsole::<FakeHal, FakeTransport<Config>>::new(transport).unwrap();
288
289        // Nothing is available to receive.
290        assert_eq!(console.recv(false).unwrap(), None);
291        assert_eq!(console.recv(true).unwrap(), None);
292
293        // Still nothing after a spurious interrupt.
294        assert_eq!(console.ack_interrupt(), Ok(false));
295        assert_eq!(console.recv(false).unwrap(), None);
296
297        // Make a character available, and simulate an interrupt.
298        {
299            let mut state = state.lock().unwrap();
300            state.write_to_queue::<QUEUE_SIZE>(QUEUE_RECEIVEQ_PORT_0, &[42]);
301
302            state.interrupt_pending = true;
303        }
304        assert_eq!(console.ack_interrupt(), Ok(true));
305        assert_eq!(state.lock().unwrap().interrupt_pending, false);
306
307        // Receive the character. If we don't pop it it is still there to read again.
308        assert_eq!(console.recv(false).unwrap(), Some(42));
309        assert_eq!(console.recv(true).unwrap(), Some(42));
310        assert_eq!(console.recv(true).unwrap(), None);
311    }
312
313    #[test]
314    fn send() {
315        let mut config_space = Config {
316            cols: ReadOnly::new(0),
317            rows: ReadOnly::new(0),
318            max_nr_ports: ReadOnly::new(0),
319            emerg_wr: WriteOnly::default(),
320        };
321        let state = Arc::new(Mutex::new(State {
322            queues: vec![QueueStatus::default(), QueueStatus::default()],
323            ..Default::default()
324        }));
325        let transport = FakeTransport {
326            device_type: DeviceType::Console,
327            max_queue_size: 2,
328            device_features: 0,
329            config_space: NonNull::from(&mut config_space),
330            state: state.clone(),
331        };
332        let mut console = VirtIOConsole::<FakeHal, FakeTransport<Config>>::new(transport).unwrap();
333
334        // Start a thread to simulate the device waiting for characters.
335        let handle = thread::spawn(move || {
336            println!("Device waiting for a character.");
337            State::wait_until_queue_notified(&state, QUEUE_TRANSMITQ_PORT_0);
338            println!("Transmit queue was notified.");
339
340            let data = state
341                .lock()
342                .unwrap()
343                .read_from_queue::<QUEUE_SIZE>(QUEUE_TRANSMITQ_PORT_0);
344            assert_eq!(data, b"Q");
345        });
346
347        assert_eq!(console.send(b'Q'), Ok(()));
348
349        handle.join().unwrap();
350    }
351}