/* * 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 #include #include #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; }