@ -13,12 +13,67 @@ import typing
import math
import math
import struct
import struct
from dataclasses import dataclass
from dataclasses import dataclass
from functools import reduce
from pathlib import Path
from pathlib import Path
from tlc . te import TransferEntry
from tlc . te import TransferEntry
TRANSFER_LIST_ENABLE_CHECKSUM = 0b1
TRANSFER_LIST_ENABLE_CHECKSUM = 0b1
# Description of each TE type. For each TE, there is a tag ID, a format (to be
# used in struct.pack to encode the TE), and a list of field names that can
# appear in the yaml file for that TE. Some fields are missing, if that TE has
# to be processed differently, or if it can only be added with a blob file.
transfer_entry_formats = {
0 : {
" tag_name " : " empty " ,
" format " : " 4x " ,
" fields " : [ ] ,
} ,
1 : {
" tag_name " : " fdt " ,
} ,
2 : {
" tag_name " : " hob_block " ,
} ,
3 : {
" tag_name " : " hob_list " ,
} ,
4 : {
" tag_name " : " acpi_table_aggregate " ,
} ,
5 : {
" tag_name " : " tpm_event_log_table " ,
" fields " : [ " event_log " , " flags " ] ,
} ,
6 : {
" tag_name " : " tpm_crb_base_address_table " ,
" format " : " QI " ,
" fields " : [ " crb_base_address " , " crb_size " ] ,
} ,
0x100 : {
" tag_name " : " optee_pageable_part " ,
" format " : " Q " ,
" fields " : [ " pp_addr " ] ,
} ,
0x101 : {
" tag_name " : " dt_spmc_manifest " ,
} ,
0x102 : {
" tag_name " : " exec_ep_info " ,
" format " : " 2BHIQI4x8Q " ,
" fields " : [ " ep_info " ] ,
} ,
0x104 : {
" tag_name " : " sram_layout " ,
" format " : " 2Q " ,
" fields " : [ " addr " , " size " ] ,
} ,
}
tag_name_to_tag_id = {
te [ " tag_name " ] : tag_id for tag_id , te in transfer_entry_formats . items ( )
}
class TransferList :
class TransferList :
""" Class representing a Transfer List based on version 1.0 of the Firmware Handoff specification. """
""" Class representing a Transfer List based on version 1.0 of the Firmware Handoff specification. """
@ -96,6 +151,28 @@ class TransferList:
return tl
return tl
@classmethod
def from_dict ( cls , config : dict ) :
""" Create a TL from data in a dictionary
The dictionary should have the same format as the yaml config files .
See the readme for more detail .
: param config : Dictionary containing the data described above .
"""
# get settings from config and set defaults
max_size = config . get ( " max_size " , 0x1000 )
has_checksum = config . get ( " has_checksum " , True )
flags = TRANSFER_LIST_ENABLE_CHECKSUM if has_checksum else 0
tl = cls ( max_size , flags )
for entry in config [ " entries " ] :
tl . add_transfer_entry_from_dict ( entry )
return tl
def header_to_bytes ( self ) - > bytes :
def header_to_bytes ( self ) - > bytes :
return struct . pack (
return struct . pack (
self . encoding ,
self . encoding ,
@ -141,6 +218,106 @@ class TransferList:
self . update_checksum ( )
self . update_checksum ( )
return te
return te
def add_transfer_entry_from_struct_format (
self , tag_id : int , struct_format : str , * args
) :
struct_format = " < " + struct_format
data = struct . pack ( struct_format , * args )
return self . add_transfer_entry ( tag_id , data )
def add_entry_point_info_transfer_entry ( self , entry : dict ) - > " TransferEntry " :
""" Add entry_point_info transfer entry
: param entry : Dictionary of the transfer entry , in the same format as
the YAML file .
"""
ep_info = entry [ " ep_info " ]
header = ep_info [ " h " ]
# size of the entry_point_info struct
entry_point_size = 88
attr = header [ " attr " ]
if type ( attr ) is str :
# convert string of flags names to an integer
# bit number | 0 | 1 |
# ------------|-----------------------|----------------------|
# 0 | secure | non-secure |
# 1 | little endian | big-endian |
# 2 | disable secure timer | enable secure timer |
# 3 | executable | non-executable |
# 4 | first exe | not first exe |
#
# Bit 5 and bit 0 are used to determine the security state.
flag_names = {
" EP_SECURE " : 0x0 ,
" EP_NON_SECURE " : 0x1 ,
" EP_REALM " : 0x21 ,
" EP_EE_LITTLE " : 0x0 ,
" EP_EE_BIG " : 0x2 ,
" EP_ST_DISABLE " : 0x0 ,
" EP_ST_ENABLE " : 0x4 ,
" EP_NON_EXECUTABLE " : 0x0 ,
" EP_EXECUTABLE " : 0x8 ,
" EP_FIRST_EXE " : 0x10 ,
}
# create list of integer flags, then bitwise-or them together
flags = [ flag_names [ f . strip ( ) ] for f in attr . split ( " | " ) ]
attr = reduce ( lambda x , y : x | y , flags )
return self . add_transfer_entry_from_struct_format (
0x102 ,
transfer_entry_formats [ 0x102 ] [ " format " ] ,
header [ " type " ] ,
header [ " version " ] ,
entry_point_size ,
attr ,
ep_info [ " pc " ] ,
ep_info [ " spsr " ] ,
* ep_info [ " args " ] ,
)
def add_transfer_entry_from_dict (
self ,
entry : dict ,
) - > " TransferEntry " :
""" Add a transfer entry from data in a dictionary
The dictionary should have the same format as the entries in the yaml
config files . See the readme for more detail .
: param entry : Dictionary containing the data described above .
"""
# Tag_id is either a tag name or a tag id. Use it to get the TE format.
tag_id = entry [ " tag_id " ]
if tag_id in tag_name_to_tag_id :
tag_id = tag_name_to_tag_id [ tag_id ]
te_format = transfer_entry_formats [ tag_id ]
tag_name = te_format [ " tag_name " ]
if " blob_file_path " in entry :
return self . add_transfer_entry_from_file ( tag_id , entry [ " blob_file_path " ] )
elif tag_name == " tpm_event_log_table " :
with open ( entry [ " event_log " ] , " rb " ) as f :
event_log_data = f . read ( )
flags_bytes = entry [ " flags " ] . to_bytes ( 4 , " little " )
data = flags_bytes + event_log_data
return self . add_transfer_entry ( tag_id , data )
elif tag_name == " exec_ep_info " :
return self . add_entry_point_info_transfer_entry ( entry )
elif " format " in te_format and " fields " in te_format :
fields = [ entry [ field ] for field in te_format [ " fields " ] ]
return self . add_transfer_entry_from_struct_format (
tag_id , te_format [ " format " ] , * fields
)
else :
raise ValueError ( f " Invalid transfer entry { entry } . " )
def add_transfer_entry_from_file ( self , tag_id : int , path : Path ) - > " TransferEntry " :
def add_transfer_entry_from_file ( self , tag_id : int , path : Path ) - > " TransferEntry " :
with open ( path , " rb " ) as f :
with open ( path , " rb " ) as f :
return self . add_transfer_entry ( tag_id , f . read ( ) )
return self . add_transfer_entry ( tag_id , f . read ( ) )