diff mbox series

[6/9] soc: qcom: mdt_loader: Enhance split binary detection

Message ID 20230213185218.166520-7-quic_molvera@quicinc.com
State Superseded
Headers show
Series [1/9] dt-bindings: firmware: qcom,scm: Update QDU1000/QRU1000 compatible | expand

Commit Message

Melody Olvera Feb. 13, 2023, 6:52 p.m. UTC
From: Gokul Krishna Krishnakumar <quic_gokukris@quicinc.com>

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@quicinc.com>
Signed-off-by: Melody Olvera <quic_molvera@quicinc.com>
---
 drivers/soc/qcom/mdt_loader.c | 64 +++++++++++++++++++----------------
 1 file changed, 35 insertions(+), 29 deletions(-)
diff mbox series

Patch

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)