partition_table: implement Python API for parttool

Closes https://github.com/espressif/esp-idf/issues/1494
This commit is contained in:
Renz Christian Bagaporo 2019-05-27 11:07:54 +08:00
parent 2c55fae6cf
commit 63bd57c1d7
8 changed files with 373 additions and 205 deletions

View file

@ -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

View file

@ -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)

View file

@ -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__':

View file

@ -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)

View file

@ -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

View file

@ -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)
$(shell $(GET_PART_INFO) --partition-table-file $(PARTITION_TABLE_BIN) \
get_partition_info --partition-name $(partition) --info offset) $(BUILD_DIR_BASE)/$(partition).bin)

View file

@ -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<partition_table/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

View file

@ -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