// SPDX-License-Identifier: GPL-2.0-only /* * SPDX-FileCopyrightText: Copyright (c) 2019-2025 NVIDIA CORPORATION & AFFILIATES. * All rights reserved. * * Cryptographic API. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "tegra-hv-vse.h" #define SE_MAX_SCHEDULE_TIMEOUT LONG_MAX #define TEGRA_HV_VSE_SHA_MAX_LL_NUM_1 1 #define TEGRA_HV_VSE_AES_CMAC_MAX_LL_NUM 1 #define TEGRA_HV_VSE_MAX_TASKS_PER_SUBMIT 1 #define TEGRA_HV_VSE_MAX_TSEC_TASKS_PER_SUBMIT 1 #define TEGRA_HV_VSE_TIMEOUT (msecs_to_jiffies(10000)) #define TEGRA_HV_VSE_SHA_MAX_BLOCK_SIZE 128 #define TEGRA_VIRTUAL_SE_AES_BLOCK_SIZE 16 #define TEGRA_VIRTUAL_SE_AES_GCM_TAG_SIZE 16 #define TEGRA_VIRTUAL_SE_AES_GCM_TAG_IV_SIZE 32 #define TEGRA_VIRTUAL_SE_AES_MIN_KEY_SIZE 16 #define TEGRA_VIRTUAL_SE_AES_MAX_KEY_SIZE 32 #define TEGRA_VIRTUAL_SE_AES_IV_SIZE 16 #define TEGRA_VIRTUAL_SE_AES_GCM_IV_SIZE 12 #define TEGRA_VIRTUAL_SE_AES_MAX_IV_SIZE TEGRA_VIRTUAL_SE_AES_IV_SIZE /* Virtual Security Engines */ #define TEGRA_VIRTUAL_SE_CMD_ENG_AES 0x01000000U #define TEGRA_VIRTUAL_SE_CMD_ENG_SHA 0x02000000U #define TEGRA_VIRTUAL_SE_CMD_ENG_TSEC 0x03000000U /* Command categories for AES Engine */ #define TEGRA_VIRTUAL_SE_CMD_CATEGORY_ENC_DEC 0x00010000U #define TEGRA_VIRTUAL_SE_CMD_CATEGORY_AUTH 0x00020000U #define TEGRA_VIRTUAL_SE_CMD_CATEGORY_RNG 0x00030000U /* Command categories for SHA Engine */ #define TEGRA_VIRTUAL_SE_CMD_CATEGORY_SHA 0x00010000U #define TEGRA_VIRTUAL_SE_CMD_CATEGORY_HMAC 0x00030000U /* Command categories for TSEC Engine */ #define TEGRA_VIRTUAL_SE_CMD_CATEGORY_TSEC_KEYS 0x00010000U #define TEGRA_VIRTUAL_SE_CMD_CATEGORY_TSEC_AUTH 0x00020000U /* Command sets for Encryption/Decryption with AES Engine */ #define TEGRA_VIRTUAL_SE_CMD_SET_AES_ENC_DEC 0x00001000U #define TEGRA_VIRTUAL_SE_CMD_SET_GCM_ENC_DEC 0x00002000U /* Command sets for Authentication using AES engine */ #define TEGRA_VIRTUAL_SE_CMD_SET_CMAC 0x00001000U #define TEGRA_VIRTUAL_SE_CMD_SET_GMAC 0x00002000U /* Commands in the AES Encryption/Decryption set */ #define TEGRA_VIRTUAL_SE_CMD_OP_AES_ENC_INIT 0x00000001U #define TEGRA_VIRTUAL_SE_CMD_OP_AES_ENC 0x00000002U #define TEGRA_VIRTUAL_SE_CMD_OP_AES_DEC 0x00000003U /* Commands in the GCM Encryption/Decryption set */ #define TEGRA_VIRTUAL_SE_CMD_OP_GCM_ENC 0x00000001U #define TEGRA_VIRTUAL_SE_CMD_OP_GCM_DEC 0x00000002U #define TEGRA_VIRTUAL_SE_CMD_OP_GCM_GET_DEC 0x00000003U /* Commands in the CMAC Authentication set*/ #define TEGRA_VIRTUAL_SE_CMD_OP_CMAC_SIGN 0x00000001U #define TEGRA_VIRTUAL_SE_CMD_OP_CMAC_VERIFY 0x00000002U #define TEGRA_VIRTUAL_SE_CMD_OP_CMAC_GET_SIGN 0x00000003U #define TEGRA_VIRTUAL_SE_CMD_OP_CMAC_GET_VERIFY 0x00000004U /* Commands in the GMAC Authentication set */ #define TEGRA_VIRTUAL_SE_CMD_OP_GMAC_INIT 0x00000001U #define TEGRA_VIRTUAL_SE_CMD_OP_GMAC_SIGN 0x00000002U #define TEGRA_VIRTUAL_SE_CMD_OP_GMAC_VERIFY 0x00000003U #define TEGRA_VIRTUAL_SE_CMD_OP_GMAC_GET_VERIFY 0x00000004U #define TEGRA_VIRTUAL_SE_CMD_OP_GMAC_GET_IV 0x00000005U /* Commands in the AES RNG Category*/ #define TEGRA_VIRTUAL_SE_CMD_OP_AES_RNG 0x00000001U /* Commands in the SHA Category */ #define TEGRA_VIRTUAL_SE_CMD_OP_SHA 0x00000001U /* Commands in the HMAC Category */ #define TEGRA_VIRTUAL_SE_CMD_OP_HMAC_SIGN 0x00000001U #define TEGRA_VIRTUAL_SE_CMD_OP_HMAC_VERIFY 0x00000002U #define TEGRA_VIRTUAL_SE_CMD_OP_HMAC_GET_VERIFY 0x00000004U /* Commands in the TSEC keys category */ #define TEGRA_VIRTUAL_SE_CMD_OP_TSEC_KEYLOAD_STATUS 0x00000001U /* Commands in the TSEC Authentication category */ #define TEGRA_VIRTUAL_SE_CMD_OP_TSEC_CMAC_SIGN \ TEGRA_VIRTUAL_SE_CMD_OP_CMAC_SIGN #define TEGRA_VIRTUAL_SE_CMD_OP_TSEC_GET_CMAC_SIGN \ TEGRA_VIRTUAL_SE_CMD_OP_CMAC_GET_SIGN #define TEGRA_VIRTUAL_SE_CMD_OP_TSEC_CMAC_VERIFY \ TEGRA_VIRTUAL_SE_CMD_OP_CMAC_VERIFY #define TEGRA_VIRTUAL_SE_CMD_OP_TSEC_GET_CMAC_VERIFY \ TEGRA_VIRTUAL_SE_CMD_OP_CMAC_GET_VERIFY #define TEGRA_VIRTUAL_SE_CMD_AES_SET_KEY 0xF1 #define TEGRA_VIRTUAL_SE_CMD_AES_ALLOC_KEY 0xF0 #define TEGRA_VIRTUAL_SE_CMD_AES_ENCRYPT_INIT (TEGRA_VIRTUAL_SE_CMD_ENG_AES \ | TEGRA_VIRTUAL_SE_CMD_CATEGORY_ENC_DEC \ | TEGRA_VIRTUAL_SE_CMD_SET_AES_ENC_DEC \ | TEGRA_VIRTUAL_SE_CMD_OP_AES_ENC_INIT) #define TEGRA_VIRTUAL_SE_CMD_AES_ENCRYPT (TEGRA_VIRTUAL_SE_CMD_ENG_AES \ | TEGRA_VIRTUAL_SE_CMD_CATEGORY_ENC_DEC \ | TEGRA_VIRTUAL_SE_CMD_SET_AES_ENC_DEC \ | TEGRA_VIRTUAL_SE_CMD_OP_AES_ENC) #define TEGRA_VIRTUAL_SE_CMD_AES_DECRYPT (TEGRA_VIRTUAL_SE_CMD_ENG_AES \ | TEGRA_VIRTUAL_SE_CMD_CATEGORY_ENC_DEC \ | TEGRA_VIRTUAL_SE_CMD_SET_AES_ENC_DEC \ | TEGRA_VIRTUAL_SE_CMD_OP_AES_DEC) #define TEGRA_VIRTUAL_SE_CMD_AES_CMAC 0x23 #define TEGRA_VIRTUAL_SE_CMD_AES_CMAC_GEN_SUBKEY 0x24 #define TEGRA_VIRTUAL_SE_CMD_AES_RNG_DBRG (TEGRA_VIRTUAL_SE_CMD_ENG_AES \ | TEGRA_VIRTUAL_SE_CMD_CATEGORY_RNG \ | TEGRA_VIRTUAL_SE_CMD_OP_AES_RNG) #define TEGRA_VIRTUAL_SE_CMD_AES_GCM_CMD_ENCRYPT (TEGRA_VIRTUAL_SE_CMD_ENG_AES \ | TEGRA_VIRTUAL_SE_CMD_CATEGORY_ENC_DEC \ | TEGRA_VIRTUAL_SE_CMD_SET_GCM_ENC_DEC \ | TEGRA_VIRTUAL_SE_CMD_OP_GCM_ENC) #define TEGRA_VIRTUAL_SE_CMD_AES_GCM_CMD_DECRYPT (TEGRA_VIRTUAL_SE_CMD_ENG_AES \ | TEGRA_VIRTUAL_SE_CMD_CATEGORY_ENC_DEC \ | TEGRA_VIRTUAL_SE_CMD_SET_GCM_ENC_DEC \ | TEGRA_VIRTUAL_SE_CMD_OP_GCM_DEC) #define TEGRA_VIRTUAL_SE_CMD_AES_CMD_GET_GCM_DEC (TEGRA_VIRTUAL_SE_CMD_ENG_AES \ | TEGRA_VIRTUAL_SE_CMD_CATEGORY_ENC_DEC \ | TEGRA_VIRTUAL_SE_CMD_SET_GCM_ENC_DEC \ | TEGRA_VIRTUAL_SE_CMD_OP_GCM_GET_DEC) #define TEGRA_VIRTUAL_SE_CMD_AES_CMAC_SIGN (TEGRA_VIRTUAL_SE_CMD_ENG_AES \ | TEGRA_VIRTUAL_SE_CMD_CATEGORY_AUTH \ | TEGRA_VIRTUAL_SE_CMD_SET_CMAC \ | TEGRA_VIRTUAL_SE_CMD_OP_CMAC_SIGN) #define TEGRA_VIRTUAL_SE_CMD_AES_CMAC_VERIFY (TEGRA_VIRTUAL_SE_CMD_ENG_AES \ | TEGRA_VIRTUAL_SE_CMD_CATEGORY_AUTH \ | TEGRA_VIRTUAL_SE_CMD_SET_CMAC \ | TEGRA_VIRTUAL_SE_CMD_OP_CMAC_VERIFY) #define TEGRA_VIRTUAL_SE_CMD_AES_CMD_GET_CMAC_SIGN (TEGRA_VIRTUAL_SE_CMD_ENG_AES \ | TEGRA_VIRTUAL_SE_CMD_CATEGORY_AUTH \ | TEGRA_VIRTUAL_SE_CMD_SET_CMAC \ | TEGRA_VIRTUAL_SE_CMD_OP_CMAC_GET_SIGN) #define TEGRA_VIRTUAL_SE_CMD_AES_CMD_GET_CMAC_VERIFY (TEGRA_VIRTUAL_SE_CMD_ENG_AES \ | TEGRA_VIRTUAL_SE_CMD_CATEGORY_AUTH \ | TEGRA_VIRTUAL_SE_CMD_SET_CMAC \ | TEGRA_VIRTUAL_SE_CMD_OP_CMAC_GET_VERIFY) #define TEGRA_VIRTUAL_SE_CMD_AES_GMAC_CMD_INIT (TEGRA_VIRTUAL_SE_CMD_ENG_AES \ | TEGRA_VIRTUAL_SE_CMD_CATEGORY_AUTH \ | TEGRA_VIRTUAL_SE_CMD_SET_GMAC \ | TEGRA_VIRTUAL_SE_CMD_OP_GMAC_INIT) #define TEGRA_VIRTUAL_SE_CMD_AES_GMAC_CMD_SIGN (TEGRA_VIRTUAL_SE_CMD_ENG_AES \ | TEGRA_VIRTUAL_SE_CMD_CATEGORY_AUTH \ | TEGRA_VIRTUAL_SE_CMD_SET_GMAC \ | TEGRA_VIRTUAL_SE_CMD_OP_GMAC_SIGN) #define TEGRA_VIRTUAL_SE_CMD_AES_GMAC_CMD_VERIFY (TEGRA_VIRTUAL_SE_CMD_ENG_AES \ | TEGRA_VIRTUAL_SE_CMD_CATEGORY_AUTH \ | TEGRA_VIRTUAL_SE_CMD_SET_GMAC \ | TEGRA_VIRTUAL_SE_CMD_OP_GMAC_VERIFY) #define TEGRA_VIRTUAL_SE_CMD_AES_CMD_GET_GMAC_IV (TEGRA_VIRTUAL_SE_CMD_ENG_AES \ | TEGRA_VIRTUAL_SE_CMD_CATEGORY_AUTH \ | TEGRA_VIRTUAL_SE_CMD_SET_GMAC \ | TEGRA_VIRTUAL_SE_CMD_OP_GMAC_GET_IV) #define TEGRA_VIRTUAL_SE_CMD_AES_CMD_GET_GMAC_VERIFY (TEGRA_VIRTUAL_SE_CMD_ENG_AES \ | TEGRA_VIRTUAL_SE_CMD_CATEGORY_AUTH \ | TEGRA_VIRTUAL_SE_CMD_SET_GMAC \ | TEGRA_VIRTUAL_SE_CMD_OP_GMAC_GET_VERIFY) #define TEGRA_VIRTUAL_TSEC_CMD_GET_KEYLOAD_STATUS (TEGRA_VIRTUAL_SE_CMD_ENG_TSEC \ | TEGRA_VIRTUAL_SE_CMD_CATEGORY_TSEC_KEYS \ | TEGRA_VIRTUAL_SE_CMD_OP_TSEC_KEYLOAD_STATUS) #define TEGRA_VIRTUAL_SE_CMD_TSEC_SIGN (TEGRA_VIRTUAL_SE_CMD_ENG_TSEC \ | TEGRA_VIRTUAL_SE_CMD_CATEGORY_TSEC_AUTH \ | TEGRA_VIRTUAL_SE_CMD_OP_TSEC_CMAC_SIGN) #define TEGRA_VIRTUAL_SE_CMD_TSEC_VERIFY (TEGRA_VIRTUAL_SE_CMD_ENG_TSEC \ | TEGRA_VIRTUAL_SE_CMD_CATEGORY_TSEC_AUTH \ | TEGRA_VIRTUAL_SE_CMD_OP_TSEC_CMAC_VERIFY) #define TEGRA_VIRTUAL_SE_CMD_AES_CMD_GET_TSEC_VERIFY (TEGRA_VIRTUAL_SE_CMD_ENG_TSEC \ | TEGRA_VIRTUAL_SE_CMD_CATEGORY_TSEC_AUTH \ | TEGRA_VIRTUAL_SE_CMD_OP_TSEC_GET_CMAC_VERIFY) #define TEGRA_VIRTUAL_SE_AES_GMAC_SV_CFG_FIRST_REQ_SHIFT (0x00U) #define TEGRA_VIRTUAL_SE_AES_GMAC_SV_CFG_LAST_REQ_SHIFT (0x01U) #define TEGRA_VIRTUAL_SE_CMD_SHA_HASH (TEGRA_VIRTUAL_SE_CMD_ENG_SHA \ | TEGRA_VIRTUAL_SE_CMD_CATEGORY_SHA \ | TEGRA_VIRTUAL_SE_CMD_OP_SHA) #define TEGRA_VIRTUAL_SE_CMD_HMAC_SIGN (TEGRA_VIRTUAL_SE_CMD_ENG_SHA \ | TEGRA_VIRTUAL_SE_CMD_CATEGORY_HMAC \ | TEGRA_VIRTUAL_SE_CMD_OP_HMAC_SIGN) #define TEGRA_VIRTUAL_SE_CMD_HMAC_VERIFY (TEGRA_VIRTUAL_SE_CMD_ENG_SHA \ | TEGRA_VIRTUAL_SE_CMD_CATEGORY_HMAC \ | TEGRA_VIRTUAL_SE_CMD_OP_HMAC_VERIFY) #define TEGRA_VIRTUAL_SE_CMD_HMAC_GET_VERIFY (TEGRA_VIRTUAL_SE_CMD_ENG_SHA \ | TEGRA_VIRTUAL_SE_CMD_CATEGORY_HMAC \ | TEGRA_VIRTUAL_SE_CMD_OP_HMAC_GET_VERIFY) #define TEGRA_VIRTUAL_SE_SHA_HASH_BLOCK_SIZE_512BIT (512 / 8) #define TEGRA_VIRTUAL_SE_SHA_HASH_BLOCK_SIZE_576BIT (576 / 8) #define TEGRA_VIRTUAL_SE_SHA_HASH_BLOCK_SIZE_832BIT (832 / 8) #define TEGRA_VIRTUAL_SE_SHA_HASH_BLOCK_SIZE_1024BIT (1024 / 8) #define TEGRA_VIRTUAL_SE_SHA_HASH_BLOCK_SIZE_1088BIT (1088 / 8) #define TEGRA_VIRTUAL_SE_SHA_HASH_BLOCK_SIZE_1344BIT (1344 / 8) #define TEGRA_VIRTUAL_SE_SHA_MAX_HMAC_SHA_LENGTH (32U) #define SHA3_STATE_SIZE 200 #define TEGRA_VIRTUAL_SE_TIMEOUT_1S 1000000 #define TEGRA_VIRTUAL_SE_AES_CMAC_DIGEST_SIZE 16 #define TEGRA_VIRTUAL_SE_AES_CMAC_STATE_SIZE 16 #define TEGRA_VIRTUAL_SE_MAX_BUFFER_SIZE 0x1000000 #define TEGRA_VIRTUAL_SE_AES_KEYTBL_TYPE_KEY 1 #define TEGRA_VIRTUAL_SE_AES_KEYTBL_TYPE_OIV 2 #define TEGRA_VIRTUAL_SE_AES_KEYTBL_TYPE_UIV 4 #define TEGRA_VIRTUAL_SE_AES_KEYSLOT_LABEL_SIZE 16 #define TEGRA_VIRTUAL_SE_AES_KEYSLOT_LABEL "NVSEAES" #define TEGRA_VIRTUAL_SE_AES_LCTR_SIZE 16 #define TEGRA_VIRTUAL_SE_AES_LCTR_CNTN 1 #define TEGRA_VIRTUAL_SE_AES_CMAC_CONFIG_NONLASTBLK 0x00 #define TEGRA_VIRTUAL_SE_AES_CMAC_CONFIG_LASTBLK 0x01 #define TEGRA_VIRTUAL_SE_AES_CMAC_CONFIG_FINAL 0x02 #define TEGRA_VIRTUAL_SE_AES_CMAC_SV_CONFIG_FIRSTREQ 0x01 #define TEGRA_VIRTUAL_SE_AES_CMAC_SV_CONFIG_LASTREQ 0x02 #define TEGRA_VIRTUAL_SE_RNG_IV_SIZE 16 #define TEGRA_VIRTUAL_SE_RNG_DT_SIZE 16 #define TEGRA_VIRTUAL_SE_RNG_KEY_SIZE 16 #define TEGRA_VIRTUAL_SE_RNG_SEED_SIZE (TEGRA_VIRTUAL_SE_RNG_IV_SIZE + \ TEGRA_VIRTUAL_SE_RNG_KEY_SIZE + \ TEGRA_VIRTUAL_SE_RNG_DT_SIZE) #define TEGRA_VIRTUAL_SE_MAX_SUPPORTED_BUFLEN ((1U << 24) - 1U) #define TEGRA_VIRTUAL_SE_MAX_GCMDEC_BUFLEN (0x500000U) /* 5 MB */ #define TEGRA_VIRTUAL_TSEC_MAX_SUPPORTED_BUFLEN (8U * 1024U) /* 8 KB */ #define TEGRA_VIRTUAL_SE_ERR_MAC_INVALID 11 #define MAX_NUMBER_MISC_DEVICES 46U #define MAX_IVC_Q_PRIORITY 2U #define TEGRA_IVC_ID_OFFSET 0U #define TEGRA_SE_ENGINE_ID_OFFSET 1U #define TEGRA_CRYPTO_DEV_ID_OFFSET 2U #define TEGRA_IVC_PRIORITY_OFFSET 3U #define TEGRA_MAX_BUFFER_SIZE 4U #define TEGRA_CHANNEL_GROUPID_OFFSET 5U #define TEGRA_GCM_SUPPORTED_FLAG_OFFSET 7U #define TEGRA_GCM_DEC_BUFFER_SIZE 8U #define TEGRA_GCM_DEC_MEMPOOL_ID 9U #define TEGRA_GCM_DEC_MEMPOOL_SIZE 10U #define TEGRA_IVCCFG_ARRAY_LEN 11U #define VSE_MSG_ERR_TSEC_KEYLOAD_FAILED 21U #define VSE_MSG_ERR_TSEC_KEYLOAD_STATUS_CHECK_TIMEOUT 20U #define NVVSE_STATUS_SE_SERVER_TSEC_KEYLOAD_FAILED 105U #define NVVSE_STATUS_SE_SERVER_TSEC_KEYLOAD_TIMEOUT 150U #define NVVSE_STATUS_SE_SERVER_ERROR 102U #define SE_HW_VALUE_MATCH_CODE 0x5A5A5A5A #define SE_HW_VALUE_MISMATCH_CODE 0xBDBDBDBD #define RESULT_COMPARE_BUF_SIZE 4U #define AES_TAG_BUF_SIZE 64U #define SHA_HASH_BUF_SIZE 1024U #define NVVSE_TSEC_CMD_STATUS_ERR_MASK ((uint32_t)0xFFFFFFU) static struct crypto_dev_to_ivc_map g_crypto_to_ivc_map[MAX_NUMBER_MISC_DEVICES]; static struct tegra_vse_node_dma g_node_dma[MAX_NUMBER_MISC_DEVICES]; static bool gcm_supports_dma; static struct device *gpcdma_dev; /* Security Engine Linked List */ struct tegra_virtual_se_ll { dma_addr_t addr; /* DMA buffer address */ u32 data_len; /* Data length in DMA buffer */ }; struct tegra_vse_tag { unsigned int *priv_data; }; /* Tegra Virtual Security Engine commands */ enum tegra_virtual_se_command { VIRTUAL_SE_AES_CRYPTO, VIRTUAL_SE_KEY_SLOT, VIRTUAL_SE_PROCESS, VIRTUAL_CMAC_PROCESS, VIRTUAL_SE_AES_GCM_ENC_PROCESS }; enum rng_call { HW_RNG = 0x5A5A5A5A, CRYPTODEV_RNG = 0xABABABAB }; /* CMAC response */ struct tegra_vse_cmac_data { u8 status; u8 data[TEGRA_VIRTUAL_SE_AES_BLOCK_SIZE]; }; /* * @enum vse_sym_cipher_choice * @brief Symmetric cipher to be used for CMAC sign/verify * Currently two choices are supported - AES, SM4. */ enum vse_sym_cipher_choice { VSE_SYM_CIPH_AES = 0, VSE_SYM_CIPH_SM4 = 0xFFFFFFFF }; struct tegra_vse_priv_data { struct skcipher_request *req; struct tegra_virtual_se_dev *se_dev; struct completion alg_complete; int cmd; int slot_num; struct scatterlist sg; void *buf; dma_addr_t buf_addr; u32 rx_status; u8 iv[TEGRA_VIRTUAL_SE_AES_MAX_IV_SIZE]; struct tegra_vse_cmac_data cmac; uint32_t syncpt_id; uint32_t syncpt_threshold; uint32_t syncpt_id_valid; }; struct tegra_virtual_se_addr { u32 lo; u32 hi; }; struct tegra_virtual_se_addr64_buf_size { u64 addr; u32 buf_size; }; union tegra_virtual_se_aes_args { struct keyiv { u8 slot[KEYSLOT_SIZE_BYTES]; u32 length; u32 type; u8 data[32]; u8 oiv[TEGRA_VIRTUAL_SE_AES_IV_SIZE]; u8 uiv[TEGRA_VIRTUAL_SE_AES_IV_SIZE]; } key; struct aes_encdec { u8 keyslot[KEYSLOT_SIZE_BYTES]; u32 mode; u32 ivsel; u8 lctr[TEGRA_VIRTUAL_SE_AES_LCTR_SIZE]; u32 ctr_cntn; u64 src_addr; u32 src_buf_size; u64 dst_addr; u32 dst_buf_size; u32 key_length; } op; struct aes_gcm { /** * keyslot handle returned by TOS as part of load key operation. * It must be the first variable in the structure. */ uint8_t keyslot[KEYSLOT_SIZE_BYTES]; uint64_t dst_addr; uint32_t dst_buf_size; uint64_t src_addr; uint32_t src_buf_size; uint64_t aad_addr; uint32_t aad_buf_size; uint64_t tag_addr; uint32_t tag_buf_size; /* TODO: ESLC-6207: use lctr instead*/ uint8_t iv[12]; /** * Key length in bytes. * * Supported key length is 16 bytes */ uint32_t key_length; /* Config for AES-GMAC request */ uint32_t config; u8 expected_tag[TEGRA_VIRTUAL_SE_AES_BLOCK_SIZE]; uint64_t gcm_vrfy_res_addr; enum vse_sym_cipher_choice sym_ciph; } op_gcm; struct aes_cmac_sv { u8 keyslot[KEYSLOT_SIZE_BYTES]; u32 config; u32 lastblock_len; u8 lastblock[TEGRA_VIRTUAL_SE_AES_BLOCK_SIZE]; u64 src_addr; u32 src_buf_size; u32 key_length; u8 cmac_result[TEGRA_VIRTUAL_SE_AES_BLOCK_SIZE]; u64 mac_addr; u64 mac_comp_res_addr; enum vse_sym_cipher_choice sym_ciph; } op_cmac_sv; struct aes_rng { struct tegra_virtual_se_addr dst_addr; } op_rng; }; union tegra_virtual_se_sha_args { struct hash { u32 msg_total_length[4]; u32 msg_left_length[4]; u32 hash[50]; u64 dst; u64 src_addr; u32 src_buf_size; u32 mode; u32 hash_length; } op_hash; } __attribute__((__packed__)); struct tegra_virtual_se_hmac_sha_args { u8 keyslot[KEYSLOT_SIZE_BYTES]; u32 mode; u32 lastblock_len; u8 lastblock[TEGRA_VIRTUAL_SE_SHA_HASH_BLOCK_SIZE_512BIT]; u32 msg_total_length[4]; u32 msg_left_length[4]; u64 dst_addr; u64 src_addr; u32 src_buf_size; u8 expected_hmac_sha[TEGRA_VIRTUAL_SE_SHA_MAX_HMAC_SHA_LENGTH]; uint64_t hmac_addr; }; struct tegra_virtual_tsec_args { /** * Keyslot index for keyslot containing TSEC key */ uint64_t keyslot; /** * IOVA address of the input buffer. * Although it is a 64-bit integer, only least significant 40 bits are * used because only a 40-bit address space is supported. */ uint64_t src_addr; /** * IOVA address of the output buffer. * It is expected to point to a buffer with size at least 16 bytes. * Although it is a 64-bit integer, only least significant 40 bits are * used because only a 40-bit address space is supported. */ uint64_t dst_addr; /** * IOVA address of the buffer for status returned by TSEC firmware. * It is expected to point to a buffer with size at least 4 bytes * Although it is a 64-bit integer, only least significant 40 bits are * used because only a 40-bit address space is supported. */ uint64_t fw_status_addr; /** * Size of input buffer in bytes. * The maximum size is given by the macro TEGRA_VIRTUAL_TSEC_MAX_SUPPORTED_BUFLEN */ uint32_t src_buf_size; /** * For CMAC Verify, this array contains the value to be verified. * Not used for CMAC Sign. */ uint8_t cmac_result[TEGRA_VIRTUAL_SE_AES_BLOCK_SIZE]; }; struct tegra_virtual_se_ivc_resp_msg_t { u32 tag; u32 cmd; u32 status; union { /** The init vector of AES-CBC encryption */ unsigned char iv[TEGRA_VIRTUAL_SE_AES_IV_SIZE]; /** Hash result for AES CMAC */ unsigned char cmac_result[TEGRA_VIRTUAL_SE_AES_CMAC_DIGEST_SIZE]; /** Keyslot for non */ unsigned char keyslot; }; uint32_t syncpt_id; uint32_t syncpt_threshold; uint32_t syncpt_id_valid; }; struct tegra_virtual_se_ivc_tx_msg_t { u32 tag; u32 cmd; union { union tegra_virtual_se_aes_args aes; union tegra_virtual_se_sha_args sha; struct tegra_virtual_tsec_args tsec[TEGRA_HV_VSE_MAX_TSEC_TASKS_PER_SUBMIT]; struct tegra_virtual_se_hmac_sha_args hmac; }; }; struct tegra_virtual_se_ivc_hdr_t { u8 header_magic[4]; u32 num_reqs; u32 engine; u8 tag[0x10]; u32 status; }; struct tegra_virtual_se_ivc_msg_t { struct tegra_virtual_se_ivc_hdr_t ivc_hdr; union { struct tegra_virtual_se_ivc_tx_msg_t tx[TEGRA_HV_VSE_MAX_TASKS_PER_SUBMIT]; struct tegra_virtual_se_ivc_resp_msg_t rx[TEGRA_HV_VSE_MAX_TSEC_TASKS_PER_SUBMIT]; }; }; struct sha_zero_length_vector { unsigned int size; char *digest; }; /* Tegra Virtual Security Engine operation modes */ enum tegra_virtual_se_op_mode { /* (SM3-256) mode */ VIRTUAL_SE_OP_MODE_SM3 = 0, /* Secure Hash Algorithm-256 (SHA256) mode */ VIRTUAL_SE_OP_MODE_SHA256 = 5, /* Secure Hash Algorithm-384 (SHA384) mode */ VIRTUAL_SE_OP_MODE_SHA384, /* Secure Hash Algorithm-512 (SHA512) mode */ VIRTUAL_SE_OP_MODE_SHA512, /* Secure Hash Algorithm-3 (SHA3-256) mode */ VIRTUAL_SE_OP_MODE_SHA3_256 = 10, /* Secure Hash Algorithm-3 (SHA3-384) mode */ VIRTUAL_SE_OP_MODE_SHA3_384, /* Secure Hash Algorithm-3 (SHA3-512) mode */ VIRTUAL_SE_OP_MODE_SHA3_512, /* Secure Hash Algorithm-3 (SHAKE128) mode */ VIRTUAL_SE_OP_MODE_SHAKE128, /* Secure Hash Algorithm-3 (SHAKE256) mode */ VIRTUAL_SE_OP_MODE_SHAKE256, }; enum tegra_virtual_se_aes_op_mode { AES_CBC = 0U, AES_CTR = 2U, AES_SM4_CBC = 0x10000U, AES_SM4_CTR = 0x10002U, }; /* Security Engine request context */ struct tegra_virtual_se_aes_req_context { /* Security Engine device */ struct tegra_virtual_se_dev *se_dev; /* Security Engine operation mode */ enum tegra_virtual_se_aes_op_mode op_mode; /* Operation type */ bool encrypt; /* Engine id */ u8 engine_id; }; enum se_engine_id { VIRTUAL_SE_AES0, VIRTUAL_SE_AES1, VIRTUAL_SE_SHA = 2, VIRTUAL_SE_TSEC = 6, VIRTUAL_GCSE1_AES0 = 7, VIRTUAL_GCSE1_AES1 = 8, VIRTUAL_GCSE1_SHA = 9, VIRTUAL_GCSE2_AES0 = 10, VIRTUAL_GCSE2_AES1 = 11, VIRTUAL_GCSE2_SHA = 12, VIRTUAL_MAX_SE_ENGINE_NUM = 13 }; enum tegra_virtual_se_aes_iv_type { AES_ORIGINAL_IV, AES_UPDATED_IV, AES_IV_REG }; enum aes_buf_idx { AES_SRC_BUF_IDX, AES_AAD_BUF_IDX, AES_TAG_BUF_IDX, AES_COMP_BUF_IDX }; enum sha_buf_idx { SHA_SRC_BUF_IDX, SHA_HASH_BUF_IDX, HMAC_SHA_COMP_BUF_IDX }; enum tsec_buf_idx { TSEC_SRC_BUF_IDX, TSEC_MAC_BUF_IDX, TSEC_FW_STATUS_BUF_IDX }; struct crypto_dev_to_ivc_map *tegra_hv_vse_get_db(void) { return &g_crypto_to_ivc_map[0]; } EXPORT_SYMBOL(tegra_hv_vse_get_db); static int status_to_errno(u32 err) { int32_t ret = 0; switch (err) { case 0: ret = 0; break; case 1: /* VSE_MSG_ERR_INVALID_CMD */ case 3: /* VSE_MSG_ERR_INVALID_ARGS */ case 11: /* VSE_MSG_ERR_MAC_INVALID */ ret = -EINVAL; break; case 4: /* VSE_MSG_ERR_INVALID_KEY */ case 5: /* VSE_MSG_ERR_CTR_OVERFLOW */ case 6: /* VSE_MSG_ERR_INVALID_SUBKEY */ case 7: /* VSE_MSG_ERR_CTR_NONCE_INVALID */ case 8: /* VSE_MSG_ERR_GCM_IV_INVALID */ case 9: /* VSE_MSG_ERR_GCM_NONCE_INVALID */ case 10: /* VSE_MSG_ERR_GMAC_INVALID_PARAMS */ ret = -EPERM; break; default: ret = -EINVAL; break; } return ret; } static int32_t validate_header( struct tegra_virtual_se_dev *se_dev, struct tegra_virtual_se_ivc_hdr_t *pivc_hdr, bool *is_dummy) { int32_t ret = -EIO; if ((pivc_hdr->header_magic[0] == (uint8_t)'N') && (pivc_hdr->header_magic[1] == (uint8_t)'V') && (pivc_hdr->header_magic[2] == (uint8_t)'D') && (pivc_hdr->header_magic[3] == (uint8_t)'A')) { pr_debug("Message header\n"); *is_dummy = false; ret = 0; } else if ((pivc_hdr->header_magic[0] == (uint8_t)'D') && (pivc_hdr->header_magic[1] == (uint8_t)'I') && (pivc_hdr->header_magic[2] == (uint8_t)'S') && (pivc_hdr->header_magic[3] == (uint8_t)'C')) { pr_debug("Filler\n"); *is_dummy = true; ret = 0; } else { dev_err(se_dev->dev, "Invalid message header value.\n"); } return ret; } static int is_aes_mode_valid(uint32_t opmode) { int ret = 0; if ((opmode == (uint32_t)AES_CBC) || (opmode == (uint32_t)AES_SM4_CBC) || (opmode == (uint32_t)AES_SM4_CTR) || (opmode == (uint32_t)AES_CTR)) { ret = 1; } return ret; } static int read_and_validate_dummy_msg( struct tegra_virtual_se_dev *se_dev, struct tegra_hv_ivc_cookie *pivck, uint32_t node_id, bool *is_dummy) { int err = 0, read_size = -1; struct tegra_virtual_se_ivc_msg_t *ivc_msg; struct tegra_virtual_se_ivc_hdr_t *ivc_hdr; size_t size_ivc_msg = sizeof(struct tegra_virtual_se_ivc_msg_t); ivc_msg = devm_kzalloc(se_dev->dev, size_ivc_msg, GFP_KERNEL); if (!ivc_msg) return -ENOMEM; read_size = tegra_hv_ivc_read(pivck, ivc_msg, size_ivc_msg); if (read_size > 0 && read_size < size_ivc_msg) { devm_kfree(se_dev->dev, ivc_msg); dev_err(se_dev->dev, "Wrong read msg len %d\n", read_size); return -EINVAL; } ivc_hdr = &(ivc_msg->ivc_hdr); err = validate_header(se_dev, ivc_hdr, is_dummy); devm_kfree(se_dev->dev, ivc_msg); return err; } static int read_and_validate_valid_msg( struct tegra_virtual_se_dev *se_dev, struct tegra_hv_ivc_cookie *pivck, uint32_t node_id, bool *is_dummy, bool waited) { struct tegra_vse_tag *p_dat; struct tegra_vse_priv_data *priv; struct tegra_virtual_se_ivc_msg_t *ivc_msg; struct tegra_virtual_se_ivc_hdr_t *ivc_hdr; struct tegra_virtual_se_aes_req_context *req_ctx; struct tegra_virtual_se_ivc_resp_msg_t *ivc_rx; enum ivc_irq_state *irq_state; int read_size = -1, err = 0; size_t size_ivc_msg = sizeof(struct tegra_virtual_se_ivc_msg_t); irq_state = &(g_crypto_to_ivc_map[node_id].wait_interrupt); if (!tegra_hv_ivc_can_read(pivck)) { *irq_state = INTERMEDIATE_REQ_INTERRUPT; dev_info(se_dev->dev, "%s(): no valid message, await interrupt.\n", __func__); return -EAGAIN; } ivc_msg = devm_kzalloc(se_dev->dev, size_ivc_msg, GFP_KERNEL); if (!ivc_msg) return -ENOMEM; read_size = tegra_hv_ivc_read(pivck, ivc_msg, size_ivc_msg); if (read_size > 0 && read_size < size_ivc_msg) { dev_err(se_dev->dev, "Wrong read msg len %d\n", read_size); err = -EINVAL; goto deinit; } ivc_hdr = &(ivc_msg->ivc_hdr); err = validate_header(se_dev, ivc_hdr, is_dummy); if (err != 0) goto deinit; if (*is_dummy) { dev_err(se_dev->dev, "%s(): Wrong response sequence\n", __func__); goto deinit; } p_dat = (struct tegra_vse_tag *)ivc_msg->ivc_hdr.tag; priv = (struct tegra_vse_priv_data *)p_dat->priv_data; if (!priv) { pr_err("%s no call back info\n", __func__); goto deinit; } priv->syncpt_id = ivc_msg->rx[0].syncpt_id; priv->syncpt_threshold = ivc_msg->rx[0].syncpt_threshold; priv->syncpt_id_valid = ivc_msg->rx[0].syncpt_id_valid; switch (priv->cmd) { case VIRTUAL_SE_AES_CRYPTO: priv->rx_status = ivc_msg->rx[0].status; req_ctx = skcipher_request_ctx(priv->req); if ((!priv->rx_status) && (req_ctx->encrypt == true) && (is_aes_mode_valid(req_ctx->op_mode) == 1)) { memcpy(priv->iv, ivc_msg->rx[0].iv, TEGRA_VIRTUAL_SE_AES_IV_SIZE); } break; case VIRTUAL_SE_KEY_SLOT: ivc_rx = &ivc_msg->rx[0]; priv->slot_num = ivc_rx->keyslot; break; case VIRTUAL_SE_PROCESS: ivc_rx = &ivc_msg->rx[0]; priv->rx_status = ivc_rx->status; break; case VIRTUAL_CMAC_PROCESS: ivc_rx = &ivc_msg->rx[0]; priv->rx_status = ivc_rx->status; priv->cmac.status = ivc_rx->status; if (!ivc_rx->status) { memcpy(priv->cmac.data, ivc_rx->cmac_result, TEGRA_VIRTUAL_SE_AES_CMAC_DIGEST_SIZE); } break; case VIRTUAL_SE_AES_GCM_ENC_PROCESS: ivc_rx = &ivc_msg->rx[0]; priv->rx_status = ivc_rx->status; if (!ivc_rx->status) memcpy(priv->iv, ivc_rx->iv, TEGRA_VIRTUAL_SE_AES_GCM_IV_SIZE); break; default: dev_err(se_dev->dev, "Unknown command\n"); waited = false; } if (waited) complete(&priv->alg_complete); deinit: devm_kfree(se_dev->dev, ivc_msg); return err; } static int tegra_hv_vse_safety_send_ivc( struct tegra_virtual_se_dev *se_dev, struct tegra_hv_ivc_cookie *pivck, void *pbuf, int length) { u32 timeout; int err = 0; timeout = TEGRA_VIRTUAL_SE_TIMEOUT_1S; while (tegra_hv_ivc_channel_notified(pivck) != 0) { if (!timeout) { dev_err(se_dev->dev, "ivc reset timeout\n"); return -EINVAL; } udelay(1); timeout--; } timeout = TEGRA_VIRTUAL_SE_TIMEOUT_1S; while (tegra_hv_ivc_can_write(pivck) == 0) { if (!timeout) { dev_err(se_dev->dev, "ivc send message timeout\n"); return -EINVAL; } udelay(1); timeout--; } if (length > sizeof(struct tegra_virtual_se_ivc_msg_t)) { dev_err(se_dev->dev, "Wrong write msg len %d\n", length); return -E2BIG; } err = tegra_hv_ivc_write(pivck, pbuf, length); if (err < 0) { dev_err(se_dev->dev, "ivc write error!!! error=%d\n", err); return err; } return 0; } static int tegra_hv_vse_safety_send_ivc_wait( struct tegra_virtual_se_dev *se_dev, struct tegra_hv_ivc_cookie *pivck, struct tegra_vse_priv_data *priv, void *pbuf, int length, uint32_t node_id) { struct host1x_syncpt *sp; struct host1x *host1x; int err; bool is_dummy = false; u64 time_left; enum ivc_irq_state *irq_state; mutex_lock(&g_crypto_to_ivc_map[node_id].se_ivc_lock); if (!se_dev->host1x_pdev) { dev_err(se_dev->dev, "host1x pdev not initialized\n"); err = -ENODATA; goto exit; } host1x = platform_get_drvdata(se_dev->host1x_pdev); if (!host1x) { dev_err(se_dev->dev, "No platform data for host1x!\n"); err = -ENODATA; goto exit; } /* Return error if engine is in suspended state */ if (atomic_read(&se_dev->se_suspended)) { err = -ENODEV; goto exit; } err = tegra_hv_vse_safety_send_ivc(se_dev, pivck, pbuf, length); if (err) { dev_err(se_dev->dev, "\n %s send ivc failed %d\n", __func__, err); goto exit; } mutex_lock(&(g_crypto_to_ivc_map[node_id].irq_state_lock)); irq_state = &(se_dev->crypto_to_ivc_map[node_id].wait_interrupt); if (*irq_state == NO_INTERRUPT) { err = read_and_validate_dummy_msg(se_dev, pivck, node_id, &is_dummy); if (err != 0) { dev_err(se_dev->dev, "Failed to read and validate dummy message.\n"); mutex_unlock(&(g_crypto_to_ivc_map[node_id].irq_state_lock)); goto exit; } if (is_dummy) { err = read_and_validate_valid_msg(se_dev, pivck, node_id, &is_dummy, false); if (err != 0 && err != -EAGAIN) { dev_err(se_dev->dev, "Failed to read & validate valid message.\n"); mutex_unlock(&(g_crypto_to_ivc_map[node_id].irq_state_lock)); goto exit; } mutex_unlock(&(g_crypto_to_ivc_map[node_id].irq_state_lock)); if (err == -EAGAIN) { err = 0; pr_debug("%s(): wait_interrupt = %u", __func__, *irq_state); time_left = wait_for_completion_timeout(&priv->alg_complete, TEGRA_HV_VSE_TIMEOUT); if (time_left == 0) { dev_err(se_dev->dev, "%s timeout\n", __func__); err = -ETIMEDOUT; goto exit; } } pr_debug("%s(): wait_interrupt = %u", __func__, *irq_state); } else { dev_err(se_dev->dev, "%s(): Invalid resonse sequence, expected dummy message.\n", __func__); mutex_unlock(&(g_crypto_to_ivc_map[node_id].irq_state_lock)); goto exit; } } else { mutex_unlock(&(g_crypto_to_ivc_map[node_id].irq_state_lock)); time_left = wait_for_completion_timeout(&priv->alg_complete, TEGRA_HV_VSE_TIMEOUT); if (time_left == 0) { dev_err(se_dev->dev, "%s timeout\n", __func__); err = -ETIMEDOUT; goto exit; } } /* If this is not last request then wait using nvhost API*/ if (priv->syncpt_id_valid) { sp = host1x_syncpt_get_by_id_noref(host1x, priv->syncpt_id); if (!sp) { dev_err(se_dev->dev, "No syncpt for syncpt id %d\n", priv->syncpt_id); err = -ENODATA; goto exit; } err = host1x_syncpt_wait(sp, priv->syncpt_threshold, (u32)SE_MAX_SCHEDULE_TIMEOUT, NULL); if (err) { dev_err(se_dev->dev, "timed out for syncpt %u threshold %u err %d\n", priv->syncpt_id, priv->syncpt_threshold, err); err = -ETIMEDOUT; goto exit; } } exit: mutex_unlock(&g_crypto_to_ivc_map[node_id].se_ivc_lock); return err; } static const struct tegra_vse_dma_buf *tegra_hv_vse_get_dma_buf( uint32_t node_id, uint32_t buf_idx, uint32_t buf_size) { if (buf_idx >= MAX_SE_DMA_BUFS) { pr_err("%s offset invalid\n", __func__); return NULL; } if (node_id >= MAX_NUMBER_MISC_DEVICES) { pr_err("%s node_id invalid\n", __func__); return NULL; } if (buf_size > g_node_dma[node_id].se_dma_buf[buf_idx].buf_len) { pr_err("%s requested buffer size is too large\n", __func__); return NULL; } return &g_node_dma[node_id].se_dma_buf[buf_idx]; } static int tegra_vse_validate_hmac_sha_params(struct tegra_virtual_se_hmac_sha_context *hmac_ctx, bool is_last) { if ((hmac_ctx->user_src_buf_size == 0) || (hmac_ctx->user_src_buf_size > TEGRA_VIRTUAL_SE_MAX_SUPPORTED_BUFLEN)) { pr_err("%s: input buffer size is invalid\n", __func__); return -EINVAL; } if (hmac_ctx->node_id >= MAX_NUMBER_MISC_DEVICES) { pr_err("%s: Node id is not valid\n", __func__); return -EINVAL; } if (hmac_ctx->digest_size == 0) { pr_err("%s: Digest size is not valid\n", __func__); return -EINVAL; } if (!hmac_ctx->is_key_slot_allocated) { pr_err("%s key is not allocated\n", __func__); return -EINVAL; } if (!is_last) { if (hmac_ctx->mode == VIRTUAL_SE_OP_MODE_SHA256) { if (hmac_ctx->user_src_buf_size % TEGRA_VIRTUAL_SE_SHA_HASH_BLOCK_SIZE_512BIT != 0) { pr_err("%s: non-last buffer size is invalid\n", __func__); return -EINVAL; } } } if (hmac_ctx->user_src_buf == NULL) { pr_err("%s: src buf is NULL\n", __func__); return -EINVAL; } return 0; } static int tegra_vse_validate_aes_param(struct tegra_virtual_se_aes_context *aes_ctx) { if (aes_ctx->node_id >= MAX_NUMBER_MISC_DEVICES) { pr_err("%s: Node id is not valid\n", __func__); return -EINVAL; } if (aes_ctx->user_src_buf == NULL) { pr_err("%s: src buf is NULL\n", __func__); return -EINVAL; } if (!aes_ctx->is_key_slot_allocated) { pr_err("AES Key slot not allocated\n"); return -EINVAL; } if (aes_ctx->user_dst_buf == NULL) { pr_err("%s: dst buf is NULL\n", __func__); return -EINVAL; } if (aes_ctx->user_src_buf_size == 0 || (aes_ctx->user_src_buf_size > TEGRA_VIRTUAL_SE_MAX_SUPPORTED_BUFLEN)) { pr_err("%s: src buffer size is invalid\n", __func__); return -EINVAL; } return 0; } static int tegra_vse_validate_cmac_params(struct tegra_virtual_se_aes_cmac_context *cmac_ctx, bool is_last) { if (cmac_ctx->node_id >= MAX_NUMBER_MISC_DEVICES) { pr_err("%s: Node id is not valid\n", __func__); return -EINVAL; } if (cmac_ctx->user_src_buf == NULL) { pr_err("%s: src buf is NULL\n", __func__); return -EINVAL; } if (!cmac_ctx->req_context_initialized) { pr_err("%s Request ctx not initialized\n", __func__); return -EINVAL; } if (cmac_ctx->user_src_buf_size <= 0 || (cmac_ctx->user_src_buf_size > TEGRA_VIRTUAL_SE_MAX_SUPPORTED_BUFLEN)) { pr_err("%s: src buffer size is invalid\n", __func__); return -EINVAL; } if (!cmac_ctx->is_key_slot_allocated) { pr_err("%s key is not allocated\n", __func__); return -EINVAL; } if (cmac_ctx->user_mac_buf == NULL) { pr_err("%s: mac buf is NULL\n", __func__); return -EINVAL; } return 0; } static int tegra_vse_validate_aes_rng_param(struct tegra_virtual_se_rng_context *rng_ctx) { if (rng_ctx == NULL) return -EINVAL; if (rng_ctx->node_id >= MAX_NUMBER_MISC_DEVICES) { pr_err("%s: Node id is not valid\n", __func__); return -EINVAL; } return 0; } static int tegra_hv_vse_safety_sha_init(struct ahash_request *req) { struct crypto_ahash *tfm; struct tegra_virtual_se_req_context *req_ctx; struct tegra_virtual_se_sha_context *sha_ctx; struct tegra_virtual_se_dev *se_dev; uint32_t engine_id; if (!req) { pr_err("%s: SHA request invalid\n", __func__); return -EINVAL; } req_ctx = ahash_request_ctx(req); if (!req_ctx) { pr_err("%s: SHA req_ctx not valid\n", __func__); return -EINVAL; } tfm = crypto_ahash_reqtfm(req); if (!tfm) { pr_err("%s: SHA transform not valid\n", __func__); return -EINVAL; } sha_ctx = crypto_ahash_ctx(tfm); engine_id = g_crypto_to_ivc_map[sha_ctx->node_id].engine_id; se_dev = g_crypto_to_ivc_map[sha_ctx->node_id].se_dev; /* Return error if engine is in suspended state */ if (atomic_read(&se_dev->se_suspended)) return -ENODEV; if (strcmp(crypto_ahash_alg_name(tfm), "sha256-vse") == 0) { sha_ctx->mode = VIRTUAL_SE_OP_MODE_SHA256; sha_ctx->blk_size = TEGRA_VIRTUAL_SE_SHA_HASH_BLOCK_SIZE_512BIT; sha_ctx->intermediate_digest_size = SHA256_DIGEST_SIZE; sha_ctx->digest_size = crypto_ahash_digestsize(tfm); } else if (strcmp(crypto_ahash_alg_name(tfm), "sha384-vse") == 0) { sha_ctx->mode = VIRTUAL_SE_OP_MODE_SHA384; sha_ctx->blk_size = TEGRA_VIRTUAL_SE_SHA_HASH_BLOCK_SIZE_1024BIT; /* * The intermediate digest size of SHA384 is same as SHA512 */ sha_ctx->intermediate_digest_size = SHA512_DIGEST_SIZE; sha_ctx->digest_size = crypto_ahash_digestsize(tfm); } else if (strcmp(crypto_ahash_alg_name(tfm), "sha512-vse") == 0) { sha_ctx->mode = VIRTUAL_SE_OP_MODE_SHA512; sha_ctx->blk_size = TEGRA_VIRTUAL_SE_SHA_HASH_BLOCK_SIZE_1024BIT; sha_ctx->intermediate_digest_size = SHA512_DIGEST_SIZE; sha_ctx->digest_size = crypto_ahash_digestsize(tfm); } else if (strcmp(crypto_ahash_alg_name(tfm), "sha3-256-vse") == 0) { sha_ctx->mode = VIRTUAL_SE_OP_MODE_SHA3_256; sha_ctx->blk_size = TEGRA_VIRTUAL_SE_SHA_HASH_BLOCK_SIZE_1088BIT; sha_ctx->intermediate_digest_size = SHA3_STATE_SIZE; sha_ctx->digest_size = crypto_ahash_digestsize(tfm); } else if (strcmp(crypto_ahash_alg_name(tfm), "sha3-384-vse") == 0) { sha_ctx->mode = VIRTUAL_SE_OP_MODE_SHA3_384; sha_ctx->blk_size = TEGRA_VIRTUAL_SE_SHA_HASH_BLOCK_SIZE_832BIT; sha_ctx->intermediate_digest_size = SHA3_STATE_SIZE; sha_ctx->digest_size = crypto_ahash_digestsize(tfm); } else if (strcmp(crypto_ahash_alg_name(tfm), "sha3-512-vse") == 0) { sha_ctx->mode = VIRTUAL_SE_OP_MODE_SHA3_512; sha_ctx->blk_size = TEGRA_VIRTUAL_SE_SHA_HASH_BLOCK_SIZE_576BIT; sha_ctx->intermediate_digest_size = SHA3_STATE_SIZE; sha_ctx->digest_size = crypto_ahash_digestsize(tfm); } else if (strcmp(crypto_ahash_alg_name(tfm), "shake128-vse") == 0) { sha_ctx->mode = VIRTUAL_SE_OP_MODE_SHAKE128; sha_ctx->blk_size = TEGRA_VIRTUAL_SE_SHA_HASH_BLOCK_SIZE_1344BIT; sha_ctx->intermediate_digest_size = SHA3_STATE_SIZE; } else if (strcmp(crypto_ahash_alg_name(tfm), "shake256-vse") == 0) { sha_ctx->mode = VIRTUAL_SE_OP_MODE_SHAKE256; sha_ctx->blk_size = TEGRA_VIRTUAL_SE_SHA_HASH_BLOCK_SIZE_1088BIT; sha_ctx->intermediate_digest_size = SHA3_STATE_SIZE; } else if ((strcmp(crypto_ahash_alg_name(tfm), "sm3-vse") == 0) && (se_dev->chipdata->sm_supported)) { sha_ctx->mode = VIRTUAL_SE_OP_MODE_SM3; sha_ctx->blk_size = SM3_BLOCK_SIZE; sha_ctx->intermediate_digest_size = SM3_DIGEST_SIZE; sha_ctx->digest_size = crypto_ahash_digestsize(tfm); } else { dev_err(se_dev->dev, "Invalid SHA Mode\n"); return -EINVAL; } req_ctx->req_context_initialized = true; return 0; } static int tegra_vse_validate_sha_params(struct tegra_virtual_se_sha_context *sha_ctx, bool is_last) { int ret = 0; bool is_zero_copy; if (sha_ctx->node_id >= MAX_NUMBER_MISC_DEVICES) { pr_err("%s: Node id is not valid\n", __func__); ret = -EINVAL; goto exit; } is_zero_copy = g_crypto_to_ivc_map[sha_ctx->node_id].is_zero_copy_node; if (is_last == 0 && is_zero_copy) { pr_err("%s(): Multipart SHA is not supported for zero-copy\n", __func__); ret = -EINVAL; goto exit; } if (!is_zero_copy) { if (sha_ctx->user_src_buf_size > 0 && sha_ctx->user_src_buf == NULL) { pr_err("%s: src buf is NULL\n", __func__); ret = -EINVAL; goto exit; } } if (sha_ctx->intermediate_digest == NULL) { pr_err("%s: intermediate_digest is NULL\n", __func__); ret = -EINVAL; goto exit; } if (sha_ctx->digest_size == 0) { pr_err("%s: Digest size is not valid\n", __func__); ret = -EINVAL; goto exit; } if (sha_ctx->user_src_buf_size > TEGRA_VIRTUAL_SE_MAX_SUPPORTED_BUFLEN) { pr_err("%s: input buffer size is invalid\n", __func__); ret = -EINVAL; goto exit; } if (sha_ctx->blk_size == 0U) { pr_err("SHA blk_size is invalid\n"); ret = -EINVAL; goto exit; } if ((!is_last) && (sha_ctx->user_src_buf_size % sha_ctx->blk_size != 0)) { pr_err("%s: non-last buffer size is invalid\n", __func__); ret = -EINVAL; goto exit; } exit: return ret; } static int tegra_hv_vse_safety_sha_op(struct tegra_virtual_se_sha_context *sha_ctx, bool is_last) { struct tegra_virtual_se_dev *se_dev; struct tegra_virtual_se_ivc_hdr_t *ivc_hdr; struct tegra_virtual_se_ivc_tx_msg_t *ivc_tx; struct tegra_virtual_se_ivc_msg_t ivc_req_msg = {0}; union tegra_virtual_se_sha_args *psha; struct tegra_hv_ivc_cookie *pivck = g_crypto_to_ivc_map[sha_ctx->node_id].ivck; struct tegra_vse_priv_data priv = {0}; struct tegra_vse_tag *priv_data_ptr; u64 msg_len = 0, temp_len = 0; uint32_t engine_id; int err = 0; const struct tegra_vse_dma_buf *plaintext, *hash_result; bool is_zero_copy; engine_id = g_crypto_to_ivc_map[sha_ctx->node_id].engine_id; se_dev = g_crypto_to_ivc_map[sha_ctx->node_id].se_dev; is_zero_copy = g_crypto_to_ivc_map[sha_ctx->node_id].is_zero_copy_node; if (sha_ctx->mode == VIRTUAL_SE_OP_MODE_SHAKE128 || sha_ctx->mode == VIRTUAL_SE_OP_MODE_SHAKE256) { if (sha_ctx->digest_size == 0) { dev_info(se_dev->dev, "digest size is 0\n"); return 0; } } g_crypto_to_ivc_map[sha_ctx->node_id].vse_thread_start = true; msg_len = sha_ctx->user_src_buf_size; if (!is_zero_copy) { plaintext = tegra_hv_vse_get_dma_buf(sha_ctx->node_id, SHA_SRC_BUF_IDX, sha_ctx->user_src_buf_size); if (!plaintext) { dev_err(se_dev->dev, "%s src_buf is NULL\n", __func__); return -ENOMEM; } if (msg_len > 0) { err = copy_from_user(plaintext->buf_ptr, sha_ctx->user_src_buf, msg_len); if (err) { pr_err("%s(): Failed to copy plaintext: %d\n", __func__, err); goto exit; } } } else { if (g_node_dma[sha_ctx->node_id].mapped_membuf_count == 0U) { dev_err(se_dev->dev, "%s no mapped membuf found\n", __func__); return -ENOMEM; } } hash_result = tegra_hv_vse_get_dma_buf(sha_ctx->node_id, SHA_HASH_BUF_IDX, sha_ctx->digest_size); if (!hash_result) { dev_err(se_dev->dev, "%s hash_result is NULL\n", __func__); return -ENOMEM; } ivc_tx = &ivc_req_msg.tx[0]; ivc_hdr = &ivc_req_msg.ivc_hdr; ivc_hdr->num_reqs = 1; ivc_hdr->header_magic[0] = 'N'; ivc_hdr->header_magic[1] = 'V'; ivc_hdr->header_magic[2] = 'D'; ivc_hdr->header_magic[3] = 'A'; ivc_hdr->engine = VIRTUAL_SE_SHA; ivc_tx->cmd = TEGRA_VIRTUAL_SE_CMD_SHA_HASH; psha = &(ivc_tx->sha); psha->op_hash.mode = sha_ctx->mode; psha->op_hash.msg_total_length[2] = 0; psha->op_hash.msg_total_length[3] = 0; psha->op_hash.msg_left_length[2] = 0; psha->op_hash.msg_left_length[3] = 0; psha->op_hash.hash_length = sha_ctx->digest_size; psha->op_hash.dst = hash_result->buf_iova; if (!sha_ctx->is_first) memcpy(psha->op_hash.hash, sha_ctx->intermediate_digest, sha_ctx->intermediate_digest_size); if (is_last == true && (sha_ctx->mode == VIRTUAL_SE_OP_MODE_SHAKE128 || sha_ctx->mode == VIRTUAL_SE_OP_MODE_SHAKE256)) { ((uint8_t *)plaintext->buf_ptr)[msg_len] = 0xff; msg_len++; sha_ctx->total_count++; } temp_len = msg_len; if (is_last) { /* Set msg left length equal to input buffer size */ psha->op_hash.msg_left_length[0] = msg_len & 0xFFFFFFFF; psha->op_hash.msg_left_length[1] = msg_len >> 32; /* Set msg total length equal to sum of all input buffer size */ psha->op_hash.msg_total_length[0] = sha_ctx->total_count & 0xFFFFFFFF; psha->op_hash.msg_total_length[1] = sha_ctx->total_count >> 32; } else { /* Set msg left length greater than input buffer size */ temp_len += 8; psha->op_hash.msg_left_length[0] = temp_len & 0xFFFFFFFF; psha->op_hash.msg_left_length[1] = temp_len >> 32; /* Set msg total length greater than msg left length for non-first request */ if (!sha_ctx->is_first) temp_len += 8; psha->op_hash.msg_total_length[0] = temp_len & 0xFFFFFFFF; psha->op_hash.msg_total_length[1] = temp_len >> 32; } if (!is_zero_copy) psha->op_hash.src_addr = plaintext->buf_iova; else psha->op_hash.src_addr = sha_ctx->user_src_iova; psha->op_hash.src_buf_size = msg_len; priv_data_ptr = (struct tegra_vse_tag *)ivc_req_msg.ivc_hdr.tag; priv_data_ptr->priv_data = (unsigned int *)&priv; priv.cmd = VIRTUAL_SE_PROCESS; priv.se_dev = se_dev; init_completion(&priv.alg_complete); err = tegra_hv_vse_safety_send_ivc_wait(se_dev, pivck, &priv, &ivc_req_msg, sizeof(struct tegra_virtual_se_ivc_msg_t), sha_ctx->node_id); if (err) { dev_err(se_dev->dev, "failed to send data over ivc err %d\n", err); goto exit; } if (priv.rx_status != 0) { err = status_to_errno(priv.rx_status); dev_err(se_dev->dev, "%s: SE server returned error %u\n", __func__, priv.rx_status); goto exit; } if (is_last && sha_ctx->digest_size > 0) { err = copy_to_user(sha_ctx->user_digest_buffer, hash_result->buf_ptr, sha_ctx->digest_size); if (err) { pr_err("%s(): Failed to copy dst_buf: %d\n", __func__, err); goto exit; } } else memcpy(sha_ctx->intermediate_digest, hash_result->buf_ptr, sha_ctx->intermediate_digest_size); exit: return err; } static int tegra_hv_vse_safety_sha_update(struct ahash_request *req) { struct tegra_virtual_se_req_context *req_ctx; struct tegra_virtual_se_sha_context *sha_ctx; struct tegra_virtual_se_dev *se_dev; uint32_t engine_id; int ret = 0; if (!req) { pr_err("%s SHA request not valid\n", __func__); return -EINVAL; } req_ctx = ahash_request_ctx(req); if (!req_ctx) { pr_err("%s SHA req not valid\n", __func__); return -EINVAL; } if (!req_ctx->req_context_initialized) { pr_err("%s Request ctx not initialized\n", __func__); return -EINVAL; } sha_ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(req)); if (!sha_ctx) { pr_err("%s SHA req_ctx not valid\n", __func__); return -EINVAL; } ret = tegra_vse_validate_sha_params(sha_ctx, false); if (ret) { pr_err("%s: invalid SHA params\n", __func__); return ret; } engine_id = g_crypto_to_ivc_map[sha_ctx->node_id].engine_id; se_dev = g_crypto_to_ivc_map[sha_ctx->node_id].se_dev; /* Return error if engine is in suspended state */ if (atomic_read(&se_dev->se_suspended)) return -ENODEV; ret = tegra_hv_vse_safety_sha_op(sha_ctx, false); if (ret) dev_err(se_dev->dev, "tegra_se_sha_update failed - %d\n", ret); return ret; } static int tegra_hv_vse_safety_sha_finup(struct ahash_request *req) { struct tegra_virtual_se_req_context *req_ctx; struct tegra_virtual_se_sha_context *sha_ctx = NULL; struct tegra_virtual_se_dev *se_dev; uint32_t engine_id; int ret = 0; if (!req) { pr_err("%s SHA request not valid\n", __func__); return -EINVAL; } sha_ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(req)); if (!sha_ctx) { pr_err("%s SHA req_ctx not valid\n", __func__); return -EINVAL; } req_ctx = ahash_request_ctx(req); if (!req_ctx) { pr_err("%s SHA req not valid\n", __func__); return -EINVAL; } if (!req_ctx->req_context_initialized) { pr_err("%s Request ctx not initialized\n", __func__); return -EINVAL; } ret = tegra_vse_validate_sha_params(sha_ctx, true); if (ret) { pr_err("%s: invalid SHA params\n", __func__); return ret; } engine_id = g_crypto_to_ivc_map[sha_ctx->node_id].engine_id; se_dev = g_crypto_to_ivc_map[sha_ctx->node_id].se_dev; /* Return error if engine is in suspended state */ if (atomic_read(&se_dev->se_suspended)) return -ENODEV; ret = tegra_hv_vse_safety_sha_op(sha_ctx, true); if (ret) dev_err(se_dev->dev, "tegra_se_sha_finup failed - %d\n", ret); req_ctx->req_context_initialized = false; return ret; } static int tegra_hv_vse_safety_sha_final(struct ahash_request *req) { // Unsupported return -EINVAL; } static int tegra_hv_vse_safety_sha_digest(struct ahash_request *req) { // Unsupported return -EINVAL; } static int tegra_hv_vse_safety_hmac_sha_setkey(struct crypto_ahash *tfm, const u8 *key, unsigned int keylen) { struct tegra_virtual_se_hmac_sha_context *hmac_ctx; struct tegra_virtual_se_dev *se_dev; int err = 0; s8 label[TEGRA_VIRTUAL_SE_AES_MAX_KEY_SIZE]; bool is_keyslot_label; if (!tfm) { pr_err("HMAC SHA transform not valid\n"); return -EINVAL; } hmac_ctx = crypto_tfm_ctx(crypto_ahash_tfm(tfm)); if (!hmac_ctx) return -EINVAL; if (hmac_ctx->node_id >= MAX_NUMBER_MISC_DEVICES) { pr_err("%s: Node id is not valid\n", __func__); return -EINVAL; } se_dev = g_crypto_to_ivc_map[hmac_ctx->node_id].se_dev; if (keylen != 32) { dev_err(se_dev->dev, "%s: Unsupported key length: %d", __func__, keylen); return -EINVAL; } /* format: 'NVSEAES 1234567\0' */ is_keyslot_label = sscanf(key, "%s", label) == 1 && !strcmp(label, TEGRA_VIRTUAL_SE_AES_KEYSLOT_LABEL); if (is_keyslot_label) { hmac_ctx->keylen = keylen; memcpy(hmac_ctx->aes_keyslot, key + KEYSLOT_OFFSET_BYTES, KEYSLOT_SIZE_BYTES); hmac_ctx->is_key_slot_allocated = true; } else { dev_err(se_dev->dev, "%s: Invalid keyslot label %s\n", __func__, key); return -EINVAL; } return err; } static int tegra_hv_vse_safety_hmac_sha_init(struct ahash_request *req) { struct crypto_ahash *tfm; struct tegra_virtual_se_req_context *req_ctx; struct tegra_virtual_se_hmac_sha_context *hmac_ctx; struct tegra_virtual_se_dev *se_dev; if (!req) { pr_err("%s HMAC SHA request not valid\n", __func__); return -EINVAL; } req_ctx = ahash_request_ctx(req); if (!req_ctx) { pr_err("%s HMAC SHA req_ctx not valid\n", __func__); return -EINVAL; } tfm = crypto_ahash_reqtfm(req); if (!tfm) { pr_err("%s HMAC SHA transform not valid\n", __func__); return -EINVAL; } hmac_ctx = crypto_ahash_ctx(tfm); hmac_ctx->digest_size = crypto_ahash_digestsize(tfm); se_dev = g_crypto_to_ivc_map[hmac_ctx->node_id].se_dev; /* Return error if engine is in suspended state */ if (atomic_read(&se_dev->se_suspended)) return -ENODEV; if (!hmac_ctx->is_key_slot_allocated) { dev_err(se_dev->dev, "%s key is not allocated\n", __func__); return -EINVAL; } if (strcmp(crypto_ahash_alg_name(tfm), "hmac-sha256-vse") == 0) { hmac_ctx->mode = VIRTUAL_SE_OP_MODE_SHA256; hmac_ctx->blk_size = TEGRA_VIRTUAL_SE_SHA_HASH_BLOCK_SIZE_512BIT; } else { dev_err(se_dev->dev, "Invalid HMAC-SHA Alg\n"); return -EINVAL; } req_ctx->req_context_initialized = true; return 0; } static int tegra_hv_vse_safety_hmac_sha_sv_op(struct ahash_request *req, struct tegra_virtual_se_hmac_sha_context *hmac_ctx, bool is_last) { struct tegra_virtual_se_dev *se_dev = g_crypto_to_ivc_map[hmac_ctx->node_id].se_dev; struct tegra_virtual_se_ivc_hdr_t *ivc_hdr; struct tegra_virtual_se_ivc_tx_msg_t *ivc_tx; struct tegra_virtual_se_ivc_msg_t ivc_req_msg = {0}; struct tegra_virtual_se_hmac_sha_args *phmac; struct tegra_hv_ivc_cookie *pivck = g_crypto_to_ivc_map[hmac_ctx->node_id].ivck; int err = 0; struct tegra_vse_priv_data priv = {0}; struct tegra_vse_tag *priv_data_ptr; const struct tegra_vse_dma_buf *src, *hash, *match; u32 cmd = 0; u32 matchcode = SE_HW_VALUE_MATCH_CODE; u32 mismatch_code = SE_HW_VALUE_MISMATCH_CODE; u32 blocks_to_process, last_block_bytes = 0; u64 msg_len = 0, temp_len = 0; src = tegra_hv_vse_get_dma_buf(hmac_ctx->node_id, SHA_SRC_BUF_IDX, hmac_ctx->user_src_buf_size); if (!src) { pr_err("%s src buf is NULL\n", __func__); return -ENOMEM; } if (hmac_ctx->request_type == TEGRA_HV_VSE_HMAC_SHA_SIGN) { hash = tegra_hv_vse_get_dma_buf(hmac_ctx->node_id, SHA_HASH_BUF_IDX, hmac_ctx->digest_size); if (!hash) { pr_err("%s hash buf is NULL\n", __func__); return -ENOMEM; } memset(hash->buf_ptr, 0, TEGRA_VIRTUAL_SE_SHA_MAX_HMAC_SHA_LENGTH); cmd = TEGRA_VIRTUAL_SE_CMD_HMAC_SIGN; } else { cmd = TEGRA_VIRTUAL_SE_CMD_HMAC_VERIFY; } if ((se_dev->chipdata->hmac_verify_hw_support == true) && (is_last && (hmac_ctx->request_type == TEGRA_HV_VSE_HMAC_SHA_VERIFY))) { hash = tegra_hv_vse_get_dma_buf(hmac_ctx->node_id, SHA_HASH_BUF_IDX, TEGRA_VIRTUAL_SE_SHA_MAX_HMAC_SHA_LENGTH); if (!hash) { pr_err("%s verify result buf is NULL\n", __func__); return -ENOMEM; } match = tegra_hv_vse_get_dma_buf(hmac_ctx->node_id, HMAC_SHA_COMP_BUF_IDX, RESULT_COMPARE_BUF_SIZE); if (!match) { pr_err("%s match code buf is NULL\n", __func__); return -ENOMEM; } } g_crypto_to_ivc_map[hmac_ctx->node_id].vse_thread_start = true; msg_len = hmac_ctx->user_src_buf_size; temp_len = msg_len; ivc_tx = &ivc_req_msg.tx[0]; ivc_hdr = &ivc_req_msg.ivc_hdr; ivc_hdr->num_reqs = 1; ivc_hdr->header_magic[0] = 'N'; ivc_hdr->header_magic[1] = 'V'; ivc_hdr->header_magic[2] = 'D'; ivc_hdr->header_magic[3] = 'A'; ivc_hdr->engine = VIRTUAL_SE_SHA; ivc_tx->cmd = cmd; phmac = &(ivc_tx->hmac); phmac->mode = hmac_ctx->mode; phmac->msg_total_length[2] = 0; phmac->msg_total_length[3] = 0; phmac->msg_left_length[2] = 0; phmac->msg_left_length[3] = 0; memcpy(phmac->keyslot, hmac_ctx->aes_keyslot, KEYSLOT_SIZE_BYTES); phmac->src_addr = src->buf_iova; if (hmac_ctx->request_type == TEGRA_HV_VSE_HMAC_SHA_SIGN) phmac->dst_addr = hash->buf_iova; if (is_last) { /* Set msg left length equal to input buffer size */ phmac->msg_left_length[0] = msg_len & 0xFFFFFFFF; phmac->msg_left_length[1] = msg_len >> 32; /* Set msg total length equal to sum of all input buffer size */ phmac->msg_total_length[0] = hmac_ctx->total_count & 0xFFFFFFFF; phmac->msg_total_length[1] = hmac_ctx->total_count >> 32; } else { /* Set msg left length greater than input buffer size */ temp_len += 8; phmac->msg_left_length[0] = temp_len & 0xFFFFFFFF; phmac->msg_left_length[1] = temp_len >> 32; /* Set msg total length greater than msg left length for non-first request */ if (!hmac_ctx->is_first) temp_len += 8; phmac->msg_total_length[0] = temp_len & 0xFFFFFFFF; phmac->msg_total_length[1] = temp_len >> 32; } if (se_dev->chipdata->hmac_verify_hw_support == false) { if (is_last && (hmac_ctx->request_type == TEGRA_HV_VSE_HMAC_SHA_VERIFY)) { blocks_to_process = msg_len / hmac_ctx->blk_size; /* num of bytes less than block size */ if ((hmac_ctx->user_src_buf_size % hmac_ctx->blk_size) || blocks_to_process == 0) { last_block_bytes = msg_len % hmac_ctx->blk_size; } else { /* decrement num of blocks */ blocks_to_process--; last_block_bytes = hmac_ctx->blk_size; } if (blocks_to_process > 0) { err = copy_from_user(src->buf_ptr, hmac_ctx->user_src_buf, blocks_to_process * hmac_ctx->blk_size); if (err) { pr_err("%s(): Failed to copy src_buf: %d\n", __func__, err); goto unmap_exit; } } phmac->src_buf_size = blocks_to_process * hmac_ctx->blk_size; phmac->lastblock_len = last_block_bytes; err = copy_from_user(phmac->expected_hmac_sha, hmac_ctx->user_digest_buffer, TEGRA_VIRTUAL_SE_SHA_MAX_HMAC_SHA_LENGTH); if (err) { pr_err("%s(): Failed to copy digest buf: %d\n", __func__, err); goto unmap_exit; } if (last_block_bytes > 0) { err = copy_from_user(phmac->lastblock, &hmac_ctx->user_src_buf[blocks_to_process * hmac_ctx->blk_size], last_block_bytes); if (err) { pr_err("%s(): Failed to copy src_buf: %d\n", __func__, err); goto unmap_exit; } } } else { phmac->src_buf_size = msg_len; phmac->lastblock_len = 0; if (msg_len > 0) { err = copy_from_user(src->buf_ptr, hmac_ctx->user_src_buf, msg_len); if (err) { pr_err("%s(): Failed to copy src_buf: %d\n", __func__, err); goto unmap_exit; } } } } else { phmac->src_buf_size = msg_len; phmac->lastblock_len = 0; if (msg_len > 0) { err = copy_from_user(src->buf_ptr, hmac_ctx->user_src_buf, msg_len); if (err) { pr_err("%s(): Failed to copy src_buf: %d\n", __func__, err); goto unmap_exit; } } if (is_last && (hmac_ctx->request_type == TEGRA_HV_VSE_HMAC_SHA_VERIFY)) { phmac->hmac_addr = hash->buf_iova; err = copy_from_user(hash->buf_ptr, hmac_ctx->user_digest_buffer, TEGRA_VIRTUAL_SE_SHA_MAX_HMAC_SHA_LENGTH); if (err) { pr_err("%s(): Failed to copy dst_buf: %d\n", __func__, err); goto unmap_exit; } phmac->dst_addr = match->buf_iova; } } priv_data_ptr = (struct tegra_vse_tag *)ivc_req_msg.ivc_hdr.tag; priv_data_ptr->priv_data = (unsigned int *)&priv; priv.cmd = VIRTUAL_SE_PROCESS; priv.se_dev = se_dev; init_completion(&priv.alg_complete); err = tegra_hv_vse_safety_send_ivc_wait(se_dev, pivck, &priv, &ivc_req_msg, sizeof(struct tegra_virtual_se_ivc_msg_t), hmac_ctx->node_id); if (err) { dev_err(se_dev->dev, "failed to send data over ivc err %d\n", err); goto unmap_exit; } if (priv.rx_status != 0) { err = status_to_errno(priv.rx_status); dev_err(se_dev->dev, "%s: SE server returned error %u\n", __func__, priv.rx_status); goto unmap_exit; } if (is_last) { if (hmac_ctx->request_type == TEGRA_HV_VSE_HMAC_SHA_VERIFY) { if (se_dev->chipdata->hmac_verify_hw_support == false) { ivc_tx->cmd = TEGRA_VIRTUAL_SE_CMD_HMAC_GET_VERIFY; priv.cmd = VIRTUAL_SE_PROCESS; init_completion(&priv.alg_complete); err = tegra_hv_vse_safety_send_ivc_wait(se_dev, pivck, &priv, &ivc_req_msg, sizeof(struct tegra_virtual_se_ivc_msg_t), hmac_ctx->node_id); if (err) { dev_err(se_dev->dev, "failed to send data over ivc err %d\n", err); goto unmap_exit; } if (priv.rx_status == 0) { hmac_ctx->result = 0; } else if (priv.rx_status == TEGRA_VIRTUAL_SE_ERR_MAC_INVALID) { dev_dbg(se_dev->dev, "%s: tag mismatch", __func__); hmac_ctx->result = 1; } else { err = status_to_errno(priv.rx_status); dev_err(se_dev->dev, "%s: SE server returned error %u\n", __func__, priv.rx_status); } } else { if (memcmp(match->buf_ptr, &matchcode, 4) == 0) { hmac_ctx->result = 0; } else if (memcmp(match->buf_ptr, &mismatch_code, 4) == 0) { dev_dbg(se_dev->dev, "%s: tag mismatch", __func__); hmac_ctx->result = 1; } else { dev_err(se_dev->dev, "%s: invalid tag match code", __func__); err = -EINVAL; } } } else { err = copy_to_user(hmac_ctx->user_digest_buffer, hash->buf_ptr, TEGRA_VIRTUAL_SE_SHA_MAX_HMAC_SHA_LENGTH); if (err) pr_err("%s(): Failed to copy dst_buf: %d\n", __func__, err); } } unmap_exit: return err; } static int tegra_hv_vse_safety_hmac_sha_update(struct ahash_request *req) { struct tegra_virtual_se_req_context *req_ctx; struct tegra_virtual_se_hmac_sha_context *hmac_ctx; struct tegra_virtual_se_dev *se_dev; int ret = 0; if (!req) { pr_err("%s HMAC SHA request not valid\n", __func__); return -EINVAL; } hmac_ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(req)); if (!hmac_ctx) { pr_err("%s HMAC SHA req_ctx not valid\n", __func__); return -EINVAL; } req_ctx = ahash_request_ctx(req); if (!req_ctx) { pr_err("%s HMAC SHA req not valid\n", __func__); return -EINVAL; } if (!req_ctx->req_context_initialized) { pr_err("%s Request ctx not initialized\n", __func__); return -EINVAL; } ret = tegra_vse_validate_hmac_sha_params(hmac_ctx, false); if (ret) { pr_err("%s: invalid HMAC SHA params\n", __func__); return ret; } se_dev = g_crypto_to_ivc_map[hmac_ctx->node_id].se_dev; /* Return error if engine is in suspended state */ if (atomic_read(&se_dev->se_suspended)) return -ENODEV; ret = tegra_hv_vse_safety_hmac_sha_sv_op(req, hmac_ctx, false); if (ret) dev_err(se_dev->dev, "tegra_se_hmac_sha_update failed - %d\n", ret); return ret; } static int tegra_hv_vse_safety_hmac_sha_finup(struct ahash_request *req) { struct tegra_virtual_se_req_context *req_ctx; struct tegra_virtual_se_hmac_sha_context *hmac_ctx = NULL; struct tegra_virtual_se_dev *se_dev; int ret = 0; if (!req) { pr_err("%s HMAC-SHA request not valid\n", __func__); return -EINVAL; } hmac_ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(req)); if (!hmac_ctx) { pr_err("%s HMAC-SHA req_ctx not valid\n", __func__); return -EINVAL; } req_ctx = ahash_request_ctx(req); if (!req_ctx) { pr_err("%s HMAC-SHA req not valid\n", __func__); return -EINVAL; } if (!req_ctx->req_context_initialized) { pr_err("%s Request ctx not initialized\n", __func__); return -EINVAL; } ret = tegra_vse_validate_hmac_sha_params(hmac_ctx, true); if (ret) { pr_err("%s: invalid HMAC SHA params\n", __func__); return ret; } if (!hmac_ctx->is_key_slot_allocated) { pr_err("%s key is not allocated\n", __func__); return -EINVAL; } se_dev = g_crypto_to_ivc_map[hmac_ctx->node_id].se_dev; /* Return error if engine is in suspended state */ if (atomic_read(&se_dev->se_suspended)) return -ENODEV; ret = tegra_hv_vse_safety_hmac_sha_sv_op(req, hmac_ctx, true); if (ret) dev_err(se_dev->dev, "tegra_se_hmac_sha_finup failed - %d\n", ret); hmac_ctx->is_key_slot_allocated = false; req_ctx->req_context_initialized = false; return ret; } static int tegra_hv_vse_safety_hmac_sha_final(struct ahash_request *req) { // Unsupported return -EINVAL; } static int tegra_hv_vse_safety_hmac_sha_digest(struct ahash_request *req) { // Unsupported return -EINVAL; } static int tegra_hv_vse_safety_sha_export(struct ahash_request *req, void *out) { struct tegra_virtual_se_req_context *req_ctx = ahash_request_ctx(req); memcpy(out, req_ctx, sizeof(*req_ctx)); return 0; } static int tegra_hv_vse_safety_sha_import(struct ahash_request *req, const void *in) { struct tegra_virtual_se_req_context *req_ctx = ahash_request_ctx(req); memcpy(req_ctx, in, sizeof(*req_ctx)); return 0; } static int tegra_hv_vse_safety_sha_cra_init(struct crypto_tfm *tfm) { crypto_ahash_set_reqsize(__crypto_ahash_cast(tfm), sizeof(struct tegra_virtual_se_req_context)); return 0; } static void tegra_hv_vse_safety_sha_cra_exit(struct crypto_tfm *tfm) { } static void tegra_hv_vse_safety_prepare_cmd(struct tegra_virtual_se_dev *se_dev, struct tegra_virtual_se_ivc_tx_msg_t *ivc_tx, struct tegra_virtual_se_aes_req_context *req_ctx, struct tegra_virtual_se_aes_context *aes_ctx, struct skcipher_request *req) { union tegra_virtual_se_aes_args *aes; aes = &ivc_tx->aes; if (req_ctx->encrypt == true) ivc_tx->cmd = TEGRA_VIRTUAL_SE_CMD_AES_ENCRYPT; else ivc_tx->cmd = TEGRA_VIRTUAL_SE_CMD_AES_DECRYPT; memcpy(aes->op.keyslot, aes_ctx->aes_keyslot, KEYSLOT_SIZE_BYTES); aes->op.key_length = aes_ctx->keylen; aes->op.mode = req_ctx->op_mode; aes->op.ivsel = AES_ORIGINAL_IV; memcpy(aes->op.lctr, aes_ctx->iv, TEGRA_VIRTUAL_SE_AES_LCTR_SIZE); if ((req_ctx->op_mode == AES_CTR) || (req_ctx->op_mode == AES_SM4_CTR)) aes->op.ctr_cntn = TEGRA_VIRTUAL_SE_AES_LCTR_CNTN; else if ((req_ctx->op_mode == AES_CBC) || (req_ctx->op_mode == AES_SM4_CBC)) { if (req_ctx->encrypt == true && aes_ctx->user_nonce == 1U && aes_ctx->b_is_first != 1U) aes->op.ivsel = AES_UPDATED_IV; else aes->op.ivsel = AES_IV_REG; } } static int tegra_hv_vse_safety_aes_gen_random_iv( struct tegra_virtual_se_dev *se_dev, struct skcipher_request *req, struct tegra_virtual_se_aes_context *aes_ctx, struct tegra_vse_priv_data *priv, struct tegra_virtual_se_ivc_msg_t *ivc_req_msg) { struct tegra_virtual_se_ivc_tx_msg_t *ivc_tx = &ivc_req_msg->tx[0]; struct tegra_hv_ivc_cookie *pivck; union tegra_virtual_se_aes_args *aes = &ivc_tx->aes; int err = 0; ivc_tx->cmd = TEGRA_VIRTUAL_SE_CMD_AES_ENCRYPT_INIT; priv->cmd = VIRTUAL_SE_PROCESS; memcpy(aes->op.keyslot, aes_ctx->aes_keyslot, KEYSLOT_SIZE_BYTES); aes->op.key_length = aes_ctx->keylen; pivck = g_crypto_to_ivc_map[aes_ctx->node_id].ivck; init_completion(&priv->alg_complete); err = tegra_hv_vse_safety_send_ivc_wait(se_dev, pivck, priv, ivc_req_msg, sizeof(struct tegra_virtual_se_ivc_msg_t), aes_ctx->node_id); if (err) { dev_err(se_dev->dev, "failed to send data over ivc err %d\n", err); return err; } err = status_to_errno(priv->rx_status); if (err) { dev_err(se_dev->dev, "\n %s IV generation failed %d\n", __func__, err); } return err; } static int tegra_hv_vse_safety_process_aes_req(struct tegra_virtual_se_dev *se_dev, struct tegra_virtual_se_aes_context *aes_ctx, struct skcipher_request *req) { struct tegra_virtual_se_aes_req_context *req_ctx; struct tegra_virtual_se_ivc_tx_msg_t *ivc_tx = NULL; struct tegra_virtual_se_ivc_hdr_t *ivc_hdr = NULL; struct tegra_hv_ivc_cookie *pivck; int err = 0; struct tegra_virtual_se_ivc_msg_t *ivc_req_msg = NULL; struct tegra_vse_priv_data *priv = NULL; struct tegra_vse_tag *priv_data_ptr; union tegra_virtual_se_aes_args *aes; const struct tegra_vse_dma_buf *src; priv = devm_kzalloc(se_dev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) { err = -ENOMEM; goto exit; } priv->req = req; ivc_req_msg = devm_kzalloc(se_dev->dev, sizeof(*ivc_req_msg), GFP_KERNEL); if (!ivc_req_msg) { err = -ENOMEM; goto exit; } ivc_tx = &ivc_req_msg->tx[0]; aes = &ivc_tx->aes; req_ctx = skcipher_request_ctx(req); src = tegra_hv_vse_get_dma_buf(aes_ctx->node_id, AES_SRC_BUF_IDX, aes_ctx->user_src_buf_size); if (!src) { dev_err(req_ctx->se_dev->dev, "%s src_buf is NULL\n", __func__); return -ENOMEM; } err = copy_from_user(src->buf_ptr, aes_ctx->user_src_buf, aes_ctx->user_src_buf_size); if (err) { pr_err("%s(): Failed to copy src_buf: %d\n", __func__, err); goto exit; } pivck = g_crypto_to_ivc_map[aes_ctx->node_id].ivck; ivc_hdr = &ivc_req_msg->ivc_hdr; //Currently we support only one request per IVC message ivc_hdr->num_reqs = 1U; ivc_hdr->header_magic[0] = 'N'; ivc_hdr->header_magic[1] = 'V'; ivc_hdr->header_magic[2] = 'D'; ivc_hdr->header_magic[3] = 'A'; ivc_hdr->engine = g_crypto_to_ivc_map[aes_ctx->node_id].engine_id; priv_data_ptr = (struct tegra_vse_tag *)ivc_hdr->tag; priv_data_ptr->priv_data = (unsigned int *)priv; priv->se_dev = se_dev; g_crypto_to_ivc_map[aes_ctx->node_id].vse_thread_start = true; /* * If aes_ctx->iv[0] is 1 and the request is for AES CBC/CTR encryption, * it means that generation of random IV is required. * If userNonce is not provided random IV generation is needed. */ if (req_ctx->encrypt && (is_aes_mode_valid(req_ctx->op_mode) == 1) && (aes_ctx->user_nonce == 0U) && (aes_ctx->iv[0] == 1)) { //Random IV generation is required err = tegra_hv_vse_safety_aes_gen_random_iv(se_dev, req, aes_ctx, priv, ivc_req_msg); if (err) goto exit; } priv->cmd = VIRTUAL_SE_AES_CRYPTO; tegra_hv_vse_safety_prepare_cmd(se_dev, ivc_tx, req_ctx, aes_ctx, req); aes->op.src_addr = (u64)src->buf_iova; aes->op.src_buf_size = aes_ctx->user_src_buf_size; aes->op.dst_addr = (u64)src->buf_iova; aes->op.dst_buf_size = aes_ctx->user_src_buf_size; init_completion(&priv->alg_complete); err = tegra_hv_vse_safety_send_ivc_wait(se_dev, pivck, priv, ivc_req_msg, sizeof(struct tegra_virtual_se_ivc_msg_t), aes_ctx->node_id); if (err) { dev_err(se_dev->dev, "failed to send data over ivc err %d\n", err); goto exit; } if (priv->rx_status == 0U) { err = copy_to_user(aes_ctx->user_dst_buf, src->buf_ptr, aes_ctx->user_src_buf_size); if (err) { pr_err("%s(): Failed to copy dst_buf: %d\n", __func__, err); goto exit; } if ((is_aes_mode_valid(req_ctx->op_mode) == 1) && (req_ctx->encrypt == true) && (aes_ctx->user_nonce == 0U)) memcpy(aes_ctx->iv, priv->iv, TEGRA_VIRTUAL_SE_AES_IV_SIZE); } else { dev_err(se_dev->dev, "%s: SE server returned error %u\n", __func__, priv->rx_status); } err = status_to_errno(priv->rx_status); exit: if (priv) devm_kfree(se_dev->dev, priv); if (ivc_req_msg) devm_kfree(se_dev->dev, ivc_req_msg); return err; } static int tegra_hv_vse_safety_aes_cra_init(struct crypto_skcipher *tfm) { tfm->reqsize = sizeof(struct tegra_virtual_se_aes_req_context); return 0; } static void tegra_hv_vse_safety_aes_cra_exit(struct crypto_skcipher *tfm) { /* nothing to do as user releases the keyslot through tzvault TA */ } static int tegra_hv_vse_safety_aes_cbc_encrypt(struct skcipher_request *req) { int err = 0; struct tegra_virtual_se_aes_req_context *req_ctx = NULL; struct tegra_virtual_se_aes_context *aes_ctx; if (!req) { pr_err("NULL req received by %s", __func__); return -EINVAL; } aes_ctx = crypto_skcipher_ctx(crypto_skcipher_reqtfm(req)); if (!aes_ctx) { pr_err("%s AES req_ctx not valid\n", __func__); return -EINVAL; } err = tegra_vse_validate_aes_param(aes_ctx); if (err) { pr_err("%s: invalid AES params\n", __func__); return err; } req_ctx = skcipher_request_ctx(req); if (!req_ctx) { pr_err("%s AES req not valid\n", __func__); return -EINVAL; } req_ctx->encrypt = true; req_ctx->engine_id = g_crypto_to_ivc_map[aes_ctx->node_id].engine_id; req_ctx->se_dev = g_crypto_to_ivc_map[aes_ctx->node_id].se_dev; if ((req_ctx->se_dev->chipdata->sm_supported == false) && (aes_ctx->b_is_sm4 == 1U)) { pr_err("%s: SM4 CBC is not supported for selected platform\n", __func__); return -EINVAL; } if (aes_ctx->b_is_sm4 == 1U) req_ctx->op_mode = AES_SM4_CBC; else req_ctx->op_mode = AES_CBC; err = tegra_hv_vse_safety_process_aes_req(req_ctx->se_dev, aes_ctx, req); if (err) dev_err(req_ctx->se_dev->dev, "%s failed with error %d\n", __func__, err); return err; } static int tegra_hv_vse_safety_aes_cbc_decrypt(struct skcipher_request *req) { int err = 0; struct tegra_virtual_se_aes_req_context *req_ctx = NULL; struct tegra_virtual_se_aes_context *aes_ctx; if (!req) { pr_err("NULL req received by %s", __func__); return -EINVAL; } aes_ctx = crypto_skcipher_ctx(crypto_skcipher_reqtfm(req)); if (!aes_ctx) { pr_err("%s AES req_ctx not valid\n", __func__); return -EINVAL; } err = tegra_vse_validate_aes_param(aes_ctx); if (err) { pr_err("%s: invalid AES params\n", __func__); return err; } req_ctx = skcipher_request_ctx(req); if (!req_ctx) { pr_err("%s AES req not valid\n", __func__); return -EINVAL; } req_ctx->encrypt = false; req_ctx->engine_id = g_crypto_to_ivc_map[aes_ctx->node_id].engine_id; req_ctx->se_dev = g_crypto_to_ivc_map[aes_ctx->node_id].se_dev; if ((req_ctx->se_dev->chipdata->sm_supported == false) && (aes_ctx->b_is_sm4 == 1U)) { pr_err("%s: SM4 CBC is not supported for selected platform\n", __func__); return -EINVAL; } if (aes_ctx->b_is_sm4 == 1U) req_ctx->op_mode = AES_SM4_CBC; else req_ctx->op_mode = AES_CBC; err = tegra_hv_vse_safety_process_aes_req(req_ctx->se_dev, aes_ctx, req); if (err) dev_err(req_ctx->se_dev->dev, "%s failed with error %d\n", __func__, err); return err; } static int tegra_hv_vse_safety_aes_ctr_encrypt(struct skcipher_request *req) { int err = 0; struct tegra_virtual_se_aes_req_context *req_ctx = NULL; struct tegra_virtual_se_aes_context *aes_ctx; if (!req) { pr_err("NULL req received by %s", __func__); return -EINVAL; } aes_ctx = crypto_skcipher_ctx(crypto_skcipher_reqtfm(req)); if (!aes_ctx) { pr_err("%s AES req_ctx not valid\n", __func__); return -EINVAL; } err = tegra_vse_validate_aes_param(aes_ctx); if (err) { pr_err("%s: invalid AES params\n", __func__); return err; } req_ctx = skcipher_request_ctx(req); if (!req_ctx) { pr_err("%s AES req not valid\n", __func__); return -EINVAL; } req_ctx->encrypt = true; req_ctx->engine_id = g_crypto_to_ivc_map[aes_ctx->node_id].engine_id; req_ctx->se_dev = g_crypto_to_ivc_map[aes_ctx->node_id].se_dev; if ((req_ctx->se_dev->chipdata->sm_supported == false) && (aes_ctx->b_is_sm4 == 1U)) { pr_err("%s: SM4 CTR is not supported for selected platform\n", __func__); return -EINVAL; } if (aes_ctx->b_is_sm4 == 1U) req_ctx->op_mode = AES_SM4_CTR; else req_ctx->op_mode = AES_CTR; err = tegra_hv_vse_safety_process_aes_req(req_ctx->se_dev, aes_ctx, req); if (err) dev_err(req_ctx->se_dev->dev, "%s failed with error %d\n", __func__, err); return err; } static int tegra_hv_vse_safety_aes_ctr_decrypt(struct skcipher_request *req) { int err = 0; struct tegra_virtual_se_aes_req_context *req_ctx = NULL; struct tegra_virtual_se_aes_context *aes_ctx; if (!req) { pr_err("NULL req received by %s", __func__); return -EINVAL; } aes_ctx = crypto_skcipher_ctx(crypto_skcipher_reqtfm(req)); if (!aes_ctx) { pr_err("%s AES req_ctx not valid\n", __func__); return -EINVAL; } err = tegra_vse_validate_aes_param(aes_ctx); if (err) { pr_err("%s: invalid AES params\n", __func__); return err; } req_ctx = skcipher_request_ctx(req); req_ctx->encrypt = false; req_ctx->engine_id = g_crypto_to_ivc_map[aes_ctx->node_id].engine_id; req_ctx->se_dev = g_crypto_to_ivc_map[aes_ctx->node_id].se_dev; if ((req_ctx->se_dev->chipdata->sm_supported == false) && (aes_ctx->b_is_sm4 == 1U)) { pr_err("%s: SM4 CTR is not supported for selected platform\n", __func__); return -EINVAL; } if (aes_ctx->b_is_sm4 == 1U) req_ctx->op_mode = AES_SM4_CTR; else req_ctx->op_mode = AES_CTR; err = tegra_hv_vse_safety_process_aes_req(req_ctx->se_dev, aes_ctx, req); if (err) dev_err(req_ctx->se_dev->dev, "%s failed with error %d\n", __func__, err); return err; } static int tegra_hv_vse_safety_tsec_sv_op(struct ahash_request *req, struct tegra_virtual_se_aes_cmac_context *cmac_ctx) { struct tegra_virtual_se_dev *se_dev = g_crypto_to_ivc_map[cmac_ctx->node_id].se_dev; struct tegra_virtual_se_ivc_hdr_t *ivc_hdr; struct tegra_virtual_se_ivc_tx_msg_t *ivc_tx; struct tegra_virtual_se_ivc_msg_t *ivc_req_msg; struct tegra_hv_ivc_cookie *pivck = g_crypto_to_ivc_map[cmac_ctx->node_id].ivck; int err = 0; struct tegra_vse_priv_data *priv = NULL; struct tegra_vse_tag *priv_data_ptr; uint32_t tsec_fw_err; const struct tegra_vse_dma_buf *src, *mac, *fw_status; ivc_req_msg = devm_kzalloc(se_dev->dev, sizeof(*ivc_req_msg), GFP_KERNEL); if (!ivc_req_msg) return -ENOMEM; priv = devm_kzalloc(se_dev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) { devm_kfree(se_dev->dev, ivc_req_msg); return -ENOMEM; } ivc_tx = &ivc_req_msg->tx[0]; ivc_hdr = &ivc_req_msg->ivc_hdr; ivc_hdr->num_reqs = 1; ivc_hdr->header_magic[0] = 'N'; ivc_hdr->header_magic[1] = 'V'; ivc_hdr->header_magic[2] = 'D'; ivc_hdr->header_magic[3] = 'A'; ivc_hdr->engine = g_crypto_to_ivc_map[cmac_ctx->node_id].engine_id; g_crypto_to_ivc_map[cmac_ctx->node_id].vse_thread_start = true; src = tegra_hv_vse_get_dma_buf(cmac_ctx->node_id, TSEC_SRC_BUF_IDX, cmac_ctx->user_src_buf_size); if (!src) { pr_err("%s src buf is NULL\n", __func__); return -ENOMEM; } mac = tegra_hv_vse_get_dma_buf(cmac_ctx->node_id, TSEC_MAC_BUF_IDX, TEGRA_VIRTUAL_SE_AES_CMAC_DIGEST_SIZE); if (!mac) return -ENOMEM; fw_status = tegra_hv_vse_get_dma_buf(cmac_ctx->node_id, TSEC_FW_STATUS_BUF_IDX, RESULT_COMPARE_BUF_SIZE); if (!fw_status) return -ENOMEM; *((uint32_t *)fw_status->buf_ptr) = 0xFFFFFFFF; if (cmac_ctx->user_src_buf_size > 0) { err = copy_from_user(src->buf_ptr, cmac_ctx->user_src_buf, cmac_ctx->user_src_buf_size); if (err) { pr_err("%s(): Failed to copy src_buf: %d\n", __func__, err); goto free_mem; } } ivc_tx->tsec[0U].src_addr = src->buf_iova; ivc_tx->tsec[0U].dst_addr = mac->buf_iova; ivc_tx->tsec[0U].fw_status_addr = fw_status->buf_iova; ivc_tx->tsec[0U].src_buf_size = cmac_ctx->user_src_buf_size; ivc_tx->tsec[0U].keyslot = *((uint64_t *)cmac_ctx->aes_keyslot); if (cmac_ctx->request_type == TEGRA_HV_VSE_CMAC_SIGN) { ivc_tx->cmd = TEGRA_VIRTUAL_SE_CMD_TSEC_SIGN; } else { ivc_tx->cmd = TEGRA_VIRTUAL_SE_CMD_TSEC_VERIFY; err = copy_from_user(ivc_tx->tsec[0U].cmac_result, cmac_ctx->user_mac_buf, TEGRA_VIRTUAL_SE_AES_CMAC_DIGEST_SIZE); if (err) { pr_err("%s(): Failed to copy mac_buf: %d\n", __func__, err); err = -EINVAL; goto free_mem; } } priv_data_ptr = (struct tegra_vse_tag *)ivc_req_msg->ivc_hdr.tag; priv_data_ptr->priv_data = (unsigned int *)priv; priv->cmd = VIRTUAL_SE_PROCESS; priv->se_dev = se_dev; init_completion(&priv->alg_complete); err = tegra_hv_vse_safety_send_ivc_wait(se_dev, pivck, priv, ivc_req_msg, sizeof(struct tegra_virtual_se_ivc_msg_t), cmac_ctx->node_id); if (err) { dev_err(se_dev->dev, "failed to send data over ivc err %d\n", err); goto free_mem; } if (priv->rx_status != 0) { err = status_to_errno(priv->rx_status); dev_err(se_dev->dev, "%s: SE server returned error %u\n", __func__, priv->rx_status); goto free_mem; } if (cmac_ctx->request_type == TEGRA_HV_VSE_CMAC_VERIFY) { ivc_tx->cmd = TEGRA_VIRTUAL_SE_CMD_AES_CMD_GET_TSEC_VERIFY; priv->cmd = VIRTUAL_CMAC_PROCESS; init_completion(&priv->alg_complete); err = tegra_hv_vse_safety_send_ivc_wait(se_dev, pivck, priv, ivc_req_msg, sizeof(struct tegra_virtual_se_ivc_msg_t), cmac_ctx->node_id); if (err) { dev_err(se_dev->dev, "failed to send data over ivc err %d\n", err); goto free_mem; } } if (cmac_ctx->request_type == TEGRA_HV_VSE_CMAC_SIGN) { tsec_fw_err = (*((uint32_t *)fw_status->buf_ptr) & NVVSE_TSEC_CMD_STATUS_ERR_MASK); if (tsec_fw_err == 0U) { err = copy_to_user(cmac_ctx->user_mac_buf, mac->buf_ptr, TEGRA_VIRTUAL_SE_AES_CMAC_DIGEST_SIZE); if (err) { pr_err("%s(): Failed to copy mac_buf: %d\n", __func__, err); goto free_mem; } } else { err = -EINVAL; dev_err(se_dev->dev, "%s: TSEC FW returned error %u\n", __func__, tsec_fw_err); goto free_mem; } } else { if (priv->rx_status == 0) cmac_ctx->result = 0; else cmac_ctx->result = 1; } if ((priv->rx_status != 0) && (priv->rx_status != TEGRA_VIRTUAL_SE_ERR_MAC_INVALID)) { err = status_to_errno(priv->rx_status); dev_err(se_dev->dev, "%s: SE server returned error %u\n", __func__, priv->rx_status); } free_mem: devm_kfree(se_dev->dev, priv); devm_kfree(se_dev->dev, ivc_req_msg); return err; } static int tegra_hv_vse_safety_cmac_sv_op_hw_verify_supported( struct ahash_request *req, struct tegra_virtual_se_aes_cmac_context *cmac_ctx, bool is_last) { struct tegra_virtual_se_dev *se_dev = g_crypto_to_ivc_map[cmac_ctx->node_id].se_dev; struct tegra_virtual_se_ivc_hdr_t *ivc_hdr; struct tegra_virtual_se_ivc_tx_msg_t *ivc_tx; struct tegra_virtual_se_ivc_msg_t *ivc_req_msg; struct tegra_hv_ivc_cookie *pivck = g_crypto_to_ivc_map[cmac_ctx->node_id].ivck; int err = 0; struct tegra_vse_priv_data *priv = NULL; struct tegra_vse_tag *priv_data_ptr; u32 match_code = SE_HW_VALUE_MATCH_CODE; u32 mac_buf_size = 16; const struct tegra_vse_dma_buf *src, *mac, *comp; ivc_req_msg = devm_kzalloc(se_dev->dev, sizeof(*ivc_req_msg), GFP_KERNEL); if (!ivc_req_msg) return -ENOMEM; priv = devm_kzalloc(se_dev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) { devm_kfree(se_dev->dev, ivc_req_msg); return -ENOMEM; } src = tegra_hv_vse_get_dma_buf(cmac_ctx->node_id, AES_SRC_BUF_IDX, cmac_ctx->user_src_buf_size); if (!src) { pr_err("%s src buf is NULL\n", __func__); return -ENOMEM; } mac = tegra_hv_vse_get_dma_buf(cmac_ctx->node_id, AES_TAG_BUF_IDX, mac_buf_size); if (!mac) { pr_err("%s mac buf is NULL\n", __func__); return -ENOMEM; } ivc_tx = &ivc_req_msg->tx[0]; ivc_hdr = &ivc_req_msg->ivc_hdr; ivc_hdr->num_reqs = 1; ivc_hdr->header_magic[0] = 'N'; ivc_hdr->header_magic[1] = 'V'; ivc_hdr->header_magic[2] = 'D'; ivc_hdr->header_magic[3] = 'A'; if (cmac_ctx->request_type == TEGRA_HV_VSE_CMAC_VERIFY) { comp = tegra_hv_vse_get_dma_buf(cmac_ctx->node_id, AES_COMP_BUF_IDX, RESULT_COMPARE_BUF_SIZE); if (!comp) { pr_err("%s mac comp buf is NULL\n", __func__); return -ENOMEM; } } g_crypto_to_ivc_map[cmac_ctx->node_id].vse_thread_start = true; if (cmac_ctx->user_src_buf_size > 0) { err = copy_from_user(src->buf_ptr, cmac_ctx->user_src_buf, cmac_ctx->user_src_buf_size); if (err) { pr_err("%s(): Failed to copy src_buf: %d\n", __func__, err); goto free_mem; } } ivc_tx->aes.op_cmac_sv.src_addr = src->buf_iova; ivc_tx->aes.op_cmac_sv.src_buf_size = cmac_ctx->user_src_buf_size; ivc_hdr->engine = g_crypto_to_ivc_map[cmac_ctx->node_id].engine_id; if (cmac_ctx->request_type == TEGRA_HV_VSE_CMAC_SIGN) ivc_tx->cmd = TEGRA_VIRTUAL_SE_CMD_AES_CMAC_SIGN; else { ivc_tx->cmd = TEGRA_VIRTUAL_SE_CMD_AES_CMAC_VERIFY; ivc_tx->aes.op_cmac_sv.mac_comp_res_addr = comp->buf_iova; } memcpy(ivc_tx->aes.op_cmac_sv.keyslot, cmac_ctx->aes_keyslot, KEYSLOT_SIZE_BYTES); ivc_tx->aes.op_cmac_sv.key_length = cmac_ctx->keylen; ivc_tx->aes.op_cmac_sv.config = 0; if (cmac_ctx->b_is_sm4 == 1U) ivc_tx->aes.op_cmac_sv.sym_ciph = VSE_SYM_CIPH_SM4; else ivc_tx->aes.op_cmac_sv.sym_ciph = VSE_SYM_CIPH_AES; if (is_last == true) ivc_tx->aes.op_cmac_sv.config |= TEGRA_VIRTUAL_SE_AES_CMAC_SV_CONFIG_LASTREQ; if (cmac_ctx->is_first) { ivc_tx->aes.op_cmac_sv.config |= TEGRA_VIRTUAL_SE_AES_CMAC_SV_CONFIG_FIRSTREQ; if (cmac_ctx->request_type == TEGRA_HV_VSE_CMAC_VERIFY) { err = copy_from_user((uint8_t *)mac->buf_ptr, cmac_ctx->user_mac_buf, TEGRA_VIRTUAL_SE_AES_CMAC_DIGEST_SIZE); if (err) { pr_err("%s(): Failed to copy mac_buf: %d\n", __func__, err); goto free_mem; } } cmac_ctx->is_first = false; } ivc_tx->aes.op_cmac_sv.mac_addr = mac->buf_iova; priv_data_ptr = (struct tegra_vse_tag *)ivc_req_msg->ivc_hdr.tag; priv_data_ptr->priv_data = (unsigned int *)priv; priv->cmd = VIRTUAL_CMAC_PROCESS; priv->se_dev = se_dev; init_completion(&priv->alg_complete); err = tegra_hv_vse_safety_send_ivc_wait(se_dev, pivck, priv, ivc_req_msg, sizeof(struct tegra_virtual_se_ivc_msg_t), cmac_ctx->node_id); if (err) { dev_err(se_dev->dev, "failed to send data over ivc err %d\n", err); goto free_mem; } if (priv->rx_status != 0) { err = status_to_errno(priv->rx_status); dev_err(se_dev->dev, "%s: SE server returned error %u\n", __func__, priv->rx_status); goto free_mem; } if (cmac_ctx->request_type == TEGRA_HV_VSE_CMAC_SIGN) { if (priv->rx_status == 0) { err = copy_to_user(cmac_ctx->user_mac_buf, (uint8_t *)mac->buf_ptr, TEGRA_VIRTUAL_SE_AES_CMAC_DIGEST_SIZE); if (err) { pr_err("%s(): Failed to copy mac_buf: %d\n", __func__, err); goto free_mem; } } } else { if (memcmp((uint8_t *)comp->buf_ptr, &match_code, 4) == 0) cmac_ctx->result = 0; else cmac_ctx->result = 1; } free_mem: devm_kfree(se_dev->dev, priv); devm_kfree(se_dev->dev, ivc_req_msg); return err; } static int tegra_hv_vse_safety_cmac_sv_op(struct ahash_request *req, struct tegra_virtual_se_aes_cmac_context *cmac_ctx, bool is_last) { struct tegra_virtual_se_dev *se_dev = g_crypto_to_ivc_map[cmac_ctx->node_id].se_dev; struct tegra_virtual_se_ivc_hdr_t *ivc_hdr; struct tegra_virtual_se_ivc_tx_msg_t *ivc_tx; struct tegra_virtual_se_ivc_msg_t *ivc_req_msg; struct tegra_hv_ivc_cookie *pivck = g_crypto_to_ivc_map[cmac_ctx->node_id].ivck; u32 blocks_to_process, last_block_bytes = 0; unsigned int total_len; int err = 0; struct tegra_vse_priv_data *priv = NULL; struct tegra_vse_tag *priv_data_ptr; const struct tegra_vse_dma_buf *src; ivc_req_msg = devm_kzalloc(se_dev->dev, sizeof(*ivc_req_msg), GFP_KERNEL); if (!ivc_req_msg) return -ENOMEM; priv = devm_kzalloc(se_dev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) { devm_kfree(se_dev->dev, ivc_req_msg); return -ENOMEM; } blocks_to_process = cmac_ctx->user_src_buf_size / TEGRA_VIRTUAL_SE_AES_BLOCK_SIZE; /* num of bytes less than block size */ if ((cmac_ctx->user_src_buf_size % TEGRA_VIRTUAL_SE_AES_BLOCK_SIZE) || blocks_to_process == 0) { last_block_bytes = cmac_ctx->user_src_buf_size % TEGRA_VIRTUAL_SE_AES_BLOCK_SIZE; } else { /* decrement num of blocks */ blocks_to_process--; last_block_bytes = TEGRA_VIRTUAL_SE_AES_BLOCK_SIZE; } ivc_tx = &ivc_req_msg->tx[0]; ivc_hdr = &ivc_req_msg->ivc_hdr; ivc_hdr->num_reqs = 1; ivc_hdr->header_magic[0] = 'N'; ivc_hdr->header_magic[1] = 'V'; ivc_hdr->header_magic[2] = 'D'; ivc_hdr->header_magic[3] = 'A'; g_crypto_to_ivc_map[cmac_ctx->node_id].vse_thread_start = true; src = tegra_hv_vse_get_dma_buf(cmac_ctx->node_id, AES_SRC_BUF_IDX, cmac_ctx->user_src_buf_size); if (!src) { pr_err("%s src buf is NULL\n", __func__); err = -ENOMEM; goto free_mem; } /* first process all blocks except last block */ if (blocks_to_process) { total_len = blocks_to_process * TEGRA_VIRTUAL_SE_AES_BLOCK_SIZE; if (total_len > 0) { err = copy_from_user(src->buf_ptr, cmac_ctx->user_src_buf, total_len); if (err) { pr_err("%s(): Failed to copy src_buf: %d\n", __func__, err); goto free_mem; } } ivc_tx->aes.op_cmac_sv.src_addr = src->buf_iova; ivc_tx->aes.op_cmac_sv.src_buf_size = total_len; } ivc_tx->aes.op_cmac_sv.lastblock_len = last_block_bytes; if (cmac_ctx->b_is_sm4 == 1U) { ivc_tx->aes.op_cmac_sv.sym_ciph = VSE_SYM_CIPH_SM4; } else { ivc_tx->aes.op_cmac_sv.sym_ciph = VSE_SYM_CIPH_AES; } if (last_block_bytes > 0) { err = copy_from_user(ivc_tx->aes.op_cmac_sv.lastblock, &cmac_ctx->user_src_buf[blocks_to_process * TEGRA_VIRTUAL_SE_AES_BLOCK_SIZE], last_block_bytes); if (err) { pr_err("%s(): Failed to copy src_buf: %d\n", __func__, err); goto free_mem; } } ivc_hdr->engine = g_crypto_to_ivc_map[cmac_ctx->node_id].engine_id; if (cmac_ctx->request_type == TEGRA_HV_VSE_CMAC_SIGN) ivc_tx->cmd = TEGRA_VIRTUAL_SE_CMD_AES_CMAC_SIGN; else ivc_tx->cmd = TEGRA_VIRTUAL_SE_CMD_AES_CMAC_VERIFY; memcpy(ivc_tx->aes.op_cmac_sv.keyslot, cmac_ctx->aes_keyslot, KEYSLOT_SIZE_BYTES); ivc_tx->aes.op_cmac_sv.key_length = cmac_ctx->keylen; ivc_tx->aes.op_cmac_sv.src_buf_size = blocks_to_process * TEGRA_VIRTUAL_SE_AES_BLOCK_SIZE; ivc_tx->aes.op_cmac_sv.config = 0; if (is_last == true) ivc_tx->aes.op_cmac_sv.config |= TEGRA_VIRTUAL_SE_AES_CMAC_SV_CONFIG_LASTREQ; if (cmac_ctx->is_first) { ivc_tx->aes.op_cmac_sv.config |= TEGRA_VIRTUAL_SE_AES_CMAC_SV_CONFIG_FIRSTREQ; if (cmac_ctx->request_type == TEGRA_HV_VSE_CMAC_VERIFY) { err = copy_from_user(ivc_tx->aes.op_cmac_sv.cmac_result, cmac_ctx->user_mac_buf, TEGRA_VIRTUAL_SE_AES_CMAC_DIGEST_SIZE); if (err) { pr_err("%s(): Failed to copy mac_buf: %d\n", __func__, err); goto free_mem; } } cmac_ctx->is_first = false; } priv_data_ptr = (struct tegra_vse_tag *)ivc_req_msg->ivc_hdr.tag; priv_data_ptr->priv_data = (unsigned int *)priv; priv->cmd = VIRTUAL_SE_PROCESS; priv->se_dev = se_dev; init_completion(&priv->alg_complete); err = tegra_hv_vse_safety_send_ivc_wait(se_dev, pivck, priv, ivc_req_msg, sizeof(struct tegra_virtual_se_ivc_msg_t), cmac_ctx->node_id); if (err) { dev_err(se_dev->dev, "failed to send data over ivc err %d\n", err); goto free_mem; } if (priv->rx_status != 0) { err = status_to_errno(priv->rx_status); dev_err(se_dev->dev, "%s: SE server returned error %u\n", __func__, priv->rx_status); goto free_mem; } if (is_last) { if (cmac_ctx->request_type == TEGRA_HV_VSE_CMAC_SIGN) ivc_tx->cmd = TEGRA_VIRTUAL_SE_CMD_AES_CMD_GET_CMAC_SIGN; else ivc_tx->cmd = TEGRA_VIRTUAL_SE_CMD_AES_CMD_GET_CMAC_VERIFY; priv->cmd = VIRTUAL_CMAC_PROCESS; init_completion(&priv->alg_complete); err = tegra_hv_vse_safety_send_ivc_wait(se_dev, pivck, priv, ivc_req_msg, sizeof(struct tegra_virtual_se_ivc_msg_t), cmac_ctx->node_id); if (err) { dev_err(se_dev->dev, "failed to send data over ivc err %d\n", err); goto free_mem; } if (cmac_ctx->request_type == TEGRA_HV_VSE_CMAC_SIGN) { if (priv->rx_status == 0) { err = copy_to_user(cmac_ctx->user_mac_buf, priv->cmac.data, TEGRA_VIRTUAL_SE_AES_CMAC_DIGEST_SIZE); if (err) { pr_err("%s(): Failed to copy mac_buf: %d\n", __func__, err); goto free_mem; } } } else { if (priv->rx_status == 0) cmac_ctx->result = 0; else cmac_ctx->result = 1; } if ((priv->rx_status != 0) && (priv->rx_status != TEGRA_VIRTUAL_SE_ERR_MAC_INVALID)) { err = status_to_errno(priv->rx_status); dev_err(se_dev->dev, "%s: SE server returned error %u\n", __func__, priv->rx_status); } } free_mem: devm_kfree(se_dev->dev, priv); devm_kfree(se_dev->dev, ivc_req_msg); return err; } static int tegra_hv_vse_safety_cmac_init(struct ahash_request *req) { struct tegra_virtual_se_dev *se_dev; struct crypto_ahash *tfm; struct tegra_virtual_se_aes_cmac_context *cmac_ctx; if (!req) { pr_err("%s AES-CMAC request not valid\n", __func__); return -EINVAL; } tfm = crypto_ahash_reqtfm(req); if (!tfm) { pr_err("%s AES-CMAC transform not valid\n", __func__); return -EINVAL; } cmac_ctx = crypto_ahash_ctx(tfm); if (!cmac_ctx) { pr_err("%s AES-CMAC req_ctx not valid\n", __func__); return -EINVAL; } se_dev = g_crypto_to_ivc_map[cmac_ctx->node_id].se_dev; /* Return error if engine is in suspended state */ if (atomic_read(&se_dev->se_suspended)) return -ENODEV; cmac_ctx->digest_size = crypto_ahash_digestsize(tfm); cmac_ctx->is_first = true; cmac_ctx->req_context_initialized = true; return 0; } static void tegra_hv_vse_safety_cmac_req_deinit(struct ahash_request *req) { struct tegra_virtual_se_aes_cmac_context *cmac_ctx; cmac_ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(req)); if (!cmac_ctx) { pr_err("%s AES-CMAC req_ctx not valid\n", __func__); return; } cmac_ctx->req_context_initialized = false; } static int tegra_hv_vse_safety_cmac_update(struct ahash_request *req) { struct tegra_virtual_se_aes_cmac_context *cmac_ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(req)); struct tegra_virtual_se_dev *se_dev; int ret = 0; if (!req) { pr_err("%s AES-CMAC request not valid\n", __func__); return -EINVAL; } cmac_ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(req)); if (!cmac_ctx) { pr_err("%s AES-CMAC req_ctx not valid\n", __func__); return -EINVAL; } ret = tegra_vse_validate_cmac_params(cmac_ctx, false); if (ret) { pr_err("%s: invalid AES CMAC params\n", __func__); return ret; } se_dev = g_crypto_to_ivc_map[cmac_ctx->node_id].se_dev; /* Return error if engine is in suspended state */ if (atomic_read(&se_dev->se_suspended)) return -ENODEV; /* Do not process data in given request */ if (se_dev->chipdata->cmac_hw_verify_supported) ret = tegra_hv_vse_safety_cmac_sv_op_hw_verify_supported(req, cmac_ctx, false); else ret = tegra_hv_vse_safety_cmac_sv_op(req, cmac_ctx, false); if (ret) dev_err(se_dev->dev, "tegra_se_cmac_update failed - %d\n", ret); return ret; } static int tegra_hv_tsec_safety_cmac_update(struct ahash_request *req) { pr_err("%s cmac_update is not supported for tsec\n", __func__); return -EINVAL; } static int tegra_hv_vse_safety_cmac_final(struct ahash_request *req) { struct tegra_virtual_se_aes_cmac_context *cmac_ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(req)); struct tegra_virtual_se_dev *se_dev = g_crypto_to_ivc_map[cmac_ctx->node_id].se_dev; /* Return error if engine is in suspended state */ if (atomic_read(&se_dev->se_suspended)) return -ENODEV; pr_err("%s cmac_final is not supported\n", __func__); return -EINVAL; } static int tegra_hv_vse_safety_cmac_finup(struct ahash_request *req) { struct tegra_virtual_se_aes_cmac_context *cmac_ctx = NULL; struct tegra_virtual_se_dev *se_dev; int ret = 0; if (!req) { pr_err("%s AES-CMAC request not valid\n", __func__); return -EINVAL; } cmac_ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(req)); if (!cmac_ctx) { pr_err("%s AES-CMAC req_ctx not valid\n", __func__); return -EINVAL; } ret = tegra_vse_validate_cmac_params(cmac_ctx, true); if (ret) { pr_err("%s: invalid AES CMAC params\n", __func__); return ret; } se_dev = g_crypto_to_ivc_map[cmac_ctx->node_id].se_dev; /* Return error if engine is in suspended state */ if (atomic_read(&se_dev->se_suspended)) return -ENODEV; /* Do not process data in given request */ if (se_dev->chipdata->cmac_hw_verify_supported) ret = tegra_hv_vse_safety_cmac_sv_op_hw_verify_supported(req, cmac_ctx, true); else ret = tegra_hv_vse_safety_cmac_sv_op(req, cmac_ctx, true); if (ret) dev_err(se_dev->dev, "tegra_se_cmac_finup failed - %d\n", ret); tegra_hv_vse_safety_cmac_req_deinit(req); return ret; } static int tegra_hv_tsec_safety_cmac_finup(struct ahash_request *req) { struct tegra_virtual_se_aes_cmac_context *cmac_ctx = NULL; struct tegra_virtual_se_dev *se_dev = NULL; int ret = 0; if (!req) { pr_err("%s TSEC request not valid\n", __func__); return -EINVAL; } cmac_ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(req)); if (!cmac_ctx) { pr_err("%s TSEC req_ctx not valid\n", __func__); return -EINVAL; } se_dev = g_crypto_to_ivc_map[cmac_ctx->node_id].se_dev; /* Return error if engine is in suspended state */ if (atomic_read(&se_dev->se_suspended)) return -ENODEV; ret = tegra_hv_vse_safety_tsec_sv_op(req, cmac_ctx); if (ret) dev_err(se_dev->dev, "tegra_se_tsec_finup failed - %d\n", ret); tegra_hv_vse_safety_cmac_req_deinit(req); return ret; } static int tegra_hv_vse_safety_cmac_digest(struct ahash_request *req) { struct tegra_virtual_se_aes_cmac_context *cmac_ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(req)); struct tegra_virtual_se_dev *se_dev = g_crypto_to_ivc_map[cmac_ctx->node_id].se_dev; /* Return error if engine is in suspended state */ if (atomic_read(&se_dev->se_suspended)) return -ENODEV; return tegra_hv_vse_safety_cmac_init(req) ?: tegra_hv_vse_safety_cmac_final(req); } int tegra_hv_vse_safety_tsec_get_keyload_status(uint32_t node_id, uint32_t *err_code) { struct tegra_virtual_se_dev *se_dev = NULL; struct tegra_virtual_se_ivc_hdr_t *ivc_hdr = NULL; struct tegra_virtual_se_ivc_tx_msg_t *ivc_tx = NULL; struct tegra_hv_ivc_cookie *pivck = NULL; struct tegra_virtual_se_ivc_msg_t *ivc_req_msg = NULL; struct tegra_vse_priv_data *priv = NULL; struct tegra_vse_tag *priv_data_ptr = NULL; int err = 0; if (node_id >= MAX_NUMBER_MISC_DEVICES) return -ENODEV; se_dev = g_crypto_to_ivc_map[node_id].se_dev; pivck = g_crypto_to_ivc_map[node_id].ivck; ivc_req_msg = devm_kzalloc(se_dev->dev, sizeof(*ivc_req_msg), GFP_KERNEL); if (!ivc_req_msg) return -ENOMEM; priv = devm_kzalloc(se_dev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) { devm_kfree(se_dev->dev, ivc_req_msg); dev_err(se_dev->dev, "Priv Data allocation failed\n"); return -ENOMEM; } ivc_hdr = &ivc_req_msg->ivc_hdr; ivc_tx = &ivc_req_msg->tx[0]; ivc_hdr->num_reqs = 1; ivc_hdr->header_magic[0] = 'N'; ivc_hdr->header_magic[1] = 'V'; ivc_hdr->header_magic[2] = 'D'; ivc_hdr->header_magic[3] = 'A'; g_crypto_to_ivc_map[node_id].vse_thread_start = true; ivc_hdr->engine = g_crypto_to_ivc_map[node_id].engine_id; ivc_tx->cmd = TEGRA_VIRTUAL_TSEC_CMD_GET_KEYLOAD_STATUS; priv_data_ptr = (struct tegra_vse_tag *)ivc_req_msg->ivc_hdr.tag; priv_data_ptr->priv_data = (unsigned int *)priv; priv->cmd = VIRTUAL_SE_PROCESS; priv->se_dev = se_dev; init_completion(&priv->alg_complete); err = tegra_hv_vse_safety_send_ivc_wait(se_dev, pivck, priv, ivc_req_msg, sizeof(struct tegra_virtual_se_ivc_msg_t), node_id); if (err) { dev_err(se_dev->dev, "failed to send data over ivc err %d\n", err); goto free_exit; } if (priv->rx_status != 0U) { err = -EINVAL; if (priv->rx_status == VSE_MSG_ERR_TSEC_KEYLOAD_FAILED) *err_code = NVVSE_STATUS_SE_SERVER_TSEC_KEYLOAD_FAILED; else if (priv->rx_status == VSE_MSG_ERR_TSEC_KEYLOAD_STATUS_CHECK_TIMEOUT) *err_code = NVVSE_STATUS_SE_SERVER_TSEC_KEYLOAD_TIMEOUT; else *err_code = NVVSE_STATUS_SE_SERVER_ERROR; } else { err = 0; *err_code = 0U; } free_exit: devm_kfree(se_dev->dev, priv); devm_kfree(se_dev->dev, ivc_req_msg); return err; } EXPORT_SYMBOL(tegra_hv_vse_safety_tsec_get_keyload_status); static int tegra_hv_vse_safety_validate_membuf_common(struct tegra_virtual_se_membuf_context *ctx) { struct tegra_virtual_se_dev *se_dev = NULL; int err = 0; if (!ctx) { pr_err("%s ctx is null\n", __func__); err = -EINVAL; goto exit; } if (ctx->node_id >= MAX_NUMBER_MISC_DEVICES) { pr_err("%s node_id is invalid\n", __func__); err = -ENODEV; goto exit; } se_dev = g_crypto_to_ivc_map[ctx->node_id].se_dev; if (ctx->fd < 0) { dev_err(se_dev->dev, "%s fd is invalid\n", __func__); err = -EINVAL; goto exit; } exit: return err; } int tegra_hv_vse_safety_map_membuf(struct tegra_virtual_se_membuf_context *ctx) { struct tegra_virtual_se_dev *se_dev = NULL; struct tegra_vse_membuf_ctx *membuf_ctx = NULL; struct dma_buf *dmabuf; struct dma_buf_attachment *attach; struct sg_table *sgt; dma_addr_t dma_addr; dma_addr_t phys_addr; uint32_t i; int err = 0; err = tegra_hv_vse_safety_validate_membuf_common(ctx); if (err != 0) return err; se_dev = g_crypto_to_ivc_map[ctx->node_id].se_dev; if (g_node_dma[ctx->node_id].mapped_membuf_count >= MAX_ZERO_COPY_BUFS) { dev_err(se_dev->dev, "%s no free membuf_ctx\n", __func__); return -ENOMEM; } for (i = 0U; i < MAX_ZERO_COPY_BUFS; i++) { membuf_ctx = &g_node_dma[ctx->node_id].membuf_ctx[i]; if (membuf_ctx->fd == -1) break; } if (i == MAX_ZERO_COPY_BUFS) { dev_err(se_dev->dev, "%s no free membuf_ctx\n", __func__); return -ENOMEM; } dmabuf = dma_buf_get(ctx->fd); if (IS_ERR_OR_NULL(dmabuf)) { dev_err(se_dev->dev, "%s dma_buf_get failed\n", __func__); return -EFAULT; } membuf_ctx->dmabuf = dmabuf; attach = dma_buf_attach(dmabuf, se_dev->dev); if (IS_ERR_OR_NULL(attach)) { err = PTR_ERR(dmabuf); dev_err(se_dev->dev, "%s dma_buf_attach failed\n", __func__); goto buf_attach_err; } membuf_ctx->attach = attach; sgt = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL); if (IS_ERR_OR_NULL(sgt)) { err = PTR_ERR(sgt); dev_err(se_dev->dev, "%s dma_buf_map_attachment failed\n", __func__); goto buf_map_err; } phys_addr = sg_phys(sgt->sgl); dma_addr = sg_dma_address(sgt->sgl); if (!dma_addr) dma_addr = phys_addr; ctx->iova = dma_addr; membuf_ctx->fd = ctx->fd; g_node_dma[ctx->node_id].mapped_membuf_count += 1U; return err; buf_map_err: dma_buf_detach(dmabuf, attach); buf_attach_err: dma_buf_put(dmabuf); membuf_ctx->fd = -1; return err; } EXPORT_SYMBOL(tegra_hv_vse_safety_map_membuf); void tegra_hv_vse_safety_unmap_all_membufs(uint32_t node_id) { struct tegra_vse_membuf_ctx *membuf_ctx = NULL; uint32_t i; if (node_id >= MAX_NUMBER_MISC_DEVICES) { pr_err("%s node_id is invalid\n", __func__); return; } for (i = 0U; i < MAX_ZERO_COPY_BUFS; i++) { membuf_ctx = &g_node_dma[node_id].membuf_ctx[i]; if (membuf_ctx->fd == -1) continue; dma_buf_detach(membuf_ctx->dmabuf, membuf_ctx->attach); dma_buf_put(membuf_ctx->dmabuf); membuf_ctx->fd = -1; } g_node_dma[node_id].mapped_membuf_count = 0U; } EXPORT_SYMBOL(tegra_hv_vse_safety_unmap_all_membufs); int tegra_hv_vse_safety_unmap_membuf(struct tegra_virtual_se_membuf_context *ctx) { struct tegra_virtual_se_dev *se_dev; struct tegra_vse_membuf_ctx *membuf_ctx = NULL; uint32_t i; int err = 0; err = tegra_hv_vse_safety_validate_membuf_common(ctx); if (err != 0) return err; se_dev = g_crypto_to_ivc_map[ctx->node_id].se_dev; if (g_node_dma[ctx->node_id].mapped_membuf_count == 0U) { dev_err(se_dev->dev, "%s no mapped membuf to free\n", __func__); return -EINVAL; } for (i = 0U; i < MAX_ZERO_COPY_BUFS; i++) { membuf_ctx = &g_node_dma[ctx->node_id].membuf_ctx[i]; if (membuf_ctx->fd == ctx->fd) break; } if (i == MAX_ZERO_COPY_BUFS) { dev_err(se_dev->dev, "%s fd not found\n", __func__); return -EINVAL; } dma_buf_detach(membuf_ctx->dmabuf, membuf_ctx->attach); dma_buf_put(membuf_ctx->dmabuf); membuf_ctx->fd = -1; g_node_dma[ctx->node_id].mapped_membuf_count -= 1U; return 0; } EXPORT_SYMBOL(tegra_hv_vse_safety_unmap_membuf); static int tegra_hv_vse_safety_cmac_setkey(struct crypto_ahash *tfm, const u8 *key, unsigned int keylen) { struct tegra_virtual_se_aes_cmac_context *ctx = crypto_tfm_ctx(crypto_ahash_tfm(tfm)); struct tegra_virtual_se_dev *se_dev; int err = 0; s8 label[TEGRA_VIRTUAL_SE_AES_MAX_KEY_SIZE]; bool is_keyslot_label; if (!ctx) return -EINVAL; se_dev = g_crypto_to_ivc_map[ctx->node_id].se_dev; if ((keylen != 16) && (keylen != 32)) { dev_err(se_dev->dev, "%s: Unsupported key length: %d", __func__, keylen); return -EINVAL; } /* format: 'NVSEAES 1234567\0' */ is_keyslot_label = sscanf(key, "%s", label) == 1 && !strcmp(label, TEGRA_VIRTUAL_SE_AES_KEYSLOT_LABEL); if (is_keyslot_label) { ctx->keylen = keylen; memcpy(ctx->aes_keyslot, key + KEYSLOT_OFFSET_BYTES, KEYSLOT_SIZE_BYTES); ctx->is_key_slot_allocated = true; } else { dev_err(se_dev->dev, "%s: Invalid keyslot label %s\n", __func__, key); return -EINVAL; } return err; } static int tegra_hv_vse_safety_cmac_cra_init(struct crypto_tfm *tfm) { crypto_ahash_set_reqsize(__crypto_ahash_cast(tfm), sizeof(struct tegra_virtual_se_aes_cmac_context)); return 0; } static void tegra_hv_vse_safety_cmac_cra_exit(struct crypto_tfm *tfm) { /* nothing to do as user releases the keyslot through tzvault TA */ } static int tegra_hv_vse_safety_aes_setkey(struct crypto_skcipher *tfm, const u8 *key, u32 keylen) { struct tegra_virtual_se_aes_context *ctx = crypto_skcipher_ctx(tfm); struct tegra_virtual_se_dev *se_dev; s8 label[TEGRA_VIRTUAL_SE_AES_MAX_KEY_SIZE]; int err = 0; bool is_keyslot_label; if (!ctx) return -EINVAL; se_dev = g_crypto_to_ivc_map[ctx->node_id].se_dev; if ((keylen != 16) && (keylen != 32)) { dev_err(se_dev->dev, "%s: Unsupported key length: %d", __func__, keylen); return -EINVAL; } /* format: 'NVSEAES 1234567\0' */ is_keyslot_label = sscanf(key, "%s", label) == 1 && !strcmp(label, TEGRA_VIRTUAL_SE_AES_KEYSLOT_LABEL); if (is_keyslot_label) { ctx->keylen = keylen; memcpy(ctx->aes_keyslot, key + KEYSLOT_OFFSET_BYTES, KEYSLOT_SIZE_BYTES); ctx->is_key_slot_allocated = true; } else { dev_err(se_dev->dev, "%s: Invalid keyslot label %s", __func__, key); err = -EINVAL; } return err; } static int tegra_hv_vse_safety_rng_drbg_init(struct crypto_tfm *tfm) { return 0; } static void tegra_hv_vse_safety_rng_drbg_exit(struct crypto_tfm *tfm) { return; } static int tegra_hv_vse_safety_get_random(struct tegra_virtual_se_rng_context *rng_ctx, u8 *rdata, unsigned int dlen, enum rng_call is_hw_req) { struct tegra_virtual_se_dev *se_dev = g_crypto_to_ivc_map[rng_ctx->node_id].se_dev; u8 *rdata_addr; int err = 0, j, num_blocks, data_len = 0; struct tegra_virtual_se_ivc_tx_msg_t *ivc_tx; struct tegra_hv_ivc_cookie *pivck = g_crypto_to_ivc_map[rng_ctx->node_id].ivck; struct tegra_virtual_se_ivc_msg_t *ivc_req_msg; struct tegra_virtual_se_ivc_hdr_t *ivc_hdr = NULL; struct tegra_vse_priv_data *priv = NULL; struct tegra_vse_tag *priv_data_ptr; const struct tegra_vse_dma_buf *src; if (atomic_read(&se_dev->se_suspended)) return -ENODEV; if (dlen == 0) { return -EINVAL; } num_blocks = (dlen / TEGRA_VIRTUAL_SE_RNG_DT_SIZE); data_len = (dlen % TEGRA_VIRTUAL_SE_RNG_DT_SIZE); if (data_len == 0) num_blocks = num_blocks - 1; ivc_req_msg = devm_kzalloc(se_dev->dev, sizeof(*ivc_req_msg), GFP_KERNEL); if (!ivc_req_msg) return -ENOMEM; priv = devm_kzalloc(se_dev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) { dev_err(se_dev->dev, "Priv Data allocation failed\n"); devm_kfree(se_dev->dev, ivc_req_msg); return 0; } if (is_hw_req == CRYPTODEV_RNG) { src = tegra_hv_vse_get_dma_buf(rng_ctx->node_id, AES_SRC_BUF_IDX, TEGRA_VIRTUAL_SE_RNG_DT_SIZE); if (!src) { pr_err("%s src is NULL\n", __func__); return -ENOMEM; } } else src = &rng_ctx->hwrng_dma_buf; ivc_tx = &ivc_req_msg->tx[0]; ivc_hdr = &ivc_req_msg->ivc_hdr; ivc_hdr->num_reqs = 1; ivc_hdr->header_magic[0] = 'N'; ivc_hdr->header_magic[1] = 'V'; ivc_hdr->header_magic[2] = 'D'; ivc_hdr->header_magic[3] = 'A'; ivc_hdr->engine = g_crypto_to_ivc_map[rng_ctx->node_id].engine_id; priv_data_ptr = (struct tegra_vse_tag *)ivc_hdr->tag; priv_data_ptr->priv_data = (unsigned int *)priv; priv->cmd = VIRTUAL_SE_PROCESS; priv->se_dev = se_dev; ivc_tx->cmd = TEGRA_VIRTUAL_SE_CMD_AES_RNG_DBRG; for (j = 0; j <= num_blocks; j++) { ivc_tx->aes.op_rng.dst_addr.lo = src->buf_iova & 0xFFFFFFFF; ivc_tx->aes.op_rng.dst_addr.hi = (src->buf_iova >> 32) | TEGRA_VIRTUAL_SE_RNG_DT_SIZE; init_completion(&priv->alg_complete); g_crypto_to_ivc_map[rng_ctx->node_id].vse_thread_start = true; err = tegra_hv_vse_safety_send_ivc_wait(se_dev, pivck, priv, ivc_req_msg, sizeof(struct tegra_virtual_se_ivc_msg_t), rng_ctx->node_id); if (err) { dev_err(se_dev->dev, "failed to send data over ivc err %d\n", err); goto exit; } rdata_addr = (rdata + (j * TEGRA_VIRTUAL_SE_RNG_DT_SIZE)); if (data_len && num_blocks == j) { memcpy(rdata_addr, src->buf_ptr, data_len); } else { memcpy(rdata_addr, src->buf_ptr, TEGRA_VIRTUAL_SE_RNG_DT_SIZE); } } exit: devm_kfree(se_dev->dev, priv); devm_kfree(se_dev->dev, ivc_req_msg); return dlen; } static int tegra_hv_vse_safety_rng_drbg_get_random(struct crypto_rng *tfm, const u8 *src, unsigned int slen, u8 *rdata, unsigned int dlen) { struct tegra_virtual_se_rng_context *rng_ctx = crypto_rng_ctx(tfm); int ret = 0; ret = tegra_vse_validate_aes_rng_param(rng_ctx); if (ret) return ret; return tegra_hv_vse_safety_get_random(crypto_rng_ctx(tfm), rdata, dlen, CRYPTODEV_RNG); } static int tegra_hv_vse_safety_rng_drbg_reset(struct crypto_rng *tfm, const u8 *seed, unsigned int slen) { return 0; } static int tegra_vse_aes_gcm_setkey(struct crypto_aead *tfm, const u8 *key, u32 keylen) { /* copied from normal aes keyset, will remove if no modification needed*/ struct tegra_virtual_se_aes_context *ctx = crypto_aead_ctx(tfm); struct tegra_virtual_se_dev *se_dev; s8 label[TEGRA_VIRTUAL_SE_AES_MAX_KEY_SIZE]; int err = 0; bool is_keyslot_label; if (!ctx) return -EINVAL; se_dev = g_crypto_to_ivc_map[ctx->node_id].se_dev; if ((keylen != 16) && (keylen != 32)) { dev_err(se_dev->dev, "%s: Unsupported key length: %d", __func__, keylen); return -EINVAL; } /* format: 'NVSEAES 1234567\0' */ is_keyslot_label = sscanf(key, "%s", label) == 1 && !strcmp(label, TEGRA_VIRTUAL_SE_AES_KEYSLOT_LABEL); if (is_keyslot_label) { ctx->keylen = keylen; memcpy(ctx->aes_keyslot, key + KEYSLOT_OFFSET_BYTES, KEYSLOT_SIZE_BYTES); ctx->is_key_slot_allocated = true; } else { dev_err(se_dev->dev, "%s: Invalid keyslot label %s\n", __func__, key); err = -EINVAL; } return err; } static int tegra_vse_aes_gcm_setauthsize(struct crypto_aead *tfm, unsigned int authsize) { struct tegra_virtual_se_aes_context *ctx = crypto_aead_ctx(tfm); switch (authsize) { case 16: ctx->user_tag_buf_size = authsize; break; default: return -EINVAL; } return 0; } static int tegra_vse_aes_gcm_init(struct crypto_aead *tfm) { return 0; } static void tegra_vse_aes_gcm_exit(struct crypto_aead *tfm) { /* nothing to do as user unloads the key manually with tzvault*/ } static int tegra_vse_aes_gcm_check_params(struct aead_request *req) { struct crypto_aead *tfm = crypto_aead_reqtfm(req); struct tegra_virtual_se_aes_context *aes_ctx = crypto_aead_ctx(tfm); if (!tfm) { pr_err("%s: transform not valid\n", __func__); return -EINVAL; } if (!aes_ctx) { pr_err("%s: aes ctx invalid\n", __func__); return -EINVAL; } if (aes_ctx->node_id >= MAX_NUMBER_MISC_DEVICES) { pr_err("%s: Node id is not valid\n", __func__); return -EINVAL; } if ((aes_ctx->user_aad_buf_size > 0 && aes_ctx->user_aad_buf == NULL) || (aes_ctx->user_aad_buf_size > TEGRA_VIRTUAL_SE_MAX_SUPPORTED_BUFLEN)) { pr_err("%s: aad buf is invalid\n", __func__); return -EINVAL; } if ((aes_ctx->user_src_buf_size > 0 && aes_ctx->user_src_buf == NULL) || (aes_ctx->user_src_buf_size > TEGRA_VIRTUAL_SE_MAX_SUPPORTED_BUFLEN)) { pr_err("%s: src buf is invalid\n", __func__); return -EINVAL; } if (aes_ctx->user_src_buf_size > 0 && aes_ctx->user_dst_buf == NULL) { pr_err("%s: dst buf is NULL\n", __func__); return -EINVAL; } if ((aes_ctx->user_tag_buf_size > 0 && aes_ctx->user_tag_buf == NULL) || (aes_ctx->user_tag_buf_size != TEGRA_VIRTUAL_SE_AES_GCM_TAG_SIZE)) { pr_err("%s: tag buf is invalid\n", __func__); return -EINVAL; } if (unlikely(!aes_ctx->is_key_slot_allocated)) { pr_err("%s: AES Key slot not allocated\n", __func__); return -EINVAL; } return 0; } static int tegra_vse_aes_gcm_enc_dec(struct aead_request *req, struct tegra_virtual_se_aes_context *aes_ctx, bool encrypt) { struct crypto_aead *tfm = crypto_aead_reqtfm(req); struct tegra_virtual_se_dev *se_dev = g_crypto_to_ivc_map[aes_ctx->node_id].se_dev; struct tegra_virtual_se_ivc_msg_t *ivc_req_msg = NULL; struct tegra_virtual_se_ivc_hdr_t *ivc_hdr; struct tegra_virtual_se_ivc_tx_msg_t *ivc_tx; struct tegra_hv_ivc_cookie *pivck = g_crypto_to_ivc_map[aes_ctx->node_id].ivck; struct tegra_vse_priv_data *priv = NULL; struct tegra_vse_tag *priv_data_ptr; int err = 0; const struct tegra_vse_dma_buf *src, *aad, *tag; if (aes_ctx->user_aad_buf_size > 0) { aad = tegra_hv_vse_get_dma_buf(aes_ctx->node_id, AES_AAD_BUF_IDX, aes_ctx->user_aad_buf_size); if (!aad) { pr_err("%s aad_buf is NULL\n", __func__); err = -ENOMEM; goto free_exit; } err = copy_from_user(aad->buf_ptr, aes_ctx->user_aad_buf, aes_ctx->user_aad_buf_size); if (err) { pr_err("%s(): Failed to copy aad data: %d\n", __func__, err); goto free_exit; } } if (aes_ctx->user_src_buf_size > 0) { if (!encrypt) if (aes_ctx->user_src_buf_size > g_crypto_to_ivc_map[aes_ctx->node_id].mempool.buf_len) src = &g_node_dma[aes_ctx->node_id].gpc_dma_buf; else src = &g_crypto_to_ivc_map[aes_ctx->node_id].mempool; else src = tegra_hv_vse_get_dma_buf(aes_ctx->node_id, AES_SRC_BUF_IDX, aes_ctx->user_src_buf_size); if (!src) { pr_err("%s enc src_buf is NULL\n", __func__); err = -ENOMEM; goto free_exit; } err = copy_from_user(src->buf_ptr, aes_ctx->user_src_buf, aes_ctx->user_src_buf_size); if (err) { pr_err("%s(): Failed to copy src_buf: %d\n", __func__, err); goto free_exit; } } if (encrypt) { tag = tegra_hv_vse_get_dma_buf(aes_ctx->node_id, AES_TAG_BUF_IDX, TEGRA_VIRTUAL_SE_AES_GCM_TAG_IV_SIZE); if (!tag->buf_ptr) { pr_err("%s tag_buf is NULL\n", __func__); err = -ENOMEM; goto free_exit; } } priv = devm_kzalloc(se_dev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) { err = -ENOMEM; goto free_exit; } ivc_req_msg = devm_kzalloc(se_dev->dev, sizeof(*ivc_req_msg), GFP_KERNEL); if (!ivc_req_msg) { err = -ENOMEM; goto free_exit; } ivc_tx = &ivc_req_msg->tx[0]; ivc_hdr = &ivc_req_msg->ivc_hdr; ivc_hdr->num_reqs = 1; ivc_hdr->header_magic[0] = 'N'; ivc_hdr->header_magic[1] = 'V'; ivc_hdr->header_magic[2] = 'D'; ivc_hdr->header_magic[3] = 'A'; ivc_hdr->engine = g_crypto_to_ivc_map[aes_ctx->node_id].engine_id; priv_data_ptr = (struct tegra_vse_tag *)ivc_hdr->tag; priv_data_ptr->priv_data = (unsigned int *)priv; priv->se_dev = se_dev; g_crypto_to_ivc_map[aes_ctx->node_id].vse_thread_start = true; memcpy(ivc_tx->aes.op_gcm.keyslot, aes_ctx->aes_keyslot, KEYSLOT_SIZE_BYTES); ivc_tx->aes.op_gcm.key_length = aes_ctx->keylen; if (encrypt) { /* * If aes_ctx->iv[0] is 1 and the request is for AES CBC/CTR encryption, * it means that generation of random IV is required. * IV generation is not required if user nonce is provided. */ if (aes_ctx->iv[0] == 1 && aes_ctx->user_nonce == 0U) { //Random IV generation is required ivc_tx->cmd = TEGRA_VIRTUAL_SE_CMD_AES_ENCRYPT_INIT; priv->cmd = VIRTUAL_SE_PROCESS; init_completion(&priv->alg_complete); err = tegra_hv_vse_safety_send_ivc_wait(se_dev, pivck, priv, ivc_req_msg, sizeof(struct tegra_virtual_se_ivc_msg_t), aes_ctx->node_id); if (err) { dev_err(se_dev->dev, "failed to send data over ivc err %d\n", err); goto free_exit; } err = status_to_errno(priv->rx_status); if (err) { dev_err(se_dev->dev, "\n %s IV generation failed %d\n", __func__, err); goto free_exit; } priv->cmd = VIRTUAL_SE_AES_GCM_ENC_PROCESS; } else { priv->cmd = VIRTUAL_SE_PROCESS; } ivc_tx->cmd = TEGRA_VIRTUAL_SE_CMD_AES_GCM_CMD_ENCRYPT; } else { priv->cmd = VIRTUAL_SE_PROCESS; ivc_tx->cmd = TEGRA_VIRTUAL_SE_CMD_AES_GCM_CMD_DECRYPT; } if (!encrypt) { /* copy iv for decryption*/ memcpy(ivc_tx->aes.op_gcm.iv, aes_ctx->iv, crypto_aead_ivsize(tfm)); /* copy expected tag */ err = copy_from_user(ivc_tx->aes.op_gcm.expected_tag, aes_ctx->user_tag_buf, TEGRA_VIRTUAL_SE_AES_GCM_TAG_SIZE); if (err) { pr_err("%s(): Failed to copy tag_buf: %d\n", __func__, err); goto free_exit; } } else { if (aes_ctx->user_nonce != 0U) memcpy(ivc_tx->aes.op_gcm.iv, aes_ctx->iv, crypto_aead_ivsize(tfm)); } ivc_tx->aes.op_gcm.src_buf_size = aes_ctx->user_src_buf_size; ivc_tx->aes.op_gcm.dst_buf_size = aes_ctx->user_src_buf_size; if (aes_ctx->user_src_buf_size > 0) { ivc_tx->aes.op_gcm.src_addr = (uint32_t)src->buf_iova; /* same source buffer can be used for destination buffer */ ivc_tx->aes.op_gcm.dst_addr = ivc_tx->aes.op_gcm.src_addr; } ivc_tx->aes.op_gcm.aad_buf_size = aes_ctx->user_aad_buf_size; if (aes_ctx->user_aad_buf_size > 0) ivc_tx->aes.op_gcm.aad_addr = aad->buf_iova; if (encrypt) { ivc_tx->aes.op_gcm.tag_buf_size = aes_ctx->user_tag_buf_size; ivc_tx->aes.op_gcm.tag_addr = tag->buf_iova; } init_completion(&priv->alg_complete); err = tegra_hv_vse_safety_send_ivc_wait(se_dev, pivck, priv, ivc_req_msg, sizeof(struct tegra_virtual_se_ivc_msg_t), aes_ctx->node_id); if (err) { dev_err(se_dev->dev, "failed to send data over ivc err %d\n", err); goto free_exit; } if (priv->rx_status != 0) { dev_err(se_dev->dev, "%s: SE Server returned error %u\n", __func__, priv->rx_status); err = status_to_errno(priv->rx_status); goto free_exit; } if (encrypt) { if (aes_ctx->user_nonce == 0U) { /* copy iv to req for encryption*/ memcpy(aes_ctx->iv, priv->iv, crypto_aead_ivsize(tfm)); } if (aes_ctx->user_tag_buf_size > 0) { err = copy_to_user(aes_ctx->user_tag_buf, tag->buf_ptr, aes_ctx->user_tag_buf_size); if (err) { pr_err("%s(): Failed to copy tag_buf %d\n", __func__, err); goto free_exit; } } } else { priv->cmd = VIRTUAL_SE_PROCESS; ivc_tx->cmd = TEGRA_VIRTUAL_SE_CMD_AES_CMD_GET_GCM_DEC; init_completion(&priv->alg_complete); err = tegra_hv_vse_safety_send_ivc_wait(se_dev, pivck, priv, ivc_req_msg, sizeof(struct tegra_virtual_se_ivc_msg_t), aes_ctx->node_id); if (err) { dev_err(se_dev->dev, "failed to send data over ivc err %d\n", err); goto free_exit; } if (priv->rx_status != 0) { dev_err(se_dev->dev, "%s: SE Server returned error %u\n", __func__, priv->rx_status); err = status_to_errno(priv->rx_status); goto free_exit; } } if (aes_ctx->user_src_buf_size > 0) { err = copy_to_user(aes_ctx->user_dst_buf, src->buf_ptr, aes_ctx->user_src_buf_size); if (err) { pr_err("%s(): Failed to copy dst_buf %d\n", __func__, err); goto free_exit; } } free_exit: if (ivc_req_msg) devm_kfree(se_dev->dev, ivc_req_msg); if (priv) devm_kfree(se_dev->dev, priv); return err; } static int tegra_vse_aes_gcm_enc_dec_hw_support(struct aead_request *req, struct tegra_virtual_se_aes_context *aes_ctx, bool encrypt) { struct crypto_aead *tfm = crypto_aead_reqtfm(req); struct tegra_virtual_se_dev *se_dev = g_crypto_to_ivc_map[aes_ctx->node_id].se_dev; struct tegra_virtual_se_ivc_msg_t *ivc_req_msg = NULL; struct tegra_virtual_se_ivc_hdr_t *ivc_hdr; struct tegra_virtual_se_ivc_tx_msg_t *ivc_tx; struct tegra_hv_ivc_cookie *pivck = g_crypto_to_ivc_map[aes_ctx->node_id].ivck; struct tegra_vse_priv_data *priv = NULL; struct tegra_vse_tag *priv_data_ptr; int err = 0; u32 match_code = SE_HW_VALUE_MATCH_CODE; u32 mismatch_code = SE_HW_VALUE_MISMATCH_CODE; const struct tegra_vse_dma_buf *src, *aad, *tag, *comp; comp = tegra_hv_vse_get_dma_buf(aes_ctx->node_id, AES_COMP_BUF_IDX, RESULT_COMPARE_BUF_SIZE); if (!comp) { pr_err("%s mac comp buf is NULL\n", __func__); return -ENOMEM; } if (aes_ctx->user_aad_buf_size > 0) { aad = tegra_hv_vse_get_dma_buf(aes_ctx->node_id, AES_AAD_BUF_IDX, aes_ctx->user_aad_buf_size); if (!aad) { pr_err("%s aad buf is NULL\n", __func__); return -ENOMEM; } err = copy_from_user(aad->buf_ptr, aes_ctx->user_aad_buf, aes_ctx->user_aad_buf_size); if (err) { pr_err("%s(): Failed to copy aad data: %d\n", __func__, err); return err; } } if (aes_ctx->user_src_buf_size > 0) { src = tegra_hv_vse_get_dma_buf(aes_ctx->node_id, AES_SRC_BUF_IDX, aes_ctx->user_src_buf_size); if (!src) { pr_err("%s src buf is NULL\n", __func__); return -ENOMEM; } err = copy_from_user(src->buf_ptr, aes_ctx->user_src_buf, aes_ctx->user_src_buf_size); if (err) { pr_err("%s(): Failed to copy src_buf: %d\n", __func__, err); return err; } } if (encrypt) { tag = tegra_hv_vse_get_dma_buf(aes_ctx->node_id, AES_TAG_BUF_IDX, TEGRA_VIRTUAL_SE_AES_GCM_TAG_IV_SIZE); if (!tag) { pr_err("%s tag buf is NULL\n", __func__); return -ENOMEM; } } priv = devm_kzalloc(se_dev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) { err = -ENOMEM; goto free_exit; } ivc_req_msg = devm_kzalloc(se_dev->dev, sizeof(*ivc_req_msg), GFP_KERNEL); if (!ivc_req_msg) { err = -ENOMEM; goto free_exit; } ivc_tx = &ivc_req_msg->tx[0]; ivc_hdr = &ivc_req_msg->ivc_hdr; ivc_hdr->num_reqs = 1; ivc_hdr->header_magic[0] = 'N'; ivc_hdr->header_magic[1] = 'V'; ivc_hdr->header_magic[2] = 'D'; ivc_hdr->header_magic[3] = 'A'; ivc_hdr->engine = g_crypto_to_ivc_map[aes_ctx->node_id].engine_id; priv_data_ptr = (struct tegra_vse_tag *)ivc_hdr->tag; priv_data_ptr->priv_data = (unsigned int *)priv; priv->se_dev = se_dev; g_crypto_to_ivc_map[aes_ctx->node_id].vse_thread_start = true; memcpy(ivc_tx->aes.op_gcm.keyslot, aes_ctx->aes_keyslot, KEYSLOT_SIZE_BYTES); ivc_tx->aes.op_gcm.key_length = aes_ctx->keylen; if (encrypt) { ivc_tx->cmd = TEGRA_VIRTUAL_SE_CMD_AES_GCM_CMD_ENCRYPT; priv->cmd = VIRTUAL_SE_PROCESS; } else { priv->cmd = VIRTUAL_SE_PROCESS; ivc_tx->cmd = TEGRA_VIRTUAL_SE_CMD_AES_GCM_CMD_DECRYPT; } if (!encrypt) { /* copy iv for decryption*/ memcpy(ivc_tx->aes.op_gcm.iv, aes_ctx->iv, crypto_aead_ivsize(tfm)); /* copy expected tag */ err = copy_from_user(ivc_tx->aes.op_gcm.expected_tag, aes_ctx->user_tag_buf, TEGRA_VIRTUAL_SE_AES_GCM_TAG_SIZE); if (err) { pr_err("%s(): Failed to copy tag_buf: %d\n", __func__, err); goto free_exit; } } else { if (aes_ctx->user_nonce != 0U) memcpy(ivc_tx->aes.op_gcm.iv, aes_ctx->iv, crypto_aead_ivsize(tfm)); } ivc_tx->aes.op_gcm.src_buf_size = aes_ctx->user_src_buf_size; ivc_tx->aes.op_gcm.dst_buf_size = aes_ctx->user_src_buf_size; if (aes_ctx->user_src_buf_size > 0) { ivc_tx->aes.op_gcm.src_addr = (uint32_t)src->buf_iova; ivc_tx->aes.op_gcm.src_buf_size |= (uint32_t)((src->buf_iova >> 8) & ~((1U << 24) - 1U)); /* same source buffer can be used for destination buffer */ ivc_tx->aes.op_gcm.dst_addr = ivc_tx->aes.op_gcm.src_addr; ivc_tx->aes.op_gcm.dst_buf_size = ivc_tx->aes.op_gcm.src_buf_size; } ivc_tx->aes.op_gcm.aad_buf_size = aes_ctx->user_aad_buf_size; if (aes_ctx->user_aad_buf_size > 0) ivc_tx->aes.op_gcm.aad_addr = aad->buf_iova; if (encrypt) { if (aes_ctx->user_nonce == 0U) ivc_tx->aes.op_gcm.tag_buf_size = TEGRA_VIRTUAL_SE_AES_GCM_TAG_IV_SIZE; else ivc_tx->aes.op_gcm.tag_buf_size = aes_ctx->user_tag_buf_size; ivc_tx->aes.op_gcm.tag_addr = tag->buf_iova; } else ivc_tx->aes.op_gcm.gcm_vrfy_res_addr = comp->buf_iova; init_completion(&priv->alg_complete); err = tegra_hv_vse_safety_send_ivc_wait(se_dev, pivck, priv, ivc_req_msg, sizeof(struct tegra_virtual_se_ivc_msg_t), aes_ctx->node_id); if (err) { dev_err(se_dev->dev, "failed to send data over ivc err %d\n", err); goto free_exit; } if (priv->rx_status != 0) { dev_err(se_dev->dev, "%s: SE Server returned error %u\n", __func__, priv->rx_status); err = status_to_errno(priv->rx_status); goto free_exit; } if (encrypt) { if (aes_ctx->user_nonce == 0U) { /* Copy the IV (located after the 16-byte tag) from the buffer to aes_ctx->iv, * based on the required IV size. */ memcpy(aes_ctx->iv, &((uint8_t *)tag->buf_ptr)[16], crypto_aead_ivsize(tfm)); } /* copy tag to req for encryption */ if (aes_ctx->user_tag_buf_size > 0) { err = copy_to_user(aes_ctx->user_tag_buf, tag->buf_ptr, aes_ctx->user_tag_buf_size); if (err) { pr_err("%s(): Failed to copy tag_buf %d\n", __func__, err); goto free_exit; } } } else { if (memcmp(comp->buf_ptr, &match_code, 4) != 0) { if (memcmp(comp->buf_ptr, &mismatch_code, 4) == 0) dev_dbg(se_dev->dev, "%s: tag mismatch\n", __func__); err = -EINVAL; goto free_exit; } } if (aes_ctx->user_src_buf_size > 0) { err = copy_to_user(aes_ctx->user_dst_buf, src->buf_ptr, aes_ctx->user_src_buf_size); if (err) { pr_err("%s(): Failed to copy dst_buf %d\n", __func__, err); goto free_exit; } } free_exit: if (ivc_req_msg) devm_kfree(se_dev->dev, ivc_req_msg); if (priv) devm_kfree(se_dev->dev, priv); return err; } static int tegra_vse_aes_gcm_encrypt(struct aead_request *req) { struct crypto_aead *tfm; struct tegra_virtual_se_aes_context *aes_ctx; struct tegra_virtual_se_dev *se_dev; int err = 0; if (!req) { pr_err("%s: req is invalid\n", __func__); return -EINVAL; } tfm = crypto_aead_reqtfm(req); aes_ctx = crypto_aead_ctx(tfm); err = tegra_vse_aes_gcm_check_params(req); if (err) { pr_err("%s: invalid AES params\n", __func__); return err; } se_dev = g_crypto_to_ivc_map[aes_ctx->node_id].se_dev; if (se_dev->chipdata->gcm_hw_iv_supported) err = tegra_vse_aes_gcm_enc_dec_hw_support(req, aes_ctx, true); else err = tegra_vse_aes_gcm_enc_dec(req, aes_ctx, true); if (err) dev_err(se_dev->dev, "%s failed %d\n", __func__, err); return err; } static int tegra_vse_aes_gcm_decrypt(struct aead_request *req) { struct crypto_aead *tfm; struct tegra_virtual_se_aes_context *aes_ctx; struct tegra_virtual_se_dev *se_dev; int err = 0; if (!req) { pr_err("%s: req is invalid\n", __func__); return -EINVAL; } tfm = crypto_aead_reqtfm(req); aes_ctx = crypto_aead_ctx(tfm); err = tegra_vse_aes_gcm_check_params(req); if (err) { pr_err("%s: invalid AES params\n", __func__); return err; } se_dev = g_crypto_to_ivc_map[aes_ctx->node_id].se_dev; if (g_crypto_to_ivc_map[aes_ctx->node_id].gcm_dec_supported == GCM_DEC_OP_SUPPORTED) { if (se_dev->chipdata->gcm_hw_iv_supported) err = tegra_vse_aes_gcm_enc_dec_hw_support(req, aes_ctx, false); else err = tegra_vse_aes_gcm_enc_dec(req, aes_ctx, false); if (err) dev_err(se_dev->dev, "%s failed %d\n", __func__, err); } else { err = -EACCES; dev_err(se_dev->dev, "%s failed for node_id %u\n", __func__, aes_ctx->node_id); } return err; } static int tegra_vse_aes_gmac_sv_check_params(struct ahash_request *req, bool is_last) { struct tegra_virtual_se_aes_gmac_context *gmac_ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(req)); struct tegra_virtual_se_dev *se_dev = g_crypto_to_ivc_map[gmac_ctx->node_id].se_dev; int err = 0; bool is_zero_copy; if (gmac_ctx->node_id >= MAX_NUMBER_MISC_DEVICES) { dev_err(se_dev->dev, "%s: Node id is not valid\n", __func__); err = -EINVAL; } if (gmac_ctx->is_key_slot_allocated == false) { pr_err("%s: keyslot is not allocated\n", __func__); err = -EINVAL; } /* Validate aad buf len */ if (gmac_ctx->user_aad_buf_size > TEGRA_VIRTUAL_SE_MAX_SUPPORTED_BUFLEN) { dev_err(se_dev->dev, "%s: aad buf length exceeds max supported size\n", __func__); err = -EINVAL; } is_zero_copy = g_crypto_to_ivc_map[gmac_ctx->node_id].is_zero_copy_node; if (!is_zero_copy) { if (gmac_ctx->user_aad_buf == NULL) { dev_err(se_dev->dev, "%s: aad buf is NULL\n", __func__); err = -EINVAL; } } if (gmac_ctx->request_type == TEGRA_HV_VSE_GMAC_VERIFY) { if (is_last != 0U) { if (gmac_ctx->authsize > 0 && gmac_ctx->user_tag_buf == NULL) { dev_err(se_dev->dev, "%s: tag buf length exceeds max supported size\n", __func__); err = -EINVAL; } } } return err; } static int tegra_hv_vse_safety_gmac_cra_init(struct crypto_tfm *tfm) { crypto_ahash_set_reqsize(__crypto_ahash_cast(tfm), sizeof(struct tegra_virtual_se_aes_gmac_context)); return 0; } static void tegra_hv_vse_safety_gmac_cra_exit(struct crypto_tfm *tfm) { /* nothing to do as user releases the keyslot through tzvault TA */ } static int tegra_hv_vse_aes_gmac_setkey(struct crypto_ahash *tfm, const u8 *key, unsigned int keylen) { struct tegra_virtual_se_aes_gmac_context *ctx = crypto_ahash_ctx(tfm); struct tegra_virtual_se_dev *se_dev; s8 label[TEGRA_VIRTUAL_SE_AES_KEYSLOT_LABEL_SIZE]; int err = 0; bool is_keyslot_label; if (!ctx) { pr_err("%s: gmac ctx invalid", __func__); err = -EINVAL; goto exit; } se_dev = g_crypto_to_ivc_map[ctx->node_id].se_dev; if ((keylen != 16) && (keylen != 32)) { dev_err(se_dev->dev, "%s: Unsupported key length: %d", __func__, keylen); err = -EINVAL; goto exit; } /* format: 'NVSEAES 1234567\0' */ is_keyslot_label = sscanf(key, "%s", label) == 1 && (!strcmp(label, TEGRA_VIRTUAL_SE_AES_KEYSLOT_LABEL)); if (is_keyslot_label) { ctx->keylen = keylen; memcpy(ctx->aes_keyslot, key + KEYSLOT_OFFSET_BYTES, KEYSLOT_SIZE_BYTES); ctx->is_key_slot_allocated = true; } else { dev_err(se_dev->dev, "\n %s: Invalid keyslot label: %s\n", __func__, key); err = -EINVAL; } exit: return err; } static int tegra_hv_vse_aes_gmac_sv_init(struct ahash_request *req) { struct tegra_virtual_se_dev *se_dev; struct crypto_ahash *tfm = NULL; struct tegra_virtual_se_aes_gmac_context *gmac_ctx = NULL; struct tegra_virtual_se_ivc_msg_t *ivc_req_msg = NULL; struct tegra_virtual_se_ivc_hdr_t *ivc_hdr = NULL; struct tegra_virtual_se_ivc_tx_msg_t *ivc_tx = NULL; struct tegra_hv_ivc_cookie *pivck; struct tegra_vse_tag *priv_data_ptr = NULL; struct tegra_vse_priv_data *priv = NULL; int err = 0; if (!req) { pr_err("%s: request invalid\n", __func__); err = -EINVAL; goto exit; } tfm = crypto_ahash_reqtfm(req); if (!tfm) { pr_err("%s: transform not valid\n", __func__); err = -EINVAL; goto exit; } gmac_ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(req)); if (!gmac_ctx) { pr_err("%s: req ctx invalid\n", __func__); err = -EINVAL; goto exit; } if (gmac_ctx->is_key_slot_allocated == false) { pr_err("%s: keyslot is not allocated\n", __func__); err = -EPERM; goto exit; } se_dev = g_crypto_to_ivc_map[gmac_ctx->node_id].se_dev; /* Return error if engine is in suspended state */ if (atomic_read(&se_dev->se_suspended)) { dev_err(se_dev->dev, "%s: engine is in suspended state", __func__); err = -ENODEV; goto exit; } if ((gmac_ctx->request_type == TEGRA_HV_VSE_GMAC_VERIFY) || (gmac_ctx->request_type == TEGRA_HV_VSE_GMAC_SIGN)) { /* Initialize GMAC ctx */ gmac_ctx->authsize = crypto_ahash_digestsize(tfm); gmac_ctx->req_context_initialized = true; /* Exit as GMAC_INIT request need not be sent to SE Server for SIGN/VERIFY */ err = 0; goto exit; } priv = devm_kzalloc(se_dev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) { err = -ENOMEM; goto exit; } ivc_req_msg = devm_kzalloc(se_dev->dev, sizeof(*ivc_req_msg), GFP_KERNEL); if (!ivc_req_msg) { err = -ENOMEM; goto free_exit; } ivc_tx = &ivc_req_msg->tx[0]; ivc_hdr = &ivc_req_msg->ivc_hdr; ivc_hdr->num_reqs = 1; ivc_hdr->header_magic[0] = 'N'; ivc_hdr->header_magic[1] = 'V'; ivc_hdr->header_magic[2] = 'D'; ivc_hdr->header_magic[3] = 'A'; ivc_hdr->engine = g_crypto_to_ivc_map[gmac_ctx->node_id].engine_id; priv_data_ptr = (struct tegra_vse_tag *)ivc_hdr->tag; priv_data_ptr->priv_data = (unsigned int *)priv; priv->cmd = VIRTUAL_SE_PROCESS; priv->se_dev = se_dev; ivc_tx->cmd = TEGRA_VIRTUAL_SE_CMD_AES_GMAC_CMD_INIT; memcpy(ivc_tx->aes.op_gcm.keyslot, gmac_ctx->aes_keyslot, KEYSLOT_SIZE_BYTES); ivc_tx->aes.op_gcm.key_length = gmac_ctx->keylen; g_crypto_to_ivc_map[gmac_ctx->node_id].vse_thread_start = true; pivck = g_crypto_to_ivc_map[gmac_ctx->node_id].ivck; init_completion(&priv->alg_complete); err = tegra_hv_vse_safety_send_ivc_wait(se_dev, pivck, priv, ivc_req_msg, sizeof(struct tegra_virtual_se_ivc_msg_t), gmac_ctx->node_id); if (err) { dev_err(se_dev->dev, "failed to send data over ivc err %d\n", err); goto free_exit; } if (priv->rx_status != 0) { dev_err(se_dev->dev, "%s: SE server returned error %u\n", __func__, priv->rx_status); err = status_to_errno(priv->rx_status); goto free_exit; } ivc_tx->cmd = TEGRA_VIRTUAL_SE_CMD_AES_CMD_GET_GMAC_IV; priv->cmd = VIRTUAL_SE_AES_GCM_ENC_PROCESS; init_completion(&priv->alg_complete); err = tegra_hv_vse_safety_send_ivc_wait(se_dev, pivck, priv, ivc_req_msg, sizeof(struct tegra_virtual_se_ivc_msg_t), gmac_ctx->node_id); if (err) { dev_err(se_dev->dev, "failed to send data over ivc err %d\n", err); goto free_exit; } if (priv->rx_status != 0) { dev_err(se_dev->dev, "%s: SE server returned error %u\n", __func__, priv->rx_status); err = status_to_errno(priv->rx_status); goto free_exit; } memcpy(gmac_ctx->iv, priv->iv, TEGRA_VIRTUAL_SE_AES_GCM_IV_SIZE); free_exit: if (ivc_req_msg) devm_kfree(se_dev->dev, ivc_req_msg); if (priv) devm_kfree(se_dev->dev, priv); exit: return err; } static void tegra_hv_vse_aes_gmac_deinit(struct ahash_request *req) { struct tegra_virtual_se_aes_gmac_context *gmac_ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(req)); gmac_ctx->is_key_slot_allocated = false; gmac_ctx->req_context_initialized = false; } static int tegra_hv_vse_aes_gmac_sv_op(struct ahash_request *req, struct tegra_virtual_se_aes_gmac_context *gmac_ctx, bool is_last) { struct tegra_virtual_se_dev *se_dev; struct tegra_virtual_se_ivc_msg_t *ivc_req_msg = NULL; struct tegra_virtual_se_ivc_hdr_t *ivc_hdr; struct tegra_virtual_se_ivc_tx_msg_t *ivc_tx; struct tegra_hv_ivc_cookie *pivck; struct tegra_vse_priv_data *priv = NULL; struct tegra_vse_tag *priv_data_ptr; int err = 0; const struct tegra_vse_dma_buf *aad, *tag; se_dev = g_crypto_to_ivc_map[gmac_ctx->node_id].se_dev; pivck = g_crypto_to_ivc_map[gmac_ctx->node_id].ivck; aad = tegra_hv_vse_get_dma_buf(gmac_ctx->node_id, AES_AAD_BUF_IDX, gmac_ctx->user_aad_buf_size); if (!aad) { pr_err("%s aad buf is NULL\n", __func__); err = -ENOMEM; goto free_exit; } if (gmac_ctx->user_aad_buf_size > 0) { err = copy_from_user(aad->buf_ptr, gmac_ctx->user_aad_buf, gmac_ctx->user_aad_buf_size); if (err) { pr_err("%s(): Failed to copy aad_buf: %d\n", __func__, err); goto exit; } } if (gmac_ctx->request_type == TEGRA_HV_VSE_GMAC_SIGN) { tag = tegra_hv_vse_get_dma_buf(gmac_ctx->node_id, AES_TAG_BUF_IDX, gmac_ctx->authsize); if (!tag) { pr_err("%s tag buf is NULL\n", __func__); err = -ENOMEM; goto free_exit; } } priv = devm_kzalloc(se_dev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) { err = -ENOMEM; goto free_exit; } ivc_req_msg = devm_kzalloc(se_dev->dev, sizeof(*ivc_req_msg), GFP_KERNEL); if (!ivc_req_msg) { err = -ENOMEM; goto free_exit; } ivc_tx = &ivc_req_msg->tx[0]; ivc_hdr = &ivc_req_msg->ivc_hdr; ivc_hdr->num_reqs = 1; ivc_hdr->header_magic[0] = 'N'; ivc_hdr->header_magic[1] = 'V'; ivc_hdr->header_magic[2] = 'D'; ivc_hdr->header_magic[3] = 'A'; ivc_hdr->engine = g_crypto_to_ivc_map[gmac_ctx->node_id].engine_id; priv_data_ptr = (struct tegra_vse_tag *)ivc_hdr->tag; priv_data_ptr->priv_data = (unsigned int *)priv; priv->cmd = VIRTUAL_SE_PROCESS; priv->se_dev = se_dev; if (gmac_ctx->request_type == TEGRA_HV_VSE_GMAC_SIGN) ivc_tx->cmd = TEGRA_VIRTUAL_SE_CMD_AES_GMAC_CMD_SIGN; else ivc_tx->cmd = TEGRA_VIRTUAL_SE_CMD_AES_GMAC_CMD_VERIFY; memcpy(ivc_tx->aes.op_gcm.keyslot, gmac_ctx->aes_keyslot, KEYSLOT_SIZE_BYTES); ivc_tx->aes.op_gcm.key_length = gmac_ctx->keylen; ivc_tx->aes.op_gcm.aad_buf_size = gmac_ctx->user_aad_buf_size; ivc_tx->aes.op_gcm.aad_addr = (u32)(aad->buf_iova & U32_MAX); if (gmac_ctx->request_type == TEGRA_HV_VSE_GMAC_SIGN) { ivc_tx->aes.op_gcm.tag_buf_size = gmac_ctx->authsize; ivc_tx->aes.op_gcm.tag_addr = (u32)(tag->buf_iova & U32_MAX); } if (gmac_ctx->is_first) ivc_tx->aes.op_gcm.config |= (1 << TEGRA_VIRTUAL_SE_AES_GMAC_SV_CFG_FIRST_REQ_SHIFT); if (is_last == true) { ivc_tx->aes.op_gcm.config |= (1 << TEGRA_VIRTUAL_SE_AES_GMAC_SV_CFG_LAST_REQ_SHIFT); if (gmac_ctx->request_type == TEGRA_HV_VSE_GMAC_VERIFY) { memcpy(ivc_tx->aes.op_gcm.iv, gmac_ctx->iv, TEGRA_VIRTUAL_SE_AES_GCM_IV_SIZE); if (gmac_ctx->authsize > 0) { err = copy_from_user(ivc_tx->aes.op_gcm.expected_tag, gmac_ctx->user_tag_buf, gmac_ctx->authsize); if (err) { pr_err("%s(): Failed to copy mac_buf: %d\n", __func__, err); goto free_exit; } } } } g_crypto_to_ivc_map[gmac_ctx->node_id].vse_thread_start = true; init_completion(&priv->alg_complete); err = tegra_hv_vse_safety_send_ivc_wait(se_dev, pivck, priv, ivc_req_msg, sizeof(struct tegra_virtual_se_ivc_msg_t), gmac_ctx->node_id); if (err) { dev_err(se_dev->dev, "failed to send data over ivc err %d\n", err); goto free_exit; } if (priv->rx_status != 0) { dev_err(se_dev->dev, "%s: SE server returned error %u\n", __func__, priv->rx_status); err = status_to_errno(priv->rx_status); goto free_exit; } else { if (is_last && gmac_ctx->request_type == TEGRA_HV_VSE_GMAC_SIGN) { /* copy tag to req for last GMAC_SIGN requests */ if (gmac_ctx->authsize > 0) { err = copy_to_user(gmac_ctx->user_tag_buf, tag->buf_ptr, gmac_ctx->authsize); if (err) { pr_err("%s(): Failed to copy mac_buf: %d\n", __func__, err); goto free_exit; } } } } if (is_last && gmac_ctx->request_type == TEGRA_HV_VSE_GMAC_VERIFY) { ivc_tx->cmd = TEGRA_VIRTUAL_SE_CMD_AES_CMD_GET_GMAC_VERIFY; init_completion(&priv->alg_complete); err = tegra_hv_vse_safety_send_ivc_wait(se_dev, pivck, priv, ivc_req_msg, sizeof(struct tegra_virtual_se_ivc_msg_t), gmac_ctx->node_id); if (err) { dev_err(se_dev->dev, "failed to send data over ivc err %d\n", err); goto free_exit; } if (priv->rx_status != 0) { if (priv->rx_status == 11U) gmac_ctx->result = 1; else err = status_to_errno(priv->rx_status); } else { gmac_ctx->result = 0; } } free_exit: if (ivc_req_msg) devm_kfree(se_dev->dev, ivc_req_msg); if (priv) devm_kfree(se_dev->dev, priv); exit: return err; } static int tegra_hv_vse_aes_gmac_sv_op_hw_support(struct ahash_request *req, struct tegra_virtual_se_aes_gmac_context *gmac_ctx, bool is_last) { struct tegra_virtual_se_dev *se_dev; struct tegra_virtual_se_ivc_msg_t *ivc_req_msg = NULL; struct tegra_virtual_se_ivc_hdr_t *ivc_hdr; struct tegra_virtual_se_ivc_tx_msg_t *ivc_tx; struct tegra_hv_ivc_cookie *pivck; struct tegra_vse_priv_data *priv = NULL; struct tegra_vse_tag *priv_data_ptr; int err = 0; u32 match_code = SE_HW_VALUE_MATCH_CODE; u32 mismatch_code = SE_HW_VALUE_MISMATCH_CODE; const struct tegra_vse_dma_buf *aad, *tag, *comp; dma_addr_t aad_addr = 0UL; dma_addr_t tag_addr = 0UL; bool is_zero_copy; se_dev = g_crypto_to_ivc_map[gmac_ctx->node_id].se_dev; pivck = g_crypto_to_ivc_map[gmac_ctx->node_id].ivck; is_zero_copy = g_crypto_to_ivc_map[gmac_ctx->node_id].is_zero_copy_node; err = tegra_vse_aes_gmac_sv_check_params(req, is_last); if (err != 0) goto exit; if (!is_zero_copy) { aad = tegra_hv_vse_get_dma_buf(gmac_ctx->node_id, AES_AAD_BUF_IDX, gmac_ctx->user_aad_buf_size); if (!aad) { pr_err("%s aad buf is NULL\n", __func__); return -ENOMEM; } if (gmac_ctx->user_aad_buf_size > 0) { err = copy_from_user(aad->buf_ptr, gmac_ctx->user_aad_buf, gmac_ctx->user_aad_buf_size); if (err) { pr_err("%s(): Failed to copy aad_buf: %d\n", __func__, err); goto exit; } } aad_addr = aad->buf_iova; if (gmac_ctx->request_type == TEGRA_HV_VSE_GMAC_SIGN) { tag = tegra_hv_vse_get_dma_buf(gmac_ctx->node_id, AES_TAG_BUF_IDX, gmac_ctx->authsize); if (!tag) { pr_err("%s tag buf is NULL\n", __func__); return -ENOMEM; } tag_addr = tag->buf_iova; } } else { if (g_node_dma[gmac_ctx->node_id].mapped_membuf_count == 0U) { dev_err(se_dev->dev, "%s no mapped membuf found\n", __func__); return -ENOMEM; } aad_addr = gmac_ctx->user_aad_iova; if (gmac_ctx->request_type == TEGRA_HV_VSE_GMAC_SIGN) tag_addr = gmac_ctx->user_tag_iova; } if (gmac_ctx->request_type == TEGRA_HV_VSE_GMAC_VERIFY) { comp = tegra_hv_vse_get_dma_buf(gmac_ctx->node_id, AES_COMP_BUF_IDX, RESULT_COMPARE_BUF_SIZE); if (!comp) { pr_err("%s mac comp buf is NULL\n", __func__); return -ENOMEM; } } priv = devm_kzalloc(se_dev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) { err = -ENOMEM; goto free_exit; } ivc_req_msg = devm_kzalloc(se_dev->dev, sizeof(*ivc_req_msg), GFP_KERNEL); if (!ivc_req_msg) { err = -ENOMEM; goto free_exit; } ivc_tx = &ivc_req_msg->tx[0]; ivc_hdr = &ivc_req_msg->ivc_hdr; ivc_hdr->num_reqs = 1; ivc_hdr->header_magic[0] = 'N'; ivc_hdr->header_magic[1] = 'V'; ivc_hdr->header_magic[2] = 'D'; ivc_hdr->header_magic[3] = 'A'; ivc_hdr->engine = g_crypto_to_ivc_map[gmac_ctx->node_id].engine_id; priv_data_ptr = (struct tegra_vse_tag *)ivc_hdr->tag; priv_data_ptr->priv_data = (unsigned int *)priv; priv->cmd = VIRTUAL_SE_PROCESS; priv->se_dev = se_dev; if (gmac_ctx->request_type == TEGRA_HV_VSE_GMAC_SIGN) ivc_tx->cmd = TEGRA_VIRTUAL_SE_CMD_AES_GMAC_CMD_SIGN; else ivc_tx->cmd = TEGRA_VIRTUAL_SE_CMD_AES_GMAC_CMD_VERIFY; memcpy(ivc_tx->aes.op_gcm.keyslot, gmac_ctx->aes_keyslot, KEYSLOT_SIZE_BYTES); ivc_tx->aes.op_gcm.key_length = gmac_ctx->keylen; ivc_tx->aes.op_gcm.aad_buf_size = gmac_ctx->user_aad_buf_size; ivc_tx->aes.op_gcm.aad_addr = aad_addr; if (gmac_ctx->request_type == TEGRA_HV_VSE_GMAC_SIGN) { ivc_tx->aes.op_gcm.tag_buf_size = gmac_ctx->authsize; ivc_tx->aes.op_gcm.tag_addr = tag_addr; } if (gmac_ctx->is_first) ivc_tx->aes.op_gcm.config |= (1 << TEGRA_VIRTUAL_SE_AES_GMAC_SV_CFG_FIRST_REQ_SHIFT); if (is_last == true) { ivc_tx->aes.op_gcm.config |= (1 << TEGRA_VIRTUAL_SE_AES_GMAC_SV_CFG_LAST_REQ_SHIFT); if (gmac_ctx->request_type == TEGRA_HV_VSE_GMAC_VERIFY) { memcpy(ivc_tx->aes.op_gcm.iv, gmac_ctx->iv, TEGRA_VIRTUAL_SE_AES_GCM_IV_SIZE); if (gmac_ctx->authsize > 0) { err = copy_from_user(ivc_tx->aes.op_gcm.expected_tag, gmac_ctx->user_tag_buf, gmac_ctx->authsize); if (err) { pr_err("%s(): Failed to copy tag_buf: %d\n", __func__, err); goto free_exit; } } ivc_tx->aes.op_gcm.gcm_vrfy_res_addr = comp->buf_iova; } } if (gmac_ctx->b_is_sm4 == 1U) ivc_tx->aes.op_gcm.sym_ciph = VSE_SYM_CIPH_SM4; else ivc_tx->aes.op_gcm.sym_ciph = VSE_SYM_CIPH_AES; g_crypto_to_ivc_map[gmac_ctx->node_id].vse_thread_start = true; init_completion(&priv->alg_complete); err = tegra_hv_vse_safety_send_ivc_wait(se_dev, pivck, priv, ivc_req_msg, sizeof(struct tegra_virtual_se_ivc_msg_t), gmac_ctx->node_id); if (err) { dev_err(se_dev->dev, "failed to send data over ivc err %d\n", err); goto free_exit; } if (priv->rx_status != 0) { dev_err(se_dev->dev, "%s: SE server returned error %u\n", __func__, priv->rx_status); err = status_to_errno(priv->rx_status); goto free_exit; } if (is_last) { if (gmac_ctx->request_type == TEGRA_HV_VSE_GMAC_SIGN) { /* copy tag to req for last GMAC_SIGN requests */ if (!is_zero_copy && (gmac_ctx->authsize > 0)) { err = copy_to_user(gmac_ctx->user_tag_buf, tag->buf_ptr, gmac_ctx->authsize); if (err) { pr_err("%s(): Failed to copy mac_buf: %d\n", __func__, err); goto free_exit; } } } else { if (memcmp(comp->buf_ptr, &match_code, 4) == 0) gmac_ctx->result = 0; else if (memcmp(comp->buf_ptr, &mismatch_code, 4) == 0) gmac_ctx->result = 1; else err = -EINVAL; } } free_exit: if (ivc_req_msg) devm_kfree(se_dev->dev, ivc_req_msg); if (priv) devm_kfree(se_dev->dev, priv); exit: return err; } static int tegra_hv_vse_aes_gmac_sv_update(struct ahash_request *req) { struct tegra_virtual_se_aes_gmac_context *gmac_ctx = NULL; struct tegra_virtual_se_dev *se_dev; int ret = 0; if (!req) { pr_err("%s: request not valid\n", __func__); ret = -EINVAL; goto exit; } ret = tegra_vse_aes_gmac_sv_check_params(req, false); if (ret != 0) { pr_err("%s: Invalid params\n", __func__); goto exit; } gmac_ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(req)); if (!gmac_ctx->req_context_initialized) { pr_err("%s Request ctx not initialized\n", __func__); ret = -EPERM; goto exit; } se_dev = g_crypto_to_ivc_map[gmac_ctx->node_id].se_dev; /* Return error if engine is in suspended state */ if (atomic_read(&se_dev->se_suspended)) { dev_err(se_dev->dev, "%s: engine is in suspended state\n", __func__); ret = -ENODEV; goto exit; } if (se_dev->chipdata->gcm_hw_iv_supported) ret = tegra_hv_vse_aes_gmac_sv_op_hw_support(req, gmac_ctx, false); else ret = tegra_hv_vse_aes_gmac_sv_op(req, gmac_ctx, false); if (ret) dev_err(se_dev->dev, "%s failed %d\n", __func__, ret); exit: return ret; } static int tegra_hv_vse_aes_gmac_sv_finup(struct ahash_request *req) { struct tegra_virtual_se_aes_gmac_context *gmac_ctx = NULL; struct tegra_virtual_se_dev *se_dev; int ret = 0; if (!req) { pr_err("%s: request not valid\n", __func__); ret = -EINVAL; goto exit; } ret = tegra_vse_aes_gmac_sv_check_params(req, true); if (ret != 0) { pr_err("%s: Invalid params\n", __func__); goto exit; } gmac_ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(req)); if (!gmac_ctx->req_context_initialized) { pr_err("%s: Request ctx not initialized\n", __func__); ret = -EPERM; goto exit; } se_dev = g_crypto_to_ivc_map[gmac_ctx->node_id].se_dev; /* Return error if engine is in suspended state */ if (atomic_read(&se_dev->se_suspended)) { dev_err(se_dev->dev, "%s: engine is in suspended state\n", __func__); ret = -ENODEV; goto exit; } if (se_dev->chipdata->gcm_hw_iv_supported) ret = tegra_hv_vse_aes_gmac_sv_op_hw_support(req, gmac_ctx, true); else ret = tegra_hv_vse_aes_gmac_sv_op(req, gmac_ctx, true); if (ret) dev_err(se_dev->dev, "%s failed %d\n", __func__, ret); tegra_hv_vse_aes_gmac_deinit(req); exit: return ret; } static int tegra_hv_vse_aes_gmac_sv_final(struct ahash_request *req) { struct tegra_virtual_se_aes_gmac_context *gmac_ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(req)); struct tegra_virtual_se_dev *se_dev = g_crypto_to_ivc_map[gmac_ctx->node_id].se_dev; dev_err(se_dev->dev, "%s: final not supported", __func__); return -EPERM; } #define HV_SAFETY_AES_CTX_SIZE sizeof(struct tegra_virtual_se_aes_context) static struct rng_alg rng_alg = { .generate = tegra_hv_vse_safety_rng_drbg_get_random, .seed = tegra_hv_vse_safety_rng_drbg_reset, .seedsize = TEGRA_VIRTUAL_SE_RNG_SEED_SIZE, .base = { .cra_name = "rng_drbg", .cra_driver_name = "rng_drbg-aes-tegra", .cra_priority = 100, .cra_flags = CRYPTO_ALG_TYPE_RNG, .cra_ctxsize = sizeof(struct tegra_virtual_se_rng_context), .cra_module = THIS_MODULE, .cra_init = tegra_hv_vse_safety_rng_drbg_init, .cra_exit = tegra_hv_vse_safety_rng_drbg_exit, } }; static struct aead_alg aead_algs[] = { { .setkey = tegra_vse_aes_gcm_setkey, .setauthsize = tegra_vse_aes_gcm_setauthsize, .encrypt = tegra_vse_aes_gcm_encrypt, .decrypt = tegra_vse_aes_gcm_decrypt, .init = tegra_vse_aes_gcm_init, .exit = tegra_vse_aes_gcm_exit, .ivsize = TEGRA_VIRTUAL_SE_AES_GCM_IV_SIZE, .maxauthsize = TEGRA_VIRTUAL_SE_AES_GCM_TAG_SIZE, .chunksize = TEGRA_VIRTUAL_SE_AES_BLOCK_SIZE, .base = { .cra_name = "gcm-vse(aes)", .cra_driver_name = "gcm-aes-tegra-safety", .cra_priority = 1000, .cra_blocksize = TEGRA_VIRTUAL_SE_AES_BLOCK_SIZE, .cra_ctxsize = HV_SAFETY_AES_CTX_SIZE, .cra_module = THIS_MODULE, } } }; static struct skcipher_alg aes_algs[] = { { .base.cra_name = "cbc-vse(aes)", .base.cra_driver_name = "cbc-aes-tegra", .base.cra_priority = 400, .base.cra_flags = CRYPTO_ALG_TYPE_SKCIPHER | CRYPTO_ALG_ASYNC, .base.cra_blocksize = TEGRA_VIRTUAL_SE_AES_BLOCK_SIZE, .base.cra_ctxsize = HV_SAFETY_AES_CTX_SIZE, .base.cra_alignmask = 0, .base.cra_module = THIS_MODULE, .init = tegra_hv_vse_safety_aes_cra_init, .exit = tegra_hv_vse_safety_aes_cra_exit, .setkey = tegra_hv_vse_safety_aes_setkey, .encrypt = tegra_hv_vse_safety_aes_cbc_encrypt, .decrypt = tegra_hv_vse_safety_aes_cbc_decrypt, .min_keysize = TEGRA_VIRTUAL_SE_AES_MIN_KEY_SIZE, .max_keysize = TEGRA_VIRTUAL_SE_AES_MAX_KEY_SIZE, .ivsize = TEGRA_VIRTUAL_SE_AES_IV_SIZE, }, { .base.cra_name = "ctr-vse(aes)", .base.cra_driver_name = "ctr-aes-tegra-safety", .base.cra_priority = 400, .base.cra_flags = CRYPTO_ALG_TYPE_SKCIPHER | CRYPTO_ALG_ASYNC, .base.cra_blocksize = TEGRA_VIRTUAL_SE_AES_BLOCK_SIZE, .base.cra_ctxsize = HV_SAFETY_AES_CTX_SIZE, .base.cra_alignmask = 0, .base.cra_module = THIS_MODULE, .init = tegra_hv_vse_safety_aes_cra_init, .exit = tegra_hv_vse_safety_aes_cra_exit, .setkey = tegra_hv_vse_safety_aes_setkey, .encrypt = tegra_hv_vse_safety_aes_ctr_encrypt, .decrypt = tegra_hv_vse_safety_aes_ctr_decrypt, .min_keysize = TEGRA_VIRTUAL_SE_AES_MIN_KEY_SIZE, .max_keysize = TEGRA_VIRTUAL_SE_AES_MAX_KEY_SIZE, .ivsize = TEGRA_VIRTUAL_SE_AES_IV_SIZE, }, }; static struct ahash_alg tsec_alg = { .init = tegra_hv_vse_safety_cmac_init, .update = tegra_hv_tsec_safety_cmac_update, .final = tegra_hv_vse_safety_cmac_final, .finup = tegra_hv_tsec_safety_cmac_finup, .digest = tegra_hv_vse_safety_cmac_digest, .setkey = tegra_hv_vse_safety_cmac_setkey, .halg.digestsize = TEGRA_VIRTUAL_SE_AES_CMAC_DIGEST_SIZE, .halg.statesize = TEGRA_VIRTUAL_SE_AES_CMAC_STATE_SIZE, .halg.base = { .cra_name = "cmac-tsec(aes)", .cra_driver_name = "tegra-hv-vse-safety-tsec(aes)", .cra_priority = 400, .cra_flags = CRYPTO_ALG_TYPE_AHASH, .cra_blocksize = TEGRA_VIRTUAL_SE_AES_BLOCK_SIZE, .cra_ctxsize = sizeof(struct tegra_virtual_se_aes_cmac_context), .cra_alignmask = 0, .cra_module = THIS_MODULE, .cra_init = tegra_hv_vse_safety_cmac_cra_init, .cra_exit = tegra_hv_vse_safety_cmac_cra_exit, } }; static struct ahash_alg cmac_alg = { .init = tegra_hv_vse_safety_cmac_init, .update = tegra_hv_vse_safety_cmac_update, .final = tegra_hv_vse_safety_cmac_final, .finup = tegra_hv_vse_safety_cmac_finup, .digest = tegra_hv_vse_safety_cmac_digest, .setkey = tegra_hv_vse_safety_cmac_setkey, .halg.digestsize = TEGRA_VIRTUAL_SE_AES_CMAC_DIGEST_SIZE, .halg.statesize = TEGRA_VIRTUAL_SE_AES_CMAC_STATE_SIZE, .halg.base = { .cra_name = "cmac-vse(aes)", .cra_driver_name = "tegra-hv-vse-safety-cmac(aes)", .cra_priority = 400, .cra_flags = CRYPTO_ALG_TYPE_AHASH, .cra_blocksize = TEGRA_VIRTUAL_SE_AES_BLOCK_SIZE, .cra_ctxsize = sizeof(struct tegra_virtual_se_aes_cmac_context), .cra_alignmask = 0, .cra_module = THIS_MODULE, .cra_init = tegra_hv_vse_safety_cmac_cra_init, .cra_exit = tegra_hv_vse_safety_cmac_cra_exit, } }; static struct ahash_alg gmac_alg = { .init = tegra_hv_vse_aes_gmac_sv_init, .update = tegra_hv_vse_aes_gmac_sv_update, .finup = tegra_hv_vse_aes_gmac_sv_finup, .final = tegra_hv_vse_aes_gmac_sv_final, .setkey = tegra_hv_vse_aes_gmac_setkey, .halg.digestsize = TEGRA_VIRTUAL_SE_AES_GCM_TAG_SIZE, .halg.statesize = TEGRA_VIRTUAL_SE_AES_GCM_TAG_SIZE, .halg.base = { .cra_name = "gmac-vse(aes)", .cra_driver_name = "tegra-hv-vse-gmac(aes)", .cra_priority = 400, .cra_flags = CRYPTO_ALG_TYPE_AHASH, .cra_blocksize = TEGRA_VIRTUAL_SE_AES_BLOCK_SIZE, .cra_ctxsize = sizeof(struct tegra_virtual_se_aes_gmac_context), .cra_alignmask = 0, .cra_module = THIS_MODULE, .cra_init = tegra_hv_vse_safety_gmac_cra_init, .cra_exit = tegra_hv_vse_safety_gmac_cra_exit, } }; static struct ahash_alg sha_algs[] = { { .init = tegra_hv_vse_safety_sha_init, .update = tegra_hv_vse_safety_sha_update, .final = tegra_hv_vse_safety_sha_final, .finup = tegra_hv_vse_safety_sha_finup, .digest = tegra_hv_vse_safety_sha_digest, .export = tegra_hv_vse_safety_sha_export, .import = tegra_hv_vse_safety_sha_import, .halg.digestsize = SHA256_DIGEST_SIZE, .halg.statesize = sizeof(struct tegra_virtual_se_req_context), .halg.base = { .cra_name = "sha256-vse", .cra_driver_name = "tegra-hv-vse-safety-sha256", .cra_priority = 300, .cra_flags = CRYPTO_ALG_TYPE_AHASH, .cra_blocksize = SHA256_BLOCK_SIZE, .cra_ctxsize = sizeof(struct tegra_virtual_se_sha_context), .cra_alignmask = 0, .cra_module = THIS_MODULE, .cra_init = tegra_hv_vse_safety_sha_cra_init, .cra_exit = tegra_hv_vse_safety_sha_cra_exit, } }, { .init = tegra_hv_vse_safety_sha_init, .update = tegra_hv_vse_safety_sha_update, .final = tegra_hv_vse_safety_sha_final, .finup = tegra_hv_vse_safety_sha_finup, .digest = tegra_hv_vse_safety_sha_digest, .export = tegra_hv_vse_safety_sha_export, .import = tegra_hv_vse_safety_sha_import, .halg.digestsize = SHA384_DIGEST_SIZE, .halg.statesize = sizeof(struct tegra_virtual_se_req_context), .halg.base = { .cra_name = "sha384-vse", .cra_driver_name = "tegra-hv-vse-safety-sha384", .cra_priority = 300, .cra_flags = CRYPTO_ALG_TYPE_AHASH, .cra_blocksize = SHA384_BLOCK_SIZE, .cra_ctxsize = sizeof(struct tegra_virtual_se_sha_context), .cra_alignmask = 0, .cra_module = THIS_MODULE, .cra_init = tegra_hv_vse_safety_sha_cra_init, .cra_exit = tegra_hv_vse_safety_sha_cra_exit, } }, { .init = tegra_hv_vse_safety_sha_init, .update = tegra_hv_vse_safety_sha_update, .final = tegra_hv_vse_safety_sha_final, .finup = tegra_hv_vse_safety_sha_finup, .digest = tegra_hv_vse_safety_sha_digest, .export = tegra_hv_vse_safety_sha_export, .import = tegra_hv_vse_safety_sha_import, .halg.digestsize = SHA512_DIGEST_SIZE, .halg.statesize = sizeof(struct tegra_virtual_se_req_context), .halg.base = { .cra_name = "sha512-vse", .cra_driver_name = "tegra-hv-vse-safety-sha512", .cra_priority = 300, .cra_flags = CRYPTO_ALG_TYPE_AHASH, .cra_blocksize = SHA512_BLOCK_SIZE, .cra_ctxsize = sizeof(struct tegra_virtual_se_sha_context), .cra_alignmask = 0, .cra_module = THIS_MODULE, .cra_init = tegra_hv_vse_safety_sha_cra_init, .cra_exit = tegra_hv_vse_safety_sha_cra_exit, } }, { .init = tegra_hv_vse_safety_sha_init, .update = tegra_hv_vse_safety_sha_update, .final = tegra_hv_vse_safety_sha_final, .finup = tegra_hv_vse_safety_sha_finup, .digest = tegra_hv_vse_safety_sha_digest, .export = tegra_hv_vse_safety_sha_export, .import = tegra_hv_vse_safety_sha_import, .halg.digestsize = SHA3_256_DIGEST_SIZE, .halg.statesize = sizeof(struct tegra_virtual_se_req_context), .halg.base = { .cra_name = "sha3-256-vse", .cra_driver_name = "tegra-hv-vse-safety-sha3-256", .cra_priority = 300, .cra_flags = CRYPTO_ALG_TYPE_AHASH, .cra_blocksize = SHA3_256_BLOCK_SIZE, .cra_ctxsize = sizeof(struct tegra_virtual_se_sha_context), .cra_alignmask = 0, .cra_module = THIS_MODULE, .cra_init = tegra_hv_vse_safety_sha_cra_init, .cra_exit = tegra_hv_vse_safety_sha_cra_exit, } }, { .init = tegra_hv_vse_safety_sha_init, .update = tegra_hv_vse_safety_sha_update, .final = tegra_hv_vse_safety_sha_final, .finup = tegra_hv_vse_safety_sha_finup, .digest = tegra_hv_vse_safety_sha_digest, .export = tegra_hv_vse_safety_sha_export, .import = tegra_hv_vse_safety_sha_import, .halg.digestsize = SHA3_384_DIGEST_SIZE, .halg.statesize = sizeof(struct tegra_virtual_se_req_context), .halg.base = { .cra_name = "sha3-384-vse", .cra_driver_name = "tegra-hv-vse-safety-sha3-384", .cra_priority = 300, .cra_flags = CRYPTO_ALG_TYPE_AHASH, .cra_blocksize = SHA3_384_BLOCK_SIZE, .cra_ctxsize = sizeof(struct tegra_virtual_se_sha_context), .cra_alignmask = 0, .cra_module = THIS_MODULE, .cra_init = tegra_hv_vse_safety_sha_cra_init, .cra_exit = tegra_hv_vse_safety_sha_cra_exit, } }, { .init = tegra_hv_vse_safety_sha_init, .update = tegra_hv_vse_safety_sha_update, .final = tegra_hv_vse_safety_sha_final, .finup = tegra_hv_vse_safety_sha_finup, .digest = tegra_hv_vse_safety_sha_digest, .export = tegra_hv_vse_safety_sha_export, .import = tegra_hv_vse_safety_sha_import, .halg.digestsize = SHA3_512_DIGEST_SIZE, .halg.statesize = sizeof(struct tegra_virtual_se_req_context), .halg.base = { .cra_name = "sha3-512-vse", .cra_driver_name = "tegra-hv-vse-safety-sha3-512", .cra_priority = 300, .cra_flags = CRYPTO_ALG_TYPE_AHASH, .cra_blocksize = SHA3_512_BLOCK_SIZE, .cra_ctxsize = sizeof(struct tegra_virtual_se_sha_context), .cra_alignmask = 0, .cra_module = THIS_MODULE, .cra_init = tegra_hv_vse_safety_sha_cra_init, .cra_exit = tegra_hv_vse_safety_sha_cra_exit, } }, { .init = tegra_hv_vse_safety_sha_init, .update = tegra_hv_vse_safety_sha_update, .final = tegra_hv_vse_safety_sha_final, .finup = tegra_hv_vse_safety_sha_finup, .digest = tegra_hv_vse_safety_sha_digest, .export = tegra_hv_vse_safety_sha_export, .import = tegra_hv_vse_safety_sha_import, .halg.digestsize = SHA3_512_DIGEST_SIZE, .halg.statesize = sizeof(struct tegra_virtual_se_req_context), .halg.base = { .cra_name = "shake128-vse", .cra_driver_name = "tegra-hv-vse-safety-shake128", .cra_priority = 300, .cra_flags = CRYPTO_ALG_TYPE_AHASH, .cra_blocksize = SHA3_512_BLOCK_SIZE, .cra_ctxsize = sizeof(struct tegra_virtual_se_sha_context), .cra_alignmask = 0, .cra_module = THIS_MODULE, .cra_init = tegra_hv_vse_safety_sha_cra_init, .cra_exit = tegra_hv_vse_safety_sha_cra_exit, } }, { .init = tegra_hv_vse_safety_sha_init, .update = tegra_hv_vse_safety_sha_update, .final = tegra_hv_vse_safety_sha_final, .finup = tegra_hv_vse_safety_sha_finup, .digest = tegra_hv_vse_safety_sha_digest, .export = tegra_hv_vse_safety_sha_export, .import = tegra_hv_vse_safety_sha_import, .halg.digestsize = SHA3_512_DIGEST_SIZE, .halg.statesize = sizeof(struct tegra_virtual_se_req_context), .halg.base = { .cra_name = "shake256-vse", .cra_driver_name = "tegra-hv-vse-safety-shake256", .cra_priority = 300, .cra_flags = CRYPTO_ALG_TYPE_AHASH, .cra_blocksize = SHA3_512_BLOCK_SIZE, .cra_ctxsize = sizeof(struct tegra_virtual_se_sha_context), .cra_alignmask = 0, .cra_module = THIS_MODULE, .cra_init = tegra_hv_vse_safety_sha_cra_init, .cra_exit = tegra_hv_vse_safety_sha_cra_exit, } }, { .init = tegra_hv_vse_safety_sha_init, .update = tegra_hv_vse_safety_sha_update, .final = tegra_hv_vse_safety_sha_final, .finup = tegra_hv_vse_safety_sha_finup, .digest = tegra_hv_vse_safety_sha_digest, .export = tegra_hv_vse_safety_sha_export, .import = tegra_hv_vse_safety_sha_import, .halg.digestsize = SM3_DIGEST_SIZE, .halg.statesize = sizeof(struct tegra_virtual_se_req_context), .halg.base = { .cra_name = "sm3-vse", .cra_driver_name = "tegra-hv-vse-safety-sm3", .cra_priority = 300, .cra_flags = CRYPTO_ALG_TYPE_AHASH, .cra_blocksize = SM3_BLOCK_SIZE, .cra_ctxsize = sizeof(struct tegra_virtual_se_sha_context), .cra_alignmask = 0, .cra_module = THIS_MODULE, .cra_init = tegra_hv_vse_safety_sha_cra_init, .cra_exit = tegra_hv_vse_safety_sha_cra_exit, } }, { .init = tegra_hv_vse_safety_hmac_sha_init, .update = tegra_hv_vse_safety_hmac_sha_update, .final = tegra_hv_vse_safety_hmac_sha_final, .finup = tegra_hv_vse_safety_hmac_sha_finup, .digest = tegra_hv_vse_safety_hmac_sha_digest, .export = tegra_hv_vse_safety_sha_export, .import = tegra_hv_vse_safety_sha_import, .setkey = tegra_hv_vse_safety_hmac_sha_setkey, .halg.digestsize = SHA256_DIGEST_SIZE, .halg.statesize = sizeof(struct tegra_virtual_se_req_context), .halg.base = { .cra_name = "hmac-sha256-vse", .cra_driver_name = "tegra-hv-vse-safety-hmac-sha256", .cra_priority = 300, .cra_flags = CRYPTO_ALG_TYPE_AHASH, .cra_blocksize = SHA256_BLOCK_SIZE, .cra_ctxsize = sizeof(struct tegra_virtual_se_hmac_sha_context), .cra_alignmask = 0, .cra_module = THIS_MODULE, .cra_init = tegra_hv_vse_safety_sha_cra_init, .cra_exit = tegra_hv_vse_safety_sha_cra_exit, } } }; static const struct tegra_vse_soc_info t194_vse_sinfo = { .gcm_decrypt_supported = false, .cmac_hw_verify_supported = false, .sm_supported = false, .gcm_hw_iv_supported = false, .hmac_verify_hw_support = false, .zero_copy_supported = false, }; static const struct tegra_vse_soc_info t234_vse_sinfo = { .gcm_decrypt_supported = true, .cmac_hw_verify_supported = false, .sm_supported = false, .gcm_hw_iv_supported = false, .hmac_verify_hw_support = false, .zero_copy_supported = false, }; static const struct tegra_vse_soc_info se_51_vse_sinfo = { .gcm_decrypt_supported = true, .cmac_hw_verify_supported = true, .sm_supported = true, .gcm_hw_iv_supported = true, .hmac_verify_hw_support = true, .zero_copy_supported = true, }; static const struct of_device_id tegra_hv_vse_safety_of_match[] = { { .compatible = "nvidia,tegra194-hv-vse-safety", .data = &t194_vse_sinfo, }, { .compatible = "nvidia,tegra234-hv-vse-safety", .data = &t234_vse_sinfo, }, { .compatible = "nvidia,tegra-se-5.1-hv-vse-safety", .data = &se_51_vse_sinfo, }, {}, }; MODULE_DEVICE_TABLE(of, tegra_hv_vse_safety_of_match); static irqreturn_t tegra_vse_irq_handler(int irq, void *data) { uint32_t node_id = *((uint32_t *)data); struct tegra_hv_ivc_cookie *ivck = g_crypto_to_ivc_map[node_id].ivck; if (tegra_hv_ivc_can_read(ivck)) complete(&g_crypto_to_ivc_map[node_id].tegra_vse_complete); return IRQ_HANDLED; } static int tegra_vse_kthread(void *data) { uint32_t node_id = *((uint32_t *)data); struct tegra_virtual_se_dev *se_dev = NULL; struct tegra_hv_ivc_cookie *pivck = g_crypto_to_ivc_map[node_id].ivck; struct tegra_virtual_se_ivc_msg_t *ivc_msg; int err = 0; int timeout; int ret; bool is_dummy = false; size_t size_ivc_msg = sizeof(struct tegra_virtual_se_ivc_msg_t); enum ivc_irq_state *irq_state; se_dev = g_crypto_to_ivc_map[node_id].se_dev; ivc_msg = devm_kzalloc(se_dev->dev, size_ivc_msg, GFP_KERNEL); if (!ivc_msg) return -ENOMEM; while (!kthread_should_stop()) { err = 0; ret = wait_for_completion_interruptible( &g_crypto_to_ivc_map[node_id].tegra_vse_complete); if (ret < 0) { pr_err("%s completion err\n", __func__); reinit_completion(&g_crypto_to_ivc_map[node_id].tegra_vse_complete); continue; } if (!g_crypto_to_ivc_map[node_id].vse_thread_start) { reinit_completion(&g_crypto_to_ivc_map[node_id].tegra_vse_complete); continue; } timeout = TEGRA_VIRTUAL_SE_TIMEOUT_1S; while (tegra_hv_ivc_channel_notified(pivck) != 0) { if (!timeout) { reinit_completion( &g_crypto_to_ivc_map[node_id].tegra_vse_complete); pr_err("%s:%d ivc channel_notifier timeout\n", __func__, __LINE__); err = -EAGAIN; break; } udelay(1); timeout--; } if (err == -EAGAIN) { err = 0; continue; } mutex_lock(&(se_dev->crypto_to_ivc_map[node_id].irq_state_lock)); irq_state = &(se_dev->crypto_to_ivc_map[node_id].wait_interrupt); while (tegra_hv_ivc_can_read(pivck) && *irq_state != NO_INTERRUPT) { pr_debug("%s(): wait_interrupt = %u", __func__, *irq_state); if (*irq_state == INTERMEDIATE_REQ_INTERRUPT) { err = read_and_validate_valid_msg(se_dev, pivck, node_id, &is_dummy, true); if (err != 0) { dev_err(se_dev->dev, "%s(): Unable to read validate message", __func__); } *irq_state = NO_INTERRUPT; pr_debug("%s():%d wait_interrupt = %u\n", __func__, __LINE__, *irq_state); break; } else if (*irq_state == FIRST_REQ_INTERRUPT) { err = read_and_validate_dummy_msg(se_dev, pivck, node_id, &is_dummy); if (err != 0) { dev_err(se_dev->dev, "%s:%d Invalid response header\n", __func__, __LINE__); err = 0; continue; } if (is_dummy == true) { *irq_state = INTERMEDIATE_REQ_INTERRUPT; pr_debug("%s():%d Dummy message read. Read valid message.", __func__, __LINE__); continue; } else { dev_err(se_dev->dev, "Invalid response sequence"); break; } } else { dev_err(se_dev->dev, "Invalid irq state - %u", *irq_state); return -EINVAL; } } mutex_unlock(&(se_dev->crypto_to_ivc_map[node_id].irq_state_lock)); } devm_kfree(se_dev->dev, ivc_msg); return 0; } #if defined(CONFIG_HW_RANDOM) static int tegra_hv_vse_safety_hwrng_read(struct hwrng *rng, void *buf, size_t size, bool wait) { struct tegra_virtual_se_rng_context *ctx; if (!wait) return 0; ctx = (struct tegra_virtual_se_rng_context *)rng->priv; return tegra_hv_vse_safety_get_random(ctx, buf, size, HW_RNG); } #endif /* CONFIG_HW_RANDOM */ static int tegra_hv_vse_safety_register_hwrng(struct tegra_virtual_se_dev *se_dev) { #if defined(CONFIG_HW_RANDOM) int ret; struct hwrng *vse_hwrng = NULL; struct tegra_virtual_se_rng_context *rng_ctx = NULL; vse_hwrng = devm_kzalloc(se_dev->dev, sizeof(*vse_hwrng), GFP_KERNEL); if (!vse_hwrng) { ret = -ENOMEM; goto out; } rng_ctx = devm_kzalloc(se_dev->dev, sizeof(*rng_ctx), GFP_KERNEL); if (!rng_ctx) { ret = -ENOMEM; goto out; } rng_ctx->se_dev = se_dev; /* To ensure the memory is not overwritten when hwrng req arrives while * AES CBC/CMAC or other AES operations are in progress */ rng_ctx->hwrng_dma_buf.buf_ptr = dma_alloc_coherent(se_dev->dev, TEGRA_VIRTUAL_SE_RNG_DT_SIZE, &rng_ctx->hwrng_dma_buf.buf_iova, GFP_KERNEL); if (!rng_ctx->hwrng_dma_buf.buf_ptr) return -ENOMEM; vse_hwrng->name = "tegra_hv_vse_safety"; vse_hwrng->read = tegra_hv_vse_safety_hwrng_read; vse_hwrng->quality = 1024; vse_hwrng->priv = (unsigned long)rng_ctx; ret = devm_hwrng_register(se_dev->dev, vse_hwrng); out: if (ret) { if (rng_ctx) { dma_free_coherent(se_dev->dev, TEGRA_VIRTUAL_SE_RNG_DT_SIZE, rng_ctx->hwrng_dma_buf.buf_ptr, rng_ctx->hwrng_dma_buf.buf_iova); devm_kfree(se_dev->dev, rng_ctx); } if (vse_hwrng) devm_kfree(se_dev->dev, vse_hwrng); } else { se_dev->hwrng = vse_hwrng; } return ret; #else return 0; #endif /* CONFIG_HW_RANDOM */ } static void tegra_hv_vse_safety_unregister_hwrng(struct tegra_virtual_se_dev *se_dev) { #if defined(CONFIG_HW_RANDOM) struct tegra_virtual_se_rng_context *rng_ctx; if (se_dev->hwrng) { devm_hwrng_unregister(se_dev->dev, se_dev->hwrng); rng_ctx = (struct tegra_virtual_se_rng_context *)se_dev->hwrng->priv; dma_free_coherent(se_dev->dev, TEGRA_VIRTUAL_SE_RNG_DT_SIZE, rng_ctx->hwrng_dma_buf.buf_ptr, rng_ctx->hwrng_dma_buf.buf_iova); devm_kfree(se_dev->dev, rng_ctx); devm_kfree(se_dev->dev, se_dev->hwrng); se_dev->hwrng = NULL; } #endif /* CONFIG_HW_RANDOM */ } static const struct of_device_id host1x_match[] = { { .compatible = "nvidia,tegra234-host1x", }, { .compatible = "nvidia,tegra264-host1x", }, {}, }; static int se_get_nvhost_dev(struct tegra_virtual_se_dev *se_dev) { struct platform_device *host1x_pdev; struct device_node *np; np = of_find_matching_node(NULL, host1x_match); if (!np) { dev_err(se_dev->dev, "Failed to find host1x, syncpt support disabled"); return -ENODATA; } host1x_pdev = of_find_device_by_node(np); if (!host1x_pdev) { dev_err(se_dev->dev, "host1x device not available"); return -EPROBE_DEFER; } se_dev->host1x_pdev = host1x_pdev; return 0; } static int tegra_vse_validate_ivc_node_id(uint32_t ivc_id, uint32_t instance_id, int32_t engine_id) { uint32_t cnt; for (cnt = 0; cnt < MAX_NUMBER_MISC_DEVICES; cnt++) { if (g_crypto_to_ivc_map[cnt].node_in_use != true) break; if (g_crypto_to_ivc_map[cnt].ivc_id == ivc_id) { pr_err("%s: ivc id %u is already used\n", __func__, ivc_id); return -EINVAL; } if ((g_crypto_to_ivc_map[cnt].engine_id == engine_id) && (g_crypto_to_ivc_map[cnt].instance_id == instance_id)) { pr_err("%s: instance id %u is already used for engine id %d\n", __func__, instance_id, engine_id); return -EINVAL; } } return 0; } static bool tegra_mempool_check_entry(struct tegra_virtual_se_dev *se_dev, uint32_t mempool_id) { uint32_t cnt; for (cnt = 0; cnt < MAX_NUMBER_MISC_DEVICES; cnt++) { if (g_crypto_to_ivc_map[cnt].mempool.buf_len > 0) if (g_crypto_to_ivc_map[cnt].mempool_id == mempool_id) return true; } return false; } static int tegra_hv_vse_allocate_gpc_dma_bufs(struct tegra_vse_node_dma *node_dma, struct device *gpcdma_dev, struct crypto_dev_to_ivc_map *ivc_map) { int32_t err = -ENOMEM; if (!node_dma) { pr_err("%s node_dma is null\n", __func__); err = -EINVAL; goto exit; } if (!gpcdma_dev) { pr_err("%s gpcdma_dev is null\n", __func__); err = -EINVAL; goto exit; } if (!ivc_map) { pr_err("%s ivc_map is null\n", __func__); err = -EINVAL; goto exit; } if ((ivc_map->engine_id != VIRTUAL_SE_AES0) && (ivc_map->engine_id != VIRTUAL_SE_AES1)) { /* No GPCDMA buffer allocation is needed in case of non AES engines */ err = 0; goto exit; } if (ivc_map->gcm_dec_buffer_size > 0) { node_dma->gpc_dma_buf.buf_ptr = dma_alloc_coherent(gpcdma_dev, ALIGN(ivc_map->gcm_dec_buffer_size, 64U), &node_dma->gpc_dma_buf.buf_iova, GFP_KERNEL); if (!node_dma->gpc_dma_buf.buf_ptr) { dev_err(gpcdma_dev, "%s dma_alloc_coherent failed\n", __func__); err = -ENOMEM; goto exit; } node_dma->gpcdma_dev = gpcdma_dev; node_dma->gpc_dma_buf.buf_len = ivc_map->gcm_dec_buffer_size; } err = 0; exit: return err; } static void tegra_hv_vse_release_gpc_dma_bufs(struct device *gpcdma_dev) { uint32_t i; struct tegra_vse_dma_buf *dma_buf = NULL; if (!gpcdma_dev) { pr_err("%s gpcdma_dev is null\n", __func__); return; } for (i = 0; i < MAX_NUMBER_MISC_DEVICES; i++) { if (g_node_dma[i].gpcdma_dev == gpcdma_dev) { dma_buf = &g_node_dma[i].gpc_dma_buf; if ((dma_buf->buf_len > 0U) && (dma_buf->buf_ptr != NULL)) { dma_free_coherent(gpcdma_dev, dma_buf->buf_len, dma_buf->buf_ptr, dma_buf->buf_iova); dma_buf->buf_len = 0U; dma_buf->buf_ptr = NULL; } } } } static int tegra_hv_vse_allocate_se_dma_bufs(struct tegra_vse_node_dma *node_dma, struct device *se_dev, struct crypto_dev_to_ivc_map *ivc_map) { int32_t err = -ENOMEM; uint32_t buf_sizes[MAX_SE_DMA_BUFS] = {0U}; uint32_t i; if (!node_dma) { pr_err("%s node_dma is null\n", __func__); err = -EINVAL; goto exit; } if (!se_dev) { pr_err("%s se_dev is null\n", __func__); err = -EINVAL; goto exit; } if (!ivc_map) { pr_err("%s ivc_map is null\n", __func__); err = -EINVAL; goto exit; } switch (ivc_map->engine_id) { case VIRTUAL_SE_AES0: case VIRTUAL_SE_AES1: case VIRTUAL_GCSE1_AES0: case VIRTUAL_GCSE1_AES1: case VIRTUAL_GCSE2_AES0: case VIRTUAL_GCSE2_AES1: /* * For AES algs, the worst case requirement is for AES-GCM encryption: * 1. src buffer(requires up to max limit specified in DT) * 2. aad buffer(requires up to max limit specified in DT) * 3. mac/tag buffer(requires 64 bytes) * 4. comp/match buffer(requires 4 bytes) */ buf_sizes[AES_SRC_BUF_IDX] = ivc_map->max_buffer_size; buf_sizes[AES_AAD_BUF_IDX] = ivc_map->max_buffer_size; buf_sizes[AES_TAG_BUF_IDX] = AES_TAG_BUF_SIZE; buf_sizes[AES_COMP_BUF_IDX] = RESULT_COMPARE_BUF_SIZE; break; case VIRTUAL_SE_SHA: case VIRTUAL_GCSE1_SHA: case VIRTUAL_GCSE2_SHA: /* * For SHA algs, the worst case requirement for SHAKE128/SHAKE256: * 1. plaintext buffer(requires up to max limit specified in DT) * 2. digest buffer(support a maximum digest size of 1024 bytes for SHAKE) * 3. match code/comp buffer(requires 4 bytes) */ buf_sizes[SHA_SRC_BUF_IDX] = ivc_map->max_buffer_size; buf_sizes[SHA_HASH_BUF_IDX] = SHA_HASH_BUF_SIZE; buf_sizes[HMAC_SHA_COMP_BUF_IDX] = RESULT_COMPARE_BUF_SIZE; break; case VIRTUAL_SE_TSEC: buf_sizes[TSEC_SRC_BUF_IDX] = ivc_map->max_buffer_size; buf_sizes[TSEC_MAC_BUF_IDX] = TEGRA_VIRTUAL_SE_AES_CMAC_DIGEST_SIZE; buf_sizes[TSEC_FW_STATUS_BUF_IDX] = RESULT_COMPARE_BUF_SIZE; break; default: err = 0; goto exit; } node_dma->se_dev = se_dev; for (i = 0; i < MAX_SE_DMA_BUFS; i++) { if (buf_sizes[i] == 0U) continue; node_dma->se_dma_buf[i].buf_ptr = dma_alloc_coherent(se_dev, buf_sizes[i], &node_dma->se_dma_buf[i].buf_iova, GFP_KERNEL); if (!node_dma->se_dma_buf[i].buf_ptr) { dev_err(se_dev, "%s dma_alloc_coherent failed\n", __func__); err = -ENOMEM; goto exit; } node_dma->se_dma_buf[i].buf_len = buf_sizes[i]; } err = 0; exit: return err; } static void tegra_hv_vse_release_se_dma_bufs(struct device *se_dev) { uint32_t i, j; struct tegra_vse_dma_buf *dma_buf = NULL; if (!se_dev) { pr_err("%s se_dev is null\n", __func__); return; } for (i = 0; i < MAX_NUMBER_MISC_DEVICES; i++) { if (g_node_dma[i].se_dev == se_dev) { for (j = 0; j < MAX_SE_DMA_BUFS; j++) { dma_buf = &g_node_dma[i].se_dma_buf[j]; if ((dma_buf->buf_len > 0U) && (dma_buf->buf_ptr != NULL)) { dma_free_coherent(se_dev, dma_buf->buf_len, dma_buf->buf_ptr, dma_buf->buf_iova); dma_buf->buf_len = 0U; dma_buf->buf_ptr = NULL; } } } } } static void tegra_hv_vse_release_all_dma_bufs(void) { uint32_t i, j; struct tegra_vse_dma_buf *dma_buf = NULL; for (i = 0; i < MAX_NUMBER_MISC_DEVICES; i++) { if (g_node_dma[i].se_dev) { for (j = 0; j < MAX_SE_DMA_BUFS; j++) { dma_buf = &g_node_dma[i].se_dma_buf[j]; if ((dma_buf->buf_len > 0U) && (dma_buf->buf_ptr != NULL)) { dma_free_coherent(g_node_dma[i].se_dev, dma_buf->buf_len, dma_buf->buf_ptr, dma_buf->buf_iova); dma_buf->buf_len = 0U; dma_buf->buf_ptr = NULL; } } } if (g_node_dma[i].gpcdma_dev) { dma_buf = &g_node_dma[i].gpc_dma_buf; if ((dma_buf->buf_len > 0U) && (dma_buf->buf_ptr != NULL)) { dma_free_coherent(g_node_dma[i].gpcdma_dev, dma_buf->buf_len, dma_buf->buf_ptr, dma_buf->buf_iova); dma_buf->buf_len = 0U; dma_buf->buf_ptr = NULL; } } } } static int tegra_hv_vse_safety_probe(struct platform_device *pdev) { struct tegra_virtual_se_dev *se_dev = NULL; struct crypto_dev_to_ivc_map *crypto_dev = NULL; struct device_node *np; int err = 0; int i; unsigned int ivc_id; unsigned int mempool_id; unsigned int engine_id; const struct of_device_id *match; struct tegra_vse_soc_info *pdata = NULL; static uint32_t s_node_id; uint32_t ivc_cnt, cnt, instance_id; bool has_zero_copy_prop; static bool s_aes_alg_register_done; static bool s_sha_alg_register_done; static bool s_tsec_alg_register_done; bool is_aes_alg, is_sha_alg, is_tsec_alg; gcm_supports_dma = of_property_read_bool(pdev->dev.of_node, "nvidia,gcm-dma-support"); if (gcm_supports_dma) { gpcdma_dev = &pdev->dev; for (i = 0; i < MAX_NUMBER_MISC_DEVICES; i++) { err = tegra_hv_vse_allocate_gpc_dma_bufs(&g_node_dma[i], gpcdma_dev, &g_crypto_to_ivc_map[i]); if (err) { dev_err(gpcdma_dev, "%s returned error %d for node id %d\n", __func__, err, i); tegra_hv_vse_release_gpc_dma_bufs(gpcdma_dev); goto exit; } } return 0; } has_zero_copy_prop = of_property_read_bool(pdev->dev.of_node, "#zero-copy"); se_dev = devm_kzalloc(&pdev->dev, sizeof(struct tegra_virtual_se_dev), GFP_KERNEL); if (!se_dev) { pr_err("%s devm_kzalloc failed\n", __func__); err = -ENOMEM; goto exit; } /* set host1x platform device */ err = se_get_nvhost_dev(se_dev); if (err) { dev_err(&pdev->dev, "Failed to get nvhost dev with err: %d\n", err); goto exit; } np = pdev->dev.of_node; se_dev->crypto_to_ivc_map = g_crypto_to_ivc_map; se_dev->dev = &pdev->dev; err = of_property_read_u32(np, "se-engine-id", &engine_id); if (err) { dev_err(&pdev->dev, "se-engine-id property not present\n"); err = -ENODEV; goto exit; } switch (engine_id) { case VIRTUAL_SE_AES0: case VIRTUAL_SE_AES1: case VIRTUAL_GCSE1_AES0: case VIRTUAL_GCSE1_AES1: case VIRTUAL_GCSE2_AES0: case VIRTUAL_GCSE2_AES1: is_aes_alg = true; break; case VIRTUAL_SE_SHA: case VIRTUAL_GCSE1_SHA: case VIRTUAL_GCSE2_SHA: is_sha_alg = true; break; case VIRTUAL_SE_TSEC: is_tsec_alg = true; break; default: dev_err(se_dev->dev, "%s unsupported engine id %u\n", __func__, engine_id); err = -EINVAL; goto exit; } /* read ivccfg from dts */ err = of_property_read_u32_index(np, "nvidia,ivccfg_cnt", 0, &ivc_cnt); if (err) { pr_err("Error: failed to read ivc_cnt. err %u\n", err); err = -ENODEV; goto exit; } if (ivc_cnt > MAX_NUMBER_MISC_DEVICES) { pr_err("%s Error: Unsupported IVC queue count %u\n", __func__, ivc_cnt); err = -EINVAL; goto exit; } if (s_node_id > (MAX_NUMBER_MISC_DEVICES - ivc_cnt)) { pr_err("%s Error: IVC queue count exceeds maximum supported value of %u\n", __func__, MAX_NUMBER_MISC_DEVICES); err = -EINVAL; goto exit; } if (pdev->dev.of_node) { match = of_match_device(of_match_ptr(tegra_hv_vse_safety_of_match), &pdev->dev); if (!match) { dev_err(&pdev->dev, "Error: No device match found\n"); return -ENODEV; } pdata = (struct tegra_vse_soc_info *)match->data; } else { pdata = (struct tegra_vse_soc_info *)pdev->id_entry->driver_data; } se_dev->chipdata = pdata; for (cnt = 0; cnt < ivc_cnt; cnt++) { err = of_property_read_u32_index(np, "nvidia,ivccfg", cnt * TEGRA_IVCCFG_ARRAY_LEN + TEGRA_CRYPTO_DEV_ID_OFFSET, &instance_id); if (err) { pr_err("%s Error: failed to read instance id. err %d\n", __func__, err); err = -ENODEV; goto exit; } crypto_dev = &g_crypto_to_ivc_map[s_node_id]; err = of_property_read_u32_index(np, "nvidia,ivccfg", cnt * TEGRA_IVCCFG_ARRAY_LEN + TEGRA_IVC_ID_OFFSET, &ivc_id); if (err) { pr_err("Error: failed to read ivc_id. err %d\n", err); err = -ENODEV; goto exit; } err = tegra_vse_validate_ivc_node_id(ivc_id, instance_id, engine_id); if (err) { err = -ENODEV; goto exit; } crypto_dev->ivc_id = ivc_id; crypto_dev->node_id = s_node_id; crypto_dev->instance_id = instance_id; crypto_dev->se_dev = se_dev; crypto_dev->node_in_use = true; err = of_property_read_u32_index(np, "nvidia,ivccfg", cnt * TEGRA_IVCCFG_ARRAY_LEN + TEGRA_SE_ENGINE_ID_OFFSET, &crypto_dev->engine_id); if (err) { pr_err("Error: failed to read engine_id. err %d\n", err); err = -ENODEV; goto exit; } if (engine_id != crypto_dev->engine_id) { pr_err("Error: se engine mismatch for ivc_id %u\n", crypto_dev->ivc_id); err = -ENODEV; goto exit; } err = of_property_read_u32_index(np, "nvidia,ivccfg", cnt * TEGRA_IVCCFG_ARRAY_LEN + TEGRA_IVC_PRIORITY_OFFSET, &crypto_dev->priority); if (err || crypto_dev->priority > MAX_IVC_Q_PRIORITY) { pr_err("Error: invalid queue priority. err %d\n", err); err = -ENODEV; goto exit; } err = of_property_read_u32_index(np, "nvidia,ivccfg", cnt * TEGRA_IVCCFG_ARRAY_LEN + TEGRA_MAX_BUFFER_SIZE, &crypto_dev->max_buffer_size); if (err) { pr_err("Error: invalid max buffer size. err %d\n", err); err = -ENODEV; goto exit; } if (has_zero_copy_prop) { if (!se_dev->chipdata->zero_copy_supported) { pr_err("Error: zero copy is not supported on this platform\n"); err = -ENODEV; goto exit; } if (crypto_dev->max_buffer_size > 0U) { pr_err("Error: max buffer size must be 0 if 0-copy is supported\n"); err = -ENODEV; goto exit; } crypto_dev->is_zero_copy_node = true; } else { crypto_dev->is_zero_copy_node = false; } err = of_property_read_u32_index(np, "nvidia,ivccfg", cnt * TEGRA_IVCCFG_ARRAY_LEN + TEGRA_CHANNEL_GROUPID_OFFSET, &crypto_dev->channel_grp_id); if (err) { pr_err("Error: invalid channel group id. err %d\n", err); err = -ENODEV; goto exit; } err = of_property_read_u32_index(np, "nvidia,ivccfg", cnt * TEGRA_IVCCFG_ARRAY_LEN + TEGRA_GCM_SUPPORTED_FLAG_OFFSET, &crypto_dev->gcm_dec_supported); if (err || crypto_dev->gcm_dec_supported > GCM_DEC_OP_SUPPORTED) { pr_err("Error: invalid gcm decrypt supported flag. err %d\n", err); err = -ENODEV; goto exit; } err = of_property_read_u32_index(np, "nvidia,ivccfg", cnt * TEGRA_IVCCFG_ARRAY_LEN + TEGRA_GCM_DEC_BUFFER_SIZE, &crypto_dev->gcm_dec_buffer_size); if (err || (crypto_dev->gcm_dec_supported != GCM_DEC_OP_SUPPORTED && crypto_dev->gcm_dec_buffer_size != 0)) { pr_err("Error: invalid gcm decrypt buffer size. err %d\n", err); err = -ENODEV; goto exit; } err = of_property_read_u32_index(np, "nvidia,ivccfg", cnt * TEGRA_IVCCFG_ARRAY_LEN + TEGRA_GCM_DEC_MEMPOOL_ID, &mempool_id); if (err || ((crypto_dev->gcm_dec_supported != GCM_DEC_OP_SUPPORTED) && (mempool_id != 0))) { pr_err("Error: invalid mempool id. err %d\n", err); err = -ENODEV; goto exit; } err = of_property_read_u32_index(np, "nvidia,ivccfg", cnt * TEGRA_IVCCFG_ARRAY_LEN + TEGRA_GCM_DEC_MEMPOOL_SIZE, &crypto_dev->mempool.buf_len); if (err || ((crypto_dev->gcm_dec_supported == GCM_DEC_OP_SUPPORTED) && (crypto_dev->mempool.buf_len > crypto_dev->gcm_dec_buffer_size))) { pr_err("Error: invalid mempool size err %d\n", err); err = -ENODEV; goto exit; } dev_info(se_dev->dev, "Virtual SE channel number: %d", ivc_id); crypto_dev->ivck = tegra_hv_ivc_reserve(NULL, ivc_id, NULL); if (IS_ERR_OR_NULL(crypto_dev->ivck)) { dev_err(&pdev->dev, "Failed reserve channel number\n"); err = -ENODEV; goto exit; } tegra_hv_ivc_channel_reset(crypto_dev->ivck); if (!se_dev->chipdata->gcm_hw_iv_supported && (crypto_dev->mempool.buf_len > 0)) { dev_info(se_dev->dev, "Virtual SE mempool channel number: %d\n", mempool_id); if (tegra_mempool_check_entry(se_dev, mempool_id) == false) { crypto_dev->mempool_id = mempool_id; } else { pr_err("Error: mempool id %u is already used\n", mempool_id); err = -ENODEV; goto exit; } crypto_dev->ivmk = tegra_hv_mempool_reserve(crypto_dev->mempool_id); if (IS_ERR_OR_NULL(crypto_dev->ivmk)) { dev_err(&pdev->dev, "Failed to reserve mempool channel %d\n", crypto_dev->mempool_id); err = -ENODEV; goto exit; } if (crypto_dev->ivmk->size < crypto_dev->mempool.buf_len) { pr_err("Error: mempool %u size(%llu) is smaller than DT value(%u)", crypto_dev->mempool_id, crypto_dev->ivmk->size, crypto_dev->mempool.buf_len); err = -ENODEV; goto exit; } crypto_dev->mempool.buf_ptr = devm_memremap(&pdev->dev, crypto_dev->ivmk->ipa, crypto_dev->ivmk->size, MEMREMAP_WB); if (IS_ERR_OR_NULL(crypto_dev->mempool.buf_ptr)) { dev_err(&pdev->dev, "Failed to map mempool area %d\n", crypto_dev->mempool_id); err = -ENOMEM; goto exit; } /* For GCM decrypt buffer IOVA field represents offset */ crypto_dev->mempool.buf_iova = 0; } init_completion(&crypto_dev->tegra_vse_complete); mutex_init(&crypto_dev->se_ivc_lock); mutex_init(&crypto_dev->irq_state_lock); crypto_dev->tegra_vse_task = kthread_run(tegra_vse_kthread, &crypto_dev->node_id, "tegra_vse_kthread-%u", s_node_id); if (IS_ERR(crypto_dev->tegra_vse_task)) { dev_err(se_dev->dev, "Couldn't create kthread for vse with node id %u\n", s_node_id); err = PTR_ERR(crypto_dev->tegra_vse_task); goto exit; } if (request_irq(crypto_dev->ivck->irq, tegra_vse_irq_handler, 0, "vse", &crypto_dev->node_id)) { dev_err(se_dev->dev, "Failed to request irq %d for node id %u\n", crypto_dev->ivck->irq, s_node_id); err = -EINVAL; goto exit; } crypto_dev->wait_interrupt = FIRST_REQ_INTERRUPT; err = tegra_hv_vse_allocate_se_dma_bufs(&g_node_dma[s_node_id], se_dev->dev, crypto_dev); if (err) { dev_err(se_dev->dev, "%s returned error %d for engine id %d, node id %d\n", __func__, err, engine_id, crypto_dev->node_id); goto exit; } s_node_id++; } if (is_aes_alg && !s_aes_alg_register_done) { err = crypto_register_ahash(&cmac_alg); if (err) { dev_err(&pdev->dev, "cmac alg register failed. Err %d\n", err); goto release_bufs; } err = crypto_register_ahash(&gmac_alg); if (err) { dev_err(&pdev->dev, "gmac alg register failed. Err %d\n", err); goto release_bufs; } err = crypto_register_rng(&rng_alg); if (err) { dev_err(&pdev->dev, "rng alg register failed. Err %d\n", err); goto release_bufs; } err = tegra_hv_vse_safety_register_hwrng(se_dev); if (err) { dev_err(&pdev->dev, "hwrng register failed. Err %d\n", err); goto release_bufs; } err = crypto_register_skciphers(aes_algs, ARRAY_SIZE(aes_algs)); if (err) { dev_err(&pdev->dev, "aes alg register failed: %d\n", err); goto release_bufs; } if (se_dev->chipdata->gcm_decrypt_supported) { err = crypto_register_aeads(aead_algs, ARRAY_SIZE(aead_algs)); if (err) { dev_err(&pdev->dev, "aead alg register failed: %d\n", err); goto release_bufs; } } s_aes_alg_register_done = true; } if (is_sha_alg && !s_sha_alg_register_done) { for (i = 0; i < ARRAY_SIZE(sha_algs); i++) { err = crypto_register_ahash(&sha_algs[i]); if (err) { dev_err(&pdev->dev, "sha alg register failed idx[%d]\n", i); goto release_bufs; } } s_sha_alg_register_done = true; } if (is_tsec_alg && !s_tsec_alg_register_done) { err = crypto_register_ahash(&tsec_alg); if (err) { dev_err(&pdev->dev, "Tsec alg register failed. Err %d\n", err); goto release_bufs; } s_tsec_alg_register_done = true; } se_dev->engine_id = engine_id; /* Set Engine suspended state to false*/ atomic_set(&se_dev->se_suspended, 0); platform_set_drvdata(pdev, se_dev); return 0; release_bufs: tegra_hv_vse_release_se_dma_bufs(se_dev->dev); exit: return err; } static void tegra_hv_vse_safety_shutdown(struct platform_device *pdev) { struct tegra_virtual_se_dev *se_dev = platform_get_drvdata(pdev); uint32_t cnt; /* skip checking pending request for the node with "nvidia,gcm-dma-support" * which only used to allocate buffer for gpcdma * for other vse nodes which doesn't have "nvidia,gcm-dma-support", * it will still check pending request. */ if (gcm_supports_dma) return; /* Set engine to suspend state */ atomic_set(&se_dev->se_suspended, 1); for (cnt = 0; cnt < MAX_NUMBER_MISC_DEVICES; cnt++) { if (g_crypto_to_ivc_map[cnt].engine_id == se_dev->engine_id && g_crypto_to_ivc_map[cnt].ivck != NULL) { /* Wait for SE server to be free*/ while (mutex_is_locked(&g_crypto_to_ivc_map[cnt].se_ivc_lock) || mutex_is_locked(&g_crypto_to_ivc_map[cnt].irq_state_lock)) usleep_range(8, 10); } } } static int tegra_hv_vse_safety_remove(struct platform_device *pdev) { int i; tegra_hv_vse_safety_unregister_hwrng(platform_get_drvdata(pdev)); for (i = 0; i < ARRAY_SIZE(sha_algs); i++) crypto_unregister_ahash(&sha_algs[i]); tegra_hv_vse_release_all_dma_bufs(); return 0; } #if defined(CONFIG_PM) static int tegra_hv_vse_safety_suspend(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); /* Keep engine in suspended state */ tegra_hv_vse_safety_shutdown(pdev); return 0; } static int tegra_hv_vse_safety_resume(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct tegra_virtual_se_dev *se_dev = platform_get_drvdata(pdev); /* skip checking pending request for the node with "nvidia,gcm-dma-support" * which only used to allocate buffer for gpcdma * for other vse nodes which doesn't have "nvidia,gcm-dma-support", * it will still set engine suspend state to 1. */ if (gcm_supports_dma) return 0; /* Set engine to suspend state to 1 to make it as false */ atomic_set(&se_dev->se_suspended, 0); return 0; } static const struct dev_pm_ops tegra_hv_pm_ops = { .suspend = tegra_hv_vse_safety_suspend, .resume = tegra_hv_vse_safety_resume, }; #endif /* CONFIG_PM */ #if defined(NV_PLATFORM_DRIVER_STRUCT_REMOVE_RETURNS_VOID) /* Linux v6.11 */ static void tegra_hv_vse_safety_remove_wrapper(struct platform_device *pdev) { tegra_hv_vse_safety_remove(pdev); } #else static int tegra_hv_vse_safety_remove_wrapper(struct platform_device *pdev) { return tegra_hv_vse_safety_remove(pdev); } #endif static struct platform_driver tegra_hv_vse_safety_driver = { .probe = tegra_hv_vse_safety_probe, .remove = tegra_hv_vse_safety_remove_wrapper, .shutdown = tegra_hv_vse_safety_shutdown, .driver = { .name = "tegra_hv_vse_safety", .owner = THIS_MODULE, .of_match_table = of_match_ptr(tegra_hv_vse_safety_of_match), #if defined(CONFIG_PM) .pm = &tegra_hv_pm_ops, #endif }, }; static int __init tegra_hv_vse_safety_module_init(void) { uint32_t i, j; for (i = 0U; i < MAX_NUMBER_MISC_DEVICES; i++) { g_crypto_to_ivc_map[i].node_in_use = false; for (j = 0; j < MAX_ZERO_COPY_BUFS; j++) g_node_dma[i].membuf_ctx[j].fd = -1; } return platform_driver_register(&tegra_hv_vse_safety_driver); } static void __exit tegra_hv_vse_safety_module_exit(void) { platform_driver_unregister(&tegra_hv_vse_safety_driver); } module_init(tegra_hv_vse_safety_module_init); module_exit(tegra_hv_vse_safety_module_exit); #if defined(NV_MODULE_IMPORT_NS_CALLS_STRINGIFY) MODULE_IMPORT_NS(DMA_BUF); #else MODULE_IMPORT_NS("DMA_BUF"); #endif MODULE_AUTHOR("Mallikarjun Kasoju "); MODULE_DESCRIPTION("Virtual Security Engine driver over Tegra Hypervisor IVC channel"); MODULE_LICENSE("GPL");