@@ -304,6 +304,106 @@ impl<'a> Tdvf<'a> {
304304 ) )
305305 }
306306
307+ /// RTMR[0] event log for UEFI disk boot (UKI mode).
308+ ///
309+ /// Differences from direct kernel boot (-kernel mode):
310+ /// - EFI variables have different values (SecureBoot not enforced, empty PK/KEK/db/dbx)
311+ /// but the digest calculation is the same (measure_tdx_efi_variable with data_len=0)
312+ /// - BootOrder contains 2 entries [0x0000, 0x0001] instead of 1
313+ /// Digest = SHA384(variable_data_bytes), not SHA384(UEFI_VARIABLE_DATA struct)
314+ /// - Boot0001 added: UEFI Misc Device (virtio-blk disk)
315+ /// Digest = SHA384(EFI_LOAD_OPTION bytes)
316+ /// - Boot0000 is the same (UiApp from OVMF)
317+ /// - Second separator at the end
318+ ///
319+ /// Verified against TDX hardware CCEL event log.
320+ pub fn rtmr0_log_uefi_disk (
321+ & self ,
322+ machine : & Machine ,
323+ _bootloader_data : & [ u8 ] ,
324+ ) -> Result < ( RtmrLog , Tables ) > {
325+ let td_hob_hash = self . measure_td_hob ( machine. memory_size ) ?;
326+ let cfv_image_hash = hex ! (
327+ "344BC51C980BA621AAA00DA3ED7436F7D6E549197DFE699515DFA2C6583D95E6412AF21C097D473155875FFD561D6790"
328+ ) ;
329+ // Boot0000: UiApp from OVMF (fixed, same in both boot modes).
330+ // Digest = SHA384(EFI_LOAD_OPTION variable data).
331+ let boot000_hash = hex ! (
332+ "23ADA07F5261F12F34A0BD8E46760962D6B4D576A416F1FEA1C64BC656B1D28EACF7047AE6E967C58FD2A98BFA74C298"
333+ ) ;
334+
335+ let tables = machine. build_tables ( ) ?;
336+ let acpi_tables_hash = measure_sha384 ( & tables. tables ) ;
337+ let acpi_rsdp_hash = measure_sha384 ( & tables. rsdp ) ;
338+ let acpi_loader_hash = measure_sha384 ( & tables. loader ) ;
339+
340+ // BootOrder for UEFI disk boot: [0x0000, 0x0001] (2 boot entries).
341+ // Digest = SHA384(variable_data_bytes), NOT SHA384(UEFI_VARIABLE_DATA struct).
342+ let boot_order_data: [ u8 ; 4 ] = [ 0x00 , 0x00 , 0x01 , 0x00 ] ; // LE u16: 0, 1
343+ let boot_order_hash = measure_sha384 ( & boot_order_data) ;
344+
345+ // Boot0001: "UEFI Misc Device" at PciRoot(0x0)/Pci(0x1,0x0) — first virtio-blk.
346+ // This is the EFI_LOAD_OPTION structure for the disk boot entry.
347+ // The device path encodes PciRoot(0x0)/Pci(0x1,0x0)/End.
348+ // Digest = SHA384(EFI_LOAD_OPTION variable data).
349+ //
350+ // EFI_LOAD_OPTION layout:
351+ // Attributes(4): 0x00000001 (LOAD_OPTION_ACTIVE)
352+ // FilePathListLength(2): 0x0016 (22 bytes)
353+ // Description(UTF-16LE+null): "UEFI Misc Device\0"
354+ // FilePathList: PciRoot(0x0)/Pci(0x1,0x0)/End
355+ // OptionalData: VenMedia GUID (virtio device signature)
356+ let boot0001_data: Vec < u8 > = {
357+ let mut d = Vec :: new ( ) ;
358+ // Attributes: LOAD_OPTION_ACTIVE
359+ d. extend_from_slice ( & 0x00000001u32 . to_le_bytes ( ) ) ;
360+ // FilePathListLength
361+ d. extend_from_slice ( & 0x0016u16 . to_le_bytes ( ) ) ;
362+ // Description: "UEFI Misc Device" in UTF-16LE + null terminator
363+ for c in "UEFI Misc Device" . encode_utf16 ( ) {
364+ d. extend_from_slice ( & c. to_le_bytes ( ) ) ;
365+ }
366+ d. extend_from_slice ( & [ 0x00 , 0x00 ] ) ; // null terminator
367+ // FilePathList: PciRoot(0x0)/Pci(0x1,0x0)/End
368+ // ACPI device path: type=0x02, subtype=0x01, length=0x0c, HID=0x0a0341d0(PNP0A03), UID=0
369+ d. extend_from_slice ( & [ 0x02 , 0x01 , 0x0c , 0x00 ] ) ;
370+ d. extend_from_slice ( & 0x0a0341d0u32 . to_le_bytes ( ) ) ; // PNP0A03 (PCI root)
371+ d. extend_from_slice ( & 0x00000000u32 . to_le_bytes ( ) ) ; // UID=0
372+ // PCI device path: type=0x01, subtype=0x01, length=0x06, Function=0, Device=1
373+ d. extend_from_slice ( & [ 0x01 , 0x01 , 0x06 , 0x00 , 0x00 , 0x01 ] ) ;
374+ // End device path: type=0x7f, subtype=0xff, length=0x04
375+ d. extend_from_slice ( & [ 0x7f , 0xff , 0x04 , 0x00 ] ) ;
376+ // OptionalData: VenMedia GUID for virtio device signature
377+ // 4e ac 08 81 11 9f 59 4d 85 0e e2 1a 52 2c 59 b2
378+ d. extend_from_slice ( & [
379+ 0x4e , 0xac , 0x08 , 0x81 , 0x11 , 0x9f , 0x59 , 0x4d , 0x85 , 0x0e , 0xe2 , 0x1a , 0x52 , 0x2c ,
380+ 0x59 , 0xb2 ,
381+ ] ) ;
382+ d
383+ } ;
384+ let boot0001_hash = measure_sha384 ( & boot0001_data) ;
385+
386+ Ok ( (
387+ vec ! [
388+ td_hob_hash,
389+ cfv_image_hash. to_vec( ) ,
390+ measure_tdx_efi_variable( "8BE4DF61-93CA-11D2-AA0D-00E098032B8C" , "SecureBoot" ) ?,
391+ measure_tdx_efi_variable( "8BE4DF61-93CA-11D2-AA0D-00E098032B8C" , "PK" ) ?,
392+ measure_tdx_efi_variable( "8BE4DF61-93CA-11D2-AA0D-00E098032B8C" , "KEK" ) ?,
393+ measure_tdx_efi_variable( "D719B2CB-3D3A-4596-A3BC-DAD00E67656F" , "db" ) ?,
394+ measure_tdx_efi_variable( "D719B2CB-3D3A-4596-A3BC-DAD00E67656F" , "dbx" ) ?,
395+ measure_sha384( & [ 0x00 , 0x00 , 0x00 , 0x00 ] ) , // Separator
396+ acpi_loader_hash,
397+ acpi_rsdp_hash,
398+ acpi_tables_hash,
399+ boot_order_hash,
400+ boot000_hash. to_vec( ) ,
401+ boot0001_hash,
402+ ] ,
403+ tables,
404+ ) )
405+ }
406+
307407 fn measure_td_hob ( & self , memory_size : u64 ) -> Result < Vec < u8 > > {
308408 let mut memory_acceptor = MemoryAcceptor :: new ( 0 , memory_size) ;
309409 let mut td_hob = Vec :: new ( ) ;
0 commit comments