style: format python files with isort and double-quote-string-fixer

This commit is contained in:
Fu Hanxi
2021-01-26 10:49:01 +08:00
parent dc8402ea61
commit 0146f258d7
276 changed files with 8241 additions and 8162 deletions

View File

@@ -16,23 +16,24 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import print_function, division
from __future__ import division, print_function
import argparse
import os
import sys
import binascii
import tempfile
import collections
import os
import struct
import sys
import tempfile
try:
from parttool import PartitionName, PartitionType, ParttoolTarget, PARTITION_TABLE_OFFSET
from parttool import PARTITION_TABLE_OFFSET, PartitionName, PartitionType, ParttoolTarget
except ImportError:
COMPONENTS_PATH = os.path.expandvars(os.path.join("$IDF_PATH", "components"))
PARTTOOL_DIR = os.path.join(COMPONENTS_PATH, "partition_table")
COMPONENTS_PATH = os.path.expandvars(os.path.join('$IDF_PATH', 'components'))
PARTTOOL_DIR = os.path.join(COMPONENTS_PATH, 'partition_table')
sys.path.append(PARTTOOL_DIR)
from parttool import PartitionName, PartitionType, ParttoolTarget, PARTITION_TABLE_OFFSET
from parttool import PARTITION_TABLE_OFFSET, PartitionName, PartitionType, ParttoolTarget
__version__ = '2.0'
@@ -48,7 +49,7 @@ def status(msg):
class OtatoolTarget():
OTADATA_PARTITION = PartitionType("data", "ota")
OTADATA_PARTITION = PartitionType('data', 'ota')
def __init__(self, port=None, baud=None, partition_table_offset=PARTITION_TABLE_OFFSET, partition_table_file=None,
spi_flash_sec_size=SPI_FLASH_SEC_SIZE, esptool_args=[], esptool_write_args=[],
@@ -61,14 +62,14 @@ class OtatoolTarget():
temp_file.close()
try:
self.target.read_partition(OtatoolTarget.OTADATA_PARTITION, temp_file.name)
with open(temp_file.name, "rb") as f:
with open(temp_file.name, 'rb') as f:
self.otadata = f.read()
finally:
os.unlink(temp_file.name)
def _check_otadata_partition(self):
if not self.otadata:
raise Exception("No otadata partition found")
raise Exception('No otadata partition found')
def erase_otadata(self):
self._check_otadata_partition()
@@ -77,7 +78,7 @@ class OtatoolTarget():
def _get_otadata_info(self):
info = []
otadata_info = collections.namedtuple("otadata_info", "seq crc")
otadata_info = collections.namedtuple('otadata_info', 'seq crc')
for i in range(2):
start = i * (self.spi_flash_sec_size >> 1)
@@ -94,7 +95,7 @@ class OtatoolTarget():
def _get_partition_id_from_ota_id(self, ota_id):
if isinstance(ota_id, int):
return PartitionType("app", "ota_" + str(ota_id))
return PartitionType('app', 'ota_' + str(ota_id))
else:
return PartitionName(ota_id)
@@ -106,7 +107,7 @@ class OtatoolTarget():
def is_otadata_info_valid(status):
seq = status.seq % (1 << 32)
crc = hex(binascii.crc32(struct.pack("I", seq), 0xFFFFFFFF) % (1 << 32))
crc = hex(binascii.crc32(struct.pack('I', seq), 0xFFFFFFFF) % (1 << 32))
return seq < (int('0xFFFFFFFF', 16) % (1 << 32)) and status.crc == crc
partition_table = self.target.partition_table
@@ -124,7 +125,7 @@ class OtatoolTarget():
ota_partitions = sorted(ota_partitions, key=lambda p: p.subtype)
if not ota_partitions:
raise Exception("No ota app partitions found")
raise Exception('No ota app partitions found')
# Look for the app partition to switch to
ota_partition_next = None
@@ -137,7 +138,7 @@ class OtatoolTarget():
ota_partition_next = list(ota_partition_next)[0]
except IndexError:
raise Exception("Partition to switch to not found")
raise Exception('Partition to switch to not found')
otadata_info = self._get_otadata_info()
@@ -177,15 +178,15 @@ class OtatoolTarget():
ota_seq_next = target_seq
# Create binary data from computed values
ota_seq_next = struct.pack("I", ota_seq_next)
ota_seq_next = struct.pack('I', ota_seq_next)
ota_seq_crc_next = binascii.crc32(ota_seq_next, 0xFFFFFFFF) % (1 << 32)
ota_seq_crc_next = struct.pack("I", ota_seq_crc_next)
ota_seq_crc_next = struct.pack('I', ota_seq_crc_next)
temp_file = tempfile.NamedTemporaryFile(delete=False)
temp_file.close()
try:
with open(temp_file.name, "wb") as otadata_next_file:
with open(temp_file.name, 'wb') as otadata_next_file:
start = (1 if otadata_compute_base == 0 else 0) * (self.spi_flash_sec_size >> 1)
otadata_next_file.write(self.otadata)
@@ -217,14 +218,14 @@ def _read_otadata(target):
otadata_info = target._get_otadata_info()
print(" {:8s} \t {:8s} | \t {:8s} \t {:8s}".format("OTA_SEQ", "CRC", "OTA_SEQ", "CRC"))
print("Firmware: 0x{:8x} \t0x{:8x} | \t0x{:8x} \t 0x{:8x}".format(otadata_info[0].seq, otadata_info[0].crc,
print(' {:8s} \t {:8s} | \t {:8s} \t {:8s}'.format('OTA_SEQ', 'CRC', 'OTA_SEQ', 'CRC'))
print('Firmware: 0x{:8x} \t0x{:8x} | \t0x{:8x} \t 0x{:8x}'.format(otadata_info[0].seq, otadata_info[0].crc,
otadata_info[1].seq, otadata_info[1].crc))
def _erase_otadata(target):
target.erase_otadata()
status("Erased ota_data partition contents")
status('Erased ota_data partition contents')
def _switch_ota_partition(target, ota_id):
@@ -233,68 +234,68 @@ def _switch_ota_partition(target, ota_id):
def _read_ota_partition(target, ota_id, output):
target.read_ota_partition(ota_id, output)
status("Read ota partition contents to file {}".format(output))
status('Read ota partition contents to file {}'.format(output))
def _write_ota_partition(target, ota_id, input):
target.write_ota_partition(ota_id, input)
status("Written contents of file {} to ota partition".format(input))
status('Written contents of file {} to ota partition'.format(input))
def _erase_ota_partition(target, ota_id):
target.erase_ota_partition(ota_id)
status("Erased contents of ota partition")
status('Erased contents of ota partition')
def main():
if sys.version_info[0] < 3:
print("WARNING: Support for Python 2 is deprecated and will be removed in future versions.", file=sys.stderr)
print('WARNING: Support for Python 2 is deprecated and will be removed in future versions.', file=sys.stderr)
elif sys.version_info[0] == 3 and sys.version_info[1] < 6:
print("WARNING: Python 3 versions older than 3.6 are not supported.", file=sys.stderr)
print('WARNING: Python 3 versions older than 3.6 are not supported.', file=sys.stderr)
global quiet
parser = argparse.ArgumentParser("ESP-IDF OTA Partitions Tool")
parser = argparse.ArgumentParser('ESP-IDF OTA Partitions Tool')
parser.add_argument("--quiet", "-q", help="suppress stderr messages", action="store_true")
parser.add_argument("--esptool-args", help="additional main arguments for esptool", nargs="+")
parser.add_argument("--esptool-write-args", help="additional subcommand arguments for esptool write_flash", nargs="+")
parser.add_argument("--esptool-read-args", help="additional subcommand arguments for esptool read_flash", nargs="+")
parser.add_argument("--esptool-erase-args", help="additional subcommand arguments for esptool erase_region", nargs="+")
parser.add_argument('--quiet', '-q', help='suppress stderr messages', action='store_true')
parser.add_argument('--esptool-args', help='additional main arguments for esptool', nargs='+')
parser.add_argument('--esptool-write-args', help='additional subcommand arguments for esptool write_flash', nargs='+')
parser.add_argument('--esptool-read-args', help='additional subcommand arguments for esptool read_flash', nargs='+')
parser.add_argument('--esptool-erase-args', help='additional subcommand arguments for esptool erase_region', nargs='+')
# There are two possible sources for the partition table: a device attached to the host
# or a partition table CSV/binary file. These sources are mutually exclusive.
parser.add_argument("--port", "-p", help="port where the device to read the partition table from is attached")
parser.add_argument('--port', '-p', help='port where the device to read the partition table from is attached')
parser.add_argument("--baud", "-b", help="baudrate to use", type=int)
parser.add_argument('--baud', '-b', help='baudrate to use', type=int)
parser.add_argument("--partition-table-offset", "-o", help="offset to read the partition table from", type=str)
parser.add_argument('--partition-table-offset', '-o', help='offset to read the partition table from', type=str)
parser.add_argument("--partition-table-file", "-f", help="file (CSV/binary) to read the partition table from; \
overrides device attached to specified port as the partition table source when defined")
parser.add_argument('--partition-table-file', '-f', help='file (CSV/binary) to read the partition table from; \
overrides device attached to specified port as the partition table source when defined')
subparsers = parser.add_subparsers(dest="operation", help="run otatool -h for additional help")
subparsers = parser.add_subparsers(dest='operation', help='run otatool -h for additional help')
spi_flash_sec_size = argparse.ArgumentParser(add_help=False)
spi_flash_sec_size.add_argument("--spi-flash-sec-size", help="value of SPI_FLASH_SEC_SIZE macro", type=str)
spi_flash_sec_size.add_argument('--spi-flash-sec-size', help='value of SPI_FLASH_SEC_SIZE macro', type=str)
# Specify the supported operations
subparsers.add_parser("read_otadata", help="read otadata partition", parents=[spi_flash_sec_size])
subparsers.add_parser("erase_otadata", help="erase otadata partition")
subparsers.add_parser('read_otadata', help='read otadata partition', parents=[spi_flash_sec_size])
subparsers.add_parser('erase_otadata', help='erase otadata partition')
slot_or_name_parser = argparse.ArgumentParser(add_help=False)
slot_or_name_parser_args = slot_or_name_parser.add_mutually_exclusive_group()
slot_or_name_parser_args.add_argument("--slot", help="slot number of the ota partition", type=int)
slot_or_name_parser_args.add_argument("--name", help="name of the ota partition")
slot_or_name_parser_args.add_argument('--slot', help='slot number of the ota partition', type=int)
slot_or_name_parser_args.add_argument('--name', help='name of the ota partition')
subparsers.add_parser("switch_ota_partition", help="switch otadata partition", parents=[slot_or_name_parser, spi_flash_sec_size])
subparsers.add_parser('switch_ota_partition', help='switch otadata partition', parents=[slot_or_name_parser, spi_flash_sec_size])
read_ota_partition_subparser = subparsers.add_parser("read_ota_partition", help="read contents of an ota partition", parents=[slot_or_name_parser])
read_ota_partition_subparser.add_argument("--output", help="file to write the contents of the ota partition to")
read_ota_partition_subparser = subparsers.add_parser('read_ota_partition', help='read contents of an ota partition', parents=[slot_or_name_parser])
read_ota_partition_subparser.add_argument('--output', help='file to write the contents of the ota partition to')
write_ota_partition_subparser = subparsers.add_parser("write_ota_partition", help="write contents to an ota partition", parents=[slot_or_name_parser])
write_ota_partition_subparser.add_argument("--input", help="file whose contents to write to the ota partition")
write_ota_partition_subparser = subparsers.add_parser('write_ota_partition', help='write contents to an ota partition', parents=[slot_or_name_parser])
write_ota_partition_subparser.add_argument('--input', help='file whose contents to write to the ota partition')
subparsers.add_parser("erase_ota_partition", help="erase contents of an ota partition", parents=[slot_or_name_parser])
subparsers.add_parser('erase_ota_partition', help='erase contents of an ota partition', parents=[slot_or_name_parser])
args = parser.parse_args()
@@ -309,34 +310,34 @@ def main():
target_args = {}
if args.port:
target_args["port"] = args.port
target_args['port'] = args.port
if args.partition_table_file:
target_args["partition_table_file"] = args.partition_table_file
target_args['partition_table_file'] = args.partition_table_file
if args.partition_table_offset:
target_args["partition_table_offset"] = int(args.partition_table_offset, 0)
target_args['partition_table_offset'] = int(args.partition_table_offset, 0)
try:
if args.spi_flash_sec_size:
target_args["spi_flash_sec_size"] = int(args.spi_flash_sec_size, 0)
target_args['spi_flash_sec_size'] = int(args.spi_flash_sec_size, 0)
except AttributeError:
pass
if args.esptool_args:
target_args["esptool_args"] = args.esptool_args
target_args['esptool_args'] = args.esptool_args
if args.esptool_write_args:
target_args["esptool_write_args"] = args.esptool_write_args
target_args['esptool_write_args'] = args.esptool_write_args
if args.esptool_read_args:
target_args["esptool_read_args"] = args.esptool_read_args
target_args['esptool_read_args'] = args.esptool_read_args
if args.esptool_erase_args:
target_args["esptool_erase_args"] = args.esptool_erase_args
target_args['esptool_erase_args'] = args.esptool_erase_args
if args.baud:
target_args["baud"] = args.baud
target_args['baud'] = args.baud
target = OtatoolTarget(**target_args)
@@ -347,10 +348,10 @@ def main():
try:
if args.name is not None:
ota_id = ["name"]
ota_id = ['name']
else:
if args.slot is not None:
ota_id = ["slot"]
ota_id = ['slot']
except AttributeError:
pass
@@ -358,8 +359,8 @@ def main():
'read_otadata':(_read_otadata, []),
'erase_otadata':(_erase_otadata, []),
'switch_ota_partition':(_switch_ota_partition, ota_id),
'read_ota_partition':(_read_ota_partition, ["output"] + ota_id),
'write_ota_partition':(_write_ota_partition, ["input"] + ota_id),
'read_ota_partition':(_read_ota_partition, ['output'] + ota_id),
'write_ota_partition':(_write_ota_partition, ['input'] + ota_id),
'erase_ota_partition':(_erase_ota_partition, ota_id)
}

View File

@@ -17,18 +17,19 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import print_function, division
from __future__ import division, print_function
import argparse
import hashlib
import os
import re
import sys
import hashlib
__version__ = '1.0'
quiet = False
max_blk_len = 256
idf_target = "esp32"
idf_target = 'esp32'
copyright = '''// Copyright 2017-2020 Espressif Systems (Shanghai) PTE LTD
//
@@ -61,7 +62,7 @@ def critical(msg):
class FuseTable(list):
def __init__(self):
super(FuseTable, self).__init__(self)
self.md5_digest_table = ""
self.md5_digest_table = ''
@classmethod
def from_csv(cls, csv_contents):
@@ -77,14 +78,14 @@ class FuseTable(list):
for line_no in range(len(lines)):
line = expand_vars(lines[line_no]).strip()
if line.startswith("#") or len(line) == 0:
if line.startswith('#') or len(line) == 0:
continue
try:
res.append(FuseDefinition.from_csv(line))
except InputError as e:
raise InputError("Error at line %d: %s" % (line_no + 1, e))
raise InputError('Error at line %d: %s' % (line_no + 1, e))
except Exception:
critical("Unexpected error parsing line %d: %s" % (line_no + 1, line))
critical('Unexpected error parsing line %d: %s' % (line_no + 1, line))
raise
# fix up missing bit_start
@@ -102,9 +103,9 @@ class FuseTable(list):
# fix up missing field_name
last_field = None
for e in res:
if e.field_name == "" and last_field is None:
raise InputError("Error at line %d: %s missing field name" % (line_no + 1, e))
elif e.field_name == "" and last_field is not None:
if e.field_name == '' and last_field is None:
raise InputError('Error at line %d: %s missing field name' % (line_no + 1, e))
elif e.field_name == '' and last_field is not None:
e.field_name = last_field.field_name
last_field = e
@@ -136,12 +137,12 @@ class FuseTable(list):
fl_error = False
for p in self:
field_name = p.field_name + p.group
if field_name != "" and len(duplicates.intersection([field_name])) != 0:
if field_name != '' and len(duplicates.intersection([field_name])) != 0:
fl_error = True
print("Field at %s, %s, %s, %s have dublicate field_name" %
print('Field at %s, %s, %s, %s have dublicate field_name' %
(p.field_name, p.efuse_block, p.bit_start, p.bit_count))
if fl_error is True:
raise InputError("Field names must be unique")
raise InputError('Field names must be unique')
def verify(self, type_table=None):
for p in self:
@@ -153,7 +154,7 @@ class FuseTable(list):
last = None
for p in sorted(self, key=lambda x:(x.efuse_block, x.bit_start)):
if last is not None and last.efuse_block == p.efuse_block and p.bit_start < last.bit_start + last.bit_count:
raise InputError("Field at %s, %s, %s, %s overlaps %s, %s, %s, %s" %
raise InputError('Field at %s, %s, %s, %s overlaps %s, %s, %s, %s' %
(p.field_name, p.efuse_block, p.bit_start, p.bit_count,
last.field_name, last.efuse_block, last.bit_start, last.bit_count))
last = p
@@ -161,7 +162,7 @@ class FuseTable(list):
def calc_md5(self):
txt_table = ''
for p in self:
txt_table += "%s %s %d %s %s" % (p.field_name, p.efuse_block, p.bit_start, str(p.get_bit_count()), p.comment) + "\n"
txt_table += '%s %s %d %s %s' % (p.field_name, p.efuse_block, p.bit_start, str(p.get_bit_count()), p.comment) + '\n'
self.md5_digest_table = hashlib.md5(txt_table.encode('utf-8')).hexdigest()
def show_range_used_bits(self):
@@ -169,9 +170,9 @@ class FuseTable(list):
rows = ''
rows += 'Sorted efuse table:\n'
num = 1
rows += "{0} \t{1:<30} \t{2} \t{3} \t{4}".format("#", "field_name", "efuse_block", "bit_start", "bit_count") + "\n"
rows += '{0} \t{1:<30} \t{2} \t{3} \t{4}'.format('#', 'field_name', 'efuse_block', 'bit_start', 'bit_count') + '\n'
for p in sorted(self, key=lambda x:(x.efuse_block, x.bit_start)):
rows += "{0} \t{1:<30} \t{2} \t{3:^8} \t{4:^8}".format(num, p.field_name, p.efuse_block, p.bit_start, p.bit_count) + "\n"
rows += '{0} \t{1:<30} \t{2} \t{3:^8} \t{4:^8}'.format(num, p.field_name, p.efuse_block, p.bit_start, p.bit_count) + '\n'
num += 1
rows += '\nUsed bits in efuse table:\n'
@@ -204,30 +205,30 @@ class FuseTable(list):
def to_header(self, file_name):
rows = [copyright]
rows += ["#ifdef __cplusplus",
rows += ['#ifdef __cplusplus',
'extern "C" {',
"#endif",
"",
"",
"// md5_digest_table " + self.md5_digest_table,
"// This file was generated from the file " + file_name + ".csv. DO NOT CHANGE THIS FILE MANUALLY.",
"// If you want to change some fields, you need to change " + file_name + ".csv file",
"// then run `efuse_common_table` or `efuse_custom_table` command it will generate this file.",
'#endif',
'',
'',
'// md5_digest_table ' + self.md5_digest_table,
'// This file was generated from the file ' + file_name + '.csv. DO NOT CHANGE THIS FILE MANUALLY.',
'// If you want to change some fields, you need to change ' + file_name + '.csv file',
'// then run `efuse_common_table` or `efuse_custom_table` command it will generate this file.',
"// To show efuse_table run the command 'show_efuse_table'.",
"",
""]
'',
'']
last_field_name = ''
for p in self:
if (p.field_name != last_field_name):
rows += ["extern const esp_efuse_desc_t* " + "ESP_EFUSE_" + p.field_name + "[];"]
rows += ['extern const esp_efuse_desc_t* ' + 'ESP_EFUSE_' + p.field_name + '[];']
last_field_name = p.field_name
rows += ["",
"#ifdef __cplusplus",
"}",
"#endif",
""]
rows += ['',
'#ifdef __cplusplus',
'}',
'#endif',
'']
return '\n'.join(rows)
def to_c_file(self, file_name, debug):
@@ -236,33 +237,33 @@ class FuseTable(list):
'#include "esp_efuse.h"',
'#include <assert.h>',
'#include "' + file_name + '.h"',
"",
"// md5_digest_table " + self.md5_digest_table,
"// This file was generated from the file " + file_name + ".csv. DO NOT CHANGE THIS FILE MANUALLY.",
"// If you want to change some fields, you need to change " + file_name + ".csv file",
"// then run `efuse_common_table` or `efuse_custom_table` command it will generate this file.",
'',
'// md5_digest_table ' + self.md5_digest_table,
'// This file was generated from the file ' + file_name + '.csv. DO NOT CHANGE THIS FILE MANUALLY.',
'// If you want to change some fields, you need to change ' + file_name + '.csv file',
'// then run `efuse_common_table` or `efuse_custom_table` command it will generate this file.',
"// To show efuse_table run the command 'show_efuse_table'."]
rows += [""]
rows += ['']
if idf_target == "esp32":
rows += ["#define MAX_BLK_LEN CONFIG_EFUSE_MAX_BLK_LEN"]
if idf_target == 'esp32':
rows += ['#define MAX_BLK_LEN CONFIG_EFUSE_MAX_BLK_LEN']
rows += [""]
rows += ['']
last_free_bit_blk1 = self.get_str_position_last_free_bit_in_blk("EFUSE_BLK1")
last_free_bit_blk2 = self.get_str_position_last_free_bit_in_blk("EFUSE_BLK2")
last_free_bit_blk3 = self.get_str_position_last_free_bit_in_blk("EFUSE_BLK3")
last_free_bit_blk1 = self.get_str_position_last_free_bit_in_blk('EFUSE_BLK1')
last_free_bit_blk2 = self.get_str_position_last_free_bit_in_blk('EFUSE_BLK2')
last_free_bit_blk3 = self.get_str_position_last_free_bit_in_blk('EFUSE_BLK3')
rows += ["// The last free bit in the block is counted over the entire file."]
rows += ['// The last free bit in the block is counted over the entire file.']
if last_free_bit_blk1 is not None:
rows += ["#define LAST_FREE_BIT_BLK1 " + last_free_bit_blk1]
rows += ['#define LAST_FREE_BIT_BLK1 ' + last_free_bit_blk1]
if last_free_bit_blk2 is not None:
rows += ["#define LAST_FREE_BIT_BLK2 " + last_free_bit_blk2]
rows += ['#define LAST_FREE_BIT_BLK2 ' + last_free_bit_blk2]
if last_free_bit_blk3 is not None:
rows += ["#define LAST_FREE_BIT_BLK3 " + last_free_bit_blk3]
rows += ['#define LAST_FREE_BIT_BLK3 ' + last_free_bit_blk3]
rows += [""]
rows += ['']
if last_free_bit_blk1 is not None:
rows += ['_Static_assert(LAST_FREE_BIT_BLK1 <= MAX_BLK_LEN, "The eFuse table does not match the coding scheme. '
@@ -274,50 +275,50 @@ class FuseTable(list):
rows += ['_Static_assert(LAST_FREE_BIT_BLK3 <= MAX_BLK_LEN, "The eFuse table does not match the coding scheme. '
'Edit the table and restart the efuse_common_table or efuse_custom_table command to regenerate the new files.");']
rows += [""]
rows += ['']
last_name = ''
for p in self:
if (p.field_name != last_name):
if last_name != '':
rows += ["};\n"]
rows += ["static const esp_efuse_desc_t " + p.field_name + "[] = {"]
rows += ['};\n']
rows += ['static const esp_efuse_desc_t ' + p.field_name + '[] = {']
last_name = p.field_name
rows += [p.to_struct(debug) + ","]
rows += ["};\n"]
rows += ["\n\n\n"]
rows += [p.to_struct(debug) + ',']
rows += ['};\n']
rows += ['\n\n\n']
last_name = ''
for p in self:
if (p.field_name != last_name):
if last_name != '':
rows += [" NULL",
"};\n"]
rows += ["const esp_efuse_desc_t* " + "ESP_EFUSE_" + p.field_name + "[] = {"]
rows += [' NULL',
'};\n']
rows += ['const esp_efuse_desc_t* ' + 'ESP_EFUSE_' + p.field_name + '[] = {']
last_name = p.field_name
index = str(0) if str(p.group) == "" else str(p.group)
rows += [" &" + p.field_name + "[" + index + "], \t\t// " + p.comment]
rows += [" NULL",
"};\n"]
index = str(0) if str(p.group) == '' else str(p.group)
rows += [' &' + p.field_name + '[' + index + '], \t\t// ' + p.comment]
rows += [' NULL',
'};\n']
return '\n'.join(rows)
class FuseDefinition(object):
def __init__(self):
self.field_name = ""
self.group = ""
self.efuse_block = ""
self.field_name = ''
self.group = ''
self.efuse_block = ''
self.bit_start = None
self.bit_count = None
self.define = None
self.comment = ""
self.comment = ''
@classmethod
def from_csv(cls, line):
""" Parse a line from the CSV """
line_w_defaults = line + ",,,," # lazy way to support default fields
fields = [f.strip() for f in line_w_defaults.split(",")]
line_w_defaults = line + ',,,,' # lazy way to support default fields
fields = [f.strip() for f in line_w_defaults.split(',')]
res = FuseDefinition()
res.field_name = fields[0]
@@ -330,12 +331,12 @@ class FuseDefinition(object):
return res
def parse_num(self, strval):
if strval == "":
if strval == '':
return None # Field will fill in default
return self.parse_int(strval)
def parse_bit_count(self, strval):
if strval == "MAX_BLK_LEN":
if strval == 'MAX_BLK_LEN':
self.define = strval
return self.get_max_bits_of_block()
else:
@@ -345,18 +346,18 @@ class FuseDefinition(object):
try:
return int(v, 0)
except ValueError:
raise InputError("Invalid field value %s" % v)
raise InputError('Invalid field value %s' % v)
def parse_block(self, strval):
if strval == "":
if strval == '':
raise InputError("Field 'efuse_block' can't be left empty.")
if idf_target == "esp32":
if strval not in ["EFUSE_BLK0", "EFUSE_BLK1", "EFUSE_BLK2", "EFUSE_BLK3"]:
if idf_target == 'esp32':
if strval not in ['EFUSE_BLK0', 'EFUSE_BLK1', 'EFUSE_BLK2', 'EFUSE_BLK3']:
raise InputError("Field 'efuse_block' should be one of EFUSE_BLK0..EFUSE_BLK3")
else:
if strval not in ["EFUSE_BLK0", "EFUSE_BLK1", "EFUSE_BLK2", "EFUSE_BLK3", "EFUSE_BLK4",
"EFUSE_BLK5", "EFUSE_BLK6", "EFUSE_BLK7", "EFUSE_BLK8", "EFUSE_BLK9",
"EFUSE_BLK10"]:
if strval not in ['EFUSE_BLK0', 'EFUSE_BLK1', 'EFUSE_BLK2', 'EFUSE_BLK3', 'EFUSE_BLK4',
'EFUSE_BLK5', 'EFUSE_BLK6', 'EFUSE_BLK7', 'EFUSE_BLK8', 'EFUSE_BLK9',
'EFUSE_BLK10']:
raise InputError("Field 'efuse_block' should be one of EFUSE_BLK0..EFUSE_BLK10")
return strval
@@ -365,32 +366,32 @@ class FuseDefinition(object):
'''common_table: EFUSE_BLK0, EFUSE_BLK1, EFUSE_BLK2, EFUSE_BLK3
custom_table: ----------, ----------, ----------, EFUSE_BLK3(some reserved in common_table)
'''
if self.efuse_block == "EFUSE_BLK0":
if self.efuse_block == 'EFUSE_BLK0':
return 256
else:
return max_blk_len
def verify(self, type_table):
if self.efuse_block is None:
raise ValidationError(self, "efuse_block field is not set")
raise ValidationError(self, 'efuse_block field is not set')
if self.bit_count is None:
raise ValidationError(self, "bit_count field is not set")
raise ValidationError(self, 'bit_count field is not set')
if type_table is not None:
if type_table == "custom_table":
if self.efuse_block != "EFUSE_BLK3":
raise ValidationError(self, "custom_table should use only EFUSE_BLK3")
if type_table == 'custom_table':
if self.efuse_block != 'EFUSE_BLK3':
raise ValidationError(self, 'custom_table should use only EFUSE_BLK3')
max_bits = self.get_max_bits_of_block()
if self.bit_start + self.bit_count > max_bits:
raise ValidationError(self, "The field is outside the boundaries(max_bits = %d) of the %s block" % (max_bits, self.efuse_block))
raise ValidationError(self, 'The field is outside the boundaries(max_bits = %d) of the %s block' % (max_bits, self.efuse_block))
def get_full_name(self):
def get_postfix(group):
postfix = ""
if group != "":
postfix = "_PART_" + group
postfix = ''
if group != '':
postfix = '_PART_' + group
return postfix
return self.field_name + get_postfix(self.group)
@@ -402,19 +403,19 @@ class FuseDefinition(object):
return self.bit_count
def to_struct(self, debug):
start = " {"
start = ' {'
if debug is True:
start = " {" + '"' + self.field_name + '" ,'
return ", ".join([start + self.efuse_block,
start = ' {' + '"' + self.field_name + '" ,'
return ', '.join([start + self.efuse_block,
str(self.bit_start),
str(self.get_bit_count()) + "}, \t // " + self.comment])
str(self.get_bit_count()) + '}, \t // ' + self.comment])
def process_input_file(file, type_table):
status("Parsing efuse CSV input file " + file.name + " ...")
status('Parsing efuse CSV input file ' + file.name + ' ...')
input = file.read()
table = FuseTable.from_csv(input)
status("Verifying efuse table...")
status('Verifying efuse table...')
table.verify(type_table)
return table
@@ -432,35 +433,35 @@ def create_output_files(name, output_table, debug):
file_name = os.path.splitext(os.path.basename(name))[0]
gen_dir = os.path.dirname(name)
dir_for_file_h = gen_dir + "/include"
dir_for_file_h = gen_dir + '/include'
try:
os.stat(dir_for_file_h)
except Exception:
os.mkdir(dir_for_file_h)
file_h_path = os.path.join(dir_for_file_h, file_name + ".h")
file_c_path = os.path.join(gen_dir, file_name + ".c")
file_h_path = os.path.join(dir_for_file_h, file_name + '.h')
file_c_path = os.path.join(gen_dir, file_name + '.c')
# src files are the same
if ckeck_md5_in_file(output_table.md5_digest_table, file_c_path) is False:
status("Creating efuse *.h file " + file_h_path + " ...")
status('Creating efuse *.h file ' + file_h_path + ' ...')
output = output_table.to_header(file_name)
with open(file_h_path, 'w') as f:
f.write(output)
status("Creating efuse *.c file " + file_c_path + " ...")
status('Creating efuse *.c file ' + file_c_path + ' ...')
output = output_table.to_c_file(file_name, debug)
with open(file_c_path, 'w') as f:
f.write(output)
else:
print("Source files do not require updating correspond to csv file.")
print('Source files do not require updating correspond to csv file.')
def main():
if sys.version_info[0] < 3:
print("WARNING: Support for Python 2 is deprecated and will be removed in future versions.", file=sys.stderr)
print('WARNING: Support for Python 2 is deprecated and will be removed in future versions.', file=sys.stderr)
elif sys.version_info[0] == 3 and sys.version_info[1] < 6:
print("WARNING: Python 3 versions older than 3.6 are not supported.", file=sys.stderr)
print('WARNING: Python 3 versions older than 3.6 are not supported.', file=sys.stderr)
global quiet
global max_blk_len
global idf_target
@@ -468,8 +469,8 @@ def main():
parser = argparse.ArgumentParser(description='ESP32 eFuse Manager')
parser.add_argument('--idf_target', '-t', help='Target chip type', choices=['esp32', 'esp32s2', 'esp32s3', 'esp32c3'], default='esp32')
parser.add_argument('--quiet', '-q', help="Don't print non-critical status messages to stderr", action='store_true')
parser.add_argument('--debug', help='Create header file with debug info', default=False, action="store_false")
parser.add_argument('--info', help='Print info about range of used bits', default=False, action="store_true")
parser.add_argument('--debug', help='Create header file with debug info', default=False, action='store_false')
parser.add_argument('--info', help='Print info about range of used bits', default=False, action='store_true')
parser.add_argument('--max_blk_len', help='Max number of bits in BLOCKs', type=int, default=256)
parser.add_argument('common_input', help='Path to common CSV file to parse.', type=argparse.FileType('r'))
parser.add_argument('custom_input', help='Path to custom CSV file to parse.', type=argparse.FileType('r'), nargs='?', default=None)
@@ -479,18 +480,18 @@ def main():
idf_target = args.idf_target
max_blk_len = args.max_blk_len
print("Max number of bits in BLK %d" % (max_blk_len))
print('Max number of bits in BLK %d' % (max_blk_len))
if max_blk_len not in [256, 192, 128]:
raise InputError("Unsupported block length = %d" % (max_blk_len))
raise InputError('Unsupported block length = %d' % (max_blk_len))
quiet = args.quiet
debug = args.debug
info = args.info
common_table = process_input_file(args.common_input, "common_table")
common_table = process_input_file(args.common_input, 'common_table')
two_table = common_table
if args.custom_input is not None:
custom_table = process_input_file(args.custom_input, "custom_table")
custom_table = process_input_file(args.custom_input, 'custom_table')
two_table += custom_table
two_table.verify()
@@ -512,7 +513,7 @@ class InputError(RuntimeError):
class ValidationError(InputError):
def __init__(self, p, message):
super(ValidationError, self).__init__("Entry %s invalid: %s" % (p.field_name, message))
super(ValidationError, self).__init__('Entry %s invalid: %s' % (p.field_name, message))
if __name__ == '__main__':

View File

@@ -1,12 +1,13 @@
#!/usr/bin/env python
from __future__ import print_function, division
import unittest
from __future__ import division, print_function
import sys
import unittest
try:
import efuse_table_gen
except ImportError:
sys.path.append("..")
sys.path.append('..')
import efuse_table_gen
@@ -117,7 +118,7 @@ name2, EFUSE_BLK2, ,
, EFUSE_BLK2, , 4,
name1, EFUSE_BLK3, , 5,
"""
with self.assertRaisesRegex(efuse_table_gen.InputError, "Field names must be unique"):
with self.assertRaisesRegex(efuse_table_gen.InputError, 'Field names must be unique'):
efuse_table_gen.FuseTable.from_csv(csv)
def test_seq_bit_start5_fill(self):
@@ -154,7 +155,7 @@ name1, EFUSE_BLK3, 1,
name2, EFUSE_BLK3, 5, 4, Use for test name 2
"""
t = efuse_table_gen.FuseTable.from_csv(csv)
with self.assertRaisesRegex(efuse_table_gen.InputError, "overlap"):
with self.assertRaisesRegex(efuse_table_gen.InputError, 'overlap'):
t.verify()
def test_empty_field_name_fail(self):
@@ -163,7 +164,7 @@ name2, EFUSE_BLK3, 5,
, EFUSE_BLK3, , 5,
name2, EFUSE_BLK2, , 4,
"""
with self.assertRaisesRegex(efuse_table_gen.InputError, "missing field name"):
with self.assertRaisesRegex(efuse_table_gen.InputError, 'missing field name'):
efuse_table_gen.FuseTable.from_csv(csv)
def test_unique_field_name_fail(self):
@@ -172,7 +173,7 @@ name2, EFUSE_BLK2, ,
name1, EFUSE_BLK3, 0, 5, Use for test name 1
name1, EFUSE_BLK3, 5, 4, Use for test name 2
"""
with self.assertRaisesRegex(efuse_table_gen.InputError, "Field names must be unique"):
with self.assertRaisesRegex(efuse_table_gen.InputError, 'Field names must be unique'):
efuse_table_gen.FuseTable.from_csv(csv)
def test_bit_count_empty_fail(self):
@@ -181,7 +182,7 @@ name1, EFUSE_BLK3, 5,
name1, EFUSE_BLK3, 0, , Use for test name 1
name2, EFUSE_BLK3, 5, 4, Use for test name 2
"""
with self.assertRaisesRegex(efuse_table_gen.InputError, "empty"):
with self.assertRaisesRegex(efuse_table_gen.InputError, 'empty'):
efuse_table_gen.FuseTable.from_csv(csv)
def test_bit_start_num_fail(self):
@@ -190,7 +191,7 @@ name2, EFUSE_BLK3, 5,
name1, EFUSE_BLK3, k, 5, Use for test name 1
name2, EFUSE_BLK3, 5, 4, Use for test name 2
"""
with self.assertRaisesRegex(efuse_table_gen.InputError, "Invalid field value"):
with self.assertRaisesRegex(efuse_table_gen.InputError, 'Invalid field value'):
efuse_table_gen.FuseTable.from_csv(csv)
def test_join_entry(self):
@@ -257,7 +258,7 @@ name2, EFUSE_BLK3, 191,
"""
efuse_table_gen.max_blk_len = 192
t = efuse_table_gen.FuseTable.from_csv(csv)
with self.assertRaisesRegex(efuse_table_gen.InputError, "The field is outside the boundaries"):
with self.assertRaisesRegex(efuse_table_gen.InputError, 'The field is outside the boundaries'):
t.verify()
def test_field_blk1_size_is_more(self):
@@ -267,7 +268,7 @@ name1, EFUSE_BLK0, 0,
name2, EFUSE_BLK1, 1, 256, Use for test name 2
"""
t = efuse_table_gen.FuseTable.from_csv(csv)
with self.assertRaisesRegex(efuse_table_gen.InputError, "The field is outside the boundaries"):
with self.assertRaisesRegex(efuse_table_gen.InputError, 'The field is outside the boundaries'):
t.verify()
@@ -311,8 +312,8 @@ name1, EFUSE_BLK3, 0,
name2, EFUSE_BLK2, 5, 4, Use for test name 2
"""
t = efuse_table_gen.FuseTable.from_csv(csv)
with self.assertRaisesRegex(efuse_table_gen.ValidationError, "custom_table should use only EFUSE_BLK3"):
t.verify("custom_table")
with self.assertRaisesRegex(efuse_table_gen.ValidationError, 'custom_table should use only EFUSE_BLK3'):
t.verify('custom_table')
def test_common_and_custom_table_use_the_same_bits(self):
csv_common = """
@@ -321,7 +322,7 @@ name1, EFUSE_BLK3, 0,
name2, EFUSE_BLK2, 5, 4, Use for test name 2
"""
common_table = efuse_table_gen.FuseTable.from_csv(csv_common)
common_table.verify("common_table")
common_table.verify('common_table')
two_tables = common_table
csv_custom = """
@@ -330,12 +331,12 @@ name3, EFUSE_BLK3, 20,
name4, EFUSE_BLK3, 4, 1, Use for test name 2
"""
custom_table = efuse_table_gen.FuseTable.from_csv(csv_custom)
custom_table.verify("custom_table")
custom_table.verify('custom_table')
two_tables += custom_table
with self.assertRaisesRegex(efuse_table_gen.InputError, "overlaps"):
with self.assertRaisesRegex(efuse_table_gen.InputError, 'overlaps'):
two_tables.verify()
if __name__ == "__main__":
if __name__ == '__main__':
unittest.main()

View File

@@ -2,13 +2,13 @@
import hashlib
import hmac
import struct
import os
import random
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
import struct
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.utils import int_to_bytes
@@ -19,9 +19,9 @@ def number_as_bignum_words(number):
"""
result = []
while number != 0:
result.append("0x%08x" % (number & 0xFFFFFFFF))
result.append('0x%08x' % (number & 0xFFFFFFFF))
number >>= 32
return "{ " + ", ".join(result) + " }"
return '{ ' + ', '.join(result) + ' }'
def number_as_bytes(number, pad_bits=None):
@@ -38,7 +38,7 @@ def bytes_as_char_array(b):
"""
Given a sequence of bytes, format as a char array
"""
return "{ " + ", ".join("0x%02x" % x for x in b) + " }"
return '{ ' + ', '.join('0x%02x' % x for x in b) + ' }'
NUM_HMAC_KEYS = 3
@@ -50,36 +50,36 @@ hmac_keys = [os.urandom(32) for x in range(NUM_HMAC_KEYS)]
messages = [random.randrange(0, 1 << 4096) for x in range(NUM_MESSAGES)]
with open("digital_signature_test_cases.h", "w") as f:
f.write("/* File generated by gen_digital_signature_tests.py */\n\n")
with open('digital_signature_test_cases.h', 'w') as f:
f.write('/* File generated by gen_digital_signature_tests.py */\n\n')
# Write out HMAC keys
f.write("#define NUM_HMAC_KEYS %d\n\n" % NUM_HMAC_KEYS)
f.write("static const uint8_t test_hmac_keys[NUM_HMAC_KEYS][32] = {\n")
f.write('#define NUM_HMAC_KEYS %d\n\n' % NUM_HMAC_KEYS)
f.write('static const uint8_t test_hmac_keys[NUM_HMAC_KEYS][32] = {\n')
for h in hmac_keys:
f.write(" %s,\n" % bytes_as_char_array(h))
f.write("};\n\n")
f.write(' %s,\n' % bytes_as_char_array(h))
f.write('};\n\n')
# Write out messages
f.write("#define NUM_MESSAGES %d\n\n" % NUM_MESSAGES)
f.write("static const uint32_t test_messages[NUM_MESSAGES][4096/32] = {\n")
f.write('#define NUM_MESSAGES %d\n\n' % NUM_MESSAGES)
f.write('static const uint32_t test_messages[NUM_MESSAGES][4096/32] = {\n')
for m in messages:
f.write(" // Message %d\n" % messages.index(m))
f.write(" %s,\n" % number_as_bignum_words(m))
f.write(" };\n")
f.write("\n\n\n")
f.write(' // Message %d\n' % messages.index(m))
f.write(' %s,\n' % number_as_bignum_words(m))
f.write(' };\n')
f.write('\n\n\n')
f.write("#define NUM_CASES %d\n\n" % NUM_CASES)
f.write("static const encrypt_testcase_t test_cases[NUM_CASES] = {\n")
f.write('#define NUM_CASES %d\n\n' % NUM_CASES)
f.write('static const encrypt_testcase_t test_cases[NUM_CASES] = {\n')
for case in range(NUM_CASES):
f.write(" { /* Case %d */\n" % case)
f.write(' { /* Case %d */\n' % case)
iv = os.urandom(16)
f.write(" .iv = %s,\n" % (bytes_as_char_array(iv)))
f.write(' .iv = %s,\n' % (bytes_as_char_array(iv)))
hmac_key_idx = random.randrange(0, NUM_HMAC_KEYS)
aes_key = hmac.HMAC(hmac_keys[hmac_key_idx], b"\xFF" * 32, hashlib.sha256).digest()
aes_key = hmac.HMAC(hmac_keys[hmac_key_idx], b'\xFF' * 32, hashlib.sha256).digest()
sizes = [4096, 3072, 2048, 1024, 512]
key_size = sizes[case % len(sizes)]
@@ -100,13 +100,13 @@ with open("digital_signature_test_cases.h", "w") as f:
mprime &= 0xFFFFFFFF
length = key_size // 32 - 1
f.write(" .p_data = {\n")
f.write(" .Y = %s,\n" % number_as_bignum_words(Y))
f.write(" .M = %s,\n" % number_as_bignum_words(M))
f.write(" .Rb = %s,\n" % number_as_bignum_words(rinv))
f.write(" .M_prime = 0x%08x,\n" % mprime)
f.write(" .length = %d, // %d bit\n" % (length, key_size))
f.write(" },\n")
f.write(' .p_data = {\n')
f.write(' .Y = %s,\n' % number_as_bignum_words(Y))
f.write(' .M = %s,\n' % number_as_bignum_words(M))
f.write(' .Rb = %s,\n' % number_as_bignum_words(rinv))
f.write(' .M_prime = 0x%08x,\n' % mprime)
f.write(' .length = %d, // %d bit\n' % (length, key_size))
f.write(' },\n')
# calculate MD from preceding values and IV
@@ -114,7 +114,7 @@ with open("digital_signature_test_cases.h", "w") as f:
md_in = number_as_bytes(Y, 4096) + \
number_as_bytes(M, 4096) + \
number_as_bytes(rinv, 4096) + \
struct.pack("<II", mprime, length) + \
struct.pack('<II', mprime, length) + \
iv
assert len(md_in) == 12480 / 8
md = hashlib.sha256(md_in).digest()
@@ -126,7 +126,7 @@ with open("digital_signature_test_cases.h", "w") as f:
number_as_bytes(M, 4096) + \
number_as_bytes(rinv, 4096) + \
md + \
struct.pack("<II", mprime, length) + \
struct.pack('<II', mprime, length) + \
b'\x08' * 8
assert len(p) == 12672 / 8
@@ -135,16 +135,16 @@ with open("digital_signature_test_cases.h", "w") as f:
encryptor = cipher.encryptor()
c = encryptor.update(p) + encryptor.finalize()
f.write(" .expected_c = %s,\n" % bytes_as_char_array(c))
f.write(" .hmac_key_idx = %d,\n" % (hmac_key_idx))
f.write(' .expected_c = %s,\n' % bytes_as_char_array(c))
f.write(' .hmac_key_idx = %d,\n' % (hmac_key_idx))
f.write(" // results of message array encrypted with these keys\n")
f.write(" .expected_results = {\n")
f.write(' // results of message array encrypted with these keys\n')
f.write(' .expected_results = {\n')
mask = (1 << key_size) - 1 # truncate messages if needed
for m in messages:
f.write(" // Message %d\n" % messages.index(m))
f.write(" %s," % (number_as_bignum_words(pow(m & mask, Y, M))))
f.write(" },\n")
f.write(" },\n")
f.write(' // Message %d\n' % messages.index(m))
f.write(' %s,' % (number_as_bignum_words(pow(m & mask, Y, M))))
f.write(' },\n')
f.write(' },\n')
f.write("};\n")
f.write('};\n')

View File

@@ -3,12 +3,14 @@
# source: esp_local_ctrl.proto
import sys
_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1'))
from google.protobuf.internal import enum_type_wrapper
from google.protobuf import descriptor as _descriptor
from google.protobuf import message as _message
from google.protobuf import reflection as _reflection
from google.protobuf import symbol_database as _symbol_database
from google.protobuf.internal import enum_type_wrapper
# @@protoc_insertion_point(imports)
_sym_db = _symbol_database.Default()
@@ -16,7 +18,6 @@ _sym_db = _symbol_database.Default()
import constants_pb2 as constants__pb2
DESCRIPTOR = _descriptor.FileDescriptor(
name='esp_local_ctrl.proto',
package='',
@@ -153,7 +154,7 @@ _PROPERTYINFO = _descriptor.Descriptor(
_descriptor.FieldDescriptor(
name='name', full_name='PropertyInfo.name', index=1,
number=2, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=_b("").decode('utf-8'),
has_default_value=False, default_value=_b('').decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR),
@@ -174,7 +175,7 @@ _PROPERTYINFO = _descriptor.Descriptor(
_descriptor.FieldDescriptor(
name='value', full_name='PropertyInfo.value', index=4,
number=5, type=12, cpp_type=9, label=1,
has_default_value=False, default_value=_b(""),
has_default_value=False, default_value=_b(''),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR),
@@ -281,7 +282,7 @@ _PROPERTYVALUE = _descriptor.Descriptor(
_descriptor.FieldDescriptor(
name='value', full_name='PropertyValue.value', index=1,
number=2, type=12, cpp_type=9, label=1,
has_default_value=False, default_value=_b(""),
has_default_value=False, default_value=_b(''),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR),

View File

@@ -1,13 +1,13 @@
import re
import os
import re
import socket
from threading import Thread, Event
import subprocess
import time
from shutil import copyfile
from threading import Event, Thread
from tiny_test_fw import Utility, DUT
import ttfw_idf
from tiny_test_fw import DUT, Utility
stop_sock_listener = Event()
stop_io_listener = Event()
@@ -22,16 +22,16 @@ def io_listener(dut1):
data = b''
while not stop_io_listener.is_set():
try:
data = dut1.expect(re.compile(r"PacketOut:\[([a-fA-F0-9]+)\]"), timeout=5)
data = dut1.expect(re.compile(r'PacketOut:\[([a-fA-F0-9]+)\]'), timeout=5)
except DUT.ExpectTimeout:
continue
if data != () and data[0] != b'':
packet_data = data[0]
print("Packet_data>{}<".format(packet_data))
print('Packet_data>{}<'.format(packet_data))
response = bytearray.fromhex(packet_data.decode())
print("Sending to socket:")
print('Sending to socket:')
packet = ' '.join(format(x, '02x') for x in bytearray(response))
print("Packet>{}<".format(packet))
print('Packet>{}<'.format(packet))
if client_address is not None:
sock.sendto(response, ('127.0.0.1', 7777))
@@ -50,7 +50,7 @@ def sock_listener(dut1):
try:
payload, client_address = sock.recvfrom(1024)
packet = ' '.join(format(x, '02x') for x in bytearray(payload))
print("Received from address {}, data {}".format(client_address, packet))
print('Received from address {}, data {}'.format(client_address, packet))
dut1.write(str.encode(packet))
except socket.timeout:
pass
@@ -59,7 +59,7 @@ def sock_listener(dut1):
sock = None
@ttfw_idf.idf_example_test(env_tag="Example_WIFI")
@ttfw_idf.idf_example_test(env_tag='Example_WIFI')
def lwip_test_suite(env, extra_data):
global stop_io_listener
global stop_sock_listener
@@ -70,12 +70,12 @@ def lwip_test_suite(env, extra_data):
3. Execute ttcn3 test suite
4. Collect result from ttcn3
"""
dut1 = env.get_dut("net_suite", "examples/system/network_tests", dut_class=ttfw_idf.ESP32DUT)
dut1 = env.get_dut('net_suite', 'examples/system/network_tests', dut_class=ttfw_idf.ESP32DUT)
# check and log bin size
binary_file = os.path.join(dut1.app.binary_path, "net_suite.bin")
binary_file = os.path.join(dut1.app.binary_path, 'net_suite.bin')
bin_size = os.path.getsize(binary_file)
ttfw_idf.log_performance("net_suite", "{}KB".format(bin_size // 1024))
ttfw_idf.check_performance("net_suite", bin_size // 1024, dut1.TARGET)
ttfw_idf.log_performance('net_suite', '{}KB'.format(bin_size // 1024))
ttfw_idf.check_performance('net_suite', bin_size // 1024, dut1.TARGET)
dut1.start_app()
thread1 = Thread(target=sock_listener, args=(dut1, ))
thread2 = Thread(target=io_listener, args=(dut1, ))
@@ -84,48 +84,48 @@ def lwip_test_suite(env, extra_data):
TTCN_SRC = 'esp32_netsuite.ttcn'
TTCN_CFG = 'esp32_netsuite.cfg'
# System Paths
netsuite_path = os.getenv("NETSUITE_PATH")
netsuite_src_path = os.path.join(netsuite_path, "src")
netsuite_path = os.getenv('NETSUITE_PATH')
netsuite_src_path = os.path.join(netsuite_path, 'src')
test_dir = os.path.dirname(os.path.realpath(__file__))
# Building the suite
print("Rebuilding the test suite")
print("-------------------------")
print('Rebuilding the test suite')
print('-------------------------')
# copy esp32 specific files to ttcn net-suite dir
copyfile(os.path.join(test_dir, TTCN_SRC), os.path.join(netsuite_src_path, TTCN_SRC))
copyfile(os.path.join(test_dir, TTCN_CFG), os.path.join(netsuite_src_path, TTCN_CFG))
proc = subprocess.Popen(['bash', '-c', 'cd ' + netsuite_src_path + ' && source make.sh'],
cwd=netsuite_path, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
output = proc.stdout.read()
print("Note: First build step we expect failure (titan/net_suite build system not suitable for multijob make)")
print('Note: First build step we expect failure (titan/net_suite build system not suitable for multijob make)')
print(output)
proc = subprocess.Popen(['bash', '-c', 'cd ' + netsuite_src_path + ' && make'],
cwd=netsuite_path, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
print("Note: This time all dependencies shall be generated -- multijob make shall pass")
print('Note: This time all dependencies shall be generated -- multijob make shall pass')
output = proc.stdout.read()
print(output)
# Executing the test suite
thread1.start()
thread2.start()
time.sleep(2)
print("Executing the test suite")
print("------------------------")
print('Executing the test suite')
print('------------------------')
proc = subprocess.Popen(['ttcn3_start', os.path.join(netsuite_src_path,'test_suite'), os.path.join(netsuite_src_path, TTCN_CFG)],
stdout=subprocess.PIPE)
output = proc.stdout.read()
print(output)
print("Collecting results")
print("------------------")
print('Collecting results')
print('------------------')
verdict_stats = re.search('(Verdict statistics:.*)', output)
if verdict_stats:
verdict_stats = verdict_stats.group(1)
else:
verdict_stats = b""
verdict_stats = b''
verdict = re.search('Overall verdict: pass', output)
if verdict:
print("Test passed!")
Utility.console_log(verdict_stats, "green")
print('Test passed!')
Utility.console_log(verdict_stats, 'green')
else:
Utility.console_log(verdict_stats, "red")
Utility.console_log(verdict_stats, 'red')
raise ValueError('Test failed with: {}'.format(verdict_stats))
else:
try:
@@ -137,8 +137,8 @@ def lwip_test_suite(env, extra_data):
time.sleep(0.5)
except KeyboardInterrupt:
pass
print("Executing done, waiting for tests to finish")
print("-------------------------------------------")
print('Executing done, waiting for tests to finish')
print('-------------------------------------------')
stop_io_listener.set()
stop_sock_listener.set()
thread1.join()
@@ -146,6 +146,6 @@ def lwip_test_suite(env, extra_data):
if __name__ == '__main__':
print("Manual execution, please build and start ttcn in a separate console")
print('Manual execution, please build and start ttcn in a separate console')
manual_test = True
lwip_test_suite()

View File

@@ -24,12 +24,12 @@
from __future__ import with_statement
import os
import sys
import struct
import argparse
import csv
import os
import re
import struct
import sys
from io import open
try:
@@ -80,22 +80,22 @@ class CertificateBundle:
def add_from_file(self, file_path):
try:
if file_path.endswith('.pem'):
status("Parsing certificates from %s" % file_path)
status('Parsing certificates from %s' % file_path)
with open(file_path, 'r', encoding='utf-8') as f:
crt_str = f.read()
self.add_from_pem(crt_str)
return True
elif file_path.endswith('.der'):
status("Parsing certificates from %s" % file_path)
status('Parsing certificates from %s' % file_path)
with open(file_path, 'rb') as f:
crt_str = f.read()
self.add_from_der(crt_str)
return True
except ValueError:
critical("Invalid certificate in %s" % file_path)
raise InputError("Invalid certificate")
critical('Invalid certificate in %s' % file_path)
raise InputError('Invalid certificate')
return False
@@ -119,13 +119,13 @@ class CertificateBundle:
crt += strg
if(count == 0):
raise InputError("No certificate found")
raise InputError('No certificate found')
status("Successfully added %d certificates" % count)
status('Successfully added %d certificates' % count)
def add_from_der(self, crt_str):
self.certificates.append(x509.load_der_x509_certificate(crt_str, default_backend()))
status("Successfully added 1 certificate")
status('Successfully added 1 certificate')
def create_bundle(self):
# Sort certificates in order to do binary search when looking up certificates
@@ -162,7 +162,7 @@ class CertificateBundle:
for row in csv_reader:
filter_set.add(row[1])
status("Parsing certificates from %s" % crts_path)
status('Parsing certificates from %s' % crts_path)
crt_str = []
with open(crts_path, 'r', encoding='utf-8') as f:
crt_str = f.read()
@@ -202,14 +202,14 @@ def main():
for path in args.input:
if os.path.isfile(path):
if os.path.basename(path) == "cacrt_all.pem" and args.filter:
if os.path.basename(path) == 'cacrt_all.pem' and args.filter:
bundle.add_with_filter(path, args.filter)
else:
bundle.add_from_file(path)
elif os.path.isdir(path):
bundle.add_from_path(path)
else:
raise InputError("Invalid --input=%s, is neither file nor folder" % args.input)
raise InputError('Invalid --input=%s, is neither file nor folder' % args.input)
status('Successfully added %d certificates in total' % len(bundle.certificates))

View File

@@ -1,13 +1,13 @@
#!/usr/bin/env python
import unittest
import sys
import os
import sys
import unittest
try:
import gen_crt_bundle
except ImportError:
sys.path.append("..")
sys.path.append('..')
import gen_crt_bundle
@@ -67,11 +67,11 @@ class GenCrtBundleTests(Py23TestCase):
def test_invalid_crt_input(self):
bundle = gen_crt_bundle.CertificateBundle()
with self.assertRaisesRegex(gen_crt_bundle.InputError, "Invalid certificate"):
with self.assertRaisesRegex(gen_crt_bundle.InputError, 'Invalid certificate'):
bundle.add_from_file(test_crts_path + invalid_test_file)
with self.assertRaisesRegex(gen_crt_bundle.InputError, "No certificate found"):
bundle.add_from_pem("")
with self.assertRaisesRegex(gen_crt_bundle.InputError, 'No certificate found'):
bundle.add_from_pem('')
def test_non_ascii_crt_input(self):
bundle = gen_crt_bundle.CertificateBundle()
@@ -80,5 +80,5 @@ class GenCrtBundleTests(Py23TestCase):
self.assertTrue(len(bundle.certificates))
if __name__ == "__main__":
if __name__ == '__main__':
unittest.main()

View File

@@ -1,36 +1,35 @@
from __future__ import print_function
from __future__ import unicode_literals
from builtins import str
import re
import sys
import ssl
import paho.mqtt.client as mqtt
from threading import Thread, Event
import time
import string
from __future__ import print_function, unicode_literals
import random
import re
import ssl
import string
import sys
import time
from builtins import str
from threading import Event, Thread
from tiny_test_fw import DUT
import paho.mqtt.client as mqtt
import ttfw_idf
from tiny_test_fw import DUT
event_client_connected = Event()
event_stop_client = Event()
event_client_received_correct = Event()
message_log = ""
message_log = ''
broker_host = {}
broker_port = {}
expected_data = ""
subscribe_topic = ""
publish_topic = ""
expected_data = ''
subscribe_topic = ''
publish_topic = ''
expected_count = 0
# The callback for when the client receives a CONNACK response from the server.
def on_connect(client, userdata, flags, rc):
print("Connected with result code " + str(rc))
print('Connected with result code ' + str(rc))
event_client_connected.set()
client.subscribe("/topic/qos0")
client.subscribe('/topic/qos0')
def mqtt_client_task(client):
@@ -52,8 +51,8 @@ def on_message(client, userdata, msg):
payload = msg.payload.decode()
if payload == expected_data:
expected_count += 1
print("[{}] Received...".format(msg.mid))
message_log += "Received data:" + msg.topic + " " + payload + "\n"
print('[{}] Received...'.format(msg.mid))
message_log += 'Received data:' + msg.topic + ' ' + payload + '\n'
def test_single_config(dut, transport, qos, repeat, published, queue=0):
@@ -63,49 +62,49 @@ def test_single_config(dut, transport, qos, repeat, published, queue=0):
sample_string = ''.join(random.choice(string.ascii_uppercase + string.ascii_lowercase + string.digits) for _ in range(16))
event_client_connected.clear()
expected_count = 0
message_log = ""
message_log = ''
expected_data = sample_string * repeat
print("PUBLISH TEST: transport:{}, qos:{}, sequence:{}, enqueue:{}, sample msg:'{}'".format(transport, qos, published, queue, expected_data))
client = None
try:
if transport in ["ws", "wss"]:
client = mqtt.Client(transport="websockets")
if transport in ['ws', 'wss']:
client = mqtt.Client(transport='websockets')
else:
client = mqtt.Client()
client.on_connect = on_connect
client.on_message = on_message
if transport in ["ssl", "wss"]:
if transport in ['ssl', 'wss']:
client.tls_set(None, None, None, cert_reqs=ssl.CERT_NONE, tls_version=ssl.PROTOCOL_TLSv1_2, ciphers=None)
client.tls_insecure_set(True)
print("Connecting...")
print('Connecting...')
client.connect(broker_host[transport], broker_port[transport], 60)
except Exception:
print("ENV_TEST_FAILURE: Unexpected error while connecting to broker {}: {}:".format(broker_host[transport], sys.exc_info()[0]))
print('ENV_TEST_FAILURE: Unexpected error while connecting to broker {}: {}:'.format(broker_host[transport], sys.exc_info()[0]))
raise
# Starting a py-client in a separate thread
thread1 = Thread(target=mqtt_client_task, args=(client,))
thread1.start()
print("Connecting py-client to broker {}:{}...".format(broker_host[transport], broker_port[transport]))
print('Connecting py-client to broker {}:{}...'.format(broker_host[transport], broker_port[transport]))
if not event_client_connected.wait(timeout=30):
raise ValueError("ENV_TEST_FAILURE: Test script cannot connect to broker: {}".format(broker_host[transport]))
raise ValueError('ENV_TEST_FAILURE: Test script cannot connect to broker: {}'.format(broker_host[transport]))
client.subscribe(subscribe_topic, qos)
dut.write(' '.join(str(x) for x in (transport, sample_string, repeat, published, qos, queue)), eol="\n")
dut.write(' '.join(str(x) for x in (transport, sample_string, repeat, published, qos, queue)), eol='\n')
try:
# waiting till subscribed to defined topic
dut.expect(re.compile(r"MQTT_EVENT_SUBSCRIBED"), timeout=30)
dut.expect(re.compile(r'MQTT_EVENT_SUBSCRIBED'), timeout=30)
for i in range(published):
client.publish(publish_topic, sample_string * repeat, qos)
print("Publishing...")
print("Checking esp-client received msg published from py-client...")
dut.expect(re.compile(r"Correct pattern received exactly x times"), timeout=60)
print('Publishing...')
print('Checking esp-client received msg published from py-client...')
dut.expect(re.compile(r'Correct pattern received exactly x times'), timeout=60)
start = time.time()
while expected_count < published and time.time() - start <= 60:
time.sleep(1)
# Note: tolerate that messages qos=1 to be received more than once
if expected_count == published or (expected_count > published and qos == 1):
print("All data received from ESP32...")
print('All data received from ESP32...')
else:
raise ValueError("Not all data received from ESP32: Expected:{}x{}, Received:{}x{}".format(expected_count, published, expected_data, message_log))
raise ValueError('Not all data received from ESP32: Expected:{}x{}, Received:{}x{}'.format(expected_count, published, expected_data, message_log))
finally:
event_stop_client.set()
thread1.join()
@@ -113,7 +112,7 @@ def test_single_config(dut, transport, qos, repeat, published, queue=0):
event_stop_client.clear()
@ttfw_idf.idf_custom_test(env_tag="Example_WIFI")
@ttfw_idf.idf_custom_test(env_tag='Example_WIFI')
def test_weekend_mqtt_publish(env, extra_data):
# Using broker url dictionary for different transport
global broker_host
@@ -127,28 +126,28 @@ def test_weekend_mqtt_publish(env, extra_data):
3. Test evaluates python client received correct qos0 message
4. Test ESP32 client received correct qos0 message
"""
dut1 = env.get_dut("mqtt_publish_connect_test", "tools/test_apps/protocols/mqtt/publish_connect_test")
dut1 = env.get_dut('mqtt_publish_connect_test', 'tools/test_apps/protocols/mqtt/publish_connect_test')
# Look for host:port in sdkconfig
try:
# python client subscribes to the topic to which esp client publishes and vice versa
publish_topic = dut1.app.get_sdkconfig()["CONFIG_EXAMPLE_SUBSCIBE_TOPIC"].replace('"','')
subscribe_topic = dut1.app.get_sdkconfig()["CONFIG_EXAMPLE_PUBLISH_TOPIC"].replace('"','')
broker_host["ssl"], broker_port["ssl"] = get_host_port_from_dut(dut1, "CONFIG_EXAMPLE_BROKER_SSL_URI")
broker_host["tcp"], broker_port["tcp"] = get_host_port_from_dut(dut1, "CONFIG_EXAMPLE_BROKER_TCP_URI")
broker_host["ws"], broker_port["ws"] = get_host_port_from_dut(dut1, "CONFIG_EXAMPLE_BROKER_WS_URI")
broker_host["wss"], broker_port["wss"] = get_host_port_from_dut(dut1, "CONFIG_EXAMPLE_BROKER_WSS_URI")
publish_topic = dut1.app.get_sdkconfig()['CONFIG_EXAMPLE_SUBSCIBE_TOPIC'].replace('"','')
subscribe_topic = dut1.app.get_sdkconfig()['CONFIG_EXAMPLE_PUBLISH_TOPIC'].replace('"','')
broker_host['ssl'], broker_port['ssl'] = get_host_port_from_dut(dut1, 'CONFIG_EXAMPLE_BROKER_SSL_URI')
broker_host['tcp'], broker_port['tcp'] = get_host_port_from_dut(dut1, 'CONFIG_EXAMPLE_BROKER_TCP_URI')
broker_host['ws'], broker_port['ws'] = get_host_port_from_dut(dut1, 'CONFIG_EXAMPLE_BROKER_WS_URI')
broker_host['wss'], broker_port['wss'] = get_host_port_from_dut(dut1, 'CONFIG_EXAMPLE_BROKER_WSS_URI')
except Exception:
print('ENV_TEST_FAILURE: Cannot find broker url in sdkconfig')
raise
dut1.start_app()
try:
ip_address = dut1.expect(re.compile(r" IPv4 address: ([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)"), timeout=30)
print("Connected to AP with IP: {}".format(ip_address))
ip_address = dut1.expect(re.compile(r' IPv4 address: ([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)'), timeout=30)
print('Connected to AP with IP: {}'.format(ip_address))
except DUT.ExpectTimeout:
print('ENV_TEST_FAILURE: Cannot connect to AP')
raise
for qos in [0, 1, 2]:
for transport in ["tcp", "ssl", "ws", "wss"]:
for transport in ['tcp', 'ssl', 'ws', 'wss']:
for q in [0, 1]:
if broker_host[transport] is None:
print('Skipping transport: {}...'.format(transport))
@@ -156,14 +155,14 @@ def test_weekend_mqtt_publish(env, extra_data):
# simple test with empty message
test_single_config(dut1, transport, qos, 0, 5, q)
# decide on broker what level of test will pass (local broker works the best)
if broker_host[transport].startswith("192.168") and qos > 0 and q == 0:
if broker_host[transport].startswith('192.168') and qos > 0 and q == 0:
# medium size, medium repeated
test_single_config(dut1, transport, qos, 5, 50, q)
# long data
test_single_config(dut1, transport, qos, 1000, 10, q)
# short data, many repeats
test_single_config(dut1, transport, qos, 2, 200, q)
elif transport in ["ws", "wss"]:
elif transport in ['ws', 'wss']:
# more relaxed criteria for websockets!
test_single_config(dut1, transport, qos, 2, 5, q)
test_single_config(dut1, transport, qos, 50, 1, q)

View File

@@ -19,40 +19,42 @@
#
from __future__ import division, print_function
from future.moves.itertools import zip_longest
from builtins import int, range, bytes
from io import open
import sys
import argparse
import binascii
import random
import struct
import os
import array
import zlib
import binascii
import codecs
import datetime
import distutils.dir_util
import os
import random
import struct
import sys
import zlib
from builtins import bytes, int, range
from io import open
from future.moves.itertools import zip_longest
try:
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
except ImportError:
print('The cryptography package is not installed.'
'Please refer to the Get Started section of the ESP-IDF Programming Guide for '
'setting up the required packages.')
raise
VERSION1_PRINT = "V1 - Multipage Blob Support Disabled"
VERSION2_PRINT = "V2 - Multipage Blob Support Enabled"
VERSION1_PRINT = 'V1 - Multipage Blob Support Disabled'
VERSION2_PRINT = 'V2 - Multipage Blob Support Enabled'
def reverse_hexbytes(addr_tmp):
addr = []
reversed_bytes = ""
reversed_bytes = ''
for i in range(0, len(addr_tmp), 2):
addr.append(addr_tmp[i:i + 2])
reversed_bytes = "".join(reversed(addr))
reversed_bytes = ''.join(reversed(addr))
return reversed_bytes
@@ -62,10 +64,10 @@ def reverse_hexbytes(addr_tmp):
class Page(object):
PAGE_PARAMS = {
"max_size": 4096,
"max_old_blob_size": 1984,
"max_new_blob_size": 4000,
"max_entries": 126
'max_size': 4096,
'max_old_blob_size': 1984,
'max_new_blob_size': 4000,
'max_entries': 126
}
# Item type codes
@@ -98,7 +100,7 @@ class Page(object):
self.entry_num = 0
self.bitmap_array = array.array('B')
self.version = version
self.page_buf = bytearray(b'\xff') * Page.PAGE_PARAMS["max_size"]
self.page_buf = bytearray(b'\xff') * Page.PAGE_PARAMS['max_size']
if not is_rsrv_page:
self.bitmap_array = self.create_bitmap_array()
self.set_header(page_num, version)
@@ -167,7 +169,7 @@ class Page(object):
else:
encr_key_input = codecs.decode(nvs_obj.encr_key, 'hex')
rel_addr = nvs_obj.page_num * Page.PAGE_PARAMS["max_size"] + Page.FIRST_ENTRY_OFFSET
rel_addr = nvs_obj.page_num * Page.PAGE_PARAMS['max_size'] + Page.FIRST_ENTRY_OFFSET
if not isinstance(data_input, bytearray):
byte_arr = bytearray(b'\xff') * 32
@@ -249,8 +251,8 @@ class Page(object):
chunk_size = 0
# Get the size available in current page
tailroom = (Page.PAGE_PARAMS["max_entries"] - self.entry_num - 1) * Page.SINGLE_ENTRY_SIZE
assert tailroom >= 0, "Page overflow!!"
tailroom = (Page.PAGE_PARAMS['max_entries'] - self.entry_num - 1) * Page.SINGLE_ENTRY_SIZE
assert tailroom >= 0, 'Page overflow!!'
# Split the binary data into two and store a chunk of available size onto curr page
if tailroom < remaining_size:
@@ -358,14 +360,14 @@ class Page(object):
# Set size of data
datalen = len(data)
if datalen > Page.PAGE_PARAMS["max_old_blob_size"]:
if datalen > Page.PAGE_PARAMS['max_old_blob_size']:
if self.version == Page.VERSION1:
raise InputError(" Input File: Size (%d) exceeds max allowed length `%s` bytes for key `%s`."
% (datalen, Page.PAGE_PARAMS["max_old_blob_size"], key))
raise InputError(' Input File: Size (%d) exceeds max allowed length `%s` bytes for key `%s`.'
% (datalen, Page.PAGE_PARAMS['max_old_blob_size'], key))
else:
if encoding == "string":
raise InputError(" Input File: Size (%d) exceeds max allowed length `%s` bytes for key `%s`."
% (datalen, Page.PAGE_PARAMS["max_old_blob_size"], key))
if encoding == 'string':
raise InputError(' Input File: Size (%d) exceeds max allowed length `%s` bytes for key `%s`.'
% (datalen, Page.PAGE_PARAMS['max_old_blob_size'], key))
# Calculate no. of entries data will require
rounded_size = (datalen + 31) & ~31
@@ -373,10 +375,10 @@ class Page(object):
total_entry_count = data_entry_count + 1 # +1 for the entry header
# Check if page is already full and new page is needed to be created right away
if self.entry_num >= Page.PAGE_PARAMS["max_entries"]:
if self.entry_num >= Page.PAGE_PARAMS['max_entries']:
raise PageFullError()
elif (self.entry_num + total_entry_count) >= Page.PAGE_PARAMS["max_entries"]:
if not (self.version == Page.VERSION2 and encoding in ["hex2bin", "binary", "base64"]):
elif (self.entry_num + total_entry_count) >= Page.PAGE_PARAMS['max_entries']:
if not (self.version == Page.VERSION2 and encoding in ['hex2bin', 'binary', 'base64']):
raise PageFullError()
# Entry header
@@ -385,7 +387,7 @@ class Page(object):
entry_struct[0] = ns_index
# Set Span
if self.version == Page.VERSION2:
if encoding == "string":
if encoding == 'string':
entry_struct[2] = data_entry_count + 1
# Set Chunk Index
chunk_index = Page.CHUNK_ANY
@@ -399,12 +401,12 @@ class Page(object):
entry_struct[8:8 + len(key)] = key.encode()
# set Type
if encoding == "string":
if encoding == 'string':
entry_struct[1] = Page.SZ
elif encoding in ["hex2bin", "binary", "base64"]:
elif encoding in ['hex2bin', 'binary', 'base64']:
entry_struct[1] = Page.BLOB
if self.version == Page.VERSION2 and (encoding in ["hex2bin", "binary", "base64"]):
if self.version == Page.VERSION2 and (encoding in ['hex2bin', 'binary', 'base64']):
entry_struct = self.write_varlen_binary_data(entry_struct,ns_index,key,data,
datalen,total_entry_count, encoding, nvs_obj)
else:
@@ -413,7 +415,7 @@ class Page(object):
""" Low-level function to write data of primitive type into page buffer. """
def write_primitive_data(self, key, data, encoding, ns_index,nvs_obj):
# Check if entry exceeds max number of entries allowed per page
if self.entry_num >= Page.PAGE_PARAMS["max_entries"]:
if self.entry_num >= Page.PAGE_PARAMS['max_entries']:
raise PageFullError()
entry_struct = bytearray(b'\xff') * 32
@@ -427,28 +429,28 @@ class Page(object):
entry_struct[8:24] = key_array
entry_struct[8:8 + len(key)] = key.encode()
if encoding == "u8":
if encoding == 'u8':
entry_struct[1] = Page.U8
struct.pack_into('<B', entry_struct, 24, data)
elif encoding == "i8":
elif encoding == 'i8':
entry_struct[1] = Page.I8
struct.pack_into('<b', entry_struct, 24, data)
elif encoding == "u16":
elif encoding == 'u16':
entry_struct[1] = Page.U16
struct.pack_into('<H', entry_struct, 24, data)
elif encoding == "i16":
elif encoding == 'i16':
entry_struct[1] = Page.I16
struct.pack_into('<h', entry_struct, 24, data)
elif encoding == "u32":
elif encoding == 'u32':
entry_struct[1] = Page.U32
struct.pack_into('<I', entry_struct, 24, data)
elif encoding == "i32":
elif encoding == 'i32':
entry_struct[1] = Page.I32
struct.pack_into('<i', entry_struct, 24, data)
elif encoding == "u64":
elif encoding == 'u64':
entry_struct[1] = Page.U64
struct.pack_into('<Q', entry_struct, 24, data)
elif encoding == "i64":
elif encoding == 'i64':
entry_struct[1] = Page.I64
struct.pack_into('<q', entry_struct, 24, data)
@@ -516,9 +518,9 @@ class NVS(object):
version = self.version
# Update available size as each page is created
if self.size == 0:
raise InsufficientSizeError("Error: Size parameter is less than the size of data in csv.Please increase size.")
raise InsufficientSizeError('Error: Size parameter is less than the size of data in csv.Please increase size.')
if not is_rsrv_page:
self.size = self.size - Page.PAGE_PARAMS["max_size"]
self.size = self.size - Page.PAGE_PARAMS['max_size']
self.page_num += 1
# Set version for each page and page header
new_page = Page(self.page_num, version, is_rsrv_page)
@@ -533,10 +535,10 @@ class NVS(object):
def write_namespace(self, key):
self.namespace_idx += 1
try:
self.cur_page.write_primitive_data(key, self.namespace_idx, "u8", 0,self)
self.cur_page.write_primitive_data(key, self.namespace_idx, 'u8', 0,self)
except PageFullError:
new_page = self.create_new_page()
new_page.write_primitive_data(key, self.namespace_idx, "u8", 0,self)
new_page.write_primitive_data(key, self.namespace_idx, 'u8', 0,self)
"""
Write key-value pair. Function accepts value in the form of ascii character and converts
@@ -545,23 +547,23 @@ class NVS(object):
We don't have to guard re-invocation with try-except since no entry can span multiple pages.
"""
def write_entry(self, key, value, encoding):
if encoding == "hex2bin":
if encoding == 'hex2bin':
value = value.strip()
if len(value) % 2 != 0:
raise InputError("%s: Invalid data length. Should be multiple of 2." % key)
raise InputError('%s: Invalid data length. Should be multiple of 2.' % key)
value = binascii.a2b_hex(value)
if encoding == "base64":
if encoding == 'base64':
value = binascii.a2b_base64(value)
if encoding == "string":
if encoding == 'string':
if type(value) == bytes:
value = value.decode()
value += '\0'
encoding = encoding.lower()
varlen_encodings = ["string", "binary", "hex2bin", "base64"]
primitive_encodings = ["u8", "i8", "u16", "i16", "u32", "i32", "u64", "i64"]
varlen_encodings = ['string', 'binary', 'hex2bin', 'base64']
primitive_encodings = ['u8', 'i8', 'u16', 'i16', 'u32', 'i32', 'u64', 'i64']
if encoding in varlen_encodings:
try:
@@ -576,7 +578,7 @@ class NVS(object):
new_page = self.create_new_page()
new_page.write_primitive_data(key, int(value), encoding, self.namespace_idx,self)
else:
raise InputError("%s: Unsupported encoding" % encoding)
raise InputError('%s: Unsupported encoding' % encoding)
""" Return accumulated data of all pages """
def get_binary_data(self):
@@ -600,7 +602,7 @@ class InputError(RuntimeError):
Represents error on the input
"""
def __init__(self, e):
print("\nError:")
print('\nError:')
super(InputError, self).__init__(e)
@@ -634,7 +636,7 @@ def write_entry(nvs_instance, key, datatype, encoding, value):
:return: None
"""
if datatype == "file":
if datatype == 'file':
abs_file_path = value
if os.path.isabs(value) is False:
script_dir = os.getcwd()
@@ -643,7 +645,7 @@ def write_entry(nvs_instance, key, datatype, encoding, value):
with open(abs_file_path, 'rb') as f:
value = f.read()
if datatype == "namespace":
if datatype == 'namespace':
nvs_instance.write_namespace(key)
else:
nvs_instance.write_entry(key, value, encoding)
@@ -667,13 +669,13 @@ def check_size(size):
# Set size
input_size = int(size, 0)
if input_size % 4096 != 0:
sys.exit("Size of partition must be multiple of 4096")
sys.exit('Size of partition must be multiple of 4096')
# Update size as a page needs to be reserved of size 4KB
input_size = input_size - Page.PAGE_PARAMS["max_size"]
input_size = input_size - Page.PAGE_PARAMS['max_size']
if input_size < (2 * Page.PAGE_PARAMS["max_size"]):
sys.exit("Minimum NVS partition size needed is 0x3000 bytes.")
if input_size < (2 * Page.PAGE_PARAMS['max_size']):
sys.exit('Minimum NVS partition size needed is 0x3000 bytes.')
return input_size
except Exception as e:
print(e)
@@ -708,7 +710,7 @@ def set_target_filepath(outdir, filepath):
if os.path.isabs(filepath):
if not outdir == os.getcwd():
print("\nWarning: `%s` \n\t==> absolute path given so outdir is ignored for this file." % filepath)
print('\nWarning: `%s` \n\t==> absolute path given so outdir is ignored for this file.' % filepath)
# Set to empty as outdir is ignored here
outdir = ''
@@ -728,11 +730,11 @@ def encrypt(args):
check_size(args.size)
if (args.keygen is False) and (not args.inputkey):
sys.exit("Error. --keygen or --inputkey argument needed.")
sys.exit('Error. --keygen or --inputkey argument needed.')
elif args.keygen and args.inputkey:
sys.exit("Error. --keygen and --inputkey both are not allowed.")
sys.exit('Error. --keygen and --inputkey both are not allowed.')
elif not args.keygen and args.keyfile:
print("\nWarning:","--inputkey argument is given. --keyfile argument will be ignored...")
print('\nWarning:','--inputkey argument is given. --keyfile argument will be ignored...')
if args.inputkey:
# Check if key file has .bin extension
@@ -835,7 +837,7 @@ def decrypt(args):
start_entry_offset += nvs_read_bytes
output_file.write(output_buf)
print("\nCreated NVS decrypted binary: ===>", args.output)
print('\nCreated NVS decrypted binary: ===>', args.output)
def generate_key(args):
@@ -850,7 +852,7 @@ def generate_key(args):
if not args.keyfile:
timestamp = datetime.datetime.now().strftime('%m-%d_%H-%M')
args.keyfile = "keys-" + timestamp + bin_ext
args.keyfile = 'keys-' + timestamp + bin_ext
keys_outdir = os.path.join(args.outdir,keys_dir, '')
# Create keys/ dir in <outdir> if does not exist
@@ -872,7 +874,7 @@ def generate_key(args):
with open(output_keyfile, 'wb') as output_keys_file:
output_keys_file.write(keys_buf)
print("\nCreated encryption keys: ===> ", output_keyfile)
print('\nCreated encryption keys: ===> ', output_keyfile)
return key
@@ -914,7 +916,7 @@ def generate(args, is_encr_enabled=False, encr_key=None):
else:
version_set = VERSION2_PRINT
print("\nCreating NVS binary with version:", version_set)
print('\nCreating NVS binary with version:', version_set)
line = input_file.readline().strip()
@@ -939,25 +941,25 @@ def generate(args, is_encr_enabled=False, encr_key=None):
try:
# Check key length
if len(data["key"]) > 15:
raise InputError("Length of key `{}` should be <= 15 characters.".format(data["key"]))
write_entry(nvs_obj, data["key"], data["type"], data["encoding"], data["value"])
if len(data['key']) > 15:
raise InputError('Length of key `{}` should be <= 15 characters.'.format(data['key']))
write_entry(nvs_obj, data['key'], data['type'], data['encoding'], data['value'])
except InputError as e:
print(e)
filedir, filename = os.path.split(args.output)
if filename:
print("\nWarning: NVS binary not created...")
print('\nWarning: NVS binary not created...')
os.remove(args.output)
if is_dir_new and not filedir == os.getcwd():
print("\nWarning: Output dir not created...")
print('\nWarning: Output dir not created...')
os.rmdir(filedir)
sys.exit(-2)
print("\nCreated NVS binary: ===>", args.output)
print('\nCreated NVS binary: ===>', args.output)
def main():
parser = argparse.ArgumentParser(description="\nESP NVS partition generation utility", formatter_class=argparse.RawTextHelpFormatter)
parser = argparse.ArgumentParser(description='\nESP NVS partition generation utility', formatter_class=argparse.RawTextHelpFormatter)
subparser = parser.add_subparsers(title='Commands',
dest='command',
help='\nRun nvs_partition_gen.py {command} -h for additional help\n\n')
@@ -1022,7 +1024,7 @@ def main():
\nVersion 2 - Multipage blob support enabled.\
\nDefault: Version 2''')
parser_encr.add_argument('--keygen',
action="store_true",
action='store_true',
default=False,
help='Generates key for encrypting NVS partition')
parser_encr.add_argument('--keyfile',
@@ -1057,5 +1059,5 @@ def main():
args.func(args)
if __name__ == "__main__":
if __name__ == '__main__':
main()

View File

@@ -17,8 +17,8 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import print_function, division
from __future__ import unicode_literals
from __future__ import division, print_function, unicode_literals
import argparse
import sys
@@ -28,7 +28,7 @@ quiet = False
def generate_blanked_file(size, output_path):
output = b"\xFF" * size
output = b'\xFF' * size
try:
stdout_binary = sys.stdout.buffer # Python 3
except AttributeError:

View File

@@ -20,19 +20,19 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import print_function, division
from __future__ import unicode_literals
from __future__ import division, print_function, unicode_literals
import argparse
import binascii
import errno
import hashlib
import os
import re
import struct
import sys
import hashlib
import binascii
import errno
MAX_PARTITION_LENGTH = 0xC00 # 3K for partition data (96 entries) leaves 1K in a 4K sector for signature
MD5_PARTITION_BEGIN = b"\xEB\xEB" + b"\xFF" * 14 # The first 2 bytes are like magic numbers for MD5 sum
MD5_PARTITION_BEGIN = b'\xEB\xEB' + b'\xFF' * 14 # The first 2 bytes are like magic numbers for MD5 sum
PARTITION_TABLE_SIZE = 0x1000 # Size of partition table
MIN_PARTITION_SUBTYPE_APP_OTA = 0x10
@@ -44,26 +44,26 @@ APP_TYPE = 0x00
DATA_TYPE = 0x01
TYPES = {
"app": APP_TYPE,
"data": DATA_TYPE,
'app': APP_TYPE,
'data': DATA_TYPE,
}
# Keep this map in sync with esp_partition_subtype_t enum in esp_partition.h
SUBTYPES = {
APP_TYPE: {
"factory": 0x00,
"test": 0x20,
'factory': 0x00,
'test': 0x20,
},
DATA_TYPE: {
"ota": 0x00,
"phy": 0x01,
"nvs": 0x02,
"coredump": 0x03,
"nvs_keys": 0x04,
"efuse": 0x05,
"esphttpd": 0x80,
"fat": 0x81,
"spiffs": 0x82,
'ota': 0x00,
'phy': 0x01,
'nvs': 0x02,
'coredump': 0x03,
'nvs_keys': 0x04,
'efuse': 0x05,
'esphttpd': 0x80,
'fat': 0x81,
'spiffs': 0x82,
},
}
@@ -103,14 +103,14 @@ class PartitionTable(list):
for line_no in range(len(lines)):
line = expand_vars(lines[line_no]).strip()
if line.startswith("#") or len(line) == 0:
if line.startswith('#') or len(line) == 0:
continue
try:
res.append(PartitionDefinition.from_csv(line, line_no + 1))
except InputError as e:
raise InputError("Error at line %d: %s" % (line_no + 1, e))
raise InputError('Error at line %d: %s' % (line_no + 1, e))
except Exception:
critical("Unexpected error parsing CSV line %d: %s" % (line_no + 1, line))
critical('Unexpected error parsing CSV line %d: %s' % (line_no + 1, line))
raise
# fix up missing offsets & negative sizes
@@ -118,10 +118,10 @@ class PartitionTable(list):
for e in res:
if e.offset is not None and e.offset < last_end:
if e == res[0]:
raise InputError("CSV Error: First partition offset 0x%x overlaps end of partition table 0x%x"
raise InputError('CSV Error: First partition offset 0x%x overlaps end of partition table 0x%x'
% (e.offset, last_end))
else:
raise InputError("CSV Error: Partitions overlap. Partition at line %d sets offset 0x%x. Previous partition ends 0x%x"
raise InputError('CSV Error: Partitions overlap. Partition at line %d sets offset 0x%x. Previous partition ends 0x%x'
% (e.line_no, e.offset, last_end))
if e.offset is None:
pad_to = 0x10000 if e.type == APP_TYPE else 4
@@ -186,19 +186,19 @@ class PartitionTable(list):
# print sorted duplicate partitions by name
if len(duplicates) != 0:
print("A list of partitions that have the same name:")
print('A list of partitions that have the same name:')
for p in sorted(self, key=lambda x:x.name):
if len(duplicates.intersection([p.name])) != 0:
print("%s" % (p.to_csv()))
raise InputError("Partition names must be unique")
print('%s' % (p.to_csv()))
raise InputError('Partition names must be unique')
# check for overlaps
last = None
for p in sorted(self, key=lambda x:x.offset):
if p.offset < offset_part_table + PARTITION_TABLE_SIZE:
raise InputError("Partition offset 0x%x is below 0x%x" % (p.offset, offset_part_table + PARTITION_TABLE_SIZE))
raise InputError('Partition offset 0x%x is below 0x%x' % (p.offset, offset_part_table + PARTITION_TABLE_SIZE))
if last is not None and p.offset < last.offset + last.size:
raise InputError("Partition at 0x%x overlaps 0x%x-0x%x" % (p.offset, last.offset, last.offset + last.size - 1))
raise InputError('Partition at 0x%x overlaps 0x%x-0x%x' % (p.offset, last.offset, last.offset + last.size - 1))
last = p
def flash_size(self):
@@ -218,7 +218,7 @@ class PartitionTable(list):
for o in range(0,len(b),32):
data = b[o:o + 32]
if len(data) != 32:
raise InputError("Partition table length must be a multiple of 32 bytes")
raise InputError('Partition table length must be a multiple of 32 bytes')
if data == b'\xFF' * 32:
return result # got end marker
if md5sum and data[:2] == MD5_PARTITION_BEGIN[:2]: # check only the magic number part
@@ -229,26 +229,26 @@ class PartitionTable(list):
else:
md5.update(data)
result.append(PartitionDefinition.from_binary(data))
raise InputError("Partition table is missing an end-of-table marker")
raise InputError('Partition table is missing an end-of-table marker')
def to_binary(self):
result = b"".join(e.to_binary() for e in self)
result = b''.join(e.to_binary() for e in self)
if md5sum:
result += MD5_PARTITION_BEGIN + hashlib.md5(result).digest()
if len(result) >= MAX_PARTITION_LENGTH:
raise InputError("Binary partition table length (%d) longer than max" % len(result))
result += b"\xFF" * (MAX_PARTITION_LENGTH - len(result)) # pad the sector, for signing
raise InputError('Binary partition table length (%d) longer than max' % len(result))
result += b'\xFF' * (MAX_PARTITION_LENGTH - len(result)) # pad the sector, for signing
return result
def to_csv(self, simple_formatting=False):
rows = ["# ESP-IDF Partition Table",
"# Name, Type, SubType, Offset, Size, Flags"]
rows = ['# ESP-IDF Partition Table',
'# Name, Type, SubType, Offset, Size, Flags']
rows += [x.to_csv(simple_formatting) for x in self]
return "\n".join(rows) + "\n"
return '\n'.join(rows) + '\n'
class PartitionDefinition(object):
MAGIC_BYTES = b"\xAA\x50"
MAGIC_BYTES = b'\xAA\x50'
ALIGNMENT = {
APP_TYPE: 0x10000,
@@ -258,15 +258,15 @@ class PartitionDefinition(object):
# dictionary maps flag name (as used in CSV flags list, property name)
# to bit set in flags words in binary format
FLAGS = {
"encrypted": 0
'encrypted': 0
}
# add subtypes for the 16 OTA slot values ("ota_XX, etc.")
for ota_slot in range(NUM_PARTITION_SUBTYPE_APP_OTA):
SUBTYPES[TYPES["app"]]["ota_%d" % ota_slot] = MIN_PARTITION_SUBTYPE_APP_OTA + ota_slot
SUBTYPES[TYPES['app']]['ota_%d' % ota_slot] = MIN_PARTITION_SUBTYPE_APP_OTA + ota_slot
def __init__(self):
self.name = ""
self.name = ''
self.type = None
self.subtype = None
self.offset = None
@@ -276,8 +276,8 @@ class PartitionDefinition(object):
@classmethod
def from_csv(cls, line, line_no):
""" Parse a line from the CSV """
line_w_defaults = line + ",,,," # lazy way to support default fields
fields = [f.strip() for f in line_w_defaults.split(",")]
line_w_defaults = line + ',,,,' # lazy way to support default fields
fields = [f.strip() for f in line_w_defaults.split(',')]
res = PartitionDefinition()
res.line_no = line_no
@@ -289,7 +289,7 @@ class PartitionDefinition(object):
if res.size is None:
raise InputError("Size field can't be empty")
flags = fields[5].split(":")
flags = fields[5].split(':')
for flag in flags:
if flag in cls.FLAGS:
setattr(res, flag, True)
@@ -305,7 +305,7 @@ class PartitionDefinition(object):
def __repr__(self):
def maybe_hex(x):
return "0x%x" % x if x is not None else "None"
return '0x%x' % x if x is not None else 'None'
return "PartitionDefinition('%s', 0x%x, 0x%x, %s, %s)" % (self.name, self.type, self.subtype or 0,
maybe_hex(self.offset), maybe_hex(self.size))
@@ -328,65 +328,65 @@ class PartitionDefinition(object):
return self.offset >= other.offset
def parse_type(self, strval):
if strval == "":
if strval == '':
raise InputError("Field 'type' can't be left empty.")
return parse_int(strval, TYPES)
def parse_subtype(self, strval):
if strval == "":
if strval == '':
return 0 # default
return parse_int(strval, SUBTYPES.get(self.type, {}))
def parse_address(self, strval):
if strval == "":
if strval == '':
return None # PartitionTable will fill in default
return parse_int(strval)
def verify(self):
if self.type is None:
raise ValidationError(self, "Type field is not set")
raise ValidationError(self, 'Type field is not set')
if self.subtype is None:
raise ValidationError(self, "Subtype field is not set")
raise ValidationError(self, 'Subtype field is not set')
if self.offset is None:
raise ValidationError(self, "Offset field is not set")
raise ValidationError(self, 'Offset field is not set')
align = self.ALIGNMENT.get(self.type, 4)
if self.offset % align:
raise ValidationError(self, "Offset 0x%x is not aligned to 0x%x" % (self.offset, align))
raise ValidationError(self, 'Offset 0x%x is not aligned to 0x%x' % (self.offset, align))
if self.size % align and secure:
raise ValidationError(self, "Size 0x%x is not aligned to 0x%x" % (self.size, align))
raise ValidationError(self, 'Size 0x%x is not aligned to 0x%x' % (self.size, align))
if self.size is None:
raise ValidationError(self, "Size field is not set")
raise ValidationError(self, 'Size field is not set')
if self.name in TYPES and TYPES.get(self.name, "") != self.type:
if self.name in TYPES and TYPES.get(self.name, '') != self.type:
critical("WARNING: Partition has name '%s' which is a partition type, but does not match this partition's "
"type (0x%x). Mistake in partition table?" % (self.name, self.type))
'type (0x%x). Mistake in partition table?' % (self.name, self.type))
all_subtype_names = []
for names in (t.keys() for t in SUBTYPES.values()):
all_subtype_names += names
if self.name in all_subtype_names and SUBTYPES.get(self.type, {}).get(self.name, "") != self.subtype:
if self.name in all_subtype_names and SUBTYPES.get(self.type, {}).get(self.name, '') != self.subtype:
critical("WARNING: Partition has name '%s' which is a partition subtype, but this partition has "
"non-matching type 0x%x and subtype 0x%x. Mistake in partition table?" % (self.name, self.type, self.subtype))
'non-matching type 0x%x and subtype 0x%x. Mistake in partition table?' % (self.name, self.type, self.subtype))
STRUCT_FORMAT = b"<2sBBLL16sL"
STRUCT_FORMAT = b'<2sBBLL16sL'
@classmethod
def from_binary(cls, b):
if len(b) != 32:
raise InputError("Partition definition length must be exactly 32 bytes. Got %d bytes." % len(b))
raise InputError('Partition definition length must be exactly 32 bytes. Got %d bytes.' % len(b))
res = cls()
(magic, res.type, res.subtype, res.offset,
res.size, res.name, flags) = struct.unpack(cls.STRUCT_FORMAT, b)
if b"\x00" in res.name: # strip null byte padding from name string
res.name = res.name[:res.name.index(b"\x00")]
if b'\x00' in res.name: # strip null byte padding from name string
res.name = res.name[:res.name.index(b'\x00')]
res.name = res.name.decode()
if magic != cls.MAGIC_BYTES:
raise InputError("Invalid magic bytes (%r) for partition definition" % magic)
raise InputError('Invalid magic bytes (%r) for partition definition' % magic)
for flag,bit in cls.FLAGS.items():
if flags & (1 << bit):
setattr(res, flag, True)
flags &= ~(1 << bit)
if flags != 0:
critical("WARNING: Partition definition had unknown flag(s) 0x%08x. Newer binary format?" % flags)
critical('WARNING: Partition definition had unknown flag(s) 0x%08x. Newer binary format?' % flags)
return res
def get_flags_list(self):
@@ -404,22 +404,22 @@ class PartitionDefinition(object):
def to_csv(self, simple_formatting=False):
def addr_format(a, include_sizes):
if not simple_formatting and include_sizes:
for (val, suffix) in [(0x100000, "M"), (0x400, "K")]:
for (val, suffix) in [(0x100000, 'M'), (0x400, 'K')]:
if a % val == 0:
return "%d%s" % (a // val, suffix)
return "0x%x" % a
return '%d%s' % (a // val, suffix)
return '0x%x' % a
def lookup_keyword(t, keywords):
for k,v in keywords.items():
if simple_formatting is False and t == v:
return k
return "%d" % t
return '%d' % t
def generate_text_flags():
""" colon-delimited list of flags """
return ":".join(self.get_flags_list())
return ':'.join(self.get_flags_list())
return ",".join([self.name,
return ','.join([self.name,
lookup_keyword(self.type, TYPES),
lookup_keyword(self.subtype, SUBTYPES.get(self.type, {})),
addr_format(self.offset, False),
@@ -432,17 +432,17 @@ def parse_int(v, keywords={}):
k/m/K/M suffixes and 'keyword' value lookup.
"""
try:
for letter, multiplier in [("k", 1024), ("m", 1024 * 1024)]:
for letter, multiplier in [('k', 1024), ('m', 1024 * 1024)]:
if v.lower().endswith(letter):
return parse_int(v[:-1], keywords) * multiplier
return int(v, 0)
except ValueError:
if len(keywords) == 0:
raise InputError("Invalid field value %s" % v)
raise InputError('Invalid field value %s' % v)
try:
return keywords[v.lower()]
except KeyError:
raise InputError("Value '%s' is not valid. Known keywords: %s" % (v, ", ".join(keywords)))
raise InputError("Value '%s' is not valid. Known keywords: %s" % (v, ', '.join(keywords)))
def main():
@@ -456,11 +456,11 @@ def main():
nargs='?', choices=['1MB', '2MB', '4MB', '8MB', '16MB'])
parser.add_argument('--disable-md5sum', help='Disable md5 checksum for the partition table', default=False, action='store_true')
parser.add_argument('--no-verify', help="Don't verify partition table fields", action='store_true')
parser.add_argument('--verify', '-v', help="Verify partition table fields (deprecated, this behaviour is "
"enabled by default and this flag does nothing.", action='store_true')
parser.add_argument('--verify', '-v', help='Verify partition table fields (deprecated, this behaviour is '
'enabled by default and this flag does nothing.', action='store_true')
parser.add_argument('--quiet', '-q', help="Don't print non-critical status messages to stderr", action='store_true')
parser.add_argument('--offset', '-o', help='Set offset partition table', default='0x8000')
parser.add_argument('--secure', help="Require app partitions to be suitable for secure boot", action='store_true')
parser.add_argument('--secure', help='Require app partitions to be suitable for secure boot', action='store_true')
parser.add_argument('input', help='Path to CSV or binary file to parse.', type=argparse.FileType('rb'))
parser.add_argument('output', help='Path to output converted binary or CSV file. Will use stdout if omitted.',
nargs='?', default='-')
@@ -474,19 +474,19 @@ def main():
input = args.input.read()
input_is_binary = input[0:2] == PartitionDefinition.MAGIC_BYTES
if input_is_binary:
status("Parsing binary partition input...")
status('Parsing binary partition input...')
table = PartitionTable.from_binary(input)
else:
input = input.decode()
status("Parsing CSV input...")
status('Parsing CSV input...')
table = PartitionTable.from_csv(input)
if not args.no_verify:
status("Verifying table...")
status('Verifying table...')
table.verify()
if args.flash_size:
size_mb = int(args.flash_size.replace("MB", ""))
size_mb = int(args.flash_size.replace('MB', ''))
size = size_mb * 1024 * 1024 # flash memory uses honest megabytes!
table_size = table.flash_size()
if size < table_size:
@@ -526,7 +526,7 @@ class InputError(RuntimeError):
class ValidationError(InputError):
def __init__(self, partition, message):
super(ValidationError, self).__init__(
"Partition %s invalid: %s" % (partition.name, message))
'Partition %s invalid: %s' % (partition.name, message))
if __name__ == '__main__':

View File

@@ -16,20 +16,21 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import print_function, division
from __future__ import division, print_function
import argparse
import os
import sys
import subprocess
import tempfile
import re
import gen_esp32part as gen
import subprocess
import sys
import tempfile
import gen_esp32part as gen
__version__ = '2.0'
COMPONENTS_PATH = os.path.expandvars(os.path.join("$IDF_PATH", "components"))
ESPTOOL_PY = os.path.join(COMPONENTS_PATH, "esptool_py", "esptool", "esptool.py")
COMPONENTS_PATH = os.path.expandvars(os.path.join('$IDF_PATH', 'components'))
ESPTOOL_PY = os.path.join(COMPONENTS_PATH, 'esptool_py', 'esptool', 'esptool.py')
PARTITION_TABLE_OFFSET = 0x8000
@@ -78,14 +79,14 @@ class ParttoolTarget():
def parse_esptool_args(esptool_args):
results = list()
for arg in esptool_args:
pattern = re.compile(r"(.+)=(.+)")
pattern = re.compile(r'(.+)=(.+)')
result = pattern.match(arg)
try:
key = result.group(1)
value = result.group(2)
results.extend(["--" + key, value])
results.extend(['--' + key, value])
except AttributeError:
results.extend(["--" + arg])
results.extend(['--' + arg])
return results
self.esptool_args = parse_esptool_args(esptool_args)
@@ -95,14 +96,14 @@ class ParttoolTarget():
if partition_table_file:
partition_table = None
with open(partition_table_file, "rb") as f:
with open(partition_table_file, 'rb') as f:
input_is_binary = (f.read(2) == gen.PartitionDefinition.MAGIC_BYTES)
f.seek(0)
if input_is_binary:
partition_table = gen.PartitionTable.from_binary(f.read())
if partition_table is None:
with open(partition_table_file, "r") as f:
with open(partition_table_file, 'r') as f:
f.seek(0)
partition_table = gen.PartitionTable.from_csv(f.read())
else:
@@ -110,8 +111,8 @@ class ParttoolTarget():
temp_file.close()
try:
self._call_esptool(["read_flash", str(partition_table_offset), str(gen.MAX_PARTITION_LENGTH), temp_file.name])
with open(temp_file.name, "rb") as f:
self._call_esptool(['read_flash', str(partition_table_offset), str(gen.MAX_PARTITION_LENGTH), temp_file.name])
with open(temp_file.name, 'rb') as f:
partition_table = gen.PartitionTable.from_binary(f.read())
finally:
os.unlink(temp_file.name)
@@ -125,18 +126,18 @@ class ParttoolTarget():
esptool_args = [sys.executable, ESPTOOL_PY] + self.esptool_args
if self.port:
esptool_args += ["--port", self.port]
esptool_args += ['--port', self.port]
if self.baud:
esptool_args += ["--baud", str(self.baud)]
esptool_args += ['--baud', str(self.baud)]
esptool_args += args
print("Running %s..." % (" ".join(esptool_args)))
print('Running %s...' % (' '.join(esptool_args)))
try:
subprocess.check_call(esptool_args, stdout=out, stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as e:
print("An exception: **", str(e), "** occurred in _call_esptool.", file=out)
print('An exception: **', str(e), '** occurred in _call_esptool.', file=out)
raise e
def get_partition_info(self, partition_id):
@@ -149,37 +150,37 @@ class ParttoolTarget():
if not partition_id.part_list:
partition = partition[0]
else: # default boot partition
search = ["factory"] + ["ota_{}".format(d) for d in range(16)]
search = ['factory'] + ['ota_{}'.format(d) for d in range(16)]
for subtype in search:
partition = next(self.partition_table.find_by_type("app", subtype), None)
partition = next(self.partition_table.find_by_type('app', subtype), None)
if partition:
break
if not partition:
raise Exception("Partition does not exist")
raise Exception('Partition does not exist')
return partition
def erase_partition(self, partition_id):
partition = self.get_partition_info(partition_id)
self._call_esptool(["erase_region", str(partition.offset), str(partition.size)] + self.esptool_erase_args)
self._call_esptool(['erase_region', str(partition.offset), str(partition.size)] + self.esptool_erase_args)
def read_partition(self, partition_id, output):
partition = self.get_partition_info(partition_id)
self._call_esptool(["read_flash", str(partition.offset), str(partition.size), output] + self.esptool_read_args)
self._call_esptool(['read_flash', str(partition.offset), str(partition.size), output] + self.esptool_read_args)
def write_partition(self, partition_id, input):
self.erase_partition(partition_id)
partition = self.get_partition_info(partition_id)
with open(input, "rb") as input_file:
with open(input, 'rb') as input_file:
content_len = len(input_file.read())
if content_len > partition.size:
raise Exception("Input file size exceeds partition size")
raise Exception('Input file size exceeds partition size')
self._call_esptool(["write_flash", str(partition.offset), input] + self.esptool_write_args)
self._call_esptool(['write_flash', str(partition.offset), input] + self.esptool_write_args)
def _write_partition(target, partition_id, input):
@@ -214,41 +215,41 @@ def _get_partition_info(target, partition_id, info):
try:
for p in partitions:
info_dict = {
"name": '{}'.format(p.name),
"type": '{}'.format(p.type),
"subtype": '{}'.format(p.subtype),
"offset": '0x{:x}'.format(p.offset),
"size": '0x{:x}'.format(p.size),
"encrypted": '{}'.format(p.encrypted)
'name': '{}'.format(p.name),
'type': '{}'.format(p.type),
'subtype': '{}'.format(p.subtype),
'offset': '0x{:x}'.format(p.offset),
'size': '0x{:x}'.format(p.size),
'encrypted': '{}'.format(p.encrypted)
}
for i in info:
infos += [info_dict[i]]
except KeyError:
raise RuntimeError("Request for unknown partition info {}".format(i))
raise RuntimeError('Request for unknown partition info {}'.format(i))
print(" ".join(infos))
print(' '.join(infos))
def main():
global quiet
parser = argparse.ArgumentParser("ESP-IDF Partitions Tool")
parser = argparse.ArgumentParser('ESP-IDF Partitions Tool')
parser.add_argument("--quiet", "-q", help="suppress stderr messages", action="store_true")
parser.add_argument("--esptool-args", help="additional main arguments for esptool", nargs="+")
parser.add_argument("--esptool-write-args", help="additional subcommand arguments when writing to flash", nargs="+")
parser.add_argument("--esptool-read-args", help="additional subcommand arguments when reading flash", nargs="+")
parser.add_argument("--esptool-erase-args", help="additional subcommand arguments when erasing regions of flash", nargs="+")
parser.add_argument('--quiet', '-q', help='suppress stderr messages', action='store_true')
parser.add_argument('--esptool-args', help='additional main arguments for esptool', nargs='+')
parser.add_argument('--esptool-write-args', help='additional subcommand arguments when writing to flash', nargs='+')
parser.add_argument('--esptool-read-args', help='additional subcommand arguments when reading flash', nargs='+')
parser.add_argument('--esptool-erase-args', help='additional subcommand arguments when erasing regions of flash', nargs='+')
# By default the device attached to the specified port is queried for the partition table. If a partition table file
# is specified, that is used instead.
parser.add_argument("--port", "-p", help="port where the target device of the command is connected to; the partition table is sourced from this device \
when the partition table file is not defined")
parser.add_argument("--baud", "-b", help="baudrate to use", type=int)
parser.add_argument('--port', '-p', help='port where the target device of the command is connected to; the partition table is sourced from this device \
when the partition table file is not defined')
parser.add_argument('--baud', '-b', help='baudrate to use', type=int)
parser.add_argument("--partition-table-offset", "-o", help="offset to read the partition table from", type=str)
parser.add_argument("--partition-table-file", "-f", help="file (CSV/binary) to read the partition table from; \
overrides device attached to specified port as the partition table source when defined")
parser.add_argument('--partition-table-offset', '-o', help='offset to read the partition table from', type=str)
parser.add_argument('--partition-table-file', '-f', help='file (CSV/binary) to read the partition table from; \
overrides device attached to specified port as the partition table source when defined')
partition_selection_parser = argparse.ArgumentParser(add_help=False)
@@ -256,30 +257,30 @@ def main():
# partition name or the first partition that matches the specified type/subtype
partition_selection_args = partition_selection_parser.add_mutually_exclusive_group()
partition_selection_args.add_argument("--partition-name", "-n", help="name of the partition")
partition_selection_args.add_argument("--partition-type", "-t", help="type of the partition")
partition_selection_args.add_argument('--partition-boot-default', "-d", help='select the default boot partition \
using the same fallback logic as the IDF bootloader', action="store_true")
partition_selection_args.add_argument('--partition-name', '-n', help='name of the partition')
partition_selection_args.add_argument('--partition-type', '-t', help='type of the partition')
partition_selection_args.add_argument('--partition-boot-default', '-d', help='select the default boot partition \
using the same fallback logic as the IDF bootloader', action='store_true')
partition_selection_parser.add_argument("--partition-subtype", "-s", help="subtype of the partition")
partition_selection_parser.add_argument('--partition-subtype', '-s', help='subtype of the partition')
subparsers = parser.add_subparsers(dest="operation", help="run parttool -h for additional help")
subparsers = parser.add_subparsers(dest='operation', help='run parttool -h for additional help')
# Specify the supported operations
read_part_subparser = subparsers.add_parser("read_partition", help="read partition from device and dump contents into a file",
read_part_subparser = subparsers.add_parser('read_partition', help='read partition from device and dump contents into a file',
parents=[partition_selection_parser])
read_part_subparser.add_argument("--output", help="file to dump the read partition contents to")
read_part_subparser.add_argument('--output', help='file to dump the read partition contents to')
write_part_subparser = subparsers.add_parser("write_partition", help="write contents of a binary file to partition on device",
write_part_subparser = subparsers.add_parser('write_partition', help='write contents of a binary file to partition on device',
parents=[partition_selection_parser])
write_part_subparser.add_argument("--input", help="file whose contents are to be written to the partition offset")
write_part_subparser.add_argument('--input', help='file whose contents are to be written to the partition offset')
subparsers.add_parser("erase_partition", help="erase the contents of a partition on the device", parents=[partition_selection_parser])
subparsers.add_parser('erase_partition', help='erase the contents of a partition on the device', parents=[partition_selection_parser])
print_partition_info_subparser = subparsers.add_parser("get_partition_info", help="get partition information", parents=[partition_selection_parser])
print_partition_info_subparser.add_argument("--info", help="type of partition information to get",
choices=["name", "type", "subtype", "offset", "size", "encrypted"], default=["offset", "size"], nargs="+")
print_partition_info_subparser.add_argument('--part_list', help="Get a list of partitions suitable for a given type", action='store_true')
print_partition_info_subparser = subparsers.add_parser('get_partition_info', help='get partition information', parents=[partition_selection_parser])
print_partition_info_subparser.add_argument('--info', help='type of partition information to get',
choices=['name', 'type', 'subtype', 'offset', 'size', 'encrypted'], default=['offset', 'size'], nargs='+')
print_partition_info_subparser.add_argument('--part_list', help='Get a list of partitions suitable for a given type', action='store_true')
args = parser.parse_args()
quiet = args.quiet
@@ -295,40 +296,40 @@ def main():
partition_id = PartitionName(args.partition_name)
elif args.partition_type:
if not args.partition_subtype:
raise RuntimeError("--partition-subtype should be defined when --partition-type is defined")
raise RuntimeError('--partition-subtype should be defined when --partition-type is defined')
partition_id = PartitionType(args.partition_type, args.partition_subtype, getattr(args, 'part_list', None))
elif args.partition_boot_default:
partition_id = PARTITION_BOOT_DEFAULT
else:
raise RuntimeError("Partition to operate on should be defined using --partition-name OR \
partition-type,--partition-subtype OR partition-boot-default")
raise RuntimeError('Partition to operate on should be defined using --partition-name OR \
partition-type,--partition-subtype OR partition-boot-default')
# Prepare the device to perform operation on
target_args = {}
if args.port:
target_args["port"] = args.port
target_args['port'] = args.port
if args.baud:
target_args["baud"] = args.baud
target_args['baud'] = args.baud
if args.partition_table_file:
target_args["partition_table_file"] = args.partition_table_file
target_args['partition_table_file'] = args.partition_table_file
if args.partition_table_offset:
target_args["partition_table_offset"] = int(args.partition_table_offset, 0)
target_args['partition_table_offset'] = int(args.partition_table_offset, 0)
if args.esptool_args:
target_args["esptool_args"] = args.esptool_args
target_args['esptool_args'] = args.esptool_args
if args.esptool_write_args:
target_args["esptool_write_args"] = args.esptool_write_args
target_args['esptool_write_args'] = args.esptool_write_args
if args.esptool_read_args:
target_args["esptool_read_args"] = args.esptool_read_args
target_args['esptool_read_args'] = args.esptool_read_args
if args.esptool_erase_args:
target_args["esptool_erase_args"] = args.esptool_erase_args
target_args['esptool_erase_args'] = args.esptool_erase_args
target = ParttoolTarget(**target_args)
@@ -336,9 +337,9 @@ def main():
common_args = {'target':target, 'partition_id':partition_id}
parttool_ops = {
'erase_partition':(_erase_partition, []),
'read_partition':(_read_partition, ["output"]),
'write_partition':(_write_partition, ["input"]),
'get_partition_info':(_get_partition_info, ["info"])
'read_partition':(_read_partition, ['output']),
'write_partition':(_write_partition, ['input']),
'get_partition_info':(_get_partition_info, ['info'])
}
(op, op_args) = parttool_ops[args.operation]

View File

@@ -1,18 +1,19 @@
#!/usr/bin/env python
from __future__ import print_function, division
import unittest
import struct
from __future__ import division, print_function
import csv
import sys
import subprocess
import tempfile
import os
import io
import os
import struct
import subprocess
import sys
import tempfile
import unittest
try:
import gen_esp32part
except ImportError:
sys.path.append("..")
sys.path.append('..')
import gen_esp32part
SIMPLE_CSV = """
@@ -20,40 +21,40 @@ SIMPLE_CSV = """
factory,0,2,65536,1048576,
"""
LONGER_BINARY_TABLE = b""
LONGER_BINARY_TABLE = b''
# type 0x00, subtype 0x00,
# offset 64KB, size 1MB
LONGER_BINARY_TABLE += b"\xAA\x50\x00\x00" + \
b"\x00\x00\x01\x00" + \
b"\x00\x00\x10\x00" + \
b"factory\0" + (b"\0" * 8) + \
b"\x00\x00\x00\x00"
LONGER_BINARY_TABLE += b'\xAA\x50\x00\x00' + \
b'\x00\x00\x01\x00' + \
b'\x00\x00\x10\x00' + \
b'factory\0' + (b'\0' * 8) + \
b'\x00\x00\x00\x00'
# type 0x01, subtype 0x20,
# offset 0x110000, size 128KB
LONGER_BINARY_TABLE += b"\xAA\x50\x01\x20" + \
b"\x00\x00\x11\x00" + \
b"\x00\x02\x00\x00" + \
b"data" + (b"\0" * 12) + \
b"\x00\x00\x00\x00"
LONGER_BINARY_TABLE += b'\xAA\x50\x01\x20' + \
b'\x00\x00\x11\x00' + \
b'\x00\x02\x00\x00' + \
b'data' + (b'\0' * 12) + \
b'\x00\x00\x00\x00'
# type 0x10, subtype 0x00,
# offset 0x150000, size 1MB
LONGER_BINARY_TABLE += b"\xAA\x50\x10\x00" + \
b"\x00\x00\x15\x00" + \
b"\x00\x10\x00\x00" + \
b"second" + (b"\0" * 10) + \
b"\x00\x00\x00\x00"
LONGER_BINARY_TABLE += b'\xAA\x50\x10\x00' + \
b'\x00\x00\x15\x00' + \
b'\x00\x10\x00\x00' + \
b'second' + (b'\0' * 10) + \
b'\x00\x00\x00\x00'
# MD5 checksum
LONGER_BINARY_TABLE += b"\xEB\xEB" + b"\xFF" * 14
LONGER_BINARY_TABLE += b'\xEB\xEB' + b'\xFF' * 14
LONGER_BINARY_TABLE += b'\xf9\xbd\x06\x1b\x45\x68\x6f\x86\x57\x1a\x2c\xd5\x2a\x1d\xa6\x5b'
# empty partition
LONGER_BINARY_TABLE += b"\xFF" * 32
LONGER_BINARY_TABLE += b'\xFF' * 32
def _strip_trailing_ffs(binary_table):
"""
Strip all FFs down to the last 32 bytes (terminating entry)
"""
while binary_table.endswith(b"\xFF" * 64):
while binary_table.endswith(b'\xFF' * 64):
binary_table = binary_table[0:len(binary_table) - 32]
return binary_table
@@ -75,7 +76,7 @@ class CSVParserTests(Py23TestCase):
def test_simple_partition(self):
table = gen_esp32part.PartitionTable.from_csv(SIMPLE_CSV)
self.assertEqual(len(table), 1)
self.assertEqual(table[0].name, "factory")
self.assertEqual(table[0].name, 'factory')
self.assertEqual(table[0].type, 0)
self.assertEqual(table[0].subtype, 2)
self.assertEqual(table[0].offset, 65536)
@@ -86,7 +87,7 @@ class CSVParserTests(Py23TestCase):
# Name,Type, SubType,Offset,Size
ihavenotype,
"""
with self.assertRaisesRegex(gen_esp32part.InputError, "type"):
with self.assertRaisesRegex(gen_esp32part.InputError, 'type'):
gen_esp32part.PartitionTable.from_csv(csv)
def test_type_subtype_names(self):
@@ -115,15 +116,15 @@ myota_status, data, ota,, 0x100000
nomagic = gen_esp32part.PartitionTable.from_csv(csv_nomagicnumbers)
nomagic.verify()
self.assertEqual(nomagic["myapp"].type, 0)
self.assertEqual(nomagic["myapp"].subtype, 0)
self.assertEqual(nomagic["myapp"], magic["myapp"])
self.assertEqual(nomagic["myota_0"].type, 0)
self.assertEqual(nomagic["myota_0"].subtype, 0x10)
self.assertEqual(nomagic["myota_0"], magic["myota_0"])
self.assertEqual(nomagic["myota_15"], magic["myota_15"])
self.assertEqual(nomagic["mytest"], magic["mytest"])
self.assertEqual(nomagic["myota_status"], magic["myota_status"])
self.assertEqual(nomagic['myapp'].type, 0)
self.assertEqual(nomagic['myapp'].subtype, 0)
self.assertEqual(nomagic['myapp'], magic['myapp'])
self.assertEqual(nomagic['myota_0'].type, 0)
self.assertEqual(nomagic['myota_0'].subtype, 0x10)
self.assertEqual(nomagic['myota_0'], magic['myota_0'])
self.assertEqual(nomagic['myota_15'], magic['myota_15'])
self.assertEqual(nomagic['mytest'], magic['mytest'])
self.assertEqual(nomagic['myota_status'], magic['myota_status'])
# self.assertEqual(nomagic.to_binary(), magic.to_binary())
@@ -176,7 +177,7 @@ second, data, 0x15, , 1M
first, app, factory, 0x100000, 2M
second, app, ota_0, 0x200000, 1M
"""
with self.assertRaisesRegex(gen_esp32part.InputError, "overlap"):
with self.assertRaisesRegex(gen_esp32part.InputError, 'overlap'):
t = gen_esp32part.PartitionTable.from_csv(csv)
t.verify()
@@ -185,7 +186,7 @@ second, app, ota_0, 0x200000, 1M
first, app, factory, 0x100000, 1M
first, app, ota_0, 0x200000, 1M
"""
with self.assertRaisesRegex(gen_esp32part.InputError, "Partition names must be unique"):
with self.assertRaisesRegex(gen_esp32part.InputError, 'Partition names must be unique'):
t = gen_esp32part.PartitionTable.from_csv(csv)
t.verify()
@@ -200,10 +201,10 @@ first, 0x30, 0xEE, 0x100400, 0x300000
self.assertEqual(len(tb), 64 + 32)
self.assertEqual(b'\xAA\x50', tb[0:2]) # magic
self.assertEqual(b'\x30\xee', tb[2:4]) # type, subtype
eo, es = struct.unpack("<LL", tb[4:12])
eo, es = struct.unpack('<LL', tb[4:12])
self.assertEqual(eo, 0x100400) # offset
self.assertEqual(es, 0x300000) # size
self.assertEqual(b"\xEB\xEB" + b"\xFF" * 14, tb[32:48])
self.assertEqual(b'\xEB\xEB' + b'\xFF' * 14, tb[32:48])
self.assertEqual(b'\x43\x03\x3f\x33\x40\x87\x57\x51\x69\x83\x9b\x40\x61\xb1\x27\x26', tb[48:64])
def test_multiple_entries(self):
@@ -233,12 +234,12 @@ class BinaryParserTests(Py23TestCase):
def test_parse_one_entry(self):
# type 0x30, subtype 0xee,
# offset 1MB, size 2MB
entry = b"\xAA\x50\x30\xee" + \
b"\x00\x00\x10\x00" + \
b"\x00\x00\x20\x00" + \
b"0123456789abc\0\0\0" + \
b"\x00\x00\x00\x00" + \
b"\xFF" * 32
entry = b'\xAA\x50\x30\xee' + \
b'\x00\x00\x10\x00' + \
b'\x00\x00\x20\x00' + \
b'0123456789abc\0\0\0' + \
b'\x00\x00\x00\x00' + \
b'\xFF' * 32
# verify that parsing 32 bytes as a table
# or as a single Definition are the same thing
t = gen_esp32part.PartitionTable.from_binary(entry)
@@ -253,7 +254,7 @@ class BinaryParserTests(Py23TestCase):
self.assertEqual(e.subtype, 0xEE)
self.assertEqual(e.offset, 0x100000)
self.assertEqual(e.size, 0x200000)
self.assertEqual(e.name, "0123456789abc")
self.assertEqual(e.name, '0123456789abc')
def test_multiple_entries(self):
t = gen_esp32part.PartitionTable.from_binary(LONGER_BINARY_TABLE)
@@ -261,53 +262,53 @@ class BinaryParserTests(Py23TestCase):
self.assertEqual(3, len(t))
self.assertEqual(t[0].type, gen_esp32part.APP_TYPE)
self.assertEqual(t[0].name, "factory")
self.assertEqual(t[0].name, 'factory')
self.assertEqual(t[1].type, gen_esp32part.DATA_TYPE)
self.assertEqual(t[1].name, "data")
self.assertEqual(t[1].name, 'data')
self.assertEqual(t[2].type, 0x10)
self.assertEqual(t[2].name, "second")
self.assertEqual(t[2].name, 'second')
round_trip = _strip_trailing_ffs(t.to_binary())
self.assertEqual(round_trip, LONGER_BINARY_TABLE)
def test_bad_magic(self):
bad_magic = b"OHAI" + \
b"\x00\x00\x10\x00" + \
b"\x00\x00\x20\x00" + \
b"0123456789abc\0\0\0" + \
b"\x00\x00\x00\x00"
with self.assertRaisesRegex(gen_esp32part.InputError, "Invalid magic bytes"):
bad_magic = b'OHAI' + \
b'\x00\x00\x10\x00' + \
b'\x00\x00\x20\x00' + \
b'0123456789abc\0\0\0' + \
b'\x00\x00\x00\x00'
with self.assertRaisesRegex(gen_esp32part.InputError, 'Invalid magic bytes'):
gen_esp32part.PartitionTable.from_binary(bad_magic)
def test_bad_length(self):
bad_length = b"OHAI" + \
b"\x00\x00\x10\x00" + \
b"\x00\x00\x20\x00" + \
b"0123456789"
with self.assertRaisesRegex(gen_esp32part.InputError, "32 bytes"):
bad_length = b'OHAI' + \
b'\x00\x00\x10\x00' + \
b'\x00\x00\x20\x00' + \
b'0123456789'
with self.assertRaisesRegex(gen_esp32part.InputError, '32 bytes'):
gen_esp32part.PartitionTable.from_binary(bad_length)
class CSVOutputTests(Py23TestCase):
def _readcsv(self, source_str):
return list(csv.reader(source_str.split("\n")))
return list(csv.reader(source_str.split('\n')))
def test_output_simple_formatting(self):
table = gen_esp32part.PartitionTable.from_csv(SIMPLE_CSV)
as_csv = table.to_csv(True)
c = self._readcsv(as_csv)
# first two lines should start with comments
self.assertEqual(c[0][0][0], "#")
self.assertEqual(c[1][0][0], "#")
self.assertEqual(c[0][0][0], '#')
self.assertEqual(c[1][0][0], '#')
row = c[2]
self.assertEqual(row[0], "factory")
self.assertEqual(row[1], "0")
self.assertEqual(row[2], "2")
self.assertEqual(row[3], "0x10000") # reformatted as hex
self.assertEqual(row[4], "0x100000") # also hex
self.assertEqual(row[0], 'factory')
self.assertEqual(row[1], '0')
self.assertEqual(row[2], '2')
self.assertEqual(row[3], '0x10000') # reformatted as hex
self.assertEqual(row[4], '0x100000') # also hex
# round trip back to a PartitionTable and check is identical
roundtrip = gen_esp32part.PartitionTable.from_csv(as_csv)
@@ -318,14 +319,14 @@ class CSVOutputTests(Py23TestCase):
as_csv = table.to_csv(False)
c = self._readcsv(as_csv)
# first two lines should start with comments
self.assertEqual(c[0][0][0], "#")
self.assertEqual(c[1][0][0], "#")
self.assertEqual(c[0][0][0], '#')
self.assertEqual(c[1][0][0], '#')
row = c[2]
self.assertEqual(row[0], "factory")
self.assertEqual(row[1], "app")
self.assertEqual(row[2], "2")
self.assertEqual(row[3], "0x10000")
self.assertEqual(row[4], "1M")
self.assertEqual(row[0], 'factory')
self.assertEqual(row[1], 'app')
self.assertEqual(row[2], '2')
self.assertEqual(row[3], '0x10000')
self.assertEqual(row[4], '1M')
# round trip back to a PartitionTable and check is identical
roundtrip = gen_esp32part.PartitionTable.from_csv(as_csv)
@@ -344,18 +345,18 @@ class CommandLineTests(Py23TestCase):
f.write(LONGER_BINARY_TABLE)
# run gen_esp32part.py to convert binary file to CSV
output = subprocess.check_output([sys.executable, "../gen_esp32part.py",
output = subprocess.check_output([sys.executable, '../gen_esp32part.py',
binpath, csvpath], stderr=subprocess.STDOUT)
# reopen the CSV and check the generated binary is identical
self.assertNotIn(b"WARNING", output)
self.assertNotIn(b'WARNING', output)
with open(csvpath, 'r') as f:
from_csv = gen_esp32part.PartitionTable.from_csv(f.read())
self.assertEqual(_strip_trailing_ffs(from_csv.to_binary()), LONGER_BINARY_TABLE)
# run gen_esp32part.py to conver the CSV to binary again
output = subprocess.check_output([sys.executable, "../gen_esp32part.py",
output = subprocess.check_output([sys.executable, '../gen_esp32part.py',
csvpath, binpath], stderr=subprocess.STDOUT)
self.assertNotIn(b"WARNING", output)
self.assertNotIn(b'WARNING', output)
# assert that file reads back as identical
with open(binpath, 'rb') as f:
binary_readback = f.read()
@@ -377,7 +378,7 @@ class VerificationTests(Py23TestCase):
# Name,Type, SubType,Offset,Size
app,app, factory, 32K, 1M
"""
with self.assertRaisesRegex(gen_esp32part.ValidationError, r"Offset.+not aligned"):
with self.assertRaisesRegex(gen_esp32part.ValidationError, r'Offset.+not aligned'):
t = gen_esp32part.PartitionTable.from_csv(csv)
t.verify()
@@ -385,16 +386,16 @@ app,app, factory, 32K, 1M
try:
sys.stderr = io.StringIO() # capture stderr
csv_1 = "app, 1, 2, 32K, 1M\n"
csv_1 = 'app, 1, 2, 32K, 1M\n'
gen_esp32part.PartitionTable.from_csv(csv_1).verify()
self.assertIn("WARNING", sys.stderr.getvalue())
self.assertIn("partition type", sys.stderr.getvalue())
self.assertIn('WARNING', sys.stderr.getvalue())
self.assertIn('partition type', sys.stderr.getvalue())
sys.stderr = io.StringIO()
csv_2 = "ota_0, app, ota_1, , 1M\n"
csv_2 = 'ota_0, app, ota_1, , 1M\n'
gen_esp32part.PartitionTable.from_csv(csv_2).verify()
self.assertIn("WARNING", sys.stderr.getvalue())
self.assertIn("partition subtype", sys.stderr.getvalue())
self.assertIn('WARNING', sys.stderr.getvalue())
self.assertIn('partition subtype', sys.stderr.getvalue())
finally:
sys.stderr = sys.__stderr__
@@ -404,13 +405,13 @@ class PartToolTests(Py23TestCase):
def _run_parttool(self, csvcontents, args):
csvpath = tempfile.mktemp()
with open(csvpath, "w") as f:
with open(csvpath, 'w') as f:
f.write(csvcontents)
try:
output = subprocess.check_output([sys.executable, "../parttool.py", "-q", "--partition-table-file",
csvpath, "get_partition_info"] + args,
output = subprocess.check_output([sys.executable, '../parttool.py', '-q', '--partition-table-file',
csvpath, 'get_partition_info'] + args,
stderr=subprocess.STDOUT)
self.assertNotIn(b"WARNING", output)
self.assertNotIn(b'WARNING', output)
return output.strip()
finally:
os.remove(csvpath)
@@ -431,41 +432,41 @@ nvs_key2, data, nvs_keys, 0x119000, 0x1000, encrypted
return self._run_parttool(csv, args)
self.assertEqual(
rpt(["--partition-type", "data", "--partition-subtype", "nvs", "--info", "offset"]), b"0x9000")
rpt(['--partition-type', 'data', '--partition-subtype', 'nvs', '--info', 'offset']), b'0x9000')
self.assertEqual(
rpt(["--partition-type", "data", "--partition-subtype", "nvs", "--info", "size"]), b"0x4000")
rpt(['--partition-type', 'data', '--partition-subtype', 'nvs', '--info', 'size']), b'0x4000')
self.assertEqual(
rpt(["--partition-name", "otadata", "--info", "offset"]), b"0xd000")
rpt(['--partition-name', 'otadata', '--info', 'offset']), b'0xd000')
self.assertEqual(
rpt(["--partition-boot-default", "--info", "offset"]), b"0x10000")
rpt(['--partition-boot-default', '--info', 'offset']), b'0x10000')
self.assertEqual(
rpt(["--partition-type", "data", "--partition-subtype", "nvs", "--info", "name", "offset", "size", "encrypted"]),
b"nvs 0x9000 0x4000 False")
rpt(['--partition-type', 'data', '--partition-subtype', 'nvs', '--info', 'name', 'offset', 'size', 'encrypted']),
b'nvs 0x9000 0x4000 False')
self.assertEqual(
rpt(["--partition-type", "data", "--partition-subtype", "nvs", "--info", "name", "offset", "size", "encrypted", "--part_list"]),
b"nvs 0x9000 0x4000 False nvs1_user 0x110000 0x4000 False nvs2_user 0x114000 0x4000 False")
rpt(['--partition-type', 'data', '--partition-subtype', 'nvs', '--info', 'name', 'offset', 'size', 'encrypted', '--part_list']),
b'nvs 0x9000 0x4000 False nvs1_user 0x110000 0x4000 False nvs2_user 0x114000 0x4000 False')
self.assertEqual(
rpt(["--partition-type", "data", "--partition-subtype", "nvs", "--info", "name", "--part_list"]),
b"nvs nvs1_user nvs2_user")
rpt(['--partition-type', 'data', '--partition-subtype', 'nvs', '--info', 'name', '--part_list']),
b'nvs nvs1_user nvs2_user')
self.assertEqual(
rpt(["--partition-type", "data", "--partition-subtype", "nvs_keys", "--info", "name", "--part_list"]),
b"nvs_key1 nvs_key2")
rpt(['--partition-type', 'data', '--partition-subtype', 'nvs_keys', '--info', 'name', '--part_list']),
b'nvs_key1 nvs_key2')
self.assertEqual(
rpt(["--partition-name", "nvs", "--info", "encrypted"]), b"False")
rpt(['--partition-name', 'nvs', '--info', 'encrypted']), b'False')
self.assertEqual(
rpt(["--partition-name", "nvs1_user", "--info", "encrypted"]), b"False")
rpt(['--partition-name', 'nvs1_user', '--info', 'encrypted']), b'False')
self.assertEqual(
rpt(["--partition-name", "nvs2_user", "--info", "encrypted"]), b"False")
rpt(['--partition-name', 'nvs2_user', '--info', 'encrypted']), b'False')
self.assertEqual(
rpt(["--partition-name", "nvs_key1", "--info", "encrypted"]), b"True")
rpt(['--partition-name', 'nvs_key1', '--info', 'encrypted']), b'True')
self.assertEqual(
rpt(["--partition-name", "nvs_key2", "--info", "encrypted"]), b"True")
rpt(['--partition-name', 'nvs_key2', '--info', 'encrypted']), b'True')
self.assertEqual(
rpt(["--partition-type", "data", "--partition-subtype", "nvs_keys", "--info", "name", "encrypted", "--part_list"]),
b"nvs_key1 True nvs_key2 True")
rpt(['--partition-type', 'data', '--partition-subtype', 'nvs_keys', '--info', 'name', 'encrypted', '--part_list']),
b'nvs_key1 True nvs_key2 True')
self.assertEqual(
rpt(["--partition-type", "data", "--partition-subtype", "nvs", "--info", "name", "encrypted", "--part_list"]),
b"nvs False nvs1_user False nvs2_user False")
rpt(['--partition-type', 'data', '--partition-subtype', 'nvs', '--info', 'name', 'encrypted', '--part_list']),
b'nvs False nvs1_user False nvs2_user False')
def test_fallback(self):
csv = """
@@ -480,14 +481,14 @@ ota_1, app, ota_1, , 1M
return self._run_parttool(csv, args)
self.assertEqual(
rpt(["--partition-type", "app", "--partition-subtype", "ota_1", "--info", "offset"]), b"0x130000")
rpt(['--partition-type', 'app', '--partition-subtype', 'ota_1', '--info', 'offset']), b'0x130000')
self.assertEqual(
rpt(["--partition-boot-default", "--info", "offset"]), b"0x30000") # ota_0
csv_mod = csv.replace("ota_0", "ota_2")
rpt(['--partition-boot-default', '--info', 'offset']), b'0x30000') # ota_0
csv_mod = csv.replace('ota_0', 'ota_2')
self.assertEqual(
self._run_parttool(csv_mod, ["--partition-boot-default", "--info", "offset"]),
b"0x130000") # now default is ota_1
self._run_parttool(csv_mod, ['--partition-boot-default', '--info', 'offset']),
b'0x130000') # now default is ota_1
if __name__ == "__main__":
if __name__ == '__main__':
unittest.main()

View File

@@ -2,13 +2,15 @@
# source: constants.proto
import sys
_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1'))
from google.protobuf.internal import enum_type_wrapper
from google.protobuf import descriptor as _descriptor
from google.protobuf import descriptor_pb2
from google.protobuf import message as _message
from google.protobuf import reflection as _reflection
from google.protobuf import symbol_database as _symbol_database
from google.protobuf import descriptor_pb2
from google.protobuf.internal import enum_type_wrapper
# @@protoc_insertion_point(imports)
_sym_db = _symbol_database.Default()

View File

@@ -2,13 +2,15 @@
# source: sec0.proto
import sys
_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1'))
from google.protobuf.internal import enum_type_wrapper
from google.protobuf import descriptor as _descriptor
from google.protobuf import descriptor_pb2
from google.protobuf import message as _message
from google.protobuf import reflection as _reflection
from google.protobuf import symbol_database as _symbol_database
from google.protobuf import descriptor_pb2
from google.protobuf.internal import enum_type_wrapper
# @@protoc_insertion_point(imports)
_sym_db = _symbol_database.Default()
@@ -16,7 +18,6 @@ _sym_db = _symbol_database.Default()
import constants_pb2 as constants__pb2
DESCRIPTOR = _descriptor.FileDescriptor(
name='sec0.proto',
package='',

View File

@@ -2,13 +2,15 @@
# source: sec1.proto
import sys
_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1'))
from google.protobuf.internal import enum_type_wrapper
from google.protobuf import descriptor as _descriptor
from google.protobuf import descriptor_pb2
from google.protobuf import message as _message
from google.protobuf import reflection as _reflection
from google.protobuf import symbol_database as _symbol_database
from google.protobuf import descriptor_pb2
from google.protobuf.internal import enum_type_wrapper
# @@protoc_insertion_point(imports)
_sym_db = _symbol_database.Default()
@@ -16,7 +18,6 @@ _sym_db = _symbol_database.Default()
import constants_pb2 as constants__pb2
DESCRIPTOR = _descriptor.FileDescriptor(
name='sec1.proto',
package='',
@@ -73,7 +74,7 @@ _SESSIONCMD1 = _descriptor.Descriptor(
_descriptor.FieldDescriptor(
name='client_verify_data', full_name='SessionCmd1.client_verify_data', index=0,
number=2, type=12, cpp_type=9, label=1,
has_default_value=False, default_value=_b(""),
has_default_value=False, default_value=_b(''),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None, file=DESCRIPTOR),
@@ -111,7 +112,7 @@ _SESSIONRESP1 = _descriptor.Descriptor(
_descriptor.FieldDescriptor(
name='device_verify_data', full_name='SessionResp1.device_verify_data', index=1,
number=3, type=12, cpp_type=9, label=1,
has_default_value=False, default_value=_b(""),
has_default_value=False, default_value=_b(''),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None, file=DESCRIPTOR),
@@ -142,7 +143,7 @@ _SESSIONCMD0 = _descriptor.Descriptor(
_descriptor.FieldDescriptor(
name='client_pubkey', full_name='SessionCmd0.client_pubkey', index=0,
number=1, type=12, cpp_type=9, label=1,
has_default_value=False, default_value=_b(""),
has_default_value=False, default_value=_b(''),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None, file=DESCRIPTOR),
@@ -180,14 +181,14 @@ _SESSIONRESP0 = _descriptor.Descriptor(
_descriptor.FieldDescriptor(
name='device_pubkey', full_name='SessionResp0.device_pubkey', index=1,
number=2, type=12, cpp_type=9, label=1,
has_default_value=False, default_value=_b(""),
has_default_value=False, default_value=_b(''),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='device_random', full_name='SessionResp0.device_random', index=2,
number=3, type=12, cpp_type=9, label=1,
has_default_value=False, default_value=_b(""),
has_default_value=False, default_value=_b(''),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None, file=DESCRIPTOR),

View File

@@ -2,13 +2,15 @@
# source: session.proto
import sys
_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1'))
from google.protobuf.internal import enum_type_wrapper
from google.protobuf import descriptor as _descriptor
from google.protobuf import descriptor_pb2
from google.protobuf import message as _message
from google.protobuf import reflection as _reflection
from google.protobuf import symbol_database as _symbol_database
from google.protobuf import descriptor_pb2
from google.protobuf.internal import enum_type_wrapper
# @@protoc_insertion_point(imports)
_sym_db = _symbol_database.Default()
@@ -17,7 +19,6 @@ _sym_db = _symbol_database.Default()
import sec0_pb2 as sec0__pb2
import sec1_pb2 as sec1__pb2
DESCRIPTOR = _descriptor.FileDescriptor(
name='session.proto',
package='',

View File

@@ -17,13 +17,14 @@
# limitations under the License.
from __future__ import division, print_function
import os
import sys
import io
import math
import struct
import argparse
import ctypes
import io
import math
import os
import struct
import sys
SPIFFS_PH_FLAG_USED_FINAL_INDEX = 0xF8
SPIFFS_PH_FLAG_USED_FINAL = 0xFC
@@ -45,7 +46,7 @@ class SpiffsBuildConfig():
block_ix_len, meta_len, obj_name_len, obj_id_len,
span_ix_len, packed, aligned, endianness, use_magic, use_magic_len):
if block_size % page_size != 0:
raise RuntimeError("block size should be a multiple of page size")
raise RuntimeError('block size should be a multiple of page size')
self.page_size = page_size
self.block_size = block_size
@@ -88,15 +89,15 @@ class SpiffsFullError(RuntimeError):
class SpiffsPage():
_endianness_dict = {
"little": "<",
"big": ">"
'little': '<',
'big': '>'
}
_len_dict = {
1: "B",
2: "H",
4: "I",
8: "Q"
1: 'B',
2: 'H',
4: 'I',
8: 'Q'
}
_type_dict = {
@@ -137,7 +138,7 @@ class SpiffsObjLuPage(SpiffsPage):
def to_binary(self):
global test
img = b""
img = b''
for (obj_id, page_type) in self.obj_ids:
if page_type == SpiffsObjIndexPage:
@@ -147,7 +148,7 @@ class SpiffsObjLuPage(SpiffsPage):
assert(len(img) <= self.build_config.page_size)
img += b"\xFF" * (self.build_config.page_size - len(img))
img += b'\xFF' * (self.build_config.page_size - len(img))
return img
@@ -205,7 +206,7 @@ class SpiffsObjIndexPage(SpiffsPage):
SPIFFS_PH_FLAG_USED_FINAL_INDEX)
# Add padding before the object index page specific information
img += b"\xFF" * self.build_config.OBJ_DATA_PAGE_HEADER_LEN_ALIGNED_PAD
img += b'\xFF' * self.build_config.OBJ_DATA_PAGE_HEADER_LEN_ALIGNED_PAD
# If this is the first object index page for the object, add filname, type
# and size information
@@ -216,7 +217,7 @@ class SpiffsObjIndexPage(SpiffsPage):
self.size,
SPIFFS_TYPE_FILE)
img += self.name.encode() + (b"\x00" * ((self.build_config.obj_name_len - len(self.name)) + self.build_config.meta_len))
img += self.name.encode() + (b'\x00' * ((self.build_config.obj_name_len - len(self.name)) + self.build_config.meta_len))
# Finally, add the page index of daa pages
for page in self.pages:
@@ -226,7 +227,7 @@ class SpiffsObjIndexPage(SpiffsPage):
assert(len(img) <= self.build_config.page_size)
img += b"\xFF" * (self.build_config.page_size - len(img))
img += b'\xFF' * (self.build_config.page_size - len(img))
return img
@@ -252,7 +253,7 @@ class SpiffsObjDataPage(SpiffsPage):
assert(len(img) <= self.build_config.page_size)
img += b"\xFF" * (self.build_config.page_size - len(img))
img += b'\xFF' * (self.build_config.page_size - len(img))
return img
@@ -296,7 +297,7 @@ class SpiffsBlock():
except AttributeError: # no next lookup page
# Since the amount of lookup pages is pre-computed at every block instance,
# this should never occur
raise RuntimeError("invalid attempt to add page to a block when there is no more space in lookup")
raise RuntimeError('invalid attempt to add page to a block when there is no more space in lookup')
self.pages.append(page)
@@ -335,7 +336,7 @@ class SpiffsBlock():
return self.remaining_pages <= 0
def to_binary(self, blocks_lim):
img = b""
img = b''
if self.build_config.use_magic:
for (idx, page) in enumerate(self.pages):
@@ -348,14 +349,14 @@ class SpiffsBlock():
assert(len(img) <= self.build_config.block_size)
img += b"\xFF" * (self.build_config.block_size - len(img))
img += b'\xFF' * (self.build_config.block_size - len(img))
return img
class SpiffsFS():
def __init__(self, img_size, build_config):
if img_size % build_config.block_size != 0:
raise RuntimeError("image size should be a multiple of block size")
raise RuntimeError('image size should be a multiple of block size')
self.img_size = img_size
self.build_config = build_config
@@ -367,7 +368,7 @@ class SpiffsFS():
def _create_block(self):
if self.is_full():
raise SpiffsFullError("the image size has been exceeded")
raise SpiffsFullError('the image size has been exceeded')
block = SpiffsBlock(len(self.blocks), self.blocks_lim, self.build_config)
self.blocks.append(block)
@@ -385,7 +386,7 @@ class SpiffsFS():
name = img_path
with open(file_path, "rb") as obj:
with open(file_path, 'rb') as obj:
contents = obj.read()
stream = io.BytesIO(contents)
@@ -434,7 +435,7 @@ class SpiffsFS():
self.cur_obj_id += 1
def to_binary(self):
img = b""
img = b''
for block in self.blocks:
img += block.to_binary(self.blocks_lim)
bix = len(self.blocks)
@@ -447,78 +448,78 @@ class SpiffsFS():
bix += 1
else:
# Just fill remaining spaces FF's
img += "\xFF" * (self.img_size - len(img))
img += '\xFF' * (self.img_size - len(img))
return img
def main():
if sys.version_info[0] < 3:
print("WARNING: Support for Python 2 is deprecated and will be removed in future versions.", file=sys.stderr)
print('WARNING: Support for Python 2 is deprecated and will be removed in future versions.', file=sys.stderr)
elif sys.version_info[0] == 3 and sys.version_info[1] < 6:
print("WARNING: Python 3 versions older than 3.6 are not supported.", file=sys.stderr)
parser = argparse.ArgumentParser(description="SPIFFS Image Generator",
print('WARNING: Python 3 versions older than 3.6 are not supported.', file=sys.stderr)
parser = argparse.ArgumentParser(description='SPIFFS Image Generator',
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument("image_size",
help="Size of the created image")
parser.add_argument('image_size',
help='Size of the created image')
parser.add_argument("base_dir",
help="Path to directory from which the image will be created")
parser.add_argument('base_dir',
help='Path to directory from which the image will be created')
parser.add_argument("output_file",
help="Created image output file path")
parser.add_argument('output_file',
help='Created image output file path')
parser.add_argument("--page-size",
help="Logical page size. Set to value same as CONFIG_SPIFFS_PAGE_SIZE.",
parser.add_argument('--page-size',
help='Logical page size. Set to value same as CONFIG_SPIFFS_PAGE_SIZE.',
type=int,
default=256)
parser.add_argument("--block-size",
parser.add_argument('--block-size',
help="Logical block size. Set to the same value as the flash chip's sector size (g_rom_flashchip.sector_size).",
type=int,
default=4096)
parser.add_argument("--obj-name-len",
help="File full path maximum length. Set to value same as CONFIG_SPIFFS_OBJ_NAME_LEN.",
parser.add_argument('--obj-name-len',
help='File full path maximum length. Set to value same as CONFIG_SPIFFS_OBJ_NAME_LEN.',
type=int,
default=32)
parser.add_argument("--meta-len",
help="File metadata length. Set to value same as CONFIG_SPIFFS_META_LENGTH.",
parser.add_argument('--meta-len',
help='File metadata length. Set to value same as CONFIG_SPIFFS_META_LENGTH.',
type=int,
default=4)
parser.add_argument("--use-magic",
help="Use magic number to create an identifiable SPIFFS image. Specify if CONFIG_SPIFFS_USE_MAGIC.",
action="store_true",
parser.add_argument('--use-magic',
help='Use magic number to create an identifiable SPIFFS image. Specify if CONFIG_SPIFFS_USE_MAGIC.',
action='store_true',
default=True)
parser.add_argument("--follow-symlinks",
help="Take into account symbolic links during partition image creation.",
action="store_true",
parser.add_argument('--follow-symlinks',
help='Take into account symbolic links during partition image creation.',
action='store_true',
default=False)
parser.add_argument("--use-magic-len",
help="Use position in memory to create different magic numbers for each block. Specify if CONFIG_SPIFFS_USE_MAGIC_LENGTH.",
action="store_true",
parser.add_argument('--use-magic-len',
help='Use position in memory to create different magic numbers for each block. Specify if CONFIG_SPIFFS_USE_MAGIC_LENGTH.',
action='store_true',
default=True)
parser.add_argument("--big-endian",
help="Specify if the target architecture is big-endian. If not specified, little-endian is assumed.",
action="store_true",
parser.add_argument('--big-endian',
help='Specify if the target architecture is big-endian. If not specified, little-endian is assumed.',
action='store_true',
default=False)
args = parser.parse_args()
if not os.path.exists(args.base_dir):
raise RuntimeError("given base directory %s does not exist" % args.base_dir)
raise RuntimeError('given base directory %s does not exist' % args.base_dir)
with open(args.output_file, "wb") as image_file:
with open(args.output_file, 'wb') as image_file:
image_size = int(args.image_size, 0)
spiffs_build_default = SpiffsBuildConfig(args.page_size, SPIFFS_PAGE_IX_LEN,
args.block_size, SPIFFS_BLOCK_IX_LEN, args.meta_len,
args.obj_name_len, SPIFFS_OBJ_ID_LEN, SPIFFS_SPAN_IX_LEN,
True, True, "big" if args.big_endian else "little",
True, True, 'big' if args.big_endian else 'little',
args.use_magic, args.use_magic_len)
spiffs = SpiffsFS(image_size, spiffs_build_default)
@@ -526,12 +527,12 @@ def main():
for root, dirs, files in os.walk(args.base_dir, followlinks=args.follow_symlinks):
for f in files:
full_path = os.path.join(root, f)
spiffs.create_file("/" + os.path.relpath(full_path, args.base_dir).replace("\\", "/"), full_path)
spiffs.create_file('/' + os.path.relpath(full_path, args.base_dir).replace('\\', '/'), full_path)
image = spiffs.to_binary()
image_file.write(image)
if __name__ == "__main__":
if __name__ == '__main__':
main()

View File

@@ -6,58 +6,59 @@
# Distributed under the terms of Apache License v2.0 found in the top-level LICENSE file.
from __future__ import print_function
from optparse import OptionParser
import sys
from optparse import OptionParser
BASE_ADDR = 0x50000000
def gen_ld_h_from_sym(f_sym, f_ld, f_h):
f_ld.write("/* Variable definitions for ESP32ULP linker\n")
f_ld.write(" * This file is generated automatically by esp32ulp_mapgen.py utility.\n")
f_ld.write(" */\n\n")
f_h.write("// Variable definitions for ESP32ULP\n")
f_h.write("// This file is generated automatically by esp32ulp_mapgen.py utility\n\n")
f_h.write("#pragma once\n\n")
f_ld.write('/* Variable definitions for ESP32ULP linker\n')
f_ld.write(' * This file is generated automatically by esp32ulp_mapgen.py utility.\n')
f_ld.write(' */\n\n')
f_h.write('// Variable definitions for ESP32ULP\n')
f_h.write('// This file is generated automatically by esp32ulp_mapgen.py utility\n\n')
f_h.write('#pragma once\n\n')
for line in f_sym:
name, _, addr_str = line.split(" ", 2)
name, _, addr_str = line.split(' ', 2)
addr = int(addr_str, 16) + BASE_ADDR
f_h.write("extern uint32_t ulp_{0};\n".format(name))
f_ld.write("PROVIDE ( ulp_{0} = 0x{1:08x} );\n".format(name, addr))
f_h.write('extern uint32_t ulp_{0};\n'.format(name))
f_ld.write('PROVIDE ( ulp_{0} = 0x{1:08x} );\n'.format(name, addr))
def gen_ld_h_from_sym_riscv(f_sym, f_ld, f_h):
f_ld.write("/* Variable definitions for ESP32ULP linker\n")
f_ld.write(" * This file is generated automatically by esp32ulp_mapgen.py utility.\n")
f_ld.write(" */\n\n")
f_h.write("// Variable definitions for ESP32ULP\n")
f_h.write("// This file is generated automatically by esp32ulp_mapgen.py utility\n\n")
f_h.write("#pragma once\n\n")
f_ld.write('/* Variable definitions for ESP32ULP linker\n')
f_ld.write(' * This file is generated automatically by esp32ulp_mapgen.py utility.\n')
f_ld.write(' */\n\n')
f_h.write('// Variable definitions for ESP32ULP\n')
f_h.write('// This file is generated automatically by esp32ulp_mapgen.py utility\n\n')
f_h.write('#pragma once\n\n')
for line in f_sym:
addr_str, _, name = line.split()
addr = int(addr_str, 16) + BASE_ADDR
f_h.write("extern uint32_t ulp_{0};\n".format(name))
f_ld.write("PROVIDE ( ulp_{0} = 0x{1:08x} );\n".format(name, addr))
f_h.write('extern uint32_t ulp_{0};\n'.format(name))
f_ld.write('PROVIDE ( ulp_{0} = 0x{1:08x} );\n'.format(name, addr))
def main():
if sys.version_info[0] < 3:
print("WARNING: Support for Python 2 is deprecated and will be removed in future versions.", file=sys.stderr)
print('WARNING: Support for Python 2 is deprecated and will be removed in future versions.', file=sys.stderr)
elif sys.version_info[0] == 3 and sys.version_info[1] < 6:
print("WARNING: Python 3 versions older than 3.6 are not supported.", file=sys.stderr)
description = ("This application generates .h and .ld files for symbols defined in input file. "
"The input symbols file can be generated using nm utility like this: "
"esp32-ulp-nm -g -f posix <elf_file> > <symbols_file>")
print('WARNING: Python 3 versions older than 3.6 are not supported.', file=sys.stderr)
description = ('This application generates .h and .ld files for symbols defined in input file. '
'The input symbols file can be generated using nm utility like this: '
'esp32-ulp-nm -g -f posix <elf_file> > <symbols_file>')
parser = OptionParser(description=description)
parser.add_option("-s", "--symfile", dest="symfile",
help="symbols file name", metavar="SYMFILE")
parser.add_option("-o", "--outputfile", dest="outputfile",
help="destination .h and .ld files name prefix", metavar="OUTFILE")
parser.add_option('-s', '--symfile', dest='symfile',
help='symbols file name', metavar='SYMFILE')
parser.add_option('-o', '--outputfile', dest='outputfile',
help='destination .h and .ld files name prefix', metavar='OUTFILE')
parser.add_option("--riscv", action="store_true", help="use format for ulp riscv .sym file")
parser.add_option('--riscv', action='store_true', help='use format for ulp riscv .sym file')
(options, args) = parser.parse_args()
if options.symfile is None:
@@ -69,14 +70,14 @@ def main():
return 1
if options.riscv:
with open(options.outputfile + ".h", 'w') as f_h, open(options.outputfile + ".ld", 'w') as f_ld, open(options.symfile) as f_sym:
with open(options.outputfile + '.h', 'w') as f_h, open(options.outputfile + '.ld', 'w') as f_ld, open(options.symfile) as f_sym:
gen_ld_h_from_sym_riscv(f_sym, f_ld, f_h)
return 0
with open(options.outputfile + ".h", 'w') as f_h, open(options.outputfile + ".ld", 'w') as f_ld, open(options.symfile) as f_sym:
with open(options.outputfile + '.h', 'w') as f_h, open(options.outputfile + '.ld', 'w') as f_ld, open(options.symfile) as f_sym:
gen_ld_h_from_sym(f_sym, f_ld, f_h)
return 0
if __name__ == "__main__":
if __name__ == '__main__':
exit(main())

View File

@@ -2,13 +2,15 @@
# source: wifi_config.proto
import sys
_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1'))
from google.protobuf.internal import enum_type_wrapper
from google.protobuf import descriptor as _descriptor
from google.protobuf import descriptor_pb2
from google.protobuf import message as _message
from google.protobuf import reflection as _reflection
from google.protobuf import symbol_database as _symbol_database
from google.protobuf import descriptor_pb2
from google.protobuf.internal import enum_type_wrapper
# @@protoc_insertion_point(imports)
_sym_db = _symbol_database.Default()
@@ -17,7 +19,6 @@ _sym_db = _symbol_database.Default()
import constants_pb2 as constants__pb2
import wifi_constants_pb2 as wifi__constants__pb2
DESCRIPTOR = _descriptor.FileDescriptor(
name='wifi_config.proto',
package='',
@@ -163,21 +164,21 @@ _CMDSETCONFIG = _descriptor.Descriptor(
_descriptor.FieldDescriptor(
name='ssid', full_name='CmdSetConfig.ssid', index=0,
number=1, type=12, cpp_type=9, label=1,
has_default_value=False, default_value=_b(""),
has_default_value=False, default_value=_b(''),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='passphrase', full_name='CmdSetConfig.passphrase', index=1,
number=2, type=12, cpp_type=9, label=1,
has_default_value=False, default_value=_b(""),
has_default_value=False, default_value=_b(''),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='bssid', full_name='CmdSetConfig.bssid', index=2,
number=3, type=12, cpp_type=9, label=1,
has_default_value=False, default_value=_b(""),
has_default_value=False, default_value=_b(''),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None, file=DESCRIPTOR),

View File

@@ -2,13 +2,15 @@
# source: wifi_constants.proto
import sys
_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1'))
from google.protobuf.internal import enum_type_wrapper
from google.protobuf import descriptor as _descriptor
from google.protobuf import descriptor_pb2
from google.protobuf import message as _message
from google.protobuf import reflection as _reflection
from google.protobuf import symbol_database as _symbol_database
from google.protobuf import descriptor_pb2
from google.protobuf.internal import enum_type_wrapper
# @@protoc_insertion_point(imports)
_sym_db = _symbol_database.Default()
@@ -141,7 +143,7 @@ _WIFICONNECTEDSTATE = _descriptor.Descriptor(
_descriptor.FieldDescriptor(
name='ip4_addr', full_name='WifiConnectedState.ip4_addr', index=0,
number=1, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=_b("").decode('utf-8'),
has_default_value=False, default_value=_b('').decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None, file=DESCRIPTOR),
@@ -155,14 +157,14 @@ _WIFICONNECTEDSTATE = _descriptor.Descriptor(
_descriptor.FieldDescriptor(
name='ssid', full_name='WifiConnectedState.ssid', index=2,
number=3, type=12, cpp_type=9, label=1,
has_default_value=False, default_value=_b(""),
has_default_value=False, default_value=_b(''),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='bssid', full_name='WifiConnectedState.bssid', index=3,
number=4, type=12, cpp_type=9, label=1,
has_default_value=False, default_value=_b(""),
has_default_value=False, default_value=_b(''),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None, file=DESCRIPTOR),

View File

@@ -3,12 +3,14 @@
# source: wifi_scan.proto
import sys
_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1'))
from google.protobuf.internal import enum_type_wrapper
from google.protobuf import descriptor as _descriptor
from google.protobuf import message as _message
from google.protobuf import reflection as _reflection
from google.protobuf import symbol_database as _symbol_database
from google.protobuf.internal import enum_type_wrapper
# @@protoc_insertion_point(imports)
_sym_db = _symbol_database.Default()
@@ -17,7 +19,6 @@ _sym_db = _symbol_database.Default()
import constants_pb2 as constants__pb2
import wifi_constants_pb2 as wifi__constants__pb2
DESCRIPTOR = _descriptor.FileDescriptor(
name='wifi_scan.proto',
package='',
@@ -261,7 +262,7 @@ _WIFISCANRESULT = _descriptor.Descriptor(
_descriptor.FieldDescriptor(
name='ssid', full_name='WiFiScanResult.ssid', index=0,
number=1, type=12, cpp_type=9, label=1,
has_default_value=False, default_value=_b(""),
has_default_value=False, default_value=_b(''),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR),
@@ -282,7 +283,7 @@ _WIFISCANRESULT = _descriptor.Descriptor(
_descriptor.FieldDescriptor(
name='bssid', full_name='WiFiScanResult.bssid', index=3,
number=4, type=12, cpp_type=9, label=1,
has_default_value=False, default_value=_b(""),
has_default_value=False, default_value=_b(''),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR),

View File

@@ -50,11 +50,12 @@
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import print_function
import sys
# Check if loaded into GDB
try:
assert gdb.__name__ == "gdb"
assert gdb.__name__ == 'gdb'
WITH_GDB = True
except NameError:
WITH_GDB = False
@@ -114,7 +115,7 @@ class TraxPacket(object):
return result
def __str__(self):
return "%d byte packet%s" % (self.size_bytes, " (truncated)" if self.truncated else "")
return '%d byte packet%s' % (self.size_bytes, ' (truncated)' if self.truncated else '')
class TraxMessage(object):
@@ -175,7 +176,7 @@ class TraxMessage(object):
self.icnt = self.packets[0].get_bits(12, -1)
self.is_correlation = True
else:
raise NotImplementedError("Unknown message type (%d)" % self.msg_type)
raise NotImplementedError('Unknown message type (%d)' % self.msg_type)
def process_forward(self, cur_pc):
"""
@@ -229,23 +230,23 @@ class TraxMessage(object):
return prev_pc
def __str__(self):
desc = "Unknown (%d)" % self.msg_type
extra = ""
desc = 'Unknown (%d)' % self.msg_type
extra = ''
if self.truncated:
desc = "Truncated"
desc = 'Truncated'
if self.msg_type == TVAL_INDBR:
desc = "Indirect branch"
extra = ", icnt=%d, uaddr=0x%x, exc=%d" % (self.icnt, self.uaddr, self.is_exception)
desc = 'Indirect branch'
extra = ', icnt=%d, uaddr=0x%x, exc=%d' % (self.icnt, self.uaddr, self.is_exception)
if self.msg_type == TVAL_INDBRSYNC:
desc = "Indirect branch w/sync"
extra = ", icnt=%d, dcont=%d, exc=%d" % (self.icnt, self.dcont, self.is_exception)
desc = 'Indirect branch w/sync'
extra = ', icnt=%d, dcont=%d, exc=%d' % (self.icnt, self.dcont, self.is_exception)
if self.msg_type == TVAL_SYNC:
desc = "Synchronization"
extra = ", icnt=%d, dcont=%d" % (self.icnt, self.dcont)
desc = 'Synchronization'
extra = ', icnt=%d, dcont=%d' % (self.icnt, self.dcont)
if self.msg_type == TVAL_CORR:
desc = "Correlation"
extra = ", icnt=%d" % self.icnt
return "%s message, %d packets, PC range 0x%08x - 0x%08x, target PC 0x%08x" % (
desc = 'Correlation'
extra = ', icnt=%d' % self.icnt
return '%s message, %d packets, PC range 0x%08x - 0x%08x, target PC 0x%08x' % (
desc, len(self.packets), self.pc_start, self.pc_end, self.pc_target) + extra
@@ -264,7 +265,7 @@ def load_messages(data):
# Iterate over the input data, splitting bytes into packets and messages
for i, b in enumerate(data):
if (b & MSEO_MSGEND) and not (b & MSEO_PKTEND):
raise AssertionError("Invalid MSEO bits in b=0x%x. Not a TRAX dump?" % b)
raise AssertionError('Invalid MSEO bits in b=0x%x. Not a TRAX dump?' % b)
if b & MSEO_PKTEND:
pkt_cnt += 1
@@ -276,7 +277,7 @@ def load_messages(data):
try:
messages.append(TraxMessage(packets, len(messages) == 0))
except NotImplementedError as e:
sys.stderr.write("Failed to parse message #%03d (at %d bytes): %s\n" % (msg_cnt, i, str(e)))
sys.stderr.write('Failed to parse message #%03d (at %d bytes): %s\n' % (msg_cnt, i, str(e)))
packets = []
# Resolve PC ranges of messages.
@@ -312,32 +313,32 @@ def parse_and_dump(filename, disassemble=WITH_GDB):
data = f.read()
messages = load_messages(data)
sys.stderr.write("Loaded %d messages in %d bytes\n" % (len(messages), len(data)))
sys.stderr.write('Loaded %d messages in %d bytes\n' % (len(messages), len(data)))
for i, m in enumerate(messages):
if m.truncated:
continue
print("%04d: %s" % (i, str(m)))
print('%04d: %s' % (i, str(m)))
if m.is_exception:
print("*** Exception occurred ***")
print('*** Exception occurred ***')
if disassemble and WITH_GDB:
try:
gdb.execute("disassemble 0x%08x, 0x%08x" % (m.pc_start, m.pc_end)) # noqa: F821
gdb.execute('disassemble 0x%08x, 0x%08x' % (m.pc_start, m.pc_end)) # noqa: F821
except gdb.MemoryError: # noqa: F821
print("Failed to disassemble from 0x%08x to 0x%08x" % (m.pc_start, m.pc_end))
print('Failed to disassemble from 0x%08x to 0x%08x' % (m.pc_start, m.pc_end))
def main():
if sys.version_info[0] < 3:
print("WARNING: Support for Python 2 is deprecated and will be removed in future versions.", file=sys.stderr)
print('WARNING: Support for Python 2 is deprecated and will be removed in future versions.', file=sys.stderr)
elif sys.version_info[0] == 3 and sys.version_info[1] < 6:
print("WARNING: Python 3 versions older than 3.6 are not supported.", file=sys.stderr)
print('WARNING: Python 3 versions older than 3.6 are not supported.', file=sys.stderr)
if len(sys.argv) < 2:
sys.stderr.write("Usage: %s <dump_file>\n")
sys.stderr.write('Usage: %s <dump_file>\n')
raise SystemExit(1)
parse_and_dump(sys.argv[1])
if __name__ == "__main__" and not WITH_GDB:
if __name__ == '__main__' and not WITH_GDB:
main()