[04/10] drm/i915: Add HDCP SRM Blob parsing

Submitted by Ramalingam C on Feb. 26, 2019, 7:36 a.m.

Details

Message ID 1551166569-19683-5-git-send-email-ramalingam.c@intel.com
State New
Headers show
Series "HDCP2.2 Phase II" ( rev: 1 ) in Intel GFX

Not browsing as part of any series.

Commit Message

Ramalingam C Feb. 26, 2019, 7:36 a.m.
This patch adds a drm blob property to selected connectors.
And also adds capability to parse the new HDCP1.4 srm blob passed
through cp_srm_property.

The revocated KSV list and their counts are stored in
the intel_hdcp. This list should be used for revocation check
of BKSVs in first stage HDCP authentication and for revocation check of
ksv_fifo in second stage authentication.

Signed-off-by: Ramalingam C <ramalingam.c@intel.com>
---
 drivers/gpu/drm/i915/intel_ddi.c  |   6 +-
 drivers/gpu/drm/i915/intel_drv.h  |   8 ++-
 drivers/gpu/drm/i915/intel_hdcp.c | 147 +++++++++++++++++++++++++++++++++++++-
 include/drm/drm_hdcp.h            |  14 ++++
 4 files changed, 170 insertions(+), 5 deletions(-)

Patch hide | download patch | download mbox

diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c
index ffe7759a3f3a..1f935fe9c466 100644
--- a/drivers/gpu/drm/i915/intel_ddi.c
+++ b/drivers/gpu/drm/i915/intel_ddi.c
@@ -3500,7 +3500,8 @@  static void intel_enable_ddi(struct intel_encoder *encoder,
 	if (conn_state->content_protection ==
 	    DRM_MODE_CONTENT_PROTECTION_DESIRED)
 		intel_hdcp_enable(to_intel_connector(conn_state->connector),
-				  (u8)conn_state->cp_content_type);
+				  (u8)conn_state->cp_content_type,
+				  conn_state->cp_srm_blob_id);
 }
 
 static void intel_disable_ddi_dp(struct intel_encoder *encoder,
@@ -3570,7 +3571,8 @@  static void intel_ddi_update_hdcp(struct intel_encoder *encoder,
 	if (conn_state->content_protection ==
 	    DRM_MODE_CONTENT_PROTECTION_DESIRED)
 		intel_hdcp_enable(to_intel_connector(conn_state->connector),
-				  (u8)conn_state->cp_content_type);
+				  (u8)conn_state->cp_content_type,
+				  conn_state->cp_srm_blob_id);
 	else if (conn_state->content_protection ==
 		 DRM_MODE_CONTENT_PROTECTION_UNDESIRED)
 		intel_hdcp_disable(to_intel_connector(conn_state->connector));
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 04cff672aead..53a463d40f43 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -482,6 +482,11 @@  struct intel_hdcp {
 	wait_queue_head_t cp_irq_queue;
 	atomic_t cp_irq_count;
 	int cp_irq_count_cached;
+
+	/* list of Revocated KSVs and their count from SRM blob Parsing */
+	unsigned int revocated_ksv_cnt;
+	u8 *revocated_ksv_list;
+	u32 srm_blob_id;
 };
 
 struct intel_connector {
@@ -2151,7 +2156,8 @@  void intel_hdcp_atomic_check(struct drm_connector *connector,
 			     struct drm_connector_state *new_state);
 int intel_hdcp_init(struct intel_connector *connector,
 		    const struct intel_hdcp_shim *hdcp_shim);
-int intel_hdcp_enable(struct intel_connector *connector, u8 content_type);
+int intel_hdcp_enable(struct intel_connector *connector, u8 content_type,
+		      u32 srm_blob_id);
 int intel_hdcp_disable(struct intel_connector *connector);
 bool is_hdcp_supported(struct drm_i915_private *dev_priv, enum port port);
 bool intel_hdcp_capable(struct intel_connector *connector);
diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c
index ab25264a74a4..4b12274d8987 100644
--- a/drivers/gpu/drm/i915/intel_hdcp.c
+++ b/drivers/gpu/drm/i915/intel_hdcp.c
@@ -1805,6 +1805,10 @@  int intel_hdcp_init(struct intel_connector *connector,
 	if (ret)
 		return ret;
 
+	ret = drm_connector_attach_cp_srm_property(&connector->base);
+	if (ret)
+		return ret;
+
 	hdcp->shim = shim;
 	mutex_init(&hdcp->mutex);
 	INIT_DELAYED_WORK(&hdcp->check_work, intel_hdcp_check_work);
@@ -1817,7 +1821,144 @@  int intel_hdcp_init(struct intel_connector *connector,
 	return 0;
 }
 
-int intel_hdcp_enable(struct intel_connector *connector, u8 content_type)
+static u32 intel_hdcp_get_revocated_ksv_count(u8 *buf, u32 vrls_length)
+{
+	u32 parsed_bytes = 0, ksv_count = 0, vrl_ksv_cnt, vrl_sz;
+
+	do {
+		vrl_ksv_cnt = *buf;
+		ksv_count += vrl_ksv_cnt;
+
+		vrl_sz = (vrl_ksv_cnt * DRM_HDCP_KSV_LEN) + 1;
+		buf += vrl_sz;
+		parsed_bytes += vrl_sz;
+	} while (parsed_bytes < vrls_length);
+
+	return ksv_count;
+}
+
+static u32 intel_hdcp_get_revocated_ksvs(u8 *ksv_list, const u8 *buf,
+					 u32 vrls_length)
+{
+	u32 parsed_bytes = 0, ksv_count = 0;
+	u32 vrl_ksv_cnt, vrl_ksv_sz, vrl_idx = 0;
+
+	do {
+		vrl_ksv_cnt = *buf;
+		vrl_ksv_sz = vrl_ksv_cnt * DRM_HDCP_KSV_LEN;
+		buf++;
+
+		DRM_DEBUG_KMS("vrl: %d, Revoked KSVs: %d\n", vrl_idx++,
+			      vrl_ksv_cnt);
+		memcpy(ksv_list, buf, vrl_ksv_sz);
+		ksv_count += vrl_ksv_cnt;
+		ksv_list += vrl_ksv_sz;
+		buf += vrl_ksv_sz;
+
+		parsed_bytes += (vrl_ksv_sz + 1);
+	} while (parsed_bytes < vrls_length);
+
+	return ksv_count;
+}
+
+static int intel_hdcp_parse_srm(struct drm_connector *connector,
+				struct drm_property_blob *blob)
+{
+	struct intel_hdcp *hdcp = &(to_intel_connector(connector)->hdcp);
+	struct hdcp_srm_header *header;
+	u32 vrl_length, ksv_count;
+	u8 *buf;
+
+	if (blob->length < (sizeof(struct hdcp_srm_header) +
+	    DRM_HDCP_1_4_VRL_LENGTH_SIZE + DRM_HDCP_1_4_DCP_SIG_SIZE)) {
+		DRM_ERROR("Invalid blob length\n");
+		return -EINVAL;
+	}
+
+	header = (struct hdcp_srm_header *)blob->data;
+
+	DRM_DEBUG_KMS("SRM ID: 0x%x, SRM Ver: 0x%x, SRM Gen No: 0x%x\n",
+		      header->spec_indicator.srm_id,
+		      __swab16(header->srm_version), header->srm_gen_no);
+	WARN_ON(header->spec_indicator.reserved_hi ||
+		header->spec_indicator.reserved_lo);
+
+	if (header->spec_indicator.srm_id != DRM_HDCP_1_4_SRM_ID) {
+		DRM_ERROR("Invalid srm_id\n");
+		return -EINVAL;
+	}
+
+	buf = blob->data + sizeof(*header);
+	vrl_length = (*buf << 16 | *(buf + 1) << 8 | *(buf + 2));
+	if (blob->length < (sizeof(struct hdcp_srm_header) + vrl_length) ||
+	    vrl_length < (DRM_HDCP_1_4_VRL_LENGTH_SIZE +
+			  DRM_HDCP_1_4_DCP_SIG_SIZE)) {
+		DRM_ERROR("Invalid blob length or vrl length\n");
+		return -EINVAL;
+	}
+
+	/* Length of the all vrls combined */
+	vrl_length -= (DRM_HDCP_1_4_VRL_LENGTH_SIZE +
+		       DRM_HDCP_1_4_DCP_SIG_SIZE);
+	if (!vrl_length) {
+		DRM_DEBUG("No vrl found\n");
+		return -EINVAL;
+	}
+	buf += DRM_HDCP_1_4_VRL_LENGTH_SIZE;
+
+	ksv_count = intel_hdcp_get_revocated_ksv_count(buf, vrl_length);
+	if (!ksv_count) {
+		DRM_DEBUG_KMS("Revocated KSV count is 0\n");
+		return 0;
+	}
+
+	kfree(hdcp->revocated_ksv_list);
+	hdcp->revocated_ksv_list = kzalloc(ksv_count * DRM_HDCP_KSV_LEN,
+					   GFP_KERNEL);
+	if (!hdcp->revocated_ksv_list) {
+		DRM_ERROR("Out of Memory\n");
+		return -ENOMEM;
+	}
+
+	if (intel_hdcp_get_revocated_ksvs(hdcp->revocated_ksv_list,
+					  buf, vrl_length) != ksv_count) {
+		hdcp->revocated_ksv_cnt = 0;
+		kfree(hdcp->revocated_ksv_list);
+		return -EINVAL;
+	}
+
+	hdcp->revocated_ksv_cnt = ksv_count;
+	return 0;
+}
+
+static void intel_hdcp_update_srm(struct intel_connector *intel_connector,
+				  u32 srm_blob_id)
+{
+	struct drm_connector *connector = &intel_connector->base;
+	struct intel_hdcp *hdcp = &intel_connector->hdcp;
+	struct drm_property_blob *blob;
+
+	DRM_DEBUG_KMS("srm_blob_id %u\n", srm_blob_id);
+
+	if (!srm_blob_id) {
+		kfree(hdcp->revocated_ksv_list);
+		hdcp->revocated_ksv_cnt = 0;
+		hdcp->srm_blob_id = srm_blob_id;
+		return;
+	}
+
+	blob = drm_property_lookup_blob(connector->dev, srm_blob_id);
+	if (!blob || !blob->data)
+		return;
+
+	if (!intel_hdcp_parse_srm(connector, blob))
+		hdcp->srm_blob_id = srm_blob_id;
+
+	drm_property_blob_put(blob);
+}
+
+int intel_hdcp_enable(struct intel_connector *connector, u8 content_type,
+		      u32 srm_blob_id)
 {
 	struct intel_hdcp *hdcp = &connector->hdcp;
 	unsigned long check_link_interval = DRM_HDCP_CHECK_PERIOD_MS;
@@ -1828,9 +1969,11 @@  int intel_hdcp_enable(struct intel_connector *connector, u8 content_type)
 
 	mutex_lock(&hdcp->mutex);
 	WARN_ON(hdcp->value == DRM_MODE_CONTENT_PROTECTION_ENABLED);
-
 	hdcp->content_type = content_type;
 
+	if (srm_blob_id != hdcp->srm_blob_id)
+		intel_hdcp_update_srm(connector, srm_blob_id);
+
 	/*
 	 * Considering that HDCP2.2 is more secure than HDCP1.4, If the setup
 	 * is capable of HDCP2.2, it is preferred to use HDCP2.2.
diff --git a/include/drm/drm_hdcp.h b/include/drm/drm_hdcp.h
index f243408ecf26..1ccae47fcff5 100644
--- a/include/drm/drm_hdcp.h
+++ b/include/drm/drm_hdcp.h
@@ -265,4 +265,18 @@  void drm_hdcp2_u32_to_seq_num(u8 seq_num[HDCP_2_2_SEQ_NUM_LEN], u32 val)
 	seq_num[2] = val;
 }
 
+#define DRM_HDCP_1_4_SRM_ID			0x8
+#define DRM_HDCP_1_4_VRL_LENGTH_SIZE		3
+#define DRM_HDCP_1_4_DCP_SIG_SIZE		40
+
+struct hdcp_srm_header {
+	struct {
+		u8 reserved_hi:4;
+		u8 srm_id:4;
+		u8 reserved_lo;
+	} spec_indicator;
+	u16 srm_version;
+	u8 srm_gen_no;
+} __packed;
+
 #endif