162306a36Sopenharmony_ci// SPDX-License-Identifier: MIT
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright(c) 2020 Intel Corporation.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci#include <linux/workqueue.h>
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include "gem/i915_gem_context.h"
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include "gt/intel_context.h"
1062306a36Sopenharmony_ci#include "gt/intel_gt.h"
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include "i915_drv.h"
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include "intel_pxp.h"
1562306a36Sopenharmony_ci#include "intel_pxp_gsccs.h"
1662306a36Sopenharmony_ci#include "intel_pxp_irq.h"
1762306a36Sopenharmony_ci#include "intel_pxp_regs.h"
1862306a36Sopenharmony_ci#include "intel_pxp_session.h"
1962306a36Sopenharmony_ci#include "intel_pxp_tee.h"
2062306a36Sopenharmony_ci#include "intel_pxp_types.h"
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci/**
2362306a36Sopenharmony_ci * DOC: PXP
2462306a36Sopenharmony_ci *
2562306a36Sopenharmony_ci * PXP (Protected Xe Path) is a feature available in Gen12 and newer platforms.
2662306a36Sopenharmony_ci * It allows execution and flip to display of protected (i.e. encrypted)
2762306a36Sopenharmony_ci * objects. The SW support is enabled via the CONFIG_DRM_I915_PXP kconfig.
2862306a36Sopenharmony_ci *
2962306a36Sopenharmony_ci * Objects can opt-in to PXP encryption at creation time via the
3062306a36Sopenharmony_ci * I915_GEM_CREATE_EXT_PROTECTED_CONTENT create_ext flag. For objects to be
3162306a36Sopenharmony_ci * correctly protected they must be used in conjunction with a context created
3262306a36Sopenharmony_ci * with the I915_CONTEXT_PARAM_PROTECTED_CONTENT flag. See the documentation
3362306a36Sopenharmony_ci * of those two uapi flags for details and restrictions.
3462306a36Sopenharmony_ci *
3562306a36Sopenharmony_ci * Protected objects are tied to a pxp session; currently we only support one
3662306a36Sopenharmony_ci * session, which i915 manages and whose index is available in the uapi
3762306a36Sopenharmony_ci * (I915_PROTECTED_CONTENT_DEFAULT_SESSION) for use in instructions targeting
3862306a36Sopenharmony_ci * protected objects.
3962306a36Sopenharmony_ci * The session is invalidated by the HW when certain events occur (e.g.
4062306a36Sopenharmony_ci * suspend/resume). When this happens, all the objects that were used with the
4162306a36Sopenharmony_ci * session are marked as invalid and all contexts marked as using protected
4262306a36Sopenharmony_ci * content are banned. Any further attempt at using them in an execbuf call is
4362306a36Sopenharmony_ci * rejected, while flips are converted to black frames.
4462306a36Sopenharmony_ci *
4562306a36Sopenharmony_ci * Some of the PXP setup operations are performed by the Management Engine,
4662306a36Sopenharmony_ci * which is handled by the mei driver; communication between i915 and mei is
4762306a36Sopenharmony_ci * performed via the mei_pxp component module.
4862306a36Sopenharmony_ci */
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_cibool intel_pxp_is_supported(const struct intel_pxp *pxp)
5162306a36Sopenharmony_ci{
5262306a36Sopenharmony_ci	return IS_ENABLED(CONFIG_DRM_I915_PXP) && pxp;
5362306a36Sopenharmony_ci}
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_cibool intel_pxp_is_enabled(const struct intel_pxp *pxp)
5662306a36Sopenharmony_ci{
5762306a36Sopenharmony_ci	return IS_ENABLED(CONFIG_DRM_I915_PXP) && pxp && pxp->ce;
5862306a36Sopenharmony_ci}
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_cibool intel_pxp_is_active(const struct intel_pxp *pxp)
6162306a36Sopenharmony_ci{
6262306a36Sopenharmony_ci	return IS_ENABLED(CONFIG_DRM_I915_PXP) && pxp && pxp->arb_is_valid;
6362306a36Sopenharmony_ci}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cistatic void kcr_pxp_set_status(const struct intel_pxp *pxp, bool enable)
6662306a36Sopenharmony_ci{
6762306a36Sopenharmony_ci	u32 val = enable ? _MASKED_BIT_ENABLE(KCR_INIT_ALLOW_DISPLAY_ME_WRITES) :
6862306a36Sopenharmony_ci		  _MASKED_BIT_DISABLE(KCR_INIT_ALLOW_DISPLAY_ME_WRITES);
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	intel_uncore_write(pxp->ctrl_gt->uncore, KCR_INIT(pxp->kcr_base), val);
7162306a36Sopenharmony_ci}
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_cistatic void kcr_pxp_enable(const struct intel_pxp *pxp)
7462306a36Sopenharmony_ci{
7562306a36Sopenharmony_ci	kcr_pxp_set_status(pxp, true);
7662306a36Sopenharmony_ci}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_cistatic void kcr_pxp_disable(const struct intel_pxp *pxp)
7962306a36Sopenharmony_ci{
8062306a36Sopenharmony_ci	kcr_pxp_set_status(pxp, false);
8162306a36Sopenharmony_ci}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_cistatic int create_vcs_context(struct intel_pxp *pxp)
8462306a36Sopenharmony_ci{
8562306a36Sopenharmony_ci	static struct lock_class_key pxp_lock;
8662306a36Sopenharmony_ci	struct intel_gt *gt = pxp->ctrl_gt;
8762306a36Sopenharmony_ci	struct intel_engine_cs *engine;
8862306a36Sopenharmony_ci	struct intel_context *ce;
8962306a36Sopenharmony_ci	int i;
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	/*
9262306a36Sopenharmony_ci	 * Find the first VCS engine present. We're guaranteed there is one
9362306a36Sopenharmony_ci	 * if we're in this function due to the check in has_pxp
9462306a36Sopenharmony_ci	 */
9562306a36Sopenharmony_ci	for (i = 0, engine = NULL; !engine; i++)
9662306a36Sopenharmony_ci		engine = gt->engine_class[VIDEO_DECODE_CLASS][i];
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	GEM_BUG_ON(!engine || engine->class != VIDEO_DECODE_CLASS);
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	ce = intel_engine_create_pinned_context(engine, engine->gt->vm, SZ_4K,
10162306a36Sopenharmony_ci						I915_GEM_HWS_PXP_ADDR,
10262306a36Sopenharmony_ci						&pxp_lock, "pxp_context");
10362306a36Sopenharmony_ci	if (IS_ERR(ce)) {
10462306a36Sopenharmony_ci		drm_err(&gt->i915->drm, "failed to create VCS ctx for PXP\n");
10562306a36Sopenharmony_ci		return PTR_ERR(ce);
10662306a36Sopenharmony_ci	}
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	pxp->ce = ce;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	return 0;
11162306a36Sopenharmony_ci}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_cistatic void destroy_vcs_context(struct intel_pxp *pxp)
11462306a36Sopenharmony_ci{
11562306a36Sopenharmony_ci	if (pxp->ce)
11662306a36Sopenharmony_ci		intel_engine_destroy_pinned_context(fetch_and_zero(&pxp->ce));
11762306a36Sopenharmony_ci}
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_cistatic void pxp_init_full(struct intel_pxp *pxp)
12062306a36Sopenharmony_ci{
12162306a36Sopenharmony_ci	struct intel_gt *gt = pxp->ctrl_gt;
12262306a36Sopenharmony_ci	int ret;
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	/*
12562306a36Sopenharmony_ci	 * we'll use the completion to check if there is a termination pending,
12662306a36Sopenharmony_ci	 * so we start it as completed and we reinit it when a termination
12762306a36Sopenharmony_ci	 * is triggered.
12862306a36Sopenharmony_ci	 */
12962306a36Sopenharmony_ci	init_completion(&pxp->termination);
13062306a36Sopenharmony_ci	complete_all(&pxp->termination);
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	if (pxp->ctrl_gt->type == GT_MEDIA)
13362306a36Sopenharmony_ci		pxp->kcr_base = MTL_KCR_BASE;
13462306a36Sopenharmony_ci	else
13562306a36Sopenharmony_ci		pxp->kcr_base = GEN12_KCR_BASE;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	intel_pxp_session_management_init(pxp);
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	ret = create_vcs_context(pxp);
14062306a36Sopenharmony_ci	if (ret)
14162306a36Sopenharmony_ci		return;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	if (HAS_ENGINE(pxp->ctrl_gt, GSC0))
14462306a36Sopenharmony_ci		ret = intel_pxp_gsccs_init(pxp);
14562306a36Sopenharmony_ci	else
14662306a36Sopenharmony_ci		ret = intel_pxp_tee_component_init(pxp);
14762306a36Sopenharmony_ci	if (ret)
14862306a36Sopenharmony_ci		goto out_context;
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	drm_info(&gt->i915->drm, "Protected Xe Path (PXP) protected content support initialized\n");
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	return;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ciout_context:
15562306a36Sopenharmony_ci	destroy_vcs_context(pxp);
15662306a36Sopenharmony_ci}
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_cistatic struct intel_gt *find_gt_for_required_teelink(struct drm_i915_private *i915)
15962306a36Sopenharmony_ci{
16062306a36Sopenharmony_ci	/*
16162306a36Sopenharmony_ci	 * NOTE: Only certain platforms require PXP-tee-backend dependencies
16262306a36Sopenharmony_ci	 * for HuC authentication. For now, its limited to DG2.
16362306a36Sopenharmony_ci	 */
16462306a36Sopenharmony_ci	if (IS_ENABLED(CONFIG_INTEL_MEI_PXP) && IS_ENABLED(CONFIG_INTEL_MEI_GSC) &&
16562306a36Sopenharmony_ci	    intel_huc_is_loaded_by_gsc(&to_gt(i915)->uc.huc) && intel_uc_uses_huc(&to_gt(i915)->uc))
16662306a36Sopenharmony_ci		return to_gt(i915);
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	return NULL;
16962306a36Sopenharmony_ci}
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_cistatic struct intel_gt *find_gt_for_required_protected_content(struct drm_i915_private *i915)
17262306a36Sopenharmony_ci{
17362306a36Sopenharmony_ci	if (!IS_ENABLED(CONFIG_DRM_I915_PXP) || !INTEL_INFO(i915)->has_pxp)
17462306a36Sopenharmony_ci		return NULL;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	/*
17762306a36Sopenharmony_ci	 * For MTL onwards, PXP-controller-GT needs to have a valid GSC engine
17862306a36Sopenharmony_ci	 * on the media GT. NOTE: if we have a media-tile with a GSC-engine,
17962306a36Sopenharmony_ci	 * the VDBOX is already present so skip that check. We also have to
18062306a36Sopenharmony_ci	 * ensure the GSC and HUC firmware are coming online
18162306a36Sopenharmony_ci	 */
18262306a36Sopenharmony_ci	if (i915->media_gt && HAS_ENGINE(i915->media_gt, GSC0) &&
18362306a36Sopenharmony_ci	    intel_uc_fw_is_loadable(&i915->media_gt->uc.gsc.fw) &&
18462306a36Sopenharmony_ci	    intel_uc_fw_is_loadable(&i915->media_gt->uc.huc.fw))
18562306a36Sopenharmony_ci		return i915->media_gt;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	/*
18862306a36Sopenharmony_ci	 * Else we rely on mei-pxp module but only on legacy platforms
18962306a36Sopenharmony_ci	 * prior to having separate media GTs and has a valid VDBOX.
19062306a36Sopenharmony_ci	 */
19162306a36Sopenharmony_ci	if (IS_ENABLED(CONFIG_INTEL_MEI_PXP) && !i915->media_gt && VDBOX_MASK(to_gt(i915)))
19262306a36Sopenharmony_ci		return to_gt(i915);
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	return NULL;
19562306a36Sopenharmony_ci}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ciint intel_pxp_init(struct drm_i915_private *i915)
19862306a36Sopenharmony_ci{
19962306a36Sopenharmony_ci	struct intel_gt *gt;
20062306a36Sopenharmony_ci	bool is_full_feature = false;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	/*
20362306a36Sopenharmony_ci	 * NOTE: Get the ctrl_gt before checking intel_pxp_is_supported since
20462306a36Sopenharmony_ci	 * we still need it if PXP's backend tee transport is needed.
20562306a36Sopenharmony_ci	 */
20662306a36Sopenharmony_ci	gt = find_gt_for_required_protected_content(i915);
20762306a36Sopenharmony_ci	if (gt)
20862306a36Sopenharmony_ci		is_full_feature = true;
20962306a36Sopenharmony_ci	else
21062306a36Sopenharmony_ci		gt = find_gt_for_required_teelink(i915);
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	if (!gt)
21362306a36Sopenharmony_ci		return -ENODEV;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	/*
21662306a36Sopenharmony_ci	 * At this point, we will either enable full featured PXP capabilities
21762306a36Sopenharmony_ci	 * including session and object management, or we will init the backend tee
21862306a36Sopenharmony_ci	 * channel for internal users such as HuC loading by GSC
21962306a36Sopenharmony_ci	 */
22062306a36Sopenharmony_ci	i915->pxp = kzalloc(sizeof(*i915->pxp), GFP_KERNEL);
22162306a36Sopenharmony_ci	if (!i915->pxp)
22262306a36Sopenharmony_ci		return -ENOMEM;
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	/* init common info used by all feature-mode usages*/
22562306a36Sopenharmony_ci	i915->pxp->ctrl_gt = gt;
22662306a36Sopenharmony_ci	mutex_init(&i915->pxp->tee_mutex);
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	/*
22962306a36Sopenharmony_ci	 * If full PXP feature is not available but HuC is loaded by GSC on pre-MTL
23062306a36Sopenharmony_ci	 * such as DG2, we can skip the init of the full PXP session/object management
23162306a36Sopenharmony_ci	 * and just init the tee channel.
23262306a36Sopenharmony_ci	 */
23362306a36Sopenharmony_ci	if (is_full_feature)
23462306a36Sopenharmony_ci		pxp_init_full(i915->pxp);
23562306a36Sopenharmony_ci	else
23662306a36Sopenharmony_ci		intel_pxp_tee_component_init(i915->pxp);
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	return 0;
23962306a36Sopenharmony_ci}
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_civoid intel_pxp_fini(struct drm_i915_private *i915)
24262306a36Sopenharmony_ci{
24362306a36Sopenharmony_ci	if (!i915->pxp)
24462306a36Sopenharmony_ci		return;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	i915->pxp->arb_is_valid = false;
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	if (HAS_ENGINE(i915->pxp->ctrl_gt, GSC0))
24962306a36Sopenharmony_ci		intel_pxp_gsccs_fini(i915->pxp);
25062306a36Sopenharmony_ci	else
25162306a36Sopenharmony_ci		intel_pxp_tee_component_fini(i915->pxp);
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	destroy_vcs_context(i915->pxp);
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	kfree(i915->pxp);
25662306a36Sopenharmony_ci	i915->pxp = NULL;
25762306a36Sopenharmony_ci}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_civoid intel_pxp_mark_termination_in_progress(struct intel_pxp *pxp)
26062306a36Sopenharmony_ci{
26162306a36Sopenharmony_ci	pxp->arb_is_valid = false;
26262306a36Sopenharmony_ci	reinit_completion(&pxp->termination);
26362306a36Sopenharmony_ci}
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_cistatic void pxp_queue_termination(struct intel_pxp *pxp)
26662306a36Sopenharmony_ci{
26762306a36Sopenharmony_ci	struct intel_gt *gt = pxp->ctrl_gt;
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	/*
27062306a36Sopenharmony_ci	 * We want to get the same effect as if we received a termination
27162306a36Sopenharmony_ci	 * interrupt, so just pretend that we did.
27262306a36Sopenharmony_ci	 */
27362306a36Sopenharmony_ci	spin_lock_irq(gt->irq_lock);
27462306a36Sopenharmony_ci	intel_pxp_mark_termination_in_progress(pxp);
27562306a36Sopenharmony_ci	pxp->session_events |= PXP_TERMINATION_REQUEST;
27662306a36Sopenharmony_ci	queue_work(system_unbound_wq, &pxp->session_work);
27762306a36Sopenharmony_ci	spin_unlock_irq(gt->irq_lock);
27862306a36Sopenharmony_ci}
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_cistatic bool pxp_component_bound(struct intel_pxp *pxp)
28162306a36Sopenharmony_ci{
28262306a36Sopenharmony_ci	bool bound = false;
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	mutex_lock(&pxp->tee_mutex);
28562306a36Sopenharmony_ci	if (pxp->pxp_component)
28662306a36Sopenharmony_ci		bound = true;
28762306a36Sopenharmony_ci	mutex_unlock(&pxp->tee_mutex);
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	return bound;
29062306a36Sopenharmony_ci}
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ciint intel_pxp_get_backend_timeout_ms(struct intel_pxp *pxp)
29362306a36Sopenharmony_ci{
29462306a36Sopenharmony_ci	if (HAS_ENGINE(pxp->ctrl_gt, GSC0))
29562306a36Sopenharmony_ci		return GSCFW_MAX_ROUND_TRIP_LATENCY_MS;
29662306a36Sopenharmony_ci	else
29762306a36Sopenharmony_ci		return 250;
29862306a36Sopenharmony_ci}
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_cistatic int __pxp_global_teardown_final(struct intel_pxp *pxp)
30162306a36Sopenharmony_ci{
30262306a36Sopenharmony_ci	int timeout;
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	if (!pxp->arb_is_valid)
30562306a36Sopenharmony_ci		return 0;
30662306a36Sopenharmony_ci	/*
30762306a36Sopenharmony_ci	 * To ensure synchronous and coherent session teardown completion
30862306a36Sopenharmony_ci	 * in response to suspend or shutdown triggers, don't use a worker.
30962306a36Sopenharmony_ci	 */
31062306a36Sopenharmony_ci	intel_pxp_mark_termination_in_progress(pxp);
31162306a36Sopenharmony_ci	intel_pxp_terminate(pxp, false);
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	timeout = intel_pxp_get_backend_timeout_ms(pxp);
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	if (!wait_for_completion_timeout(&pxp->termination, msecs_to_jiffies(timeout)))
31662306a36Sopenharmony_ci		return -ETIMEDOUT;
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	return 0;
31962306a36Sopenharmony_ci}
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_cistatic int __pxp_global_teardown_restart(struct intel_pxp *pxp)
32262306a36Sopenharmony_ci{
32362306a36Sopenharmony_ci	int timeout;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	if (pxp->arb_is_valid)
32662306a36Sopenharmony_ci		return 0;
32762306a36Sopenharmony_ci	/*
32862306a36Sopenharmony_ci	 * The arb-session is currently inactive and we are doing a reset and restart
32962306a36Sopenharmony_ci	 * due to a runtime event. Use the worker that was designed for this.
33062306a36Sopenharmony_ci	 */
33162306a36Sopenharmony_ci	pxp_queue_termination(pxp);
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	timeout = intel_pxp_get_backend_timeout_ms(pxp);
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	if (!wait_for_completion_timeout(&pxp->termination, msecs_to_jiffies(timeout)))
33662306a36Sopenharmony_ci		return -ETIMEDOUT;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	return 0;
33962306a36Sopenharmony_ci}
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_civoid intel_pxp_end(struct intel_pxp *pxp)
34262306a36Sopenharmony_ci{
34362306a36Sopenharmony_ci	struct drm_i915_private *i915 = pxp->ctrl_gt->i915;
34462306a36Sopenharmony_ci	intel_wakeref_t wakeref;
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	if (!intel_pxp_is_enabled(pxp))
34762306a36Sopenharmony_ci		return;
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	wakeref = intel_runtime_pm_get(&i915->runtime_pm);
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	mutex_lock(&pxp->arb_mutex);
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	if (__pxp_global_teardown_final(pxp))
35462306a36Sopenharmony_ci		drm_dbg(&i915->drm, "PXP end timed out\n");
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	mutex_unlock(&pxp->arb_mutex);
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	intel_pxp_fini_hw(pxp);
35962306a36Sopenharmony_ci	intel_runtime_pm_put(&i915->runtime_pm, wakeref);
36062306a36Sopenharmony_ci}
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci/*
36362306a36Sopenharmony_ci * this helper is used by both intel_pxp_start and by
36462306a36Sopenharmony_ci * the GET_PARAM IOCTL that user space calls. Thus, the
36562306a36Sopenharmony_ci * return values here should match the UAPI spec.
36662306a36Sopenharmony_ci */
36762306a36Sopenharmony_ciint intel_pxp_get_readiness_status(struct intel_pxp *pxp)
36862306a36Sopenharmony_ci{
36962306a36Sopenharmony_ci	if (!intel_pxp_is_enabled(pxp))
37062306a36Sopenharmony_ci		return -ENODEV;
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	if (HAS_ENGINE(pxp->ctrl_gt, GSC0)) {
37362306a36Sopenharmony_ci		if (wait_for(intel_pxp_gsccs_is_ready_for_sessions(pxp), 250))
37462306a36Sopenharmony_ci			return 2;
37562306a36Sopenharmony_ci	} else {
37662306a36Sopenharmony_ci		if (wait_for(pxp_component_bound(pxp), 250))
37762306a36Sopenharmony_ci			return 2;
37862306a36Sopenharmony_ci	}
37962306a36Sopenharmony_ci	return 1;
38062306a36Sopenharmony_ci}
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci/*
38362306a36Sopenharmony_ci * the arb session is restarted from the irq work when we receive the
38462306a36Sopenharmony_ci * termination completion interrupt
38562306a36Sopenharmony_ci */
38662306a36Sopenharmony_ciint intel_pxp_start(struct intel_pxp *pxp)
38762306a36Sopenharmony_ci{
38862306a36Sopenharmony_ci	int ret = 0;
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	ret = intel_pxp_get_readiness_status(pxp);
39162306a36Sopenharmony_ci	if (ret < 0)
39262306a36Sopenharmony_ci		return ret;
39362306a36Sopenharmony_ci	else if (ret > 1)
39462306a36Sopenharmony_ci		return -EIO; /* per UAPI spec, user may retry later */
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	mutex_lock(&pxp->arb_mutex);
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	ret = __pxp_global_teardown_restart(pxp);
39962306a36Sopenharmony_ci	if (ret)
40062306a36Sopenharmony_ci		goto unlock;
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	/* make sure the compiler doesn't optimize the double access */
40362306a36Sopenharmony_ci	barrier();
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	if (!pxp->arb_is_valid)
40662306a36Sopenharmony_ci		ret = -EIO;
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ciunlock:
40962306a36Sopenharmony_ci	mutex_unlock(&pxp->arb_mutex);
41062306a36Sopenharmony_ci	return ret;
41162306a36Sopenharmony_ci}
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_civoid intel_pxp_init_hw(struct intel_pxp *pxp)
41462306a36Sopenharmony_ci{
41562306a36Sopenharmony_ci	kcr_pxp_enable(pxp);
41662306a36Sopenharmony_ci	intel_pxp_irq_enable(pxp);
41762306a36Sopenharmony_ci}
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_civoid intel_pxp_fini_hw(struct intel_pxp *pxp)
42062306a36Sopenharmony_ci{
42162306a36Sopenharmony_ci	kcr_pxp_disable(pxp);
42262306a36Sopenharmony_ci	intel_pxp_irq_disable(pxp);
42362306a36Sopenharmony_ci}
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ciint intel_pxp_key_check(struct intel_pxp *pxp,
42662306a36Sopenharmony_ci			struct drm_i915_gem_object *obj,
42762306a36Sopenharmony_ci			bool assign)
42862306a36Sopenharmony_ci{
42962306a36Sopenharmony_ci	if (!intel_pxp_is_active(pxp))
43062306a36Sopenharmony_ci		return -ENODEV;
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	if (!i915_gem_object_is_protected(obj))
43362306a36Sopenharmony_ci		return -EINVAL;
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	GEM_BUG_ON(!pxp->key_instance);
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	/*
43862306a36Sopenharmony_ci	 * If this is the first time we're using this object, it's not
43962306a36Sopenharmony_ci	 * encrypted yet; it will be encrypted with the current key, so mark it
44062306a36Sopenharmony_ci	 * as such. If the object is already encrypted, check instead if the
44162306a36Sopenharmony_ci	 * used key is still valid.
44262306a36Sopenharmony_ci	 */
44362306a36Sopenharmony_ci	if (!obj->pxp_key_instance && assign)
44462306a36Sopenharmony_ci		obj->pxp_key_instance = pxp->key_instance;
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	if (obj->pxp_key_instance != pxp->key_instance)
44762306a36Sopenharmony_ci		return -ENOEXEC;
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	return 0;
45062306a36Sopenharmony_ci}
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_civoid intel_pxp_invalidate(struct intel_pxp *pxp)
45362306a36Sopenharmony_ci{
45462306a36Sopenharmony_ci	struct drm_i915_private *i915 = pxp->ctrl_gt->i915;
45562306a36Sopenharmony_ci	struct i915_gem_context *ctx, *cn;
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	/* ban all contexts marked as protected */
45862306a36Sopenharmony_ci	spin_lock_irq(&i915->gem.contexts.lock);
45962306a36Sopenharmony_ci	list_for_each_entry_safe(ctx, cn, &i915->gem.contexts.list, link) {
46062306a36Sopenharmony_ci		struct i915_gem_engines_iter it;
46162306a36Sopenharmony_ci		struct intel_context *ce;
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci		if (!kref_get_unless_zero(&ctx->ref))
46462306a36Sopenharmony_ci			continue;
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci		if (likely(!i915_gem_context_uses_protected_content(ctx))) {
46762306a36Sopenharmony_ci			i915_gem_context_put(ctx);
46862306a36Sopenharmony_ci			continue;
46962306a36Sopenharmony_ci		}
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci		spin_unlock_irq(&i915->gem.contexts.lock);
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci		/*
47462306a36Sopenharmony_ci		 * By the time we get here we are either going to suspend with
47562306a36Sopenharmony_ci		 * quiesced execution or the HW keys are already long gone and
47662306a36Sopenharmony_ci		 * in this case it is worthless to attempt to close the context
47762306a36Sopenharmony_ci		 * and wait for its execution. It will hang the GPU if it has
47862306a36Sopenharmony_ci		 * not already. So, as a fast mitigation, we can ban the
47962306a36Sopenharmony_ci		 * context as quick as we can. That might race with the
48062306a36Sopenharmony_ci		 * execbuffer, but currently this is the best that can be done.
48162306a36Sopenharmony_ci		 */
48262306a36Sopenharmony_ci		for_each_gem_engine(ce, i915_gem_context_lock_engines(ctx), it)
48362306a36Sopenharmony_ci			intel_context_ban(ce, NULL);
48462306a36Sopenharmony_ci		i915_gem_context_unlock_engines(ctx);
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci		/*
48762306a36Sopenharmony_ci		 * The context has been banned, no need to keep the wakeref.
48862306a36Sopenharmony_ci		 * This is safe from races because the only other place this
48962306a36Sopenharmony_ci		 * is touched is context_release and we're holding a ctx ref
49062306a36Sopenharmony_ci		 */
49162306a36Sopenharmony_ci		if (ctx->pxp_wakeref) {
49262306a36Sopenharmony_ci			intel_runtime_pm_put(&i915->runtime_pm,
49362306a36Sopenharmony_ci					     ctx->pxp_wakeref);
49462306a36Sopenharmony_ci			ctx->pxp_wakeref = 0;
49562306a36Sopenharmony_ci		}
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci		spin_lock_irq(&i915->gem.contexts.lock);
49862306a36Sopenharmony_ci		list_safe_reset_next(ctx, cn, link);
49962306a36Sopenharmony_ci		i915_gem_context_put(ctx);
50062306a36Sopenharmony_ci	}
50162306a36Sopenharmony_ci	spin_unlock_irq(&i915->gem.contexts.lock);
50262306a36Sopenharmony_ci}
503