1 /* SPDX-License-Identifier: GPL-2.0-only */
4 #include "cbfs_sections.h"
5 #include "elfparsing.h"
8 * NOTE: This currently only implements support for MBN version 6 (as used by sc7180). Support
9 * for other MBN versions could probably be added but may require more parsing to tell them
10 * apart, and minor modifications (e.g. different hash algorithm). Add later as needed.
12 static void *qualcomm_find_hash(struct buffer
*in
, size_t bb_offset
, struct vb2_hash
*real_hash
)
15 buffer_clone(&elf
, in
);
17 /* When buffer_size(&elf) becomes this small, we know we've searched through 32KiB (or
18 the whole bootblock) without finding anything, so we know we can stop looking. */
19 size_t search_end_size
= MIN(0, buffer_size(in
) - 32 * KiB
);
21 /* To identify a Qualcomm image, first we find the GPT header... */
22 while (buffer_size(&elf
) > search_end_size
&&
23 !buffer_check_magic(&elf
, "EFI PART", 8))
24 buffer_seek(&elf
, 512);
26 /* ...then shortly afterwards there's an ELF header... */
27 while (buffer_size(&elf
) > search_end_size
&&
28 !buffer_check_magic(&elf
, ELFMAG
, 4))
29 buffer_seek(&elf
, 512);
31 if (buffer_size(&elf
) <= search_end_size
)
32 return NULL
; /* Doesn't seem to be a Qualcomm image. */
34 struct parsed_elf pelf
;
35 if (parse_elf(&elf
, &pelf
, ELF_PARSE_PHDR
))
36 return NULL
; /* Not an ELF -- guess not a Qualcomm MBN after all? */
38 /* Qualcomm stores an array of SHA-384 hashes in a special ELF segment. One special one
39 to start with, and then one for each segment in order. */
41 void *hashtable
= NULL
;
44 for (i
= 0; i
< pelf
.ehdr
.e_phnum
; i
++) {
45 Elf64_Phdr
*ph
= &pelf
.phdr
[i
];
46 if ((ph
->p_flags
& PF_QC_SG_MASK
) == PF_QC_SG_HASH
) {
47 if ((int)ph
->p_filesz
!=
48 (pelf
.ehdr
.e_phnum
+ 1) * VB2_SHA384_DIGEST_SIZE
) {
49 ERROR("fixups: Qualcomm hash segment has wrong size!\n");
51 } /* Found the table with the hashes -- store its address. */
52 hashtable
= buffer_get(&elf
) + ph
->p_offset
;
53 } else if (bb_segment
< 0 && ph
->p_offset
+ ph
->p_filesz
< buffer_size(&elf
) &&
54 buffer_offset(&elf
) + ph
->p_offset
<= bb_offset
&&
55 buffer_offset(&elf
) + ph
->p_offset
+ ph
->p_filesz
> bb_offset
) {
56 bb_segment
= i
; /* Found the bootblock segment -- store its index. */
59 if (!hashtable
) /* ELF but no special QC hash segment -- guess not QC after all? */
61 if (bb_segment
< 0) { /* Can assume it's QC if we found the special segment. */
62 ERROR("fixups: Cannot find bootblock code in Qualcomm MBN!\n");
66 /* Pass out the actual hash of the current bootblock segment in |real_hash|. */
67 if (vb2_hash_calculate(buffer_get(&elf
) + pelf
.phdr
[bb_segment
].p_offset
,
68 pelf
.phdr
[bb_segment
].p_filesz
, VB2_HASH_SHA384
, real_hash
)) {
69 ERROR("fixups: vboot digest error\n");
71 } /* Return pointer to where the bootblock hash needs to go in Qualcomm's table. */
72 bb_hash
= hashtable
+ (bb_segment
+ 1) * VB2_SHA384_DIGEST_SIZE
;
75 parsed_elf_destroy(&pelf
);
79 static bool qualcomm_probe(struct buffer
*buffer
, size_t offset
)
81 struct vb2_hash real_hash
;
82 void *table_hash
= qualcomm_find_hash(buffer
, offset
, &real_hash
);
86 if (memcmp(real_hash
.raw
, table_hash
, VB2_SHA384_DIGEST_SIZE
)) {
87 ERROR("fixups: Identified Qualcomm MBN, but existing hash doesn't match!\n");
94 static int qualcomm_fixup(struct buffer
*buffer
, size_t offset
)
96 struct vb2_hash real_hash
;
97 void *table_hash
= qualcomm_find_hash(buffer
, offset
, &real_hash
);
99 ERROR("fixups: Cannot find Qualcomm MBN headers anymore!\n");
103 memcpy(table_hash
, real_hash
.raw
, VB2_SHA384_DIGEST_SIZE
);
104 INFO("fixups: Updated Qualcomm MBN header bootblock hash.\n");
108 platform_fixup_func
platform_fixups_probe(struct buffer
*buffer
, size_t offset
,
109 const char *region_name
)
111 if (!strcmp(region_name
, SECTION_NAME_BOOTBLOCK
)) {
112 if (qualcomm_probe(buffer
, offset
))
113 return qualcomm_fixup
;
114 } else if (!strcmp(region_name
, SECTION_NAME_PRIMARY_CBFS
)) {
115 /* TODO: add fixups for primary CBFS bootblock platforms, if needed */
117 ERROR("%s called for unexpected FMAP region %s!\n", __func__
, region_name
);