[10/10] drm/i915: Disable use of stolen area by User when Intel RST is present

Submitted by ankitprasad.r.sharma@intel.com on Feb. 4, 2016, 9:30 a.m.

Details

Message ID 1454578211-24823-11-git-send-email-ankitprasad.r.sharma@intel.com
State New
Headers show
Series "Support for creating/using Stolen memory backed objects" ( rev: 9 ) in Intel GFX

Not browsing as part of any series.

Commit Message

ankitprasad.r.sharma@intel.com Feb. 4, 2016, 9:30 a.m.
From: Ankitprasad Sharma <ankitprasad.r.sharma@intel.com>

The BIOS RapidStartTechnology may corrupt the stolen memory across S3
suspend due to unalarmed hibernation, in which case we will not be able
to preserve the User data stored in the stolen region. Hence this patch
tries to identify presence of the RST device on the ACPI bus, and
disables use of stolen memory (for persistent data) if found.

v2: Updated comment, updated/corrected new functions private to driver
(Chris/Tvrtko)

v3: Disabling stolen by default, wait till required acpi changes to
detect device presence are pulled in (Ankit)

v4: Enabled stolen by default as required acpi changes are merged
(Ankit)

Signed-off-by: Ankitprasad Sharma <ankitprasad.r.sharma@intel.com>
---
 drivers/gpu/drm/i915/i915_drv.h        | 11 +++++++++++
 drivers/gpu/drm/i915/i915_gem.c        |  8 ++++++++
 drivers/gpu/drm/i915/i915_gem_stolen.c | 14 ++++++++++++++
 drivers/gpu/drm/i915/intel_acpi.c      | 10 ++++++++++
 4 files changed, 43 insertions(+)

Patch hide | download patch | download mbox

diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 16f2f94..9d67097 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -1349,6 +1349,16 @@  struct i915_gem_mm {
 	 */
 	bool busy;
 
+	/**
+	 * Stolen will be lost upon hibernate (as the memory is unpowered).
+	 * Across resume, we expect stolen to be intact - however, it may
+	 * also be utililised by third parties (e.g. Intel RapidStart
+	 * Technology) and if so we have to assume that any data stored in
+	 * stolen across resume is lost and we set this flag to indicate that
+	 * the stolen memory is volatile.
+	 */
+	bool nonvolatile_stolen;
+
 	/* the indicator for dispatch video commands on two BSD rings */
 	unsigned int bsd_ring_dispatch_index;
 
@@ -3465,6 +3475,7 @@  intel_opregion_notify_adapter(struct drm_device *dev, pci_power_t state)
 #endif
 
 /* intel_acpi.c */
+bool intel_detect_acpi_rst(void);
 #ifdef CONFIG_ACPI
 extern void intel_register_dsm_handler(void);
 extern void intel_unregister_dsm_handler(void);
diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
index 0cd57d4..63dab63 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -396,8 +396,16 @@  static struct drm_i915_gem_object *
 i915_gem_alloc_object_stolen(struct drm_device *dev, size_t size)
 {
 	struct drm_i915_gem_object *obj;
+	struct drm_i915_private *dev_priv = dev->dev_private;
 	int ret;
 
+	if (!dev_priv->mm.nonvolatile_stolen) {
+		/* Stolen may be overwritten by external parties
+		 * so unsuitable for persistent user data.
+		 */
+		return ERR_PTR(-ENODEV);
+	}
+
 	mutex_lock(&dev->struct_mutex);
 	obj = i915_gem_object_create_stolen(dev, size);
 	if (IS_ERR(obj))
diff --git a/drivers/gpu/drm/i915/i915_gem_stolen.c b/drivers/gpu/drm/i915/i915_gem_stolen.c
index 335a1ef..4f44531 100644
--- a/drivers/gpu/drm/i915/i915_gem_stolen.c
+++ b/drivers/gpu/drm/i915/i915_gem_stolen.c
@@ -482,6 +482,20 @@  int i915_gem_init_stolen(struct drm_device *dev)
 	 */
 	drm_mm_init(&dev_priv->mm.stolen, 0, dev_priv->gtt.stolen_usable_size);
 
+	/* If the stolen region can be modified behind our backs upon suspend,
+	 * then we cannot use it to store nonvolatile contents (i.e user data)
+	 * as it will be corrupted upon resume.
+	 */
+	dev_priv->mm.nonvolatile_stolen = true;
+#ifdef CONFIG_SUSPEND
+	if (intel_detect_acpi_rst()) {
+		/* BIOSes using RapidStart Technology have been reported
+		 * to overwrite stolen across S3, not just S4.
+		 */
+		dev_priv->mm.nonvolatile_stolen = false;
+	}
+#endif
+
 	return 0;
 }
 
diff --git a/drivers/gpu/drm/i915/intel_acpi.c b/drivers/gpu/drm/i915/intel_acpi.c
index eb638a1..67dc9b2 100644
--- a/drivers/gpu/drm/i915/intel_acpi.c
+++ b/drivers/gpu/drm/i915/intel_acpi.c
@@ -23,6 +23,11 @@  static const u8 intel_dsm_guid[] = {
 	0x0f, 0x13, 0x17, 0xb0, 0x1c, 0x2c
 };
 
+static const struct acpi_device_id irst_ids[] = {
+	{"INT3392", 0},
+	{"", 0}
+};
+
 static char *intel_dsm_port_name(u8 id)
 {
 	switch (id) {
@@ -162,3 +167,8 @@  void intel_register_dsm_handler(void)
 void intel_unregister_dsm_handler(void)
 {
 }
+
+bool intel_detect_acpi_rst(void)
+{
+	return acpi_dev_present(irst_ids[0].id);
+}

Comments

Hi,

On Thu, Feb 04, 2016 at 03:00:11PM +0530, ankitprasad.r.sharma@intel.com wrote:
> From: Ankitprasad Sharma <ankitprasad.r.sharma@intel.com>
> 
> The BIOS RapidStartTechnology may corrupt the stolen memory across S3
> suspend due to unalarmed hibernation, in which case we will not be able
> to preserve the User data stored in the stolen region. Hence this patch
> tries to identify presence of the RST device on the ACPI bus, and
> disables use of stolen memory (for persistent data) if found.
> 
> v2: Updated comment, updated/corrected new functions private to driver
> (Chris/Tvrtko)
> 
> v3: Disabling stolen by default, wait till required acpi changes to
> detect device presence are pulled in (Ankit)
> 
> v4: Enabled stolen by default as required acpi changes are merged
> (Ankit)
> 
> Signed-off-by: Ankitprasad Sharma <ankitprasad.r.sharma@intel.com>
> ---
>  drivers/gpu/drm/i915/i915_drv.h        | 11 +++++++++++
>  drivers/gpu/drm/i915/i915_gem.c        |  8 ++++++++
>  drivers/gpu/drm/i915/i915_gem_stolen.c | 14 ++++++++++++++
>  drivers/gpu/drm/i915/intel_acpi.c      | 10 ++++++++++
>  4 files changed, 43 insertions(+)
> 
> diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
> index 16f2f94..9d67097 100644
> --- a/drivers/gpu/drm/i915/i915_drv.h
> +++ b/drivers/gpu/drm/i915/i915_drv.h
> @@ -1349,6 +1349,16 @@ struct i915_gem_mm {
>  	 */
>  	bool busy;
>  
> +	/**
> +	 * Stolen will be lost upon hibernate (as the memory is unpowered).
> +	 * Across resume, we expect stolen to be intact - however, it may
> +	 * also be utililised by third parties (e.g. Intel RapidStart
> +	 * Technology) and if so we have to assume that any data stored in
> +	 * stolen across resume is lost and we set this flag to indicate that
> +	 * the stolen memory is volatile.
> +	 */
> +	bool nonvolatile_stolen;
> +
>  	/* the indicator for dispatch video commands on two BSD rings */
>  	unsigned int bsd_ring_dispatch_index;
>  
> @@ -3465,6 +3475,7 @@ intel_opregion_notify_adapter(struct drm_device *dev, pci_power_t state)
>  #endif
>  
>  /* intel_acpi.c */
> +bool intel_detect_acpi_rst(void);
>  #ifdef CONFIG_ACPI
>  extern void intel_register_dsm_handler(void);
>  extern void intel_unregister_dsm_handler(void);
> diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
> index 0cd57d4..63dab63 100644
> --- a/drivers/gpu/drm/i915/i915_gem.c
> +++ b/drivers/gpu/drm/i915/i915_gem.c
> @@ -396,8 +396,16 @@ static struct drm_i915_gem_object *
>  i915_gem_alloc_object_stolen(struct drm_device *dev, size_t size)
>  {
>  	struct drm_i915_gem_object *obj;
> +	struct drm_i915_private *dev_priv = dev->dev_private;
>  	int ret;
>  
> +	if (!dev_priv->mm.nonvolatile_stolen) {
> +		/* Stolen may be overwritten by external parties
> +		 * so unsuitable for persistent user data.
> +		 */
> +		return ERR_PTR(-ENODEV);
> +	}
> +
>  	mutex_lock(&dev->struct_mutex);
>  	obj = i915_gem_object_create_stolen(dev, size);
>  	if (IS_ERR(obj))
> diff --git a/drivers/gpu/drm/i915/i915_gem_stolen.c b/drivers/gpu/drm/i915/i915_gem_stolen.c
> index 335a1ef..4f44531 100644
> --- a/drivers/gpu/drm/i915/i915_gem_stolen.c
> +++ b/drivers/gpu/drm/i915/i915_gem_stolen.c
> @@ -482,6 +482,20 @@ int i915_gem_init_stolen(struct drm_device *dev)
>  	 */
>  	drm_mm_init(&dev_priv->mm.stolen, 0, dev_priv->gtt.stolen_usable_size);
>  
> +	/* If the stolen region can be modified behind our backs upon suspend,
> +	 * then we cannot use it to store nonvolatile contents (i.e user data)
> +	 * as it will be corrupted upon resume.
> +	 */
> +	dev_priv->mm.nonvolatile_stolen = true;
> +#ifdef CONFIG_SUSPEND
> +	if (intel_detect_acpi_rst()) {
> +		/* BIOSes using RapidStart Technology have been reported
> +		 * to overwrite stolen across S3, not just S4.
> +		 */
> +		dev_priv->mm.nonvolatile_stolen = false;
> +	}
> +#endif
> +

I'd suggest simplifying it like this:

	dev_priv->mm.nonvolatile_stolen = !(IS_ENABLED(CONFIG_SUSPEND) &&
					    acpi_dev_present("INT3392"));

And add to i915_gem_stolen.c:

	#include <linux/acpi.h>

Best regards,

Lukas

>  	return 0;
>  }
>  
> diff --git a/drivers/gpu/drm/i915/intel_acpi.c b/drivers/gpu/drm/i915/intel_acpi.c
> index eb638a1..67dc9b2 100644
> --- a/drivers/gpu/drm/i915/intel_acpi.c
> +++ b/drivers/gpu/drm/i915/intel_acpi.c
> @@ -23,6 +23,11 @@ static const u8 intel_dsm_guid[] = {
>  	0x0f, 0x13, 0x17, 0xb0, 0x1c, 0x2c
>  };
>  
> +static const struct acpi_device_id irst_ids[] = {
> +	{"INT3392", 0},
> +	{"", 0}
> +};
> +
>  static char *intel_dsm_port_name(u8 id)
>  {
>  	switch (id) {
> @@ -162,3 +167,8 @@ void intel_register_dsm_handler(void)
>  void intel_unregister_dsm_handler(void)
>  {
>  }
> +
> +bool intel_detect_acpi_rst(void)
> +{
> +	return acpi_dev_present(irst_ids[0].id);
> +}
> -- 
> 1.9.1
> 
> _______________________________________________
> Intel-gfx mailing list
> Intel-gfx@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/intel-gfx
On Thu, Feb 04, 2016 at 04:46:55PM +0100, Lukas Wunner wrote:
> > +	/* If the stolen region can be modified behind our backs upon suspend,
> > +	 * then we cannot use it to store nonvolatile contents (i.e user data)
> > +	 * as it will be corrupted upon resume.
> > +	 */
> > +	dev_priv->mm.nonvolatile_stolen = true;
> > +#ifdef CONFIG_SUSPEND
> > +	if (intel_detect_acpi_rst()) {
> > +		/* BIOSes using RapidStart Technology have been reported
> > +		 * to overwrite stolen across S3, not just S4.
> > +		 */
> > +		dev_priv->mm.nonvolatile_stolen = false;
> > +	}
> > +#endif
> > +
> 
> I'd suggest simplifying it like this:
> 
> 	dev_priv->mm.nonvolatile_stolen = !(IS_ENABLED(CONFIG_SUSPEND) &&
> 					    acpi_dev_present("INT3392"));

Using if (IS_ENABLED(CONFIG_SUSPEND) && intel_detect_acpi_rst()) would
be better indeed. My main concern here is that we document carefully why we had
to disable this, and to leave room for future caveats. Hence my
preference for the verbose layout.

We could #define INTEL_RAPID_START "INT339" for
if (IS_ENABLED(CONFIG_SUSPEND) && acpi_dev_present(INTEL_RAPID_START))
but Anki wanted to keep the acpi details themselves out of stolen (hence
the current split).
-Chris
Hi,

On Thu, Feb 04, 2016 at 04:05:04PM +0000, Chris Wilson wrote:
> On Thu, Feb 04, 2016 at 04:46:55PM +0100, Lukas Wunner wrote:
> > > +	/* If the stolen region can be modified behind our backs upon suspend,
> > > +	 * then we cannot use it to store nonvolatile contents (i.e user data)
> > > +	 * as it will be corrupted upon resume.
> > > +	 */
> > > +	dev_priv->mm.nonvolatile_stolen = true;
> > > +#ifdef CONFIG_SUSPEND
> > > +	if (intel_detect_acpi_rst()) {
> > > +		/* BIOSes using RapidStart Technology have been reported
> > > +		 * to overwrite stolen across S3, not just S4.
> > > +		 */
> > > +		dev_priv->mm.nonvolatile_stolen = false;
> > > +	}
> > > +#endif
> > > +
> > 
> > I'd suggest simplifying it like this:
> > 
> > 	dev_priv->mm.nonvolatile_stolen = !(IS_ENABLED(CONFIG_SUSPEND) &&
> > 					    acpi_dev_present("INT3392"));
> 
> Using if (IS_ENABLED(CONFIG_SUSPEND) && intel_detect_acpi_rst()) would
> be better indeed. My main concern here is that we document carefully why
> we had to disable this, and to leave room for future caveats.

Yes absolutely, keep the comments. (Or meld them into one if simplifying
the code as suggested.)

> We could #define INTEL_RAPID_START "INT3392" for
> if (IS_ENABLED(CONFIG_SUSPEND) && acpi_dev_present(INTEL_RAPID_START))
> but Anki wanted to keep the acpi details themselves out of stolen (hence
> the current split).

At the very least the #ifdef needs to be replaced by IS_ENABLED,
Documentation/CodingStyle chapter 20 very clearly states this is
to be preferred.

Less code is almost always better, it's more work for a reader to
follow the logic if things are split across multiple files.

If you absolutely positively want to keep the current split,
the "static const struct acpi_device_id irst_ids[]" data structure
should be replaced by a "static const char*" in order to not waste
memory.

I'd also suggest renaming "dev_priv->mm.nonvolatile_stolen" to
"volatile_stolen" since the only user of the flag uses its negation
and its calculation requires negation as well.

Best regards,

Lukas
On 04/02/16 09:30, ankitprasad.r.sharma@intel.com wrote:
> From: Ankitprasad Sharma <ankitprasad.r.sharma@intel.com>
>
> The BIOS RapidStartTechnology may corrupt the stolen memory across S3
> suspend due to unalarmed hibernation, in which case we will not be able
> to preserve the User data stored in the stolen region. Hence this patch
> tries to identify presence of the RST device on the ACPI bus, and
> disables use of stolen memory (for persistent data) if found.
>
> v2: Updated comment, updated/corrected new functions private to driver
> (Chris/Tvrtko)
>
> v3: Disabling stolen by default, wait till required acpi changes to
> detect device presence are pulled in (Ankit)
>
> v4: Enabled stolen by default as required acpi changes are merged
> (Ankit)
>
> Signed-off-by: Ankitprasad Sharma <ankitprasad.r.sharma@intel.com>
> ---
>   drivers/gpu/drm/i915/i915_drv.h        | 11 +++++++++++
>   drivers/gpu/drm/i915/i915_gem.c        |  8 ++++++++
>   drivers/gpu/drm/i915/i915_gem_stolen.c | 14 ++++++++++++++
>   drivers/gpu/drm/i915/intel_acpi.c      | 10 ++++++++++
>   4 files changed, 43 insertions(+)
>
> diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
> index 16f2f94..9d67097 100644
> --- a/drivers/gpu/drm/i915/i915_drv.h
> +++ b/drivers/gpu/drm/i915/i915_drv.h
> @@ -1349,6 +1349,16 @@ struct i915_gem_mm {
>   	 */
>   	bool busy;
>
> +	/**
> +	 * Stolen will be lost upon hibernate (as the memory is unpowered).
> +	 * Across resume, we expect stolen to be intact - however, it may
> +	 * also be utililised by third parties (e.g. Intel RapidStart
> +	 * Technology) and if so we have to assume that any data stored in
> +	 * stolen across resume is lost and we set this flag to indicate that
> +	 * the stolen memory is volatile.
> +	 */
> +	bool nonvolatile_stolen;

I agree with a point made by Lukas that volatile_stolen would be better. 
Even the comment above suggests so. :)

> +
>   	/* the indicator for dispatch video commands on two BSD rings */
>   	unsigned int bsd_ring_dispatch_index;
>
> @@ -3465,6 +3475,7 @@ intel_opregion_notify_adapter(struct drm_device *dev, pci_power_t state)
>   #endif
>
>   /* intel_acpi.c */
> +bool intel_detect_acpi_rst(void);
>   #ifdef CONFIG_ACPI
>   extern void intel_register_dsm_handler(void);
>   extern void intel_unregister_dsm_handler(void);
> diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
> index 0cd57d4..63dab63 100644
> --- a/drivers/gpu/drm/i915/i915_gem.c
> +++ b/drivers/gpu/drm/i915/i915_gem.c
> @@ -396,8 +396,16 @@ static struct drm_i915_gem_object *
>   i915_gem_alloc_object_stolen(struct drm_device *dev, size_t size)
>   {
>   	struct drm_i915_gem_object *obj;
> +	struct drm_i915_private *dev_priv = dev->dev_private;
>   	int ret;
>
> +	if (!dev_priv->mm.nonvolatile_stolen) {
> +		/* Stolen may be overwritten by external parties
> +		 * so unsuitable for persistent user data.
> +		 */
> +		return ERR_PTR(-ENODEV);
> +	}
> +
>   	mutex_lock(&dev->struct_mutex);
>   	obj = i915_gem_object_create_stolen(dev, size);
>   	if (IS_ERR(obj))
> diff --git a/drivers/gpu/drm/i915/i915_gem_stolen.c b/drivers/gpu/drm/i915/i915_gem_stolen.c
> index 335a1ef..4f44531 100644
> --- a/drivers/gpu/drm/i915/i915_gem_stolen.c
> +++ b/drivers/gpu/drm/i915/i915_gem_stolen.c
> @@ -482,6 +482,20 @@ int i915_gem_init_stolen(struct drm_device *dev)
>   	 */
>   	drm_mm_init(&dev_priv->mm.stolen, 0, dev_priv->gtt.stolen_usable_size);
>
> +	/* If the stolen region can be modified behind our backs upon suspend,
> +	 * then we cannot use it to store nonvolatile contents (i.e user data)
> +	 * as it will be corrupted upon resume.
> +	 */
> +	dev_priv->mm.nonvolatile_stolen = true;
> +#ifdef CONFIG_SUSPEND
> +	if (intel_detect_acpi_rst()) {
> +		/* BIOSes using RapidStart Technology have been reported
> +		 * to overwrite stolen across S3, not just S4.
> +		 */
> +		dev_priv->mm.nonvolatile_stolen = false;
> +	}
> +#endif

I also agree with the IS_ENABLED(CONFIG_SUSPEND) suggestion.

> +
>   	return 0;
>   }
>
> diff --git a/drivers/gpu/drm/i915/intel_acpi.c b/drivers/gpu/drm/i915/intel_acpi.c
> index eb638a1..67dc9b2 100644
> --- a/drivers/gpu/drm/i915/intel_acpi.c
> +++ b/drivers/gpu/drm/i915/intel_acpi.c
> @@ -23,6 +23,11 @@ static const u8 intel_dsm_guid[] = {
>   	0x0f, 0x13, 0x17, 0xb0, 0x1c, 0x2c
>   };
>
> +static const struct acpi_device_id irst_ids[] = {
> +	{"INT3392", 0},
> +	{"", 0}
> +};
> +
>   static char *intel_dsm_port_name(u8 id)
>   {
>   	switch (id) {
> @@ -162,3 +167,8 @@ void intel_register_dsm_handler(void)
>   void intel_unregister_dsm_handler(void)
>   {
>   }
> +
> +bool intel_detect_acpi_rst(void)
> +{
> +	return acpi_dev_present(irst_ids[0].id);
> +}

I also agree with Lukas'es suggestion to get rid of this and just do:

dev_priv->mm.volatile_stolen = IS_ENABLED(CONFIG_SUSPEND) &&
			       acpi_dev_present("INT3392");

With the existing comment it should be OK.

Regards,

Tvrtko
Hi Ankitprasad,

On Thu, Feb 04, 2016 at 05:43:17PM +0100, Lukas Wunner wrote:
> On Thu, Feb 04, 2016 at 04:05:04PM +0000, Chris Wilson wrote:
> > We could #define INTEL_RAPID_START "INT3392" for
> > if (IS_ENABLED(CONFIG_SUSPEND) && acpi_dev_present(INTEL_RAPID_START))
> > but Anki wanted to keep the acpi details themselves out of stolen (hence
> > the current split).
> 
> Less code is almost always better, it's more work for a reader to
> follow the logic if things are split across multiple files.
> 
> If you absolutely positively want to keep the current split,
> the "static const struct acpi_device_id irst_ids[]" data structure
> should be replaced by a "static const char*" in order to not waste
> memory.

As I've learned the hard way yesterday, acpi_dev_present() is undefined
if the kernel is compiled without CONFIG_ACPI, so you made the right
call splitting that out into intel_acpi.c and my suggested simplification
was wrong. Sorry for the noise. :-/

I'd still suggest to invoke acpi_dev_present() with the string literal
"INT3392" in intel_acpi.c:intel_detect_acpi_rst() though, or at least
replace "static const struct acpi_device_id irst_ids[]" with
"static const char*".

Best regards,

Lukas