• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2017 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 //! Runs hardware devices in child processes.
6 
7 use std::fs;
8 use std::fs::File;
9 use std::io::BufReader;
10 use std::io::BufWriter;
11 use std::io::Seek;
12 use std::io::Write;
13 
14 use anyhow::anyhow;
15 use anyhow::Context;
16 use base::error;
17 use base::info;
18 use base::with_as_descriptor;
19 use base::AsRawDescriptor;
20 #[cfg(feature = "swap")]
21 use base::AsRawDescriptors;
22 use base::RawDescriptor;
23 use base::SharedMemory;
24 use base::Tube;
25 use base::TubeError;
26 use jail::fork::fork_process;
27 use libc::pid_t;
28 use minijail::Minijail;
29 use remain::sorted;
30 use serde::Deserialize;
31 use serde::Serialize;
32 use snapshot::AnySnapshot;
33 use tempfile::tempfile;
34 use thiserror::Error;
35 
36 use crate::bus::ConfigWriteResult;
37 use crate::pci::CrosvmDeviceId;
38 use crate::pci::PciAddress;
39 use crate::BusAccessInfo;
40 use crate::BusDevice;
41 use crate::BusRange;
42 use crate::BusType;
43 use crate::DeviceId;
44 use crate::Suspendable;
45 
46 /// Errors for proxy devices.
47 #[sorted]
48 #[derive(Error, Debug)]
49 pub enum Error {
50     #[error("Failed to activate ProxyDevice")]
51     ActivatingProxyDevice,
52     #[error("Failed to fork jail process: {0}")]
53     ForkingJail(#[from] minijail::Error),
54     #[error("Failed to configure swap: {0}")]
55     Swap(anyhow::Error),
56     #[error("Failed to configure tube: {0}")]
57     Tube(#[from] TubeError),
58 }
59 
60 pub type Result<T> = std::result::Result<T, Error>;
61 
62 /// Wrapper for sending snapshots to and receiving snapshots from proxied devices using a file
63 /// to handle the case of snapshot being potentially too large to send across a Tube in a single
64 /// message.
65 #[derive(Debug, Serialize, Deserialize)]
66 struct SnapshotFile {
67     #[serde(with = "with_as_descriptor")]
68     file: File,
69 }
70 
71 impl SnapshotFile {
new() -> anyhow::Result<SnapshotFile>72     fn new() -> anyhow::Result<SnapshotFile> {
73         Ok(SnapshotFile {
74             file: tempfile().context("failed to create snasphot wrapper tempfile")?,
75         })
76     }
77 
from_data(data: AnySnapshot) -> anyhow::Result<SnapshotFile>78     fn from_data(data: AnySnapshot) -> anyhow::Result<SnapshotFile> {
79         let mut snapshot = SnapshotFile::new()?;
80         snapshot.write(data)?;
81         Ok(snapshot)
82     }
83 
read(&mut self) -> anyhow::Result<AnySnapshot>84     fn read(&mut self) -> anyhow::Result<AnySnapshot> {
85         let data: AnySnapshot = {
86             let mut reader = BufReader::new(&self.file);
87 
88             serde_json::from_reader(&mut reader)
89                 .context("failed to read snapshot data from snapshot temp file")?
90         };
91 
92         self.file
93             .rewind()
94             .context("failed to rewind snapshot temp file after read")?;
95 
96         Ok(data)
97     }
98 
write(&mut self, data: AnySnapshot) -> anyhow::Result<()>99     fn write(&mut self, data: AnySnapshot) -> anyhow::Result<()> {
100         {
101             let mut writer = BufWriter::new(&self.file);
102 
103             serde_json::to_writer(&mut writer, &data)
104                 .context("failed to write data to snasphot temp file")?;
105 
106             writer
107                 .flush()
108                 .context("failed to flush data to snapshot temp file")?;
109         }
110 
111         self.file
112             .rewind()
113             .context("failed to rewind snapshot temp file after write")?;
114 
115         Ok(())
116     }
117 }
118 
119 #[derive(Debug, Serialize, Deserialize)]
120 enum Command {
121     Activate,
122     Read {
123         len: u32,
124         info: BusAccessInfo,
125     },
126     Write {
127         len: u32,
128         info: BusAccessInfo,
129         data: [u8; 8],
130     },
131     ReadConfig(u32),
132     WriteConfig {
133         reg_idx: u32,
134         offset: u32,
135         len: u32,
136         data: [u8; 4],
137     },
138     InitPciConfigMapping {
139         shmem: SharedMemory,
140         base: usize,
141         len: usize,
142     },
143     ReadVirtualConfig(u32),
144     WriteVirtualConfig {
145         reg_idx: u32,
146         value: u32,
147     },
148     DestroyDevice,
149     Shutdown,
150     GetRanges,
151     Snapshot {
152         // NOTE: the SnapshotFile is created by the parent and sent to the child proxied device
153         // as the jailed child may not have permission to create a temp file.
154         snapshot: SnapshotFile,
155     },
156     Restore {
157         snapshot: SnapshotFile,
158     },
159     Sleep,
160     Wake,
161 }
162 
163 #[derive(Debug, Serialize, Deserialize)]
164 enum CommandResult {
165     Ok,
166     ReadResult([u8; 8]),
167     ReadConfigResult(u32),
168     WriteConfigResult {
169         mmio_remove: Vec<BusRange>,
170         mmio_add: Vec<BusRange>,
171         io_remove: Vec<BusRange>,
172         io_add: Vec<BusRange>,
173         removed_pci_devices: Vec<PciAddress>,
174     },
175     InitPciConfigMappingResult(bool),
176     ReadVirtualConfigResult(u32),
177     GetRangesResult(Vec<(BusRange, BusType)>),
178     SnapshotResult(std::result::Result<SnapshotFile, String>),
179     RestoreResult(std::result::Result<(), String>),
180     SleepResult(std::result::Result<(), String>),
181     WakeResult(std::result::Result<(), String>),
182 }
183 
child_proc<D: BusDevice>(tube: Tube, mut device: D)184 fn child_proc<D: BusDevice>(tube: Tube, mut device: D) {
185     // Wait for activation signal to function as BusDevice.
186     match tube.recv() {
187         Ok(Command::Activate) => {
188             if let Err(e) = tube.send(&CommandResult::Ok) {
189                 error!(
190                     "sending {} activation result failed: {}",
191                     device.debug_label(),
192                     e,
193                 );
194                 return;
195             }
196         }
197         // Commands other than activate is unexpected, close device.
198         Ok(cmd) => {
199             panic!("Receiving Command {:?} before device is activated", &cmd);
200         }
201         // Most likely tube error is caused by other end is dropped, release resource.
202         Err(e) => {
203             error!(
204                 "{} device failed before activation: {}. Dropping device",
205                 device.debug_label(),
206                 e,
207             );
208             drop(device);
209             return;
210         }
211     };
212     loop {
213         let cmd = match tube.recv() {
214             Ok(cmd) => cmd,
215             Err(e) => {
216                 error!(
217                     "recv from {} child device process failed: {}",
218                     device.debug_label(),
219                     e,
220                 );
221                 break;
222             }
223         };
224 
225         let res = match cmd {
226             Command::Activate => {
227                 panic!("Device shall only be activated once, duplicated ProxyDevice likely");
228             }
229             Command::Read { len, info } => {
230                 let mut buffer = [0u8; 8];
231                 device.read(info, &mut buffer[0..len as usize]);
232                 tube.send(&CommandResult::ReadResult(buffer))
233             }
234             Command::Write { len, info, data } => {
235                 let len = len as usize;
236                 device.write(info, &data[0..len]);
237                 // Command::Write does not have a result.
238                 Ok(())
239             }
240             Command::ReadConfig(idx) => {
241                 let val = device.config_register_read(idx as usize);
242                 tube.send(&CommandResult::ReadConfigResult(val))
243             }
244             Command::WriteConfig {
245                 reg_idx,
246                 offset,
247                 len,
248                 data,
249             } => {
250                 let len = len as usize;
251                 let res =
252                     device.config_register_write(reg_idx as usize, offset as u64, &data[0..len]);
253                 tube.send(&CommandResult::WriteConfigResult {
254                     mmio_remove: res.mmio_remove,
255                     mmio_add: res.mmio_add,
256                     io_remove: res.io_remove,
257                     io_add: res.io_add,
258                     removed_pci_devices: res.removed_pci_devices,
259                 })
260             }
261             Command::InitPciConfigMapping { shmem, base, len } => {
262                 let success = device.init_pci_config_mapping(&shmem, base, len);
263                 tube.send(&CommandResult::InitPciConfigMappingResult(success))
264             }
265             Command::ReadVirtualConfig(idx) => {
266                 let val = device.virtual_config_register_read(idx as usize);
267                 tube.send(&CommandResult::ReadVirtualConfigResult(val))
268             }
269             Command::WriteVirtualConfig { reg_idx, value } => {
270                 device.virtual_config_register_write(reg_idx as usize, value);
271                 tube.send(&CommandResult::Ok)
272             }
273             Command::DestroyDevice => {
274                 device.destroy_device();
275                 Ok(())
276             }
277             Command::Shutdown => {
278                 // Explicitly drop the device so that its Drop implementation has a chance to run
279                 // before sending the `Command::Shutdown` response.
280                 drop(device);
281 
282                 let _ = tube.send(&CommandResult::Ok);
283                 return;
284             }
285             Command::GetRanges => {
286                 let ranges = device.get_ranges();
287                 tube.send(&CommandResult::GetRangesResult(ranges))
288             }
289             Command::Snapshot { mut snapshot } => {
290                 let res = device.snapshot().and_then(|data| {
291                     snapshot.write(data)?;
292                     Ok(snapshot)
293                 });
294                 tube.send(&CommandResult::SnapshotResult(
295                     res.map_err(|e| e.to_string()),
296                 ))
297             }
298             Command::Restore { mut snapshot } => {
299                 let res = snapshot.read().and_then(|data| device.restore(data));
300                 tube.send(&CommandResult::RestoreResult(
301                     res.map_err(|e| e.to_string()),
302                 ))
303             }
304             Command::Sleep => {
305                 let res = device.sleep();
306                 tube.send(&CommandResult::SleepResult(res.map_err(|e| e.to_string())))
307             }
308             Command::Wake => {
309                 let res = device.wake();
310                 tube.send(&CommandResult::WakeResult(res.map_err(|e| e.to_string())))
311             }
312         };
313         if let Err(e) = res {
314             error!(
315                 "send to {} child device process failed: {}",
316                 device.debug_label(),
317                 e,
318             );
319         }
320     }
321 }
322 
323 /// ChildProcIntf is the interface to the device child process.
324 ///
325 /// ChildProcIntf implements Serialize, and can be sent across process before it functions as a
326 /// ProxyDevice. However, a child process shall only correspond to one ProxyDevice. The uniqueness
327 /// is checked when ChildProcIntf is casted into ProxyDevice.
328 #[derive(Serialize, Deserialize)]
329 pub struct ChildProcIntf {
330     tube: Tube,
331     pid: pid_t,
332     debug_label: String,
333 }
334 
335 impl ChildProcIntf {
336     /// Creates ChildProcIntf that shall be turned into exactly one ProxyDevice.
337     ///
338     /// The ChildProcIntf struct holds the interface to the device process. It shall be turned into
339     /// a ProxyDevice exactly once (at an arbitrary process). Since ChildProcIntf may be duplicated
340     /// by serde, the uniqueness of the interface is checked when ChildProcIntf is converted into
341     /// ProxyDevice.
342     ///
343     /// # Arguments
344     /// * `device` - The device to isolate to another process.
345     /// * `jail` - The jail to use for isolating the given device.
346     /// * `keep_rds` - File descriptors that will be kept open in the child.
new<D: BusDevice, #[cfg(feature = "swap")] P: swap::PrepareFork>( mut device: D, jail: Minijail, mut keep_rds: Vec<RawDescriptor>, #[cfg(feature = "swap")] swap_prepare_fork: &mut Option<P>, ) -> Result<ChildProcIntf>347     pub fn new<D: BusDevice, #[cfg(feature = "swap")] P: swap::PrepareFork>(
348         mut device: D,
349         jail: Minijail,
350         mut keep_rds: Vec<RawDescriptor>,
351         #[cfg(feature = "swap")] swap_prepare_fork: &mut Option<P>,
352     ) -> Result<ChildProcIntf> {
353         let debug_label = device.debug_label();
354         let (child_tube, parent_tube) = Tube::pair()?;
355 
356         keep_rds.push(child_tube.as_raw_descriptor());
357 
358         #[cfg(feature = "swap")]
359         let swap_device_uffd_sender = if let Some(prepare_fork) = swap_prepare_fork {
360             let sender = prepare_fork.prepare_fork().map_err(Error::Swap)?;
361             keep_rds.extend(sender.as_raw_descriptors());
362             Some(sender)
363         } else {
364             None
365         };
366 
367         // This will be removed after b/183540186 gets fixed.
368         // Only enabled it for x86_64 since the original bug mostly happens on x86 boards.
369         if cfg!(target_arch = "x86_64") && debug_label == "pcivirtio-gpu" {
370             if let Ok(cmd) = fs::read_to_string("/proc/self/cmdline") {
371                 if cmd.contains("arcvm") {
372                     if let Ok(share) = fs::read_to_string("/sys/fs/cgroup/cpu/arcvm/cpu.shares") {
373                         info!("arcvm cpu share when booting gpu is {:}", share.trim());
374                     }
375                 }
376             }
377         }
378 
379         let child_process = fork_process(jail, keep_rds, Some(debug_label.clone()), || {
380             #[cfg(feature = "swap")]
381             if let Some(swap_device_uffd_sender) = swap_device_uffd_sender {
382                 if let Err(e) = swap_device_uffd_sender.on_process_forked() {
383                     error!("failed to SwapController::on_process_forked: {:?}", e);
384                     // SAFETY:
385                     // exit() is trivially safe.
386                     unsafe { libc::exit(1) };
387                 }
388             }
389 
390             device.on_sandboxed();
391             child_proc(child_tube, device);
392 
393             // We're explicitly not using std::process::exit here to avoid the cleanup of
394             // stdout/stderr globals. This can cause cascading panics and SIGILL if a worker
395             // thread attempts to log to stderr after at_exit handlers have been run.
396             // TODO(crbug.com/992494): Remove this once device shutdown ordering is clearly
397             // defined.
398             //
399             // SAFETY:
400             // exit() is trivially safe.
401             // ! Never returns
402             unsafe { libc::exit(0) };
403         })?;
404 
405         // Suppress the no waiting warning from `base::sys::linux::process::Child` because crosvm
406         // does not wait for the processes from ProxyDevice explicitly. Instead it reaps all the
407         // child processes on its exit by `crosvm::sys::linux::main::wait_all_children()`.
408         let pid = child_process.into_pid();
409 
410         Ok(ChildProcIntf {
411             tube: parent_tube,
412             pid,
413             debug_label,
414         })
415     }
416 }
417 
418 /// Wraps an inner `BusDevice` that is run inside a child process via fork.
419 ///
420 /// The forked device process will automatically be terminated when this is dropped.
421 pub struct ProxyDevice {
422     child_proc_intf: ChildProcIntf,
423 }
424 
425 impl TryFrom<ChildProcIntf> for ProxyDevice {
426     type Error = Error;
try_from(child_proc_intf: ChildProcIntf) -> Result<Self>427     fn try_from(child_proc_intf: ChildProcIntf) -> Result<Self> {
428         // Notify child process to be activated as a BusDevice.
429         child_proc_intf.tube.send(&Command::Activate)?;
430         // Device returns Ok if it is activated only once.
431         match child_proc_intf.tube.recv()? {
432             CommandResult::Ok => Ok(Self { child_proc_intf }),
433             _ => Err(Error::ActivatingProxyDevice),
434         }
435     }
436 }
437 
438 impl ProxyDevice {
439     /// Takes the given device and isolates it into another process via fork before returning.
440     ///
441     /// Because forks are very unfriendly to destructors and all memory mappings and file
442     /// descriptors are inherited, this should be used as early as possible in the main process.
443     /// ProxyDevice::new shall not be used for hotplugging. Call ChildProcIntf::new on jail warden
444     /// process, send using serde, then cast into ProxyDevice instead.
445     ///
446     /// # Arguments
447     /// * `device` - The device to isolate to another process.
448     /// * `jail` - The jail to use for isolating the given device.
449     /// * `keep_rds` - File descriptors that will be kept open in the child.
new<D: BusDevice, #[cfg(feature = "swap")] P: swap::PrepareFork>( device: D, jail: Minijail, keep_rds: Vec<RawDescriptor>, #[cfg(feature = "swap")] swap_prepare_fork: &mut Option<P>, ) -> Result<ProxyDevice>450     pub fn new<D: BusDevice, #[cfg(feature = "swap")] P: swap::PrepareFork>(
451         device: D,
452         jail: Minijail,
453         keep_rds: Vec<RawDescriptor>,
454         #[cfg(feature = "swap")] swap_prepare_fork: &mut Option<P>,
455     ) -> Result<ProxyDevice> {
456         ChildProcIntf::new(
457             device,
458             jail,
459             keep_rds,
460             #[cfg(feature = "swap")]
461             swap_prepare_fork,
462         )?
463         .try_into()
464     }
465 
pid(&self) -> pid_t466     pub fn pid(&self) -> pid_t {
467         self.child_proc_intf.pid
468     }
469 
470     /// Send a command that does not expect a response from the child device process.
send_no_result(&self, cmd: &Command)471     fn send_no_result(&self, cmd: &Command) {
472         let res = self.child_proc_intf.tube.send(cmd);
473         if let Err(e) = res {
474             error!(
475                 "failed write to child device process {}: {}",
476                 self.child_proc_intf.debug_label, e,
477             );
478         }
479     }
480 
481     /// Send a command and read its response from the child device process.
sync_send(&self, cmd: &Command) -> Option<CommandResult>482     fn sync_send(&self, cmd: &Command) -> Option<CommandResult> {
483         self.send_no_result(cmd);
484         match self.child_proc_intf.tube.recv() {
485             Err(e) => {
486                 error!(
487                     "failed to read result of {:?} from child device process {}: {}",
488                     cmd, self.child_proc_intf.debug_label, e,
489                 );
490                 None
491             }
492             Ok(r) => Some(r),
493         }
494     }
495 }
496 
497 impl BusDevice for ProxyDevice {
device_id(&self) -> DeviceId498     fn device_id(&self) -> DeviceId {
499         CrosvmDeviceId::ProxyDevice.into()
500     }
501 
debug_label(&self) -> String502     fn debug_label(&self) -> String {
503         self.child_proc_intf.debug_label.clone()
504     }
505 
config_register_write( &mut self, reg_idx: usize, offset: u64, data: &[u8], ) -> ConfigWriteResult506     fn config_register_write(
507         &mut self,
508         reg_idx: usize,
509         offset: u64,
510         data: &[u8],
511     ) -> ConfigWriteResult {
512         let len = data.len() as u32;
513         let mut buffer = [0u8; 4];
514         buffer[0..data.len()].clone_from_slice(data);
515         let reg_idx = reg_idx as u32;
516         let offset = offset as u32;
517         if let Some(CommandResult::WriteConfigResult {
518             mmio_remove,
519             mmio_add,
520             io_remove,
521             io_add,
522             removed_pci_devices,
523         }) = self.sync_send(&Command::WriteConfig {
524             reg_idx,
525             offset,
526             len,
527             data: buffer,
528         }) {
529             ConfigWriteResult {
530                 mmio_remove,
531                 mmio_add,
532                 io_remove,
533                 io_add,
534                 removed_pci_devices,
535             }
536         } else {
537             Default::default()
538         }
539     }
540 
config_register_read(&self, reg_idx: usize) -> u32541     fn config_register_read(&self, reg_idx: usize) -> u32 {
542         let res = self.sync_send(&Command::ReadConfig(reg_idx as u32));
543         if let Some(CommandResult::ReadConfigResult(val)) = res {
544             val
545         } else {
546             0
547         }
548     }
549 
init_pci_config_mapping(&mut self, shmem: &SharedMemory, base: usize, len: usize) -> bool550     fn init_pci_config_mapping(&mut self, shmem: &SharedMemory, base: usize, len: usize) -> bool {
551         let Ok(shmem) = shmem.try_clone() else {
552             error!("Failed to clone pci config mapping shmem");
553             return false;
554         };
555         let res = self.sync_send(&Command::InitPciConfigMapping { shmem, base, len });
556         matches!(res, Some(CommandResult::InitPciConfigMappingResult(true)))
557     }
558 
virtual_config_register_write(&mut self, reg_idx: usize, value: u32)559     fn virtual_config_register_write(&mut self, reg_idx: usize, value: u32) {
560         let reg_idx = reg_idx as u32;
561         self.sync_send(&Command::WriteVirtualConfig { reg_idx, value });
562     }
563 
virtual_config_register_read(&self, reg_idx: usize) -> u32564     fn virtual_config_register_read(&self, reg_idx: usize) -> u32 {
565         let res = self.sync_send(&Command::ReadVirtualConfig(reg_idx as u32));
566         if let Some(CommandResult::ReadVirtualConfigResult(val)) = res {
567             val
568         } else {
569             0
570         }
571     }
572 
read(&mut self, info: BusAccessInfo, data: &mut [u8])573     fn read(&mut self, info: BusAccessInfo, data: &mut [u8]) {
574         let len = data.len() as u32;
575         if let Some(CommandResult::ReadResult(buffer)) =
576             self.sync_send(&Command::Read { len, info })
577         {
578             let len = data.len();
579             data.clone_from_slice(&buffer[0..len]);
580         }
581     }
582 
write(&mut self, info: BusAccessInfo, data: &[u8])583     fn write(&mut self, info: BusAccessInfo, data: &[u8]) {
584         let mut buffer = [0u8; 8];
585         let len = data.len() as u32;
586         buffer[0..data.len()].clone_from_slice(data);
587         self.send_no_result(&Command::Write {
588             len,
589             info,
590             data: buffer,
591         });
592     }
593 
get_ranges(&self) -> Vec<(BusRange, BusType)>594     fn get_ranges(&self) -> Vec<(BusRange, BusType)> {
595         if let Some(CommandResult::GetRangesResult(ranges)) = self.sync_send(&Command::GetRanges) {
596             ranges
597         } else {
598             Default::default()
599         }
600     }
601 
destroy_device(&mut self)602     fn destroy_device(&mut self) {
603         self.send_no_result(&Command::DestroyDevice);
604     }
605 }
606 
607 impl Suspendable for ProxyDevice {
snapshot(&mut self) -> anyhow::Result<AnySnapshot>608     fn snapshot(&mut self) -> anyhow::Result<AnySnapshot> {
609         let res = self.sync_send(&Command::Snapshot {
610             snapshot: SnapshotFile::new()?,
611         });
612         match res {
613             Some(CommandResult::SnapshotResult(Ok(mut snapshot))) => snapshot.read(),
614             Some(CommandResult::SnapshotResult(Err(e))) => Err(anyhow!(
615                 "failed to snapshot {}: {:#}",
616                 self.debug_label(),
617                 e
618             )),
619             _ => Err(anyhow!("unexpected snapshot result {:?}", res)),
620         }
621     }
622 
restore(&mut self, data: AnySnapshot) -> anyhow::Result<()>623     fn restore(&mut self, data: AnySnapshot) -> anyhow::Result<()> {
624         let res = self.sync_send(&Command::Restore {
625             snapshot: SnapshotFile::from_data(data)?,
626         });
627         match res {
628             Some(CommandResult::RestoreResult(Ok(()))) => Ok(()),
629             Some(CommandResult::RestoreResult(Err(e))) => {
630                 Err(anyhow!("failed to restore {}: {:#}", self.debug_label(), e))
631             }
632             _ => Err(anyhow!("unexpected restore result {:?}", res)),
633         }
634     }
635 
sleep(&mut self) -> anyhow::Result<()>636     fn sleep(&mut self) -> anyhow::Result<()> {
637         let res = self.sync_send(&Command::Sleep);
638         match res {
639             Some(CommandResult::SleepResult(Ok(()))) => Ok(()),
640             Some(CommandResult::SleepResult(Err(e))) => {
641                 Err(anyhow!("failed to sleep {}: {:#}", self.debug_label(), e))
642             }
643             _ => Err(anyhow!("unexpected sleep result {:?}", res)),
644         }
645     }
646 
wake(&mut self) -> anyhow::Result<()>647     fn wake(&mut self) -> anyhow::Result<()> {
648         let res = self.sync_send(&Command::Wake);
649         match res {
650             Some(CommandResult::WakeResult(Ok(()))) => Ok(()),
651             Some(CommandResult::WakeResult(Err(e))) => {
652                 Err(anyhow!("failed to wake {}: {:#}", self.debug_label(), e))
653             }
654             _ => Err(anyhow!("unexpected wake result {:?}", res)),
655         }
656     }
657 }
658 
659 impl Drop for ProxyDevice {
drop(&mut self)660     fn drop(&mut self) {
661         self.sync_send(&Command::Shutdown);
662     }
663 }
664 
665 /// Note: These tests must be run with --test-threads=1 to allow minijail to fork
666 /// the process.
667 #[cfg(test)]
668 mod tests {
669     use super::*;
670     use crate::pci::PciId;
671 
672     /// A simple test echo device that outputs the same u8 that was written to it.
673     struct EchoDevice {
674         data: u8,
675         config: u8,
676     }
677     impl EchoDevice {
new() -> EchoDevice678         fn new() -> EchoDevice {
679             EchoDevice { data: 0, config: 0 }
680         }
681     }
682     impl BusDevice for EchoDevice {
device_id(&self) -> DeviceId683         fn device_id(&self) -> DeviceId {
684             PciId::new(0, 0).into()
685         }
686 
debug_label(&self) -> String687         fn debug_label(&self) -> String {
688             "EchoDevice".to_owned()
689         }
690 
write(&mut self, _info: BusAccessInfo, data: &[u8])691         fn write(&mut self, _info: BusAccessInfo, data: &[u8]) {
692             assert!(data.len() == 1);
693             self.data = data[0];
694         }
695 
read(&mut self, _info: BusAccessInfo, data: &mut [u8])696         fn read(&mut self, _info: BusAccessInfo, data: &mut [u8]) {
697             assert!(data.len() == 1);
698             data[0] = self.data;
699         }
700 
config_register_write( &mut self, _reg_idx: usize, _offset: u64, data: &[u8], ) -> ConfigWriteResult701         fn config_register_write(
702             &mut self,
703             _reg_idx: usize,
704             _offset: u64,
705             data: &[u8],
706         ) -> ConfigWriteResult {
707             let result = ConfigWriteResult {
708                 ..Default::default()
709             };
710             assert!(data.len() == 1);
711             self.config = data[0];
712             result
713         }
714 
config_register_read(&self, _reg_idx: usize) -> u32715         fn config_register_read(&self, _reg_idx: usize) -> u32 {
716             self.config as u32
717         }
718     }
719 
720     impl Suspendable for EchoDevice {}
721 
new_proxied_echo_device() -> ProxyDevice722     fn new_proxied_echo_device() -> ProxyDevice {
723         let device = EchoDevice::new();
724         let keep_fds: Vec<RawDescriptor> = Vec::new();
725         let minijail = Minijail::new().unwrap();
726         ProxyDevice::new(
727             device,
728             minijail,
729             keep_fds,
730             #[cfg(feature = "swap")]
731             &mut None::<swap::SwapController>,
732         )
733         .unwrap()
734     }
735 
736     // TODO(b/173833661): Find a way to ensure these tests are run single-threaded.
737     #[test]
738     #[ignore]
test_debug_label()739     fn test_debug_label() {
740         let proxy_device = new_proxied_echo_device();
741         assert_eq!(proxy_device.debug_label(), "EchoDevice");
742     }
743 
744     #[test]
745     #[ignore]
test_proxied_read_write()746     fn test_proxied_read_write() {
747         let mut proxy_device = new_proxied_echo_device();
748         let address = BusAccessInfo {
749             offset: 0,
750             address: 0,
751             id: 0,
752         };
753         proxy_device.write(address, &[42]);
754         let mut read_buffer = [0];
755         proxy_device.read(address, &mut read_buffer);
756         assert_eq!(read_buffer, [42]);
757     }
758 
759     #[test]
760     #[ignore]
test_proxied_config()761     fn test_proxied_config() {
762         let mut proxy_device = new_proxied_echo_device();
763         proxy_device.config_register_write(0, 0, &[42]);
764         assert_eq!(proxy_device.config_register_read(0), 42);
765     }
766 }
767