diff --git a/drivers/cam_cdm/cam_cdm_util.c b/drivers/cam_cdm/cam_cdm_util.c index 2eb77b217a..31b0b9b8bd 100644 --- a/drivers/cam_cdm/cam_cdm_util.c +++ b/drivers/cam_cdm/cam_cdm_util.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved. - * Copyright (c) 2022-2023, Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2022-2024, Qualcomm Innovation Center, Inc. All rights reserved. */ #include @@ -489,115 +489,253 @@ int cam_cdm_get_ioremap_from_base(uint32_t hw_base, return ret; } -static int cam_cdm_util_reg_cont_write(void __iomem *base_addr, - uint32_t *cmd_buf, uint32_t cmd_buf_size, uint32_t *used_bytes) +static int cam_cdm_util_cmd_buf_validation(void __iomem *base_addr, + uint32_t base_array_size, + struct cam_soc_reg_map *base_table[CAM_SOC_MAX_BLOCK], + uint32_t cmd_buf_size, uint32_t *cmd_buf, void *buf, + resource_size_t *size, + enum cam_cdm_command cmd_type) { - int ret = 0; - uint32_t *data; - struct cdm_regcontinuous_cmd *reg_cont; - if ((cmd_buf_size < cam_cdm_get_cmd_header_size(CAM_CDM_CMD_REG_CONT)) || - (!base_addr)) { - CAM_ERR(CAM_CDM, "invalid base addr and data length %d %pK", - cmd_buf_size, base_addr); - return -EINVAL; - } - - reg_cont = (struct cdm_regcontinuous_cmd *)cmd_buf; - if ((!reg_cont->count) || (((reg_cont->count * sizeof(uint32_t)) + - cam_cdm_get_cmd_header_size(CAM_CDM_CMD_REG_CONT)) > - cmd_buf_size)) { - CAM_ERR(CAM_CDM, "buffer size %d is not sufficient for count%d", - cmd_buf_size, reg_cont->count); - return -EINVAL; - } - data = cmd_buf + cam_cdm_get_cmd_header_size(CAM_CDM_CMD_REG_CONT); - cam_io_memcpy(base_addr + reg_cont->offset, data, - reg_cont->count * sizeof(uint32_t)); - - *used_bytes = (reg_cont->count * sizeof(uint32_t)) + - (4 * cam_cdm_get_cmd_header_size(CAM_CDM_CMD_REG_CONT)); - - return ret; -} - -static int cam_cdm_util_reg_random_write(void __iomem *base_addr, - uint32_t *cmd_buf, uint32_t cmd_buf_size, uint32_t *used_bytes) -{ - uint32_t i; - struct cdm_regrandom_cmd *reg_random; - uint32_t *data; + int i, ret = 0; if (!base_addr) { CAM_ERR(CAM_CDM, "invalid base address"); return -EINVAL; } - reg_random = (struct cdm_regrandom_cmd *) cmd_buf; - if ((!reg_random->count) || (((reg_random->count * (sizeof(uint32_t) * 2)) + - cam_cdm_get_cmd_header_size(CAM_CDM_CMD_REG_RANDOM)) > - cmd_buf_size)) { - CAM_ERR(CAM_CDM, "invalid reg_count %d cmd_buf_size %d", - reg_random->count, cmd_buf_size); + for (i = 0; i < base_array_size; i++) { + if ((base_table[i]) && + ((base_table[i])->mem_base == base_addr)) { + *size = (base_table[i])->size; + break; + } + } + + if (*size == 0) { + CAM_ERR(CAM_CDM, "Could not retrieve ioremap size, address not mapped!"); return -EINVAL; } + + switch (cmd_type) { + case CAM_CDM_CMD_REG_RANDOM: { + struct cdm_regrandom_cmd *reg_random = (struct cdm_regrandom_cmd *)buf; + uint32_t *data, offset; + + if ((!reg_random->count) || (((reg_random->count * (sizeof(uint32_t) * 2)) + + cam_cdm_get_cmd_header_size(CAM_CDM_CMD_REG_RANDOM)) > + cmd_buf_size)) { + CAM_ERR(CAM_CDM, "invalid reg_count %d cmd_buf_size %d", + reg_random->count, cmd_buf_size); + ret = -EINVAL; + } + + data = cmd_buf + cam_cdm_get_cmd_header_size(CAM_CDM_CMD_REG_RANDOM); + + for (i = 0; i < reg_random->count; i++) { + offset = data[0]; + if (offset > *size) { + CAM_ERR(CAM_CDM, "Offset out of mapped range! size:%llu offset:%u", + *size, offset); + return -EINVAL; + } + data += 2; + } + } + break; + case CAM_CDM_CMD_REG_CONT: { + struct cdm_regcontinuous_cmd *reg_cont = (struct cdm_regcontinuous_cmd *) buf; + + if ((!reg_cont->count) || (((reg_cont->count * sizeof(uint32_t)) + + cam_cdm_get_cmd_header_size(CAM_CDM_CMD_REG_CONT)) > + cmd_buf_size)) { + CAM_ERR(CAM_CDM, "buffer size %d is not sufficient for count%d", + cmd_buf_size, reg_cont->count); + ret = -EINVAL; + } + + if ((reg_cont->offset > *size) && ((reg_cont->offset + + (reg_cont->count * sizeof(uint32_t))) > *size)) { + CAM_ERR(CAM_CDM, "Offset out of mapped range! size: %lu, offset: %u", + *size, reg_cont->offset); + return -EINVAL; + } + } + break; + case CAM_CDM_CMD_SWD_DMI_64: { + struct cdm_dmi_cmd *swd_dmi = (struct cdm_dmi_cmd *) buf; + + if (cmd_buf_size < (cam_cdm_required_size_dmi() + swd_dmi->length + 1)) { + CAM_ERR(CAM_CDM, "invalid CDM_SWD_DMI length %d", + swd_dmi->length + 1); + ret = -EINVAL; + } + + if ((swd_dmi->DMIAddr + CAM_CDM_DMI_DATA_LO_OFFSET > *size) || + (swd_dmi->DMIAddr + CAM_CDM_DMI_DATA_HI_OFFSET > *size)) { + CAM_ERR(CAM_CDM, + "Offset out of mapped range! size:%llu lo_offset:%u hi_offset:%u", + *size, swd_dmi->DMIAddr + CAM_CDM_DMI_DATA_LO_OFFSET, + swd_dmi->DMIAddr + CAM_CDM_DMI_DATA_LO_OFFSET); + return -EINVAL; + } + } + break; + case CAM_CDM_CMD_SWD_DMI_32: { + struct cdm_dmi_cmd *swd_dmi = (struct cdm_dmi_cmd *) buf; + + if (cmd_buf_size < (cam_cdm_required_size_dmi() + swd_dmi->length + 1)) { + CAM_ERR(CAM_CDM, "invalid CDM_SWD_DMI length %d", + swd_dmi->length + 1); + ret = -EINVAL; + } + + if (swd_dmi->DMIAddr + CAM_CDM_DMI_DATA_LO_OFFSET > *size) { + CAM_ERR(CAM_CDM, + "Offset out of mapped range! size:%llu lo_offset:%u", + *size, swd_dmi->DMIAddr + CAM_CDM_DMI_DATA_LO_OFFSET); + return -EINVAL; + } + } + break; + case CAM_CDM_CMD_DMI: { + struct cdm_dmi_cmd *swd_dmi = (struct cdm_dmi_cmd *) buf; + + if (cmd_buf_size < (cam_cdm_required_size_dmi() + swd_dmi->length + 1)) { + CAM_ERR(CAM_CDM, "invalid CDM_SWD_DMI length %d", + swd_dmi->length + 1); + ret = -EINVAL; + } + + if (swd_dmi->DMIAddr + CAM_CDM_DMI_DATA_OFFSET > *size) { + CAM_ERR(CAM_CDM, + "Offset out of mapped range! size:%llu offset:%u", + *size, swd_dmi->DMIAddr + CAM_CDM_DMI_DATA_OFFSET); + return -EINVAL; + } + } + break; + default: + CAM_ERR(CAM_CDM, "unsupported cdm_cmd_type type 0%x", + cmd_type); + ret = -EINVAL; + break; + } + + return ret; +} + +static int cam_cdm_util_reg_cont_write(void __iomem *base_addr, + uint32_t *cmd_buf, uint32_t cmd_buf_size, uint32_t *used_bytes, + uint32_t base_array_size, + struct cam_soc_reg_map *base_table[CAM_SOC_MAX_BLOCK]) +{ + int rc; + uint32_t *data; + struct cdm_regcontinuous_cmd reg_cont; + resource_size_t size = 0; + + memcpy(®_cont, cmd_buf, sizeof(struct cdm_regcontinuous_cmd)); + rc = cam_cdm_util_cmd_buf_validation(base_addr, base_array_size, base_table, + cmd_buf_size, cmd_buf, (void *)®_cont, + &size, CAM_CDM_CMD_REG_CONT); + if (rc) { + CAM_ERR(CAM_CDM, "Validation failed! rc=%d", rc); + return rc; + } + + data = cmd_buf + cam_cdm_get_cmd_header_size(CAM_CDM_CMD_REG_CONT); + + cam_io_memcpy(base_addr + reg_cont.offset, data, + reg_cont.count * sizeof(uint32_t)); + *used_bytes = (reg_cont.count * sizeof(uint32_t)) + + (4 * cam_cdm_get_cmd_header_size(CAM_CDM_CMD_REG_CONT)); + + return rc; +} + +static int cam_cdm_util_reg_random_write(void __iomem *base_addr, + uint32_t *cmd_buf, uint32_t cmd_buf_size, uint32_t *used_bytes, + uint32_t base_array_size, + struct cam_soc_reg_map *base_table[CAM_SOC_MAX_BLOCK]) +{ + int i, rc; + struct cdm_regrandom_cmd reg_random; + uint32_t *data, offset; + resource_size_t size = 0; + + memcpy(®_random, cmd_buf, sizeof(struct cdm_regrandom_cmd)); + + rc = cam_cdm_util_cmd_buf_validation(base_addr, base_array_size, base_table, + cmd_buf_size, cmd_buf, (void *)®_random, + &size, CAM_CDM_CMD_REG_RANDOM); + if (rc) { + CAM_ERR(CAM_CDM, "Validation failed! rc=%d", rc); + return rc; + } + data = cmd_buf + cam_cdm_get_cmd_header_size(CAM_CDM_CMD_REG_RANDOM); - for (i = 0; i < reg_random->count; i++) { + for (i = 0; i < reg_random.count; i++) { + offset = data[0]; CAM_DBG(CAM_CDM, "reg random: offset %pK, value 0x%x", - ((void __iomem *)(base_addr + data[0])), + ((void __iomem *)(base_addr + offset)), data[1]); - cam_io_w(data[1], base_addr + data[0]); + cam_io_w(data[1], base_addr + offset); data += 2; } - *used_bytes = ((reg_random->count * (sizeof(uint32_t) * 2)) + + *used_bytes = ((reg_random.count * (sizeof(uint32_t) * 2)) + (4 * cam_cdm_get_cmd_header_size(CAM_CDM_CMD_REG_RANDOM))); - return 0; + return rc; } static int cam_cdm_util_swd_dmi_write(uint32_t cdm_cmd_type, void __iomem *base_addr, uint32_t *cmd_buf, uint32_t cmd_buf_size, - uint32_t *used_bytes) + uint32_t *used_bytes, uint32_t base_array_size, + struct cam_soc_reg_map *base_table[CAM_SOC_MAX_BLOCK]) { - uint32_t i; - struct cdm_dmi_cmd *swd_dmi; + int i, rc; + struct cdm_dmi_cmd swd_dmi; uint32_t *data; + resource_size_t size = 0; - swd_dmi = (struct cdm_dmi_cmd *)cmd_buf; - - if (cmd_buf_size < (cam_cdm_required_size_dmi() + swd_dmi->length + 1)) { - CAM_ERR(CAM_CDM, "invalid CDM_SWD_DMI length %d", - swd_dmi->length + 1); - return -EINVAL; + memcpy(&swd_dmi, cmd_buf, sizeof(struct cdm_dmi_cmd)); + rc = cam_cdm_util_cmd_buf_validation(base_addr, base_array_size, base_table, + cmd_buf_size, cmd_buf, (void *)&swd_dmi, + &size, cdm_cmd_type); + if (rc) { + CAM_ERR(CAM_CDM, "Validation failed! rc=%d", rc); + return rc; } + data = cmd_buf + cam_cdm_required_size_dmi(); if (cdm_cmd_type == CAM_CDM_CMD_SWD_DMI_64) { - for (i = 0; i < (swd_dmi->length + 1)/8; i++) { + for (i = 0; i < (swd_dmi.length + 1)/8; i++) { cam_io_w_mb(data[0], base_addr + - swd_dmi->DMIAddr + CAM_CDM_DMI_DATA_LO_OFFSET); + swd_dmi.DMIAddr + CAM_CDM_DMI_DATA_LO_OFFSET); cam_io_w_mb(data[1], base_addr + - swd_dmi->DMIAddr + CAM_CDM_DMI_DATA_HI_OFFSET); + swd_dmi.DMIAddr + CAM_CDM_DMI_DATA_HI_OFFSET); data += 2; } } else if (cdm_cmd_type == CAM_CDM_CMD_DMI) { - for (i = 0; i < (swd_dmi->length + 1)/4; i++) { + for (i = 0; i < (swd_dmi.length + 1)/4; i++) { cam_io_w_mb(data[0], base_addr + - swd_dmi->DMIAddr + CAM_CDM_DMI_DATA_OFFSET); + swd_dmi.DMIAddr + CAM_CDM_DMI_DATA_OFFSET); data += 1; } } else { - for (i = 0; i < (swd_dmi->length + 1)/4; i++) { + for (i = 0; i < (swd_dmi.length + 1)/4; i++) { cam_io_w_mb(data[0], base_addr + - swd_dmi->DMIAddr + CAM_CDM_DMI_DATA_LO_OFFSET); + swd_dmi.DMIAddr + CAM_CDM_DMI_DATA_LO_OFFSET); data += 1; } } - *used_bytes = (4 * cam_cdm_required_size_dmi()) + swd_dmi->length + 1; + *used_bytes = (4 * cam_cdm_required_size_dmi()) + swd_dmi.length + 1; - return 0; + return rc; } int cam_cdm_util_cmd_buf_write(void __iomem **current_device_base, @@ -615,7 +753,8 @@ int cam_cdm_util_cmd_buf_write(void __iomem **current_device_base, switch (cdm_cmd_type) { case CAM_CDM_CMD_REG_CONT: { ret = cam_cdm_util_reg_cont_write(*current_device_base, - cmd_buf, cmd_buf_size, &used_bytes); + cmd_buf, cmd_buf_size, &used_bytes, + base_array_size, base_table); if (ret) break; @@ -628,7 +767,7 @@ int cam_cdm_util_cmd_buf_write(void __iomem **current_device_base, case CAM_CDM_CMD_REG_RANDOM: { ret = cam_cdm_util_reg_random_write( *current_device_base, cmd_buf, cmd_buf_size, - &used_bytes); + &used_bytes, base_array_size, base_table); if (ret) break; @@ -650,7 +789,7 @@ int cam_cdm_util_cmd_buf_write(void __iomem **current_device_base, } ret = cam_cdm_util_swd_dmi_write(cdm_cmd_type, *current_device_base, cmd_buf, cmd_buf_size, - &used_bytes); + &used_bytes, base_array_size, base_table); if (ret) break;