1use crate::{
8 GPAddr, MonotonicInstant, NullableHandle, Signals, Status, VcpuContents, guest, ok, sys,
9};
10use bitflags::bitflags;
11use std::mem;
12
13#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
18#[repr(transparent)]
19pub struct Port(NullableHandle);
20impl_handle_based!(Port);
21
22bitflags! {
23 pub struct PortOptions: u32 {
25 const BIND_TO_INTERRUPT = sys::ZX_PORT_BIND_TO_INTERRUPT;
26 }
27}
28
29#[derive(Debug, Copy, Clone, Eq, PartialEq)]
31pub enum PacketContents {
32 User(UserPacket),
34 SignalOne(SignalPacket),
36 GuestBell(GuestBellPacket),
38 GuestMem(GuestMemPacket),
40 GuestIo(GuestIoPacket),
42 GuestVcpu(GuestVcpuPacket),
44 Pager(PagerPacket),
46 Interrupt(InterruptPacket),
48 PowerTransition(PowerTransitionPacket),
51
52 #[doc(hidden)]
53 __Nonexhaustive,
54}
55
56#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
59pub struct UserPacket(sys::zx_packet_user_t);
60
61#[derive(Copy, Clone, Debug, Eq, PartialEq)]
64pub struct SignalPacket(sys::zx_packet_signal_t);
65
66#[derive(Copy, Clone, Debug, Eq, PartialEq)]
69pub struct GuestBellPacket(sys::zx_packet_guest_bell_t);
70
71#[derive(Copy, Clone, Debug, Eq, PartialEq)]
74pub struct GuestMemPacket(sys::zx_packet_guest_mem_t);
75
76#[derive(Copy, Clone, Debug, Eq, PartialEq)]
79pub struct GuestIoPacket(sys::zx_packet_guest_io_t);
80
81#[derive(Copy, Clone, Debug, Eq, PartialEq)]
84pub struct GuestVcpuPacket(sys::zx_packet_guest_vcpu_t);
85
86#[derive(Copy, Clone, Debug, Eq, PartialEq)]
89pub struct PagerPacket(sys::zx_packet_page_request_t);
90
91#[derive(Copy, Clone, Debug, Eq, PartialEq)]
94pub struct InterruptPacket(sys::zx_packet_interrupt_t);
95
96#[derive(Copy, Clone, Debug, Eq, PartialEq)]
99pub struct PowerTransitionPacket(sys::zx_packet_processor_power_level_transition_request_t);
100
101#[derive(PartialEq, Eq, Debug)]
104pub struct Packet(pub(crate) sys::zx_port_packet_t);
105
106impl Packet {
107 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 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 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 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 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 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 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 pub fn key(&self) -> u64 {
181 self.0.key
182 }
183
184 pub fn status(&self) -> i32 {
187 self.0.status
188 }
189
190 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 pub fn trigger(&self) -> Signals {
265 Signals::from_bits_truncate(self.0.trigger)
266 }
267
268 pub fn observed(&self) -> Signals {
270 Signals::from_bits_truncate(self.0.observed)
271 }
272
273 pub fn count(&self) -> u64 {
275 self.0.count
276 }
277
278 pub fn timestamp(&self) -> sys::zx_time_t {
280 self.0.timestamp
281 }
282
283 pub fn raw_packet(&self) -> &sys::zx_packet_signal_t {
285 &self.0
286 }
287}
288
289impl GuestBellPacket {
290 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 pub fn addr(&self) -> GPAddr {
303 GPAddr(self.0.addr)
304 }
305}
306
307#[cfg(target_arch = "aarch64")]
308impl GuestMemPacket {
309 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 pub fn sign_extend(&self) -> bool {
324 self.0.sign_extend
325 }
326
327 pub fn reg(&self) -> u8 {
329 self.0.xt
330 }
331
332 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 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 pub fn default_operand_size(&self) -> Option<guest::CSDefaultOperandSize> {
361 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 pub fn port(&self) -> u16 {
377 self.0.port
378 }
379
380 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 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 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 pub fn command(&self) -> sys::zx_page_request_command_t {
445 self.0.command
446 }
447
448 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 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 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 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 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 #[repr(transparent)]
547 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
548 pub struct WaitAsyncOpts: u32 {
549 const TIMESTAMP = sys::ZX_WAIT_ASYNC_TIMESTAMP;
551
552 const BOOT_TIMESTAMP = sys::ZX_WAIT_ASYNC_BOOT_TIMESTAMP;
554
555 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 assert_eq!(port.wait(MonotonicInstant::after(ten_ms)), Err(Status::TIMED_OUT));
575
576 let packet = Packet::from_user_packet(42, 123, UserPacket::from_u8_array([13; 32]));
578 assert!(port.queue(&packet).is_ok());
579
580 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 assert_eq!(port.wait(MonotonicInstant::after(ten_ms)), Err(Status::TIMED_OUT));
603
604 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 assert_eq!(port.wait(MonotonicInstant::after(ten_ms)), Err(Status::TIMED_OUT));
620
621 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 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 assert!(event.signal(Signals::USER_0, Signals::NONE).is_ok()); 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 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}