cbfstool: Add multi ELF support
The current QCOM fixup function qualcomm_find_hash() assumes only one
ELF will be loaded by the primary boot loader and the bootblock is one
of the segments of that ELF.
However, the primary boot loader for QCOM X1P42100 SoC loads multiple
ELFs for QC_SEC, TME Sequencer, TME FW and bootblock. This change
updates the fixup functionality to handle bootblock being a separate
ELF.
If the bootblock offset does not fit within the first ELF, then the
fixup function understands that it is a multi ELF.
Additionally, it ensures the bootblock ELF uses MBN v7 format.
BUG=b:420542130
TEST=1. Create a image.serial.bin and ensure it boots on X1P42100
2. Used the following script to verify the hash
#! /bin/bash
image=/build/bluey/firmware/image-bluey.serial.bin
bin=/tmp/bb.bin
seg=/tmp/bb.seg
hash=/tmp/bb.hash
#
# Bootblock is the final ELF of the multi ELF. Hopefully
# there is no other ELF in ${image}. Get the offset of the
# final ELF in ${image} and get it out
#
bb_offset=`od -Ad -w4 -tx4 ${image} | grep 464c457f | tail -1 | cut -f1 -d ' '`
dd if=${image} of=${bin} skip=1 bs=${bb_offset} &> /dev/null
#
# The last two segments of the bootblock ELF have the actual
# executable and the hash. 'LOAD' is the executable segment and
# the other is the hash segment. Get their offsets and convert
# to decimal.
#
offs=`readelf -lW ${bin} | tail -2 | awk '{print $1" "$2" "$5}'`
offs=(`printf "%s %u %u %s %u %u" ${offs}`)
#
# Get the executable and hash segments
#
if [ ${offs[0]} = "LOAD" ]; then
first=${seg}
second=${hash}
else
first=${hash}
second=${seg}
fi
dd if=${bin} skip=${offs[1]} bs=1 count=${offs[2]} of=${first} &> /dev/null
dd if=${bin} skip=${offs[4]} bs=1 count=${offs[5]} of=${second} &> /dev/null
#
# Find the SHA384 hash for the executable segment
#
sha=`sha384sum ${seg} | cut -f1 -d ' ' | sed 's/../& /g'`
echo ===================================================
echo Expected hash:
echo
echo ${sha} | fold -w48
echo ===================================================
echo
hexdump -C ${hash} | grep -A4 "`echo ${sha} | cut -f1-4 -d ' '`"
Change-Id: If57ba0cc9a4f08b69d7712f27c215339307e73d4
Signed-off-by: Varadarajan Narayanan <vnarayan@qualcomm.corp-partner.google.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/88148
Reviewed-by: Kapil Porwal <kapilporwal@google.com>
Reviewed-by: Subrata Banik <subratabanik@google.com>
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
This commit is contained in:
parent
6a02f2d4a7
commit
0dcea61e7c
1 changed files with 111 additions and 1 deletions
|
|
@ -7,6 +7,105 @@
|
||||||
#include "cbfs_sections.h"
|
#include "cbfs_sections.h"
|
||||||
#include "elfparsing.h"
|
#include "elfparsing.h"
|
||||||
|
|
||||||
|
#define MBN_V7 7
|
||||||
|
|
||||||
|
struct mbnv7_hashtbl_seg {
|
||||||
|
uint32_t reserved;
|
||||||
|
uint32_t version;
|
||||||
|
uint32_t common_metadata_size;
|
||||||
|
uint32_t qti_metadata_size;
|
||||||
|
uint32_t oem_metadata_size;
|
||||||
|
uint32_t hash_table_size;
|
||||||
|
uint32_t qti_signature_size;
|
||||||
|
uint32_t qti_certificate_chain_size;
|
||||||
|
uint32_t oem_signature_size;
|
||||||
|
uint32_t oem_certificate_chain_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum hash_table_algo {
|
||||||
|
QUALCOMM_HASH_TABLE_ALGO_SHA384 = 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct common_metadata {
|
||||||
|
uint32_t major;
|
||||||
|
uint32_t minor;
|
||||||
|
uint32_t software_id;
|
||||||
|
uint32_t secondary_software_id;
|
||||||
|
uint32_t hash_table_algorithm;
|
||||||
|
uint32_t measurement_register_target;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void *qualcomm_find_hash_mbnv7(struct buffer *elf, size_t bb_offset,
|
||||||
|
struct vb2_hash *real_hash)
|
||||||
|
{
|
||||||
|
void *hashtable = NULL, *bb_hash = NULL;
|
||||||
|
struct common_metadata *cmn = NULL;
|
||||||
|
struct mbnv7_hashtbl_seg *mbn;
|
||||||
|
struct parsed_elf pelf;
|
||||||
|
int bb_segment = -1;
|
||||||
|
Elf64_Phdr *ph;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
size_t tmp;
|
||||||
|
|
||||||
|
if (!buffer_check_magic(elf, ELFMAG, 4) ||
|
||||||
|
parse_elf(elf, &pelf, ELF_PARSE_PHDR))
|
||||||
|
return NULL; /* Not an ELF -- guess not a Qualcomm MBN after all? */
|
||||||
|
|
||||||
|
ph = &pelf.phdr[pelf.ehdr.e_phnum - 1];
|
||||||
|
|
||||||
|
tmp = ph->p_offset + ph->p_filesz;
|
||||||
|
|
||||||
|
if (buffer_offset(elf) < bb_offset &&
|
||||||
|
bb_offset < (buffer_offset(elf) + tmp)) /* found BB */
|
||||||
|
break;
|
||||||
|
|
||||||
|
parsed_elf_destroy(&pelf);
|
||||||
|
|
||||||
|
buffer_seek(elf, tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Qualcomm stores an array of SHA-384 hashes in a special ELF segment,
|
||||||
|
one for each segment in order. */
|
||||||
|
for (int i = 0; i < pelf.ehdr.e_phnum; i++) {
|
||||||
|
ph = &pelf.phdr[i];
|
||||||
|
mbn = buffer_get(elf) + ph->p_offset;
|
||||||
|
|
||||||
|
if (((ph->p_flags & PF_QC_SG_MASK) == PF_QC_SG_HASH) &&
|
||||||
|
(mbn->version == MBN_V7)) {
|
||||||
|
cmn = buffer_get(elf) + ph->p_offset +
|
||||||
|
sizeof(*mbn);
|
||||||
|
|
||||||
|
hashtable = buffer_get(elf) + ph->p_offset +
|
||||||
|
sizeof(*mbn) + mbn->common_metadata_size +
|
||||||
|
mbn->qti_metadata_size +
|
||||||
|
mbn->oem_metadata_size;
|
||||||
|
} else if (ph->p_type == PT_LOAD && ph->p_flags == (PF_X | PF_W | PF_R)) {
|
||||||
|
bb_segment = i; /* Found the bootblock segment -- store its index. */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hashtable || !cmn)
|
||||||
|
goto destroy_elf;
|
||||||
|
|
||||||
|
/* MBNv7 supports only SHA384, ensure that. */
|
||||||
|
if (cmn->hash_table_algorithm != QUALCOMM_HASH_TABLE_ALGO_SHA384)
|
||||||
|
goto destroy_elf;
|
||||||
|
|
||||||
|
bb_hash = hashtable + bb_segment * VB2_SHA384_DIGEST_SIZE;
|
||||||
|
|
||||||
|
/* Pass out the actual hash of the current bootblock segment in |real_hash|. */
|
||||||
|
if (vb2_hash_calculate(false, 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. */
|
||||||
|
|
||||||
|
destroy_elf:
|
||||||
|
parsed_elf_destroy(&pelf);
|
||||||
|
return bb_hash;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* NOTE: This currently only implements support for MBN version 6 (as used by sc7180). Support
|
* 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
|
* for other MBN versions could probably be added but may require more parsing to tell them
|
||||||
|
|
@ -14,6 +113,7 @@
|
||||||
*/
|
*/
|
||||||
static void *qualcomm_find_hash(struct buffer *in, size_t bb_offset, struct vb2_hash *real_hash)
|
static void *qualcomm_find_hash(struct buffer *in, size_t bb_offset, struct vb2_hash *real_hash)
|
||||||
{
|
{
|
||||||
|
Elf64_Phdr *ph;
|
||||||
struct buffer elf;
|
struct buffer elf;
|
||||||
buffer_clone(&elf, in);
|
buffer_clone(&elf, in);
|
||||||
|
|
||||||
|
|
@ -38,6 +138,16 @@ static void *qualcomm_find_hash(struct buffer *in, size_t bb_offset, struct vb2_
|
||||||
if (parse_elf(&elf, &pelf, ELF_PARSE_PHDR))
|
if (parse_elf(&elf, &pelf, ELF_PARSE_PHDR))
|
||||||
return NULL; /* Not an ELF -- guess not a Qualcomm MBN after all? */
|
return NULL; /* Not an ELF -- guess not a Qualcomm MBN after all? */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check if it is a multi ELF or single ELF. If bb_offset is beyond the
|
||||||
|
* final segment of first ELF, it is a multi ELF.
|
||||||
|
*/
|
||||||
|
ph = &pelf.phdr[pelf.ehdr.e_phnum - 1];
|
||||||
|
if (bb_offset > (ph->p_offset + ph->p_filesz)) { /* MBNv7 multi ELF */
|
||||||
|
parsed_elf_destroy(&pelf);
|
||||||
|
return qualcomm_find_hash_mbnv7(&elf, bb_offset, real_hash);
|
||||||
|
}
|
||||||
|
|
||||||
/* Qualcomm stores an array of SHA-384 hashes in a special ELF segment. One special one
|
/* 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. */
|
to start with, and then one for each segment in order. */
|
||||||
void *bb_hash = NULL;
|
void *bb_hash = NULL;
|
||||||
|
|
@ -45,7 +155,7 @@ static void *qualcomm_find_hash(struct buffer *in, size_t bb_offset, struct vb2_
|
||||||
int i;
|
int i;
|
||||||
int bb_segment = -1;
|
int bb_segment = -1;
|
||||||
for (i = 0; i < pelf.ehdr.e_phnum; i++) {
|
for (i = 0; i < pelf.ehdr.e_phnum; i++) {
|
||||||
Elf64_Phdr *ph = &pelf.phdr[i];
|
ph = &pelf.phdr[i];
|
||||||
if ((ph->p_flags & PF_QC_SG_MASK) == PF_QC_SG_HASH) {
|
if ((ph->p_flags & PF_QC_SG_MASK) == PF_QC_SG_HASH) {
|
||||||
if ((int)ph->p_filesz !=
|
if ((int)ph->p_filesz !=
|
||||||
(pelf.ehdr.e_phnum + 1) * VB2_SHA384_DIGEST_SIZE) {
|
(pelf.ehdr.e_phnum + 1) * VB2_SHA384_DIGEST_SIZE) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue