msm: ipa3: Validate IPA and GSI firmwares before loading

IPA and GSI firmwares are saved on the file-system as an
ELF file. IPA driver extracts the firmwares and load
them during driver initialization.
This change adds validation steps to each firmware before
loading: load addresses, memory sizes, firmware sizes and
more...

Change-Id: I7d7f66e8e8a9ca0efae08b1e57b25ae4e44cc5bb
CRs-fixed: 1110522
Signed-off-by: Ghanim Fodi <gfodi@codeaurora.org>
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa.c b/drivers/platform/msm/ipa/ipa_v3/ipa.c
index d5daadc..d68e15d 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa.c
@@ -4015,7 +4015,7 @@
 
 	IPADBG("FWs are available for loading\n");
 
-	result = ipa3_load_fws(fw);
+	result = ipa3_load_fws(fw, ipa3_res.transport_mem_base);
 	if (result) {
 		IPAERR("IPA FWs loading has failed\n");
 		release_firmware(fw);
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
index ce83771..4f6bf55 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
@@ -2031,7 +2031,7 @@
 	unsigned long event, void *ptr);
 void ipa3_inc_acquire_wakelock(void);
 void ipa3_dec_release_wakelock(void);
-int ipa3_load_fws(const struct firmware *firmware);
+int ipa3_load_fws(const struct firmware *firmware, phys_addr_t gsi_mem_base);
 int ipa3_register_ipa_ready_cb(void (*ipa_ready_cb)(void *), void *user_data);
 const char *ipa_hw_error_str(enum ipa3_hw_errors err_type);
 int ipa_gsi_ch20_wa(void);
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
index ad18013f..d478a06 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -3968,75 +3968,164 @@
 	return res;
 }
 
+static int ipa3_load_single_fw(const struct firmware *firmware,
+	const struct elf32_phdr *phdr)
+{
+	uint32_t *fw_mem_base;
+	int index;
+	const uint32_t *elf_data_ptr;
+
+	if (phdr->p_offset > firmware->size) {
+		IPAERR("Invalid ELF: offset=%u is beyond elf_size=%zu\n",
+			phdr->p_offset, firmware->size);
+		return -EINVAL;
+	}
+	if ((firmware->size - phdr->p_offset) < phdr->p_filesz) {
+		IPAERR("Invalid ELF: offset=%u filesz=%u elf_size=%zu\n",
+			phdr->p_offset, phdr->p_filesz, firmware->size);
+		return -EINVAL;
+	}
+
+	if (phdr->p_memsz % sizeof(uint32_t)) {
+		IPAERR("FW mem size %u doesn't align to 32bit\n",
+			phdr->p_memsz);
+		return -EFAULT;
+	}
+
+	if (phdr->p_filesz > phdr->p_memsz) {
+		IPAERR("FW image too big src_size=%u dst_size=%u\n",
+			phdr->p_filesz, phdr->p_memsz);
+		return -EFAULT;
+	}
+
+	fw_mem_base = ioremap(phdr->p_vaddr, phdr->p_memsz);
+	if (!fw_mem_base) {
+		IPAERR("Failed to map 0x%x for the size of %u\n",
+			phdr->p_vaddr, phdr->p_memsz);
+		return -ENOMEM;
+	}
+
+	/* Set the entire region to 0s */
+	memset(fw_mem_base, 0, phdr->p_memsz);
+
+	elf_data_ptr = (uint32_t *)(firmware->data + phdr->p_offset);
+
+	/* Write the FW */
+	for (index = 0; index < phdr->p_filesz/sizeof(uint32_t); index++) {
+		writel_relaxed(*elf_data_ptr, &fw_mem_base[index]);
+		elf_data_ptr++;
+	}
+
+	iounmap(fw_mem_base);
+
+	return 0;
+}
+
 /**
  * ipa3_load_fws() - Load the IPAv3 FWs into IPA&GSI SRAM.
  *
  * @firmware: Structure which contains the FW data from the user space.
+ * @gsi_mem_base: GSI base address
  *
  * Return value: 0 on success, negative otherwise
  *
  */
