virtio_drivers/device/
console.rs

1//! Driver for VirtIO console devices.
2
3#[cfg(feature = "embedded-io")]
4mod embedded_io;
5
6use crate::config::{read_config, write_config, ReadOnly, WriteOnly};
7use crate::hal::Hal;
8use crate::queue::VirtQueue;
9use crate::transport::{InterruptStatus, Transport};
10use crate::{Error, Result, PAGE_SIZE};
11use alloc::boxed::Box;
12use bitflags::bitflags;
13use core::fmt::{self, Display, Formatter, Write};
14use log::error;
15use zerocopy::{FromBytes, Immutable, IntoBytes};
16
17const QUEUE_RECEIVEQ_PORT_0: u16 = 0;
18const QUEUE_TRANSMITQ_PORT_0: u16 = 1;
19const QUEUE_SIZE: usize = 2;
20const SUPPORTED_FEATURES: Features = Features::RING_EVENT_IDX
21    .union(Features::RING_INDIRECT_DESC)
22    .union(Features::SIZE)
23    .union(Features::EMERG_WRITE)
24    .union(Features::VERSION_1);
25
26/// Driver for a VirtIO console device.
27///
28/// Only a single port is supported.
29///
30/// # Example
31///
32/// ```
33/// # use virtio_drivers::{Error, Hal, transport::Transport};
34/// use virtio_drivers::device::console::VirtIOConsole;
35/// # fn example<HalImpl: Hal, T: Transport>(transport: T) -> Result<(), Error> {
36/// let mut console = VirtIOConsole::<HalImpl, _>::new(transport)?;
37///
38/// let size = console.size().unwrap().unwrap();
39/// println!("VirtIO console {}x{}", size.rows, size.columns);
40///
41/// for &c in b"Hello console!\n" {
42///   console.send(c)?;
43/// }
44///
45/// let c = console.recv(true)?;
46/// println!("Read {:?} from console.", c);
47/// # Ok(())
48/// # }
49/// ```
50pub struct VirtIOConsole<H: Hal, T: Transport> {
51    transport: T,
52    negotiated_features: Features,
53    receiveq: VirtQueue<H, QUEUE_SIZE>,
54    transmitq: VirtQueue<H, QUEUE_SIZE>,
55    queue_buf_rx: Box<[u8; PAGE_SIZE]>,
56    /// The index of the next byte in `queue_buf_rx` which `recv` should return.
57    cursor: usize,
58    /// The number of bytes read into `queue_buf_rx`.
59    pending_len: usize,
60    /// The token of the outstanding receive request, if there is one.
61    receive_token: Option<u16>,
62}
63
64// SAFETY: The config space can be accessed from any thread.
65unsafe impl<H: Hal, T: Transport + Send> Send for VirtIOConsole<H, T> where
66    VirtQueue<H, QUEUE_SIZE>: Send
67{
68}
69
70// SAFETY: A `&VirtIOConsole` only allows reading the config space.
71unsafe impl<H: Hal, T: Transport + Sync> Sync for VirtIOConsole<H, T> where
72    VirtQueue<H, QUEUE_SIZE>: Sync
73{
74}
75
76/// The width and height of a console, in characters.
77#[derive(Copy, Clone, Debug, Eq, PartialEq)]
78pub struct Size {
79    /// The console width in characters.
80    pub columns: u16,
81    /// The console height in characters.
82    pub rows: u16,
83}
84
85impl Display for Size {
86    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
87        write!(f, "{}x{}", self.columns, self.rows)
88    }
89}
90
91impl<H: Hal, T: Transport> VirtIOConsole<H, T> {
92    /// Creates a new VirtIO console driver.
93    pub fn new(mut transport: T) -> Result<Self> {
94        let negotiated_features = transport.begin_init(SUPPORTED_FEATURES);
95        let receiveq = VirtQueue::new(
96            &mut transport,
97            QUEUE_RECEIVEQ_PORT_0,
98            negotiated_features.contains(Features::RING_INDIRECT_DESC),
99            negotiated_features.contains(Features::RING_EVENT_IDX),
100        )?;
101        let transmitq = VirtQueue::new(
102            &mut transport,
103            QUEUE_TRANSMITQ_PORT_0,
104            negotiated_features.contains(Features::RING_INDIRECT_DESC),
105            negotiated_features.contains(Features::RING_EVENT_IDX),
106        )?;
107
108        // Safe because no alignment or initialisation is required for [u8], the DMA buffer is
109        // dereferenceable, and the lifetime of the reference matches the lifetime of the DMA buffer
110        // (which we don't otherwise access).
111        let queue_buf_rx = Box::new([0; PAGE_SIZE]);
112
113        transport.finish_init();
114        let mut console = VirtIOConsole {
115            transport,
116            negotiated_features,
117            receiveq,
118            transmitq,
119            queue_buf_rx,
120            cursor: 0,
121            pending_len: 0,
122            receive_token: None,
123        };
124        console.poll_retrieve()?;
125        Ok(console)
126    }
127
128    /// Returns the size of the console, if the device supports reporting this.
129    pub fn size(&self) -> Result<Option<Size>> {
130        if self.negotiated_features.contains(Features::SIZE) {
131            self.transport.read_consistent(|| {
132                Ok(Some(Size {
133                    columns: read_config!(self.transport, Config, cols)?,
134                    rows: read_config!(self.transport, Config, rows)?,
135                }))
136            })
137        } else {
138            Ok(None)
139        }
140    }
141
142    /// Makes a request to the device to receive data, if there is not already an outstanding
143    /// receive request or some data already received and not yet returned.
144    fn poll_retrieve(&mut self) -> Result<()> {
145        if self.receive_token.is_none() && self.cursor == self.pending_len {
146            // SAFETY: The buffer lasts at least as long as the queue, and there are no other
147            // outstanding requests using the buffer.
148            self.receive_token = Some(unsafe {
149                self.receiveq
150                    .add(&[], &mut [self.queue_buf_rx.as_mut_slice()])
151            }?);
152            if self.receiveq.should_notify() {
153                self.transport.notify(QUEUE_RECEIVEQ_PORT_0);
154            }
155        }
156        Ok(())
157    }
158
159    /// Acknowledges a pending interrupt, if any, and completes the outstanding finished read
160    /// request if there is one.
161    ///
162    /// Returns true if new data has been received.
163    pub fn ack_interrupt(&mut self) -> Result<bool> {
164        let status = self.transport.ack_interrupt();
165        if !status.contains(InterruptStatus::QUEUE_INTERRUPT) {
166            return Ok(false);
167        }
168
169        self.finish_receive()
170    }
171
172    /// If there is an outstanding receive request and it has finished, completes it.
173    ///
174    /// Returns true if new data has been received.
175    fn finish_receive(&mut self) -> Result<bool> {
176        let mut flag = false;
177        if let Some(receive_token) = self.receive_token {
178            if self.receive_token == self.receiveq.peek_used() {
179                // SAFETY: We are passing the same buffer as we passed to `VirtQueue::add` in
180                // `poll_retrieve` and it is still valid.
181                let len = unsafe {
182                    self.receiveq.pop_used(
183                        receive_token,
184                        &[],
185                        &mut [self.queue_buf_rx.as_mut_slice()],
186                    )?
187                };
188                flag = true;
189                assert_ne!(len, 0);
190                self.cursor = 0;
191                self.pending_len = len as usize;
192                // Clear `receive_token` so that when the buffer is used up the next call to
193                // `poll_retrieve` will add a new pending request.
194                self.receive_token.take();
195            }
196        }
197        Ok(flag)
198    }
199
200    /// Returns the next available character from the console, if any.
201    ///
202    /// If no data has been received this will not block but immediately return `Ok<None>`.
203    pub fn recv(&mut self, pop: bool) -> Result<Option<u8>> {
204        self.finish_receive()?;
205        if self.cursor == self.pending_len {
206            return Ok(None);
207        }
208        let ch = self.queue_buf_rx[self.cursor];
209        if pop {
210            self.cursor += 1;
211            self.poll_retrieve()?;
212        }
213        Ok(Some(ch))
214    }
215
216    /// Sends a character to the console.
217    pub fn send(&mut self, chr: u8) -> Result<()> {
218        let buf: [u8; 1] = [chr];
219        self.transmitq
220            .add_notify_wait_pop(&[&buf], &mut [], &mut self.transport)?;
221        Ok(())
222    }
223
224    /// Sends one or more bytes to the console.
225    pub fn send_bytes(&mut self, buffer: &[u8]) -> Result {
226        self.transmitq
227            .add_notify_wait_pop(&[buffer], &mut [], &mut self.transport)?;
228        Ok(())
229    }
230
231    /// Blocks until at least one character is available to read.
232    fn wait_for_receive(&mut self) -> Result {
233        self.poll_retrieve()?;
234        while self.cursor == self.pending_len {
235            self.finish_receive()?;
236        }
237        Ok(())
238    }
239
240    /// Sends a character to the console using the emergency write feature.
241    ///
242    /// Returns an error if the device doesn't support emergency write.
243    pub fn emergency_write(&mut self, chr: u8) -> Result<()> {
244        if self.negotiated_features.contains(Features::EMERG_WRITE) {
245            write_config!(self.transport, Config, emerg_wr, chr.into())?;
246            Ok(())
247        } else {
248            Err(Error::Unsupported)
249        }
250    }
251}
252
253impl<H: Hal, T: Transport> Write for VirtIOConsole<H, T> {
254    fn write_str(&mut self, s: &str) -> fmt::Result {
255        self.send_bytes(s.as_bytes()).map_err(|e| {
256            error!("Error writing to conosel: {}", e);
257            fmt::Error
258        })
259    }
260}
261
262impl<H: Hal, T: Transport> Drop for VirtIOConsole<H, T> {
263    fn drop(&mut self) {
264        // Clear any pointers pointing to DMA regions, so the device doesn't try to access them
265        // after they have been freed.
266        self.transport.queue_unset(QUEUE_RECEIVEQ_PORT_0);
267        self.transport.queue_unset(QUEUE_TRANSMITQ_PORT_0);
268    }
269}
270
271#[derive(FromBytes, Immutable, IntoBytes)]
272#[repr(C)]
273struct Config {
274    cols: ReadOnly<u16>,
275    rows: ReadOnly<u16>,
276    max_nr_ports: ReadOnly<u32>,
277    emerg_wr: WriteOnly<u32>,
278}
279
280bitflags! {
281    #[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
282    struct Features: u64 {
283        const SIZE                  = 1 << 0;
284        const MULTIPORT             = 1 << 1;
285        const EMERG_WRITE           = 1 << 2;
286
287        // device independent
288        const NOTIFY_ON_EMPTY       = 1 << 24; // legacy
289        const ANY_LAYOUT            = 1 << 27; // legacy
290        const RING_INDIRECT_DESC    = 1 << 28;
291        const RING_EVENT_IDX        = 1 << 29;
292        const UNUSED                = 1 << 30; // legacy
293        const VERSION_1             = 1 << 32; // detect legacy
294
295        // since virtio v1.1
296        const ACCESS_PLATFORM       = 1 << 33;
297        const RING_PACKED           = 1 << 34;
298        const IN_ORDER              = 1 << 35;
299        const ORDER_PLATFORM        = 1 << 36;
300        const SR_IOV                = 1 << 37;
301        const NOTIFICATION_DATA     = 1 << 38;
302    }
303}
304
305#[cfg(test)]
306mod tests {
307    use super::*;
308    use crate::{
309        hal::fake::FakeHal,
310        transport::{
311            fake::{FakeTransport, QueueStatus, State},
312            DeviceType,
313        },
314    };
315    use alloc::{sync::Arc, vec};
316    use std::{sync::Mutex, thread};
317
318    #[test]
319    fn config_info_no_features() {
320        let config_space = Config {
321            cols: ReadOnly::new(80),
322            rows: ReadOnly::new(42),
323            max_nr_ports: ReadOnly::new(0),
324            emerg_wr: WriteOnly::default(),
325        };
326        let state = Arc::new(Mutex::new(State::new(
327            vec![QueueStatus::default(), QueueStatus::default()],
328            config_space,
329        )));
330        let transport = FakeTransport {
331            device_type: DeviceType::Console,
332            max_queue_size: 2,
333            device_features: 0,
334            state: state.clone(),
335        };
336        let console = VirtIOConsole::<FakeHal, FakeTransport<Config>>::new(transport).unwrap();
337
338        assert_eq!(console.size(), Ok(None));
339    }
340
341    #[test]
342    fn config_info() {
343        let config_space = Config {
344            cols: ReadOnly::new(80),
345            rows: ReadOnly::new(42),
346            max_nr_ports: ReadOnly::new(0),
347            emerg_wr: WriteOnly::default(),
348        };
349        let state = Arc::new(Mutex::new(State::new(
350            vec![QueueStatus::default(), QueueStatus::default()],
351            config_space,
352        )));
353        let transport = FakeTransport {
354            device_type: DeviceType::Console,
355            max_queue_size: 2,
356            device_features: 0x07,
357            state: state.clone(),
358        };
359        let console = VirtIOConsole::<FakeHal, FakeTransport<Config>>::new(transport).unwrap();
360
361        assert_eq!(
362            console.size(),
363            Ok(Some(Size {
364                columns: 80,
365                rows: 42
366            }))
367        );
368    }
369
370    #[test]
371    fn emergency_write() {
372        let config_space = Config {
373            cols: ReadOnly::new(0),
374            rows: ReadOnly::new(0),
375            max_nr_ports: ReadOnly::new(0),
376            emerg_wr: WriteOnly::default(),
377        };
378        let state = Arc::new(Mutex::new(State::new(
379            vec![QueueStatus::default(), QueueStatus::default()],
380            config_space,
381        )));
382        let transport = FakeTransport {
383            device_type: DeviceType::Console,
384            max_queue_size: 2,
385            device_features: 0x07,
386            state: state.clone(),
387        };
388        let mut console = VirtIOConsole::<FakeHal, FakeTransport<Config>>::new(transport).unwrap();
389
390        console.emergency_write(42).unwrap();
391        assert_eq!(state.lock().unwrap().config_space.emerg_wr.0, 42);
392    }
393
394    #[test]
395    fn receive() {
396        let config_space = Config {
397            cols: ReadOnly::new(0),
398            rows: ReadOnly::new(0),
399            max_nr_ports: ReadOnly::new(0),
400            emerg_wr: WriteOnly::default(),
401        };
402        let state = Arc::new(Mutex::new(State::new(
403            vec![QueueStatus::default(), QueueStatus::default()],
404            config_space,
405        )));
406        let transport = FakeTransport {
407            device_type: DeviceType::Console,
408            max_queue_size: 2,
409            device_features: 0,
410            state: state.clone(),
411        };
412        let mut console = VirtIOConsole::<FakeHal, FakeTransport<Config>>::new(transport).unwrap();
413
414        // Nothing is available to receive.
415        assert_eq!(console.recv(false).unwrap(), None);
416        assert_eq!(console.recv(true).unwrap(), None);
417
418        // Still nothing after a spurious interrupt.
419        assert_eq!(console.ack_interrupt(), Ok(false));
420        assert_eq!(console.recv(false).unwrap(), None);
421
422        // Make a character available, and simulate an interrupt.
423        {
424            let mut state = state.lock().unwrap();
425            state.write_to_queue::<QUEUE_SIZE>(QUEUE_RECEIVEQ_PORT_0, &[42]);
426
427            state.interrupt_pending = true;
428        }
429        assert_eq!(console.ack_interrupt(), Ok(true));
430        assert_eq!(state.lock().unwrap().interrupt_pending, false);
431
432        // Receive the character. If we don't pop it it is still there to read again.
433        assert_eq!(console.recv(false).unwrap(), Some(42));
434        assert_eq!(console.recv(true).unwrap(), Some(42));
435        assert_eq!(console.recv(true).unwrap(), None);
436    }
437
438    #[test]
439    fn send() {
440        let config_space = Config {
441            cols: ReadOnly::new(0),
442            rows: ReadOnly::new(0),
443            max_nr_ports: ReadOnly::new(0),
444            emerg_wr: WriteOnly::default(),
445        };
446        let state = Arc::new(Mutex::new(State::new(
447            vec![QueueStatus::default(), QueueStatus::default()],
448            config_space,
449        )));
450        let transport = FakeTransport {
451            device_type: DeviceType::Console,
452            max_queue_size: 2,
453            device_features: 0,
454            state: state.clone(),
455        };
456        let mut console = VirtIOConsole::<FakeHal, FakeTransport<Config>>::new(transport).unwrap();
457
458        // Start a thread to simulate the device waiting for characters.
459        let handle = thread::spawn(move || {
460            println!("Device waiting for a character.");
461            State::wait_until_queue_notified(&state, QUEUE_TRANSMITQ_PORT_0);
462            println!("Transmit queue was notified.");
463
464            let data = state
465                .lock()
466                .unwrap()
467                .read_from_queue::<QUEUE_SIZE>(QUEUE_TRANSMITQ_PORT_0);
468            assert_eq!(data, b"Q");
469        });
470
471        assert_eq!(console.send(b'Q'), Ok(()));
472
473        handle.join().unwrap();
474    }
475}