import argparse
import sys
+MAX_PARTITION_LENGTH = 0xC00 # 3K for partition data (96 entries) leaves 1K in a 4K sector for signature
+
__version__ = '1.0'
quiet = False
return result
def to_binary(self):
- return "".join(e.to_binary() for e in self)
+ result = "".join(e.to_binary() for e in self)
+ if len(result )>= MAX_PARTITION_LENGTH:
+ raise InputError("Binary partition table length (%d) longer than max" % len(result))
+ result += "\xFF" * (MAX_PARTITION_LENGTH - len(result)) # pad the sector, for signing
+ return result
def to_csv(self, simple_formatting=False):
rows = [ "# Espressif ESP32 Partition Table",
"second" + ("\0"*10) + \
"\x00\x00\x00\x00"
+def _strip_trailing_ffs(binary_table):
+ while binary_table.endswith("\xFF"):
+ binary_table = binary_table[0:len(binary_table)-1]
+ return binary_table
class CSVParserTests(unittest.TestCase):
first, 0x30, 0xEE, 0x100400, 0x300000
"""
t = PartitionTable.from_csv(csv)
- tb = t.to_binary()
+ tb = _strip_trailing_ffs(t.to_binary())
self.assertEqual(len(tb), 32)
self.assertEqual('\xAA\x50', tb[0:2]) # magic
self.assertEqual('\x30\xee', tb[2:4]) # type, subtype
second,0x31, 0xEF, , 0x100000
"""
t = PartitionTable.from_csv(csv)
- tb = t.to_binary()
+ tb = _strip_trailing_ffs(t.to_binary())
self.assertEqual(len(tb), 64)
self.assertEqual('\xAA\x50', tb[0:2])
self.assertEqual('\xAA\x50', tb[32:34])
self.assertEqual(t[2].type, 0x10)
self.assertEqual(t[2].name, "second")
- round_trip = t.to_binary()
+ round_trip = _strip_trailing_ffs(t.to_binary())
self.assertEqual(round_trip, LONGER_BINARY_TABLE)
def test_bad_magic(self):
self.assertEqual(row[0], "factory")
self.assertEqual(row[1], "app")
self.assertEqual(row[2], "2")
- self.assertEqual(row[3], "64K")
+ self.assertEqual(row[3], "0x10000")
self.assertEqual(row[4], "1M")
# round trip back to a PartitionTable and check is identical
# reopen the CSV and check the generated binary is identical
with open(csvpath, 'r') as f:
from_csv = PartitionTable.from_csv(f.read())
- self.assertEqual(from_csv.to_binary(), LONGER_BINARY_TABLE)
+ self.assertEqual(_strip_trailing_ffs(from_csv.to_binary()), LONGER_BINARY_TABLE)
# run gen_esp32part.py to conver the CSV to binary again
subprocess.check_call([sys.executable, "../gen_esp32part.py",
# assert that file reads back as identical
with open(binpath, 'rb') as f:
binary_readback = f.read()
+ binary_readback = _strip_trailing_ffs(binary_readback)
self.assertEqual(binary_readback, LONGER_BINARY_TABLE)
finally:
A single ESP32's flash can contain multiple apps, as well as many different kinds of data (calibration data, filesystems, parameter storage, etc). For this reason a partition table is flashed to offset 0x4000 in the flash.
+Partition table length is 0xC00 bytes (maximum 95 partition table entries). If the partition table is signed due to `secure boot`, the signature is appended after the table data.
+
Each entry in the partition table has a name (label), type (app, data, or something else), subtype and the offset in flash where the partition is loaded.
The simplest way to use the partition table is to `make menuconfig` and choose one of the simple predefined partition tables:
* ``make flash``: Will flash everything including the partition table.
A manual flashing command is also printed as part of ``make partition_table``.
+
+
+.. _secure boot: security/secure-boot.rst