1 /* SPDX-License-Identifier: GPL-2.0-only */
3 #include <commonlib/endian.h>
7 #include "cbfs_sections.h"
8 #include "elfparsing.h"
11 * NOTE: This currently only implements support for MBN version 6 (as used by sc7180). Support
12 * for other MBN versions could probably be added but may require more parsing to tell them
13 * apart, and minor modifications (e.g. different hash algorithm). Add later as needed.
15 static void *qualcomm_find_hash(struct buffer
*in
, size_t bb_offset
, struct vb2_hash
*real_hash
)
18 buffer_clone(&elf
, in
);
20 /* When buffer_size(&elf) becomes this small, we know we've searched through 32KiB (or
21 the whole bootblock) without finding anything, so we know we can stop looking. */
22 size_t search_end_size
= MIN(0, buffer_size(in
) - 32 * KiB
);
24 /* To identify a Qualcomm image, first we find the GPT header... */
25 while (buffer_size(&elf
) > search_end_size
&&
26 !buffer_check_magic(&elf
, "EFI PART", 8))
27 buffer_seek(&elf
, 512);
29 /* ...then shortly afterwards there's an ELF header... */
30 while (buffer_size(&elf
) > search_end_size
&&
31 !buffer_check_magic(&elf
, ELFMAG
, 4))
32 buffer_seek(&elf
, 512);
34 if (buffer_size(&elf
) <= search_end_size
)
35 return NULL
; /* Doesn't seem to be a Qualcomm image. */
37 struct parsed_elf pelf
;
38 if (parse_elf(&elf
, &pelf
, ELF_PARSE_PHDR
))
39 return NULL
; /* Not an ELF -- guess not a Qualcomm MBN after all? */
41 /* Qualcomm stores an array of SHA-384 hashes in a special ELF segment. One special one
42 to start with, and then one for each segment in order. */
44 void *hashtable
= NULL
;
47 for (i
= 0; i
< pelf
.ehdr
.e_phnum
; i
++) {
48 Elf64_Phdr
*ph
= &pelf
.phdr
[i
];
49 if ((ph
->p_flags
& PF_QC_SG_MASK
) == PF_QC_SG_HASH
) {
50 if ((int)ph
->p_filesz
!=
51 (pelf
.ehdr
.e_phnum
+ 1) * VB2_SHA384_DIGEST_SIZE
) {
52 ERROR("fixups: Qualcomm hash segment has wrong size!\n");
54 } /* Found the table with the hashes -- store its address. */
55 hashtable
= buffer_get(&elf
) + ph
->p_offset
;
56 } else if (bb_segment
< 0 && ph
->p_offset
+ ph
->p_filesz
< buffer_size(&elf
) &&
57 buffer_offset(&elf
) + ph
->p_offset
<= bb_offset
&&
58 buffer_offset(&elf
) + ph
->p_offset
+ ph
->p_filesz
> bb_offset
) {
59 bb_segment
= i
; /* Found the bootblock segment -- store its index. */
62 if (!hashtable
) /* ELF but no special QC hash segment -- guess not QC after all? */
64 if (bb_segment
< 0) { /* Can assume it's QC if we found the special segment. */
65 ERROR("fixups: Cannot find bootblock code in Qualcomm MBN!\n");
69 /* Pass out the actual hash of the current bootblock segment in |real_hash|. */
70 if (vb2_hash_calculate(false, buffer_get(&elf
) + pelf
.phdr
[bb_segment
].p_offset
,
71 pelf
.phdr
[bb_segment
].p_filesz
, VB2_HASH_SHA384
, real_hash
)) {
72 ERROR("fixups: vboot digest error\n");
74 } /* Return pointer to where the bootblock hash needs to go in Qualcomm's table. */
75 bb_hash
= hashtable
+ (bb_segment
+ 1) * VB2_SHA384_DIGEST_SIZE
;
78 parsed_elf_destroy(&pelf
);
82 static bool qualcomm_probe(struct buffer
*buffer
, size_t offset
)
84 struct vb2_hash real_hash
;
85 void *table_hash
= qualcomm_find_hash(buffer
, offset
, &real_hash
);
89 if (memcmp(real_hash
.raw
, table_hash
, VB2_SHA384_DIGEST_SIZE
)) {
90 ERROR("fixups: Identified Qualcomm MBN, but existing hash doesn't match!\n");
97 static int qualcomm_fixup(struct buffer
*buffer
, size_t offset
)
99 struct vb2_hash real_hash
;
100 void *table_hash
= qualcomm_find_hash(buffer
, offset
, &real_hash
);
102 ERROR("fixups: Cannot find Qualcomm MBN headers anymore!\n");
106 memcpy(table_hash
, real_hash
.raw
, VB2_SHA384_DIGEST_SIZE
);
107 INFO("fixups: Updated Qualcomm MBN header bootblock hash.\n");
112 * MediaTek bootblock.bin layout (see util/mtkheader/gen-bl-img.py):
114 * gfh info 176 bytes, where bytes 32-35 (in little endian) is the
115 * total size excluding the header (gfh info + data + hash)
116 * data `data_size` bytes
117 * hash 32 bytes, SHA256 of "gfh info + data"
120 #define MEDIATEK_BOOTBLOCK_HEADER_SIZE 2048
121 #define MEDIATEK_BOOTBLOCK_GFH_SIZE 176
122 static void *mediatek_find_hash(struct buffer
*bootblock
, struct vb2_hash
*real_hash
)
124 struct buffer buffer
;
126 const char emmc_magic
[] = "EMMC_BOOT";
127 const char sf_magic
[] = "SF_BOOT";
128 const char brlyt_magic
[] = "BRLYT";
129 const size_t brlyt_offset
= 512;
131 buffer_clone(&buffer
, bootblock
);
133 /* Doesn't seem to be MediaTek image */
134 if (buffer_size(&buffer
) <
135 MEDIATEK_BOOTBLOCK_HEADER_SIZE
+ MEDIATEK_BOOTBLOCK_GFH_SIZE
)
138 /* Check header magic */
139 if (!buffer_check_magic(&buffer
, emmc_magic
, strlen(emmc_magic
)) &&
140 !buffer_check_magic(&buffer
, sf_magic
, strlen(sf_magic
)))
144 buffer_seek(&buffer
, brlyt_offset
);
145 if (!buffer_check_magic(&buffer
, brlyt_magic
, strlen(brlyt_magic
)))
148 buffer_seek(&buffer
, MEDIATEK_BOOTBLOCK_HEADER_SIZE
- brlyt_offset
);
149 data_size
= read_le32(buffer_get(&buffer
) + 32);
150 if (data_size
<= MEDIATEK_BOOTBLOCK_GFH_SIZE
+ VB2_SHA256_DIGEST_SIZE
) {
151 ERROR("fixups: MediaTek: data size too small: %zu\n", data_size
);
154 data_size
-= MEDIATEK_BOOTBLOCK_GFH_SIZE
+ VB2_SHA256_DIGEST_SIZE
;
156 if (buffer_size(&buffer
) <
157 MEDIATEK_BOOTBLOCK_GFH_SIZE
+ data_size
+ VB2_SHA256_DIGEST_SIZE
) {
158 ERROR("fixups: MediaTek: not enough data: %zu\n", buffer_size(&buffer
));
162 if (vb2_hash_calculate(false, buffer_get(&buffer
),
163 MEDIATEK_BOOTBLOCK_GFH_SIZE
+ data_size
,
164 VB2_HASH_SHA256
, real_hash
)) {
165 ERROR("fixups: MediaTek: vboot digest error\n");
169 buffer_seek(&buffer
, MEDIATEK_BOOTBLOCK_GFH_SIZE
+ data_size
);
170 return buffer_get(&buffer
);
173 static bool mediatek_probe(struct buffer
*buffer
)
175 struct vb2_hash real_hash
;
176 void *hash
= mediatek_find_hash(buffer
, &real_hash
);
180 if (memcmp(real_hash
.raw
, hash
, VB2_SHA256_DIGEST_SIZE
)) {
181 ERROR("fixups: Found MediaTek bootblock, but existing hash doesn't match!\n");
188 static int mediatek_fixup(struct buffer
*buffer
, unused
size_t offset
)
190 struct vb2_hash real_hash
;
191 void *hash
= mediatek_find_hash(buffer
, &real_hash
);
193 ERROR("fixups: Cannot find MediaTek header anymore!\n");
197 memcpy(hash
, real_hash
.raw
, VB2_SHA256_DIGEST_SIZE
);
198 INFO("fixups: Updated MediaTek bootblock hash.\n");
202 platform_fixup_func
platform_fixups_probe(struct buffer
*buffer
, size_t offset
,
203 const char *region_name
)
205 if (!strcmp(region_name
, SECTION_NAME_BOOTBLOCK
)) {
206 if (qualcomm_probe(buffer
, offset
))
207 return qualcomm_fixup
;
208 else if (mediatek_probe(buffer
))
209 return mediatek_fixup
;
210 } else if (!strcmp(region_name
, SECTION_NAME_PRIMARY_CBFS
)) {
211 /* TODO: add fixups for primary CBFS bootblock platforms, if needed */
213 ERROR("%s called for unexpected FMAP region %s!\n", __func__
, region_name
);