18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Accelerated GHASH implementation with ARMv8 PMULL instructions.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2014 - 2018 Linaro Ltd. <ard.biesheuvel@linaro.org>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <asm/neon.h>
98c2ecf20Sopenharmony_ci#include <asm/simd.h>
108c2ecf20Sopenharmony_ci#include <asm/unaligned.h>
118c2ecf20Sopenharmony_ci#include <crypto/aes.h>
128c2ecf20Sopenharmony_ci#include <crypto/algapi.h>
138c2ecf20Sopenharmony_ci#include <crypto/b128ops.h>
148c2ecf20Sopenharmony_ci#include <crypto/gf128mul.h>
158c2ecf20Sopenharmony_ci#include <crypto/internal/aead.h>
168c2ecf20Sopenharmony_ci#include <crypto/internal/hash.h>
178c2ecf20Sopenharmony_ci#include <crypto/internal/simd.h>
188c2ecf20Sopenharmony_ci#include <crypto/internal/skcipher.h>
198c2ecf20Sopenharmony_ci#include <crypto/scatterwalk.h>
208c2ecf20Sopenharmony_ci#include <linux/cpufeature.h>
218c2ecf20Sopenharmony_ci#include <linux/crypto.h>
228c2ecf20Sopenharmony_ci#include <linux/module.h>
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("GHASH and AES-GCM using ARMv8 Crypto Extensions");
258c2ecf20Sopenharmony_ciMODULE_AUTHOR("Ard Biesheuvel <ard.biesheuvel@linaro.org>");
268c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
278c2ecf20Sopenharmony_ciMODULE_ALIAS_CRYPTO("ghash");
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci#define GHASH_BLOCK_SIZE	16
308c2ecf20Sopenharmony_ci#define GHASH_DIGEST_SIZE	16
318c2ecf20Sopenharmony_ci#define GCM_IV_SIZE		12
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_cistruct ghash_key {
348c2ecf20Sopenharmony_ci	be128			k;
358c2ecf20Sopenharmony_ci	u64			h[][2];
368c2ecf20Sopenharmony_ci};
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistruct ghash_desc_ctx {
398c2ecf20Sopenharmony_ci	u64 digest[GHASH_DIGEST_SIZE/sizeof(u64)];
408c2ecf20Sopenharmony_ci	u8 buf[GHASH_BLOCK_SIZE];
418c2ecf20Sopenharmony_ci	u32 count;
428c2ecf20Sopenharmony_ci};
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_cistruct gcm_aes_ctx {
458c2ecf20Sopenharmony_ci	struct crypto_aes_ctx	aes_key;
468c2ecf20Sopenharmony_ci	struct ghash_key	ghash_key;
478c2ecf20Sopenharmony_ci};
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ciasmlinkage void pmull_ghash_update_p64(int blocks, u64 dg[], const char *src,
508c2ecf20Sopenharmony_ci				       u64 const h[][2], const char *head);
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ciasmlinkage void pmull_ghash_update_p8(int blocks, u64 dg[], const char *src,
538c2ecf20Sopenharmony_ci				      u64 const h[][2], const char *head);
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ciasmlinkage void pmull_gcm_encrypt(int bytes, u8 dst[], const u8 src[],
568c2ecf20Sopenharmony_ci				  u64 const h[][2], u64 dg[], u8 ctr[],
578c2ecf20Sopenharmony_ci				  u32 const rk[], int rounds, u8 tag[]);
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ciasmlinkage void pmull_gcm_decrypt(int bytes, u8 dst[], const u8 src[],
608c2ecf20Sopenharmony_ci				  u64 const h[][2], u64 dg[], u8 ctr[],
618c2ecf20Sopenharmony_ci				  u32 const rk[], int rounds, u8 tag[]);
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_cistatic int ghash_init(struct shash_desc *desc)
648c2ecf20Sopenharmony_ci{
658c2ecf20Sopenharmony_ci	struct ghash_desc_ctx *ctx = shash_desc_ctx(desc);
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	*ctx = (struct ghash_desc_ctx){};
688c2ecf20Sopenharmony_ci	return 0;
698c2ecf20Sopenharmony_ci}
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_cistatic void ghash_do_update(int blocks, u64 dg[], const char *src,
728c2ecf20Sopenharmony_ci			    struct ghash_key *key, const char *head)
738c2ecf20Sopenharmony_ci{
748c2ecf20Sopenharmony_ci	be128 dst = { cpu_to_be64(dg[1]), cpu_to_be64(dg[0]) };
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	do {
778c2ecf20Sopenharmony_ci		const u8 *in = src;
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci		if (head) {
808c2ecf20Sopenharmony_ci			in = head;
818c2ecf20Sopenharmony_ci			blocks++;
828c2ecf20Sopenharmony_ci			head = NULL;
838c2ecf20Sopenharmony_ci		} else {
848c2ecf20Sopenharmony_ci			src += GHASH_BLOCK_SIZE;
858c2ecf20Sopenharmony_ci		}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci		crypto_xor((u8 *)&dst, in, GHASH_BLOCK_SIZE);
888c2ecf20Sopenharmony_ci		gf128mul_lle(&dst, &key->k);
898c2ecf20Sopenharmony_ci	} while (--blocks);
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	dg[0] = be64_to_cpu(dst.b);
928c2ecf20Sopenharmony_ci	dg[1] = be64_to_cpu(dst.a);
938c2ecf20Sopenharmony_ci}
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_cistatic __always_inline
968c2ecf20Sopenharmony_civoid ghash_do_simd_update(int blocks, u64 dg[], const char *src,
978c2ecf20Sopenharmony_ci			  struct ghash_key *key, const char *head,
988c2ecf20Sopenharmony_ci			  void (*simd_update)(int blocks, u64 dg[],
998c2ecf20Sopenharmony_ci					      const char *src,
1008c2ecf20Sopenharmony_ci					      u64 const h[][2],
1018c2ecf20Sopenharmony_ci					      const char *head))
1028c2ecf20Sopenharmony_ci{
1038c2ecf20Sopenharmony_ci	if (likely(crypto_simd_usable())) {
1048c2ecf20Sopenharmony_ci		kernel_neon_begin();
1058c2ecf20Sopenharmony_ci		simd_update(blocks, dg, src, key->h, head);
1068c2ecf20Sopenharmony_ci		kernel_neon_end();
1078c2ecf20Sopenharmony_ci	} else {
1088c2ecf20Sopenharmony_ci		ghash_do_update(blocks, dg, src, key, head);
1098c2ecf20Sopenharmony_ci	}
1108c2ecf20Sopenharmony_ci}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci/* avoid hogging the CPU for too long */
1138c2ecf20Sopenharmony_ci#define MAX_BLOCKS	(SZ_64K / GHASH_BLOCK_SIZE)
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_cistatic int ghash_update(struct shash_desc *desc, const u8 *src,
1168c2ecf20Sopenharmony_ci			unsigned int len)
1178c2ecf20Sopenharmony_ci{
1188c2ecf20Sopenharmony_ci	struct ghash_desc_ctx *ctx = shash_desc_ctx(desc);
1198c2ecf20Sopenharmony_ci	unsigned int partial = ctx->count % GHASH_BLOCK_SIZE;
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	ctx->count += len;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	if ((partial + len) >= GHASH_BLOCK_SIZE) {
1248c2ecf20Sopenharmony_ci		struct ghash_key *key = crypto_shash_ctx(desc->tfm);
1258c2ecf20Sopenharmony_ci		int blocks;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci		if (partial) {
1288c2ecf20Sopenharmony_ci			int p = GHASH_BLOCK_SIZE - partial;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci			memcpy(ctx->buf + partial, src, p);
1318c2ecf20Sopenharmony_ci			src += p;
1328c2ecf20Sopenharmony_ci			len -= p;
1338c2ecf20Sopenharmony_ci		}
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci		blocks = len / GHASH_BLOCK_SIZE;
1368c2ecf20Sopenharmony_ci		len %= GHASH_BLOCK_SIZE;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci		do {
1398c2ecf20Sopenharmony_ci			int chunk = min(blocks, MAX_BLOCKS);
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci			ghash_do_simd_update(chunk, ctx->digest, src, key,
1428c2ecf20Sopenharmony_ci					     partial ? ctx->buf : NULL,
1438c2ecf20Sopenharmony_ci					     pmull_ghash_update_p8);
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci			blocks -= chunk;
1468c2ecf20Sopenharmony_ci			src += chunk * GHASH_BLOCK_SIZE;
1478c2ecf20Sopenharmony_ci			partial = 0;
1488c2ecf20Sopenharmony_ci		} while (unlikely(blocks > 0));
1498c2ecf20Sopenharmony_ci	}
1508c2ecf20Sopenharmony_ci	if (len)
1518c2ecf20Sopenharmony_ci		memcpy(ctx->buf + partial, src, len);
1528c2ecf20Sopenharmony_ci	return 0;
1538c2ecf20Sopenharmony_ci}
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_cistatic int ghash_final(struct shash_desc *desc, u8 *dst)
1568c2ecf20Sopenharmony_ci{
1578c2ecf20Sopenharmony_ci	struct ghash_desc_ctx *ctx = shash_desc_ctx(desc);
1588c2ecf20Sopenharmony_ci	unsigned int partial = ctx->count % GHASH_BLOCK_SIZE;
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	if (partial) {
1618c2ecf20Sopenharmony_ci		struct ghash_key *key = crypto_shash_ctx(desc->tfm);
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci		memset(ctx->buf + partial, 0, GHASH_BLOCK_SIZE - partial);
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci		ghash_do_simd_update(1, ctx->digest, ctx->buf, key, NULL,
1668c2ecf20Sopenharmony_ci				     pmull_ghash_update_p8);
1678c2ecf20Sopenharmony_ci	}
1688c2ecf20Sopenharmony_ci	put_unaligned_be64(ctx->digest[1], dst);
1698c2ecf20Sopenharmony_ci	put_unaligned_be64(ctx->digest[0], dst + 8);
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	*ctx = (struct ghash_desc_ctx){};
1728c2ecf20Sopenharmony_ci	return 0;
1738c2ecf20Sopenharmony_ci}
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_cistatic void ghash_reflect(u64 h[], const be128 *k)
1768c2ecf20Sopenharmony_ci{
1778c2ecf20Sopenharmony_ci	u64 carry = be64_to_cpu(k->a) & BIT(63) ? 1 : 0;
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	h[0] = (be64_to_cpu(k->b) << 1) | carry;
1808c2ecf20Sopenharmony_ci	h[1] = (be64_to_cpu(k->a) << 1) | (be64_to_cpu(k->b) >> 63);
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	if (carry)
1838c2ecf20Sopenharmony_ci		h[1] ^= 0xc200000000000000UL;
1848c2ecf20Sopenharmony_ci}
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_cistatic int ghash_setkey(struct crypto_shash *tfm,
1878c2ecf20Sopenharmony_ci			const u8 *inkey, unsigned int keylen)
1888c2ecf20Sopenharmony_ci{
1898c2ecf20Sopenharmony_ci	struct ghash_key *key = crypto_shash_ctx(tfm);
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	if (keylen != GHASH_BLOCK_SIZE)
1928c2ecf20Sopenharmony_ci		return -EINVAL;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	/* needed for the fallback */
1958c2ecf20Sopenharmony_ci	memcpy(&key->k, inkey, GHASH_BLOCK_SIZE);
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	ghash_reflect(key->h[0], &key->k);
1988c2ecf20Sopenharmony_ci	return 0;
1998c2ecf20Sopenharmony_ci}
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_cistatic struct shash_alg ghash_alg = {
2028c2ecf20Sopenharmony_ci	.base.cra_name		= "ghash",
2038c2ecf20Sopenharmony_ci	.base.cra_driver_name	= "ghash-neon",
2048c2ecf20Sopenharmony_ci	.base.cra_priority	= 150,
2058c2ecf20Sopenharmony_ci	.base.cra_blocksize	= GHASH_BLOCK_SIZE,
2068c2ecf20Sopenharmony_ci	.base.cra_ctxsize	= sizeof(struct ghash_key) + sizeof(u64[2]),
2078c2ecf20Sopenharmony_ci	.base.cra_module	= THIS_MODULE,
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	.digestsize		= GHASH_DIGEST_SIZE,
2108c2ecf20Sopenharmony_ci	.init			= ghash_init,
2118c2ecf20Sopenharmony_ci	.update			= ghash_update,
2128c2ecf20Sopenharmony_ci	.final			= ghash_final,
2138c2ecf20Sopenharmony_ci	.setkey			= ghash_setkey,
2148c2ecf20Sopenharmony_ci	.descsize		= sizeof(struct ghash_desc_ctx),
2158c2ecf20Sopenharmony_ci};
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_cistatic int num_rounds(struct crypto_aes_ctx *ctx)
2188c2ecf20Sopenharmony_ci{
2198c2ecf20Sopenharmony_ci	/*
2208c2ecf20Sopenharmony_ci	 * # of rounds specified by AES:
2218c2ecf20Sopenharmony_ci	 * 128 bit key		10 rounds
2228c2ecf20Sopenharmony_ci	 * 192 bit key		12 rounds
2238c2ecf20Sopenharmony_ci	 * 256 bit key		14 rounds
2248c2ecf20Sopenharmony_ci	 * => n byte key	=> 6 + (n/4) rounds
2258c2ecf20Sopenharmony_ci	 */
2268c2ecf20Sopenharmony_ci	return 6 + ctx->key_length / 4;
2278c2ecf20Sopenharmony_ci}
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_cistatic int gcm_setkey(struct crypto_aead *tfm, const u8 *inkey,
2308c2ecf20Sopenharmony_ci		      unsigned int keylen)
2318c2ecf20Sopenharmony_ci{
2328c2ecf20Sopenharmony_ci	struct gcm_aes_ctx *ctx = crypto_aead_ctx(tfm);
2338c2ecf20Sopenharmony_ci	u8 key[GHASH_BLOCK_SIZE];
2348c2ecf20Sopenharmony_ci	be128 h;
2358c2ecf20Sopenharmony_ci	int ret;
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	ret = aes_expandkey(&ctx->aes_key, inkey, keylen);
2388c2ecf20Sopenharmony_ci	if (ret)
2398c2ecf20Sopenharmony_ci		return -EINVAL;
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	aes_encrypt(&ctx->aes_key, key, (u8[AES_BLOCK_SIZE]){});
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	/* needed for the fallback */
2448c2ecf20Sopenharmony_ci	memcpy(&ctx->ghash_key.k, key, GHASH_BLOCK_SIZE);
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	ghash_reflect(ctx->ghash_key.h[0], &ctx->ghash_key.k);
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	h = ctx->ghash_key.k;
2498c2ecf20Sopenharmony_ci	gf128mul_lle(&h, &ctx->ghash_key.k);
2508c2ecf20Sopenharmony_ci	ghash_reflect(ctx->ghash_key.h[1], &h);
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	gf128mul_lle(&h, &ctx->ghash_key.k);
2538c2ecf20Sopenharmony_ci	ghash_reflect(ctx->ghash_key.h[2], &h);
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	gf128mul_lle(&h, &ctx->ghash_key.k);
2568c2ecf20Sopenharmony_ci	ghash_reflect(ctx->ghash_key.h[3], &h);
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	return 0;
2598c2ecf20Sopenharmony_ci}
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_cistatic int gcm_setauthsize(struct crypto_aead *tfm, unsigned int authsize)
2628c2ecf20Sopenharmony_ci{
2638c2ecf20Sopenharmony_ci	switch (authsize) {
2648c2ecf20Sopenharmony_ci	case 4:
2658c2ecf20Sopenharmony_ci	case 8:
2668c2ecf20Sopenharmony_ci	case 12 ... 16:
2678c2ecf20Sopenharmony_ci		break;
2688c2ecf20Sopenharmony_ci	default:
2698c2ecf20Sopenharmony_ci		return -EINVAL;
2708c2ecf20Sopenharmony_ci	}
2718c2ecf20Sopenharmony_ci	return 0;
2728c2ecf20Sopenharmony_ci}
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_cistatic void gcm_update_mac(u64 dg[], const u8 *src, int count, u8 buf[],
2758c2ecf20Sopenharmony_ci			   int *buf_count, struct gcm_aes_ctx *ctx)
2768c2ecf20Sopenharmony_ci{
2778c2ecf20Sopenharmony_ci	if (*buf_count > 0) {
2788c2ecf20Sopenharmony_ci		int buf_added = min(count, GHASH_BLOCK_SIZE - *buf_count);
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci		memcpy(&buf[*buf_count], src, buf_added);
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci		*buf_count += buf_added;
2838c2ecf20Sopenharmony_ci		src += buf_added;
2848c2ecf20Sopenharmony_ci		count -= buf_added;
2858c2ecf20Sopenharmony_ci	}
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	if (count >= GHASH_BLOCK_SIZE || *buf_count == GHASH_BLOCK_SIZE) {
2888c2ecf20Sopenharmony_ci		int blocks = count / GHASH_BLOCK_SIZE;
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci		ghash_do_simd_update(blocks, dg, src, &ctx->ghash_key,
2918c2ecf20Sopenharmony_ci				     *buf_count ? buf : NULL,
2928c2ecf20Sopenharmony_ci				     pmull_ghash_update_p64);
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci		src += blocks * GHASH_BLOCK_SIZE;
2958c2ecf20Sopenharmony_ci		count %= GHASH_BLOCK_SIZE;
2968c2ecf20Sopenharmony_ci		*buf_count = 0;
2978c2ecf20Sopenharmony_ci	}
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	if (count > 0) {
3008c2ecf20Sopenharmony_ci		memcpy(buf, src, count);
3018c2ecf20Sopenharmony_ci		*buf_count = count;
3028c2ecf20Sopenharmony_ci	}
3038c2ecf20Sopenharmony_ci}
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_cistatic void gcm_calculate_auth_mac(struct aead_request *req, u64 dg[])
3068c2ecf20Sopenharmony_ci{
3078c2ecf20Sopenharmony_ci	struct crypto_aead *aead = crypto_aead_reqtfm(req);
3088c2ecf20Sopenharmony_ci	struct gcm_aes_ctx *ctx = crypto_aead_ctx(aead);
3098c2ecf20Sopenharmony_ci	u8 buf[GHASH_BLOCK_SIZE];
3108c2ecf20Sopenharmony_ci	struct scatter_walk walk;
3118c2ecf20Sopenharmony_ci	u32 len = req->assoclen;
3128c2ecf20Sopenharmony_ci	int buf_count = 0;
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	scatterwalk_start(&walk, req->src);
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	do {
3178c2ecf20Sopenharmony_ci		u32 n = scatterwalk_clamp(&walk, len);
3188c2ecf20Sopenharmony_ci		u8 *p;
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci		if (!n) {
3218c2ecf20Sopenharmony_ci			scatterwalk_start(&walk, sg_next(walk.sg));
3228c2ecf20Sopenharmony_ci			n = scatterwalk_clamp(&walk, len);
3238c2ecf20Sopenharmony_ci		}
3248c2ecf20Sopenharmony_ci		p = scatterwalk_map(&walk);
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci		gcm_update_mac(dg, p, n, buf, &buf_count, ctx);
3278c2ecf20Sopenharmony_ci		len -= n;
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci		scatterwalk_unmap(p);
3308c2ecf20Sopenharmony_ci		scatterwalk_advance(&walk, n);
3318c2ecf20Sopenharmony_ci		scatterwalk_done(&walk, 0, len);
3328c2ecf20Sopenharmony_ci	} while (len);
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	if (buf_count) {
3358c2ecf20Sopenharmony_ci		memset(&buf[buf_count], 0, GHASH_BLOCK_SIZE - buf_count);
3368c2ecf20Sopenharmony_ci		ghash_do_simd_update(1, dg, buf, &ctx->ghash_key, NULL,
3378c2ecf20Sopenharmony_ci				     pmull_ghash_update_p64);
3388c2ecf20Sopenharmony_ci	}
3398c2ecf20Sopenharmony_ci}
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_cistatic int gcm_encrypt(struct aead_request *req)
3428c2ecf20Sopenharmony_ci{
3438c2ecf20Sopenharmony_ci	struct crypto_aead *aead = crypto_aead_reqtfm(req);
3448c2ecf20Sopenharmony_ci	struct gcm_aes_ctx *ctx = crypto_aead_ctx(aead);
3458c2ecf20Sopenharmony_ci	int nrounds = num_rounds(&ctx->aes_key);
3468c2ecf20Sopenharmony_ci	struct skcipher_walk walk;
3478c2ecf20Sopenharmony_ci	u8 buf[AES_BLOCK_SIZE];
3488c2ecf20Sopenharmony_ci	u8 iv[AES_BLOCK_SIZE];
3498c2ecf20Sopenharmony_ci	u64 dg[2] = {};
3508c2ecf20Sopenharmony_ci	be128 lengths;
3518c2ecf20Sopenharmony_ci	u8 *tag;
3528c2ecf20Sopenharmony_ci	int err;
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	lengths.a = cpu_to_be64(req->assoclen * 8);
3558c2ecf20Sopenharmony_ci	lengths.b = cpu_to_be64(req->cryptlen * 8);
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	if (req->assoclen)
3588c2ecf20Sopenharmony_ci		gcm_calculate_auth_mac(req, dg);
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	memcpy(iv, req->iv, GCM_IV_SIZE);
3618c2ecf20Sopenharmony_ci	put_unaligned_be32(2, iv + GCM_IV_SIZE);
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci	err = skcipher_walk_aead_encrypt(&walk, req, false);
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	if (likely(crypto_simd_usable())) {
3668c2ecf20Sopenharmony_ci		do {
3678c2ecf20Sopenharmony_ci			const u8 *src = walk.src.virt.addr;
3688c2ecf20Sopenharmony_ci			u8 *dst = walk.dst.virt.addr;
3698c2ecf20Sopenharmony_ci			int nbytes = walk.nbytes;
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci			tag = (u8 *)&lengths;
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci			if (unlikely(nbytes > 0 && nbytes < AES_BLOCK_SIZE)) {
3748c2ecf20Sopenharmony_ci				src = dst = memcpy(buf + sizeof(buf) - nbytes,
3758c2ecf20Sopenharmony_ci						   src, nbytes);
3768c2ecf20Sopenharmony_ci			} else if (nbytes < walk.total) {
3778c2ecf20Sopenharmony_ci				nbytes &= ~(AES_BLOCK_SIZE - 1);
3788c2ecf20Sopenharmony_ci				tag = NULL;
3798c2ecf20Sopenharmony_ci			}
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci			kernel_neon_begin();
3828c2ecf20Sopenharmony_ci			pmull_gcm_encrypt(nbytes, dst, src, ctx->ghash_key.h,
3838c2ecf20Sopenharmony_ci					  dg, iv, ctx->aes_key.key_enc, nrounds,
3848c2ecf20Sopenharmony_ci					  tag);
3858c2ecf20Sopenharmony_ci			kernel_neon_end();
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci			if (unlikely(!nbytes))
3888c2ecf20Sopenharmony_ci				break;
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci			if (unlikely(nbytes > 0 && nbytes < AES_BLOCK_SIZE))
3918c2ecf20Sopenharmony_ci				memcpy(walk.dst.virt.addr,
3928c2ecf20Sopenharmony_ci				       buf + sizeof(buf) - nbytes, nbytes);
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci			err = skcipher_walk_done(&walk, walk.nbytes - nbytes);
3958c2ecf20Sopenharmony_ci		} while (walk.nbytes);
3968c2ecf20Sopenharmony_ci	} else {
3978c2ecf20Sopenharmony_ci		while (walk.nbytes >= AES_BLOCK_SIZE) {
3988c2ecf20Sopenharmony_ci			int blocks = walk.nbytes / AES_BLOCK_SIZE;
3998c2ecf20Sopenharmony_ci			const u8 *src = walk.src.virt.addr;
4008c2ecf20Sopenharmony_ci			u8 *dst = walk.dst.virt.addr;
4018c2ecf20Sopenharmony_ci			int remaining = blocks;
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci			do {
4048c2ecf20Sopenharmony_ci				aes_encrypt(&ctx->aes_key, buf, iv);
4058c2ecf20Sopenharmony_ci				crypto_xor_cpy(dst, src, buf, AES_BLOCK_SIZE);
4068c2ecf20Sopenharmony_ci				crypto_inc(iv, AES_BLOCK_SIZE);
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci				dst += AES_BLOCK_SIZE;
4098c2ecf20Sopenharmony_ci				src += AES_BLOCK_SIZE;
4108c2ecf20Sopenharmony_ci			} while (--remaining > 0);
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci			ghash_do_update(blocks, dg, walk.dst.virt.addr,
4138c2ecf20Sopenharmony_ci					&ctx->ghash_key, NULL);
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci			err = skcipher_walk_done(&walk,
4168c2ecf20Sopenharmony_ci						 walk.nbytes % AES_BLOCK_SIZE);
4178c2ecf20Sopenharmony_ci		}
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci		/* handle the tail */
4208c2ecf20Sopenharmony_ci		if (walk.nbytes) {
4218c2ecf20Sopenharmony_ci			aes_encrypt(&ctx->aes_key, buf, iv);
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci			crypto_xor_cpy(walk.dst.virt.addr, walk.src.virt.addr,
4248c2ecf20Sopenharmony_ci				       buf, walk.nbytes);
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci			memcpy(buf, walk.dst.virt.addr, walk.nbytes);
4278c2ecf20Sopenharmony_ci			memset(buf + walk.nbytes, 0, sizeof(buf) - walk.nbytes);
4288c2ecf20Sopenharmony_ci		}
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci		tag = (u8 *)&lengths;
4318c2ecf20Sopenharmony_ci		ghash_do_update(1, dg, tag, &ctx->ghash_key,
4328c2ecf20Sopenharmony_ci				walk.nbytes ? buf : NULL);
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci		if (walk.nbytes)
4358c2ecf20Sopenharmony_ci			err = skcipher_walk_done(&walk, 0);
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci		put_unaligned_be64(dg[1], tag);
4388c2ecf20Sopenharmony_ci		put_unaligned_be64(dg[0], tag + 8);
4398c2ecf20Sopenharmony_ci		put_unaligned_be32(1, iv + GCM_IV_SIZE);
4408c2ecf20Sopenharmony_ci		aes_encrypt(&ctx->aes_key, iv, iv);
4418c2ecf20Sopenharmony_ci		crypto_xor(tag, iv, AES_BLOCK_SIZE);
4428c2ecf20Sopenharmony_ci	}
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	if (err)
4458c2ecf20Sopenharmony_ci		return err;
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci	/* copy authtag to end of dst */
4488c2ecf20Sopenharmony_ci	scatterwalk_map_and_copy(tag, req->dst, req->assoclen + req->cryptlen,
4498c2ecf20Sopenharmony_ci				 crypto_aead_authsize(aead), 1);
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci	return 0;
4528c2ecf20Sopenharmony_ci}
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_cistatic int gcm_decrypt(struct aead_request *req)
4558c2ecf20Sopenharmony_ci{
4568c2ecf20Sopenharmony_ci	struct crypto_aead *aead = crypto_aead_reqtfm(req);
4578c2ecf20Sopenharmony_ci	struct gcm_aes_ctx *ctx = crypto_aead_ctx(aead);
4588c2ecf20Sopenharmony_ci	unsigned int authsize = crypto_aead_authsize(aead);
4598c2ecf20Sopenharmony_ci	int nrounds = num_rounds(&ctx->aes_key);
4608c2ecf20Sopenharmony_ci	struct skcipher_walk walk;
4618c2ecf20Sopenharmony_ci	u8 buf[AES_BLOCK_SIZE];
4628c2ecf20Sopenharmony_ci	u8 iv[AES_BLOCK_SIZE];
4638c2ecf20Sopenharmony_ci	u64 dg[2] = {};
4648c2ecf20Sopenharmony_ci	be128 lengths;
4658c2ecf20Sopenharmony_ci	u8 *tag;
4668c2ecf20Sopenharmony_ci	int err;
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci	lengths.a = cpu_to_be64(req->assoclen * 8);
4698c2ecf20Sopenharmony_ci	lengths.b = cpu_to_be64((req->cryptlen - authsize) * 8);
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci	if (req->assoclen)
4728c2ecf20Sopenharmony_ci		gcm_calculate_auth_mac(req, dg);
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci	memcpy(iv, req->iv, GCM_IV_SIZE);
4758c2ecf20Sopenharmony_ci	put_unaligned_be32(2, iv + GCM_IV_SIZE);
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_ci	err = skcipher_walk_aead_decrypt(&walk, req, false);
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci	if (likely(crypto_simd_usable())) {
4808c2ecf20Sopenharmony_ci		do {
4818c2ecf20Sopenharmony_ci			const u8 *src = walk.src.virt.addr;
4828c2ecf20Sopenharmony_ci			u8 *dst = walk.dst.virt.addr;
4838c2ecf20Sopenharmony_ci			int nbytes = walk.nbytes;
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci			tag = (u8 *)&lengths;
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ci			if (unlikely(nbytes > 0 && nbytes < AES_BLOCK_SIZE)) {
4888c2ecf20Sopenharmony_ci				src = dst = memcpy(buf + sizeof(buf) - nbytes,
4898c2ecf20Sopenharmony_ci						   src, nbytes);
4908c2ecf20Sopenharmony_ci			} else if (nbytes < walk.total) {
4918c2ecf20Sopenharmony_ci				nbytes &= ~(AES_BLOCK_SIZE - 1);
4928c2ecf20Sopenharmony_ci				tag = NULL;
4938c2ecf20Sopenharmony_ci			}
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci			kernel_neon_begin();
4968c2ecf20Sopenharmony_ci			pmull_gcm_decrypt(nbytes, dst, src, ctx->ghash_key.h,
4978c2ecf20Sopenharmony_ci					  dg, iv, ctx->aes_key.key_enc, nrounds,
4988c2ecf20Sopenharmony_ci					  tag);
4998c2ecf20Sopenharmony_ci			kernel_neon_end();
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci			if (unlikely(!nbytes))
5028c2ecf20Sopenharmony_ci				break;
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci			if (unlikely(nbytes > 0 && nbytes < AES_BLOCK_SIZE))
5058c2ecf20Sopenharmony_ci				memcpy(walk.dst.virt.addr,
5068c2ecf20Sopenharmony_ci				       buf + sizeof(buf) - nbytes, nbytes);
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci			err = skcipher_walk_done(&walk, walk.nbytes - nbytes);
5098c2ecf20Sopenharmony_ci		} while (walk.nbytes);
5108c2ecf20Sopenharmony_ci	} else {
5118c2ecf20Sopenharmony_ci		while (walk.nbytes >= AES_BLOCK_SIZE) {
5128c2ecf20Sopenharmony_ci			int blocks = walk.nbytes / AES_BLOCK_SIZE;
5138c2ecf20Sopenharmony_ci			const u8 *src = walk.src.virt.addr;
5148c2ecf20Sopenharmony_ci			u8 *dst = walk.dst.virt.addr;
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci			ghash_do_update(blocks, dg, walk.src.virt.addr,
5178c2ecf20Sopenharmony_ci					&ctx->ghash_key, NULL);
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci			do {
5208c2ecf20Sopenharmony_ci				aes_encrypt(&ctx->aes_key, buf, iv);
5218c2ecf20Sopenharmony_ci				crypto_xor_cpy(dst, src, buf, AES_BLOCK_SIZE);
5228c2ecf20Sopenharmony_ci				crypto_inc(iv, AES_BLOCK_SIZE);
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci				dst += AES_BLOCK_SIZE;
5258c2ecf20Sopenharmony_ci				src += AES_BLOCK_SIZE;
5268c2ecf20Sopenharmony_ci			} while (--blocks > 0);
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci			err = skcipher_walk_done(&walk,
5298c2ecf20Sopenharmony_ci						 walk.nbytes % AES_BLOCK_SIZE);
5308c2ecf20Sopenharmony_ci		}
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci		/* handle the tail */
5338c2ecf20Sopenharmony_ci		if (walk.nbytes) {
5348c2ecf20Sopenharmony_ci			memcpy(buf, walk.src.virt.addr, walk.nbytes);
5358c2ecf20Sopenharmony_ci			memset(buf + walk.nbytes, 0, sizeof(buf) - walk.nbytes);
5368c2ecf20Sopenharmony_ci		}
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_ci		tag = (u8 *)&lengths;
5398c2ecf20Sopenharmony_ci		ghash_do_update(1, dg, tag, &ctx->ghash_key,
5408c2ecf20Sopenharmony_ci				walk.nbytes ? buf : NULL);
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci		if (walk.nbytes) {
5438c2ecf20Sopenharmony_ci			aes_encrypt(&ctx->aes_key, buf, iv);
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci			crypto_xor_cpy(walk.dst.virt.addr, walk.src.virt.addr,
5468c2ecf20Sopenharmony_ci				       buf, walk.nbytes);
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci			err = skcipher_walk_done(&walk, 0);
5498c2ecf20Sopenharmony_ci		}
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci		put_unaligned_be64(dg[1], tag);
5528c2ecf20Sopenharmony_ci		put_unaligned_be64(dg[0], tag + 8);
5538c2ecf20Sopenharmony_ci		put_unaligned_be32(1, iv + GCM_IV_SIZE);
5548c2ecf20Sopenharmony_ci		aes_encrypt(&ctx->aes_key, iv, iv);
5558c2ecf20Sopenharmony_ci		crypto_xor(tag, iv, AES_BLOCK_SIZE);
5568c2ecf20Sopenharmony_ci	}
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci	if (err)
5598c2ecf20Sopenharmony_ci		return err;
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci	/* compare calculated auth tag with the stored one */
5628c2ecf20Sopenharmony_ci	scatterwalk_map_and_copy(buf, req->src,
5638c2ecf20Sopenharmony_ci				 req->assoclen + req->cryptlen - authsize,
5648c2ecf20Sopenharmony_ci				 authsize, 0);
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	if (crypto_memneq(tag, buf, authsize))
5678c2ecf20Sopenharmony_ci		return -EBADMSG;
5688c2ecf20Sopenharmony_ci	return 0;
5698c2ecf20Sopenharmony_ci}
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_cistatic struct aead_alg gcm_aes_alg = {
5728c2ecf20Sopenharmony_ci	.ivsize			= GCM_IV_SIZE,
5738c2ecf20Sopenharmony_ci	.chunksize		= AES_BLOCK_SIZE,
5748c2ecf20Sopenharmony_ci	.maxauthsize		= AES_BLOCK_SIZE,
5758c2ecf20Sopenharmony_ci	.setkey			= gcm_setkey,
5768c2ecf20Sopenharmony_ci	.setauthsize		= gcm_setauthsize,
5778c2ecf20Sopenharmony_ci	.encrypt		= gcm_encrypt,
5788c2ecf20Sopenharmony_ci	.decrypt		= gcm_decrypt,
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci	.base.cra_name		= "gcm(aes)",
5818c2ecf20Sopenharmony_ci	.base.cra_driver_name	= "gcm-aes-ce",
5828c2ecf20Sopenharmony_ci	.base.cra_priority	= 300,
5838c2ecf20Sopenharmony_ci	.base.cra_blocksize	= 1,
5848c2ecf20Sopenharmony_ci	.base.cra_ctxsize	= sizeof(struct gcm_aes_ctx) +
5858c2ecf20Sopenharmony_ci				  4 * sizeof(u64[2]),
5868c2ecf20Sopenharmony_ci	.base.cra_module	= THIS_MODULE,
5878c2ecf20Sopenharmony_ci};
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_cistatic int __init ghash_ce_mod_init(void)
5908c2ecf20Sopenharmony_ci{
5918c2ecf20Sopenharmony_ci	if (!cpu_have_named_feature(ASIMD))
5928c2ecf20Sopenharmony_ci		return -ENODEV;
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_ci	if (cpu_have_named_feature(PMULL))
5958c2ecf20Sopenharmony_ci		return crypto_register_aead(&gcm_aes_alg);
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci	return crypto_register_shash(&ghash_alg);
5988c2ecf20Sopenharmony_ci}
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_cistatic void __exit ghash_ce_mod_exit(void)
6018c2ecf20Sopenharmony_ci{
6028c2ecf20Sopenharmony_ci	if (cpu_have_named_feature(PMULL))
6038c2ecf20Sopenharmony_ci		crypto_unregister_aead(&gcm_aes_alg);
6048c2ecf20Sopenharmony_ci	else
6058c2ecf20Sopenharmony_ci		crypto_unregister_shash(&ghash_alg);
6068c2ecf20Sopenharmony_ci}
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_cistatic const struct cpu_feature ghash_cpu_feature[] = {
6098c2ecf20Sopenharmony_ci	{ cpu_feature(PMULL) }, { }
6108c2ecf20Sopenharmony_ci};
6118c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(cpu, ghash_cpu_feature);
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_cimodule_init(ghash_ce_mod_init);
6148c2ecf20Sopenharmony_cimodule_exit(ghash_ce_mod_exit);
615