>-----Original Message----- >From: C, Ramalingam >Sent: Saturday, July 14, 2018 8:45 AM >To: intel-gfx@xxxxxxxxxxxxxxxxxxxxx; dri-devel@xxxxxxxxxxxxxxxxxxxxx; >daniel@xxxxxxxx; seanpaul@xxxxxxxxxxxx; Winkler, Tomas ><tomas.winkler@xxxxxxxxx>; Usyskin, Alexander <alexander.usyskin@xxxxxxxxx>; >Shankar, Uma <uma.shankar@xxxxxxxxx> >Cc: Sharma, Shashank <shashank.sharma@xxxxxxxxx>; C, Ramalingam ><ramalingam.c@xxxxxxxxx> >Subject: [PATCH v6 12/35] drm/i915: Implement HDCP2.2 receiver authentication > >Implements HDCP2.2 authentication for hdcp2.2 receivers, with following steps: > Authentication and Key exchange (AKE). > Locality Check (LC). > Session Key Exchange(SKE). > DP Errata for stream type configuration for receivers. > >At AKE, the HDCP Receiver’s public key certificate is verified by the HDCP >Transmitter. A Master Key k m is exchanged. > >At LC, the HDCP Transmitter enforces locality on the content by requiring that the >Round Trip Time (RTT) between a pair of messages is not more than 20 ms. > >At SKE, The HDCP Transmitter exchanges Session Key ks with the HDCP Receiver. > >In DP HDCP2.2 encryption and decryption logics use the stream type as one of the >parameter. So Before enabling the Encryption DP HDCP2.2 receiver needs to be >communicated with stream type. This is added to spec as ERRATA. > >This generic implementation is complete only with the hdcp2_shim defined. > >v2: > Rebased. >v3: > No Changes. >v4: > %s/PARING/PAIRING > Coding style fixing [Uma] >v5: > Rebased as part of patch reordering. > Defined the functions for mei services. [Daniel] >v6: > Redefined the mei service functions as per comp redesign. > Required intel_hdcp members are defined [Sean Paul] > >Signed-off-by: Ramalingam C <ramalingam.c@xxxxxxxxx> >--- > drivers/gpu/drm/i915/intel_drv.h | 34 ++++ drivers/gpu/drm/i915/intel_hdcp.c >| 370 +++++++++++++++++++++++++++++++++++++- > 2 files changed, 401 insertions(+), 3 deletions(-) > >diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h >index 21683702bcdc..ed429a6fb9ab 100644 >--- a/drivers/gpu/drm/i915/intel_drv.h >+++ b/drivers/gpu/drm/i915/intel_drv.h >@@ -384,6 +384,22 @@ struct intel_hdcp_shim { > /* Detects whether Panel is HDCP2.2 capable */ > int (*hdcp_2_2_capable)(struct intel_digital_port *intel_dig_port, > bool *capable); >+ >+ /* Write HDCP2.2 messages */ >+ int (*write_2_2_msg)(struct intel_digital_port *intel_dig_port, >+ void *buf, size_t size); >+ >+ /* Read HDCP2.2 messages */ >+ int (*read_2_2_msg)(struct intel_digital_port *intel_dig_port, >+ uint8_t msg_id, void *buf, size_t size); >+ >+ /* >+ * Implementation of DP HDCP2.2 Errata for the communication of >stream >+ * type to Receivers. In DP HDCP2.2 Stream type is one of the input to >+ * the HDCP2.2 Chiper for En/De-Cryption. Not applicable for HDMI. This should be Cipher. >+ */ >+ int (*config_stream_type)(struct intel_digital_port *intel_dig_port, >+ void *buf, size_t size); > }; > > struct intel_hdcp { >@@ -408,6 +424,24 @@ struct intel_hdcp { > > /* mei interface related information */ > struct mei_hdcp_data mei_data; >+ >+ bool is_paired; >+ bool is_repeater; >+ >+ /* >+ * Count of ReceiverID_List received. Initialized to 0 at AKE_INIT. >+ * Incremented after processing the RepeaterAuth_Send_ReceiverID_List. >+ * When it rolls over re-auth has to be triggered. >+ */ >+ uint32_t seq_num_v; >+ >+ /* >+ * Count of RepeaterAuth_Stream_Manage msg propagated. >+ * Initialized to 0 on AKE_INIT. Incremented after every successful >+ * transmission of RepeaterAuth_Stream_Manage message. When it rolls >+ * over re-Auth has to be triggered. >+ */ >+ uint32_t seq_num_m; > }; > > struct intel_connector { >diff --git a/drivers/gpu/drm/i915/intel_hdcp.c >b/drivers/gpu/drm/i915/intel_hdcp.c >index 4ace64315baa..ee1e06bac175 100644 >--- a/drivers/gpu/drm/i915/intel_hdcp.c >+++ b/drivers/gpu/drm/i915/intel_hdcp.c >@@ -18,6 +18,7 @@ > > #define KEY_LOAD_TRIES 5 > #define TIME_FOR_ENCRYPT_STATUS_CHANGE 32 >+#define HDCP2_LC_RETRY_CNT 3 Spec says it can be tried upto 1024 times, any specific reasons of limiting it to just 3. > #define GET_MEI_DDI_INDEX(port) (((port) == PORT_A) ? DDI_A : \ > (enum hdcp_physical_port)(port)) > >@@ -978,6 +979,191 @@ int intel_hdcp_check_link(struct intel_connector >*connector) > return ret; > } > >+static int >+hdcp2_prepare_ake_init(struct intel_connector *connector, >+ struct hdcp2_ake_init *ake_data) { >+ struct mei_hdcp_data *data = &connector->hdcp.mei_data; >+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev); >+ struct i915_component_master *comp = dev_priv->comp_master; >+ int ret; >+ >+ if (!comp) >+ return -EINVAL; >+ >+ if (!comp->hdcp_ops || !comp->mei_cldev) >+ return -EINVAL; >+ >+ if (data->port == INVALID_PORT && connector->encoder) >+ data->port = GET_MEI_DDI_INDEX(connector->encoder->port); >+ >+ /* Clear ME FW instance for the port, just incase */ >+ comp->hdcp_ops->close_hdcp_session(comp->mei_cldev, data); The callback is not yet assigned here, this may cause a crash during bisect. I would suggest to define the callback functions earlier in the series and then assign them in subsequent patches or protect it with a NULL check to be safe. This may not get called till this point due to hdcp2 flag as false in hdcp_init, but this is changed in 0021-drm-i915-Add-HDCP2.2-support-for-HDMI-connectors.patch which will start giving the above problem from there on till you finally define it in 0035-misc-mei-hdcp-Closing-wired-HDCP2.2-Tx-Session.patch >+ >+ ret = comp->hdcp_ops->initiate_hdcp2_session(comp->mei_cldev, >+ data, ake_data); Same here. Check wherever applicable universally. >+ >+ return ret; >+} >+ >+static int >+hdcp2_verify_rx_cert_prepare_km(struct intel_connector *connector, >+ struct hdcp2_ake_send_cert *rx_cert, >+ bool *paired, >+ struct hdcp2_ake_no_stored_km *ek_pub_km, >+ size_t *msg_sz) >+{ >+ struct mei_hdcp_data *data = &connector->hdcp.mei_data; >+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev); >+ struct i915_component_master *comp = dev_priv->comp_master; >+ int ret; >+ >+ if (!comp) >+ return -EINVAL; >+ >+ if (!comp->hdcp_ops || !comp->mei_cldev || data->port == >INVALID_PORT) >+ return -EINVAL; >+ >+ ret = comp->hdcp_ops->verify_receiver_cert_prepare_km(comp- >>mei_cldev, >+ data, rx_cert, >+ paired, ek_pub_km, >+ msg_sz); >+ if (ret < 0) >+ comp->hdcp_ops->close_hdcp_session(comp->mei_cldev, data); >+ >+ return ret; >+} >+ >+static int hdcp2_verify_hprime(struct intel_connector *connector, >+ struct hdcp2_ake_send_hprime *rx_hprime) { >+ struct mei_hdcp_data *data = &connector->hdcp.mei_data; >+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev); >+ struct i915_component_master *comp = dev_priv->comp_master; >+ int ret; >+ >+ if (!comp) >+ return -EINVAL; >+ >+ if (!comp->hdcp_ops || !comp->mei_cldev || data->port == >INVALID_PORT) >+ return -EINVAL; >+ >+ ret = comp->hdcp_ops->verify_hprime(comp->mei_cldev, data, >rx_hprime); >+ if (ret < 0) >+ comp->hdcp_ops->close_hdcp_session(comp->mei_cldev, data); >+ >+ return ret; >+} >+ >+static int >+hdcp2_store_pairing_info(struct intel_connector *connector, >+ struct hdcp2_ake_send_pairing_info *pairing_info) { >+ struct mei_hdcp_data *data = &connector->hdcp.mei_data; >+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev); >+ struct i915_component_master *comp = dev_priv->comp_master; >+ int ret; >+ >+ if (!comp) >+ return -EINVAL; >+ >+ if (!comp->hdcp_ops || !comp->mei_cldev || data->port == >INVALID_PORT) >+ return -EINVAL; >+ >+ ret = comp->hdcp_ops->store_pairing_info(comp->mei_cldev, >+ data, pairing_info); >+ if (ret < 0) >+ comp->hdcp_ops->close_hdcp_session(comp->mei_cldev, data); >+ >+ return ret; >+} >+ >+static int >+hdcp2_prepare_lc_init(struct intel_connector *connector, >+ struct hdcp2_lc_init *lc_init) >+{ >+ struct mei_hdcp_data *data = &connector->hdcp.mei_data; >+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev); >+ struct i915_component_master *comp = dev_priv->comp_master; >+ int ret; >+ >+ if (!comp) >+ return -EINVAL; >+ >+ if (!comp->hdcp_ops || !comp->mei_cldev || data->port == >INVALID_PORT) >+ return -EINVAL; >+ >+ ret = comp->hdcp_ops->initiate_locality_check(comp->mei_cldev, >+ data, lc_init); >+ if (ret < 0) >+ comp->hdcp_ops->close_hdcp_session(comp->mei_cldev, data); >+ >+ return ret; >+} >+ >+static int >+hdcp2_verify_lprime(struct intel_connector *connector, >+ struct hdcp2_lc_send_lprime *rx_lprime) { >+ struct mei_hdcp_data *data = &connector->hdcp.mei_data; >+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev); >+ struct i915_component_master *comp = dev_priv->comp_master; >+ int ret; >+ >+ if (!comp) >+ return -EINVAL; >+ >+ if (!comp->hdcp_ops || !comp->mei_cldev || data->port == >INVALID_PORT) >+ return -EINVAL; >+ >+ ret = comp->hdcp_ops->verify_lprime(comp->mei_cldev, data, >rx_lprime); >+ if (ret < 0) >+ comp->hdcp_ops->close_hdcp_session(comp->mei_cldev, data); >+ >+ return ret; >+} >+ >+static int hdcp2_prepare_skey(struct intel_connector *connector, >+ struct hdcp2_ske_send_eks *ske_data) { >+ struct mei_hdcp_data *data = &connector->hdcp.mei_data; >+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev); >+ struct i915_component_master *comp = dev_priv->comp_master; >+ int ret; >+ >+ if (!comp) >+ return -EINVAL; >+ >+ if (!comp->hdcp_ops || !comp->mei_cldev || data->port == >INVALID_PORT) >+ return -EINVAL; >+ >+ ret = comp->hdcp_ops->get_session_key(comp->mei_cldev, data, >ske_data); >+ if (ret < 0) >+ comp->hdcp_ops->close_hdcp_session(comp->mei_cldev, data); >+ >+ return ret; >+} >+ >+static int hdcp2_authenticate_port(struct intel_connector *connector) { >+ struct mei_hdcp_data *data = &connector->hdcp.mei_data; >+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev); >+ struct i915_component_master *comp = dev_priv->comp_master; >+ int ret; >+ >+ if (!comp) >+ return -EINVAL; >+ >+ if (!comp->hdcp_ops || !comp->mei_cldev || data->port == >INVALID_PORT) >+ return -EINVAL; >+ >+ ret = comp->hdcp_ops->enable_hdcp_authentication(comp->mei_cldev, >data); >+ if (ret < 0) >+ comp->hdcp_ops->close_hdcp_session(comp->mei_cldev, data); >+ >+ return ret; >+} >+ > static int hdcp2_close_mei_session(struct intel_connector *connector) { > struct mei_hdcp_data *data = &connector->hdcp.mei_data; @@ -998,12 >+1184,190 @@ static int hdcp2_deauthenticate_port(struct intel_connector >*connector) > return hdcp2_close_mei_session(connector); > } > >+/* Authentication flow starts from here */ static int >+hdcp2_authentication_key_exchange(struct intel_connector *connector) { >+ struct intel_digital_port *intel_dig_port = conn_to_dig_port(connector); >+ struct intel_hdcp *hdcp = &connector->hdcp; >+ union { >+ struct hdcp2_ake_init ake_init; >+ struct hdcp2_ake_send_cert send_cert; >+ struct hdcp2_ake_no_stored_km no_stored_km; >+ struct hdcp2_ake_send_hprime send_hprime; >+ struct hdcp2_ake_send_pairing_info pairing_info; >+ } msgs; >+ const struct intel_hdcp_shim *shim = hdcp->shim; >+ size_t size; >+ int ret; >+ >+ /* Init for seq_num */ >+ hdcp->seq_num_v = 0; >+ hdcp->seq_num_m = 0; >+ >+ ret = hdcp2_prepare_ake_init(connector, &msgs.ake_init); >+ if (ret < 0) >+ return ret; >+ >+ ret = shim->write_2_2_msg(intel_dig_port, &msgs.ake_init, >+ sizeof(msgs.ake_init)); >+ if (ret < 0) >+ return ret; >+ >+ ret = shim->read_2_2_msg(intel_dig_port, HDCP_2_2_AKE_SEND_CERT, >+ &msgs.send_cert, sizeof(msgs.send_cert)); >+ if (ret < 0) >+ return ret; >+ >+ if (msgs.send_cert.rx_caps[0] != HDCP_2_2_RX_CAPS_VERSION_VAL) >+ return -EINVAL; >+ >+ hdcp->is_repeater = >HDCP_2_2_RX_REPEATER(msgs.send_cert.rx_caps[2]); >+ >+ /* >+ * Here msgs.no_stored_km will hold msgs corresponding to the km >+ * stored also. >+ */ >+ ret = hdcp2_verify_rx_cert_prepare_km(connector, &msgs.send_cert, >+ &hdcp->is_paired, >+ &msgs.no_stored_km, &size); >+ if (ret < 0) >+ return ret; >+ >+ ret = shim->write_2_2_msg(intel_dig_port, &msgs.no_stored_km, size); >+ if (ret < 0) >+ return ret; >+ >+ ret = shim->read_2_2_msg(intel_dig_port, >HDCP_2_2_AKE_SEND_HPRIME, >+ &msgs.send_hprime, >sizeof(msgs.send_hprime)); How are ensuring that this read happens within 200ms as defined by spec ? >+ if (ret < 0) >+ return ret; >+ >+ ret = hdcp2_verify_hprime(connector, &msgs.send_hprime); >+ if (ret < 0) >+ return ret; >+ >+ if (!hdcp->is_paired) { >+ /* Pairing is required */ >+ ret = shim->read_2_2_msg(intel_dig_port, >+ HDCP_2_2_AKE_SEND_PAIRING_INFO, >+ &msgs.pairing_info, >+ sizeof(msgs.pairing_info)); >+ if (ret < 0) >+ return ret; >+ >+ ret = hdcp2_store_pairing_info(connector, &msgs.pairing_info); >+ if (ret < 0) >+ return ret; >+ hdcp->is_paired = true; >+ } >+ >+ return 0; >+} >+ >+static int hdcp2_locality_check(struct intel_connector *connector) { >+ struct intel_digital_port *intel_dig_port = conn_to_dig_port(connector); >+ struct intel_hdcp *hdcp = &connector->hdcp; >+ union { >+ struct hdcp2_lc_init lc_init; >+ struct hdcp2_lc_send_lprime send_lprime; >+ } msgs; >+ const struct intel_hdcp_shim *shim = hdcp->shim; >+ int tries = HDCP2_LC_RETRY_CNT, ret, i; >+ >+ for (i = 0; i < tries; i++) { >+ ret = hdcp2_prepare_lc_init(connector, &msgs.lc_init); >+ if (ret < 0) >+ continue; >+ >+ ret = shim->write_2_2_msg(intel_dig_port, &msgs.lc_init, >+ sizeof(msgs.lc_init)); >+ if (ret < 0) >+ continue; >+ >+ ret = shim->read_2_2_msg(intel_dig_port, >+ HDCP_2_2_LC_SEND_LPRIME, >+ &msgs.send_lprime, >+ sizeof(msgs.send_lprime)); Again how it ensures the 20ms timeout per spec ? >+ if (ret < 0) >+ continue; >+ >+ ret = hdcp2_verify_lprime(connector, &msgs.send_lprime); >+ if (!ret) >+ break; >+ } >+ >+ return ret; >+} >+ >+static int hdcp2_session_key_exchange(struct intel_connector >+*connector) { >+ struct intel_digital_port *intel_dig_port = conn_to_dig_port(connector); >+ struct intel_hdcp *hdcp = &connector->hdcp; >+ struct hdcp2_ske_send_eks send_eks; >+ int ret; >+ >+ ret = hdcp2_prepare_skey(connector, &send_eks); >+ if (ret < 0) >+ return ret; >+ >+ ret = hdcp->shim->write_2_2_msg(intel_dig_port, &send_eks, >+ sizeof(send_eks)); >+ if (ret < 0) >+ return ret; >+ >+ return 0; >+} >+ > static int hdcp2_authenticate_sink(struct intel_connector *connector) { >- struct drm_i915_private *dev_priv = to_i915(connector->base.dev); >+ 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 hdcp2_dp_errata_stream_type stream_type_msg; >+ int ret; > >- DRM_ERROR("Sink authentication is done in subsequent patches\n"); >- return -EINVAL; >+ ret = hdcp2_authentication_key_exchange(connector); >+ if (ret < 0) { >+ DRM_DEBUG_KMS("AKE Failed. Err : %d\n", ret); >+ return ret; >+ } >+ >+ ret = hdcp2_locality_check(connector); >+ if (ret < 0) { >+ DRM_DEBUG_KMS("Locality Check failed. Err : %d\n", ret); >+ return ret; >+ } >+ >+ ret = hdcp2_session_key_exchange(connector); >+ if (ret < 0) { >+ DRM_DEBUG_KMS("SKE Failed. Err : %d\n", ret); >+ return ret; >+ } >+ >+ if (!hdcp->is_repeater && shim->config_stream_type) { >+ /* >+ * Errata for DP: As Stream type is used for encryption, >+ * Receiver should be communicated with stream type for the >+ * decryption of the content. >+ * Repeater will be communicated with stream type as a >+ * part of it's auth later in time. >+ */ >+ stream_type_msg.msg_id = >HDCP_2_2_ERRATA_DP_STREAM_TYPE; >+ stream_type_msg.stream_type = hdcp->content_type; >+ >+ ret = shim->config_stream_type(intel_dig_port, >&stream_type_msg, >+ sizeof(stream_type_msg)); >+ if (ret < 0) >+ return ret; >+ } >+ >+ hdcp->mei_data.streams[0].stream_type = hdcp->content_type; >+ ret = hdcp2_authenticate_port(connector); >+ if (ret < 0) >+ return ret; >+ >+ return ret; > } > > static int hdcp2_enable_encryption(struct intel_connector *connector) >-- >2.7.4 _______________________________________________ dri-devel mailing list dri-devel@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/dri-devel