Commit d3ac8d42 authored by Daniele Ceraolo Spurio's avatar Daniele Ceraolo Spurio Committed by Rodrigo Vivi
Browse files

drm/i915/pxp: interfaces for using protected objects



This api allow user mode to create protected buffers and to mark
contexts as making use of such objects. Only when using contexts
marked in such a way is the execution guaranteed to work as expected.

Contexts can only be marked as using protected content at creation time
(i.e. the parameter is immutable) and they must be both bannable and not
recoverable. Given that the protected session gets invalidated on
suspend, contexts created this way hold a runtime pm wakeref until
they're either destroyed or invalidated.

All protected objects and contexts will be considered invalid when the
PXP session is destroyed and all new submissions using them will be
rejected. All intel contexts within the invalidated gem contexts will be
marked banned. Userspace can detect that an invalidation has occurred via
the RESET_STATS ioctl, where we report it the same way as a ban due to a
hang.

v5: squash patches, rebase on proto_ctx, update kerneldoc

v6: rebase on obj create_ext changes

v7: Use session counter to check if an object it valid, hold wakeref in
    context, don't add a new flag to RESET_STATS (Daniel)

v8: don't increase guilty count for contexts banned during pxp
    invalidation (Rodrigo)

v9: better comments, avoid wakeref put race between pxp_inval and
    context_close, add usage examples (Rodrigo)

v10: modify internal set/get-protected-context functions to not
     return -ENODEV when setting PXP param to false or getting param
     when running on pxp-unsupported hw or getting param when i915
     was built with CONFIG_PXP off

Signed-off-by: default avatarAlan Previn <alan.previn.teres.alexis@intel.com>
Signed-off-by: default avatarDaniele Ceraolo Spurio <daniele.ceraolospurio@intel.com>
Signed-off-by: default avatarBommu Krishnaiah <krishnaiah.bommu@intel.com>
Cc: Rodrigo Vivi <rodrigo.vivi@intel.com>
Cc: Chris Wilson <chris@chris-wilson.co.uk>
Cc: Lionel Landwerlin <lionel.g.landwerlin@intel.com>
Cc: Jason Ekstrand <jason@jlekstrand.net>
Cc: Daniel Vetter <daniel.vetter@intel.com>
Reviewed-by: default avatarRodrigo Vivi <rodrigo.vivi@intel.com>
Signed-off-by: default avatarRodrigo Vivi <rodrigo.vivi@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20210924191452.1539378-11-alan.previn.teres.alexis@intel.com
parent 2ae09687
Loading
Loading
Loading
Loading
+81 −14
Original line number Diff line number Diff line
@@ -77,6 +77,8 @@
#include "gt/intel_gpu_commands.h"
#include "gt/intel_ring.h"

#include "pxp/intel_pxp.h"

#include "i915_gem_context.h"
#include "i915_trace.h"
#include "i915_user_extensions.h"
@@ -186,10 +188,13 @@ static int validate_priority(struct drm_i915_private *i915,
	return 0;
}

static void proto_context_close(struct i915_gem_proto_context *pc)
static void proto_context_close(struct drm_i915_private *i915,
				struct i915_gem_proto_context *pc)
{
	int i;

