summaryrefslogtreecommitdiff
path: root/exynos-hdcp2-teeif.c
diff options
context:
space:
mode:
Diffstat (limited to 'exynos-hdcp2-teeif.c')
-rw-r--r--exynos-hdcp2-teeif.c338
1 files changed, 236 insertions, 102 deletions
diff --git a/exynos-hdcp2-teeif.c b/exynos-hdcp2-teeif.c
index baa8f68..25dcfef 100644
--- a/exynos-hdcp2-teeif.c
+++ b/exynos-hdcp2-teeif.c
@@ -8,8 +8,11 @@
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
-#include <soc/samsung/exynos-smc.h>
+#include <linux/device.h>
+#include <linux/jiffies.h>
+#include <linux/soc/samsung/exynos-smc.h>
#include <linux/slab.h>
+#include <linux/trusty/trusty_ipc.h>
#include <linux/module.h>
#include <asm/cacheflush.h>
#include <linux/dma-mapping.h>
@@ -18,106 +21,258 @@
#include "exynos-hdcp2.h"
#include "exynos-hdcp2-log.h"
-extern struct device *device_hdcp;
-extern void __dma_inv_area(const void *start, size_t size);
+#define TZ_CON_TIMEOUT 5000
+#define TZ_BUF_TIMEOUT 10000
+#define TZ_MSG_TIMEOUT 10000
+#define HDCP_TA_PORT "com.android.trusty.hdcp.auth"
-static struct hci_ctx hctx = {
- .msg = NULL,
- .state = HCI_DISCONNECTED
+struct hdcp_auth_req {
+ uint32_t cmd;
+ int32_t arg;
};
+struct hdcp_auth_rsp {
+ uint32_t cmd;
+ int32_t err;
+ int32_t arg;
+};
+
+struct hdcp_tz_chan_ctx {
+ struct tipc_chan *chan;
+ struct mutex rsp_lock;
+ struct completion reply_comp;
+ struct hdcp_auth_rsp rsp;
+ struct hci_message **msg_ptr;
+};
+static struct hdcp_tz_chan_ctx hdcp_ta_ctx;
+
+static size_t deduce_num_payload(size_t msg_len)
+{
+ switch (msg_len) {
+ case (sizeof(struct hdcp_auth_rsp)):
+ return 1;
+ case (sizeof(struct hdcp_auth_rsp) + sizeof(struct hci_message)):
+ return 2;
+ default:
+ return 0;
+ }
+}
+static struct tipc_msg_buf *tz_srv_handle_msg(void *data,
+ struct tipc_msg_buf* rxbuf)
+{
+ struct hdcp_tz_chan_ctx *ctx = data;
+ size_t len;
+ size_t payload_num;
+
+ len = mb_avail_data(rxbuf);
+ payload_num = deduce_num_payload(len);
+
+ if (payload_num == 0) {
+ hdcp_err("TZ: invalid RSP buffer size (%zd)\n", len);
+ }
+
+ if (payload_num > 0) {
+ memcpy(&ctx->rsp,
+ mb_get_data(rxbuf, sizeof(struct hdcp_auth_rsp)),
+ sizeof(struct hdcp_auth_rsp));
+ }
+ if (payload_num > 1 && ctx->msg_ptr) {
+ memcpy(*ctx->msg_ptr,
+ mb_get_data(rxbuf, sizeof(struct hci_message)),
+ sizeof(struct hci_message));
+ }
+
+ complete(&ctx->reply_comp);
+
+ return rxbuf;
+}
+
+static void tz_srv_handle_event(void *data, int event)
+{
+ struct hdcp_tz_chan_ctx *ctx = data;
+ complete(&ctx->reply_comp);
+}
+
+static const struct tipc_chan_ops tz_srv_ops = {
+ .handle_msg = tz_srv_handle_msg,
+ .handle_event = tz_srv_handle_event,
+};
+
+void hdcp_tee_init(void)
+{
+ init_completion(&hdcp_ta_ctx.reply_comp);
+ mutex_init(&hdcp_ta_ctx.rsp_lock);
+ hdcp_ta_ctx.msg_ptr = NULL;
+}
+
int hdcp_tee_open(void)
{
int ret = 0;
- u64 phys_addr = 0;
+ struct tipc_chan *chan;
- if (hctx.state == HCI_CONNECTED) {
+ if (hdcp_ta_ctx.chan) {
hdcp_info("HCI is already connected\n");
return 0;
}
- /* Allocate WSM for HDCP commnad interface */
- hctx.msg = (struct hci_message *)kzalloc(sizeof(struct hci_message), GFP_KERNEL);
- if (!hctx.msg) {
- hdcp_err("alloc wsm for HDCP SWd is failed\n");
- return -ENOMEM;
+ chan = tipc_create_channel(NULL, &tz_srv_ops, &hdcp_ta_ctx);
+ if (IS_ERR(chan)) {
+ hdcp_err("TZ: failed (%ld) to create chan\n", PTR_ERR(chan));
+ return PTR_ERR(chan);
}
- /* send WSM address to SWd */
- phys_addr = virt_to_phys((void *)hctx.msg);
+ reinit_completion(&hdcp_ta_ctx.reply_comp);
+
+ ret = tipc_chan_connect(chan, HDCP_TA_PORT);
+ if (ret < 0) {
+ hdcp_err("TZ: failed (%d) to connect\n", ret);
+ tipc_chan_destroy(chan);
+ return ret;
+ }
- ret = exynos_smc(SMC_HDCP_INIT, phys_addr, sizeof(struct hci_message), 0);
- if (ret) {
- hdcp_err("Fail to set up connection with SWd. ret(%d)\n", ret);
- kfree(hctx.msg);
- hctx.msg = NULL;
- return -ECONNREFUSED;
+ hdcp_ta_ctx.chan = chan;
+ ret = wait_for_completion_timeout(&hdcp_ta_ctx.reply_comp,
+ msecs_to_jiffies(TZ_CON_TIMEOUT));
+ if (ret <= 0) {
+ ret = (!ret) ? -ETIMEDOUT : ret;
+ hdcp_err("TZ: failed (%d) to wait for connect\n", ret);
+ hdcp_tee_close();
+ return ret;
}
- hctx.state = HCI_CONNECTED;
+ return 0;
+}
+
+int hdcp_tee_close(void)
+{
+ if (!hdcp_ta_ctx.chan) {
+ hdcp_info("HCI is already disconnected\n");
+ return 0;
+ }
+ tipc_chan_shutdown(hdcp_ta_ctx.chan);
+ tipc_chan_destroy(hdcp_ta_ctx.chan);
+ hdcp_ta_ctx.chan = NULL;
return 0;
}
-int hdcp_tee_close()
+static int hdcp_tee_comm_xchg_internal(uint32_t cmd, int32_t arg,
+ int32_t *rsp, struct hci_message *hci)
{
int ret;
+ struct tipc_msg_buf *txbuf;
+ struct hdcp_auth_req auth_req;
- if (hctx.state == HCI_DISCONNECTED) {
- hdcp_info("HCI is already disconnected\n");
- return 0;
+ ret = hdcp_tee_open();
+ if (ret)
+ return ret;
+
+ txbuf = tipc_chan_get_txbuf_timeout(hdcp_ta_ctx.chan, TZ_BUF_TIMEOUT);
+ if (IS_ERR(txbuf)) {
+ hdcp_err("TZ: failed (%ld) to get txbuf\n", PTR_ERR(txbuf));
+ return PTR_ERR(txbuf);
+ }
+
+ auth_req.cmd = cmd;
+ auth_req.arg = arg;
+ memcpy(mb_put_data(txbuf, sizeof(struct hdcp_auth_req)),
+ &auth_req, sizeof(struct hdcp_auth_req));
+
+ if (hci) {
+ memcpy(mb_put_data(txbuf, sizeof(struct hci_message)),
+ hci, sizeof(struct hci_message));
+ hdcp_ta_ctx.msg_ptr = &hci;
+ } else {
+ hdcp_ta_ctx.msg_ptr = NULL;
+ }
+
+ reinit_completion(&hdcp_ta_ctx.reply_comp);
+
+ ret = tipc_chan_queue_msg(hdcp_ta_ctx.chan, txbuf);
+ if (ret < 0) {
+ hdcp_err("TZ: failed(%d) to queue msg\n", ret);
+ tipc_chan_put_txbuf(hdcp_ta_ctx.chan, txbuf);
+ hdcp_tee_close();
+ return ret;
}
- if (hctx.msg) {
- kfree(hctx.msg);
- hctx.msg = NULL;
+ ret = wait_for_completion_timeout(&hdcp_ta_ctx.reply_comp,
+ msecs_to_jiffies(TZ_MSG_TIMEOUT));
+ if (ret <= 0) {
+ ret = (!ret) ? -ETIMEDOUT : ret;
+ hdcp_err("TZ: failed (%d) to wait for reply\n", ret);
+ hdcp_tee_close();
+ return ret;
}
- /* send terminate command to SWd */
- ret = exynos_smc(SMC_HDCP_TERMINATE, 0, 0, 0);
- if (ret) {
- hdcp_err("HDCP: Fail to set up connection with SWd. ret(%d)\n", ret);
- return -EFAULT;
+ if (hdcp_ta_ctx.rsp.cmd != (cmd | HDCP_CMD_AUTH_RESP)) {
+ hdcp_err("TZ: hdcp had an unexpected rsp cmd (%x vs %x)",
+ hdcp_ta_ctx.rsp.cmd, cmd | HDCP_CMD_AUTH_RESP);
+ return -EIO;
}
- hctx.state = HCI_DISCONNECTED;
+ if (hdcp_ta_ctx.rsp.err) {
+ hdcp_err("TZ: hdcp had an unexpected rsp err (%d)",
+ hdcp_ta_ctx.rsp.err);
+ return -EIO;
+ }
+ if (rsp)
+ *rsp = hdcp_ta_ctx.rsp.arg;
return 0;
}
-int hdcp_tee_comm(struct hci_message *hci)
-{
+static int hdcp_tee_comm_xchg(uint32_t cmd, int32_t arg, int32_t *rsp,
+ struct hci_message *hci) {
int ret;
+ int retries = 2;
+
+ mutex_lock(&hdcp_ta_ctx.rsp_lock);
+ while (retries) {
+ retries--;
+ ret = hdcp_tee_comm_xchg_internal(cmd, arg, rsp, hci);
+ if (!ret) {
+ mutex_unlock(&hdcp_ta_ctx.rsp_lock);
+ return 0;
+ }
+ }
+ mutex_unlock(&hdcp_ta_ctx.rsp_lock);
- if (!hci)
- return -EINVAL;
+ return ret;
+}
- /**
- * kernel & TEE does not share cache.
- * So, cache should be flushed before sending data to TEE
- * and invalidate after come back to kernel
- */
+static irq_hw_number_t saved_hwirq = 0;
- // mark on 5.4 __flush_dcache_area((void *)hci, sizeof(struct hci_message));
- dma_map_single(device_hdcp, (void *)hci, sizeof(struct hci_message), DMA_TO_DEVICE);
- ret = exynos_smc(SMC_HDCP_PROT_MSG, 0, 0, 0);
- // mark on 5.4 __inval_dcache_area((void *)hci, sizeof(struct hci_message));
- dma_map_single(device_hdcp, (void *)hci, sizeof(struct hci_message), DMA_FROM_DEVICE);
+int hdcp_tee_send_cmd(uint32_t cmd) {
+ if (HDCP_CMD_AUTH_START == cmd)
+ hdcp_tee_comm_xchg(HDCP_CMD_NOTIFY_INTR_NUM, saved_hwirq, NULL, NULL);
+ return hdcp_tee_comm_xchg(cmd, 0, NULL, NULL);
+}
- if (ret) {
- hdcp_info("SWd returned(%x)\n", ret);
- return ret;
- }
+int hdcp_tee_check_protection(int* version) {
+ return hdcp_tee_comm_xchg(HDCP_CMD_PROTECTION_CHECK, 0, version, NULL);
+}
+int hdcp_tee_notify_intr_num(irq_hw_number_t hwirq) {
+ saved_hwirq = hwirq;
return 0;
}
+int hdcp_tee_set_test_mode(bool enable) {
+ return hdcp_tee_comm_xchg(HDCP_CMD_SET_TEST_MODE, enable, NULL, NULL);
+}
+
+static int hdcp_tee_comm(struct hci_message *hci) {
+ return hdcp_tee_comm_xchg(HDCP_CMD_PROTOCOL, 0, NULL, hci);
+}
+
int teei_gen_rtx(uint32_t lk_type,
uint8_t *rtx, size_t rtx_len,
uint8_t *caps, uint32_t caps_len)
{
int ret = 0;
- struct hci_message *hci = hctx.msg;
+ struct hci_message msg;
+ struct hci_message *hci = &msg;
/* todo: input check */
@@ -143,7 +298,8 @@ int teei_verify_cert(uint8_t *cert, size_t cert_len,
uint8_t *rx_caps, size_t rx_caps_len)
{
int ret = 0;
- struct hci_message *hci = hctx.msg;
+ struct hci_message msg;
+ struct hci_message *hci = &msg;
/* todo: input check */
/* Update TCI buffer */
@@ -165,7 +321,8 @@ int teei_verify_cert(uint8_t *cert, size_t cert_len,
int teei_generate_master_key(uint32_t lk_type, uint8_t *emkey, size_t emkey_len)
{
int ret = 0;
- struct hci_message *hci = hctx.msg;
+ struct hci_message msg;
+ struct hci_message *hci = &msg;
/* todo: input check */
/* Update TCI buffer */
@@ -187,7 +344,8 @@ int teei_generate_master_key(uint32_t lk_type, uint8_t *emkey, size_t emkey_len)
int teei_compare_ake_hmac(uint8_t *rx_hmac, size_t rx_hmac_len)
{
int ret = 0;
- struct hci_message *hci = hctx.msg;
+ struct hci_message msg;
+ struct hci_message *hci = &msg;
/* todo: input check */
/* Update TCI buffer */
@@ -204,7 +362,8 @@ int teei_compare_ake_hmac(uint8_t *rx_hmac, size_t rx_hmac_len)
int teei_set_pairing_info(uint8_t *ekh_mkey, size_t ekh_mkey_len)
{
int ret = 0;
- struct hci_message *hci = hctx.msg;
+ struct hci_message msg;
+ struct hci_message *hci = &msg;
/* todo: input check */
@@ -220,10 +379,12 @@ int teei_set_pairing_info(uint8_t *ekh_mkey, size_t ekh_mkey_len)
}
int teei_get_pairing_info(uint8_t *ekh_mkey, size_t ekh_mkey_len,
- uint8_t *m, size_t m_len)
+ uint8_t *m, size_t m_len,
+ int *found)
{
int ret = 0;
- struct hci_message *hci = hctx.msg;
+ struct hci_message msg;
+ struct hci_message *hci = &msg;
/* todo: input check */
@@ -236,6 +397,7 @@ int teei_get_pairing_info(uint8_t *ekh_mkey, size_t ekh_mkey_len,
memcpy(ekh_mkey, hci->getpairing.ekh_mkey, ekh_mkey_len);
memcpy(m, hci->getpairing.m, m_len);
+ *found = hci->getpairing.found ? 1 : 0;
return ret;
}
@@ -243,7 +405,8 @@ int teei_get_pairing_info(uint8_t *ekh_mkey, size_t ekh_mkey_len,
int teei_gen_rn(uint8_t *out, size_t len)
{
int ret = 0;
- struct hci_message *hci = hctx.msg;
+ struct hci_message msg;
+ struct hci_message *hci = &msg;
/* todo: input check */
@@ -264,7 +427,8 @@ int teei_gen_rn(uint8_t *out, size_t len)
int teei_compare_lc_hmac(uint8_t *rx_hmac, size_t rx_hmac_len)
{
int ret = 0;
- struct hci_message *hci = hctx.msg;
+ struct hci_message msg;
+ struct hci_message *hci = &msg;
/* todo: input check */
/* Update TCI buffer */
@@ -281,7 +445,8 @@ int teei_compare_lc_hmac(uint8_t *rx_hmac, size_t rx_hmac_len)
int teei_generate_riv(uint8_t *out, size_t len)
{
int ret = 0;
- struct hci_message *hci = hctx.msg;
+ struct hci_message msg;
+ struct hci_message *hci = &msg;
/* todo: input check */
@@ -304,7 +469,8 @@ int teei_generate_skey(uint32_t lk_type,
int share_skey)
{
int ret = 0;
- struct hci_message *hci = hctx.msg;
+ struct hci_message msg;
+ struct hci_message *hci = &msg;
/* todo: input check */
/* Update TCI buffer */
@@ -332,7 +498,8 @@ int teei_set_rcvlist_info(uint8_t *rx_info,
uint8_t *valid)
{
int ret = 0;
- struct hci_message *hci = hctx.msg;
+ struct hci_message msg;
+ struct hci_message *hci = &msg;
/* todo: input check */
hci->cmd_id = HDCP_TEEI_SET_RCV_ID_LIST;
@@ -364,7 +531,8 @@ int teei_gen_stream_manage(uint16_t stream_num,
uint8_t *streamid_type)
{
int ret = 0;
- struct hci_message *hci = hctx.msg;
+ struct hci_message msg;
+ struct hci_message *hci = &msg;
/* todo: input check */
/* Update TCI buffer */
@@ -389,7 +557,8 @@ int teei_gen_stream_manage(uint16_t stream_num,
int teei_verify_m_prime(uint8_t *m_prime, uint8_t *input, size_t input_len)
{
int ret = 0;
- struct hci_message *hci = hctx.msg;
+ struct hci_message msg;
+ struct hci_message *hci = &msg;
hci->cmd_id = HDCP_TEEI_VERIFY_M_PRIME;
memcpy(hci->verifymprime.m_prime, m_prime, HDCP_RP_HMAC_M_LEN);
@@ -402,38 +571,3 @@ int teei_verify_m_prime(uint8_t *m_prime, uint8_t *input, size_t input_len)
return ret;
}
-
-int teei_wrapped_key(uint8_t *key, uint32_t wrapped, uint32_t key_len)
-{
- int ret = 0;
- struct hci_message *hci = hctx.msg;
-
- hci->cmd_id = HDCP_TEEI_WRAP_KEY;
-
- if (key_len > (sizeof(hci->wrap_key.enc_key) - HDCP_WRAP_AUTH_TAG)) {
- hdcp_err("(un)wraaping key size is wrong. 0x%x\n", key_len);
- return HDCP_ERROR_WRONG_SIZE;
- }
-
- if (key_len < HDCP_WRAP_AUTH_TAG || key_len > HDCP_WRAP_MAX_SIZE - HDCP_WRAP_AUTH_TAG)
- return HDCP_ERROR_WRONG_SIZE;
-
- if (wrapped == UNWRAP)
- memcpy(hci->wrap_key.enc_key, key, key_len + HDCP_WRAP_AUTH_TAG);
- else
- memcpy(hci->wrap_key.key, key, key_len);
-
- hci->wrap_key.wrapped = wrapped;
- hci->wrap_key.key_len = key_len;
-
- if ((ret = hdcp_tee_comm(hci)) < 0)
- return ret;
-
- if (hci->wrap_key.wrapped == WRAP) {
- memcpy(key, hci->wrap_key.enc_key, key_len + HDCP_WRAP_AUTH_TAG);
- }
-
- return ret;
-}
-
-MODULE_LICENSE("GPL");