From 342d16ac6fb01e304ec75344c693257e00628ecf Mon Sep 17 00:00:00 2001 From: Ghanim Fodi Date: Tue, 24 Jan 2017 15:42:30 +0200 Subject: 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 --- drivers/platform/msm/gsi/gsi.c | 10 ++ drivers/platform/msm/gsi/gsi_reg.h | 4 +- drivers/platform/msm/ipa/ipa_v3/ipa.c | 2 +- drivers/platform/msm/ipa/ipa_v3/ipa_i.h | 2 +- drivers/platform/msm/ipa/ipa_v3/ipa_utils.c | 187 +++++++++++++++------ drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.c | 18 +- drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.h | 12 +- drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_i.h | 6 +- .../platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.c | 6 + .../platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.h | 4 +- include/linux/msm_gsi.h | 21 ++- 11 files changed, 215 insertions(+), 57 deletions(-) diff --git a/drivers/platform/msm/gsi/gsi.c b/drivers/platform/msm/gsi/gsi.c index 24fdd61..a30d806 100644 --- a/drivers/platform/msm/gsi/gsi.c +++ b/drivers/platform/msm/gsi/gsi.c @@ -2742,6 +2742,16 @@ int gsi_enable_fw(phys_addr_t gsi_base_addr, u32 gsi_size, enum gsi_ver ver) } 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; diff --git a/drivers/platform/msm/gsi/gsi_reg.h b/drivers/platform/msm/gsi/gsi_reg.h index fa1e848..1acaf74 100644 --- a/drivers/platform/msm/gsi/gsi_reg.h +++ b/drivers/platform/msm/gsi/gsi_reg.h @@ -1,4 +1,4 @@ -/* 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__ */ diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa.c b/drivers/platform/msm/ipa/ipa_v3/ipa.c index aa83cbd..82887d05 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa.c @@ -4187,7 +4187,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); diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h index 3f19c21..fa6dd64 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h @@ -2015,7 +2015,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); diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c index 7b7ae75..ba255a2 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c @@ -6107,75 +6107,164 @@ int ipa3_generate_eq_from_hw_rule( return 0; } +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; - - ehdr = (struct elf32_hdr *) firmware->data; - - elf_phdr_ptr = firmware->data + sizeof(*ehdr); + 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; + } - 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; + 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; + } - /* - * 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; - } + 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)); - /* Set the entire region to 0s */ - memset(fw_mem_base, 0, phdr->p_memsz); + /* + * 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 + */ - /* - * 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); + /* 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; - if (phdr->p_memsz % sizeof(uint32_t)) { - IPAERR("FW size %u doesn't align to 32bit\n", - phdr->p_memsz); - return -EFAULT; - } + phdr++; + ipa_reg_mem_base = ipa3_ctx->ipa_wrapper_base + ipahal_get_reg_base(); - /* 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++; - } + /* 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; - iounmap(fw_mem_base); + phdr++; - elf_phdr_ptr = elf_phdr_ptr + sizeof(*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; } - IPADBG("IPA FWs (GSI FW, HPS and DPS) were loaded\n"); + 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 d023522..95a97ed 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 @@ -1253,6 +1253,22 @@ int ipahal_get_proc_ctx_needed_len(enum ipa_hdr_proc_type type) 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) { int result; diff --git a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.h b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.h index 00b2058..746bc30 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 @@ void ipahal_cp_proc_ctx_to_hw_buff(enum ipa_hdr_proc_type type, */ 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); 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 6a22240..5f02b4df 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 @@ -542,4 +542,8 @@ struct ipa_hw_hdr_proc_ctx_add_hdr_cmd_seq { 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 6b606ab..6a70fc0 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.c @@ -1157,6 +1157,12 @@ static struct ipahal_reg_obj ipahal_reg_objs[IPA_HW_MAX][IPA_REG_MAX] = { [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 98894c3..6ca16bf 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 @@ enum ipahal_reg_name { IPA_QSB_MAX_READS, IPA_TX_CFG, IPA_IDLE_INDICATION_CFG, + IPA_DPS_SEQUENCER_FIRST, + IPA_HPS_SEQUENCER_FIRST, IPA_REG_MAX, }; diff --git a/include/linux/msm_gsi.h b/include/linux/msm_gsi.h index 4825fc7..18d4e72 100644 --- a/include/linux/msm_gsi.h +++ b/include/linux/msm_gsi.h @@ -1,4 +1,4 @@ -/* 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 @@ -1041,6 +1041,19 @@ int gsi_configure_regs(phys_addr_t gsi_base_addr, u32 gsi_size, */ int gsi_enable_fw(phys_addr_t gsi_base_addr, u32 gsi_size, enum gsi_ver ver); +/** + * gsi_get_inst_ram_offset_and_size - Peripheral should call this function + * to get instruction RAM base address offset and size. Peripheral typically + * uses this info to load GSI FW into the IRAM. + * + * @base_offset:[OUT] - IRAM base offset address + * @size: [OUT] - IRAM size + + * @Return none + */ +void gsi_get_inst_ram_offset_and_size(unsigned long *base_offset, + unsigned long *size); + /* * Here is a typical sequence of calls * @@ -1228,9 +1241,15 @@ static inline int gsi_configure_regs(phys_addr_t gsi_base_addr, u32 gsi_size, { return -GSI_STATUS_UNSUPPORTED_OP; } + static inline int gsi_enable_fw(phys_addr_t gsi_base_addr, u32 gsi_size) { return -GSI_STATUS_UNSUPPORTED_OP; } + +static inline void gsi_get_inst_ram_offset_and_size(unsigned long *base_offset, + unsigned long *size) +{ +} #endif #endif -- cgit v1.1