-int ipa3_load_fws(const struct firmware *firmware)
+int ipa3_load_fws(const struct firmware *firmware, phys_addr_t gsi_mem_base)
 {
 	const struct elf32_hdr *ehdr;
 	const struct elf32_phdr *phdr;
-	const uint8_t *elf_phdr_ptr;
-	uint32_t *elf_data_ptr;
-	int phdr_idx, index;
-	uint32_t *fw_mem_base;
+	unsigned long gsi_iram_ofst;
+	unsigned long gsi_iram_size;
+	phys_addr_t ipa_reg_mem_base;
+	u32 ipa_reg_ofst;
+	int rc;
+
+	if (!gsi_mem_base) {
+		IPAERR("Invalid GSI base address\n");
+		return -EINVAL;
+	}
+
+	ipa_assert_on(!firmware);
+	/* One program header per FW image: GSI, DPS and HPS */
+	if (firmware->size < (sizeof(*ehdr) + 3 * sizeof(*phdr))) {
+		IPAERR("Missing ELF and Program headers firmware size=%zu\n",
+			firmware->size);
+		return -EINVAL;
+	}
 
 	ehdr = (struct elf32_hdr *) firmware->data;
-
-	elf_phdr_ptr = firmware->data + sizeof(*ehdr);
-
-	for (phdr_idx = 0; phdr_idx < ehdr->e_phnum; phdr_idx++) {
-		/*
-		 * The ELF program header will contain the starting
-		 * address to which the firmware needs to copied.
-		 */
-		phdr = (struct elf32_phdr *)elf_phdr_ptr;
-
-		/*
-		 * p_vaddr will contain the starting address to which the
-		 * FW needs to be loaded.
-		 * p_memsz will contain the size of the IRAM.
-		 * p_filesz will contain the size of the FW image.
-		 */
-		fw_mem_base = ioremap(phdr->p_vaddr, phdr->p_memsz);
-		if (!fw_mem_base) {
-			IPAERR("Failed to map 0x%x for the size of %u\n",
-				phdr->p_vaddr, phdr->p_memsz);
-				return -ENOMEM;
-		}
-
-		/* Set the entire region to 0s */
-		memset(fw_mem_base, 0, phdr->p_memsz);
-
-		/*
-		 * p_offset will contain and absolute offset from the beginning
-		 * of the ELF file.
-		 */
-		elf_data_ptr = (uint32_t *)
-				((uint8_t *)firmware->data + phdr->p_offset);
-
-		if (phdr->p_memsz % sizeof(uint32_t)) {
-			IPAERR("FW size %u doesn't align to 32bit\n",
-				phdr->p_memsz);
-			return -EFAULT;
-		}
-
-		/* Write the FW */
-		for (index = 0; index < phdr->p_filesz/sizeof(uint32_t);
-			index++) {
-			writel_relaxed(*elf_data_ptr, &fw_mem_base[index]);
-			elf_data_ptr++;
-		}
-
-		iounmap(fw_mem_base);
-
-		elf_phdr_ptr = elf_phdr_ptr + sizeof(*phdr);
+	ipa_assert_on(!ehdr);
+	if (ehdr->e_phnum != 3) {
+		IPAERR("Unexpected number of ELF program headers\n");
+		return -EINVAL;
 	}
-	IPADBG("IPA FWs (GSI FW, HPS and DPS) were loaded\n");
+	phdr = (struct elf32_phdr *)(firmware->data + sizeof(*ehdr));
+
+	/*
+	 * Each ELF program header represents a FW image and contains:
+	 *  p_vaddr : The starting address to which the FW needs to loaded.
+	 *  p_memsz : The size of the IRAM (where the image loaded)
+	 *  p_filesz: The size of the FW image embedded inside the ELF
+	 *  p_offset: Absolute offset to the image from the head of the ELF
+	 */
+
+	/* Load GSI FW image */
+	gsi_get_inst_ram_offset_and_size(&gsi_iram_ofst, &gsi_iram_size);
+	if (phdr->p_vaddr != (gsi_mem_base + gsi_iram_ofst)) {
+		IPAERR(
+			"Invalid GSI FW img load addr vaddr=0x%x gsi_mem_base=%pa gsi_iram_ofst=0x%lx\n"
+			, phdr->p_vaddr, &gsi_mem_base, gsi_iram_ofst);
+		return -EINVAL;
+	}
+	if (phdr->p_memsz > gsi_iram_size) {
+		IPAERR("Invalid GSI FW img size memsz=%d gsi_iram_size=%lu\n",
+			phdr->p_memsz, gsi_iram_size);
+		return -EINVAL;
+	}
+	rc = ipa3_load_single_fw(firmware, phdr);
+	if (rc)
+		return rc;
+
+	phdr++;
+	ipa_reg_mem_base = ipa3_ctx->ipa_wrapper_base + ipahal_get_reg_base();
+
+	/* Load IPA DPS FW image */
+	ipa_reg_ofst = ipahal_get_reg_ofst(IPA_DPS_SEQUENCER_FIRST);
+	if (phdr->p_vaddr != (ipa_reg_mem_base + ipa_reg_ofst)) {
+		IPAERR(
+			"Invalid IPA DPS img load addr vaddr=0x%x ipa_reg_mem_base=%pa ipa_reg_ofst=%u\n"
+			, phdr->p_vaddr, &ipa_reg_mem_base, ipa_reg_ofst);
+		return -EINVAL;
+	}
+	if (phdr->p_memsz > ipahal_get_dps_img_mem_size()) {
+		IPAERR("Invalid IPA DPS img size memsz=%d dps_mem_size=%u\n",
+			phdr->p_memsz, ipahal_get_dps_img_mem_size());
+		return -EINVAL;
+	}
+	rc = ipa3_load_single_fw(firmware, phdr);
+	if (rc)
+		return rc;
+
+	phdr++;
+
+	/* Load IPA HPS FW image */
+	ipa_reg_ofst = ipahal_get_reg_ofst(IPA_HPS_SEQUENCER_FIRST);
+	if (phdr->p_vaddr != (ipa_reg_mem_base + ipa_reg_ofst)) {
+		IPAERR(
+			"Invalid IPA HPS img load addr vaddr=0x%x ipa_reg_mem_base=%pa ipa_reg_ofst=%u\n"
+			, phdr->p_vaddr, &ipa_reg_mem_base, ipa_reg_ofst);
+		return -EINVAL;
+	}
+	if (phdr->p_memsz > ipahal_get_hps_img_mem_size()) {
+		IPAERR("Invalid IPA HPS img size memsz=%d dps_mem_size=%u\n",
+			phdr->p_memsz, ipahal_get_hps_img_mem_size());
+		return -EINVAL;
+	}
+	rc = ipa3_load_single_fw(firmware, phdr);
+	if (rc)
+		return rc;
+
+	IPADBG("IPA FWs (GSI FW, DPS and HPS) loaded successfully\n");
 	return 0;
 }
 
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.c b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.c
index e39874e..fa9c6c8 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -1257,6 +1257,21 @@
 	return res;
 }
 
