Commit | Line | Data |
---|---|---|
6498bf58 R |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * Copyright (C) 2019 Intel Corporation. | |
4 | * | |
5 | * Authors: | |
6 | * Ramalingam C <ramalingam.c@intel.com> | |
7 | */ | |
8 | ||
9 | #include <linux/device.h> | |
10 | #include <linux/err.h> | |
11 | #include <linux/gfp.h> | |
12 | #include <linux/export.h> | |
13 | #include <linux/slab.h> | |
14 | #include <linux/firmware.h> | |
15 | ||
16 | #include <drm/drm_hdcp.h> | |
17 | #include <drm/drm_sysfs.h> | |
18 | #include <drm/drm_print.h> | |
19 | #include <drm/drm_device.h> | |
c16fd9be R |
20 | #include <drm/drm_property.h> |
21 | #include <drm/drm_mode_object.h> | |
22 | #include <drm/drm_connector.h> | |
6498bf58 | 23 | |
cdd07596 R |
24 | #include "drm_internal.h" |
25 | ||
6498bf58 R |
26 | static inline void drm_hdcp_print_ksv(const u8 *ksv) |
27 | { | |
28 | DRM_DEBUG("\t%#02x, %#02x, %#02x, %#02x, %#02x\n", | |
29 | ksv[0], ksv[1], ksv[2], ksv[3], ksv[4]); | |
30 | } | |
31 | ||
32 | static u32 drm_hdcp_get_revoked_ksv_count(const u8 *buf, u32 vrls_length) | |
33 | { | |
34 | u32 parsed_bytes = 0, ksv_count = 0, vrl_ksv_cnt, vrl_sz; | |
35 | ||
36 | while (parsed_bytes < vrls_length) { | |
37 | vrl_ksv_cnt = *buf; | |
38 | ksv_count += vrl_ksv_cnt; | |
39 | ||
40 | vrl_sz = (vrl_ksv_cnt * DRM_HDCP_KSV_LEN) + 1; | |
41 | buf += vrl_sz; | |
42 | parsed_bytes += vrl_sz; | |
43 | } | |
44 | ||
45 | /* | |
46 | * When vrls are not valid, ksvs are not considered. | |
47 | * Hence SRM will be discarded. | |
48 | */ | |
49 | if (parsed_bytes != vrls_length) | |
50 | ksv_count = 0; | |
51 | ||
52 | return ksv_count; | |
53 | } | |
54 | ||
79643fdd | 55 | static u32 drm_hdcp_get_revoked_ksvs(const u8 *buf, u8 **revoked_ksv_list, |
6498bf58 R |
56 | u32 vrls_length) |
57 | { | |
6498bf58 | 58 | u32 vrl_ksv_cnt, vrl_ksv_sz, vrl_idx = 0; |
79643fdd | 59 | u32 parsed_bytes = 0, ksv_count = 0; |
6498bf58 R |
60 | |
61 | do { | |
62 | vrl_ksv_cnt = *buf; | |
63 | vrl_ksv_sz = vrl_ksv_cnt * DRM_HDCP_KSV_LEN; | |
64 | ||
65 | buf++; | |
66 | ||
67 | DRM_DEBUG("vrl: %d, Revoked KSVs: %d\n", vrl_idx++, | |
68 | vrl_ksv_cnt); | |
79643fdd R |
69 | memcpy((*revoked_ksv_list) + (ksv_count * DRM_HDCP_KSV_LEN), |
70 | buf, vrl_ksv_sz); | |
6498bf58 R |
71 | |
72 | ksv_count += vrl_ksv_cnt; | |
6498bf58 R |
73 | buf += vrl_ksv_sz; |
74 | ||
75 | parsed_bytes += (vrl_ksv_sz + 1); | |
76 | } while (parsed_bytes < vrls_length); | |
77 | ||
78 | return ksv_count; | |
79 | } | |
80 | ||
81 | static inline u32 get_vrl_length(const u8 *buf) | |
82 | { | |
83 | return drm_hdcp_be24_to_cpu(buf); | |
84 | } | |
85 | ||
79643fdd R |
86 | static int drm_hdcp_parse_hdcp1_srm(const u8 *buf, size_t count, |
87 | u8 **revoked_ksv_list, u32 *revoked_ksv_cnt) | |
6498bf58 R |
88 | { |
89 | struct hdcp_srm_header *header; | |
90 | u32 vrl_length, ksv_count; | |
91 | ||
92 | if (count < (sizeof(struct hdcp_srm_header) + | |
93 | DRM_HDCP_1_4_VRL_LENGTH_SIZE + DRM_HDCP_1_4_DCP_SIG_SIZE)) { | |
94 | DRM_ERROR("Invalid blob length\n"); | |
95 | return -EINVAL; | |
96 | } | |
97 | ||
98 | header = (struct hdcp_srm_header *)buf; | |
99 | DRM_DEBUG("SRM ID: 0x%x, SRM Ver: 0x%x, SRM Gen No: 0x%x\n", | |
100 | header->srm_id, | |
101 | be16_to_cpu(header->srm_version), header->srm_gen_no); | |
102 | ||
103 | WARN_ON(header->reserved); | |
104 | ||
105 | buf = buf + sizeof(*header); | |
106 | vrl_length = get_vrl_length(buf); | |
107 | if (count < (sizeof(struct hdcp_srm_header) + vrl_length) || | |
108 | vrl_length < (DRM_HDCP_1_4_VRL_LENGTH_SIZE + | |
109 | DRM_HDCP_1_4_DCP_SIG_SIZE)) { | |
110 | DRM_ERROR("Invalid blob length or vrl length\n"); | |
111 | return -EINVAL; | |
112 | } | |
113 | ||
114 | /* Length of the all vrls combined */ | |
115 | vrl_length -= (DRM_HDCP_1_4_VRL_LENGTH_SIZE + | |
116 | DRM_HDCP_1_4_DCP_SIG_SIZE); | |
117 | ||
118 | if (!vrl_length) { | |
119 | DRM_ERROR("No vrl found\n"); | |
120 | return -EINVAL; | |
121 | } | |
122 | ||
123 | buf += DRM_HDCP_1_4_VRL_LENGTH_SIZE; | |
124 | ksv_count = drm_hdcp_get_revoked_ksv_count(buf, vrl_length); | |
125 | if (!ksv_count) { | |
126 | DRM_DEBUG("Revoked KSV count is 0\n"); | |
79643fdd | 127 | return 0; |
6498bf58 R |
128 | } |
129 | ||
79643fdd R |
130 | *revoked_ksv_list = kcalloc(ksv_count, DRM_HDCP_KSV_LEN, GFP_KERNEL); |
131 | if (!*revoked_ksv_list) { | |
6498bf58 R |
132 | DRM_ERROR("Out of Memory\n"); |
133 | return -ENOMEM; | |
134 | } | |
135 | ||
79643fdd | 136 | if (drm_hdcp_get_revoked_ksvs(buf, revoked_ksv_list, |
6498bf58 | 137 | vrl_length) != ksv_count) { |
79643fdd R |
138 | *revoked_ksv_cnt = 0; |
139 | kfree(*revoked_ksv_list); | |
6498bf58 R |
140 | return -EINVAL; |
141 | } | |
142 | ||
79643fdd R |
143 | *revoked_ksv_cnt = ksv_count; |
144 | return 0; | |
6498bf58 R |
145 | } |
146 | ||
79643fdd R |
147 | static int drm_hdcp_parse_hdcp2_srm(const u8 *buf, size_t count, |
148 | u8 **revoked_ksv_list, u32 *revoked_ksv_cnt) | |
6498bf58 R |
149 | { |
150 | struct hdcp_srm_header *header; | |
151 | u32 vrl_length, ksv_count, ksv_sz; | |
152 | ||
153 | if (count < (sizeof(struct hdcp_srm_header) + | |
154 | DRM_HDCP_2_VRL_LENGTH_SIZE + DRM_HDCP_2_DCP_SIG_SIZE)) { | |
155 | DRM_ERROR("Invalid blob length\n"); | |
156 | return -EINVAL; | |
157 | } | |
158 | ||
159 | header = (struct hdcp_srm_header *)buf; | |
160 | DRM_DEBUG("SRM ID: 0x%x, SRM Ver: 0x%x, SRM Gen No: 0x%x\n", | |
161 | header->srm_id & DRM_HDCP_SRM_ID_MASK, | |
162 | be16_to_cpu(header->srm_version), header->srm_gen_no); | |
163 | ||
164 | if (header->reserved) | |
165 | return -EINVAL; | |
166 | ||
167 | buf = buf + sizeof(*header); | |
168 | vrl_length = get_vrl_length(buf); | |
169 | ||
170 | if (count < (sizeof(struct hdcp_srm_header) + vrl_length) || | |
171 | vrl_length < (DRM_HDCP_2_VRL_LENGTH_SIZE + | |
172 | DRM_HDCP_2_DCP_SIG_SIZE)) { | |
173 | DRM_ERROR("Invalid blob length or vrl length\n"); | |
174 | return -EINVAL; | |
175 | } | |
176 | ||
177 | /* Length of the all vrls combined */ | |
178 | vrl_length -= (DRM_HDCP_2_VRL_LENGTH_SIZE + | |
179 | DRM_HDCP_2_DCP_SIG_SIZE); | |
180 | ||
181 | if (!vrl_length) { | |
182 | DRM_ERROR("No vrl found\n"); | |
183 | return -EINVAL; | |
184 | } | |
185 | ||
186 | buf += DRM_HDCP_2_VRL_LENGTH_SIZE; | |
187 | ksv_count = (*buf << 2) | DRM_HDCP_2_KSV_COUNT_2_LSBITS(*(buf + 1)); | |
188 | if (!ksv_count) { | |
189 | DRM_DEBUG("Revoked KSV count is 0\n"); | |
79643fdd | 190 | return 0; |
6498bf58 R |
191 | } |
192 | ||
79643fdd R |
193 | *revoked_ksv_list = kcalloc(ksv_count, DRM_HDCP_KSV_LEN, GFP_KERNEL); |
194 | if (!*revoked_ksv_list) { | |
6498bf58 R |
195 | DRM_ERROR("Out of Memory\n"); |
196 | return -ENOMEM; | |
197 | } | |
198 | ||
199 | ksv_sz = ksv_count * DRM_HDCP_KSV_LEN; | |
200 | buf += DRM_HDCP_2_NO_OF_DEV_PLUS_RESERVED_SZ; | |
201 | ||
202 | DRM_DEBUG("Revoked KSVs: %d\n", ksv_count); | |
79643fdd | 203 | memcpy(*revoked_ksv_list, buf, ksv_sz); |
6498bf58 | 204 | |
79643fdd R |
205 | *revoked_ksv_cnt = ksv_count; |
206 | return 0; | |
6498bf58 R |
207 | } |
208 | ||
209 | static inline bool is_srm_version_hdcp1(const u8 *buf) | |
210 | { | |
211 | return *buf == (u8)(DRM_HDCP_1_4_SRM_ID << 4); | |
212 | } | |
213 | ||
214 | static inline bool is_srm_version_hdcp2(const u8 *buf) | |
215 | { | |
216 | return *buf == (u8)(DRM_HDCP_2_SRM_ID << 4 | DRM_HDCP_2_INDICATOR); | |
217 | } | |
218 | ||
79643fdd R |
219 | static int drm_hdcp_srm_update(const u8 *buf, size_t count, |
220 | u8 **revoked_ksv_list, u32 *revoked_ksv_cnt) | |
6498bf58 R |
221 | { |
222 | if (count < sizeof(struct hdcp_srm_header)) | |
79643fdd | 223 | return -EINVAL; |
6498bf58 R |
224 | |
225 | if (is_srm_version_hdcp1(buf)) | |
79643fdd R |
226 | return drm_hdcp_parse_hdcp1_srm(buf, count, revoked_ksv_list, |
227 | revoked_ksv_cnt); | |
6498bf58 | 228 | else if (is_srm_version_hdcp2(buf)) |
79643fdd R |
229 | return drm_hdcp_parse_hdcp2_srm(buf, count, revoked_ksv_list, |
230 | revoked_ksv_cnt); | |
231 | else | |
232 | return -EINVAL; | |
6498bf58 R |
233 | } |
234 | ||
79643fdd R |
235 | static int drm_hdcp_request_srm(struct drm_device *drm_dev, |
236 | u8 **revoked_ksv_list, u32 *revoked_ksv_cnt) | |
6498bf58 R |
237 | { |
238 | char fw_name[36] = "display_hdcp_srm.bin"; | |
239 | const struct firmware *fw; | |
6498bf58 R |
240 | int ret; |
241 | ||
242 | ret = request_firmware_direct(&fw, (const char *)fw_name, | |
243 | drm_dev->dev); | |
5fe89a6a SP |
244 | if (ret < 0) { |
245 | *revoked_ksv_cnt = 0; | |
246 | *revoked_ksv_list = NULL; | |
247 | ret = 0; | |
6498bf58 | 248 | goto exit; |
5fe89a6a | 249 | } |
6498bf58 R |
250 | |
251 | if (fw->size && fw->data) | |
79643fdd R |
252 | ret = drm_hdcp_srm_update(fw->data, fw->size, revoked_ksv_list, |
253 | revoked_ksv_cnt); | |
6498bf58 R |
254 | |
255 | exit: | |
256 | release_firmware(fw); | |
79643fdd | 257 | return ret; |
6498bf58 R |
258 | } |
259 | ||
260 | /** | |
261 | * drm_hdcp_check_ksvs_revoked - Check the revoked status of the IDs | |
262 | * | |
263 | * @drm_dev: drm_device for which HDCP revocation check is requested | |
264 | * @ksvs: List of KSVs (HDCP receiver IDs) | |
265 | * @ksv_count: KSV count passed in through @ksvs | |
266 | * | |
267 | * This function reads the HDCP System renewability Message(SRM Table) | |
268 | * from userspace as a firmware and parses it for the revoked HDCP | |
269 | * KSVs(Receiver IDs) detected by DCP LLC. Once the revoked KSVs are known, | |
270 | * revoked state of the KSVs in the list passed in by display drivers are | |
271 | * decided and response is sent. | |
272 | * | |
273 | * SRM should be presented in the name of "display_hdcp_srm.bin". | |
274 | * | |
bc0380b9 R |
275 | * Format of the SRM table, that userspace needs to write into the binary file, |
276 | * is defined at: | |
277 | * 1. Renewability chapter on 55th page of HDCP 1.4 specification | |
278 | * https://www.digital-cp.com/sites/default/files/specifications/HDCP%20Specification%20Rev1_4_Secure.pdf | |
279 | * 2. Renewability chapter on 63rd page of HDCP 2.2 specification | |
280 | * https://www.digital-cp.com/sites/default/files/specifications/HDCP%20on%20HDMI%20Specification%20Rev2_2_Final1.pdf | |
281 | * | |
6498bf58 | 282 | * Returns: |
79643fdd | 283 | * Count of the revoked KSVs or -ve error number incase of the failure. |
6498bf58 | 284 | */ |
79643fdd R |
285 | int drm_hdcp_check_ksvs_revoked(struct drm_device *drm_dev, u8 *ksvs, |
286 | u32 ksv_count) | |
6498bf58 | 287 | { |
79643fdd R |
288 | u32 revoked_ksv_cnt = 0, i, j; |
289 | u8 *revoked_ksv_list = NULL; | |
290 | int ret = 0; | |
291 | ||
292 | ret = drm_hdcp_request_srm(drm_dev, &revoked_ksv_list, | |
293 | &revoked_ksv_cnt); | |
5fe89a6a SP |
294 | if (ret) |
295 | return ret; | |
79643fdd R |
296 | |
297 | /* revoked_ksv_cnt will be zero when above function failed */ | |
298 | for (i = 0; i < revoked_ksv_cnt; i++) | |
299 | for (j = 0; j < ksv_count; j++) | |
300 | if (!memcmp(&ksvs[j * DRM_HDCP_KSV_LEN], | |
301 | &revoked_ksv_list[i * DRM_HDCP_KSV_LEN], | |
302 | DRM_HDCP_KSV_LEN)) { | |
303 | DRM_DEBUG("Revoked KSV is "); | |
304 | drm_hdcp_print_ksv(&ksvs[j * DRM_HDCP_KSV_LEN]); | |
305 | ret++; | |
306 | } | |
307 | ||
308 | kfree(revoked_ksv_list); | |
309 | return ret; | |
6498bf58 R |
310 | } |
311 | EXPORT_SYMBOL_GPL(drm_hdcp_check_ksvs_revoked); | |
312 | ||
c16fd9be R |
313 | static struct drm_prop_enum_list drm_cp_enum_list[] = { |
314 | { DRM_MODE_CONTENT_PROTECTION_UNDESIRED, "Undesired" }, | |
315 | { DRM_MODE_CONTENT_PROTECTION_DESIRED, "Desired" }, | |
316 | { DRM_MODE_CONTENT_PROTECTION_ENABLED, "Enabled" }, | |
317 | }; | |
318 | DRM_ENUM_NAME_FN(drm_get_content_protection_name, drm_cp_enum_list) | |
319 | ||
7672dbba R |
320 | static struct drm_prop_enum_list drm_hdcp_content_type_enum_list[] = { |
321 | { DRM_MODE_HDCP_CONTENT_TYPE0, "HDCP Type0" }, | |
322 | { DRM_MODE_HDCP_CONTENT_TYPE1, "HDCP Type1" }, | |
323 | }; | |
324 | DRM_ENUM_NAME_FN(drm_get_hdcp_content_type_name, | |
325 | drm_hdcp_content_type_enum_list) | |
326 | ||
c16fd9be R |
327 | /** |
328 | * drm_connector_attach_content_protection_property - attach content protection | |
329 | * property | |
330 | * | |
331 | * @connector: connector to attach CP property on. | |
7672dbba | 332 | * @hdcp_content_type: is HDCP Content Type property needed for connector |
c16fd9be R |
333 | * |
334 | * This is used to add support for content protection on select connectors. | |
335 | * Content Protection is intentionally vague to allow for different underlying | |
336 | * technologies, however it is most implemented by HDCP. | |
337 | * | |
7672dbba R |
338 | * When hdcp_content_type is true enum property called HDCP Content Type is |
339 | * created (if it is not already) and attached to the connector. | |
340 | * | |
341 | * This property is used for sending the protected content's stream type | |
342 | * from userspace to kernel on selected connectors. Protected content provider | |
343 | * will decide their type of their content and declare the same to kernel. | |
344 | * | |
345 | * Content type will be used during the HDCP 2.2 authentication. | |
346 | * Content type will be set to &drm_connector_state.hdcp_content_type. | |
347 | * | |
c16fd9be R |
348 | * The content protection will be set to &drm_connector_state.content_protection |
349 | * | |
bb5a45d4 R |
350 | * When kernel triggered content protection state change like DESIRED->ENABLED |
351 | * and ENABLED->DESIRED, will use drm_hdcp_update_content_protection() to update | |
352 | * the content protection state of a connector. | |
353 | * | |
c16fd9be R |
354 | * Returns: |
355 | * Zero on success, negative errno on failure. | |
356 | */ | |
357 | int drm_connector_attach_content_protection_property( | |
7672dbba | 358 | struct drm_connector *connector, bool hdcp_content_type) |
c16fd9be R |
359 | { |
360 | struct drm_device *dev = connector->dev; | |
361 | struct drm_property *prop = | |
362 | dev->mode_config.content_protection_property; | |
363 | ||
364 | if (!prop) | |
365 | prop = drm_property_create_enum(dev, 0, "Content Protection", | |
366 | drm_cp_enum_list, | |
367 | ARRAY_SIZE(drm_cp_enum_list)); | |
368 | if (!prop) | |
369 | return -ENOMEM; | |
370 | ||
371 | drm_object_attach_property(&connector->base, prop, | |
372 | DRM_MODE_CONTENT_PROTECTION_UNDESIRED); | |
373 | dev->mode_config.content_protection_property = prop; | |
374 | ||
7672dbba R |
375 | if (!hdcp_content_type) |
376 | return 0; | |
377 | ||
378 | prop = dev->mode_config.hdcp_content_type_property; | |
379 | if (!prop) | |
380 | prop = drm_property_create_enum(dev, 0, "HDCP Content Type", | |
381 | drm_hdcp_content_type_enum_list, | |
382 | ARRAY_SIZE( | |
383 | drm_hdcp_content_type_enum_list)); | |
384 | if (!prop) | |
385 | return -ENOMEM; | |
386 | ||
387 | drm_object_attach_property(&connector->base, prop, | |
388 | DRM_MODE_HDCP_CONTENT_TYPE0); | |
389 | dev->mode_config.hdcp_content_type_property = prop; | |
390 | ||
c16fd9be R |
391 | return 0; |
392 | } | |
393 | EXPORT_SYMBOL(drm_connector_attach_content_protection_property); | |
bb5a45d4 R |
394 | |
395 | /** | |
396 | * drm_hdcp_update_content_protection - Updates the content protection state | |
397 | * of a connector | |
398 | * | |
399 | * @connector: drm_connector on which content protection state needs an update | |
400 | * @val: New state of the content protection property | |
401 | * | |
402 | * This function can be used by display drivers, to update the kernel triggered | |
403 | * content protection state changes of a drm_connector such as DESIRED->ENABLED | |
404 | * and ENABLED->DESIRED. No uevent for DESIRED->UNDESIRED or ENABLED->UNDESIRED, | |
405 | * as userspace is triggering such state change and kernel performs it without | |
406 | * fail.This function update the new state of the property into the connector's | |
407 | * state and generate an uevent to notify the userspace. | |
408 | */ | |
409 | void drm_hdcp_update_content_protection(struct drm_connector *connector, | |
410 | u64 val) | |
411 | { | |
412 | struct drm_device *dev = connector->dev; | |
413 | struct drm_connector_state *state = connector->state; | |
414 | ||
415 | WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex)); | |
416 | if (state->content_protection == val) | |
417 | return; | |
418 | ||
419 | state->content_protection = val; | |
420 | drm_sysfs_connector_status_event(connector, | |
421 | dev->mode_config.content_protection_property); | |
422 | } | |
423 | EXPORT_SYMBOL(drm_hdcp_update_content_protection); |