diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/processpptables.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/processpptables.c index bfd8fbb0b49d..56926eec6820 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/processpptables.c +++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/processpptables.c @@ -47,26 +47,53 @@ #define NUM_BITS_CLOCK_INFO_ARRAY_INDEX 6 +static bool pp_table_has_space(struct pp_hwmgr *hwmgr, size_t offset, + size_t size) +{ + size_t table_size = hwmgr->soft_pp_table_size; + + return offset <= table_size && size <= table_size - offset; +} + +static const ATOM_PPLIB_EXTENDEDHEADER * +get_extended_header(struct pp_hwmgr *hwmgr, + const ATOM_PPLIB_POWERPLAYTABLE *powerplay_table, + size_t min_size) +{ + const ATOM_PPLIB_POWERPLAYTABLE3 *powerplay_table3; + const ATOM_PPLIB_EXTENDEDHEADER *extended_header; + u16 offset; + + if (le16_to_cpu(powerplay_table->usTableSize) < + sizeof(ATOM_PPLIB_POWERPLAYTABLE3) || + !pp_table_has_space(hwmgr, 0, sizeof(ATOM_PPLIB_POWERPLAYTABLE3))) + return NULL; + + powerplay_table3 = (const ATOM_PPLIB_POWERPLAYTABLE3 *)powerplay_table; + offset = le16_to_cpu(powerplay_table3->usExtendendedHeaderOffset); + if (!offset || !pp_table_has_space(hwmgr, offset, + sizeof(extended_header->usSize))) + return NULL; + + extended_header = (const ATOM_PPLIB_EXTENDEDHEADER *) + (((unsigned long)powerplay_table) + offset); + if (le16_to_cpu(extended_header->usSize) < min_size || + !pp_table_has_space(hwmgr, offset, min_size)) + return NULL; + + return extended_header; +} + static uint16_t get_vce_table_offset(struct pp_hwmgr *hwmgr, const ATOM_PPLIB_POWERPLAYTABLE *powerplay_table) { uint16_t vce_table_offset = 0; + const ATOM_PPLIB_EXTENDEDHEADER *extended_header; - if (le16_to_cpu(powerplay_table->usTableSize) >= - sizeof(ATOM_PPLIB_POWERPLAYTABLE3)) { - const ATOM_PPLIB_POWERPLAYTABLE3 *powerplay_table3 = - (const ATOM_PPLIB_POWERPLAYTABLE3 *)powerplay_table; - - if (powerplay_table3->usExtendendedHeaderOffset > 0) { - const ATOM_PPLIB_EXTENDEDHEADER *extended_header = - (const ATOM_PPLIB_EXTENDEDHEADER *) - (((unsigned long)powerplay_table3) + - le16_to_cpu(powerplay_table3->usExtendendedHeaderOffset)); - if (le16_to_cpu(extended_header->usSize) >= - SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V2) - vce_table_offset = le16_to_cpu(extended_header->usVCETableOffset); - } - } + extended_header = get_extended_header(hwmgr, powerplay_table, + SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V2); + if (extended_header) + vce_table_offset = le16_to_cpu(extended_header->usVCETableOffset); return vce_table_offset; } @@ -93,7 +120,14 @@ static uint16_t get_vce_clock_info_array_size(struct pp_hwmgr *hwmgr, if (table_offset > 0) { const VCEClockInfoArray *p = (const VCEClockInfoArray *) (((unsigned long) powerplay_table) + table_offset); - table_size = sizeof(uint8_t) + p->ucNumEntries * sizeof(VCEClockInfo); + size_t size; + + if (!pp_table_has_space(hwmgr, table_offset, sizeof(p->ucNumEntries))) + return 0; + + size = sizeof(uint8_t) + p->ucNumEntries * sizeof(VCEClockInfo); + if (pp_table_has_space(hwmgr, table_offset, size)) + table_size = size; } return table_size; @@ -104,10 +138,13 @@ static uint16_t get_vce_clock_voltage_limit_table_offset(struct pp_hwmgr *hwmgr, { uint16_t table_offset = get_vce_clock_info_array_offset(hwmgr, powerplay_table); + u16 table_size; - if (table_offset > 0) - return table_offset + get_vce_clock_info_array_size(hwmgr, - powerplay_table); + if (table_offset > 0) { + table_size = get_vce_clock_info_array_size(hwmgr, powerplay_table); + if (table_size) + return table_offset + table_size; + } return 0; } @@ -121,8 +158,15 @@ static uint16_t get_vce_clock_voltage_limit_table_size(struct pp_hwmgr *hwmgr, if (table_offset > 0) { const ATOM_PPLIB_VCE_Clock_Voltage_Limit_Table *ptable = (const ATOM_PPLIB_VCE_Clock_Voltage_Limit_Table *)(((unsigned long) powerplay_table) + table_offset); + size_t size; - table_size = sizeof(uint8_t) + ptable->numEntries * sizeof(ATOM_PPLIB_VCE_Clock_Voltage_Limit_Record); + if (!pp_table_has_space(hwmgr, table_offset, sizeof(ptable->numEntries))) + return 0; + + size = sizeof(uint8_t) + + ptable->numEntries * sizeof(ATOM_PPLIB_VCE_Clock_Voltage_Limit_Record); + if (pp_table_has_space(hwmgr, table_offset, size)) + table_size = size; } return table_size; } @@ -130,9 +174,13 @@ static uint16_t get_vce_clock_voltage_limit_table_size(struct pp_hwmgr *hwmgr, static uint16_t get_vce_state_table_offset(struct pp_hwmgr *hwmgr, const ATOM_PPLIB_POWERPLAYTABLE *powerplay_table) { uint16_t table_offset = get_vce_clock_voltage_limit_table_offset(hwmgr, powerplay_table); + u16 table_size; - if (table_offset > 0) - return table_offset + get_vce_clock_voltage_limit_table_size(hwmgr, powerplay_table); + if (table_offset > 0) { + table_size = get_vce_clock_voltage_limit_table_size(hwmgr, powerplay_table); + if (table_size) + return table_offset + table_size; + } return 0; } @@ -143,8 +191,12 @@ static const ATOM_PPLIB_VCE_State_Table *get_vce_state_table( { uint16_t table_offset = get_vce_state_table_offset(hwmgr, powerplay_table); - if (table_offset > 0) - return (const ATOM_PPLIB_VCE_State_Table *)(((unsigned long) powerplay_table) + table_offset); + if (table_offset > 0) { + if (pp_table_has_space(hwmgr, table_offset, + sizeof(((ATOM_PPLIB_VCE_State_Table *)0)->numEntries))) + return (const ATOM_PPLIB_VCE_State_Table *) + (((unsigned long)powerplay_table) + table_offset); + } return NULL; } @@ -153,21 +205,13 @@ static uint16_t get_uvd_table_offset(struct pp_hwmgr *hwmgr, const ATOM_PPLIB_POWERPLAYTABLE *powerplay_table) { uint16_t uvd_table_offset = 0; + const ATOM_PPLIB_EXTENDEDHEADER *extended_header; + + extended_header = get_extended_header(hwmgr, powerplay_table, + SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V3); + if (extended_header) + uvd_table_offset = le16_to_cpu(extended_header->usUVDTableOffset); - if (le16_to_cpu(powerplay_table->usTableSize) >= - sizeof(ATOM_PPLIB_POWERPLAYTABLE3)) { - const ATOM_PPLIB_POWERPLAYTABLE3 *powerplay_table3 = - (const ATOM_PPLIB_POWERPLAYTABLE3 *)powerplay_table; - if (powerplay_table3->usExtendendedHeaderOffset > 0) { - const ATOM_PPLIB_EXTENDEDHEADER *extended_header = - (const ATOM_PPLIB_EXTENDEDHEADER *) - (((unsigned long)powerplay_table3) + - le16_to_cpu(powerplay_table3->usExtendendedHeaderOffset)); - if (le16_to_cpu(extended_header->usSize) >= - SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V3) - uvd_table_offset = le16_to_cpu(extended_header->usUVDTableOffset); - } - } return uvd_table_offset; } @@ -193,8 +237,14 @@ static uint16_t get_uvd_clock_info_array_size(struct pp_hwmgr *hwmgr, const UVDClockInfoArray *p = (const UVDClockInfoArray *) (((unsigned long) powerplay_table) + table_offset); - table_size = sizeof(UCHAR) + - p->ucNumEntries * sizeof(UVDClockInfo); + size_t size; + + if (!pp_table_has_space(hwmgr, table_offset, sizeof(p->ucNumEntries))) + return 0; + + size = sizeof(UCHAR) + p->ucNumEntries * sizeof(UVDClockInfo); + if (pp_table_has_space(hwmgr, table_offset, size)) + table_size = size; } return table_size; @@ -206,10 +256,13 @@ static uint16_t get_uvd_clock_voltage_limit_table_offset( { uint16_t table_offset = get_uvd_clock_info_array_offset(hwmgr, powerplay_table); + u16 table_size; - if (table_offset > 0) - return table_offset + - get_uvd_clock_info_array_size(hwmgr, powerplay_table); + if (table_offset > 0) { + table_size = get_uvd_clock_info_array_size(hwmgr, powerplay_table); + if (table_size) + return table_offset + table_size; + } return 0; } @@ -218,21 +271,12 @@ static uint16_t get_samu_table_offset(struct pp_hwmgr *hwmgr, const ATOM_PPLIB_POWERPLAYTABLE *powerplay_table) { uint16_t samu_table_offset = 0; + const ATOM_PPLIB_EXTENDEDHEADER *extended_header; - if (le16_to_cpu(powerplay_table->usTableSize) >= - sizeof(ATOM_PPLIB_POWERPLAYTABLE3)) { - const ATOM_PPLIB_POWERPLAYTABLE3 *powerplay_table3 = - (const ATOM_PPLIB_POWERPLAYTABLE3 *)powerplay_table; - if (powerplay_table3->usExtendendedHeaderOffset > 0) { - const ATOM_PPLIB_EXTENDEDHEADER *extended_header = - (const ATOM_PPLIB_EXTENDEDHEADER *) - (((unsigned long)powerplay_table3) + - le16_to_cpu(powerplay_table3->usExtendendedHeaderOffset)); - if (le16_to_cpu(extended_header->usSize) >= - SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V4) - samu_table_offset = le16_to_cpu(extended_header->usSAMUTableOffset); - } - } + extended_header = get_extended_header(hwmgr, powerplay_table, + SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V4); + if (extended_header) + samu_table_offset = le16_to_cpu(extended_header->usSAMUTableOffset); return samu_table_offset; } @@ -254,21 +298,12 @@ static uint16_t get_acp_table_offset(struct pp_hwmgr *hwmgr, const ATOM_PPLIB_POWERPLAYTABLE *powerplay_table) { uint16_t acp_table_offset = 0; + const ATOM_PPLIB_EXTENDEDHEADER *extended_header; - if (le16_to_cpu(powerplay_table->usTableSize) >= - sizeof(ATOM_PPLIB_POWERPLAYTABLE3)) { - const ATOM_PPLIB_POWERPLAYTABLE3 *powerplay_table3 = - (const ATOM_PPLIB_POWERPLAYTABLE3 *)powerplay_table; - if (powerplay_table3->usExtendendedHeaderOffset > 0) { - const ATOM_PPLIB_EXTENDEDHEADER *pExtendedHeader = - (const ATOM_PPLIB_EXTENDEDHEADER *) - (((unsigned long)powerplay_table3) + - le16_to_cpu(powerplay_table3->usExtendendedHeaderOffset)); - if (le16_to_cpu(pExtendedHeader->usSize) >= - SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V6) - acp_table_offset = le16_to_cpu(pExtendedHeader->usACPTableOffset); - } - } + extended_header = get_extended_header(hwmgr, powerplay_table, + SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V6); + if (extended_header) + acp_table_offset = le16_to_cpu(extended_header->usACPTableOffset); return acp_table_offset; } @@ -290,21 +325,12 @@ static uint16_t get_cacp_tdp_table_offset( const ATOM_PPLIB_POWERPLAYTABLE *powerplay_table) { uint16_t cacTdpTableOffset = 0; + const ATOM_PPLIB_EXTENDEDHEADER *extended_header; - if (le16_to_cpu(powerplay_table->usTableSize) >= - sizeof(ATOM_PPLIB_POWERPLAYTABLE3)) { - const ATOM_PPLIB_POWERPLAYTABLE3 *powerplay_table3 = - (const ATOM_PPLIB_POWERPLAYTABLE3 *)powerplay_table; - if (powerplay_table3->usExtendendedHeaderOffset > 0) { - const ATOM_PPLIB_EXTENDEDHEADER *pExtendedHeader = - (const ATOM_PPLIB_EXTENDEDHEADER *) - (((unsigned long)powerplay_table3) + - le16_to_cpu(powerplay_table3->usExtendendedHeaderOffset)); - if (le16_to_cpu(pExtendedHeader->usSize) >= - SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V7) - cacTdpTableOffset = le16_to_cpu(pExtendedHeader->usPowerTuneTableOffset); - } - } + extended_header = get_extended_header(hwmgr, powerplay_table, + SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V7); + if (extended_header) + cacTdpTableOffset = le16_to_cpu(extended_header->usPowerTuneTableOffset); return cacTdpTableOffset; } @@ -341,22 +367,13 @@ static uint16_t get_sclk_vdd_gfx_table_offset(struct pp_hwmgr *hwmgr, const ATOM_PPLIB_POWERPLAYTABLE *powerplay_table) { uint16_t sclk_vdd_gfx_table_offset = 0; + const ATOM_PPLIB_EXTENDEDHEADER *extended_header; - if (le16_to_cpu(powerplay_table->usTableSize) >= - sizeof(ATOM_PPLIB_POWERPLAYTABLE3)) { - const ATOM_PPLIB_POWERPLAYTABLE3 *powerplay_table3 = - (const ATOM_PPLIB_POWERPLAYTABLE3 *)powerplay_table; - if (powerplay_table3->usExtendendedHeaderOffset > 0) { - const ATOM_PPLIB_EXTENDEDHEADER *pExtendedHeader = - (const ATOM_PPLIB_EXTENDEDHEADER *) - (((unsigned long)powerplay_table3) + - le16_to_cpu(powerplay_table3->usExtendendedHeaderOffset)); - if (le16_to_cpu(pExtendedHeader->usSize) >= - SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V8) - sclk_vdd_gfx_table_offset = - le16_to_cpu(pExtendedHeader->usSclkVddgfxTableOffset); - } - } + extended_header = get_extended_header(hwmgr, powerplay_table, + SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V8); + if (extended_header) + sclk_vdd_gfx_table_offset = + le16_to_cpu(extended_header->usSclkVddgfxTableOffset); return sclk_vdd_gfx_table_offset; } @@ -769,20 +786,37 @@ static ULONG size_of_entry_v2(ULONG num_dpm_levels) } static const ATOM_PPLIB_STATE_V2 *get_state_entry_v2( + struct pp_hwmgr *hwmgr, const StateArray * pstate_arrays, + u16 state_array_offset, ULONG entry_index) { ULONG i; const ATOM_PPLIB_STATE_V2 *pstate; + size_t entry_offset; + size_t entry_size; + if (entry_index >= pstate_arrays->ucNumEntries) + return NULL; + + entry_offset = state_array_offset + sizeof(pstate_arrays->ucNumEntries); pstate = pstate_arrays->states; - if (entry_index <= pstate_arrays->ucNumEntries) { - for (i = 0; i < entry_index; i++) - pstate = (ATOM_PPLIB_STATE_V2 *)( - (unsigned long)pstate + - size_of_entry_v2(pstate->ucNumDPMLevels)); + for (i = 0; i <= entry_index; i++) { + if (!pp_table_has_space(hwmgr, entry_offset, sizeof(*pstate))) + return NULL; + + entry_size = size_of_entry_v2(pstate->ucNumDPMLevels); + if (!pp_table_has_space(hwmgr, entry_offset, entry_size)) + return NULL; + + if (i == entry_index) + return pstate; + + entry_offset += entry_size; + pstate = (ATOM_PPLIB_STATE_V2 *)((unsigned long)pstate + entry_size); } - return pstate; + + return NULL; } static const unsigned char soft_dummy_pp_table[] = { @@ -850,6 +884,8 @@ int pp_tables_get_response_times(struct pp_hwmgr *hwmgr, PP_ASSERT_WITH_CODE(NULL != powerplay_tab, "Missing PowerPlay Table!", return -EINVAL); + PP_ASSERT_WITH_CODE(pp_table_has_space(hwmgr, 0, sizeof(*powerplay_tab)), + "Invalid PowerPlay Table!", return -EINVAL); *vol_rep_time = (uint32_t)le16_to_cpu(powerplay_tab->usVoltageTime); *bb_rep_time = (uint32_t)le16_to_cpu(powerplay_tab->usBackbiasTime); @@ -862,13 +898,21 @@ int pp_tables_get_num_of_entries(struct pp_hwmgr *hwmgr, { const StateArray *pstate_arrays; const ATOM_PPLIB_POWERPLAYTABLE *powerplay_table = get_powerplay_table(hwmgr); + u16 state_array_offset; if (powerplay_table == NULL) return -1; + if (!pp_table_has_space(hwmgr, 0, sizeof(*powerplay_table))) + return -1; if (powerplay_table->sHeader.ucTableFormatRevision >= 6) { + state_array_offset = le16_to_cpu(powerplay_table->usStateArrayOffset); + if (!pp_table_has_space(hwmgr, state_array_offset, + sizeof(pstate_arrays->ucNumEntries))) + return -1; + pstate_arrays = (StateArray *)(((unsigned long)powerplay_table) + - le16_to_cpu(powerplay_table->usStateArrayOffset)); + state_array_offset); *num_of_entries = (unsigned long)(pstate_arrays->ucNumEntries); } else @@ -895,49 +939,128 @@ int pp_tables_get_entry(struct pp_hwmgr *hwmgr, const NonClockInfoArray *pnon_clock_arrays; const ATOM_PPLIB_STATE *pstate_entry; + u16 state_array_offset; + u16 clock_info_array_offset; + u16 non_clock_info_array_offset; + size_t clock_info_offset; + size_t non_clock_info_offset; + size_t state_entry_offset; if (powerplay_table == NULL) return -1; + if (!pp_table_has_space(hwmgr, 0, sizeof(*powerplay_table))) + return -1; ps->classification.bios_index = entry_index; if (powerplay_table->sHeader.ucTableFormatRevision >= 6) { - pstate_arrays = (StateArray *)(((unsigned long)powerplay_table) + - le16_to_cpu(powerplay_table->usStateArrayOffset)); - - if (entry_index > pstate_arrays->ucNumEntries) + state_array_offset = le16_to_cpu(powerplay_table->usStateArrayOffset); + if (!pp_table_has_space(hwmgr, state_array_offset, + sizeof(pstate_arrays->ucNumEntries))) + return -1; + + pstate_arrays = (StateArray *)(((unsigned long)powerplay_table) + + state_array_offset); + + if (entry_index >= pstate_arrays->ucNumEntries) + return -1; + + pstate_entry_v2 = get_state_entry_v2(hwmgr, pstate_arrays, + state_array_offset, + entry_index); + if (!pstate_entry_v2) + return -1; + + clock_info_array_offset = + le16_to_cpu(powerplay_table->usClockInfoArrayOffset); + if (!pp_table_has_space(hwmgr, clock_info_array_offset, + sizeof(*pclock_arrays))) return -1; - pstate_entry_v2 = get_state_entry_v2(pstate_arrays, entry_index); pclock_arrays = (ClockInfoArray *)(((unsigned long)powerplay_table) + - le16_to_cpu(powerplay_table->usClockInfoArrayOffset)); + clock_info_array_offset); + if (!pclock_arrays->ucEntrySize) + return -1; + + non_clock_info_array_offset = + le16_to_cpu(powerplay_table->usNonClockInfoArrayOffset); + if (!pp_table_has_space(hwmgr, non_clock_info_array_offset, + sizeof(*pnon_clock_arrays))) + return -1; pnon_clock_arrays = (NonClockInfoArray *)(((unsigned long)powerplay_table) + - le16_to_cpu(powerplay_table->usNonClockInfoArrayOffset)); + non_clock_info_array_offset); + if (!pnon_clock_arrays->ucEntrySize || + pnon_clock_arrays->ucEntrySize < ATOM_PPLIB_NONCLOCKINFO_VER1 || + (pnon_clock_arrays->ucEntrySize > ATOM_PPLIB_NONCLOCKINFO_VER1 && + pnon_clock_arrays->ucEntrySize < ATOM_PPLIB_NONCLOCKINFO_VER2) || + pstate_entry_v2->nonClockInfoIndex >= pnon_clock_arrays->ucNumEntries) + return -1; + non_clock_info_offset = non_clock_info_array_offset + + offsetof(NonClockInfoArray, nonClockInfo) + + pstate_entry_v2->nonClockInfoIndex * pnon_clock_arrays->ucEntrySize; + if (!pp_table_has_space(hwmgr, non_clock_info_offset, + pnon_clock_arrays->ucEntrySize)) + return -1; pnon_clock_info = (ATOM_PPLIB_NONCLOCK_INFO *)((unsigned long)(pnon_clock_arrays->nonClockInfo) + (pstate_entry_v2->nonClockInfoIndex * pnon_clock_arrays->ucEntrySize)); result = init_non_clock_fields(hwmgr, ps, pnon_clock_arrays->ucEntrySize, pnon_clock_info); for (i = 0; i < pstate_entry_v2->ucNumDPMLevels; i++) { - const void *pclock_info = (const void *)( - (unsigned long)(pclock_arrays->clockInfo) + - (pstate_entry_v2->clockInfoIndex[i] * pclock_arrays->ucEntrySize)); + const void *pclock_info; + + if (pstate_entry_v2->clockInfoIndex[i] >= + pclock_arrays->ucNumEntries) + return -1; + + clock_info_offset = clock_info_array_offset + + offsetof(ClockInfoArray, clockInfo) + + pstate_entry_v2->clockInfoIndex[i] * pclock_arrays->ucEntrySize; + if (!pp_table_has_space(hwmgr, clock_info_offset, + pclock_arrays->ucEntrySize)) + return -1; + + pclock_info = (const void *) + ((unsigned long)(pclock_arrays->clockInfo) + + (pstate_entry_v2->clockInfoIndex[i] * + pclock_arrays->ucEntrySize)); res = func(hwmgr, &ps->hardware, i, pclock_info); if ((0 == result) && (0 != res)) result = res; } } else { - if (entry_index > powerplay_table->ucNumStates) + if (entry_index >= powerplay_table->ucNumStates || + !powerplay_table->ucStateEntrySize || + !powerplay_table->ucNonClockSize || + powerplay_table->ucNonClockSize < ATOM_PPLIB_NONCLOCKINFO_VER1 || + (powerplay_table->ucNonClockSize > ATOM_PPLIB_NONCLOCKINFO_VER1 && + powerplay_table->ucNonClockSize < ATOM_PPLIB_NONCLOCKINFO_VER2) || + !powerplay_table->ucClockInfoSize) + return -1; + + state_array_offset = le16_to_cpu(powerplay_table->usStateArrayOffset); + state_entry_offset = state_array_offset + + entry_index * powerplay_table->ucStateEntrySize; + if (!pp_table_has_space(hwmgr, state_entry_offset, + powerplay_table->ucStateEntrySize)) return -1; pstate_entry = (ATOM_PPLIB_STATE *)((unsigned long)powerplay_table + - le16_to_cpu(powerplay_table->usStateArrayOffset) + + state_array_offset + entry_index * powerplay_table->ucStateEntrySize); + non_clock_info_array_offset = + le16_to_cpu(powerplay_table->usNonClockInfoArrayOffset); + non_clock_info_offset = non_clock_info_array_offset + + pstate_entry->ucNonClockStateIndex * powerplay_table->ucNonClockSize; + if (!pp_table_has_space(hwmgr, non_clock_info_offset, + powerplay_table->ucNonClockSize)) + return -1; + pnon_clock_info = (ATOM_PPLIB_NONCLOCK_INFO *)((unsigned long)powerplay_table + - le16_to_cpu(powerplay_table->usNonClockInfoArrayOffset) + + non_clock_info_array_offset + pstate_entry->ucNonClockStateIndex * powerplay_table->ucNonClockSize); @@ -946,12 +1069,23 @@ int pp_tables_get_entry(struct pp_hwmgr *hwmgr, pnon_clock_info); for (i = 0; i < powerplay_table->ucStateEntrySize-1; i++) { - const void *pclock_info = (const void *)((unsigned long)powerplay_table + - le16_to_cpu(powerplay_table->usClockInfoArrayOffset) + + const void *pclock_info; + + clock_info_array_offset = + le16_to_cpu(powerplay_table->usClockInfoArrayOffset); + clock_info_offset = clock_info_array_offset + + pstate_entry->ucClockStateIndices[i] * + powerplay_table->ucClockInfoSize; + if (!pp_table_has_space(hwmgr, clock_info_offset, + powerplay_table->ucClockInfoSize)) + return -1; + + pclock_info = (const void *)((unsigned long)powerplay_table + + clock_info_array_offset + pstate_entry->ucClockStateIndices[i] * powerplay_table->ucClockInfoSize); - int res = func(hwmgr, &ps->hardware, i, pclock_info); + res = func(hwmgr, &ps->hardware, i, pclock_info); if ((0 == result) && (0 != res)) result = res; @@ -1661,21 +1795,70 @@ static int get_vce_state_table_entry(struct pp_hwmgr *hwmgr, unsigned long *flag) { const ATOM_PPLIB_POWERPLAYTABLE *powerplay_table = get_powerplay_table(hwmgr); + const ATOM_PPLIB_VCE_State_Table *vce_state_table; + const ATOM_PPLIB_VCE_State_Record *record; + const VCEClockInfoArray *vce_clock_info_array; + const VCEClockInfo *vce_clock_info; + const ClockInfoArray *clock_arrays; + u16 vce_state_table_offset; + u16 vce_clock_info_array_offset; + u16 clock_info_array_offset; + unsigned long clockInfoIndex; + size_t record_offset; + size_t vce_clock_info_offset; + size_t clock_info_offset; - const ATOM_PPLIB_VCE_State_Table *vce_state_table = get_vce_state_table(hwmgr, powerplay_table); + if (!powerplay_table || !pp_table_has_space(hwmgr, 0, sizeof(*powerplay_table))) + return -1; - unsigned short vce_clock_info_array_offset = get_vce_clock_info_array_offset(hwmgr, powerplay_table); + vce_state_table_offset = get_vce_state_table_offset(hwmgr, powerplay_table); + vce_state_table = get_vce_state_table(hwmgr, powerplay_table); + if (!vce_state_table || i >= vce_state_table->numEntries) + return -1; - const VCEClockInfoArray *vce_clock_info_array = (const VCEClockInfoArray *)(((unsigned long) powerplay_table) + vce_clock_info_array_offset); + record_offset = vce_state_table_offset + + offsetof(ATOM_PPLIB_VCE_State_Table, entries) + + i * sizeof(*record); + if (!pp_table_has_space(hwmgr, record_offset, sizeof(*record))) + return -1; - const ClockInfoArray *clock_arrays = (ClockInfoArray *)(((unsigned long)powerplay_table) + - le16_to_cpu(powerplay_table->usClockInfoArrayOffset)); + record = &vce_state_table->entries[i]; - const ATOM_PPLIB_VCE_State_Record *record = &vce_state_table->entries[i]; + vce_clock_info_array_offset = + get_vce_clock_info_array_offset(hwmgr, powerplay_table); + if (!pp_table_has_space(hwmgr, vce_clock_info_array_offset, + sizeof(vce_clock_info_array->ucNumEntries))) + return -1; - const VCEClockInfo *vce_clock_info = &vce_clock_info_array->entries[record->ucVCEClockInfoIndex]; + vce_clock_info_array = (const VCEClockInfoArray *) + (((unsigned long)powerplay_table) + vce_clock_info_array_offset); + if (record->ucVCEClockInfoIndex >= vce_clock_info_array->ucNumEntries) + return -1; - unsigned long clockInfoIndex = record->ucClockInfoIndex & 0x3F; + vce_clock_info_offset = vce_clock_info_array_offset + + offsetof(VCEClockInfoArray, entries) + + record->ucVCEClockInfoIndex * sizeof(*vce_clock_info); + if (!pp_table_has_space(hwmgr, vce_clock_info_offset, sizeof(*vce_clock_info))) + return -1; + + vce_clock_info = &vce_clock_info_array->entries[record->ucVCEClockInfoIndex]; + + clock_info_array_offset = le16_to_cpu(powerplay_table->usClockInfoArrayOffset); + if (!pp_table_has_space(hwmgr, clock_info_array_offset, + sizeof(*clock_arrays))) + return -1; + + clock_arrays = (ClockInfoArray *)(((unsigned long)powerplay_table) + + clock_info_array_offset); + clockInfoIndex = record->ucClockInfoIndex & 0x3F; + if (!clock_arrays->ucEntrySize || clockInfoIndex >= clock_arrays->ucNumEntries) + return -1; + + clock_info_offset = clock_info_array_offset + + offsetof(ClockInfoArray, clockInfo) + + clockInfoIndex * clock_arrays->ucEntrySize; + if (!pp_table_has_space(hwmgr, clock_info_offset, clock_arrays->ucEntrySize)) + return -1; *flag = (record->ucClockInfoIndex >> NUM_BITS_CLOCK_INFO_ARRAY_INDEX); @@ -1699,6 +1882,10 @@ static int pp_tables_initialize(struct pp_hwmgr *hwmgr) hwmgr->need_pp_table_upload = true; powerplay_table = get_powerplay_table(hwmgr); + PP_ASSERT_WITH_CODE((powerplay_table), + "Missing PowerPlay Table!", return -1); + PP_ASSERT_WITH_CODE(pp_table_has_space(hwmgr, 0, sizeof(*powerplay_table)), + "Invalid PowerPlay Table!", return -1); result = init_powerplay_tables(hwmgr, powerplay_table); @@ -1801,4 +1988,3 @@ const struct pp_table_func pptable_funcs = { .pptable_get_vce_state_table_entry = get_vce_state_table_entry, }; -