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

Daniel Vetter
2019-03-27T10:16:44Z

On Fri, Mar 22, 2019 at 06:14:42AM +0530, Ramalingam C wrote:
> 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 ++++

Needs to be split out as a drm core patch.

I also wonder whether some of the SRM parsing should be helper functions
in a new drm_hdcp.c file ... Would fit really well with the overall single
drm sysfs file for SRM design, where all the parsing is done once in the
core. And then drivers just have functions to either get the entire SRM
(for upload to ME), or check a ksv against the revocation list (hdcp1.x).

Since the parsing code might be slightly different if we integrate it more
tightly with a sysfs binary file I'll wait with reviewing the details
until we've settled on the big picture. But looks all reasonable.
-Daniel

> 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
>

--
Daniel Vetter
Software Engineer, Intel Corporation