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        #[repr(C)]
403        union DataUnion {
404            bit8: [u8; 4],
405            bit16: [u16; 2],
406            bit32: [u32; 1],
407        }
408        if let guest::AccessType::Write = self.access_type() {
409            unsafe {
410                let data = &DataUnion { bit8: self.0.data };
411                self.access_size().map(|size| match size {
412                    guest::PortAccessSize::Bits8 => guest::PortData::Data8(data.bit8[0]),
413                    guest::PortAccessSize::Bits16 => guest::PortData::Data16(data.bit16[0]),
414                    guest::PortAccessSize::Bits32 => guest::PortData::Data32(data.bit32[0]),
415                })
416            }
417        } else {
418            None
419        }
420    }
421}
422
423impl GuestVcpuPacket {
424    pub fn from_raw(vcpu: sys::zx_packet_guest_vcpu_t) -> GuestVcpuPacket {
425        GuestVcpuPacket(vcpu)
426    }
427
428    pub fn contents(&self) -> VcpuContents {
429        match self.0.r#type {
430            sys::zx_packet_guest_vcpu_type_t::ZX_PKT_GUEST_VCPU_INTERRUPT => unsafe {
431                VcpuContents::Interrupt {
432                    mask: self.0.union.interrupt.mask,
433                    vector: self.0.union.interrupt.vector,
434                }
435            },
436            sys::zx_packet_guest_vcpu_type_t::ZX_PKT_GUEST_VCPU_STARTUP => unsafe {
437                VcpuContents::Startup {
438                    id: self.0.union.startup.id,
439                    entry: self.0.union.startup.entry,
440                }
441            },
442            _ => panic!("unexpected VCPU packet type"),
443        }
444    }
445}
446
447impl PagerPacket {
448    /// Returns the page request command.
449    pub fn command(&self) -> sys::zx_page_request_command_t {
450        self.0.command
451    }
452
453    /// Returns the range for the page request.
454    pub fn range(&self) -> std::ops::Range<u64> {
455        self.0.offset..self.0.offset + self.0.length
456    }
457}
458
459impl InterruptPacket {
460    pub fn timestamp(&self) -> sys::zx_time_t {
461        self.0.timestamp
462    }
463}
464
465impl PowerTransitionPacket {
466    pub fn from_raw(
467        packet: sys::zx_packet_processor_power_level_transition_request_t,
468    ) -> PowerTransitionPacket {
469        PowerTransitionPacket(packet)
470    }
471
472    pub fn domain_id(&self) -> u32 {
473        self.0.domain_id
474    }
475
476    pub fn options(&self) -> u32 {
477        self.0.options
478    }
479
480    pub fn control_interface(&self) -> u64 {
481        self.0.control_interface
482    }
483
484    pub fn control_argument(&self) -> u64 {
485        self.0.control_argument
486    }
487}
488
489impl Port {
490    /// Create an IO port, allowing IO packets to be read and enqueued.
491    ///
492    /// Wraps the
493    /// [zx_port_create](https://fuchsia.dev/fuchsia-src/reference/syscalls/port_create.md)
494    /// syscall.
495    ///
496    /// # Panics
497    ///
498    /// If the kernel reports no memory available to create a port or the process' job policy
499    /// denies port creation.
500    pub fn create() -> Self {
501        Self::create_with_opts(PortOptions::from_bits_truncate(0))
502    }
503
504    pub fn create_with_opts(opts: PortOptions) -> Self {
505        unsafe {
506            let mut handle = 0;
507            let status = sys::zx_port_create(opts.bits(), &mut handle);
508            ok(status).expect(
509                "port creation always succeeds except with OOM or when job policy denies it",
510            );
511            NullableHandle::from_raw(handle).into()
512        }
513    }
514
515    /// Attempt to queue a user packet to the IO port.
516    ///
517    /// Wraps the
518    /// [zx_port_queue](https://fuchsia.dev/fuchsia-src/reference/syscalls/port_queue.md)
519    /// syscall.
520    pub fn queue(&self, packet: &Packet) -> Result<(), Status> {
521        let status =
522            unsafe { sys::zx_port_queue(self.raw_handle(), std::ptr::from_ref(&packet.0)) };
523        ok(status)
524    }
525
526    /// Wait for a packet to arrive on a (V2) port.
527    ///
528    /// Wraps the
529    /// [zx_port_wait](https://fuchsia.dev/fuchsia-src/reference/syscalls/port_wait.md)
530    /// syscall.
531    pub fn wait(&self, deadline: MonotonicInstant) -> Result<Packet, Status> {
532        let mut packet = Default::default();
533        ok(unsafe { sys::zx_port_wait(self.raw_handle(), deadline.into_nanos(), &mut packet) })?;
534        Ok(Packet(packet))
535    }
536
537    /// Cancel pending wait_async calls for a given key.
538    ///
539    /// Wraps the
540    /// [zx_port_cancel_key](https://fuchsia.dev/fuchsia-src/reference/syscalls/port_cancel_key.md)
541    /// syscall.
542    pub fn cancel(&self, key: u64) -> Result<(), Status> {
543        let options = 0;
544        let status = unsafe { sys::zx_port_cancel_key(self.raw_handle(), options, key) };
545        ok(status)
546    }
547}
548
549bitflags! {
550    /// Options for wait_async
551    #[repr(transparent)]
552    #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
553    pub struct WaitAsyncOpts: u32 {
554        /// When set, causes the system to capture a timestamp when the wait triggered.
555        const TIMESTAMP = sys::ZX_WAIT_ASYNC_TIMESTAMP;
556
557        /// When set, causes the system to capture a boot timestamp when the wait triggered.
558        const BOOT_TIMESTAMP = sys::ZX_WAIT_ASYNC_BOOT_TIMESTAMP;
559
560        /// When set, causes the port to not enqueue a packet for signals active at
561        /// the time of the zx_object_wait_async() call.
562        const EDGE_TRIGGERED = sys::ZX_WAIT_ASYNC_EDGE;
563    }
564}
565
566#[cfg(test)]
567mod tests {
568    use super::*;
569    use crate::{Duration, Event};
570    use assert_matches::assert_matches;
571
572    #[test]
573    fn port_basic() {
574        let ten_ms = Duration::from_millis(10);
575
576        let port = Port::create();
577
578        // Waiting now should time out.
579        assert_eq!(port.wait(MonotonicInstant::after(ten_ms)), Err(Status::TIMED_OUT));
580
581        // Send a valid packet.
582        let packet = Packet::from_user_packet(42, 123, UserPacket::from_u8_array([13; 32]));
583        assert!(port.queue(&packet).is_ok());
584
585        // Waiting should succeed this time. We should get back the packet we sent.
586        let read_packet = port.wait(MonotonicInstant::after(ten_ms)).unwrap();
587        assert_eq!(read_packet, packet);
588    }
589
590    #[test]
591    fn create_with_opts() {
592        let _port = Port::create_with_opts(PortOptions::BIND_TO_INTERRUPT);
593    }
594
595    #[test]
596    fn wait_async_once() {
597        let ten_ms = Duration::from_millis(10);
598        let key = 42;
599
600        let port = Port::create();
601        let event = Event::create();
602        let no_opts = WaitAsyncOpts::empty();
603
604        assert!(event.wait_async(&port, key, Signals::USER_0 | Signals::USER_1, no_opts).is_ok());
605
606        // Waiting without setting any signal should time out.
607        assert_eq!(port.wait(MonotonicInstant::after(ten_ms)), Err(Status::TIMED_OUT));
608
609        // If we set a signal, we should be able to wait for it.
610        assert!(event.signal(Signals::NONE, Signals::USER_0).is_ok());
611        let read_packet = port.wait(MonotonicInstant::after(ten_ms)).unwrap();
612        assert_eq!(read_packet.key(), key);
613        assert_eq!(read_packet.status(), 0);
614        match read_packet.contents() {
615            PacketContents::SignalOne(sig) => {
616                assert_eq!(sig.trigger(), Signals::USER_0 | Signals::USER_1);
617                assert_eq!(sig.observed(), Signals::USER_0);
618                assert_eq!(sig.count(), 1);
619            }
620            _ => panic!("wrong packet type"),
621        }
622
623        // Shouldn't get any more packets.
624        assert_eq!(port.wait(MonotonicInstant::after(ten_ms)), Err(Status::TIMED_OUT));
625
626        // Calling wait_async again should result in another packet.
627        assert!(event.wait_async(&port, key, Signals::USER_0, no_opts).is_ok());
628        let read_packet = port.wait(MonotonicInstant::after(ten_ms)).unwrap();
629        assert_eq!(read_packet.key(), key);
630        assert_eq!(read_packet.status(), 0);
631        match read_packet.contents() {
632            PacketContents::SignalOne(sig) => {
633                assert_eq!(sig.trigger(), Signals::USER_0);
634                assert_eq!(sig.observed(), Signals::USER_0);
635                assert_eq!(sig.count(), 1);
636            }
637            _ => panic!("wrong packet type"),
638        }
639
640        // Calling wait_async_handle then cancel, we should not get a packet as cancel will
641        // remove it from  the queue.
642        assert!(event.wait_async(&port, key, Signals::USER_0, no_opts).is_ok());
643        assert!(port.cancel(key).is_ok());
644        assert_eq!(port.wait(MonotonicInstant::after(ten_ms)), Err(Status::TIMED_OUT));
645
646        // If the event is signalled after the cancel, we also shouldn't get a packet.
647        assert!(event.signal(Signals::USER_0, Signals::NONE).is_ok()); // clear signal
648        assert!(event.wait_async(&port, key, Signals::USER_0, no_opts).is_ok());
649        assert!(port.cancel(key).is_ok());
650        assert!(event.signal(Signals::NONE, Signals::USER_0).is_ok());
651        assert_eq!(port.wait(MonotonicInstant::after(ten_ms)), Err(Status::TIMED_OUT));
652    }
653
654    #[test]
655    fn guest_mem_packet() {
656        #[cfg(target_arch = "x86_64")]
657        let guest_mem_packet = sys::zx_packet_guest_mem_t {
658            addr: 0xaaaabbbbccccdddd,
659            cr3: 0x0123456789abcdef,
660            rip: 0xffffffff00000000,
661            instruction_size: 8,
662            default_operand_size: 2,
663        };
664        #[cfg(target_arch = "aarch64")]
665        let guest_mem_packet = sys::zx_packet_guest_mem_t {
666            addr: 0x8877665544332211,
667            access_size: 8,
668            sign_extend: true,
669            xt: 0xf3,
670            read: false,
671            data: 0x1122334455667788,
672        };
673        #[cfg(target_arch = "riscv64")]
674        let guest_mem_packet = {
675            let mut ret = sys::zx_packet_guest_mem_t::default();
676            ret.addr = 0x8877665544332211;
677            ret
678        };
679        const KEY: u64 = 0x5555555555555555;
680        const STATUS: i32 = sys::ZX_ERR_INTERNAL;
681
682        let packet =
683            Packet::from_guest_mem_packet(KEY, STATUS, GuestMemPacket::from_raw(guest_mem_packet));
684
685        assert_matches!(packet.contents(), PacketContents::GuestMem(GuestMemPacket(packet)) if packet == guest_mem_packet);
686        assert_eq!(packet.key(), KEY);
687        assert_eq!(packet.status(), STATUS);
688    }
689
690    #[test]
691    fn guest_io_packet() {
692        const GUEST_IO_PACKET: sys::zx_packet_guest_io_t = sys::zx_packet_guest_io_t {
693            port: 0xabcd,
694            access_size: 4,
695            input: true,
696            data: [0xaa, 0xbb, 0xcc, 0xdd],
697        };
698        const KEY: u64 = 0x0123456789abcdef;
699        const STATUS: i32 = sys::ZX_ERR_NO_RESOURCES;
700
701        let packet =
702            Packet::from_guest_io_packet(KEY, STATUS, GuestIoPacket::from_raw(GUEST_IO_PACKET));
703
704        assert_matches!(packet.contents(), PacketContents::GuestIo(GuestIoPacket(packet)) if packet == GUEST_IO_PACKET);
705        assert_eq!(packet.key(), KEY);
706        assert_eq!(packet.status(), STATUS);
707    }
708
709    #[test]
710    fn guest_vcpu_interrupt_packet() {
711        // Unable to use 'const' here because we need Default::default to initialize the PadBytes.
712        let mut guest_vcpu_packet = sys::zx_packet_guest_vcpu_t::default();
713        guest_vcpu_packet.r#type = sys::zx_packet_guest_vcpu_type_t::ZX_PKT_GUEST_VCPU_INTERRUPT;
714        guest_vcpu_packet.union = sys::zx_packet_guest_vcpu_union_t {
715            interrupt: {
716                let mut interrupt = sys::zx_packet_guest_vcpu_interrupt_t::default();
717                interrupt.mask = 0xaaaaaaaaaaaaaaaa;
718                interrupt.vector = 0x12;
719                interrupt
720            },
721        };
722        const KEY: u64 = 0x0123456789abcdef;
723        const STATUS: i32 = sys::ZX_ERR_NO_RESOURCES;
724
725        let packet = Packet::from_guest_vcpu_packet(
726            KEY,
727            STATUS,
728            GuestVcpuPacket::from_raw(guest_vcpu_packet),
729        );
730
731        assert_matches!(packet.contents(), PacketContents::GuestVcpu(GuestVcpuPacket(packet)) if packet == guest_vcpu_packet);
732        assert_eq!(packet.key(), KEY);
733        assert_eq!(packet.status(), STATUS);
734    }
735
736    #[test]
737    fn guest_vcpu_startup_packet() {
738        let mut guest_vcpu_packet = sys::zx_packet_guest_vcpu_t::default();
739        guest_vcpu_packet.r#type = sys::zx_packet_guest_vcpu_type_t::ZX_PKT_GUEST_VCPU_STARTUP;
740        guest_vcpu_packet.union = sys::zx_packet_guest_vcpu_union_t {
741            startup: sys::zx_packet_guest_vcpu_startup_t { id: 16, entry: 0xffffffff11111111 },
742        };
743        const KEY: u64 = 0x0123456789abcdef;
744        const STATUS: i32 = sys::ZX_ERR_NO_RESOURCES;
745
746        let packet = Packet::from_guest_vcpu_packet(
747            KEY,
748            STATUS,
749            GuestVcpuPacket::from_raw(guest_vcpu_packet),
750        );
751
752        assert_matches!(packet.contents(), PacketContents::GuestVcpu(GuestVcpuPacket(packet)) if packet == guest_vcpu_packet);
753        assert_eq!(packet.key(), KEY);
754        assert_eq!(packet.status(), STATUS);
755    }
756
757    #[test]
758    fn power_transition_packet() {
759        let power_transition_packet =
760            sys::zx_packet_processor_power_level_transition_request_t::default();
761        const KEY: u64 = 0x0123456789abcdef;
762        const STATUS: i32 = sys::ZX_ERR_NO_RESOURCES;
763
764        let packet = Packet::from_power_transition_packet(
765            KEY,
766            STATUS,
767            PowerTransitionPacket::from_raw(power_transition_packet),
768        );
769
770        assert_matches!(
771            packet.contents(),
772            PacketContents::PowerTransition(PowerTransitionPacket(packet)) if packet == power_transition_packet
773        );
774        assert_eq!(packet.key(), KEY);
775        assert_eq!(packet.status(), STATUS);
776    }
777}