// Copyright 2018 The Chromium OS Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. use bit_field::Error as BitFieldError; use bit_field::*; use data_model::DataInit; use std::fmt::{self, Display}; use sys_util::GuestAddress; #[derive(Debug)] pub enum Error { UnknownTrbType(BitFieldError), CannotCastTrb, } type Result = std::result::Result; impl Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { use self::Error::*; match self { UnknownTrbType(e) => write!(f, "we got an unknown trb type value: {}", e), CannotCastTrb => write!(f, "cannot cast trb from raw memory"), } } } // Fixed size of all TRB types. const TRB_SIZE: usize = 16; // Size of segment table. const SEGMENT_TABLE_SIZE: usize = 16; /// All kinds of trb. #[bitfield] #[bits = 6] #[derive(PartialEq, Debug, Clone, Copy)] pub enum TrbType { Reserved = 0, Normal = 1, SetupStage = 2, DataStage = 3, StatusStage = 4, Isoch = 5, Link = 6, EventData = 7, Noop = 8, EnableSlotCommand = 9, DisableSlotCommand = 10, AddressDeviceCommand = 11, ConfigureEndpointCommand = 12, EvaluateContextCommand = 13, ResetEndpointCommand = 14, StopEndpointCommand = 15, SetTRDequeuePointerCommand = 16, ResetDeviceCommand = 17, NoopCommand = 23, TransferEvent = 32, CommandCompletionEvent = 33, PortStatusChangeEvent = 34, } /// Completion code of trb types. #[bitfield] #[bits = 8] #[derive(PartialEq, Debug)] pub enum TrbCompletionCode { Success = 1, TransactionError = 4, TrbError = 5, NoSlotsAvailableError = 9, SlotNotEnabledError = 11, ShortPacket = 13, ContextStateError = 19, } /// State of device slot. #[bitfield] #[bits = 5] #[derive(PartialEq, Debug)] pub enum DeviceSlotState { // The same value (0) is used for both the enabled and disabled states. See // xhci spec table 60. DisabledOrEnabled = 0, Default = 1, Addressed = 2, Configured = 3, } /// State of endpoint. #[bitfield] #[bits = 3] #[derive(PartialEq, Debug)] pub enum EndpointState { Disabled = 0, Running = 1, } #[bitfield] #[bits = 60] #[derive(PartialEq, Debug)] pub struct DequeuePtr(u64); impl DequeuePtr { pub fn new(addr: GuestAddress) -> Self { DequeuePtr(addr.0 >> 4) } // Get the guest physical address. pub fn get_gpa(&self) -> GuestAddress { GuestAddress(self.0 << 4) } } // Generic TRB struct containing only fields common to all types. #[bitfield] #[derive(Clone, Copy, PartialEq)] pub struct Trb { parameter: B64, status: B32, cycle: bool, flags: B9, trb_type: TrbType, control: B16, } impl Trb { fn fmt_helper(&self, f: &mut fmt::Formatter) -> Result { match self.get_trb_type().map_err(Error::UnknownTrbType)? { TrbType::Reserved => Ok(write!(f, "reserved trb type")), TrbType::Normal => { let t = self.cast::()?; Ok(write!(f, "trb: {:?}", t)) } TrbType::SetupStage => { let t = self.cast::()?; Ok(write!(f, "trb: {:?}", t)) } TrbType::DataStage => { let t = self.cast::()?; Ok(write!(f, "trb: {:?}", t)) } TrbType::StatusStage => { let t = self.cast::()?; Ok(write!(f, "trb: {:?}", t)) } TrbType::Isoch => { let t = self.cast::()?; Ok(write!(f, "trb: {:?}", t)) } TrbType::Link => { let t = self.cast::()?; Ok(write!(f, "trb: {:?}", t)) } TrbType::EventData => { let t = self.cast::()?; Ok(write!(f, "trb: {:?}", t)) } TrbType::Noop => { let t = self.cast::()?; Ok(write!(f, "trb: {:?}", t)) } TrbType::EnableSlotCommand => Ok(write!(f, "trb: enable slot command {:?}", self)), TrbType::DisableSlotCommand => { let t = self.cast::()?; Ok(write!(f, "trb: {:?}", t)) } TrbType::AddressDeviceCommand => { let t = self.cast::()?; Ok(write!(f, "trb: {:?}", t)) } TrbType::ConfigureEndpointCommand => { let t = self.cast::()?; Ok(write!(f, "trb: {:?}", t)) } TrbType::EvaluateContextCommand => { let t = self.cast::()?; Ok(write!(f, "trb: {:?}", t)) } TrbType::ResetEndpointCommand => { let t = self.cast::()?; Ok(write!(f, "trb: {:?}", t)) } TrbType::StopEndpointCommand => { let t = self.cast::()?; Ok(write!(f, "trb: {:?}", t)) } TrbType::SetTRDequeuePointerCommand => { let t = self.cast::()?; Ok(write!(f, "trb: {:?}", t)) } TrbType::ResetDeviceCommand => { let t = self.cast::()?; Ok(write!(f, "trb: {:?}", t)) } TrbType::NoopCommand => Ok(write!(f, "trb: noop command {:?}", self)), TrbType::TransferEvent => { let t = self.cast::()?; Ok(write!(f, "trb: {:?}", t)) } TrbType::CommandCompletionEvent => { let t = self.cast::()?; Ok(write!(f, "trb: {:?}", t)) } TrbType::PortStatusChangeEvent => { let t = self.cast::()?; Ok(write!(f, "trb: {:?}", t)) } } } } impl Display for Trb { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self.fmt_helper(f) { Ok(f) => f, Err(e) => write!(f, "fail to format trb {}", e), } } } impl Trb { /// Get chain bit. pub fn get_chain_bit(&self) -> Result { Ok(match self.get_trb_type() { Ok(TrbType::Normal) => self.cast::()?.get_chain(), Ok(TrbType::DataStage) => self.cast::()?.get_chain(), Ok(TrbType::StatusStage) => self.cast::()?.get_chain(), Ok(TrbType::Isoch) => self.cast::()?.get_chain(), Ok(TrbType::Noop) => self.cast::()?.get_chain(), Ok(TrbType::Link) => self.cast::()?.get_chain(), Ok(TrbType::EventData) => self.cast::()?.get_chain(), _ => false, }) } /// Get interrupt target. pub fn interrupter_target(&self) -> u8 { const STATUS_INTERRUPTER_TARGET_OFFSET: u8 = 22; (self.get_status() >> STATUS_INTERRUPTER_TARGET_OFFSET) as u8 } /// Only some of trb types could appear in transfer ring. pub fn can_be_in_transfer_ring(&self) -> Result { match self.get_trb_type().map_err(Error::UnknownTrbType)? { TrbType::Normal | TrbType::SetupStage | TrbType::DataStage | TrbType::StatusStage | TrbType::Isoch | TrbType::Link | TrbType::EventData | TrbType::Noop => Ok(true), _ => Ok(false), } } /// Length of this transfer. pub fn transfer_length(&self) -> Result { const STATUS_TRANSFER_LENGTH_MASK: u32 = 0x1ffff; match self.get_trb_type().map_err(Error::UnknownTrbType)? { TrbType::Normal | TrbType::SetupStage | TrbType::DataStage | TrbType::Isoch => { Ok(self.get_status() & STATUS_TRANSFER_LENGTH_MASK) } _ => Ok(0), } } /// Returns true if interrupt is required on completion. pub fn interrupt_on_completion(&self) -> bool { const FLAGS_INTERRUPT_ON_COMPLETION_MASK: u16 = 0x10; (self.get_flags() & FLAGS_INTERRUPT_ON_COMPLETION_MASK) > 0 } /// Returns true if interrupt is required on transfer of short packet. pub fn interrupt_on_short_packet(&self) -> bool { const FLAGS_INTERRUPT_ON_SHORT_PACKET: u16 = 0x2; (self.get_flags() & FLAGS_INTERRUPT_ON_SHORT_PACKET) > 0 } /// Returns true if this trb is immediate data. pub fn immediate_data(&self) -> Result { const FLAGS_IMMEDIATE_DATA_MASK: u16 = 0x20; match self.get_trb_type().map_err(Error::UnknownTrbType)? { TrbType::Normal | TrbType::SetupStage | TrbType::DataStage | TrbType::Isoch => { Ok((self.get_flags() & FLAGS_IMMEDIATE_DATA_MASK) != 0) } _ => Ok(false), } } } #[bitfield] #[derive(Clone, Copy)] pub struct NormalTrb { data_buffer: B64, trb_transfer_length: B17, td_size: B5, interrupter_target: B10, cycle: bool, evaluate_next_trb: B1, interrupt_on_short_packet: B1, no_snoop: B1, chain: bool, interrupt_on_completion: B1, immediate_data: B1, reserved: B2, block_event_interrupt: B1, trb_type: TrbType, reserved1: B16, } #[bitfield] #[derive(Clone, Copy)] pub struct SetupStageTrb { request_type: B8, request: B8, value: B16, index: B16, length: B16, trb_transfer_length: B17, reserved0: B5, interrupter_target: B10, cycle: bool, reserved1: B4, interrupt_on_completion: B1, immediate_data: B1, reserved2: B3, trb_type: TrbType, transfer_type: B2, reserved3: B14, } #[bitfield] #[derive(Clone, Copy)] pub struct DataStageTrb { data_buffer_pointer: B64, trb_transfer_length: B17, td_size: B5, interrupter_target: B10, cycle: bool, evaluate_next_trb: B1, interrupt_on_short_packet: B1, no_snoop: B1, chain: bool, interrupt_on_completion: B1, immediate_data: B1, reserved0: B3, trb_type: TrbType, direction: B1, reserved1: B15, } #[bitfield] #[derive(Clone, Copy)] pub struct StatusStageTrb { reserved0: B64, reserved1: B22, interrupter_target: B10, cycle: bool, evaluate_next_trb: B1, reserved2: B2, chain: bool, interrupt_on_completion: B1, reserved3: B4, trb_type: TrbType, direction: B1, reserved4: B15, } #[bitfield] #[derive(Clone, Copy)] pub struct IsochTrb { data_buffer_pointer: B64, trb_transfer_length: B17, td_size: B5, interrupter_target: B10, cycle: bool, evaulate_nex_trb: B1, interrupt_on_short_packet: B1, no_snoop: B1, chain: bool, interrupt_on_completion: B1, immediate_data: B1, transfer_burst_count: B2, block_event_interrupt: B1, trb_type: TrbType, tlbpc: B4, frame_id: B11, sia: B1, } #[bitfield] #[derive(Clone, Copy)] pub struct LinkTrb { ring_segment_pointer: B64, reserved0: B22, interrupter_target: B10, cycle: bool, toggle_cycle: bool, reserved1: B2, chain: bool, interrupt_on_completion: bool, reserved2: B4, trb_type: TrbType, reserved3: B16, } #[bitfield] #[derive(Clone, Copy)] pub struct EventDataTrb { event_data: B64, reserved0: B22, interrupter_target: B10, cycle: bool, evaluate_next_trb: B1, reserved1: B2, chain: bool, interrupt_on_completion: B1, reserved2: B3, block_event_interrupt: B1, trb_type: TrbType, reserved3: B16, } #[bitfield] #[derive(Clone, Copy)] pub struct NoopTrb { reserved0: B64, reserved1: B22, interrupter_target: B10, cycle: bool, evaluate_next_trb: B1, reserved2: B2, chain: bool, interrupt_on_completion: B1, reserved3: B4, trb_type: TrbType, reserved4: B16, } #[bitfield] #[derive(Clone, Copy)] pub struct DisableSlotCommandTrb { reserved0: B32, reserved1: B32, reserved2: B32, cycle: bool, reserved3: B9, trb_type: TrbType, reserved4: B8, slot_id: B8, } #[bitfield] #[derive(Clone, Copy)] pub struct AddressDeviceCommandTrb { input_context_pointer: B64, reserved: B32, cycle: bool, reserved2: B8, block_set_address_request: bool, trb_type: TrbType, reserved3: B8, slot_id: B8, } #[bitfield] #[derive(Clone, Copy)] pub struct ConfigureEndpointCommandTrb { input_context_pointer: B64, reserved0: B32, cycle: bool, reserved1: B8, deconfigure: bool, trb_type: TrbType, reserved2: B8, slot_id: B8, } #[bitfield] #[derive(Clone, Copy)] pub struct EvaluateContextCommandTrb { input_context_pointer: B64, reserved0: B32, cycle: bool, reserved1: B9, trb_type: TrbType, reserved2: B8, slot_id: B8, } #[bitfield] #[derive(Clone, Copy)] pub struct ResetEndpointCommandTrb { reserved0: B32, reserved1: B32, reserved2: B32, cycle: bool, reserved3: B8, transfer_state_preserve: B1, trb_type: TrbType, endpoint_id: B5, reserved4: B3, slot_id: B8, } #[bitfield] #[derive(Clone, Copy)] pub struct StopEndpointCommandTrb { reserved0: B32, reserved1: B32, reserved2: B32, cycle: bool, reserved3: B9, trb_type: TrbType, endpoint_id: B5, reserved4: B2, suspend: B1, slot_id: B8, } #[bitfield] #[derive(Clone, Copy)] pub struct SetTRDequeuePointerCommandTrb { dequeue_cycle_state: bool, stream_context_type: B3, dequeue_ptr: DequeuePtr, reserved0: B16, stream_id: B16, cycle: bool, reserved1: B9, trb_type: TrbType, endpoint_id: B5, reserved3: B2, suspend: B1, slot_id: B8, } #[bitfield] #[derive(Clone, Copy)] pub struct ResetDeviceCommandTrb { reserved0: B32, reserved1: B32, reserved2: B32, cycle: bool, reserved3: B9, trb_type: TrbType, reserved4: B8, slot_id: B8, } #[bitfield] #[derive(Clone, Copy)] pub struct TransferEventTrb { trb_pointer: B64, trb_transfer_length: B24, completion_code: TrbCompletionCode, cycle: bool, reserved0: B1, event_data: B1, reserved1: B7, trb_type: TrbType, endpoint_id: B5, reserved2: B3, slot_id: B8, } #[bitfield] #[derive(Clone, Copy)] pub struct CommandCompletionEventTrb { trb_pointer: B64, command_completion_parameter: B24, completion_code: TrbCompletionCode, cycle: bool, reserved: B9, trb_type: TrbType, vf_id: B8, slot_id: B8, } #[bitfield] #[derive(Clone, Copy)] pub struct PortStatusChangeEventTrb { reserved0: B24, port_id: B8, reserved1: B32, reserved2: B24, completion_code: TrbCompletionCode, cycle: bool, reserved3: B9, trb_type: TrbType, reserved4: B16, } /// Associate real type of trb. pub trait TypedTrb { const TY: TrbType; } impl TypedTrb for Trb { const TY: TrbType = TrbType::Reserved; } impl TypedTrb for NormalTrb { const TY: TrbType = TrbType::Normal; } impl TypedTrb for SetupStageTrb { const TY: TrbType = TrbType::SetupStage; } impl TypedTrb for DataStageTrb { const TY: TrbType = TrbType::DataStage; } impl TypedTrb for StatusStageTrb { const TY: TrbType = TrbType::StatusStage; } impl TypedTrb for IsochTrb { const TY: TrbType = TrbType::Isoch; } impl TypedTrb for LinkTrb { const TY: TrbType = TrbType::Link; } impl TypedTrb for EventDataTrb { const TY: TrbType = TrbType::EventData; } impl TypedTrb for NoopTrb { const TY: TrbType = TrbType::Noop; } impl TypedTrb for DisableSlotCommandTrb { const TY: TrbType = TrbType::DisableSlotCommand; } impl TypedTrb for AddressDeviceCommandTrb { const TY: TrbType = TrbType::AddressDeviceCommand; } impl TypedTrb for ConfigureEndpointCommandTrb { const TY: TrbType = TrbType::ConfigureEndpointCommand; } impl TypedTrb for EvaluateContextCommandTrb { const TY: TrbType = TrbType::EvaluateContextCommand; } impl TypedTrb for ResetEndpointCommandTrb { const TY: TrbType = TrbType::ResetEndpointCommand; } impl TypedTrb for StopEndpointCommandTrb { const TY: TrbType = TrbType::StopEndpointCommand; } impl TypedTrb for SetTRDequeuePointerCommandTrb { const TY: TrbType = TrbType::SetTRDequeuePointerCommand; } impl TypedTrb for ResetDeviceCommandTrb { const TY: TrbType = TrbType::ResetDeviceCommand; } impl TypedTrb for TransferEventTrb { const TY: TrbType = TrbType::TransferEvent; } impl TypedTrb for CommandCompletionEventTrb { const TY: TrbType = TrbType::CommandCompletionEvent; } impl TypedTrb for PortStatusChangeEventTrb { const TY: TrbType = TrbType::PortStatusChangeEvent; } /// All trb structs have the same size. One trb could be safely casted to another, though the /// values might be invalid. pub unsafe trait TrbCast: DataInit + TypedTrb { fn cast(&self) -> Result<&T> { T::from_slice(self.as_slice()).ok_or(Error::CannotCastTrb) } fn cast_mut(&mut self) -> Result<&mut T> { T::from_mut_slice(self.as_mut_slice()).ok_or(Error::CannotCastTrb) } fn checked_cast(&self) -> Result<&T> { if Trb::from_slice(self.as_slice()) .ok_or(Error::CannotCastTrb)? .get_trb_type() .map_err(Error::UnknownTrbType)? != T::TY { return Err(Error::CannotCastTrb); } T::from_slice(self.as_slice()).ok_or(Error::CannotCastTrb) } fn checked_mut_cast(&mut self) -> Result<&mut T> { if Trb::from_slice(self.as_slice()) .ok_or(Error::CannotCastTrb)? .get_trb_type() .map_err(Error::UnknownTrbType)? != T::TY { return Err(Error::CannotCastTrb); } T::from_mut_slice(self.as_mut_slice()).ok_or(Error::CannotCastTrb) } } unsafe impl DataInit for Trb {} unsafe impl DataInit for NormalTrb {} unsafe impl DataInit for SetupStageTrb {} unsafe impl DataInit for DataStageTrb {} unsafe impl DataInit for StatusStageTrb {} unsafe impl DataInit for IsochTrb {} unsafe impl DataInit for LinkTrb {} unsafe impl DataInit for EventDataTrb {} unsafe impl DataInit for NoopTrb {} unsafe impl DataInit for DisableSlotCommandTrb {} unsafe impl DataInit for AddressDeviceCommandTrb {} unsafe impl DataInit for ConfigureEndpointCommandTrb {} unsafe impl DataInit for EvaluateContextCommandTrb {} unsafe impl DataInit for ResetEndpointCommandTrb {} unsafe impl DataInit for StopEndpointCommandTrb {} unsafe impl DataInit for SetTRDequeuePointerCommandTrb {} unsafe impl DataInit for ResetDeviceCommandTrb {} unsafe impl DataInit for TransferEventTrb {} unsafe impl DataInit for CommandCompletionEventTrb {} unsafe impl DataInit for PortStatusChangeEventTrb {} unsafe impl DataInit for EventRingSegmentTableEntry {} unsafe impl DataInit for InputControlContext {} unsafe impl DataInit for SlotContext {} unsafe impl DataInit for EndpointContext {} unsafe impl DataInit for DeviceContext {} unsafe impl DataInit for AddressedTrb {} unsafe impl TrbCast for Trb {} unsafe impl TrbCast for NormalTrb {} unsafe impl TrbCast for SetupStageTrb {} unsafe impl TrbCast for DataStageTrb {} unsafe impl TrbCast for StatusStageTrb {} unsafe impl TrbCast for IsochTrb {} unsafe impl TrbCast for LinkTrb {} unsafe impl TrbCast for EventDataTrb {} unsafe impl TrbCast for NoopTrb {} unsafe impl TrbCast for DisableSlotCommandTrb {} unsafe impl TrbCast for AddressDeviceCommandTrb {} unsafe impl TrbCast for ConfigureEndpointCommandTrb {} unsafe impl TrbCast for EvaluateContextCommandTrb {} unsafe impl TrbCast for ResetEndpointCommandTrb {} unsafe impl TrbCast for StopEndpointCommandTrb {} unsafe impl TrbCast for SetTRDequeuePointerCommandTrb {} unsafe impl TrbCast for ResetDeviceCommandTrb {} unsafe impl TrbCast for TransferEventTrb {} unsafe impl TrbCast for CommandCompletionEventTrb {} unsafe impl TrbCast for PortStatusChangeEventTrb {} #[bitfield] #[derive(Clone, Copy)] pub struct EventRingSegmentTableEntry { ring_segment_base_address: B64, ring_segment_size: B16, reserved2: B48, } #[bitfield] #[derive(Clone, Copy)] pub struct InputControlContext { // Xhci spec 6.2.5.1. drop_context_flags: B32, add_context_flags: B32, reserved0: B32, reserved1: B32, reserved2: B32, reserved3: B32, reserved4: B32, configuration_value: B8, interface_number: B8, alternate_setting: B8, reserved5: B8, } impl InputControlContext { /// Get drop context flag. pub fn drop_context_flag(&self, idx: u8) -> bool { (self.get_drop_context_flags() & (1 << idx)) != 0 } /// Get add context flag. pub fn add_context_flag(&self, idx: u8) -> bool { (self.get_add_context_flags() & (1 << idx)) != 0 } } // Size of device context entries (SlotContext and EndpointContext). pub const DEVICE_CONTEXT_ENTRY_SIZE: usize = 32usize; #[bitfield] #[derive(Clone, Copy)] pub struct SlotContext { route_string: B20, speed: B4, reserved1: B1, mtt: B1, hub: B1, context_entries: B5, max_exit_latency: B16, root_hub_port_number: B8, num_ports: B8, tt_hub_slot_id: B8, tt_port_number: B8, tt_think_time: B2, reserved2: B4, interrupter_target: B10, usb_device_address: B8, reserved3: B19, slot_state: DeviceSlotState, reserved4: B32, reserved5: B32, reserved6: B32, reserved7: B32, } #[bitfield] #[derive(Clone, Copy)] pub struct EndpointContext { endpoint_state: EndpointState, reserved1: B5, mult: B2, max_primary_streams: B5, linear_stream_array: B1, interval: B8, max_esit_payload_hi: B8, reserved2: B1, error_count: B2, endpoint_type: B3, reserved3: B1, host_initiate_disable: B1, max_burst_size: B8, max_packet_size: B16, dequeue_cycle_state: bool, reserved4: B3, tr_dequeue_pointer: DequeuePtr, average_trb_length: B16, max_esit_payload_lo: B16, reserved5: B32, reserved6: B32, reserved7: B32, } /// Device context. #[derive(Clone, Copy, Debug)] pub struct DeviceContext { pub slot_context: SlotContext, pub endpoint_context: [EndpointContext; 31], } /// POD struct associates a TRB with its address in guest memory. This is /// useful because transfer and command completion event TRBs must contain /// pointers to the original TRB that generated the event. #[derive(Clone, Copy, Debug, PartialEq)] pub struct AddressedTrb { pub trb: Trb, pub gpa: u64, } pub type TransferDescriptor = Vec; #[cfg(test)] mod tests { use super::*; #[test] fn check_struct_sizes() { assert_eq!(std::mem::size_of::(), TRB_SIZE); assert_eq!(std::mem::size_of::(), TRB_SIZE); assert_eq!(std::mem::size_of::(), TRB_SIZE); assert_eq!(std::mem::size_of::(), TRB_SIZE); assert_eq!(std::mem::size_of::(), TRB_SIZE); assert_eq!(std::mem::size_of::(), TRB_SIZE); assert_eq!(std::mem::size_of::(), TRB_SIZE); assert_eq!(std::mem::size_of::(), TRB_SIZE); assert_eq!(std::mem::size_of::(), TRB_SIZE); assert_eq!(std::mem::size_of::(), TRB_SIZE); assert_eq!(std::mem::size_of::(), TRB_SIZE); assert_eq!(std::mem::size_of::(), TRB_SIZE); assert_eq!(std::mem::size_of::(), TRB_SIZE); assert_eq!(std::mem::size_of::(), TRB_SIZE); assert_eq!(std::mem::size_of::(), TRB_SIZE); assert_eq!( std::mem::size_of::(), TRB_SIZE ); assert_eq!(std::mem::size_of::(), TRB_SIZE); assert_eq!(std::mem::size_of::(), TRB_SIZE); assert_eq!(std::mem::size_of::(), TRB_SIZE); assert_eq!(std::mem::size_of::(), TRB_SIZE); assert_eq!( std::mem::size_of::(), SEGMENT_TABLE_SIZE ); assert_eq!(std::mem::size_of::(), 32); assert_eq!( std::mem::size_of::(), DEVICE_CONTEXT_ENTRY_SIZE ); assert_eq!( std::mem::size_of::(), DEVICE_CONTEXT_ENTRY_SIZE ); assert_eq!( std::mem::size_of::(), 32 * DEVICE_CONTEXT_ENTRY_SIZE ); } }