From: Gokul Krishna Krishnakumar <quic_gokukris@xxxxxxxxxxx> When booting with split binaries, it is possible that the mdt loader misdetects if a binary is split and only loads one of the segments, so enhance the detection of the split binaries to ensure the entirety of the firmware is loaded. Signed-off-by: Gokul Krishna Krishnakumar <quic_gokukris@xxxxxxxxxxx> Signed-off-by: Melody Olvera <quic_molvera@xxxxxxxxxxx> --- drivers/soc/qcom/mdt_loader.c | 64 +++++++++++++++++++---------------- 1 file changed, 35 insertions(+), 29 deletions(-) diff --git a/drivers/soc/qcom/mdt_loader.c b/drivers/soc/qcom/mdt_loader.c index 33dd8c315eb7..3aadce299c02 100644 --- a/drivers/soc/qcom/mdt_loader.c +++ b/drivers/soc/qcom/mdt_loader.c @@ -31,6 +31,26 @@ static bool mdt_phdr_valid(const struct elf32_phdr *phdr) return true; } +static bool qcom_mdt_bins_are_split(const struct firmware *fw) +{ + const struct elf32_phdr *phdrs; + const struct elf32_hdr *ehdr; + uint64_t seg_start, seg_end; + int i; + + ehdr = (struct elf32_hdr *)fw->data; + phdrs = (struct elf32_phdr *)(ehdr + 1); + + for (i = 0; i < ehdr->e_phnum; i++) { + seg_start = phdrs[i].p_offset; + seg_end = phdrs[i].p_offset + phdrs[i].p_filesz; + if (seg_start > fw->size || seg_end > fw->size) + return true; + } + + return false; +} + static ssize_t mdt_load_split_segment(void *ptr, const struct elf32_phdr *phdrs, unsigned int segment, const char *fw_name, struct device *dev) @@ -167,23 +187,13 @@ void *qcom_mdt_read_metadata(const struct firmware *fw, size_t *data_len, /* Copy ELF header */ memcpy(data, fw->data, ehdr_size); - if (ehdr_size + hash_size == fw->size) { - /* Firmware is split and hash is packed following the ELF header */ - hash_offset = phdrs[0].p_filesz; - memcpy(data + ehdr_size, fw->data + hash_offset, hash_size); - } else if (phdrs[hash_segment].p_offset + hash_size <= fw->size) { - /* Hash is in its own segment, but within the loaded file */ + + if (qcom_mdt_bins_are_split(fw)) { + ret = mdt_load_split_segment(data + ehdr_size, phdrs, hash_segment, fw_name, dev); + } else { hash_offset = phdrs[hash_segment].p_offset; memcpy(data + ehdr_size, fw->data + hash_offset, hash_size); - } else { - /* Hash is in its own segment, beyond the loaded file */ - ret = mdt_load_split_segment(data + ehdr_size, phdrs, hash_segment, fw_name, dev); - if (ret) { - kfree(data); - return ERR_PTR(ret); - } } - *data_len = ehdr_size + hash_size; return data; @@ -270,6 +280,7 @@ static int __qcom_mdt_load(struct device *dev, const struct firmware *fw, phys_addr_t min_addr = PHYS_ADDR_MAX; ssize_t offset; bool relocate = false; + bool is_split; void *ptr; int ret = 0; int i; @@ -277,6 +288,7 @@ static int __qcom_mdt_load(struct device *dev, const struct firmware *fw, if (!fw || !mem_region || !mem_phys || !mem_size) return -EINVAL; + is_split = qcom_mdt_bins_are_split(fw); ehdr = (struct elf32_hdr *)fw->data; phdrs = (struct elf32_phdr *)(ehdr + 1); @@ -330,22 +342,16 @@ static int __qcom_mdt_load(struct device *dev, const struct firmware *fw, ptr = mem_region + offset; - if (phdr->p_filesz && phdr->p_offset < fw->size && - phdr->p_offset + phdr->p_filesz <= fw->size) { - /* Firmware is large enough to be non-split */ - if (phdr->p_offset + phdr->p_filesz > fw->size) { - dev_err(dev, "file %s segment %d would be truncated\n", - fw_name, i); - ret = -EINVAL; - break; + if (phdr->p_filesz) { + if (!is_split) { + /* Firmware is large enough to be non-split */ + memcpy(ptr, fw->data + phdr->p_offset, phdr->p_filesz); + } else { + /* Firmware not large enough, load split-out segments */ + ret = mdt_load_split_segment(ptr, phdrs, i, fw_name, dev); + if (ret) + break; } - - memcpy(ptr, fw->data + phdr->p_offset, phdr->p_filesz); - } else if (phdr->p_filesz) { - /* Firmware not large enough, load split-out segments */ - ret = mdt_load_split_segment(ptr, phdrs, i, fw_name, dev); - if (ret) - break; } if (phdr->p_memsz > phdr->p_filesz) -- 2.25.1