+/*
+ * Get IPA Data Processing Star image memory size at IPA SRAM
+ */
+u32 ipahal_get_dps_img_mem_size(void)
+{
+	return IPA_HW_DPS_IMG_MEM_SIZE_V3_0;
+}
+
+/*
+ * Get IPA Header Processing Star image memory size at IPA SRAM
+ */
+u32 ipahal_get_hps_img_mem_size(void)
+{
+	return IPA_HW_HPS_IMG_MEM_SIZE_V3_0;
+}
 
 int ipahal_init(enum ipa_hw_type ipa_hw_type, void __iomem *base,
 	struct device *ipa_pdev)
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.h b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.h
index 6549775..154045f 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.h
+++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -634,6 +634,16 @@
  */
 int ipahal_get_proc_ctx_needed_len(enum ipa_hdr_proc_type type);
 
+/*
+ * Get IPA Data Processing Star image memory size at IPA SRAM
+ */
+u32 ipahal_get_dps_img_mem_size(void);
+
+/*
+ * Get IPA Header Processing Star image memory size at IPA SRAM
+ */
+u32 ipahal_get_hps_img_mem_size(void);
+
 int ipahal_init(enum ipa_hw_type ipa_hw_type, void __iomem *base,
 	struct device *ipa_pdev);
 void ipahal_destroy(void);
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_i.h b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_i.h
index 4c4b666..d6a496e 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_i.h
+++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_i.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -546,4 +546,8 @@
 	struct ipa_hw_hdr_proc_ctx_tlv end;
 };
 
+/* IPA HW DPS/HPS image memory sizes */
+#define IPA_HW_DPS_IMG_MEM_SIZE_V3_0 128
+#define IPA_HW_HPS_IMG_MEM_SIZE_V3_0 320
+
 #endif /* _IPAHAL_I_H_ */
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.c b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.c
index 430f58f..30fd737 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -1137,6 +1137,12 @@
 	[IPA_HW_v3_0][IPA_QSB_MAX_READS] = {
 		ipareg_construct_qsb_max_reads, ipareg_parse_dummy,
 		0x00000078, 0},
+	[IPA_HW_v3_0][IPA_DPS_SEQUENCER_FIRST] = {
+		ipareg_construct_dummy, ipareg_parse_dummy,
+		0x0001e000, 0},
+	[IPA_HW_v3_0][IPA_HPS_SEQUENCER_FIRST] = {
+		ipareg_construct_dummy, ipareg_parse_dummy,
+		0x0001e080, 0},
 
 
 	/* IPAv3.1 */
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.h b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.h
index 3334df6..8b6ed39 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.h
+++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -84,6 +84,8 @@
 	IPA_QSB_MAX_READS,
 	IPA_TX_CFG,
 	IPA_IDLE_INDICATION_CFG,
+	IPA_DPS_SEQUENCER_FIRST,
+	IPA_HPS_SEQUENCER_FIRST,
 	IPA_REG_MAX,
 };