Commit 779187f2 authored by Alex Deucher's avatar Alex Deucher

drm/radeon/atom: fix voltage table parsing

The arrays items are variable sized.
Signed-off-by: default avatarAlex Deucher <alexander.deucher@amd.com>
parent da289525
...@@ -3118,6 +3118,63 @@ union voltage_object_info { ...@@ -3118,6 +3118,63 @@ union voltage_object_info {
struct _ATOM_VOLTAGE_OBJECT_INFO_V3_1 v3; struct _ATOM_VOLTAGE_OBJECT_INFO_V3_1 v3;
}; };
union voltage_object {
struct _ATOM_VOLTAGE_OBJECT v1;
struct _ATOM_VOLTAGE_OBJECT_V2 v2;
union _ATOM_VOLTAGE_OBJECT_V3 v3;
};
static ATOM_VOLTAGE_OBJECT *atom_lookup_voltage_object_v1(ATOM_VOLTAGE_OBJECT_INFO *v1,
u8 voltage_type)
{
u32 size = v1->sHeader.usStructureSize;
u32 offset = offsetof(ATOM_VOLTAGE_OBJECT_INFO, asVoltageObj[0]);
u8 *start = (u8 *)v1;
while (offset < size) {
ATOM_VOLTAGE_OBJECT *vo = (ATOM_VOLTAGE_OBJECT *)(start + offset);
if (vo->ucVoltageType == voltage_type)
return vo;
offset += offsetof(ATOM_VOLTAGE_OBJECT, asFormula.ucVIDAdjustEntries) +
vo->asFormula.ucNumOfVoltageEntries;
}
return NULL;
}
static ATOM_VOLTAGE_OBJECT_V2 *atom_lookup_voltage_object_v2(ATOM_VOLTAGE_OBJECT_INFO_V2 *v2,
u8 voltage_type)
{
u32 size = v2->sHeader.usStructureSize;
u32 offset = offsetof(ATOM_VOLTAGE_OBJECT_INFO_V2, asVoltageObj[0]);
u8 *start = (u8*)v2;
while (offset < size) {
ATOM_VOLTAGE_OBJECT_V2 *vo = (ATOM_VOLTAGE_OBJECT_V2 *)(start + offset);
if (vo->ucVoltageType == voltage_type)
return vo;
offset += offsetof(ATOM_VOLTAGE_OBJECT_V2, asFormula.asVIDAdjustEntries) +
(vo->asFormula.ucNumOfVoltageEntries * sizeof(VOLTAGE_LUT_ENTRY));
}
return NULL;
}
static ATOM_VOLTAGE_OBJECT_V3 *atom_lookup_voltage_object_v3(ATOM_VOLTAGE_OBJECT_INFO_V3_1 *v3,
u8 voltage_type, u8 voltage_mode)
{
u32 size = v3->sHeader.usStructureSize;
u32 offset = offsetof(ATOM_VOLTAGE_OBJECT_INFO_V3_1, asVoltageObj[0]);
u8 *start = (u8*)v3;
while (offset < size) {
ATOM_VOLTAGE_OBJECT_V3 *vo = (ATOM_VOLTAGE_OBJECT_V3 *)(start + offset);
if ((vo->asGpioVoltageObj.sHeader.ucVoltageType == voltage_type) &&
(vo->asGpioVoltageObj.sHeader.ucVoltageMode == voltage_mode))
return vo;
offset += vo->asGpioVoltageObj.sHeader.usSize;
}
return NULL;
}
bool bool
radeon_atom_is_voltage_gpio(struct radeon_device *rdev, radeon_atom_is_voltage_gpio(struct radeon_device *rdev,
u8 voltage_type, u8 voltage_mode) u8 voltage_type, u8 voltage_mode)
...@@ -3125,8 +3182,8 @@ radeon_atom_is_voltage_gpio(struct radeon_device *rdev, ...@@ -3125,8 +3182,8 @@ radeon_atom_is_voltage_gpio(struct radeon_device *rdev,
int index = GetIndexIntoMasterTable(DATA, VoltageObjectInfo); int index = GetIndexIntoMasterTable(DATA, VoltageObjectInfo);
u8 frev, crev; u8 frev, crev;
u16 data_offset, size; u16 data_offset, size;
int num_indices, i;
union voltage_object_info *voltage_info; union voltage_object_info *voltage_info;
union voltage_object *voltage_object = NULL;
if (atom_parse_data_header(rdev->mode_info.atom_context, index, &size, if (atom_parse_data_header(rdev->mode_info.atom_context, index, &size,
&frev, &crev, &data_offset)) { &frev, &crev, &data_offset)) {
...@@ -3138,26 +3195,18 @@ radeon_atom_is_voltage_gpio(struct radeon_device *rdev, ...@@ -3138,26 +3195,18 @@ radeon_atom_is_voltage_gpio(struct radeon_device *rdev,
case 2: case 2:
switch (crev) { switch (crev) {
case 1: case 1:
num_indices = (size - sizeof(ATOM_COMMON_TABLE_HEADER)) / voltage_object = (union voltage_object *)
sizeof(ATOM_VOLTAGE_OBJECT); atom_lookup_voltage_object_v1(&voltage_info->v1, voltage_type);
if (voltage_object &&
for (i = 0; i < num_indices; i++) { (voltage_object->v1.asControl.ucVoltageControlId == VOLTAGE_CONTROLLED_BY_GPIO))
if ((voltage_info->v1.asVoltageObj[i].ucVoltageType == voltage_type) && return true;
(voltage_info->v1.asVoltageObj[i].asControl.ucVoltageControlId ==
VOLTAGE_CONTROLLED_BY_GPIO))
return true;
}
break; break;
case 2: case 2:
num_indices = (size - sizeof(ATOM_COMMON_TABLE_HEADER)) / voltage_object = (union voltage_object *)
sizeof(ATOM_VOLTAGE_OBJECT_V2); atom_lookup_voltage_object_v2(&voltage_info->v2, voltage_type);
if (voltage_object &&
for (i = 0; i < num_indices; i++) { (voltage_object->v2.asControl.ucVoltageControlId == VOLTAGE_CONTROLLED_BY_GPIO))
if ((voltage_info->v2.asVoltageObj[i].ucVoltageType == voltage_type) && return true;
(voltage_info->v2.asVoltageObj[i].asControl.ucVoltageControlId ==
VOLTAGE_CONTROLLED_BY_GPIO))
return true;
}
break; break;
default: default:
DRM_ERROR("unknown voltage object table\n"); DRM_ERROR("unknown voltage object table\n");
...@@ -3167,16 +3216,9 @@ radeon_atom_is_voltage_gpio(struct radeon_device *rdev, ...@@ -3167,16 +3216,9 @@ radeon_atom_is_voltage_gpio(struct radeon_device *rdev,
case 3: case 3:
switch (crev) { switch (crev) {
case 1: case 1:
num_indices = (size - sizeof(ATOM_COMMON_TABLE_HEADER)) / if (atom_lookup_voltage_object_v3(&voltage_info->v3,
sizeof(ATOM_VOLTAGE_OBJECT_V3); voltage_type, voltage_mode))
return true;
for (i = 0; i < num_indices; i++) {
if ((voltage_info->v3.asVoltageObj[i].asGpioVoltageObj.sHeader.ucVoltageType ==
voltage_type) &&
(voltage_info->v3.asVoltageObj[i].asGpioVoltageObj.sHeader.ucVoltageMode ==
voltage_mode))
return true;
}
break; break;
default: default:
DRM_ERROR("unknown voltage object table\n"); DRM_ERROR("unknown voltage object table\n");
...@@ -3198,8 +3240,8 @@ int radeon_atom_get_max_voltage(struct radeon_device *rdev, ...@@ -3198,8 +3240,8 @@ int radeon_atom_get_max_voltage(struct radeon_device *rdev,
int index = GetIndexIntoMasterTable(DATA, VoltageObjectInfo); int index = GetIndexIntoMasterTable(DATA, VoltageObjectInfo);
u8 frev, crev; u8 frev, crev;
u16 data_offset, size; u16 data_offset, size;
int num_indices, i;
union voltage_object_info *voltage_info; union voltage_object_info *voltage_info;
union voltage_object *voltage_object = NULL;
if (atom_parse_data_header(rdev->mode_info.atom_context, index, &size, if (atom_parse_data_header(rdev->mode_info.atom_context, index, &size,
&frev, &crev, &data_offset)) { &frev, &crev, &data_offset)) {
...@@ -3208,42 +3250,36 @@ int radeon_atom_get_max_voltage(struct radeon_device *rdev, ...@@ -3208,42 +3250,36 @@ int radeon_atom_get_max_voltage(struct radeon_device *rdev,
switch (crev) { switch (crev) {
case 1: case 1:
num_indices = (size - sizeof(ATOM_COMMON_TABLE_HEADER)) / voltage_object = (union voltage_object *)
sizeof(ATOM_VOLTAGE_OBJECT); atom_lookup_voltage_object_v1(&voltage_info->v1, voltage_type);
if (voltage_object) {
for (i = 0; i < num_indices; i++) { ATOM_VOLTAGE_FORMULA *formula =
if (voltage_info->v1.asVoltageObj[i].ucVoltageType == voltage_type) { &voltage_object->v1.asFormula;
ATOM_VOLTAGE_FORMULA *formula = if (formula->ucFlag & 1)
&voltage_info->v1.asVoltageObj[i].asFormula; *max_voltage =
if (formula->ucFlag & 1) le16_to_cpu(formula->usVoltageBaseLevel) +
*max_voltage = formula->ucNumOfVoltageEntries / 2 *
le16_to_cpu(formula->usVoltageBaseLevel) + le16_to_cpu(formula->usVoltageStep);
formula->ucNumOfVoltageEntries / 2 * else
le16_to_cpu(formula->usVoltageStep); *max_voltage =
else le16_to_cpu(formula->usVoltageBaseLevel) +
*max_voltage = (formula->ucNumOfVoltageEntries - 1) *
le16_to_cpu(formula->usVoltageBaseLevel) + le16_to_cpu(formula->usVoltageStep);
(formula->ucNumOfVoltageEntries - 1) * return 0;
le16_to_cpu(formula->usVoltageStep);
return 0;
}
} }
break; break;
case 2: case 2:
num_indices = (size - sizeof(ATOM_COMMON_TABLE_HEADER)) / voltage_object = (union voltage_object *)
sizeof(ATOM_VOLTAGE_OBJECT_V2); atom_lookup_voltage_object_v2(&voltage_info->v2, voltage_type);
if (voltage_object) {
for (i = 0; i < num_indices; i++) { ATOM_VOLTAGE_FORMULA_V2 *formula =
if (voltage_info->v2.asVoltageObj[i].ucVoltageType == voltage_type) { &voltage_object->v2.asFormula;
ATOM_VOLTAGE_FORMULA_V2 *formula = if (formula->ucNumOfVoltageEntries) {
&voltage_info->v2.asVoltageObj[i].asFormula; *max_voltage =
if (formula->ucNumOfVoltageEntries) { le16_to_cpu(formula->asVIDAdjustEntries[
*max_voltage = formula->ucNumOfVoltageEntries - 1
le16_to_cpu(formula->asVIDAdjustEntries[ ].usVoltageValue);
formula->ucNumOfVoltageEntries - 1 return 0;
].usVoltageValue);
return 0;
}
} }
} }
break; break;
...@@ -3262,8 +3298,8 @@ int radeon_atom_get_min_voltage(struct radeon_device *rdev, ...@@ -3262,8 +3298,8 @@ int radeon_atom_get_min_voltage(struct radeon_device *rdev,
int index = GetIndexIntoMasterTable(DATA, VoltageObjectInfo); int index = GetIndexIntoMasterTable(DATA, VoltageObjectInfo);
u8 frev, crev; u8 frev, crev;
u16 data_offset, size; u16 data_offset, size;
int num_indices, i;
union voltage_object_info *voltage_info; union voltage_object_info *voltage_info;
union voltage_object *voltage_object = NULL;
if (atom_parse_data_header(rdev->mode_info.atom_context, index, &size, if (atom_parse_data_header(rdev->mode_info.atom_context, index, &size,
&frev, &crev, &data_offset)) { &frev, &crev, &data_offset)) {
...@@ -3272,34 +3308,28 @@ int radeon_atom_get_min_voltage(struct radeon_device *rdev, ...@@ -3272,34 +3308,28 @@ int radeon_atom_get_min_voltage(struct radeon_device *rdev,
switch (crev) { switch (crev) {
case 1: case 1:
num_indices = (size - sizeof(ATOM_COMMON_TABLE_HEADER)) / voltage_object = (union voltage_object *)
sizeof(ATOM_VOLTAGE_OBJECT); atom_lookup_voltage_object_v1(&voltage_info->v1, voltage_type);
if (voltage_object) {
for (i = 0; i < num_indices; i++) { ATOM_VOLTAGE_FORMULA *formula =
if (voltage_info->v1.asVoltageObj[i].ucVoltageType == voltage_type) { &voltage_object->v1.asFormula;
ATOM_VOLTAGE_FORMULA *formula = *min_voltage =
&voltage_info->v1.asVoltageObj[i].asFormula; le16_to_cpu(formula->usVoltageBaseLevel);
*min_voltage = return 0;
le16_to_cpu(formula->usVoltageBaseLevel);
return 0;
}
} }
break; break;
case 2: case 2:
num_indices = (size - sizeof(ATOM_COMMON_TABLE_HEADER)) / voltage_object = (union voltage_object *)
sizeof(ATOM_VOLTAGE_OBJECT_V2); atom_lookup_voltage_object_v2(&voltage_info->v2, voltage_type);
if (voltage_object) {
for (i = 0; i < num_indices; i++) { ATOM_VOLTAGE_FORMULA_V2 *formula =
if (voltage_info->v2.asVoltageObj[i].ucVoltageType == voltage_type) { &voltage_object->v2.asFormula;
ATOM_VOLTAGE_FORMULA_V2 *formula = if (formula->ucNumOfVoltageEntries) {
&voltage_info->v2.asVoltageObj[i].asFormula; *min_voltage =
if (formula->ucNumOfVoltageEntries) { le16_to_cpu(formula->asVIDAdjustEntries[
*min_voltage = 0
le16_to_cpu(formula->asVIDAdjustEntries[ ].usVoltageValue);
0 return 0;
].usVoltageValue);
return 0;
}
} }
} }
break; break;
...@@ -3318,8 +3348,8 @@ int radeon_atom_get_voltage_step(struct radeon_device *rdev, ...@@ -3318,8 +3348,8 @@ int radeon_atom_get_voltage_step(struct radeon_device *rdev,
int index = GetIndexIntoMasterTable(DATA, VoltageObjectInfo); int index = GetIndexIntoMasterTable(DATA, VoltageObjectInfo);
u8 frev, crev; u8 frev, crev;
u16 data_offset, size; u16 data_offset, size;
int num_indices, i;
union voltage_object_info *voltage_info; union voltage_object_info *voltage_info;
union voltage_object *voltage_object = NULL;
if (atom_parse_data_header(rdev->mode_info.atom_context, index, &size, if (atom_parse_data_header(rdev->mode_info.atom_context, index, &size,
&frev, &crev, &data_offset)) { &frev, &crev, &data_offset)) {
...@@ -3328,21 +3358,18 @@ int radeon_atom_get_voltage_step(struct radeon_device *rdev, ...@@ -3328,21 +3358,18 @@ int radeon_atom_get_voltage_step(struct radeon_device *rdev,
switch (crev) { switch (crev) {
case 1: case 1:
num_indices = (size - sizeof(ATOM_COMMON_TABLE_HEADER)) / voltage_object = (union voltage_object *)
sizeof(ATOM_VOLTAGE_OBJECT); atom_lookup_voltage_object_v1(&voltage_info->v1, voltage_type);
if (voltage_object) {
for (i = 0; i < num_indices; i++) { ATOM_VOLTAGE_FORMULA *formula =
if (voltage_info->v1.asVoltageObj[i].ucVoltageType == voltage_type) { &voltage_object->v1.asFormula;
ATOM_VOLTAGE_FORMULA *formula = if (formula->ucFlag & 1)
&voltage_info->v1.asVoltageObj[i].asFormula; *voltage_step =
if (formula->ucFlag & 1) (le16_to_cpu(formula->usVoltageStep) + 1) / 2;
*voltage_step = else
(le16_to_cpu(formula->usVoltageStep) + 1) / 2; *voltage_step =
else le16_to_cpu(formula->usVoltageStep);
*voltage_step = return 0;
le16_to_cpu(formula->usVoltageStep);
return 0;
}
} }
break; break;
case 2: case 2:
...@@ -3389,8 +3416,9 @@ int radeon_atom_get_voltage_table(struct radeon_device *rdev, ...@@ -3389,8 +3416,9 @@ int radeon_atom_get_voltage_table(struct radeon_device *rdev,
int index = GetIndexIntoMasterTable(DATA, VoltageObjectInfo); int index = GetIndexIntoMasterTable(DATA, VoltageObjectInfo);
u8 frev, crev; u8 frev, crev;
u16 data_offset, size; u16 data_offset, size;
int num_indices, i, j, ret; int i, ret;
union voltage_object_info *voltage_info; union voltage_object_info *voltage_info;
union voltage_object *voltage_object = NULL;
if (atom_parse_data_header(rdev->mode_info.atom_context, index, &size, if (atom_parse_data_header(rdev->mode_info.atom_context, index, &size,
&frev, &crev, &data_offset)) { &frev, &crev, &data_offset)) {
...@@ -3405,29 +3433,26 @@ int radeon_atom_get_voltage_table(struct radeon_device *rdev, ...@@ -3405,29 +3433,26 @@ int radeon_atom_get_voltage_table(struct radeon_device *rdev,
DRM_ERROR("old table version %d, %d\n", frev, crev); DRM_ERROR("old table version %d, %d\n", frev, crev);
return -EINVAL; return -EINVAL;
case 2: case 2:
num_indices = (size - sizeof(ATOM_COMMON_TABLE_HEADER)) / voltage_object = (union voltage_object *)
sizeof(ATOM_VOLTAGE_OBJECT_V2); atom_lookup_voltage_object_v2(&voltage_info->v2, voltage_type);
if (voltage_object) {
for (i = 0; i < num_indices; i++) { ATOM_VOLTAGE_FORMULA_V2 *formula =
if (voltage_info->v2.asVoltageObj[i].ucVoltageType == voltage_type) { &voltage_object->v2.asFormula;
ATOM_VOLTAGE_FORMULA_V2 *formula = if (formula->ucNumOfVoltageEntries > MAX_VOLTAGE_ENTRIES)
&voltage_info->v2.asVoltageObj[i].asFormula; return -EINVAL;
if (formula->ucNumOfVoltageEntries > MAX_VOLTAGE_ENTRIES) for (i = 0; i < formula->ucNumOfVoltageEntries; i++) {
return -EINVAL; voltage_table->entries[i].value =
for (j = 0; j < formula->ucNumOfVoltageEntries; j++) { le16_to_cpu(formula->asVIDAdjustEntries[i].usVoltageValue);
voltage_table->entries[j].value = ret = radeon_atom_get_voltage_gpio_settings(rdev,
le16_to_cpu(formula->asVIDAdjustEntries[j].usVoltageValue); voltage_table->entries[i].value,
ret = radeon_atom_get_voltage_gpio_settings(rdev, voltage_type,
voltage_table->entries[j].value, &voltage_table->entries[i].smio_low,
voltage_type, &voltage_table->mask_low);
&voltage_table->entries[j].smio_low, if (ret)
&voltage_table->mask_low); return ret;
if (ret)
return ret;
}
voltage_table->count = formula->ucNumOfVoltageEntries;
return 0;
} }
voltage_table->count = formula->ucNumOfVoltageEntries;
return 0;
} }
break; break;
default: default:
...@@ -3438,29 +3463,24 @@ int radeon_atom_get_voltage_table(struct radeon_device *rdev, ...@@ -3438,29 +3463,24 @@ int radeon_atom_get_voltage_table(struct radeon_device *rdev,
case 3: case 3:
switch (crev) { switch (crev) {
case 1: case 1:
num_indices = (size - sizeof(ATOM_COMMON_TABLE_HEADER)) / voltage_object = (union voltage_object *)
sizeof(ATOM_VOLTAGE_OBJECT_V3); atom_lookup_voltage_object_v3(&voltage_info->v3,
voltage_type, voltage_mode);
for (i = 0; i < num_indices; i++) { if (voltage_object) {
if ((voltage_info->v3.asVoltageObj[i].asGpioVoltageObj.sHeader.ucVoltageType == ATOM_GPIO_VOLTAGE_OBJECT_V3 *gpio =
voltage_type) && &voltage_object->v3.asGpioVoltageObj;
(voltage_info->v3.asVoltageObj[i].asGpioVoltageObj.sHeader.ucVoltageMode == if (gpio->ucGpioEntryNum > MAX_VOLTAGE_ENTRIES)
voltage_mode)) { return -EINVAL;
ATOM_GPIO_VOLTAGE_OBJECT_V3 *gpio = for (i = 0; i < gpio->ucGpioEntryNum; i++) {
&voltage_info->v3.asVoltageObj[i].asGpioVoltageObj; voltage_table->entries[i].value =
if (gpio->ucGpioEntryNum > MAX_VOLTAGE_ENTRIES) le16_to_cpu(gpio->asVolGpioLut[i].usVoltageValue);
return -EINVAL; voltage_table->entries[i].smio_low =
for (j = 0; j < gpio->ucGpioEntryNum; j++) { le32_to_cpu(gpio->asVolGpioLut[i].ulVoltageId);
voltage_table->entries[j].value =
le16_to_cpu(gpio->asVolGpioLut[j].usVoltageValue);
voltage_table->entries[j].smio_low =
le32_to_cpu(gpio->asVolGpioLut[j].ulVoltageId);
}
voltage_table->mask_low = le32_to_cpu(gpio->ulGpioMaskVal);
voltage_table->count = gpio->ucGpioEntryNum;
voltage_table->phase_delay = gpio->ucPhaseDelay;
return 0;
} }
voltage_table->mask_low = le32_to_cpu(gpio->ulGpioMaskVal);
voltage_table->count = gpio->ucGpioEntryNum;
voltage_table->phase_delay = gpio->ucPhaseDelay;
return 0;
} }
break; break;
default: default:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment