diff --git a/components/partition_table/Makefile.projbuild b/components/partition_table/Makefile.projbuild index 1785ad811..353a7655d 100644 --- a/components/partition_table/Makefile.projbuild +++ b/components/partition_table/Makefile.projbuild @@ -63,16 +63,16 @@ $(PARTITION_TABLE_BIN_UNSIGNED): $(PARTITION_TABLE_CSV_PATH) $(SDKCONFIG_MAKEFIL all_binaries: $(PARTITION_TABLE_BIN) partition_table_get_info check_table_contents partition_table_get_info: $(PARTITION_TABLE_BIN) - $(eval PHY_DATA_OFFSET:=$(shell $(GET_PART_INFO) --partition-type data --partition-subtype phy \ - --partition-table-file $(PARTITION_TABLE_BIN) get_partition_info --info offset)) - $(eval APP_OFFSET:=$(shell $(GET_PART_INFO) --partition-boot-default \ - --partition-table-file $(PARTITION_TABLE_BIN) get_partition_info --info offset)) - $(eval OTA_DATA_OFFSET:=$(shell $(GET_PART_INFO) --partition-type data --partition-subtype ota \ - --partition-table-file $(PARTITION_TABLE_BIN) get_partition_info --info offset)) - $(eval OTA_DATA_SIZE:=$(shell $(GET_PART_INFO) --partition-type data --partition-subtype ota \ - --partition-table-file $(PARTITION_TABLE_BIN) get_partition_info --info size)) - $(eval FACTORY_OFFSET:=$(shell $(GET_PART_INFO) --partition-type app --partition-subtype factory \ - --partition-table-file $(PARTITION_TABLE_BIN) get_partition_info --info offset)) + $(eval PHY_DATA_OFFSET:=$(shell $(GET_PART_INFO) --partition-table-file $(PARTITION_TABLE_BIN) \ + get_partition_info --partition-type data --partition-subtype phy --info offset)) + $(eval APP_OFFSET:=$(shell $(GET_PART_INFO) --partition-table-file $(PARTITION_TABLE_BIN) \ + get_partition_info --partition-boot-default --info offset)) + $(eval OTA_DATA_OFFSET:=$(shell $(GET_PART_INFO) --partition-table-file $(PARTITION_TABLE_BIN) \ + get_partition_info --partition-type data --partition-subtype ota --info offset)) + $(eval OTA_DATA_SIZE:=$(shell $(GET_PART_INFO) --partition-table-file $(PARTITION_TABLE_BIN) \ + get_partition_info --partition-type data --partition-subtype ota --info size)) + $(eval FACTORY_OFFSET:=$(shell $(GET_PART_INFO) --partition-table-file $(PARTITION_TABLE_BIN) \ + get_partition_info --partition-type app --partition-subtype factory --info offset)) export APP_OFFSET export PHY_DATA_OFFSET diff --git a/components/partition_table/gen_empty_partition.py b/components/partition_table/gen_empty_partition.py new file mode 100644 index 000000000..f65f74d70 --- /dev/null +++ b/components/partition_table/gen_empty_partition.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python +# +# generates an empty binary file +# +# This tool generates an empty binary file of the required size. +# +# Copyright 2018 Espressif Systems (Shanghai) PTE LTD +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http:#www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# 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 +import argparse +import sys + +__version__ = '1.0' + +quiet = False + + +def generate_blanked_file(size, output_path): + output = b"\xFF" * size + try: + stdout_binary = sys.stdout.buffer # Python 3 + except AttributeError: + stdout_binary = sys.stdout + with stdout_binary if output_path == '-' else open(output_path, 'wb') as f: + f.write(output) + + +def main(): + parser = argparse.ArgumentParser(description='Generates an empty binary file of the required size.') + parser.add_argument('size', help='Size of generated the file', type=str) + + parser.add_argument('output', help='Path for binary file.', nargs='?', default='-') + args = parser.parse_args() + + size = int(args.size, 0) + if size > 0: + generate_blanked_file(size, args.output) + return 0 + + +class InputError(RuntimeError): + def __init__(self, e): + super(InputError, self).__init__(e) + + +if __name__ == '__main__': + try: + r = main() + sys.exit(r) + except InputError as e: + print(e, file=sys.stderr) + sys.exit(2) diff --git a/components/partition_table/parttool.py b/components/partition_table/parttool.py index 14fcdf4fe..f2c005a10 100755 --- a/components/partition_table/parttool.py +++ b/components/partition_table/parttool.py @@ -24,193 +24,165 @@ import subprocess import tempfile import gen_esp32part as gen -__version__ = '1.0' -IDF_COMPONENTS_PATH = os.path.expandvars(os.path.join("$IDF_PATH", "components")) +__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") + +PARTITION_TABLE_OFFSET = 0x8000 -ESPTOOL_PY = os.path.join(IDF_COMPONENTS_PATH, "esptool_py", "esptool", "esptool.py") quiet = False def status(msg): - """ Print status message to stderr """ if not quiet: print(msg) -def _invoke_esptool(esptool_args, args): - m_esptool_args = [sys.executable, ESPTOOL_PY] +class _PartitionId(): - if args.port != "": - m_esptool_args.extend(["--port", args.port]) - - m_esptool_args.extend(esptool_args) - - if quiet: - with open(os.devnull, "w") as fnull: - subprocess.check_call(m_esptool_args, stdout=fnull, stderr=fnull) - else: - subprocess.check_call(m_esptool_args) + def __init__(self, name=None, type=None, subtype=None): + self.name = name + self.type = type + self.subtype = subtype -def _get_partition_table(args): - partition_table = None +class PartitionName(_PartitionId): - gen.offset_part_table = int(args.partition_table_offset, 0) - - if args.partition_table_file: - status("Reading partition table from partition table file...") - - try: - with open(args.partition_table_file, "rb") as partition_table_file: - partition_table = gen.PartitionTable.from_binary(partition_table_file.read()) - status("Partition table read from binary file {}".format(partition_table_file.name)) - except (gen.InputError, TypeError): - with open(args.partition_table_file, "r") as partition_table_file: - partition_table_file.seek(0) - partition_table = gen.PartitionTable.from_csv(partition_table_file.read()) - status("Partition table read from CSV file {}".format(partition_table_file.name)) - else: - port_info = (" on port " + args.port if args.port else "") - status("Reading partition table from device{}...".format(port_info)) - - f_name = None - with tempfile.NamedTemporaryFile(delete=False) as f: - f_name = f.name - - try: - invoke_args = ["read_flash", str(gen.offset_part_table), str(gen.MAX_PARTITION_LENGTH), f_name] - _invoke_esptool(invoke_args, args) - with open(f_name, "rb") as f: - partition_table = gen.PartitionTable.from_binary(f.read()) - status("Partition table read from device" + port_info) - finally: - os.unlink(f_name) - - return partition_table + def __init__(self, name): + _PartitionId.__init__(self, name=name) -def _get_partition(args): - partition_table = _get_partition_table(args) +class PartitionType(_PartitionId): - partition = None - - if args.partition_name: - partition = partition_table.find_by_name(args.partition_name) - elif args.partition_type and args.partition_subtype: - partition = partition_table.find_by_type(args.partition_type, args.partition_subtype) - elif args.partition_boot_default: - search = ["factory"] + ["ota_{}".format(d) for d in range(16)] - for subtype in search: - partition = partition_table.find_by_type("app", subtype) - if partition is not None: - break - else: - raise RuntimeError("Invalid partition selection arguments. Specify --partition-name OR \ - --partition-type and --partition-subtype OR --partition--boot-default.") - - if partition: - status("Found partition {}".format(str(partition))) - - return partition + def __init__(self, type, subtype): + _PartitionId.__init__(self, type=type, subtype=subtype) -def _get_and_check_partition(args): - partition = None - - partition = _get_partition(args) - - if not partition: - raise RuntimeError("Unable to find specified partition.") - - return partition +PARTITION_BOOT_DEFAULT = _PartitionId() -def write_partition(args): - erase_partition(args) +class ParttoolTarget(): - partition = _get_and_check_partition(args) + def __init__(self, port=None, partition_table_offset=PARTITION_TABLE_OFFSET, partition_table_file=None): + self.port = port - status("Checking input file size...") + gen.offset_part_table = partition_table_offset - with open(args.input, "rb") as input_file: - content_len = len(input_file.read()) - - if content_len != partition.size: - status("File size (0x{:x}) does not match partition size (0x{:x})".format(content_len, partition.size)) + if partition_table_file: + try: + with open(partition_table_file, "rb") as f: + partition_table = gen.PartitionTable.from_binary(f.read()) + except (gen.InputError, IOError, TypeError): + with open(partition_table_file, "r") as f: + f.seek(0) + partition_table = gen.PartitionTable.from_csv(f.read()) else: - status("File size matches partition size (0x{:x})".format(partition.size)) - - _invoke_esptool(["write_flash", str(partition.offset), args.input], args) - - status("Written contents of file '{}' to device at offset 0x{:x}".format(args.input, partition.offset)) - - -def read_partition(args): - partition = _get_and_check_partition(args) - _invoke_esptool(["read_flash", str(partition.offset), str(partition.size), args.output], args) - status("Read partition contents from device at offset 0x{:x} to file '{}'".format(partition.offset, args.output)) - - -def erase_partition(args): - partition = _get_and_check_partition(args) - _invoke_esptool(["erase_region", str(partition.offset), str(partition.size)], args) - status("Erased partition at offset 0x{:x} on device".format(partition.offset)) - - -def get_partition_info(args): - partition = None - - if args.table: - partition_table = _get_partition_table(args) - - if args.table.endswith(".csv"): - partition_table = partition_table.to_csv() - else: - partition_table = partition_table.to_binary() - - with open(args.table, "wb") as table_file: - table_file.write(partition_table) - status("Partition table written to " + table_file.name) - else: - partition = _get_partition(args) - - if partition: - info_dict = { - "offset": '0x{:x}'.format(partition.offset), - "size": '0x{:x}'.format(partition.size) - } - - infos = [] + temp_file = tempfile.NamedTemporaryFile(delete=False) + temp_file.close() try: - for info in args.info: - infos += [info_dict[info]] - except KeyError: - raise RuntimeError("Request for unknown partition info {}".format(info)) + 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) - status("Requested partition information [{}]:".format(", ".join(args.info))) - print(" ".join(infos)) - else: - status("Partition not found") + self.partition_table = partition_table + + def _call_esptool(self, args, out=None): + esptool_args = [sys.executable, ESPTOOL_PY] + + if self.port: + esptool_args += ["--port", self.port] + + esptool_args += args + + with open(os.devnull, "w") as null_file: + subprocess.check_call(esptool_args, stdout=null_file, stderr=null_file) + + def get_partition_info(self, partition_id): + partition = None + + if partition_id.name: + partition = self.partition_table.find_by_name(partition_id.name) + elif partition_id.type and partition_id.subtype: + partition = self.partition_table.find_by_type(partition_id.type, partition_id.subtype) + else: # default boot partition + search = ["factory"] + ["ota_{}".format(d) for d in range(16)] + for subtype in search: + partition = self.partition_table.find_by_type("app", subtype) + if partition: + break + + if not partition: + 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)]) + + 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]) + + 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: + content_len = len(input_file.read()) + + if content_len > partition.size: + raise Exception("Input file size exceeds partition size") + + self._call_esptool(["write_flash", str(partition.offset), input]) -def generate_blank_partition_file(args): - output = None - stdout_binary = None +def _write_partition(target, partition_id, input): + target.write_partition(partition_id, input) + partition = target.get_partition_info(partition_id) + status("Written contents of file '{}' at offset 0x{:x}".format(input, partition.offset)) - partition = _get_and_check_partition(args) - output = b"\xFF" * partition.size + +def _read_partition(target, partition_id, output): + target.read_partition(partition_id, output) + partition = target.get_partition_info(partition_id) + status("Read partition '{}' contents from device at offset 0x{:x} to file '{}'" + .format(partition.name, partition.offset, output)) + + +def _erase_partition(target, partition_id): + target.erase_partition(partition_id) + partition = target.get_partition_info(partition_id) + status("Erased partition '{}' at offset 0x{:x}".format(partition.name, partition.offset)) + + +def _get_partition_info(target, partition_id, info): + try: + partition = target.get_partition_info(partition_id) + except Exception: + return + + info_dict = { + "offset": '0x{:x}'.format(partition.offset), + "size": '0x{:x}'.format(partition.size) + } + + infos = [] try: - stdout_binary = sys.stdout.buffer # Python 3 - except AttributeError: - stdout_binary = sys.stdout + for i in info: + infos += [info_dict[i]] + except KeyError: + raise RuntimeError("Request for unknown partition info {}".format(i)) - with stdout_binary if args.output == "" else open(args.output, 'wb') as f: - f.write(output) - status("Blank partition file '{}' generated".format(args.output)) + print(" ".join(infos)) def main(): @@ -220,48 +192,45 @@ def main(): parser.add_argument("--quiet", "-q", help="suppress stderr messages", action="store_true") - # 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. - partition_table_info_source_args = parser.add_mutually_exclusive_group() + # 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") - partition_table_info_source_args.add_argument("--port", "-p", help="port where the device to read the partition table from is attached", default="") - partition_table_info_source_args.add_argument("--partition-table-file", "-f", help="file (CSV/binary) to read the partition table from") + 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", default="0x8000") + partition_selection_parser = argparse.ArgumentParser(add_help=False) # Specify what partition to perform the operation on. This can either be specified using the # partition name or the first partition that matches the specified type/subtype - partition_selection_args = parser.add_mutually_exclusive_group() + 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") - 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") # 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") - 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") - subparsers.add_parser("erase_partition", help="erase the contents of a partition on the device") + 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") - print_partition_info_subparser_info_type = print_partition_info_subparser.add_mutually_exclusive_group() - print_partition_info_subparser_info_type.add_argument("--info", help="type of partition information to get", nargs="+") - print_partition_info_subparser_info_type.add_argument("--table", help="dump the partition table to a file") - - generate_blank_subparser = subparsers.add_parser("generate_blank_partition_file", help="generate a blank (all 0xFF) partition file of \ - the specified partition that can be flashed to the device") - generate_blank_subparser.add_argument("--output", help="blank partition file filename") + 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", nargs="+") args = parser.parse_args() - quiet = args.quiet # No operation specified, display help and exit @@ -270,17 +239,55 @@ def main(): parser.print_help() sys.exit(1) - # Else execute the operation - operation_func = globals()[args.operation] + # Prepare the partition to perform operation on + if args.partition_name: + 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") + partition_id = PartitionType(args.partition_type, args.partition_subtype) + 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") + + # Prepare the device to perform operation on + target_args = {} + + if args.port: + target_args["port"] = args.port + + if 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 = ParttoolTarget(**target_args) + + # Create the operation table and execute the operation + 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"]) + } + + (op, op_args) = parttool_ops[args.operation] + + for op_arg in op_args: + common_args.update({op_arg:vars(args)[op_arg]}) if quiet: # If exceptions occur, suppress and exit quietly try: - operation_func(args) + op(**common_args) except Exception: sys.exit(2) else: - operation_func(args) + op(**common_args) if __name__ == '__main__': diff --git a/components/partition_table/project_include.cmake b/components/partition_table/project_include.cmake index d8e31c7d1..9f0dc9385 100644 --- a/components/partition_table/project_include.cmake +++ b/components/partition_table/project_include.cmake @@ -39,7 +39,7 @@ function(partition_table_get_partition_info result get_part_info_args part_info) ${idf_path}/components/partition_table/parttool.py -q --partition-table-offset ${PARTITION_TABLE_OFFSET} --partition-table-file ${PARTITION_CSV_PATH} - ${get_part_info_args} get_partition_info --info ${part_info} + get_partition_info ${get_part_info_args} --info ${part_info} OUTPUT_VARIABLE info RESULT_VARIABLE exit_code OUTPUT_STRIP_TRAILING_WHITESPACE) diff --git a/components/partition_table/test_gen_esp32part_host/gen_esp32part_tests.py b/components/partition_table/test_gen_esp32part_host/gen_esp32part_tests.py index 989d4c475..17f16c5b4 100755 --- a/components/partition_table/test_gen_esp32part_host/gen_esp32part_tests.py +++ b/components/partition_table/test_gen_esp32part_host/gen_esp32part_tests.py @@ -403,13 +403,13 @@ app,app, factory, 32K, 1M class PartToolTests(Py23TestCase): - def _run_parttool(self, csvcontents, args, info): + def _run_parttool(self, csvcontents, args): csvpath = tempfile.mktemp() with open(csvpath, "w") as f: f.write(csvcontents) try: - output = subprocess.check_output([sys.executable, "../parttool.py"] + args.split(" ") - + ["--partition-table-file", csvpath, "get_partition_info", "--info", info], + 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) m = re.search(b"0x[0-9a-fA-F]+", output) @@ -425,17 +425,17 @@ phy_init, data, phy, 0xf000, 0x1000 factory, app, factory, 0x10000, 1M """ - def rpt(args, info): - return self._run_parttool(csv, args, info) + def rpt(args): + return self._run_parttool(csv, args) self.assertEqual( - rpt("--partition-type=data --partition-subtype=nvs -q", "offset"), b"0x9000") + rpt(["--partition-type", "data", "--partition-subtype", "nvs", "--info", "offset"]), b"0x9000") self.assertEqual( - rpt("--partition-type=data --partition-subtype=nvs -q", "size"), b"0x4000") + rpt(["--partition-type", "data", "--partition-subtype", "nvs", "--info", "size"]), b"0x4000") self.assertEqual( - rpt("--partition-name=otadata -q", "offset"), b"0xd000") + rpt(["--partition-name", "otadata", "--info", "offset"]), b"0xd000") self.assertEqual( - rpt("--partition-boot-default -q", "offset"), b"0x10000") + rpt(["--partition-boot-default", "--info", "offset"]), b"0x10000") def test_fallback(self): csv = """ @@ -446,16 +446,16 @@ ota_0, app, ota_0, 0x30000, 1M ota_1, app, ota_1, , 1M """ - def rpt(args, info): - return self._run_parttool(csv, args, info) + def rpt(args): + return self._run_parttool(csv, args) self.assertEqual( - rpt("--partition-type=app --partition-subtype=ota_1 -q", "offset"), b"0x130000") + rpt(["--partition-type", "app", "--partition-subtype", "ota_1", "--info", "offset"]), b"0x130000") self.assertEqual( - rpt("--partition-boot-default -q", "offset"), b"0x30000") # ota_0 + 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 -q", "offset"), + self._run_parttool(csv_mod, ["--partition-boot-default", "--info", "offset"]), b"0x130000") # now default is ota_1 diff --git a/components/spiffs/Makefile.projbuild b/components/spiffs/Makefile.projbuild index 2fab90b17..13b2223a9 100644 --- a/components/spiffs/Makefile.projbuild +++ b/components/spiffs/Makefile.projbuild @@ -21,9 +21,9 @@ define spiffs_create_partition_image $(1)_bin: $(PARTITION_TABLE_BIN) | check_python_dependencies - partition_size=`$(GET_PART_INFO) --partition-name $(1) \ + partition_size=`$(GET_PART_INFO) \ --partition-table-file $(PARTITION_TABLE_BIN) \ - get_partition_info --info size`; \ + get_partition_info --partition-name $(1) --info size`; \ $(PYTHON) $(SPIFFSGEN_PY) $$$$partition_size $(2) $(BUILD_DIR_BASE)/$(1).bin \ --page-size=$(CONFIG_SPIFFS_PAGE_SIZE) \ --obj-name-len=$(CONFIG_SPIFFS_OBJ_NAME_LEN) \ @@ -41,5 +41,5 @@ endif endef ESPTOOL_ALL_FLASH_ARGS += $(foreach partition,$(SPIFFSGEN_FLASH_IN_PROJECT), \ -$(shell $(GET_PART_INFO) --partition-name $(partition) \ ---partition-table-file $(PARTITION_TABLE_BIN) get_partition_info --info offset) $(BUILD_DIR_BASE)/$(partition).bin) \ No newline at end of file +$(shell $(GET_PART_INFO) --partition-table-file $(PARTITION_TABLE_BIN) \ +get_partition_info --partition-name $(partition) --info offset) $(BUILD_DIR_BASE)/$(partition).bin) \ No newline at end of file diff --git a/docs/en/api-guides/partition-tables.rst b/docs/en/api-guides/partition-tables.rst index 502c7118c..000a9b361 100644 --- a/docs/en/api-guides/partition-tables.rst +++ b/docs/en/api-guides/partition-tables.rst @@ -169,4 +169,100 @@ A manual flashing command is also printed as part of ``make partition_table``. Note that updating the partition table doesn't erase data that may have been stored according to the old partition table. You can use ``make erase_flash`` (or ``esptool.py erase_flash``) to erase the entire flash contents. +Partition Tool (parttool.py) +---------------------------- + +The component `partition_table` provides a tool :component_file:`parttool.py` for performing partition-related operations on a target device. The following operations can be performed using the tool: + + - reading a partition and saving the contents to a file (read_partition) + - writing the contents of a file to a partition (write_partition) + - erasing a partition (erase_partition) + - retrieving info such as offset and size of a given partition (get_partition_info) + +The tool can either be imported and used from another Python script or invoked from shell script for users wanting to perform operation programmatically. This is facilitated by the tool's Python API +and command-line interface, respectively. + +Python API +~~~~~~~~~~~ + +Before anything else, make sure that the `parttool` module is imported. + +.. code-block:: python + + import sys + import os + + idf_path = os.environ["IDF_PATH"] # get value of IDF_PATH from environment + parttool_dir = os.path.join(idf_path, "components", "partition_table") # parttool.py lives in $IDF_PATH/components/partition_table + + sys.path.append(parttool_dir) # this enables Python to find parttool module + from parttool import * # import all names inside parttool module + +The starting point for using the tool's Python API to do is create a `ParttoolTarget` object: + +.. code-block:: python + + # Create a partool.py target device connected on serial port /dev/ttyUSB1 + target = ParttoolTarget("/dev/ttyUSB1") + +The created object can now be used to perform operations on the target device: + +.. code-block:: python + + # Erase partition with name 'storage' + target.erase_partition(PartitionName("storage")) + + # Read partition with type 'data' and subtype 'spiffs' and save to file 'spiffs.bin' + target.read_partition(PartitionType("data", "spiffs"), "spiffs.bin") + + # Write to partition 'factory' the contents of a file named 'factory.bin' + target.write_partition(PartitionName("factory"), "factory.bin") + + # Print the size of default boot partition + storage = target.get_partition_info(PARTITION_BOOT_DEFAULT) + print(storage.size) + +The partition to operate on is specified using `PartitionName` or `PartitionType` or PARTITION_BOOT_DEFAULT. As the name implies, these can be used to refer +to partitions of a particular name, type-subtype combination, or the default boot partition. + +More information on the Python API is available in the docstrings for the tool. + +Command-line Interface +~~~~~~~~~~~~~~~~~~~~~~ + +The command-line interface of `parttool.py` has the following structure: + +.. code-block:: bash + + parttool.py [command-args] [subcommand] [subcommand-args] + + - command-args - These are arguments that are needed for executing the main command (parttool.py), mostly pertaining to the target device + - subcommand - This is the operation to be performed + - subcommand-args - These are arguments that are specific to the chosen operation + +.. code-block:: bash + + # Erase partition with name 'storage' + parttool.py --port "/dev/ttyUSB1" erase_partition --partition-name=storage + + # Read partition with type 'data' and subtype 'spiffs' and save to file 'spiffs.bin' + parttool.py --port "/dev/ttyUSB1" read_partition --partition-type=data --partition-subtype=spiffs "spiffs.bin" + + # Write to partition 'factory' the contents of a file named 'factory.bin' + parttool.py --port "/dev/ttyUSB1" write_partition --partition-name=factory "factory.bin" + + # Print the size of default boot partition + parttool.py --port "/dev/ttyUSB1" get_partition_info --partition-boot-default --info size + +More information can be obtained by specifying `--help` as argument: + +.. code-block:: bash + + # Display possible subcommands and show main command argument descriptions + parttool.py --help + + # Show descriptions for specific subcommand arguments + parttool.py [subcommand] --help + + .. _secure boot: security/secure-boot.rst diff --git a/tools/ci/executable-list.txt b/tools/ci/executable-list.txt index 5cbf7b3a2..0dc1c7fd1 100644 --- a/tools/ci/executable-list.txt +++ b/tools/ci/executable-list.txt @@ -7,6 +7,7 @@ components/espcoredump/test/test_espcoredump.sh components/heap/test_multi_heap_host/test_all_configs.sh components/idf_test/unit_test/TestCaseScript/IDFUnitTest/__init__.py components/nvs_flash/nvs_partition_generator/nvs_partition_gen.py +components/partition_table/gen_empty_partition.py components/partition_table/gen_esp32part.py components/partition_table/parttool.py components/partition_table/test_gen_esp32part_host/gen_esp32part_tests.py