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

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
Loading
Loading
Loading
Loading
+170 −150
Original line number Diff line number Diff line
@@ -3118,6 +3118,63 @@ union voltage_object_info {
	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
radeon_atom_is_voltage_gpio(struct radeon_device *rdev,
			    u8 voltage_type, u8 voltage_mode)
@@ -3125,8 +3182,8 @@ radeon_atom_is_voltage_gpio(struct radeon_device *rdev,
	int index = GetIndexIntoMasterTable(DATA, VoltageObjectInfo);
	u8 frev, crev;
	u16 data_offset, size;
	int num_indices, i;
	union voltage_object_info *voltage_info;
	union voltage_object *voltage_object = NULL;

	if (atom_parse_data_header(rdev->mode_info.atom_context, index, &size,
				   &frev, &crev, &data_offset)) {
@@ -3138,26 +3195,18 @@ radeon_atom_is_voltage_gpio(struct radeon_device *rdev,
		case 2:
			switch (crev) {
			case 1:
				num_indices = (size - sizeof(ATOM_COMMON_TABLE_HEADER)) /
					sizeof(ATOM_VOLTAGE_OBJECT);

				for (i = 0; i < num_indices; i++) {
					if ((voltage_info->v1.asVoltageObj[i].ucVoltageType == voltage_type) &&
					    (voltage_info->v1.asVoltageObj[i].asControl.ucVoltageControlId ==
					     VOLTAGE_CONTROLLED_BY_GPIO))
				voltage_object = (union voltage_object *)
					atom_lookup_voltage_object_v1(&voltage_info->v1, voltage_type);
				if (voltage_object &&
				    (voltage_object->v1.asControl.ucVoltageControlId == VOLTAGE_CONTROLLED_BY_GPIO))
					return true;
				}
				break;
			case 2:
				num_indices = (size - sizeof(ATOM_COMMON_TABLE_HEADER)) /
					sizeof(ATOM_VOLTAGE_OBJECT_V2);

				for (i = 0; i < num_indices; i++) {
					if ((voltage_info->v2.asVoltageObj[i].ucVoltageType == voltage_type) &&
					    (voltage_info->v2.asVoltageObj[i].asControl.ucVoltageControlId ==
					     VOLTAGE_CONTROLLED_BY_GPIO))
				voltage_object = (union voltage_object *)
					atom_lookup_voltage_object_v2(&voltage_info->v2, voltage_type);
				if (voltage_object &&
				    (voltage_object->v2.asControl.ucVoltageControlId == VOLTAGE_CONTROLLED_BY_GPIO))
					return true;
				}
				break;
			default:
				DRM_ERROR("unknown voltage object table\n");
@@ -3167,16 +3216,9 @@ radeon_atom_is_voltage_gpio(struct radeon_device *rdev,
		case 3:
			switch (crev) {
			case 1:
				num_indices = (size - sizeof(ATOM_COMMON_TABLE_HEADER)) /
					sizeof(ATOM_VOLTAGE_OBJECT_V3);

				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))
				if (atom_lookup_voltage_object_v3(&voltage_info->v3,
								  voltage_type, voltage_mode))
					return true;
				}
				break;
			default:
				DRM_ERROR("unknown voltage object table\n");
@@ -3198,8 +3240,8 @@ int radeon_atom_get_max_voltage(struct radeon_device *rdev,
	int index = GetIndexIntoMasterTable(DATA, VoltageObjectInfo);
	u8 frev, crev;
	u16 data_offset, size;
	int num_indices, i;
	union voltage_object_info *voltage_info;
	union voltage_object *voltage_object = NULL;

	if (atom_parse_data_header(rdev->mode_info.atom_context, index, &size,
				   &frev, &crev, &data_offset)) {
@@ -3208,13 +3250,11 @@ int radeon_atom_get_max_voltage(struct radeon_device *rdev,

		switch (crev) {
		case 1:
			num_indices = (size - sizeof(ATOM_COMMON_TABLE_HEADER)) /
				sizeof(ATOM_VOLTAGE_OBJECT);

			for (i = 0; i < num_indices; i++) {
				if (voltage_info->v1.asVoltageObj[i].ucVoltageType == voltage_type) {
			voltage_object = (union voltage_object *)
				atom_lookup_voltage_object_v1(&voltage_info->v1, voltage_type);
			if (voltage_object) {
				ATOM_VOLTAGE_FORMULA *formula =
						&voltage_info->v1.asVoltageObj[i].asFormula;
					&voltage_object->v1.asFormula;
				if (formula->ucFlag & 1)
					*max_voltage =
						le16_to_cpu(formula->usVoltageBaseLevel) +
@@ -3227,16 +3267,13 @@ int radeon_atom_get_max_voltage(struct radeon_device *rdev,
						le16_to_cpu(formula->usVoltageStep);
				return 0;
			}
			}
			break;
		case 2:
			num_indices = (size - sizeof(ATOM_COMMON_TABLE_HEADER)) /
				sizeof(ATOM_VOLTAGE_OBJECT_V2);

			for (i = 0; i < num_indices; i++) {
				if (voltage_info->v2.asVoltageObj[i].ucVoltageType == voltage_type) {
			voltage_object = (union voltage_object *)
				atom_lookup_voltage_object_v2(&voltage_info->v2, voltage_type);
			if (voltage_object) {
				ATOM_VOLTAGE_FORMULA_V2 *formula =
						&voltage_info->v2.asVoltageObj[i].asFormula;
					&voltage_object->v2.asFormula;
				if (formula->ucNumOfVoltageEntries) {
					*max_voltage =
						le16_to_cpu(formula->asVIDAdjustEntries[
@@ -3245,7 +3282,6 @@ int radeon_atom_get_max_voltage(struct radeon_device *rdev,
					return 0;
				}
			}
			}
			break;
		default:
			DRM_ERROR("unknown voltage object table\n");
@@ -3262,8 +3298,8 @@ int radeon_atom_get_min_voltage(struct radeon_device *rdev,
	int index = GetIndexIntoMasterTable(DATA, VoltageObjectInfo);
	u8 frev, crev;
	u16 data_offset, size;
	int num_indices, i;
	union voltage_object_info *voltage_info;
	union voltage_object *voltage_object = NULL;

	if (atom_parse_data_header(rdev->mode_info.atom_context, index, &size,
				   &frev, &crev, &data_offset)) {
@@ -3272,27 +3308,22 @@ int radeon_atom_get_min_voltage(struct radeon_device *rdev,

		switch (crev) {
		case 1:
			num_indices = (size - sizeof(ATOM_COMMON_TABLE_HEADER)) /
				sizeof(ATOM_VOLTAGE_OBJECT);

			for (i = 0; i < num_indices; i++) {
				if (voltage_info->v1.asVoltageObj[i].ucVoltageType == voltage_type) {
			voltage_object = (union voltage_object *)
				atom_lookup_voltage_object_v1(&voltage_info->v1, voltage_type);
			if (voltage_object) {
				ATOM_VOLTAGE_FORMULA *formula =
						&voltage_info->v1.asVoltageObj[i].asFormula;
					&voltage_object->v1.asFormula;
				*min_voltage =
					le16_to_cpu(formula->usVoltageBaseLevel);
				return 0;
			}
			}
			break;
		case 2:
			num_indices = (size - sizeof(ATOM_COMMON_TABLE_HEADER)) /
				sizeof(ATOM_VOLTAGE_OBJECT_V2);

			for (i = 0; i < num_indices; i++) {
				if (voltage_info->v2.asVoltageObj[i].ucVoltageType == voltage_type) {
			voltage_object = (union voltage_object *)
				atom_lookup_voltage_object_v2(&voltage_info->v2, voltage_type);
			if (voltage_object) {
				ATOM_VOLTAGE_FORMULA_V2 *formula =
						&voltage_info->v2.asVoltageObj[i].asFormula;
					&voltage_object->v2.asFormula;
				if (formula->ucNumOfVoltageEntries) {
					*min_voltage =
						le16_to_cpu(formula->asVIDAdjustEntries[
@@ -3301,7 +3332,6 @@ int radeon_atom_get_min_voltage(struct radeon_device *rdev,
					return 0;
				}
			}
			}
			break;
		default:
			DRM_ERROR("unknown voltage object table\n");
@@ -3318,8 +3348,8 @@ int radeon_atom_get_voltage_step(struct radeon_device *rdev,
	int index = GetIndexIntoMasterTable(DATA, VoltageObjectInfo);
	u8 frev, crev;
	u16 data_offset, size;
	int num_indices, i;
	union voltage_object_info *voltage_info;
	union voltage_object *voltage_object = NULL;

	if (atom_parse_data_header(rdev->mode_info.atom_context, index, &size,
				   &frev, &crev, &data_offset)) {
@@ -3328,13 +3358,11 @@ int radeon_atom_get_voltage_step(struct radeon_device *rdev,

		switch (crev) {
		case 1:
			num_indices = (size - sizeof(ATOM_COMMON_TABLE_HEADER)) /
				sizeof(ATOM_VOLTAGE_OBJECT);

			for (i = 0; i < num_indices; i++) {
				if (voltage_info->v1.asVoltageObj[i].ucVoltageType == voltage_type) {
			voltage_object = (union voltage_object *)
				atom_lookup_voltage_object_v1(&voltage_info->v1, voltage_type);
			if (voltage_object) {
				ATOM_VOLTAGE_FORMULA *formula =
						&voltage_info->v1.asVoltageObj[i].asFormula;
					&voltage_object->v1.asFormula;
				if (formula->ucFlag & 1)
					*voltage_step =
						(le16_to_cpu(formula->usVoltageStep) + 1) / 2;
@@ -3343,7 +3371,6 @@ int radeon_atom_get_voltage_step(struct radeon_device *rdev,
						le16_to_cpu(formula->usVoltageStep);
				return 0;
			}
			}
			break;
		case 2:
			return -EINVAL;
@@ -3389,8 +3416,9 @@ int radeon_atom_get_voltage_table(struct radeon_device *rdev,
	int index = GetIndexIntoMasterTable(DATA, VoltageObjectInfo);
	u8 frev, crev;
	u16 data_offset, size;
	int num_indices, i, j, ret;
	int i, ret;
	union voltage_object_info *voltage_info;
	union voltage_object *voltage_object = NULL;

	if (atom_parse_data_header(rdev->mode_info.atom_context, index, &size,
				   &frev, &crev, &data_offset)) {
@@ -3405,22 +3433,20 @@ int radeon_atom_get_voltage_table(struct radeon_device *rdev,
				DRM_ERROR("old table version %d, %d\n", frev, crev);
				return -EINVAL;
			case 2:
				num_indices = (size - sizeof(ATOM_COMMON_TABLE_HEADER)) /
					sizeof(ATOM_VOLTAGE_OBJECT_V2);

				for (i = 0; i < num_indices; i++) {
					if (voltage_info->v2.asVoltageObj[i].ucVoltageType == voltage_type) {
				voltage_object = (union voltage_object *)
					atom_lookup_voltage_object_v2(&voltage_info->v2, voltage_type);
				if (voltage_object) {
					ATOM_VOLTAGE_FORMULA_V2 *formula =
							&voltage_info->v2.asVoltageObj[i].asFormula;
						&voltage_object->v2.asFormula;
					if (formula->ucNumOfVoltageEntries > MAX_VOLTAGE_ENTRIES)
						return -EINVAL;
						for (j = 0; j < formula->ucNumOfVoltageEntries; j++) {
							voltage_table->entries[j].value =
								le16_to_cpu(formula->asVIDAdjustEntries[j].usVoltageValue);
					for (i = 0; i < formula->ucNumOfVoltageEntries; i++) {
						voltage_table->entries[i].value =
							le16_to_cpu(formula->asVIDAdjustEntries[i].usVoltageValue);
						ret = radeon_atom_get_voltage_gpio_settings(rdev,
												    voltage_table->entries[j].value,
											    voltage_table->entries[i].value,
											    voltage_type,
												    &voltage_table->entries[j].smio_low,
											    &voltage_table->entries[i].smio_low,
											    &voltage_table->mask_low);
						if (ret)
							return ret;
@@ -3428,7 +3454,6 @@ int radeon_atom_get_voltage_table(struct radeon_device *rdev,
					voltage_table->count = formula->ucNumOfVoltageEntries;
					return 0;
				}
				}
				break;
			default:
				DRM_ERROR("unknown voltage object table\n");
@@ -3438,30 +3463,25 @@ int radeon_atom_get_voltage_table(struct radeon_device *rdev,
		case 3:
			switch (crev) {
			case 1:
				num_indices = (size - sizeof(ATOM_COMMON_TABLE_HEADER)) /
					sizeof(ATOM_VOLTAGE_OBJECT_V3);

				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)) {
				voltage_object = (union voltage_object *)
					atom_lookup_voltage_object_v3(&voltage_info->v3,
								      voltage_type, voltage_mode);
				if (voltage_object) {
					ATOM_GPIO_VOLTAGE_OBJECT_V3 *gpio =
							&voltage_info->v3.asVoltageObj[i].asGpioVoltageObj;
						&voltage_object->v3.asGpioVoltageObj;
					if (gpio->ucGpioEntryNum > MAX_VOLTAGE_ENTRIES)
						return -EINVAL;
						for (j = 0; j < gpio->ucGpioEntryNum; j++) {
							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);
					for (i = 0; i < gpio->ucGpioEntryNum; i++) {
						voltage_table->entries[i].value =
							le16_to_cpu(gpio->asVolGpioLut[i].usVoltageValue);
						voltage_table->entries[i].smio_low =
							le32_to_cpu(gpio->asVolGpioLut[i].ulVoltageId);
					}
					voltage_table->mask_low = le32_to_cpu(gpio->ulGpioMaskVal);
					voltage_table->count = gpio->ucGpioEntryNum;
					voltage_table->phase_delay = gpio->ucPhaseDelay;
					return 0;
				}
				}
				break;
			default:
				DRM_ERROR("unknown voltage object table\n");