[PATCH v3 04/10] drm/i915: HDCP SRM parsing and revocation check

Ramalingam C
2019-03-22T00:44:42Z

Implements the SRM table parsing for HDCP 1.4 and 2.2.
And also revocation check is added at authentication of HDCP1.4
and 2.2

Signed-off-by: Ramalingam C <ramalingam.c@intel.com>
---
drivers/gpu/drm/i915/i915_drv.c | 1 +
drivers/gpu/drm/i915/i915_drv.h | 6 +
drivers/gpu/drm/i915/intel_drv.h | 2 +
drivers/gpu/drm/i915/intel_hdcp.c | 308 ++++++++++++++++++++++++++++--
include/drm/drm_hdcp.h | 32 ++++
5 files changed, 334 insertions(+), 15 deletions(-)

diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
index a3b00ecc58c9..60a894fab4fc 100644
--- a/drivers/gpu/drm/i915/i915_drv.c
+++ b/drivers/gpu/drm/i915/i915_drv.c
@@ -873,6 +873,7 @@ static int i915_driver_init_early(struct drm_i915_private *dev_priv)
mutex_init(&dev_priv->wm.wm_mutex);
mutex_init(&dev_priv->pps_mutex);
mutex_init(&dev_priv->hdcp_comp_mutex);
+ mutex_init(&dev_priv->srm_mutex);

i915_memcpy_init_early(dev_priv);
intel_runtime_pm_init_early(dev_priv);
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index c65c2e6649df..9b9daf6c9490 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -2061,6 +2061,12 @@ struct drm_i915_private {
/* Mutex to protect the above hdcp component related values. */
struct mutex hdcp_comp_mutex;

+ unsigned int revocated_ksv_cnt;
+ u8 *revocated_ksv_list;
+
+ /* Mutex to protect the data about revocated ksvs */
+ struct mutex srm_mutex;
+
/*
* NOTE: This is the dri1/ums dungeon, don't add stuff here. Your patch
* will be rejected. Instead look for a better place.
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index e387e842f414..4db15ee81042 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -2187,6 +2187,8 @@ void intel_hdcp_component_init(struct drm_i915_private *dev_priv);
void intel_hdcp_component_fini(struct drm_i915_private *dev_priv);
void intel_hdcp_cleanup(struct intel_connector *connector);
void intel_hdcp_handle_cp_irq(struct intel_connector *connector);
+ssize_t intel_hdcp_srm_update(struct drm_i915_private *dev_priv, char *buf,
+ size_t count);

/* intel_psr.c */
#define CAN_PSR(dev_priv) (HAS_PSR(dev_priv) && dev_priv->psr.sink_support)
diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c
index 9b4904a62744..921f07c64350 100644
--- a/drivers/gpu/drm/i915/intel_hdcp.c
+++ b/drivers/gpu/drm/i915/intel_hdcp.c
@@ -273,6 +273,62 @@ u32 intel_hdcp_get_repeater_ctl(struct intel_digital_port *intel_dig_port)
return -EINVAL;
}

