#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"
        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) {
        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)
 {
        return pc;
 
 proto_close:
-       proto_context_close(pc);
+       proto_context_close(i915, pc);
        return err;
 }
 
                        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;
        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:
                                                    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:
        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);
 
                        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;
        }
 
        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;
        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);
 
        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)
                        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;
        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:
                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:
 
        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);
                        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);
        return 0;
 
 err_pc:
-       proto_context_close(ext_data.pc);
+       proto_context_close(i915, ext_data.pc);
        return ret;
 }
 
        GEM_WARN_ON(ctx && pc);
 
        if (pc)
-               proto_context_close(pc);
+               proto_context_close(file_priv->dev_priv, pc);
 
        if (ctx)
                context_close(ctx);
                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:
 
        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);
 
 
 
        /** @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;
 };
 
 /**
 #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;
 
 
 #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"
        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,
-                             struct intel_memory_region **placements,
-                             unsigned int n_placements)
+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 ext_flags)
 {
        struct intel_memory_region *mr = placements[0];
        struct drm_i915_gem_object *obj;
 
        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;
 
        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,
        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,
        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,
 };
 
 /**
                ext_data.n_placements = 1;
        }
 
-       obj = __i915_gem_object_create_user(i915, args->size,
-                                           ext_data.placements,
-                                           ext_data.n_placements);
+       obj = __i915_gem_object_create_user_ext(i915, args->size,
+                                               ext_data.placements,
+                                               ext_data.n_placements,
+                                               ext_data.flags);
        if (IS_ERR(obj))
                return PTR_ERR(obj);
 
 
 #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"
                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);
 
 #include <linux/sched/mm.h>
 
 #include "display/intel_frontbuffer.h"
+#include "pxp/intel_pxp.h"
 #include "i915_drv.h"
 #include "i915_gem_clflush.h"
 #include "i915_gem_context.h"
 
        clear_bit(I915_TILING_QUIRK_BIT, &obj->flags);
 }
 
+static inline bool
+i915_gem_object_is_protected(const struct drm_i915_gem_object *obj)
+{
+       return obj->flags & I915_BO_PROTECTED;
+}
+
 static inline bool
 i915_gem_object_type_has(const struct drm_i915_gem_object *obj,
                         unsigned long flags)
 
                             I915_BO_ALLOC_PM_EARLY)
 #define I915_BO_READONLY          BIT(6)
 #define I915_TILING_QUIRK_BIT     7 /* unknown swizzling; do not release! */
-
+#define I915_BO_PROTECTED         BIT(8)
        /**
         * @mem_flags - Mutable placement-related flags
         *
                bool created:1;
        } ttm;
 
+       /*
+        * Record which PXP key instance this object was created against (if
+        * any), so we can use it to determine if the encryption is valid by
+        * comparing against the current key instance.
+        */
+       u32 pxp_key_instance;
+
        /** Record of address bit 17 of each page at last unbind. */
        unsigned long *bit_17;
 
 
                return ERR_CAST(pc);
 
        ctx = i915_gem_create_context(i915, pc);
-       proto_context_close(pc);
+       proto_context_close(i915, pc);
        if (IS_ERR(ctx))
                return ctx;
 
        }
 
        ctx = i915_gem_create_context(i915, pc);
-       proto_context_close(pc);
+       proto_context_close(i915, pc);
        if (IS_ERR(ctx))
                return ctx;
 
 
 #include "intel_pxp_irq.h"
 #include "intel_pxp_session.h"
 #include "intel_pxp_tee.h"
+#include "gem/i915_gem_context.h"
 #include "gt/intel_context.h"
 #include "i915_drv.h"
 
 
        intel_pxp_irq_disable(pxp);
 }
