summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRamalingam C <ramalingam.c@intel.com>2019-05-07 12:27:38 -0400
committerDaniel Vetter <daniel.vetter@ffwll.ch>2019-05-09 03:44:41 -0400
commit6498bf5800a302ef69e7f4914e727893f278bb2f (patch)
treeee75415a66311a04fc62a8cd0105cebd6ca42d49
parent0de655cae416b20ed7876adb480e5c65810274ea (diff)
drm: revocation check at drm subsystem
On every hdcp revocation check request SRM is read from fw file /lib/firmware/display_hdcp_srm.bin SRM table is parsed and stored at drm_hdcp.c, with functions exported for the services for revocation check from drivers (which implements the HDCP authentication) This patch handles the HDCP1.4 and 2.2 versions of SRM table. v2: moved the uAPI to request_firmware_direct() [Daniel] v3: kdoc added. [Daniel] srm_header unified and bit field definitions are removed. [Daniel] locking improved. [Daniel] vrl length violation is fixed. [Daniel] v4: s/__swab16/be16_to_cpu [Daniel] be24_to_cpu is done through a global func [Daniel] Unused variables are removed. [Daniel] unchecked return values are dropped from static funcs [Daniel] Signed-off-by: Ramalingam C <ramalingam.c@intel.com> Acked-by: Satyeshwar Singh <satyeshwar.singh@intel.com> Reviewed-by: Daniel Vetter <daniel@ffwll.ch> Acked-by: Dave Airlie <airlied@gmail.com> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> Link: https://patchwork.freedesktop.org/patch/msgid/20190507162745.25600-5-ramalingam.c@intel.com
-rw-r--r--Documentation/gpu/drm-kms-helpers.rst6
-rw-r--r--drivers/gpu/drm/Makefile2
-rw-r--r--drivers/gpu/drm/drm_hdcp.c333
-rw-r--r--drivers/gpu/drm/drm_internal.h4
-rw-r--r--drivers/gpu/drm/drm_sysfs.c2
-rw-r--r--include/drm/drm_hdcp.h24
6 files changed, 370 insertions, 1 deletions
diff --git a/Documentation/gpu/drm-kms-helpers.rst b/Documentation/gpu/drm-kms-helpers.rst
index 14102ae035dc..0fe726a6ee67 100644
--- a/Documentation/gpu/drm-kms-helpers.rst
+++ b/Documentation/gpu/drm-kms-helpers.rst
@@ -181,6 +181,12 @@ Panel Helper Reference
181.. kernel-doc:: drivers/gpu/drm/drm_panel_orientation_quirks.c 181.. kernel-doc:: drivers/gpu/drm/drm_panel_orientation_quirks.c
182 :export: 182 :export:
183 183
184HDCP Helper Functions Reference
185===============================
186
187.. kernel-doc:: drivers/gpu/drm/drm_hdcp.c
188 :export:
189
184Display Port Helper Functions Reference 190Display Port Helper Functions Reference
185======================================= 191=======================================
186 192
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 72f5036d9bfa..dd02e9dec810 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -17,7 +17,7 @@ drm-y := drm_auth.o drm_cache.o \
17 drm_plane.o drm_color_mgmt.o drm_print.o \ 17 drm_plane.o drm_color_mgmt.o drm_print.o \
18 drm_dumb_buffers.o drm_mode_config.o drm_vblank.o \ 18 drm_dumb_buffers.o drm_mode_config.o drm_vblank.o \
19 drm_syncobj.o drm_lease.o drm_writeback.o drm_client.o \ 19 drm_syncobj.o drm_lease.o drm_writeback.o drm_client.o \
20 drm_atomic_uapi.o 20 drm_atomic_uapi.o drm_hdcp.o
21 21
22drm-$(CONFIG_DRM_LEGACY) += drm_legacy_misc.o drm_bufs.o drm_context.o drm_dma.o drm_scatter.o drm_lock.o 22drm-$(CONFIG_DRM_LEGACY) += drm_legacy_misc.o drm_bufs.o drm_context.o drm_dma.o drm_scatter.o drm_lock.o
23drm-$(CONFIG_DRM_LIB_RANDOM) += lib/drm_random.o 23drm-$(CONFIG_DRM_LIB_RANDOM) += lib/drm_random.o
diff --git a/drivers/gpu/drm/drm_hdcp.c b/drivers/gpu/drm/drm_hdcp.c
new file mode 100644
index 000000000000..5e5409505c31
--- /dev/null
+++ b/drivers/gpu/drm/drm_hdcp.c
@@ -0,0 +1,333 @@
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>
20
21struct hdcp_srm {
22 u32 revoked_ksv_cnt;
23 u8 *revoked_ksv_list;
24
25 /* Mutex to protect above struct member */
26 struct mutex mutex;
27} *srm_data;
28
29static inline void drm_hdcp_print_ksv(const u8 *ksv)
30{
31 DRM_DEBUG("\t%#02x, %#02x, %#02x, %#02x, %#02x\n",
32 ksv[0], ksv[1], ksv[2], ksv[3], ksv[4]);
33}
34
35static u32 drm_hdcp_get_revoked_ksv_count(const u8 *buf, u32 vrls_length)
36{
37 u32 parsed_bytes = 0, ksv_count = 0, vrl_ksv_cnt, vrl_sz;
38
39 while (parsed_bytes < vrls_length) {
40 vrl_ksv_cnt = *buf;
41 ksv_count += vrl_ksv_cnt;
42
43 vrl_sz = (vrl_ksv_cnt * DRM_HDCP_KSV_LEN) + 1;
44 buf += vrl_sz;
45 parsed_bytes += vrl_sz;
46 }
47
48 /*
49 * When vrls are not valid, ksvs are not considered.
50 * Hence SRM will be discarded.
51 */
52 if (parsed_bytes != vrls_length)
53 ksv_count = 0;
54
55 return ksv_count;
56}
57
58static u32 drm_hdcp_get_revoked_ksvs(const u8 *buf, u8 *revoked_ksv_list,
59 u32 vrls_length)
60{
61 u32 parsed_bytes = 0, ksv_count = 0;
62 u32 vrl_ksv_cnt, vrl_ksv_sz, vrl_idx = 0;
63
64 do {
65 vrl_ksv_cnt = *buf;
66 vrl_ksv_sz = vrl_ksv_cnt * DRM_HDCP_KSV_LEN;
67
68 buf++;
69
70 DRM_DEBUG("vrl: %d, Revoked KSVs: %d\n", vrl_idx++,
71 vrl_ksv_cnt);
72 memcpy(revoked_ksv_list, buf, vrl_ksv_sz);
73
74 ksv_count += vrl_ksv_cnt;
75 revoked_ksv_list += vrl_ksv_sz;
76 buf += vrl_ksv_sz;
77
78 parsed_bytes += (vrl_ksv_sz + 1);
79 } while (parsed_bytes < vrls_length);
80
81 return ksv_count;
82}
83
84static inline u32 get_vrl_length(const u8 *buf)
85{
86 return drm_hdcp_be24_to_cpu(buf);
87}
88
89static int drm_hdcp_parse_hdcp1_srm(const u8 *buf, size_t count)
90{
91 struct hdcp_srm_header *header;
92 u32 vrl_length, ksv_count;
93
94 if (count < (sizeof(struct hdcp_srm_header) +
95 DRM_HDCP_1_4_VRL_LENGTH_SIZE + DRM_HDCP_1_4_DCP_SIG_SIZE)) {
96 DRM_ERROR("Invalid blob length\n");
97 return -EINVAL;
98 }
99
100 header = (struct hdcp_srm_header *)buf;
101 DRM_DEBUG("SRM ID: 0x%x, SRM Ver: 0x%x, SRM Gen No: 0x%x\n",
102 header->srm_id,
103 be16_to_cpu(header->srm_version), header->srm_gen_no);
104
105 WARN_ON(header->reserved);
106
107 buf = buf + sizeof(*header);
108 vrl_length = get_vrl_length(buf);
109 if (count < (sizeof(struct hdcp_srm_header) + vrl_length) ||
110 vrl_length < (DRM_HDCP_1_4_VRL_LENGTH_SIZE +
111 DRM_HDCP_1_4_DCP_SIG_SIZE)) {
112 DRM_ERROR("Invalid blob length or vrl length\n");
113 return -EINVAL;
114 }
115
116 /* Length of the all vrls combined */
117 vrl_length -= (DRM_HDCP_1_4_VRL_LENGTH_SIZE +
118 DRM_HDCP_1_4_DCP_SIG_SIZE);
119
120 if (!vrl_length) {
121 DRM_ERROR("No vrl found\n");
122 return -EINVAL;
123 }
124
125 buf += DRM_HDCP_1_4_VRL_LENGTH_SIZE;
126 ksv_count = drm_hdcp_get_revoked_ksv_count(buf, vrl_length);
127 if (!ksv_count) {
128 DRM_DEBUG("Revoked KSV count is 0\n");
129 return count;
130 }
131
132 kfree(srm_data->revoked_ksv_list);
133 srm_data->revoked_ksv_list = kcalloc(ksv_count, DRM_HDCP_KSV_LEN,
134 GFP_KERNEL);
135 if (!srm_data->revoked_ksv_list) {
136 DRM_ERROR("Out of Memory\n");
137 return -ENOMEM;
138 }
139
140 if (drm_hdcp_get_revoked_ksvs(buf, srm_data->revoked_ksv_list,
141 vrl_length) != ksv_count) {
142 srm_data->revoked_ksv_cnt = 0;
143 kfree(srm_data->revoked_ksv_list);
144 return -EINVAL;
145 }
146
147 srm_data->revoked_ksv_cnt = ksv_count;
148 return count;
149}
150
151static int drm_hdcp_parse_hdcp2_srm(const u8 *buf, size_t count)
152{
153 struct hdcp_srm_header *header;
154 u32 vrl_length, ksv_count, ksv_sz;
155
156 if (count < (sizeof(struct hdcp_srm_header) +
157 DRM_HDCP_2_VRL_LENGTH_SIZE + DRM_HDCP_2_DCP_SIG_SIZE)) {
158 DRM_ERROR("Invalid blob length\n");
159 return -EINVAL;
160 }
161
162 header = (struct hdcp_srm_header *)buf;
163 DRM_DEBUG("SRM ID: 0x%x, SRM Ver: 0x%x, SRM Gen No: 0x%x\n",
164 header->srm_id & DRM_HDCP_SRM_ID_MASK,
165 be16_to_cpu(header->srm_version), header->srm_gen_no);
166
167 if (header->reserved)
168 return -EINVAL;
169
170 buf = buf + sizeof(*header);
171 vrl_length = get_vrl_length(buf);
172
173 if (count < (sizeof(struct hdcp_srm_header) + vrl_length) ||
174 vrl_length < (DRM_HDCP_2_VRL_LENGTH_SIZE +
175 DRM_HDCP_2_DCP_SIG_SIZE)) {
176 DRM_ERROR("Invalid blob length or vrl length\n");
177 return -EINVAL;
178 }
179
180 /* Length of the all vrls combined */
181 vrl_length -= (DRM_HDCP_2_VRL_LENGTH_SIZE +
182 DRM_HDCP_2_DCP_SIG_SIZE);
183
184 if (!vrl_length) {
185 DRM_ERROR("No vrl found\n");
186 return -EINVAL;
187 }
188
189 buf += DRM_HDCP_2_VRL_LENGTH_SIZE;
190 ksv_count = (*buf << 2) | DRM_HDCP_2_KSV_COUNT_2_LSBITS(*(buf + 1));
191 if (!ksv_count) {
192 DRM_DEBUG("Revoked KSV count is 0\n");
193 return count;
194 }
195
196 kfree(srm_data->revoked_ksv_list);
197 srm_data->revoked_ksv_list = kcalloc(ksv_count, DRM_HDCP_KSV_LEN,
198 GFP_KERNEL);
199 if (!srm_data->revoked_ksv_list) {
200 DRM_ERROR("Out of Memory\n");
201 return -ENOMEM;
202 }
203
204 ksv_sz = ksv_count * DRM_HDCP_KSV_LEN;
205 buf += DRM_HDCP_2_NO_OF_DEV_PLUS_RESERVED_SZ;
206
207 DRM_DEBUG("Revoked KSVs: %d\n", ksv_count);
208 memcpy(srm_data->revoked_ksv_list, buf, ksv_sz);
209
210 srm_data->revoked_ksv_cnt = ksv_count;
211 return count;
212}
213
214static inline bool is_srm_version_hdcp1(const u8 *buf)
215{
216 return *buf == (u8)(DRM_HDCP_1_4_SRM_ID << 4);
217}
218
219static inline bool is_srm_version_hdcp2(const u8 *buf)
220{
221 return *buf == (u8)(DRM_HDCP_2_SRM_ID << 4 | DRM_HDCP_2_INDICATOR);
222}
223
224static void drm_hdcp_srm_update(const u8 *buf, size_t count)
225{
226 if (count < sizeof(struct hdcp_srm_header))
227 return;
228
229 if (is_srm_version_hdcp1(buf))
230 drm_hdcp_parse_hdcp1_srm(buf, count);
231 else if (is_srm_version_hdcp2(buf))
232 drm_hdcp_parse_hdcp2_srm(buf, count);
233}
234
235void drm_hdcp_request_srm(struct drm_device *drm_dev)
236{
237 char fw_name[36] = "display_hdcp_srm.bin";
238 const struct firmware *fw;
239
240 int ret;
241
242 ret = request_firmware_direct(&fw, (const char *)fw_name,
243 drm_dev->dev);
244 if (ret < 0)
245 goto exit;
246
247 if (fw->size && fw->data)
248 drm_hdcp_srm_update(fw->data, fw->size);
249
250exit:
251 release_firmware(fw);
252}
253
254/**
255 * drm_hdcp_check_ksvs_revoked - Check the revoked status of the IDs
256 *
257 * @drm_dev: drm_device for which HDCP revocation check is requested
258 * @ksvs: List of KSVs (HDCP receiver IDs)
259 * @ksv_count: KSV count passed in through @ksvs
260 *
261 * This function reads the HDCP System renewability Message(SRM Table)
262 * from userspace as a firmware and parses it for the revoked HDCP
263 * KSVs(Receiver IDs) detected by DCP LLC. Once the revoked KSVs are known,
264 * revoked state of the KSVs in the list passed in by display drivers are
265 * decided and response is sent.
266 *
267 * SRM should be presented in the name of "display_hdcp_srm.bin".
268 *
269 * Returns:
270 * TRUE on any of the KSV is revoked, else FALSE.
271 */
272bool drm_hdcp_check_ksvs_revoked(struct drm_device *drm_dev, u8 *ksvs,
273 u32 ksv_count)
274{
275 u32 rev_ksv_cnt, cnt, i, j;
276 u8 *rev_ksv_list;
277
278 if (!srm_data)
279 return false;
280
281 mutex_lock(&srm_data->mutex);
282 drm_hdcp_request_srm(drm_dev);
283
284 rev_ksv_cnt = srm_data->revoked_ksv_cnt;
285 rev_ksv_list = srm_data->revoked_ksv_list;
286
287 /* If the Revoked ksv list is empty */
288 if (!rev_ksv_cnt || !rev_ksv_list) {
289 mutex_unlock(&srm_data->mutex);
290 return false;
291 }
292
293 for (cnt = 0; cnt < ksv_count; cnt++) {
294 rev_ksv_list = srm_data->revoked_ksv_list;
295 for (i = 0; i < rev_ksv_cnt; i++) {
296 for (j = 0; j < DRM_HDCP_KSV_LEN; j++)
297 if (ksvs[j] != rev_ksv_list[j]) {
298 break;
299 } else if (j == (DRM_HDCP_KSV_LEN - 1)) {
300 DRM_DEBUG("Revoked KSV is ");
301 drm_hdcp_print_ksv(ksvs);
302 mutex_unlock(&srm_data->mutex);
303 return true;
304 }
305 /* Move the offset to next KSV in the revoked list */
306 rev_ksv_list += DRM_HDCP_KSV_LEN;
307 }
308
309 /* Iterate to next ksv_offset */
310 ksvs += DRM_HDCP_KSV_LEN;
311 }
312 mutex_unlock(&srm_data->mutex);
313 return false;
314}
315EXPORT_SYMBOL_GPL(drm_hdcp_check_ksvs_revoked);
316
317int drm_setup_hdcp_srm(struct class *drm_class)
318{
319 srm_data = kzalloc(sizeof(*srm_data), GFP_KERNEL);
320 if (!srm_data)
321 return -ENOMEM;
322 mutex_init(&srm_data->mutex);
323
324 return 0;
325}
326
327void drm_teardown_hdcp_srm(struct class *drm_class)
328{
329 if (srm_data) {
330 kfree(srm_data->revoked_ksv_list);
331 kfree(srm_data);
332 }
333}
diff --git a/drivers/gpu/drm/drm_internal.h b/drivers/gpu/drm/drm_internal.h
index e19ac7ca602d..476a422414f6 100644
--- a/drivers/gpu/drm/drm_internal.h
+++ b/drivers/gpu/drm/drm_internal.h
@@ -201,3 +201,7 @@ int drm_syncobj_query_ioctl(struct drm_device *dev, void *data,
201void drm_framebuffer_print_info(struct drm_printer *p, unsigned int indent, 201void drm_framebuffer_print_info(struct drm_printer *p, unsigned int indent,
202 const struct drm_framebuffer *fb); 202 const struct drm_framebuffer *fb);
203int drm_framebuffer_debugfs_init(struct drm_minor *minor); 203int drm_framebuffer_debugfs_init(struct drm_minor *minor);
204
205/* drm_hdcp.c */
206int drm_setup_hdcp_srm(struct class *drm_class);
207void drm_teardown_hdcp_srm(struct class *drm_class);
diff --git a/drivers/gpu/drm/drm_sysfs.c b/drivers/gpu/drm/drm_sysfs.c
index ecb7b33002bb..18b1ac442997 100644
--- a/drivers/gpu/drm/drm_sysfs.c
+++ b/drivers/gpu/drm/drm_sysfs.c
@@ -78,6 +78,7 @@ int drm_sysfs_init(void)
78 } 78 }
79 79
80 drm_class->devnode = drm_devnode; 80 drm_class->devnode = drm_devnode;
81 drm_setup_hdcp_srm(drm_class);
81 return 0; 82 return 0;
82} 83}
83 84
@@ -90,6 +91,7 @@ void drm_sysfs_destroy(void)
90{ 91{
91 if (IS_ERR_OR_NULL(drm_class)) 92 if (IS_ERR_OR_NULL(drm_class))
92 return; 93 return;
94 drm_teardown_hdcp_srm(drm_class);
93 class_remove_file(drm_class, &class_attr_version.attr); 95 class_remove_file(drm_class, &class_attr_version.attr);
94 class_destroy(drm_class); 96 class_destroy(drm_class);
95 drm_class = NULL; 97 drm_class = NULL;
diff --git a/include/drm/drm_hdcp.h b/include/drm/drm_hdcp.h
index 1cc66df05a43..2f0335d0a50f 100644
--- a/include/drm/drm_hdcp.h
+++ b/include/drm/drm_hdcp.h
@@ -265,4 +265,28 @@ void drm_hdcp_cpu_to_be24(u8 seq_num[HDCP_2_2_SEQ_NUM_LEN], u32 val)
265 seq_num[2] = val; 265 seq_num[2] = val;
266} 266}
267 267
268#define DRM_HDCP_SRM_GEN1_MAX_BYTES (5 * 1024)
269#define DRM_HDCP_1_4_SRM_ID 0x8
270#define DRM_HDCP_SRM_ID_MASK (0xF << 4)
271#define DRM_HDCP_1_4_VRL_LENGTH_SIZE 3
272#define DRM_HDCP_1_4_DCP_SIG_SIZE 40
273#define DRM_HDCP_2_SRM_ID 0x9
274#define DRM_HDCP_2_INDICATOR 0x1
275#define DRM_HDCP_2_INDICATOR_MASK 0xF
276#define DRM_HDCP_2_VRL_LENGTH_SIZE 3
277#define DRM_HDCP_2_DCP_SIG_SIZE 384
278#define DRM_HDCP_2_NO_OF_DEV_PLUS_RESERVED_SZ 4
279#define DRM_HDCP_2_KSV_COUNT_2_LSBITS(byte) (((byte) & 0xC) >> 6)
280
281struct hdcp_srm_header {
282 u8 srm_id;
283 u8 reserved;
284 __be16 srm_version;
285 u8 srm_gen_no;
286} __packed;
287
288struct drm_device;
289
290bool drm_hdcp_check_ksvs_revoked(struct drm_device *dev,
291 u8 *ksvs, u32 ksv_count);
268#endif 292#endif