1#!/usr/bin/env python3
2#
3# Copyright (c) 2020 Intel Corporation
4#
5# SPDX-License-Identifier: Apache-2.0
6
7import binascii
8import logging
9import struct
10
11from coredump_parser.elf_parser import ThreadInfoOffset
12from gdbstubs.gdbstub import GdbStub
13
14
15logger = logging.getLogger("gdbstub")
16
17
18class RegNum():
19    R0 = 0
20    R1 = 1
21    R2 = 2
22    R3 = 3
23    R4 = 4
24    R5 = 5
25    R6 = 6
26    R7 = 7
27    R8 = 8
28    R9 = 9
29    R10 = 10
30    R11 = 11
31    R12 = 12
32    SP = 13
33    LR = 14
34    PC = 15
35    XPSR = 16
36
37
38class GdbStub_ARM_CortexM(GdbStub):
39    ARCH_DATA_BLK_STRUCT    = "<IIIIIIIII"
40    ARCH_DATA_BLK_STRUCT_V2 = "<IIIIIIIIIIIIIIIII"
41
42    GDB_SIGNAL_DEFAULT = 7
43
44    GDB_G_PKT_NUM_REGS = 17
45
46    def __init__(self, logfile, elffile):
47        super().__init__(logfile=logfile, elffile=elffile)
48        self.registers = None
49        self.gdb_signal = self.GDB_SIGNAL_DEFAULT
50
51        self.parse_arch_data_block()
52
53    def parse_arch_data_block(self):
54        arch_data_blk = self.logfile.get_arch_data()['data']
55        arch_data_ver = self.logfile.get_arch_data()['hdr_ver']
56
57        if arch_data_ver == 1:
58            tu = struct.unpack(self.ARCH_DATA_BLK_STRUCT, arch_data_blk)
59        elif arch_data_ver == 2:
60            tu = struct.unpack(self.ARCH_DATA_BLK_STRUCT_V2, arch_data_blk)
61
62        self.registers = dict()
63
64        self.registers[RegNum.R0] = tu[0]
65        self.registers[RegNum.R1] = tu[1]
66        self.registers[RegNum.R2] = tu[2]
67        self.registers[RegNum.R3] = tu[3]
68        self.registers[RegNum.R12] = tu[4]
69        self.registers[RegNum.LR] = tu[5]
70        self.registers[RegNum.PC] = tu[6]
71        self.registers[RegNum.XPSR] = tu[7]
72        self.registers[RegNum.SP] = tu[8]
73
74        if arch_data_ver > 1:
75            self.registers[RegNum.R4]  = tu[9]
76            self.registers[RegNum.R5]  = tu[10]
77            self.registers[RegNum.R6]  = tu[11]
78            self.registers[RegNum.R7]  = tu[12]
79            self.registers[RegNum.R8]  = tu[13]
80            self.registers[RegNum.R9]  = tu[14]
81            self.registers[RegNum.R10] = tu[15]
82            self.registers[RegNum.R11] = tu[16]
83
84    def send_registers_packet(self, registers):
85        reg_fmt = "<I"
86
87        idx = 0
88        pkt = b''
89
90        while idx < self.GDB_G_PKT_NUM_REGS:
91            if idx in registers:
92                bval = struct.pack(reg_fmt, registers[idx])
93                pkt += binascii.hexlify(bval)
94            else:
95                # Register not in coredump -> unknown value
96                # Send in "xxxxxxxx"
97                pkt += b'x' * 8
98
99            idx += 1
100
101        self.put_gdb_packet(pkt)
102
103    def handle_register_group_read_packet(self):
104        if not self.elffile.has_kernel_thread_info():
105            self.send_registers_packet(self.registers)
106        else:
107            self.handle_thread_register_group_read_packet()
108
109    def handle_register_single_read_packet(self, pkt):
110        # Mark registers as "<unavailable>".
111        # 'p' packets are usually used for registers
112        # other than the general ones (e.g. eax, ebx)
113        # so we can safely reply "xxxxxxxx" here.
114        self.put_gdb_packet(b'x' * 8)
115
116    def handle_register_single_write_packet(self, pkt):
117        pkt_str = pkt.decode("ascii")
118        reg = int(pkt_str[1:pkt_str.index('=')], 16)
119        self.registers[reg] = int.from_bytes(binascii.unhexlify(pkt[3:]), byteorder = 'little')
120        self.put_gdb_packet(b'+')
121
122    def arch_supports_thread_operations(self):
123        return True
124
125    def handle_thread_register_group_read_packet(self):
126        # For selected_thread 0, use the register data retrieved from the dump's arch section
127        if self.selected_thread == 0:
128            self.send_registers_packet(self.registers)
129        else:
130            thread_ptr = self.thread_ptrs[self.selected_thread]
131
132            # Get stack pointer out of thread struct
133            t_stack_ptr_offset = self.elffile.get_kernel_thread_info_offset(ThreadInfoOffset.THREAD_INFO_OFFSET_T_STACK_PTR)
134            size_t_size = self.elffile.get_kernel_thread_info_size_t_size()
135            stack_ptr_bytes = self.get_memory(thread_ptr + t_stack_ptr_offset, size_t_size)
136
137            thread_registers = dict()
138
139            if stack_ptr_bytes is not None:
140                # Read registers stored at top of stack
141                stack_ptr = int.from_bytes(stack_ptr_bytes, "little")
142                barray = self.get_memory(stack_ptr, (size_t_size * 8))
143
144                if barray is not None:
145                    tu = struct.unpack("<IIIIIIII", barray)
146                    thread_registers[RegNum.R0] = tu[0]
147                    thread_registers[RegNum.R1] = tu[1]
148                    thread_registers[RegNum.R2] = tu[2]
149                    thread_registers[RegNum.R3] = tu[3]
150                    thread_registers[RegNum.R12] = tu[4]
151                    thread_registers[RegNum.LR] = tu[5]
152                    thread_registers[RegNum.PC] = tu[6]
153                    thread_registers[RegNum.XPSR] = tu[7]
154
155                    # Set SP to point to stack just after these registers
156                    thread_registers[RegNum.SP] = stack_ptr + 32
157
158                    # Read the exc_return value from the thread's arch struct
159                    t_arch_offset = self.elffile.get_kernel_thread_info_offset(ThreadInfoOffset.THREAD_INFO_OFFSET_T_ARCH)
160                    t_exc_return_offset = self.elffile.get_kernel_thread_info_offset(ThreadInfoOffset.THREAD_INFO_OFFSET_T_ARM_EXC_RETURN)
161
162                    # Value of 0xffffffff indicates THREAD_INFO_UNIMPLEMENTED
163                    if t_exc_return_offset != 0xffffffff:
164                        exc_return_bytes = self.get_memory(thread_ptr + t_arch_offset + t_exc_return_offset, 1)
165                        exc_return = int.from_bytes(exc_return_bytes, "little")
166
167                        # If the bit 4 is not set, the stack frame is extended for floating point data, adjust the SP accordingly
168                        if (exc_return & (1 << 4)) == 0:
169                            thread_registers[RegNum.SP] = thread_registers[RegNum.SP] + 72
170
171                    # Set R7 to match the stack pointer in case the frame pointer is not omitted
172                    thread_registers[RegNum.R7] = thread_registers[RegNum.SP]
173
174            self.send_registers_packet(thread_registers)
175