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