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