11//! EFI TCG2 protocol interface for TPM2 PCR measurements.
22
3- use core:: ffi:: c_void;
4-
5- use anyhow:: { Context as _, Result , bail} ;
6- use uefi:: { Guid , Status } ;
7-
8- use crate :: pe:: loader:: protocol;
9-
10- const EFI_TCG2_PROTOCOL_GUID : Guid = Guid :: parse_or_panic ( "607f766c-7455-42be-930b-e4d76db2720f" ) ;
3+ use anyhow:: { Context as _, Result } ;
4+ use uefi:: Identify as _;
5+ use uefi:: boot:: { self , ScopedProtocol , SearchType } ;
6+ use uefi:: proto:: tcg:: v2:: { HashLogExtendEventFlags , PcrEventInputs , Tcg } ;
7+ use uefi:: proto:: tcg:: { EventType , PcrIndex } ;
118
129const PCR_INDEX : u32 = 11 ;
13- const EV_IPL : u32 = 0x0000_000D ;
14- const TCG2_EVENT_HEADER_SIZE : usize = 28 ;
15-
16- type HashLogExtendEventFn = unsafe extern "efiapi" fn (
17- this : * mut Tcg2Protocol ,
18- flags : u64 ,
19- data_to_hash : u64 ,
20- data_to_hash_len : u64 ,
21- efi_tcg2_event : * const Tcg2Event ,
22- ) -> Status ;
2310
24- #[ repr( C ) ]
25- struct Tcg2Protocol {
26- get_capability : * const c_void ,
27- get_event_log : * const c_void ,
28- hash_log_extend_event : HashLogExtendEventFn ,
29- submit_command : * const c_void ,
30- get_active_pcr_banks : * const c_void ,
31- set_active_pcr_banks : * const c_void ,
32- get_result_of_set_active_pcr_banks : * const c_void ,
33- }
34-
35- #[ repr( C , packed) ]
36- struct Tcg2Event {
37- size : u32 ,
38- header : Tcg2EventHeader ,
39- }
40-
41- #[ repr( C , packed) ]
42- struct Tcg2EventHeader {
43- header_size : u32 ,
44- header_version : u16 ,
45- pcr_index : u32 ,
46- event_type : u32 ,
47- }
11+ /// Prefix of an `EFI_TCG2_EVENT`: `u32` size field + 14-byte `EFI_TCG2_EVENT_HEADER`.
12+ const PCR_EVENT_PREFIX_LEN : usize = 18 ;
4813
4914/// Returns whether the EFI TCG2 protocol is available (TPM2 present).
5015#[ must_use]
5116pub fn is_available ( ) -> bool {
52- // SAFETY: firmware-managed pointer valid during boot services.
53- unsafe { protocol :: locate_raw ( & EFI_TCG2_PROTOCOL_GUID ) . is_some ( ) }
17+ boot:: locate_handle_buffer ( SearchType :: ByProtocol ( & Tcg :: GUID ) )
18+ . is_ok_and ( |handles| !handles . is_empty ( ) )
5419}
5520
5621/// Measures a UKI section into PCR#11 via the EFI TCG2 protocol.
@@ -59,80 +24,38 @@ pub fn is_available() -> bool {
5924///
6025/// Returns an error if the TCG2 protocol is unavailable or measurement fails.
6126pub fn measure_section ( name : & str , data : & [ u8 ] ) -> Result < ( ) > {
27+ let mut tcg = open_tcg ( ) ?;
28+
6229 let mut name_bytes = name. as_bytes ( ) . to_vec ( ) ;
6330 name_bytes. push ( 0_u8 ) ;
64- hash_log_extend ( name, & name_bytes) ?;
65- hash_log_extend ( name, data) ?;
31+ hash_log_extend ( & mut tcg, name, & name_bytes) ?;
32+ hash_log_extend ( & mut tcg, name, data) ?;
33+
6634 Ok ( ( ) )
6735}
6836
69- /// Performs a single `HashLogExtendEvent` call into PCR#11.
70- fn hash_log_extend ( event_name : & str , data : & [ u8 ] ) -> Result < ( ) > {
71- // SAFETY: firmware-managed pointer valid during boot services; layout matches EFI TCG2 ABI.
72- let proto = match unsafe { protocol :: locate_raw ( & EFI_TCG2_PROTOCOL_GUID ) } {
73- Some ( protocol ) => protocol . cast :: < Tcg2Protocol > ( ) ,
74- None => bail ! ( "EFI_TCG2_PROTOCOL not available" ) ,
75- } ;
37+ fn open_tcg ( ) -> Result < ScopedProtocol < Tcg > > {
38+ let handles = boot :: locate_handle_buffer ( SearchType :: ByProtocol ( & Tcg :: GUID ) )
39+ . context ( "Failed to search for EFI_TCG2_PROTOCOL handles" ) ? ;
40+ let handle = handles . first ( ) . context ( "EFI_TCG2_PROTOCOL not available" ) ? ;
41+
42+ boot :: open_protocol_exclusive :: < Tcg > ( * handle ) . context ( "Failed to open EFI_TCG2_PROTOCOL" )
43+ }
7644
45+ fn hash_log_extend ( tcg : & mut Tcg , event_name : & str , data : & [ u8 ] ) -> Result < ( ) > {
7746 let event_desc = event_name. as_bytes ( ) ;
78- let event_total_size = TCG2_EVENT_HEADER_SIZE
47+ let event_size = PCR_EVENT_PREFIX_LEN
7948 . checked_add ( event_desc. len ( ) )
8049 . context ( "TCG2 event size overflow" ) ?;
81- let event_total_size_u32 = u32:: try_from ( event_total_size) . context ( "TCG2 event too large" ) ?;
82- let header_size = u32:: try_from ( size_of :: < Tcg2EventHeader > ( ) )
83- . context ( "TCG2 event header size exceeds u32" ) ?;
84-
85- let mut event_buf = vec ! [ 0_u8 ; event_total_size] ;
86- write_event_u32 ( & mut event_buf, 0 , event_total_size_u32) ?;
87- write_event_u32 ( & mut event_buf, 4 , header_size) ?;
88- write_event_u16 ( & mut event_buf, 8 , 1 ) ?;
89- write_event_u32 ( & mut event_buf, 10 , PCR_INDEX ) ?;
90- write_event_u32 ( & mut event_buf, 14 , EV_IPL ) ?;
91-
92- let event_data_offset = size_of :: < Tcg2Event > ( ) ;
93- let event_data_end = event_data_offset
94- . checked_add ( event_desc. len ( ) )
95- . context ( "TCG2 event data bounds overflow" ) ?;
96- event_buf
97- . get_mut ( event_data_offset..event_data_end)
98- . context ( "TCG2 event data range unavailable" ) ?
99- . copy_from_slice ( event_desc) ;
100-
101- let data_address =
102- u64:: try_from ( data. as_ptr ( ) . addr ( ) ) . context ( "hash data address exceeds u64" ) ?;
103- let data_len = u64:: try_from ( data. len ( ) ) . context ( "hash data length exceeds u64" ) ?;
104- let event = event_buf. as_ptr ( ) . cast :: < Tcg2Event > ( ) ;
105- // SAFETY: `proto` is firmware-provided and valid during boot services.
106- let hash_log_extend_event = unsafe { ( * proto) . hash_log_extend_event } ;
107- // SAFETY: `proto` and its function pointer are valid firmware-provided values; `data` and
108- // `event_buf` outlive the call.
109- let status = unsafe { hash_log_extend_event ( proto, 0 , data_address, data_len, event) } ;
110-
111- if status != Status :: SUCCESS {
112- bail ! ( "HashLogExtendEvent failed for section {event_name}: {status:?}" ) ;
113- }
114-
115- Ok ( ( ) )
116- }
117-
118- fn write_event_u16 ( event_buf : & mut [ u8 ] , offset : usize , value : u16 ) -> Result < ( ) > {
119- let end = offset
120- . checked_add ( size_of :: < u16 > ( ) )
121- . context ( "TCG2 u16 field bounds overflow" ) ?;
122- event_buf
123- . get_mut ( offset..end)
124- . context ( "TCG2 u16 field range unavailable" ) ?
125- . copy_from_slice ( & value. to_le_bytes ( ) ) ;
126- Ok ( ( ) )
127- }
128-
129- fn write_event_u32 ( event_buf : & mut [ u8 ] , offset : usize , value : u32 ) -> Result < ( ) > {
130- let end = offset
131- . checked_add ( size_of :: < u32 > ( ) )
132- . context ( "TCG2 u32 field bounds overflow" ) ?;
133- event_buf
134- . get_mut ( offset..end)
135- . context ( "TCG2 u32 field range unavailable" ) ?
136- . copy_from_slice ( & value. to_le_bytes ( ) ) ;
137- Ok ( ( ) )
50+ let mut event_buf = vec ! [ 0_u8 ; event_size] ;
51+ let event = PcrEventInputs :: new_in_buffer (
52+ & mut event_buf,
53+ PcrIndex ( PCR_INDEX ) ,
54+ EventType :: IPL ,
55+ event_desc,
56+ )
57+ . context ( "Failed to build TCG2 event" ) ?;
58+
59+ tcg. hash_log_extend_event ( HashLogExtendEventFlags :: empty ( ) , data, event)
60+ . with_context ( || format ! ( "HashLogExtendEvent failed for section {event_name}" ) )
13861}
0 commit comments