+
+int intel_pxp_key_check(struct intel_pxp *pxp, struct drm_i915_gem_object *obj)
+{
+       if (!intel_pxp_is_active(pxp))
+               return -ENODEV;
+
+       if (!i915_gem_object_is_protected(obj))
+               return -EINVAL;
+
+       GEM_BUG_ON(!pxp->key_instance);
+
+       /*
+        * If this is the first time we're using this object, it's not
+        * encrypted yet; it will be encrypted with the current key, so mark it
+        * as such. If the object is already encrypted, check instead if the
+        * used key is still valid.
+        */
+       if (!obj->pxp_key_instance)
+               obj->pxp_key_instance = pxp->key_instance;
+       else if (obj->pxp_key_instance != pxp->key_instance)
+               return -ENOEXEC;
+
+       return 0;
+}
+
+void intel_pxp_invalidate(struct intel_pxp *pxp)
+{
+       struct drm_i915_private *i915 = pxp_to_gt(pxp)->i915;
+       struct i915_gem_context *ctx, *cn;
+
+       /* ban all contexts marked as protected */
+       spin_lock_irq(&i915->gem.contexts.lock);
+       list_for_each_entry_safe(ctx, cn, &i915->gem.contexts.list, link) {
+               struct i915_gem_engines_iter it;
+               struct intel_context *ce;
+
+               if (!kref_get_unless_zero(&ctx->ref))
+                       continue;
+
+               if (likely(!i915_gem_context_uses_protected_content(ctx))) {
+                       i915_gem_context_put(ctx);
+                       continue;
+               }
+
+               spin_unlock_irq(&i915->gem.contexts.lock);
+
+               /*
+                * By the time we get here we are either going to suspend with
+                * quiesced execution or the HW keys are already long gone and
+                * in this case it is worthless to attempt to close the context
+                * and wait for its execution. It will hang the GPU if it has
+                * not already. So, as a fast mitigation, we can ban the
+                * context as quick as we can. That might race with the
+                * execbuffer, but currently this is the best that can be done.
+                */
+               for_each_gem_engine(ce, i915_gem_context_lock_engines(ctx), it)
+                       intel_context_ban(ce, NULL);
+               i915_gem_context_unlock_engines(ctx);
+
+               /*
+                * The context has been banned, no need to keep the wakeref.
+                * This is safe from races because the only other place this
+                * is touched is context_release and we're holding a ctx ref
+                */
+               if (ctx->pxp_wakeref) {
+                       intel_runtime_pm_put(&i915->runtime_pm,
+                                            ctx->pxp_wakeref);
+                       ctx->pxp_wakeref = 0;
+               }
+
+               spin_lock_irq(&i915->gem.contexts.lock);
+               list_safe_reset_next(ctx, cn, link);
+               i915_gem_context_put(ctx);
+       }
+       spin_unlock_irq(&i915->gem.contexts.lock);
+}
 
 
 #include "intel_pxp_types.h"
 
