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