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}