diff --git a/common/tegra_hwpm_alist_utils.c b/common/tegra_hwpm_alist_utils.c index f6d9c2f..67ca26b 100644 --- a/common/tegra_hwpm_alist_utils.c +++ b/common/tegra_hwpm_alist_utils.c @@ -26,6 +26,7 @@ #include #include #include +#include int tegra_soc_hwpm_get_allowlist_size(struct tegra_soc_hwpm *hwpm) { @@ -52,8 +53,8 @@ int tegra_soc_hwpm_update_allowlist(struct tegra_soc_hwpm *hwpm, void *ioctl_struct) { int err = 0; - long pinned_pages = 0; - long page_idx = 0; + u64 pinned_pages = 0; + u64 page_idx = 0; u64 alist_buf_size = 0; u64 num_pages = 0; u64 *full_alist_u64 = NULL; @@ -74,11 +75,17 @@ int tegra_soc_hwpm_update_allowlist(struct tegra_soc_hwpm *hwpm, tegra_hwpm_err(hwpm, "alist_buf_size uninitialized"); return -ENODEV; } - alist_buf_size = hwpm->full_alist_size * - hwpm->active_chip->get_alist_buf_size(hwpm); + alist_buf_size = tegra_hwpm_safe_mult_u64(hwpm->full_alist_size, + hwpm->active_chip->get_alist_buf_size(hwpm)); /* Memory map user buffer into kernel address space */ - num_pages = DIV_ROUND_UP(offset + alist_buf_size, PAGE_SIZE); + alist_buf_size = tegra_hwpm_safe_add_u64(offset, alist_buf_size); + + /* Round-up and Divide */ + alist_buf_size = tegra_hwpm_safe_sub_u64( + tegra_hwpm_safe_add_u64(alist_buf_size, PAGE_SIZE), 1ULL); + num_pages = alist_buf_size / PAGE_SIZE; + pages = (struct page **)kzalloc(sizeof(*pages) * num_pages, GFP_KERNEL); if (!pages) { tegra_hwpm_err(hwpm, @@ -121,7 +128,7 @@ alist_unmap: if (full_alist) vunmap(full_alist); if (pinned_pages > 0) { - for (page_idx = 0; page_idx < pinned_pages; page_idx++) { + for (page_idx = 0ULL; page_idx < pinned_pages; page_idx++) { set_page_dirty(pages[page_idx]); put_page(pages[page_idx]); } diff --git a/common/tegra_hwpm_mem_buf_utils.c b/common/tegra_hwpm_mem_buf_utils.c index f7ea2ae..c824d8c 100644 --- a/common/tegra_hwpm_mem_buf_utils.c +++ b/common/tegra_hwpm_mem_buf_utils.c @@ -25,13 +25,15 @@ #include #include #include +#include static int tegra_hwpm_dma_map_stream_buffer(struct tegra_soc_hwpm *hwpm, struct tegra_soc_hwpm_alloc_pma_stream *alloc_pma_stream) { tegra_hwpm_fn(hwpm, " "); - hwpm->stream_dma_buf = dma_buf_get(alloc_pma_stream->stream_buf_fd); + hwpm->stream_dma_buf = dma_buf_get(tegra_hwpm_safe_cast_u64_to_s32( + alloc_pma_stream->stream_buf_fd)); if (IS_ERR(hwpm->stream_dma_buf)) { tegra_hwpm_err(hwpm, "Unable to get stream dma_buf"); return PTR_ERR(hwpm->stream_dma_buf); @@ -56,8 +58,8 @@ static int tegra_hwpm_dma_map_mem_bytes_buffer(struct tegra_soc_hwpm *hwpm, { tegra_hwpm_fn(hwpm, " "); - hwpm->mem_bytes_dma_buf = - dma_buf_get(alloc_pma_stream->mem_bytes_buf_fd); + hwpm->mem_bytes_dma_buf = dma_buf_get(tegra_hwpm_safe_cast_u64_to_s32( + alloc_pma_stream->mem_bytes_buf_fd)); if (IS_ERR(hwpm->mem_bytes_dma_buf)) { tegra_hwpm_err(hwpm, "Unable to get mem bytes dma_buf"); return PTR_ERR(hwpm->mem_bytes_dma_buf); @@ -325,7 +327,7 @@ int tegra_hwpm_update_mem_bytes(struct tegra_soc_hwpm *hwpm, return -ENODEV; } update_get_put->b_overflowed = - hwpm->active_chip->membuf_overflow_status(hwpm); + (u8) hwpm->active_chip->membuf_overflow_status(hwpm); tegra_hwpm_dbg(hwpm, hwpm_verbose, "OVERFLOWED = %u", update_get_put->b_overflowed); } diff --git a/common/tegra_hwpm_resource_utils.c b/common/tegra_hwpm_resource_utils.c index 0faf73c..2ceb826 100644 --- a/common/tegra_hwpm_resource_utils.c +++ b/common/tegra_hwpm_resource_utils.c @@ -17,6 +17,7 @@ #include #include #include +#include int tegra_soc_hwpm_reserve_resource(struct tegra_soc_hwpm *hwpm, u32 resource) { diff --git a/hal/t234/t234_hwpm_alist_utils.c b/hal/t234/t234_hwpm_alist_utils.c index 2b2c3fb..3e1a808 100644 --- a/hal/t234/t234_hwpm_alist_utils.c +++ b/hal/t234/t234_hwpm_alist_utils.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -35,8 +36,9 @@ int t234_hwpm_zero_alist_regs(struct tegra_soc_hwpm *hwpm, for (alist_idx = 0; alist_idx < aperture->alist_size; alist_idx++) { if (aperture->alist[alist_idx].zero_at_init) { regops_writel(hwpm, aperture, - aperture->start_abs_pa + - aperture->alist[alist_idx].reg_offset, 0U); + tegra_hwpm_safe_add_u64(aperture->start_abs_pa, + aperture->alist[alist_idx].reg_offset), + 0U); } } return 0; @@ -86,8 +88,10 @@ int t234_hwpm_get_alist_size(struct tegra_soc_hwpm *hwpm) } if (perfmux->alist) { - hwpm->full_alist_size += - perfmux->alist_size; + hwpm->full_alist_size = + tegra_hwpm_safe_add_u64( + hwpm->full_alist_size, + perfmux->alist_size); } else { tegra_hwpm_err(hwpm, "IP %d" " perfmux %d NULL alist", @@ -110,8 +114,10 @@ int t234_hwpm_get_alist_size(struct tegra_soc_hwpm *hwpm) } if (perfmon->alist) { - hwpm->full_alist_size += - perfmon->alist_size; + hwpm->full_alist_size = + tegra_hwpm_safe_add_u64( + hwpm->full_alist_size, + perfmon->alist_size); } else { tegra_hwpm_err(hwpm, "IP %d" " perfmon %d NULL alist", @@ -144,7 +150,8 @@ static int t234_hwpm_copy_alist(struct tegra_soc_hwpm *hwpm, return -ENOMEM; } - full_alist[f_alist_idx++] = (aperture->start_abs_pa + + full_alist[f_alist_idx++] = tegra_hwpm_safe_add_u64( + aperture->start_abs_pa, aperture->alist[alist_idx].reg_offset); } @@ -264,7 +271,7 @@ bool t234_hwpm_check_alist(struct tegra_soc_hwpm *hwpm, return false; } - reg_offset = phys_addr - aperture->start_abs_pa; + reg_offset = tegra_hwpm_safe_sub_u64(phys_addr, aperture->start_abs_pa); for (alist_idx = 0; alist_idx < aperture->alist_size; alist_idx++) { if (reg_offset == aperture->alist[alist_idx].reg_offset) { diff --git a/hal/t234/t234_hwpm_interface_utils.c b/hal/t234/t234_hwpm_interface_utils.c index 09c2579..9f17af1 100644 --- a/hal/t234/t234_hwpm_interface_utils.c +++ b/hal/t234/t234_hwpm_interface_utils.c @@ -16,6 +16,7 @@ #include #include +#include #include #include @@ -65,7 +66,7 @@ struct tegra_soc_hwpm_chip t234_chip_info = { }; bool t234_hwpm_is_ip_active(struct tegra_soc_hwpm *hwpm, - enum tegra_soc_hwpm_ip ip_index, u32 *config_ip_index) + u32 ip_index, u32 *config_ip_index) { u32 config_ip = TEGRA_SOC_HWPM_IP_INACTIVE; @@ -161,7 +162,7 @@ bool t234_hwpm_is_ip_active(struct tegra_soc_hwpm *hwpm, } bool t234_hwpm_is_resource_active(struct tegra_soc_hwpm *hwpm, - enum tegra_soc_hwpm_resource res_index, u32 *config_ip_index) + u32 res_index, u32 *config_ip_index) { u32 config_ip = TEGRA_SOC_HWPM_IP_INACTIVE; @@ -275,10 +276,11 @@ static int t234_hwpm_init_ip_perfmux_apertures(struct tegra_soc_hwpm *hwpm, return 0; } - perfmux_address_range = chip_ip->perfmux_range_end - - chip_ip->perfmux_range_start + 1ULL; - chip_ip->num_perfmux_slots = - (u32) (perfmux_address_range / chip_ip->inst_perfmux_stride); + perfmux_address_range = tegra_hwpm_safe_add_u64( + tegra_hwpm_safe_sub_u64(chip_ip->perfmux_range_end, + chip_ip->perfmux_range_start), 1ULL); + chip_ip->num_perfmux_slots = tegra_hwpm_safe_cast_u64_to_u32( + perfmux_address_range / chip_ip->inst_perfmux_stride); chip_ip->ip_perfmux = kzalloc( sizeof(hwpm_ip_perfmux *) * chip_ip->num_perfmux_slots, @@ -299,11 +301,12 @@ static int t234_hwpm_init_ip_perfmux_apertures(struct tegra_soc_hwpm *hwpm, perfmux = &chip_ip->perfmux_static_array[perfmux_idx]; /* Compute perfmux offset from perfmux range start */ - perfmux_offset = - perfmux->start_abs_pa - chip_ip->perfmux_range_start; + perfmux_offset = tegra_hwpm_safe_sub_u64( + perfmux->start_abs_pa, chip_ip->perfmux_range_start); /* Compute perfmux slot index */ - idx = (u32)(perfmux_offset / chip_ip->inst_perfmux_stride); + idx = tegra_hwpm_safe_cast_u64_to_u32( + perfmux_offset / chip_ip->inst_perfmux_stride); /* Set perfmux slot pointer */ chip_ip->ip_perfmux[idx] = perfmux; @@ -325,10 +328,11 @@ static int t234_hwpm_init_ip_perfmon_apertures(struct tegra_soc_hwpm *hwpm, return 0; } - perfmon_address_range = chip_ip->perfmon_range_end - - chip_ip->perfmon_range_start + 1ULL; - chip_ip->num_perfmon_slots = - (u32) (perfmon_address_range / chip_ip->inst_perfmon_stride); + perfmon_address_range = tegra_hwpm_safe_add_u64( + tegra_hwpm_safe_sub_u64(chip_ip->perfmon_range_end, + chip_ip->perfmon_range_start), 1ULL); + chip_ip->num_perfmon_slots = tegra_hwpm_safe_cast_u64_to_u32( + perfmon_address_range / chip_ip->inst_perfmon_stride); chip_ip->ip_perfmon = kzalloc( sizeof(hwpm_ip_perfmon *) * chip_ip->num_perfmon_slots, @@ -349,11 +353,12 @@ static int t234_hwpm_init_ip_perfmon_apertures(struct tegra_soc_hwpm *hwpm, perfmon = &chip_ip->perfmon_static_array[perfmon_idx]; /* Compute perfmon offset from perfmon range start */ - perfmon_offset = - perfmon->start_abs_pa - chip_ip->perfmon_range_start; + perfmon_offset = tegra_hwpm_safe_sub_u64( + perfmon->start_abs_pa, chip_ip->perfmon_range_start); /* Compute perfmon slot index */ - idx = (u32)(perfmon_offset / chip_ip->inst_perfmon_stride); + idx = tegra_hwpm_safe_cast_u64_to_u32( + perfmon_offset / chip_ip->inst_perfmon_stride); /* Set perfmon slot pointer */ chip_ip->ip_perfmon[idx] = perfmon; diff --git a/hal/t234/t234_hwpm_internal.h b/hal/t234/t234_hwpm_internal.h index be6c2b5..99e7469 100644 --- a/hal/t234/t234_hwpm_internal.h +++ b/hal/t234/t234_hwpm_internal.h @@ -66,9 +66,9 @@ struct tegra_soc_hwpm; struct hwpm_ip_aperture; bool t234_hwpm_is_ip_active(struct tegra_soc_hwpm *hwpm, - enum tegra_soc_hwpm_ip ip_index, u32 *config_ip_index); + u32 ip_index, u32 *config_ip_index); bool t234_hwpm_is_resource_active(struct tegra_soc_hwpm *hwpm, - enum tegra_soc_hwpm_resource res_index, u32 *config_ip_index); + u32 res_index, u32 *config_ip_index); int t234_hwpm_extract_ip_ops(struct tegra_soc_hwpm *hwpm, struct tegra_soc_hwpm_ip_ops *hwpm_ip_ops, bool available); diff --git a/hal/t234/t234_hwpm_ip_utils.c b/hal/t234/t234_hwpm_ip_utils.c index 95f7213..90233cd 100644 --- a/hal/t234/t234_hwpm_ip_utils.c +++ b/hal/t234/t234_hwpm_ip_utils.c @@ -16,6 +16,7 @@ #include #include +#include #include #include @@ -115,8 +116,8 @@ static int t234_hwpm_update_ip_ops_info(struct tegra_soc_hwpm *hwpm, } /* Update IP ops info for all perfmux in the instance */ - max_num_perfmux = - chip_ip->num_instances * chip_ip->num_perfmux_per_inst; + max_num_perfmux = tegra_hwpm_safe_mult_u32( + chip_ip->num_instances, chip_ip->num_perfmux_per_inst); for (perfmux_idx = 0U; perfmux_idx < max_num_perfmux; perfmux_idx++) { perfmux = &chip_ip->perfmux_static_array[perfmux_idx]; @@ -235,8 +236,10 @@ static int t234_hwpm_find_ip_perfmux_index(struct tegra_soc_hwpm *hwpm, * Since all IP instances are configured to be in consecutive memory, * instance index can be found using instance physical address stride. */ - addr_offset = base_addr - chip_ip->perfmux_range_start; - perfmux_idx = (u32)(addr_offset / chip_ip->inst_perfmux_stride); + addr_offset = tegra_hwpm_safe_sub_u64( + base_addr, chip_ip->perfmux_range_start); + perfmux_idx = tegra_hwpm_safe_cast_u64_to_u32( + addr_offset / chip_ip->inst_perfmux_stride); /* Make sure instance index is valid */ if (perfmux_idx >= chip_ip->num_perfmux_slots) { diff --git a/hal/t234/t234_hwpm_regops_utils.c b/hal/t234/t234_hwpm_regops_utils.c index ca3c933..623a2fd 100644 --- a/hal/t234/t234_hwpm_regops_utils.c +++ b/hal/t234/t234_hwpm_regops_utils.c @@ -13,6 +13,7 @@ #include +#include #include #include #include @@ -36,8 +37,10 @@ static bool t234_hwpm_is_addr_in_ip_perfmon(struct tegra_soc_hwpm *hwpm, } /* Find perfmon idx corresponding phys addr */ - address_offset = phys_addr - chip_ip->perfmon_range_start; - perfmon_idx = (u32)(address_offset / chip_ip->inst_perfmon_stride); + address_offset = tegra_hwpm_safe_sub_u64( + phys_addr, chip_ip->perfmon_range_start); + perfmon_idx = tegra_hwpm_safe_cast_u64_to_u32( + address_offset / chip_ip->inst_perfmon_stride); perfmon = chip_ip->ip_perfmon[perfmon_idx]; @@ -108,8 +111,10 @@ static bool t234_hwpm_is_addr_in_ip_perfmux(struct tegra_soc_hwpm *hwpm, } /* Find perfmux idx corresponding phys addr */ - address_offset = phys_addr - chip_ip->perfmux_range_start; - perfmux_idx = (u32)(address_offset / chip_ip->inst_perfmux_stride); + address_offset = tegra_hwpm_safe_sub_u64( + phys_addr, chip_ip->perfmux_range_start); + perfmux_idx = tegra_hwpm_safe_cast_u64_to_u32( + address_offset / chip_ip->inst_perfmux_stride); perfmux = chip_ip->ip_perfmux[perfmux_idx]; @@ -214,6 +219,7 @@ int t234_hwpm_exec_reg_ops(struct tegra_soc_hwpm *hwpm, int ret = 0; u32 reg_val = 0U; u32 ip_idx = TEGRA_SOC_HWPM_IP_INACTIVE; /* ip_idx is unknown */ + u64 addr_hi = 0ULL; struct hwpm_ip_aperture *aperture = NULL; tegra_hwpm_fn(hwpm, " "); @@ -250,12 +256,13 @@ int t234_hwpm_exec_reg_ops(struct tegra_soc_hwpm *hwpm, break; case TEGRA_SOC_HWPM_REG_OP_CMD_RD64: + addr_hi = tegra_hwpm_safe_add_u64(reg_op->phys_addr, 4ULL); reg_op->reg_val_lo = regops_readl(hwpm, aperture, reg_op->phys_addr); reg_op->reg_val_hi = regops_readl(hwpm, aperture, - reg_op->phys_addr + 4ULL); + addr_hi); reg_op->status = TEGRA_SOC_HWPM_REG_OP_STATUS_SUCCESS; break; @@ -270,6 +277,8 @@ int t234_hwpm_exec_reg_ops(struct tegra_soc_hwpm *hwpm, /* Read Modify Write operation */ case TEGRA_SOC_HWPM_REG_OP_CMD_WR64: + addr_hi = tegra_hwpm_safe_add_u64(reg_op->phys_addr, 4ULL); + /* Lower 32 bits */ reg_val = regops_readl(hwpm, aperture, reg_op->phys_addr); reg_val = set_field(reg_val, reg_op->mask_lo, @@ -277,8 +286,7 @@ int t234_hwpm_exec_reg_ops(struct tegra_soc_hwpm *hwpm, regops_writel(hwpm, aperture, reg_op->phys_addr, reg_val); /* Upper 32 bits */ - reg_val = regops_readl(hwpm, aperture, - reg_op->phys_addr + 4ULL); + reg_val = regops_readl(hwpm, aperture, addr_hi); reg_val = set_field(reg_val, reg_op->mask_hi, reg_op->reg_val_hi); regops_writel(hwpm, aperture, reg_op->phys_addr, reg_val); diff --git a/hal/t234/t234_hwpm_resource_utils.c b/hal/t234/t234_hwpm_resource_utils.c index 6c62d50..00660f0 100644 --- a/hal/t234/t234_hwpm_resource_utils.c +++ b/hal/t234/t234_hwpm_resource_utils.c @@ -16,6 +16,7 @@ #include #include +#include #include #include #include @@ -76,11 +77,12 @@ static int t234_hwpm_perfmux_reserve(struct tegra_soc_hwpm *hwpm, /* Allocate fake registers */ if (hwpm->fake_registers_enabled) { - u64 num_regs = 0; + u64 address_range = tegra_hwpm_safe_add_u64( + tegra_hwpm_safe_sub_u64( + perfmux->end_pa, perfmux->start_pa), 1ULL); + u64 num_regs = address_range / sizeof(u32); u32 **fake_regs = &perfmux->fake_registers; - num_regs = (perfmux->end_pa + 1 - perfmux->start_pa) / - sizeof(u32); *fake_regs = (u32 *)kzalloc(sizeof(u32) * num_regs, GFP_KERNEL); if (!(*fake_regs)) { tegra_hwpm_err(hwpm, "Aperture(0x%llx - 0x%llx):" @@ -165,7 +167,9 @@ int t234_hwpm_perfmon_reserve(struct tegra_soc_hwpm *hwpm, perfmon->end_pa = res->end; if (hwpm->fake_registers_enabled) { - u64 num_regs = (res->end + 1 - res->start) / sizeof(u32); + u64 address_range = tegra_hwpm_safe_add_u64( + tegra_hwpm_safe_sub_u64(res->end, res->start), 1ULL); + u64 num_regs = address_range / sizeof(u32); perfmon->fake_registers = (u32 *)kzalloc(sizeof(u32) * num_regs, GFP_KERNEL); if (perfmon->fake_registers == NULL) { diff --git a/include/tegra_hwpm.h b/include/tegra_hwpm.h index f4452a5..086eff2 100644 --- a/include/tegra_hwpm.h +++ b/include/tegra_hwpm.h @@ -158,9 +158,9 @@ struct tegra_soc_hwpm_chip { /* Chip HALs */ bool (*is_ip_active)(struct tegra_soc_hwpm *hwpm, - enum tegra_soc_hwpm_ip ip_index, u32 *config_ip_index); + u32 ip_index, u32 *config_ip_index); bool (*is_resource_active)(struct tegra_soc_hwpm *hwpm, - enum tegra_soc_hwpm_resource res_index, u32 *config_ip_index); + u32 res_index, u32 *config_ip_index); int (*extract_ip_ops)(struct tegra_soc_hwpm *hwpm, struct tegra_soc_hwpm_ip_ops *hwpm_ip_ops, bool available); diff --git a/include/tegra_hwpm_static_analysis.h b/include/tegra_hwpm_static_analysis.h new file mode 100644 index 0000000..686461d --- /dev/null +++ b/include/tegra_hwpm_static_analysis.h @@ -0,0 +1,227 @@ +/* + * Copyright (c) 2021-2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#ifndef TEGRA_HWPM_STATIC_ANALYSIS_H +#define TEGRA_HWPM_STATIC_ANALYSIS_H + +#include +#include + +/** + * @brief Add two u32 values and check for overflow. + * + * @param ui_a [in] Addend value for adding. + * @param ui_b [in] Addend value for adding. + * + * Adds the two u32 (unsigned 32 bit) values unless the result will overflow a + * u32. Overflow will happen if the difference between UNIT_MAX (the max number + * representable with 32 bits) and one addend is less than the other addend. + * Call #BUG() in such a case else return the sum. + * + * @return If no overflow, sum of the two integers (\a ui_a + \a ui_b). + */ +static inline u32 tegra_hwpm_safe_add_u32(u32 ui_a, u32 ui_b) +{ + if ((UINT_MAX - ui_a) < ui_b) { + WARN_ON(true); + return 0U; + } else { + return ui_a + ui_b; + } +} + +/** + * @brief Add two u64 values and check for overflow. + * + * @param ul_a [in] Addend value for adding. + * @param ul_b [in] Addend value for adding. + * + * Adds the two u64 (unsigned 64 bit) values unless the result will overflow a + * u64. Overflow will happen if the difference between ULONG_MAX (the max number + * representable with 64 bits) and one addend is less than the other addend. + * Call #BUG() in such a case else return the sum. + * + * @return If no overflow, sum of the two integers (\a ul_a + \a ul_b). + */ +static inline u64 tegra_hwpm_safe_add_u64(u64 ul_a, u64 ul_b) +{ + if ((ULONG_MAX - ul_a) < ul_b) { + WARN_ON(true); + return 0U; + } else { + return ul_a + ul_b; + } +} + +/** + * @brief Subtract two u32 values and check for underflow. + * + * @param ui_a [in] Value of minuend. + * @param ui_b [in] Value of subtrahend. + * + * Subtracts \a ui_b from \a ui_a unless the result will underflow a u32 + * (unsigned 32 bit). If \a ui_a is less than \a ui_b then result would + * underflow so call #BUG(). + * + * @return If no overflow, difference of the two integers (\a ui_a - \a ui_b). + */ +static inline u32 tegra_hwpm_safe_sub_u32(u32 ui_a, u32 ui_b) +{ + if (ui_a < ui_b) { + WARN_ON(true); + return 0U; + } else { + return ui_a - ui_b; + } +} + +/** + * @brief Subtract two u64 values and check for underflow. + * + * @param ul_a [in] Value of minuend. + * @param ul_b [in] Value of subtrahend. + * + * Subtracts \a ul_b from \a ul_a unless the result will underflow a u64 + * (unsigned 64 bit). If \a ul_a is less than \a ul_b then result would + * underflow so call #BUG(). + * + * @return If no overflow, difference of the two integers (\a ul_a - \a ul_b). + */ +static inline u64 tegra_hwpm_safe_sub_u64(u64 ul_a, u64 ul_b) +{ + if (ul_a < ul_b) { + WARN_ON(true); + return 0U; + } else { + return ul_a - ul_b; + } +} + +/** + * @brief Multiply two u32 values and check for overflow. + * + * @param ui_a [in] Value of multiplicand. + * @param ui_b [in] Value of multiplier. + * + * Multiplies \a ui_a and \a ui_b unless the result will overflow a u32. If the + * result would overflow, call #BUG(). To determine whether the multiplication + * will cause an overflow, following steps are performed. + * - If \a ui_a or \a ui_b is 0 return 0 as multiplication result. + * - Else if \a ui_a is greater than UINT_MAX division by \a ui_b, + * multiplication will overflow so call #BUG(). + * - Else return result of multiplication. + * + * @return If no overflow, product of the two integers (\a ui_a * \a ui_b). + */ +static inline u32 tegra_hwpm_safe_mult_u32(u32 ui_a, u32 ui_b) +{ + if ((ui_a == 0U) || (ui_b == 0U)) { + return 0U; + } else if (ui_a > (UINT_MAX / ui_b)) { + WARN_ON(true); + return 0U; + } else { + return ui_a * ui_b; + } +} + +/** + * @brief Multiply two u64 values and check for overflow. + * + * @param ul_a [in] Value of multiplicand. + * @param ul_b [in] Value of multiplier. + * + * Multiplies \a ul_a and \a ul_b unless the result will overflow a u32. If the + * result would overflow, call #BUG(). To determine whether the multiplication + * will cause an overflow, following steps are performed. + * - If \a ul_a or \a ul_b is 0 return 0 as multiplication result. + * - Else if \a ul_a is greater than ULONG_MAX division by \a ul_b, + * multiplication will overflow so call #BUG(). + * - Else return result of multiplication. + * + * @return If no overflow, product of the two integers (\a ul_a * \a ul_b). + */ +static inline u64 tegra_hwpm_safe_mult_u64(u64 ul_a, u64 ul_b) +{ + if ((ul_a == 0UL) || (ul_b == 0UL)) { + return 0UL; + } else if (ul_a > (ULONG_MAX / ul_b)) { + WARN_ON(true); + return 0U; + } else { + return ul_a * ul_b; + } +} + +/** + * @brief Cast u64 to u32 and check for overflow. + * + * @param ul_a [in] Value to cast. + * + * Casts \a ul_a to a u32 unless the result will overflow a u32. If \a ul_a is + * greater than UINT_MAX which indicates overflow, call #BUG(). + * + * @return If no overflow, u32 representation of the value in \a ul_a. + */ +static inline u32 tegra_hwpm_safe_cast_u64_to_u32(u64 ul_a) +{ + if (ul_a > UINT_MAX) { + WARN_ON(true); + return 0U; + } else { + return (u32)ul_a; + } +} + +/** + * @brief Cast s32 to u64 and check for underflow. + * + * @param si_a [in] Value to cast. + * + * Casts \a si_a to a u64 unless the result will underflow a u64. If \a si_a is + * less than 0 which indicates underflow, call #BUG(). + * + * @return If no underflow, u64 representation of the value in \a si_a. + */ +static inline u64 tegra_hwpm_safe_cast_s32_to_u64(s32 si_a) +{ + if (si_a < 0) { + WARN_ON(true); + return 0U; + } else { + return (u64)si_a; + } +} + +/** + * @brief Cast u64 to s32 and check for overflow. + * + * @param ul_a [in] Value to cast. + * + * Casts \a ul_a to a s32 unless the result will overflow a s32. If \a ul_a is + * greater than INT_MAX (typecasted to u64) which indicates overflow, call + * #BUG(). + * + * @return If no overflow, s32 representation of the value in \a ul_a. + */ +static inline s32 tegra_hwpm_safe_cast_u64_to_s32(u64 ul_a) +{ + if (ul_a > tegra_hwpm_safe_cast_s32_to_u64(INT_MAX)) { + WARN_ON(true); + return 0; + } else { + return (s32)ul_a; + } +} + +#endif /* TEGRA_HWPM_STATIC_ANALYSIS_H */ diff --git a/os/linux/tegra_hwpm_io.c b/os/linux/tegra_hwpm_io.c index 4ee9cb0..608f7b1 100644 --- a/os/linux/tegra_hwpm_io.c +++ b/os/linux/tegra_hwpm_io.c @@ -20,6 +20,7 @@ #include #include +#include #include static u32 fake_readl(struct tegra_soc_hwpm *hwpm, @@ -79,12 +80,14 @@ static u32 ip_readl(struct tegra_soc_hwpm *hwpm, } else { /* Fall back to un-registered IP method */ void __iomem *ptr = NULL; + u64 reg_addr = tegra_hwpm_safe_add_u64( + aperture->start_abs_pa, offset); - ptr = ioremap(aperture->start_abs_pa + offset, 0x4); + ptr = ioremap(reg_addr, 0x4); if (!ptr) { tegra_hwpm_err(hwpm, "Failed to map register(0x%llx)", - aperture->start_abs_pa + offset); + reg_addr); return 0U; } reg_val = __raw_readl(ptr); @@ -125,12 +128,14 @@ static void ip_writel(struct tegra_soc_hwpm *hwpm, } else { /* Fall back to un-registered IP method */ void __iomem *ptr = NULL; + u64 reg_addr = tegra_hwpm_safe_add_u64( + aperture->start_abs_pa, offset); - ptr = ioremap(aperture->start_abs_pa + offset, 0x4); + ptr = ioremap(reg_addr, 0x4); if (!ptr) { tegra_hwpm_err(hwpm, "Failed to map register(0x%llx)", - aperture->start_abs_pa + offset); + reg_addr); return; } __raw_writel(val, ptr); @@ -144,10 +149,10 @@ static void ip_writel(struct tegra_soc_hwpm *hwpm, * PERFMONs, PMA and RTR registers fall in this category */ static u32 hwpm_readl(struct tegra_soc_hwpm *hwpm, - struct hwpm_ip_aperture *aperture, u32 offset) + struct hwpm_ip_aperture *aperture, u64 offset) { tegra_hwpm_dbg(hwpm, hwpm_register, - "Aperture (0x%llx-0x%llx) offset(0x%x)", + "Aperture (0x%llx-0x%llx) offset(0x%llx)", aperture->start_abs_pa, aperture->end_abs_pa, offset); if (aperture->dt_mmio == NULL) { @@ -167,10 +172,10 @@ static u32 hwpm_readl(struct tegra_soc_hwpm *hwpm, * PERFMONs, PMA and RTR registers fall in this category */ static void hwpm_writel(struct tegra_soc_hwpm *hwpm, - struct hwpm_ip_aperture *aperture, u32 offset, u32 val) + struct hwpm_ip_aperture *aperture, u64 offset, u32 val) { tegra_hwpm_dbg(hwpm, hwpm_register, - "Aperture (0x%llx-0x%llx) offset(0x%x) val(0x%x)", + "Aperture (0x%llx-0x%llx) offset(0x%llx) val(0x%x)", aperture->start_abs_pa, aperture->end_abs_pa, offset, val); if (aperture->dt_mmio == NULL) { @@ -196,15 +201,17 @@ u32 tegra_hwpm_readl(struct tegra_soc_hwpm *hwpm, if (!aperture) { tegra_hwpm_err(hwpm, "aperture is NULL"); - return -EINVAL; + return 0; } if (aperture->is_hwpm_element) { + u64 reg_offset = tegra_hwpm_safe_sub_u64( + addr, aperture->base_pa); /* HWPM domain registers */ - reg_val = hwpm_readl(hwpm, aperture, addr - aperture->base_pa); + reg_val = hwpm_readl(hwpm, aperture, reg_offset); } else { tegra_hwpm_err(hwpm, "IP aperture read is not expected"); - return -EINVAL; + return 0; } return reg_val; } @@ -222,8 +229,10 @@ void tegra_hwpm_writel(struct tegra_soc_hwpm *hwpm, } if (aperture->is_hwpm_element) { + u64 reg_offset = tegra_hwpm_safe_sub_u64( + addr, aperture->base_pa); /* HWPM domain internal registers */ - hwpm_writel(hwpm, aperture, addr - aperture->base_pa, val); + hwpm_writel(hwpm, aperture, reg_offset, val); } else { tegra_hwpm_err(hwpm, "IP aperture write is not expected"); return; @@ -238,19 +247,20 @@ u32 regops_readl(struct tegra_soc_hwpm *hwpm, struct hwpm_ip_aperture *aperture, u64 addr) { u32 reg_val = 0; + u64 reg_offset = 0ULL; if (!aperture) { tegra_hwpm_err(hwpm, "aperture is NULL"); return 0; } + reg_offset = tegra_hwpm_safe_sub_u64(addr, aperture->start_abs_pa); + if (aperture->is_hwpm_element) { /* HWPM unit internal registers */ - reg_val = hwpm_readl(hwpm, aperture, - addr - aperture->start_abs_pa); + reg_val = hwpm_readl(hwpm, aperture, reg_offset); } else { - reg_val = ip_readl(hwpm, aperture, - addr - aperture->start_abs_pa); + reg_val = ip_readl(hwpm, aperture, reg_offset); } return reg_val; } @@ -262,15 +272,19 @@ u32 regops_readl(struct tegra_soc_hwpm *hwpm, void regops_writel(struct tegra_soc_hwpm *hwpm, struct hwpm_ip_aperture *aperture, u64 addr, u32 val) { + u64 reg_offset = 0ULL; + if (!aperture) { tegra_hwpm_err(hwpm, "aperture is NULL"); return; } + reg_offset = tegra_hwpm_safe_sub_u64(addr, aperture->start_abs_pa); + if (aperture->is_hwpm_element) { /* HWPM unit internal registers */ - hwpm_writel(hwpm, aperture, addr - aperture->start_abs_pa, val); + hwpm_writel(hwpm, aperture, reg_offset, val); } else { - ip_writel(hwpm, aperture, addr - aperture->start_abs_pa, val); + ip_writel(hwpm, aperture, reg_offset, val); } }