lineage_kernel_xcoverpro/drivers/soc/samsung/exynos-hdcp/exynos-hdcp2-teeif.c

539 lines
12 KiB
C
Executable File

/*
* drivers/soc/samsung/exynos-hdcp/exynos-hdcp2-teeif.c
*
* Copyright (c) 2016 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/smc.h>
#include <linux/slab.h>
#include <asm/cacheflush.h>
#include "exynos-hdcp2-teeif.h"
#include "exynos-hdcp2.h"
#include "exynos-hdcp2-log.h"
extern void __dma_inv_area(const void *start, size_t size);
static struct hci_ctx hctx = {
.msg = NULL,
.state = HCI_DISCONNECTED
};
int hdcp_tee_open(void)
{
int ret = 0;
u64 phys_addr = 0;
if (hctx.state == HCI_CONNECTED) {
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;
}
/* send WSM address to SWd */
phys_addr = virt_to_phys((void *)hctx.msg);
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;
}
hctx.state = HCI_CONNECTED;
return 0;
}
int hdcp_tee_close()
{
int ret;
if (hctx.state == HCI_DISCONNECTED) {
hdcp_info("HCI is already disconnected\n");
return 0;
}
if (hctx.msg) {
kfree(hctx.msg);
hctx.msg = NULL;
}
/* 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;
}
hctx.state = HCI_DISCONNECTED;
return 0;
}
int hdcp_tee_comm(struct hci_message *hci)
{
int ret;
if (!hci)
return -EINVAL;
/**
* kernel & TEE does not share cache.
* So, cache should be flushed before sending data to TEE
* and invalidate after come back to kernel
*/
__flush_dcache_area((void *)hci, sizeof(struct hci_message));
ret = exynos_smc(SMC_HDCP_PROT_MSG, 0, 0, 0);
__inval_dcache_area((void *)hci, sizeof(struct hci_message));
if (ret) {
hdcp_info("SWd returned(%x)\n", ret);
return ret;
}
return 0;
}
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;
/* todo: input check */
/* Update TCI buffer */
hci->cmd_id = HDCP_TEEI_GEN_RTX;
hci->genrtx.lk_type = lk_type;
hci->genrtx.len = rtx_len;
if ((ret = hdcp_tee_comm(hci)) < 0)
return ret;
/* check returned message from SWD */
if (rtx && rtx_len)
memcpy(rtx, hci->genrtx.rtx, rtx_len);
if (caps && caps_len)
memcpy(caps, hci->genrtx.tx_caps, caps_len);
return ret;
}
int teei_get_txinfo(uint8_t *version, size_t version_len,
uint8_t *caps_mask, uint32_t caps_mask_len)
{
int ret = 0;
struct hci_message *hci = hctx.msg;
/* todo: input check */
/* Update TCI buffer */
hci->cmd_id = HDCP_TEEI_GET_TXINFO;
/* send command to swd */
if ((ret = hdcp_tee_comm(hci)) < 0)
return ret;
/* check returned message from SWD */
memcpy(version, hci->gettxinfo.version, version_len);
memcpy(caps_mask, hci->gettxinfo.caps_mask, caps_mask_len);
return ret;
}
int teei_set_rxinfo(uint8_t *version, size_t version_len,
uint8_t *caps_mask, uint32_t caps_mask_len)
{
int ret = 0;
struct hci_message *hci = hctx.msg;
/* todo: input check */
/* Update TCI buffer */
hci->cmd_id = HDCP_TEEI_SET_RXINFO;
memcpy(hci->setrxinfo.version, version, version_len);
memcpy(hci->setrxinfo.caps_mask, caps_mask, caps_mask_len);
/* send command to swd */
if ((ret = hdcp_tee_comm(hci)) < 0)
return ret;
return ret;
}
int teei_verify_cert(uint8_t *cert, size_t cert_len,
uint8_t *rrx, size_t rrx_len,
uint8_t *rx_caps, size_t rx_caps_len)
{
int ret = 0;
struct hci_message *hci = hctx.msg;
/* todo: input check */
/* Update TCI buffer */
hci->cmd_id = HDCP_TEEI_VERIFY_CERT;
hci->vfcert.len = cert_len;
memcpy(hci->vfcert.cert, cert, cert_len);
if (rrx && rrx_len)
memcpy(hci->vfcert.rrx, rrx, rrx_len);
if (rx_caps && rx_caps_len)
memcpy(hci->vfcert.rx_caps, rx_caps, rx_caps_len);
if ((ret = hdcp_tee_comm(hci)) < 0)
return ret;
/* return verification result */
return ret;
}
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;
/* todo: input check */
/* Update TCI buffer */
hci->cmd_id = HDCP_TEEI_GEN_MKEY;
hci->genmkey.lk_type = lk_type;
hci->genmkey.emkey_len = emkey_len;
if ((ret = hdcp_tee_comm(hci)) < 0)
return ret;
/* copy encrypted mkey & wrapped mkey to hdcp ctx */
memcpy(emkey, hci->genmkey.emkey, hci->genmkey.emkey_len);
/* check returned message from SWD */
return 0;
}
int teei_set_rrx(uint8_t *rrx, size_t rrx_len)
{
int ret = 0;
struct hci_message *hci = hctx.msg;
/* todo: input check */
/* Update TCI buffer */
hci->cmd_id = HDCP_TEEI_SET_RRX;
memcpy(hci->setrrx.rrx, rrx, rrx_len);
/* send command to swd */
if ((ret = hdcp_tee_comm(hci)) < 0)
return ret;
return ret;
}
int teei_compare_ake_hmac(uint8_t *rx_hmac, size_t rx_hmac_len)
{
int ret = 0;
struct hci_message *hci = hctx.msg;
/* todo: input check */
/* Update TCI buffer */
hci->cmd_id = HDCP_TEEI_COMPARE_AKE_HMAC;
hci->comphmac.rx_hmac_len = rx_hmac_len;
memcpy(hci->comphmac.rx_hmac, rx_hmac, rx_hmac_len);
if ((ret = hdcp_tee_comm(hci)) < 0)
return ret;
return ret;
}
int teei_set_pairing_info(uint8_t *ekh_mkey, size_t ekh_mkey_len)
{
int ret = 0;
struct hci_message *hci = hctx.msg;
/* todo: input check */
/* Update TCI buffer */
hci->cmd_id = HDCP_TEEI_SET_PAIRING_INFO;
memcpy(hci->setpairing.ekh_mkey, ekh_mkey, ekh_mkey_len);
/* send command to swd */
if ((ret = hdcp_tee_comm(hci)) < 0)
return ret;
return ret;
}
int teei_get_pairing_info(uint8_t *ekh_mkey, size_t ekh_mkey_len,
uint8_t *m, size_t m_len)
{
int ret = 0;
struct hci_message *hci = hctx.msg;
/* todo: input check */
/* Update TCI buffer */
hci->cmd_id = HDCP_TEEI_GET_PAIRING_INFO;
/* send command to swd */
if ((ret = hdcp_tee_comm(hci)) < 0)
return ret;
memcpy(ekh_mkey, hci->getpairing.ekh_mkey, ekh_mkey_len);
memcpy(m, hci->getpairing.m, m_len);
return ret;
}
int teei_gen_rn(uint8_t *out, size_t len)
{
int ret = 0;
struct hci_message *hci = hctx.msg;
/* todo: input check */
/* Update TCI buffer */
hci->cmd_id = HDCP_TEEI_GEN_RN;
hci->genrn.len = len;
if ((ret = hdcp_tee_comm(hci)) < 0)
return ret;
/* check returned message from SWD */
memcpy(out, hci->genrn.rn, len);
return ret;
}
int teei_gen_lc_hmac(uint32_t lk_type, uint8_t *lsb16_hmac)
{
int ret = 0;
struct hci_message *hci = hctx.msg;
/* todo: input check */
/* Update TCI buffer */
hci->cmd_id = HDCP_TEEI_GEN_LC_HMAC;
hci->genlchmac.lk_type = lk_type;
if ((ret = hdcp_tee_comm(hci)) < 0)
return ret;
/* Send LSB 16bytes to Rx */
if (lsb16_hmac)
memcpy(lsb16_hmac, hci->genlchmac.lsb16_hmac, (HDCP_HMAC_SHA256_LEN / 2));
/* todo: check returned message from SWD */
return 0;
}
int teei_compare_lc_hmac(uint8_t *rx_hmac, size_t rx_hmac_len)
{
int ret = 0;
struct hci_message *hci = hctx.msg;
/* todo: input check */
/* Update TCI buffer */
hci->cmd_id = HDCP_TEEI_COMPARE_LC_HMAC;
hci->complchmac.rx_hmac_len = rx_hmac_len;
memcpy(hci->complchmac.rx_hmac, rx_hmac, rx_hmac_len);
if ((ret = hdcp_tee_comm(hci)) < 0)
return ret;
return ret;
}
int teei_generate_riv(uint8_t *out, size_t len)
{
int ret = 0;
struct hci_message *hci = hctx.msg;
/* todo: input check */
/* Update TCI buffer */
hci->cmd_id = HDCP_TEEI_GEN_RIV;
hci->genriv.len = len;
if ((ret = hdcp_tee_comm(hci)) < 0)
return ret;
memcpy(out, hci->genriv.riv, len);
/* todo: check returned message from SWD */
return ret;
}
int teei_generate_skey(uint32_t lk_type,
uint8_t *eskey, size_t eskey_len,
int share_skey)
{
int ret = 0;
struct hci_message *hci = hctx.msg;
/* todo: input check */
/* Update TCI buffer */
hci->cmd_id = HDCP_TEEI_GEN_SKEY;
hci->genskey.lk_type = lk_type;
hci->genskey.eskey_len = eskey_len;
hci->genskey.share_skey = share_skey;
if ((ret = hdcp_tee_comm(hci)) < 0)
return ret;
/* copy encrypted mkey & wrapped mkey to hdcp ctx */
memcpy(eskey, hci->genskey.eskey, hci->genskey.eskey_len);
/* todo: check returned message from SWD */
return 0;
}
int teei_encrypt_packet(uint8_t *input, size_t input_len,
uint8_t *output, size_t output_len,
uint8_t *str_ctr, size_t str_ctr_len,
uint8_t *input_ctr, size_t input_ctr_len)
{
int ret = 0;
struct hci_message *hci = hctx.msg;
/* todo: input check */
/* Update TCI buffer */
hci->cmd_id = HDCP_TEEI_ENC_PACKET;
hci->encpacket.input_len = input_len;
hci->encpacket.output_len = output_len;
hci->encpacket.input_addr = (uint64_t)input;
hci->encpacket.output_addr = (uint64_t)output;
memcpy(hci->encpacket.str_ctr, str_ctr, str_ctr_len);
memcpy(hci->encpacket.input_ctr, input_ctr, input_ctr_len);
hdcp_debug("teeif command(%x)\n", hci->cmd_id);
hdcp_debug("input(%llx), output(%llx)\n", hci->encpacket.input_addr, hci->encpacket.output_addr);
if ((ret = hdcp_tee_comm(hci)) < 0)
return ret;
return 0;
}
int teei_set_rcvlist_info(uint8_t *rx_info,
uint8_t *seq_num_v,
uint8_t *v_prime,
uint8_t *rcvid_list,
uint8_t *v,
uint8_t *valid)
{
int ret = 0;
struct hci_message *hci = hctx.msg;
/* todo: input check */
hci->cmd_id = HDCP_TEEI_SET_RCV_ID_LIST;
memcpy(hci->setrcvlist.rcvid_lst, rcvid_list, HDCP_RP_RCVID_LIST_LEN);
memcpy(hci->setrcvlist.v_prime, v_prime, HDCP_RP_HMAC_V_LEN / 2);
/* Only used DP */
if (rx_info != NULL && seq_num_v != NULL) {
memcpy(hci->setrcvlist.rx_info, rx_info, HDCP_RP_RX_INFO_LEN);
memcpy(hci->setrcvlist.seq_num_v, seq_num_v, HDCP_RP_SEQ_NUM_V_LEN);
}
ret = hdcp_tee_comm(hci);
if (ret != 0) {
*valid = 1;
return ret;
}
memcpy(v, hci->setrcvlist.v, HDCP_RP_HMAC_V_LEN / 2);
*valid = 0;
return 0;
}
int teei_gen_stream_manage(uint16_t stream_num,
uint8_t *streamid,
uint8_t *seq_num_m,
uint8_t *k,
uint8_t *streamid_type)
{
int ret = 0;
struct hci_message *hci = hctx.msg;
/* todo: input check */
/* Update TCI buffer */
hci->cmd_id = HDCP_TEEI_GEN_STREAM_MANAGE;
hci->genstrminfo.stream_num = stream_num;
memcpy(hci->genstrminfo.streamid, streamid, sizeof(uint8_t) * stream_num);
if ((ret = hdcp_tee_comm(hci)) < 0)
return ret;
memcpy(seq_num_m, hci->genstrminfo.seq_num_m, HDCP_RP_SEQ_NUM_M_LEN);
memcpy(k, hci->genstrminfo.k, HDCP_RP_K_LEN);
memcpy(streamid_type, hci->genstrminfo.streamid_type, HDCP_RP_STREAMID_TYPE_LEN);
/* check returned message from SWD */
/* return verification result */
return ret;
}
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;
hci->cmd_id = HDCP_TEEI_VERIFY_M_PRIME;
memcpy(hci->verifymprime.m_prime, m_prime, HDCP_RP_HMAC_M_LEN);
if (input && input_len < sizeof(hci->verifymprime.strmsg)) {
memcpy(hci->verifymprime.strmsg, input, input_len);
hci->verifymprime.str_len = input_len;
}
ret = hdcp_tee_comm(hci);
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;
}