Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1// SPDX-License-Identifier: MIT
  2/*
  3 * Copyright(c) 2023 Intel Corporation.
  4 */
  5
  6#include "gem/i915_gem_internal.h"
  7
  8#include "gt/intel_context.h"
  9#include "gt/intel_gt.h"
 10#include "gt/uc/intel_gsc_fw.h"
 11#include "gt/uc/intel_gsc_uc_heci_cmd_submit.h"
 12
 13#include "i915_drv.h"
 14#include "intel_pxp.h"
 15#include "intel_pxp_cmd_interface_42.h"
 16#include "intel_pxp_cmd_interface_43.h"
 17#include "intel_pxp_gsccs.h"
 18#include "intel_pxp_types.h"
 19
 20static bool
 21is_fw_err_platform_config(struct intel_pxp *pxp, u32 type)
 22{
 23	switch (type) {
 24	case PXP_STATUS_ERROR_API_VERSION:
 25	case PXP_STATUS_PLATFCONFIG_KF1_NOVERIF:
 26	case PXP_STATUS_PLATFCONFIG_KF1_BAD:
 27		pxp->platform_cfg_is_bad = true;
 28		return true;
 29	default:
 30		break;
 31	}
 32	return false;
 33}
 34
 35static const char *
 36fw_err_to_string(u32 type)
 37{
 38	switch (type) {
 39	case PXP_STATUS_ERROR_API_VERSION:
 40		return "ERR_API_VERSION";
 41	case PXP_STATUS_NOT_READY:
 42		return "ERR_NOT_READY";
 43	case PXP_STATUS_PLATFCONFIG_KF1_NOVERIF:
 44	case PXP_STATUS_PLATFCONFIG_KF1_BAD:
 45		return "ERR_PLATFORM_CONFIG";
 46	default:
 47		break;
 48	}
 49	return NULL;
 50}
 51
 52static int
 53gsccs_send_message(struct intel_pxp *pxp,
 54		   void *msg_in, size_t msg_in_size,
 55		   void *msg_out, size_t msg_out_size_max,
 56		   size_t *msg_out_len,
 57		   u64 *gsc_msg_handle_retry)
 58{
 59	struct intel_gt *gt = pxp->ctrl_gt;
 60	struct drm_i915_private *i915 = gt->i915;
 61	struct gsccs_session_resources *exec_res =  &pxp->gsccs_res;
 62	struct intel_gsc_mtl_header *header = exec_res->pkt_vaddr;
 63	struct intel_gsc_heci_non_priv_pkt pkt;
 64	size_t max_msg_size;
 65	u32 reply_size;
 66	int ret;
 67
 68	if (!exec_res->ce)
 69		return -ENODEV;
 70
 71	max_msg_size = PXP43_MAX_HECI_INOUT_SIZE - sizeof(*header);
 72
 73	if (msg_in_size > max_msg_size || msg_out_size_max > max_msg_size)
 74		return -ENOSPC;
 75
 76	if (!exec_res->pkt_vma || !exec_res->bb_vma)
 77		return -ENOENT;
 78
 79	GEM_BUG_ON(exec_res->pkt_vma->size < (2 * PXP43_MAX_HECI_INOUT_SIZE));
 80
 81	mutex_lock(&pxp->tee_mutex);
 82
 83	memset(header, 0, sizeof(*header));
 84	intel_gsc_uc_heci_cmd_emit_mtl_header(header, HECI_MEADDRESS_PXP,
 85					      msg_in_size + sizeof(*header),
 86					      exec_res->host_session_handle);
 87
 88	/* check if this is a host-session-handle cleanup call (empty packet) */
 89	if (!msg_in && !msg_out)
 90		header->flags |= GSC_INFLAG_MSG_CLEANUP;
 91
 92	/* copy caller provided gsc message handle if this is polling for a prior msg completion */
 93	header->gsc_message_handle = *gsc_msg_handle_retry;
 94
 95	/* NOTE: zero size packets are used for session-cleanups */
 96	if (msg_in && msg_in_size)
 97		memcpy(exec_res->pkt_vaddr + sizeof(*header), msg_in, msg_in_size);
 98
 99	pkt.addr_in = i915_vma_offset(exec_res->pkt_vma);
100	pkt.size_in = header->message_size;
101	pkt.addr_out = pkt.addr_in + PXP43_MAX_HECI_INOUT_SIZE;
102	pkt.size_out = msg_out_size_max + sizeof(*header);
103	pkt.heci_pkt_vma = exec_res->pkt_vma;
104	pkt.bb_vma = exec_res->bb_vma;
105
106	/*
107	 * Before submitting, let's clear-out the validity marker on the reply offset.
108	 * We use offset PXP43_MAX_HECI_INOUT_SIZE for reply location so point header there.
109	 */
110	header = exec_res->pkt_vaddr + PXP43_MAX_HECI_INOUT_SIZE;
111	header->validity_marker = 0;
112
113	ret = intel_gsc_uc_heci_cmd_submit_nonpriv(&gt->uc.gsc,
114						   exec_res->ce, &pkt, exec_res->bb_vaddr,
115						   GSC_HECI_REPLY_LATENCY_MS);
116	if (ret) {
117		drm_err(&i915->drm, "failed to send gsc PXP msg (%d)\n", ret);
118		goto unlock;
119	}
120
121	/* Response validity marker, status and busyness */
122	if (header->validity_marker != GSC_HECI_VALIDITY_MARKER) {
123		drm_err(&i915->drm, "gsc PXP reply with invalid validity marker\n");
124		ret = -EINVAL;
125		goto unlock;
126	}
127	if (header->status != 0) {
128		drm_dbg(&i915->drm, "gsc PXP reply status has error = 0x%08x\n",
129			header->status);
130		ret = -EINVAL;
131		goto unlock;
132	}
133	if (header->flags & GSC_OUTFLAG_MSG_PENDING) {
134		drm_dbg(&i915->drm, "gsc PXP reply is busy\n");
135		/*
136		 * When the GSC firmware replies with pending bit, it means that the requested
137		 * operation has begun but the completion is pending and the caller needs
138		 * to re-request with the gsc_message_handle that was returned by the firmware.
139		 * until the pending bit is turned off.
140		 */
141		*gsc_msg_handle_retry = header->gsc_message_handle;
142		ret = -EAGAIN;
143		goto unlock;
144	}
145
146	reply_size = header->message_size - sizeof(*header);
147	if (reply_size > msg_out_size_max) {
148		drm_warn(&i915->drm, "caller with insufficient PXP reply size %u (%zu)\n",
149			 reply_size, msg_out_size_max);
150		reply_size = msg_out_size_max;
151	}
152
153	if (msg_out)
154		memcpy(msg_out, exec_res->pkt_vaddr + PXP43_MAX_HECI_INOUT_SIZE + sizeof(*header),
155		       reply_size);
156	if (msg_out_len)
157		*msg_out_len = reply_size;
158
159unlock:
160	mutex_unlock(&pxp->tee_mutex);
161	return ret;
162}
163
164static int
165gsccs_send_message_retry_complete(struct intel_pxp *pxp,
166				  void *msg_in, size_t msg_in_size,
167				  void *msg_out, size_t msg_out_size_max,
168				  size_t *msg_out_len)
169{
170	u64 gsc_session_retry = 0;
171	int ret, tries = 0;
172
173	/*
174	 * Keep sending request if GSC firmware was busy. Based on fw specs +
175	 * sw overhead (and testing) we expect a worst case pending-bit delay of
176	 * GSC_PENDING_RETRY_MAXCOUNT x GSC_PENDING_RETRY_PAUSE_MS millisecs.
177	 */
178	do {
179		ret = gsccs_send_message(pxp, msg_in, msg_in_size, msg_out, msg_out_size_max,
180					 msg_out_len, &gsc_session_retry);
181		/* Only try again if gsc says so */
182		if (ret != -EAGAIN)
183			break;
184
185		msleep(GSC_PENDING_RETRY_PAUSE_MS);
186	} while (++tries < GSC_PENDING_RETRY_MAXCOUNT);
187
188	return ret;
189}
190
191bool intel_pxp_gsccs_is_ready_for_sessions(struct intel_pxp *pxp)
192{
193	/*
194	 * GSC-fw loading, HuC-fw loading, HuC-fw authentication and
195	 * GSC-proxy init flow (requiring an mei component driver)
196	 * must all occur first before we can start requesting for PXP
197	 * sessions. Checking for completion on HuC authentication and
198	 * gsc-proxy init flow (the last set of dependencies that
199	 * are out of order) will suffice.
200	 */
201	if (intel_huc_is_authenticated(&pxp->ctrl_gt->uc.huc, INTEL_HUC_AUTH_BY_GSC) &&
202	    intel_gsc_uc_fw_proxy_init_done(&pxp->ctrl_gt->uc.gsc, true))
203		return true;
204
205	return false;
206}
207
208int intel_pxp_gsccs_create_session(struct intel_pxp *pxp,
209				   int arb_session_id)
210{
211	struct drm_i915_private *i915 = pxp->ctrl_gt->i915;
212	struct pxp43_create_arb_in msg_in = {};
213	struct pxp43_create_arb_out msg_out = {};
214	int ret;
215
216	msg_in.header.api_version = PXP_APIVER(4, 3);
217	msg_in.header.command_id = PXP43_CMDID_INIT_SESSION;
218	msg_in.header.stream_id = (FIELD_PREP(PXP43_INIT_SESSION_APPID, arb_session_id) |
219				   FIELD_PREP(PXP43_INIT_SESSION_VALID, 1) |
220				   FIELD_PREP(PXP43_INIT_SESSION_APPTYPE, 0));
221	msg_in.header.buffer_len = sizeof(msg_in) - sizeof(msg_in.header);
222	msg_in.protection_mode = PXP43_INIT_SESSION_PROTECTION_ARB;
223
224	ret = gsccs_send_message_retry_complete(pxp,
225						&msg_in, sizeof(msg_in),
226						&msg_out, sizeof(msg_out), NULL);
227	if (ret) {
228		drm_err(&i915->drm, "Failed to init session %d, ret=[%d]\n", arb_session_id, ret);
229	} else if (msg_out.header.status != 0) {
230		if (is_fw_err_platform_config(pxp, msg_out.header.status)) {
231			drm_info_once(&i915->drm,
232				      "PXP init-session-%d failed due to BIOS/SOC:0x%08x:%s\n",
233				      arb_session_id, msg_out.header.status,
234				      fw_err_to_string(msg_out.header.status));
235		} else {
236			drm_dbg(&i915->drm, "PXP init-session-%d failed 0x%08x:%st:\n",
237				arb_session_id, msg_out.header.status,
238				fw_err_to_string(msg_out.header.status));
239			drm_dbg(&i915->drm, "     cmd-detail: ID=[0x%08x],API-Ver-[0x%08x]\n",
240				msg_in.header.command_id, msg_in.header.api_version);
241		}
242	}
243
244	return ret;
245}
246
247void intel_pxp_gsccs_end_arb_fw_session(struct intel_pxp *pxp, u32 session_id)
248{
249	struct drm_i915_private *i915 = pxp->ctrl_gt->i915;
250	struct pxp42_inv_stream_key_in msg_in = {};
251	struct pxp42_inv_stream_key_out msg_out = {};
252	int ret = 0;
253
254	/*
255	 * Stream key invalidation reuses the same version 4.2 input/output
256	 * command format but firmware requires 4.3 API interaction
257	 */
258	msg_in.header.api_version = PXP_APIVER(4, 3);
259	msg_in.header.command_id = PXP42_CMDID_INVALIDATE_STREAM_KEY;
260	msg_in.header.buffer_len = sizeof(msg_in) - sizeof(msg_in.header);
261
262	msg_in.header.stream_id = FIELD_PREP(PXP_CMDHDR_EXTDATA_SESSION_VALID, 1);
263	msg_in.header.stream_id |= FIELD_PREP(PXP_CMDHDR_EXTDATA_APP_TYPE, 0);
264	msg_in.header.stream_id |= FIELD_PREP(PXP_CMDHDR_EXTDATA_SESSION_ID, session_id);
265
266	ret = gsccs_send_message_retry_complete(pxp,
267						&msg_in, sizeof(msg_in),
268						&msg_out, sizeof(msg_out), NULL);
269	if (ret) {
270		drm_err(&i915->drm, "Failed to inv-stream-key-%u, ret=[%d]\n",
271			session_id, ret);
272	} else if (msg_out.header.status != 0) {
273		if (is_fw_err_platform_config(pxp, msg_out.header.status)) {
274			drm_info_once(&i915->drm,
275				      "PXP inv-stream-key-%u failed due to BIOS/SOC :0x%08x:%s\n",
276				      session_id, msg_out.header.status,
277				      fw_err_to_string(msg_out.header.status));
278		} else {
279			drm_dbg(&i915->drm, "PXP inv-stream-key-%u failed 0x%08x:%s:\n",
280				session_id, msg_out.header.status,
281				fw_err_to_string(msg_out.header.status));
282			drm_dbg(&i915->drm, "     cmd-detail: ID=[0x%08x],API-Ver-[0x%08x]\n",
283				msg_in.header.command_id, msg_in.header.api_version);
284		}
285	}
286}
287
288static void
289gsccs_cleanup_fw_host_session_handle(struct intel_pxp *pxp)
290{
291	struct drm_i915_private *i915 = pxp->ctrl_gt->i915;
292	int ret;
293
294	ret = gsccs_send_message_retry_complete(pxp, NULL, 0, NULL, 0, NULL);
295	if (ret)
296		drm_dbg(&i915->drm, "Failed to send gsccs msg host-session-cleanup: ret=[%d]\n",
297			ret);
298}
299
300static void
301gsccs_destroy_execution_resource(struct intel_pxp *pxp)
302{
303	struct gsccs_session_resources *exec_res = &pxp->gsccs_res;
304
305	if (exec_res->host_session_handle)
306		gsccs_cleanup_fw_host_session_handle(pxp);
307	if (exec_res->ce)
308		intel_context_put(exec_res->ce);
309	if (exec_res->bb_vma)
310		i915_vma_unpin_and_release(&exec_res->bb_vma, I915_VMA_RELEASE_MAP);
311	if (exec_res->pkt_vma)
312		i915_vma_unpin_and_release(&exec_res->pkt_vma, I915_VMA_RELEASE_MAP);
313
314	memset(exec_res, 0, sizeof(*exec_res));
315}
316
317static int
318gsccs_create_buffer(struct intel_gt *gt,
319		    const char *bufname, size_t size,
320		    struct i915_vma **vma, void **map)
321{
322	struct drm_i915_private *i915 = gt->i915;
323	struct drm_i915_gem_object *obj;
324	int err = 0;
325
326	obj = i915_gem_object_create_internal(i915, size);
327	if (IS_ERR(obj)) {
328		drm_err(&i915->drm, "Failed to allocate gsccs backend %s.\n", bufname);
329		err = PTR_ERR(obj);
330		goto out_none;
331	}
332
333	*vma = i915_vma_instance(obj, gt->vm, NULL);
334	if (IS_ERR(*vma)) {
335		drm_err(&i915->drm, "Failed to vma-instance gsccs backend %s.\n", bufname);
336		err = PTR_ERR(*vma);
337		goto out_put;
338	}
339
340	/* return a virtual pointer */
341	*map = i915_gem_object_pin_map_unlocked(obj, intel_gt_coherent_map_type(gt, obj, true));
342	if (IS_ERR(*map)) {
343		drm_err(&i915->drm, "Failed to map gsccs backend %s.\n", bufname);
344		err = PTR_ERR(*map);
345		goto out_put;
346	}
347
348	/* all PXP sessions commands are treated as non-privileged */
349	err = i915_vma_pin(*vma, 0, 0, PIN_USER);
350	if (err) {
351		drm_err(&i915->drm, "Failed to vma-pin gsccs backend %s.\n", bufname);
352		goto out_unmap;
353	}
354
355	return 0;
356
357out_unmap:
358	i915_gem_object_unpin_map(obj);
359out_put:
360	i915_gem_object_put(obj);
361out_none:
362	*vma = NULL;
363	*map = NULL;
364
365	return err;
366}
367
368static int
369gsccs_allocate_execution_resource(struct intel_pxp *pxp)
370{
371	struct intel_gt *gt = pxp->ctrl_gt;
372	struct gsccs_session_resources *exec_res = &pxp->gsccs_res;
373	struct intel_engine_cs *engine = gt->engine[GSC0];
374	struct intel_context *ce;
375	int err = 0;
376
377	/*
378	 * First, ensure the GSC engine is present.
379	 * NOTE: Backend would only be called with the correct gt.
380	 */
381	if (!engine)
382		return -ENODEV;
383
384	/*
385	 * Now, allocate, pin and map two objects, one for the heci message packet
386	 * and another for the batch buffer we submit into GSC engine (that includes the packet).
387	 * NOTE: GSC-CS backend is currently only supported on MTL, so we allocate shmem.
388	 */
389	err = gsccs_create_buffer(pxp->ctrl_gt, "Heci Packet",
390				  2 * PXP43_MAX_HECI_INOUT_SIZE,
391				  &exec_res->pkt_vma, &exec_res->pkt_vaddr);
392	if (err)
393		return err;
394
395	err = gsccs_create_buffer(pxp->ctrl_gt, "Batch Buffer", PAGE_SIZE,
396				  &exec_res->bb_vma, &exec_res->bb_vaddr);
397	if (err)
398		goto free_pkt;
399
400	/* Finally, create an intel_context to be used during the submission */
401	ce = intel_context_create(engine);
402	if (IS_ERR(ce)) {
403		drm_err(&gt->i915->drm, "Failed creating gsccs backend ctx\n");
404		err = PTR_ERR(ce);
405		goto free_batch;
406	}
407
408	i915_vm_put(ce->vm);
409	ce->vm = i915_vm_get(pxp->ctrl_gt->vm);
410	exec_res->ce = ce;
411
412	/* initialize host-session-handle (for all i915-to-gsc-firmware PXP cmds) */
413	get_random_bytes(&exec_res->host_session_handle, sizeof(exec_res->host_session_handle));
414
415	return 0;
416
417free_batch:
418	i915_vma_unpin_and_release(&exec_res->bb_vma, I915_VMA_RELEASE_MAP);
419free_pkt:
420	i915_vma_unpin_and_release(&exec_res->pkt_vma, I915_VMA_RELEASE_MAP);
421	memset(exec_res, 0, sizeof(*exec_res));
422
423	return err;
424}
425
426void intel_pxp_gsccs_fini(struct intel_pxp *pxp)
427{
428	intel_wakeref_t wakeref;
429
430	gsccs_destroy_execution_resource(pxp);
431	with_intel_runtime_pm(&pxp->ctrl_gt->i915->runtime_pm, wakeref)
432		intel_pxp_fini_hw(pxp);
433}
434
435int intel_pxp_gsccs_init(struct intel_pxp *pxp)
436{
437	int ret;
438	intel_wakeref_t wakeref;
439
440	ret = gsccs_allocate_execution_resource(pxp);
441	if (!ret) {
442		with_intel_runtime_pm(&pxp->ctrl_gt->i915->runtime_pm, wakeref)
443			intel_pxp_init_hw(pxp);
444	}
445	return ret;
446}