162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * s390 ChaCha stream cipher.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright IBM Corp. 2021
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#define KMSG_COMPONENT "chacha_s390"
962306a36Sopenharmony_ci#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <crypto/internal/chacha.h>
1262306a36Sopenharmony_ci#include <crypto/internal/skcipher.h>
1362306a36Sopenharmony_ci#include <crypto/algapi.h>
1462306a36Sopenharmony_ci#include <linux/cpufeature.h>
1562306a36Sopenharmony_ci#include <linux/kernel.h>
1662306a36Sopenharmony_ci#include <linux/module.h>
1762306a36Sopenharmony_ci#include <linux/sizes.h>
1862306a36Sopenharmony_ci#include <asm/fpu/api.h>
1962306a36Sopenharmony_ci#include "chacha-s390.h"
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_cistatic void chacha20_crypt_s390(u32 *state, u8 *dst, const u8 *src,
2262306a36Sopenharmony_ci				unsigned int nbytes, const u32 *key,
2362306a36Sopenharmony_ci				u32 *counter)
2462306a36Sopenharmony_ci{
2562306a36Sopenharmony_ci	struct kernel_fpu vxstate;
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci	kernel_fpu_begin(&vxstate, KERNEL_VXR);
2862306a36Sopenharmony_ci	chacha20_vx(dst, src, nbytes, key, counter);
2962306a36Sopenharmony_ci	kernel_fpu_end(&vxstate, KERNEL_VXR);
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci	*counter += round_up(nbytes, CHACHA_BLOCK_SIZE) / CHACHA_BLOCK_SIZE;
3262306a36Sopenharmony_ci}
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_cistatic int chacha20_s390(struct skcipher_request *req)
3562306a36Sopenharmony_ci{
3662306a36Sopenharmony_ci	struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req);
3762306a36Sopenharmony_ci	struct chacha_ctx *ctx = crypto_skcipher_ctx(tfm);
3862306a36Sopenharmony_ci	u32 state[CHACHA_STATE_WORDS] __aligned(16);
3962306a36Sopenharmony_ci	struct skcipher_walk walk;
4062306a36Sopenharmony_ci	unsigned int nbytes;
4162306a36Sopenharmony_ci	int rc;
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	rc = skcipher_walk_virt(&walk, req, false);
4462306a36Sopenharmony_ci	chacha_init_generic(state, ctx->key, req->iv);
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	while (walk.nbytes > 0) {
4762306a36Sopenharmony_ci		nbytes = walk.nbytes;
4862306a36Sopenharmony_ci		if (nbytes < walk.total)
4962306a36Sopenharmony_ci			nbytes = round_down(nbytes, walk.stride);
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci		if (nbytes <= CHACHA_BLOCK_SIZE) {
5262306a36Sopenharmony_ci			chacha_crypt_generic(state, walk.dst.virt.addr,
5362306a36Sopenharmony_ci					     walk.src.virt.addr, nbytes,
5462306a36Sopenharmony_ci					     ctx->nrounds);
5562306a36Sopenharmony_ci		} else {
5662306a36Sopenharmony_ci			chacha20_crypt_s390(state, walk.dst.virt.addr,
5762306a36Sopenharmony_ci					    walk.src.virt.addr, nbytes,
5862306a36Sopenharmony_ci					    &state[4], &state[12]);
5962306a36Sopenharmony_ci		}
6062306a36Sopenharmony_ci		rc = skcipher_walk_done(&walk, walk.nbytes - nbytes);
6162306a36Sopenharmony_ci	}
6262306a36Sopenharmony_ci	return rc;
6362306a36Sopenharmony_ci}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_civoid hchacha_block_arch(const u32 *state, u32 *stream, int nrounds)
6662306a36Sopenharmony_ci{
6762306a36Sopenharmony_ci	/* TODO: implement hchacha_block_arch() in assembly */
6862306a36Sopenharmony_ci	hchacha_block_generic(state, stream, nrounds);
6962306a36Sopenharmony_ci}
7062306a36Sopenharmony_ciEXPORT_SYMBOL(hchacha_block_arch);
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_civoid chacha_init_arch(u32 *state, const u32 *key, const u8 *iv)
7362306a36Sopenharmony_ci{
7462306a36Sopenharmony_ci	chacha_init_generic(state, key, iv);
7562306a36Sopenharmony_ci}
7662306a36Sopenharmony_ciEXPORT_SYMBOL(chacha_init_arch);
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_civoid chacha_crypt_arch(u32 *state, u8 *dst, const u8 *src,
7962306a36Sopenharmony_ci		       unsigned int bytes, int nrounds)
8062306a36Sopenharmony_ci{
8162306a36Sopenharmony_ci	/* s390 chacha20 implementation has 20 rounds hard-coded,
8262306a36Sopenharmony_ci	 * it cannot handle a block of data or less, but otherwise
8362306a36Sopenharmony_ci	 * it can handle data of arbitrary size
8462306a36Sopenharmony_ci	 */
8562306a36Sopenharmony_ci	if (bytes <= CHACHA_BLOCK_SIZE || nrounds != 20 || !MACHINE_HAS_VX)
8662306a36Sopenharmony_ci		chacha_crypt_generic(state, dst, src, bytes, nrounds);
8762306a36Sopenharmony_ci	else
8862306a36Sopenharmony_ci		chacha20_crypt_s390(state, dst, src, bytes,
8962306a36Sopenharmony_ci				    &state[4], &state[12]);
9062306a36Sopenharmony_ci}
9162306a36Sopenharmony_ciEXPORT_SYMBOL(chacha_crypt_arch);
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_cistatic struct skcipher_alg chacha_algs[] = {
9462306a36Sopenharmony_ci	{
9562306a36Sopenharmony_ci		.base.cra_name		= "chacha20",
9662306a36Sopenharmony_ci		.base.cra_driver_name	= "chacha20-s390",
9762306a36Sopenharmony_ci		.base.cra_priority	= 900,
9862306a36Sopenharmony_ci		.base.cra_blocksize	= 1,
9962306a36Sopenharmony_ci		.base.cra_ctxsize	= sizeof(struct chacha_ctx),
10062306a36Sopenharmony_ci		.base.cra_module	= THIS_MODULE,
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci		.min_keysize		= CHACHA_KEY_SIZE,
10362306a36Sopenharmony_ci		.max_keysize		= CHACHA_KEY_SIZE,
10462306a36Sopenharmony_ci		.ivsize			= CHACHA_IV_SIZE,
10562306a36Sopenharmony_ci		.chunksize		= CHACHA_BLOCK_SIZE,
10662306a36Sopenharmony_ci		.setkey			= chacha20_setkey,
10762306a36Sopenharmony_ci		.encrypt		= chacha20_s390,
10862306a36Sopenharmony_ci		.decrypt		= chacha20_s390,
10962306a36Sopenharmony_ci	}
11062306a36Sopenharmony_ci};
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_cistatic int __init chacha_mod_init(void)
11362306a36Sopenharmony_ci{
11462306a36Sopenharmony_ci	return IS_REACHABLE(CONFIG_CRYPTO_SKCIPHER) ?
11562306a36Sopenharmony_ci		crypto_register_skciphers(chacha_algs, ARRAY_SIZE(chacha_algs)) : 0;
11662306a36Sopenharmony_ci}
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_cistatic void __exit chacha_mod_fini(void)
11962306a36Sopenharmony_ci{
12062306a36Sopenharmony_ci	if (IS_REACHABLE(CONFIG_CRYPTO_SKCIPHER))
12162306a36Sopenharmony_ci		crypto_unregister_skciphers(chacha_algs, ARRAY_SIZE(chacha_algs));
12262306a36Sopenharmony_ci}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_cimodule_cpu_feature_match(S390_CPU_FEATURE_VXRS, chacha_mod_init);
12562306a36Sopenharmony_cimodule_exit(chacha_mod_fini);
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ciMODULE_DESCRIPTION("ChaCha20 stream cipher");
12862306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ciMODULE_ALIAS_CRYPTO("chacha20");
131