// SPDX-License-Identifier: GPL-2.0-only /* * Intel Keem Bay OCS HCU Crypto Driver. * * Copyright (C) 2018-2020 Intel Corporation */ #include <linux/delay.h> #include <linux/device.h> #include <linux/iopoll.h> #include <linux/irq.h> #include <linux/module.h> #include <crypto/sha2.h> #include "ocs-hcu.h" /* Registers. */ #define OCS_HCU_MODE … #define OCS_HCU_CHAIN … #define OCS_HCU_OPERATION … #define OCS_HCU_KEY_0 … #define OCS_HCU_ISR … #define OCS_HCU_IER … #define OCS_HCU_STATUS … #define OCS_HCU_MSG_LEN_LO … #define OCS_HCU_MSG_LEN_HI … #define OCS_HCU_KEY_BYTE_ORDER_CFG … #define OCS_HCU_DMA_SRC_ADDR … #define OCS_HCU_DMA_SRC_SIZE … #define OCS_HCU_DMA_DST_SIZE … #define OCS_HCU_DMA_DMA_MODE … #define OCS_HCU_DMA_NEXT_SRC_DESCR … #define OCS_HCU_DMA_MSI_ISR … #define OCS_HCU_DMA_MSI_IER … #define OCS_HCU_DMA_MSI_MASK … /* Register bit definitions. */ #define HCU_MODE_ALGO_SHIFT … #define HCU_MODE_HMAC_SHIFT … #define HCU_STATUS_BUSY … #define HCU_BYTE_ORDER_SWAP … #define HCU_IRQ_HASH_DONE … #define HCU_IRQ_HASH_ERR_MASK … #define HCU_DMA_IRQ_SRC_DONE … #define HCU_DMA_IRQ_SAI_ERR … #define HCU_DMA_IRQ_BAD_COMP_ERR … #define HCU_DMA_IRQ_INBUF_RD_ERR … #define HCU_DMA_IRQ_INBUF_WD_ERR … #define HCU_DMA_IRQ_OUTBUF_WR_ERR … #define HCU_DMA_IRQ_OUTBUF_RD_ERR … #define HCU_DMA_IRQ_CRD_ERR … #define HCU_DMA_IRQ_ERR_MASK … #define HCU_DMA_SNOOP_MASK … #define HCU_DMA_SRC_LL_EN … #define HCU_DMA_EN … #define OCS_HCU_ENDIANNESS_VALUE … #define HCU_DMA_MSI_UNMASK … #define HCU_DMA_MSI_DISABLE … #define HCU_IRQ_DISABLE … #define OCS_HCU_START … #define OCS_HCU_TERMINATE … #define OCS_LL_DMA_FLAG_TERMINATE … #define OCS_HCU_HW_KEY_LEN_U32 … #define HCU_DATA_WRITE_ENDIANNESS_OFFSET … #define OCS_HCU_NUM_CHAINS_SHA256_224_SM3 … #define OCS_HCU_NUM_CHAINS_SHA384_512 … /* * While polling on a busy HCU, wait maximum 200us between one check and the * other. */ #define OCS_HCU_WAIT_BUSY_RETRY_DELAY_US … /* Wait on a busy HCU for maximum 1 second. */ #define OCS_HCU_WAIT_BUSY_TIMEOUT_US … /** * struct ocs_hcu_dma_entry - An entry in an OCS DMA linked list. * @src_addr: Source address of the data. * @src_len: Length of data to be fetched. * @nxt_desc: Next descriptor to fetch. * @ll_flags: Flags (Freeze @ terminate) for the DMA engine. */ struct ocs_hcu_dma_entry { … }; /** * struct ocs_hcu_dma_list - OCS-specific DMA linked list. * @head: The head of the list (points to the array backing the list). * @tail: The current tail of the list; NULL if the list is empty. * @dma_addr: The DMA address of @head (i.e., the DMA address of the backing * array). * @max_nents: Maximum number of entries in the list (i.e., number of elements * in the backing array). * * The OCS DMA list is an array-backed list of OCS DMA descriptors. The array * backing the list is allocated with dma_alloc_coherent() and pointed by * @head. */ struct ocs_hcu_dma_list { … }; static inline u32 ocs_hcu_num_chains(enum ocs_hcu_algo algo) { … } static inline u32 ocs_hcu_digest_size(enum ocs_hcu_algo algo) { … } /** * ocs_hcu_wait_busy() - Wait for HCU OCS hardware to became usable. * @hcu_dev: OCS HCU device to wait for. * * Return: 0 if device free, -ETIMEOUT if device busy and internal timeout has * expired. */ static int ocs_hcu_wait_busy(struct ocs_hcu_dev *hcu_dev) { … } static void ocs_hcu_done_irq_en(struct ocs_hcu_dev *hcu_dev) { … } static void ocs_hcu_dma_irq_en(struct ocs_hcu_dev *hcu_dev) { … } static void ocs_hcu_irq_dis(struct ocs_hcu_dev *hcu_dev) { … } static int ocs_hcu_wait_and_disable_irq(struct ocs_hcu_dev *hcu_dev) { … } /** * ocs_hcu_get_intermediate_data() - Get intermediate data. * @hcu_dev: The target HCU device. * @data: Where to store the intermediate. * @algo: The algorithm being used. * * This function is used to save the current hashing process state in order to * continue it in the future. * * Note: once all data has been processed, the intermediate data actually * contains the hashing result. So this function is also used to retrieve the * final result of a hashing process. * * Return: 0 on success, negative error code otherwise. */ static int ocs_hcu_get_intermediate_data(struct ocs_hcu_dev *hcu_dev, struct ocs_hcu_idata *data, enum ocs_hcu_algo algo) { … } /** * ocs_hcu_set_intermediate_data() - Set intermediate data. * @hcu_dev: The target HCU device. * @data: The intermediate data to be set. * @algo: The algorithm being used. * * This function is used to continue a previous hashing process. */ static void ocs_hcu_set_intermediate_data(struct ocs_hcu_dev *hcu_dev, const struct ocs_hcu_idata *data, enum ocs_hcu_algo algo) { … } static int ocs_hcu_get_digest(struct ocs_hcu_dev *hcu_dev, enum ocs_hcu_algo algo, u8 *dgst, size_t dgst_len) { … } /** * ocs_hcu_hw_cfg() - Configure the HCU hardware. * @hcu_dev: The HCU device to configure. * @algo: The algorithm to be used by the HCU device. * @use_hmac: Whether or not HW HMAC should be used. * * Return: 0 on success, negative error code otherwise. */ static int ocs_hcu_hw_cfg(struct ocs_hcu_dev *hcu_dev, enum ocs_hcu_algo algo, bool use_hmac) { … } /** * ocs_hcu_clear_key() - Clear key stored in OCS HMAC KEY registers. * @hcu_dev: The OCS HCU device whose key registers should be cleared. */ static void ocs_hcu_clear_key(struct ocs_hcu_dev *hcu_dev) { … } /** * ocs_hcu_write_key() - Write key to OCS HMAC KEY registers. * @hcu_dev: The OCS HCU device the key should be written to. * @key: The key to be written. * @len: The size of the key to write. It must be OCS_HCU_HW_KEY_LEN. * * Return: 0 on success, negative error code otherwise. */ static int ocs_hcu_write_key(struct ocs_hcu_dev *hcu_dev, const u8 *key, size_t len) { … } /** * ocs_hcu_ll_dma_start() - Start OCS HCU hashing via DMA * @hcu_dev: The OCS HCU device to use. * @dma_list: The OCS DMA list mapping the data to hash. * @finalize: Whether or not this is the last hashing operation and therefore * the final hash should be compute even if data is not * block-aligned. * * Return: 0 on success, negative error code otherwise. */ static int ocs_hcu_ll_dma_start(struct ocs_hcu_dev *hcu_dev, const struct ocs_hcu_dma_list *dma_list, bool finalize) { … } struct ocs_hcu_dma_list *ocs_hcu_dma_list_alloc(struct ocs_hcu_dev *hcu_dev, int max_nents) { … } void ocs_hcu_dma_list_free(struct ocs_hcu_dev *hcu_dev, struct ocs_hcu_dma_list *dma_list) { … } /* Add a new DMA entry at the end of the OCS DMA list. */ int ocs_hcu_dma_list_add_tail(struct ocs_hcu_dev *hcu_dev, struct ocs_hcu_dma_list *dma_list, dma_addr_t addr, u32 len) { … } /** * ocs_hcu_hash_init() - Initialize hash operation context. * @ctx: The context to initialize. * @algo: The hashing algorithm to use. * * Return: 0 on success, negative error code otherwise. */ int ocs_hcu_hash_init(struct ocs_hcu_hash_ctx *ctx, enum ocs_hcu_algo algo) { … } /** * ocs_hcu_hash_update() - Perform a hashing iteration. * @hcu_dev: The OCS HCU device to use. * @ctx: The OCS HCU hashing context. * @dma_list: The OCS DMA list mapping the input data to process. * * Return: 0 on success; negative error code otherwise. */ int ocs_hcu_hash_update(struct ocs_hcu_dev *hcu_dev, struct ocs_hcu_hash_ctx *ctx, const struct ocs_hcu_dma_list *dma_list) { … } /** * ocs_hcu_hash_finup() - Update and finalize hash computation. * @hcu_dev: The OCS HCU device to use. * @ctx: The OCS HCU hashing context. * @dma_list: The OCS DMA list mapping the input data to process. * @dgst: The buffer where to save the computed digest. * @dgst_len: The length of @dgst. * * Return: 0 on success; negative error code otherwise. */ int ocs_hcu_hash_finup(struct ocs_hcu_dev *hcu_dev, const struct ocs_hcu_hash_ctx *ctx, const struct ocs_hcu_dma_list *dma_list, u8 *dgst, size_t dgst_len) { … } /** * ocs_hcu_hash_final() - Finalize hash computation. * @hcu_dev: The OCS HCU device to use. * @ctx: The OCS HCU hashing context. * @dgst: The buffer where to save the computed digest. * @dgst_len: The length of @dgst. * * Return: 0 on success; negative error code otherwise. */ int ocs_hcu_hash_final(struct ocs_hcu_dev *hcu_dev, const struct ocs_hcu_hash_ctx *ctx, u8 *dgst, size_t dgst_len) { … } /** * ocs_hcu_digest() - Compute hash digest. * @hcu_dev: The OCS HCU device to use. * @algo: The hash algorithm to use. * @data: The input data to process. * @data_len: The length of @data. * @dgst: The buffer where to save the computed digest. * @dgst_len: The length of @dgst. * * Return: 0 on success; negative error code otherwise. */ int ocs_hcu_digest(struct ocs_hcu_dev *hcu_dev, enum ocs_hcu_algo algo, void *data, size_t data_len, u8 *dgst, size_t dgst_len) { … } /** * ocs_hcu_hmac() - Compute HMAC. * @hcu_dev: The OCS HCU device to use. * @algo: The hash algorithm to use with HMAC. * @key: The key to use. * @dma_list: The OCS DMA list mapping the input data to process. * @key_len: The length of @key. * @dgst: The buffer where to save the computed HMAC. * @dgst_len: The length of @dgst. * * Return: 0 on success; negative error code otherwise. */ int ocs_hcu_hmac(struct ocs_hcu_dev *hcu_dev, enum ocs_hcu_algo algo, const u8 *key, size_t key_len, const struct ocs_hcu_dma_list *dma_list, u8 *dgst, size_t dgst_len) { … } irqreturn_t ocs_hcu_irq_handler(int irq, void *dev_id) { … } MODULE_DESCRIPTION(…) …; MODULE_LICENSE(…) …;