1 | // SPDX-License-Identifier: MIT |
2 | /* |
3 | * Copyright 2023, Intel Corporation. |
4 | */ |
5 | |
6 | #include <drm/i915_hdcp_interface.h> |
7 | |
8 | #include "gem/i915_gem_region.h" |
9 | #include "gt/intel_gt.h" |
10 | #include "gt/uc/intel_gsc_uc_heci_cmd_submit.h" |
11 | #include "i915_drv.h" |
12 | #include "i915_utils.h" |
13 | #include "intel_hdcp_gsc.h" |
14 | #include "intel_hdcp_gsc_message.h" |
15 | |
16 | bool intel_hdcp_gsc_cs_required(struct drm_i915_private *i915) |
17 | { |
18 | return DISPLAY_VER(i915) >= 14; |
19 | } |
20 | |
21 | bool intel_hdcp_gsc_check_status(struct drm_i915_private *i915) |
22 | { |
23 | struct intel_gt *gt = i915->media_gt; |
24 | struct intel_gsc_uc *gsc = gt ? >->uc.gsc : NULL; |
25 | |
26 | if (!gsc || !intel_uc_fw_is_running(uc_fw: &gsc->fw)) { |
27 | drm_dbg_kms(&i915->drm, |
28 | "GSC components required for HDCP2.2 are not ready\n" ); |
29 | return false; |
30 | } |
31 | |
32 | return true; |
33 | } |
34 | |
35 | /*This function helps allocate memory for the command that we will send to gsc cs */ |
36 | static int intel_hdcp_gsc_initialize_message(struct drm_i915_private *i915, |
37 | struct intel_hdcp_gsc_message *hdcp_message) |
38 | { |
39 | struct intel_gt *gt = i915->media_gt; |
40 | struct drm_i915_gem_object *obj = NULL; |
41 | struct i915_vma *vma = NULL; |
42 | void *cmd_in, *cmd_out; |
43 | int err; |
44 | |
45 | /* allocate object of two page for HDCP command memory and store it */ |
46 | obj = i915_gem_object_create_shmem(i915, size: 2 * PAGE_SIZE); |
47 | |
48 | if (IS_ERR(ptr: obj)) { |
49 | drm_err(&i915->drm, "Failed to allocate HDCP streaming command!\n" ); |
50 | return PTR_ERR(ptr: obj); |
51 | } |
52 | |
53 | cmd_in = i915_gem_object_pin_map_unlocked(obj, type: intel_gt_coherent_map_type(gt, obj, always_coherent: true)); |
54 | if (IS_ERR(ptr: cmd_in)) { |
55 | drm_err(&i915->drm, "Failed to map gsc message page!\n" ); |
56 | err = PTR_ERR(ptr: cmd_in); |
57 | goto out_unpin; |
58 | } |
59 | |
60 | cmd_out = cmd_in + PAGE_SIZE; |
61 | |
62 | vma = i915_vma_instance(obj, vm: >->ggtt->vm, NULL); |
63 | if (IS_ERR(ptr: vma)) { |
64 | err = PTR_ERR(ptr: vma); |
65 | goto out_unmap; |
66 | } |
67 | |
68 | err = i915_vma_pin(vma, size: 0, alignment: 0, PIN_GLOBAL | PIN_HIGH); |
69 | if (err) |
70 | goto out_unmap; |
71 | |
72 | memset(cmd_in, 0, obj->base.size); |
73 | |
74 | hdcp_message->hdcp_cmd_in = cmd_in; |
75 | hdcp_message->hdcp_cmd_out = cmd_out; |
76 | hdcp_message->vma = vma; |
77 | |
78 | return 0; |
79 | |
80 | out_unmap: |
81 | i915_gem_object_unpin_map(obj); |
82 | out_unpin: |
83 | i915_gem_object_put(obj); |
84 | return err; |
85 | } |
86 | |
87 | static const struct i915_hdcp_ops gsc_hdcp_ops = { |
88 | .initiate_hdcp2_session = intel_hdcp_gsc_initiate_session, |
89 | .verify_receiver_cert_prepare_km = |
90 | intel_hdcp_gsc_verify_receiver_cert_prepare_km, |
91 | .verify_hprime = intel_hdcp_gsc_verify_hprime, |
92 | .store_pairing_info = intel_hdcp_gsc_store_pairing_info, |
93 | .initiate_locality_check = intel_hdcp_gsc_initiate_locality_check, |
94 | .verify_lprime = intel_hdcp_gsc_verify_lprime, |
95 | .get_session_key = intel_hdcp_gsc_get_session_key, |
96 | .repeater_check_flow_prepare_ack = |
97 | intel_hdcp_gsc_repeater_check_flow_prepare_ack, |
98 | .verify_mprime = intel_hdcp_gsc_verify_mprime, |
99 | .enable_hdcp_authentication = intel_hdcp_gsc_enable_authentication, |
100 | .close_hdcp_session = intel_hdcp_gsc_close_session, |
101 | }; |
102 | |
103 | static int intel_hdcp_gsc_hdcp2_init(struct drm_i915_private *i915) |
104 | { |
105 | struct intel_hdcp_gsc_message *hdcp_message; |
106 | int ret; |
107 | |
108 | hdcp_message = kzalloc(size: sizeof(*hdcp_message), GFP_KERNEL); |
109 | |
110 | if (!hdcp_message) |
111 | return -ENOMEM; |
112 | |
113 | /* |
114 | * NOTE: No need to lock the comp mutex here as it is already |
115 | * going to be taken before this function called |
116 | */ |
117 | i915->display.hdcp.hdcp_message = hdcp_message; |
118 | ret = intel_hdcp_gsc_initialize_message(i915, hdcp_message); |
119 | |
120 | if (ret) |
121 | drm_err(&i915->drm, "Could not initialize hdcp_message\n" ); |
122 | |
123 | return ret; |
124 | } |
125 | |
126 | static void intel_hdcp_gsc_free_message(struct drm_i915_private *i915) |
127 | { |
128 | struct intel_hdcp_gsc_message *hdcp_message = |
129 | i915->display.hdcp.hdcp_message; |
130 | |
131 | hdcp_message->hdcp_cmd_in = NULL; |
132 | hdcp_message->hdcp_cmd_out = NULL; |
133 | i915_vma_unpin_and_release(p_vma: &hdcp_message->vma, I915_VMA_RELEASE_MAP); |
134 | kfree(objp: hdcp_message); |
135 | } |
136 | |
137 | int intel_hdcp_gsc_init(struct drm_i915_private *i915) |
138 | { |
139 | struct i915_hdcp_arbiter *data; |
140 | int ret; |
141 | |
142 | data = kzalloc(size: sizeof(struct i915_hdcp_arbiter), GFP_KERNEL); |
143 | if (!data) |
144 | return -ENOMEM; |
145 | |
146 | mutex_lock(&i915->display.hdcp.hdcp_mutex); |
147 | i915->display.hdcp.arbiter = data; |
148 | i915->display.hdcp.arbiter->hdcp_dev = i915->drm.dev; |
149 | i915->display.hdcp.arbiter->ops = &gsc_hdcp_ops; |
150 | ret = intel_hdcp_gsc_hdcp2_init(i915); |
151 | mutex_unlock(lock: &i915->display.hdcp.hdcp_mutex); |
152 | |
153 | return ret; |
154 | } |
155 | |
156 | void intel_hdcp_gsc_fini(struct drm_i915_private *i915) |
157 | { |
158 | intel_hdcp_gsc_free_message(i915); |
159 | kfree(objp: i915->display.hdcp.arbiter); |
160 | } |
161 | |
162 | static int intel_gsc_send_sync(struct drm_i915_private *i915, |
163 | struct intel_gsc_mtl_header *, |
164 | struct intel_gsc_mtl_header *, |
165 | u64 addr_in, u64 addr_out, |
166 | size_t msg_out_len) |
167 | { |
168 | struct intel_gt *gt = i915->media_gt; |
169 | int ret; |
170 | |
171 | ret = intel_gsc_uc_heci_cmd_submit_packet(gsc: >->uc.gsc, addr_in, |
172 | size_in: header_in->message_size, |
173 | addr_out, |
174 | size_out: msg_out_len + sizeof(*header_out)); |
175 | if (ret) { |
176 | drm_err(&i915->drm, "failed to send gsc HDCP msg (%d)\n" , ret); |
177 | return ret; |
178 | } |
179 | |
180 | /* |
181 | * Checking validity marker and header status to see if some error has |
182 | * blocked us from sending message to gsc cs |
183 | */ |
184 | if (header_out->validity_marker != GSC_HECI_VALIDITY_MARKER) { |
185 | drm_err(&i915->drm, "invalid validity marker\n" ); |
186 | return -EINVAL; |
187 | } |
188 | |
189 | if (header_out->status != 0) { |
190 | drm_err(&i915->drm, "header status indicates error %d\n" , |
191 | header_out->status); |
192 | return -EINVAL; |
193 | } |
194 | |
195 | if (header_out->flags & GSC_OUTFLAG_MSG_PENDING) { |
196 | header_in->gsc_message_handle = header_out->gsc_message_handle; |
197 | return -EAGAIN; |
198 | } |
199 | |
200 | return 0; |
201 | } |
202 | |
203 | /* |
204 | * This function can now be used for sending requests and will also handle |
205 | * receipt of reply messages hence no different function of message retrieval |
206 | * is required. We will initialize intel_hdcp_gsc_message structure then add |
207 | * gsc cs memory header as stated in specs after which the normal HDCP payload |
208 | * will follow |
209 | */ |
210 | ssize_t intel_hdcp_gsc_msg_send(struct drm_i915_private *i915, u8 *msg_in, |
211 | size_t msg_in_len, u8 *msg_out, |
212 | size_t msg_out_len) |
213 | { |
214 | struct intel_gt *gt = i915->media_gt; |
215 | struct intel_gsc_mtl_header *, *; |
216 | const size_t max_msg_size = PAGE_SIZE - sizeof(*header_in); |
217 | struct intel_hdcp_gsc_message *hdcp_message; |
218 | u64 addr_in, addr_out, host_session_id; |
219 | u32 reply_size, msg_size_in, msg_size_out; |
220 | int ret, tries = 0; |
221 | |
222 | if (!intel_uc_uses_gsc_uc(uc: >->uc)) |
223 | return -ENODEV; |
224 | |
225 | if (msg_in_len > max_msg_size || msg_out_len > max_msg_size) |
226 | return -ENOSPC; |
227 | |
228 | msg_size_in = msg_in_len + sizeof(*header_in); |
229 | msg_size_out = msg_out_len + sizeof(*header_out); |
230 | hdcp_message = i915->display.hdcp.hdcp_message; |
231 | header_in = hdcp_message->hdcp_cmd_in; |
232 | header_out = hdcp_message->hdcp_cmd_out; |
233 | addr_in = i915_ggtt_offset(vma: hdcp_message->vma); |
234 | addr_out = addr_in + PAGE_SIZE; |
235 | |
236 | memset(header_in, 0, msg_size_in); |
237 | memset(header_out, 0, msg_size_out); |
238 | get_random_bytes(buf: &host_session_id, len: sizeof(u64)); |
239 | intel_gsc_uc_heci_cmd_emit_mtl_header(header: header_in, HECI_MEADDRESS_HDCP, |
240 | msg_size: msg_size_in, host_session_id); |
241 | memcpy(hdcp_message->hdcp_cmd_in + sizeof(*header_in), msg_in, msg_in_len); |
242 | |
243 | /* |
244 | * Keep sending request in case the pending bit is set no need to add |
245 | * message handle as we are using same address hence loc. of header is |
246 | * same and it will contain the message handle. we will send the message |
247 | * 20 times each message 50 ms apart |
248 | */ |
249 | do { |
250 | ret = intel_gsc_send_sync(i915, header_in, header_out, addr_in, |
251 | addr_out, msg_out_len); |
252 | |
253 | /* Only try again if gsc says so */ |
254 | if (ret != -EAGAIN) |
255 | break; |
256 | |
257 | msleep(msecs: 50); |
258 | |
259 | } while (++tries < 20); |
260 | |
261 | if (ret) |
262 | goto err; |
263 | |
264 | /* we use the same mem for the reply, so header is in the same loc */ |
265 | reply_size = header_out->message_size - sizeof(*header_out); |
266 | if (reply_size > msg_out_len) { |
267 | drm_warn(&i915->drm, "caller with insufficient HDCP reply size %u (%d)\n" , |
268 | reply_size, (u32)msg_out_len); |
269 | reply_size = msg_out_len; |
270 | } else if (reply_size != msg_out_len) { |
271 | drm_dbg_kms(&i915->drm, "caller unexpected HCDP reply size %u (%d)\n" , |
272 | reply_size, (u32)msg_out_len); |
273 | } |
274 | |
275 | memcpy(msg_out, hdcp_message->hdcp_cmd_out + sizeof(*header_out), msg_out_len); |
276 | |
277 | err: |
278 | return ret; |
279 | } |
280 | |