+static inline void intel_hdcp_print_ksv(u8 *ksv)
+{
+ DRM_DEBUG_KMS("\t%#04x, %#04x, %#04x, %#04x, %#04x\n", *ksv,
+ *(ksv + 1), *(ksv + 2), *(ksv + 3), *(ksv + 4));
+}
+
+static inline
+struct intel_connector *intel_hdcp_to_connector(struct intel_hdcp *hdcp)
+{
+ return container_of(hdcp, struct intel_connector, hdcp);
+}
+
+/* Check if any of the KSV is revocated by DCP LLC through SRM table */
+static inline
+bool intel_hdcp_ksvs_revocated(struct intel_hdcp *hdcp, u8 *ksvs, u32 ksv_count)
+{
+ struct intel_connector *connector = intel_hdcp_to_connector(hdcp);
+ struct intel_digital_port *intel_dig_port = conn_to_dig_port(connector);
+ struct drm_i915_private *dev_priv =
+ intel_dig_port->base.base.dev->dev_private;
+ u32 rev_ksv_cnt, cnt, i, j;
+ u8 *rev_ksv_list;
+
+ mutex_lock(&dev_priv->srm_mutex);
+ rev_ksv_cnt = dev_priv->revocated_ksv_cnt;
+ rev_ksv_list = dev_priv->revocated_ksv_list;
+
+ /* If the Revocated ksv list is empty */
+ if (!rev_ksv_cnt || !rev_ksv_list) {
+ mutex_unlock(&dev_priv->srm_mutex);
+ return false;
+ }
+
+ for (cnt = 0; cnt < ksv_count; cnt++) {
+ rev_ksv_list = dev_priv->revocated_ksv_list;
+ for (i = 0; i < rev_ksv_cnt; i++) {
+ for (j = 0; j < DRM_HDCP_KSV_LEN; j++)
+ if (*(ksvs + j) != *(rev_ksv_list + j)) {
+ break;
+ } else if (j == (DRM_HDCP_KSV_LEN - 1)) {
+ DRM_DEBUG_KMS("Revocated KSV is ");
+ intel_hdcp_print_ksv(ksvs);
+ mutex_unlock(&dev_priv->srm_mutex);
+ return true;
+ }
+ /* Move the offset to next KSV in the revocated list */
+ rev_ksv_list += DRM_HDCP_KSV_LEN;
+ }
+
+ /* Iterate to next ksv_offset */
+ ksvs += DRM_HDCP_KSV_LEN;
+ }
+ mutex_unlock(&dev_priv->srm_mutex);
+ return false;
+}
+
static
int intel_hdcp_validate_v_prime(struct intel_digital_port *intel_dig_port,
const struct intel_hdcp_shim *shim,
@@ -490,9 +546,10 @@ int intel_hdcp_validate_v_prime(struct intel_digital_port *intel_dig_port,

/* Implements Part 2 of the HDCP authorization procedure */
static
-int intel_hdcp_auth_downstream(struct intel_digital_port *intel_dig_port,
- const struct intel_hdcp_shim *shim)
+int intel_hdcp_auth_downstream(struct intel_hdcp *hdcp,
+ struct intel_digital_port *intel_dig_port)
{
+ const struct intel_hdcp_shim *shim = hdcp->shim;
u8 bstatus[2], num_downstream, *ksv_fifo;
int ret, i, tries = 3;

@@ -531,6 +588,11 @@ int intel_hdcp_auth_downstream(struct intel_digital_port *intel_dig_port,
if (ret)
goto err;

+ if (intel_hdcp_ksvs_revocated(hdcp, ksv_fifo, num_downstream)) {
+ DRM_ERROR("Revocated Ksv(s) in ksv_fifo\n");
+ return -EPERM;
+ }
+
/*
* When V prime mismatches, DP Spec mandates re-read of
* V prime atleast twice.
@@ -557,9 +619,11 @@ int intel_hdcp_auth_downstream(struct intel_digital_port *intel_dig_port,
}

/* Implements Part 1 of the HDCP authorization procedure */
-static int intel_hdcp_auth(struct intel_digital_port *intel_dig_port,
- const struct intel_hdcp_shim *shim)
+static int intel_hdcp_auth(struct intel_connector *connector)
{
+ struct intel_digital_port *intel_dig_port = conn_to_dig_port(connector);
+ struct intel_hdcp *hdcp = &connector->hdcp;
+ const struct intel_hdcp_shim *shim = hdcp->shim;
struct drm_i915_private *dev_priv;
enum port port;
unsigned long r0_prime_gen_start;
@@ -625,6 +689,11 @@ static int intel_hdcp_auth(struct intel_digital_port *intel_dig_port,
if (ret < 0)
return ret;

+ if (intel_hdcp_ksvs_revocated(hdcp, bksv.shim, 1)) {
+ DRM_ERROR("BKSV is revocated\n");
+ return -EPERM;
+ }
+
I915_WRITE(PORT_HDCP_BKSVLO(port), bksv.reg[0]);
I915_WRITE(PORT_HDCP_BKSVHI(port), bksv.reg[1]);

@@ -698,7 +767,7 @@ static int intel_hdcp_auth(struct intel_digital_port *intel_dig_port,
*/

if (repeater_present)
- return intel_hdcp_auth_downstream(intel_dig_port, shim);
+ return intel_hdcp_auth_downstream(hdcp, intel_dig_port);

DRM_DEBUG_KMS("HDCP is enabled (no repeater present)\n");
return 0;
@@ -735,7 +804,6 @@ static int _intel_hdcp_disable(struct intel_connector *connector)

static int _intel_hdcp_enable(struct intel_connector *connector)
{
- struct intel_hdcp *hdcp = &connector->hdcp;
struct drm_i915_private *dev_priv = connector->base.dev->dev_private;
int i, ret, tries = 3;

@@ -760,9 +828,9 @@ static int _intel_hdcp_enable(struct intel_connector *connector)

/* Incase of authentication failures, HDCP spec expects reauth. */
for (i = 0; i < tries; i++) {
- ret = intel_hdcp_auth(conn_to_dig_port(connector), hdcp->shim);
+ ret = intel_hdcp_auth(connector);
if (!ret) {
- hdcp->hdcp_encrypted = true;
+ connector->hdcp.hdcp_encrypted = true;
return 0;
}

@@ -776,12 +844,6 @@ static int _intel_hdcp_enable(struct intel_connector *connector)
return ret;
}

-static inline
-struct intel_connector *intel_hdcp_to_connector(struct intel_hdcp *hdcp)
-{
- return container_of(hdcp, struct intel_connector, hdcp);
-}
-
/* Implements Part 3 of the HDCP authorization procedure */
static int intel_hdcp_check_link(struct intel_connector *connector)
{
@@ -1193,6 +1255,12 @@ static int hdcp2_authentication_key_exchange(struct intel_connector *connector)

hdcp->is_repeater = HDCP_2_2_RX_REPEATER(msgs.send_cert.rx_caps[2]);

+ if (intel_hdcp_ksvs_revocated(hdcp,
+ msgs.send_cert.cert_rx.receiver_id, 1)) {
+ DRM_ERROR("Receiver ID is revocated\n");
+ return -EPERM;
+ }
+
/*
* Here msgs.no_stored_km will hold msgs corresponding to the km
* stored also.
@@ -1351,7 +1419,7 @@ int hdcp2_authenticate_repeater_topology(struct intel_connector *connector)
} msgs;
const struct intel_hdcp_shim *shim = hdcp->shim;
u8 *rx_info;
- u32 seq_num_v;
+ u32 seq_num_v, device_cnt;
int ret;

ret = shim->read_2_2_msg(intel_dig_port, HDCP_2_2_REP_SEND_RECVID_LIST,
@@ -1376,6 +1444,14 @@ int hdcp2_authenticate_repeater_topology(struct intel_connector *connector)
return -EINVAL;
}

+ device_cnt = HDCP_2_2_DEV_COUNT_HI(rx_info[0]) << 4 ||
+ HDCP_2_2_DEV_COUNT_LO(rx_info[1]);
+ if (intel_hdcp_ksvs_revocated(hdcp, msgs.recvid_list.receiver_ids,
+ device_cnt)) {
+ DRM_ERROR("Revoked receiver ID(s) is in list\n");
+ return -EPERM;
+ }
+
ret = hdcp2_verify_rep_topology_prepare_ack(connector,
&msgs.recvid_list,
&msgs.rep_ack);
@@ -1818,6 +1894,208 @@ int intel_hdcp_init(struct intel_connector *connector,
return 0;
}

+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_i915_private *dev_priv, char *buf,
+ size_t count)
+{
+ struct hdcp_srm_header *header;
+ u32 vrl_length, ksv_count;
+
+ if (count < (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 *)buf;
+ mutex_lock(&dev_priv->srm_mutex);
+ 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");
+ mutex_unlock(&dev_priv->srm_mutex);
+ return -EINVAL;
+ }
+
+ buf = buf + sizeof(*header);
+ vrl_length = (*buf << 16 | *(buf + 1) << 8 | *(buf + 2));
+ if (count < (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");
+ mutex_unlock(&dev_priv->srm_mutex);
+ 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_KMS("No vrl found\n");
+ mutex_unlock(&dev_priv->srm_mutex);
+ 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");
+ mutex_unlock(&dev_priv->srm_mutex);
+ return count;
+ }
+
+ kfree(dev_priv->revocated_ksv_list);
+ dev_priv->revocated_ksv_list = kzalloc(ksv_count * DRM_HDCP_KSV_LEN,
+ GFP_KERNEL);
+ if (!dev_priv->revocated_ksv_list) {
+ DRM_ERROR("Out of Memory\n");
+ mutex_unlock(&dev_priv->srm_mutex);
+ return -ENOMEM;
+ }
+
+ if (intel_hdcp_get_revocated_ksvs(dev_priv->revocated_ksv_list,
+ buf, vrl_length) != ksv_count) {
+ dev_priv->revocated_ksv_cnt = 0;
+ kfree(dev_priv->revocated_ksv_list);
+ mutex_unlock(&dev_priv->srm_mutex);
+ return -EINVAL;
+ }
+
+ dev_priv->revocated_ksv_cnt = ksv_count;
+ mutex_unlock(&dev_priv->srm_mutex);
+ return count;
+}
+
+static int intel_hdcp2_parse_srm(struct drm_i915_private *dev_priv, char *buf,
+ size_t count)
+{
+ struct hdcp2_srm_header *header;
+ u32 vrl_length, ksv_count, ksv_sz;
+
+ mutex_lock(&dev_priv->srm_mutex);
+ if (count < (sizeof(struct hdcp2_srm_header) +
+ DRM_HDCP_2_VRL_LENGTH_SIZE + DRM_HDCP_2_DCP_SIG_SIZE)) {
+ DRM_ERROR("Invalid blob length\n");
+ mutex_unlock(&dev_priv->srm_mutex);
+ return -EINVAL;
+ }
+
+ header = (struct hdcp2_srm_header *)buf;
+ 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);
+ buf = buf + sizeof(*header);
+ vrl_length = (*buf << 16 | *(buf + 1) << 8 | *(buf + 2));
+
+ if (count < (sizeof(struct hdcp2_srm_header) + vrl_length) ||
+ vrl_length < (DRM_HDCP_2_VRL_LENGTH_SIZE +
+ DRM_HDCP_2_DCP_SIG_SIZE)) {
+ DRM_ERROR("Invalid blob length or vrl length\n");
+ mutex_unlock(&dev_priv->srm_mutex);
+ return -EINVAL;
+ }
+
+ /* Length of the all vrls combined */
+ vrl_length -= (DRM_HDCP_2_VRL_LENGTH_SIZE +
+ DRM_HDCP_2_DCP_SIG_SIZE);
+
+ if (!vrl_length) {
+ DRM_DEBUG_KMS("No vrl found\n");
+ mutex_unlock(&dev_priv->srm_mutex);
+ return -EINVAL;
+ }
+
+ buf += DRM_HDCP_2_VRL_LENGTH_SIZE;
+ ksv_count = (*buf << 2) | DRM_HDCP_2_KSV_COUNT_2_LSBITS(*(buf + 1));
+ if (!ksv_count) {
+ DRM_DEBUG_KMS("Revocated KSV count is 0\n");
+ mutex_unlock(&dev_priv->srm_mutex);
+ return count;
+ }
+
+ kfree(dev_priv->revocated_ksv_list);
+ dev_priv->revocated_ksv_list = kzalloc(ksv_count * DRM_HDCP_KSV_LEN,
+ GFP_KERNEL);
+ if (!dev_priv->revocated_ksv_list) {
+ DRM_ERROR("Out of Memory\n");
+ mutex_unlock(&dev_priv->srm_mutex);
+ return -ENOMEM;
+ }
+
+ ksv_sz = ksv_count * DRM_HDCP_KSV_LEN;
+ buf += DRM_HDCP_2_NO_OF_DEV_PLUS_RESERVED_SZ;
+
+ DRM_DEBUG_KMS("Revoked KSVs: %d\n", ksv_count);
+ memcpy(dev_priv->revocated_ksv_list, buf, ksv_sz);
+
+ dev_priv->revocated_ksv_cnt = ksv_count;
+ mutex_unlock(&dev_priv->srm_mutex);
+ return count;
+}
+
+ssize_t intel_hdcp_srm_update(struct drm_i915_private *dev_priv, char *buf,
+ size_t count)
+{
+ u8 srm_id;
+
+ srm_id = *((u8 *)buf);
+ if (srm_id == 0x80)
+ return (ssize_t)intel_hdcp_parse_srm(dev_priv, buf, count);
+ else if (srm_id == 0x91)
+ return (ssize_t)intel_hdcp2_parse_srm(dev_priv, buf, count);
+
+ return (ssize_t)-EINVAL;
+}
+
int intel_hdcp_enable(struct intel_connector *connector, u8 content_type)
{
struct intel_hdcp *hdcp = &connector->hdcp;
diff --git a/include/drm/drm_hdcp.h b/include/drm/drm_hdcp.h
index f243408ecf26..652aaf5d658e 100644
--- a/include/drm/drm_hdcp.h
+++ b/include/drm/drm_hdcp.h
@@ -265,4 +265,36 @@ 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;
+
+#define DRM_HDCP_2_SRM_ID 0x9
+#define DRM_HDCP_2_INDICATOR 0x1
+#define DRM_HDCP_2_VRL_LENGTH_SIZE 3
+#define DRM_HDCP_2_DCP_SIG_SIZE 384
+#define DRM_HDCP_2_NO_OF_DEV_PLUS_RESERVED_SZ 4
+
+#define DRM_HDCP_2_KSV_COUNT_2_LSBITS(byte) (((byte) & 0xC) >> 6)
+
+struct hdcp2_srm_header {
+ struct {
+ u8 hdcp2_indicator:4;
+ u8 srm_id:4;
+ u8 reserved;
+ } spec_indicator;
+ u16 srm_version;
+ u8 srm_gen_no;
+} __packed;
+
#endif
--
2.19.1