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 i386_regnum in GDB
19    EAX = 0
20    ECX = 1
21    EDX = 2
22    EBX = 3
23    ESP = 4
24    EBP = 5
25    ESI = 6
26    EDI = 7
27    EIP = 8
28    EFLAGS = 9
29    CS = 10
30    SS = 11
31    DS = 12
32    ES = 13
33    FS = 14
34    GS = 15
35
36
37class ExceptionVectors():
38    # Matches arch/x86/include/kernel_arch_data.h
39    IV_DIVIDE_ERROR = 0
40    IV_DEBUG = 1
41    IV_NON_MASKABLE_INTERRUPT = 2
42    IV_BREAKPOINT = 3
43    IV_OVERFLOW = 4
44    IV_BOUND_RANGE = 5
45    IV_INVALID_OPCODE = 6
46    IV_DEVICE_NOT_AVAILABLE = 7
47    IV_DOUBLE_FAULT = 8
48    IV_COPROC_SEGMENT_OVERRUN = 9
49    IV_INVALID_TSS = 10
50    IV_SEGMENT_NOT_PRESENT = 11
51    IV_STACK_FAULT = 12
52    IV_GENERAL_PROTECTION = 13
53    IV_PAGE_FAULT = 14
54    IV_RESERVED = 15
55    IV_X87_FPU_FP_ERROR = 16
56    IV_ALIGNMENT_CHECK = 17
57    IV_MACHINE_CHECK = 18
58    IV_SIMD_FP = 19
59    IV_VIRT_EXCEPTION = 20
60    IV_SECURITY_EXCEPTION = 30
61
62
63class GdbStub_x86(GdbStub):
64    ARCH_DATA_BLK_STRUCT = "<IIIIIIIIIIIII"
65
66    GDB_SIGNAL_DEFAULT = 7
67
68    # Mapping is from GDB's gdb/i386-stubs.c
69    GDB_SIGNAL_MAPPING = {
70        ExceptionVectors.IV_DIVIDE_ERROR: 8,
71        ExceptionVectors.IV_DEBUG: 5,
72        ExceptionVectors.IV_BREAKPOINT: 5,
73        ExceptionVectors.IV_OVERFLOW: 16,
74        ExceptionVectors.IV_BOUND_RANGE: 16,
75        ExceptionVectors.IV_INVALID_OPCODE: 4,
76        ExceptionVectors.IV_DEVICE_NOT_AVAILABLE: 8,
77        ExceptionVectors.IV_DOUBLE_FAULT: 7,
78        ExceptionVectors.IV_COPROC_SEGMENT_OVERRUN: 11,
79        ExceptionVectors.IV_INVALID_TSS: 11,
80        ExceptionVectors.IV_SEGMENT_NOT_PRESENT: 11,
81        ExceptionVectors.IV_STACK_FAULT: 11,
82        ExceptionVectors.IV_GENERAL_PROTECTION: 11,
83        ExceptionVectors.IV_PAGE_FAULT: 11,
84        ExceptionVectors.IV_X87_FPU_FP_ERROR: 7,
85    }
86
87    GDB_G_PKT_NUM_REGS = 16
88
89    def __init__(self, logfile, elffile):
90        super().__init__(logfile=logfile, elffile=elffile)
91        self.registers = None
92        self.exception_vector = None
93        self.exception_code = None
94        self.gdb_signal = self.GDB_SIGNAL_DEFAULT
95
96        self.parse_arch_data_block()
97        self.compute_signal()
98
99    def parse_arch_data_block(self):
100        arch_data_blk = self.logfile.get_arch_data()['data']
101        tu = struct.unpack(self.ARCH_DATA_BLK_STRUCT, arch_data_blk)
102
103        self.registers = dict()
104
105        self.exception_vector = tu[0]
106        self.exception_code = tu[1]
107
108        self.registers[RegNum.EAX] = tu[2]
109        self.registers[RegNum.ECX] = tu[3]
110        self.registers[RegNum.EDX] = tu[4]
111        self.registers[RegNum.EBX] = tu[5]
112        self.registers[RegNum.ESP] = tu[6]
113        self.registers[RegNum.EBP] = tu[7]
114        self.registers[RegNum.ESI] = tu[8]
115        self.registers[RegNum.EDI] = tu[9]
116        self.registers[RegNum.EIP] = tu[10]
117        self.registers[RegNum.EFLAGS] = tu[11]
118        self.registers[RegNum.CS] = tu[12]
119
120    def compute_signal(self):
121        sig = self.GDB_SIGNAL_DEFAULT
122        vector = self.exception_vector
123
124        if vector is None:
125            sig = self.GDB_SIGNAL_DEFAULT
126
127        # Map vector number to GDB signal number
128        if vector in self.GDB_SIGNAL_MAPPING:
129            sig = self.GDB_SIGNAL_MAPPING[vector]
130
131        self.gdb_signal = sig
132
133    def handle_register_group_read_packet(self):
134        reg_fmt = "<I"
135
136        idx = 0
137        pkt = b''
138
139        while idx < self.GDB_G_PKT_NUM_REGS:
140            if idx in self.registers:
141                bval = struct.pack(reg_fmt, self.registers[idx])
142                pkt += binascii.hexlify(bval)
143            else:
144                # Register not in coredump -> unknown value
145                # Send in "xxxxxxxx"
146                pkt += b'x' * 8
147
148            idx += 1
149
150        self.put_gdb_packet(pkt)
151
152    def handle_register_single_read_packet(self, pkt):
153        # Mark registers as "<unavailable>".
154        # 'p' packets are usually used for registers
155        # other than the general ones (e.g. eax, ebx)
156        # so we can safely reply "xxxxxxxx" here.
157        self.put_gdb_packet(b'x' * 8)
158