mkbootimg changes for bootconfig

These are changes to vendor boot image v4.
Append the vendor bootconfig file to the end of the vendor boot image
and add the size of the file to the vendor boot header.
unpack_bootimg finds the size in the header, and unpacks the bootconfig
file that was used to create the image.

Test: Create the new bootconfig file and verify that is in
vendor_boot.img using (1) unpack_bootimg and (2) cuttleifish bootloader
Test: atest --host mkbootimg_test
Bug: 173815685

Change-Id: I1816c19555f7263f7f7a33a57050a65324df5717
diff --git a/include/bootimg/bootimg.h b/include/bootimg/bootimg.h
index 881f8d6..784499e 100644
--- a/include/bootimg/bootimg.h
+++ b/include/bootimg/bootimg.h
@@ -335,11 +335,14 @@
  * +------------------------+
  * | vendor ramdisk table   | r pages
  * +------------------------+
+ * | bootconfig             | s pages
+ * +------------------------+
  *
- * o = (2124 + page_size - 1) / page_size
+ * o = (2128 + page_size - 1) / page_size
  * p = (vendor_ramdisk_size + page_size - 1) / page_size
  * q = (dtb_size + page_size - 1) / page_size
  * r = (vendor_ramdisk_table_size + page_size - 1) / page_size
+ * s = (vendor_bootconfig_size + page_size - 1) / page_size
  *
  * Note that in version 4 of the vendor boot image, multiple vendor ramdisks can
  * be included in the vendor boot image. The bootloader can select a subset of
@@ -364,6 +367,12 @@
  *    - VENDOR_RAMDISK_TYPE_DLKM ramdisk contains dynamic loadable kernel
  *      modules.
  *
+ * Version 4 of the vendor boot image also adds a bootconfig section to the end
+ * of the image. This section contains Boot Configuration parameters known at
+ * build time. The bootloader is responsible for placing this section directly
+ * after the generic ramdisk, followed by the bootconfig trailer, before
+ * entering the kernel.
+ *
  * 0. all entities in the boot image are 4096-byte aligned in flash, all
  *    entities in the vendor boot image are page_size (determined by the vendor
  *    and specified in the vendor boot image header) aligned in flash
@@ -373,8 +382,10 @@
  * 3. load the vendor ramdisks at ramdisk_addr
  * 4. load the generic ramdisk immediately following the vendor ramdisk in
  *    memory
- * 5. set up registers for kernel entry as required by your architecture
- * 6. if the platform has a second stage bootloader jump to it (must be
+ * 5. load the bootconfig immediately following the generic ramdisk. Add
+ *    additional bootconfig parameters followed by the bootconfig trailer.
+ * 6. set up registers for kernel entry as required by your architecture
+ * 7. if the platform has a second stage bootloader jump to it (must be
  *    contained outside boot and vendor boot partitions), otherwise
  *    jump to kernel_addr
  */
@@ -385,6 +396,7 @@
     uint32_t vendor_ramdisk_table_size; /* size in bytes for the vendor ramdisk table */
     uint32_t vendor_ramdisk_table_entry_num; /* number of entries in the vendor ramdisk table */
     uint32_t vendor_ramdisk_table_entry_size; /* size in bytes for a vendor ramdisk table entry */
+    uint32_t bootconfig_size; /* size in bytes for the bootconfig section */
 } __attribute__((packed));
 
 struct vendor_ramdisk_table_entry_v4 {
diff --git a/mkbootimg.py b/mkbootimg.py
index 0f7634f..a3f39dc 100755
--- a/mkbootimg.py
+++ b/mkbootimg.py
@@ -33,7 +33,7 @@
 
 VENDOR_BOOT_MAGIC = 'VNDRBOOT'
 VENDOR_BOOT_IMAGE_HEADER_V3_SIZE = 2112
-VENDOR_BOOT_IMAGE_HEADER_V4_SIZE = 2124
+VENDOR_BOOT_IMAGE_HEADER_V4_SIZE = 2128
 
 VENDOR_RAMDISK_TYPE_NONE = 0
 VENDOR_RAMDISK_TYPE_PLATFORM = 1
@@ -148,7 +148,8 @@
         args.vendor_boot.write(pack('I', args.vendor_ramdisk_table_entry_num))
         # vendor ramdisk table entry size in bytes
         args.vendor_boot.write(pack('I', VENDOR_RAMDISK_TABLE_ENTRY_V4_SIZE))
-
+        # bootconfig section size in bytes
+        args.vendor_boot.write(pack('I', filesize(args.vendor_bootconfig)))
     pad_file(args.vendor_boot, args.pagesize)
 
 
@@ -488,6 +489,8 @@
                         help='vendor boot output file name')
     parser.add_argument('--vendor_ramdisk', type=FileType('rb'),
                         help='path to the vendor ramdisk')
+    parser.add_argument('--vendor_bootconfig', type=FileType('rb'),
+                        help='path to the vendor bootconfig file')
 
     args, extra_args = parser.parse_known_args()
     if args.vendor_boot is not None and args.header_version > 3:
@@ -514,6 +517,8 @@
         builder.write_ramdisks_padded(args.vendor_boot, args.pagesize)
         write_padded_file(args.vendor_boot, args.dtb, args.pagesize)
         builder.write_entries_padded(args.vendor_boot, args.pagesize)
+        write_padded_file(args.vendor_boot, args.vendor_bootconfig,
+            args.pagesize)
     else:
         write_padded_file(args.vendor_boot, args.vendor_ramdisk, args.pagesize)
         write_padded_file(args.vendor_boot, args.dtb, args.pagesize)
