Coverage Report

Created: 2025-12-31 06:16

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