binman: Allow sections to have an offset

At present sections are always placed automatically. Even if an 'offset'
property is provided it is ignored. Update the logic to support an offset
for sections.

Signed-off-by: Simon Glass <sjg@chromium.org>
Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
diff --git a/tools/binman/README b/tools/binman/README
index 04ed2b79..927fa85 100644
--- a/tools/binman/README
+++ b/tools/binman/README
@@ -342,6 +342,13 @@
 	Sets the image size in bytes, for example 'size = <0x100000>' for a
 	1MB image.
 
+offset:
+	This is similar to 'offset' in entries, setting the offset of a section
+	within the image or section containing it. The first byte of the section
+	is normally at offset 0. If 'offset' is not provided, binman sets it to
+	the end of the previous region, or the start of the image's entry area
+	(normally 0) if there is no previous region.
+
 align-size:
 	This sets the alignment of the image size. For example, to ensure
 	that the image ends on a 512-byte boundary, use 'align-size = <512>'.
diff --git a/tools/binman/bsection.py b/tools/binman/bsection.py
index ccf2920..0ba542e 100644
--- a/tools/binman/bsection.py
+++ b/tools/binman/bsection.py
@@ -57,7 +57,7 @@
         self._name = name
         self._node = node
         self._image = image
-        self._offset = 0
+        self._offset = None
         self._size = None
         self._align_size = None
         self._pad_before = 0
@@ -75,6 +75,7 @@
 
     def _ReadNode(self):
         """Read properties from the section node"""
+        self._offset = fdt_util.GetInt(self._node, 'offset')
         self._size = fdt_util.GetInt(self._node, 'size')
         self._align_size = fdt_util.GetInt(self._node, 'align-size')
         if tools.NotPowerOfTwo(self._align_size):
@@ -130,7 +131,7 @@
             entry.AddMissingProperties()
 
     def SetCalculatedProperties(self):
-        state.SetInt(self._node, 'offset', self._offset)
+        state.SetInt(self._node, 'offset', self._offset or 0)
         state.SetInt(self._node, 'size', self._size)
         image_pos = self._image_pos
         if self._parent_section:
@@ -424,8 +425,8 @@
         Args:
             fd: File to write the map to
         """
-        Entry.WriteMapLine(fd, indent, self._name, self._offset, self._size,
-                           self._image_pos)
+        Entry.WriteMapLine(fd, indent, self._name, self._offset or 0,
+                           self._size, self._image_pos)
         for entry in self._entries.values():
             entry.WriteMap(fd, indent + 1)
 
diff --git a/tools/binman/etype/section.py b/tools/binman/etype/section.py
index 7f1b413..3681a48 100644
--- a/tools/binman/etype/section.py
+++ b/tools/binman/etype/section.py
@@ -67,7 +67,8 @@
     def Pack(self, offset):
         """Pack all entries into the section"""
         self._section.PackEntries()
-        self._section.SetOffset(offset)
+        if self._section._offset is None:
+            self._section.SetOffset(offset)
         self.size = self._section.GetSize()
         return super(Entry_section, self).Pack(offset)
 
diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py
index 75658c9..daea1ea 100644
--- a/tools/binman/ftest.py
+++ b/tools/binman/ftest.py
@@ -1783,6 +1783,24 @@
         data = self._DoReadFile('100_intel_refcode.dts')
         self.assertEqual(REFCODE_DATA, data[:len(REFCODE_DATA)])
 
+    def testSectionOffset(self):
+        """Tests use of a section with an offset"""
+        data, _, map_data, _ = self._DoReadFileDtb('101_sections_offset.dts',
+                                                   map=True)
+        self.assertEqual('''ImagePos    Offset      Size  Name
+00000000  00000000  00000038  main-section
+00000004   00000004  00000010  section@0
+00000004    00000000  00000004  u-boot
+00000018   00000018  00000010  section@1
+00000018    00000000  00000004  u-boot
+0000002c   0000002c  00000004  section@2
+0000002c    00000000  00000004  u-boot
+''', map_data)
+        self.assertEqual(data,
+                         4 * chr(0x26) + U_BOOT_DATA + 12 * chr(0x21) +
+                         4 * chr(0x26) + U_BOOT_DATA + 12 * chr(0x61) +
+                         4 * chr(0x26) + U_BOOT_DATA + 8 * chr(0x26))
+
 
 if __name__ == "__main__":
     unittest.main()
diff --git a/tools/binman/test/101_sections_offset.dts b/tools/binman/test/101_sections_offset.dts
new file mode 100644
index 0000000..46708ff
--- /dev/null
+++ b/tools/binman/test/101_sections_offset.dts
@@ -0,0 +1,35 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		pad-byte = <0x26>;
+		size = <0x38>;
+		section@0 {
+			read-only;
+			offset = <0x4>;
+			size = <0x10>;
+			pad-byte = <0x21>;
+
+			u-boot {
+			};
+		};
+		section@1 {
+			size = <0x10>;
+			pad-byte = <0x61>;
+			offset = <0x18>;
+
+			u-boot {
+			};
+		};
+		section@2 {
+			offset = <0x2c>;
+			u-boot {
+			};
+		};
+	};
+};