1# This file includes the operations with eFuses for ESP32 chip
2#
3# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
4#
5# SPDX-License-Identifier: GPL-2.0-or-later
6
7import argparse
8import os  # noqa: F401. It is used in IDF scripts
9import traceback
10
11import espsecure
12
13import esptool
14
15from . import fields
16from .. import util
17from ..base_operations import (
18    add_common_commands,
19    add_force_write_always,
20    add_show_sensitive_info_option,
21    burn_bit,
22    burn_block_data,
23    burn_efuse,
24    check_error,
25    dump,
26    read_protect_efuse,
27    summary,
28    write_protect_efuse,
29)
30
31
32def add_commands(subparsers, efuses):
33    add_common_commands(subparsers, efuses)
34    p = subparsers.add_parser(
35        "burn_key",
36        help="Burn a 256-bit key to EFUSE: %s" % ", ".join(efuses.BLOCKS_FOR_KEYS),
37    )
38    p.add_argument(
39        "--no-protect-key",
40        help="Disable default read- and write-protecting of the key. "
41        "If this option is not set, once the key is flashed "
42        "it cannot be read back or changed.",
43        action="store_true",
44    )
45    add_force_write_always(p)
46    add_show_sensitive_info_option(p)
47    p.add_argument(
48        "block",
49        help='Key block to burn. "flash_encryption" (block1), '
50        '"secure_boot_v1" (block2), "secure_boot_v2" (block2)',
51        action="append",
52        choices=efuses.BLOCKS_FOR_KEYS,
53    )
54    p.add_argument(
55        "keyfile",
56        help="File containing 256 bits of binary key data",
57        action="append",
58        type=argparse.FileType("rb"),
59    )
60    for _ in efuses.BLOCKS_FOR_KEYS:
61        p.add_argument(
62            "block",
63            help='Key block to burn. "flash_encryption" (block1), '
64            '"secure_boot_v1" (block2), "secure_boot_v2" (block2)',
65            metavar="BLOCK",
66            nargs="?",
67            action="append",
68            choices=efuses.BLOCKS_FOR_KEYS,
69        )
70        p.add_argument(
71            "keyfile",
72            help="File containing 256 bits of binary key data",
73            metavar="KEYFILE",
74            nargs="?",
75            action="append",
76            type=argparse.FileType("rb"),
77        )
78
79    burn_key_digest = subparsers.add_parser(
80        "burn_key_digest",
81        help="Parse a RSA public key and burn the digest "
82        "to eFuse for use with Secure Boot V2",
83    )
84    burn_key_digest.add_argument(
85        "keyfile", help="Key file to digest (PEM format)", type=argparse.FileType("rb")
86    )
87    burn_key_digest.add_argument(
88        "--no-protect-key",
89        help="Disable default write-protecting of the key digest. "
90        "If this option is not set, once the key is flashed it cannot be changed.",
91        action="store_true",
92    )
93    add_force_write_always(burn_key_digest)
94    add_show_sensitive_info_option(burn_key_digest)
95
96    p = subparsers.add_parser(
97        "set_flash_voltage",
98        help="Permanently set the internal flash voltage regulator "
99        "to either 1.8V, 3.3V or OFF. This means GPIO12 can be high or low at reset "
100        "without changing the flash voltage.",
101    )
102    p.add_argument("voltage", help="Voltage selection", choices=["1.8V", "3.3V", "OFF"])
103
104    p = subparsers.add_parser(
105        "burn_custom_mac", help="Burn a 48-bit Custom MAC Address to EFUSE BLOCK3."
106    )
107    p.add_argument(
108        "mac",
109        help="Custom MAC Address to burn given in hexadecimal format "
110        "with bytes separated by colons "
111        "(e.g. AA:CD:EF:01:02:03).",
112        type=fields.base_fields.CheckArgValue(efuses, "CUSTOM_MAC"),
113    )
114    add_force_write_always(p)
115
116    p = subparsers.add_parser("get_custom_mac", help="Prints the Custom MAC Address.")
117
118
119def burn_custom_mac(esp, efuses, args):
120    # Writing to BLK3:
121    #  - MAC_VERSION = 1
122    #  - CUSTOM_MAC = AA:CD:EF:01:02:03
123    #  - CUSTOM_MAC_CRC = crc8(CUSTOM_MAC)
124    efuses["CUSTOM_MAC"].save(args.mac)
125    if not efuses.burn_all(check_batch_mode=True):
126        return
127    get_custom_mac(esp, efuses, args)
128    print("Successful")
129
130
131def get_custom_mac(esp, efuses, args):
132    version = efuses["MAC_VERSION"].get()
133    if version > 0:
134        print(
135            "Custom MAC Address version {}: {}".format(
136                version, efuses["CUSTOM_MAC"].get()
137            )
138        )
139    else:
140        print("Custom MAC Address is not set in the device.")
141
142
143def set_flash_voltage(esp, efuses, args):
144    sdio_force = efuses["XPD_SDIO_FORCE"]
145    sdio_tieh = efuses["XPD_SDIO_TIEH"]
146    sdio_reg = efuses["XPD_SDIO_REG"]
147
148    # check efuses aren't burned in a way which makes this impossible
149    if args.voltage == "OFF" and sdio_reg.get() != 0:
150        raise esptool.FatalError(
151            "Can't set flash regulator to OFF as XPD_SDIO_REG efuse is already burned"
152        )
153
154    if args.voltage == "1.8V" and sdio_tieh.get() != 0:
155        raise esptool.FatalError(
156            "Can't set regulator to 1.8V is XPD_SDIO_TIEH efuse is already burned"
157        )
158
159    if args.voltage == "OFF":
160        msg = "Disable internal flash voltage regulator (VDD_SDIO). "
161        "SPI flash will need to be powered from an external source.\n"
162        "The following efuse is burned: XPD_SDIO_FORCE.\n"
163        "It is possible to later re-enable the internal regulator (%s) " % (
164            "to 3.3V" if sdio_tieh.get() != 0 else "to 1.8V or 3.3V"
165        )
166        "by burning an additional efuse"
167    elif args.voltage == "1.8V":
168        msg = "Set internal flash voltage regulator (VDD_SDIO) to 1.8V.\n"
169        "The following efuses are burned: XPD_SDIO_FORCE, XPD_SDIO_REG.\n"
170        "It is possible to later increase the voltage to 3.3V (permanently) "
171        "by burning additional efuse XPD_SDIO_TIEH"
172    elif args.voltage == "3.3V":
173        msg = "Enable internal flash voltage regulator (VDD_SDIO) to 3.3V.\n"
174        "The following efuses are burned: XPD_SDIO_FORCE, XPD_SDIO_REG, XPD_SDIO_TIEH."
175    print(msg)
176    sdio_force.save(1)  # Disable GPIO12
177    if args.voltage != "OFF":
178        sdio_reg.save(1)  # Enable internal regulator
179    if args.voltage == "3.3V":
180        sdio_tieh.save(1)
181    print("VDD_SDIO setting complete.")
182    if not efuses.burn_all(check_batch_mode=True):
183        return
184    print("Successful")
185
186
187def adc_info(esp, efuses, args):
188    adc_vref = efuses["ADC_VREF"]
189    blk3_reserve = efuses["BLK3_PART_RESERVE"]
190
191    vref_raw = adc_vref.get_raw()
192    if vref_raw == 0:
193        print("ADC VRef calibration: None (1100mV nominal)")
194    else:
195        print("ADC VRef calibration: %dmV" % adc_vref.get())
196
197    if blk3_reserve.get():
198        print("ADC readings stored in efuse BLOCK3:")
199        print("    ADC1 Low reading  (150mV): %d" % efuses["ADC1_TP_LOW"].get())
200        print("    ADC1 High reading (850mV): %d" % efuses["ADC1_TP_HIGH"].get())
201        print("    ADC2 Low reading  (150mV): %d" % efuses["ADC2_TP_LOW"].get())
202        print("    ADC2 High reading (850mV): %d" % efuses["ADC2_TP_HIGH"].get())
203
204
205def burn_key(esp, efuses, args):
206    datafile_list = args.keyfile[
207        0 : len([keyfile for keyfile in args.keyfile if keyfile is not None]) :
208    ]
209    block_name_list = args.block[
210        0 : len([block for block in args.block if block is not None]) :
211    ]
212    efuses.force_write_always = args.force_write_always
213    no_protect_key = args.no_protect_key
214
215    util.check_duplicate_name_in_list(block_name_list)
216    if len(block_name_list) != len(datafile_list):
217        raise esptool.FatalError(
218            "The number of blocks (%d) and datafile (%d) should be the same."
219            % (len(block_name_list), len(datafile_list))
220        )
221
222    print("Burn keys to blocks:")
223    for block_name, datafile in zip(block_name_list, datafile_list):
224        efuse = None
225        for block in efuses.blocks:
226            if block_name == block.name or block_name in block.alias:
227                efuse = efuses[block.name]
228        if efuse is None:
229            raise esptool.FatalError("Unknown block name - %s" % (block_name))
230        num_bytes = efuse.bit_len // 8
231        data = datafile.read()
232        revers_msg = None
233        if block_name in ("flash_encryption", "secure_boot_v1"):
234            revers_msg = "\tReversing the byte order"
235            data = data[::-1]
236        print(" - %s" % (efuse.name), end=" ")
237        print(
238            "-> [{}]".format(
239                util.hexify(data, " ")
240                if args.show_sensitive_info
241                else " ".join(["??"] * len(data))
242            )
243        )
244        if revers_msg:
245            print(revers_msg)
246        if len(data) != num_bytes:
247            raise esptool.FatalError(
248                "Incorrect key file size %d. "
249                "Key file must be %d bytes (%d bits) of raw binary key data."
250                % (len(data), num_bytes, num_bytes * 8)
251            )
252
253        efuse.save(data)
254
255        if block_name in ("flash_encryption", "secure_boot_v1"):
256            if not no_protect_key:
257                print("\tDisabling read to key block")
258                efuse.disable_read()
259
260        if not no_protect_key:
261            print("\tDisabling write to key block")
262            efuse.disable_write()
263        print("")
264
265    if args.no_protect_key:
266        print("Key is left unprotected as per --no-protect-key argument.")
267
268    msg = "Burn keys in efuse blocks.\n"
269    if no_protect_key:
270        msg += (
271            "The key block will left readable and writeable (due to --no-protect-key)"
272        )
273    else:
274        msg += "The key block will be read and write protected "
275        "(no further changes or readback)"
276    print(msg, "\n")
277    if not efuses.burn_all(check_batch_mode=True):
278        return
279    print("Successful")
280
281
282def burn_key_digest(esp, efuses, args):
283    if efuses.coding_scheme == efuses.REGS.CODING_SCHEME_34:
284        raise esptool.FatalError("burn_key_digest only works with 'None' coding scheme")
285
286    chip_revision = esp.get_chip_revision()
287    if chip_revision < 300:
288        raise esptool.FatalError(
289            "Incorrect chip revision for Secure boot v2. "
290            "Detected: v%d.%d. Expected: >= v3.0"
291            % (chip_revision / 100, chip_revision % 100)
292        )
293
294    digest = espsecure._digest_sbv2_public_key(args.keyfile)
295    efuse = efuses["BLOCK2"]
296    num_bytes = efuse.bit_len // 8
297    if len(digest) != num_bytes:
298        raise esptool.FatalError(
299            "Incorrect digest size %d. "
300            "Digest must be %d bytes (%d bits) of raw binary key data."
301            % (len(digest), num_bytes, num_bytes * 8)
302        )
303    print(" - %s" % (efuse.name), end=" ")
304    print(
305        "-> [{}]".format(
306            util.hexify(digest, " ")
307            if args.show_sensitive_info
308            else " ".join(["??"] * len(digest))
309        )
310    )
311
312    efuse.save(digest)
313    if not args.no_protect_key:
314        print("Disabling write to efuse %s..." % (efuse.name))
315        efuse.disable_write()
316
317    if not efuses.burn_all(check_batch_mode=True):
318        return
319    print("Successful")
320
321
322def espefuse(esp, efuses, args, command):
323    parser = argparse.ArgumentParser()
324    subparsers = parser.add_subparsers(dest="operation")
325    add_commands(subparsers, efuses)
326    try:
327        cmd_line_args = parser.parse_args(command.split())
328    except SystemExit:
329        traceback.print_stack()
330        raise esptool.FatalError('"{}" - incorrect command'.format(command))
331    if cmd_line_args.operation == "execute_scripts":
332        configfiles = cmd_line_args.configfiles
333        index = cmd_line_args.index
334    # copy arguments from args to cmd_line_args
335    vars(cmd_line_args).update(vars(args))
336    if cmd_line_args.operation == "execute_scripts":
337        cmd_line_args.configfiles = configfiles
338        cmd_line_args.index = index
339    if cmd_line_args.operation is None:
340        parser.print_help()
341        parser.exit(1)
342    operation_func = globals()[cmd_line_args.operation]
343    # each 'operation' is a module-level function of the same name
344    operation_func(esp, efuses, cmd_line_args)
345
346
347def execute_scripts(esp, efuses, args):
348    efuses.batch_mode_cnt += 1
349    del args.operation
350    scripts = args.scripts
351    del args.scripts
352
353    for file in scripts:
354        with open(file.name, "r") as file:
355            exec(compile(file.read(), file.name, "exec"))
356
357    if args.debug:
358        for block in efuses.blocks:
359            data = block.get_bitstring(from_read=False)
360            block.print_block(data, "regs_for_burn", args.debug)
361
362    efuses.batch_mode_cnt -= 1
363    if not efuses.burn_all(check_batch_mode=True):
364        return
365    print("Successful")
366