Coverage Report

Created: 2026-09-14 06:47

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/MigTD/deps/td-shim/tdx-tdcall/src/tdreport.rs
Line
Count
Source
1
// Copyright (c) 2021 Intel Corporation
2
//
3
// SPDX-License-Identifier: BSD-2-Clause-Patent
4
5
use core::fmt;
6
use core::mem::{size_of, zeroed, MaybeUninit};
7
use core::ptr::{slice_from_raw_parts, slice_from_raw_parts_mut};
8
use scroll::{Pread, Pwrite};
9
10
use crate::{
11
    td_call, TdCallError, TdcallArgs, TDCALL_STATUS_SUCCESS, TDCALL_TDREPORT, TDCALL_VERIFYREPORT,
12
};
13
14
pub const TD_REPORT_SIZE: usize = 0x400;
15
pub const TD_REPORT_ADDITIONAL_DATA_SIZE: usize = 64;
16
17
#[repr(C)]
18
#[derive(Debug, Pread, Pwrite, Clone, Copy)]
19
pub struct ReportType {
20
    /// Trusted Execution Environment (TEE) type
21
    /// 0x81 - TDX
22
    pub r#type: u8,
23
    /// Type-specific subtype
24
    pub subtype: u8,
25
    /// Type-specific version
26
    pub version: u8,
27
    pub reserved: u8,
28
}
29
30
#[repr(C)]
31
#[derive(Debug, Pread, Pwrite, Clone, Copy)]
32
pub struct ReportMac {
33
    /// Type header structure
34
    pub report_type: ReportType,
35
    pub reserved0: [u8; 12],
36
    /// CPU SVN
37
    pub cpu_svn: [u8; 16],
38
    /// SHA384 of the TEE_TCB_INFO
39
    pub tee_tcb_info_hash: [u8; 48],
40
    /// SHA384 of the TEE_INFO (TDG.VP.INFO)
41
    pub tee_info_hash: [u8; 48],
42
    /// A set of data used for communication between the caller and the target
43
    pub report_data: [u8; 64],
44
    pub reserved1: [u8; 32],
45
    /// The MAC over the REPORTMACSTRUCT with model-specific MAC
46
    pub mac: [u8; 32],
47
}
48
49
impl ReportMac {
50
0
    pub fn as_bytes(&self) -> &[u8] {
51
0
        unsafe { &*slice_from_raw_parts(self as *const Self as *const u8, size_of::<Self>()) }
52
0
    }
53
}
54
55
impl fmt::Display for ReportMac {
56
    // This trait requires `fmt` with this exact signature.
57
0
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
58
0
        write!(
59
0
            f,
60
0
            "Report MAC:\n\tReport Type:\n\ttype: {:x?}\tsubtype: {:x?}\
61
0
                        \tversion: {:x?}\n\tCPU SVN:\n\t{:x?}\n\
62
0
                        \tTEE TCB Info Hash:\n\t{:x?}\n\tTEE Info Hash:\n\t{:x?}\n\
63
0
                        \tReport Data:\n\t{:x?}\n\tMAC:\n\t{:x?}\n",
64
            self.report_type.r#type,
65
            self.report_type.subtype,
66
            self.report_type.version,
67
            self.cpu_svn,
68
            self.tee_tcb_info_hash,
69
            self.tee_info_hash,
70
            self.report_data,
71
            self.mac
72
        )
73
0
    }
74
}
75
76
#[repr(C)]
77
#[derive(Debug, Pread, Pwrite, Clone, Copy)]
78
pub struct TeeTcbInfo {
79
    pub valid: [u8; 8],
80
    pub tee_tcb_svn: [u8; 16],
81
    pub mrseam: [u8; 48],
82
    pub mrsigner_seam: [u8; 48],
83
    pub attributes: [u8; 8],
84
    pub tee_tcb_svn2: [u8; 16],
85
    /// Reserved. Must be zero
86
    pub reserved: [u8; 95],
87
}
88
89
impl fmt::Display for TeeTcbInfo {
90
    // This trait requires `fmt` with this exact signature.
91
0
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
92
0
        write!(
93
0
            f,
94
0
            "TEE TCB Info:\n\tValid:\n\t{:x?}\n\tTEE TCB SVN:\n\t{:x?}\n\
95
0
                        \tMR SEAM:\n\t{:x?}\n\tMR Signer SEAM:\n\t{:x?}\n\
96
0
                        \tAttributes:\n\t{:x?}\n\tTEE TCB SVN2:\n\t{:x?}\n",
97
            self.valid,
98
            self.tee_tcb_svn,
99
            self.mrseam,
100
            self.mrsigner_seam,
101
            self.attributes,
102
            self.tee_tcb_svn2
103
        )
104
0
    }
