msm: camera: utils: Add address validation for io operation

Kernel panic might be caused by improper register offset which
is not memory aligned during write or read operation, this change
adds a validation for the offset to avoid accessing invalid address.

CRs-Fixed: 3542219
Change-Id: I8761c8d416890bd4571be8a64118036c0173d303
Signed-off-by: Stark Lin <quic_starlin@quicinc.com>
This commit is contained in:
Stark Lin
2023-08-10 17:13:32 -07:00
committed by Camera Software Integration
parent 277c1f983b
commit 56429494cb
2 changed files with 108 additions and 98 deletions

View File

@@ -3499,7 +3499,7 @@ static int cam_soc_util_dump_cont_reg_range(
struct cam_reg_range_read_desc *reg_read, uint32_t base_idx, struct cam_reg_range_read_desc *reg_read, uint32_t base_idx,
struct cam_reg_dump_out_buffer *dump_out_buf, uintptr_t cmd_buf_end) struct cam_reg_dump_out_buffer *dump_out_buf, uintptr_t cmd_buf_end)
{ {
int i = 0, rc = 0; int i = 0, rc = 0, val = 0;
uint32_t write_idx = 0; uint32_t write_idx = 0;
if (!soc_info || !dump_out_buf || !reg_read || !cmd_buf_end) { if (!soc_info || !dump_out_buf || !reg_read || !cmd_buf_end) {
@@ -3536,21 +3536,14 @@ static int cam_soc_util_dump_cont_reg_range(
write_idx = dump_out_buf->bytes_written / sizeof(uint32_t); write_idx = dump_out_buf->bytes_written / sizeof(uint32_t);
for (i = 0; i < reg_read->num_values; i++) { for (i = 0; i < reg_read->num_values; i++) {
if ((reg_read->offset + (i * sizeof(uint32_t))) > val = cam_soc_util_r(soc_info, base_idx,
(uint32_t)soc_info->reg_map[base_idx].size) { (reg_read->offset + (i * sizeof(uint32_t))));
CAM_ERR(CAM_UTIL, if (!val)
"Reg offset out of range, offset: 0x%X reg_map size: 0x%X", CAM_WARN(CAM_UTIL, "Possibly fails to read");
(reg_read->offset + (i * sizeof(uint32_t))),
(uint32_t)soc_info->reg_map[base_idx].size);
rc = -EINVAL;
goto end;
}
dump_out_buf->dump_data[write_idx++] = reg_read->offset + dump_out_buf->dump_data[write_idx++] = reg_read->offset +
(i * sizeof(uint32_t)); (i * sizeof(uint32_t));
dump_out_buf->dump_data[write_idx++] = dump_out_buf->dump_data[write_idx++] = val;
cam_soc_util_r(soc_info, base_idx,
(reg_read->offset + (i * sizeof(uint32_t))));
dump_out_buf->bytes_written += (2 * sizeof(uint32_t)); dump_out_buf->bytes_written += (2 * sizeof(uint32_t));
} }
@@ -3563,7 +3556,7 @@ static int cam_soc_util_dump_dmi_reg_range(
struct cam_dmi_read_desc *dmi_read, uint32_t base_idx, struct cam_dmi_read_desc *dmi_read, uint32_t base_idx,
struct cam_reg_dump_out_buffer *dump_out_buf, uintptr_t cmd_buf_end) struct cam_reg_dump_out_buffer *dump_out_buf, uintptr_t cmd_buf_end)
{ {
int i = 0, rc = 0; int i = 0, rc = 0, val = 0;
uint32_t write_idx = 0; uint32_t write_idx = 0;
if (!soc_info || !dump_out_buf || !dmi_read || !cmd_buf_end) { if (!soc_info || !dump_out_buf || !dmi_read || !cmd_buf_end) {
@@ -3618,19 +3611,14 @@ static int cam_soc_util_dump_dmi_reg_range(
write_idx = dump_out_buf->bytes_written / sizeof(uint32_t); write_idx = dump_out_buf->bytes_written / sizeof(uint32_t);
for (i = 0; i < dmi_read->num_pre_writes; i++) { for (i = 0; i < dmi_read->num_pre_writes; i++) {
if (dmi_read->pre_read_config[i].offset > rc = cam_soc_util_w_mb(soc_info, base_idx,
(uint32_t)soc_info->reg_map[base_idx].size) { dmi_read->pre_read_config[i].offset,
CAM_ERR(CAM_UTIL, dmi_read->pre_read_config[i].value);
"Reg offset out of range, offset: 0x%X reg_map size: 0x%X", if (rc) {
dmi_read->pre_read_config[i].offset, CAM_ERR(CAM_UTIL, "Fails to write for pre_read_config");
(uint32_t)soc_info->reg_map[base_idx].size);
rc = -EINVAL;
goto end; goto end;
} }
cam_soc_util_w_mb(soc_info, base_idx,
dmi_read->pre_read_config[i].offset,
dmi_read->pre_read_config[i].value);
dump_out_buf->dump_data[write_idx++] = dump_out_buf->dump_data[write_idx++] =
dmi_read->pre_read_config[i].offset; dmi_read->pre_read_config[i].offset;
dump_out_buf->dump_data[write_idx++] = dump_out_buf->dump_data[write_idx++] =
@@ -3638,39 +3626,26 @@ static int cam_soc_util_dump_dmi_reg_range(
dump_out_buf->bytes_written += (2 * sizeof(uint32_t)); dump_out_buf->bytes_written += (2 * sizeof(uint32_t));
} }
if (dmi_read->dmi_data_read.offset >
(uint32_t)soc_info->reg_map[base_idx].size) {
CAM_ERR(CAM_UTIL,
"Reg offset out of range, offset: 0x%X reg_map size: 0x%X",
dmi_read->dmi_data_read.offset,
(uint32_t)soc_info->reg_map[base_idx].size);
rc = -EINVAL;
goto end;
}
for (i = 0; i < dmi_read->dmi_data_read.num_values; i++) { for (i = 0; i < dmi_read->dmi_data_read.num_values; i++) {
val = cam_soc_util_r_mb(soc_info, base_idx,
dmi_read->dmi_data_read.offset);
if (!val)
CAM_WARN(CAM_UTIL, "Possibly fails to read for dmi_data_read");
dump_out_buf->dump_data[write_idx++] = dump_out_buf->dump_data[write_idx++] =
dmi_read->dmi_data_read.offset; dmi_read->dmi_data_read.offset;
dump_out_buf->dump_data[write_idx++] = dump_out_buf->dump_data[write_idx++] = val;
cam_soc_util_r_mb(soc_info, base_idx,
dmi_read->dmi_data_read.offset);
dump_out_buf->bytes_written += (2 * sizeof(uint32_t)); dump_out_buf->bytes_written += (2 * sizeof(uint32_t));
} }
for (i = 0; i < dmi_read->num_post_writes; i++) { for (i = 0; i < dmi_read->num_post_writes; i++) {
if (dmi_read->post_read_config[i].offset > rc = cam_soc_util_w_mb(soc_info, base_idx,
(uint32_t)soc_info->reg_map[base_idx].size) {
CAM_ERR(CAM_UTIL,
"Reg offset out of range, offset: 0x%X reg_map size: 0x%X",
dmi_read->post_read_config[i].offset,
(uint32_t)soc_info->reg_map[base_idx].size);
rc = -EINVAL;
goto end;
}
cam_soc_util_w_mb(soc_info, base_idx,
dmi_read->post_read_config[i].offset, dmi_read->post_read_config[i].offset,
dmi_read->post_read_config[i].value); dmi_read->post_read_config[i].value);
if (rc) {
CAM_ERR(CAM_UTIL, "Fails to write for post_read_config");
goto end;
}
} }
end: end:
@@ -3684,6 +3659,7 @@ static int cam_soc_util_dump_dmi_reg_range_user_buf(
{ {
int i; int i;
int rc; int rc;
int val = 0;
size_t buf_len = 0; size_t buf_len = 0;
uint8_t *dst; uint8_t *dst;
size_t remain_len; size_t remain_len;
@@ -3745,52 +3721,36 @@ static int cam_soc_util_dump_dmi_reg_range_user_buf(
*waddr = soc_info->index; *waddr = soc_info->index;
waddr++; waddr++;
for (i = 0; i < dmi_read->num_pre_writes; i++) { for (i = 0; i < dmi_read->num_pre_writes; i++) {
if (dmi_read->pre_read_config[i].offset > rc = cam_soc_util_w_mb(soc_info, base_idx,
(uint32_t)soc_info->reg_map[base_idx].size) { dmi_read->pre_read_config[i].offset,
CAM_ERR(CAM_UTIL, dmi_read->pre_read_config[i].value);
"Reg offset out of range, offset: 0x%X reg_map size: 0x%X", if (rc) {
dmi_read->pre_read_config[i].offset, CAM_ERR(CAM_UTIL, "Fails to write for pre_read_config");
(uint32_t)soc_info->reg_map[base_idx].size);
rc = -EINVAL;
goto end; goto end;
} }
cam_soc_util_w_mb(soc_info, base_idx,
dmi_read->pre_read_config[i].offset,
dmi_read->pre_read_config[i].value);
*waddr++ = dmi_read->pre_read_config[i].offset; *waddr++ = dmi_read->pre_read_config[i].offset;
*waddr++ = dmi_read->pre_read_config[i].value; *waddr++ = dmi_read->pre_read_config[i].value;
} }
if (dmi_read->dmi_data_read.offset >
(uint32_t)soc_info->reg_map[base_idx].size) {
CAM_ERR(CAM_UTIL,
"Reg offset out of range, offset: 0x%X reg_map size: 0x%X",
dmi_read->dmi_data_read.offset,
(uint32_t)soc_info->reg_map[base_idx].size);
rc = -EINVAL;
goto end;
}
for (i = 0; i < dmi_read->dmi_data_read.num_values; i++) { for (i = 0; i < dmi_read->dmi_data_read.num_values; i++) {
*waddr++ = dmi_read->dmi_data_read.offset; val = cam_soc_util_r_mb(soc_info, base_idx,
*waddr++ = cam_soc_util_r_mb(soc_info, base_idx,
dmi_read->dmi_data_read.offset); dmi_read->dmi_data_read.offset);
if (!val)
CAM_WARN(CAM_UTIL, "Possibly fails to read for dmi_data_read");
*waddr++ = dmi_read->dmi_data_read.offset;
*waddr++ = val;
} }
for (i = 0; i < dmi_read->num_post_writes; i++) { for (i = 0; i < dmi_read->num_post_writes; i++) {
if (dmi_read->post_read_config[i].offset > rc = cam_soc_util_w_mb(soc_info, base_idx,
(uint32_t)soc_info->reg_map[base_idx].size) {
CAM_ERR(CAM_UTIL,
"Reg offset out of range, offset: 0x%X reg_map size: 0x%X",
dmi_read->post_read_config[i].offset,
(uint32_t)soc_info->reg_map[base_idx].size);
rc = -EINVAL;
goto end;
}
cam_soc_util_w_mb(soc_info, base_idx,
dmi_read->post_read_config[i].offset, dmi_read->post_read_config[i].offset,
dmi_read->post_read_config[i].value); dmi_read->post_read_config[i].value);
if (rc) {
CAM_ERR(CAM_UTIL, "Fails to write for post_read_config");
goto end;
}
} }
hdr->size = (waddr - start) * hdr->word_size; hdr->size = (waddr - start) * hdr->word_size;
dump_args->offset += hdr->size + dump_args->offset += hdr->size +
@@ -3808,7 +3768,7 @@ static int cam_soc_util_dump_cont_reg_range_user_buf(
struct cam_hw_soc_dump_args *dump_args) struct cam_hw_soc_dump_args *dump_args)
{ {
int i; int i;
int rc = 0; int rc = 0, val = 0;
size_t buf_len; size_t buf_len;
uint8_t *dst; uint8_t *dst;
size_t remain_len; size_t remain_len;
@@ -3859,19 +3819,13 @@ static int cam_soc_util_dump_cont_reg_range_user_buf(
*waddr = soc_info->index; *waddr = soc_info->index;
waddr++; waddr++;
for (i = 0; i < reg_read->num_values; i++) { for (i = 0; i < reg_read->num_values; i++) {
if ((reg_read->offset + (i * sizeof(uint32_t))) > val = cam_soc_util_r(soc_info, base_idx,
(uint32_t)soc_info->reg_map[base_idx].size) { (reg_read->offset + (i * sizeof(uint32_t))));
CAM_ERR(CAM_UTIL, if (!val)
"Reg offset out of range, offset: 0x%X reg_map size: 0x%X", CAM_WARN(CAM_UTIL, "Possibly fails to read");
(reg_read->offset + (i * sizeof(uint32_t))),
(uint32_t)soc_info->reg_map[base_idx].size);
rc = -EINVAL;
goto end;
}
waddr[0] = reg_read->offset + (i * sizeof(uint32_t)); waddr[0] = reg_read->offset + (i * sizeof(uint32_t));
waddr[1] = cam_soc_util_r(soc_info, base_idx, waddr[1] = val;
(reg_read->offset + (i * sizeof(uint32_t))));
waddr += 2; waddr += 2;
} }
hdr->size = (waddr - start) * hdr->word_size; hdr->size = (waddr - start) * hdr->word_size;

View File

@@ -21,6 +21,7 @@
#include <linux/of_fdt.h> #include <linux/of_fdt.h>
#include "cam_io_util.h" #include "cam_io_util.h"
#include "cam_debug_util.h"
#include <media/cam_defs.h> #include <media/cam_defs.h>
#if IS_REACHABLE(CONFIG_MSM_MMRM) #if IS_REACHABLE(CONFIG_MSM_MMRM)
@@ -666,6 +667,37 @@ int cam_soc_util_regulator_disable(struct regulator *rgltr,
uint32_t rgltr_min_volt, uint32_t rgltr_max_volt, uint32_t rgltr_min_volt, uint32_t rgltr_max_volt,
uint32_t rgltr_op_mode, uint32_t rgltr_delay); uint32_t rgltr_op_mode, uint32_t rgltr_delay);
/**
* cam_soc_util_reg_addr_validation()
*
* @brief: Camera SOC util for validating address to be accessed
*
* @soc_info: Device soc information
* @base_index: Index of register space in the HW block
* @offset: Register offset
*
* @return: 0 or specific error code
*/
static inline int cam_soc_util_reg_addr_validation(
struct cam_hw_soc_info *soc_info,
uint32_t base_idx, uint32_t offset)
{
if (offset > (uint32_t)soc_info->reg_map[base_idx].size) {
CAM_ERR(CAM_UTIL,
"Reg offset out of range, offset: 0x%X reg_map size: 0x%X",
offset,
(uint32_t)soc_info->reg_map[base_idx].size);
return -EINVAL;
}
if (offset % 4) {
CAM_ERR(CAM_UTIL, "Offset: 0x%X is not memory aligned",
offset);
return -EINVAL;
}
return 0;
}
/** /**
* cam_soc_util_w() * cam_soc_util_w()
@@ -674,7 +706,7 @@ int cam_soc_util_regulator_disable(struct regulator *rgltr,
* *
* @soc_info: Device soc information * @soc_info: Device soc information
* @base_index: Index of register space in the HW block * @base_index: Index of register space in the HW block
* @offset: Offset of register to be read * @offset: Offset of register to be writen
* @data: Value to be written * @data: Value to be written
* *
* @return: Success or Failure * @return: Success or Failure
@@ -682,8 +714,14 @@ int cam_soc_util_regulator_disable(struct regulator *rgltr,
static inline int cam_soc_util_w(struct cam_hw_soc_info *soc_info, static inline int cam_soc_util_w(struct cam_hw_soc_info *soc_info,
uint32_t base_index, uint32_t offset, uint32_t data) uint32_t base_index, uint32_t offset, uint32_t data)
{ {
if (!CAM_SOC_GET_REG_MAP_START(soc_info, base_index)) if (!CAM_SOC_GET_REG_MAP_START(soc_info, base_index)) {
CAM_ERR(CAM_UTIL, "No valid mapped starting address found");
return -EINVAL; return -EINVAL;
}
if (cam_soc_util_reg_addr_validation(soc_info, base_index, offset))
return -EINVAL;
return cam_io_w(data, return cam_io_w(data,
CAM_SOC_GET_REG_MAP_START(soc_info, base_index) + offset); CAM_SOC_GET_REG_MAP_START(soc_info, base_index) + offset);
} }
@@ -698,7 +736,7 @@ static inline int cam_soc_util_w(struct cam_hw_soc_info *soc_info,
* *
* @soc_info: Device soc information * @soc_info: Device soc information
* @base_index: Index of register space in the HW block * @base_index: Index of register space in the HW block
* @offset: Offset of register to be read * @offset: Offset of register to be writen
* @data: Value to be written * @data: Value to be written
* *
* @return: Success or Failure * @return: Success or Failure
@@ -706,8 +744,14 @@ static inline int cam_soc_util_w(struct cam_hw_soc_info *soc_info,
static inline int cam_soc_util_w_mb(struct cam_hw_soc_info *soc_info, static inline int cam_soc_util_w_mb(struct cam_hw_soc_info *soc_info,
uint32_t base_index, uint32_t offset, uint32_t data) uint32_t base_index, uint32_t offset, uint32_t data)
{ {
if (!CAM_SOC_GET_REG_MAP_START(soc_info, base_index)) if (!CAM_SOC_GET_REG_MAP_START(soc_info, base_index)) {
CAM_ERR(CAM_UTIL, "No valid mapped starting address found");
return -EINVAL; return -EINVAL;
}
if (cam_soc_util_reg_addr_validation(soc_info, base_index, offset))
return -EINVAL;
return cam_io_w_mb(data, return cam_io_w_mb(data,
CAM_SOC_GET_REG_MAP_START(soc_info, base_index) + offset); CAM_SOC_GET_REG_MAP_START(soc_info, base_index) + offset);
} }
@@ -726,8 +770,14 @@ static inline int cam_soc_util_w_mb(struct cam_hw_soc_info *soc_info,
static inline uint32_t cam_soc_util_r(struct cam_hw_soc_info *soc_info, static inline uint32_t cam_soc_util_r(struct cam_hw_soc_info *soc_info,
uint32_t base_index, uint32_t offset) uint32_t base_index, uint32_t offset)
{ {
if (!CAM_SOC_GET_REG_MAP_START(soc_info, base_index)) if (!CAM_SOC_GET_REG_MAP_START(soc_info, base_index)) {
CAM_ERR(CAM_UTIL, "No valid mapped starting address found");
return 0; return 0;
}
if (cam_soc_util_reg_addr_validation(soc_info, base_index, offset))
return 0;
return cam_io_r( return cam_io_r(
CAM_SOC_GET_REG_MAP_START(soc_info, base_index) + offset); CAM_SOC_GET_REG_MAP_START(soc_info, base_index) + offset);
} }
@@ -749,8 +799,14 @@ static inline uint32_t cam_soc_util_r(struct cam_hw_soc_info *soc_info,
static inline uint32_t cam_soc_util_r_mb(struct cam_hw_soc_info *soc_info, static inline uint32_t cam_soc_util_r_mb(struct cam_hw_soc_info *soc_info,
uint32_t base_index, uint32_t offset) uint32_t base_index, uint32_t offset)
{ {
if (!CAM_SOC_GET_REG_MAP_START(soc_info, base_index)) if (!CAM_SOC_GET_REG_MAP_START(soc_info, base_index)) {
CAM_ERR(CAM_UTIL, "No valid mapped starting address found");
return 0; return 0;
}
if (cam_soc_util_reg_addr_validation(soc_info, base_index, offset))
return 0;
return cam_io_r_mb( return cam_io_r_mb(
CAM_SOC_GET_REG_MAP_START(soc_info, base_index) + offset); CAM_SOC_GET_REG_MAP_START(soc_info, base_index) + offset);
} }