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
17__version__ = '0.4-dev'
18
19import abc
20import os
21from abc import abstractmethod
22from importlib import import_module
23
24from future.utils import with_metaclass
25
26try:
27    from typing import Optional, Tuple
28except ImportError:
29    pass
30
31IDF_PATH = os.path.normpath(os.getenv('IDF_PATH', '.'))
32XTENSA_TARGETS = ['esp32', 'esp32s2', 'esp32s3']
33RISCV_TARGETS = ['esp32c3']
34SUPPORTED_TARGETS = XTENSA_TARGETS + RISCV_TARGETS
35
36
37class ESPCoreDumpError(RuntimeError):
38    pass
39
40
41class ESPCoreDumpLoaderError(ESPCoreDumpError):
42    pass
43
44
45class BaseArchMethodsMixin(with_metaclass(abc.ABCMeta)):  # type: ignore
46    @staticmethod
47    @abstractmethod
48    def get_registers_from_stack(data, grows_down):
49        # type: (bytes, bool) -> Tuple[list[int], Optional[dict[int, int]]]
50        """
51        Parse stack data, growing up stacks are not supported for now.
52        :param data: stack data
53        :param grows_down: stack grow direction
54        :return: return tuple (regs, exception_regs)
55        """
56        pass
57
58    @staticmethod
59    @abstractmethod
60    def build_prstatus_data(tcb_addr, task_regs):  # type: (int, list[int]) -> str
61        """
62        Build PrStatus note section
63        :param tcb_addr: tcb addr
64        :param task_regs: registers
65        :return: str
66        """
67        pass
68
69
70class BaseTargetMethods(with_metaclass(abc.ABCMeta, BaseArchMethodsMixin)):  # type: ignore
71    UNKNOWN = 'unknown'
72    TARGET = UNKNOWN
73
74    COREDUMP_FAKE_STACK_START = 0x20000000
75    COREDUMP_FAKE_STACK_LIMIT = 0x30000000
76    COREDUMP_MAX_TASK_STACK_SIZE = 64 * 1024
77
78    def __init__(self):  # type: () -> None
79        if self.TARGET == self.UNKNOWN:
80            raise ValueError('Please use the derived child-class with valid TARGET')
81
82        self._set_attr_from_soc_header()
83
84    def _set_attr_from_soc_header(self):  # type: () -> None
85        module = import_module('corefile.soc_headers.{}'.format(self.TARGET))
86        for k, v in module.__dict__.items():
87            if k.startswith('SOC_'):
88                setattr(self, k, v)
89
90    def _esp_ptr_in_dram(self, addr):  # type: (int) -> bool
91        return self.SOC_DRAM_LOW <= addr < self.SOC_DRAM_HIGH  # type: ignore
92
93    def _esp_ptr_in_iram(self, addr):  # type: (int) -> bool
94        return self.SOC_IRAM_LOW <= addr < self.SOC_IRAM_HIGH  # type: ignore
95
96    def _esp_ptr_in_rtc_slow(self, addr):  # type: (int) -> bool
97        return self.SOC_RTC_DATA_LOW <= addr < self.SOC_RTC_DATA_HIGH  # type: ignore
98
99    def _esp_ptr_in_rtc_dram_fast(self, addr):  # type: (int) -> bool
100        return self.SOC_RTC_DRAM_LOW <= addr < self.SOC_RTC_DRAM_HIGH  # type: ignore
101
102    def tcb_is_sane(self, tcb_addr, tcb_size):  # type: (int, int) -> bool
103        for func in [self._esp_ptr_in_dram,
104                     self._esp_ptr_in_iram,
105                     self._esp_ptr_in_rtc_slow,
106                     self._esp_ptr_in_rtc_dram_fast]:
107            res = func(tcb_addr) and func(tcb_addr + tcb_size - 1)
108            if res:
109                return True
110        return False
111
112    def _esp_stack_ptr_in_dram(self, addr):  # type: (int) -> bool
113        return not (addr < self.SOC_DRAM_LOW + 0x10
114                    or addr > self.SOC_DRAM_HIGH - 0x10
115                    or (addr & 0xF) != 0)
116
117    def stack_is_sane(self, stack_start, stack_end):  # type: (int, int) -> bool
118        return (self._esp_stack_ptr_in_dram(stack_start)
119                and self._esp_ptr_in_dram(stack_end)
120                and stack_start < stack_end
121                and (stack_end - stack_start) < self.COREDUMP_MAX_TASK_STACK_SIZE)
122
123    def addr_is_fake(self, addr):  # type: (int) -> bool
124        return (self.COREDUMP_FAKE_STACK_START <= addr < self.COREDUMP_FAKE_STACK_LIMIT
125                or addr > 2 ** 31 - 1)
126