diff mbox

[1/1] drm/i915: Allow parsing of variable size child device entries from VBT

Message ID 1c02cc0db18863120520926f45db4c6e320a21b8.1440165040.git.jani.nikula@intel.com (mailing list archive)
State New, archived
Headers show

Commit Message

Jani Nikula Aug. 21, 2015, 1:52 p.m. UTC
From: David Weinehall <david.weinehall@linux.intel.com>

VBT version 196 increased the size of common_child_dev_config. The
parser code assumed that the size of this structure would not change.

The modified code now copies the amount needed based on the VBT version,
and emits a debug message if the VBT version is unknown (too new); since
the struct config block won't shrink in newer versions it should be
harmless to copy the maximum known size in such cases, so that's what we
do, but emitting the warning is probably sensible anyway.

In the longer run it might make sense to modify the parser code to use a
version/feature mapping, rather than hardcoding things like this, but
for now the variants are fairly managable.

This fixes a regression introduced in

commit 75067ddecf21271631bc018d2fb23ddd09b66aae
Author: Antti Koskipaa <antti.koskipaa@linux.intel.com>
Date:   Fri Jul 10 14:10:55 2015 +0300

    drm/i915: Per-DDI I_boost override

since that commit changed the child device config size without updating
the checks and memcpy.

v2: Stricter size checks

v3 by Jani:
- Keep the checks strict, and warnigns verbose, but keep going anyway.
- Take care to copy the max amount of child device config we can.
- Fix the messages.

Signed-off-by: David Weinehall <david.weinehall@linux.intel.com>
Signed-off-by: Jani Nikula <jani.nikula@intel.com>
---
 drivers/gpu/drm/i915/intel_bios.c | 37 +++++++++++++++++++++++++++++++++----
 drivers/gpu/drm/i915/intel_bios.h |  6 ++++--
 2 files changed, 37 insertions(+), 6 deletions(-)

Comments

