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 gdbstubs.gdbstub import GdbStub
12
13
14logger = logging.getLogger("gdbstub")
15
16
17class RegNum():
18    # Matches the enum amd64_regnum in GDB
19    RAX = 0
20    RBX = 1
21    RCX = 2
22    RDX = 3
23    RSI = 4
24    RDI = 5
25    RBP = 6
26    RSP = 7
27    R8  = 8
28    R9  = 9
29    R10 = 10
30    R11 = 11
31    R12 = 12
32    R13 = 13
33    R14 = 14
34    R15 = 15
35    RIP = 16
36    EFLAGS = 17
37    CS = 18
38    SS = 19
39    DS = 20
40    ES = 21
41    FS = 22
42    GS = 23
43    FS_BASE = 24
44    GS_BASE = 25
45    K_GS_BASE = 26
46
47
48class ExceptionVectors():
49    # Matches arch/x86/include/kernel_arch_data.h
50    IV_DIVIDE_ERROR = 0
51    IV_DEBUG = 1
52    IV_NON_MASKABLE_INTERRUPT = 2
53    IV_BREAKPOINT = 3
54    IV_OVERFLOW = 4
55    IV_BOUND_RANGE = 5
56    IV_INVALID_OPCODE = 6
57    IV_DEVICE_NOT_AVAILABLE = 7
58    IV_DOUBLE_FAULT = 8
59    IV_COPROC_SEGMENT_OVERRUN = 9
60    IV_INVALID_TSS = 10
61    IV_SEGMENT_NOT_PRESENT = 11
62    IV_STACK_FAULT = 12
63    IV_GENERAL_PROTECTION = 13
64    IV_PAGE_FAULT = 14
65    IV_RESERVED = 15
66    IV_X87_FPU_FP_ERROR = 16
67    IV_ALIGNMENT_CHECK = 17
68    IV_MACHINE_CHECK = 18
69    IV_SIMD_FP = 19
70    IV_VIRT_EXCEPTION = 20
71    IV_SECURITY_EXCEPTION = 30
72
73
74class GdbStub_x86_64(GdbStub):
75    GDB_SIGNAL_DEFAULT = 7
76
77    # Mapping is from GDB's gdb/i386-stubs.c
78    GDB_SIGNAL_MAPPING = {
79        ExceptionVectors.IV_DIVIDE_ERROR: 8,
80        ExceptionVectors.IV_DEBUG: 5,
81        ExceptionVectors.IV_BREAKPOINT: 5,
82        ExceptionVectors.IV_OVERFLOW: 16,
83        ExceptionVectors.IV_BOUND_RANGE: 16,
84        ExceptionVectors.IV_INVALID_OPCODE: 4,
85        ExceptionVectors.IV_DEVICE_NOT_AVAILABLE: 8,
86        ExceptionVectors.IV_DOUBLE_FAULT: 7,
87        ExceptionVectors.IV_COPROC_SEGMENT_OVERRUN: 11,
88        ExceptionVectors.IV_INVALID_TSS: 11,
89        ExceptionVectors.IV_SEGMENT_NOT_PRESENT: 11,
90        ExceptionVectors.IV_STACK_FAULT: 11,
91        ExceptionVectors.IV_GENERAL_PROTECTION: 11,
92        ExceptionVectors.IV_PAGE_FAULT: 11,
93        ExceptionVectors.IV_X87_FPU_FP_ERROR: 7,
94    }
95
96    GDB_G_PKT_NUM_REGS = 34
97
98    GDB_32BIT_REGS = {
99        RegNum.EFLAGS,
100        RegNum.CS,
101        RegNum.SS,
102        RegNum.DS,
103        RegNum.ES,
104        RegNum.FS,
105        RegNum.GS,
106    }
107
108    def __init__(self, logfile, elffile):
109        super().__init__(logfile=logfile, elffile=elffile)
110        self.registers = None
111        self.exception_vector = None
112        self.exception_code = None
113        self.gdb_signal = self.GDB_SIGNAL_DEFAULT
114
115        self.parse_arch_data_block()
116        self.compute_signal()
117
118    def parse_arch_data_block(self):
119        arch_data_blk = self.logfile.get_arch_data()['data']
120
121        arch_data_blk_struct = "<QQQQQQQQQQQQQQQQQQQQQQ"
122        cfg_exception_debug = True
123        if len(arch_data_blk) != struct.calcsize(arch_data_blk_struct):
124            # There are fewer registers dumped
125            # when CONFIG_EXCEPTION_DEBUG=n
126            arch_data_blk_struct = "<QQQQQQQQQQQQQQQQQ"
127            cfg_exception_debug = False
128
129        tu = struct.unpack(arch_data_blk_struct, arch_data_blk)
130
131        self.registers = dict()
132
133        self.exception_vector = tu[0]
134        self.exception_code = tu[1]
135
136        self.registers[RegNum.RAX] = tu[2]
137        self.registers[RegNum.RCX] = tu[3]
138        self.registers[RegNum.RDX] = tu[4]
139        self.registers[RegNum.RSI] = tu[5]
140        self.registers[RegNum.RDI] = tu[6]
141        self.registers[RegNum.RSP] = tu[7]
142        self.registers[RegNum.R8 ] = tu[8]
143        self.registers[RegNum.R9 ] = tu[9]
144        self.registers[RegNum.R10] = tu[10]
145        self.registers[RegNum.R11] = tu[11]
146        self.registers[RegNum.RIP] = tu[12]
147        self.registers[RegNum.EFLAGS] = tu[13]
148        self.registers[RegNum.CS] = tu[14]
149        self.registers[RegNum.SS] = tu[15]
150        self.registers[RegNum.RBP] = tu[16]
151
152        if cfg_exception_debug:
153            self.registers[RegNum.RBX] = tu[17]
154            self.registers[RegNum.R12] = tu[18]
155            self.registers[RegNum.R13] = tu[19]
156            self.registers[RegNum.R14] = tu[20]
157            self.registers[RegNum.R15] = tu[21]
158
159    def compute_signal(self):
160        sig = self.GDB_SIGNAL_DEFAULT
161        vector = self.exception_vector
162
163        if vector is None:
164            sig = self.GDB_SIGNAL_DEFAULT
165
166        # Map vector number to GDB signal number
167        if vector in self.GDB_SIGNAL_MAPPING:
168            sig = self.GDB_SIGNAL_MAPPING[vector]
169
170        self.gdb_signal = sig
171
172    def handle_register_group_read_packet(self):
173        idx = 0
174        pkt = b''
175
176        while idx < self.GDB_G_PKT_NUM_REGS:
177            if idx in self.GDB_32BIT_REGS:
178                reg_fmt = "<I"
179                reg_bytes = 4
180            else:
181                reg_fmt = "<Q"
182                reg_bytes = 8
183
184            if idx in self.registers:
185                bval = struct.pack(reg_fmt, self.registers[idx])
186                pkt += binascii.hexlify(bval)
187            else:
188                # Register not in coredump -> unknown value
189                # Send in "xxxxxxxx"
190                pkt += b'x' * (reg_bytes * 2)
191
192            idx += 1
193
194        self.put_gdb_packet(pkt)
195
196    def handle_register_single_read_packet(self, pkt):
197        # Mark registers as "<unavailable>".
198        # 'p' packets are usually used for registers
199        # other than the general ones (e.g. eax, ebx)
200        # so we can safely reply "xxxxxxxx" here.
201        self.put_gdb_packet(b'x' * 16)
202