• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2023 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 #[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
6 mod aarch64;
7 
8 mod gunyah_sys;
9 use std::cmp::Reverse;
10 use std::collections::BTreeMap;
11 use std::collections::BinaryHeap;
12 use std::collections::HashSet;
13 use std::ffi::CString;
14 use std::fs::File;
15 use std::mem::size_of;
16 use std::os::raw::c_ulong;
17 use std::os::unix::prelude::OsStrExt;
18 use std::path::Path;
19 use std::path::PathBuf;
20 use std::sync::Arc;
21 
22 use anyhow::Context;
23 use base::errno_result;
24 use base::error;
25 use base::info;
26 use base::ioctl;
27 use base::ioctl_with_ref;
28 use base::ioctl_with_val;
29 use base::pagesize;
30 use base::warn;
31 use base::Error;
32 use base::FromRawDescriptor;
33 use base::MemoryMapping;
34 use base::MemoryMappingBuilder;
35 use base::MmapError;
36 use base::RawDescriptor;
37 use gunyah_sys::*;
38 use libc::open;
39 use libc::EFAULT;
40 use libc::EINVAL;
41 use libc::EIO;
42 use libc::ENOENT;
43 use libc::ENOSPC;
44 use libc::ENOTSUP;
45 use libc::EOVERFLOW;
46 use libc::O_CLOEXEC;
47 use libc::O_RDWR;
48 use sync::Mutex;
49 use vm_memory::MemoryRegionPurpose;
50 
51 use crate::*;
52 
53 pub struct Gunyah {
54     gunyah: SafeDescriptor,
55 }
56 
57 impl AsRawDescriptor for Gunyah {
as_raw_descriptor(&self) -> RawDescriptor58     fn as_raw_descriptor(&self) -> RawDescriptor {
59         self.gunyah.as_raw_descriptor()
60     }
61 }
62 
63 impl Gunyah {
new_with_path(device_path: &Path) -> Result<Gunyah>64     pub fn new_with_path(device_path: &Path) -> Result<Gunyah> {
65         let c_path = CString::new(device_path.as_os_str().as_bytes()).unwrap();
66         // SAFETY:
67         // Open calls are safe because we give a nul-terminated string and verify the result.
68         let ret = unsafe { open(c_path.as_ptr(), O_RDWR | O_CLOEXEC) };
69         if ret < 0 {
70             return errno_result();
71         }
72         Ok(Gunyah {
73             // SAFETY:
74             // Safe because we verify that ret is valid and we own the fd.
75             gunyah: unsafe { SafeDescriptor::from_raw_descriptor(ret) },
76         })
77     }
78 
new() -> Result<Gunyah>79     pub fn new() -> Result<Gunyah> {
80         Gunyah::new_with_path(&PathBuf::from("/dev/gunyah"))
81     }
82 }
83 
84 impl Hypervisor for Gunyah {
try_clone(&self) -> Result<Self> where Self: Sized,85     fn try_clone(&self) -> Result<Self>
86     where
87         Self: Sized,
88     {
89         Ok(Gunyah {
90             gunyah: self.gunyah.try_clone()?,
91         })
92     }
93 
check_capability(&self, cap: HypervisorCap) -> bool94     fn check_capability(&self, cap: HypervisorCap) -> bool {
95         match cap {
96             HypervisorCap::UserMemory => true,
97             HypervisorCap::ArmPmuV3 => false,
98             HypervisorCap::ImmediateExit => true,
99             HypervisorCap::StaticSwiotlbAllocationRequired => true,
100             HypervisorCap::HypervisorInitializedBootContext => true,
101             HypervisorCap::S390UserSigp | HypervisorCap::TscDeadlineTimer => false,
102             #[cfg(target_arch = "x86_64")]
103             HypervisorCap::Xcrs | HypervisorCap::CalibratedTscLeafRequired => false,
104         }
105     }
106 }
107 
android_lend_user_memory_region( vm: &SafeDescriptor, slot: MemSlot, read_only: bool, guest_addr: u64, memory_size: u64, userspace_addr: *mut u8, ) -> Result<()>108 unsafe fn android_lend_user_memory_region(
109     vm: &SafeDescriptor,
110     slot: MemSlot,
111     read_only: bool,
112     guest_addr: u64,
113     memory_size: u64,
114     userspace_addr: *mut u8,
115 ) -> Result<()> {
116     let mut flags = 0;
117 
118     flags |= GH_MEM_ALLOW_READ | GH_MEM_ALLOW_EXEC;
119     if !read_only {
120         flags |= GH_MEM_ALLOW_WRITE;
121     }
122 
123     let region = gh_userspace_memory_region {
124         label: slot,
125         flags,
126         guest_phys_addr: guest_addr,
127         memory_size,
128         userspace_addr: userspace_addr as u64,
129     };
130 
131     let ret = ioctl_with_ref(vm, GH_VM_ANDROID_LEND_USER_MEM, &region);
132     if ret == 0 {
133         Ok(())
134     } else {
135         errno_result()
136     }
137 }
138 
139 // Wrapper around GH_SET_USER_MEMORY_REGION ioctl, which creates, modifies, or deletes a mapping
140 // from guest physical to host user pages.
141 //
142 // SAFETY:
143 // Safe when the guest regions are guaranteed not to overlap.
set_user_memory_region( vm: &SafeDescriptor, slot: MemSlot, read_only: bool, guest_addr: u64, memory_size: u64, userspace_addr: *mut u8, ) -> Result<()>144 unsafe fn set_user_memory_region(
145     vm: &SafeDescriptor,
146     slot: MemSlot,
147     read_only: bool,
148     guest_addr: u64,
149     memory_size: u64,
150     userspace_addr: *mut u8,
151 ) -> Result<()> {
152     let mut flags = 0;
153 
154     flags |= GH_MEM_ALLOW_READ | GH_MEM_ALLOW_EXEC;
155     if !read_only {
156         flags |= GH_MEM_ALLOW_WRITE;
157     }
158 
159     let region = gh_userspace_memory_region {
160         label: slot,
161         flags,
162         guest_phys_addr: guest_addr,
163         memory_size,
164         userspace_addr: userspace_addr as u64,
165     };
166 
167     let ret = ioctl_with_ref(vm, GH_VM_SET_USER_MEM_REGION, &region);
168     if ret == 0 {
169         Ok(())
170     } else {
171         errno_result()
172     }
173 }
174 
map_cma_region( vm: &SafeDescriptor, slot: MemSlot, lend: bool, read_only: bool, guest_addr: u64, guest_mem_fd: u32, size: u64, offset: u64, ) -> Result<()>175 fn map_cma_region(
176     vm: &SafeDescriptor,
177     slot: MemSlot,
178     lend: bool,
179     read_only: bool,
180     guest_addr: u64,
181     guest_mem_fd: u32,
182     size: u64,
183     offset: u64,
184 ) -> Result<()> {
185     let mut flags = 0;
186     flags |= GUNYAH_MEM_ALLOW_READ | GUNYAH_MEM_ALLOW_EXEC;
187     if !read_only {
188         flags |= GUNYAH_MEM_ALLOW_WRITE;
189     }
190     if lend {
191         flags |= GUNYAH_MEM_FORCE_LEND;
192     }
193     else {
194         flags |= GUNYAH_MEM_FORCE_SHARE;
195     }
196     let region = gunyah_map_cma_mem_args {
197         label: slot,
198         guest_addr,
199         flags,
200         guest_mem_fd,
201         offset,
202         size,
203     };
204     // SAFETY: safe because the return value is checked.
205     let ret = unsafe { ioctl_with_ref(vm, GH_VM_ANDROID_MAP_CMA_MEM, &region) };
206     if ret == 0 {
207         Ok(())
208     } else {
209         errno_result()
210     }
211 }
212 
213 #[derive(PartialEq, Eq, Hash)]
214 pub struct GunyahIrqRoute {
215     irq: u32,
216     level: bool,
217 }
218 
219 pub struct GunyahVm {
220     gh: Gunyah,
221     vm: SafeDescriptor,
222     vm_id: Option<u16>,
223     pas_id: Option<u32>,
224     guest_mem: GuestMemory,
225     mem_regions: Arc<Mutex<BTreeMap<MemSlot, (Box<dyn MappedRegion>, GuestAddress)>>>,
226     /// A min heap of MemSlot numbers that were used and then removed and can now be re-used
227     mem_slot_gaps: Arc<Mutex<BinaryHeap<Reverse<MemSlot>>>>,
228     routes: Arc<Mutex<HashSet<GunyahIrqRoute>>>,
229     hv_cfg: crate::Config,
230 }
231 
232 impl AsRawDescriptor for GunyahVm {
as_raw_descriptor(&self) -> RawDescriptor233     fn as_raw_descriptor(&self) -> RawDescriptor {
234         self.vm.as_raw_descriptor()
235     }
236 }
237 
238 impl GunyahVm {
new(gh: &Gunyah, vm_id: Option<u16>, pas_id: Option<u32>, guest_mem: GuestMemory, cfg: Config) -> Result<GunyahVm>239     pub fn new(gh: &Gunyah, vm_id: Option<u16>, pas_id: Option<u32>, guest_mem: GuestMemory, cfg: Config) -> Result<GunyahVm> {
240         // SAFETY:
241         // Safe because we know gunyah is a real gunyah fd as this module is the only one that can
242         // make Gunyah objects.
243         let ret = unsafe { ioctl_with_val(gh, GH_CREATE_VM, 0 as c_ulong) };
244         if ret < 0 {
245             return errno_result();
246         }
247 
248         // SAFETY:
249         // Safe because we verify that ret is valid and we own the fd.
250         let vm_descriptor = unsafe { SafeDescriptor::from_raw_descriptor(ret) };
251         for region in guest_mem.regions() {
252             let lend = if cfg.protection_type.isolates_memory() {
253                 match region.options.purpose {
254                     MemoryRegionPurpose::Bios => true,
255                     MemoryRegionPurpose::GuestMemoryRegion => true,
256                     #[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
257                     MemoryRegionPurpose::ProtectedFirmwareRegion => true,
258                     MemoryRegionPurpose::ReservedMemory => true,
259                     #[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
260                     MemoryRegionPurpose::StaticSwiotlbRegion => false,
261                 }
262             } else {
263                 false
264             };
265             if region.options.file_backed.is_some() {
266                 map_cma_region(
267                         &vm_descriptor,
268                         region.index as MemSlot,
269                         lend,
270                         !region.options.file_backed.unwrap().writable,
271                         region.guest_addr.offset(),
272                         region.shm.as_raw_descriptor().try_into().unwrap(),
273                         region.size.try_into().unwrap(),
274                         region.shm_offset,
275                 )?;
276             } else if lend {
277                 // SAFETY:
278                 // Safe because the guest regions are guarnteed not to overlap.
279                 unsafe {
280                     android_lend_user_memory_region(
281                         &vm_descriptor,
282                         region.index as MemSlot,
283                         false,
284                         region.guest_addr.offset(),
285                         region.size.try_into().unwrap(),
286                         region.host_addr as *mut u8,
287                     )?;
288                 }
289             } else {
290                 // SAFETY:
291                 // Safe because the guest regions are guarnteed not to overlap.
292                 unsafe {
293                     set_user_memory_region(
294                         &vm_descriptor,
295                         region.index as MemSlot,
296                         false,
297                         region.guest_addr.offset(),
298                         region.size.try_into().unwrap(),
299                         region.host_addr as *mut u8,
300                     )?;
301                 }
302             }
303         }
304 
305         Ok(GunyahVm {
306             gh: gh.try_clone()?,
307             vm: vm_descriptor,
308             vm_id,
309             pas_id,
310             guest_mem,
311             mem_regions: Arc::new(Mutex::new(BTreeMap::new())),
312             mem_slot_gaps: Arc::new(Mutex::new(BinaryHeap::new())),
313             routes: Arc::new(Mutex::new(HashSet::new())),
314             hv_cfg: cfg,
315         })
316     }
317 
set_vm_auth_type_to_qcom_trusted_vm(&self, payload_start: GuestAddress, payload_size: u64) -> Result<()>318     pub fn set_vm_auth_type_to_qcom_trusted_vm(&self, payload_start: GuestAddress, payload_size: u64) -> Result<()> {
319         let gunyah_qtvm_auth_arg = gunyah_qtvm_auth_arg {
320             vm_id: self.vm_id.expect("VM ID not specified for a QTVM"),
321             pas_id: self.pas_id.expect("PAS ID not specified for a QTVM"),
322             // QTVMs have the metadata needed for authentication at the start of the guest addrspace.
323             guest_phys_addr: payload_start.offset(),
324             size: payload_size,
325         };
326         let gunyah_auth_desc = gunyah_auth_desc {
327             type_: gunyah_auth_type_GUNYAH_QCOM_TRUSTED_VM_TYPE,
328             arg_size: size_of::<gunyah_qtvm_auth_arg>() as u32,
329             arg: &gunyah_qtvm_auth_arg as *const gunyah_qtvm_auth_arg as u64,
330         };
331         // SAFETY: safe because the return value is checked.
332         let ret = unsafe { ioctl_with_ref(self, GH_VM_ANDROID_SET_AUTH_TYPE, &gunyah_auth_desc) };
333         if ret == 0 {
334             Ok(())
335         } else {
336             errno_result()
337         }
338     }
339 
create_vcpu(&self, id: usize) -> Result<GunyahVcpu>340     fn create_vcpu(&self, id: usize) -> Result<GunyahVcpu> {
341         let gh_fn_vcpu_arg = gh_fn_vcpu_arg {
342             id: id.try_into().unwrap(),
343         };
344 
345         let function_desc = gh_fn_desc {
346             type_: GH_FN_VCPU,
347             arg_size: size_of::<gh_fn_vcpu_arg>() as u32,
348             // Safe because kernel is expecting pointer with non-zero arg_size
349             arg: &gh_fn_vcpu_arg as *const gh_fn_vcpu_arg as u64,
350         };
351 
352         // SAFETY:
353         // Safe because we know that our file is a VM fd and we verify the return result.
354         let fd = unsafe { ioctl_with_ref(self, GH_VM_ADD_FUNCTION, &function_desc) };
355         if fd < 0 {
356             return errno_result();
357         }
358 
359         // SAFETY:
360         // Wrap the vcpu now in case the following ? returns early. This is safe because we verified
361         // the value of the fd and we own the fd.
362         let vcpu = unsafe { File::from_raw_descriptor(fd) };
363 
364         // SAFETY:
365         // Safe because we know this is a Gunyah VCPU
366         let res = unsafe { ioctl(&vcpu, GH_VCPU_MMAP_SIZE) };
367         if res < 0 {
368             return errno_result();
369         }
370         let run_mmap_size = res as usize;
371 
372         let run_mmap = MemoryMappingBuilder::new(run_mmap_size)
373             .from_file(&vcpu)
374             .build()
375             .map_err(|_| Error::new(ENOSPC))?;
376 
377         Ok(GunyahVcpu {
378             vm: self.vm.try_clone()?,
379             vcpu,
380             id,
381             run_mmap: Arc::new(run_mmap),
382         })
383     }
384 
register_irqfd(&self, label: u32, evt: &Event, level: bool) -> Result<()>385     pub fn register_irqfd(&self, label: u32, evt: &Event, level: bool) -> Result<()> {
386         let gh_fn_irqfd_arg = gh_fn_irqfd_arg {
387             fd: evt.as_raw_descriptor() as u32,
388             label,
389             flags: if level { GH_IRQFD_LEVEL } else { 0 },
390             ..Default::default()
391         };
392 
393         let function_desc = gh_fn_desc {
394             type_: GH_FN_IRQFD,
395             arg_size: size_of::<gh_fn_irqfd_arg>() as u32,
396             // SAFETY:
397             // Safe because kernel is expecting pointer with non-zero arg_size
398             arg: &gh_fn_irqfd_arg as *const gh_fn_irqfd_arg as u64,
399         };
400 
401         // SAFETY: safe because the return value is checked.
402         let ret = unsafe { ioctl_with_ref(self, GH_VM_ADD_FUNCTION, &function_desc) };
403         if ret == 0 {
404             self.routes
405                 .lock()
406                 .insert(GunyahIrqRoute { irq: label, level });
407             Ok(())
408         } else {
409             errno_result()
410         }
411     }
412 
unregister_irqfd(&self, label: u32, _evt: &Event) -> Result<()>413     pub fn unregister_irqfd(&self, label: u32, _evt: &Event) -> Result<()> {
414         let gh_fn_irqfd_arg = gh_fn_irqfd_arg {
415             label,
416             ..Default::default()
417         };
418 
419         let function_desc = gh_fn_desc {
420             type_: GH_FN_IRQFD,
421             arg_size: size_of::<gh_fn_irqfd_arg>() as u32,
422             // Safe because kernel is expecting pointer with non-zero arg_size
423             arg: &gh_fn_irqfd_arg as *const gh_fn_irqfd_arg as u64,
424         };
425 
426         // SAFETY: safe because memory is not modified and the return value is checked.
427         let ret = unsafe { ioctl_with_ref(self, GH_VM_REMOVE_FUNCTION, &function_desc) };
428         if ret == 0 {
429             Ok(())
430         } else {
431             errno_result()
432         }
433     }
434 
try_clone(&self) -> Result<Self> where Self: Sized,435     pub fn try_clone(&self) -> Result<Self>
436     where
437         Self: Sized,
438     {
439         Ok(GunyahVm {
440             gh: self.gh.try_clone()?,
441             vm: self.vm.try_clone()?,
442             vm_id: self.vm_id,
443             pas_id: self.pas_id,
444             guest_mem: self.guest_mem.clone(),
445             mem_regions: self.mem_regions.clone(),
446             mem_slot_gaps: self.mem_slot_gaps.clone(),
447             routes: self.routes.clone(),
448             hv_cfg: self.hv_cfg,
449         })
450     }
451 
set_dtb_config(&self, fdt_address: GuestAddress, fdt_size: usize) -> Result<()>452     fn set_dtb_config(&self, fdt_address: GuestAddress, fdt_size: usize) -> Result<()> {
453         let dtb_config = gh_vm_dtb_config {
454             guest_phys_addr: fdt_address.offset(),
455             size: fdt_size.try_into().unwrap(),
456         };
457 
458         // SAFETY:
459         // Safe because we know this is a Gunyah VM
460         let ret = unsafe { ioctl_with_ref(self, GH_VM_SET_DTB_CONFIG, &dtb_config) };
461         if ret == 0 {
462             Ok(())
463         } else {
464             errno_result()
465         }
466     }
467 
set_protected_vm_firmware_ipa(&self, fw_addr: GuestAddress, fw_size: u64) -> Result<()>468     fn set_protected_vm_firmware_ipa(&self, fw_addr: GuestAddress, fw_size: u64) -> Result<()> {
469         let fw_config = gh_vm_firmware_config {
470             guest_phys_addr: fw_addr.offset(),
471             size: fw_size,
472         };
473 
474         // SAFETY:
475         // Safe because we know this is a Gunyah VM
476         let ret = unsafe { ioctl_with_ref(self, GH_VM_ANDROID_SET_FW_CONFIG, &fw_config) };
477         if ret == 0 {
478             Ok(())
479         } else {
480             errno_result()
481         }
482     }
483 
set_boot_pc(&self, value: u64) -> Result<()>484     fn set_boot_pc(&self, value: u64) -> Result<()> {
485         self.set_boot_context(gh_vm_boot_context_reg::REG_SET_PC, 0, value)
486     }
487 
488     // Sets the boot context for the Gunyah VM by specifying the register type, index, and value.
set_boot_context( &self, reg_type: gh_vm_boot_context_reg::Type, reg_idx: u8, value: u64, ) -> Result<()>489     fn set_boot_context(
490         &self,
491         reg_type: gh_vm_boot_context_reg::Type,
492         reg_idx: u8,
493         value: u64,
494     ) -> Result<()> {
495         let reg_id = boot_context_reg_id(reg_type, reg_idx);
496         let boot_context = gh_vm_boot_context {
497             reg: reg_id,
498             value,
499             ..Default::default()
500         };
501 
502         // SAFETY: Safe because we ensure the boot_context is correctly initialized
503         // and the ioctl call is checked.
504         let ret = unsafe { ioctl_with_ref(self, GH_VM_SET_BOOT_CONTEXT, &boot_context) };
505         if ret == 0 {
506             Ok(())
507         } else {
508             errno_result()
509         }
510     }
511 
start(&self) -> Result<()>512     fn start(&self) -> Result<()> {
513         // SAFETY: safe because memory is not modified and the return value is checked.
514         let ret = unsafe { ioctl(self, GH_VM_START) };
515         if ret == 0 {
516             Ok(())
517         } else {
518             errno_result()
519         }
520     }
521 
handle_inflate(&self, guest_addr: GuestAddress, size: u64) -> Result<()>522     fn handle_inflate(&self, guest_addr: GuestAddress, size: u64) -> Result<()> {
523         let range = gunyah_address_range {
524             guest_phys_addr: guest_addr.0,
525             size,
526         };
527 
528         // SAFETY: Safe because we know this is a Gunyah VM
529         let ret = unsafe { ioctl_with_ref(self, GH_VM_RECLAIM_REGION, &range) };
530         if ret != 0 {
531             warn!("Gunyah failed to reclaim {:?}", range);
532             return errno_result();
533         }
534 
535         match self.guest_mem.remove_range(guest_addr, size) {
536             Ok(_) => Ok(()),
537             Err(vm_memory::Error::MemoryAccess(_, MmapError::SystemCallFailed(e))) => Err(e),
538             Err(_) => Err(Error::new(EIO)),
539         }
540     }
541 }
542 
543 impl Vm for GunyahVm {
try_clone(&self) -> Result<Self> where Self: Sized,544     fn try_clone(&self) -> Result<Self>
545     where
546         Self: Sized,
547     {
548         Ok(GunyahVm {
549             gh: self.gh.try_clone()?,
550             vm: self.vm.try_clone()?,
551             vm_id: self.vm_id,
552             pas_id: self.pas_id,
553             guest_mem: self.guest_mem.clone(),
554             mem_regions: self.mem_regions.clone(),
555             mem_slot_gaps: self.mem_slot_gaps.clone(),
556             routes: self.routes.clone(),
557             hv_cfg: self.hv_cfg,
558         })
559     }
560 
try_clone_descriptor(&self) -> Result<SafeDescriptor>561     fn try_clone_descriptor(&self) -> Result<SafeDescriptor> {
562         error!("try_clone_descriptor hasn't been tested on gunyah, returning -ENOTSUP");
563         Err(Error::new(ENOTSUP))
564     }
565 
hypervisor_kind(&self) -> HypervisorKind566     fn hypervisor_kind(&self) -> HypervisorKind {
567         HypervisorKind::Gunyah
568     }
569 
check_capability(&self, c: VmCap) -> bool570     fn check_capability(&self, c: VmCap) -> bool {
571         match c {
572             VmCap::DirtyLog => false,
573             // Strictly speaking, Gunyah supports pvclock, but Gunyah takes care
574             // of it and crosvm doesn't need to do anything for it
575             VmCap::PvClock => false,
576             VmCap::Protected => true,
577             VmCap::EarlyInitCpuid => false,
578             #[cfg(target_arch = "x86_64")]
579             VmCap::BusLockDetect => false,
580             VmCap::ReadOnlyMemoryRegion => false,
581             VmCap::MemNoncoherentDma => false,
582             #[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
583             VmCap::Sve => false,
584         }
585     }
586 
get_guest_phys_addr_bits(&self) -> u8587     fn get_guest_phys_addr_bits(&self) -> u8 {
588         40
589     }
590 
get_memory(&self) -> &GuestMemory591     fn get_memory(&self) -> &GuestMemory {
592         &self.guest_mem
593     }
594 
add_memory_region( &mut self, guest_addr: GuestAddress, mem_region: Box<dyn MappedRegion>, read_only: bool, _log_dirty_pages: bool, _cache: MemCacheType, ) -> Result<MemSlot>595     fn add_memory_region(
596         &mut self,
597         guest_addr: GuestAddress,
598         mem_region: Box<dyn MappedRegion>,
599         read_only: bool,
600         _log_dirty_pages: bool,
601         _cache: MemCacheType,
602     ) -> Result<MemSlot> {
603         let pgsz = pagesize() as u64;
604         // Gunyah require to set the user memory region with page size aligned size. Safe to extend
605         // the mem.size() to be page size aligned because the mmap will round up the size to be
606         // page size aligned if it is not.
607         let size = (mem_region.size() as u64 + pgsz - 1) / pgsz * pgsz;
608         let end_addr = guest_addr.checked_add(size).ok_or(Error::new(EOVERFLOW))?;
609 
610         if self.guest_mem.range_overlap(guest_addr, end_addr) {
611             return Err(Error::new(ENOSPC));
612         }
613 
614         let mut regions = self.mem_regions.lock();
615         let mut gaps = self.mem_slot_gaps.lock();
616         let slot = match gaps.pop() {
617             Some(gap) => gap.0,
618             None => (regions.len() + self.guest_mem.num_regions() as usize) as MemSlot,
619         };
620 
621         // SAFETY: safe because memory is not modified and the return value is checked.
622         let res = unsafe {
623             set_user_memory_region(
624                 &self.vm,
625                 slot,
626                 read_only,
627                 guest_addr.offset(),
628                 size,
629                 mem_region.as_ptr(),
630             )
631         };
632 
633         if let Err(e) = res {
634             gaps.push(Reverse(slot));
635             return Err(e);
636         }
637         regions.insert(slot, (mem_region, guest_addr));
638         Ok(slot)
639     }
640 
msync_memory_region(&mut self, slot: MemSlot, offset: usize, size: usize) -> Result<()>641     fn msync_memory_region(&mut self, slot: MemSlot, offset: usize, size: usize) -> Result<()> {
642         let mut regions = self.mem_regions.lock();
643         let (mem, _) = regions.get_mut(&slot).ok_or_else(|| Error::new(ENOENT))?;
644 
645         mem.msync(offset, size).map_err(|err| match err {
646             MmapError::InvalidAddress => Error::new(EFAULT),
647             MmapError::NotPageAligned => Error::new(EINVAL),
648             MmapError::SystemCallFailed(e) => e,
649             _ => Error::new(EIO),
650         })
651     }
652 
madvise_pageout_memory_region( &mut self, _slot: MemSlot, _offset: usize, _size: usize, ) -> Result<()>653     fn madvise_pageout_memory_region(
654         &mut self,
655         _slot: MemSlot,
656         _offset: usize,
657         _size: usize,
658     ) -> Result<()> {
659         Err(Error::new(ENOTSUP))
660     }
661 
madvise_remove_memory_region( &mut self, _slot: MemSlot, _offset: usize, _size: usize, ) -> Result<()>662     fn madvise_remove_memory_region(
663         &mut self,
664         _slot: MemSlot,
665         _offset: usize,
666         _size: usize,
667     ) -> Result<()> {
668         Err(Error::new(ENOTSUP))
669     }
670 
remove_memory_region(&mut self, _slot: MemSlot) -> Result<Box<dyn MappedRegion>>671     fn remove_memory_region(&mut self, _slot: MemSlot) -> Result<Box<dyn MappedRegion>> {
672         unimplemented!()
673     }
674 
create_device(&self, _kind: DeviceKind) -> Result<SafeDescriptor>675     fn create_device(&self, _kind: DeviceKind) -> Result<SafeDescriptor> {
676         unimplemented!()
677     }
678 
get_dirty_log(&self, _slot: MemSlot, _dirty_log: &mut [u8]) -> Result<()>679     fn get_dirty_log(&self, _slot: MemSlot, _dirty_log: &mut [u8]) -> Result<()> {
680         unimplemented!()
681     }
682 
register_ioevent( &mut self, evt: &Event, addr: IoEventAddress, datamatch: Datamatch, ) -> Result<()>683     fn register_ioevent(
684         &mut self,
685         evt: &Event,
686         addr: IoEventAddress,
687         datamatch: Datamatch,
688     ) -> Result<()> {
689         let (do_datamatch, datamatch_value, datamatch_len) = match datamatch {
690             Datamatch::AnyLength => (false, 0, 0),
691             Datamatch::U8(v) => match v {
692                 Some(u) => (true, u as u64, 1),
693                 None => (false, 0, 1),
694             },
695             Datamatch::U16(v) => match v {
696                 Some(u) => (true, u as u64, 2),
697                 None => (false, 0, 2),
698             },
699             Datamatch::U32(v) => match v {
700                 Some(u) => (true, u as u64, 4),
701                 None => (false, 0, 4),
702             },
703             Datamatch::U64(v) => match v {
704                 Some(u) => (true, u, 8),
705                 None => (false, 0, 8),
706             },
707         };
708 
709         let mut flags = 0;
710         if do_datamatch {
711             flags |= 1 << GH_IOEVENTFD_DATAMATCH;
712         }
713 
714         let maddr = if let IoEventAddress::Mmio(maddr) = addr {
715             maddr
716         } else {
717             todo!()
718         };
719 
720         let gh_fn_ioeventfd_arg = gh_fn_ioeventfd_arg {
721             fd: evt.as_raw_descriptor(),
722             datamatch: datamatch_value,
723             len: datamatch_len,
724             addr: maddr,
725             flags,
726             ..Default::default()
727         };
728 
729         let function_desc = gh_fn_desc {
730             type_: GH_FN_IOEVENTFD,
731             arg_size: size_of::<gh_fn_ioeventfd_arg>() as u32,
732             arg: &gh_fn_ioeventfd_arg as *const gh_fn_ioeventfd_arg as u64,
733         };
734 
735         // SAFETY: safe because memory is not modified and the return value is checked.
736         let ret = unsafe { ioctl_with_ref(self, GH_VM_ADD_FUNCTION, &function_desc) };
737         if ret == 0 {
738             Ok(())
739         } else {
740             errno_result()
741         }
742     }
743 
unregister_ioevent( &mut self, _evt: &Event, addr: IoEventAddress, _datamatch: Datamatch, ) -> Result<()>744     fn unregister_ioevent(
745         &mut self,
746         _evt: &Event,
747         addr: IoEventAddress,
748         _datamatch: Datamatch,
749     ) -> Result<()> {
750         let maddr = if let IoEventAddress::Mmio(maddr) = addr {
751             maddr
752         } else {
753             todo!()
754         };
755 
756         let gh_fn_ioeventfd_arg = gh_fn_ioeventfd_arg {
757             addr: maddr,
758             ..Default::default()
759         };
760 
761         let function_desc = gh_fn_desc {
762             type_: GH_FN_IOEVENTFD,
763             arg_size: size_of::<gh_fn_ioeventfd_arg>() as u32,
764             arg: &gh_fn_ioeventfd_arg as *const gh_fn_ioeventfd_arg as u64,
765         };
766 
767         // SAFETY: safe because memory is not modified and the return value is checked.
768         let ret = unsafe { ioctl_with_ref(self, GH_VM_REMOVE_FUNCTION, &function_desc) };
769         if ret == 0 {
770             Ok(())
771         } else {
772             errno_result()
773         }
774     }
775 
handle_io_events(&self, _addr: IoEventAddress, _data: &[u8]) -> Result<()>776     fn handle_io_events(&self, _addr: IoEventAddress, _data: &[u8]) -> Result<()> {
777         Ok(())
778     }
779 
get_pvclock(&self) -> Result<ClockState>780     fn get_pvclock(&self) -> Result<ClockState> {
781         unimplemented!()
782     }
783 
set_pvclock(&self, _state: &ClockState) -> Result<()>784     fn set_pvclock(&self, _state: &ClockState) -> Result<()> {
785         unimplemented!()
786     }
787 
add_fd_mapping( &mut self, slot: u32, offset: usize, size: usize, fd: &dyn AsRawDescriptor, fd_offset: u64, prot: Protection, ) -> Result<()>788     fn add_fd_mapping(
789         &mut self,
790         slot: u32,
791         offset: usize,
792         size: usize,
793         fd: &dyn AsRawDescriptor,
794         fd_offset: u64,
795         prot: Protection,
796     ) -> Result<()> {
797         let mut regions = self.mem_regions.lock();
798         let (region, _) = regions.get_mut(&slot).ok_or_else(|| Error::new(EINVAL))?;
799 
800         match region.add_fd_mapping(offset, size, fd, fd_offset, prot) {
801             Ok(()) => Ok(()),
802             Err(MmapError::SystemCallFailed(e)) => Err(e),
803             Err(_) => Err(Error::new(EIO)),
804         }
805     }
806 
remove_mapping(&mut self, slot: u32, offset: usize, size: usize) -> Result<()>807     fn remove_mapping(&mut self, slot: u32, offset: usize, size: usize) -> Result<()> {
808         let mut regions = self.mem_regions.lock();
809         let (region, _) = regions.get_mut(&slot).ok_or_else(|| Error::new(EINVAL))?;
810 
811         match region.remove_mapping(offset, size) {
812             Ok(()) => Ok(()),
813             Err(MmapError::SystemCallFailed(e)) => Err(e),
814             Err(_) => Err(Error::new(EIO)),
815         }
816     }
817 
handle_balloon_event(&mut self, event: BalloonEvent) -> Result<()>818     fn handle_balloon_event(&mut self, event: BalloonEvent) -> Result<()> {
819         match event {
820             BalloonEvent::Inflate(m) => self.handle_inflate(m.guest_address, m.size),
821             BalloonEvent::Deflate(m) => Ok(()),
822             BalloonEvent::BalloonTargetReached(_) => Ok(()),
823         }
824     }
825 }
826 
827 const GH_RM_EXIT_TYPE_VM_EXIT: u16 = 0;
828 const GH_RM_EXIT_TYPE_PSCI_POWER_OFF: u16 = 1;
829 const GH_RM_EXIT_TYPE_PSCI_SYSTEM_RESET: u16 = 2;
830 const GH_RM_EXIT_TYPE_PSCI_SYSTEM_RESET2: u16 = 3;
831 const GH_RM_EXIT_TYPE_WDT_BITE: u16 = 4;
832 const GH_RM_EXIT_TYPE_HYP_ERROR: u16 = 5;
833 const GH_RM_EXIT_TYPE_ASYNC_EXT_ABORT: u16 = 6;
834 const GH_RM_EXIT_TYPE_VM_FORCE_STOPPED: u16 = 7;
835 
836 pub struct GunyahVcpu {
837     vm: SafeDescriptor,
838     vcpu: File,
839     id: usize,
840     run_mmap: Arc<MemoryMapping>,
841 }
842 
843 struct GunyahVcpuSignalHandle {
844     run_mmap: Arc<MemoryMapping>,
845 }
846 
847 impl VcpuSignalHandleInner for GunyahVcpuSignalHandle {
signal_immediate_exit(&self)848     fn signal_immediate_exit(&self) {
849         // SAFETY: we ensure `run_mmap` is a valid mapping of `kvm_run` at creation time, and the
850         // `Arc` ensures the mapping still exists while we hold a reference to it.
851         unsafe {
852             let run = self.run_mmap.as_ptr() as *mut gh_vcpu_run;
853             (*run).immediate_exit = 1;
854         }
855     }
856 }
857 
858 impl AsRawDescriptor for GunyahVcpu {
as_raw_descriptor(&self) -> RawDescriptor859     fn as_raw_descriptor(&self) -> RawDescriptor {
860         self.vcpu.as_raw_descriptor()
861     }
862 }
863 
864 impl Vcpu for GunyahVcpu {
try_clone(&self) -> Result<Self> where Self: Sized,865     fn try_clone(&self) -> Result<Self>
866     where
867         Self: Sized,
868     {
869         let vcpu = self.vcpu.try_clone()?;
870 
871         Ok(GunyahVcpu {
872             vm: self.vm.try_clone()?,
873             vcpu,
874             id: self.id,
875             run_mmap: self.run_mmap.clone(),
876         })
877     }
878 
as_vcpu(&self) -> &dyn Vcpu879     fn as_vcpu(&self) -> &dyn Vcpu {
880         self
881     }
882 
run(&mut self) -> Result<VcpuExit>883     fn run(&mut self) -> Result<VcpuExit> {
884         // SAFETY:
885         // Safe because we know our file is a VCPU fd and we verify the return result.
886         let ret = unsafe { ioctl(self, GH_VCPU_RUN) };
887         if ret != 0 {
888             return errno_result();
889         }
890 
891         // SAFETY:
892         // Safe because we know we mapped enough memory to hold the gh_vcpu_run struct
893         // because the kernel told us how large it is.
894         let run = unsafe { &mut *(self.run_mmap.as_ptr() as *mut gh_vcpu_run) };
895         match run.exit_reason {
896             GH_VCPU_EXIT_MMIO => Ok(VcpuExit::Mmio),
897             GH_VCPU_EXIT_STATUS => {
898                 // SAFETY:
899                 // Safe because the exit_reason (which comes from the kernel) told us which
900                 // union field to use.
901                 let status = unsafe { &mut run.__bindgen_anon_1.status };
902                 match status.status {
903                     GH_VM_STATUS_GH_VM_STATUS_LOAD_FAILED => Ok(VcpuExit::FailEntry {
904                         hardware_entry_failure_reason: 0,
905                     }),
906                     GH_VM_STATUS_GH_VM_STATUS_CRASHED => Ok(VcpuExit::SystemEventCrash),
907                     GH_VM_STATUS_GH_VM_STATUS_EXITED => {
908                         info!("exit type {}", status.exit_info.type_);
909                         match status.exit_info.type_ {
910                             GH_RM_EXIT_TYPE_VM_EXIT => Ok(VcpuExit::SystemEventShutdown),
911                             GH_RM_EXIT_TYPE_PSCI_POWER_OFF => Ok(VcpuExit::SystemEventShutdown),
912                             GH_RM_EXIT_TYPE_PSCI_SYSTEM_RESET => Ok(VcpuExit::SystemEventReset),
913                             GH_RM_EXIT_TYPE_PSCI_SYSTEM_RESET2 => Ok(VcpuExit::SystemEventReset),
914                             GH_RM_EXIT_TYPE_WDT_BITE => Ok(VcpuExit::SystemEventCrash),
915                             GH_RM_EXIT_TYPE_HYP_ERROR => Ok(VcpuExit::SystemEventCrash),
916                             GH_RM_EXIT_TYPE_ASYNC_EXT_ABORT => Ok(VcpuExit::SystemEventCrash),
917                             GH_RM_EXIT_TYPE_VM_FORCE_STOPPED => Ok(VcpuExit::SystemEventShutdown),
918                             r => {
919                                 warn!("Unknown exit type: {}", r);
920                                 Err(Error::new(EINVAL))
921                             }
922                         }
923                     }
924                     r => {
925                         warn!("Unknown vm status: {}", r);
926                         Err(Error::new(EINVAL))
927                     }
928                 }
929             }
930             r => {
931                 warn!("unknown gh exit reason: {}", r);
932                 Err(Error::new(EINVAL))
933             }
934         }
935     }
936 
id(&self) -> usize937     fn id(&self) -> usize {
938         self.id
939     }
940 
set_immediate_exit(&self, exit: bool)941     fn set_immediate_exit(&self, exit: bool) {
942         // SAFETY:
943         // Safe because we know we mapped enough memory to hold the kvm_run struct because the
944         // kernel told us how large it was. The pointer is page aligned so casting to a different
945         // type is well defined, hence the clippy allow attribute.
946         let run = unsafe { &mut *(self.run_mmap.as_ptr() as *mut gh_vcpu_run) };
947         run.immediate_exit = exit.into();
948     }
949 
signal_handle(&self) -> VcpuSignalHandle950     fn signal_handle(&self) -> VcpuSignalHandle {
951         VcpuSignalHandle {
952             inner: Box::new(GunyahVcpuSignalHandle {
953                 run_mmap: self.run_mmap.clone(),
954             }),
955         }
956     }
957 
handle_mmio(&self, handle_fn: &mut dyn FnMut(IoParams) -> Result<()>) -> Result<()>958     fn handle_mmio(&self, handle_fn: &mut dyn FnMut(IoParams) -> Result<()>) -> Result<()> {
959         // SAFETY:
960         // Safe because we know we mapped enough memory to hold the gh_vcpu_run struct because the
961         // kernel told us how large it was. The pointer is page aligned so casting to a different
962         // type is well defined
963         let run = unsafe { &mut *(self.run_mmap.as_ptr() as *mut gh_vcpu_run) };
964         // Verify that the handler is called in the right context.
965         assert!(run.exit_reason == GH_VCPU_EXIT_MMIO);
966         // SAFETY:
967         // Safe because the exit_reason (which comes from the kernel) told us which
968         // union field to use.
969         let mmio = unsafe { &mut run.__bindgen_anon_1.mmio };
970         let address = mmio.phys_addr;
971         let data = &mut mmio.data[..mmio.len as usize];
972         if mmio.is_write != 0 {
973             handle_fn(IoParams {
974                 address,
975                 operation: IoOperation::Write(data),
976             })
977         } else {
978             handle_fn(IoParams {
979                 address,
980                 operation: IoOperation::Read(data),
981             })
982         }
983     }
984 
handle_io(&self, _handle_fn: &mut dyn FnMut(IoParams)) -> Result<()>985     fn handle_io(&self, _handle_fn: &mut dyn FnMut(IoParams)) -> Result<()> {
986         unreachable!()
987     }
988 
on_suspend(&self) -> Result<()>989     fn on_suspend(&self) -> Result<()> {
990         Ok(())
991     }
992 
enable_raw_capability(&self, _cap: u32, _args: &[u64; 4]) -> Result<()>993     unsafe fn enable_raw_capability(&self, _cap: u32, _args: &[u64; 4]) -> Result<()> {
994         unimplemented!()
995     }
996 }
997