Skip to content

Commit

Permalink
refactor(flasher_stub): Include stubs as json, do not embed in code
Browse files Browse the repository at this point in the history
  • Loading branch information
radimkarnis committed Jul 28, 2022
1 parent fcc8dc5 commit 94f29a5
Show file tree
Hide file tree
Showing 28 changed files with 221 additions and 597 deletions.
1 change: 1 addition & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
include README.md
include LICENSE
include esptool/targets/stub_flasher/*.json
# sdist includes test/test*.py by default, but esptool.py tests
# are so far only intended to run from the git repo itself
prune test
Expand Down
47 changes: 37 additions & 10 deletions esptool/loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
#
# SPDX-License-Identifier: GPL-2.0-or-later

import base64
import hashlib
import itertools
import json
import os
import re
import string
Expand Down Expand Up @@ -72,6 +74,14 @@
DEFAULT_SERIAL_WRITE_TIMEOUT = 10 # timeout for serial port write
DEFAULT_CONNECT_ATTEMPTS = 7 # default number of times to try connection

STUBS_DIR = os.path.join(os.path.dirname(__file__), "./targets/stub_flasher/")


def get_stub_json_path(chip_name):
chip_name = re.sub(r"[-()]", "", chip_name.lower())
chip_name = chip_name.replace("esp", "")
return STUBS_DIR + "stub_flasher_" + chip_name + ".json"


def timeout_per_mb(seconds_per_mb, size_bytes):
"""Scales timeouts which are size-specific"""
Expand Down Expand Up @@ -121,6 +131,23 @@ def esp32s3_or_newer_function_only(func):
)


class StubFlasher:
def __init__(self, json_path):
with open(json_path) as json_file:
stub = json.load(json_file)

self.text = base64.b64decode(stub["text"])
self.text_start = stub["text_start"]
self.entry = stub["entry"]

try:
self.data = base64.b64decode(stub["data"])
self.data_start = stub["data_start"]
except KeyError:
self.data = None
self.data_start = None


class ESPLoader(object):
"""Base class providing access to ESP ROM & software stub bootloaders.
Subclasses provide ESP8266 & ESP32 Family specific functionality.
Expand Down Expand Up @@ -698,12 +725,12 @@ def mem_begin(self, size, blocks, blocksize, offset):
"""Start downloading an application image to RAM"""
# check we're not going to overwrite a running stub with this data
if self.IS_STUB:
stub = self.STUB_CODE
stub = StubFlasher(get_stub_json_path(self.CHIP_NAME))
load_start = offset
load_end = offset + size
for (start, end) in [
(stub["data_start"], stub["data_start"] + len(stub["data"])),
(stub["text_start"], stub["text_start"] + len(stub["text"])),
(stub.data_start, stub.data_start + len(stub.data)),
(stub.text_start, stub.text_start + len(stub.text)),
]:
if load_start < end and load_end > start:
raise FatalError(
Expand Down Expand Up @@ -861,26 +888,26 @@ def parse_flash_freq_arg(cls, arg):

def run_stub(self, stub=None):
if stub is None:
stub = self.STUB_CODE
stub = StubFlasher(get_stub_json_path(self.CHIP_NAME))

if self.sync_stub_detected:
print("Stub is already running. No upload is necessary.")
return self.STUB_CLASS(self)

# Upload
print("Uploading stub...")
for field in ["text", "data"]:
if field in stub:
offs = stub[field + "_start"]
length = len(stub[field])
for field in [stub.text, stub.data]:
if field is not None:
offs = stub.text_start if field == stub.text else stub.data_start
length = len(field)
blocks = (length + self.ESP_RAM_BLOCK - 1) // self.ESP_RAM_BLOCK
self.mem_begin(length, blocks, self.ESP_RAM_BLOCK, offs)
for seq in range(blocks):
from_offs = seq * self.ESP_RAM_BLOCK
to_offs = from_offs + self.ESP_RAM_BLOCK
self.mem_block(stub[field][from_offs:to_offs], seq)
self.mem_block(field[from_offs:to_offs], seq)
print("Running stub...")
self.mem_finish(stub["entry"])
self.mem_finish(stub.entry)

p = self.read()
if p != b"OHAI":
Expand Down
415 changes: 0 additions & 415 deletions esptool/stub_flasher.py

This file was deleted.

3 changes: 0 additions & 3 deletions esptool/targets/esp32.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import struct

from ..loader import ESPLoader
from ..stub_flasher import ESP32StubCode
from ..util import FatalError, NotSupportedError


Expand All @@ -17,8 +16,6 @@ class ESP32ROM(ESPLoader):
IMAGE_CHIP_ID = 0
IS_STUB = False

STUB_CODE = ESP32StubCode

FPGA_SLOW_BOOT = True

CHIP_DETECT_MAGIC_VALUE = [0x00F01D83]
Expand Down
3 changes: 0 additions & 3 deletions esptool/targets/esp32c2.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,12 @@

from .esp32c3 import ESP32C3ROM
from ..loader import ESPLoader
from ..stub_flasher import ESP32C2StubCode


class ESP32C2ROM(ESP32C3ROM):
CHIP_NAME = "ESP32-C2"
IMAGE_CHIP_ID = 12

STUB_CODE = ESP32C2StubCode

IROM_MAP_START = 0x42000000
IROM_MAP_END = 0x42400000
DROM_MAP_START = 0x3C000000
Expand Down
3 changes: 0 additions & 3 deletions esptool/targets/esp32c3.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,13 @@
import struct

from .esp32 import ESP32ROM
from ..stub_flasher import ESP32C3StubCode
from ..util import FatalError, NotImplementedInROMError


class ESP32C3ROM(ESP32ROM):
CHIP_NAME = "ESP32-C3"
IMAGE_CHIP_ID = 5

STUB_CODE = ESP32C3StubCode

FPGA_SLOW_BOOT = False

IROM_MAP_START = 0x42000000
Expand Down
3 changes: 0 additions & 3 deletions esptool/targets/esp32c6beta.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
# SPDX-License-Identifier: GPL-2.0-or-later

from .esp32c3 import ESP32C3ROM
from ..stub_flasher import ESP32C6BETAStubCode


class ESP32C6BETAROM(ESP32C3ROM):
Expand All @@ -13,8 +12,6 @@ class ESP32C6BETAROM(ESP32C3ROM):

CHIP_DETECT_MAGIC_VALUE = [0x0DA1806F]

STUB_CODE = ESP32C6BETAStubCode

UART_DATE_REG_ADDR = 0x00000500

def get_chip_description(self):
Expand Down
3 changes: 0 additions & 3 deletions esptool/targets/esp32h2beta1.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,13 @@
import struct

from .esp32 import ESP32ROM
from ..stub_flasher import ESP32H2BETA1StubCode
from ..util import FatalError, NotImplementedInROMError


class ESP32H2BETA1ROM(ESP32ROM):
CHIP_NAME = "ESP32-H2(beta1)"
IMAGE_CHIP_ID = 10

STUB_CODE = ESP32H2BETA1StubCode

IROM_MAP_START = 0x42000000
IROM_MAP_END = 0x42800000
DROM_MAP_START = 0x3C000000
Expand Down
3 changes: 0 additions & 3 deletions esptool/targets/esp32h2beta2.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,12 @@
# SPDX-License-Identifier: GPL-2.0-or-later

from .esp32h2beta1 import ESP32H2BETA1ROM
from ..stub_flasher import ESP32H2BETA2StubCode


class ESP32H2BETA2ROM(ESP32H2BETA1ROM):
CHIP_NAME = "ESP32-H2(beta2)"
IMAGE_CHIP_ID = 14

STUB_CODE = ESP32H2BETA2StubCode

CHIP_DETECT_MAGIC_VALUE = [0x6881B06F]

def get_chip_description(self):
Expand Down
3 changes: 0 additions & 3 deletions esptool/targets/esp32s2.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,13 @@
import time

from .esp32 import ESP32ROM
from ..stub_flasher import ESP32S2StubCode
from ..util import FatalError, NotImplementedInROMError


class ESP32S2ROM(ESP32ROM):
CHIP_NAME = "ESP32-S2"
IMAGE_CHIP_ID = 2

STUB_CODE = ESP32S2StubCode

FPGA_SLOW_BOOT = False

IROM_MAP_START = 0x40080000
Expand Down
3 changes: 0 additions & 3 deletions esptool/targets/esp32s3.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import time

from .esp32 import ESP32ROM
from ..stub_flasher import ESP32S3StubCode
from ..util import FatalError, NotImplementedInROMError


Expand All @@ -19,8 +18,6 @@ class ESP32S3ROM(ESP32ROM):

CHIP_DETECT_MAGIC_VALUE = [0x9]

STUB_CODE = ESP32S3StubCode

FPGA_SLOW_BOOT = False

IROM_MAP_START = 0x42000000
Expand Down
3 changes: 0 additions & 3 deletions esptool/targets/esp32s3beta2.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
# SPDX-License-Identifier: GPL-2.0-or-later

from .esp32s3 import ESP32S3ROM
from ..stub_flasher import ESP32S3BETA2StubCode


class ESP32S3BETA2ROM(ESP32S3ROM):
Expand All @@ -13,8 +12,6 @@ class ESP32S3BETA2ROM(ESP32S3ROM):

CHIP_DETECT_MAGIC_VALUE = [0xEB004136]

STUB_CODE = ESP32S3BETA2StubCode

EFUSE_BASE = 0x6001A000 # BLOCK0 read base address

def get_chip_description(self):
Expand Down
3 changes: 0 additions & 3 deletions esptool/targets/esp8266.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
# SPDX-License-Identifier: GPL-2.0-or-later

from ..loader import ESPLoader
from ..stub_flasher import ESP8266StubCode
from ..util import FatalError, NotImplementedInROMError


Expand All @@ -14,8 +13,6 @@ class ESP8266ROM(ESPLoader):
CHIP_NAME = "ESP8266"
IS_STUB = False

STUB_CODE = ESP8266StubCode

CHIP_DETECT_MAGIC_VALUE = [0xFFF0C101]

# OTP ROM addresses
Expand Down
7 changes: 7 additions & 0 deletions esptool/targets/stub_flasher/stub_flasher_32.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"entry": 1074521516,
"text": "CAD0PxwA9D8AAPQ/pOv9PxAA9D82QQAh+v/AIAA4AkH5/8AgACgEICB0nOIGBQAAAEH1/4H2/8AgAKgEiAigoHTgCAALImYC54b0/yHx/8AgADkCHfAAAPgg9D/4MPQ/NkEAkf3/wCAAiAmAgCRWSP+R+v/AIACICYCAJFZI/x3wAAAAECD0PwAg9D8AAAAINkEA5fz/Ifv/DAjAIACJApH7/4H5/8AgAJJoAMAgAJgIVnn/wCAAiAJ88oAiMCAgBB3wAAAAAEA2QQBl/P8Wmv+B7f+R/P/AIACZCMAgAJgIVnn/HfAAAAAAgAAAAAABmMD9P////wAEIPQ/NkEAIfz/OEIWIwal+P8WygWIQgz5DAOHqQyIIpCIEAwZgDmDMDB0Zfr/pfP/iCKR8v9AiBGHOR+R7f/ME5Hs/6Hv/8AgAIkKgdH/wCAAmQjAIACYCFZ5/xwJDBgwiZM9CIhCMIjAiUKIIjo4OSId8JDA/T8IQP0/gIAAAISAAABAQAAASID9P5TA/T82QQCx+P8goHSltwCW6gWB9v+R9v+goHSQmIDAIACyKQCR8/+QiIDAIACSGACQkPQbycDA9MAgAMJYAJqbwCAAokkAwCAAkhgAger/kJD0gID0h5lGgeT/keX/oej/mpjAIADICbHk/4ecGUYCAHzohxrhRgkAAADAIACJCsAgALkJRgIAwCAAuQrAIACJCZHY/5qIDAnAIACSWAAd8AAAUC0GQDZBAEGw/1g0UDNjFvMDWBRaU1BcQYYAAGXr/4hEphgEiCSHpfLl4/8Wmv+oFM0DvQKB8v/gCACgoHSMOiKgxClUKBQ6IikUKDQwMsA5NB3wCCD0PwAAQABw4vo/SCQGQPAiBkA2YQDl3P+tAYH8/+AIAD0KDBLs6ogBkqIAkIgQiQGl4f+R8v+h8//AIACICaCIIMAgAIJpALIhAKHv/4Hw/+AIAKAjgx3wAAD/DwAANkEAgYT/kqABkkgAMJxBkmgCkfr/MmgBKTgwMLSaIiozMDxBDAIpWDlIpfj/LQqMGiKgxR3wAAAskgBANkEAgqDArQKHkg6ioNuB+//gCACioNyGAwCCoNuHkgiB9//gCACioN2B9P/gCAAd8AAAADZBADoyBgIAAKICABsi5fv/N5L0HfAAAAAQAABYEAAAfNoFQNguBkCc2gVAHNsFQDYhIaLREIH6/+AIAIYKAAAAUfX/vQFQQ2PNBK0CgfX/4AgAoKB0/CrNBL0BotEQgfL/4AgASiJAM8BWM/2h6/+y0RAaqoHt/+AIAKHo/xwLGqrl9/8tAwYBAAAAIqBjHfAAAAA2QQCioMCBy//gCAAd8AAAbBAAAGgQAABwEAAAdBAAAHgQAAD8ZwBA0JIAQAhoAEA2QSFh+f+B+f8aZkkGGohi0RAMBCwKWQhCZhqB9v/gCABR8f+BzP8aVVgFV7gCBjgArQaByv/gCACB7f9x6f8aiHpRWQhGJgCB6P9Ac8AaiIgIvQFweGPNB60CgcH/4AgAoKB0jMpx3/8MBVJmFnpxBg0AAKX1/3C3IK0B5ev/JfX/zQcQsSBgpiCBtv/gCAB6InpEN7TOgdX/UHTAGoiICIc3o4bv/wAMCqJGbIHQ/xqIoigAgdD/4AgAVur+sab/ogZsGrtlgwD36gz2RQlat6JLABtVhvP/sq/+t5rIZkUIUiYaN7UCV7SooZv/YLYgEKqAgZ3/4AgAZe3/oZb/HAsaqmXj/6Xs/ywKgbz/4AgAHfAAwPw/T0hBSajr/T+I4QtAFOALQAwA9D84QPQ///8AAAAAAQCMgAAAEEAAAABAAAAAwPw/BMD8PxAnAAAUAPQ/8P//AKjr/T8IwPw/sMD9P3xoAEDsZwBAWIYAQGwqBkA4MgZAFCwGQMwsBkBMLAZANIUAQMyQAEB4LgZAMO8FQFiSAEBMggBANsEAId7/DAoiYQhCoACB7v/gCAAh2f8x2v8GAQBCYgBLIjcy9+Xg/wxLosEgJdf/JeD/MeT+IeT+QdL/KiPAIAA5ArHR/yGG/gwMDFpJAoHf/+AIAEHN/1KhAcAgACgELApQIiDAIAApBIF9/+AIAIHY/+AIACHG/8AgACgCzLocxEAiECLC+AwUIKSDDAuB0f/gCADxv//RSP/Bv/+xqP7ioQAMCoHM/+AIACG8/0Gl/iozYtQrDALAIABIAxZ0/8AgAFgDDBTAIAApA0JBEEIFAQwnQkERclEJKVEmlAccN3cUHgYIAEIFA3IFAoBEEXBEIGZEEUglwCAASARJUUYBAAAcJEJRCaXS/wyLosEQ5cj/QgUDcgUCgEQRcEQgcaD/cHD0R7cSoqDA5cP/oqDupcP/5c//Rt//AHIFAQzZl5cChq8AdzlWZmcCBugA9ncgZjcCxoEA9kcIZicCRmcABigAZkcCRpUAZlcCBsQARiQADJmXlwLGpwB3ORBmdwLGxQBmhwKGIADGHQAAAGaXAka3AAy5l5cCRpAABhkAHDmXlwIGUAB3OSpmtwLGXQAcCXc5DAz57QKXlwKGRADGEAAcGZeXAgZlABwkR5cCBnsAhgsAkqDSl5cCxkAAdzkQkqDQlxdbkqDRlxdpxgQAAACSoNOXlwKGVwGSoNSXlwKGVgDtAnKg/0bAACxJ7QJyoMCXFAIGvQApUUKgByCiIKW0/yCiICW0/2XA/2XA/7KgCKLBEAtEZbb/VvT9RiYAAAAMF1Y0LIFk/+AIAKB0g8atAAAAACaEBAwXBqsAQiUCciUDcJQgkJC0Vrn+Jaf/cESAnBoG+P8AoKxBgVj/4AgAVjr9ctfwcKTAzCcGgQAAoID0Vhj+RgQAoKD1gVH/4AgAVir7gTv/gHfAkTr/cKTAdznkxgMAAKCsQYFI/+AIAFY6+XLX8HCkwFan/sZwAHKgwCaEAoaMAO0CDAfGigAmtPXGYwByoAEmtAKGhgCyJQOiJQJlrf8GCQAAcqABJrQCBoEAkSb/QiUEIOIgcqDCR7kCBn0AuFWoJQwX5aD/oHKDxngADBlmtCxIRaEc/+0CcqDCR7oCBnQAeDW4VaglcHSCmeFlnv9B/f2Y4SlkQtQreSSgkoN9CQZrAJH4/e0CogkAcqDGFgoaeFmYJULE8ECZwKKgwJB6kwwKkqDvhgIAAKq1sgsYG6qwmTBHKvKiBQVCBQSAqhFAqiBCBQbtAgBEEaCkIEIFB4BEAaBEIECZwEKgwZB0k4ZTAEHg/e0CkgQAcqDGFgkUmDRyoMhWiROSRAB4VAZMAAAcie0CDBeXFALGSADoZfh12FXIRbg1qCWB+P7gCADtCqByg0ZCAAwXJkQCxj8AqCW9AoHw/uAIAAYfAABAoDTtAnKgwFaKDkC0QYuVTQp8/IYOAACoOZnhucHJ0YHr/uAIAJjhuMF4KagZ2AmgpxDCIQ0mBw7AIADiLQBwfDDgdxBwqiDAIACpDRtEkskQtzTCBpr/ZkQChpj/7QJyoMBGIwAMFya0AsYgAEHH/phVeCWZBEHG/nkEfQIGHACxwv4MF8gLQsTwnQJAl5PAcpNwmRDtAnKgxlZZBYG8/nKgydgIRz1KQKAUcqDAVhoEfQoMH0YCAHqVmGlLd5kKnQ9w7cB6rEc37RYp36kL6QjGev8MF2aEF0Gt/ngEjBdyoMgpBAwaQan+cKKDKQR9Cu0CcKB04mEMZYX/4iEM4KB05YT/JZH/Vge5QgUBcqAPdxRARzcUZkQCRnkAZmQCxn8AJjQChtz+hh8AHCd3lAKGcwBHNwscF3eUAgY6AEbW/gByoNJ3FE9yoNR3FHNG0v4AAACYNaGP/lglmeGBm/7gCABBjP6Bjf7AIABIBJjhQHQ1wEQRgEQQQEcgkESCrQJQtMKBkv7gCACio+iBj/7gCAAGwf4AANIlBcIlBLIlA6glJYr/Rrz+ALIFA0IFAoC7EUC7ILLL8KLFGGVq/wa2/kIFA3IFAoBEEXBEIHFW/ULE8Jg3kERjFuSrmBealJCcQQYCAJJhDqVU/5IhDqInBKYaBKgnp6nrpUz/Fpr/oicBQMQgssUYgXL+4AgAFkoAgqDEiVeIF0qIiReIN0BIwEk3xpz+ggUDcgUCgIgRcIggQsUYgsjwDBUGIAAAkVf+cVn9WAmJcVB3wHlheCYMGne4AQw6idGZ4anBZU3/qMFxUP6pAaFP/u0FvQTywRjdB8LBHIFY/uAIAF0KuCaocYjRmOGgu8C5JqCIwLgJqkSoYQweqrutAlCug7kJoKB0cLvAzHrS24DQroMW6gCtB4nRmeGlWv+Y4YjReQmRGf14OYyoUJ8xUJnA1ikAVsf21qUAURT9QqDHSVVGAACMNZwHxmz+FgebgQ/9QqDISVhGaf4AkQz9QqDJSVlGZv4ASCVWNJmtAoE0/uAIAKEg/oEu/uAIAIEx/uAIAEZe/gBINRY0l60CgSz+4AgAoqPogSb+4AgA4AQABlf+HfAAADZBAJ0CgqDAKAOHmQ/MMgwShgcADAIpA3zihg4AJhIHJiIWhgMAAACCoNuAKSOHmSYMIikDfPJGBwAioNwnmQgMEikDLQiGAwCCoN188oeZBgwSKQMioNsd8AAA",
"text_start": 1074520064,
"data": "CMD8Pw==",
"data_start": 1073605544
}
7 changes: 7 additions & 0 deletions esptool/targets/stub_flasher/stub_flasher_32c2.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"entry": 1077413328,
"text": "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",
"text_start": 1077411840,
"data": "DEDKPw==",
"data_start": 1070295976
}
Loading

0 comments on commit 94f29a5

Please sign in to comment.