From ef193b7094d3baf5521de0858625ab6cde04e58a Mon Sep 17 00:00:00 2001 From: Abhinav Kumar Date: Tue, 7 Nov 2017 22:21:22 -0800 Subject: [PATCH] drm/msm: add SRM support for HDCP 1.4 Add support for clients to notify SRM update to HDCP 1.x driver. Integrate the SRM validation check in the HDCP 1.x authentication flow to check HDCP 1.x receiver/repeater KSV against the SRM revoked list and fail the authentication if the sink is found to be present in the list. Change-Id: I6615122f785bde94cb746ec4df7ab63b9f878528 Signed-off-by: Abhinav Kumar --- drivers/gpu/drm/msm/sde_hdcp.h | 2 + drivers/gpu/drm/msm/sde_hdcp_1x.c | 163 +++++++++++++++++++++++- drivers/misc/hdcp.c | 205 +++++++++++++++++++++++++++--- include/linux/hdcp_qseecom.h | 12 ++ 4 files changed, 362 insertions(+), 20 deletions(-) diff --git a/drivers/gpu/drm/msm/sde_hdcp.h b/drivers/gpu/drm/msm/sde_hdcp.h index c414f68a8e0d..415e8467c99f 100644 --- a/drivers/gpu/drm/msm/sde_hdcp.h +++ b/drivers/gpu/drm/msm/sde_hdcp.h @@ -33,6 +33,8 @@ #define SDE_HDCP_DEBUG(fmt, args...) SDE_DEBUG(fmt, ##args) #endif +#define SDE_HDCP_SRM_FAIL 29 + enum sde_hdcp_client_id { HDCP_CLIENT_HDMI, HDCP_CLIENT_DP, diff --git a/drivers/gpu/drm/msm/sde_hdcp_1x.c b/drivers/gpu/drm/msm/sde_hdcp_1x.c index c2d29a084c7f..6c69cd58c0ed 100644 --- a/drivers/gpu/drm/msm/sde_hdcp_1x.c +++ b/drivers/gpu/drm/msm/sde_hdcp_1x.c @@ -748,6 +748,107 @@ error: return rc; } +static u8 *sde_hdcp_1x_swap_byte_order(u8 *bksv_in, int num_dev) +{ + u8 *bksv_out; + u8 *tmp_out; + u8 *tmp_in; + int i, j; + + /* Dont exceed max downstream devices */ + if (num_dev > MAX_DEVICES_SUPPORTED) { + pr_err("invalid params\n"); + return NULL; + } + + bksv_out = kzalloc(RECV_ID_SIZE * num_dev, GFP_KERNEL); + + if (!bksv_out) + return NULL; + + SDE_HDCP_DEBUG("num_dev = %d\n", num_dev); + + /* Store temporarily for return */ + tmp_out = bksv_out; + tmp_in = bksv_in; + + for (i = 0; i < num_dev; i++) { + for (j = 0; j < RECV_ID_SIZE; j++) + bksv_out[j] = tmp_in[RECV_ID_SIZE - j - 1]; + + /* Each KSV is 5 bytes long */ + bksv_out += RECV_ID_SIZE; + tmp_in += RECV_ID_SIZE; + } + + return tmp_out; +} + +static int sde_hdcp_1x_revoked_rcv_chk(struct sde_hdcp_1x *hdcp) +{ + int rc = 0; + u8 *bksv = hdcp->current_tp.bksv; + u8 *bksv_out; + struct hdcp_srm_device_id_t *bksv_srm; + + bksv_out = sde_hdcp_1x_swap_byte_order(bksv, 1); + + if (!bksv_out) { + rc = -ENOMEM; + goto exit; + } + + SDE_HDCP_DEBUG("bksv_out : 0x%2x%2x%2x%2x%2x\n", + bksv_out[4], bksv_out[3], bksv_out[2], + bksv_out[1], bksv_out[0]); + + bksv_srm = (struct hdcp_srm_device_id_t *)bksv_out; + /* Here we are checking only receiver ID + * hence the device count is one + */ + rc = hdcp1_validate_receiver_ids(bksv_srm, 1); + + kfree(bksv_out); + +exit: + return rc; +} + +static int sde_hdcp_1x_revoked_rpt_chk(struct sde_hdcp_1x *hdcp) +{ + int rc = 0; + int i; + u8 *bksv = hdcp->current_tp.ksv_list; + u8 *bksv_out; + struct hdcp_srm_device_id_t *bksv_srm; + + for (i = 0; i < hdcp->sink_addr.ksv_fifo.len; + i += RECV_ID_SIZE) { + SDE_HDCP_DEBUG("bksv : 0x%2x%2x%2x%2x%2x\n", + bksv[i + 4], + bksv[i + 3], bksv[i + 2], + bksv[i + 1], bksv[i]); + } + + bksv_out = sde_hdcp_1x_swap_byte_order(bksv, + hdcp->current_tp.dev_count); + + if (!bksv_out) { + rc = -ENOMEM; + goto exit; + } + + bksv_srm = (struct hdcp_srm_device_id_t *)bksv_out; + /* Here we are checking repeater ksv list */ + rc = hdcp1_validate_receiver_ids(bksv_srm, + hdcp->current_tp.dev_count); + + kfree(bksv_out); + +exit: + return rc; +} + static void sde_hdcp_1x_enable_sink_irq_hpd(struct sde_hdcp_1x *hdcp) { int rc; @@ -872,6 +973,12 @@ static int sde_hdcp_1x_authentication_part1(struct sde_hdcp_1x *hdcp) if (rc) goto error; + rc = sde_hdcp_1x_revoked_rcv_chk(hdcp); + if (rc) { + rc = -SDE_HDCP_SRM_FAIL; + goto error; + } + rc = sde_hdcp_1x_send_an_aksv_to_sink(hdcp); if (rc) goto error; @@ -1196,6 +1303,12 @@ static int sde_hdcp_1x_authentication_part2(struct sde_hdcp_1x *hdcp) if (rc) goto error; + rc = sde_hdcp_1x_revoked_rpt_chk(hdcp); + if (rc) { + rc = -SDE_HDCP_SRM_FAIL; + goto error; + } + do { rc = sde_hdcp_1x_transfer_v_h(hdcp); if (rc) @@ -1317,8 +1430,11 @@ static void sde_hdcp_1x_auth_work(struct work_struct *work) end: - if (rc && !sde_hdcp_1x_state(HDCP_STATE_INACTIVE)) + if (rc && !sde_hdcp_1x_state(HDCP_STATE_INACTIVE)) { hdcp->hdcp_state = HDCP_STATE_AUTH_FAIL; + if (rc == -SDE_HDCP_SRM_FAIL) + hdcp->hdcp_state = HDCP_STATE_AUTH_FAIL_NOREAUTH; + } /* * Disabling software DDC before going into part3 to make sure @@ -1581,6 +1697,7 @@ void sde_hdcp_1x_deinit(void *input) if (hdcp->workq) destroy_workqueue(hdcp->workq); + hdcp1_client_unregister(); kfree(hdcp); } /* hdcp_1x_deinit */ @@ -1680,6 +1797,44 @@ irq_not_handled: return -EINVAL; } +static void sde_hdcp_1x_srm_cb(void *input) +{ + + struct sde_hdcp_1x *hdcp = (struct sde_hdcp_1x *)input; + int rc = 0; + + if (!hdcp) { + pr_err("invalid input\n"); + return; + } + + rc = sde_hdcp_1x_revoked_rcv_chk(hdcp); + + if (rc) { + pr_err("receiver failed SRM check\n"); + goto fail_noreauth; + } + + /* If its not a repeater we are done */ + if (hdcp->current_tp.ds_type != DS_REPEATER) + return; + + + /* Check the repeater KSV against SRM */ + rc = sde_hdcp_1x_revoked_rpt_chk(hdcp); + if (rc) { + pr_err("repeater failed SRM check\n"); + goto fail_noreauth; + } + + return; + + fail_noreauth: + /* No reauth in case of SRM failure */ + hdcp->hdcp_state = HDCP_STATE_AUTH_FAIL_NOREAUTH; + sde_hdcp_1x_update_auth_status(hdcp); +} + void *sde_hdcp_1x_init(struct sde_hdcp_init_data *init_data) { struct sde_hdcp_1x *hdcp = NULL; @@ -1692,6 +1847,10 @@ void *sde_hdcp_1x_init(struct sde_hdcp_init_data *init_data) .off = sde_hdcp_1x_off }; + static struct hdcp_client_ops client_ops = { + .srm_cb = sde_hdcp_1x_srm_cb, + }; + if (!init_data || !init_data->core_io || !init_data->qfprom_io || !init_data->mutex || !init_data->notify_status || !init_data->workq || !init_data->cb_data) { @@ -1729,6 +1888,8 @@ void *sde_hdcp_1x_init(struct sde_hdcp_init_data *init_data) init_completion(&hdcp->r0_checked); init_completion(&hdcp->sink_r0_available); + /* Register client ctx and the srm_cb with hdcp lib */ + hdcp1_client_register((void *)hdcp, &client_ops); SDE_HDCP_DEBUG("HDCP module initialized. HDCP_STATE=%s\n", SDE_HDCP_STATE_NAME); diff --git a/drivers/misc/hdcp.c b/drivers/misc/hdcp.c index 87daee7cf1c6..09c7c1eeca7a 100644 --- a/drivers/misc/hdcp.c +++ b/drivers/misc/hdcp.c @@ -93,6 +93,8 @@ #define RXINFO_SIZE 2 #define SEQ_NUM_V_SIZE 3 +#define HDCP_SRM_CMD_CHECK_DEVICE_ID 2 + #define RCVR_ID_SIZE BITS_40_IN_BYTES #define MAX_RCVR_IDS_ALLOWED_IN_LIST 31 #define MAX_RCVR_ID_LIST_SIZE \ @@ -437,6 +439,17 @@ struct __attribute__ ((__packed__)) hdcp_update_srm_rsp { uint32_t commandid; }; +struct __attribute__ ((__packed__)) hdcp_srm_check_device_ids_req { + uint32_t commandid; + uint32_t num_device_ids; + uint8_t device_ids[1]; +}; + +struct __attribute__ ((__packed__)) hdcp_srm_check_device_ids_rsp { + uint32_t commandid; + int32_t retval; +}; + struct __attribute__ ((__packed__)) hdcp_get_topology_req { uint32_t commandid; uint32_t ctxhandle; @@ -496,6 +509,20 @@ struct __attribute__ ((__packed__)) hdcp_rcv_id_list_rsp { uint32_t commandid; }; +/* + * struct hdcp1_lib_handle - handle for hdcp1 client + * @qseecom_handle - for sending commands to hdcp1 TA + * @srm_handle - for sending commands to SRM TA + * @client_ops - handle to call APIs exposed by hdcp1 client + * @client_ctx - client context maintained by hdmi + */ +struct hdcp1_lib_handle { + struct qseecom_handle *qsee_handle; + struct qseecom_handle *srm_handle; + struct hdcp_client_ops *client_ops; + void *client_ctx; +}; + /* * struct hdcp_lib_handle - handle for hdcp client * @qseecom_handle - for sending commands to qseecom @@ -585,9 +612,10 @@ static void hdcp_lib_stream(struct hdcp_lib_handle *handle); static int hdcp_lib_txmtr_init(struct hdcp_lib_handle *handle); static int hdcp_lib_txmtr_init_legacy(struct hdcp_lib_handle *handle); -static struct qseecom_handle *hdcp1_handle; static struct qseecom_handle *hdcpsrm_handle; +static struct hdcp1_lib_handle *hdcp1_handle; + static bool hdcp1_supported = true; static bool hdcp1_enc_enabled; static struct mutex hdcp1_ta_cmd_lock; @@ -2283,31 +2311,69 @@ static void hdcp_lib_wait_work(struct kthread_work *work) bool hdcp1_check_if_supported_load_app(void) { int rc = 0; + bool hdcp1_srm_supported = true; /* start hdcp1 app */ - if (hdcp1_supported && !hdcp1_handle) { - rc = qseecom_start_app(&hdcp1_handle, HDCP1_APP_NAME, - QSEECOM_SBUFF_SIZE); + if (hdcp1_supported && !hdcp1_handle->qsee_handle) { + rc = qseecom_start_app(&hdcp1_handle->qsee_handle, + HDCP1_APP_NAME, + QSEECOM_SBUFF_SIZE); if (rc) { - pr_err("qseecom_start_app failed %d\n", rc); + pr_err("hdcp1 qseecom_start_app failed %d\n", rc); hdcp1_supported = false; - } else { - mutex_init(&hdcp1_ta_cmd_lock); + kfree(hdcp1_handle); + } + } + + /* if hdcp1 app succeeds load SRM TA as well */ + if (hdcp1_supported && !hdcp1_handle->srm_handle) { + mutex_init(&hdcp1_ta_cmd_lock); + rc = qseecom_start_app(&hdcp1_handle->srm_handle, + SRMAPP_NAME, + QSEECOM_SBUFF_SIZE); + if (rc) { + hdcp1_srm_supported = false; + pr_err("hdcp1_srm qseecom_start_app failed %d\n", rc); } } pr_debug("hdcp1 app %s loaded\n", hdcp1_supported ? "successfully" : "not"); + pr_debug("hdcp1 srm app %s loaded\n", + hdcp1_srm_supported ? "successfully" : "not"); return hdcp1_supported; } +void hdcp1_client_register(void *client_ctx, struct hdcp_client_ops *ops) +{ + /* initialize the hdcp1 handle */ + hdcp1_handle = kzalloc(sizeof(*hdcp1_handle), GFP_KERNEL); + + if (hdcp1_handle) { + hdcp1_handle->client_ops = ops; + hdcp1_handle->client_ctx = client_ctx; + } +} + +void hdcp1_client_unregister(void) +{ + if (hdcp1_handle && hdcp1_handle->qsee_handle) + qseecom_shutdown_app(&hdcp1_handle->qsee_handle); + + if (hdcp1_handle && hdcp1_handle->srm_handle) + qseecom_shutdown_app(&hdcp1_handle->srm_handle); + + kfree(hdcp1_handle); +} + /* APIs exposed to all clients */ int hdcp1_set_keys(uint32_t *aksv_msb, uint32_t *aksv_lsb) { int rc = 0; struct hdcp1_key_set_req *key_set_req; struct hdcp1_key_set_rsp *key_set_rsp; + struct qseecom_handle *hdcp1_qsee_handle; if (aksv_msb == NULL || aksv_lsb == NULL) return -EINVAL; @@ -2315,12 +2381,17 @@ int hdcp1_set_keys(uint32_t *aksv_msb, uint32_t *aksv_lsb) if (!hdcp1_supported || !hdcp1_handle) return -EINVAL; + hdcp1_qsee_handle = hdcp1_handle->qsee_handle; + + if (!hdcp1_qsee_handle) + return -EINVAL; + /* set keys and request aksv */ - key_set_req = (struct hdcp1_key_set_req *)hdcp1_handle->sbuf; + key_set_req = (struct hdcp1_key_set_req *)hdcp1_qsee_handle->sbuf; key_set_req->commandid = HDCP1_SET_KEY_MESSAGE_ID; - key_set_rsp = (struct hdcp1_key_set_rsp *)(hdcp1_handle->sbuf + + key_set_rsp = (struct hdcp1_key_set_rsp *)(hdcp1_qsee_handle->sbuf + QSEECOM_ALIGN(sizeof(struct hdcp1_key_set_req))); - rc = qseecom_send_command(hdcp1_handle, key_set_req, + rc = qseecom_send_command(hdcp1_qsee_handle, key_set_req, QSEECOM_ALIGN(sizeof (struct hdcp1_key_set_req)), key_set_rsp, @@ -2351,6 +2422,85 @@ int hdcp1_set_keys(uint32_t *aksv_msb, uint32_t *aksv_lsb) return 0; } +int hdcp1_validate_receiver_ids(struct hdcp_srm_device_id_t *device_ids, + uint32_t device_id_cnt) +{ + int rc = 0; + struct hdcp_srm_check_device_ids_req *recv_id_req; + struct hdcp_srm_check_device_ids_rsp *recv_id_rsp; + uint32_t sbuf_len; + uint32_t rbuf_len; + int i = 0; + struct qseecom_handle *hdcp1_srmhandle; + + /* If client has not been registered return */ + if (!hdcp1_supported || !hdcp1_handle) + return -EINVAL; + + /* Start the hdcp srm app if not already started */ + if (hdcp1_handle && !hdcp1_handle->srm_handle) { + rc = qseecom_start_app(&hdcp1_handle->srm_handle, + SRMAPP_NAME, QSEECOM_SBUFF_SIZE); + if (rc) { + pr_err("qseecom_start_app failed for SRM TA %d\n", rc); + goto end; + } + } + + pr_debug("device_id_cnt = %d\n", device_id_cnt); + + hdcp1_srmhandle = hdcp1_handle->srm_handle; + + sbuf_len = sizeof(struct hdcp_srm_check_device_ids_req) + + sizeof(struct hdcp_srm_device_id_t) * device_id_cnt + - 1; + + rbuf_len = sizeof(struct hdcp_srm_check_device_ids_rsp); + + /* Create a SRM validate receiver ID request */ + recv_id_req = (struct hdcp_srm_check_device_ids_req *) + hdcp1_srmhandle->sbuf; + recv_id_req->commandid = HDCP_SRM_CMD_CHECK_DEVICE_ID; + recv_id_req->num_device_ids = device_id_cnt; + memcpy(recv_id_req->device_ids, device_ids, + device_id_cnt * sizeof(struct hdcp_srm_device_id_t)); + + for (i = 0; i < device_id_cnt * sizeof(struct hdcp_srm_device_id_t); + i++) { + pr_debug("recv_id_req->device_ids[%d] = 0x%x\n", i, + recv_id_req->device_ids[i]); + } + + recv_id_rsp = (struct hdcp_srm_check_device_ids_rsp *) + (hdcp1_srmhandle->sbuf + + QSEECOM_ALIGN(sbuf_len)); + + rc = qseecom_send_command(hdcp1_srmhandle, + recv_id_req, + QSEECOM_ALIGN(sbuf_len), + recv_id_rsp, + QSEECOM_ALIGN(rbuf_len)); + + if (rc < 0) { + pr_err("qseecom cmd failed err=%d\n", rc); + goto end; + } + + rc = recv_id_rsp->retval; + if (rc) { + pr_err("enc cmd failed, rsp=%d\n", recv_id_rsp->retval); + rc = -EINVAL; + goto end; + } + + pr_debug("rsp=%d\n", recv_id_rsp->retval); + pr_debug("commandid=%d\n", recv_id_rsp->commandid); + +end: + return rc; +} + + static int hdcp_validate_recv_id(struct hdcp_lib_handle *handle) { int rc = 0; @@ -2398,6 +2548,7 @@ int hdcp1_set_enc(bool enable) int rc = 0; struct hdcp1_set_enc_req *set_enc_req; struct hdcp1_set_enc_rsp *set_enc_rsp; + struct qseecom_handle *hdcp1_qsee_handle; mutex_lock(&hdcp1_ta_cmd_lock); @@ -2406,18 +2557,23 @@ int hdcp1_set_enc(bool enable) goto end; } + hdcp1_qsee_handle = hdcp1_handle->qsee_handle; + + if (!hdcp1_qsee_handle) + return -EINVAL; + if (hdcp1_enc_enabled == enable) { pr_info("already %s\n", enable ? "enabled" : "disabled"); goto end; } /* set keys and request aksv */ - set_enc_req = (struct hdcp1_set_enc_req *)hdcp1_handle->sbuf; + set_enc_req = (struct hdcp1_set_enc_req *)hdcp1_qsee_handle->sbuf; set_enc_req->commandid = HDCP1_SET_ENC_MESSAGE_ID; set_enc_req->enable = enable; - set_enc_rsp = (struct hdcp1_set_enc_rsp *)(hdcp1_handle->sbuf + + set_enc_rsp = (struct hdcp1_set_enc_rsp *)(hdcp1_qsee_handle->sbuf + QSEECOM_ALIGN(sizeof(struct hdcp1_set_enc_req))); - rc = qseecom_send_command(hdcp1_handle, set_enc_req, + rc = qseecom_send_command(hdcp1_qsee_handle, set_enc_req, QSEECOM_ALIGN(sizeof (struct hdcp1_set_enc_req)), set_enc_rsp, @@ -2679,6 +2835,8 @@ static ssize_t hdmi_hdcp_srm_updated(struct device *dev, int srm_updated; struct hdcp_lib_handle *handle; ssize_t ret = count; + struct hdcp_client_ops *client_ops; + void *hdcp_client_ctx; handle = hdcp_drv_mgr->handle; @@ -2689,12 +2847,21 @@ static ssize_t hdmi_hdcp_srm_updated(struct device *dev, } if (srm_updated) { - if (hdcp_validate_recv_id(handle)) { - pr_debug("SRM check FAILED\n"); - if (handle && handle->client_ops->srm_cb) - handle->client_ops->srm_cb(handle->client_ctx); - } else { - pr_debug("SRM check PASSED\n"); + if (handle && handle->qseecom_handle) { + client_ops = handle->client_ops; + hdcp_client_ctx = handle->client_ctx; + if (hdcp_validate_recv_id(handle)) { + pr_debug("HDCP 2.2 SRM check FAILED\n"); + if (handle && client_ops->srm_cb) + client_ops->srm_cb(hdcp_client_ctx); + } else + pr_debug("HDCP 2.2 SRM check PASSED\n"); + } else if (hdcp1_handle && hdcp1_handle->qsee_handle) { + pr_debug("HDCP 1.4 SRM check\n"); + hdcp_client_ctx = hdcp1_handle->client_ctx; + client_ops = hdcp1_handle->client_ops; + if (client_ops->srm_cb) + client_ops->srm_cb(hdcp_client_ctx); } } diff --git a/include/linux/hdcp_qseecom.h b/include/linux/hdcp_qseecom.h index 8e1b853e738e..11c2af725bb1 100644 --- a/include/linux/hdcp_qseecom.h +++ b/include/linux/hdcp_qseecom.h @@ -15,6 +15,8 @@ #include #define HDCP_MAX_MESSAGE_PARTS 4 +#define RECV_ID_SIZE 5 +#define MAX_DEVICES_SUPPORTED 127 enum hdcp_lib_wakeup_cmd { HDCP_LIB_WKUP_CMD_INVALID, @@ -115,6 +117,10 @@ static inline char *hdcp_lib_cmd_to_str(uint32_t cmd) } } +struct hdcp_srm_device_id_t { + uint8_t data[RECV_ID_SIZE]; +}; + struct hdcp_txmtr_ops { int (*wakeup)(struct hdcp_lib_wakeup_data *data); bool (*feature_supported)(void *phdcpcontext); @@ -148,6 +154,12 @@ void hdcp_library_deregister(void *phdcpcontext); bool hdcp1_check_if_supported_load_app(void); int hdcp1_set_keys(uint32_t *aksv_msb, uint32_t *aksv_lsb); int hdcp1_set_enc(bool enable); +int hdcp1_validate_receiver_ids(struct hdcp_srm_device_id_t *device_ids, +uint32_t device_id_cnt); void hdcp1_cache_repeater_topology(void *hdcp1_cached_tp); void hdcp1_notify_topology(void); +void hdcp1_client_register(void *client_ctx, +struct hdcp_client_ops *ops); +void hdcp1_client_unregister(void); + #endif /* __HDCP_QSEECOM_H */