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