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, ®ion);
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, ®ion);
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, ®ion) };
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