Skip to main content

zx/
port.rs

1// Copyright 2017 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! Type-safe bindings for Zircon port objects.
6
7use crate::{
8    GPAddr, MonotonicInstant, NullableHandle, Signals, Status, VcpuContents, guest, ok, sys,
9};
10use bitflags::bitflags;
11use std::mem;
12
13/// An object representing a Zircon
14/// [port](https://fuchsia.dev/fuchsia-src/concepts/objects/port.md).
15///
16/// As essentially a subtype of `NullableHandle`, it can be freely interconverted.
17#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
18#[repr(transparent)]
19pub struct Port(NullableHandle);
20impl_handle_based!(Port);
21
22bitflags! {
23    /// Options that may be used when creating a `Port`.
24    pub struct PortOptions: u32 {
25        const BIND_TO_INTERRUPT = sys::ZX_PORT_BIND_TO_INTERRUPT;
26    }
27}
28
29/// The contents of a `Packet`.
30#[derive(Debug, Copy, Clone, Eq, PartialEq)]
31pub enum PacketContents {
32    /// A user-generated packet.
33    User(UserPacket),
34    /// A one-shot signal packet generated via `object_wait_async`.
35    SignalOne(SignalPacket),
36    /// A guest bell packet
37    GuestBell(GuestBellPacket),
38    /// A guest mem packet
39    GuestMem(GuestMemPacket),
40    /// A guest I/O packet
41    GuestIo(GuestIoPacket),
42    /// A guest VCPU packet
43    GuestVcpu(GuestVcpuPacket),
44    /// Pager packets
45    Pager(PagerPacket),
46    /// Interrupt packets
47    Interrupt(InterruptPacket),
48    /// power transition request packets.
49    /// This is gated under syscalls-next.h
50    PowerTransition(PowerTransitionPacket),
51
52    #[doc(hidden)]
53    __Nonexhaustive,
54}
55
56/// Contents of a user packet (one sent by `port_queue`). This is a type-safe wrapper for
57/// [zx_packet_user_t](https://fuchsia.dev/fuchsia-src/reference/syscalls/port_wait.md).
58#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
59pub struct UserPacket(sys::zx_packet_user_t);
60
61/// Contents of a signal packet (one generated by the kernel). This is a type-safe wrapper for
62/// [zx_packet_signal_t](https://fuchsia.dev/fuchsia-src/reference/syscalls/port_wait.md).
63#[derive(Copy, Clone, Debug, Eq, PartialEq)]
64pub struct SignalPacket(sys::zx_packet_signal_t);
65
66/// Contents of a guest bell packet generated by the kernel. This is a type-safe wrapper for
67/// zx_packet_guest_bell_t
68#[derive(Copy, Clone, Debug, Eq, PartialEq)]
69pub struct GuestBellPacket(sys::zx_packet_guest_bell_t);
70
71/// Contents of a guest memory packet generated by the kernel. This is a type-safe wrapper for
72/// zx_packet_guest_memory_t
73#[derive(Copy, Clone, Debug, Eq, PartialEq)]
74pub struct GuestMemPacket(sys::zx_packet_guest_mem_t);
75
76/// Contents of a guest I/O packet generated by the kernel. This is a type-safe wrapper for
77/// zx_packet_guest_io_t
78#[derive(Copy, Clone, Debug, Eq, PartialEq)]
79pub struct GuestIoPacket(sys::zx_packet_guest_io_t);
80
81/// Contents of a guest VCPU packet generated by the kernel. This is a type-safe wrapper for
82/// zx_packet_guest_vcpu_t
83#[derive(Copy, Clone, Debug, Eq, PartialEq)]
84pub struct GuestVcpuPacket(sys::zx_packet_guest_vcpu_t);
85
86/// Contents of a pager packet generated by the kernel. This is a type-safe wrapper for
87/// zx_packet_page_request_t
88#[derive(Copy, Clone, Debug, Eq, PartialEq)]
89pub struct PagerPacket(sys::zx_packet_page_request_t);
90
91/// Contents of an interrupt packet (one received because of an interrupt bound to this port).
92/// This is a type-safe wrapper for zx_packet_interrupt_t.
93#[derive(Copy, Clone, Debug, Eq, PartialEq)]
94pub struct InterruptPacket(sys::zx_packet_interrupt_t);
95
96/// Contents of a power transition packet generated by the kernel. This is a type-safe wrapper for
97/// zx_packet_processor_power_level_transition_request_t
98#[derive(Copy, Clone, Debug, Eq, PartialEq)]
99pub struct PowerTransitionPacket(sys::zx_packet_processor_power_level_transition_request_t);
100
101/// A packet sent through a port. This is a type-safe wrapper for
102/// [zx_port_packet_t](https://fuchsia.dev/fuchsia-src/reference/syscalls/port_wait.md).
103#[derive(PartialEq, Eq, Debug)]
104pub struct Packet(pub(crate) sys::zx_port_packet_t);
105
106impl Packet {
107    /// Creates a new packet with `UserPacket` data.
108    pub fn from_user_packet(key: u64, status: i32, user: UserPacket) -> Packet {
109        Packet(sys::zx_port_packet_t {
110            key: key,
111            packet_type: sys::zx_packet_type_t::ZX_PKT_TYPE_USER,
112            status: status,
113            union: user.0,
114        })
115    }
116
117    /// Creates a new packet with `GuestMemPacket` data.
118    pub fn from_guest_mem_packet(key: u64, status: i32, mem: GuestMemPacket) -> Packet {
119        let mut raw = sys::zx_port_packet_t {
120            key,
121            packet_type: sys::zx_packet_type_t::ZX_PKT_TYPE_GUEST_MEM,
122            status,
123            union: Default::default(),
124        };
125        // transmute_copy doesn't work because the mem packet is too small and
126        // transmute_copy requires that Dst is not larger than Src.
127        //
128        // Note that at the time of writing this only applied to arm64 builds;
129        // x64 builds work fine with transmute_copy. This is because the
130        // underlying `zx_packet_guest_mem_t` struct has a different size on x64
131        // vs arm64.
132        let bytes: &[u8; std::mem::size_of::<sys::zx_packet_guest_mem_t>()] =
133            unsafe { mem::transmute(&mem.0) };
134        raw.union[0..std::mem::size_of::<sys::zx_packet_guest_mem_t>()].copy_from_slice(bytes);
135        Packet(raw)
136    }
137
138    /// Creates a new packet with `GuestIoPacket` data.
139    pub fn from_guest_io_packet(key: u64, status: i32, io: GuestIoPacket) -> Packet {
140        let mut raw = sys::zx_port_packet_t {
141            key,
142            packet_type: sys::zx_packet_type_t::ZX_PKT_TYPE_GUEST_IO,
143            status,
144            union: Default::default(),
145        };
146        // transmute_copy doesn't work because the io packet is too small and
147        // transmute_copy requires that Dst is not larger than Src.
148        let bytes: &[u8; std::mem::size_of::<sys::zx_packet_guest_io_t>()] =
149            unsafe { mem::transmute(&io.0) };
150        raw.union[0..std::mem::size_of::<sys::zx_packet_guest_io_t>()].copy_from_slice(bytes);
151        Packet(raw)
152    }
153
154    /// Creates a new packet with `GuestVcpuPacket` data.
155    pub fn from_guest_vcpu_packet(key: u64, status: i32, vcpu: GuestVcpuPacket) -> Packet {
156        Packet(sys::zx_port_packet_t {
157            key,
158            packet_type: sys::zx_packet_type_t::ZX_PKT_TYPE_GUEST_VCPU,
159            status,
160            union: unsafe { mem::transmute_copy(&vcpu.0) },
161        })
162    }
163
164    /// Creates a new packet with `PowerTransitionPacket` data.
165    pub fn from_power_transition_packet(
166        key: u64,
167        status: i32,
168        power_transition_packet: PowerTransitionPacket,
169    ) -> Packet {
170        Packet(sys::zx_port_packet_t {
171            key: key,
172            packet_type:
173                sys::zx_packet_type_t::ZX_PKT_TYPE_PROCESSOR_POWER_LEVEL_TRANSITION_REQUEST,
174            status: status,
175            union: unsafe { mem::transmute_copy(&power_transition_packet.0) },
176        })
177    }
178
179    /// The packet's key.
180    pub fn key(&self) -> u64 {
181        self.0.key
182    }
183
184    /// The packet's status.
185    // TODO: should this type be wrapped?
186    pub fn status(&self) -> i32 {
187        self.0.status
188    }
189
190    /// The contents of the packet.
191    pub fn contents(&self) -> PacketContents {
192        match self.0.packet_type {
193            sys::zx_packet_type_t::ZX_PKT_TYPE_USER => {
194                PacketContents::User(UserPacket(self.0.union))
195            }
196
197            sys::zx_packet_type_t::ZX_PKT_TYPE_SIGNAL_ONE => {
198                PacketContents::SignalOne(SignalPacket(unsafe {
199                    mem::transmute_copy(&self.0.union)
200                }))
201            }
202
203            sys::zx_packet_type_t::ZX_PKT_TYPE_GUEST_BELL => {
204                PacketContents::GuestBell(GuestBellPacket(unsafe {
205                    mem::transmute_copy(&self.0.union)
206                }))
207            }
208
209            sys::zx_packet_type_t::ZX_PKT_TYPE_GUEST_MEM => {
210                PacketContents::GuestMem(GuestMemPacket(unsafe {
211                    mem::transmute_copy(&self.0.union)
212                }))
213            }
214
215            sys::zx_packet_type_t::ZX_PKT_TYPE_GUEST_IO => {
216                PacketContents::GuestIo(GuestIoPacket(unsafe {
217                    mem::transmute_copy(&self.0.union)
218                }))
219            }
220
221            sys::zx_packet_type_t::ZX_PKT_TYPE_GUEST_VCPU => {
222                PacketContents::GuestVcpu(GuestVcpuPacket(unsafe {
223                    mem::transmute_copy(&self.0.union)
224                }))
225            }
226
227            sys::zx_packet_type_t::ZX_PKT_TYPE_PAGE_REQUEST => {
228                PacketContents::Pager(PagerPacket(unsafe { mem::transmute_copy(&self.0.union) }))
229            }
230
231            sys::zx_packet_type_t::ZX_PKT_TYPE_INTERRUPT => {
232                PacketContents::Interrupt(InterruptPacket(unsafe {
233                    mem::transmute_copy(&self.0.union)
234                }))
235            }
236
237            sys::zx_packet_type_t::ZX_PKT_TYPE_PROCESSOR_POWER_LEVEL_TRANSITION_REQUEST => {
238                PacketContents::PowerTransition(PowerTransitionPacket(unsafe {
239                    mem::transmute_copy(&self.0.union)
240                }))
241            }
242
243            _ => panic!("unexpected packet type"),
244        }
245    }
246}
247
248impl UserPacket {
249    pub fn from_u8_array(val: [u8; 32]) -> UserPacket {
250        UserPacket(val)
251    }
252
253    pub fn as_u8_array(&self) -> &[u8; 32] {
254        &self.0
255    }
256
257    pub fn as_mut_u8_array(&mut self) -> &mut [u8; 32] {
258        &mut self.0
259    }
260}
261
262impl SignalPacket {
263    /// The signals used in the call to `object_wait_async`.
264    pub fn trigger(&self) -> Signals {
265        Signals::from_bits_truncate(self.0.trigger)
266    }
267
268    /// The observed signals.
269    pub fn observed(&self) -> Signals {
270        Signals::from_bits_truncate(self.0.observed)
271    }
272
273    /// A per object count of pending operations.
274    pub fn count(&self) -> u64 {
275        self.0.count
276    }
277
278    // The timestamp at which the packet was enqueued.
279    pub fn timestamp(&self) -> sys::zx_time_t {
280        self.0.timestamp
281    }
282
283    /// Get a reference to the raw underlying packet.
284    pub fn raw_packet(&self) -> &sys::zx_packet_signal_t {
285        &self.0
286    }
287}
288
289impl GuestBellPacket {
290    /// The guest physical address that was accessed that triggered the bell.
291    pub fn addr(&self) -> GPAddr {
292        GPAddr(self.0.addr)
293    }
294}
295
296impl GuestMemPacket {
297    pub fn from_raw(mem: sys::zx_packet_guest_mem_t) -> GuestMemPacket {
298        GuestMemPacket(mem)
299    }
300
301    /// The guest physical address that was accessed that triggered this signal.
302    pub fn addr(&self) -> GPAddr {
303        GPAddr(self.0.addr)
304    }
305}
306
307#[cfg(target_arch = "aarch64")]
308impl GuestMemPacket {
309    /// Size of the access.
310    ///
311    /// Returns `None` should it find and invalid size in the packet.
312    pub fn access_size(&self) -> Option<guest::MemAccessSize> {
313        match self.0.access_size {
314            1 => Some(guest::MemAccessSize::Bits8),
315            2 => Some(guest::MemAccessSize::Bits16),
316            4 => Some(guest::MemAccessSize::Bits32),
317            8 => Some(guest::MemAccessSize::Bits64),
318            _ => None,
319        }
320    }
321
322    /// Whether or not data should be sign extended.
323    pub fn sign_extend(&self) -> bool {
324        self.0.sign_extend
325    }
326
327    /// Register number of the Rt operand of the faulting instruction.
328    pub fn reg(&self) -> u8 {
329        self.0.xt
330    }
331
332    /// For `AccessType::Write` this is the data that was being written.
333    pub fn data(&self) -> Option<guest::MemData> {
334        if let guest::AccessType::Write = self.access_type() {
335            self.access_size().map(|size| match size {
336                guest::MemAccessSize::Bits8 => guest::MemData::Data8(self.0.data as u8),
337                guest::MemAccessSize::Bits16 => guest::MemData::Data16(self.0.data as u16),
338                guest::MemAccessSize::Bits32 => guest::MemData::Data32(self.0.data as u32),
339                guest::MemAccessSize::Bits64 => guest::MemData::Data64(self.0.data),
340            })
341        } else {
342            None
343        }
344    }
345
346    /// Type of access (read or write).
347    pub fn access_type(&self) -> guest::AccessType {
348        match self.0.read {
349            true => guest::AccessType::Read,
350            false => guest::AccessType::Write,
351        }
352    }
353}
354
355#[cfg(target_arch = "x86_64")]
356impl GuestMemPacket {
357    /// Specifies the default operand size encoded by the CS descriptor.
358    ///
359    /// Returns a `None` should it find an invalid size in the packet.
360    pub fn default_operand_size(&self) -> Option<guest::CSDefaultOperandSize> {
361        // TODO: use try_from when it is stable.
362        match self.0.default_operand_size {
363            2 => Some(guest::CSDefaultOperandSize::Bits16),
364            4 => Some(guest::CSDefaultOperandSize::Bits32),
365            _ => None,
366        }
367    }
368}
369
370impl GuestIoPacket {
371    pub fn from_raw(io: sys::zx_packet_guest_io_t) -> GuestIoPacket {
372        GuestIoPacket(io)
373    }
374
375    /// First port number of the attempted access
376    pub fn port(&self) -> u16 {
377        self.0.port
378    }
379
380    /// Size of the access.
381    ///
382    /// Returns `None` should it find an invalid size in the packet.
383    pub fn access_size(&self) -> Option<guest::PortAccessSize> {
384        match self.0.access_size {
385            1 => Some(guest::PortAccessSize::Bits8),
386            2 => Some(guest::PortAccessSize::Bits16),
387            4 => Some(guest::PortAccessSize::Bits32),
388            _ => None,
389        }
390    }
391
392    /// Type of access (read or write).
393    pub fn access_type(&self) -> guest::AccessType {
394        match self.0.input {
395            true => guest::AccessType::Read,
396            false => guest::AccessType::Write,
397        }
398    }
399
400    /// For `PortAccessType::Write` this is the data that was being written.
401    pub fn data(&self) -> Option<guest::PortData> {
402        if let guest::AccessType::Write = self.access_type() {
403            self.access_size().map(|size| match size {
404                guest::PortAccessSize::Bits8 => guest::PortData::Data8(self.0.data[0]),
405                guest::PortAccessSize::Bits16 => {
406                    guest::PortData::Data16(u16::from_ne_bytes([self.0.data[0], self.0.data[1]]))
407                }
408                guest::PortAccessSize::Bits32 => {
409                    guest::PortData::Data32(u32::from_ne_bytes(self.0.data))
410                }
411            })
412        } else {
413            None
414        }
415    }
416}
417
418impl GuestVcpuPacket {
419    pub fn from_raw(vcpu: sys::zx_packet_guest_vcpu_t) -> GuestVcpuPacket {
420        GuestVcpuPacket(vcpu)
421    }
422
423    pub fn contents(&self) -> VcpuContents {
424        match self.0.r#type {
425            sys::zx_packet_guest_vcpu_type_t::ZX_PKT_GUEST_VCPU_INTERRUPT => unsafe {
426                VcpuContents::Interrupt {
427                    mask: self.0.union.interrupt.mask,
428                    vector: self.0.union.interrupt.vector,
429                }
430            },
431            sys::zx_packet_guest_vcpu_type_t::ZX_PKT_GUEST_VCPU_STARTUP => unsafe {
432                VcpuContents::Startup {
433                    id: self.0.union.startup.id,
434                    entry: self.0.union.startup.entry,
435                }
436            },
437            _ => panic!("unexpected VCPU packet type"),
438        }
439    }
440}
441
442impl PagerPacket {
443    /// Returns the page request command.
444    pub fn command(&self) -> sys::zx_page_request_command_t {
445        self.0.command
446    }
447
448    /// Returns the range for the page request.
449    pub fn range(&self) -> std::ops::Range<u64> {
450        self.0.offset..self.0.offset + self.0.length
451    }
452}
453
454impl InterruptPacket {
455    pub fn timestamp(&self) -> sys::zx_time_t {
456        self.0.timestamp
457    }
458}
459
460impl PowerTransitionPacket {
461    pub fn from_raw(
462        packet: sys::zx_packet_processor_power_level_transition_request_t,
463    ) -> PowerTransitionPacket {
464        PowerTransitionPacket(packet)
465    }
466
467    pub fn domain_id(&self) -> u32 {
468        self.0.domain_id
469    }
470
471    pub fn options(&self) -> u32 {
472        self.0.options
473    }
474
475    pub fn control_interface(&self) -> u64 {
476        self.0.control_interface
477    }
478
479    pub fn control_argument(&self) -> u64 {
480        self.0.control_argument
481    }
482}
483
484impl Port {
485    /// Create an IO port, allowing IO packets to be read and enqueued.
486    ///
487    /// Wraps the
488    /// [zx_port_create](https://fuchsia.dev/fuchsia-src/reference/syscalls/port_create.md)
489    /// syscall.
490    ///
491    /// # Panics
492    ///
493    /// If the kernel reports no memory available to create a port or the process' job policy
494    /// denies port creation.
495    pub fn create() -> Self {
496        Self::create_with_opts(PortOptions::from_bits_truncate(0))
497    }
498
499    pub fn create_with_opts(opts: PortOptions) -> Self {
500        unsafe {
501            let mut handle = 0;
502            let status = sys::zx_port_create(opts.bits(), &mut handle);
503            ok(status).expect(
504                "port creation always succeeds except with OOM or when job policy denies it",
505            );
506            NullableHandle::from_raw(handle).into()
507        }
508    }
509
510    /// Attempt to queue a user packet to the IO port.
511    ///
512    /// Wraps the
513    /// [zx_port_queue](https://fuchsia.dev/fuchsia-src/reference/syscalls/port_queue.md)
514    /// syscall.
515    pub fn queue(&self, packet: &Packet) -> Result<(), Status> {
516        let status =
517            unsafe { sys::zx_port_queue(self.raw_handle(), std::ptr::from_ref(&packet.0)) };
518        ok(status)
519    }
520
521    /// Wait for a packet to arrive on a (V2) port.
522    ///
523    /// Wraps the
524    /// [zx_port_wait](https://fuchsia.dev/fuchsia-src/reference/syscalls/port_wait.md)
525    /// syscall.
526    pub fn wait(&self, deadline: MonotonicInstant) -> Result<Packet, Status> {
527        let mut packet = Default::default();
528        ok(unsafe { sys::zx_port_wait(self.raw_handle(), deadline.into_nanos(), &mut packet) })?;
529        Ok(Packet(packet))
530    }
531
532    /// Cancel pending wait_async calls for a given key.
533    ///
534    /// Wraps the
535    /// [zx_port_cancel_key](https://fuchsia.dev/fuchsia-src/reference/syscalls/port_cancel_key.md)
536    /// syscall.
537    pub fn cancel(&self, key: u64) -> Result<(), Status> {
538        let options = 0;
539        let status = unsafe { sys::zx_port_cancel_key(self.raw_handle(), options, key) };
540        ok(status)
541    }
542}
543
544bitflags! {
545    /// Options for wait_async
546    #[repr(transparent)]
547    #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
548    pub struct WaitAsyncOpts: u32 {
549        /// When set, causes the system to capture a timestamp when the wait triggered.
550        const TIMESTAMP = sys::ZX_WAIT_ASYNC_TIMESTAMP;
551
552        /// When set, causes the system to capture a boot timestamp when the wait triggered.
553        const BOOT_TIMESTAMP = sys::ZX_WAIT_ASYNC_BOOT_TIMESTAMP;
554
555        /// When set, causes the port to not enqueue a packet for signals active at
556        /// the time of the zx_object_wait_async() call.
557        const EDGE_TRIGGERED = sys::ZX_WAIT_ASYNC_EDGE;
558    }
559}
560
561#[cfg(test)]
562mod tests {
563    use super::*;
564    use crate::{Duration, Event};
565    use assert_matches::assert_matches;
566
567    #[test]
568    fn port_basic() {
569        let ten_ms = Duration::from_millis(10);
570
571        let port = Port::create();
572
573        // Waiting now should time out.
574        assert_eq!(port.wait(MonotonicInstant::after(ten_ms)), Err(Status::TIMED_OUT));
575
576        // Send a valid packet.
577        let packet = Packet::from_user_packet(42, 123, UserPacket::from_u8_array([13; 32]));
578        assert!(port.queue(&packet).is_ok());
579
580        // Waiting should succeed this time. We should get back the packet we sent.
581        let read_packet = port.wait(MonotonicInstant::after(ten_ms)).unwrap();
582        assert_eq!(read_packet, packet);
583    }
584
585    #[test]
586    fn create_with_opts() {
587        let _port = Port::create_with_opts(PortOptions::BIND_TO_INTERRUPT);
588    }
589
590    #[test]
591    fn wait_async_once() {
592        let ten_ms = Duration::from_millis(10);
593        let key = 42;
594
595        let port = Port::create();
596        let event = Event::create();
597        let no_opts = WaitAsyncOpts::empty();
598
599        assert!(event.wait_async(&port, key, Signals::USER_0 | Signals::USER_1, no_opts).is_ok());
600
601        // Waiting without setting any signal should time out.
602        assert_eq!(port.wait(MonotonicInstant::after(ten_ms)), Err(Status::TIMED_OUT));
603
604        // If we set a signal, we should be able to wait for it.
605        assert!(event.signal(Signals::NONE, Signals::USER_0).is_ok());
606        let read_packet = port.wait(MonotonicInstant::after(ten_ms)).unwrap();
607        assert_eq!(read_packet.key(), key);
608        assert_eq!(read_packet.status(), 0);
609        match read_packet.contents() {
610            PacketContents::SignalOne(sig) => {
611                assert_eq!(sig.trigger(), Signals::USER_0 | Signals::USER_1);
612                assert_eq!(sig.observed(), Signals::USER_0);
613                assert_eq!(sig.count(), 1);
614            }
615            _ => panic!("wrong packet type"),
616        }
617
618        // Shouldn't get any more packets.
619        assert_eq!(port.wait(MonotonicInstant::after(ten_ms)), Err(Status::TIMED_OUT));
620
621        // Calling wait_async again should result in another packet.
622        assert!(event.wait_async(&port, key, Signals::USER_0, no_opts).is_ok());
623        let read_packet = port.wait(MonotonicInstant::after(ten_ms)).unwrap();
624        assert_eq!(read_packet.key(), key);
625        assert_eq!(read_packet.status(), 0);
626        match read_packet.contents() {
627            PacketContents::SignalOne(sig) => {
628                assert_eq!(sig.trigger(), Signals::USER_0);
629                assert_eq!(sig.observed(), Signals::USER_0);
630                assert_eq!(sig.count(), 1);
631            }
632            _ => panic!("wrong packet type"),
633        }
634
635        // Calling wait_async_handle then cancel, we should not get a packet as cancel will
636        // remove it from  the queue.
637        assert!(event.wait_async(&port, key, Signals::USER_0, no_opts).is_ok());
638        assert!(port.cancel(key).is_ok());
639        assert_eq!(port.wait(MonotonicInstant::after(ten_ms)), Err(Status::TIMED_OUT));
640
641        // If the event is signalled after the cancel, we also shouldn't get a packet.
642        assert!(event.signal(Signals::USER_0, Signals::NONE).is_ok()); // clear signal
643        assert!(event.wait_async(&port, key, Signals::USER_0, no_opts).is_ok());
644        assert!(port.cancel(key).is_ok());
645        assert!(event.signal(Signals::NONE, Signals::USER_0).is_ok());
646        assert_eq!(port.wait(MonotonicInstant::after(ten_ms)), Err(Status::TIMED_OUT));
647    }
648
649    #[test]
650    fn guest_mem_packet() {
651        #[cfg(target_arch = "x86_64")]
652        let guest_mem_packet = sys::zx_packet_guest_mem_t {
653            addr: 0xaaaabbbbccccdddd,
654            cr3: 0x0123456789abcdef,
655            rip: 0xffffffff00000000,
656            instruction_size: 8,
657            default_operand_size: 2,
658        };
659        #[cfg(target_arch = "aarch64")]
660        let guest_mem_packet = sys::zx_packet_guest_mem_t {
661            addr: 0x8877665544332211,
662            access_size: 8,
663            sign_extend: true,
664            xt: 0xf3,
665            read: false,
666            data: 0x1122334455667788,
667        };
668        #[cfg(target_arch = "riscv64")]
669        let guest_mem_packet = {
670            let mut ret = sys::zx_packet_guest_mem_t::default();
671            ret.addr = 0x8877665544332211;
672            ret
673        };
674        const KEY: u64 = 0x5555555555555555;
675        const STATUS: i32 = sys::ZX_ERR_INTERNAL;
676
677        let packet =
678            Packet::from_guest_mem_packet(KEY, STATUS, GuestMemPacket::from_raw(guest_mem_packet));
679
680        assert_matches!(packet.contents(), PacketContents::GuestMem(GuestMemPacket(packet)) if packet == guest_mem_packet);
681        assert_eq!(packet.key(), KEY);
682        assert_eq!(packet.status(), STATUS);
683    }
684
685    #[test]
686    fn guest_io_packet() {
687        const GUEST_IO_PACKET: sys::zx_packet_guest_io_t = sys::zx_packet_guest_io_t {
688            port: 0xabcd,
689            access_size: 4,
690            input: true,
691            data: [0xaa, 0xbb, 0xcc, 0xdd],
692        };
693        const KEY: u64 = 0x0123456789abcdef;
694        const STATUS: i32 = sys::ZX_ERR_NO_RESOURCES;
695
696        let packet =
697            Packet::from_guest_io_packet(KEY, STATUS, GuestIoPacket::from_raw(GUEST_IO_PACKET));
698
699        assert_matches!(packet.contents(), PacketContents::GuestIo(GuestIoPacket(packet)) if packet == GUEST_IO_PACKET);
700        assert_eq!(packet.key(), KEY);
701        assert_eq!(packet.status(), STATUS);
702    }
703
704    #[test]
705    fn guest_vcpu_interrupt_packet() {
706        // Unable to use 'const' here because we need Default::default to initialize the PadBytes.
707        let mut guest_vcpu_packet = sys::zx_packet_guest_vcpu_t::default();
708        guest_vcpu_packet.r#type = sys::zx_packet_guest_vcpu_type_t::ZX_PKT_GUEST_VCPU_INTERRUPT;
709        guest_vcpu_packet.union = sys::zx_packet_guest_vcpu_union_t {
710            interrupt: {
711                let mut interrupt = sys::zx_packet_guest_vcpu_interrupt_t::default();
712                interrupt.mask = 0xaaaaaaaaaaaaaaaa;
713                interrupt.vector = 0x12;
714                interrupt
715            },
716        };
717        const KEY: u64 = 0x0123456789abcdef;
718        const STATUS: i32 = sys::ZX_ERR_NO_RESOURCES;
719
720        let packet = Packet::from_guest_vcpu_packet(
721            KEY,
722            STATUS,
723            GuestVcpuPacket::from_raw(guest_vcpu_packet),
724        );
725
726        assert_matches!(packet.contents(), PacketContents::GuestVcpu(GuestVcpuPacket(packet)) if packet == guest_vcpu_packet);
727        assert_eq!(packet.key(), KEY);
728        assert_eq!(packet.status(), STATUS);
729    }
730
731    #[test]
732    fn guest_vcpu_startup_packet() {
733        let mut guest_vcpu_packet = sys::zx_packet_guest_vcpu_t::default();
734        guest_vcpu_packet.r#type = sys::zx_packet_guest_vcpu_type_t::ZX_PKT_GUEST_VCPU_STARTUP;
735        guest_vcpu_packet.union = sys::zx_packet_guest_vcpu_union_t {
736            startup: sys::zx_packet_guest_vcpu_startup_t { id: 16, entry: 0xffffffff11111111 },
737        };
738        const KEY: u64 = 0x0123456789abcdef;
739        const STATUS: i32 = sys::ZX_ERR_NO_RESOURCES;
740
741        let packet = Packet::from_guest_vcpu_packet(
742            KEY,
743            STATUS,
744            GuestVcpuPacket::from_raw(guest_vcpu_packet),
745        );
746
747        assert_matches!(packet.contents(), PacketContents::GuestVcpu(GuestVcpuPacket(packet)) if packet == guest_vcpu_packet);
748        assert_eq!(packet.key(), KEY);
749        assert_eq!(packet.status(), STATUS);
750    }
751
752    #[test]
753    fn power_transition_packet() {
754        let power_transition_packet =
755            sys::zx_packet_processor_power_level_transition_request_t::default();
756        const KEY: u64 = 0x0123456789abcdef;
757        const STATUS: i32 = sys::ZX_ERR_NO_RESOURCES;
758
759        let packet = Packet::from_power_transition_packet(
760            KEY,
761            STATUS,
762            PowerTransitionPacket::from_raw(power_transition_packet),
763        );
764
765        assert_matches!(
766            packet.contents(),
767            PacketContents::PowerTransition(PowerTransitionPacket(packet)) if packet == power_transition_packet
768        );
769        assert_eq!(packet.key(), KEY);
770        assert_eq!(packet.status(), STATUS);
771    }
772}