Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 37b6495c authored by Ghanim Fodi's avatar Ghanim Fodi
Browse files

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: default avatarGhanim Fodi <gfodi@codeaurora.org>
parent 53c1534d
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -2727,6 +2727,16 @@ int gsi_enable_fw(phys_addr_t gsi_base_addr, u32 gsi_size)
}
EXPORT_SYMBOL(gsi_enable_fw);

void gsi_get_inst_ram_offset_and_size(unsigned long *base_offset,
		unsigned long *size)
{
	if (base_offset)
		*base_offset = GSI_GSI_INST_RAM_BASE_OFFS;
	if (size)
		*size = GSI_GSI_INST_RAM_SIZE;
}
EXPORT_SYMBOL(gsi_get_inst_ram_offset_and_size);

static int msm_gsi_probe(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
+3 −1
Original line number Diff line number Diff line
/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
/* Copyright (c) 2015-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
@@ -1838,5 +1838,7 @@
#define GSI_INTER_EE_n_SRC_EV_CH_IRQ_CLR_EV_CH_BIT_MAP_BMSK 0xffffffff
#define GSI_INTER_EE_n_SRC_EV_CH_IRQ_CLR_EV_CH_BIT_MAP_SHFT 0x0

#define GSI_GSI_INST_RAM_BASE_OFFS	0x4000
#define GSI_GSI_INST_RAM_SIZE		0x4000

#endif /* __GSI_REG_H__ */
+1 −1
Original line number Diff line number Diff line
@@ -4015,7 +4015,7 @@ static int ipa3_trigger_fw_loading_mdms(void)

	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);
+1 −1
Original line number Diff line number Diff line
@@ -2031,7 +2031,7 @@ int ipa3_uc_panic_notifier(struct notifier_block *this,
	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);
+139 −50
Original line number Diff line number Diff line
/* 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,40 +3968,36 @@ int ipa3_stop_gsi_channel(u32 clnt_hdl)
	return res;
}

/**
 * ipa3_load_fws() - Load the IPAv3 FWs into IPA&GSI SRAM.
 *
 * @firmware: Structure which contains the FW data from the user space.
 *
 * Return value: 0 on success, negative otherwise
 *
 */
int ipa3_load_fws(const struct firmware *firmware)
static int ipa3_load_single_fw(const struct firmware *firmware,
	const struct elf32_phdr *phdr)
{
	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;
	int index;
	const uint32_t *elf_data_ptr;

	ehdr = (struct elf32_hdr *) firmware->data;
	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;
	}

	elf_phdr_ptr = firmware->data + sizeof(*ehdr);
	if (phdr->p_memsz % sizeof(uint32_t)) {
		IPAERR("FW mem size %u doesn't align to 32bit\n",
			phdr->p_memsz);
		return -EFAULT;
	}

	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;
	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;
	}

		/*
		 * 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",
@@ -4012,31 +4008,124 @@ int ipa3_load_fws(const struct firmware *firmware)
	/* 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;
		}
	elf_data_ptr = (uint32_t *)(firmware->data + phdr->p_offset);

	/* Write the FW */
		for (index = 0; index < phdr->p_filesz/sizeof(uint32_t);
			index++) {
	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);
	return 0;
}
	IPADBG("IPA FWs (GSI FW, HPS and DPS) were loaded\n");

/**
 * 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, phys_addr_t gsi_mem_base)
{
	const struct elf32_hdr *ehdr;
	const struct elf32_phdr *phdr;
	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;
	ipa_assert_on(!ehdr);
	if (ehdr->e_phnum != 3) {
		IPAERR("Unexpected number of ELF program headers\n");
		return -EINVAL;
	}
	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;
}

Loading