105
}
106
107
impl TeeTcbInfo {
108
0
    pub fn as_bytes(&self) -> &[u8] {
109
        unsafe {
110
0
            core::slice::from_raw_parts(self as *const TeeTcbInfo as *const u8, size_of::<Self>())
111
        }
112
0
    }
113
}
114
115
/// Defined as the TDX-specific TEE_INFO part of the TDG.MR.REPORT.
116
/// Contains the measurements and initial configuration of the TD that
117
/// The output of the TDG.MR.REPORT function
118
#[repr(C)]
119
#[derive(Debug, Pread, Pwrite, Clone, Copy)]
120
pub struct TdInfo {
121
    /// TD's attributes
122
    pub attributes: [u8; 8],
123
    /// TD's XFAM
124
    pub xfam: [u8; 8],
125
    /// Measurement of the initial contents of the TD
126
    pub mrtd: [u8; 48],
127
    /// Software-defined ID for non-owner-defined configuration of
128
    /// the guest TD
129
    pub mrconfig_id: [u8; 48],
130
    /// Software-defined ID for the guest TD's owner
131
    pub mrowner: [u8; 48],
132
    /// Software-defined ID for owner-defined configuration of
133
    /// the guest TD
134
    pub mrownerconfig: [u8; 48],
135
    /// Runtime extendable measurement registers
136
    pub rtmr0: [u8; 48],
137
    pub rtmr1: [u8; 48],
138
    pub rtmr2: [u8; 48],
139
    pub rtmr3: [u8; 48],
140
    /// SHA384 hash of the TDINFO_STRUCTs of bound Service TDs
141
    pub servtd_hash: [u8; 48],
142
    /// Reserved. Must be zero
143
    pub reserved: [u8; 64],
144
}
145
146
impl TdInfo {
147
0
    pub fn as_bytes(&self) -> &[u8] {
148
        unsafe {
149
0
            core::slice::from_raw_parts(self as *const TdInfo as *const u8, size_of::<Self>())
150
        }
151
0
    }
152
}
153
154
impl fmt::Display for TdInfo {
155
    // This trait requires `fmt` with this exact signature.
156
0
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
157
0
        write!(
158
0
            f,
159
0
            "TdInfo:\n\tAttributes:\n\t{:x?}\n\txfam:\n\t{:x?}\n\
160
0
                        \tMR TD:\n\t{:x?}\n\tMR Config ID:\n\t{:x?}\n\
161
0
                        \tMR Owner:\n\t{:x?}\n\tMR Owner Config:\n\t{:x?}\n\
162
0
                        \tRTMR[0]:\n\t{:x?}\n\tRTMR[1]:\n\t{:x?}\n\
163
0
                        \tRTMR[2]:\n\t{:x?}\n\tRTMR[3]:\n\t{:x?}\n\
164
0
                        \tServTD Hash:\n\t{:x?}\n",
165
            self.attributes,
166
            self.xfam,
167
            self.mrtd,
168
            self.mrconfig_id,
169
            self.mrowner,
170
            self.mrownerconfig,
171
            self.rtmr0,
172
            self.rtmr1,
173
            self.rtmr2,
174
            self.rtmr3,
175
            self.servtd_hash
176
        )
177
0
    }
178
}
179
180
/// Known as the TDREPORT_STRUCT defined in TDX Module Spec.
181
/// The output of the TDG.MR.REPORT function
182
///
183
/// Detailed information can be found in TDX Module Spec section 'TDREPORT_STRUCT'
184
#[repr(C, packed)]
185
#[derive(Debug, Pread, Pwrite)]
186
pub struct TdxReport {
187
    pub report_mac: ReportMac,
188
    pub tee_tcb_info: TeeTcbInfo,
189
    pub reserved: [u8; 17],
190
    pub td_info: TdInfo,
191
}
192
193
impl fmt::Display for TdxReport {
194
    // This trait requires `fmt` with this exact signature.
195
0
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
196
0
        write!(
197
0
            f,
198
0
            "TDX Report:\n{}\n{}\n{}\n",
199
            self.report_mac, self.tee_tcb_info, self.td_info
200
        )
201
0
    }
