1#
2# Copyright 2021 Espressif Systems (Shanghai) CO., LTD
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8#     http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15#
16
17from construct import Int16ul, Int32ul, Int64ul, Struct
18
19from . import BaseArchMethodsMixin, BaseTargetMethods, ESPCoreDumpLoaderError
20
21try:
22    from typing import Any, Optional, Tuple
23except ImportError:
24    pass
25
26INVALID_CAUSE_VALUE = 0xFFFF
27XCHAL_EXCCAUSE_NUM = 64
28
29# Exception cause dictionary to get translation of exccause register
30# From 4.4.1.5 table 4-64 Exception Causes of Xtensa
31# Instruction Set Architecture (ISA) Reference Manual
32
33XTENSA_EXCEPTION_CAUSE_DICT = {
34    0: ('IllegalInstructionCause', 'Illegal instruction'),
35    1: ('SyscallCause', 'SYSCALL instruction'),
36    2: ('InstructionFetchErrorCause',
37        'Processor internal physical address or data error during instruction fetch. (See EXCVADDR for more information)'),
38    3: ('LoadStoreErrorCause',
39        'Processor internal physical address or data error during load or store. (See EXCVADDR for more information)'),
40    4: ('Level1InterruptCause', 'Level-1 interrupt as indicated by set level-1 bits in the INTERRUPT register'),
41    5: ('AllocaCause', 'MOVSP instruction, if caller`s registers are not in the register file'),
42    6: ('IntegerDivideByZeroCause', 'QUOS: QUOU, REMS: or REMU divisor operand is zero'),
43    8: ('PrivilegedCause', 'Attempt to execute a privileged operation when CRING ? 0'),
44    9: ('LoadStoreAlignmentCause', 'Load or store to an unaligned address. (See EXCVADDR for more information)'),
45    12: ('InstrPIFDataErrorCause', 'PIF data error during instruction fetch. (See EXCVADDR for more information)'),
46    13: ('LoadStorePIFDataErrorCause',
47         'Synchronous PIF data error during LoadStore access. (See EXCVADDR for more information)'),
48    14: ('InstrPIFAddrErrorCause', 'PIF address error during instruction fetch. (See EXCVADDR for more information)'),
49    15: ('LoadStorePIFAddrErrorCause',
50         'Synchronous PIF address error during LoadStore access. (See EXCVADDR for more information)'),
51    16: ('InstTLBMissCause', 'Error during Instruction TLB refill. (See EXCVADDR for more information)'),
52    17: ('InstTLBMultiHitCause', 'Multiple instruction TLB entries matched. (See EXCVADDR for more information)'),
53    18: ('InstFetchPrivilegeCause',
54         'An instruction fetch referenced a virtual address at a ring level less than CRING. (See EXCVADDR for more information)'),
55    20: ('InstFetchProhibitedCause',
56         'An instruction fetch referenced a page mapped with an attribute that does not permit instruction fetch (EXCVADDR).'),
57    24: ('LoadStoreTLBMissCause', 'Error during TLB refill for a load or store. (See EXCVADDR for more information)'),
58    25: ('LoadStoreTLBMultiHitCause',
59         'Multiple TLB entries matched for a load or store. (See EXCVADDR for more information)'),
60    26: ('LoadStorePrivilegeCause',
61         'A load or store referenced a virtual address at a ring level less than CRING. (See EXCVADDR for more information)'),
62    28: ('LoadProhibitedCause',
63         'A load referenced a page mapped with an attribute that does not permit loads. (See EXCVADDR for more information)'),
64    29: ('StoreProhibitedCause',
65         'A store referenced a page mapped with an attribute that does not permit stores [Region Protection Option or MMU Option].'),
66    32: ('Coprocessor0Disabled', 'Coprocessor 0 instruction when cp0 disabled'),
67    33: ('Coprocessor1Disabled', 'Coprocessor 1 instruction when cp1 disabled'),
68    34: ('Coprocessor2Disabled', 'Coprocessor 2 instruction when cp2 disabled'),
69    35: ('Coprocessor3Disabled', 'Coprocessor 3 instruction when cp3 disabled'),
70    36: ('Coprocessor4Disabled', 'Coprocessor 4 instruction when cp4 disabled'),
71    37: ('Coprocessor5Disabled', 'Coprocessor 5 instruction when cp5 disabled'),
72    38: ('Coprocessor6Disabled', 'Coprocessor 6 instruction when cp6 disabled'),
73    39: ('Coprocessor7Disabled', 'Coprocessor 7 instruction when cp7 disabled'),
74    INVALID_CAUSE_VALUE: (
75        'InvalidCauseRegister', 'Invalid EXCCAUSE register value or current task is broken and was skipped'),
76    # ESP panic pseudo reasons
77    XCHAL_EXCCAUSE_NUM + 0: ('UnknownException', 'Unknown exception'),
78    XCHAL_EXCCAUSE_NUM + 1: ('DebugException', 'Unhandled debug exception'),
79    XCHAL_EXCCAUSE_NUM + 2: ('DoubleException', 'Double exception'),
80    XCHAL_EXCCAUSE_NUM + 3: ('KernelException', 'Unhandled kernel exception'),
81    XCHAL_EXCCAUSE_NUM + 4: ('CoprocessorException', 'Coprocessor exception'),
82    XCHAL_EXCCAUSE_NUM + 5: ('InterruptWDTTimoutCPU0', 'Interrupt wdt timeout on CPU0'),
83    XCHAL_EXCCAUSE_NUM + 6: ('InterruptWDTTimoutCPU1', 'Interrupt wdt timeout on CPU1'),
84    XCHAL_EXCCAUSE_NUM + 7: ('CacheError', 'Cache disabled but cached memory region accessed'),
85}
86
87
88class ExceptionRegisters(object):
89    # extra regs IDs used in EXTRA_INFO note
90    EXCCAUSE_IDX = 0
91    EXCVADDR_IDX = 1
92    EPC1_IDX = 177
93    EPC2_IDX = 178
94    EPC3_IDX = 179
95    EPC4_IDX = 180
96    EPC5_IDX = 181
97    EPC6_IDX = 182
98    EPC7_IDX = 183
99    EPS2_IDX = 194
100    EPS3_IDX = 195
101    EPS4_IDX = 196
102    EPS5_IDX = 197
103    EPS6_IDX = 198
104    EPS7_IDX = 199
105
106    @property
107    def registers(self):  # type: () -> dict[str, int]
108        return {k: v for k, v in self.__class__.__dict__.items()
109                if not k.startswith('__') and isinstance(v, int)}
110
111
112# Following structs are based on source code
113# IDF_PATH/components/espcoredump/src/core_dump_port.c
114PrStatus = Struct(
115    'si_signo' / Int32ul,
116    'si_code' / Int32ul,
117    'si_errno' / Int32ul,
118    'pr_cursig' / Int16ul,
119    'pr_pad0' / Int16ul,
120    'pr_sigpend' / Int32ul,
121    'pr_sighold' / Int32ul,
122    'pr_pid' / Int32ul,
123    'pr_ppid' / Int32ul,
124    'pr_pgrp' / Int32ul,
125    'pr_sid' / Int32ul,
126    'pr_utime' / Int64ul,
127    'pr_stime' / Int64ul,
128    'pr_cutime' / Int64ul,
129    'pr_cstime' / Int64ul,
130)
131
132
133def print_exc_regs_info(extra_info):  # type: (list[int]) -> None
134    """
135    Print the register info by parsing extra_info
136    :param extra_info: extra info data str
137    :return: None
138    """
139    exccause = extra_info[1 + 2 * ExceptionRegisters.EXCCAUSE_IDX + 1]
140    exccause_str = XTENSA_EXCEPTION_CAUSE_DICT.get(exccause)
141    if not exccause_str:
142        exccause_str = ('Invalid EXCCAUSE code', 'Invalid EXCAUSE description or not found.')
143    print('exccause       0x%x (%s)' % (exccause, exccause_str[0]))
144    print('excvaddr       0x%x' % extra_info[1 + 2 * ExceptionRegisters.EXCVADDR_IDX + 1])
145
146    # skip crashed_task_tcb, exccause, and excvaddr
147    for i in range(5, len(extra_info), 2):
148        if (extra_info[i] >= ExceptionRegisters.EPC1_IDX and extra_info[i] <= ExceptionRegisters.EPC7_IDX):
149            print('epc%d           0x%x' % ((extra_info[i] - ExceptionRegisters.EPC1_IDX + 1), extra_info[i + 1]))
150
151    # skip crashed_task_tcb, exccause, and excvaddr
152    for i in range(5, len(extra_info), 2):
153        if (extra_info[i] >= ExceptionRegisters.EPS2_IDX and extra_info[i] <= ExceptionRegisters.EPS7_IDX):
154            print('eps%d           0x%x' % ((extra_info[i] - ExceptionRegisters.EPS2_IDX + 2), extra_info[i + 1]))
155
156
157# from "gdb/xtensa-tdep.h"
158# typedef struct
159# {
160# 0    xtensa_elf_greg_t pc;
161# 1    xtensa_elf_greg_t ps;
162# 2    xtensa_elf_greg_t lbeg;
163# 3    xtensa_elf_greg_t lend;
164# 4    xtensa_elf_greg_t lcount;
165# 5    xtensa_elf_greg_t sar;
166# 6    xtensa_elf_greg_t windowstart;
167# 7    xtensa_elf_greg_t windowbase;
168# 8..63 xtensa_elf_greg_t reserved[8+48];
169# 64   xtensa_elf_greg_t ar[64];
170# } xtensa_elf_gregset_t;
171REG_PC_IDX = 0
172REG_PS_IDX = 1
173REG_LB_IDX = 2
174REG_LE_IDX = 3
175REG_LC_IDX = 4
176REG_SAR_IDX = 5
177# REG_WS_IDX = 6
178# REG_WB_IDX = 7
179REG_AR_START_IDX = 64
180# REG_AR_NUM = 64
181# FIXME: acc to xtensa_elf_gregset_t number of regs must be 128,
182# but gdb complains when it less then 129
183REG_NUM = 129
184
185# XT_SOL_EXIT = 0
186XT_SOL_PC = 1
187XT_SOL_PS = 2
188# XT_SOL_NEXT = 3
189XT_SOL_AR_START = 4
190XT_SOL_AR_NUM = 4
191# XT_SOL_FRMSZ = 8
192
193XT_STK_EXIT = 0
194XT_STK_PC = 1
195XT_STK_PS = 2
196XT_STK_AR_START = 3
197XT_STK_AR_NUM = 16
198XT_STK_SAR = 19
199XT_STK_EXCCAUSE = 20
200XT_STK_EXCVADDR = 21
201XT_STK_LBEG = 22
202XT_STK_LEND = 23
203XT_STK_LCOUNT = 24
204XT_STK_FRMSZ = 25
205
206
207class XtensaMethodsMixin(BaseArchMethodsMixin):
208    @staticmethod
209    def get_registers_from_stack(data, grows_down):
210        # type: (bytes, bool) -> Tuple[list[int], Optional[dict[int, int]]]
211        extra_regs = {v: 0 for v in ExceptionRegisters().registers.values()}
212        regs = [0] * REG_NUM
213        # TODO: support for growing up stacks
214        if not grows_down:
215            raise ESPCoreDumpLoaderError('Growing up stacks are not supported for now!')
216        ex_struct = Struct(
217            'stack' / Int32ul[XT_STK_FRMSZ]
218        )
219        if len(data) < ex_struct.sizeof():
220            raise ESPCoreDumpLoaderError('Too small stack to keep frame: %d bytes!' % len(data))
221
222        stack = ex_struct.parse(data).stack
223        # Stack frame type indicator is always the first item
224        rc = stack[XT_STK_EXIT]
225        if rc != 0:
226            regs[REG_PC_IDX] = stack[XT_STK_PC]
227            regs[REG_PS_IDX] = stack[XT_STK_PS]
228            for i in range(XT_STK_AR_NUM):
229                regs[REG_AR_START_IDX + i] = stack[XT_STK_AR_START + i]
230            regs[REG_SAR_IDX] = stack[XT_STK_SAR]
231            regs[REG_LB_IDX] = stack[XT_STK_LBEG]
232            regs[REG_LE_IDX] = stack[XT_STK_LEND]
233            regs[REG_LC_IDX] = stack[XT_STK_LCOUNT]
234            # FIXME: crashed and some running tasks (e.g. prvIdleTask) have EXCM bit set
235            #   and GDB can not unwind callstack properly (it implies not windowed call0)
236            if regs[REG_PS_IDX] & (1 << 5):
237                regs[REG_PS_IDX] &= ~(1 << 4)
238            if stack[XT_STK_EXCCAUSE] in XTENSA_EXCEPTION_CAUSE_DICT:
239                extra_regs[ExceptionRegisters.EXCCAUSE_IDX] = stack[XT_STK_EXCCAUSE]
240            else:
241                extra_regs[ExceptionRegisters.EXCCAUSE_IDX] = INVALID_CAUSE_VALUE
242            extra_regs[ExceptionRegisters.EXCVADDR_IDX] = stack[XT_STK_EXCVADDR]
243        else:
244            regs[REG_PC_IDX] = stack[XT_SOL_PC]
245            regs[REG_PS_IDX] = stack[XT_SOL_PS]
246            for i in range(XT_SOL_AR_NUM):
247                regs[REG_AR_START_IDX + i] = stack[XT_SOL_AR_START + i]
248            # nxt = stack[XT_SOL_NEXT]
249        return regs, extra_regs
250
251    @staticmethod
252    def build_prstatus_data(tcb_addr, task_regs):  # type: (int, list[int]) -> Any
253        return PrStatus.build({
254            'si_signo': 0,
255            'si_code': 0,
256            'si_errno': 0,
257            'pr_cursig': 0,  # TODO: set sig only for current/failed task
258            'pr_pad0': 0,
259            'pr_sigpend': 0,
260            'pr_sighold': 0,
261            'pr_pid': tcb_addr,
262            'pr_ppid': 0,
263            'pr_pgrp': 0,
264            'pr_sid': 0,
265            'pr_utime': 0,
266            'pr_stime': 0,
267            'pr_cutime': 0,
268            'pr_cstime': 0,
269        }) + Int32ul[len(task_regs)].build(task_regs)
270
271
272class Esp32Methods(BaseTargetMethods, XtensaMethodsMixin):
273    TARGET = 'esp32'
274
275
276class Esp32S2Methods(BaseTargetMethods, XtensaMethodsMixin):
277    TARGET = 'esp32s2'
278
279
280class Esp32S3Methods(BaseTargetMethods, XtensaMethodsMixin):
281    TARGET = 'esp32s3'
282