• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2 * Copyright (C) 2025 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 
17 //! Structs and items that define TPM communications with the GSC
18 
19 use zerocopy::{FromBytes, FromZeros, Immutable, IntoBytes, KnownLayout, Unaligned, BE, U16, U32};
20 
21 /// Indicates that the request will run without a session. All TPM vendor
22 /// commands use this tag
23 const NO_SESSION_TAG: U16<BE> = U16::new(0x8001);
24 
25 /// The ordinal value that is used for all vendor commands
26 const VENDOR_COMMAND_ORDINAL: U32<BE> = U32::new(0x20000000);
27 
28 /// The ordinal value that is used for all extension commands
29 const EXTENSION_COMMAND_ORDINAL: U32<BE> = U32::new(0xBACCD00A);
30 
31 /// A `u32` that is always equal to zero
32 #[derive(
33     Clone, Copy, Debug, Default, Eq, FromZeros, IntoBytes, Immutable, KnownLayout, PartialEq,
34 )]
35 #[repr(u32)]
36 pub enum AlignedZeroU32 {
37     /// The sole enum
38     #[default]
39     Zero = 0,
40 }
41 
42 /// An unaligned wrapper around [`AlignedZeroU32`]
43 /// TODO: https://github.com/google/zerocopy/issues/2273 - Replace with zerocopy implementation
44 #[repr(C, packed)]
45 #[derive(
46     Clone,
47     Copy,
48     Debug,
49     Default,
50     Eq,
51     FromZeros,
52     IntoBytes,
53     Immutable,
54     KnownLayout,
55     PartialEq,
56     Unaligned,
57 )]
58 pub struct ZeroU32(AlignedZeroU32);
59 
60 /// Possible return values from a [`get_version_info::Request`]
61 #[derive(
62     Clone, Copy, Debug, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq, Unaligned,
63 )]
64 #[repr(C)]
65 // TODO: https://github.com/kupiakos/open-enum/issues/27 - use open_enum when it supports U32<BE>
66 pub struct UpdateStatus(pub U32<BE>);
67 
68 impl UpdateStatus {
69     #![allow(nonstandard_style, dead_code, missing_docs)]
70     pub const Success: Self = UpdateStatus(U32::new(0));
71     pub const BadAddr: Self = UpdateStatus(U32::new(1));
72     pub const EraseFailure: Self = UpdateStatus(U32::new(2));
73     pub const DataError: Self = UpdateStatus(U32::new(3));
74     pub const WriteFailure: Self = UpdateStatus(U32::new(4));
75     pub const VerifyError: Self = UpdateStatus(U32::new(5));
76     pub const GenError: Self = UpdateStatus(U32::new(6));
77     pub const NoMemError: Self = UpdateStatus(U32::new(7));
78     pub const RollbackError: Self = UpdateStatus(U32::new(8));
79     pub const RateLimitError: Self = UpdateStatus(U32::new(9));
80     pub const UnalignedBlockError: Self = UpdateStatus(U32::new(10));
81     pub const TruncatedHeaderError: Self = UpdateStatus(U32::new(11));
82     pub const BoardIdError: Self = UpdateStatus(U32::new(12));
83     pub const BoardFlagsError: Self = UpdateStatus(U32::new(13));
84     pub const DevIdMismatch: Self = UpdateStatus(U32::new(14));
85 }
86 
87 /// A trait used to define the constants required by each type of TPMV request
88 /// All TPMV requests are required to implement this trait
89 pub trait TpmvRequest: IntoBytes + Immutable + Unaligned + Sized {
90     /// The first 4 bytes of every TPM request and response.
91     /// Used to indicate if the request requires a session to be run
92     const TAG: U16<BE>;
93 
94     /// A code that is unique to each request type that the TPM will recognize
95     /// and know how to process
96     const COMMAND_CODE: U16<BE>;
97 
98     /// The ordinal associated with the request type.
99     const ORDINAL: U32<BE>;
100 
101     /// Each request type has an associated response type used to deserialize
102     /// bytes received from the TPM after a request is processed
103     type TpmvResponse: FromBytes + ?Sized + Unaligned;
104 }
105 
106 /// Represents a complete TPM request. Contains both the request header and any
107 /// additional fields required by the request type
108 #[derive(Debug, Eq, IntoBytes, Immutable, PartialEq)]
109 #[repr(C)]
110 pub struct TpmRequestMessage<T: TpmvRequest> {
111     /// The request header
112     header: TpmRequestHeader,
113 
114     /// Contains fields specific to the type of request being sent
115     pub request: T,
116 }
117 
118 impl<T: TpmvRequest> TpmRequestMessage<T> {
119     /// Generates a new `TpmvRequest` of the provided type T
new(request: T) -> Self120     pub fn new(request: T) -> Self {
121         Self {
122             header: TpmRequestHeader {
123                 tag: T::TAG,
124                 length: U32::new(size_of::<Self>() as u32),
125                 ordinal: T::ORDINAL,
126                 command_code: T::COMMAND_CODE,
127             },
128             request,
129         }
130     }
131 }
132 
133 /// Represents the initial bytes of every TPM request.
134 #[derive(Debug, Eq, IntoBytes, Immutable, PartialEq, Unaligned)]
135 #[repr(C)]
136 pub struct TpmRequestHeader {
137     /// The first 4 bytes of every TPM request
138     pub tag: U16<BE>,
139 
140     /// The total number of bytes in the request
141     pub length: U32<BE>,
142 
143     /// The request's ordinal
144     pub ordinal: U32<BE>,
145 
146     /// A code that is unique to each request type that the TPM will recognize
147     /// and know how to process
148     pub command_code: U16<BE>,
149 }
150 
151 /// Represents a complete TPM response. Contains both the response header and any
152 /// additional fields required by the response type
153 #[derive(Debug, Eq, FromBytes, Immutable, KnownLayout, PartialEq)]
154 #[repr(C)]
155 pub struct TpmResponseMessage<T: ?Sized> {
156     /// The response header
157     pub header: TpmResponseHeader,
158 
159     /// Contains fields specific to the type of response being sent
160     pub response: T,
161 }
162 
163 /// Represents the initial bytes of every TPM response.
164 #[derive(Debug, Eq, FromBytes, Immutable, KnownLayout, PartialEq)]
165 #[repr(C)]
166 pub struct TpmResponseHeader {
167     /// The first 4 bytes of every TPM response
168     /// Used to indicate if the request requires a session to be run
169     pub tag: U16<BE>,
170 
171     /// The total number of bytes in the response
172     pub length: U32<BE>,
173 
174     /// Used to indicate if an error occurred while processing the request
175     pub error_code: U32<BE>,
176 
177     /// The same command code present in the initial request
178     pub command_code: U16<BE>,
179 }
180 
181 /// A module containing the requests and responses used to retrieve console
182 /// logs from ths GSC
183 pub mod get_console_log {
184     use super::*;
185     /// Used to retrieve the oldest (2048 - header size) bytes worth of console logs from the device.
186     /// Repeated calls can be used to consume all previously unread console logs.
187     /// Returns an empty string when there are no more logs to read
188     #[derive(Debug, Eq, IntoBytes, Immutable, PartialEq, Unaligned)]
189     #[repr(C)]
190     pub struct Request;
191 
192     impl TpmvRequest for Request {
193         const TAG: U16<BE> = NO_SESSION_TAG;
194         const COMMAND_CODE: U16<BE> = U16::new(0x0043);
195         const ORDINAL: U32<BE> = VENDOR_COMMAND_ORDINAL;
196         type TpmvResponse = Response;
197     }
198 
199     /// Represents a response from a [`Request`]
200     #[derive(Debug, Eq, FromBytes, Immutable, KnownLayout, PartialEq, Unaligned)]
201     #[repr(C)]
202     pub struct Response {
203         /// A dynamically sized slice containing ASCII characters
204         pub data: [u8],
205     }
206 }
207 
208 /// A module containing the requests and responses used to retrieve version
209 /// info from ths GSC
210 pub mod get_version_info {
211     use super::*;
212     /// Requests version info from the device
213     #[derive(Default, IntoBytes, Immutable, Unaligned)]
214     #[repr(C)]
215     pub struct Request {
216         /// The block digest. Always equal to zero when getting version info
217         pub digest: ZeroU32,
218         /// The destination address. Always equal to zero when getting version info
219         pub address: ZeroU32,
220     }
221 
222     impl TpmvRequest for Request {
223         const TAG: U16<BE> = NO_SESSION_TAG;
224         const COMMAND_CODE: U16<BE> = U16::new(0x0004);
225         const ORDINAL: U32<BE> = EXTENSION_COMMAND_ORDINAL;
226         type TpmvResponse = Response;
227     }
228 
229     /// Represents a response from a [`Request`]
230     /// TODO: b/394187914 - Add support for older protocols
231     #[derive(Debug, FromBytes, Immutable, KnownLayout, PartialEq, Unaligned)]
232     #[repr(C)]
233     pub struct Response {
234         /// Indicates what error (if any) occurred while fetching version info
235         pub return_value: UpdateStatus,
236         /// The update protocol currently being used by the GSC. Mostly used to
237         /// indicate which fields will be present in the response
238         pub protocol_version: U32<BE>,
239         /// The offset at which the RO section of the GSC image begins
240         pub backup_ro_offset: U32<BE>,
241         /// The offset at which the RW section of the GSC image begins
242         pub backup_rw_offset: U32<BE>,
243         /// The RO minor version
244         pub ro_minor: U32<BE>,
245         /// The RO major version
246         pub ro_major: U32<BE>,
247         /// The RO epoch
248         pub ro_epoch: U32<BE>,
249         /// The RW minor version
250         pub rw_minor: U32<BE>,
251         /// The RW major version
252         pub rw_major: U32<BE>,
253         /// The RW epoch
254         pub rw_epoch: U32<BE>,
255         /// The key id of the currently active RO section
256         pub ro_keyid: U32<BE>,
257         /// The key id of the currently active RW section
258         pub rw_keyid: U32<BE>,
259     }
260 }
261 
262 #[cfg(test)]
263 mod tests {
264     use super::get_console_log;
265     use super::get_version_info;
266     use super::*;
267 
268     #[test]
test_get_console_logs_serialize()269     fn test_get_console_logs_serialize() {
270         let request = TpmRequestMessage::new(get_console_log::Request {});
271         let expected_bytes = vec![
272             0x80, 0x01, // header.tag
273             0x00, 0x00, 0x00, 0x0C, // header.length
274             0x20, 0x00, 0x00, 0x00, // header.ordinal
275             0x00, 0x43, // header.subcmd
276         ];
277 
278         assert_eq!(expected_bytes, request.as_bytes());
279     }
280 
281     #[test]
test_get_console_logs_deserialize()282     fn test_get_console_logs_deserialize() {
283         let bytes = vec![
284             0x80, 0x01, // header.tag
285             0x00, 0x00, 0x00, 0x24, // header.length
286             0xDE, 0xAD, 0xBE, 0xEF, // header.error_code
287             0x00, 0x43, // header.subcmd
288             0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x21, // data
289         ];
290 
291         let tpm_response =
292             TpmResponseMessage::<get_console_log::Response>::ref_from_bytes(&bytes).unwrap();
293         assert_eq!(
294             String::from("Hello!"),
295             String::from_utf8(Vec::from(&tpm_response.response.data)).unwrap(),
296         );
297     }
298 
299     #[test]
test_version_info_serialize()300     fn test_version_info_serialize() {
301         let request = TpmRequestMessage::<get_version_info::Request>::new(Default::default());
302         let expected_bytes = vec![
303             0x80, 0x01, // header.tag
304             0x00, 0x00, 0x00, 0x14, // header.length
305             0xBA, 0xCC, 0xD0, 0x0A, // header.ordinal
306             0x00, 0x04, // header.subcmd
307             0x00, 0x00, 0x00, 0x00, // response.digest
308             0x00, 0x00, 0x00, 0x00, // response.address
309         ];
310 
311         assert_eq!(expected_bytes, request.as_bytes());
312     }
313 
314     #[test]
test_version_info_deserialize()315     fn test_version_info_deserialize() {
316         let bytes = vec![
317             0x80, 0x01, // header.tag
318             0x00, 0x00, 0x00, 0x3C, // header.length
319             0xDE, 0xAD, 0xBE, 0xEF, // header.error_code
320             0x00, 0x04, // header.subcmd
321             0x00, 0x00, 0x00, 0x01, // response.return_value
322             0x00, 0x00, 0x00, 0x06, // response.protocol_version
323             0x00, 0x00, 0x00, 0xAA, // response.backup_ro_offset
324             0x00, 0x00, 0x00, 0xBB, // response.backup_rw_offset
325             0x00, 0x00, 0x00, 0x11, // response.ro_minor
326             0x00, 0x00, 0x00, 0x22, // response.ro_major
327             0x00, 0x00, 0x00, 0x33, // response.ro_epoch
328             0x00, 0x00, 0x00, 0x44, // response.rw_minor
329             0x00, 0x00, 0x00, 0x55, // response.rw_major
330             0x00, 0x00, 0x00, 0x66, // response.rw_epoch
331             0x00, 0x00, 0x00, 0xCC, // response.ro_keyid
332             0x00, 0x00, 0x00, 0xDD, // response.rw_keyid
333         ];
334 
335         let expected_response = TpmResponseMessage {
336             header: TpmResponseHeader {
337                 tag: U16::new(0x8001),
338                 length: U32::new(0x0000003C),
339                 error_code: U32::new(0xDEADBEEF),
340                 command_code: U16::new(0x0004),
341             },
342 
343             response: get_version_info::Response {
344                 return_value: UpdateStatus::BadAddr,
345                 protocol_version: U32::new(0x00000006),
346                 backup_ro_offset: U32::new(0x000000AA),
347                 backup_rw_offset: U32::new(0x000000BB),
348                 ro_minor: U32::new(0x00000011),
349                 ro_major: U32::new(0x00000022),
350                 ro_epoch: U32::new(0x00000033),
351                 rw_minor: U32::new(0x00000044),
352                 rw_major: U32::new(0x00000055),
353                 rw_epoch: U32::new(0x00000066),
354                 ro_keyid: U32::new(0x000000CC),
355                 rw_keyid: U32::new(0x000000DD),
356             },
357         };
358 
359         let tpm_response =
360             TpmResponseMessage::<get_version_info::Response>::ref_from_bytes(&bytes).unwrap();
361         assert_eq!(*tpm_response, expected_response);
362     }
363 }
364