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