xref: /aosp_15_r20/external/crosvm/devices/src/virtio/tpm.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
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::Read;
8 use std::io::Write;
9 use std::ops::BitOrAssign;
10 
11 use anyhow::anyhow;
12 use anyhow::Context;
13 use base::error;
14 use base::Event;
15 use base::EventToken;
16 use base::RawDescriptor;
17 use base::WaitContext;
18 use base::WorkerThread;
19 use remain::sorted;
20 use thiserror::Error;
21 use vm_memory::GuestMemory;
22 
23 use super::DescriptorChain;
24 use super::DeviceType;
25 use super::Interrupt;
26 use super::Queue;
27 use super::VirtioDevice;
28 
29 // A single queue of size 2. The guest kernel driver will enqueue a single
30 // descriptor chain containing one command buffer and one response buffer at a
31 // time.
32 const QUEUE_SIZE: u16 = 2;
33 const QUEUE_SIZES: &[u16] = &[QUEUE_SIZE];
34 
35 // Maximum command or response message size permitted by this device
36 // implementation. Named to match the equivalent constant in Linux's tpm.h.
37 // There is no hard requirement that the value is the same but it makes sense.
38 const TPM_BUFSIZE: usize = 4096;
39 
40 struct Worker {
41     interrupt: Interrupt,
42     queue: Queue,
43     backend: Box<dyn TpmBackend>,
44 }
45 
46 pub trait TpmBackend: Send {
execute_command<'a>(&'a mut self, command: &[u8]) -> &'a [u8]47     fn execute_command<'a>(&'a mut self, command: &[u8]) -> &'a [u8];
48 }
49 
50 impl Worker {
perform_work(&mut self, desc: &mut DescriptorChain) -> Result<u32>51     fn perform_work(&mut self, desc: &mut DescriptorChain) -> Result<u32> {
52         let available_bytes = desc.reader.available_bytes();
53         if available_bytes > TPM_BUFSIZE {
54             return Err(Error::CommandTooLong {
55                 size: available_bytes,
56             });
57         }
58 
59         let mut command = vec![0u8; available_bytes];
60         desc.reader.read_exact(&mut command).map_err(Error::Read)?;
61 
62         let response = self.backend.execute_command(&command);
63 
64         if response.len() > TPM_BUFSIZE {
65             return Err(Error::ResponseTooLong {
66                 size: response.len(),
67             });
68         }
69 
70         let writer_len = desc.writer.available_bytes();
71         if response.len() > writer_len {
72             return Err(Error::BufferTooSmall {
73                 size: writer_len,
74                 required: response.len(),
75             });
76         }
77 
78         desc.writer.write_all(response).map_err(Error::Write)?;
79 
80         Ok(desc.writer.bytes_written() as u32)
81     }
82 
process_queue(&mut self) -> NeedsInterrupt83     fn process_queue(&mut self) -> NeedsInterrupt {
84         let mut needs_interrupt = NeedsInterrupt::No;
85         while let Some(mut avail_desc) = self.queue.pop() {
86             let len = match self.perform_work(&mut avail_desc) {
87                 Ok(len) => len,
88                 Err(err) => {
89                     error!("{}", err);
90                     0
91                 }
92             };
93 
94             self.queue.add_used(avail_desc, len);
95             needs_interrupt = NeedsInterrupt::Yes;
96         }
97 
98         needs_interrupt
99     }
100 
run(mut self, kill_evt: Event) -> anyhow::Result<()>101     fn run(mut self, kill_evt: Event) -> anyhow::Result<()> {
102         #[derive(EventToken, Debug)]
103         enum Token {
104             // A request is ready on the queue.
105             QueueAvailable,
106             // Check if any interrupts need to be re-asserted.
107             InterruptResample,
108             // The parent thread requested an exit.
109             Kill,
110         }
111 
112         let wait_ctx = WaitContext::build_with(&[
113             (self.queue.event(), Token::QueueAvailable),
114             (&kill_evt, Token::Kill),
115         ])
116         .context("WaitContext::build_with")?;
117 
118         if let Some(resample_evt) = self.interrupt.get_resample_evt() {
119             wait_ctx
120                 .add(resample_evt, Token::InterruptResample)
121                 .context("WaitContext::add")?;
122         }
123 
124         loop {
125             let events = wait_ctx.wait().context("WaitContext::wait")?;
126             let mut needs_interrupt = NeedsInterrupt::No;
127             for event in events.iter().filter(|e| e.is_readable) {
128                 match event.token {
129                     Token::QueueAvailable => {
130                         self.queue.event().wait().context("Event::wait")?;
131                         needs_interrupt |= self.process_queue();
132                     }
133                     Token::InterruptResample => {
134                         self.interrupt.interrupt_resample();
135                     }
136                     Token::Kill => return Ok(()),
137                 }
138             }
139             if needs_interrupt == NeedsInterrupt::Yes {
140                 self.queue.trigger_interrupt();
141             }
142         }
143     }
144 }
145 
146 /// Virtio vTPM device.
147 pub struct Tpm {
148     backend: Option<Box<dyn TpmBackend>>,
149     worker_thread: Option<WorkerThread<()>>,
150     features: u64,
151 }
152 
153 impl Tpm {
new(backend: Box<dyn TpmBackend>, base_features: u64) -> Tpm154     pub fn new(backend: Box<dyn TpmBackend>, base_features: u64) -> Tpm {
155         Tpm {
156             backend: Some(backend),
157             worker_thread: None,
158             features: base_features,
159         }
160     }
161 }
162 
163 impl VirtioDevice for Tpm {
keep_rds(&self) -> Vec<RawDescriptor>164     fn keep_rds(&self) -> Vec<RawDescriptor> {
165         Vec::new()
166     }
167 
device_type(&self) -> DeviceType168     fn device_type(&self) -> DeviceType {
169         DeviceType::Tpm
170     }
171 
queue_max_sizes(&self) -> &[u16]172     fn queue_max_sizes(&self) -> &[u16] {
173         QUEUE_SIZES
174     }
175 
features(&self) -> u64176     fn features(&self) -> u64 {
177         self.features
178     }
179 
activate( &mut self, _mem: GuestMemory, interrupt: Interrupt, mut queues: BTreeMap<usize, Queue>, ) -> anyhow::Result<()>180     fn activate(
181         &mut self,
182         _mem: GuestMemory,
183         interrupt: Interrupt,
184         mut queues: BTreeMap<usize, Queue>,
185     ) -> anyhow::Result<()> {
186         if queues.len() != 1 {
187             return Err(anyhow!("expected 1 queue, got {}", queues.len()));
188         }
189         let queue = queues.pop_first().unwrap().1;
190 
191         let backend = self.backend.take().context("no backend in vtpm")?;
192 
193         let worker = Worker {
194             interrupt,
195             queue,
196             backend,
197         };
198 
199         self.worker_thread = Some(WorkerThread::start("v_tpm", |kill_evt| {
200             if let Err(e) = worker.run(kill_evt) {
201                 error!("virtio-tpm worker failed: {:#}", e);
202             }
203         }));
204 
205         Ok(())
206     }
207 }
208 
209 #[derive(PartialEq, Eq)]
210 enum NeedsInterrupt {
211     Yes,
212     No,
213 }
214 
215 impl BitOrAssign for NeedsInterrupt {
bitor_assign(&mut self, rhs: NeedsInterrupt)216     fn bitor_assign(&mut self, rhs: NeedsInterrupt) {
217         if rhs == NeedsInterrupt::Yes {
218             *self = NeedsInterrupt::Yes;
219         }
220     }
221 }
222 
223 type Result<T> = std::result::Result<T, Error>;
224 
225 #[sorted]
226 #[derive(Error, Debug)]
227 enum Error {
228     #[error("vtpm response buffer is too small: {size} < {required} bytes")]
229     BufferTooSmall { size: usize, required: usize },
230     #[error("vtpm command is too long: {size} > {} bytes", TPM_BUFSIZE)]
231     CommandTooLong { size: usize },
232     #[error("vtpm failed to read from guest memory: {0}")]
233     Read(io::Error),
234     #[error(
235         "vtpm simulator generated a response that is unexpectedly long: {size} > {} bytes",
236         TPM_BUFSIZE
237     )]
238     ResponseTooLong { size: usize },
239     #[error("vtpm failed to write to guest memory: {0}")]
240     Write(io::Error),
241 }
242