Commit 62f58b16 authored by Neal Liu's avatar Neal Liu Committed by Herbert Xu
Browse files

crypto: aspeed - add HACE crypto driver



Add HACE crypto driver to support symmetric-key
encryption and decryption with multiple modes of
operation.

Signed-off-by: default avatarNeal Liu <neal_liu@aspeedtech.com>
Signed-off-by: default avatarJohnny Huang <johnny_huang@aspeedtech.com>
Reviewed-by: default avatarDhananjay Phadke <dphadke@linux.microsoft.com>
Signed-off-by: default avatarHerbert Xu <herbert@gondor.apana.org.au>
parent c3708e65
Loading
Loading
Loading
Loading
+17 −0
Original line number Diff line number Diff line
@@ -30,3 +30,20 @@ config CRYPTO_DEV_ASPEED_HACE_HASH
	  hash driver.
	  Supports multiple message digest standards, including
	  SHA-1, SHA-224, SHA-256, SHA-384, SHA-512, and so on.

config CRYPTO_DEV_ASPEED_HACE_CRYPTO
	bool "Enable Aspeed Hash & Crypto Engine (HACE) crypto"
	depends on CRYPTO_DEV_ASPEED
	select CRYPTO_ENGINE
	select CRYPTO_AES
	select CRYPTO_DES
	select CRYPTO_ECB
	select CRYPTO_CBC
	select CRYPTO_CFB
	select CRYPTO_OFB
	select CRYPTO_CTR
	help
	  Select here to enable Aspeed Hash & Crypto Engine (HACE)
	  crypto driver.
	  Supports AES/DES symmetric-key encryption and decryption
	  with ECB/CBC/CFB/OFB/CTR options.
+5 −2
Original line number Diff line number Diff line
obj-$(CONFIG_CRYPTO_DEV_ASPEED) += aspeed_crypto.o
aspeed_crypto-objs := aspeed-hace.o	\
		      $(hace-hash-y)
		      $(hace-hash-y)	\
		      $(hace-crypto-y)

obj-$(CONFIG_CRYPTO_DEV_ASPEED_HACE_HASH) += aspeed-hace-hash.o
hace-hash-$(CONFIG_CRYPTO_DEV_ASPEED_HACE_HASH) := aspeed-hace-hash.o
obj-$(CONFIG_CRYPTO_DEV_ASPEED_HACE_CRYPTO) += aspeed-hace-crypto.o
hace-crypto-$(CONFIG_CRYPTO_DEV_ASPEED_HACE_CRYPTO) := aspeed-hace-crypto.o
+1135 −0

File added.

Preview size limit exceeded, changes collapsed.

+83 −1
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@
static irqreturn_t aspeed_hace_irq(int irq, void *dev)
{
	struct aspeed_hace_dev *hace_dev = (struct aspeed_hace_dev *)dev;
	struct aspeed_engine_crypto *crypto_engine = &hace_dev->crypto_engine;
	struct aspeed_engine_hash *hash_engine = &hace_dev->hash_engine;
	u32 sts;

@@ -40,9 +41,24 @@ static irqreturn_t aspeed_hace_irq(int irq, void *dev)
			dev_warn(hace_dev->dev, "HASH no active requests.\n");
	}

	if (sts & HACE_CRYPTO_ISR) {
		if (crypto_engine->flags & CRYPTO_FLAGS_BUSY)
			tasklet_schedule(&crypto_engine->done_task);
		else
			dev_warn(hace_dev->dev, "CRYPTO no active requests.\n");
	}

	return IRQ_HANDLED;
}

static void aspeed_hace_crypto_done_task(unsigned long data)
{
	struct aspeed_hace_dev *hace_dev = (struct aspeed_hace_dev *)data;
	struct aspeed_engine_crypto *crypto_engine = &hace_dev->crypto_engine;

	crypto_engine->resume(hace_dev);
}

static void aspeed_hace_hash_done_task(unsigned long data)
{
	struct aspeed_hace_dev *hace_dev = (struct aspeed_hace_dev *)data;
@@ -56,6 +72,9 @@ static void aspeed_hace_register(struct aspeed_hace_dev *hace_dev)
#ifdef CONFIG_CRYPTO_DEV_ASPEED_HACE_HASH
	aspeed_register_hace_hash_algs(hace_dev);
#endif
#ifdef CONFIG_CRYPTO_DEV_ASPEED_HACE_CRYPTO
	aspeed_register_hace_crypto_algs(hace_dev);
#endif
}

