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