1 //! Driver for VirtIO console devices.
2 
3 use crate::hal::Hal;
4 use crate::queue::VirtQueue;
5 use crate::transport::Transport;
6 use crate::volatile::{volread, ReadOnly, WriteOnly};
7 use crate::{Result, PAGE_SIZE};
8 use alloc::boxed::Box;
9 use bitflags::bitflags;
10 use core::ptr::NonNull;
11 
12 const QUEUE_RECEIVEQ_PORT_0: u16 = 0;
13 const QUEUE_TRANSMITQ_PORT_0: u16 = 1;
14 const QUEUE_SIZE: usize = 2;
15 const 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 /// ```
42 pub 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.
55 unsafe 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.
61 unsafe 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)]
68 pub 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 
77 impl<H: Hal, T: Transport> VirtIOConsole<H, T> {
78     /// Creates a new VirtIO console driver.
new(mut transport: T) -> Result<Self>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.
info(&self) -> ConsoleInfo116     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.
poll_retrieve(&mut self) -> Result<()>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.
ack_interrupt(&mut self) -> Result<bool>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.
finish_receive(&mut self) -> Result<bool>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>`.
recv(&mut self, pop: bool) -> Result<Option<u8>>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.
send(&mut self, chr: u8) -> Result<()>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 
212 impl<H: Hal, T: Transport> Drop for VirtIOConsole<H, T> {
drop(&mut self)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)]
222 struct Config {
223     cols: ReadOnly<u16>,
224     rows: ReadOnly<u16>,
225     max_nr_ports: ReadOnly<u32>,
226     emerg_wr: WriteOnly<u32>,
227 }
228 
229 bitflags! {
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)]
255 mod 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]
receive()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]
send()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 }
352