Merge tag 'drm-intel-gt-next-2023-10-19' of git://anongit.freedesktop.org/drm/drm...
[sfrench/cifs-2.6.git] / drivers / gpu / drm / i915 / pxp / intel_pxp_gsccs.c
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
20 static bool
21 is_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
35 static const char *
36 fw_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
52 static int
53 gsccs_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
159 unlock:
160         mutex_unlock(&pxp->tee_mutex);
161         return ret;
162 }
163
164 static int
165 gsccs_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
191 bool 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
208 int 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
247 void 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
288 static void
289 gsccs_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
300 static void
301 gsccs_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
317 static int
318 gsccs_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
357 out_unmap:
358         i915_gem_object_unpin_map(obj);
359 out_put:
360         i915_gem_object_put(obj);
361 out_none:
362         *vma = NULL;
363         *map = NULL;
364
365         return err;
366 }
367
368 static int
369 gsccs_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
417 free_batch:
418         i915_vma_unpin_and_release(&exec_res->bb_vma, I915_VMA_RELEASE_MAP);
419 free_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
426 void 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
435 int 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 }