Ville Syrjälä Aug. 21, 2015, 2:24 p.m. UTC | #1
On Fri, Aug 21, 2015 at 04:52:01PM +0300, Jani Nikula wrote:
> From: David Weinehall <david.weinehall@linux.intel.com>
> 
> VBT version 196 increased the size of common_child_dev_config. The
> parser code assumed that the size of this structure would not change.
> 
> The modified code now copies the amount needed based on the VBT version,
> and emits a debug message if the VBT version is unknown (too new); since
> the struct config block won't shrink in newer versions it should be
> harmless to copy the maximum known size in such cases, so that's what we
> do, but emitting the warning is probably sensible anyway.
> 
> In the longer run it might make sense to modify the parser code to use a
> version/feature mapping, rather than hardcoding things like this, but
> for now the variants are fairly managable.
> 
> This fixes a regression introduced in
> 
> commit 75067ddecf21271631bc018d2fb23ddd09b66aae
> Author: Antti Koskipaa <antti.koskipaa@linux.intel.com>
> Date:   Fri Jul 10 14:10:55 2015 +0300
> 
>     drm/i915: Per-DDI I_boost override
> 
> since that commit changed the child device config size without updating
> the checks and memcpy.
> 
> v2: Stricter size checks
> 
> v3 by Jani:
> - Keep the checks strict, and warnigns verbose, but keep going anyway.
> - Take care to copy the max amount of child device config we can.
> - Fix the messages.
> 
> Signed-off-by: David Weinehall <david.weinehall@linux.intel.com>
> Signed-off-by: Jani Nikula <jani.nikula@intel.com>
> ---
>  drivers/gpu/drm/i915/intel_bios.c | 37 +++++++++++++++++++++++++++++++++----
>  drivers/gpu/drm/i915/intel_bios.h |  6 ++++--
>  2 files changed, 37 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/gpu/drm/i915/intel_bios.c b/drivers/gpu/drm/i915/intel_bios.c
> index 64e5b15ae0b6..be83b77aa018 100644
> --- a/drivers/gpu/drm/i915/intel_bios.c
> +++ b/drivers/gpu/drm/i915/intel_bios.c
> @@ -1051,17 +1051,39 @@ parse_device_mapping(struct drm_i915_private *dev_priv,
>  	const union child_device_config *p_child;
>  	union child_device_config *child_dev_ptr;
>  	int i, child_device_num, count;
> -	u16	block_size;
> +	u8 expected_size;
> +	u16 block_size;
>  
>  	p_defs = find_section(bdb, BDB_GENERAL_DEFINITIONS);
>  	if (!p_defs) {
>  		DRM_DEBUG_KMS("No general definition block is found, no devices defined.\n");
>  		return;
>  	}
> -	if (p_defs->child_dev_size < sizeof(*p_child)) {
> -		DRM_ERROR("General definiton block child device size is too small.\n");
> +	if (bdb->version < 195) {
> +		expected_size = sizeof(struct old_child_dev_config);
> +	} else if (bdb->version == 195) {
> +		expected_size = 37;
> +	} else if (bdb->version <= 197) {
> +		expected_size = 38;
> +	} else {
> +		expected_size = 38;
> +		BUILD_BUG_ON(sizeof(*p_child) < 38);
> +		DRM_DEBUG_DRIVER("Expected child device config size for VBT version %u not known; assuming %u\n",
> +				 bdb->version, expected_size);
> +	}
> +
> +	/* The legacy sized child device config is the minimum we need. */
> +	if (p_defs->child_dev_size < sizeof(struct old_child_dev_config)) {
> +		DRM_ERROR("Child device config size %u is too small.\n",
> +			  p_defs->child_dev_size);
>  		return;
>  	}
> +
> +	/* Flag an error for unexpected size, but continue anyway. */
> +	if (p_defs->child_dev_size != expected_size)
> +		DRM_ERROR("Unexpected child device config size %u (expected %u for VBT version %u)\n",
> +			  p_defs->child_dev_size, expected_size, bdb->version);
> +
>  	/* get the block size of general definitions */
>  	block_size = get_blocksize(p_defs);
>  	/* get the number of child device */
> @@ -1106,7 +1128,14 @@ parse_device_mapping(struct drm_i915_private *dev_priv,
>  
>  		child_dev_ptr = dev_priv->vbt.child_dev + count;
>  		count++;
> -		memcpy(child_dev_ptr, p_child, sizeof(*p_child));
> +
> +		/*
> +		 * Copy as much as we know (sizeof) and is available
> +		 * (child_dev_size) of the child device. Accessing the data must
> +		 * depend on VBT version.
> +		 */
> +		memcpy(child_dev_ptr, p_child,
> +		       min_t(size_t, p_defs->child_dev_size, sizeof(*p_child)));

Looks good. Could maybe use a
BUILD_BUG_ON(sizeof(struct old_child_dev_config) != 33)
somewhere to make sure people don't make a mess of things.

Reviewed-by: Ville Syrjälä <ville.syrjala@linux.intel.com>

>  	}
>  	return;
>  }
> diff --git a/drivers/gpu/drm/i915/intel_bios.h b/drivers/gpu/drm/i915/intel_bios.h
> index 6d909efbf43f..06d0dbde2be6 100644
> --- a/drivers/gpu/drm/i915/intel_bios.h
> +++ b/drivers/gpu/drm/i915/intel_bios.h
> @@ -203,9 +203,11 @@ struct bdb_general_features {
>  #define DEVICE_PORT_DVOB	0x01
>  #define DEVICE_PORT_DVOC	0x02
>  
> -/* We used to keep this struct but without any version control. We should avoid
> +/*
> + * We used to keep this struct but without any version control. We should avoid
>   * using it in the future, but it should be safe to keep using it in the old
> - * code. */
> + * code. Do not change; we rely on its size.
> + */
>  struct old_child_dev_config {
>  	u16 handle;
>  	u16 device_type;
> -- 
> 2.1.4
Jani Nikula Aug. 24, 2015, 7:40 a.m. UTC | #2
On Fri, 21 Aug 2015, Ville Syrjälä <ville.syrjala@linux.intel.com> wrote:
> On Fri, Aug 21, 2015 at 04:52:01PM +0300, Jani Nikula wrote:
>> From: David Weinehall <david.weinehall@linux.intel.com>
>> 
>> VBT version 196 increased the size of common_child_dev_config. The
>> parser code assumed that the size of this structure would not change.
>> 
>> The modified code now copies the amount needed based on the VBT version,
>> and emits a debug message if the VBT version is unknown (too new); since
>> the struct config block won't shrink in newer versions it should be
>> harmless to copy the maximum known size in such cases, so that's what we
>> do, but emitting the warning is probably sensible anyway.
>> 
>> In the longer run it might make sense to modify the parser code to use a
>> version/feature mapping, rather than hardcoding things like this, but
>> for now the variants are fairly managable.
>> 
>> This fixes a regression introduced in
>> 
>> commit 75067ddecf21271631bc018d2fb23ddd09b66aae
>> Author: Antti Koskipaa <antti.koskipaa@linux.intel.com>
>> Date:   Fri Jul 10 14:10:55 2015 +0300
>> 
>>     drm/i915: Per-DDI I_boost override
>> 
>> since that commit changed the child device config size without updating
>> the checks and memcpy.
>> 
>> v2: Stricter size checks
>> 
>> v3 by Jani:
>> - Keep the checks strict, and warnigns verbose, but keep going anyway.
>> - Take care to copy the max amount of child device config we can.
>> - Fix the messages.
>> 
>> Signed-off-by: David Weinehall <david.weinehall@linux.intel.com>
>> Signed-off-by: Jani Nikula <jani.nikula@intel.com>
>> ---
>>  drivers/gpu/drm/i915/intel_bios.c | 37 +++++++++++++++++++++++++++++++++----
>>  drivers/gpu/drm/i915/intel_bios.h |  6 ++++--
>>  2 files changed, 37 insertions(+), 6 deletions(-)
>> 
>> diff --git a/drivers/gpu/drm/i915/intel_bios.c b/drivers/gpu/drm/i915/intel_bios.c
>> index 64e5b15ae0b6..be83b77aa018 100644
>> --- a/drivers/gpu/drm/i915/intel_bios.c
>> +++ b/drivers/gpu/drm/i915/intel_bios.c
>> @@ -1051,17 +1051,39 @@ parse_device_mapping(struct drm_i915_private *dev_priv,
>>  	const union child_device_config *p_child;
>>  	union child_device_config *child_dev_ptr;
>>  	int i, child_device_num, count;
>> -	u16	block_size;
>> +	u8 expected_size;
>> +	u16 block_size;
>>  
>>  	p_defs = find_section(bdb, BDB_GENERAL_DEFINITIONS);
>>  	if (!p_defs) {
>>  		DRM_DEBUG_KMS("No general definition block is found, no devices defined.\n");
>>  		return;
>>  	}
>> -	if (p_defs->child_dev_size < sizeof(*p_child)) {
>> -		DRM_ERROR("General definiton block child device size is too small.\n");
>> +	if (bdb->version < 195) {
>> +		expected_size = sizeof(struct old_child_dev_config);
>> +	} else if (bdb->version == 195) {
>> +		expected_size = 37;
>> +	} else if (bdb->version <= 197) {
>> +		expected_size = 38;
>> +	} else {
>> +		expected_size = 38;
>> +		BUILD_BUG_ON(sizeof(*p_child) < 38);
>> +		DRM_DEBUG_DRIVER("Expected child device config size for VBT version %u not known; assuming %u\n",
>> +				 bdb->version, expected_size);
>> +	}
>> +
>> +	/* The legacy sized child device config is the minimum we need. */
>> +	if (p_defs->child_dev_size < sizeof(struct old_child_dev_config)) {
>> +		DRM_ERROR("Child device config size %u is too small.\n",
>> +			  p_defs->child_dev_size);
>>  		return;
>>  	}
>> +
>> +	/* Flag an error for unexpected size, but continue anyway. */
>> +	if (p_defs->child_dev_size != expected_size)
>> +		DRM_ERROR("Unexpected child device config size %u (expected %u for VBT version %u)\n",
>> +			  p_defs->child_dev_size, expected_size, bdb->version);
>> +
>>  	/* get the block size of general definitions */
>>  	block_size = get_blocksize(p_defs);
>>  	/* get the number of child device */
>> @@ -1106,7 +1128,14 @@ parse_device_mapping(struct drm_i915_private *dev_priv,
>>  
>>  		child_dev_ptr = dev_priv->vbt.child_dev + count;
>>  		count++;
>> -		memcpy(child_dev_ptr, p_child, sizeof(*p_child));
>> +
>> +		/*
>> +		 * Copy as much as we know (sizeof) and is available
>> +		 * (child_dev_size) of the child device. Accessing the data must
>> +		 * depend on VBT version.
>> +		 */
>> +		memcpy(child_dev_ptr, p_child,
>> +		       min_t(size_t, p_defs->child_dev_size, sizeof(*p_child)));
>
> Looks good. Could maybe use a
> BUILD_BUG_ON(sizeof(struct old_child_dev_config) != 33)
> somewhere to make sure people don't make a mess of things.
>
> Reviewed-by: Ville Syrjälä <ville.syrjala@linux.intel.com>

Pushed to drm-intel-next-fixes, thanks for the review.

BR,
Jani.


>
>>  	}
>>  	return;
>>  }
>> diff --git a/drivers/gpu/drm/i915/intel_bios.h b/drivers/gpu/drm/i915/intel_bios.h
>> index 6d909efbf43f..06d0dbde2be6 100644
>> --- a/drivers/gpu/drm/i915/intel_bios.h
>> +++ b/drivers/gpu/drm/i915/intel_bios.h
>> @@ -203,9 +203,11 @@ struct bdb_general_features {
>>  #define DEVICE_PORT_DVOB	0x01
>>  #define DEVICE_PORT_DVOC	0x02
>>  
>> -/* We used to keep this struct but without any version control. We should avoid
>> +/*
>> + * We used to keep this struct but without any version control. We should avoid
>>   * using it in the future, but it should be safe to keep using it in the old
>> - * code. */
>> + * code. Do not change; we rely on its size.
>> + */
>>  struct old_child_dev_config {
>>  	u16 handle;
>>  	u16 device_type;
>> -- 
>> 2.1.4
>
> -- 
> Ville Syrjälä
> Intel OTC
diff mbox

Patch

diff --git a/drivers/gpu/drm/i915/intel_bios.c b/drivers/gpu/drm/i915/intel_bios.c
index 64e5b15ae0b6..be83b77aa018 100644
--- a/drivers/gpu/drm/i915/intel_bios.c
+++ b/drivers/gpu/drm/i915/intel_bios.c
@@ -1051,17 +1051,39 @@  parse_device_mapping(struct drm_i915_private *dev_priv,
 	const union child_device_config *p_child;
 	union child_device_config *child_dev_ptr;
 	int i, child_device_num, count;
-	u16	block_size;
+	u8 expected_size;
+	u16 block_size;
 
 	p_defs = find_section(bdb, BDB_GENERAL_DEFINITIONS);
 	if (!p_defs) {
 		DRM_DEBUG_KMS("No general definition block is found, no devices defined.\n");
 		return;
 	}
-	if (p_defs->child_dev_size < sizeof(*p_child)) {
-		DRM_ERROR("General definiton block child device size is too small.\n");
+	if (bdb->version < 195) {
+		expected_size = sizeof(struct old_child_dev_config);
+	} else if (bdb->version == 195) {
+		expected_size = 37;
+	} else if (bdb->version <= 197) {
+		expected_size = 38;
+	} else {
+		expected_size = 38;
+		BUILD_BUG_ON(sizeof(*p_child) < 38);
+		DRM_DEBUG_DRIVER("Expected child device config size for VBT version %u not known; assuming %u\n",
+				 bdb->version, expected_size);
+	}
+
+	/* The legacy sized child device config is the minimum we need. */
+	if (p_defs->child_dev_size < sizeof(struct old_child_dev_config)) {
+		DRM_ERROR("Child device config size %u is too small.\n",
+			  p_defs->child_dev_size);
 		return;
 	}
+
+	/* Flag an error for unexpected size, but continue anyway. */
+	if (p_defs->child_dev_size != expected_size)
+		DRM_ERROR("Unexpected child device config size %u (expected %u for VBT version %u)\n",
+			  p_defs->child_dev_size, expected_size, bdb->version);
+
 	/* get the block size of general definitions */
 	block_size = get_blocksize(p_defs);
 	/* get the number of child device */
@@ -1106,7 +1128,14 @@  parse_device_mapping(struct drm_i915_private *dev_priv,
 
 		child_dev_ptr = dev_priv->vbt.child_dev + count;
 		count++;
-		memcpy(child_dev_ptr, p_child, sizeof(*p_child));
+
+		/*
+		 * Copy as much as we know (sizeof) and is available
+		 * (child_dev_size) of the child device. Accessing the data must
+		 * depend on VBT version.
+		 */
+		memcpy(child_dev_ptr, p_child,
+		       min_t(size_t, p_defs->child_dev_size, sizeof(*p_child)));
 	}
 	return;
 }
diff --git a/drivers/gpu/drm/i915/intel_bios.h b/drivers/gpu/drm/i915/intel_bios.h
index 6d909efbf43f..06d0dbde2be6 100644
--- a/drivers/gpu/drm/i915/intel_bios.h
+++ b/drivers/gpu/drm/i915/intel_bios.h
@@ -203,9 +203,11 @@  struct bdb_general_features {
 #define DEVICE_PORT_DVOB	0x01
 #define DEVICE_PORT_DVOC	0x02
 
-/* We used to keep this struct but without any version control. We should avoid
+/*
+ * We used to keep this struct but without any version control. We should avoid
  * using it in the future, but it should be safe to keep using it in the old
- * code. */
+ * code. Do not change; we rely on its size.
+ */
 struct old_child_dev_config {
 	u16 handle;
 	u16 device_type;