162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * AES CTR routines supporting VMX instructions on the Power 8
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2015 International Business Machines Inc.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Author: Marcelo Henrique Cerri <mhcerri@br.ibm.com>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <asm/simd.h>
1162306a36Sopenharmony_ci#include <asm/switch_to.h>
1262306a36Sopenharmony_ci#include <crypto/aes.h>
1362306a36Sopenharmony_ci#include <crypto/internal/simd.h>
1462306a36Sopenharmony_ci#include <crypto/internal/skcipher.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include "aesp8-ppc.h"
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_cistruct p8_aes_ctr_ctx {
1962306a36Sopenharmony_ci	struct crypto_skcipher *fallback;
2062306a36Sopenharmony_ci	struct aes_key enc_key;
2162306a36Sopenharmony_ci};
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_cistatic int p8_aes_ctr_init(struct crypto_skcipher *tfm)
2462306a36Sopenharmony_ci{
2562306a36Sopenharmony_ci	struct p8_aes_ctr_ctx *ctx = crypto_skcipher_ctx(tfm);
2662306a36Sopenharmony_ci	struct crypto_skcipher *fallback;
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci	fallback = crypto_alloc_skcipher("ctr(aes)", 0,
2962306a36Sopenharmony_ci					 CRYPTO_ALG_NEED_FALLBACK |
3062306a36Sopenharmony_ci					 CRYPTO_ALG_ASYNC);
3162306a36Sopenharmony_ci	if (IS_ERR(fallback)) {
3262306a36Sopenharmony_ci		pr_err("Failed to allocate ctr(aes) fallback: %ld\n",
3362306a36Sopenharmony_ci		       PTR_ERR(fallback));
3462306a36Sopenharmony_ci		return PTR_ERR(fallback);
3562306a36Sopenharmony_ci	}
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	crypto_skcipher_set_reqsize(tfm, sizeof(struct skcipher_request) +
3862306a36Sopenharmony_ci				    crypto_skcipher_reqsize(fallback));
3962306a36Sopenharmony_ci	ctx->fallback = fallback;
4062306a36Sopenharmony_ci	return 0;
4162306a36Sopenharmony_ci}
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistatic void p8_aes_ctr_exit(struct crypto_skcipher *tfm)
4462306a36Sopenharmony_ci{
4562306a36Sopenharmony_ci	struct p8_aes_ctr_ctx *ctx = crypto_skcipher_ctx(tfm);
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	crypto_free_skcipher(ctx->fallback);
4862306a36Sopenharmony_ci}
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_cistatic int p8_aes_ctr_setkey(struct crypto_skcipher *tfm, const u8 *key,
5162306a36Sopenharmony_ci			     unsigned int keylen)
5262306a36Sopenharmony_ci{
5362306a36Sopenharmony_ci	struct p8_aes_ctr_ctx *ctx = crypto_skcipher_ctx(tfm);
5462306a36Sopenharmony_ci	int ret;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	preempt_disable();
5762306a36Sopenharmony_ci	pagefault_disable();
5862306a36Sopenharmony_ci	enable_kernel_vsx();
5962306a36Sopenharmony_ci	ret = aes_p8_set_encrypt_key(key, keylen * 8, &ctx->enc_key);
6062306a36Sopenharmony_ci	disable_kernel_vsx();
6162306a36Sopenharmony_ci	pagefault_enable();
6262306a36Sopenharmony_ci	preempt_enable();
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	ret |= crypto_skcipher_setkey(ctx->fallback, key, keylen);
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	return ret ? -EINVAL : 0;
6762306a36Sopenharmony_ci}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_cistatic void p8_aes_ctr_final(const struct p8_aes_ctr_ctx *ctx,
7062306a36Sopenharmony_ci			     struct skcipher_walk *walk)
7162306a36Sopenharmony_ci{
7262306a36Sopenharmony_ci	u8 *ctrblk = walk->iv;
7362306a36Sopenharmony_ci	u8 keystream[AES_BLOCK_SIZE];
7462306a36Sopenharmony_ci	u8 *src = walk->src.virt.addr;
7562306a36Sopenharmony_ci	u8 *dst = walk->dst.virt.addr;
7662306a36Sopenharmony_ci	unsigned int nbytes = walk->nbytes;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	preempt_disable();
7962306a36Sopenharmony_ci	pagefault_disable();
8062306a36Sopenharmony_ci	enable_kernel_vsx();
8162306a36Sopenharmony_ci	aes_p8_encrypt(ctrblk, keystream, &ctx->enc_key);
8262306a36Sopenharmony_ci	disable_kernel_vsx();
8362306a36Sopenharmony_ci	pagefault_enable();
8462306a36Sopenharmony_ci	preempt_enable();
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	crypto_xor_cpy(dst, keystream, src, nbytes);
8762306a36Sopenharmony_ci	crypto_inc(ctrblk, AES_BLOCK_SIZE);
8862306a36Sopenharmony_ci}
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_cistatic int p8_aes_ctr_crypt(struct skcipher_request *req)
9162306a36Sopenharmony_ci{
9262306a36Sopenharmony_ci	struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req);
9362306a36Sopenharmony_ci	const struct p8_aes_ctr_ctx *ctx = crypto_skcipher_ctx(tfm);
9462306a36Sopenharmony_ci	struct skcipher_walk walk;
9562306a36Sopenharmony_ci	unsigned int nbytes;
9662306a36Sopenharmony_ci	int ret;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	if (!crypto_simd_usable()) {
9962306a36Sopenharmony_ci		struct skcipher_request *subreq = skcipher_request_ctx(req);
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci		*subreq = *req;
10262306a36Sopenharmony_ci		skcipher_request_set_tfm(subreq, ctx->fallback);
10362306a36Sopenharmony_ci		return crypto_skcipher_encrypt(subreq);
10462306a36Sopenharmony_ci	}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	ret = skcipher_walk_virt(&walk, req, false);
10762306a36Sopenharmony_ci	while ((nbytes = walk.nbytes) >= AES_BLOCK_SIZE) {
10862306a36Sopenharmony_ci		preempt_disable();
10962306a36Sopenharmony_ci		pagefault_disable();
11062306a36Sopenharmony_ci		enable_kernel_vsx();
11162306a36Sopenharmony_ci		aes_p8_ctr32_encrypt_blocks(walk.src.virt.addr,
11262306a36Sopenharmony_ci					    walk.dst.virt.addr,
11362306a36Sopenharmony_ci					    nbytes / AES_BLOCK_SIZE,
11462306a36Sopenharmony_ci					    &ctx->enc_key, walk.iv);
11562306a36Sopenharmony_ci		disable_kernel_vsx();
11662306a36Sopenharmony_ci		pagefault_enable();
11762306a36Sopenharmony_ci		preempt_enable();
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci		do {
12062306a36Sopenharmony_ci			crypto_inc(walk.iv, AES_BLOCK_SIZE);
12162306a36Sopenharmony_ci		} while ((nbytes -= AES_BLOCK_SIZE) >= AES_BLOCK_SIZE);
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci		ret = skcipher_walk_done(&walk, nbytes);
12462306a36Sopenharmony_ci	}
12562306a36Sopenharmony_ci	if (nbytes) {
12662306a36Sopenharmony_ci		p8_aes_ctr_final(ctx, &walk);
12762306a36Sopenharmony_ci		ret = skcipher_walk_done(&walk, 0);
12862306a36Sopenharmony_ci	}
12962306a36Sopenharmony_ci	return ret;
13062306a36Sopenharmony_ci}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_cistruct skcipher_alg p8_aes_ctr_alg = {
13362306a36Sopenharmony_ci	.base.cra_name = "ctr(aes)",
13462306a36Sopenharmony_ci	.base.cra_driver_name = "p8_aes_ctr",
13562306a36Sopenharmony_ci	.base.cra_module = THIS_MODULE,
13662306a36Sopenharmony_ci	.base.cra_priority = 2000,
13762306a36Sopenharmony_ci	.base.cra_flags = CRYPTO_ALG_NEED_FALLBACK,
13862306a36Sopenharmony_ci	.base.cra_blocksize = 1,
13962306a36Sopenharmony_ci	.base.cra_ctxsize = sizeof(struct p8_aes_ctr_ctx),
14062306a36Sopenharmony_ci	.setkey = p8_aes_ctr_setkey,
14162306a36Sopenharmony_ci	.encrypt = p8_aes_ctr_crypt,
14262306a36Sopenharmony_ci	.decrypt = p8_aes_ctr_crypt,
14362306a36Sopenharmony_ci	.init = p8_aes_ctr_init,
14462306a36Sopenharmony_ci	.exit = p8_aes_ctr_exit,
14562306a36Sopenharmony_ci	.min_keysize = AES_MIN_KEY_SIZE,
14662306a36Sopenharmony_ci	.max_keysize = AES_MAX_KEY_SIZE,
14762306a36Sopenharmony_ci	.ivsize = AES_BLOCK_SIZE,
14862306a36Sopenharmony_ci	.chunksize = AES_BLOCK_SIZE,
14962306a36Sopenharmony_ci};
150