1# SPDX-FileCopyrightText: 2014-2022 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 struct
7import sys
8
9
10def byte(bitstr, index):
11    return bitstr[index]
12
13
14def mask_to_shift(mask):
15    """Return the index of the least significant bit in the mask"""
16    shift = 0
17    while mask & 0x1 == 0:
18        shift += 1
19        mask >>= 1
20    return shift
21
22
23def div_roundup(a, b):
24    """Return a/b rounded up to nearest integer,
25    equivalent result to int(math.ceil(float(int(a)) / float(int(b))), only
26    without possible floating point accuracy errors.
27    """
28    return (int(a) + int(b) - 1) // int(b)
29
30
31def flash_size_bytes(size):
32    """Given a flash size of the type passed in args.flash_size
33    (ie 512KB or 1MB) then return the size in bytes.
34    """
35    if "MB" in size:
36        return int(size[: size.index("MB")]) * 1024 * 1024
37    elif "KB" in size:
38        return int(size[: size.index("KB")]) * 1024
39    else:
40        raise FatalError("Unknown size %s" % size)
41
42
43def hexify(s, uppercase=True):
44    format_str = "%02X" if uppercase else "%02x"
45    return "".join(format_str % c for c in s)
46
47
48def pad_to(data, alignment, pad_character=b"\xFF"):
49    """Pad to the next alignment boundary"""
50    pad_mod = len(data) % alignment
51    if pad_mod != 0:
52        data += pad_character * (alignment - pad_mod)
53    return data
54
55
56def print_overwrite(message, last_line=False):
57    """Print a message, overwriting the currently printed line.
58
59    If last_line is False, don't append a newline at the end
60    (expecting another subsequent call will overwrite this one.)
61
62    After a sequence of calls with last_line=False, call once with last_line=True.
63
64    If output is not a TTY (for example redirected a pipe),
65    no overwriting happens and this function is the same as print().
66    """
67    if sys.stdout.isatty():
68        print("\r%s" % message, end="\n" if last_line else "")
69    else:
70        print(message)
71
72
73class FatalError(RuntimeError):
74    """
75    Wrapper class for runtime errors that aren't caused by internal bugs, but by
76    ESP ROM responses or input content.
77    """
78
79    def __init__(self, message):
80        RuntimeError.__init__(self, message)
81
82    @staticmethod
83    def WithResult(message, result):
84        """
85        Return a fatal error object that appends the hex values of
86        'result' and its meaning as a string formatted argument.
87        """
88
89        err_defs = {
90            # ROM error codes
91            0x101: "Out of memory",
92            0x102: "Invalid argument",
93            0x103: "Invalid state",
94            0x104: "Invalid size",
95            0x105: "Requested resource not found",
96            0x106: "Operation or feature not supported",
97            0x107: "Operation timed out",
98            0x108: "Received response was invalid",
99            0x109: "CRC or checksum was invalid",
100            0x10A: "Version was invalid",
101            0x10B: "MAC address was invalid",
102            # Flasher stub error codes
103            0xC000: "Bad data length",
104            0xC100: "Bad data checksum",
105            0xC200: "Bad blocksize",
106            0xC300: "Invalid command",
107            0xC400: "Failed SPI operation",
108            0xC500: "Failed SPI unlock",
109            0xC600: "Not in flash mode",
110            0xC700: "Inflate error",
111            0xC800: "Not enough data",
112            0xC900: "Too much data",
113            0xFF00: "Command not implemented",
114        }
115
116        err_code = struct.unpack(">H", result[:2])
117        message += " (result was {}: {})".format(
118            hexify(result), err_defs.get(err_code[0], "Unknown result")
119        )
120        return FatalError(message)
121
122
123class NotImplementedInROMError(FatalError):
124    """
125    Wrapper class for the error thrown when a particular ESP bootloader function
126    is not implemented in the ROM bootloader.
127    """
128
129    def __init__(self, bootloader, func):
130        FatalError.__init__(
131            self,
132            "%s ROM does not support function %s."
133            % (bootloader.CHIP_NAME, func.__name__),
134        )
135
136
137class NotSupportedError(FatalError):
138    def __init__(self, esp, function_name):
139        FatalError.__init__(
140            self,
141            "Function %s is not supported for %s." % (function_name, esp.CHIP_NAME),
142        )
143
144
145class UnsupportedCommandError(RuntimeError):
146    """
147    Wrapper class for when ROM loader returns an invalid command response.
148
149    Usually this indicates the loader is running in Secure Download Mode.
150    """
151
152    def __init__(self, esp, op):
153        if esp.secure_download_mode:
154            msg = "This command (0x%x) is not supported in Secure Download Mode" % op
155        else:
156            msg = "Invalid (unsupported) command 0x%x" % op
157        RuntimeError.__init__(self, msg)
158