summaryrefslogtreecommitdiff
path: root/util/cbfstool
diff options
context:
space:
mode:
Diffstat (limited to 'util/cbfstool')
-rw-r--r--util/cbfstool/Makefile.inc1
-rw-r--r--util/cbfstool/cbfs.h5
-rw-r--r--util/cbfstool/cbfstool.c5
-rw-r--r--util/cbfstool/elf.h2
-rw-r--r--util/cbfstool/platform_fixups.c121
5 files changed, 134 insertions, 0 deletions
diff --git a/util/cbfstool/Makefile.inc b/util/cbfstool/Makefile.inc
index 7a13a793a3..5b49fe80ad 100644
--- a/util/cbfstool/Makefile.inc
+++ b/util/cbfstool/Makefile.inc
@@ -22,6 +22,7 @@ cbfsobj += elfheaders.o
cbfsobj += rmodule.o
cbfsobj += xdr.o
cbfsobj += partitioned_file.o
+cbfsobj += platform_fixups.o
# COMMONLIB
cbfsobj += cbfs_private.o
cbfsobj += fsp_relocate.o
diff --git a/util/cbfstool/cbfs.h b/util/cbfstool/cbfs.h
index 7f07751a8f..67dc6163f0 100644
--- a/util/cbfstool/cbfs.h
+++ b/util/cbfstool/cbfs.h
@@ -72,4 +72,9 @@ void xdr_segs(struct buffer *output,
void xdr_get_seg(struct cbfs_payload_segment *out,
struct cbfs_payload_segment *in);
+/* platform_fixups.c */
+typedef int (*platform_fixup_func)(struct buffer *buffer, size_t offset);
+platform_fixup_func platform_fixups_probe(struct buffer *buffer, size_t offset,
+ const char *region_name);
+
#endif
diff --git a/util/cbfstool/cbfstool.c b/util/cbfstool/cbfstool.c
index c55838b6a6..16654d96a0 100644
--- a/util/cbfstool/cbfstool.c
+++ b/util/cbfstool/cbfstool.c
@@ -129,6 +129,7 @@ struct mh_cache {
const char *region;
size_t offset;
struct vb2_hash cbfs_hash;
+ platform_fixup_func fixup;
bool initialized;
};
@@ -185,6 +186,8 @@ static struct mh_cache *get_mh_cache(void)
}
mhc.cbfs_hash = anchor->cbfs_hash;
mhc.offset = (void *)anchor - buffer_get(&buffer);
+ mhc.fixup = platform_fixups_probe(&buffer, mhc.offset,
+ mhc.region);
return &mhc;
}
@@ -223,6 +226,8 @@ static int update_anchor(struct mh_cache *mhc, uint8_t *fmap_hash)
metadata_hash_anchor_fmap_hash(anchor), fmap_hash,
vb2_digest_size(anchor->cbfs_hash.algo));
}
+ if (mhc->fixup && mhc->fixup(&buffer, mhc->offset) != 0)
+ return -1;
if (!partitioned_file_write_region(param.image_file, &buffer))
return -1;
return 0;
diff --git a/util/cbfstool/elf.h b/util/cbfstool/elf.h
index 2549e0431e..e2d0421929 100644
--- a/util/cbfstool/elf.h
+++ b/util/cbfstool/elf.h
@@ -561,6 +561,8 @@ typedef struct
#define PF_W (1 << 1) /* Segment is writable */
#define PF_R (1 << 2) /* Segment is readable */
#define PF_MASKOS 0x0ff00000 /* OS-specific */
+#define PF_QC_SG_MASK 0x07000000 /* Qualcomm "segment" types */
+#define PF_QC_SG_HASH 0x02000000 /* Qualcomm hash segment */
#define PF_MASKPROC 0xf0000000 /* Processor-specific */
/* Legal values for note segment descriptor types for core files. */
diff --git a/util/cbfstool/platform_fixups.c b/util/cbfstool/platform_fixups.c
new file mode 100644
index 0000000000..ea2d3161a2
--- /dev/null
+++ b/util/cbfstool/platform_fixups.c
@@ -0,0 +1,121 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include "cbfs.h"
+#include "cbfs_sections.h"
+#include "elfparsing.h"
+
+/*
+ * NOTE: This currently only implements support for MBN version 6 (as used by sc7180). Support
+ * for other MBN versions could probably be added but may require more parsing to tell them
+ * apart, and minor modifications (e.g. different hash algorithm). Add later as needed.
+ */
+static void *qualcomm_find_hash(struct buffer *in, size_t bb_offset, struct vb2_hash *real_hash)
+{
+ struct buffer elf;
+ buffer_clone(&elf, in);
+
+ /* When buffer_size(&elf) becomes this small, we know we've searched through 32KiB (or
+ the whole bootblock) without finding anything, so we know we can stop looking. */
+ size_t search_end_size = MIN(0, buffer_size(in) - 32 * KiB);
+
+ /* To identify a Qualcomm image, first we find the GPT header... */
+ while (buffer_size(&elf) > search_end_size &&
+ !buffer_check_magic(&elf, "EFI PART", 8))
+ buffer_seek(&elf, 512);
+
+ /* ...then shortly afterwards there's an ELF header... */
+ while (buffer_size(&elf) > search_end_size &&
+ !buffer_check_magic(&elf, ELFMAG, 4))
+ buffer_seek(&elf, 512);
+
+ if (buffer_size(&elf) <= search_end_size)
+ return NULL; /* Doesn't seem to be a Qualcomm image. */
+
+ struct parsed_elf pelf;
+ if (parse_elf(&elf, &pelf, ELF_PARSE_PHDR))
+ return NULL; /* Not an ELF -- guess not a Qualcomm MBN after all? */
+
+ /* Qualcomm stores an array of SHA-384 hashes in a special ELF segment. One special one
+ to start with, and then one for each segment in order. */
+ void *bb_hash = NULL;
+ void *hashtable = NULL;
+ int i;
+ int bb_segment = -1;
+ for (i = 0; i < pelf.ehdr.e_phnum; i++) {
+ Elf64_Phdr *ph = &pelf.phdr[i];
+ if ((ph->p_flags & PF_QC_SG_MASK) == PF_QC_SG_HASH) {
+ if ((int)ph->p_filesz !=
+ (pelf.ehdr.e_phnum + 1) * VB2_SHA384_DIGEST_SIZE) {
+ ERROR("fixups: Qualcomm hash segment has wrong size!\n");
+ goto destroy_elf;
+ } /* Found the table with the hashes -- store its address. */
+ hashtable = buffer_get(&elf) + ph->p_offset;
+ } else if (bb_segment < 0 && ph->p_offset + ph->p_filesz < buffer_size(&elf) &&
+ buffer_offset(&elf) + ph->p_offset <= bb_offset &&
+ buffer_offset(&elf) + ph->p_offset + ph->p_filesz > bb_offset) {
+ bb_segment = i; /* Found the bootblock segment -- store its index. */
+ }
+ }
+ if (!hashtable) /* ELF but no special QC hash segment -- guess not QC after all? */
+ goto destroy_elf;
+ if (bb_segment < 0) { /* Can assume it's QC if we found the special segment. */
+ ERROR("fixups: Cannot find bootblock code in Qualcomm MBN!\n");
+ goto destroy_elf;
+ }
+
+ /* Pass out the actual hash of the current bootblock segment in |real_hash|. */
+ if (vb2_hash_calculate(buffer_get(&elf) + pelf.phdr[bb_segment].p_offset,
+ pelf.phdr[bb_segment].p_filesz, VB2_HASH_SHA384, real_hash)) {
+ ERROR("fixups: vboot digest error\n");
+ goto destroy_elf;
+ } /* Return pointer to where the bootblock hash needs to go in Qualcomm's table. */
+ bb_hash = hashtable + (bb_segment + 1) * VB2_SHA384_DIGEST_SIZE;
+
+destroy_elf:
+ parsed_elf_destroy(&pelf);
+ return bb_hash;
+}
+
+static bool qualcomm_probe(struct buffer *buffer, size_t offset)
+{
+ struct vb2_hash real_hash;
+ void *table_hash = qualcomm_find_hash(buffer, offset, &real_hash);
+ if (!table_hash)
+ return false;
+
+ if (memcmp(real_hash.raw, table_hash, VB2_SHA384_DIGEST_SIZE)) {
+ ERROR("fixups: Identified Qualcomm MBN, but existing hash doesn't match!\n");
+ return false;
+ }
+
+ return true;
+}
+
+static int qualcomm_fixup(struct buffer *buffer, size_t offset)
+{
+ struct vb2_hash real_hash;
+ void *table_hash = qualcomm_find_hash(buffer, offset, &real_hash);
+ if (!table_hash) {
+ ERROR("fixups: Cannot find Qualcomm MBN headers anymore!\n");
+ return -1;
+ }
+
+ memcpy(table_hash, real_hash.raw, VB2_SHA384_DIGEST_SIZE);
+ INFO("fixups: Updated Qualcomm MBN header bootblock hash.\n");
+ return 0;
+}
+
+platform_fixup_func platform_fixups_probe(struct buffer *buffer, size_t offset,
+ const char *region_name)
+{
+ if (!strcmp(region_name, SECTION_NAME_BOOTBLOCK)) {
+ if (qualcomm_probe(buffer, offset))
+ return qualcomm_fixup;
+ } else if (!strcmp(region_name, SECTION_NAME_PRIMARY_CBFS)) {
+ /* TODO: add fixups for primary CBFS bootblock platforms, if needed */
+ } else {
+ ERROR("%s called for unexpected FMAP region %s!\n", __func__, region_name);
+ }
+
+ return NULL;
+}