11bb76ff1Sjsg // SPDX-License-Identifier: MIT
21bb76ff1Sjsg /*
31bb76ff1Sjsg * Copyright(c) 2020, Intel Corporation. All rights reserved.
41bb76ff1Sjsg */
51bb76ff1Sjsg
61bb76ff1Sjsg #include "i915_drv.h"
71bb76ff1Sjsg
81bb76ff1Sjsg #include "intel_pxp.h"
91bb76ff1Sjsg #include "intel_pxp_cmd.h"
10*f005ef32Sjsg #include "intel_pxp_gsccs.h"
111bb76ff1Sjsg #include "intel_pxp_session.h"
121bb76ff1Sjsg #include "intel_pxp_tee.h"
131bb76ff1Sjsg #include "intel_pxp_types.h"
14*f005ef32Sjsg #include "intel_pxp_regs.h"
151bb76ff1Sjsg
161bb76ff1Sjsg #define ARB_SESSION I915_PROTECTED_CONTENT_DEFAULT_SESSION /* shorter define */
171bb76ff1Sjsg
intel_pxp_session_is_in_play(struct intel_pxp * pxp,u32 id)181bb76ff1Sjsg static bool intel_pxp_session_is_in_play(struct intel_pxp *pxp, u32 id)
191bb76ff1Sjsg {
20*f005ef32Sjsg struct intel_uncore *uncore = pxp->ctrl_gt->uncore;
211bb76ff1Sjsg intel_wakeref_t wakeref;
221bb76ff1Sjsg u32 sip = 0;
231bb76ff1Sjsg
241bb76ff1Sjsg /* if we're suspended the session is considered off */
251bb76ff1Sjsg with_intel_runtime_pm_if_in_use(uncore->rpm, wakeref)
26*f005ef32Sjsg sip = intel_uncore_read(uncore, KCR_SIP(pxp->kcr_base));
271bb76ff1Sjsg
281bb76ff1Sjsg return sip & BIT(id);
291bb76ff1Sjsg }
301bb76ff1Sjsg
pxp_wait_for_session_state(struct intel_pxp * pxp,u32 id,bool in_play)311bb76ff1Sjsg static int pxp_wait_for_session_state(struct intel_pxp *pxp, u32 id, bool in_play)
321bb76ff1Sjsg {
33*f005ef32Sjsg struct intel_uncore *uncore = pxp->ctrl_gt->uncore;
341bb76ff1Sjsg intel_wakeref_t wakeref;
351bb76ff1Sjsg u32 mask = BIT(id);
361bb76ff1Sjsg int ret;
371bb76ff1Sjsg
381bb76ff1Sjsg /* if we're suspended the session is considered off */
391bb76ff1Sjsg wakeref = intel_runtime_pm_get_if_in_use(uncore->rpm);
401bb76ff1Sjsg if (!wakeref)
411bb76ff1Sjsg return in_play ? -ENODEV : 0;
421bb76ff1Sjsg
431bb76ff1Sjsg ret = intel_wait_for_register(uncore,
44*f005ef32Sjsg KCR_SIP(pxp->kcr_base),
451bb76ff1Sjsg mask,
461bb76ff1Sjsg in_play ? mask : 0,
47*f005ef32Sjsg 250);
481bb76ff1Sjsg
491bb76ff1Sjsg intel_runtime_pm_put(uncore->rpm, wakeref);
501bb76ff1Sjsg
511bb76ff1Sjsg return ret;
521bb76ff1Sjsg }
531bb76ff1Sjsg
pxp_create_arb_session(struct intel_pxp * pxp)541bb76ff1Sjsg static int pxp_create_arb_session(struct intel_pxp *pxp)
551bb76ff1Sjsg {
56*f005ef32Sjsg struct intel_gt *gt = pxp->ctrl_gt;
571bb76ff1Sjsg int ret;
581bb76ff1Sjsg
591bb76ff1Sjsg pxp->arb_is_valid = false;
601bb76ff1Sjsg
611bb76ff1Sjsg if (intel_pxp_session_is_in_play(pxp, ARB_SESSION)) {
621bb76ff1Sjsg drm_err(>->i915->drm, "arb session already in play at creation time\n");
631bb76ff1Sjsg return -EEXIST;
641bb76ff1Sjsg }
651bb76ff1Sjsg
66*f005ef32Sjsg if (HAS_ENGINE(pxp->ctrl_gt, GSC0))
67*f005ef32Sjsg ret = intel_pxp_gsccs_create_session(pxp, ARB_SESSION);
68*f005ef32Sjsg else
691bb76ff1Sjsg ret = intel_pxp_tee_cmd_create_arb_session(pxp, ARB_SESSION);
701bb76ff1Sjsg if (ret) {
711bb76ff1Sjsg drm_err(>->i915->drm, "tee cmd for arb session creation failed\n");
721bb76ff1Sjsg return ret;
731bb76ff1Sjsg }
741bb76ff1Sjsg
751bb76ff1Sjsg ret = pxp_wait_for_session_state(pxp, ARB_SESSION, true);
761bb76ff1Sjsg if (ret) {
77*f005ef32Sjsg drm_dbg(>->i915->drm, "arb session failed to go in play\n");
781bb76ff1Sjsg return ret;
791bb76ff1Sjsg }
80*f005ef32Sjsg drm_dbg(>->i915->drm, "PXP ARB session is alive\n");
811bb76ff1Sjsg
821bb76ff1Sjsg if (!++pxp->key_instance)
831bb76ff1Sjsg ++pxp->key_instance;
841bb76ff1Sjsg
851bb76ff1Sjsg pxp->arb_is_valid = true;
861bb76ff1Sjsg
871bb76ff1Sjsg return 0;
881bb76ff1Sjsg }
891bb76ff1Sjsg
pxp_terminate_arb_session_and_global(struct intel_pxp * pxp)901bb76ff1Sjsg static int pxp_terminate_arb_session_and_global(struct intel_pxp *pxp)
911bb76ff1Sjsg {
921bb76ff1Sjsg int ret;
93*f005ef32Sjsg struct intel_gt *gt = pxp->ctrl_gt;
941bb76ff1Sjsg
951bb76ff1Sjsg /* must mark termination in progress calling this function */
961bb76ff1Sjsg GEM_WARN_ON(pxp->arb_is_valid);
971bb76ff1Sjsg
981bb76ff1Sjsg /* terminate the hw sessions */
991bb76ff1Sjsg ret = intel_pxp_terminate_session(pxp, ARB_SESSION);
1001bb76ff1Sjsg if (ret) {
1011bb76ff1Sjsg drm_err(>->i915->drm, "Failed to submit session termination\n");
1021bb76ff1Sjsg return ret;
1031bb76ff1Sjsg }
1041bb76ff1Sjsg
1051bb76ff1Sjsg ret = pxp_wait_for_session_state(pxp, ARB_SESSION, false);
1061bb76ff1Sjsg if (ret) {
1071bb76ff1Sjsg drm_err(>->i915->drm, "Session state did not clear\n");
1081bb76ff1Sjsg return ret;
1091bb76ff1Sjsg }
1101bb76ff1Sjsg
111*f005ef32Sjsg intel_uncore_write(gt->uncore, KCR_GLOBAL_TERMINATE(pxp->kcr_base), 1);
112*f005ef32Sjsg
113*f005ef32Sjsg if (HAS_ENGINE(gt, GSC0))
114*f005ef32Sjsg intel_pxp_gsccs_end_arb_fw_session(pxp, ARB_SESSION);
115*f005ef32Sjsg else
116*f005ef32Sjsg intel_pxp_tee_end_arb_fw_session(pxp, ARB_SESSION);
1171bb76ff1Sjsg
1181bb76ff1Sjsg return ret;
1191bb76ff1Sjsg }
1201bb76ff1Sjsg
intel_pxp_terminate(struct intel_pxp * pxp,bool post_invalidation_needs_restart)121*f005ef32Sjsg void intel_pxp_terminate(struct intel_pxp *pxp, bool post_invalidation_needs_restart)
1221bb76ff1Sjsg {
1231bb76ff1Sjsg int ret;
1241bb76ff1Sjsg
125*f005ef32Sjsg pxp->hw_state_invalidated = post_invalidation_needs_restart;
1261bb76ff1Sjsg
1271bb76ff1Sjsg /*
1281bb76ff1Sjsg * if we fail to submit the termination there is no point in waiting for
1291bb76ff1Sjsg * it to complete. PXP will be marked as non-active until the next
1301bb76ff1Sjsg * termination is issued.
1311bb76ff1Sjsg */
1321bb76ff1Sjsg ret = pxp_terminate_arb_session_and_global(pxp);
1331bb76ff1Sjsg if (ret)
1341bb76ff1Sjsg complete_all(&pxp->termination);
1351bb76ff1Sjsg }
1361bb76ff1Sjsg
pxp_terminate_complete(struct intel_pxp * pxp)1371bb76ff1Sjsg static void pxp_terminate_complete(struct intel_pxp *pxp)
1381bb76ff1Sjsg {
1391bb76ff1Sjsg /* Re-create the arb session after teardown handle complete */
1401bb76ff1Sjsg if (fetch_and_zero(&pxp->hw_state_invalidated))
1411bb76ff1Sjsg pxp_create_arb_session(pxp);
1421bb76ff1Sjsg
1431bb76ff1Sjsg complete_all(&pxp->termination);
1441bb76ff1Sjsg }
1451bb76ff1Sjsg
pxp_session_work(struct work_struct * work)146*f005ef32Sjsg static void pxp_session_work(struct work_struct *work)
1471bb76ff1Sjsg {
1481bb76ff1Sjsg struct intel_pxp *pxp = container_of(work, typeof(*pxp), session_work);
149*f005ef32Sjsg struct intel_gt *gt = pxp->ctrl_gt;
1501bb76ff1Sjsg intel_wakeref_t wakeref;
1511bb76ff1Sjsg u32 events = 0;
1521bb76ff1Sjsg
1531bb76ff1Sjsg spin_lock_irq(gt->irq_lock);
1541bb76ff1Sjsg events = fetch_and_zero(&pxp->session_events);
1551bb76ff1Sjsg spin_unlock_irq(gt->irq_lock);
1561bb76ff1Sjsg
1571bb76ff1Sjsg if (!events)
1581bb76ff1Sjsg return;
1591bb76ff1Sjsg
1601bb76ff1Sjsg if (events & PXP_INVAL_REQUIRED)
1611bb76ff1Sjsg intel_pxp_invalidate(pxp);
1621bb76ff1Sjsg
1631bb76ff1Sjsg /*
1641bb76ff1Sjsg * If we're processing an event while suspending then don't bother,
1651bb76ff1Sjsg * we're going to re-init everything on resume anyway.
1661bb76ff1Sjsg */
1671bb76ff1Sjsg wakeref = intel_runtime_pm_get_if_in_use(gt->uncore->rpm);
1681bb76ff1Sjsg if (!wakeref)
1691bb76ff1Sjsg return;
1701bb76ff1Sjsg
1711bb76ff1Sjsg if (events & PXP_TERMINATION_REQUEST) {
1721bb76ff1Sjsg events &= ~PXP_TERMINATION_COMPLETE;
173*f005ef32Sjsg intel_pxp_terminate(pxp, true);
1741bb76ff1Sjsg }
1751bb76ff1Sjsg
1761bb76ff1Sjsg if (events & PXP_TERMINATION_COMPLETE)
1771bb76ff1Sjsg pxp_terminate_complete(pxp);
1781bb76ff1Sjsg
1791bb76ff1Sjsg intel_runtime_pm_put(gt->uncore->rpm, wakeref);
1801bb76ff1Sjsg }
181*f005ef32Sjsg
intel_pxp_session_management_init(struct intel_pxp * pxp)182*f005ef32Sjsg void intel_pxp_session_management_init(struct intel_pxp *pxp)
183*f005ef32Sjsg {
184*f005ef32Sjsg mutex_init(&pxp->arb_mutex);
185*f005ef32Sjsg INIT_WORK(&pxp->session_work, pxp_session_work);
186*f005ef32Sjsg }
187