Quelle intel_pxp.c
Sprache: C
// SPDX-License-Identifier: MIT
/*
* Copyright ( c ) 2020 Intel Corporation .
*/
#include <linux/workqueue.h>
#include "gem/i915_gem_context.h"
#include "gt/intel_context.h"
#include "gt/intel_gt.h"
#include "i915_drv.h"
#include "intel_pxp.h"
#include "intel_pxp_gsccs.h"
#include "intel_pxp_irq.h"
#include "intel_pxp_regs.h"
#include "intel_pxp_session.h"
#include "intel_pxp_tee.h"
#include "intel_pxp_types.h"
/**
* DOC : PXP
*
* PXP ( Protected Xe Path ) is a feature available in Gen12 and newer platforms .
* It allows execution and flip to display of protected ( i . e . encrypted )
* objects . The SW support is enabled via the CONFIG_DRM_I915_PXP kconfig .
*
* Objects can opt - in to PXP encryption at creation time via the
* I915_GEM_CREATE_EXT_PROTECTED_CONTENT create_ext flag . For objects to be
* correctly protected they must be used in conjunction with a context created
* with the I915_CONTEXT_PARAM_PROTECTED_CONTENT flag . See the documentation
* of those two uapi flags for details and restrictions .
*
* Protected objects are tied to a pxp session ; currently we only support one
* session , which i915 manages and whose index is available in the uapi
* ( I915_PROTECTED_CONTENT_DEFAULT_SESSION ) for use in instructions targeting
* protected objects .
* The session is invalidated by the HW when certain events occur ( e . g .
* suspend / resume ) . When this happens , all the objects that were used with the
* session are marked as invalid and all contexts marked as using protected
* content are banned . Any further attempt at using them in an execbuf call is
* rejected , while flips are converted to black frames .
*
* Some of the PXP setup operations are performed by the Management Engine ,
* which is handled by the mei driver ; communication between i915 and mei is
* performed via the mei_pxp component module .
*/
bool intel_pxp_is_supported(const struct intel_pxp *pxp)
{
return IS_ENABLED(CONFIG_DRM_I915_PXP) && pxp;
}
bool intel_pxp_is_enabled(const struct intel_pxp *pxp)
{
return IS_ENABLED(CONFIG_DRM_I915_PXP) && pxp && pxp->ce;
}
bool intel_pxp_is_active(const struct intel_pxp *pxp)
{
return IS_ENABLED(CONFIG_DRM_I915_PXP) && pxp && pxp->arb_is_valid;
}
static void kcr_pxp_set_status(const struct intel_pxp *pxp, bool enable)
{
u32 val = enable ? _MASKED_BIT_ENABLE(KCR_INIT_ALLOW_DISPLAY_ME_WRITES) :
_MASKED_BIT_DISABLE(KCR_INIT_ALLOW_DISPLAY_ME_WRITES);
intel_uncore_write(pxp->ctrl_gt->uncore, KCR_INIT(pxp->kcr_base), val);
}
static void kcr_pxp_enable(const struct intel_pxp *pxp)
{
kcr_pxp_set_status(pxp, true );
}
static void kcr_pxp_disable(const struct intel_pxp *pxp)
{
kcr_pxp_set_status(pxp, false );
}
static int create_vcs_context(struct intel_pxp *pxp)
{
static struct lock_class_key pxp_lock;
struct intel_gt *gt = pxp->ctrl_gt;
struct intel_engine_cs *engine;
struct intel_context *ce;
int i;
/*
* Find the first VCS engine present . We ' re guaranteed there is one
* if we ' re in this function due to the check in has_pxp
*/
for (i = 0 , engine = NULL; !engine; i++)
engine = gt->engine_class[VIDEO_DECODE_CLASS][i];
GEM_BUG_ON(!engine || engine->class != VIDEO_DECODE_CLASS);
ce = intel_engine_create_pinned_context(engine, engine->gt->vm, SZ_4K,
I915_GEM_HWS_PXP_ADDR,
&pxp_lock, "pxp_context" );
if (IS_ERR(ce)) {
drm_err(>->i915->drm, "failed to create VCS ctx for PXP\n" );
return PTR_ERR(ce);
}
pxp->ce = ce;
return 0 ;
}
static void destroy_vcs_context(struct intel_pxp *pxp)
{
if (pxp->ce)
intel_engine_destroy_pinned_context(fetch_and_zero(&pxp->ce));
}
static void pxp_init_full(struct intel_pxp *pxp)
{
struct intel_gt *gt = pxp->ctrl_gt;
int ret;
/*
* we ' ll use the completion to check if there is a termination pending ,
* so we start it as completed and we reinit it when a termination
* is triggered .
*/
init_completion(&pxp->termination);
complete_all(&pxp->termination);
if (pxp->ctrl_gt->type == GT_MEDIA)
pxp->kcr_base = MTL_KCR_BASE;
else
pxp->kcr_base = GEN12_KCR_BASE;
intel_pxp_session_management_init(pxp);
ret = create_vcs_context(pxp);
if (ret)
return ;
if (HAS_ENGINE(pxp->ctrl_gt, GSC0))
ret = intel_pxp_gsccs_init(pxp);
else
ret = intel_pxp_tee_component_init(pxp);
if (ret)
goto out_context;
drm_info(>->i915->drm, "Protected Xe Path (PXP) protected content support initialized\n" );
return ;
out_context:
destroy_vcs_context(pxp);
}
static struct intel_gt *find_gt_for_required_teelink(struct drm_i915_private *i915)
{
/*
* NOTE : Only certain platforms require PXP - tee - backend dependencies
* for HuC authentication . For now , its limited to DG2 .
*/
if (IS_ENABLED(CONFIG_INTEL_MEI_PXP) && IS_ENABLED(CONFIG_INTEL_MEI_GSC) &&
intel_huc_is_loaded_by_gsc(&to_gt(i915)->uc.huc) && intel_uc_uses_huc(&to_gt(i915)->uc))
return to_gt(i915);
return NULL;
}
static struct intel_gt *find_gt_for_required_protected_content(struct drm_i915_private *i915)
{
if (!HAS_PXP(i915))
return NULL;
/*
* For MTL onwards , PXP - controller - GT needs to have a valid GSC engine
* on the media GT . NOTE : if we have a media - tile with a GSC - engine ,
* the VDBOX is already present so skip that check . We also have to
* ensure the GSC and HUC firmware are coming online
*/
if (i915->media_gt && HAS_ENGINE(i915->media_gt, GSC0) &&
intel_uc_fw_is_loadable(&i915->media_gt->uc.gsc.fw) &&
intel_uc_fw_is_loadable(&i915->media_gt->uc.huc.fw))
return i915->media_gt;
/*
* Else we rely on mei - pxp module but only on legacy platforms
* prior to having separate media GTs and has a valid VDBOX .
*/
if (IS_ENABLED(CONFIG_INTEL_MEI_PXP) && !i915->media_gt && VDBOX_MASK(to_gt(i915)))
return to_gt(i915);
return NULL;
}
int intel_pxp_init(struct drm_i915_private *i915)
{
struct intel_gt *gt;
bool is_full_feature = false ;
if (intel_gt_is_wedged(to_gt(i915)))
return -ENOTCONN;
/*
* NOTE : Get the ctrl_gt before checking intel_pxp_is_supported since
* we still need it if PXP ' s backend tee transport is needed .
*/
gt = find_gt_for_required_protected_content(i915);
if (gt)
is_full_feature = true ;
else
gt = find_gt_for_required_teelink(i915);
if (!gt)
return -ENODEV;
/*
* At this point , we will either enable full featured PXP capabilities
* including session and object management , or we will init the backend tee
* channel for internal users such as HuC loading by GSC
*/
i915->pxp = kzalloc(sizeof (*i915->pxp), GFP_KERNEL);
if (!i915->pxp)
return -ENOMEM;
/* init common info used by all feature-mode usages*/
i915->pxp->ctrl_gt = gt;
mutex_init(&i915->pxp->tee_mutex);
/*
* If full PXP feature is not available but HuC is loaded by GSC on pre - MTL
* such as DG2 , we can skip the init of the full PXP session / object management
* and just init the tee channel .
*/
if (is_full_feature)
pxp_init_full(i915->pxp);
else
intel_pxp_tee_component_init(i915->pxp);
return 0 ;
}
void intel_pxp_fini(struct drm_i915_private *i915)
{
if (!i915->pxp)
return ;
i915->pxp->arb_is_valid = false ;
if (HAS_ENGINE(i915->pxp->ctrl_gt, GSC0))
intel_pxp_gsccs_fini(i915->pxp);
else
intel_pxp_tee_component_fini(i915->pxp);
destroy_vcs_context(i915->pxp);
kfree(i915->pxp);
i915->pxp = NULL;
}
void intel_pxp_mark_termination_in_progress(struct intel_pxp *pxp)
{
pxp->arb_is_valid = false ;
reinit_completion(&pxp->termination);
}
static void pxp_queue_termination(struct intel_pxp *pxp)
{
struct intel_gt *gt = pxp->ctrl_gt;
/*
* We want to get the same effect as if we received a termination
* interrupt , so just pretend that we did .
*/
spin_lock_irq(gt->irq_lock);
intel_pxp_mark_termination_in_progress(pxp);
pxp->session_events |= PXP_TERMINATION_REQUEST;
queue_work(system_unbound_wq, &pxp->session_work);
spin_unlock_irq(gt->irq_lock);
}
static bool pxp_component_bound(struct intel_pxp *pxp)
{
bool bound = false ;
mutex_lock(&pxp->tee_mutex);
if (pxp->pxp_component)
bound = true ;
mutex_unlock(&pxp->tee_mutex);
return bound;
}
int intel_pxp_get_backend_timeout_ms(struct intel_pxp *pxp)
{
if (HAS_ENGINE(pxp->ctrl_gt, GSC0))
return GSCFW_MAX_ROUND_TRIP_LATENCY_MS;
else
return 250 ;
}
static int __pxp_global_teardown_final(struct intel_pxp *pxp)
{
int timeout;
if (!pxp->arb_is_valid)
return 0 ;
drm_dbg(&pxp->ctrl_gt->i915->drm, "PXP: teardown for suspend/fini" );
/*
* To ensure synchronous and coherent session teardown completion
* in response to suspend or shutdown triggers , don ' t use a worker .
*/
intel_pxp_mark_termination_in_progress(pxp);
intel_pxp_terminate(pxp, false );
timeout = intel_pxp_get_backend_timeout_ms(pxp);
if (!wait_for_completion_timeout(&pxp->termination, msecs_to_jiffies(timeout)))
return -ETIMEDOUT;
return 0 ;
}
static int __pxp_global_teardown_restart(struct intel_pxp *pxp)
{
int timeout;
if (pxp->arb_is_valid)
return 0 ;
drm_dbg(&pxp->ctrl_gt->i915->drm, "PXP: teardown for restart" );
/*
* The arb - session is currently inactive and we are doing a reset and restart
* due to a runtime event . Use the worker that was designed for this .
*/
pxp_queue_termination(pxp);
timeout = intel_pxp_get_backend_timeout_ms(pxp);
if (!wait_for_completion_timeout(&pxp->termination, msecs_to_jiffies(timeout))) {
drm_dbg(&pxp->ctrl_gt->i915->drm, "PXP: restart backend timed out (%d ms)" ,
timeout);
return -ETIMEDOUT;
}
return 0 ;
}
void intel_pxp_end(struct intel_pxp *pxp)
{
struct drm_i915_private *i915 = pxp->ctrl_gt->i915;
intel_wakeref_t wakeref;
if (!intel_pxp_is_enabled(pxp))
return ;
wakeref = intel_runtime_pm_get(&i915->runtime_pm);
mutex_lock(&pxp->arb_mutex);
if (__pxp_global_teardown_final(pxp))
drm_dbg(&i915->drm, "PXP end timed out\n" );
mutex_unlock(&pxp->arb_mutex);
intel_pxp_fini_hw(pxp);
intel_runtime_pm_put(&i915->runtime_pm, wakeref);
}
static bool pxp_required_fw_failed(struct intel_pxp *pxp)
{
if (__intel_uc_fw_status(&pxp->ctrl_gt->uc.huc.fw) == INTEL_UC_FIRMWARE_LOAD_FAIL)
return true ;
if (HAS_ENGINE(pxp->ctrl_gt, GSC0) &&
__intel_uc_fw_status(&pxp->ctrl_gt->uc.gsc.fw) == INTEL_UC_FIRMWARE_LOAD_FAIL)
return true ;
return false ;
}
static bool pxp_fw_dependencies_completed(struct intel_pxp *pxp)
{
if (HAS_ENGINE(pxp->ctrl_gt, GSC0))
return intel_pxp_gsccs_is_ready_for_sessions(pxp);
return pxp_component_bound(pxp);
}
/*
* this helper is used by both intel_pxp_start and by
* the GET_PARAM IOCTL that user space calls . Thus , the
* return values here should match the UAPI spec .
*/
int intel_pxp_get_readiness_status(struct intel_pxp *pxp, int timeout_ms)
{
if (!intel_pxp_is_enabled(pxp))
return -ENODEV;
if (pxp_required_fw_failed(pxp))
return -ENODEV;
if (pxp->platform_cfg_is_bad)
return -ENODEV;
if (timeout_ms) {
if (wait_for(pxp_fw_dependencies_completed(pxp), timeout_ms))
return 2 ;
} else if (!pxp_fw_dependencies_completed(pxp)) {
return 2 ;
}
return 1 ;
}
/*
* the arb session is restarted from the irq work when we receive the
* termination completion interrupt
*/
#define PXP_READINESS_TIMEOUT 250
int intel_pxp_start(struct intel_pxp *pxp)
{
int ret = 0 ;
ret = intel_pxp_get_readiness_status(pxp, PXP_READINESS_TIMEOUT);
if (ret < 0 ) {
drm_dbg(&pxp->ctrl_gt->i915->drm, "PXP: tried but not-avail (%d)" , ret);
return ret;
} else if (ret > 1 ) {
return -EIO; /* per UAPI spec, user may retry later */
}
mutex_lock(&pxp->arb_mutex);
ret = __pxp_global_teardown_restart(pxp);
if (ret)
goto unlock;
/* make sure the compiler doesn't optimize the double access */
barrier();
if (!pxp->arb_is_valid)
ret = -EIO;
unlock:
mutex_unlock(&pxp->arb_mutex);
return ret;
}
void intel_pxp_init_hw(struct intel_pxp *pxp)
{
kcr_pxp_enable(pxp);
intel_pxp_irq_enable(pxp);
}
void intel_pxp_fini_hw(struct intel_pxp *pxp)
{
kcr_pxp_disable(pxp);
intel_pxp_irq_disable(pxp);
}
int intel_pxp_key_check(struct drm_gem_object *_obj, bool assign)
{
struct drm_i915_gem_object *obj = to_intel_bo(_obj);
struct drm_i915_private *i915 = to_i915(_obj->dev);
struct intel_pxp *pxp = i915->pxp;
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 && assign)
obj->pxp_key_instance = pxp->key_instance;
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->ctrl_gt->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 = NULL;
}
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);
}
Messung V0.5 in Prozent C=96 H=98 G=96
¤ Dauer der Verarbeitung: 0.14 Sekunden
(vorverarbeitet am 2026-10-03)
¤
*© Formatika GbR, Deutschland
2026-10-10
Neuigkeiten
Aktuelles
Motto des Tages
Open Source Software
Jenseits des Üblichen ....
Besucherstatistik
Statistik
#Sources=1126864
#Domains=1897691