[igt-dev] [PATCH i-g-t v4 5/6] kms_content_protection: srm and topology_info test

Ramalingam C ramalingam.c at intel.com
Thu Mar 21 17:44:44 UTC 2019


Retrieve the topology info and use that to stitch a srm and
verify the revocation process.

Adds a connector properties called "HDCP Topology"

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]
v3:
  s/CP_downstream_info/HDCP Topology
v4:
  Rebased.

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 | 222 ++++++++++++++++++++++++++++++---
 3 files changed, 206 insertions(+), 18 deletions(-)

diff --git a/lib/igt_kms.c b/lib/igt_kms.c
index 63962e958455..3b897164630f 100644
--- a/lib/igt_kms.c
+++ b/lib/igt_kms.c
@@ -202,6 +202,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_HDCP_CONTENT_TYPE] = "HDCP Content Type",
+	[IGT_CONNECTOR_HDCP_TOPOLOGY_INFO] = "HDCP Topology",
 };
 
 /*
diff --git a/lib/igt_kms.h b/lib/igt_kms.h
index d281c4931b83..78b8920128ff 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_HDCP_CONTENT_TYPE,
+       IGT_CONNECTOR_HDCP_TOPOLOGY_INFO,
        IGT_NUM_CONNECTOR_PROPS
 };
 
diff --git a/tests/kms_content_protection.c b/tests/kms_content_protection.c
index abbecc4b0c56..1c2b263a253e 100644
--- a/tests/kms_content_protection.c
+++ b/tests/kms_content_protection.c
@@ -58,6 +58,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 hdcp_topology_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_topology_info(struct hdcp_topology_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 & HDCP_CONTENT_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_topology_info_prepare_srm(igt_output_t *output)
+{
+	drmModePropertyBlobRes *ds_info_prop = NULL;
+	uint64_t topology_blob_id;
+	struct hdcp_topology_info *ds_info;
+	int i;
+
+	igt_info("HDCP topology property is attached\n");
+
+	topology_blob_id = igt_output_get_prop(output,
+				IGT_CONNECTOR_HDCP_TOPOLOGY_INFO);
+
+	igt_assert_f(topology_blob_id,
+				"Invalid topology blob id\n");
+
+	ds_info_prop = drmModeGetPropertyBlob(data.drm_fd,
+						topology_blob_id);
+
+	igt_assert(ds_info_prop);
+	igt_assert_eq(ds_info_prop->length,
+				sizeof(struct hdcp_topology_info));
+	ds_info = ds_info_prop->data;
+
+	parse_topology_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)
 {
@@ -355,7 +465,7 @@ 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,
-				      bool test_type_change)
+				      bool test_type_change, bool test_srm)
 {
 	int retry_orig = retry;
 	bool ret;
@@ -373,6 +483,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");
 }
@@ -397,15 +510,31 @@ 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);
+	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 test_type_change)
+				  bool mei_reload_test, bool test_type_change,
+				  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))
@@ -420,13 +549,15 @@ 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,
-					  false);
+					  false, false);
+
 		if (test_type_change && content_type == HDCP_CONTENT_TYPE_1)
 			test_cp_enable_with_retry(output, s, 3,
 						  HDCP_CONTENT_TYPE_0, false,
-						  test_type_change);
+						  test_type_change, false);
 
 		if (mei_reload_test) {
 			igt_assert_f(!igt_kmod_unload("mei_hdcp", 0),
@@ -434,18 +565,47 @@ test_content_protection_on_output(igt_output_t *output, enum igt_commit_style s,
 
 			/* Expected to fail */
 			test_cp_enable_with_retry(output, s, 3,
-						  content_type, true, false);
+						  content_type, true, false,
+						  false);
 
 			igt_assert_f(!igt_kmod_load("mei_hdcp", NULL),
 				     "mei_hdcp load failed");
 
 			/* Expected to pass */
 			test_cp_enable_with_retry(output, s, 3,
-						  content_type, false, false);
+						  content_type, false, false,
+						  false);
 		}
 
 		test_cp_lic(output);
 
+		if (output->props[IGT_CONNECTOR_HDCP_TOPOLOGY_INFO] &&
+		    test_srm) {
+			retrieve_topology_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, 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, false);
+		}
+
 		if (dpms_test) {
 			igt_pipe_set_prop_value(display, pipe,
 						IGT_CRTC_ACTIVE, 0);
@@ -460,7 +620,7 @@ test_content_protection_on_output(igt_output_t *output, enum igt_commit_style s,
 			if (!ret)
 				test_cp_enable_with_retry(output, s, 2,
 							  content_type, false,
-							  false);
+							  false, false);
 		}
 
 		test_cp_disable(output, s);
@@ -526,7 +686,7 @@ 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,
-			bool test_type_change)
+			bool test_type_change, bool test_srm)
 {
 	igt_display_t *display = &data.display;
 	igt_output_t *output;
@@ -557,9 +717,8 @@ 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,
-						  test_type_change);
+						  content_type, mei_reload_test,
+						  test_type_change, test_srm);
 		valid_tests++;
 	}
 
@@ -579,33 +738,33 @@ igt_main
 	igt_subtest("legacy")
 		test_content_protection(COMMIT_LEGACY, false,
 					HDCP_CONTENT_TYPE_0, false,
-					false);
+					false, false);
 
 	igt_subtest("atomic") {
 		igt_require(data.display.is_atomic);
 		test_content_protection(COMMIT_ATOMIC, false,
-					HDCP_CONTENT_TYPE_0, false,
+					HDCP_CONTENT_TYPE_0, false, false,
 					false);
 	}
 
 	igt_subtest("atomic-dpms") {
 		igt_require(data.display.is_atomic);
 		test_content_protection(COMMIT_ATOMIC, true,
-					HDCP_CONTENT_TYPE_0, false,
+					HDCP_CONTENT_TYPE_0, false, false,
 					false);
 	}
 
 	igt_subtest("type1") {
 		igt_require(data.display.is_atomic);
 		test_content_protection(COMMIT_ATOMIC, false,
-					HDCP_CONTENT_TYPE_1, false,
+					HDCP_CONTENT_TYPE_1, false, false,
 					false);
 	}
 
 	igt_subtest("type1_mei_interface") {
 		igt_require(data.display.is_atomic);
 		test_content_protection(COMMIT_ATOMIC, false,
-					HDCP_CONTENT_TYPE_1, true,
+					HDCP_CONTENT_TYPE_1, true, false,
 					false);
 	}
 
@@ -613,9 +772,36 @@ igt_main
 		igt_require(data.display.is_atomic);
 		test_content_protection(COMMIT_ATOMIC, false,
 					HDCP_CONTENT_TYPE_1, false,
-					true);
+					true, false);
+	}
+
+	/*
+	 * SRM subtest perform the HDCP authentication, and then retrive the
+	 * receiver id through topology info.
+	 *
+	 * Using the receiver ID, facsimile SRM table is modified with
+	 * receiver ID retrieved from teh topology 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 topology 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,
+					HDCP_CONTENT_TYPE_1, false, false, true);
 	}
 
+
 	igt_fixture
 		igt_display_fini(&data.display);
 }
-- 
2.19.1



More information about the igt-dev mailing list