tdisp/
devicereport.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4use bitfield_struct::bitfield;
5use zerocopy::FromBytes;
6use zerocopy::Immutable;
7use zerocopy::KnownLayout;
8
9/// PCI Express Base Specification Revision 6.3 Section 11.3.11 DEVICE_INTERFACE_REPORT
10#[bitfield(u16)]
11#[derive(KnownLayout, FromBytes, Immutable)]
12pub struct TdispTdiReportInterfaceInfo {
13    /// When 1, indicates that device firmware updates are not permitted
14    /// while in CONFIG_LOCKED or RUN. When 0, indicates that firmware
15    /// updates are permitted while in these states
16    pub firmware_update_allowed: bool,
17
18    /// TDI generates DMA requests without PASID
19    pub generate_dma_without_pasid: bool,
20
21    /// TDI generates DMA requests with PASID
22    pub generate_dma_with_pasid: bool,
23
24    /// ATS supported and enabled for the TDI
25    pub ats_support_enabled: bool,
26
27    /// PRS supported and enabled for the TDI
28    pub prs_support_enabled: bool,
29    #[bits(11)]
30    _reserved0: u16,
31}
32
33/// PCI Express Base Specification Revision 6.3 Section 11.3.11 DEVICE_INTERFACE_REPORT
34#[bitfield(u16)]
35#[derive(KnownLayout, FromBytes, Immutable)]
36pub struct TdispTdiReportMmioFlags {
37    /// MSI-X Table – if the range maps MSI-X table. This must be reported only if locked by the LOCK_INTERFACE_REQUEST.
38    pub range_maps_msix_table: bool,
39
40    /// MSI-X PBA – if the range maps MSI-X PBA. This must be reported only if locked by the LOCK_INTERFACE_REQUEST.
41    pub range_maps_msix_pba: bool,
42
43    /// IS_NON_TEE_MEM – must be 1b if the range is non-TEE memory.
44    /// For attribute updatable ranges (see below), this field must indicate attribute of the range when the TDI was locked.
45    pub is_non_tee_mem: bool,
46
47    /// IS_MEM_ATTR_UPDATABLE – must be 1b if the attributes of this range is updatable using SET_MMIO_ATTRIBUTE_REQUEST
48    pub is_mem_attr_updatable: bool,
49    #[bits(12)]
50    _reserved0: u16,
51}
52
53/// PCI Express Base Specification Revision 6.3 Section 11.3.11 DEVICE_INTERFACE_REPORT
54#[derive(KnownLayout, FromBytes, Immutable, Clone, Debug)]
55pub struct TdispTdiReportMmioInterfaceInfo {
56    /// First 4K page with offset added
57    pub first_4k_page_offset: u64,
58
59    /// Number of 4K pages in this range
60    pub num_4k_pages: u32,
61
62    /// Range Attributes
63    pub flags: TdispTdiReportMmioFlags,
64
65    /// Range ID – a device specific identifier for the specified range.
66    /// The range ID may be used to logically group one or more MMIO ranges into a larger range.
67    pub range_id: u16,
68}
69
70static_assertions::const_assert_eq!(size_of::<TdispTdiReportMmioInterfaceInfo>(), 0x10);
71
72/// PCI Express Base Specification Revision 6.3 Section 11.3.11 DEVICE_INTERFACE_REPORT
73#[derive(KnownLayout, FromBytes, Immutable, Debug)]
74#[repr(C)]
75struct TdiReportStructSerialized {
76    pub interface_info: TdispTdiReportInterfaceInfo,
77    _reserved0: u16,
78    pub msi_x_message_control: u16,
79    pub lnr_control: u16,
80    pub tph_control: u32,
81    pub mmio_range_count: u32,
82    // Follows is a variable-sized # of `MmioInterfaceInfo` structs
83    // based on the value of `mmio_range_count`.
84}
85
86static_assertions::const_assert_eq!(size_of::<TdiReportStructSerialized>(), 0x10);
87
88/// The deserialized form of a TDI interface report.
89#[derive(Debug)]
90pub struct TdiReportStruct {
91    /// See: `TdispTdiReportInterfaceInfo`
92    pub interface_info: TdispTdiReportInterfaceInfo,
93
94    /// MSI-X capability message control register state. Must be Clear if
95    /// a) capability is not supported or b) MSI-X table is not locked
96    pub msi_x_message_control: u16,
97
98    /// LNR control register from LN Requester Extended Capability.
99    /// Must be Clear if LNR capability is not supported. LN is deprecated in PCIe Revision 6.0.
100    pub lnr_control: u16,
101
102    /// TPH Requester Control Register from the TPH Requester Extended Capability.
103    /// Must be Clear if a) TPH capability is not support or b) MSI-X table is not locked
104    pub tph_control: u32,
105
106    /// Each MMIO Range of the TDI is reported with the MMIO reporting offset added.
107    /// Base and size in units of 4K pages
108    pub mmio_interface_info: Vec<TdispTdiReportMmioInterfaceInfo>,
109}
110
111/// Reads a TDI interface report provided from the host into a struct.
112pub fn deserialize_tdi_report(data: &[u8]) -> anyhow::Result<TdiReportStruct> {
113    // Deserialize the static part of the report.
114    let report_header = TdiReportStructSerialized::read_from_prefix(data)
115        .map_err(|e| anyhow::anyhow!("failed to deserialize TDI report header: {e:?}"))?;
116    let variable_portion_offset = report_header.1;
117    let report = report_header.0;
118
119    // Deserialize the variable portion of the report.
120    let read_mmio_elems = <[TdispTdiReportMmioInterfaceInfo]>::ref_from_prefix_with_elems(
121        variable_portion_offset,
122        report.mmio_range_count as usize,
123    )
124    .map_err(|e| anyhow::anyhow!("failed to deserialize TDI report mmio_interface_info: {e:?}"))?;
125
126    // TDISP TODO: Parse the vendor specific info
127    let _vendor_specific_info = read_mmio_elems.1.to_vec();
128
129    Ok(TdiReportStruct {
130        interface_info: report.interface_info,
131        msi_x_message_control: report.msi_x_message_control,
132        lnr_control: report.lnr_control,
133        tph_control: report.tph_control,
134        mmio_interface_info: read_mmio_elems.0.to_vec(),
135    })
136}