1# SPDX-FileCopyrightText: 2014-2023 Fredrik Ahlberg, Angus Gratton,
2# Espressif Systems (Shanghai) CO LTD, other contributors as noted.
3#
4# SPDX-License-Identifier: GPL-2.0-or-later
5
6import os
7import struct
8from typing import Dict
9
10from .esp32 import ESP32ROM
11from ..loader import ESPLoader
12from ..reset import HardReset
13from ..util import FatalError, NotImplementedInROMError
14
15
16class ESP32S3ROM(ESP32ROM):
17    CHIP_NAME = "ESP32-S3"
18
19    IMAGE_CHIP_ID = 9
20
21    CHIP_DETECT_MAGIC_VALUE = [0x9]
22
23    IROM_MAP_START = 0x42000000
24    IROM_MAP_END = 0x44000000
25    DROM_MAP_START = 0x3C000000
26    DROM_MAP_END = 0x3E000000
27
28    UART_DATE_REG_ADDR = 0x60000080
29
30    SPI_REG_BASE = 0x60002000
31    SPI_USR_OFFS = 0x18
32    SPI_USR1_OFFS = 0x1C
33    SPI_USR2_OFFS = 0x20
34    SPI_MOSI_DLEN_OFFS = 0x24
35    SPI_MISO_DLEN_OFFS = 0x28
36    SPI_W0_OFFS = 0x58
37
38    SPI_ADDR_REG_MSB = False
39
40    BOOTLOADER_FLASH_OFFSET = 0x0
41
42    SUPPORTS_ENCRYPTED_FLASH = True
43
44    FLASH_ENCRYPTED_WRITE_ALIGN = 16
45
46    # todo: use espefuse APIs to get this info
47    EFUSE_BASE = 0x60007000  # BLOCK0 read base address
48    EFUSE_BLOCK1_ADDR = EFUSE_BASE + 0x44
49    EFUSE_BLOCK2_ADDR = EFUSE_BASE + 0x5C
50    MAC_EFUSE_REG = EFUSE_BASE + 0x044
51
52    EFUSE_RD_REG_BASE = EFUSE_BASE + 0x030  # BLOCK0 read base address
53
54    EFUSE_PURPOSE_KEY0_REG = EFUSE_BASE + 0x34
55    EFUSE_PURPOSE_KEY0_SHIFT = 24
56    EFUSE_PURPOSE_KEY1_REG = EFUSE_BASE + 0x34
57    EFUSE_PURPOSE_KEY1_SHIFT = 28
58    EFUSE_PURPOSE_KEY2_REG = EFUSE_BASE + 0x38
59    EFUSE_PURPOSE_KEY2_SHIFT = 0
60    EFUSE_PURPOSE_KEY3_REG = EFUSE_BASE + 0x38
61    EFUSE_PURPOSE_KEY3_SHIFT = 4
62    EFUSE_PURPOSE_KEY4_REG = EFUSE_BASE + 0x38
63    EFUSE_PURPOSE_KEY4_SHIFT = 8
64    EFUSE_PURPOSE_KEY5_REG = EFUSE_BASE + 0x38
65    EFUSE_PURPOSE_KEY5_SHIFT = 12
66
67    EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG = EFUSE_RD_REG_BASE
68    EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT = 1 << 20
69
70    EFUSE_SPI_BOOT_CRYPT_CNT_REG = EFUSE_BASE + 0x034
71    EFUSE_SPI_BOOT_CRYPT_CNT_MASK = 0x7 << 18
72
73    EFUSE_SECURE_BOOT_EN_REG = EFUSE_BASE + 0x038
74    EFUSE_SECURE_BOOT_EN_MASK = 1 << 20
75
76    EFUSE_RD_REPEAT_DATA3_REG = EFUSE_BASE + 0x3C
77    EFUSE_RD_REPEAT_DATA3_REG_FLASH_TYPE_MASK = 1 << 9
78
79    PURPOSE_VAL_XTS_AES256_KEY_1 = 2
80    PURPOSE_VAL_XTS_AES256_KEY_2 = 3
81    PURPOSE_VAL_XTS_AES128_KEY = 4
82
83    UARTDEV_BUF_NO = 0x3FCEF14C  # Variable in ROM .bss which indicates the port in use
84    UARTDEV_BUF_NO_USB_OTG = 3  # The above var when USB-OTG is used
85    UARTDEV_BUF_NO_USB_JTAG_SERIAL = 4  # The above var when USB-JTAG/Serial is used
86
87    RTCCNTL_BASE_REG = 0x60008000
88    RTC_CNTL_SWD_CONF_REG = RTCCNTL_BASE_REG + 0x00B4
89    RTC_CNTL_SWD_AUTO_FEED_EN = 1 << 31
90    RTC_CNTL_SWD_WPROTECT_REG = RTCCNTL_BASE_REG + 0x00B8
91    RTC_CNTL_SWD_WKEY = 0x8F1D312A
92
93    RTC_CNTL_WDTCONFIG0_REG = RTCCNTL_BASE_REG + 0x0098
94    RTC_CNTL_WDTWPROTECT_REG = RTCCNTL_BASE_REG + 0x00B0
95    RTC_CNTL_WDT_WKEY = 0x50D83AA1
96
97    USB_RAM_BLOCK = 0x800  # Max block size USB-OTG is used
98
99    GPIO_STRAP_REG = 0x60004038
100    GPIO_STRAP_SPI_BOOT_MASK = 0x8  # Not download mode
101    GPIO_STRAP_VDDSPI_MASK = 1 << 4
102    RTC_CNTL_OPTION1_REG = 0x6000812C
103    RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK = 0x1  # Is download mode forced over USB?
104
105    UART_CLKDIV_REG = 0x60000014
106
107    MEMORY_MAP = [
108        [0x00000000, 0x00010000, "PADDING"],
109        [0x3C000000, 0x3D000000, "DROM"],
110        [0x3D000000, 0x3E000000, "EXTRAM_DATA"],
111        [0x600FE000, 0x60100000, "RTC_DRAM"],
112        [0x3FC88000, 0x3FD00000, "BYTE_ACCESSIBLE"],
113        [0x3FC88000, 0x403E2000, "MEM_INTERNAL"],
114        [0x3FC88000, 0x3FD00000, "DRAM"],
115        [0x40000000, 0x4001A100, "IROM_MASK"],
116        [0x40370000, 0x403E0000, "IRAM"],
117        [0x600FE000, 0x60100000, "RTC_IRAM"],
118        [0x42000000, 0x42800000, "IROM"],
119        [0x50000000, 0x50002000, "RTC_DATA"],
120    ]
121
122    EFUSE_VDD_SPI_REG = EFUSE_BASE + 0x34
123    VDD_SPI_XPD = 1 << 4
124    VDD_SPI_TIEH = 1 << 5
125    VDD_SPI_FORCE = 1 << 6
126
127    UF2_FAMILY_ID = 0xC47E5767
128
129    EFUSE_MAX_KEY = 5
130    KEY_PURPOSES: Dict[int, str] = {
131        0: "USER/EMPTY",
132        1: "RESERVED",
133        2: "XTS_AES_256_KEY_1",
134        3: "XTS_AES_256_KEY_2",
135        4: "XTS_AES_128_KEY",
136        5: "HMAC_DOWN_ALL",
137        6: "HMAC_DOWN_JTAG",
138        7: "HMAC_DOWN_DIGITAL_SIGNATURE",
139        8: "HMAC_UP",
140        9: "SECURE_BOOT_DIGEST0",
141        10: "SECURE_BOOT_DIGEST1",
142        11: "SECURE_BOOT_DIGEST2",
143    }
144
145    def get_pkg_version(self):
146        num_word = 3
147        return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 21) & 0x07
148
149    def is_eco0(self, minor_raw):
150        # Workaround: The major version field was allocated to other purposes
151        # when block version is v1.1.
152        # Luckily only chip v0.0 have this kind of block version and efuse usage.
153        return (
154            (minor_raw & 0x7) == 0
155            and self.get_blk_version_major() == 1
156            and self.get_blk_version_minor() == 1
157        )
158
159    def get_minor_chip_version(self):
160        minor_raw = self.get_raw_minor_chip_version()
161        if self.is_eco0(minor_raw):
162            return 0
163        return minor_raw
164
165    def get_raw_minor_chip_version(self):
166        hi_num_word = 5
167        hi = (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * hi_num_word)) >> 23) & 0x01
168        low_num_word = 3
169        low = (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * low_num_word)) >> 18) & 0x07
170        return (hi << 3) + low
171
172    def get_blk_version_major(self):
173        num_word = 4
174        return (self.read_reg(self.EFUSE_BLOCK2_ADDR + (4 * num_word)) >> 0) & 0x03
175
176    def get_blk_version_minor(self):
177        num_word = 3
178        return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 24) & 0x07
179
180    def get_major_chip_version(self):
181        minor_raw = self.get_raw_minor_chip_version()
182        if self.is_eco0(minor_raw):
183            return 0
184        return self.get_raw_major_chip_version()
185
186    def get_raw_major_chip_version(self):
187        num_word = 5
188        return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 24) & 0x03
189
190    def get_chip_description(self):
191        major_rev = self.get_major_chip_version()
192        minor_rev = self.get_minor_chip_version()
193        pkg_version = self.get_pkg_version()
194
195        chip_name = {
196            0: "ESP32-S3 (QFN56)",
197            1: "ESP32-S3-PICO-1 (LGA56)",
198        }.get(pkg_version, "unknown ESP32-S3")
199
200        return f"{chip_name} (revision v{major_rev}.{minor_rev})"
201
202    def get_flash_cap(self):
203        num_word = 3
204        return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 27) & 0x07
205
206    def get_flash_vendor(self):
207        num_word = 4
208        vendor_id = (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 0) & 0x07
209        return {1: "XMC", 2: "GD", 3: "FM", 4: "TT", 5: "BY"}.get(vendor_id, "")
210
211    def get_psram_cap(self):
212        num_word = 4
213        return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 3) & 0x03
214
215    def get_psram_vendor(self):
216        num_word = 4
217        vendor_id = (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 7) & 0x03
218        return {1: "AP_3v3", 2: "AP_1v8"}.get(vendor_id, "")
219
220    def get_chip_features(self):
221        features = ["WiFi", "BLE"]
222
223        flash = {
224            0: None,
225            1: "Embedded Flash 8MB",
226            2: "Embedded Flash 4MB",
227        }.get(self.get_flash_cap(), "Unknown Embedded Flash")
228        if flash is not None:
229            features += [flash + f" ({self.get_flash_vendor()})"]
230
231        psram = {
232            0: None,
233            1: "Embedded PSRAM 8MB",
234            2: "Embedded PSRAM 2MB",
235        }.get(self.get_psram_cap(), "Unknown Embedded PSRAM")
236        if psram is not None:
237            features += [psram + f" ({self.get_psram_vendor()})"]
238
239        return features
240
241    def get_crystal_freq(self):
242        # ESP32S3 XTAL is fixed to 40MHz
243        return 40
244
245    def get_flash_crypt_config(self):
246        return None  # doesn't exist on ESP32-S3
247
248    def get_key_block_purpose(self, key_block):
249        if key_block < 0 or key_block > self.EFUSE_MAX_KEY:
250            raise FatalError(
251                f"Valid key block numbers must be in range 0-{self.EFUSE_MAX_KEY}"
252            )
253
254        reg, shift = [
255            (self.EFUSE_PURPOSE_KEY0_REG, self.EFUSE_PURPOSE_KEY0_SHIFT),
256            (self.EFUSE_PURPOSE_KEY1_REG, self.EFUSE_PURPOSE_KEY1_SHIFT),
257            (self.EFUSE_PURPOSE_KEY2_REG, self.EFUSE_PURPOSE_KEY2_SHIFT),
258            (self.EFUSE_PURPOSE_KEY3_REG, self.EFUSE_PURPOSE_KEY3_SHIFT),
259            (self.EFUSE_PURPOSE_KEY4_REG, self.EFUSE_PURPOSE_KEY4_SHIFT),
260            (self.EFUSE_PURPOSE_KEY5_REG, self.EFUSE_PURPOSE_KEY5_SHIFT),
261        ][key_block]
262        return (self.read_reg(reg) >> shift) & 0xF
263
264    def is_flash_encryption_key_valid(self):
265        # Need to see either an AES-128 key or two AES-256 keys
266        purposes = [
267            self.get_key_block_purpose(b) for b in range(self.EFUSE_MAX_KEY + 1)
268        ]
269
270        if any(p == self.PURPOSE_VAL_XTS_AES128_KEY for p in purposes):
271            return True
272
273        return any(p == self.PURPOSE_VAL_XTS_AES256_KEY_1 for p in purposes) and any(
274            p == self.PURPOSE_VAL_XTS_AES256_KEY_2 for p in purposes
275        )
276
277    def get_secure_boot_enabled(self):
278        return (
279            self.read_reg(self.EFUSE_SECURE_BOOT_EN_REG)
280            & self.EFUSE_SECURE_BOOT_EN_MASK
281        )
282
283    def _get_rtc_cntl_flash_voltage(self):
284        return None  # not supported on ESP32-S3
285
286    def override_vddsdio(self, new_voltage):
287        raise NotImplementedInROMError(
288            "VDD_SDIO overrides are not supported for ESP32-S3"
289        )
290
291    def read_mac(self, mac_type="BASE_MAC"):
292        """Read MAC from EFUSE region"""
293        if mac_type != "BASE_MAC":
294            return None
295        mac0 = self.read_reg(self.MAC_EFUSE_REG)
296        mac1 = self.read_reg(self.MAC_EFUSE_REG + 4)  # only bottom 16 bits are MAC
297        bitstring = struct.pack(">II", mac1, mac0)[2:]
298        return tuple(bitstring)
299
300    def flash_type(self):
301        return (
302            1
303            if self.read_reg(self.EFUSE_RD_REPEAT_DATA3_REG)
304            & self.EFUSE_RD_REPEAT_DATA3_REG_FLASH_TYPE_MASK
305            else 0
306        )
307
308    def uses_usb_otg(self):
309        """
310        Check the UARTDEV_BUF_NO register to see if USB-OTG console is being used
311        """
312        if self.secure_download_mode:
313            return False  # can't detect native USB in secure download mode
314        return self.get_uart_no() == self.UARTDEV_BUF_NO_USB_OTG
315
316    def uses_usb_jtag_serial(self):
317        """
318        Check the UARTDEV_BUF_NO register to see if USB-JTAG/Serial is being used
319        """
320        if self.secure_download_mode:
321            return False  # can't detect USB-JTAG/Serial in secure download mode
322        return self.get_uart_no() == self.UARTDEV_BUF_NO_USB_JTAG_SERIAL
323
324    def disable_watchdogs(self):
325        # When USB-JTAG/Serial is used, the RTC WDT and SWD watchdog are not reset
326        # and can then reset the board during flashing. Disable them.
327        if self.uses_usb_jtag_serial():
328            # Disable RTC WDT
329            self.write_reg(self.RTC_CNTL_WDTWPROTECT_REG, self.RTC_CNTL_WDT_WKEY)
330            self.write_reg(self.RTC_CNTL_WDTCONFIG0_REG, 0)
331            self.write_reg(self.RTC_CNTL_WDTWPROTECT_REG, 0)
332
333            # Automatically feed SWD
334            self.write_reg(self.RTC_CNTL_SWD_WPROTECT_REG, self.RTC_CNTL_SWD_WKEY)
335            self.write_reg(
336                self.RTC_CNTL_SWD_CONF_REG,
337                self.read_reg(self.RTC_CNTL_SWD_CONF_REG)
338                | self.RTC_CNTL_SWD_AUTO_FEED_EN,
339            )
340            self.write_reg(self.RTC_CNTL_SWD_WPROTECT_REG, 0)
341
342    def _post_connect(self):
343        if self.uses_usb_otg():
344            self.ESP_RAM_BLOCK = self.USB_RAM_BLOCK
345        if not self.sync_stub_detected:  # Don't run if stub is reused
346            self.disable_watchdogs()
347
348    def _check_if_can_reset(self):
349        """
350        Check the strapping register to see if we can reset out of download mode.
351        """
352        if os.getenv("ESPTOOL_TESTING") is not None:
353            print("ESPTOOL_TESTING is set, ignoring strapping mode check")
354            # Esptool tests over USB-OTG run with GPIO0 strapped low,
355            # don't complain in this case.
356            return
357        strap_reg = self.read_reg(self.GPIO_STRAP_REG)
358        force_dl_reg = self.read_reg(self.RTC_CNTL_OPTION1_REG)
359        if (
360            strap_reg & self.GPIO_STRAP_SPI_BOOT_MASK == 0
361            and force_dl_reg & self.RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK == 0
362        ):
363            raise SystemExit(
364                f"Error: {self.get_chip_description()} chip was placed into download "
365                "mode using GPIO0.\nesptool.py can not exit the download mode over "
366                "USB. To run the app, reset the chip manually.\n"
367                "To suppress this note, set --after option to 'no_reset'."
368            )
369
370    def hard_reset(self):
371        uses_usb_otg = self.uses_usb_otg()
372        if uses_usb_otg:
373            self._check_if_can_reset()
374
375        try:
376            # Clear force download boot mode to avoid the chip being stuck in download mode after reset
377            # workaround for issue: https://github.com/espressif/arduino-esp32/issues/6762
378            self.write_reg(
379                self.RTC_CNTL_OPTION1_REG, 0, self.RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK
380            )
381        except Exception:
382            # Skip if response was not valid and proceed to reset; e.g. when monitoring while resetting
383            pass
384
385        print("Hard resetting via RTS pin...")
386        HardReset(self._port, uses_usb_otg)()
387
388    def change_baud(self, baud):
389        ESPLoader.change_baud(self, baud)
390
391    def check_spi_connection(self, spi_connection):
392        if not set(spi_connection).issubset(set(range(0, 22)) | set(range(26, 49))):
393            raise FatalError("SPI Pin numbers must be in the range 0-21, or 26-48.")
394        if spi_connection[3] > 46:  # hd_gpio_num must be <= SPI_GPIO_NUM_LIMIT (46)
395            raise FatalError("SPI HD Pin number must be <= 46.")
396        if any([v for v in spi_connection if v in [19, 20]]):
397            print(
398                "WARNING: GPIO pins 19 and 20 are used by USB-Serial/JTAG and USB-OTG, "
399                "consider using other pins for SPI flash connection."
400            )
401
402
403class ESP32S3StubLoader(ESP32S3ROM):
404    """Access class for ESP32S3 stub loader, runs on top of ROM.
405
406    (Basically the same as ESP32StubLoader, but different base class.
407    Can possibly be made into a mixin.)
408    """
409
410    FLASH_WRITE_SIZE = 0x4000  # matches MAX_WRITE_BLOCK in stub_loader.c
411    STATUS_BYTES_LENGTH = 2  # same as ESP8266, different to ESP32 ROM
412    IS_STUB = True
413
414    def __init__(self, rom_loader):
415        self.secure_download_mode = rom_loader.secure_download_mode
416        self._port = rom_loader._port
417        self._trace_enabled = rom_loader._trace_enabled
418        self.cache = rom_loader.cache
419        self.flush_input()  # resets _slip_reader
420
421        if rom_loader.uses_usb_otg():
422            self.ESP_RAM_BLOCK = self.USB_RAM_BLOCK
423            self.FLASH_WRITE_SIZE = self.USB_RAM_BLOCK
424
425
426ESP32S3ROM.STUB_CLASS = ESP32S3StubLoader
427