]> granicus.if.org Git - esp-idf/commitdiff
partition_table: Support same fallback logic as bootloader for default boot partition
authorAngus Gratton <angus@espressif.com>
Fri, 22 Jun 2018 01:14:22 +0000 (11:14 +1000)
committerAngus Gratton <gus@projectgus.com>
Fri, 22 Jun 2018 01:14:22 +0000 (11:14 +1000)
Generates correct "make flash" command even when partition table has no factory partition.

Also adds unit tests for parttool.py

Closes https://github.com/espressif/esp-idf/issues/2086

components/partition_table/Makefile.projbuild
components/partition_table/gen_esp32part.py
components/partition_table/parttool.py
components/partition_table/test_gen_esp32part_host/gen_esp32part_tests.py

index 35dc4f0db3ee275ed7dbec1c94a6beddb3c98797..9e4e61052fe5ee4c2923538917e5e9f06efb83ae 100644 (file)
@@ -57,7 +57,7 @@ all_binaries: $(PARTITION_TABLE_BIN) partition_table_get_info
 
 partition_table_get_info: $(PARTITION_TABLE_BIN)
        $(eval PHY_DATA_OFFSET:=$(shell $(GET_PART_INFO) --type data --subtype phy --offset $(PARTITION_TABLE_BIN)))
-       $(eval APP_OFFSET:=$(shell $(GET_PART_INFO) --type app --subtype factory --offset $(PARTITION_TABLE_BIN)))
+       $(eval APP_OFFSET:=$(shell $(GET_PART_INFO) --default-boot-partition --offset $(PARTITION_TABLE_BIN)))
 
 export APP_OFFSET
 export PHY_DATA_OFFSET
index 86d5362934d51350ead9352f6565ef094cfea8fb..8871e90507446e3a34fe5c7edc5a9bbb5d53b532 100755 (executable)
@@ -106,6 +106,38 @@ class PartitionTable(list):
         else:
             return super(PartitionTable, self).__getitem__(item)
 
+    def find_by_type(self, ptype, subtype):
+        """ Return a partition by type & subtype, returns
+        None if not found """
+        TYPES = PartitionDefinition.TYPES
+        SUBTYPES = PartitionDefinition.SUBTYPES
+        # convert ptype & subtypes names (if supplied this way) to integer values
+        try:
+            ptype = TYPES[ptype]
+        except KeyError:
+            try:
+                ptypes = int(ptype, 0)
+            except TypeError:
+                pass
+        try:
+            subtype = SUBTYPES[int(ptype)][subtype]
+        except KeyError:
+            try:
+                ptypes = int(ptype, 0)
+            except TypeError:
+                pass
+
+        for p in self:
+            if p.type == ptype and p.subtype == subtype:
+                return p
+        return None
+
+    def find_by_name(self, name):
+        for p in self:
+            if p.name == name:
+                return p
+        return None
+
     def verify(self):
         # verify each partition individually
         for p in self:
@@ -202,7 +234,7 @@ class PartitionDefinition(object):
         "encrypted" : 0
     }
 
-    # add subtypes for the 16 OTA slot values ("ota_XXX, etc.")
+    # add subtypes for the 16 OTA slot values ("ota_XX, etc.")
     for ota_slot in range(16):
         SUBTYPES[TYPES["app"]]["ota_%d" % ota_slot] = 0x10 + ota_slot
 
index e6ccf253229c97b55a5e0ac6f9b65fdf7a44507a..8188dfe3dd0c22927b6c587b2eff72620360ecd0 100755 (executable)
@@ -48,17 +48,30 @@ def main():
     parser = argparse.ArgumentParser(description='Returns info about the required partition.')
 
     parser.add_argument('--quiet', '-q', help="Don't print status messages to stderr", action='store_true')
-    parser.add_argument('--partition-name', '-p', help='The name of the required partition', type=str, default=None)
-    parser.add_argument('--type', '-t', help='The type of the required partition', type=str, default=None)
+
+    search_type = parser.add_mutually_exclusive_group()
+    search_type.add_argument('--partition-name', '-p', help='The name of the required partition', type=str, default=None)
+    search_type.add_argument('--type', '-t', help='The type of the required partition', type=str, default=None)
+    search_type.add_argument('--default-boot-partition', help='Select the default boot partition, '+
+                             'using the same fallback logic as the IDF bootloader', action="store_true")
+
     parser.add_argument('--subtype', '-s', help='The subtype of the required partition', type=str, default=None)
-    
-    parser.add_argument('--offset', '-o', help='Return offset of required partition', action="store_true", default=None)
-    parser.add_argument('--size', help='Return size of required partition', action="store_true", default=None)
-    
-    parser.add_argument('input', help='Path to CSV or binary file to parse. Will use stdin if omitted.', type=argparse.FileType('rb'), default=sys.stdin)
+
+    parser.add_argument('--offset', '-o', help='Return offset of required partition', action="store_true")
+    parser.add_argument('--size', help='Return size of required partition', action="store_true")
+
+    parser.add_argument('input', help='Path to CSV or binary file to parse. Will use stdin if omitted.',
+                        type=argparse.FileType('rb'), default=sys.stdin)
 
     args = parser.parse_args()
 
