mkdtimg: reuse identical FDT blobs in dtbo image

When multiple dt_table_entries uses the same FDT, do
not keep multiple copies in the output dtbo partition
image. Instead, keep dt_offset in the dt_table_entries
point to the same blob.

Update mkdtimg_testdata.sh to test the above case too.

Bug: 36792954
Test: run mkdtimg_testdata.sh
Change-Id: I3413b52e1174154d9f6146efd8cead3f50c0f256
diff --git a/utils/src/mkdtimg_core.c b/utils/src/mkdtimg_core.c
index de02b19..b8930d5 100644
--- a/utils/src/mkdtimg_core.c
+++ b/utils/src/mkdtimg_core.c
@@ -34,6 +34,9 @@
   uint32_t entry_count;
   uint32_t entry_offset;
   uint32_t dt_offset;
+
+  char (*past_filenames)[1024];
+  uint32_t *past_dt_offsets;
 };
 
 
@@ -161,11 +164,10 @@
   return 0;
 }
 
-static size_t output_img_entry(FILE *img_fp,
-                               size_t entry_offset, size_t dt_offset,
-                               const char *fdt_filename,
-                               struct dt_options *options) {
-  size_t ret = 0;
+static int32_t output_img_entry(FILE *img_fp, size_t entry_offset,
+                                size_t dt_offset, const char *fdt_filename,
+                                struct dt_options *options, int reuse_fdt) {
+  int32_t ret = -1;
   void *fdt = NULL;
 
   size_t fdt_file_size;
@@ -200,11 +202,13 @@
   fwrite(&entry, sizeof(entry), 1, img_fp);
 
   /* Output FDT */
-  fseek(img_fp, dt_offset, SEEK_SET);
-  fwrite(fdt, fdt_file_size, 1, img_fp);
-
-  /* return the fdt_file_size */
-  ret = fdt_file_size;
+  if (!reuse_fdt) {
+    fseek(img_fp, dt_offset, SEEK_SET);
+    fwrite(fdt, fdt_file_size, 1, img_fp);
+    ret = fdt_file_size;
+  } else {
+    ret = 0;
+  }
 
 end:
   if (fdt) free(fdt);
@@ -215,6 +219,12 @@
 
 struct dt_image_writer *dt_image_writer_start(FILE *img_fp, uint32_t entry_count) {
   struct dt_image_writer *writer = malloc(sizeof(struct dt_image_writer));
+  if (!writer) goto error;
+  writer->past_filenames = calloc(entry_count, sizeof(*writer->past_filenames));
+  if (!writer->past_filenames) goto error;
+  writer->past_dt_offsets =
+      calloc(entry_count, sizeof(*writer->past_dt_offsets));
+  if (!writer->past_dt_offsets) goto error;
   writer->img_fp = img_fp;
   init_dt_global_options(&writer->global_options);
   init_dt_options(&writer->entry_options);
@@ -224,6 +234,14 @@
   writer->dt_offset =
       writer->entry_offset + sizeof(struct dt_table_entry) * entry_count;
   return writer;
+
+error:
+  fprintf(stderr, "Unable to start writer\n");
+  if (!writer) return NULL;
+  if (writer->past_filenames) free(writer->past_filenames);
+  if (writer->past_dt_offsets) free(writer->past_dt_offsets);
+  free(writer);
+  return NULL;
 }
 
 static int set_dt_options(struct dt_options *options,
@@ -270,14 +288,31 @@
     return 0;
   }
 
-  uint32_t dt_size = output_img_entry(
-      writer->img_fp,
-      writer->entry_offset,
-      writer->dt_offset,
-      writer->entry_filename,
-      &writer->entry_options);
-  if (dt_size == 0) {
-    return -1;
+  int reuse_fdt;
+  int fdt_idx;
+  uint32_t dt_offset;
+
+  for (fdt_idx = 0; writer->past_filenames[fdt_idx][0] != '\0'; fdt_idx++) {
+    if (strcmp(writer->past_filenames[fdt_idx], writer->entry_filename) == 0)
+      break;
+  }
+
+  if (writer->past_filenames[fdt_idx][0] != '\0') {
+    reuse_fdt = 1;
+    dt_offset = writer->past_dt_offsets[fdt_idx];
+  } else {
+    reuse_fdt = 0;
+    dt_offset = writer->dt_offset;
+  }
+  int32_t dt_size = output_img_entry(writer->img_fp, writer->entry_offset,
+                                     dt_offset, writer->entry_filename,
+                                     &writer->entry_options, reuse_fdt);
+  if (dt_size == -1) return -1;
+
+  if (!reuse_fdt) {
+    strncpy(writer->past_filenames[fdt_idx], writer->entry_filename,
+            sizeof(writer->past_filenames[fdt_idx]) - 1);
+    writer->past_dt_offsets[fdt_idx] = dt_offset;
   }
 
   writer->entry_offset += sizeof(struct dt_table_entry);
@@ -324,6 +359,8 @@
   ret = 0;
 
 end:
+  free(writer->past_filenames);
+  free(writer->past_dt_offsets);
   free(writer);
 
   return ret;
diff --git a/utils/tests/data/mkdtimg.cfg b/utils/tests/data/mkdtimg.cfg
index 8c0b024..3cf2a81 100644
--- a/utils/tests/data/mkdtimg.cfg
+++ b/utils/tests/data/mkdtimg.cfg
@@ -11,3 +11,6 @@
 
 board2v1.dts.dtb
   rev=0x201               # override with another value
+
+board1v1.dts.dtb
+  custom0=0xdef           # override with another value
diff --git a/utils/tests/mkdtimg_testdata.sh b/utils/tests/mkdtimg_testdata.sh
index fab930d..fd0594b 100755
--- a/utils/tests/mkdtimg_testdata.sh
+++ b/utils/tests/mkdtimg_testdata.sh
@@ -6,11 +6,18 @@
   board1v1_1.dts
   board2v1.dts
 "
+DTB_LIST=(
+  "board1v1.dts.dtb"
+  "board1v1_1.dts.dtb"
+  "board2v1.dts.dtb"
+  "board1v1.dts.dtb"
+)
 CONFIG="${SRCDIR}/mkdtimg.cfg"
 
 ALIGN=4
 
 OUTDIR="out"
+OUTDTB_CFG="${OUTDIR}/dump_cfg.dtb"
 OUTDTB="${OUTDIR}/dump.dtb"
 
 mkdir -p "$OUTDIR"
@@ -23,14 +30,21 @@
 
 IMG="${OUTDIR}/cfg_create.img"
 mkdtimg cfg_create "$IMG" "${CONFIG}" --dtb-dir="$OUTDIR"
-mkdtimg dump "$IMG" -b "$OUTDTB" | tee "${OUTDIR}/cfg_create.dump"
+mkdtimg dump "$IMG" -b "$OUTDTB_CFG" | tee "${OUTDIR}/cfg_create.dump"
+for index in ${!DTB_LIST[@]}; do
+  diff ${OUTDIR}/${DTB_LIST[$index]} ${OUTDTB_CFG}.$index
+done
 
 IMG="${OUTDIR}/create.img"
 mkdtimg create "$IMG" \
   --page_size=4096 --id=/:board_id --rev=/:board_rev --custom0=0xabc \
   "${OUTDIR}/board1v1.dts.dtb" \
   "${OUTDIR}/board1v1_1.dts.dtb" --id=/:another_board_id \
-  "${OUTDIR}/board2v1.dts.dtb" --rev=0x201
-mkdtimg dump "$IMG" | tee "${OUTDIR}/create.dump"
+  "${OUTDIR}/board2v1.dts.dtb" --rev=0x201 \
+  "${OUTDIR}/board1v1.dts.dtb" --custom0=0xdef
+mkdtimg dump "$IMG" -b "$OUTDTB" | tee "${OUTDIR}/create.dump"
+for index in ${!DTB_LIST[@]}; do
+  diff ${OUTDIR}/${DTB_LIST[$index]} ${OUTDTB}.$index
+done
 
 diff "${OUTDIR}/cfg_create.dump" "${OUTDIR}/create.dump"