xref: /cloud-hypervisor/virtio-devices/src/console.rs (revision 9af2968a7dc47b89bf07ea9dc5e735084efcfa3a)
1 // Copyright 2019 Intel Corporation. All Rights Reserved.
2 // SPDX-License-Identifier: Apache-2.0
3 
4 use super::Error as DeviceError;
5 use super::{
6     ActivateError, ActivateResult, EpollHelper, EpollHelperError, EpollHelperHandler, Queue,
7     VirtioCommon, VirtioDevice, VirtioDeviceType, VirtioInterruptType, EPOLL_HELPER_EVENT_LAST,
8     VIRTIO_F_IOMMU_PLATFORM, VIRTIO_F_VERSION_1,
9 };
10 use crate::seccomp_filters::{get_seccomp_filter, Thread};
11 use crate::GuestMemoryMmap;
12 use crate::VirtioInterrupt;
13 use libc::EFD_NONBLOCK;
14 use seccomp::{SeccompAction, SeccompFilter};
15 use std::cmp;
16 use std::collections::VecDeque;
17 use std::io;
18 use std::io::Write;
19 use std::ops::DerefMut;
20 use std::os::unix::io::AsRawFd;
21 use std::result;
22 use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
23 use std::sync::{Arc, Barrier, Mutex};
24 use std::thread;
25 use versionize::{VersionMap, Versionize, VersionizeResult};
26 use versionize_derive::Versionize;
27 use vm_memory::{ByteValued, Bytes, GuestAddressSpace, GuestMemoryAtomic};
28 use vm_migration::VersionMapped;
29 use vm_migration::{Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable};
30 use vmm_sys_util::eventfd::EventFd;
31 
32 const QUEUE_SIZE: u16 = 256;
33 const NUM_QUEUES: usize = 2;
34 const QUEUE_SIZES: &[u16] = &[QUEUE_SIZE; NUM_QUEUES];
35 
36 // New descriptors are pending on the virtio queue.
37 const INPUT_QUEUE_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 1;
38 const OUTPUT_QUEUE_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 2;
39 // Some input from the VMM is ready to be injected into the VM.
40 const INPUT_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 3;
41 // Console configuration change event is triggered.
42 const CONFIG_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 4;
43 
44 //Console size feature bit
45 const VIRTIO_CONSOLE_F_SIZE: u64 = 0;
46 
47 #[derive(Copy, Clone, Debug, Default, Versionize)]
48 #[repr(C, packed)]
49 pub struct VirtioConsoleConfig {
50     cols: u16,
51     rows: u16,
52     max_nr_ports: u32,
53     emerg_wr: u32,
54 }
55 
56 // Safe because it only has data and has no implicit padding.
57 unsafe impl ByteValued for VirtioConsoleConfig {}
58 
59 struct ConsoleEpollHandler {
60     queues: Vec<Queue>,
61     mem: GuestMemoryAtomic<GuestMemoryMmap>,
62     interrupt_cb: Arc<dyn VirtioInterrupt>,
63     in_buffer: Arc<Mutex<VecDeque<u8>>>,
64     out: Arc<Mutex<Box<dyn io::Write + Send + Sync + 'static>>>,
65     input_queue_evt: EventFd,
66     output_queue_evt: EventFd,
67     input_evt: EventFd,
68     config_evt: EventFd,
69     kill_evt: EventFd,
70     pause_evt: EventFd,
71 }
72 
73 impl ConsoleEpollHandler {
74     /*
75      * Each port of virtio console device has one receive
76      * queue. One or more empty buffers are placed by the
77      * driver in the receive queue for incoming data. Here,
78      * we place the input data to these empty buffers.
79      */
80     fn process_input_queue(&mut self) -> bool {
81         let mut in_buffer = self.in_buffer.lock().unwrap();
82         let recv_queue = &mut self.queues[0]; //receiveq
83         let mut used_desc_heads = [(0, 0); QUEUE_SIZE as usize];
84         let mut used_count = 0;
85 
86         if in_buffer.is_empty() {
87             return false;
88         }
89 
90         let mem = self.mem.memory();
91         for avail_desc in recv_queue.iter(&mem) {
92             let len = cmp::min(avail_desc.len as u32, in_buffer.len() as u32);
93             let source_slice = in_buffer.drain(..len as usize).collect::<Vec<u8>>();
94             if let Err(e) = mem.write_slice(&source_slice[..], avail_desc.addr) {
95                 error!("Failed to write slice: {:?}", e);
96                 recv_queue.go_to_previous_position();
97                 break;
98             }
99 
100             used_desc_heads[used_count] = (avail_desc.index, len);
101             used_count += 1;
102 
103             if in_buffer.is_empty() {
104                 break;
105             }
106         }
107 
108         for &(desc_index, len) in &used_desc_heads[..used_count] {
109             recv_queue.add_used(&mem, desc_index, len);
110         }
111 
112         used_count > 0
113     }
114 
115     /*
116      * Each port of virtio console device has one transmit
117      * queue. For outgoing data, characters are placed in
118      * the transmit queue by the driver. Therefore, here
119      * we read data from the transmit queue and flush them
120      * to the referenced address.
121      */
122     fn process_output_queue(&mut self) -> bool {
123         let trans_queue = &mut self.queues[1]; //transmitq
124         let mut used_desc_heads = [(0, 0); QUEUE_SIZE as usize];
125         let mut used_count = 0;
126 
127         let mem = self.mem.memory();
128         for avail_desc in trans_queue.iter(&mem) {
129             let len;
130             let mut out = self.out.lock().unwrap();
131             let _ = mem.write_to(
132                 avail_desc.addr,
133                 &mut out.deref_mut(),
134                 avail_desc.len as usize,
135             );
136             let _ = out.flush();
137 
138             len = avail_desc.len;
139             used_desc_heads[used_count] = (avail_desc.index, len);
140             used_count += 1;
141         }
142 
143         for &(desc_index, len) in &used_desc_heads[..used_count] {
144             trans_queue.add_used(&mem, desc_index, len);
145         }
146         used_count > 0
147     }
148 
149     fn signal_used_queue(&self) -> result::Result<(), DeviceError> {
150         self.interrupt_cb
151             .trigger(&VirtioInterruptType::Queue, Some(&self.queues[0]))
152             .map_err(|e| {
153                 error!("Failed to signal used queue: {:?}", e);
154                 DeviceError::FailedSignalingUsedQueue(e)
155             })
156     }
157 
158     fn run(
159         &mut self,
160         paused: Arc<AtomicBool>,
161         paused_sync: Arc<Barrier>,
162     ) -> result::Result<(), EpollHelperError> {
163         let mut helper = EpollHelper::new(&self.kill_evt, &self.pause_evt)?;
164         helper.add_event(self.input_queue_evt.as_raw_fd(), INPUT_QUEUE_EVENT)?;
165         helper.add_event(self.output_queue_evt.as_raw_fd(), OUTPUT_QUEUE_EVENT)?;
166         helper.add_event(self.input_evt.as_raw_fd(), INPUT_EVENT)?;
167         helper.add_event(self.config_evt.as_raw_fd(), CONFIG_EVENT)?;
168         helper.run(paused, paused_sync, self)?;
169 
170         Ok(())
171     }
172 }
173 
174 impl EpollHelperHandler for ConsoleEpollHandler {
175     fn handle_event(&mut self, _helper: &mut EpollHelper, event: &epoll::Event) -> bool {
176         let ev_type = event.data as u16;
177         match ev_type {
178             INPUT_QUEUE_EVENT => {
179                 if let Err(e) = self.input_queue_evt.read() {
180                     error!("Failed to get queue event: {:?}", e);
181                     return true;
182                 } else if self.process_input_queue() {
183                     if let Err(e) = self.signal_used_queue() {
184                         error!("Failed to signal used queue: {:?}", e);
185                         return true;
186                     }
187                 }
188             }
189             OUTPUT_QUEUE_EVENT => {
190                 if let Err(e) = self.output_queue_evt.read() {
191                     error!("Failed to get queue event: {:?}", e);
192                     return true;
193                 } else {
194                     self.process_output_queue();
195                 }
196             }
197             INPUT_EVENT => {
198                 if let Err(e) = self.input_evt.read() {
199                     error!("Failed to get input event: {:?}", e);
200                     return true;
201                 } else if self.process_input_queue() {
202                     if let Err(e) = self.signal_used_queue() {
203                         error!("Failed to signal used queue: {:?}", e);
204                         return true;
205                     }
206                 }
207             }
208             CONFIG_EVENT => {
209                 if let Err(e) = self.config_evt.read() {
210                     error!("Failed to get config event: {:?}", e);
211                     return true;
212                 } else if let Err(e) = self
213                     .interrupt_cb
214                     .trigger(&VirtioInterruptType::Config, None)
215                 {
216                     error!("Failed to signal console driver: {:?}", e);
217                     return true;
218                 }
219             }
220             _ => {
221                 error!("Unknown event for virtio-console");
222                 return true;
223             }
224         }
225         false
226     }
227 }
228 
229 /// Input device.
230 pub struct ConsoleInput {
231     input_evt: EventFd,
232     config_evt: EventFd,
233     in_buffer: Arc<Mutex<VecDeque<u8>>>,
234     config: Arc<Mutex<VirtioConsoleConfig>>,
235     acked_features: AtomicU64,
236 }
237 
238 impl ConsoleInput {
239     pub fn queue_input_bytes(&self, input: &[u8]) {
240         let mut in_buffer = self.in_buffer.lock().unwrap();
241         in_buffer.extend(input);
242         let _ = self.input_evt.write(1);
243     }
244 
245     pub fn update_console_size(&self, cols: u16, rows: u16) {
246         if self
247             .acked_features
248             .fetch_and(1u64 << VIRTIO_CONSOLE_F_SIZE, Ordering::AcqRel)
249             != 0
250         {
251             self.config.lock().unwrap().update_console_size(cols, rows);
252             //Send the interrupt to the driver
253             let _ = self.config_evt.write(1);
254         }
255     }
256 }
257 
258 impl VirtioConsoleConfig {
259     pub fn new(cols: u16, rows: u16) -> Self {
260         VirtioConsoleConfig {
261             cols,
262             rows,
263             max_nr_ports: 1u32,
264             emerg_wr: 0u32,
265         }
266     }
267 
268     pub fn update_console_size(&mut self, cols: u16, rows: u16) {
269         self.cols = cols;
270         self.rows = rows;
271     }
272 }
273 
274 /// Virtio device for exposing console to the guest OS through virtio.
275 pub struct Console {
276     common: VirtioCommon,
277     id: String,
278     config: Arc<Mutex<VirtioConsoleConfig>>,
279     input: Arc<ConsoleInput>,
280     out: Arc<Mutex<Box<dyn io::Write + Send + Sync + 'static>>>,
281     seccomp_action: SeccompAction,
282 }
283 
284 #[derive(Versionize)]
285 pub struct ConsoleState {
286     avail_features: u64,
287     acked_features: u64,
288     config: VirtioConsoleConfig,
289     in_buffer: Vec<u8>,
290 }
291 
292 impl VersionMapped for ConsoleState {}
293 
294 impl Console {
295     /// Create a new virtio console device that gets random data from /dev/urandom.
296     pub fn new(
297         id: String,
298         out: Box<dyn io::Write + Send + Sync + 'static>,
299         cols: u16,
300         rows: u16,
301         iommu: bool,
302         seccomp_action: SeccompAction,
303     ) -> io::Result<(Console, Arc<ConsoleInput>)> {
304         let mut avail_features = 1u64 << VIRTIO_F_VERSION_1 | 1u64 << VIRTIO_CONSOLE_F_SIZE;
305 
306         if iommu {
307             avail_features |= 1u64 << VIRTIO_F_IOMMU_PLATFORM;
308         }
309 
310         let input_evt = EventFd::new(EFD_NONBLOCK).unwrap();
311         let config_evt = EventFd::new(EFD_NONBLOCK).unwrap();
312         let console_config = Arc::new(Mutex::new(VirtioConsoleConfig::new(cols, rows)));
313         let console_input = Arc::new(ConsoleInput {
314             input_evt,
315             config_evt,
316             in_buffer: Arc::new(Mutex::new(VecDeque::new())),
317             config: console_config.clone(),
318             acked_features: AtomicU64::new(0),
319         });
320 
321         Ok((
322             Console {
323                 common: VirtioCommon {
324                     device_type: VirtioDeviceType::Console as u32,
325                     queue_sizes: QUEUE_SIZES.to_vec(),
326                     avail_features,
327                     paused_sync: Some(Arc::new(Barrier::new(2))),
328                     min_queues: NUM_QUEUES as u16,
329                     ..Default::default()
330                 },
331                 id,
332                 config: console_config,
333                 input: console_input.clone(),
334                 out: Arc::new(Mutex::new(out)),
335                 seccomp_action,
336             },
337             console_input,
338         ))
339     }
340 
341     fn state(&self) -> ConsoleState {
342         ConsoleState {
343             avail_features: self.common.avail_features,
344             acked_features: self.common.acked_features,
345             config: *(self.config.lock().unwrap()),
346             in_buffer: self.input.in_buffer.lock().unwrap().clone().into(),
347         }
348     }
349 
350     fn set_state(&mut self, state: &ConsoleState) {
351         self.common.avail_features = state.avail_features;
352         self.common.acked_features = state.acked_features;
353         *(self.config.lock().unwrap()) = state.config;
354         *(self.input.in_buffer.lock().unwrap()) = state.in_buffer.clone().into();
355     }
356 }
357 
358 impl Drop for Console {
359     fn drop(&mut self) {
360         if let Some(kill_evt) = self.common.kill_evt.take() {
361             // Ignore the result because there is nothing we can do about it.
362             let _ = kill_evt.write(1);
363         }
364     }
365 }
366 
367 impl VirtioDevice for Console {
368     fn device_type(&self) -> u32 {
369         self.common.device_type
370     }
371 
372     fn queue_max_sizes(&self) -> &[u16] {
373         &self.common.queue_sizes
374     }
375 
376     fn features(&self) -> u64 {
377         self.common.avail_features
378     }
379 
380     fn ack_features(&mut self, value: u64) {
381         self.common.ack_features(value)
382     }
383 
384     fn read_config(&self, offset: u64, data: &mut [u8]) {
385         self.read_config_from_slice(self.config.lock().unwrap().as_slice(), offset, data);
386     }
387 
388     fn activate(
389         &mut self,
390         mem: GuestMemoryAtomic<GuestMemoryMmap>,
391         interrupt_cb: Arc<dyn VirtioInterrupt>,
392         queues: Vec<Queue>,
393         mut queue_evts: Vec<EventFd>,
394     ) -> ActivateResult {
395         self.common.activate(&queues, &queue_evts, &interrupt_cb)?;
396         self.input
397             .acked_features
398             .store(self.common.acked_features, Ordering::Relaxed);
399 
400         if self.common.feature_acked(VIRTIO_CONSOLE_F_SIZE) {
401             if let Err(e) = interrupt_cb.trigger(&VirtioInterruptType::Config, None) {
402                 error!("Failed to signal console driver: {:?}", e);
403             }
404         }
405 
406         let (kill_evt, pause_evt) = self.common.dup_eventfds();
407 
408         let mut handler = ConsoleEpollHandler {
409             queues,
410             mem,
411             interrupt_cb,
412             in_buffer: self.input.in_buffer.clone(),
413             out: self.out.clone(),
414             input_queue_evt: queue_evts.remove(0),
415             output_queue_evt: queue_evts.remove(0),
416             input_evt: self.input.input_evt.try_clone().unwrap(),
417             config_evt: self.input.config_evt.try_clone().unwrap(),
418             kill_evt,
419             pause_evt,
420         };
421 
422         let paused = self.common.paused.clone();
423         let paused_sync = self.common.paused_sync.clone();
424         let mut epoll_threads = Vec::new();
425         // Retrieve seccomp filter for virtio_console thread
426         let virtio_console_seccomp_filter =
427             get_seccomp_filter(&self.seccomp_action, Thread::VirtioConsole)
428                 .map_err(ActivateError::CreateSeccompFilter)?;
429         thread::Builder::new()
430             .name(self.id.clone())
431             .spawn(move || {
432                 if let Err(e) = SeccompFilter::apply(virtio_console_seccomp_filter) {
433                     error!("Error applying seccomp filter: {:?}", e);
434                 } else if let Err(e) = handler.run(paused, paused_sync.unwrap()) {
435                     error!("Error running worker: {:?}", e);
436                 }
437             })
438             .map(|thread| epoll_threads.push(thread))
439             .map_err(|e| {
440                 error!("failed to clone the virtio-console epoll thread: {}", e);
441                 ActivateError::BadActivate
442             })?;
443 
444         self.common.epoll_threads = Some(epoll_threads);
445 
446         event!("virtio-device", "activated", "id", &self.id);
447         Ok(())
448     }
449 
450     fn reset(&mut self) -> Option<Arc<dyn VirtioInterrupt>> {
451         let result = self.common.reset();
452         event!("virtio-device", "reset", "id", &self.id);
453         result
454     }
455 }
456 
457 impl Pausable for Console {
458     fn pause(&mut self) -> result::Result<(), MigratableError> {
459         self.common.pause()
460     }
461 
462     fn resume(&mut self) -> result::Result<(), MigratableError> {
463         self.common.resume()
464     }
465 }
466 
467 impl Snapshottable for Console {
468     fn id(&self) -> String {
469         self.id.clone()
470     }
471 
472     fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
473         Snapshot::new_from_versioned_state(&self.id, &self.state())
474     }
475 
476     fn restore(&mut self, snapshot: Snapshot) -> std::result::Result<(), MigratableError> {
477         self.set_state(&snapshot.to_versioned_state(&self.id)?);
478         Ok(())
479     }
480 }
481 impl Transportable for Console {}
482 impl Migratable for Console {}
483