--- a/sys/dev/drm/amd/powerplay/hwmgr/processpptables.c +++ b/sys/dev/drm/amd/powerplay/hwmgr/processpptables.c @@ -45,6 +45,50 @@ #define NUM_BITS_CLOCK_INFO_ARRAY_INDEX 6 +/* + * Central bounds-validation layer for VBIOS PowerPlay table parsing. + * + * Every firmware-supplied offset, index and entry count must be proven + * against the actual size of the PowerPlay table image before it is + * dereferenced. All offsets below are relative to the start of the + * table image (hwmgr->soft_pp_table). + */ +static bool pp_table_range_ok(const struct pp_hwmgr *hwmgr, uint32_t off, + uint32_t len) +{ + if (hwmgr->soft_pp_table == NULL || hwmgr->soft_pp_table_size == 0) + return false; + if (off >= hwmgr->soft_pp_table_size) + return false; + return len <= (hwmgr->soft_pp_table_size - off); +} + +static uint32_t pp_table_offset_of(const struct pp_hwmgr *hwmgr, + const void *p) +{ + return (uint32_t)((const uint8_t *)p - + (const uint8_t *)hwmgr->soft_pp_table); +} + +/* + * Clamp a firmware entry count to the number of entries that actually + * fit in the table image. off points at the array header (1 byte for + * ucNumEntries/numEntries), entry_size is the size of one entry. + */ +static uint8_t pp_table_clamp_count(const struct pp_hwmgr *hwmgr, + uint32_t off, uint8_t num, + uint32_t entry_size) +{ + uint64_t avail; + + if (!pp_table_range_ok(hwmgr, off, 1 + entry_size)) + return 0; + avail = ((uint64_t)hwmgr->soft_pp_table_size - off - 1) / entry_size; + if (num > avail) + num = (avail > 255) ? 255 : (uint8_t)avail; + return num; +} + static uint16_t get_vce_table_offset(struct pp_hwmgr *hwmgr, const ATOM_PPLIB_POWERPLAYTABLE *powerplay_table) { @@ -91,7 +135,10 @@ if (table_offset > 0) { const VCEClockInfoArray *p = (const VCEClockInfoArray *) (((unsigned long) powerplay_table) + table_offset); - table_size = sizeof(uint8_t) + p->ucNumEntries * sizeof(VCEClockInfo); + uint8_t num = pp_table_clamp_count(hwmgr, table_offset, + p->ucNumEntries, sizeof(VCEClockInfo)); + + table_size = sizeof(uint8_t) + num * sizeof(VCEClockInfo); } return table_size; @@ -119,8 +166,11 @@ 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); + uint8_t num = pp_table_clamp_count(hwmgr, table_offset, + ptable->numEntries, + sizeof(ATOM_PPLIB_VCE_Clock_Voltage_Limit_Record)); - table_size = sizeof(uint8_t) + ptable->numEntries * sizeof(ATOM_PPLIB_VCE_Clock_Voltage_Limit_Record); + table_size = sizeof(uint8_t) + num * sizeof(ATOM_PPLIB_VCE_Clock_Voltage_Limit_Record); } return table_size; } @@ -191,8 +241,10 @@ const UVDClockInfoArray *p = (const UVDClockInfoArray *) (((unsigned long) powerplay_table) + table_offset); - table_size = sizeof(UCHAR) + - p->ucNumEntries * sizeof(UVDClockInfo); + uint8_t num = pp_table_clamp_count(hwmgr, table_offset, + p->ucNumEntries, sizeof(UVDClockInfo)); + + table_size = sizeof(UCHAR) + num * sizeof(UVDClockInfo); } return table_size; @@ -379,16 +431,21 @@ unsigned long table_size, i; struct phm_clock_voltage_dependency_table *dep_table; + uint8_t num; + + num = pp_table_clamp_count(hwmgr, + pp_table_offset_of(hwmgr, table), + table->ucNumEntries, sizeof(table->entries[0])); table_size = sizeof(unsigned long) + sizeof(struct phm_clock_voltage_dependency_table) - * table->ucNumEntries; + * num; dep_table = kzalloc(table_size, GFP_KERNEL); if (NULL == dep_table) return -ENOMEM; - dep_table->count = (unsigned long)table->ucNumEntries; + dep_table->count = (unsigned long)num; for (i = 0; i < dep_table->count; i++) { dep_table->entries[i].clk = @@ -429,6 +486,10 @@ struct phm_clock_and_voltage_limits *limits, const ATOM_PPLIB_Clock_Voltage_Limit_Table *table) { + /* The record array may be empty in a truncated table. */ + if (table->ucNumEntries == 0) + return -EINVAL; + limits->sclk = ((unsigned long)table->entries[0].ucSclkHigh << 16) | le16_to_cpu(table->entries[0].usSclkLow); limits->mclk = ((unsigned long)table->entries[0].ucMclkHigh << 16) | @@ -781,7 +842,7 @@ const ATOM_PPLIB_STATE_V2 *pstate; pstate = pstate_arrays->states; - if (entry_index <= pstate_arrays->ucNumEntries) { + if (entry_index < pstate_arrays->ucNumEntries) { for (i = 0; i < entry_index; i++) pstate = (ATOM_PPLIB_STATE_V2 *)( (unsigned long)pstate + @@ -872,6 +933,10 @@ return -1; if (powerplay_table->sHeader.ucTableFormatRevision >= 6) { + if (!pp_table_range_ok(hwmgr, + le16_to_cpu(powerplay_table->usStateArrayOffset), + sizeof(StateArray))) + return -1; pstate_arrays = (StateArray *)(((unsigned long)powerplay_table) + le16_to_cpu(powerplay_table->usStateArrayOffset)); @@ -907,10 +972,21 @@ ps->classification.bios_index = entry_index; if (powerplay_table->sHeader.ucTableFormatRevision >= 6) { + if (!pp_table_range_ok(hwmgr, + le16_to_cpu(powerplay_table->usStateArrayOffset), + sizeof(StateArray)) || + !pp_table_range_ok(hwmgr, + le16_to_cpu(powerplay_table->usClockInfoArrayOffset), + sizeof(ClockInfoArray)) || + !pp_table_range_ok(hwmgr, + le16_to_cpu(powerplay_table->usNonClockInfoArrayOffset), + sizeof(NonClockInfoArray))) + return -1; + pstate_arrays = (StateArray *)(((unsigned long)powerplay_table) + le16_to_cpu(powerplay_table->usStateArrayOffset)); - if (entry_index > pstate_arrays->ucNumEntries) + if (entry_index >= pstate_arrays->ucNumEntries) return -1; pstate_entry_v2 = get_state_entry_v2(pstate_arrays, entry_index); @@ -920,43 +996,77 @@ pnon_clock_arrays = (NonClockInfoArray *)(((unsigned long)powerplay_table) + le16_to_cpu(powerplay_table->usNonClockInfoArrayOffset)); - pnon_clock_info = (ATOM_PPLIB_NONCLOCK_INFO *)((unsigned long)(pnon_clock_arrays->nonClockInfo) + - (pstate_entry_v2->nonClockInfoIndex * pnon_clock_arrays->ucEntrySize)); + { + uint32_t off = (uint32_t) + (pp_table_offset_of(hwmgr, + pnon_clock_arrays->nonClockInfo) + + (pstate_entry_v2->nonClockInfoIndex * + pnon_clock_arrays->ucEntrySize)); + + if (!pp_table_range_ok(hwmgr, off, + pnon_clock_arrays->ucEntrySize)) + return -1; + pnon_clock_info = (ATOM_PPLIB_NONCLOCK_INFO *) + (((unsigned long)hwmgr->soft_pp_table) + off); + } 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)); - res = func(hwmgr, &ps->hardware, i, pclock_info); + uint32_t off = (uint32_t) + (pp_table_offset_of(hwmgr, + pclock_arrays->clockInfo) + + (pstate_entry_v2->clockInfoIndex[i] * + pclock_arrays->ucEntrySize)); + + if (!pp_table_range_ok(hwmgr, off, + pclock_arrays->ucEntrySize)) { + result = -EINVAL; + break; + } + res = func(hwmgr, &ps->hardware, i, + (const void *)(((unsigned long)hwmgr->soft_pp_table) + off)); if ((0 == result) && (0 != res)) result = res; } } else { - if (entry_index > powerplay_table->ucNumStates) + if (entry_index >= powerplay_table->ucNumStates) return -1; pstate_entry = (ATOM_PPLIB_STATE *)((unsigned long)powerplay_table + le16_to_cpu(powerplay_table->usStateArrayOffset) + entry_index * powerplay_table->ucStateEntrySize); - pnon_clock_info = (ATOM_PPLIB_NONCLOCK_INFO *)((unsigned long)powerplay_table + - le16_to_cpu(powerplay_table->usNonClockInfoArrayOffset) + - pstate_entry->ucNonClockStateIndex * - powerplay_table->ucNonClockSize); + { + uint32_t off = (uint32_t) + (le16_to_cpu(powerplay_table->usNonClockInfoArrayOffset) + + (pstate_entry->ucNonClockStateIndex * + powerplay_table->ucNonClockSize)); + + if (!pp_table_range_ok(hwmgr, off, + powerplay_table->ucNonClockSize)) + return -1; + pnon_clock_info = (ATOM_PPLIB_NONCLOCK_INFO *) + (((unsigned long)hwmgr->soft_pp_table) + off); + } result = init_non_clock_fields(hwmgr, ps, powerplay_table->ucNonClockSize, 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) + - pstate_entry->ucClockStateIndices[i] * - powerplay_table->ucClockInfoSize); - - int res = func(hwmgr, &ps->hardware, i, pclock_info); + uint32_t off = (uint32_t) + (le16_to_cpu(powerplay_table->usClockInfoArrayOffset) + + (pstate_entry->ucClockStateIndices[i] * + powerplay_table->ucClockInfoSize)); + + if (!pp_table_range_ok(hwmgr, off, + powerplay_table->ucClockInfoSize)) { + result = -EINVAL; + break; + } + int res = func(hwmgr, &ps->hardware, i, + (const void *)(((unsigned long)hwmgr->soft_pp_table) + off)); if ((0 == result) && (0 != res)) result = res; @@ -1029,6 +1139,12 @@ header = (ATOM_PPLIB_EXTENDEDHEADER *)(((unsigned long) powerplay_table) + le16_to_cpu(powerplay_table3->usExtendendedHeaderOffset)); + /* Do not read past the end of a short extended header. */ + if (!pp_table_range_ok(hwmgr, + le16_to_cpu(powerplay_table3->usExtendendedHeaderOffset), + SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V3)) + return 0; + hwmgr->platform_descriptor.overdriveLimit.engineClock = le32_to_cpu(header->ulMaxEngineClock); hwmgr->platform_descriptor.overdriveLimit.memoryClock = le32_to_cpu(header->ulMaxMemoryClock); @@ -1063,6 +1179,9 @@ GetIndexIntoMasterTable(DATA, FirmwareInfo), &size, &frev, &crev); + if (fw_info == NULL) + return 0; + if ((fw_info->ucTableFormatRevision == 1) && (le16_to_cpu(fw_info->usStructureSize) >= sizeof(ATOM_FIRMWARE_INFO_V1_4))) result = init_overdrive_limits_V1_4(hwmgr, @@ -1085,20 +1204,32 @@ { unsigned long table_size, i; struct phm_uvd_clock_voltage_dependency_table *uvd_table; + uint8_t num; + + num = pp_table_clamp_count(hwmgr, + pp_table_offset_of(hwmgr, table), + table->numEntries, sizeof(table->entries[0])); table_size = sizeof(unsigned long) + sizeof(struct phm_uvd_clock_voltage_dependency_table) * - table->numEntries; + num; uvd_table = kzalloc(table_size, GFP_KERNEL); if (NULL == uvd_table) return -ENOMEM; - uvd_table->count = table->numEntries; + uvd_table->count = num; - for (i = 0; i < table->numEntries; i++) { - const UVDClockInfo *entry = - &array->entries[table->entries[i].ucUVDClockInfoIndex]; + for (i = 0; i < num; i++) { + uint8_t idx = table->entries[i].ucUVDClockInfoIndex; + uint8_t max_idx = pp_table_clamp_count(hwmgr, + pp_table_offset_of(hwmgr, array), + 0xFF, sizeof(UVDClockInfo)); + const UVDClockInfo *entry; + + if (max_idx == 0 || idx >= max_idx) + idx = 0; + entry = &array->entries[idx]; uvd_table->entries[i].v = (unsigned long)le16_to_cpu(table->entries[i].usVoltage); uvd_table->entries[i].vclk = ((unsigned long)entry->ucVClkHigh << 16) | le16_to_cpu(entry->usVClkLow); @@ -1118,18 +1249,31 @@ { unsigned long table_size, i; struct phm_vce_clock_voltage_dependency_table *vce_table = NULL; + uint8_t num; + + num = pp_table_clamp_count(hwmgr, + pp_table_offset_of(hwmgr, table), + table->numEntries, sizeof(table->entries[0])); table_size = sizeof(unsigned long) + sizeof(struct phm_vce_clock_voltage_dependency_table) - * table->numEntries; + * num; vce_table = kzalloc(table_size, GFP_KERNEL); if (NULL == vce_table) return -ENOMEM; - vce_table->count = table->numEntries; - for (i = 0; i < table->numEntries; i++) { - const VCEClockInfo *entry = &array->entries[table->entries[i].ucVCEClockInfoIndex]; + vce_table->count = num; + for (i = 0; i < num; i++) { + uint8_t idx = table->entries[i].ucVCEClockInfoIndex; + uint8_t max_idx = pp_table_clamp_count(hwmgr, + pp_table_offset_of(hwmgr, array), + 0xFF, sizeof(VCEClockInfo)); + const VCEClockInfo *entry; + + if (max_idx == 0 || idx >= max_idx) + idx = 0; + entry = &array->entries[idx]; vce_table->entries[i].v = (unsigned long)le16_to_cpu(table->entries[i].usVoltage); vce_table->entries[i].evclk = ((unsigned long)entry->ucEVClkHigh << 16) @@ -1149,18 +1293,23 @@ { unsigned long table_size, i; struct phm_samu_clock_voltage_dependency_table *samu_table; + uint8_t num; + + num = pp_table_clamp_count(hwmgr, + pp_table_offset_of(hwmgr, table), + table->numEntries, sizeof(table->entries[0])); table_size = sizeof(unsigned long) + sizeof(struct phm_samu_clock_voltage_dependency_table) * - table->numEntries; + num; samu_table = kzalloc(table_size, GFP_KERNEL); if (NULL == samu_table) return -ENOMEM; - samu_table->count = table->numEntries; + samu_table->count = num; - for (i = 0; i < table->numEntries; i++) { + for (i = 0; i < num; i++) { samu_table->entries[i].v = (unsigned long)le16_to_cpu(table->entries[i].usVoltage); samu_table->entries[i].samclk = ((unsigned long)table->entries[i].ucSAMClockHigh << 16) | le16_to_cpu(table->entries[i].usSAMClockLow); @@ -1177,18 +1326,23 @@ { unsigned table_size, i; struct phm_acp_clock_voltage_dependency_table *acp_table; + uint8_t num; + + num = pp_table_clamp_count(hwmgr, + pp_table_offset_of(hwmgr, table), + table->numEntries, sizeof(table->entries[0])); table_size = sizeof(unsigned long) + sizeof(struct phm_acp_clock_voltage_dependency_table) * - table->numEntries; + num; acp_table = kzalloc(table_size, GFP_KERNEL); if (NULL == acp_table) return -ENOMEM; - acp_table->count = (unsigned long)table->numEntries; + acp_table->count = (unsigned long)num; - for (i = 0; i < table->numEntries; i++) { + for (i = 0; i < num; i++) { acp_table->entries[i].v = (unsigned long)le16_to_cpu(table->entries[i].usVoltage); acp_table->entries[i].acpclk = ((unsigned long)table->entries[i].ucACPClockHigh << 16) | le16_to_cpu(table->entries[i].usACPClockLow); @@ -1226,7 +1380,11 @@ hwmgr, powerplay_table); table_offset = get_vce_clock_voltage_limit_table_offset(hwmgr, powerplay_table); - if (vce_clock_info_array_offset > 0 && table_offset > 0) { + if (vce_clock_info_array_offset > 0 && table_offset > 0 && + pp_table_range_ok(hwmgr, table_offset, + sizeof(ATOM_PPLIB_VCE_Clock_Voltage_Limit_Table)) && + pp_table_range_ok(hwmgr, vce_clock_info_array_offset, + sizeof(VCEClockInfoArray))) { const VCEClockInfoArray *array = (const VCEClockInfoArray *) (((unsigned long) powerplay_table) + vce_clock_info_array_offset); @@ -1241,7 +1399,11 @@ uvd_clock_info_array_offset = get_uvd_clock_info_array_offset(hwmgr, powerplay_table); table_offset = get_uvd_clock_voltage_limit_table_offset(hwmgr, powerplay_table); - if (uvd_clock_info_array_offset > 0 && table_offset > 0) { + if (uvd_clock_info_array_offset > 0 && table_offset > 0 && + pp_table_range_ok(hwmgr, table_offset, + sizeof(ATOM_PPLIB_UVD_Clock_Voltage_Limit_Table)) && + pp_table_range_ok(hwmgr, uvd_clock_info_array_offset, + sizeof(UVDClockInfoArray))) { const UVDClockInfoArray *array = (const UVDClockInfoArray *) (((unsigned long) powerplay_table) + uvd_clock_info_array_offset); @@ -1255,7 +1417,9 @@ table_offset = get_samu_clock_voltage_limit_table_offset(hwmgr, powerplay_table); - if (table_offset > 0) { + if (table_offset > 0 && + pp_table_range_ok(hwmgr, table_offset, + sizeof(ATOM_PPLIB_SAMClk_Voltage_Limit_Table))) { const ATOM_PPLIB_SAMClk_Voltage_Limit_Table *ptable = (const ATOM_PPLIB_SAMClk_Voltage_Limit_Table *) (((unsigned long) powerplay_table) + table_offset); @@ -1266,7 +1430,9 @@ table_offset = get_acp_clock_voltage_limit_table_offset(hwmgr, powerplay_table); - if (table_offset > 0) { + if (table_offset > 0 && + pp_table_range_ok(hwmgr, table_offset, + sizeof(ATOM_PPLIB_ACPClk_Voltage_Limit_Table))) { const ATOM_PPLIB_ACPClk_Voltage_Limit_Table *ptable = (const ATOM_PPLIB_ACPClk_Voltage_Limit_Table *) (((unsigned long) powerplay_table) + table_offset); @@ -1275,18 +1441,25 @@ } table_offset = get_cacp_tdp_table_offset(hwmgr, powerplay_table); - if (table_offset > 0) { + if (table_offset > 0 && + pp_table_range_ok(hwmgr, table_offset, + sizeof(ATOM_PPLIB_POWERTUNE_Table))) { UCHAR rev_id = *(UCHAR *)(((unsigned long)powerplay_table) + table_offset); if (rev_id > 0) { const ATOM_PPLIB_POWERTUNE_Table_V1 *tune_table = (const ATOM_PPLIB_POWERTUNE_Table_V1 *) (((unsigned long) powerplay_table) + table_offset); + + if (!pp_table_range_ok(hwmgr, table_offset, + sizeof(ATOM_PPLIB_POWERTUNE_Table_V1))) + goto out_powertune; result = get_cac_tdp_table(hwmgr, &hwmgr->dyn_state.cac_dtp_table, &tune_table->power_tune_table, le16_to_cpu(tune_table->usMaximumPowerDeliveryLimit)); - hwmgr->dyn_state.cac_dtp_table->usDefaultTargetOperatingTemp = - le16_to_cpu(tune_table->usTjMax); + if (result == 0 && hwmgr->dyn_state.cac_dtp_table != NULL) + hwmgr->dyn_state.cac_dtp_table->usDefaultTargetOperatingTemp = + le16_to_cpu(tune_table->usTjMax); } else { const ATOM_PPLIB_POWERTUNE_Table *tune_table = (const ATOM_PPLIB_POWERTUNE_Table *) @@ -1296,12 +1469,16 @@ &tune_table->power_tune_table, 255); } } +out_powertune: if (le16_to_cpu(powerplay_table->usTableSize) >= sizeof(ATOM_PPLIB_POWERPLAYTABLE4)) { const ATOM_PPLIB_POWERPLAYTABLE4 *powerplay_table4 = (const ATOM_PPLIB_POWERPLAYTABLE4 *)powerplay_table; - if (0 != powerplay_table4->usVddcDependencyOnSCLKOffset) { + if (0 != powerplay_table4->usVddcDependencyOnSCLKOffset && + pp_table_range_ok(hwmgr, + le16_to_cpu(powerplay_table4->usVddcDependencyOnSCLKOffset), + sizeof(ATOM_PPLIB_Clock_Voltage_Dependency_Table))) { table = (ATOM_PPLIB_Clock_Voltage_Dependency_Table *) (((unsigned long) powerplay_table4) + le16_to_cpu(powerplay_table4->usVddcDependencyOnSCLKOffset)); @@ -1309,7 +1486,10 @@ &hwmgr->dyn_state.vddc_dependency_on_sclk, table); } - if (result == 0 && (0 != powerplay_table4->usVddciDependencyOnMCLKOffset)) { + if (result == 0 && (0 != powerplay_table4->usVddciDependencyOnMCLKOffset) && + pp_table_range_ok(hwmgr, + le16_to_cpu(powerplay_table4->usVddciDependencyOnMCLKOffset), + sizeof(ATOM_PPLIB_Clock_Voltage_Dependency_Table))) { table = (ATOM_PPLIB_Clock_Voltage_Dependency_Table *) (((unsigned long) powerplay_table4) + le16_to_cpu(powerplay_table4->usVddciDependencyOnMCLKOffset)); @@ -1317,7 +1497,10 @@ &hwmgr->dyn_state.vddci_dependency_on_mclk, table); } - if (result == 0 && (0 != powerplay_table4->usVddcDependencyOnMCLKOffset)) { + if (result == 0 && (0 != powerplay_table4->usVddcDependencyOnMCLKOffset) && + pp_table_range_ok(hwmgr, + le16_to_cpu(powerplay_table4->usVddcDependencyOnMCLKOffset), + sizeof(ATOM_PPLIB_Clock_Voltage_Dependency_Table))) { table = (ATOM_PPLIB_Clock_Voltage_Dependency_Table *) (((unsigned long) powerplay_table4) + le16_to_cpu(powerplay_table4->usVddcDependencyOnMCLKOffset)); @@ -1325,7 +1508,10 @@ &hwmgr->dyn_state.vddc_dependency_on_mclk, table); } - if (result == 0 && (0 != powerplay_table4->usMaxClockVoltageOnDCOffset)) { + if (result == 0 && (0 != powerplay_table4->usMaxClockVoltageOnDCOffset) && + pp_table_range_ok(hwmgr, + le16_to_cpu(powerplay_table4->usMaxClockVoltageOnDCOffset), + sizeof(ATOM_PPLIB_Clock_Voltage_Limit_Table))) { limit_table = (ATOM_PPLIB_Clock_Voltage_Limit_Table *) (((unsigned long) powerplay_table4) + le16_to_cpu(powerplay_table4->usMaxClockVoltageOnDCOffset)); @@ -1344,7 +1530,10 @@ &hwmgr->dyn_state.valid_sclk_values, hwmgr->dyn_state.vddc_dependency_on_sclk); - if (result == 0 && (0 != powerplay_table4->usMvddDependencyOnMCLKOffset)) { + if (result == 0 && (0 != powerplay_table4->usMvddDependencyOnMCLKOffset) && + pp_table_range_ok(hwmgr, + le16_to_cpu(powerplay_table4->usMvddDependencyOnMCLKOffset), + sizeof(ATOM_PPLIB_Clock_Voltage_Dependency_Table))) { table = (ATOM_PPLIB_Clock_Voltage_Dependency_Table *) (((unsigned long) powerplay_table4) + le16_to_cpu(powerplay_table4->usMvddDependencyOnMCLKOffset)); @@ -1356,7 +1545,9 @@ table_offset = get_sclk_vdd_gfx_clock_voltage_dependency_table_offset(hwmgr, powerplay_table); - if (table_offset > 0) { + if (table_offset > 0 && + pp_table_range_ok(hwmgr, table_offset, + sizeof(ATOM_PPLIB_Clock_Voltage_Dependency_Table))) { table = (ATOM_PPLIB_Clock_Voltage_Dependency_Table *) (((unsigned long) powerplay_table) + table_offset); result = get_clock_voltage_dependency_table(hwmgr, @@ -1372,19 +1563,24 @@ { struct phm_cac_leakage_table *cac_leakage_table; unsigned long table_size, i; + uint8_t num; if (hwmgr == NULL || table == NULL || ptable == NULL) return -EINVAL; + num = pp_table_clamp_count(hwmgr, + pp_table_offset_of(hwmgr, table), + table->ucNumEntries, sizeof(table->entries[0])); + table_size = sizeof(ULONG) + - (sizeof(struct phm_cac_leakage_table) * table->ucNumEntries); + (sizeof(struct phm_cac_leakage_table) * num); cac_leakage_table = kzalloc(table_size, GFP_KERNEL); if (cac_leakage_table == NULL) return -ENOMEM; - cac_leakage_table->count = (ULONG)table->ucNumEntries; + cac_leakage_table->count = (ULONG)num; for (i = 0; i < cac_leakage_table->count; i++) { if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, @@ -1469,7 +1665,10 @@ hwmgr->dyn_state.cac_leakage_table = NULL; - if (0 != ptable5->usCACLeakageTableOffset) { + if (0 != ptable5->usCACLeakageTableOffset && + pp_table_range_ok(hwmgr, + le16_to_cpu(ptable5->usCACLeakageTableOffset), + sizeof(ATOM_PPLIB_CAC_Leakage_Table))) { const ATOM_PPLIB_CAC_Leakage_Table *pCAC_leakage_table = (ATOM_PPLIB_CAC_Leakage_Table *)(((unsigned long)ptable5) + le16_to_cpu(ptable5->usCACLeakageTableOffset)); @@ -1485,15 +1684,18 @@ extended_header = (const ATOM_PPLIB_EXTENDEDHEADER *) (((unsigned long)powerplay_table) + le16_to_cpu(ptable3->usExtendendedHeaderOffset)); - if ((extended_header->usPPMTableOffset > 0) && - le16_to_cpu(extended_header->usSize) >= - SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V5) { + if (le16_to_cpu(extended_header->usSize) >= + SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V5 && + (extended_header->usPPMTableOffset > 0)) { table_offset = le16_to_cpu(extended_header->usPPMTableOffset); - atom_ppm_table = (ATOM_PPLIB_PPM_Table *) - (((unsigned long)powerplay_table) + table_offset); - if (0 == get_platform_power_management_table(hwmgr, atom_ppm_table)) - phm_cap_set(hwmgr->platform_descriptor.platformCaps, - PHM_PlatformCaps_EnablePlatformPowerManagement); + if (pp_table_range_ok(hwmgr, table_offset, + sizeof(ATOM_PPLIB_PPM_Table))) { + atom_ppm_table = (ATOM_PPLIB_PPM_Table *) + (((unsigned long)powerplay_table) + table_offset); + if (0 == get_platform_power_management_table(hwmgr, atom_ppm_table)) + phm_cap_set(hwmgr->platform_descriptor.platformCaps, + PHM_PlatformCaps_EnablePlatformPowerManagement); + } } } } @@ -1508,25 +1710,33 @@ const ATOM_PPLIB_POWERPLAYTABLE4 *powerplay_table4 = (const ATOM_PPLIB_POWERPLAYTABLE4 *)powerplay_table; - if (0 != powerplay_table4->usVddcPhaseShedLimitsTableOffset) { + if (0 != powerplay_table4->usVddcPhaseShedLimitsTableOffset && + pp_table_range_ok(hwmgr, + le16_to_cpu(powerplay_table4->usVddcPhaseShedLimitsTableOffset), + sizeof(ATOM_PPLIB_PhaseSheddingLimits_Table))) { const ATOM_PPLIB_PhaseSheddingLimits_Table *ptable = (ATOM_PPLIB_PhaseSheddingLimits_Table *) (((unsigned long)powerplay_table4) + le16_to_cpu(powerplay_table4->usVddcPhaseShedLimitsTableOffset)); struct phm_phase_shedding_limits_table *table; unsigned long size, i; + uint8_t num; + num = pp_table_clamp_count(hwmgr, + pp_table_offset_of(hwmgr, ptable), + ptable->ucNumEntries, + sizeof(ptable->entries[0])); size = sizeof(unsigned long) + (sizeof(struct phm_phase_shedding_limits_table) * - ptable->ucNumEntries); + num); table = kzalloc(size, GFP_KERNEL); if (table == NULL) return -ENOMEM; - table->count = (unsigned long)ptable->ucNumEntries; + table->count = (unsigned long)num; for (i = 0; i < table->count; i++) { table->entries[i].Voltage = (unsigned long)le16_to_cpu(ptable->entries[i].usVoltage); @@ -1568,23 +1778,51 @@ unsigned short vce_clock_info_array_offset = get_vce_clock_info_array_offset(hwmgr, powerplay_table); - const VCEClockInfoArray *vce_clock_info_array = (const VCEClockInfoArray *)(((unsigned long) powerplay_table) + vce_clock_info_array_offset); + const VCEClockInfoArray *vce_clock_info_array; + + const ClockInfoArray *clock_arrays; - const ClockInfoArray *clock_arrays = (ClockInfoArray *)(((unsigned long)powerplay_table) + - le16_to_cpu(powerplay_table->usClockInfoArrayOffset)); + const ATOM_PPLIB_VCE_State_Record *record; - const ATOM_PPLIB_VCE_State_Record *record = &vce_state_table->entries[i]; + const VCEClockInfo *vce_clock_info; - const VCEClockInfo *vce_clock_info = &vce_clock_info_array->entries[record->ucVCEClockInfoIndex]; + unsigned long clockInfoIndex; - unsigned long clockInfoIndex = record->ucClockInfoIndex & 0x3F; + uint32_t off; + + if (vce_state_table == NULL || vce_clock_info_array_offset == 0) + return -EINVAL; + + vce_clock_info_array = (const VCEClockInfoArray *)(((unsigned long) powerplay_table) + vce_clock_info_array_offset); + + clock_arrays = (ClockInfoArray *)(((unsigned long)powerplay_table) + + le16_to_cpu(powerplay_table->usClockInfoArrayOffset)); + + off = (uint32_t)(pp_table_offset_of(hwmgr, &vce_state_table->entries[0]) + + i * sizeof(vce_state_table->entries[0])); + if (!pp_table_range_ok(hwmgr, off, sizeof(vce_state_table->entries[0]))) + return -EINVAL; + record = &vce_state_table->entries[i]; + + off = (uint32_t)(vce_clock_info_array_offset + 1 + + record->ucVCEClockInfoIndex * sizeof(VCEClockInfo)); + if (!pp_table_range_ok(hwmgr, off, sizeof(VCEClockInfo))) + return -EINVAL; + vce_clock_info = (const VCEClockInfo *) + (((unsigned long) powerplay_table) + off); + + clockInfoIndex = record->ucClockInfoIndex & 0x3F; *flag = (record->ucClockInfoIndex >> NUM_BITS_CLOCK_INFO_ARRAY_INDEX); vce_state->evclk = ((uint32_t)vce_clock_info->ucEVClkHigh << 16) | le16_to_cpu(vce_clock_info->usEVClkLow); vce_state->ecclk = ((uint32_t)vce_clock_info->ucECClkHigh << 16) | le16_to_cpu(vce_clock_info->usECClkLow); - *clock_info = (void *)((unsigned long)(clock_arrays->clockInfo) + (clockInfoIndex * clock_arrays->ucEntrySize)); + off = (uint32_t)(pp_table_offset_of(hwmgr, clock_arrays->clockInfo) + + (clockInfoIndex * clock_arrays->ucEntrySize)); + if (!pp_table_range_ok(hwmgr, off, clock_arrays->ucEntrySize)) + return -EINVAL; + *clock_info = (void *)(((unsigned long)powerplay_table) + off); return 0; }