202
}
203
204
impl TdxReport {
205
0
    pub fn as_bytes(&self) -> &[u8] {
206
0
        unsafe { &*slice_from_raw_parts(self as *const Self as *const u8, size_of::<Self>()) }
207
0
    }
208
209
0
    pub fn as_bytes_mut(&mut self) -> &mut [u8] {
210
0
        unsafe { &mut *slice_from_raw_parts_mut(self as *mut Self as *mut u8, size_of::<Self>()) }
211
0
    }
212
213
0
    pub fn read_from_bytes(bytes: &[u8]) -> Option<Self> {
214
0
        if bytes.len() < size_of::<TdxReport>() {
215
0
            return None;
216
0
        }
217
218
0
        let mut uinit: MaybeUninit<TdxReport> = MaybeUninit::uninit();
219
        // Safety: MaybeUninit<TdxReport> has same layout with TdxReport
220
0
        Some(unsafe {
221
0
            core::ptr::copy_nonoverlapping(
222
0
                bytes.as_ptr(),
223
0
                uinit.as_mut_ptr() as *mut u8,
224
0
                size_of::<TdxReport>(),
225
0
            );
226
0
            uinit.assume_init()
227
0
        })
228
0
    }
229
}
230
231
impl Default for TdxReport {
232
0
    fn default() -> Self {
233
0
        unsafe { zeroed() }
234
0
    }
235
}
236
237
#[repr(C, align(1024))]
238
struct TdxReportBuf(TdxReport);
239
240
#[repr(C, align(256))]
241
struct ReportMacBuf([u8; size_of::<ReportMac>()]);
242
243
#[repr(C, align(64))]
244
struct AdditionalDataBuf([u8; TD_REPORT_ADDITIONAL_DATA_SIZE]);
245
246
/// Create a TDREPORT_STRUCT structure that contains the measurements/configuration
247
/// information of the guest TD, measurements/configuration information of the Intel
248
/// TDX module and a REPORTMACSTRUCT
249
///
250
/// Details can be found in TDX module ABI spec section 'TDG.MR.REPORT'
251
0
pub fn tdcall_report(
252
0
    additional_data: &[u8; TD_REPORT_ADDITIONAL_DATA_SIZE],
253
0
) -> Result<TdxReport, TdCallError> {
254
0
    let mut report_buf = TdxReportBuf(TdxReport::default());
255
0
    let additional_data_buf = AdditionalDataBuf(*additional_data);
256
257
0
    let mut args = TdcallArgs {
258
0
        rax: TDCALL_TDREPORT,
259
0
        rcx: &mut report_buf as *mut _ as u64,
260
0
        rdx: &additional_data_buf as *const _ as u64,
261
0
        ..Default::default()
262
0
    };
263
264
0
    let ret = td_call(&mut args);
265
0
    if ret != TDCALL_STATUS_SUCCESS {
266
0
        return Err(args.r10.into());
267
0
    }
268
269
0
    Ok(report_buf.0)
270
0
}
271
272
/// Verify a cryptographic REPORTMACSTRUCT that describes the contents of a TD,
273
/// to determine that it was created on the current TEE on the current platform
274
///
275
/// Details can be found in TDX module ABI spec section 'TDG.MR.VERIFYREPORT'
276
0
pub fn tdcall_verify_report(report_mac: &[u8]) -> Result<(), TdCallError> {
277
0
    if report_mac.len() != size_of::<ReportMac>() {
278
0
        return Err(TdCallError::TdxExitInvalidParameters);
279
0
    }
280
281
0
    let mut report_mac_buf = ReportMacBuf([0u8; size_of::<ReportMac>()]);
282
0
    report_mac_buf.0.copy_from_slice(report_mac);
283
284
0
    let mut args = TdcallArgs {
285
0
        rax: TDCALL_VERIFYREPORT,
286
0
        rcx: &mut report_mac_buf as *mut _ as u64,
287
0
        ..Default::default()
288
0
    };
289
290
0
    let ret = td_call(&mut args);
291
0
    if ret != TDCALL_STATUS_SUCCESS {
292
0
        return Err(ret.into());
293
0
    }
294
295
0
    Ok(())
296
0
}
297
298
#[cfg(test)]
299
mod tests {
300
    use super::*;
301
302
    #[test]
303
    fn test_tdx_report_size() {
304
        assert_eq!(size_of::<TdxReport>(), 0x400);
305
    }
306
}