• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2018 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 use std::collections::BTreeMap;
6 use std::io;
7 use std::io::Write;
8 use std::mem;
9 use std::result;
10 
11 use anyhow::anyhow;
12 use anyhow::Context;
13 use base::error;
14 use base::warn;
15 use base::Error as SysError;
16 use base::Event;
17 use base::EventToken;
18 use base::RawDescriptor;
19 use base::WaitContext;
20 use base::WorkerThread;
21 use remain::sorted;
22 use thiserror::Error;
23 use vm_memory::GuestMemory;
24 
25 use super::copy_config;
26 use super::queue::Queue;
27 use super::DeviceType;
28 use super::Interrupt;
29 use super::VirtioDevice;
30 
31 const QUEUE_SIZE: u16 = 128;
32 const QUEUE_SIZES: &[u16] = &[QUEUE_SIZE];
33 
34 // The only virtio_9p feature.
35 const VIRTIO_9P_MOUNT_TAG: u8 = 0;
36 
37 /// Errors that occur during operation of a virtio 9P device.
38 #[sorted]
39 #[derive(Error, Debug)]
40 pub enum P9Error {
41     /// Failed to create a 9p server.
42     #[error("failed to create 9p server: {0}")]
43     CreateServer(io::Error),
44     /// Creating WaitContext failed.
45     #[error("failed to create WaitContext: {0}")]
46     CreateWaitContext(SysError),
47     /// An internal I/O error occurred.
48     #[error("P9 internal server error: {0}")]
49     Internal(io::Error),
50     /// A request is missing readable descriptors.
51     #[error("request does not have any readable descriptors")]
52     NoReadableDescriptors,
53     /// A request is missing writable descriptors.
54     #[error("request does not have any writable descriptors")]
55     NoWritableDescriptors,
56     /// Error while reading from the virtio queue's Event.
57     #[error("failed to read from virtio queue Event: {0}")]
58     ReadQueueEvent(SysError),
59     /// Failed to signal the virio used queue.
60     #[error("failed to signal used queue: {0}")]
61     SignalUsedQueue(SysError),
62     /// The tag for the 9P device was too large to fit in the config space.
63     #[error("P9 device tag is too long: len = {0}, max = {max}", max = u16::MAX)]
64     TagTooLong(usize),
65     /// Error while polling for events.
66     #[error("failed to wait for events: {0}")]
67     WaitError(SysError),
68 }
69 
70 pub type P9Result<T> = result::Result<T, P9Error>;
71 
72 struct Worker {
73     queue: Queue,
74     server: p9::Server,
75 }
76 
77 impl Worker {
process_queue(&mut self) -> P9Result<()>78     fn process_queue(&mut self) -> P9Result<()> {
79         while let Some(mut avail_desc) = self.queue.pop() {
80             self.server
81                 .handle_message(&mut avail_desc.reader, &mut avail_desc.writer)
82                 .map_err(P9Error::Internal)?;
83 
84             let len = avail_desc.writer.bytes_written() as u32;
85 
86             self.queue.add_used(avail_desc, len);
87         }
88         self.queue.trigger_interrupt();
89 
90         Ok(())
91     }
92 
run(&mut self, kill_evt: Event) -> P9Result<()>93     fn run(&mut self, kill_evt: Event) -> P9Result<()> {
94         #[derive(EventToken)]
95         enum Token {
96             // A request is ready on the queue.
97             QueueReady,
98             // The parent thread requested an exit.
99             Kill,
100         }
101 
102         let wait_ctx: WaitContext<Token> = WaitContext::build_with(&[
103             (self.queue.event(), Token::QueueReady),
104             (&kill_evt, Token::Kill),
105         ])
106         .map_err(P9Error::CreateWaitContext)?;
107 
108         loop {
109             let events = wait_ctx.wait().map_err(P9Error::WaitError)?;
110             for event in events.iter().filter(|e| e.is_readable) {
111                 match event.token {
112                     Token::QueueReady => {
113                         self.queue.event().wait().map_err(P9Error::ReadQueueEvent)?;
114                         self.process_queue()?;
115                     }
116                     Token::Kill => return Ok(()),
117                 }
118             }
119         }
120     }
121 }
122 
123 /// Virtio device for sharing specific directories on the host system with the guest VM.
124 pub struct P9 {
125     config: Vec<u8>,
126     server: Option<p9::Server>,
127     avail_features: u64,
128     acked_features: u64,
129     worker: Option<WorkerThread<P9Result<()>>>,
130 }
131 
132 impl P9 {
new(base_features: u64, tag: &str, p9_cfg: p9::Config) -> P9Result<P9>133     pub fn new(base_features: u64, tag: &str, p9_cfg: p9::Config) -> P9Result<P9> {
134         if tag.len() > u16::MAX as usize {
135             return Err(P9Error::TagTooLong(tag.len()));
136         }
137 
138         let len = tag.len() as u16;
139         let mut cfg = Vec::with_capacity(tag.len() + mem::size_of::<u16>());
140         cfg.push(len as u8);
141         cfg.push((len >> 8) as u8);
142 
143         cfg.write_all(tag.as_bytes()).map_err(P9Error::Internal)?;
144 
145         let server = p9::Server::with_config(p9_cfg).map_err(P9Error::CreateServer)?;
146         Ok(P9 {
147             config: cfg,
148             server: Some(server),
149             avail_features: base_features | 1 << VIRTIO_9P_MOUNT_TAG,
150             acked_features: 0,
151             worker: None,
152         })
153     }
154 }
155 
156 impl VirtioDevice for P9 {
keep_rds(&self) -> Vec<RawDescriptor>157     fn keep_rds(&self) -> Vec<RawDescriptor> {
158         self.server
159             .as_ref()
160             .map(p9::Server::keep_fds)
161             .unwrap_or_default()
162     }
163 
device_type(&self) -> DeviceType164     fn device_type(&self) -> DeviceType {
165         DeviceType::P9
166     }
167 
queue_max_sizes(&self) -> &[u16]168     fn queue_max_sizes(&self) -> &[u16] {
169         QUEUE_SIZES
170     }
171 
features(&self) -> u64172     fn features(&self) -> u64 {
173         self.avail_features
174     }
175 
ack_features(&mut self, value: u64)176     fn ack_features(&mut self, value: u64) {
177         let mut v = value;
178 
179         // Check if the guest is ACK'ing a feature that we didn't claim to have.
180         let unrequested_features = v & !self.avail_features;
181         if unrequested_features != 0 {
182             warn!("virtio_9p got unknown feature ack: {:x}", v);
183 
184             // Don't count these features as acked.
185             v &= !unrequested_features;
186         }
187         self.acked_features |= v;
188     }
189 
read_config(&self, offset: u64, data: &mut [u8])190     fn read_config(&self, offset: u64, data: &mut [u8]) {
191         copy_config(data, 0, self.config.as_slice(), offset);
192     }
193 
activate( &mut self, _guest_mem: GuestMemory, _interrupt: Interrupt, mut queues: BTreeMap<usize, Queue>, ) -> anyhow::Result<()>194     fn activate(
195         &mut self,
196         _guest_mem: GuestMemory,
197         _interrupt: Interrupt,
198         mut queues: BTreeMap<usize, Queue>,
199     ) -> anyhow::Result<()> {
200         if queues.len() != 1 {
201             return Err(anyhow!("expected 1 queue, got {}", queues.len()));
202         }
203 
204         let queue = queues.remove(&0).unwrap();
205 
206         let server = self.server.take().context("missing server")?;
207 
208         self.worker = Some(WorkerThread::start("v_9p", move |kill_evt| {
209             let mut worker = Worker { queue, server };
210 
211             worker.run(kill_evt)
212         }));
213 
214         Ok(())
215     }
216 }
217