1import os 2import re 3from glob import glob 4from uuid import UUID 5 6 7SEC_DRAM_BASE = 0xe200000 8TRUSTY_PROJECT_FOLDER = os.path.dirname(os.path.realpath(__file__)) 9TRUSTY_PROJECT = ( 10 # pylint: disable=line-too-long 11 re.match(r"^.*/build-([^/]+)$", TRUSTY_PROJECT_FOLDER).group(1) #type: ignore 12) 13KERNEL_ELF_FILE = f"{TRUSTY_PROJECT_FOLDER}/lk.elf" 14ADDR_SIZE_BITS = 64 15ARCH_ID = "aarch64" 16 17 18def kernel_entry_point(debugger): 19 """Finds the starting virtual address of the kernel. 20 21 Assumes the kernel is the first target loaded into the debugger. 22 """ 23 target = debugger.GetTargetAtIndex(0) 24 module = target.GetModuleAtIndex(0) 25 section = module.GetSectionAtIndex(0) 26 return section.GetFileAddress() 27 28 29def lldb_module_offset(original_entry_point, new_entry_point): 30 """Calculate an LLDB module load offset. 31 32 Because LLDB only support sliding ELF entry points, not setting them, this 33 function calculates the appropriate offset given the original entry point 34 and the desired entry point. 35 """ 36 assert original_entry_point > 0 37 assert original_entry_point < (1 << ADDR_SIZE_BITS) 38 39 assert new_entry_point > 0 40 assert new_entry_point < (1 << ADDR_SIZE_BITS) 41 42 if original_entry_point == new_entry_point: 43 return 0 44 45 return ( 46 (new_entry_point - original_entry_point) & 47 ((1 << ADDR_SIZE_BITS) - 1) 48 ) 49 50 51def move_kernel_lldb_module(debugger, new_entry_point): 52 original_entry_point = kernel_entry_point(debugger) 53 offset = lldb_module_offset(original_entry_point, new_entry_point) 54 lk_elf = os.path.basename(KERNEL_ELF_FILE) 55 debugger.HandleCommand( 56 f"target modules load -f {lk_elf} -s {hex(offset)}" 57 ) 58 59 60def parse_uuid_bytes(uuid_bytes): 61 """Parse a UUID from a manifest file header's bytes.""" 62 # pylint: disable=line-too-long 63 return UUID( 64 f"{uuid_bytes[3]:02x}{uuid_bytes[2]:02x}{uuid_bytes[1]:02x}{uuid_bytes[0]:02x}-" 65 f"{uuid_bytes[5]:02x}{uuid_bytes[4]:02x}-" 66 f"{uuid_bytes[7]:02x}{uuid_bytes[6]:02x}-" 67 f"{uuid_bytes[8]:02x}{uuid_bytes[9]:02x}-" 68 f"{uuid_bytes[10]:02x}{uuid_bytes[11]:02x}{uuid_bytes[12]:02x}{uuid_bytes[13]:02x}{uuid_bytes[14]:02x}{uuid_bytes[15]:02x}" 69 ) 70 71 72def parse_uuid_struct(uuid_struct): 73 """Parse a UUID from a Trusty UUID struct pulled from LLDB.""" 74 time_low = uuid_struct.GetChildMemberWithName( 75 "time_low").GetValueAsUnsigned() 76 time_mid = uuid_struct.GetChildMemberWithName( 77 "time_mid").GetValueAsUnsigned() 78 time_hi_and_version = uuid_struct.GetChildMemberWithName( 79 "time_hi_and_version").GetValueAsUnsigned() 80 clock_seq_and_node = [ 81 uuid_struct.GetChildMemberWithName( 82 "clock_seq_and_node").GetChildAtIndex(i).GetValueAsUnsigned() 83 for i in range(8) 84 ] 85 86 return UUID( 87 f"{time_low:08x}-" 88 f"{time_mid:04x}-" 89 f"{time_hi_and_version:04x}-" 90 f"{''.join([f'{byte:02x}' for byte in clock_seq_and_node])}" 91 ) 92 93 94# After initialization, this will be a dict mapping TA UUIDs to symbol file 95# paths. 96uuid_symbol_map = None 97 98 99def init_symbols_file_map(): 100 global uuid_symbol_map # pylint: disable=global-statement 101 102 uuid_symbol_map = {} 103 104 for manifest_path in glob( 105 f"{TRUSTY_PROJECT_FOLDER}/user_tasks/**/*.manifest", 106 recursive=True): 107 symbol_path = manifest_path.removesuffix(".manifest") + ".syms.elf" 108 109 if not os.path.exists(symbol_path): 110 continue 111 112 with open(manifest_path, "rb") as manifest_file: 113 uuid_bytes = manifest_file.read(16) 114 115 uuid = parse_uuid_bytes(uuid_bytes) 116 uuid_symbol_map[uuid] = symbol_path 117 118 119def trusty_thread_start_hook(debugger, _command, context, result, 120 _internal_dict): 121 """Breakpoint command for extracting TA load_biases and loading 122 corresponding ELF files with those load biases. 123 """ 124 trusty_app = context.GetFrame().FindVariable( 125 "trusty_thread").GetChildMemberWithName("app") 126 load_bias = trusty_app.GetChildMemberWithName( 127 "load_bias").GetValueAsUnsigned() 128 uuid_struct = trusty_app.GetChildMemberWithName( 129 "props").GetChildMemberWithName("uuid") 130 131 uuid = parse_uuid_struct(uuid_struct) 132 133 symbols_file_path = uuid_symbol_map.get(uuid) 134 if symbols_file_path is None: 135 print(f"Could not find symbols file for UUID {uuid}", file=result) 136 return 137 138 debugger.HandleCommand(f"target modules add {symbols_file_path}") 139 symbols_file_name = os.path.basename(symbols_file_path) 140 print(f"Setting {symbols_file_name} entry point to {hex(load_bias)}") 141 debugger.HandleCommand( 142 f"target modules load -f {symbols_file_name} -s {hex(load_bias)}" 143 ) 144 debugger.HandleCommand("continue") 145 146 147def initialize_kernel_lldb_module(debugger, _command, _context, result, 148 _internal_dict): 149 print("Loading kernel ELF file", file=result) 150 debugger.HandleCommand(f"file -a {ARCH_ID} {KERNEL_ELF_FILE}") 151 152 print( 153 f"Setting kernel ELF entry point to {hex(SEC_DRAM_BASE)}", 154 file=result 155 ) 156 move_kernel_lldb_module(debugger, new_entry_point=SEC_DRAM_BASE) 157 158 159def relocate_kernel_hook(debugger, _command, context, result, _internal_dict): 160 new_base = context.GetFrame().FindVariable("new_base").GetValueAsUnsigned() 161 162 print(f"Setting kernel ELF entry point to {hex(new_base)}", file=result) 163 move_kernel_lldb_module(debugger, new_entry_point=new_base) 164 debugger.HandleCommand("continue") 165 166 167def register_command(debugger, function): 168 fname = function.__name__ 169 debugger.HandleCommand(f"command script add -f {__name__}.{fname} {fname}") 170 171 172def __lldb_init_module(debugger, _internal_dict): 173 init_symbols_file_map() 174 175 register_command(debugger, initialize_kernel_lldb_module) 176 register_command(debugger, relocate_kernel_hook) 177 register_command(debugger, trusty_thread_start_hook) 178