+struct drm_i915_gem_object;
+
 static inline bool intel_pxp_is_enabled(const struct intel_pxp *pxp)
 {
        return pxp->ce;
 
 void intel_pxp_mark_termination_in_progress(struct intel_pxp *pxp);
 int intel_pxp_wait_for_arb_start(struct intel_pxp *pxp);
+
+int intel_pxp_key_check(struct intel_pxp *pxp, struct drm_i915_gem_object *obj);
+
+void intel_pxp_invalidate(struct intel_pxp *pxp);
 #else
 static inline void intel_pxp_init(struct intel_pxp *pxp)
 {
 {
        return -ENODEV;
 }
+
+static inline int intel_pxp_key_check(struct intel_pxp *pxp,
+                                     struct drm_i915_gem_object *obj)
+{
+       return -ENODEV;
+}
 #endif
 
 #endif /* __INTEL_PXP_H__ */
 
                return ret;
        }
 
+       if (!++pxp->key_instance)
+               ++pxp->key_instance;
+
        pxp->arb_is_valid = true;
 
        return 0;
        /* must mark termination in progress calling this function */
        GEM_WARN_ON(pxp->arb_is_valid);
 
+       /* invalidate protected objects */
+       intel_pxp_invalidate(pxp);
+
        /* terminate the hw sessions */
        ret = intel_pxp_terminate_session(pxp, ARB_SESSION);
        if (ret) {
 
 #define __INTEL_PXP_TYPES_H__
 
 #include <linux/completion.h>
+#include <linux/list.h>
 #include <linux/mutex.h>
+#include <linux/spinlock.h>
 #include <linux/types.h>
 #include <linux/workqueue.h>
 
         */
        bool arb_is_valid;
 
+       /*
+        * Keep track of which key instance we're on, so we can use it to
+        * determine if an object was created using the current key or a
+        * previous one.
+        */
+       u32 key_instance;
+
        struct mutex tee_mutex; /* protects the tee channel binding */
 
        /*
 
  * attempted to use it, never re-use this context param number.
  */
 #define I915_CONTEXT_PARAM_RINGSIZE    0xc
+
+/*
+ * I915_CONTEXT_PARAM_PROTECTED_CONTENT:
+ *
+ * Mark that the context makes use of protected content, which will result
+ * in the context being invalidated when the protected content session is.
+ * Given that the protected content session is killed on suspend, the device
+ * is kept awake for the lifetime of a protected context, so the user should
+ * make sure to dispose of them once done.
+ * This flag can only be set at context creation time and, when set to true,
+ * must be preceded by an explicit setting of I915_CONTEXT_PARAM_RECOVERABLE
+ * to false. This flag can't be set to true in conjunction with setting the
+ * I915_CONTEXT_PARAM_BANNABLE flag to false. Creation example:
+ *
+ * .. code-block:: C
+ *
+ *     struct drm_i915_gem_context_create_ext_setparam p_protected = {
+ *             .base = {
+ *                     .name = I915_CONTEXT_CREATE_EXT_SETPARAM,
+ *             },
+ *             .param = {
+ *                     .param = I915_CONTEXT_PARAM_PROTECTED_CONTENT,
+ *                     .value = 1,
+ *             }
+ *     };
+ *     struct drm_i915_gem_context_create_ext_setparam p_norecover = {
+ *             .base = {
+ *                     .name = I915_CONTEXT_CREATE_EXT_SETPARAM,
+ *                     .next_extension = to_user_pointer(&p_protected),
+ *             },
+ *             .param = {
+ *                     .param = I915_CONTEXT_PARAM_RECOVERABLE,
+ *                     .value = 0,
+ *             }
+ *     };
+ *     struct drm_i915_gem_context_create_ext create = {
+ *             .flags = I915_CONTEXT_CREATE_FLAGS_USE_EXTENSIONS,
+ *             .extensions = to_user_pointer(&p_norecover);
+ *     };
+ *
+ *     ctx_id = gem_context_create_ext(drm_fd, &create);
+ *
+ * In addition to the normal failure cases, setting this flag during context
+ * creation can result in the following errors:
+ *
+ * -ENODEV: feature not available
+ * -EPERM: trying to mark a recoverable or not bannable context as protected
+ */
+#define I915_CONTEXT_PARAM_PROTECTED_CONTENT    0xd
 /* Must be kept compact -- no holes and well documented */
 
        __u64 value;
         *
         * For I915_GEM_CREATE_EXT_MEMORY_REGIONS usage see
         * struct drm_i915_gem_create_ext_memory_regions.
+        *
+        * For I915_GEM_CREATE_EXT_PROTECTED_CONTENT usage see
+        * struct drm_i915_gem_create_ext_protected_content.
         */
 #define I915_GEM_CREATE_EXT_MEMORY_REGIONS 0
+#define I915_GEM_CREATE_EXT_PROTECTED_CONTENT 1
        __u64 extensions;
 };
 
        __u64 regions;
 };
 
+/**
+ * struct drm_i915_gem_create_ext_protected_content - The
+ * I915_OBJECT_PARAM_PROTECTED_CONTENT extension.
+ *
+ * If this extension is provided, buffer contents are expected to be protected
+ * by PXP encryption and require decryption for scan out and processing. This
+ * is only possible on platforms that have PXP enabled, on all other scenarios
+ * using this extension will cause the ioctl to fail and return -ENODEV. The
+ * flags parameter is reserved for future expansion and must currently be set
+ * to zero.
+ *
+ * The buffer contents are considered invalid after a PXP session teardown.
+ *
+ * The encryption is guaranteed to be processed correctly only if the object
+ * is submitted with a context created using the
+ * I915_CONTEXT_PARAM_PROTECTED_CONTENT flag. This will also enable extra checks
+ * at submission time on the validity of the objects involved.
+ *
+ * Below is an example on how to create a protected object:
+ *
+ * .. code-block:: C
+ *
+ *      struct drm_i915_gem_create_ext_protected_content protected_ext = {
+ *              .base = { .name = I915_GEM_CREATE_EXT_PROTECTED_CONTENT },
+ *              .flags = 0,
+ *      };
+ *      struct drm_i915_gem_create_ext create_ext = {
+ *              .size = PAGE_SIZE,
+ *              .extensions = (uintptr_t)&protected_ext,
+ *      };
+ *
+ *      int err = ioctl(fd, DRM_IOCTL_I915_GEM_CREATE_EXT, &create_ext);
+ *      if (err) ...
+ */
+struct drm_i915_gem_create_ext_protected_content {
+       /** @base: Extension link. See struct i915_user_extension. */
+       struct i915_user_extension base;
+       /** @flags: reserved for future usage, currently MBZ */
+       __u32 flags;
+};
+
 /* ID of the protected content session managed by i915 when PXP is active */
 #define I915_PROTECTED_CONTENT_DEFAULT_SESSION 0xf