From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: "Romli, Khairul Anuar" Date: Wed, 20 Dec 2017 21:47:01 +0530 Subject: [PATCH] drm/i915: Add HDCP SRM Blob parsing This patch adds a drm blob property to selected connectors. And also adds capability to parse the new srm blob passed through cp_srm_property. The revocated KSV list and their counts are stored in the intel_connector. 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 --- drivers/gpu/drm/i915/intel_drv.h | 5 + drivers/gpu/drm/i915/intel_hdcp.c | 146 ++++++++++++++++++++++++++++++ include/drm/drm_hdcp.h | 14 +++ 3 files changed, 165 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 10883e04bfe6..8bc603ec9b66 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -429,6 +429,11 @@ struct intel_connector { struct delayed_work hdcp_check_work; struct work_struct hdcp_prop_work; struct work_struct hdcp_enable_work; + + /* 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_digital_connector_state { diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c index a03a745544e2..ca2c73469cb2 100644 --- a/drivers/gpu/drm/i915/intel_hdcp.c +++ b/drivers/gpu/drm/i915/intel_hdcp.c @@ -10,6 +10,7 @@ #include #include #include +#include #include "intel_drv.h" #include "i915_reg.h" @@ -732,6 +733,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; + connector->hdcp_shim = hdcp_shim; mutex_init(&connector->hdcp_mutex); INIT_DELAYED_WORK(&connector->hdcp_check_work, intel_hdcp_check_work); @@ -818,11 +823,152 @@ void intel_hdcp_atomic_pre_commit(struct drm_connector *connector, intel_hdcp_disable(to_intel_connector(connector)); } +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_INFO("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_connector *intel_connector = to_intel_connector(connector); + struct cp_srm_header *header; + u32 vrl_length, ksv_count; + u8 *buf; + + if (blob->length < (sizeof(struct cp_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 cp_srm_header *)blob->data; + + DRM_INFO("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 cp_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_INFO("Revocated KSV count is 0\n"); + return 0; + } + + kfree(intel_connector->revocated_ksv_list); + intel_connector->revocated_ksv_list = kzalloc(ksv_count * + DRM_HDCP_KSV_LEN, GFP_KERNEL); + if (!intel_connector->revocated_ksv_list) { + DRM_ERROR("Out of Memory\n"); + return -ENOMEM; + } + + if (intel_hdcp_get_revocated_ksvs(intel_connector->revocated_ksv_list, + buf, vrl_length) != ksv_count) { + intel_connector->revocated_ksv_cnt = 0; + kfree(intel_connector->revocated_ksv_list); + return -EINVAL; + } + + intel_connector->revocated_ksv_cnt = ksv_count; + return 0; +} + +static void intel_hdcp_update_srm(struct drm_connector *connector, + u32 srm_blob_id) +{ + struct intel_connector *intel_connector = to_intel_connector(connector); + struct drm_property_blob *blob; + + blob = drm_property_lookup_blob(connector->dev, srm_blob_id); + if (!blob || !blob->data) + return; + + if (!intel_hdcp_parse_srm(connector, blob)) + intel_connector->srm_blob_id = srm_blob_id; + + drm_property_blob_put(blob); +} + void intel_hdcp_atomic_commit(struct drm_connector *connector, struct drm_connector_state *new_state) { + struct intel_connector *intel_connector = to_intel_connector(connector); uint64_t new_cp = new_state->content_protection; + if (new_state->cp_srm_blob_id && + new_state->cp_srm_blob_id != intel_connector->srm_blob_id) + intel_hdcp_update_srm(connector, new_state->cp_srm_blob_id); + /* Enable hdcp if it's desired */ if (new_state->crtc && new_cp == DRM_MODE_CONTENT_PROTECTION_DESIRED) intel_hdcp_enable(to_intel_connector(connector)); diff --git a/include/drm/drm_hdcp.h b/include/drm/drm_hdcp.h index 98e63d870139..f17eb2910bdb 100644 --- a/include/drm/drm_hdcp.h +++ b/include/drm/drm_hdcp.h @@ -38,4 +38,18 @@ #define DRM_HDCP_DDC_BSTATUS 0x41 #define DRM_HDCP_DDC_KSV_FIFO 0x43 +#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 cp_srm_header { + struct { + uint8_t reserved_hi:4; + uint8_t srm_id:4; + uint8_t reserved_lo; + } spec_indicator; + uint16_t srm_version; + uint8_t srm_gen_no; +} __packed; + #endif -- https://clearlinux.org