[igt-dev] [PATCH i-g-t v2 3/5] kms_content_protection: srm and downstream_info test
Ramalingam C
ramalingam.c at intel.com
Fri Mar 8 16:30:47 UTC 2019
Retrieve the downstream info and use that to stitch a srm and
verify the revocation process.
Adds a connector properties called CP_downstream_info
SRM stitched here is polluted. Kernel trusts the userspace to validate
the SRM integrity through DCP signature. Actually this test exploits
that trust to test SRM and downstream_info features.
HDCP1.4 BKSV and HDCP2.2 receiver id of a HDCP sink is different. So we
need to force the KMD to use a single HDCP version across
downstream_info gathering and SRM setting and verifying teh revocation
process. So Type 1 is used.
v2:
binary sysfs is used to write the srm into kernel [Daniel]
Signed-off-by: Ramalingam C <ramalingam.c at intel.com>
---
lib/igt_kms.c | 1 +
lib/igt_kms.h | 1 +
tests/kms_content_protection.c | 211 +++++++++++++++++++++++++++++++--
3 files changed, 201 insertions(+), 12 deletions(-)
diff --git a/lib/igt_kms.c b/lib/igt_kms.c
index d30741f747e7..01320a9b7c40 100644
--- a/lib/igt_kms.c
+++ b/lib/igt_kms.c
@@ -200,6 +200,7 @@ const char * const igt_connector_prop_names[IGT_NUM_CONNECTOR_PROPS] = {
[IGT_CONNECTOR_CONTENT_PROTECTION] = "Content Protection",
[IGT_CONNECTOR_VRR_CAPABLE] = "vrr_capable",
[IGT_CONNECTOR_CP_CONTENT_TYPE] = "Content_protection_type",
+ [IGT_CONNECTOR_CP_DOWNSTREAM_INFO] = "CP_downstream_info",
};
/*
diff --git a/lib/igt_kms.h b/lib/igt_kms.h
index 59cda36ecd21..d0d0420298e7 100644
--- a/lib/igt_kms.h
+++ b/lib/igt_kms.h
@@ -124,6 +124,7 @@ enum igt_atomic_connector_properties {
IGT_CONNECTOR_CONTENT_PROTECTION,
IGT_CONNECTOR_VRR_CAPABLE,
IGT_CONNECTOR_CP_CONTENT_TYPE,
+ IGT_CONNECTOR_CP_DOWNSTREAM_INFO,
IGT_NUM_CONNECTOR_PROPS
};
diff --git a/tests/kms_content_protection.c b/tests/kms_content_protection.c
index 4563a6067a32..3d2a01db277b 100644
--- a/tests/kms_content_protection.c
+++ b/tests/kms_content_protection.c
@@ -55,6 +55,116 @@ struct data {
#define FLIP_EVENT_POLLING_TIMEOUT_MSEC 1000
+#define DRM_MODE_HDCP_KSV_LEN 5
+#define DRM_MODE_HDCP_MAX_DEVICE_CNT 127
+
+#define DRM_MODE_HDCP14_IN_FORCE (1<<0)
+#define DRM_MODE_HDCP22_IN_FORCE (1<<1)
+
+struct cp_downstream_info {
+
+ /* HDCP ver in force */
+ __u32 ver_in_force;
+ __u8 content_type;
+
+ /* KSV of immediate HDCP Sink. In Little-Endian Format. */
+ char bksv[DRM_MODE_HDCP_KSV_LEN];
+
+ /* Whether Immediate HDCP sink is a repeater? */
+ bool is_repeater;
+
+ /* Depth received from immediate downstream repeater */
+ __u8 depth;
+
+ /* Device count received from immediate downstream repeater */
+ __u32 device_count;
+
+ /*
+ * Max buffer required to hold ksv list received from immediate
+ * repeater. In this array first device_count * DRM_MODE_HDCP_KSV_LEN
+ * will hold the valid ksv bytes.
+ * If authentication specification is
+ * HDCP1.4 - each KSV's Bytes will be in Little-Endian format.
+ * HDCP2.2 - each KSV's Bytes will be in Big-Endian format.
+ */
+ char ksv_list[DRM_MODE_HDCP_KSV_LEN * DRM_MODE_HDCP_MAX_DEVICE_CNT];
+};
+
+
+__u8 facsimile_srm[] = {
+ 0x80, 0x0, 0x0, 0x05, 0x01, 0x0, 0x0, 0x36, 0x02, 0x51, 0x1E, 0xF2,
+ 0x1A, 0xCD, 0xE7, 0x26, 0x97, 0xF4, 0x01, 0x97, 0x10, 0x19, 0x92, 0x53,
+ 0xE9, 0xF0, 0x59, 0x95, 0xA3, 0x7A, 0x3B, 0xFE, 0xE0, 0x9C, 0x76, 0xDD,
+ 0x83, 0xAA, 0xC2, 0x5B, 0x24, 0xB3, 0x36, 0x84, 0x94, 0x75, 0x34, 0xDB,
+ 0x10, 0x9E, 0x3B, 0x23, 0x13, 0xD8, 0x7A, 0xC2, 0x30, 0x79, 0x84};
+
+static void parse_downstream_info(struct cp_downstream_info *ds_info)
+{
+ char *ksvs;
+ int i;
+
+ if (ds_info->ver_in_force & DRM_MODE_HDCP14_IN_FORCE)
+ igt_info("HDCP1.4 is Enabled\n");
+ else if (ds_info->ver_in_force & DRM_MODE_HDCP22_IN_FORCE)
+ igt_info("HDCP2.2 is Enabled. Type%d\n",
+ ds_info->content_type & CP_TYPE_1 ?
+ 1 : 0);
+ else
+ return;
+
+ igt_info("\tReceiver ID: %#04x %#04x %#04x %#04x %#04x\n",
+ ds_info->bksv[0] & 0xFF, ds_info->bksv[1] & 0xFF,
+ ds_info->bksv[2] & 0xFF, ds_info->bksv[3] & 0xFF,
+ ds_info->bksv[4] & 0xFF);
+
+ if (ds_info->is_repeater) {
+ igt_info("\tHDCP sink is a Repeater\n");
+
+ igt_info("\tDepth: %d, Device count: %d\n", ds_info->depth,
+ ds_info->device_count);
+ ksvs = ds_info->ksv_list;
+
+ for (i = 0; i < ds_info->device_count; i++) {
+ igt_info("\tksv-%d: %#04x %#04x %#04x %#04x %#04x\n", i,
+ ksvs[0] & 0xFF, ksvs[1] & 0xFF,
+ ksvs[2] & 0xFF, ksvs[3] & 0xFF,
+ ksvs[4] & 0xFF);
+ ksvs += DRM_MODE_HDCP_KSV_LEN;
+ }
+ } else {
+ igt_info("\tHDCP sink is a Receiver\n");
+ }
+}
+
+static void retrieve_downstream_info_prepare_srm(igt_output_t *output)
+{
+ drmModePropertyBlobRes *ds_info_prop = NULL;
+ uint64_t downstream_blob_id;
+ struct cp_downstream_info *ds_info;
+ int i;
+
+ igt_info("CP_downstream_info property is attached\n");
+
+ downstream_blob_id = igt_output_get_prop(output,
+ IGT_CONNECTOR_CP_DOWNSTREAM_INFO);
+
+ igt_assert_f(downstream_blob_id,
+ "Invalid downstream blob id\n");
+
+ ds_info_prop = drmModeGetPropertyBlob(data.drm_fd,
+ downstream_blob_id);
+
+ igt_assert(ds_info_prop);
+ igt_assert_eq(ds_info_prop->length,
+ sizeof(struct cp_downstream_info));
+ ds_info = ds_info_prop->data;
+
+ parse_downstream_info(ds_info);
+
+ for (i = 0; i < 5; i++)
+ facsimile_srm[i + 9] = ds_info->bksv[i];
+}
+
static void flip_handler(int fd, unsigned int sequence, unsigned int tv_sec,
unsigned int tv_usec, void *_data)
{
@@ -214,7 +324,8 @@ static void test_cp_disable(igt_output_t *output, enum igt_commit_style s)
static void test_cp_enable_with_retry(igt_output_t *output,
enum igt_commit_style s, int retry,
- int content_type, bool expect_failure)
+ int content_type, bool expect_failure,
+ bool test_srm)
{
bool ret;
@@ -229,6 +340,9 @@ static void test_cp_enable_with_retry(igt_output_t *output,
if (expect_failure)
igt_assert_f(!ret,
"CP Enabled. Though it is expected to fail\n");
+ else if (test_srm)
+ igt_assert_f(!ret,
+ "CP Enabled. Though ID is revoked through SRM\n");
else
igt_assert_f(ret, "Content Protection not enabled\n");
}
@@ -253,15 +367,30 @@ static void test_cp_lic(igt_output_t *output)
igt_assert_f(!ret, "Content Protection LIC Failed\n");
}
+static bool write_srm_into_sysfs(const char *srm, int len)
+{
+ int fd;
+ bool ret = false;
+
+ fd = igt_sysfs_open(data.drm_fd, NULL);
+ if (fd > 0) {
+ if (igt_sysfs_write(fd, "hdcp_srm", srm, len) == len)
+ ret = true;
+ close(fd);
+ }
+ return ret;
+}
+
static void
test_content_protection_on_output(igt_output_t *output, enum igt_commit_style s,
bool dpms_test, int content_type,
- bool mei_reload_test)
+ bool mei_reload_test, bool test_srm)
{
igt_display_t *display = &data.display;
igt_plane_t *primary;
enum pipe pipe;
- bool ret;
+ bool ret, srm_modified = false;
+ int i;
for_each_pipe(display, pipe) {
if (!igt_pipe_connector_valid(pipe, output))
@@ -276,8 +405,10 @@ test_content_protection_on_output(igt_output_t *output, enum igt_commit_style s,
if (!igt_pipe_is_free(display, pipe))
continue;
+ srm_modified = false;
modeset_with_fb(pipe, output, s);
- test_cp_enable_with_retry(output, s, 3, content_type, false);
+ test_cp_enable_with_retry(output, s, 3, content_type, false,
+ false);
if (mei_reload_test) {
igt_assert_f(!igt_kmod_unload("mei_hdcp", 0),
@@ -297,6 +428,33 @@ test_content_protection_on_output(igt_output_t *output, enum igt_commit_style s,
test_cp_lic(output);
+ if (output->props[IGT_CONNECTOR_CP_DOWNSTREAM_INFO] &&
+ test_srm) {
+ retrieve_downstream_info_prepare_srm(output);
+ srm_modified =
+ write_srm_into_sysfs((const char *)facsimile_srm,
+ sizeof(facsimile_srm));
+ igt_assert_f(srm_modified, "SRM update failed");
+ }
+
+ if (test_srm && srm_modified) {
+ test_cp_disable(output, s);
+ test_cp_enable_with_retry(output, s, 3, content_type,
+ false, test_srm);
+
+ /* Removing the sink's Receiver ID from SRM Blob */
+ for (i = 0; i < 5; i++)
+ facsimile_srm[i + 9] = 0;
+
+ srm_modified =
+ write_srm_into_sysfs((const char *)facsimile_srm,
+ sizeof(facsimile_srm));
+ igt_assert_f(srm_modified, "SRM update failed");
+
+ test_cp_enable_with_retry(output, s, 1, content_type,
+ false, false);
+ }
+
if (dpms_test) {
igt_pipe_set_prop_value(display, pipe,
IGT_CRTC_ACTIVE, 0);
@@ -310,7 +468,8 @@ test_content_protection_on_output(igt_output_t *output, enum igt_commit_style s,
KERNEL_AUTH_TIME_ALLOWED_MSEC);
if (!ret)
test_cp_enable_with_retry(output, s, 2,
- content_type, false);
+ content_type, false,
+ false);
}
test_cp_disable(output, s);
@@ -375,7 +534,8 @@ static bool sink_hdcp2_capable(igt_output_t *output)
static void
test_content_protection(enum igt_commit_style s, bool dpms_test,
- int content_type, bool mei_reload_test)
+ int content_type, bool mei_reload_test,
+ bool test_srm)
{
igt_display_t *display = &data.display;
igt_output_t *output;
@@ -407,7 +567,7 @@ test_content_protection(enum igt_commit_style s, bool dpms_test,
test_content_protection_on_output(output, s, dpms_test,
content_type,
- mei_reload_test);
+ mei_reload_test, test_srm);
valid_tests++;
}
@@ -426,32 +586,59 @@ igt_main
igt_subtest("legacy")
test_content_protection(COMMIT_LEGACY, false, CP_TYPE_0,
- false);
+ false, false);
igt_subtest("atomic") {
igt_require(data.display.is_atomic);
test_content_protection(COMMIT_ATOMIC, false, CP_TYPE_0,
- false);
+ false, false);
}
igt_subtest("atomic-dpms") {
igt_require(data.display.is_atomic);
test_content_protection(COMMIT_ATOMIC, true, CP_TYPE_0,
- false);
+ false, false);
}
igt_subtest("Type1") {
igt_require(data.display.is_atomic);
test_content_protection(COMMIT_ATOMIC, false, CP_TYPE_1,
- false);
+ false, false);
}
igt_subtest("type1_mei_interface") {
igt_require(data.display.is_atomic);
test_content_protection(COMMIT_ATOMIC, false, CP_TYPE_1,
- true);
+ true, false);
}
+ /*
+ * SRM subtest perform the HDCP authentication, and then retrive the
+ * receiver id through downstream info.
+ *
+ * Using the receiver ID, facsimile SRM table is modified with
+ * receiver ID retrieved from teh downstream info. After modification
+ * SRM table is not intact as per DCP Signature.
+ *
+ * But Kernel believes userspace and doesn't verify the DCP signature.
+ * So we can exploite that trust to test the SRM and downstream info
+ * features.
+ *
+ * So when modified SRM is applied Authentication will fail due to
+ * receiver ID revocation.
+ *
+ * And Kernel attempts HDCP2.2 always and on failure of it HDCP1.4
+ * will be attempted. But their ID of the sink varies between 1.4 and
+ * 2.2 versions. So we need to stick to one version. Hence HDCP2.2 is
+ * choosen using Type 1.
+ */
+ igt_subtest("srm") {
+ igt_require(data.display.is_atomic);
+ test_content_protection(COMMIT_ATOMIC, false, CP_TYPE_1,
+ false, true);
+ }
+
+
igt_fixture
igt_display_fini(&data.display);
}
--
2.19.1
More information about the igt-dev
mailing list