+    if args.type is not None and args.subtype is None:
+        status("If --type is specified, --subtype is required")
+        return 2
+    if args.type is None and args.subtype is not None:
+        status("--subtype is only used with --type")
+        return 2
+
     quiet = args.quiet
 
     input = args.input.read()
@@ -71,36 +84,30 @@ def main():
         status("Parsing CSV input...")
         table = gen.PartitionTable.from_csv(input)
 
-    if args.partition_name is not None:
-        offset = 0
-        size = 0
-        for p in table:
-            if p.name == args.partition_name:
-                offset = p.offset
-                size = p.size
-                break;
-        if args.offset is not None:
-            print('0x%x ' % (offset))
-        if args.size is not None:
-            print('0x%x' % (size))
-        return 0
-    
-    if args.type is not None and args.subtype is not None:
-        offset = 0
-        size = 0
-        TYPES = gen.PartitionDefinition.TYPES
-        SUBTYPES = gen.PartitionDefinition.SUBTYPES
-        for p in table:
-            if p.type == TYPES[args.type]:
-                if p.subtype == SUBTYPES[TYPES[args.type]][args.subtype]:
-                    offset = p.offset
-                    size = p.size
-                    break;
-        if args.offset is not None:
-            print('0x%x ' % (offset))
-        if args.size is not None:
-            print('0x%x' % (size))
-        return 0
+    found_partition = None
+
+    if args.default_boot_partition:
+        search = [ "factory" ] + [ "ota_%d" % d for d in range(16) ]
+        for subtype in search:
+            found_partition = table.find_by_type("app", subtype)
+            if found_partition is not None:
+                break
+    elif args.partition_name is not None:
+        found_partition = table.find_by_name(args.partition_name)
+    elif args.type is not None:
+        found_partition = table.find_by_type(args.type, args.subtype)
+    else:
+        raise RuntimeError("invalid partition selection choice")
+
+    if found_partition is None:
+        return 1  # nothing found
+
+    if args.offset:
+        print('0x%x ' % (found_partition.offset))
+    if args.size:
+        print('0x%x' % (found_partition.size))
+
+    return 0
 
 class InputError(RuntimeError):
     def __init__(self, e):
@@ -115,7 +122,8 @@ class ValidationError(InputError):
 
 if __name__ == '__main__':
     try:
-        main()
+        r = main()
+        sys.exit(r)
     except InputError as e:
         print(e, file=sys.stderr)
         sys.exit(2)
index 4919a53d8784447ce1401d51ff9dd11dc93e168f..a1d25034062fdc072af41bb76faae63f91a99c53 100755 (executable)
@@ -356,5 +356,53 @@ app,app, factory, 32K, 1M
             t.verify()
 
 
+class PartToolTests(unittest.TestCase):
+
+    def _run_parttool(self, csvcontents, args):
+        csvpath = tempfile.mktemp()
+        with open(csvpath, "w") as f:
+            f.write(csvcontents)
+        try:
+            return subprocess.check_output([sys.executable, "../parttool.py"] + args.split(" ") + [ csvpath ]).strip()
+        finally:
+            os.remove(csvpath)
+
+    def test_find_basic(self):
+        csv = """
+nvs,      data, nvs,     0x9000,  0x4000
+otadata,  data, ota,     0xd000,  0x2000
+phy_init, data, phy,     0xf000,  0x1000
+factory,  app, factory, 0x10000,  1M
+        """
+        rpt = lambda args: self._run_parttool(csv, args)
+
+        self.assertEqual(
+            rpt("--type data --subtype nvs --offset"), "0x9000")
+        self.assertEqual(
+            rpt("--type data --subtype nvs --size"), "0x4000")
+        self.assertEqual(
+            rpt("--partition-name otadata --offset"), "0xd000")
+        self.assertEqual(
+            rpt("--default-boot-partition --offset"), "0x10000")
+
+    def test_fallback(self):
+        csv = """
+nvs,      data, nvs,     0x9000,  0x4000
+otadata,  data, ota,     0xd000,  0x2000
+phy_init, data, phy,     0xf000,  0x1000
+ota_0,  app,    ota_0,   0x30000,  1M
+ota_1,  app,    ota_1,          ,  1M
+        """
+        rpt = lambda args: self._run_parttool(csv, args)
+
+        self.assertEqual(
+            rpt("--type app --subtype ota_1 --offset"), "0x130000")
+        self.assertEqual(
+            rpt("--default-boot-partition --offset"), "0x30000")  # ota_0
+        csv_mod = csv.replace("ota_0", "ota_2")
+        self.assertEqual(
+            self._run_parttool(csv_mod, "--default-boot-partition --offset"),
+            "0x130000")  # now default is ota_1
+
 if __name__ =="__main__":
     unittest.main()