Create transfer lists with header version 2, and accept lists with
header version 1 or 2.

Version 1.0 of the Firmware Handoff specification is withdrawn, because
it specified an XOR checksum while implementations used an additive one.
Version 2.0 went back to the additive checksum, which U-Boot already
uses, and changed the header version to 2 without changing the layout
of the header or the entries. Since TF-A v2.15, its transfer list
library creates version 2 lists, which U-Boot rejects, so it loses
everything that the earlier boot stage passed in the list.

Lists with version 1 are still accepted, since existing firmware creates
them.

Link: 
https://github.com/FirmwareHandoff/firmware_handoff/commit/dfb62d83c735064291a249d802aabcf48bffb8a0
Link: 
https://github.com/TF-Shared/transfer-list-library/commit/81d7f820853aa117a0458ada3b9892c619233db1
Signed-off-by: David Lechner <[email protected]>
---
 common/bloblist.c      | 10 +++++++++-
 include/bloblist.h     |  8 ++++++--
 test/common/bloblist.c | 21 ++++++++++++++++++++-
 3 files changed, 35 insertions(+), 4 deletions(-)

diff --git a/common/bloblist.c b/common/bloblist.c
index 4ddb4a56944..90b70b06826 100644
--- a/common/bloblist.c
+++ b/common/bloblist.c
@@ -467,7 +467,15 @@ int bloblist_check(ulong addr, uint size)
        hdr = map_sysmem(addr, sizeof(*hdr));
        if (hdr->magic != BLOBLIST_MAGIC)
                return log_msg_ret("Bad magic", -ENOENT);
-       if (hdr->version != BLOBLIST_VERSION)
+       /*
+        * Lists of version 1 and 2 are implemented the same way. Version 1.0
+        * of the Firmware Handoff specification was withdrawn because it
+        * specified an XOR checksum while implementations used the additive
+        * checksum that version 2.0 specifies. Both are accepted for backwards
+        * compatibility.
+        */
+       if (hdr->version != BLOBLIST_VERSION &&
+           hdr->version != BLOBLIST_VERSION_WITHDRAWN)
                return log_msg_ret("Bad version", -EPROTONOSUPPORT);
        if (!hdr->total_size || (size && hdr->total_size > size))
                return log_msg_ret("Bad total size", -EFBIG);
diff --git a/include/bloblist.h b/include/bloblist.h
index fcd290eeb16..f368a6acb85 100644
--- a/include/bloblist.h
+++ b/include/bloblist.h
@@ -76,7 +76,10 @@
 #include <linux/errno.h>
 
 enum {
-       BLOBLIST_VERSION        = 1,
+       /* Version of the lists supported here */
+       BLOBLIST_VERSION        = 2,
+       /* Withdrawn version 1.0 of the specification, still accepted */
+       BLOBLIST_VERSION_WITHDRAWN = 1,
        BLOBLIST_MAGIC          = 0x4a0fb10b,
 
        BLOBLIST_REGCONV_SHIFT_64 = 32,
@@ -179,7 +182,8 @@ enum bloblist_tag_t {
  *     blobs can be altered after being created, this checksum is only valid
  *     when the bloblist is finalized before jumping to the next stage of boot.
  *     This is the value needed to make all checksummed bytes sum to 0
- * @version: BLOBLIST_VERSION
+ * @version: BLOBLIST_VERSION, or BLOBLIST_VERSION_WITHDRAWN when created by
+ *     another boot stage
  * @hdr_size: Size of this header, normally sizeof(struct bloblist_hdr). The
  *     first bloblist_rec starts at this offset from the start of the header
  * @align_log2: Power of two of the maximum alignment required by this list
diff --git a/test/common/bloblist.c b/test/common/bloblist.c
index ef410ca62fc..3ae5eef3859 100644
--- a/test/common/bloblist.c
+++ b/test/common/bloblist.c
@@ -69,7 +69,10 @@ static int bloblist_test_init(struct unit_test_state *uts)
        ut_asserteq_ptr(NULL, bloblist_check_magic(TEST_ADDR));
        ut_assertok(bloblist_new(TEST_ADDR, TEST_BLOBLIST_SIZE, 0, 0));
        ut_asserteq_ptr(hdr, bloblist_check_magic(TEST_ADDR));
-       hdr->version++;
+       hdr->version = BLOBLIST_VERSION + 1;
+       ut_asserteq(-EPROTONOSUPPORT, bloblist_check(TEST_ADDR,
+                                                    TEST_BLOBLIST_SIZE));
+       hdr->version = 0;
        ut_asserteq(-EPROTONOSUPPORT, bloblist_check(TEST_ADDR,
                                                     TEST_BLOBLIST_SIZE));
 
@@ -92,6 +95,22 @@ static int bloblist_test_init(struct unit_test_state *uts)
 }
 BLOBLIST_TEST(bloblist_test_init, UFT_BLOBLIST);
 
+/* Test that a list of the withdrawn version is still accepted */
+static int bloblist_test_version(struct unit_test_state *uts)
+{
+       struct bloblist_hdr *hdr;
+
+       hdr = clear_bloblist();
+       ut_assertok(bloblist_new(TEST_ADDR, TEST_BLOBLIST_SIZE, 0, 0));
+       hdr->version = BLOBLIST_VERSION_WITHDRAWN;
+       ut_assertok(bloblist_finish());
+       ut_assertok(bloblist_check(TEST_ADDR, TEST_BLOBLIST_SIZE));
+       ut_assertnonnull(bloblist_add(TEST_TAG, TEST_SIZE, 0));
+
+       return 0;
+}
+BLOBLIST_TEST(bloblist_test_version, UFT_BLOBLIST);
+
 static int bloblist_test_blob(struct unit_test_state *uts)
 {
        struct bloblist_hdr *hdr;

-- 
2.53.0

Reply via email to