	if (pc->pxp_wakeref)
		intel_runtime_pm_put(&i915->runtime_pm, pc->pxp_wakeref);
	if (pc->vm)
		i915_vm_put(pc->vm);
	if (pc->user_engines) {
@@ -241,6 +246,33 @@ static int proto_context_set_persistence(struct drm_i915_private *i915,
	return 0;
}

static int proto_context_set_protected(struct drm_i915_private *i915,
				       struct i915_gem_proto_context *pc,
				       bool protected)
{
	int ret = 0;

	if (!protected) {
		pc->uses_protected_content = false;
	} else if (!intel_pxp_is_enabled(&i915->gt.pxp)) {
		ret = -ENODEV;
	} else if ((pc->user_flags & BIT(UCONTEXT_RECOVERABLE)) ||
		   !(pc->user_flags & BIT(UCONTEXT_BANNABLE))) {
		ret = -EPERM;
	} else {
		pc->uses_protected_content = true;

		/*
		 * protected context usage requires the PXP session to be up,
		 * which in turn requires the device to be active.
		 */
		pc->pxp_wakeref = intel_runtime_pm_get(&i915->runtime_pm);
		ret = intel_pxp_wait_for_arb_start(&i915->gt.pxp);
	}

	return ret;
}

static struct i915_gem_proto_context *
proto_context_create(struct drm_i915_private *i915, unsigned int flags)
{
@@ -269,7 +301,7 @@ proto_context_create(struct drm_i915_private *i915, unsigned int flags)
	return pc;

proto_close:
	proto_context_close(pc);
	proto_context_close(i915, pc);
	return err;
}

@@ -693,6 +725,8 @@ static int set_proto_ctx_param(struct drm_i915_file_private *fpriv,
			ret = -EPERM;
		else if (args->value)
			pc->user_flags |= BIT(UCONTEXT_BANNABLE);
		else if (pc->uses_protected_content)
			ret = -EPERM;
		else
			pc->user_flags &= ~BIT(UCONTEXT_BANNABLE);
		break;
@@ -700,10 +734,12 @@ static int set_proto_ctx_param(struct drm_i915_file_private *fpriv,
	case I915_CONTEXT_PARAM_RECOVERABLE:
		if (args->size)
			ret = -EINVAL;
		else if (args->value)
			pc->user_flags |= BIT(UCONTEXT_RECOVERABLE);
		else
		else if (!args->value)
			pc->user_flags &= ~BIT(UCONTEXT_RECOVERABLE);
		else if (pc->uses_protected_content)
			ret = -EPERM;
		else
			pc->user_flags |= BIT(UCONTEXT_RECOVERABLE);
		break;

	case I915_CONTEXT_PARAM_PRIORITY:
@@ -731,6 +767,11 @@ static int set_proto_ctx_param(struct drm_i915_file_private *fpriv,
						    args->value);
		break;

	case I915_CONTEXT_PARAM_PROTECTED_CONTENT:
		ret = proto_context_set_protected(fpriv->dev_priv, pc,
						  args->value);
		break;

	case I915_CONTEXT_PARAM_NO_ZEROMAP:
	case I915_CONTEXT_PARAM_BAN_PERIOD:
	case I915_CONTEXT_PARAM_RINGSIZE:
@@ -956,6 +997,9 @@ static void i915_gem_context_release_work(struct work_struct *work)
	if (vm)
		i915_vm_put(vm);

	if (ctx->pxp_wakeref)
		intel_runtime_pm_put(&ctx->i915->runtime_pm, ctx->pxp_wakeref);

	mutex_destroy(&ctx->engines_mutex);
	mutex_destroy(&ctx->lut_mutex);

@@ -1338,6 +1382,11 @@ i915_gem_create_context(struct drm_i915_private *i915,
			goto err_engines;
	}

	if (pc->uses_protected_content) {
		ctx->pxp_wakeref = intel_runtime_pm_get(&i915->runtime_pm);
		ctx->uses_protected_content = true;
	}

	trace_i915_context_create(ctx);

	return ctx;
@@ -1409,7 +1458,7 @@ int i915_gem_context_open(struct drm_i915_private *i915,
	}

	ctx = i915_gem_create_context(i915, pc);
	proto_context_close(pc);
	proto_context_close(i915, pc);
	if (IS_ERR(ctx)) {
		err = PTR_ERR(ctx);
		goto err;
@@ -1436,7 +1485,7 @@ void i915_gem_context_close(struct drm_file *file)
	unsigned long idx;

	xa_for_each(&file_priv->proto_context_xa, idx, pc)
		proto_context_close(pc);
		proto_context_close(file_priv->dev_priv, pc);
	xa_destroy(&file_priv->proto_context_xa);
	mutex_destroy(&file_priv->proto_context_lock);

@@ -1731,6 +1780,15 @@ static int set_priority(struct i915_gem_context *ctx,
	return 0;
}

static int get_protected(struct i915_gem_context *ctx,
			 struct drm_i915_gem_context_param *args)
{
	args->size = 0;
	args->value = i915_gem_context_uses_protected_content(ctx);

	return 0;
}

static int ctx_setparam(struct drm_i915_file_private *fpriv,
			struct i915_gem_context *ctx,
			struct drm_i915_gem_context_param *args)
@@ -1754,6 +1812,8 @@ static int ctx_setparam(struct drm_i915_file_private *fpriv,
			ret = -EPERM;
		else if (args->value)
			i915_gem_context_set_bannable(ctx);
		else if (i915_gem_context_uses_protected_content(ctx))
			ret = -EPERM; /* can't clear this for protected contexts */
		else
			i915_gem_context_clear_bannable(ctx);
		break;
@@ -1761,10 +1821,12 @@ static int ctx_setparam(struct drm_i915_file_private *fpriv,
	case I915_CONTEXT_PARAM_RECOVERABLE:
		if (args->size)
			ret = -EINVAL;
		else if (args->value)
			i915_gem_context_set_recoverable(ctx);
		else
		else if (!args->value)
			i915_gem_context_clear_recoverable(ctx);
		else if (i915_gem_context_uses_protected_content(ctx))
			ret = -EPERM; /* can't set this for protected contexts */
		else
			i915_gem_context_set_recoverable(ctx);
		break;

	case I915_CONTEXT_PARAM_PRIORITY:
@@ -1779,6 +1841,7 @@ static int ctx_setparam(struct drm_i915_file_private *fpriv,
		ret = set_persistence(ctx, args);
		break;

	case I915_CONTEXT_PARAM_PROTECTED_CONTENT:
	case I915_CONTEXT_PARAM_NO_ZEROMAP:
	case I915_CONTEXT_PARAM_BAN_PERIOD:
	case I915_CONTEXT_PARAM_RINGSIZE:
@@ -1857,7 +1920,7 @@ finalize_create_context_locked(struct drm_i915_file_private *file_priv,

	old = xa_erase(&file_priv->proto_context_xa, id);
	GEM_BUG_ON(old != pc);
	proto_context_close(pc);
	proto_context_close(file_priv->dev_priv, pc);

	/* One for the xarray and one for the caller */
	return i915_gem_context_get(ctx);
@@ -1943,7 +2006,7 @@ int i915_gem_context_create_ioctl(struct drm_device *dev, void *data,
			goto err_pc;
		}

		proto_context_close(ext_data.pc);
		proto_context_close(i915, ext_data.pc);
		gem_context_register(ctx, ext_data.fpriv, id);
	} else {
		ret = proto_context_register(ext_data.fpriv, ext_data.pc, &id);
@@ -1957,7 +2020,7 @@ int i915_gem_context_create_ioctl(struct drm_device *dev, void *data,
	return 0;

err_pc:
	proto_context_close(ext_data.pc);
	proto_context_close(i915, ext_data.pc);
	return ret;
}

@@ -1988,7 +2051,7 @@ int i915_gem_context_destroy_ioctl(struct drm_device *dev, void *data,
	GEM_WARN_ON(ctx && pc);

	if (pc)
		proto_context_close(pc);
		proto_context_close(file_priv->dev_priv, pc);

	if (ctx)
		context_close(ctx);
@@ -2106,6 +2169,10 @@ int i915_gem_context_getparam_ioctl(struct drm_device *dev, void *data,
		args->value = i915_gem_context_is_persistent(ctx);
		break;

	case I915_CONTEXT_PARAM_PROTECTED_CONTENT:
		ret = get_protected(ctx, args);
		break;

	case I915_CONTEXT_PARAM_NO_ZEROMAP:
	case I915_CONTEXT_PARAM_BAN_PERIOD:
	case I915_CONTEXT_PARAM_ENGINES:
+6 −0
Original line number Diff line number Diff line
@@ -108,6 +108,12 @@ i915_gem_context_clear_user_engines(struct i915_gem_context *ctx)
	clear_bit(CONTEXT_USER_ENGINES, &ctx->flags);
}

static inline bool
i915_gem_context_uses_protected_content(const struct i915_gem_context *ctx)
{
	return ctx->uses_protected_content;
}

/* i915_gem_context.c */
void i915_gem_init__contexts(struct drm_i915_private *i915);

+28 −0
Original line number Diff line number Diff line
@@ -198,6 +198,12 @@ struct i915_gem_proto_context {

	/** @single_timeline: See See &i915_gem_context.syncobj */
	bool single_timeline;

	/** @uses_protected_content: See &i915_gem_context.uses_protected_content */
	bool uses_protected_content;

	/** @pxp_wakeref: See &i915_gem_context.pxp_wakeref */
	intel_wakeref_t pxp_wakeref;
};

/**
@@ -321,6 +327,28 @@ struct i915_gem_context {
#define CONTEXT_CLOSED			0
#define CONTEXT_USER_ENGINES		1

	/**
	 * @uses_protected_content: context uses PXP-encrypted objects.
	 *
	 * This flag can only be set at ctx creation time and it's immutable for
	 * the lifetime of the context. See I915_CONTEXT_PARAM_PROTECTED_CONTENT
	 * in uapi/drm/i915_drm.h for more info on setting restrictions and
	 * expected behaviour of marked contexts.
	 */
	bool uses_protected_content;

	/**
	 * @pxp_wakeref: wakeref to keep the device awake when PXP is in use
	 *
	 * PXP sessions are invalidated when the device is suspended, which in
	 * turns invalidates all contexts and objects using it. To keep the
	 * flow simple, we keep the device awake when contexts using PXP objects
	 * are in use. It is expected that the userspace application only uses
	 * PXP when the display is on, so taking a wakeref here shouldn't worsen
	 * our power metrics.
	 */
	intel_wakeref_t pxp_wakeref;

	/** @mutex: guards everything that isn't engines or handles_vma */
	struct mutex mutex;

+54 −18
Original line number Diff line number Diff line
@@ -6,6 +6,7 @@
#include "gem/i915_gem_ioctls.h"
#include "gem/i915_gem_lmem.h"
#include "gem/i915_gem_region.h"
#include "pxp/intel_pxp.h"

#include "i915_drv.h"
#include "i915_trace.h"
@@ -82,21 +83,11 @@ static int i915_gem_publish(struct drm_i915_gem_object *obj,
	return 0;
}

/**
 * Creates a new object using the same path as DRM_I915_GEM_CREATE_EXT
 * @i915: i915 private
 * @size: size of the buffer, in bytes
 * @placements: possible placement regions, in priority order
 * @n_placements: number of possible placement regions
 *
 * This function is exposed primarily for selftests and does very little
 * error checking.  It is assumed that the set of placement regions has
 * already been verified to be valid.
 */
struct drm_i915_gem_object *
__i915_gem_object_create_user(struct drm_i915_private *i915, u64 size,
static struct drm_i915_gem_object *
__i915_gem_object_create_user_ext(struct drm_i915_private *i915, u64 size,
				  struct intel_memory_region **placements,
			      unsigned int n_placements)
				  unsigned int n_placements,
				  unsigned int ext_flags)
{
	struct intel_memory_region *mr = placements[0];
	struct drm_i915_gem_object *obj;
@@ -135,6 +126,9 @@ __i915_gem_object_create_user(struct drm_i915_private *i915, u64 size,

	GEM_BUG_ON(size != obj->base.size);

	/* Add any flag set by create_ext options */
	obj->flags |= ext_flags;

	trace_i915_gem_object_create(obj);
	return obj;

@@ -145,6 +139,26 @@ __i915_gem_object_create_user(struct drm_i915_private *i915, u64 size,
	return ERR_PTR(ret);
}

/**
 * Creates a new object using the same path as DRM_I915_GEM_CREATE_EXT
 * @i915: i915 private
 * @size: size of the buffer, in bytes
 * @placements: possible placement regions, in priority order
 * @n_placements: number of possible placement regions
 *
 * This function is exposed primarily for selftests and does very little
 * error checking.  It is assumed that the set of placement regions has
 * already been verified to be valid.
 */
struct drm_i915_gem_object *
__i915_gem_object_create_user(struct drm_i915_private *i915, u64 size,
			      struct intel_memory_region **placements,
			      unsigned int n_placements)
{
	return __i915_gem_object_create_user_ext(i915, size, placements,
						 n_placements, 0);
}

int
i915_gem_dumb_create(struct drm_file *file,
		     struct drm_device *dev,
@@ -224,6 +238,7 @@ struct create_ext {
	struct drm_i915_private *i915;
	struct intel_memory_region *placements[INTEL_REGION_UNKNOWN];
	unsigned int n_placements;
	unsigned long flags;
};

static void repr_placements(char *buf, size_t size,
@@ -353,8 +368,28 @@ static int ext_set_placements(struct i915_user_extension __user *base,
	return set_placements(&ext, data);
}

static int ext_set_protected(struct i915_user_extension __user *base, void *data)
{
	struct drm_i915_gem_create_ext_protected_content ext;
	struct create_ext *ext_data = data;

	if (copy_from_user(&ext, base, sizeof(ext)))
		return -EFAULT;

	if (ext.flags)
		return -EINVAL;

	if (!intel_pxp_is_enabled(&ext_data->i915->gt.pxp))
		return -ENODEV;

	ext_data->flags |= I915_BO_PROTECTED;

	return 0;
}

static const i915_user_extension_fn create_extensions[] = {
	[I915_GEM_CREATE_EXT_MEMORY_REGIONS] = ext_set_placements,
	[I915_GEM_CREATE_EXT_PROTECTED_CONTENT] = ext_set_protected,
};

/**
@@ -389,9 +424,10 @@ i915_gem_create_ext_ioctl(struct drm_device *dev, void *data,
		ext_data.n_placements = 1;
	}

	obj = __i915_gem_object_create_user(i915, args->size,
	obj = __i915_gem_object_create_user_ext(i915, args->size,
						ext_data.placements,
					    ext_data.n_placements);
						ext_data.n_placements,
						ext_data.flags);
	if (IS_ERR(obj))
		return PTR_ERR(obj);

+18 −0
Original line number Diff line number Diff line
@@ -21,6 +21,8 @@
#include "gt/intel_gt_pm.h"
#include "gt/intel_ring.h"

#include "pxp/intel_pxp.h"

#include "i915_drv.h"
#include "i915_gem_clflush.h"
#include "i915_gem_context.h"
@@ -810,6 +812,22 @@ static struct i915_vma *eb_lookup_vma(struct i915_execbuffer *eb, u32 handle)
		if (unlikely(!obj))
			return ERR_PTR(-ENOENT);

		/*
		 * If the user has opted-in for protected-object tracking, make
		 * sure the object encryption can be used.
		 * We only need to do this when the object is first used with
		 * this context, because the context itself will be banned when
		 * the protected objects become invalid.
		 */
		if (i915_gem_context_uses_protected_content(eb->gem_context) &&
		    i915_gem_object_is_protected(obj)) {
			err = intel_pxp_key_check(&vm->gt->pxp, obj);
			if (err) {
				i915_gem_object_put(obj);
				return ERR_PTR(err);
			}
		}

		vma = i915_vma_instance(obj, vm, NULL);
		if (IS_ERR(vma)) {
			i915_gem_object_put(obj);
Loading