binman: Support adding sections to FMAPs

When used with hierarchical images, use the Chromium OS convention of
adding a section before all the subentries it contains.

BUG=b:184146709
TEST=binman test

Signed-off-by: Simon Glass <sjg@chromium.org>
Change-Id: I2ff03fd22106f4301e526765f932354b9eaa5e24
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/u-boot/+/2803414
Reviewed-by: Jack Rosenthal <jrosenth@chromium.org>
diff --git a/tools/binman/README.entries b/tools/binman/README.entries
index bf8edce..3bf9a0b 100644
--- a/tools/binman/README.entries
+++ b/tools/binman/README.entries
@@ -367,8 +367,12 @@
 
 When used, this entry will be populated with an FMAP which reflects the
 entries in the current image. Note that any hierarchy is squashed, since
-FMAP does not support this. Also, CBFS entries appear as a single entry -
-the sub-entries are ignored.
+FMAP does not support this. Sections are represented as an area appearing
+before its contents, so that it is possible to reconstruct the hierarchy
+from the FMAP by using the offset information. This convention does not
+seem to be documented, but is used in Chromium OS.
+
+CBFS entries appear as a single entry, i.e. the sub-entries are ignored.
 
 
 
@@ -691,6 +695,11 @@
     name-prefix: Adds a prefix to the name of every entry in the section
         when writing out the map
 
+Properties:
+    _allow_missing: True if this section permits external blobs to be
+        missing their contents. The second will produce an image but of
+        course it will not work.
+
 Since a section is also an entry, it inherits all the properies of entries
 too.
 
diff --git a/tools/binman/etype/fmap.py b/tools/binman/etype/fmap.py
index 3e9b815..c474770 100644
--- a/tools/binman/etype/fmap.py
+++ b/tools/binman/etype/fmap.py
@@ -28,8 +28,12 @@
 
     When used, this entry will be populated with an FMAP which reflects the
     entries in the current image. Note that any hierarchy is squashed, since
-    FMAP does not support this. Also, CBFS entries appear as a single entry -
-    the sub-entries are ignored.
+    FMAP does not support this. Sections are represented as an area appearing
+    before its contents, so that it is possible to reconstruct the hierarchy
+    from the FMAP by using the offset information. This convention does not
+    seem to be documented, but is used in Chromium OS.
+
+    CBFS entries appear as a single entry, i.e. the sub-entries are ignored.
     """
     def __init__(self, section, etype, node):
         super().__init__(section, etype, node)
@@ -45,6 +49,18 @@
             tout.Debug("fmap: Add entry '%s' type '%s' (%s subentries)" %
                        (entry.GetPath(), entry.etype, ToHexSize(entries)))
             if entries and entry.etype != 'cbfs':
+                # Create an area for the section, which encompasses all entries
+                # within it
+                if entry.image_pos is None:
+                    pos = 0
+                else:
+                    pos = entry.image_pos - entry.GetRootSkipAtStart()
+
+                # Drop @ symbols in name
+                name = entry.name.replace('@', '')
+                areas.append(
+                    fmap_util.FmapArea(pos, entry.size or 0,
+                                       tools.FromUnicode(name), 0))
                 for subentry in entries.values():
                     _AddEntries(areas, subentry)
             else:
diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py
index 05eaefc..308cc33 100644
--- a/tools/binman/ftest.py
+++ b/tools/binman/ftest.py
@@ -1461,19 +1461,31 @@
         self.assertEqual(1, fhdr.ver_major)
         self.assertEqual(0, fhdr.ver_minor)
         self.assertEqual(0, fhdr.base)
-        expect_size = fmap_util.FMAP_HEADER_LEN + fmap_util.FMAP_AREA_LEN * 3
+        expect_size = fmap_util.FMAP_HEADER_LEN + fmap_util.FMAP_AREA_LEN * 5
         self.assertEqual(16 + 16 + expect_size, fhdr.image_size)
         self.assertEqual(b'FMAP', fhdr.name)
-        self.assertEqual(3, fhdr.nareas)
+        self.assertEqual(5, fhdr.nareas)
         fiter = iter(fentries)
 
         fentry = next(fiter)
+        self.assertEqual(b'SECTION0', fentry.name)
+        self.assertEqual(0, fentry.offset)
+        self.assertEqual(16, fentry.size)
+        self.assertEqual(0, fentry.flags)
+
+        fentry = next(fiter)
         self.assertEqual(b'RO_U_BOOT', fentry.name)
         self.assertEqual(0, fentry.offset)
         self.assertEqual(4, fentry.size)
         self.assertEqual(0, fentry.flags)
 
         fentry = next(fiter)
+        self.assertEqual(b'SECTION1', fentry.name)
+        self.assertEqual(16, fentry.offset)
+        self.assertEqual(16, fentry.size)
+        self.assertEqual(0, fentry.flags)
+
+        fentry = next(fiter)
         self.assertEqual(b'RW_U_BOOT', fentry.name)
         self.assertEqual(16, fentry.offset)
         self.assertEqual(4, fentry.size)
@@ -1875,8 +1887,8 @@
         self.assertEqual(expected, data[:32])
         fhdr, fentries = fmap_util.DecodeFmap(data[36:])
 
-        self.assertEqual(0x100, fhdr.image_size)
-        expect_size = fmap_util.FMAP_HEADER_LEN + fmap_util.FMAP_AREA_LEN * 3
+        self.assertEqual(0x180, fhdr.image_size)
+        expect_size = fmap_util.FMAP_HEADER_LEN + fmap_util.FMAP_AREA_LEN * 4
         fiter = iter(fentries)
 
         fentry = next(fiter)
@@ -1885,6 +1897,11 @@
         self.assertEqual(4, fentry.size)
 
         fentry = next(fiter)
+        self.assertEqual(b'SECTION', fentry.name)
+        self.assertEqual(4, fentry.offset)
+        self.assertEqual(0x20 + expect_size, fentry.size)
+
+        fentry = next(fiter)
         self.assertEqual(b'INTEL_MRC', fentry.name)
         self.assertEqual(4, fentry.offset)
         self.assertEqual(3, fentry.size)
diff --git a/tools/binman/test/095_fmap_x86_section.dts b/tools/binman/test/095_fmap_x86_section.dts
index 4cfce45..fd5f018 100644
--- a/tools/binman/test/095_fmap_x86_section.dts
+++ b/tools/binman/test/095_fmap_x86_section.dts
@@ -7,7 +7,7 @@
 
 	binman {
 		end-at-4gb;
-		size = <0x100>;
+		size = <0x180>;
 		u-boot {
 		};
 		section {