Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1// SPDX-License-Identifier: MIT
2/*
3 * Copyright © 2023 Intel Corporation
4 */
5
6#include "xe_gsc_submit.h"
7
8#include <linux/poison.h>
9
10#include "abi/gsc_command_header_abi.h"
11#include "xe_assert.h"
12#include "xe_bb.h"
13#include "xe_exec_queue.h"
14#include "xe_gt_types.h"
15#include "xe_map.h"
16#include "xe_sched_job.h"
17#include "instructions/xe_gsc_commands.h"
18
19#define GSC_HDR_SIZE (sizeof(struct intel_gsc_mtl_header)) /* shorthand define */
20
21#define mtl_gsc_header_wr(xe_, map_, offset_, field_, val_) \
22 xe_map_wr_field(xe_, map_, offset_, struct intel_gsc_mtl_header, field_, val_)
23
24#define mtl_gsc_header_rd(xe_, map_, offset_, field_) \
25 xe_map_rd_field(xe_, map_, offset_, struct intel_gsc_mtl_header, field_)
26
27/*
28 * GSC FW allows us to define the host_session_handle as we see fit, as long
29 * as we use unique identifier for each user, with handle 0 being reserved for
30 * kernel usage.
31 * To be able to differentiate which client subsystem owns the given session, we
32 * include the client id in the top 8 bits of the handle.
33 */
34#define HOST_SESSION_CLIENT_MASK GENMASK_ULL(63, 56)
35
36static struct xe_gt *
37gsc_to_gt(struct xe_gsc *gsc)
38{
39 return container_of(gsc, struct xe_gt, uc.gsc);
40}
41
42/**
43 * xe_gsc_create_host_session_id - Creates a random 64 bit host_session id with
44 * bits 56-63 masked.
45 *
46 * Returns: random host_session_id which can be used to send messages to gsc cs
47 */
48u64 xe_gsc_create_host_session_id(void)
49{
50 u64 host_session_id;
51
52 get_random_bytes(&host_session_id, sizeof(u64));
53 host_session_id &= ~HOST_SESSION_CLIENT_MASK;
54 return host_session_id;
55}
56
57/**
58 * xe_gsc_emit_header - write the MTL GSC header in memory
59 * @xe: the Xe device
60 * @map: the iosys map to write to
61 * @offset: offset from the start of the map at which to write the header
62 * @heci_client_id: client id identifying the type of command (see abi for values)
63 * @host_session_id: host session ID of the caller
64 * @payload_size: size of the payload that follows the header
65 *
66 * Returns: offset memory location following the header
67 */
68u32 xe_gsc_emit_header(struct xe_device *xe, struct iosys_map *map, u32 offset,
69 u8 heci_client_id, u64 host_session_id, u32 payload_size)
70{
71 xe_assert(xe, !(host_session_id & HOST_SESSION_CLIENT_MASK));
72
73 if (host_session_id)
74 host_session_id |= FIELD_PREP(HOST_SESSION_CLIENT_MASK, heci_client_id);
75
76 xe_map_memset(xe, map, offset, 0, GSC_HDR_SIZE);
77
78 mtl_gsc_header_wr(xe, map, offset, validity_marker, GSC_HECI_VALIDITY_MARKER);
79 mtl_gsc_header_wr(xe, map, offset, heci_client_id, heci_client_id);
80 mtl_gsc_header_wr(xe, map, offset, host_session_handle, host_session_id);
81 mtl_gsc_header_wr(xe, map, offset, header_version, MTL_GSC_HEADER_VERSION);
82 mtl_gsc_header_wr(xe, map, offset, message_size, payload_size + GSC_HDR_SIZE);
83
84 return offset + GSC_HDR_SIZE;
85};
86
87/**
88 * xe_gsc_poison_header - poison the MTL GSC header in memory
89 * @xe: the Xe device
90 * @map: the iosys map to write to
91 * @offset: offset from the start of the map at which the header resides
92 */
93void xe_gsc_poison_header(struct xe_device *xe, struct iosys_map *map, u32 offset)
94{
95 xe_map_memset(xe, map, offset, POISON_FREE, GSC_HDR_SIZE);
96};
97
98/**
99 * xe_gsc_check_and_update_pending - check the pending bit and update the input
100 * header with the retry handle from the output header
101 * @xe: the Xe device
102 * @in: the iosys map containing the input buffer
103 * @offset_in: offset within the iosys at which the input buffer is located
104 * @out: the iosys map containing the output buffer
105 * @offset_out: offset within the iosys at which the output buffer is located
106 *
107 * Returns: true if the pending bit was set, false otherwise
108 */
109bool xe_gsc_check_and_update_pending(struct xe_device *xe,
110 struct iosys_map *in, u32 offset_in,
111 struct iosys_map *out, u32 offset_out)
112{
113 if (mtl_gsc_header_rd(xe, out, offset_out, flags) & GSC_OUTFLAG_MSG_PENDING) {
114 u64 handle = mtl_gsc_header_rd(xe, out, offset_out, gsc_message_handle);
115
116 mtl_gsc_header_wr(xe, in, offset_in, gsc_message_handle, handle);
117
118 return true;
119 }
120
121 return false;
122}
123
124/**
125 * xe_gsc_read_out_header - reads and validates the output header and returns
126 * the offset of the reply following the header
127 * @xe: the Xe device
128 * @map: the iosys map containing the output buffer
129 * @offset: offset within the iosys at which the output buffer is located
130 * @min_payload_size: minimum size of the message excluding the gsc header
131 * @payload_offset: optional pointer to be set to the payload offset
132 *
133 * Returns: -errno value on failure, 0 otherwise
134 */
135int xe_gsc_read_out_header(struct xe_device *xe,
136 struct iosys_map *map, u32 offset,
137 u32 min_payload_size,
138 u32 *payload_offset)
139{
140 u32 marker = mtl_gsc_header_rd(xe, map, offset, validity_marker);
141 u32 size = mtl_gsc_header_rd(xe, map, offset, message_size);
142 u32 status = mtl_gsc_header_rd(xe, map, offset, status);
143 u32 payload_size = size - GSC_HDR_SIZE;
144
145 if (marker != GSC_HECI_VALIDITY_MARKER)
146 return -EPROTO;
147
148 if (status != 0) {
149 drm_err(&xe->drm, "GSC header readout indicates error: %d\n",
150 status);
151 return -EINVAL;
152 }
153
154 if (size < GSC_HDR_SIZE || payload_size < min_payload_size)
155 return -ENODATA;
156
157 if (payload_offset)
158 *payload_offset = offset + GSC_HDR_SIZE;
159
160 return 0;
161}
162
163/**
164 * xe_gsc_pkt_submit_kernel - submit a kernel heci pkt to the GSC
165 * @gsc: the GSC uC
166 * @addr_in: GGTT address of the message to send to the GSC
167 * @size_in: size of the message to send to the GSC
168 * @addr_out: GGTT address for the GSC to write the reply to
169 * @size_out: size of the memory reserved for the reply
170 */
171int xe_gsc_pkt_submit_kernel(struct xe_gsc *gsc, u64 addr_in, u32 size_in,
172 u64 addr_out, u32 size_out)
173{
174 struct xe_gt *gt = gsc_to_gt(gsc);
175 struct xe_bb *bb;
176 struct xe_sched_job *job;
177 struct dma_fence *fence;
178 long timeout;
179
180 if (size_in < GSC_HDR_SIZE)
181 return -ENODATA;
182
183 if (size_out < GSC_HDR_SIZE)
184 return -ENOMEM;
185
186 bb = xe_bb_new(gt, 8, false);
187 if (IS_ERR(bb))
188 return PTR_ERR(bb);
189
190 bb->cs[bb->len++] = GSC_HECI_CMD_PKT;
191 bb->cs[bb->len++] = lower_32_bits(addr_in);
192 bb->cs[bb->len++] = upper_32_bits(addr_in);
193 bb->cs[bb->len++] = size_in;
194 bb->cs[bb->len++] = lower_32_bits(addr_out);
195 bb->cs[bb->len++] = upper_32_bits(addr_out);
196 bb->cs[bb->len++] = size_out;
197 bb->cs[bb->len++] = 0;
198
199 job = xe_bb_create_job(gsc->q, bb);
200 if (IS_ERR(job)) {
201 xe_bb_free(bb, NULL);
202 return PTR_ERR(job);
203 }
204
205 xe_sched_job_arm(job);
206 fence = dma_fence_get(&job->drm.s_fence->finished);
207 xe_sched_job_push(job);
208
209 timeout = dma_fence_wait_timeout(fence, false, HZ);
210 dma_fence_put(fence);
211 xe_bb_free(bb, NULL);
212 if (timeout < 0)
213 return timeout;
214 else if (!timeout)
215 return -ETIME;
216
217 return 0;
218}