| // Copyright 2017 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| //! Type-safe bindings for Zircon port objects. |
| |
| use { |
| crate::{ |
| guest, ok, AsHandleRef, GPAddr, Handle, HandleBased, HandleRef, Packet, Signals, Status, |
| Time, VcpuContents, |
| }, |
| bitflags::bitflags, |
| fuchsia_zircon_sys as sys, |
| std::mem, |
| }; |
| |
| /// An object representing a Zircon |
| /// [port](https://fuchsia.dev/fuchsia-src/concepts/objects/port.md). |
| /// |
| /// As essentially a subtype of `Handle`, it can be freely interconverted. |
| #[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] |
| #[repr(transparent)] |
| pub struct Port(Handle); |
| impl_handle_based!(Port); |
| |
| /// The contents of a `Packet`. |
| #[derive(Debug, Copy, Clone)] |
| pub enum PacketContents { |
| /// A user-generated packet. |
| User(UserPacket), |
| /// A one-shot signal packet generated via `object_wait_async`. |
| SignalOne(SignalPacket), |
| /// A guest bell packet |
| GuestBell(GuestBellPacket), |
| /// A guest mem packet |
| GuestMem(GuestMemPacket), |
| /// A guest I/O packet |
| GuestIo(GuestIoPacket), |
| /// A guest VCPU packet |
| GuestVcpu(GuestVcpuPacket), |
| /// Pager packets |
| Pager(PagerPacket), |
| |
| #[doc(hidden)] |
| __Nonexhaustive, |
| } |
| |
| /// Contents of a user packet (one sent by `port_queue`). This is a type-safe wrapper for |
| /// [zx_packet_user_t](https://fuchsia.dev/fuchsia-src/reference/syscalls/port_wait.md). |
| #[derive(Debug, Default, Copy, Clone)] |
| pub struct UserPacket(sys::zx_packet_user_t); |
| |
| /// Contents of a signal packet (one generated by the kernel). This is a type-safe wrapper for |
| /// [zx_packet_signal_t](https://fuchsia.dev/fuchsia-src/reference/syscalls/port_wait.md). |
| #[derive(Debug, Copy, Clone)] |
| pub struct SignalPacket(sys::zx_packet_signal_t); |
| |
| /// Contents of a guest bell packet generated by the kernel. This is a type-safe wrapper for |
| /// zx_packet_guest_bell_t |
| #[derive(Debug, Copy, Clone)] |
| pub struct GuestBellPacket(sys::zx_packet_guest_bell_t); |
| |
| /// Contents of a guest memory packet generated by the kernel. This is a type-safe wrapper for |
| /// zx_packet_guest_memory_t |
| #[derive(Debug, Copy, Clone)] |
| pub struct GuestMemPacket(sys::zx_packet_guest_mem_t); |
| |
| /// Contents of a guest I/O packet generated by the kernel. This is a type-safe wrapper for |
| /// zx_packet_guest_io_t |
| #[derive(Debug, Copy, Clone)] |
| pub struct GuestIoPacket(sys::zx_packet_guest_io_t); |
| |
| /// Contents of a guest VCPU packet generated by the kernel. This is a type-safe wrapper for |
| /// zx_packet_guest_vcpu_t |
| #[derive(Debug, Copy, Clone)] |
| pub struct GuestVcpuPacket(sys::zx_packet_guest_vcpu_t); |
| |
| /// Contents of a pager packet generated by the kernel. This is a type-safe wrapper for |
| /// zx_packet_page_request_t |
| #[derive(Debug, Copy, Clone)] |
| pub struct PagerPacket(sys::zx_packet_page_request_t); |
| |
| impl Packet { |
| /// Creates a new packet with `UserPacket` data. |
| pub fn from_user_packet(key: u64, status: i32, user: UserPacket) -> Packet { |
| Packet(sys::zx_port_packet_t { |
| key: key, |
| packet_type: sys::zx_packet_type_t::ZX_PKT_TYPE_USER, |
| status: status, |
| union: user.0, |
| }) |
| } |
| |
| /// Creates a new packet with `GuestMemPacket` data. |
| pub fn from_guest_mem_packet(key: u64, status: i32, mem: GuestMemPacket) -> Packet { |
| let mut raw = sys::zx_port_packet_t { |
| key, |
| packet_type: sys::zx_packet_type_t::ZX_PKT_TYPE_GUEST_MEM, |
| status, |
| union: Default::default(), |
| }; |
| // transmute_copy doesn't work because the mem packet is too small and |
| // transmute_copy requires that Dst is not larger than Src. |
| // |
| // Note that at the time of writing this only applied to arm64 builds; |
| // x64 builds work fine with transmute_copy. This is because the |
| // underlying `zx_packet_guest_mem_t` struct has a different size on x64 |
| // vs arm64. |
| let bytes: &[u8; std::mem::size_of::<sys::zx_packet_guest_mem_t>()] = |
| unsafe { mem::transmute(&mem.0) }; |
| raw.union[0..std::mem::size_of::<sys::zx_packet_guest_mem_t>()].copy_from_slice(bytes); |
| Packet(raw) |
| } |
| |
| /// Creates a new packet with `GuestIoPacket` data. |
| pub fn from_guest_io_packet(key: u64, status: i32, io: GuestIoPacket) -> Packet { |
| let mut raw = sys::zx_port_packet_t { |
| key, |
| packet_type: sys::zx_packet_type_t::ZX_PKT_TYPE_GUEST_IO, |
| status, |
| union: Default::default(), |
| }; |
| // transmute_copy doesn't work because the io packet is too small and |
| // transmute_copy requires that Dst is not larger than Src. |
| let bytes: &[u8; std::mem::size_of::<sys::zx_packet_guest_io_t>()] = |
| unsafe { mem::transmute(&io.0) }; |
| raw.union[0..std::mem::size_of::<sys::zx_packet_guest_io_t>()].copy_from_slice(bytes); |
| Packet(raw) |
| } |
| |
| /// Creates a new packet with `GuestVcpuPacket` data. |
| pub fn from_guest_vcpu_packet(key: u64, status: i32, vcpu: GuestVcpuPacket) -> Packet { |
| Packet(sys::zx_port_packet_t { |
| key, |
| packet_type: sys::zx_packet_type_t::ZX_PKT_TYPE_GUEST_VCPU, |
| status, |
| union: unsafe { mem::transmute_copy(&vcpu.0) }, |
| }) |
| } |
| |
| /// The packet's key. |
| pub fn key(&self) -> u64 { |
| self.0.key |
| } |
| |
| /// The packet's status. |
| // TODO: should this type be wrapped? |
| pub fn status(&self) -> i32 { |
| self.0.status |
| } |
| |
| /// The contents of the packet. |
| pub fn contents(&self) -> PacketContents { |
| match self.0.packet_type { |
| sys::zx_packet_type_t::ZX_PKT_TYPE_USER => { |
| PacketContents::User(UserPacket(self.0.union)) |
| } |
| |
| sys::zx_packet_type_t::ZX_PKT_TYPE_SIGNAL_ONE => { |
| PacketContents::SignalOne(SignalPacket(unsafe { |
| mem::transmute_copy(&self.0.union) |
| })) |
| } |
| |
| sys::zx_packet_type_t::ZX_PKT_TYPE_GUEST_BELL => { |
| PacketContents::GuestBell(GuestBellPacket(unsafe { |
| mem::transmute_copy(&self.0.union) |
| })) |
| } |
| |
| sys::zx_packet_type_t::ZX_PKT_TYPE_GUEST_MEM => { |
| PacketContents::GuestMem(GuestMemPacket(unsafe { |
| mem::transmute_copy(&self.0.union) |
| })) |
| } |
| |
| sys::zx_packet_type_t::ZX_PKT_TYPE_GUEST_IO => { |
| PacketContents::GuestIo(GuestIoPacket(unsafe { |
| mem::transmute_copy(&self.0.union) |
| })) |
| } |
| |
| sys::zx_packet_type_t::ZX_PKT_TYPE_GUEST_VCPU => { |
| PacketContents::GuestVcpu(GuestVcpuPacket(unsafe { |
| mem::transmute_copy(&self.0.union) |
| })) |
| } |
| |
| sys::zx_packet_type_t::ZX_PKT_TYPE_PAGE_REQUEST => { |
| PacketContents::Pager(PagerPacket(unsafe { mem::transmute_copy(&self.0.union) })) |
| } |
| |
| _ => panic!("unexpected packet type"), |
| } |
| } |
| } |
| |
| impl UserPacket { |
| pub fn from_u8_array(val: [u8; 32]) -> UserPacket { |
| UserPacket(val) |
| } |
| |
| pub fn as_u8_array(&self) -> &[u8; 32] { |
| &self.0 |
| } |
| |
| pub fn as_mut_u8_array(&mut self) -> &mut [u8; 32] { |
| &mut self.0 |
| } |
| } |
| |
| impl SignalPacket { |
| /// The signals used in the call to `object_wait_async`. |
| pub fn trigger(&self) -> Signals { |
| Signals::from_bits_truncate(self.0.trigger) |
| } |
| |
| /// The observed signals. |
| pub fn observed(&self) -> Signals { |
| Signals::from_bits_truncate(self.0.observed) |
| } |
| |
| /// A per object count of pending operations. |
| pub fn count(&self) -> u64 { |
| self.0.count |
| } |
| |
| /// Get a reference to the raw underlying packet. |
| pub fn raw_packet(&self) -> &sys::zx_packet_signal_t { |
| &self.0 |
| } |
| } |
| |
| impl GuestBellPacket { |
| /// The guest physical address that was accessed that triggered the bell. |
| pub fn addr(&self) -> GPAddr { |
| GPAddr(self.0.addr) |
| } |
| } |
| |
| impl GuestMemPacket { |
| pub fn from_raw(mem: sys::zx_packet_guest_mem_t) -> GuestMemPacket { |
| GuestMemPacket(mem) |
| } |
| |
| /// The guest physical address that was accessed that triggered this signal. |
| pub fn addr(&self) -> GPAddr { |
| GPAddr(self.0.addr) |
| } |
| } |
| |
| #[cfg(target_arch = "aarch64")] |
| impl GuestMemPacket { |
| /// Size of the access. |
| /// |
| /// Returns `None` should it find and invalid size in the packet. |
| pub fn access_size(&self) -> Option<guest::MemAccessSize> { |
| match self.0.access_size { |
| 1 => Some(guest::MemAccessSize::Bits8), |
| 2 => Some(guest::MemAccessSize::Bits16), |
| 4 => Some(guest::MemAccessSize::Bits32), |
| 8 => Some(guest::MemAccessSize::Bits64), |
| _ => None, |
| } |
| } |
| |
| /// Whether or not data should be sign extended. |
| pub fn sign_extend(&self) -> bool { |
| self.0.sign_extend |
| } |
| |
| /// Register number of the Rt operand of the faulting instruction. |
| pub fn reg(&self) -> u8 { |
| self.0.xt |
| } |
| |
| /// For `AccessType::Write` this is the data that was being written. |
| pub fn data(&self) -> Option<guest::MemData> { |
| if let guest::AccessType::Write = self.access_type() { |
| self.access_size().map(|size| match size { |
| guest::MemAccessSize::Bits8 => guest::MemData::Data8(self.0.data as u8), |
| guest::MemAccessSize::Bits16 => guest::MemData::Data16(self.0.data as u16), |
| guest::MemAccessSize::Bits32 => guest::MemData::Data32(self.0.data as u32), |
| guest::MemAccessSize::Bits64 => guest::MemData::Data64(self.0.data), |
| }) |
| } else { |
| None |
| } |
| } |
| |
| /// Type of access (read or write). |
| pub fn access_type(&self) -> guest::AccessType { |
| match self.0.read { |
| true => guest::AccessType::Read, |
| false => guest::AccessType::Write, |
| } |
| } |
| } |
| |
| #[cfg(target_arch = "x86_64")] |
| impl GuestMemPacket { |
| /// Specifies the default operand size encoded by the CS descriptor. |
| /// |
| /// Returns a `None` should it find an invalid size in the packet. |
| pub fn default_operand_size(&self) -> Option<guest::CSDefaultOperandSize> { |
| // TODO: use try_from when it is stable. |
| match self.0.default_operand_size { |
| 2 => Some(guest::CSDefaultOperandSize::Bits16), |
| 4 => Some(guest::CSDefaultOperandSize::Bits32), |
| _ => None, |
| } |
| } |
| } |
| |
| impl GuestIoPacket { |
| pub fn from_raw(io: sys::zx_packet_guest_io_t) -> GuestIoPacket { |
| GuestIoPacket(io) |
| } |
| |
| /// First port number of the attempted access |
| pub fn port(&self) -> u16 { |
| self.0.port |
| } |
| |
| /// Size of the access. |
| /// |
| /// Returns `None` should it find an invalid size in the packet. |
| pub fn access_size(&self) -> Option<guest::PortAccessSize> { |
| match self.0.access_size { |
| 1 => Some(guest::PortAccessSize::Bits8), |
| 2 => Some(guest::PortAccessSize::Bits16), |
| 4 => Some(guest::PortAccessSize::Bits32), |
| _ => None, |
| } |
| } |
| |
| /// Type of access (read or write). |
| pub fn access_type(&self) -> guest::AccessType { |
| match self.0.input { |
| true => guest::AccessType::Read, |
| false => guest::AccessType::Write, |
| } |
| } |
| |
| /// For `PortAccessType::Write` this is the data that was being written. |
| pub fn data(&self) -> Option<guest::PortData> { |
| #[repr(C)] |
| union DataUnion { |
| bit8: [u8; 4], |
| bit16: [u16; 2], |
| bit32: [u32; 1], |
| } |
| if let guest::AccessType::Write = self.access_type() { |
| unsafe { |
| let data = &DataUnion { bit8: self.0.data }; |
| self.access_size().map(|size| match size { |
| guest::PortAccessSize::Bits8 => guest::PortData::Data8(data.bit8[0]), |
| guest::PortAccessSize::Bits16 => guest::PortData::Data16(data.bit16[0]), |
| guest::PortAccessSize::Bits32 => guest::PortData::Data32(data.bit32[0]), |
| }) |
| } |
| } else { |
| None |
| } |
| } |
| } |
| |
| impl GuestVcpuPacket { |
| pub fn from_raw(vcpu: sys::zx_packet_guest_vcpu_t) -> GuestVcpuPacket { |
| GuestVcpuPacket(vcpu) |
| } |
| |
| pub fn contents(&self) -> VcpuContents { |
| match self.0.r#type { |
| sys::zx_packet_guest_vcpu_type_t::ZX_PKT_GUEST_VCPU_INTERRUPT => unsafe { |
| VcpuContents::Interrupt { |
| mask: self.0.union.interrupt.mask, |
| vector: self.0.union.interrupt.vector, |
| } |
| }, |
| sys::zx_packet_guest_vcpu_type_t::ZX_PKT_GUEST_VCPU_STARTUP => unsafe { |
| VcpuContents::Startup { |
| id: self.0.union.startup.id, |
| entry: self.0.union.startup.entry, |
| } |
| }, |
| sys::zx_packet_guest_vcpu_type_t::ZX_PKT_GUEST_VCPU_EXIT => unsafe { |
| VcpuContents::Exit { retcode: self.0.union.exit.retcode } |
| }, |
| _ => panic!("unexpected VCPU packet type"), |
| } |
| } |
| } |
| |
| impl PagerPacket { |
| /// Returns the page request command. |
| pub fn command(&self) -> sys::zx_page_request_command_t { |
| self.0.command |
| } |
| |
| /// Returns the range for the page request. |
| pub fn range(&self) -> std::ops::Range<u64> { |
| self.0.offset..self.0.offset + self.0.length |
| } |
| } |
| |
| impl Port { |
| /// Create an IO port, allowing IO packets to be read and enqueued. |
| /// |
| /// Wraps the |
| /// [zx_port_create](https://fuchsia.dev/fuchsia-src/reference/syscalls/port_create.md) |
| /// syscall. |
| /// |
| /// # Panics |
| /// |
| /// If the kernel reports no memory available to create a port or the process' job policy |
| /// denies port creation. |
| pub fn create() -> Self { |
| unsafe { |
| let mut handle = 0; |
| let opts = 0; |
| let status = sys::zx_port_create(opts, &mut handle); |
| ok(status).expect( |
| "port creation always succeeds except with OOM or when job policy denies it", |
| ); |
| Handle::from_raw(handle).into() |
| } |
| } |
| |
| /// Attempt to queue a user packet to the IO port. |
| /// |
| /// Wraps the |
| /// [zx_port_queue](https://fuchsia.dev/fuchsia-src/reference/syscalls/port_queue.md) |
| /// syscall. |
| pub fn queue(&self, packet: &Packet) -> Result<(), Status> { |
| let status = unsafe { |
| sys::zx_port_queue(self.raw_handle(), &packet.0 as *const sys::zx_port_packet_t) |
| }; |
| ok(status) |
| } |
| |
| /// Wait for a packet to arrive on a (V2) port. |
| /// |
| /// Wraps the |
| /// [zx_port_wait](https://fuchsia.dev/fuchsia-src/reference/syscalls/port_wait.md) |
| /// syscall. |
| pub fn wait(&self, deadline: Time) -> Result<Packet, Status> { |
| let mut packet = Default::default(); |
| ok(unsafe { sys::zx_port_wait(self.raw_handle(), deadline.into_nanos(), &mut packet) })?; |
| Ok(Packet(packet)) |
| } |
| |
| /// Cancel pending wait_async calls for an object with the given key. |
| /// |
| /// Wraps the |
| /// [zx_port_cancel](https://fuchsia.dev/fuchsia-src/reference/syscalls/port_cancel.md) |
| /// syscall. |
| pub fn cancel<H>(&self, source: &H, key: u64) -> Result<(), Status> |
| where |
| H: AsHandleRef, |
| { |
| let status = unsafe { sys::zx_port_cancel(self.raw_handle(), source.raw_handle(), key) }; |
| ok(status) |
| } |
| } |
| |
| bitflags! { |
| /// Options for wait_async |
| #[repr(transparent)] |
| #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] |
| pub struct WaitAsyncOpts: u32 { |
| /// When set, causes the system to capture a timestamp when the wait triggered. |
| const TIMESTAMP = sys::ZX_WAIT_ASYNC_TIMESTAMP; |
| // When set, causes the port to not enqueue a packet for signals active at |
| // the time of the zx_object_wait_async() call. |
| const EDGE_TRIGGERED = sys::ZX_WAIT_ASYNC_EDGE; |
| } |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use super::*; |
| use crate::{DurationNum, Event}; |
| use assert_matches::assert_matches; |
| |
| #[test] |
| fn port_basic() { |
| let ten_ms = 10.millis(); |
| |
| let port = Port::create(); |
| |
| // Waiting now should time out. |
| assert_eq!(port.wait(Time::after(ten_ms)), Err(Status::TIMED_OUT)); |
| |
| // Send a valid packet. |
| let packet = Packet::from_user_packet(42, 123, UserPacket::from_u8_array([13; 32])); |
| assert!(port.queue(&packet).is_ok()); |
| |
| // Waiting should succeed this time. We should get back the packet we sent. |
| let read_packet = port.wait(Time::after(ten_ms)).unwrap(); |
| assert_eq!(read_packet, packet); |
| } |
| |
| #[test] |
| fn wait_async_once() { |
| let ten_ms = 10.millis(); |
| let key = 42; |
| |
| let port = Port::create(); |
| let event = Event::create(); |
| let no_opts = WaitAsyncOpts::empty(); |
| |
| assert!(event |
| .wait_async_handle(&port, key, Signals::USER_0 | Signals::USER_1, no_opts) |
| .is_ok()); |
| |
| // Waiting without setting any signal should time out. |
| assert_eq!(port.wait(Time::after(ten_ms)), Err(Status::TIMED_OUT)); |
| |
| // If we set a signal, we should be able to wait for it. |
| assert!(event.signal_handle(Signals::NONE, Signals::USER_0).is_ok()); |
| let read_packet = port.wait(Time::after(ten_ms)).unwrap(); |
| assert_eq!(read_packet.key(), key); |
| assert_eq!(read_packet.status(), 0); |
| match read_packet.contents() { |
| PacketContents::SignalOne(sig) => { |
| assert_eq!(sig.trigger(), Signals::USER_0 | Signals::USER_1); |
| assert_eq!(sig.observed(), Signals::USER_0); |
| assert_eq!(sig.count(), 1); |
| } |
| _ => panic!("wrong packet type"), |
| } |
| |
| // Shouldn't get any more packets. |
| assert_eq!(port.wait(Time::after(ten_ms)), Err(Status::TIMED_OUT)); |
| |
| // Calling wait_async again should result in another packet. |
| assert!(event.wait_async_handle(&port, key, Signals::USER_0, no_opts).is_ok()); |
| let read_packet = port.wait(Time::after(ten_ms)).unwrap(); |
| assert_eq!(read_packet.key(), key); |
| assert_eq!(read_packet.status(), 0); |
| match read_packet.contents() { |
| PacketContents::SignalOne(sig) => { |
| assert_eq!(sig.trigger(), Signals::USER_0); |
| assert_eq!(sig.observed(), Signals::USER_0); |
| assert_eq!(sig.count(), 1); |
| } |
| _ => panic!("wrong packet type"), |
| } |
| |
| // Calling wait_async_handle then cancel, we should not get a packet as cancel will |
| // remove it from the queue. |
| assert!(event.wait_async_handle(&port, key, Signals::USER_0, no_opts).is_ok()); |
| assert!(port.cancel(&event, key).is_ok()); |
| assert_eq!(port.wait(Time::after(ten_ms)), Err(Status::TIMED_OUT)); |
| |
| // If the event is signalled after the cancel, we also shouldn't get a packet. |
| assert!(event.signal_handle(Signals::USER_0, Signals::NONE).is_ok()); // clear signal |
| assert!(event.wait_async_handle(&port, key, Signals::USER_0, no_opts).is_ok()); |
| assert!(port.cancel(&event, key).is_ok()); |
| assert!(event.signal_handle(Signals::NONE, Signals::USER_0).is_ok()); |
| assert_eq!(port.wait(Time::after(ten_ms)), Err(Status::TIMED_OUT)); |
| } |
| |
| #[test] |
| fn guest_mem_packet() { |
| #[cfg(target_arch = "x86_64")] |
| const GUEST_MEM_PACKET: sys::zx_packet_guest_mem_t = sys::zx_packet_guest_mem_t { |
| addr: 0xaaaabbbbccccdddd, |
| cr3: 0x0123456789abcdef, |
| rip: 0xffffffff00000000, |
| instruction_size: 8, |
| default_operand_size: 2, |
| }; |
| #[cfg(target_arch = "aarch64")] |
| const GUEST_MEM_PACKET: sys::zx_packet_guest_mem_t = sys::zx_packet_guest_mem_t { |
| addr: 0x8877665544332211, |
| access_size: 8, |
| sign_extend: true, |
| xt: 0xf3, |
| read: false, |
| data: 0x1122334455667788, |
| }; |
| #[cfg(target_arch = "riscv64")] |
| const GUEST_MEM_PACKET: sys::zx_packet_guest_mem_t = |
| sys::zx_packet_guest_mem_t { addr: 0x8877665544332211, reserved: [0; 3] }; |
| const KEY: u64 = 0x5555555555555555; |
| const STATUS: i32 = sys::ZX_ERR_INTERNAL; |
| |
| let packet = |
| Packet::from_guest_mem_packet(KEY, STATUS, GuestMemPacket::from_raw(GUEST_MEM_PACKET)); |
| |
| assert_matches!(packet.contents(), PacketContents::GuestMem(GuestMemPacket(packet)) if packet == GUEST_MEM_PACKET); |
| assert_eq!(packet.key(), KEY); |
| assert_eq!(packet.status(), STATUS); |
| } |
| |
| #[test] |
| fn guest_io_packet() { |
| const GUEST_IO_PACKET: sys::zx_packet_guest_io_t = sys::zx_packet_guest_io_t { |
| port: 0xabcd, |
| access_size: 4, |
| input: true, |
| data: [0xaa, 0xbb, 0xcc, 0xdd], |
| }; |
| const KEY: u64 = 0x0123456789abcdef; |
| const STATUS: i32 = sys::ZX_ERR_NO_RESOURCES; |
| |
| let packet = |
| Packet::from_guest_io_packet(KEY, STATUS, GuestIoPacket::from_raw(GUEST_IO_PACKET)); |
| |
| assert_matches!(packet.contents(), PacketContents::GuestIo(GuestIoPacket(packet)) if packet == GUEST_IO_PACKET); |
| assert_eq!(packet.key(), KEY); |
| assert_eq!(packet.status(), STATUS); |
| } |
| |
| #[test] |
| fn guest_vcpu_interrupt_packet() { |
| // Unable to use 'const' here because we need Default::default to initialize the PadBytes. |
| let guest_vcpu_packet: sys::zx_packet_guest_vcpu_t = sys::zx_packet_guest_vcpu_t { |
| r#type: sys::zx_packet_guest_vcpu_type_t::ZX_PKT_GUEST_VCPU_INTERRUPT, |
| union: sys::zx_packet_guest_vcpu_union_t { |
| interrupt: sys::zx_packet_guest_vcpu_interrupt_t { |
| mask: 0xaaaaaaaaaaaaaaaa, |
| vector: 0x12, |
| padding1: Default::default(), |
| }, |
| }, |
| padding1: Default::default(), |
| reserved: 0, |
| }; |
| const KEY: u64 = 0x0123456789abcdef; |
| const STATUS: i32 = sys::ZX_ERR_NO_RESOURCES; |
| |
| let packet = Packet::from_guest_vcpu_packet( |
| KEY, |
| STATUS, |
| GuestVcpuPacket::from_raw(guest_vcpu_packet), |
| ); |
| |
| assert_matches!(packet.contents(), PacketContents::GuestVcpu(GuestVcpuPacket(packet)) if packet == guest_vcpu_packet); |
| assert_eq!(packet.key(), KEY); |
| assert_eq!(packet.status(), STATUS); |
| } |
| |
| #[test] |
| fn guest_vcpu_startup_packet() { |
| let guest_vcpu_packet: sys::zx_packet_guest_vcpu_t = sys::zx_packet_guest_vcpu_t { |
| r#type: sys::zx_packet_guest_vcpu_type_t::ZX_PKT_GUEST_VCPU_STARTUP, |
| union: sys::zx_packet_guest_vcpu_union_t { |
| startup: sys::zx_packet_guest_vcpu_startup_t { id: 16, entry: 0xffffffff11111111 }, |
| }, |
| padding1: Default::default(), |
| reserved: 0, |
| }; |
| const KEY: u64 = 0x0123456789abcdef; |
| const STATUS: i32 = sys::ZX_ERR_NO_RESOURCES; |
| |
| let packet = Packet::from_guest_vcpu_packet( |
| KEY, |
| STATUS, |
| GuestVcpuPacket::from_raw(guest_vcpu_packet), |
| ); |
| |
| assert_matches!(packet.contents(), PacketContents::GuestVcpu(GuestVcpuPacket(packet)) if packet == guest_vcpu_packet); |
| assert_eq!(packet.key(), KEY); |
| assert_eq!(packet.status(), STATUS); |
| } |
| |
| #[test] |
| fn guest_vcpu_exit_packet() { |
| let guest_vcpu_packet: sys::zx_packet_guest_vcpu_t = sys::zx_packet_guest_vcpu_t { |
| r#type: sys::zx_packet_guest_vcpu_type_t::ZX_PKT_GUEST_VCPU_EXIT, |
| union: sys::zx_packet_guest_vcpu_union_t { |
| exit: sys::zx_packet_guest_vcpu_exit_t { retcode: 12345678, reserved: 0 }, |
| }, |
| padding1: Default::default(), |
| reserved: 0, |
| }; |
| const KEY: u64 = 0x0123456789abcdef; |
| const STATUS: i32 = sys::ZX_ERR_NO_RESOURCES; |
| |
| let packet = Packet::from_guest_vcpu_packet( |
| KEY, |
| STATUS, |
| GuestVcpuPacket::from_raw(guest_vcpu_packet), |
| ); |
| |
| assert_matches!(packet.contents(), PacketContents::GuestVcpu(GuestVcpuPacket(packet)) if packet == guest_vcpu_packet); |
| assert_eq!(packet.key(), KEY); |
| assert_eq!(packet.status(), STATUS); |
| } |
| } |