1use std::fs;
8use std::fs::File;
9use std::io::BufReader;
10use std::io::BufWriter;
11use std::io::Seek;
12use std::io::Write;
13
14use anyhow::anyhow;
15use anyhow::Context;
16use base::error;
17use base::info;
18use base::with_as_descriptor;
19use base::AsRawDescriptor;
20#[cfg(feature = "swap")]
21use base::AsRawDescriptors;
22use base::RawDescriptor;
23use base::SharedMemory;
24use base::Tube;
25use base::TubeError;
26use jail::fork::fork_process;
27use libc::pid_t;
28use minijail::Minijail;
29use remain::sorted;
30use serde::Deserialize;
31use serde::Serialize;
32use snapshot::AnySnapshot;
33use tempfile::tempfile;
34use thiserror::Error;
35
36use crate::bus::ConfigWriteResult;
37use crate::pci::CrosvmDeviceId;
38use crate::pci::PciAddress;
39use crate::BusAccessInfo;
40use crate::BusDevice;
41use crate::BusRange;
42use crate::BusType;
43use crate::DeviceId;
44use crate::Suspendable;
45
46#[sorted]
48#[derive(Error, Debug)]
49pub 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
60pub type Result<T> = std::result::Result<T, Error>;
61
62#[derive(Debug, Serialize, Deserialize)]
66struct SnapshotFile {
67 #[serde(with = "with_as_descriptor")]
68 file: File,
69}
70
71impl SnapshotFile {
72 fn new() -> anyhow::Result<SnapshotFile> {
73 Ok(SnapshotFile {
74 file: tempfile().context("failed to create snasphot wrapper tempfile")?,
75 })
76 }
77
78 fn from_data(data: AnySnapshot) -> anyhow::Result<SnapshotFile> {
79 let mut snapshot = SnapshotFile::new()?;
80 snapshot.write(data)?;
81 Ok(snapshot)
82 }
83
84 fn read(&mut self) -> anyhow::Result<AnySnapshot> {
85 let data: AnySnapshot = ciborium::from_reader(&mut BufReader::new(&self.file))
86 .context("failed to read snapshot data from snapshot temp file")?;
87
88 self.file
89 .rewind()
90 .context("failed to rewind snapshot temp file after read")?;
91
92 Ok(data)
93 }
94
95 fn write(&mut self, data: AnySnapshot) -> anyhow::Result<()> {
96 {
97 let mut writer = BufWriter::new(&self.file);
98
99 ciborium::into_writer(&data, &mut writer)
100 .context("failed to write data to snasphot temp file")?;
101
102 writer
103 .flush()
104 .context("failed to flush data to snapshot temp file")?;
105 }
106
107 self.file
108 .rewind()
109 .context("failed to rewind snapshot temp file after write")?;
110
111 Ok(())
112 }
113}
114
115#[derive(Debug, Serialize, Deserialize)]
116enum 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 snapshot: SnapshotFile,
151 },
152 Restore {
153 snapshot: SnapshotFile,
154 },
155 Sleep,
156 Wake,
157}
158
159#[derive(Debug, Serialize, Deserialize)]
160enum 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
180fn child_proc<D: BusDevice>(tube: Tube, mut device: D) {
181 match tube.recv() {
183 Ok(Command::Activate) => {
184 if let Err(e) = tube.send(&CommandResult::Ok) {
185 error!(
186 "sending {} activation result failed: {}",
187 device.debug_label(),
188 e,
189 );
190 return;
191 }
192 }
193 Ok(cmd) => {
195 panic!("Receiving Command {:?} before device is activated", &cmd);
196 }
197 Err(e) => {
199 error!(
200 "{} device failed before activation: {}. Dropping device",
201 device.debug_label(),
202 e,
203 );
204 drop(device);
205 return;
206 }
207 };
208 loop {
209 let cmd = match tube.recv() {
210 Ok(cmd) => cmd,
211 Err(e) => {
212 error!(
213 "recv from {} child device process failed: {}",
214 device.debug_label(),
215 e,
216 );
217 break;
218 }
219 };
220
221 let res = match cmd {
222 Command::Activate => {
223 panic!("Device shall only be activated once, duplicated ProxyDevice likely");
224 }
225 Command::Read { len, info } => {
226 let mut buffer = [0u8; 8];
227 device.read(info, &mut buffer[0..len as usize]);
228 tube.send(&CommandResult::ReadResult(buffer))
229 }
230 Command::Write { len, info, data } => {
231 let len = len as usize;
232 device.write(info, &data[0..len]);
233 Ok(())
235 }
236 Command::ReadConfig(idx) => {
237 let val = device.config_register_read(idx as usize);
238 tube.send(&CommandResult::ReadConfigResult(val))
239 }
240 Command::WriteConfig {
241 reg_idx,
242 offset,
243 len,
244 data,
245 } => {
246 let len = len as usize;
247 let res =
248 device.config_register_write(reg_idx as usize, offset as u64, &data[0..len]);
249 tube.send(&CommandResult::WriteConfigResult {
250 mmio_remove: res.mmio_remove,
251 mmio_add: res.mmio_add,
252 io_remove: res.io_remove,
253 io_add: res.io_add,
254 removed_pci_devices: res.removed_pci_devices,
255 })
256 }
257 Command::InitPciConfigMapping { shmem, base, len } => {
258 let success = device.init_pci_config_mapping(&shmem, base, len);
259 tube.send(&CommandResult::InitPciConfigMappingResult(success))
260 }
261 Command::ReadVirtualConfig(idx) => {
262 let val = device.virtual_config_register_read(idx as usize);
263 tube.send(&CommandResult::ReadVirtualConfigResult(val))
264 }
265 Command::WriteVirtualConfig { reg_idx, value } => {
266 device.virtual_config_register_write(reg_idx as usize, value);
267 tube.send(&CommandResult::Ok)
268 }
269 Command::DestroyDevice => {
270 device.destroy_device();
271 Ok(())
272 }
273 Command::Shutdown => {
274 drop(device);
277
278 let _ = tube.send(&CommandResult::Ok);
279 return;
280 }
281 Command::GetRanges => {
282 let ranges = device.get_ranges();
283 tube.send(&CommandResult::GetRangesResult(ranges))
284 }
285 Command::Snapshot { mut snapshot } => {
286 let res = device.snapshot().and_then(|data| {
287 snapshot.write(data)?;
288 Ok(snapshot)
289 });
290 tube.send(&CommandResult::SnapshotResult(
291 res.map_err(|e| e.to_string()),
292 ))
293 }
294 Command::Restore { mut snapshot } => {
295 let res = snapshot.read().and_then(|data| device.restore(data));
296 tube.send(&CommandResult::RestoreResult(
297 res.map_err(|e| e.to_string()),
298 ))
299 }
300 Command::Sleep => {
301 let res = device.sleep();
302 tube.send(&CommandResult::SleepResult(res.map_err(|e| e.to_string())))
303 }
304 Command::Wake => {
305 let res = device.wake();
306 tube.send(&CommandResult::WakeResult(res.map_err(|e| e.to_string())))
307 }
308 };
309 if let Err(e) = res {
310 error!(
311 "send to {} child device process failed: {}",
312 device.debug_label(),
313 e,
314 );
315 }
316 }
317}
318
319#[derive(Serialize, Deserialize)]
325pub struct ChildProcIntf {
326 tube: Tube,
327 pid: pid_t,
328 debug_label: String,
329}
330
331impl ChildProcIntf {
332 pub fn new<D: BusDevice, #[cfg(feature = "swap")] P: swap::PrepareFork>(
344 mut device: D,
345 jail: Minijail,
346 mut keep_rds: Vec<RawDescriptor>,
347 #[cfg(feature = "swap")] swap_prepare_fork: &mut Option<P>,
348 ) -> Result<ChildProcIntf> {
349 let debug_label = device.debug_label();
350 let (child_tube, parent_tube) = Tube::pair()?;
351
352 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 if cfg!(target_arch = "x86_64") && debug_label == "pcivirtio-gpu" {
366 if let Ok(cmd) = fs::read_to_string("/proc/self/cmdline") {
367 if cmd.contains("arcvm") {
368 if let Ok(share) = fs::read_to_string("/sys/fs/cgroup/cpu/arcvm/cpu.shares") {
369 info!("arcvm cpu share when booting gpu is {:}", share.trim());
370 }
371 }
372 }
373 }
374
375 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 unsafe { libc::exit(1) };
383 }
384 }
385
386 device.on_sandboxed();
387 child_proc(child_tube, device);
388
389 unsafe { libc::exit(0) };
399 })?;
400
401 let pid = child_process.into_pid();
405
406 Ok(ChildProcIntf {
407 tube: parent_tube,
408 pid,
409 debug_label,
410 })
411 }
412}
413
414pub struct ProxyDevice {
418 child_proc_intf: ChildProcIntf,
419}
420
421impl TryFrom<ChildProcIntf> for ProxyDevice {
422 type Error = Error;
423 fn try_from(child_proc_intf: ChildProcIntf) -> Result<Self> {
424 child_proc_intf.tube.send(&Command::Activate)?;
426 match child_proc_intf.tube.recv()? {
428 CommandResult::Ok => Ok(Self { child_proc_intf }),
429 _ => Err(Error::ActivatingProxyDevice),
430 }
431 }
432}
433
434impl ProxyDevice {
435 pub fn new<D: BusDevice, #[cfg(feature = "swap")] P: swap::PrepareFork>(
447 device: D,
448 jail: Minijail,
449 keep_rds: Vec<RawDescriptor>,
450 #[cfg(feature = "swap")] swap_prepare_fork: &mut Option<P>,
451 ) -> Result<ProxyDevice> {
452 ChildProcIntf::new(
453 device,
454 jail,
455 keep_rds,
456 #[cfg(feature = "swap")]
457 swap_prepare_fork,
458 )?
459 .try_into()
460 }
461
462 pub fn pid(&self) -> pid_t {
463 self.child_proc_intf.pid
464 }
465
466 fn send_no_result(&self, cmd: &Command) {
468 let res = self.child_proc_intf.tube.send(cmd);
469 if let Err(e) = res {
470 error!(
471 "failed write to child device process {}: {}",
472 self.child_proc_intf.debug_label, e,
473 );
474 }
475 }
476
477 fn sync_send(&self, cmd: &Command) -> Option<CommandResult> {
479 self.send_no_result(cmd);
480 match self.child_proc_intf.tube.recv() {
481 Err(e) => {
482 error!(
483 "failed to read result of {:?} from child device process {}: {}",
484 cmd, self.child_proc_intf.debug_label, e,
485 );
486 None
487 }
488 Ok(r) => Some(r),
489 }
490 }
491}
492
493impl BusDevice for ProxyDevice {
494 fn device_id(&self) -> DeviceId {
495 CrosvmDeviceId::ProxyDevice.into()
496 }
497
498 fn debug_label(&self) -> String {
499 self.child_proc_intf.debug_label.clone()
500 }
501
502 fn config_register_write(
503 &mut self,
504 reg_idx: usize,
505 offset: u64,
506 data: &[u8],
507 ) -> ConfigWriteResult {
508 let len = data.len() as u32;
509 let mut buffer = [0u8; 4];
510 buffer[0..data.len()].clone_from_slice(data);
511 let reg_idx = reg_idx as u32;
512 let offset = offset as u32;
513 if let Some(CommandResult::WriteConfigResult {
514 mmio_remove,
515 mmio_add,
516 io_remove,
517 io_add,
518 removed_pci_devices,
519 }) = self.sync_send(&Command::WriteConfig {
520 reg_idx,
521 offset,
522 len,
523 data: buffer,
524 }) {
525 ConfigWriteResult {
526 mmio_remove,
527 mmio_add,
528 io_remove,
529 io_add,
530 removed_pci_devices,
531 }
532 } else {
533 Default::default()
534 }
535 }
536
537 fn config_register_read(&self, reg_idx: usize) -> u32 {
538 let res = self.sync_send(&Command::ReadConfig(reg_idx as u32));
539 if let Some(CommandResult::ReadConfigResult(val)) = res {
540 val
541 } else {
542 0
543 }
544 }
545
546 fn init_pci_config_mapping(&mut self, shmem: &SharedMemory, base: usize, len: usize) -> bool {
547 let Ok(shmem) = shmem.try_clone() else {
548 error!("Failed to clone pci config mapping shmem");
549 return false;
550 };
551 let res = self.sync_send(&Command::InitPciConfigMapping { shmem, base, len });
552 matches!(res, Some(CommandResult::InitPciConfigMappingResult(true)))
553 }
554
555 fn virtual_config_register_write(&mut self, reg_idx: usize, value: u32) {
556 let reg_idx = reg_idx as u32;
557 self.sync_send(&Command::WriteVirtualConfig { reg_idx, value });
558 }
559
560 fn virtual_config_register_read(&self, reg_idx: usize) -> u32 {
561 let res = self.sync_send(&Command::ReadVirtualConfig(reg_idx as u32));
562 if let Some(CommandResult::ReadVirtualConfigResult(val)) = res {
563 val
564 } else {
565 0
566 }
567 }
568
569 fn read(&mut self, info: BusAccessInfo, data: &mut [u8]) {
570 let len = data.len() as u32;
571 if let Some(CommandResult::ReadResult(buffer)) =
572 self.sync_send(&Command::Read { len, info })
573 {
574 let len = data.len();
575 data.clone_from_slice(&buffer[0..len]);
576 }
577 }
578
579 fn write(&mut self, info: BusAccessInfo, data: &[u8]) {
580 let mut buffer = [0u8; 8];
581 let len = data.len() as u32;
582 buffer[0..data.len()].clone_from_slice(data);
583 self.send_no_result(&Command::Write {
584 len,
585 info,
586 data: buffer,
587 });
588 }
589
590 fn get_ranges(&self) -> Vec<(BusRange, BusType)> {
591 if let Some(CommandResult::GetRangesResult(ranges)) = self.sync_send(&Command::GetRanges) {
592 ranges
593 } else {
594 Default::default()
595 }
596 }
597
598 fn destroy_device(&mut self) {
599 self.send_no_result(&Command::DestroyDevice);
600 }
601}
602
603impl Suspendable for ProxyDevice {
604 fn snapshot(&mut self) -> anyhow::Result<AnySnapshot> {
605 let res = self.sync_send(&Command::Snapshot {
606 snapshot: SnapshotFile::new()?,
607 });
608 match res {
609 Some(CommandResult::SnapshotResult(Ok(mut snapshot))) => snapshot.read(),
610 Some(CommandResult::SnapshotResult(Err(e))) => Err(anyhow!(
611 "failed to snapshot {}: {:#}",
612 self.debug_label(),
613 e
614 )),
615 _ => Err(anyhow!("unexpected snapshot result {:?}", res)),
616 }
617 }
618
619 fn restore(&mut self, data: AnySnapshot) -> anyhow::Result<()> {
620 let res = self.sync_send(&Command::Restore {
621 snapshot: SnapshotFile::from_data(data)?,
622 });
623 match res {
624 Some(CommandResult::RestoreResult(Ok(()))) => Ok(()),
625 Some(CommandResult::RestoreResult(Err(e))) => {
626 Err(anyhow!("failed to restore {}: {:#}", self.debug_label(), e))
627 }
628 _ => Err(anyhow!("unexpected restore result {:?}", res)),
629 }
630 }
631
632 fn sleep(&mut self) -> anyhow::Result<()> {
633 let res = self.sync_send(&Command::Sleep);
634 match res {
635 Some(CommandResult::SleepResult(Ok(()))) => Ok(()),
636 Some(CommandResult::SleepResult(Err(e))) => {
637 Err(anyhow!("failed to sleep {}: {:#}", self.debug_label(), e))
638 }
639 _ => Err(anyhow!("unexpected sleep result {:?}", res)),
640 }
641 }
642
643 fn wake(&mut self) -> anyhow::Result<()> {
644 let res = self.sync_send(&Command::Wake);
645 match res {
646 Some(CommandResult::WakeResult(Ok(()))) => Ok(()),
647 Some(CommandResult::WakeResult(Err(e))) => {
648 Err(anyhow!("failed to wake {}: {:#}", self.debug_label(), e))
649 }
650 _ => Err(anyhow!("unexpected wake result {:?}", res)),
651 }
652 }
653}
654
655impl Drop for ProxyDevice {
656 fn drop(&mut self) {
657 self.sync_send(&Command::Shutdown);
658 }
659}
660
661#[cfg(test)]
664mod tests {
665 use super::*;
666 use crate::pci::PciId;
667
668 struct EchoDevice {
670 data: u8,
671 config: u8,
672 }
673 impl EchoDevice {
674 fn new() -> EchoDevice {
675 EchoDevice { data: 0, config: 0 }
676 }
677 }
678 impl BusDevice for EchoDevice {
679 fn device_id(&self) -> DeviceId {
680 PciId::new(0, 0).into()
681 }
682
683 fn debug_label(&self) -> String {
684 "EchoDevice".to_owned()
685 }
686
687 fn write(&mut self, _info: BusAccessInfo, data: &[u8]) {
688 assert!(data.len() == 1);
689 self.data = data[0];
690 }
691
692 fn read(&mut self, _info: BusAccessInfo, data: &mut [u8]) {
693 assert!(data.len() == 1);
694 data[0] = self.data;
695 }
696
697 fn config_register_write(
698 &mut self,
699 _reg_idx: usize,
700 _offset: u64,
701 data: &[u8],
702 ) -> ConfigWriteResult {
703 let result = ConfigWriteResult {
704 ..Default::default()
705 };
706 assert!(data.len() == 1);
707 self.config = data[0];
708 result
709 }
710
711 fn config_register_read(&self, _reg_idx: usize) -> u32 {
712 self.config as u32
713 }
714 }
715
716 impl Suspendable for EchoDevice {}
717
718 fn new_proxied_echo_device() -> ProxyDevice {
719 let device = EchoDevice::new();
720 let keep_fds: Vec<RawDescriptor> = Vec::new();
721 let minijail = Minijail::new().unwrap();
722 ProxyDevice::new(
723 device,
724 minijail,
725 keep_fds,
726 #[cfg(feature = "swap")]
727 &mut None::<swap::SwapController>,
728 )
729 .unwrap()
730 }
731
732 #[test]
734 #[ignore]
735 fn test_debug_label() {
736 let proxy_device = new_proxied_echo_device();
737 assert_eq!(proxy_device.debug_label(), "EchoDevice");
738 }
739
740 #[test]
741 #[ignore]
742 fn test_proxied_read_write() {
743 let mut proxy_device = new_proxied_echo_device();
744 let address = BusAccessInfo {
745 offset: 0,
746 address: 0,
747 id: 0,
748 };
749 proxy_device.write(address, &[42]);
750 let mut read_buffer = [0];
751 proxy_device.read(address, &mut read_buffer);
752 assert_eq!(read_buffer, [42]);
753 }
754
755 #[test]
756 #[ignore]
757 fn test_proxied_config() {
758 let mut proxy_device = new_proxied_echo_device();
759 proxy_device.config_register_write(0, 0, &[42]);
760 assert_eq!(proxy_device.config_register_read(0), 42);
761 }
762}