static void aspeed_hace_unregister(struct aspeed_hace_dev *hace_dev)
@@ -63,6 +82,9 @@ static void aspeed_hace_unregister(struct aspeed_hace_dev *hace_dev)
#ifdef CONFIG_CRYPTO_DEV_ASPEED_HACE_HASH
	aspeed_unregister_hace_hash_algs(hace_dev);
#endif
#ifdef CONFIG_CRYPTO_DEV_ASPEED_HACE_CRYPTO
	aspeed_unregister_hace_crypto_algs(hace_dev);
#endif
}

static const struct of_device_id aspeed_hace_of_matches[] = {
@@ -73,6 +95,7 @@ static const struct of_device_id aspeed_hace_of_matches[] = {

static int aspeed_hace_probe(struct platform_device *pdev)
{
	struct aspeed_engine_crypto *crypto_engine;
	const struct of_device_id *hace_dev_id;
	struct aspeed_engine_hash *hash_engine;
	struct aspeed_hace_dev *hace_dev;
@@ -93,6 +116,7 @@ static int aspeed_hace_probe(struct platform_device *pdev)
	hace_dev->dev = &pdev->dev;
	hace_dev->version = (unsigned long)hace_dev_id->data;
	hash_engine = &hace_dev->hash_engine;
	crypto_engine = &hace_dev->crypto_engine;

	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);

@@ -146,6 +170,21 @@ static int aspeed_hace_probe(struct platform_device *pdev)
	tasklet_init(&hash_engine->done_task, aspeed_hace_hash_done_task,
		     (unsigned long)hace_dev);

	/* Initialize crypto hardware engine structure for crypto */
	hace_dev->crypt_engine_crypto = crypto_engine_alloc_init(hace_dev->dev,
								 true);
	if (!hace_dev->crypt_engine_crypto) {
		rc = -ENOMEM;
		goto err_engine_hash_start;
	}

	rc = crypto_engine_start(hace_dev->crypt_engine_crypto);
	if (rc)
		goto err_engine_crypto_start;

	tasklet_init(&crypto_engine->done_task, aspeed_hace_crypto_done_task,
		     (unsigned long)hace_dev);

	/* Allocate DMA buffer for hash engine input used */
	hash_engine->ahash_src_addr =
		dmam_alloc_coherent(&pdev->dev,
@@ -155,7 +194,45 @@ static int aspeed_hace_probe(struct platform_device *pdev)
	if (!hash_engine->ahash_src_addr) {
		dev_err(&pdev->dev, "Failed to allocate dma buffer\n");
		rc = -ENOMEM;
		goto err_engine_hash_start;
		goto err_engine_crypto_start;
	}

	/* Allocate DMA buffer for crypto engine context used */
	crypto_engine->cipher_ctx =
		dmam_alloc_coherent(&pdev->dev,
				    PAGE_SIZE,
				    &crypto_engine->cipher_ctx_dma,
				    GFP_KERNEL);
	if (!crypto_engine->cipher_ctx) {
		dev_err(&pdev->dev, "Failed to allocate cipher ctx dma\n");
		rc = -ENOMEM;
		goto err_engine_crypto_start;
	}

	/* Allocate DMA buffer for crypto engine input used */
	crypto_engine->cipher_addr =
		dmam_alloc_coherent(&pdev->dev,
				    ASPEED_CRYPTO_SRC_DMA_BUF_LEN,
				    &crypto_engine->cipher_dma_addr,
				    GFP_KERNEL);
	if (!crypto_engine->cipher_addr) {
		dev_err(&pdev->dev, "Failed to allocate cipher addr dma\n");
		rc = -ENOMEM;
		goto err_engine_crypto_start;
	}

	/* Allocate DMA buffer for crypto engine output used */
	if (hace_dev->version == AST2600_VERSION) {
		crypto_engine->dst_sg_addr =
			dmam_alloc_coherent(&pdev->dev,
					    ASPEED_CRYPTO_DST_DMA_BUF_LEN,
					    &crypto_engine->dst_sg_dma_addr,
					    GFP_KERNEL);
		if (!crypto_engine->dst_sg_addr) {
			dev_err(&pdev->dev, "Failed to allocate dst_sg dma\n");
			rc = -ENOMEM;
			goto err_engine_crypto_start;
		}
	}

	aspeed_hace_register(hace_dev);
@@ -164,6 +241,8 @@ static int aspeed_hace_probe(struct platform_device *pdev)

	return 0;

err_engine_crypto_start:
	crypto_engine_exit(hace_dev->crypt_engine_crypto);
err_engine_hash_start:
	crypto_engine_exit(hace_dev->crypt_engine_hash);
clk_exit:
@@ -175,13 +254,16 @@ static int aspeed_hace_probe(struct platform_device *pdev)
static int aspeed_hace_remove(struct platform_device *pdev)
{
	struct aspeed_hace_dev *hace_dev = platform_get_drvdata(pdev);
	struct aspeed_engine_crypto *crypto_engine = &hace_dev->crypto_engine;
	struct aspeed_engine_hash *hash_engine = &hace_dev->hash_engine;

	aspeed_hace_unregister(hace_dev);

	crypto_engine_exit(hace_dev->crypt_engine_hash);
	crypto_engine_exit(hace_dev->crypt_engine_crypto);

	tasklet_kill(&hash_engine->done_task);
	tasklet_kill(&crypto_engine->done_task);

	clk_disable_unprepare(hace_dev->clk);

+112 −0
Original line number Diff line number Diff line
@@ -7,9 +7,12 @@
#include <linux/err.h>
#include <linux/fips.h>
#include <linux/dma-mapping.h>
#include <crypto/aes.h>
#include <crypto/des.h>
#include <crypto/scatterwalk.h>
#include <crypto/internal/aead.h>
#include <crypto/internal/akcipher.h>
#include <crypto/internal/des.h>
#include <crypto/internal/hash.h>
#include <crypto/internal/kpp.h>
#include <crypto/internal/skcipher.h>
@@ -24,15 +27,75 @@
 * HACE register definitions *
 *                           *
 * ***************************/
#define ASPEED_HACE_SRC			0x00	/* Crypto Data Source Base Address Register */
#define ASPEED_HACE_DEST		0x04	/* Crypto Data Destination Base Address Register */
#define ASPEED_HACE_CONTEXT		0x08	/* Crypto Context Buffer Base Address Register */
#define ASPEED_HACE_DATA_LEN		0x0C	/* Crypto Data Length Register */
#define ASPEED_HACE_CMD			0x10	/* Crypto Engine Command Register */

/* G5 */
#define ASPEED_HACE_TAG			0x18	/* HACE Tag Register */
/* G6 */
#define ASPEED_HACE_GCM_ADD_LEN		0x14	/* Crypto AES-GCM Additional Data Length Register */
#define ASPEED_HACE_GCM_TAG_BASE_ADDR	0x18	/* Crypto AES-GCM Tag Write Buff Base Address Reg */

#define ASPEED_HACE_STS			0x1C	/* HACE Status Register */

#define ASPEED_HACE_HASH_SRC		0x20	/* Hash Data Source Base Address Register */
#define ASPEED_HACE_HASH_DIGEST_BUFF	0x24	/* Hash Digest Write Buffer Base Address Register */
#define ASPEED_HACE_HASH_KEY_BUFF	0x28	/* Hash HMAC Key Buffer Base Address Register */
#define ASPEED_HACE_HASH_DATA_LEN	0x2C	/* Hash Data Length Register */
#define ASPEED_HACE_HASH_CMD		0x30	/* Hash Engine Command Register */

/* crypto cmd */
#define  HACE_CMD_SINGLE_DES		0
#define  HACE_CMD_TRIPLE_DES		BIT(17)
#define  HACE_CMD_AES_SELECT		0
#define  HACE_CMD_DES_SELECT		BIT(16)
#define  HACE_CMD_ISR_EN		BIT(12)
#define  HACE_CMD_CONTEXT_SAVE_ENABLE	(0)
#define  HACE_CMD_CONTEXT_SAVE_DISABLE	BIT(9)
#define  HACE_CMD_AES			(0)
#define  HACE_CMD_DES			(0)
#define  HACE_CMD_RC4			BIT(8)
#define  HACE_CMD_DECRYPT		(0)
#define  HACE_CMD_ENCRYPT		BIT(7)

#define  HACE_CMD_ECB			(0x0 << 4)
#define  HACE_CMD_CBC			(0x1 << 4)
#define  HACE_CMD_CFB			(0x2 << 4)
#define  HACE_CMD_OFB			(0x3 << 4)
#define  HACE_CMD_CTR			(0x4 << 4)
#define  HACE_CMD_OP_MODE_MASK		(0x7 << 4)

#define  HACE_CMD_AES128		(0x0 << 2)
#define  HACE_CMD_AES192		(0x1 << 2)
#define  HACE_CMD_AES256		(0x2 << 2)
#define  HACE_CMD_OP_CASCADE		(0x3)
#define  HACE_CMD_OP_INDEPENDENT	(0x1)

/* G5 */
#define  HACE_CMD_RI_WO_DATA_ENABLE	(0)
#define  HACE_CMD_RI_WO_DATA_DISABLE	BIT(11)
#define  HACE_CMD_CONTEXT_LOAD_ENABLE	(0)
#define  HACE_CMD_CONTEXT_LOAD_DISABLE	BIT(10)
/* G6 */
#define  HACE_CMD_AES_KEY_FROM_OTP	BIT(24)
#define  HACE_CMD_GHASH_TAG_XOR_EN	BIT(23)
#define  HACE_CMD_GHASH_PAD_LEN_INV	BIT(22)
#define  HACE_CMD_GCM_TAG_ADDR_SEL	BIT(21)
#define  HACE_CMD_MBUS_REQ_SYNC_EN	BIT(20)
#define  HACE_CMD_DES_SG_CTRL		BIT(19)
#define  HACE_CMD_SRC_SG_CTRL		BIT(18)
#define  HACE_CMD_CTR_IV_AES_96		(0x1 << 14)
#define  HACE_CMD_CTR_IV_DES_32		(0x1 << 14)
#define  HACE_CMD_CTR_IV_AES_64		(0x2 << 14)
#define  HACE_CMD_CTR_IV_AES_32		(0x3 << 14)
#define  HACE_CMD_AES_KEY_HW_EXP	BIT(13)
#define  HACE_CMD_GCM			(0x5 << 4)

/* interrupt status reg */
#define  HACE_CRYPTO_ISR		BIT(12)
#define  HACE_HASH_ISR			BIT(9)
#define  HACE_HASH_BUSY			BIT(0)

@@ -77,6 +140,9 @@
#define ASPEED_HASH_SRC_DMA_BUF_LEN	0xa000
#define ASPEED_HASH_QUEUE_LENGTH	50

#define HACE_CMD_IV_REQUIRE		(HACE_CMD_CBC | HACE_CMD_CFB | \
					 HACE_CMD_OFB | HACE_CMD_CTR)

struct aspeed_hace_dev;

typedef int (*aspeed_hace_fn_t)(struct aspeed_hace_dev *);
@@ -147,6 +213,48 @@ struct aspeed_sham_reqctx {
	u64			digcnt[2];
};

struct aspeed_engine_crypto {
	struct tasklet_struct		done_task;
	unsigned long			flags;
	struct skcipher_request		*req;

	/* context buffer */
	void				*cipher_ctx;
	dma_addr_t			cipher_ctx_dma;

	/* input buffer, could be single/scatter-gather lists */
	void				*cipher_addr;
	dma_addr_t			cipher_dma_addr;

	/* output buffer, only used in scatter-gather lists */
	void				*dst_sg_addr;
	dma_addr_t			dst_sg_dma_addr;

	/* callback func */
	aspeed_hace_fn_t		resume;
};

struct aspeed_cipher_ctx {
	struct crypto_engine_ctx	enginectx;

	struct aspeed_hace_dev		*hace_dev;
	int				key_len;
	u8				key[AES_MAX_KEYLENGTH];

	/* callback func */
	aspeed_hace_fn_t		start;

	struct crypto_skcipher          *fallback_tfm;
};

struct aspeed_cipher_reqctx {
	int enc_cmd;
	int src_nents;
	int dst_nents;

	struct skcipher_request         fallback_req;   /* keep at the end */
};

struct aspeed_hace_dev {
	void __iomem			*regs;
	struct device			*dev;
@@ -155,8 +263,10 @@ struct aspeed_hace_dev {
	unsigned long			version;

	struct crypto_engine		*crypt_engine_hash;
	struct crypto_engine		*crypt_engine_crypto;

	struct aspeed_engine_hash	hash_engine;
	struct aspeed_engine_crypto	crypto_engine;
};

struct aspeed_hace_alg {
@@ -182,5 +292,7 @@ enum aspeed_version {

void aspeed_register_hace_hash_algs(struct aspeed_hace_dev *hace_dev);
void aspeed_unregister_hace_hash_algs(struct aspeed_hace_dev *hace_dev);
void aspeed_register_hace_crypto_algs(struct aspeed_hace_dev *hace_dev);
void aspeed_unregister_hace_crypto_algs(struct aspeed_hace_dev *hace_dev);

#endif