1 // Copyright 2019 Intel Corporation. All Rights Reserved.
2 // SPDX-License-Identifier: Apache-2.0
3
4 use std::collections::VecDeque;
5 use std::fs::File;
6 use std::io::{Read, Write};
7 use std::os::unix::io::AsRawFd;
8 use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
9 use std::sync::{Arc, Barrier, Mutex};
10 use std::{cmp, io, result};
11
12 use anyhow::anyhow;
13 use libc::{EFD_NONBLOCK, TIOCGWINSZ};
14 use seccompiler::SeccompAction;
15 use serde::{Deserialize, Serialize};
16 use serial_buffer::SerialBuffer;
17 use thiserror::Error;
18 use virtio_queue::{Queue, QueueT};
19 use vm_memory::{ByteValued, Bytes, GuestAddressSpace, GuestMemory, GuestMemoryAtomic};
20 use vm_migration::{Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable};
21 use vm_virtio::{AccessPlatform, Translatable};
22 use vmm_sys_util::eventfd::EventFd;
23
24 use super::{
25 ActivateResult, EpollHelper, EpollHelperError, EpollHelperHandler, Error as DeviceError,
26 VirtioCommon, VirtioDevice, VirtioDeviceType, VirtioInterruptType, EPOLL_HELPER_EVENT_LAST,
27 VIRTIO_F_IOMMU_PLATFORM, VIRTIO_F_VERSION_1,
28 };
29 use crate::seccomp_filters::Thread;
30 use crate::thread_helper::spawn_virtio_thread;
31 use crate::{GuestMemoryMmap, VirtioInterrupt};
32
33 const QUEUE_SIZE: u16 = 256;
34 const NUM_QUEUES: usize = 2;
35 const QUEUE_SIZES: &[u16] = &[QUEUE_SIZE; NUM_QUEUES];
36
37 // New descriptors are pending on the virtio queue.
38 const INPUT_QUEUE_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 1;
39 const OUTPUT_QUEUE_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 2;
40 // Console configuration change event is triggered.
41 const CONFIG_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 3;
42 // File written to (input ready)
43 const FILE_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 4;
44 // Console resized
45 const RESIZE_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 5;
46
47 //Console size feature bit
48 const VIRTIO_CONSOLE_F_SIZE: u64 = 0;
49
50 #[derive(Error, Debug)]
51 enum Error {
52 #[error("Descriptor chain too short")]
53 DescriptorChainTooShort,
54 #[error("Failed to read from guest memory")]
55 GuestMemoryRead(#[source] vm_memory::guest_memory::Error),
56 #[error("Failed to write to guest memory")]
57 GuestMemoryWrite(#[source] vm_memory::guest_memory::Error),
58 #[error("Failed to write_all output")]
59 OutputWriteAll(#[source] io::Error),
60 #[error("Failed to flush output")]
61 OutputFlush(#[source] io::Error),
62 #[error("Failed to add used index")]
63 QueueAddUsed(#[source] virtio_queue::Error),
64 }
65
66 #[derive(Copy, Clone, Debug, Serialize, Deserialize)]
67 #[repr(C, packed)]
68 pub struct VirtioConsoleConfig {
69 cols: u16,
70 rows: u16,
71 max_nr_ports: u32,
72 emerg_wr: u32,
73 }
74
75 impl Default for VirtioConsoleConfig {
default() -> Self76 fn default() -> Self {
77 VirtioConsoleConfig {
78 cols: 0,
79 rows: 0,
80 max_nr_ports: 1,
81 emerg_wr: 0,
82 }
83 }
84 }
85
86 // SAFETY: it only has data and has no implicit padding.
87 unsafe impl ByteValued for VirtioConsoleConfig {}
88
89 struct ConsoleEpollHandler {
90 mem: GuestMemoryAtomic<GuestMemoryMmap>,
91 input_queue: Queue,
92 output_queue: Queue,
93 interrupt_cb: Arc<dyn VirtioInterrupt>,
94 in_buffer: Arc<Mutex<VecDeque<u8>>>,
95 resizer: Arc<ConsoleResizer>,
96 endpoint: Endpoint,
97 input_queue_evt: EventFd,
98 output_queue_evt: EventFd,
99 config_evt: EventFd,
100 resize_pipe: Option<File>,
101 kill_evt: EventFd,
102 pause_evt: EventFd,
103 access_platform: Option<Arc<dyn AccessPlatform>>,
104 out: Option<Box<dyn Write + Send>>,
105 write_out: Option<Arc<AtomicBool>>,
106 file_event_registered: bool,
107 }
108
109 #[derive(Clone)]
110 pub enum Endpoint {
111 File(Arc<File>),
112 FilePair(Arc<File>, Arc<File>),
113 PtyPair(Arc<File>, Arc<File>),
114 Null,
115 }
116
117 impl Endpoint {
out_file(&self) -> Option<&File>118 fn out_file(&self) -> Option<&File> {
119 match self {
120 Self::File(f) => Some(f),
121 Self::FilePair(f, _) => Some(f),
122 Self::PtyPair(f, _) => Some(f),
123 Self::Null => None,
124 }
125 }
126
in_file(&self) -> Option<&File>127 fn in_file(&self) -> Option<&File> {
128 match self {
129 Self::File(_) => None,
130 Self::FilePair(_, f) => Some(f),
131 Self::PtyPair(_, f) => Some(f),
132 Self::Null => None,
133 }
134 }
135
is_pty(&self) -> bool136 fn is_pty(&self) -> bool {
137 matches!(self, Self::PtyPair(_, _))
138 }
139 }
140
141 impl ConsoleEpollHandler {
142 #[allow(clippy::too_many_arguments)]
new( mem: GuestMemoryAtomic<GuestMemoryMmap>, input_queue: Queue, output_queue: Queue, interrupt_cb: Arc<dyn VirtioInterrupt>, in_buffer: Arc<Mutex<VecDeque<u8>>>, resizer: Arc<ConsoleResizer>, endpoint: Endpoint, input_queue_evt: EventFd, output_queue_evt: EventFd, config_evt: EventFd, resize_pipe: Option<File>, kill_evt: EventFd, pause_evt: EventFd, access_platform: Option<Arc<dyn AccessPlatform>>, ) -> Self143 fn new(
144 mem: GuestMemoryAtomic<GuestMemoryMmap>,
145 input_queue: Queue,
146 output_queue: Queue,
147 interrupt_cb: Arc<dyn VirtioInterrupt>,
148 in_buffer: Arc<Mutex<VecDeque<u8>>>,
149 resizer: Arc<ConsoleResizer>,
150 endpoint: Endpoint,
151 input_queue_evt: EventFd,
152 output_queue_evt: EventFd,
153 config_evt: EventFd,
154 resize_pipe: Option<File>,
155 kill_evt: EventFd,
156 pause_evt: EventFd,
157 access_platform: Option<Arc<dyn AccessPlatform>>,
158 ) -> Self {
159 let out_file = endpoint.out_file();
160 let (out, write_out) = if let Some(out_file) = out_file {
161 let writer = out_file.try_clone().unwrap();
162 if endpoint.is_pty() {
163 let pty_write_out = Arc::new(AtomicBool::new(false));
164 let write_out = Some(pty_write_out.clone());
165 let buffer = SerialBuffer::new(Box::new(writer), pty_write_out);
166 (Some(Box::new(buffer) as Box<dyn Write + Send>), write_out)
167 } else {
168 (Some(Box::new(writer) as Box<dyn Write + Send>), None)
169 }
170 } else {
171 (None, None)
172 };
173
174 ConsoleEpollHandler {
175 mem,
176 input_queue,
177 output_queue,
178 interrupt_cb,
179 in_buffer,
180 resizer,
181 endpoint,
182 input_queue_evt,
183 output_queue_evt,
184 config_evt,
185 resize_pipe,
186 kill_evt,
187 pause_evt,
188 access_platform,
189 out,
190 write_out,
191 file_event_registered: false,
192 }
193 }
194
195 /*
196 * Each port of virtio console device has one receive
197 * queue. One or more empty buffers are placed by the
198 * driver in the receive queue for incoming data. Here,
199 * we place the input data to these empty buffers.
200 */
process_input_queue(&mut self) -> Result<bool, Error>201 fn process_input_queue(&mut self) -> Result<bool, Error> {
202 let mut in_buffer = self.in_buffer.lock().unwrap();
203 let recv_queue = &mut self.input_queue; //receiveq
204 let mut used_descs = false;
205
206 if in_buffer.is_empty() {
207 return Ok(false);
208 }
209
210 while let Some(mut desc_chain) = recv_queue.pop_descriptor_chain(self.mem.memory()) {
211 let desc = desc_chain.next().ok_or(Error::DescriptorChainTooShort)?;
212 let len = cmp::min(desc.len(), in_buffer.len() as u32);
213 let source_slice = in_buffer.drain(..len as usize).collect::<Vec<u8>>();
214
215 desc_chain
216 .memory()
217 .write_slice(
218 &source_slice[..],
219 desc.addr()
220 .translate_gva(self.access_platform.as_ref(), desc.len() as usize),
221 )
222 .map_err(Error::GuestMemoryWrite)?;
223
224 recv_queue
225 .add_used(desc_chain.memory(), desc_chain.head_index(), len)
226 .map_err(Error::QueueAddUsed)?;
227 used_descs = true;
228
229 if in_buffer.is_empty() {
230 break;
231 }
232 }
233
234 Ok(used_descs)
235 }
236
237 /*
238 * Each port of virtio console device has one transmit
239 * queue. For outgoing data, characters are placed in
240 * the transmit queue by the driver. Therefore, here
241 * we read data from the transmit queue and flush them
242 * to the referenced address.
243 */
process_output_queue(&mut self) -> Result<bool, Error>244 fn process_output_queue(&mut self) -> Result<bool, Error> {
245 let trans_queue = &mut self.output_queue; //transmitq
246 let mut used_descs = false;
247
248 while let Some(mut desc_chain) = trans_queue.pop_descriptor_chain(self.mem.memory()) {
249 let desc = desc_chain.next().ok_or(Error::DescriptorChainTooShort)?;
250 if let Some(out) = &mut self.out {
251 let mut buf: Vec<u8> = Vec::new();
252 desc_chain
253 .memory()
254 .write_volatile_to(
255 desc.addr()
256 .translate_gva(self.access_platform.as_ref(), desc.len() as usize),
257 &mut buf,
258 desc.len() as usize,
259 )
260 .map_err(Error::GuestMemoryRead)?;
261
262 out.write_all(&buf).map_err(Error::OutputWriteAll)?;
263 out.flush().map_err(Error::OutputFlush)?;
264 }
265 trans_queue
266 .add_used(desc_chain.memory(), desc_chain.head_index(), desc.len())
267 .map_err(Error::QueueAddUsed)?;
268 used_descs = true;
269 }
270
271 Ok(used_descs)
272 }
273
signal_used_queue(&self, queue_index: u16) -> result::Result<(), DeviceError>274 fn signal_used_queue(&self, queue_index: u16) -> result::Result<(), DeviceError> {
275 self.interrupt_cb
276 .trigger(VirtioInterruptType::Queue(queue_index))
277 .map_err(|e| {
278 error!("Failed to signal used queue: {:?}", e);
279 DeviceError::FailedSignalingUsedQueue(e)
280 })
281 }
282
run( &mut self, paused: Arc<AtomicBool>, paused_sync: Arc<Barrier>, ) -> result::Result<(), EpollHelperError>283 fn run(
284 &mut self,
285 paused: Arc<AtomicBool>,
286 paused_sync: Arc<Barrier>,
287 ) -> result::Result<(), EpollHelperError> {
288 let mut helper = EpollHelper::new(&self.kill_evt, &self.pause_evt)?;
289 helper.add_event(self.input_queue_evt.as_raw_fd(), INPUT_QUEUE_EVENT)?;
290 helper.add_event(self.output_queue_evt.as_raw_fd(), OUTPUT_QUEUE_EVENT)?;
291 helper.add_event(self.config_evt.as_raw_fd(), CONFIG_EVENT)?;
292 if let Some(resize_pipe) = self.resize_pipe.as_ref() {
293 helper.add_event(resize_pipe.as_raw_fd(), RESIZE_EVENT)?;
294 }
295 if let Some(in_file) = self.endpoint.in_file() {
296 let mut events = epoll::Events::EPOLLIN;
297 if self.endpoint.is_pty() {
298 events |= epoll::Events::EPOLLONESHOT;
299 }
300 helper.add_event_custom(in_file.as_raw_fd(), FILE_EVENT, events)?;
301 self.file_event_registered = true;
302 }
303
304 // In case of PTY, we want to be able to detect a connection on the
305 // other end of the PTY. This is done by detecting there's no event
306 // triggered on the epoll, which is the reason why we want the
307 // epoll_wait() function to return after the timeout expired.
308 // In case of TTY, we don't expect to detect such behavior, which is
309 // why we can afford to block until an actual event is triggered.
310 let (timeout, enable_event_list) = if self.endpoint.is_pty() {
311 (500, true)
312 } else {
313 (-1, false)
314 };
315 helper.run_with_timeout(paused, paused_sync, self, timeout, enable_event_list)?;
316
317 Ok(())
318 }
319
320 // This function should be called when the other end of the PTY is
321 // connected. It verifies if this is the first time it's been invoked
322 // after the connection happened, and if that's the case it flushes
323 // all output from the console to the PTY. Otherwise, it's a no-op.
trigger_pty_flush(&mut self) -> result::Result<(), anyhow::Error>324 fn trigger_pty_flush(&mut self) -> result::Result<(), anyhow::Error> {
325 if let (Some(pty_write_out), Some(out)) = (&self.write_out, &mut self.out) {
326 if pty_write_out.load(Ordering::Acquire) {
327 return Ok(());
328 }
329 pty_write_out.store(true, Ordering::Release);
330 out.flush()
331 .map_err(|e| anyhow!("Failed to flush PTY: {:?}", e))
332 } else {
333 Ok(())
334 }
335 }
336
register_file_event( &mut self, helper: &mut EpollHelper, ) -> result::Result<(), EpollHelperError>337 fn register_file_event(
338 &mut self,
339 helper: &mut EpollHelper,
340 ) -> result::Result<(), EpollHelperError> {
341 if self.file_event_registered {
342 return Ok(());
343 }
344
345 // Re-arm the file event.
346 helper.mod_event_custom(
347 self.endpoint.in_file().unwrap().as_raw_fd(),
348 FILE_EVENT,
349 epoll::Events::EPOLLIN | epoll::Events::EPOLLONESHOT,
350 )?;
351 self.file_event_registered = true;
352
353 Ok(())
354 }
355 }
356
357 impl EpollHelperHandler for ConsoleEpollHandler {
handle_event( &mut self, helper: &mut EpollHelper, event: &epoll::Event, ) -> result::Result<(), EpollHelperError>358 fn handle_event(
359 &mut self,
360 helper: &mut EpollHelper,
361 event: &epoll::Event,
362 ) -> result::Result<(), EpollHelperError> {
363 let ev_type = event.data as u16;
364
365 match ev_type {
366 INPUT_QUEUE_EVENT => {
367 self.input_queue_evt.read().map_err(|e| {
368 EpollHelperError::HandleEvent(anyhow!("Failed to get queue event: {:?}", e))
369 })?;
370 let needs_notification = self.process_input_queue().map_err(|e| {
371 EpollHelperError::HandleEvent(anyhow!(
372 "Failed to process input queue : {:?}",
373 e
374 ))
375 })?;
376 if needs_notification {
377 self.signal_used_queue(0).map_err(|e| {
378 EpollHelperError::HandleEvent(anyhow!(
379 "Failed to signal used queue: {:?}",
380 e
381 ))
382 })?;
383 }
384 }
385 OUTPUT_QUEUE_EVENT => {
386 self.output_queue_evt.read().map_err(|e| {
387 EpollHelperError::HandleEvent(anyhow!("Failed to get queue event: {:?}", e))
388 })?;
389 let needs_notification = self.process_output_queue().map_err(|e| {
390 EpollHelperError::HandleEvent(anyhow!(
391 "Failed to process output queue : {:?}",
392 e
393 ))
394 })?;
395 if needs_notification {
396 self.signal_used_queue(1).map_err(|e| {
397 EpollHelperError::HandleEvent(anyhow!(
398 "Failed to signal used queue: {:?}",
399 e
400 ))
401 })?;
402 }
403 }
404 CONFIG_EVENT => {
405 self.config_evt.read().map_err(|e| {
406 EpollHelperError::HandleEvent(anyhow!("Failed to get config event: {:?}", e))
407 })?;
408 self.interrupt_cb
409 .trigger(VirtioInterruptType::Config)
410 .map_err(|e| {
411 EpollHelperError::HandleEvent(anyhow!(
412 "Failed to signal console driver: {:?}",
413 e
414 ))
415 })?;
416 }
417 RESIZE_EVENT => {
418 self.resize_pipe
419 .as_ref()
420 .unwrap()
421 .read_exact(&mut [0])
422 .map_err(|e| {
423 EpollHelperError::HandleEvent(anyhow!(
424 "Failed to get resize event: {:?}",
425 e
426 ))
427 })?;
428 self.resizer.update_console_size();
429 }
430 FILE_EVENT => {
431 if event.events & libc::EPOLLIN as u32 != 0 {
432 let mut input = [0u8; 64];
433 if let Some(ref mut in_file) = self.endpoint.in_file() {
434 if let Ok(count) = in_file.read(&mut input) {
435 let mut in_buffer = self.in_buffer.lock().unwrap();
436 in_buffer.extend(&input[..count]);
437 }
438
439 let needs_notification = self.process_input_queue().map_err(|e| {
440 EpollHelperError::HandleEvent(anyhow!(
441 "Failed to process input queue : {:?}",
442 e
443 ))
444 })?;
445 if needs_notification {
446 self.signal_used_queue(0).map_err(|e| {
447 EpollHelperError::HandleEvent(anyhow!(
448 "Failed to signal used queue: {:?}",
449 e
450 ))
451 })?;
452 }
453 }
454 }
455 if self.endpoint.is_pty() {
456 self.file_event_registered = false;
457 if event.events & libc::EPOLLHUP as u32 != 0 {
458 if let Some(pty_write_out) = &self.write_out {
459 if pty_write_out.load(Ordering::Acquire) {
460 pty_write_out.store(false, Ordering::Release);
461 }
462 }
463 } else {
464 // If the EPOLLHUP flag is not up on the associated event, we
465 // can assume the other end of the PTY is connected and therefore
466 // we can flush the output of the serial to it.
467 self.trigger_pty_flush()
468 .map_err(EpollHelperError::HandleTimeout)?;
469
470 self.register_file_event(helper)?;
471 }
472 }
473 }
474 _ => {
475 return Err(EpollHelperError::HandleEvent(anyhow!(
476 "Unknown event for virtio-console"
477 )));
478 }
479 }
480 Ok(())
481 }
482
483 // This function will be invoked whenever the timeout is reached before
484 // any other event was triggered while waiting for the epoll.
handle_timeout(&mut self, helper: &mut EpollHelper) -> Result<(), EpollHelperError>485 fn handle_timeout(&mut self, helper: &mut EpollHelper) -> Result<(), EpollHelperError> {
486 if !self.endpoint.is_pty() {
487 return Ok(());
488 }
489
490 if self.file_event_registered {
491 // This very specific case happens when the console is connected
492 // to a PTY. We know EPOLLHUP is always present when there's nothing
493 // connected at the other end of the PTY. That's why getting no event
494 // means we can flush the output of the console through the PTY.
495 self.trigger_pty_flush()
496 .map_err(EpollHelperError::HandleTimeout)?;
497 }
498
499 // Every time we hit the timeout, let's register the FILE_EVENT to give
500 // us a chance to catch a possible event that might have been triggered.
501 self.register_file_event(helper)
502 }
503
504 // This function returns the full list of events found on the epoll before
505 // iterating through it calling handle_event(). It allows the detection of
506 // the PTY connection even when the timeout is not being triggered, which
507 // happens when there are other events preventing the timeout from being
508 // reached. This is an additional way of detecting a PTY connection.
event_list( &mut self, helper: &mut EpollHelper, events: &[epoll::Event], ) -> Result<(), EpollHelperError>509 fn event_list(
510 &mut self,
511 helper: &mut EpollHelper,
512 events: &[epoll::Event],
513 ) -> Result<(), EpollHelperError> {
514 if self.file_event_registered {
515 for event in events {
516 if event.data as u16 == FILE_EVENT && (event.events & libc::EPOLLHUP as u32) != 0 {
517 return Ok(());
518 }
519 }
520
521 // This very specific case happens when the console is connected
522 // to a PTY. We know EPOLLHUP is always present when there's nothing
523 // connected at the other end of the PTY. That's why getting no event
524 // means we can flush the output of the console through the PTY.
525 self.trigger_pty_flush()
526 .map_err(EpollHelperError::HandleTimeout)?;
527 }
528
529 self.register_file_event(helper)
530 }
531 }
532
533 /// Resize handler
534 pub struct ConsoleResizer {
535 config_evt: EventFd,
536 tty: Option<File>,
537 config: Arc<Mutex<VirtioConsoleConfig>>,
538 acked_features: AtomicU64,
539 }
540
541 impl ConsoleResizer {
update_console_size(&self)542 pub fn update_console_size(&self) {
543 if let Some(tty) = self.tty.as_ref() {
544 let (cols, rows) = get_win_size(tty);
545 self.config.lock().unwrap().update_console_size(cols, rows);
546 if self
547 .acked_features
548 .fetch_and(1u64 << VIRTIO_CONSOLE_F_SIZE, Ordering::AcqRel)
549 != 0
550 {
551 // Send the interrupt to the driver
552 let _ = self.config_evt.write(1);
553 }
554 }
555 }
556 }
557
558 impl VirtioConsoleConfig {
update_console_size(&mut self, cols: u16, rows: u16)559 pub fn update_console_size(&mut self, cols: u16, rows: u16) {
560 self.cols = cols;
561 self.rows = rows;
562 }
563 }
564
565 /// Virtio device for exposing console to the guest OS through virtio.
566 pub struct Console {
567 common: VirtioCommon,
568 id: String,
569 config: Arc<Mutex<VirtioConsoleConfig>>,
570 resizer: Arc<ConsoleResizer>,
571 resize_pipe: Option<File>,
572 endpoint: Endpoint,
573 seccomp_action: SeccompAction,
574 in_buffer: Arc<Mutex<VecDeque<u8>>>,
575 exit_evt: EventFd,
576 }
577
578 #[derive(Serialize, Deserialize)]
579 pub struct ConsoleState {
580 avail_features: u64,
581 acked_features: u64,
582 config: VirtioConsoleConfig,
583 in_buffer: Vec<u8>,
584 }
585
get_win_size(tty: &dyn AsRawFd) -> (u16, u16)586 fn get_win_size(tty: &dyn AsRawFd) -> (u16, u16) {
587 #[repr(C)]
588 #[derive(Default)]
589 struct WindowSize {
590 rows: u16,
591 cols: u16,
592 xpixel: u16,
593 ypixel: u16,
594 }
595 let mut ws: WindowSize = WindowSize::default();
596
597 // SAFETY: FFI call with correct arguments
598 unsafe {
599 libc::ioctl(tty.as_raw_fd(), TIOCGWINSZ, &mut ws);
600 }
601
602 (ws.cols, ws.rows)
603 }
604
605 impl Console {
606 /// Create a new virtio console device
new( id: String, endpoint: Endpoint, resize_pipe: Option<File>, iommu: bool, seccomp_action: SeccompAction, exit_evt: EventFd, state: Option<ConsoleState>, ) -> io::Result<(Console, Arc<ConsoleResizer>)>607 pub fn new(
608 id: String,
609 endpoint: Endpoint,
610 resize_pipe: Option<File>,
611 iommu: bool,
612 seccomp_action: SeccompAction,
613 exit_evt: EventFd,
614 state: Option<ConsoleState>,
615 ) -> io::Result<(Console, Arc<ConsoleResizer>)> {
616 let (avail_features, acked_features, config, in_buffer, paused) = if let Some(state) = state
617 {
618 info!("Restoring virtio-console {}", id);
619 (
620 state.avail_features,
621 state.acked_features,
622 state.config,
623 state.in_buffer.into(),
624 true,
625 )
626 } else {
627 let mut avail_features = (1u64 << VIRTIO_F_VERSION_1) | (1u64 << VIRTIO_CONSOLE_F_SIZE);
628 if iommu {
629 avail_features |= 1u64 << VIRTIO_F_IOMMU_PLATFORM;
630 }
631
632 (
633 avail_features,
634 0,
635 VirtioConsoleConfig::default(),
636 VecDeque::new(),
637 false,
638 )
639 };
640
641 let config_evt = EventFd::new(EFD_NONBLOCK).unwrap();
642 let console_config = Arc::new(Mutex::new(config));
643 let resizer = Arc::new(ConsoleResizer {
644 config_evt,
645 config: console_config.clone(),
646 tty: endpoint.out_file().as_ref().map(|t| t.try_clone().unwrap()),
647 acked_features: AtomicU64::new(acked_features),
648 });
649
650 resizer.update_console_size();
651
652 Ok((
653 Console {
654 common: VirtioCommon {
655 device_type: VirtioDeviceType::Console as u32,
656 queue_sizes: QUEUE_SIZES.to_vec(),
657 avail_features,
658 acked_features,
659 paused_sync: Some(Arc::new(Barrier::new(2))),
660 min_queues: NUM_QUEUES as u16,
661 paused: Arc::new(AtomicBool::new(paused)),
662 ..Default::default()
663 },
664 id,
665 config: console_config,
666 resizer: resizer.clone(),
667 resize_pipe,
668 endpoint,
669 seccomp_action,
670 in_buffer: Arc::new(Mutex::new(in_buffer)),
671 exit_evt,
672 },
673 resizer,
674 ))
675 }
676
state(&self) -> ConsoleState677 fn state(&self) -> ConsoleState {
678 ConsoleState {
679 avail_features: self.common.avail_features,
680 acked_features: self.common.acked_features,
681 config: *(self.config.lock().unwrap()),
682 in_buffer: self.in_buffer.lock().unwrap().clone().into(),
683 }
684 }
685
686 #[cfg(fuzzing)]
wait_for_epoll_threads(&mut self)687 pub fn wait_for_epoll_threads(&mut self) {
688 self.common.wait_for_epoll_threads();
689 }
690 }
691
692 impl Drop for Console {
drop(&mut self)693 fn drop(&mut self) {
694 if let Some(kill_evt) = self.common.kill_evt.take() {
695 // Ignore the result because there is nothing we can do about it.
696 let _ = kill_evt.write(1);
697 }
698 self.common.wait_for_epoll_threads();
699 }
700 }
701
702 impl VirtioDevice for Console {
device_type(&self) -> u32703 fn device_type(&self) -> u32 {
704 self.common.device_type
705 }
706
queue_max_sizes(&self) -> &[u16]707 fn queue_max_sizes(&self) -> &[u16] {
708 &self.common.queue_sizes
709 }
710
features(&self) -> u64711 fn features(&self) -> u64 {
712 self.common.avail_features
713 }
714
ack_features(&mut self, value: u64)715 fn ack_features(&mut self, value: u64) {
716 self.common.ack_features(value)
717 }
718
read_config(&self, offset: u64, data: &mut [u8])719 fn read_config(&self, offset: u64, data: &mut [u8]) {
720 self.read_config_from_slice(self.config.lock().unwrap().as_slice(), offset, data);
721 }
722
activate( &mut self, mem: GuestMemoryAtomic<GuestMemoryMmap>, interrupt_cb: Arc<dyn VirtioInterrupt>, mut queues: Vec<(usize, Queue, EventFd)>, ) -> ActivateResult723 fn activate(
724 &mut self,
725 mem: GuestMemoryAtomic<GuestMemoryMmap>,
726 interrupt_cb: Arc<dyn VirtioInterrupt>,
727 mut queues: Vec<(usize, Queue, EventFd)>,
728 ) -> ActivateResult {
729 self.common.activate(&queues, &interrupt_cb)?;
730 self.resizer
731 .acked_features
732 .store(self.common.acked_features, Ordering::Relaxed);
733
734 if self.common.feature_acked(VIRTIO_CONSOLE_F_SIZE) {
735 if let Err(e) = interrupt_cb.trigger(VirtioInterruptType::Config) {
736 error!("Failed to signal console driver: {:?}", e);
737 }
738 }
739
740 let (kill_evt, pause_evt) = self.common.dup_eventfds();
741
742 let (_, input_queue, input_queue_evt) = queues.remove(0);
743 let (_, output_queue, output_queue_evt) = queues.remove(0);
744
745 let mut handler = ConsoleEpollHandler::new(
746 mem,
747 input_queue,
748 output_queue,
749 interrupt_cb,
750 self.in_buffer.clone(),
751 Arc::clone(&self.resizer),
752 self.endpoint.clone(),
753 input_queue_evt,
754 output_queue_evt,
755 self.resizer.config_evt.try_clone().unwrap(),
756 self.resize_pipe.as_ref().map(|p| p.try_clone().unwrap()),
757 kill_evt,
758 pause_evt,
759 self.common.access_platform.clone(),
760 );
761
762 let paused = self.common.paused.clone();
763 let paused_sync = self.common.paused_sync.clone();
764 let mut epoll_threads = Vec::new();
765
766 spawn_virtio_thread(
767 &self.id,
768 &self.seccomp_action,
769 Thread::VirtioConsole,
770 &mut epoll_threads,
771 &self.exit_evt,
772 move || handler.run(paused, paused_sync.unwrap()),
773 )?;
774
775 self.common.epoll_threads = Some(epoll_threads);
776
777 event!("virtio-device", "activated", "id", &self.id);
778 Ok(())
779 }
780
reset(&mut self) -> Option<Arc<dyn VirtioInterrupt>>781 fn reset(&mut self) -> Option<Arc<dyn VirtioInterrupt>> {
782 let result = self.common.reset();
783 event!("virtio-device", "reset", "id", &self.id);
784 result
785 }
786
set_access_platform(&mut self, access_platform: Arc<dyn AccessPlatform>)787 fn set_access_platform(&mut self, access_platform: Arc<dyn AccessPlatform>) {
788 self.common.set_access_platform(access_platform)
789 }
790 }
791
792 impl Pausable for Console {
pause(&mut self) -> result::Result<(), MigratableError>793 fn pause(&mut self) -> result::Result<(), MigratableError> {
794 self.common.pause()
795 }
796
resume(&mut self) -> result::Result<(), MigratableError>797 fn resume(&mut self) -> result::Result<(), MigratableError> {
798 self.common.resume()
799 }
800 }
801
802 impl Snapshottable for Console {
id(&self) -> String803 fn id(&self) -> String {
804 self.id.clone()
805 }
806
snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError>807 fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
808 Snapshot::new_from_state(&self.state())
809 }
810 }
811 impl Transportable for Console {}
812 impl Migratable for Console {}
813