diff --git a/drivers/soc/qcom/qcom-geni-se.c b/drivers/soc/qcom/qcom-geni-se.c index 1a60832ace168..80397fbeaf0dd 100644 --- a/drivers/soc/qcom/qcom-geni-se.c +++ b/drivers/soc/qcom/qcom-geni-se.c @@ -152,8 +152,6 @@ struct se_fw_hdr { /*Magic numbers*/ #define SE_MAGIC_NUM 0x57464553 -#define MAX_GENI_CFG_RAMn_CNT 455 - #define MI_PBT_NON_PAGED_SEGMENT 0x0 #define MI_PBT_HASH_SEGMENT 0x2 #define MI_PBT_NOTUSED_SEGMENT 0x3 @@ -997,25 +995,31 @@ EXPORT_SYMBOL_GPL(geni_icc_disable); /** * geni_find_protocol_fw() - Locate and validate SE firmware for a protocol. - * @dev: Pointer to the device structure. + * @se: Pointer to the serial engine structure. * @fw: Pointer to the firmware image. * @protocol: Expected serial engine protocol type. + * @fw_size_out: Non-NULL output parameter; receives the rounded, validated + * firmware word count on success. * * Identifies the appropriate firmware image or configuration required for a - * specific communication protocol instance running on a Qualcomm GENI - * controller. + * specific communication protocol instance running on a Qualcomm GENI + * controller. Validates the firmware size against the hardware PROG_RAM_DEPTH + * read from SE_HW_PARAM_2. * - * Return: pointer to a valid 'struct se_fw_hdr' if found, or NULL otherwise. + * Return: pointer to a valid 'const struct se_fw_hdr' if found, or NULL otherwise. */ -static struct se_fw_hdr *geni_find_protocol_fw(struct device *dev, const struct firmware *fw, - enum geni_se_protocol_type protocol) +static const struct se_fw_hdr *geni_find_protocol_fw(struct geni_se *se, const struct firmware *fw, + enum geni_se_protocol_type protocol, + u32 *fw_size_out) { + struct device *dev = se->dev; const struct elf32_hdr *ehdr; const struct elf32_phdr *phdrs; const struct elf32_phdr *phdr; - struct se_fw_hdr *sefw; + const struct se_fw_hdr *sefw; u32 fw_end, cfg_idx_end, cfg_val_end; - u16 fw_size; + u32 prog_ram_depth; + u32 fw_size; int i; if (!fw || fw->size < sizeof(struct elf32_hdr)) @@ -1054,29 +1058,30 @@ static struct se_fw_hdr *geni_find_protocol_fw(struct device *dev, const struct if (phdr->p_filesz < sizeof(struct se_fw_hdr)) continue; - sefw = (struct se_fw_hdr *)(fw->data + phdr->p_offset); + sefw = (const struct se_fw_hdr *)(fw->data + phdr->p_offset); fw_size = le16_to_cpu(sefw->fw_size_in_items); - fw_end = le16_to_cpu(sefw->fw_offset) + fw_size * sizeof(u32); - cfg_idx_end = le16_to_cpu(sefw->cfg_idx_offset) + - le16_to_cpu(sefw->cfg_size_in_items) * sizeof(u8); - cfg_val_end = le16_to_cpu(sefw->cfg_val_offset) + - le16_to_cpu(sefw->cfg_size_in_items) * sizeof(u32); if (le32_to_cpu(sefw->magic) != SE_MAGIC_NUM || le32_to_cpu(sefw->version) != 1) continue; - if (le32_to_cpu(sefw->serial_protocol) != protocol) + if (le16_to_cpu(sefw->serial_protocol) != protocol) continue; - if (fw_size % 2 != 0) { + /* Round up so fw_end covers the full copy range. */ + if (fw_size % 2 != 0) fw_size++; - sefw->fw_size_in_items = cpu_to_le16(fw_size); - } - if (fw_size >= MAX_GENI_CFG_RAMn_CNT) { - dev_err(dev, - "Firmware size (%u) exceeds max allowed RAMn count (%u)\n", - fw_size, MAX_GENI_CFG_RAMn_CNT); + fw_end = le16_to_cpu(sefw->fw_offset) + fw_size * sizeof(u32); + cfg_idx_end = le16_to_cpu(sefw->cfg_idx_offset) + + le16_to_cpu(sefw->cfg_size_in_items) * sizeof(u8); + cfg_val_end = le16_to_cpu(sefw->cfg_val_offset) + + le16_to_cpu(sefw->cfg_size_in_items) * sizeof(u32); + + prog_ram_depth = FIELD_GET(PROG_RAM_DEPTH_MSK, + readl_relaxed(se->base + SE_HW_PARAM_2)); + if (fw_size >= prog_ram_depth) { + dev_err(dev, "Firmware size (%u) exceeds RAM size (%u)\n", + fw_size, prog_ram_depth); continue; } @@ -1086,6 +1091,7 @@ static struct se_fw_hdr *geni_find_protocol_fw(struct device *dev, const struct continue; } + *fw_size_out = fw_size; return sefw; } @@ -1196,17 +1202,17 @@ static int geni_load_se_fw(struct geni_se *se, const struct firmware *fw, { const u32 *fw_data, *cfg_val_arr; const u8 *cfg_idx_arr; - u32 i, reg_value; + u32 i, reg_value, fw_size_in_items; int ret; - struct se_fw_hdr *hdr; + const struct se_fw_hdr *hdr; - hdr = geni_find_protocol_fw(se->dev, fw, protocol); + hdr = geni_find_protocol_fw(se, fw, protocol, &fw_size_in_items); if (!hdr) return -EINVAL; - fw_data = (const u32 *)((u8 *)hdr + le16_to_cpu(hdr->fw_offset)); + fw_data = (const u32 *)((const u8 *)hdr + le16_to_cpu(hdr->fw_offset)); cfg_idx_arr = (const u8 *)hdr + le16_to_cpu(hdr->cfg_idx_offset); - cfg_val_arr = (const u32 *)((u8 *)hdr + le16_to_cpu(hdr->cfg_val_offset)); + cfg_val_arr = (const u32 *)((const u8 *)hdr + le16_to_cpu(hdr->cfg_val_offset)); ret = geni_icc_set_bw(se); if (ret) @@ -1277,7 +1283,7 @@ static int geni_load_se_fw(struct geni_se *se, const struct firmware *fw, /* Program RAM address space. */ memcpy_toio(se->base + SE_GENI_CFG_RAMN, fw_data, - le16_to_cpu(hdr->fw_size_in_items) * sizeof(u32)); + fw_size_in_items * sizeof(u32)); /* Put default values on GENI's output pads. */ writel_relaxed(0x1, se->base + GENI_FORCE_DEFAULT_REG); diff --git a/include/linux/soc/qcom/geni-se.h b/include/linux/soc/qcom/geni-se.h index 326744e311cea..3e812c1b40da3 100644 --- a/include/linux/soc/qcom/geni-se.h +++ b/include/linux/soc/qcom/geni-se.h @@ -120,6 +120,7 @@ struct geni_se { #define SE_DMA_RX_FSM_RST 0xd58 #define SE_HW_PARAM_0 0xe24 #define SE_HW_PARAM_1 0xe28 +#define SE_HW_PARAM_2 0xe2c /* GENI_FORCE_DEFAULT_REG fields */ #define FORCE_DEFAULT BIT(0) @@ -287,6 +288,9 @@ struct geni_se { #define RX_FIFO_DEPTH_MSK GENMASK(21, 16) #define RX_FIFO_DEPTH_SHFT 16 +/* SE_HW_PARAM_2 fields */ +#define PROG_RAM_DEPTH_MSK GENMASK(10, 0) + #define HW_VER_MAJOR_MASK GENMASK(31, 28) #define HW_VER_MAJOR_SHFT 28 #define HW_VER_MINOR_MASK GENMASK(27, 16)