diff --git a/tests/mkbootimg_test.py b/tests/mkbootimg_test.py
index e7794b7..9829ed4 100644
--- a/tests/mkbootimg_test.py
+++ b/tests/mkbootimg_test.py
@@ -14,6 +14,8 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+"""Tests mkbootimg and unpack_bootimg."""
+
 import json
 import os
 import subprocess
@@ -55,8 +57,12 @@
         with tempfile.TemporaryDirectory() as temp_out_dir:
             vendor_boot_img = os.path.join(temp_out_dir, 'vendor_boot.img')
             dtb = create_blank_file(os.path.join(temp_out_dir, 'dtb'), 0x1000)
-            ramdisk1 = create_blank_file(os.path.join(temp_out_dir, 'ramdisk1'), 0x1000)
-            ramdisk2 = create_blank_file(os.path.join(temp_out_dir, 'ramdisk2'), 0x2000)
+            ramdisk1 = create_blank_file(os.path.join(temp_out_dir, 'ramdisk1'),
+                0x1000)
+            ramdisk2 = create_blank_file(os.path.join(temp_out_dir, 'ramdisk2'),
+                0x2000)
+            bootconfig = create_blank_file(os.path.join(temp_out_dir,
+                'bootconfig'), 0x1000)
             mkbootimg_cmds = [
                 'mkbootimg',
                 '--header_version', '4',
@@ -70,6 +76,7 @@
                 '--board_id0', '0xC0FFEE',
                 '--board_id15', '0x15151515',
                 '--vendor_ramdisk_fragment', ramdisk2,
+                '--vendor_bootconfig', bootconfig,
             ]
             unpack_bootimg_cmds = [
                 'unpack_bootimg',
@@ -92,6 +99,7 @@
                 '0x00000000, 0x00000000, 0x00000000, 0x00000000,',
                 '0x00000000, 0x00000000, 0x00000000, 0x00000000,',
                 '0x00000000, 0x00000000, 0x00000000, 0x15151515,',
+                'vendor bootconfig size: 4096',
             ]
 
             subprocess.run(mkbootimg_cmds, check=True)
@@ -113,8 +121,10 @@
         """Tests mkbootimg_args.json when unpacking a boot image version 3."""
         with tempfile.TemporaryDirectory() as temp_out_dir:
             boot_img = os.path.join(temp_out_dir, 'boot.img')
-            kernel = create_blank_file(os.path.join(temp_out_dir, 'kernel'), 0x1000)
-            ramdisk = create_blank_file(os.path.join(temp_out_dir, 'ramdisk'), 0x1000)
+            kernel = create_blank_file(os.path.join(temp_out_dir, 'kernel'),
+                0x1000)
+            ramdisk = create_blank_file(os.path.join(temp_out_dir, 'ramdisk'),
+                0x1000)
             mkbootimg_cmds = [
                 'mkbootimg',
                 '--header_version', '3',
@@ -148,11 +158,14 @@
                                  expected_mkbootimg_args)
 
     def test_unpack_vendor_boot_image_v3_json_args(self):
-        """Tests mkbootimg_args.json when unpacking a vendor boot image version 3."""
+        """Tests mkbootimg_args.json when unpacking a vendor boot image version
+        3.
+        """
         with tempfile.TemporaryDirectory() as temp_out_dir:
             vendor_boot_img = os.path.join(temp_out_dir, 'vendor_boot.img')
             dtb = create_blank_file(os.path.join(temp_out_dir, 'dtb'), 0x1000)
-            ramdisk = create_blank_file(os.path.join(temp_out_dir, 'ramdisk'), 0x1000)
+            ramdisk = create_blank_file(os.path.join(temp_out_dir, 'ramdisk'),
+                0x1000)
             mkbootimg_cmds = [
                 'mkbootimg',
                 '--header_version', '3',
diff --git a/unpack_bootimg.py b/unpack_bootimg.py
index 983d647..f5e7238 100755
--- a/unpack_bootimg.py
+++ b/unpack_bootimg.py
@@ -284,6 +284,9 @@
         vendor_ramdisk_table_size = unpack('I', args.boot_img.read(4))[0]
         vendor_ramdisk_table_entry_num = unpack('I', args.boot_img.read(4))[0]
         vendor_ramdisk_table_entry_size = unpack('I', args.boot_img.read(4))[0]
+        num_vendor_ramdisk_table_pages = get_number_of_pages(
+            vendor_ramdisk_table_size, page_size)
+        vendor_bootconfig_size = unpack('I', args.boot_img.read(4))[0]
         vendor_ramdisk_table_offset = page_size * (
             num_boot_header_pages + num_boot_ramdisk_pages + num_boot_dtb_pages)
         print('vendor ramdisk table size: {}'.format(vendor_ramdisk_table_size))
@@ -319,6 +322,12 @@
             image_info_list.append((ramdisk_offset_base + ramdisk_offset,
                                     ramdisk_size, output_ramdisk_name))
         print(']')
+        bootconfig_offset = page_size * (num_boot_header_pages
+            + num_boot_ramdisk_pages + num_boot_dtb_pages
+            + num_vendor_ramdisk_table_pages)
+        image_info_list.append((bootconfig_offset, vendor_bootconfig_size,
+            'bootconfig'))
+        print('vendor bootconfig size: {}'.format(vendor_bootconfig_size))
     else:
         image_info_list.append(
             (ramdisk_offset_base, ramdisk_size, 'vendor_ramdisk'))