18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Cryptographic API.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Support for VIA PadLock hardware crypto engine.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Copyright (c) 2004  Michal Ludvig <michal@logix.cz>
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <crypto/algapi.h>
128c2ecf20Sopenharmony_ci#include <crypto/aes.h>
138c2ecf20Sopenharmony_ci#include <crypto/internal/skcipher.h>
148c2ecf20Sopenharmony_ci#include <crypto/padlock.h>
158c2ecf20Sopenharmony_ci#include <linux/module.h>
168c2ecf20Sopenharmony_ci#include <linux/init.h>
178c2ecf20Sopenharmony_ci#include <linux/types.h>
188c2ecf20Sopenharmony_ci#include <linux/errno.h>
198c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
208c2ecf20Sopenharmony_ci#include <linux/kernel.h>
218c2ecf20Sopenharmony_ci#include <linux/mm.h>
228c2ecf20Sopenharmony_ci#include <linux/percpu.h>
238c2ecf20Sopenharmony_ci#include <linux/smp.h>
248c2ecf20Sopenharmony_ci#include <linux/slab.h>
258c2ecf20Sopenharmony_ci#include <asm/cpu_device_id.h>
268c2ecf20Sopenharmony_ci#include <asm/byteorder.h>
278c2ecf20Sopenharmony_ci#include <asm/processor.h>
288c2ecf20Sopenharmony_ci#include <asm/fpu/api.h>
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci/*
318c2ecf20Sopenharmony_ci * Number of data blocks actually fetched for each xcrypt insn.
328c2ecf20Sopenharmony_ci * Processors with prefetch errata will fetch extra blocks.
338c2ecf20Sopenharmony_ci */
348c2ecf20Sopenharmony_cistatic unsigned int ecb_fetch_blocks = 2;
358c2ecf20Sopenharmony_ci#define MAX_ECB_FETCH_BLOCKS (8)
368c2ecf20Sopenharmony_ci#define ecb_fetch_bytes (ecb_fetch_blocks * AES_BLOCK_SIZE)
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistatic unsigned int cbc_fetch_blocks = 1;
398c2ecf20Sopenharmony_ci#define MAX_CBC_FETCH_BLOCKS (4)
408c2ecf20Sopenharmony_ci#define cbc_fetch_bytes (cbc_fetch_blocks * AES_BLOCK_SIZE)
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci/* Control word. */
438c2ecf20Sopenharmony_cistruct cword {
448c2ecf20Sopenharmony_ci	unsigned int __attribute__ ((__packed__))
458c2ecf20Sopenharmony_ci		rounds:4,
468c2ecf20Sopenharmony_ci		algo:3,
478c2ecf20Sopenharmony_ci		keygen:1,
488c2ecf20Sopenharmony_ci		interm:1,
498c2ecf20Sopenharmony_ci		encdec:1,
508c2ecf20Sopenharmony_ci		ksize:2;
518c2ecf20Sopenharmony_ci} __attribute__ ((__aligned__(PADLOCK_ALIGNMENT)));
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci/* Whenever making any changes to the following
548c2ecf20Sopenharmony_ci * structure *make sure* you keep E, d_data
558c2ecf20Sopenharmony_ci * and cword aligned on 16 Bytes boundaries and
568c2ecf20Sopenharmony_ci * the Hardware can access 16 * 16 bytes of E and d_data
578c2ecf20Sopenharmony_ci * (only the first 15 * 16 bytes matter but the HW reads
588c2ecf20Sopenharmony_ci * more).
598c2ecf20Sopenharmony_ci */
608c2ecf20Sopenharmony_cistruct aes_ctx {
618c2ecf20Sopenharmony_ci	u32 E[AES_MAX_KEYLENGTH_U32]
628c2ecf20Sopenharmony_ci		__attribute__ ((__aligned__(PADLOCK_ALIGNMENT)));
638c2ecf20Sopenharmony_ci	u32 d_data[AES_MAX_KEYLENGTH_U32]
648c2ecf20Sopenharmony_ci		__attribute__ ((__aligned__(PADLOCK_ALIGNMENT)));
658c2ecf20Sopenharmony_ci	struct {
668c2ecf20Sopenharmony_ci		struct cword encrypt;
678c2ecf20Sopenharmony_ci		struct cword decrypt;
688c2ecf20Sopenharmony_ci	} cword;
698c2ecf20Sopenharmony_ci	u32 *D;
708c2ecf20Sopenharmony_ci};
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cistatic DEFINE_PER_CPU(struct cword *, paes_last_cword);
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci/* Tells whether the ACE is capable to generate
758c2ecf20Sopenharmony_ci   the extended key for a given key_len. */
768c2ecf20Sopenharmony_cistatic inline int
778c2ecf20Sopenharmony_ciaes_hw_extkey_available(uint8_t key_len)
788c2ecf20Sopenharmony_ci{
798c2ecf20Sopenharmony_ci	/* TODO: We should check the actual CPU model/stepping
808c2ecf20Sopenharmony_ci	         as it's possible that the capability will be
818c2ecf20Sopenharmony_ci	         added in the next CPU revisions. */
828c2ecf20Sopenharmony_ci	if (key_len == 16)
838c2ecf20Sopenharmony_ci		return 1;
848c2ecf20Sopenharmony_ci	return 0;
858c2ecf20Sopenharmony_ci}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_cistatic inline struct aes_ctx *aes_ctx_common(void *ctx)
888c2ecf20Sopenharmony_ci{
898c2ecf20Sopenharmony_ci	unsigned long addr = (unsigned long)ctx;
908c2ecf20Sopenharmony_ci	unsigned long align = PADLOCK_ALIGNMENT;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	if (align <= crypto_tfm_ctx_alignment())
938c2ecf20Sopenharmony_ci		align = 1;
948c2ecf20Sopenharmony_ci	return (struct aes_ctx *)ALIGN(addr, align);
958c2ecf20Sopenharmony_ci}
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_cistatic inline struct aes_ctx *aes_ctx(struct crypto_tfm *tfm)
988c2ecf20Sopenharmony_ci{
998c2ecf20Sopenharmony_ci	return aes_ctx_common(crypto_tfm_ctx(tfm));
1008c2ecf20Sopenharmony_ci}
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_cistatic inline struct aes_ctx *skcipher_aes_ctx(struct crypto_skcipher *tfm)
1038c2ecf20Sopenharmony_ci{
1048c2ecf20Sopenharmony_ci	return aes_ctx_common(crypto_skcipher_ctx(tfm));
1058c2ecf20Sopenharmony_ci}
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_cistatic int aes_set_key(struct crypto_tfm *tfm, const u8 *in_key,
1088c2ecf20Sopenharmony_ci		       unsigned int key_len)
1098c2ecf20Sopenharmony_ci{
1108c2ecf20Sopenharmony_ci	struct aes_ctx *ctx = aes_ctx(tfm);
1118c2ecf20Sopenharmony_ci	const __le32 *key = (const __le32 *)in_key;
1128c2ecf20Sopenharmony_ci	struct crypto_aes_ctx gen_aes;
1138c2ecf20Sopenharmony_ci	int cpu;
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	if (key_len % 8)
1168c2ecf20Sopenharmony_ci		return -EINVAL;
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	/*
1198c2ecf20Sopenharmony_ci	 * If the hardware is capable of generating the extended key
1208c2ecf20Sopenharmony_ci	 * itself we must supply the plain key for both encryption
1218c2ecf20Sopenharmony_ci	 * and decryption.
1228c2ecf20Sopenharmony_ci	 */
1238c2ecf20Sopenharmony_ci	ctx->D = ctx->E;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	ctx->E[0] = le32_to_cpu(key[0]);
1268c2ecf20Sopenharmony_ci	ctx->E[1] = le32_to_cpu(key[1]);
1278c2ecf20Sopenharmony_ci	ctx->E[2] = le32_to_cpu(key[2]);
1288c2ecf20Sopenharmony_ci	ctx->E[3] = le32_to_cpu(key[3]);
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	/* Prepare control words. */
1318c2ecf20Sopenharmony_ci	memset(&ctx->cword, 0, sizeof(ctx->cword));
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	ctx->cword.decrypt.encdec = 1;
1348c2ecf20Sopenharmony_ci	ctx->cword.encrypt.rounds = 10 + (key_len - 16) / 4;
1358c2ecf20Sopenharmony_ci	ctx->cword.decrypt.rounds = ctx->cword.encrypt.rounds;
1368c2ecf20Sopenharmony_ci	ctx->cword.encrypt.ksize = (key_len - 16) / 8;
1378c2ecf20Sopenharmony_ci	ctx->cword.decrypt.ksize = ctx->cword.encrypt.ksize;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	/* Don't generate extended keys if the hardware can do it. */
1408c2ecf20Sopenharmony_ci	if (aes_hw_extkey_available(key_len))
1418c2ecf20Sopenharmony_ci		goto ok;
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	ctx->D = ctx->d_data;
1448c2ecf20Sopenharmony_ci	ctx->cword.encrypt.keygen = 1;
1458c2ecf20Sopenharmony_ci	ctx->cword.decrypt.keygen = 1;
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	if (aes_expandkey(&gen_aes, in_key, key_len))
1488c2ecf20Sopenharmony_ci		return -EINVAL;
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	memcpy(ctx->E, gen_aes.key_enc, AES_MAX_KEYLENGTH);
1518c2ecf20Sopenharmony_ci	memcpy(ctx->D, gen_aes.key_dec, AES_MAX_KEYLENGTH);
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ciok:
1548c2ecf20Sopenharmony_ci	for_each_online_cpu(cpu)
1558c2ecf20Sopenharmony_ci		if (&ctx->cword.encrypt == per_cpu(paes_last_cword, cpu) ||
1568c2ecf20Sopenharmony_ci		    &ctx->cword.decrypt == per_cpu(paes_last_cword, cpu))
1578c2ecf20Sopenharmony_ci			per_cpu(paes_last_cword, cpu) = NULL;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	return 0;
1608c2ecf20Sopenharmony_ci}
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_cistatic int aes_set_key_skcipher(struct crypto_skcipher *tfm, const u8 *in_key,
1638c2ecf20Sopenharmony_ci				unsigned int key_len)
1648c2ecf20Sopenharmony_ci{
1658c2ecf20Sopenharmony_ci	return aes_set_key(crypto_skcipher_tfm(tfm), in_key, key_len);
1668c2ecf20Sopenharmony_ci}
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci/* ====== Encryption/decryption routines ====== */
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci/* These are the real call to PadLock. */
1718c2ecf20Sopenharmony_cistatic inline void padlock_reset_key(struct cword *cword)
1728c2ecf20Sopenharmony_ci{
1738c2ecf20Sopenharmony_ci	int cpu = raw_smp_processor_id();
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	if (cword != per_cpu(paes_last_cword, cpu))
1768c2ecf20Sopenharmony_ci#ifndef CONFIG_X86_64
1778c2ecf20Sopenharmony_ci		asm volatile ("pushfl; popfl");
1788c2ecf20Sopenharmony_ci#else
1798c2ecf20Sopenharmony_ci		asm volatile ("pushfq; popfq");
1808c2ecf20Sopenharmony_ci#endif
1818c2ecf20Sopenharmony_ci}
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_cistatic inline void padlock_store_cword(struct cword *cword)
1848c2ecf20Sopenharmony_ci{
1858c2ecf20Sopenharmony_ci	per_cpu(paes_last_cword, raw_smp_processor_id()) = cword;
1868c2ecf20Sopenharmony_ci}
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci/*
1898c2ecf20Sopenharmony_ci * While the padlock instructions don't use FP/SSE registers, they
1908c2ecf20Sopenharmony_ci * generate a spurious DNA fault when CR0.TS is '1'.  Fortunately,
1918c2ecf20Sopenharmony_ci * the kernel doesn't use CR0.TS.
1928c2ecf20Sopenharmony_ci */
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_cistatic inline void rep_xcrypt_ecb(const u8 *input, u8 *output, void *key,
1958c2ecf20Sopenharmony_ci				  struct cword *control_word, int count)
1968c2ecf20Sopenharmony_ci{
1978c2ecf20Sopenharmony_ci	asm volatile (".byte 0xf3,0x0f,0xa7,0xc8"	/* rep xcryptecb */
1988c2ecf20Sopenharmony_ci		      : "+S"(input), "+D"(output)
1998c2ecf20Sopenharmony_ci		      : "d"(control_word), "b"(key), "c"(count));
2008c2ecf20Sopenharmony_ci}
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_cistatic inline u8 *rep_xcrypt_cbc(const u8 *input, u8 *output, void *key,
2038c2ecf20Sopenharmony_ci				 u8 *iv, struct cword *control_word, int count)
2048c2ecf20Sopenharmony_ci{
2058c2ecf20Sopenharmony_ci	asm volatile (".byte 0xf3,0x0f,0xa7,0xd0"	/* rep xcryptcbc */
2068c2ecf20Sopenharmony_ci		      : "+S" (input), "+D" (output), "+a" (iv)
2078c2ecf20Sopenharmony_ci		      : "d" (control_word), "b" (key), "c" (count));
2088c2ecf20Sopenharmony_ci	return iv;
2098c2ecf20Sopenharmony_ci}
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_cistatic void ecb_crypt_copy(const u8 *in, u8 *out, u32 *key,
2128c2ecf20Sopenharmony_ci			   struct cword *cword, int count)
2138c2ecf20Sopenharmony_ci{
2148c2ecf20Sopenharmony_ci	/*
2158c2ecf20Sopenharmony_ci	 * Padlock prefetches extra data so we must provide mapped input buffers.
2168c2ecf20Sopenharmony_ci	 * Assume there are at least 16 bytes of stack already in use.
2178c2ecf20Sopenharmony_ci	 */
2188c2ecf20Sopenharmony_ci	u8 buf[AES_BLOCK_SIZE * (MAX_ECB_FETCH_BLOCKS - 1) + PADLOCK_ALIGNMENT - 1];
2198c2ecf20Sopenharmony_ci	u8 *tmp = PTR_ALIGN(&buf[0], PADLOCK_ALIGNMENT);
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	memcpy(tmp, in, count * AES_BLOCK_SIZE);
2228c2ecf20Sopenharmony_ci	rep_xcrypt_ecb(tmp, out, key, cword, count);
2238c2ecf20Sopenharmony_ci}
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_cistatic u8 *cbc_crypt_copy(const u8 *in, u8 *out, u32 *key,
2268c2ecf20Sopenharmony_ci			   u8 *iv, struct cword *cword, int count)
2278c2ecf20Sopenharmony_ci{
2288c2ecf20Sopenharmony_ci	/*
2298c2ecf20Sopenharmony_ci	 * Padlock prefetches extra data so we must provide mapped input buffers.
2308c2ecf20Sopenharmony_ci	 * Assume there are at least 16 bytes of stack already in use.
2318c2ecf20Sopenharmony_ci	 */
2328c2ecf20Sopenharmony_ci	u8 buf[AES_BLOCK_SIZE * (MAX_CBC_FETCH_BLOCKS - 1) + PADLOCK_ALIGNMENT - 1];
2338c2ecf20Sopenharmony_ci	u8 *tmp = PTR_ALIGN(&buf[0], PADLOCK_ALIGNMENT);
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	memcpy(tmp, in, count * AES_BLOCK_SIZE);
2368c2ecf20Sopenharmony_ci	return rep_xcrypt_cbc(tmp, out, key, iv, cword, count);
2378c2ecf20Sopenharmony_ci}
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_cistatic inline void ecb_crypt(const u8 *in, u8 *out, u32 *key,
2408c2ecf20Sopenharmony_ci			     struct cword *cword, int count)
2418c2ecf20Sopenharmony_ci{
2428c2ecf20Sopenharmony_ci	/* Padlock in ECB mode fetches at least ecb_fetch_bytes of data.
2438c2ecf20Sopenharmony_ci	 * We could avoid some copying here but it's probably not worth it.
2448c2ecf20Sopenharmony_ci	 */
2458c2ecf20Sopenharmony_ci	if (unlikely(offset_in_page(in) + ecb_fetch_bytes > PAGE_SIZE)) {
2468c2ecf20Sopenharmony_ci		ecb_crypt_copy(in, out, key, cword, count);
2478c2ecf20Sopenharmony_ci		return;
2488c2ecf20Sopenharmony_ci	}
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	rep_xcrypt_ecb(in, out, key, cword, count);
2518c2ecf20Sopenharmony_ci}
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_cistatic inline u8 *cbc_crypt(const u8 *in, u8 *out, u32 *key,
2548c2ecf20Sopenharmony_ci			    u8 *iv, struct cword *cword, int count)
2558c2ecf20Sopenharmony_ci{
2568c2ecf20Sopenharmony_ci	/* Padlock in CBC mode fetches at least cbc_fetch_bytes of data. */
2578c2ecf20Sopenharmony_ci	if (unlikely(offset_in_page(in) + cbc_fetch_bytes > PAGE_SIZE))
2588c2ecf20Sopenharmony_ci		return cbc_crypt_copy(in, out, key, iv, cword, count);
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	return rep_xcrypt_cbc(in, out, key, iv, cword, count);
2618c2ecf20Sopenharmony_ci}
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_cistatic inline void padlock_xcrypt_ecb(const u8 *input, u8 *output, void *key,
2648c2ecf20Sopenharmony_ci				      void *control_word, u32 count)
2658c2ecf20Sopenharmony_ci{
2668c2ecf20Sopenharmony_ci	u32 initial = count & (ecb_fetch_blocks - 1);
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	if (count < ecb_fetch_blocks) {
2698c2ecf20Sopenharmony_ci		ecb_crypt(input, output, key, control_word, count);
2708c2ecf20Sopenharmony_ci		return;
2718c2ecf20Sopenharmony_ci	}
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	count -= initial;
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	if (initial)
2768c2ecf20Sopenharmony_ci		asm volatile (".byte 0xf3,0x0f,0xa7,0xc8"	/* rep xcryptecb */
2778c2ecf20Sopenharmony_ci			      : "+S"(input), "+D"(output)
2788c2ecf20Sopenharmony_ci			      : "d"(control_word), "b"(key), "c"(initial));
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	asm volatile (".byte 0xf3,0x0f,0xa7,0xc8"	/* rep xcryptecb */
2818c2ecf20Sopenharmony_ci		      : "+S"(input), "+D"(output)
2828c2ecf20Sopenharmony_ci		      : "d"(control_word), "b"(key), "c"(count));
2838c2ecf20Sopenharmony_ci}
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_cistatic inline u8 *padlock_xcrypt_cbc(const u8 *input, u8 *output, void *key,
2868c2ecf20Sopenharmony_ci				     u8 *iv, void *control_word, u32 count)
2878c2ecf20Sopenharmony_ci{
2888c2ecf20Sopenharmony_ci	u32 initial = count & (cbc_fetch_blocks - 1);
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	if (count < cbc_fetch_blocks)
2918c2ecf20Sopenharmony_ci		return cbc_crypt(input, output, key, iv, control_word, count);
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	count -= initial;
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	if (initial)
2968c2ecf20Sopenharmony_ci		asm volatile (".byte 0xf3,0x0f,0xa7,0xd0"	/* rep xcryptcbc */
2978c2ecf20Sopenharmony_ci			      : "+S" (input), "+D" (output), "+a" (iv)
2988c2ecf20Sopenharmony_ci			      : "d" (control_word), "b" (key), "c" (initial));
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	asm volatile (".byte 0xf3,0x0f,0xa7,0xd0"	/* rep xcryptcbc */
3018c2ecf20Sopenharmony_ci		      : "+S" (input), "+D" (output), "+a" (iv)
3028c2ecf20Sopenharmony_ci		      : "d" (control_word), "b" (key), "c" (count));
3038c2ecf20Sopenharmony_ci	return iv;
3048c2ecf20Sopenharmony_ci}
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_cistatic void padlock_aes_encrypt(struct crypto_tfm *tfm, u8 *out, const u8 *in)
3078c2ecf20Sopenharmony_ci{
3088c2ecf20Sopenharmony_ci	struct aes_ctx *ctx = aes_ctx(tfm);
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	padlock_reset_key(&ctx->cword.encrypt);
3118c2ecf20Sopenharmony_ci	ecb_crypt(in, out, ctx->E, &ctx->cword.encrypt, 1);
3128c2ecf20Sopenharmony_ci	padlock_store_cword(&ctx->cword.encrypt);
3138c2ecf20Sopenharmony_ci}
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_cistatic void padlock_aes_decrypt(struct crypto_tfm *tfm, u8 *out, const u8 *in)
3168c2ecf20Sopenharmony_ci{
3178c2ecf20Sopenharmony_ci	struct aes_ctx *ctx = aes_ctx(tfm);
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	padlock_reset_key(&ctx->cword.encrypt);
3208c2ecf20Sopenharmony_ci	ecb_crypt(in, out, ctx->D, &ctx->cword.decrypt, 1);
3218c2ecf20Sopenharmony_ci	padlock_store_cword(&ctx->cword.encrypt);
3228c2ecf20Sopenharmony_ci}
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_cistatic struct crypto_alg aes_alg = {
3258c2ecf20Sopenharmony_ci	.cra_name		=	"aes",
3268c2ecf20Sopenharmony_ci	.cra_driver_name	=	"aes-padlock",
3278c2ecf20Sopenharmony_ci	.cra_priority		=	PADLOCK_CRA_PRIORITY,
3288c2ecf20Sopenharmony_ci	.cra_flags		=	CRYPTO_ALG_TYPE_CIPHER,
3298c2ecf20Sopenharmony_ci	.cra_blocksize		=	AES_BLOCK_SIZE,
3308c2ecf20Sopenharmony_ci	.cra_ctxsize		=	sizeof(struct aes_ctx),
3318c2ecf20Sopenharmony_ci	.cra_alignmask		=	PADLOCK_ALIGNMENT - 1,
3328c2ecf20Sopenharmony_ci	.cra_module		=	THIS_MODULE,
3338c2ecf20Sopenharmony_ci	.cra_u			=	{
3348c2ecf20Sopenharmony_ci		.cipher = {
3358c2ecf20Sopenharmony_ci			.cia_min_keysize	=	AES_MIN_KEY_SIZE,
3368c2ecf20Sopenharmony_ci			.cia_max_keysize	=	AES_MAX_KEY_SIZE,
3378c2ecf20Sopenharmony_ci			.cia_setkey	   	= 	aes_set_key,
3388c2ecf20Sopenharmony_ci			.cia_encrypt	 	=	padlock_aes_encrypt,
3398c2ecf20Sopenharmony_ci			.cia_decrypt	  	=	padlock_aes_decrypt,
3408c2ecf20Sopenharmony_ci		}
3418c2ecf20Sopenharmony_ci	}
3428c2ecf20Sopenharmony_ci};
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_cistatic int ecb_aes_encrypt(struct skcipher_request *req)
3458c2ecf20Sopenharmony_ci{
3468c2ecf20Sopenharmony_ci	struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req);
3478c2ecf20Sopenharmony_ci	struct aes_ctx *ctx = skcipher_aes_ctx(tfm);
3488c2ecf20Sopenharmony_ci	struct skcipher_walk walk;
3498c2ecf20Sopenharmony_ci	unsigned int nbytes;
3508c2ecf20Sopenharmony_ci	int err;
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	padlock_reset_key(&ctx->cword.encrypt);
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	err = skcipher_walk_virt(&walk, req, false);
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	while ((nbytes = walk.nbytes) != 0) {
3578c2ecf20Sopenharmony_ci		padlock_xcrypt_ecb(walk.src.virt.addr, walk.dst.virt.addr,
3588c2ecf20Sopenharmony_ci				   ctx->E, &ctx->cword.encrypt,
3598c2ecf20Sopenharmony_ci				   nbytes / AES_BLOCK_SIZE);
3608c2ecf20Sopenharmony_ci		nbytes &= AES_BLOCK_SIZE - 1;
3618c2ecf20Sopenharmony_ci		err = skcipher_walk_done(&walk, nbytes);
3628c2ecf20Sopenharmony_ci	}
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	padlock_store_cword(&ctx->cword.encrypt);
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	return err;
3678c2ecf20Sopenharmony_ci}
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_cistatic int ecb_aes_decrypt(struct skcipher_request *req)
3708c2ecf20Sopenharmony_ci{
3718c2ecf20Sopenharmony_ci	struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req);
3728c2ecf20Sopenharmony_ci	struct aes_ctx *ctx = skcipher_aes_ctx(tfm);
3738c2ecf20Sopenharmony_ci	struct skcipher_walk walk;
3748c2ecf20Sopenharmony_ci	unsigned int nbytes;
3758c2ecf20Sopenharmony_ci	int err;
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	padlock_reset_key(&ctx->cword.decrypt);
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	err = skcipher_walk_virt(&walk, req, false);
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	while ((nbytes = walk.nbytes) != 0) {
3828c2ecf20Sopenharmony_ci		padlock_xcrypt_ecb(walk.src.virt.addr, walk.dst.virt.addr,
3838c2ecf20Sopenharmony_ci				   ctx->D, &ctx->cword.decrypt,
3848c2ecf20Sopenharmony_ci				   nbytes / AES_BLOCK_SIZE);
3858c2ecf20Sopenharmony_ci		nbytes &= AES_BLOCK_SIZE - 1;
3868c2ecf20Sopenharmony_ci		err = skcipher_walk_done(&walk, nbytes);
3878c2ecf20Sopenharmony_ci	}
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	padlock_store_cword(&ctx->cword.encrypt);
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	return err;
3928c2ecf20Sopenharmony_ci}
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_cistatic struct skcipher_alg ecb_aes_alg = {
3958c2ecf20Sopenharmony_ci	.base.cra_name		=	"ecb(aes)",
3968c2ecf20Sopenharmony_ci	.base.cra_driver_name	=	"ecb-aes-padlock",
3978c2ecf20Sopenharmony_ci	.base.cra_priority	=	PADLOCK_COMPOSITE_PRIORITY,
3988c2ecf20Sopenharmony_ci	.base.cra_blocksize	=	AES_BLOCK_SIZE,
3998c2ecf20Sopenharmony_ci	.base.cra_ctxsize	=	sizeof(struct aes_ctx),
4008c2ecf20Sopenharmony_ci	.base.cra_alignmask	=	PADLOCK_ALIGNMENT - 1,
4018c2ecf20Sopenharmony_ci	.base.cra_module	=	THIS_MODULE,
4028c2ecf20Sopenharmony_ci	.min_keysize		=	AES_MIN_KEY_SIZE,
4038c2ecf20Sopenharmony_ci	.max_keysize		=	AES_MAX_KEY_SIZE,
4048c2ecf20Sopenharmony_ci	.setkey			=	aes_set_key_skcipher,
4058c2ecf20Sopenharmony_ci	.encrypt		=	ecb_aes_encrypt,
4068c2ecf20Sopenharmony_ci	.decrypt		=	ecb_aes_decrypt,
4078c2ecf20Sopenharmony_ci};
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_cistatic int cbc_aes_encrypt(struct skcipher_request *req)
4108c2ecf20Sopenharmony_ci{
4118c2ecf20Sopenharmony_ci	struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req);
4128c2ecf20Sopenharmony_ci	struct aes_ctx *ctx = skcipher_aes_ctx(tfm);
4138c2ecf20Sopenharmony_ci	struct skcipher_walk walk;
4148c2ecf20Sopenharmony_ci	unsigned int nbytes;
4158c2ecf20Sopenharmony_ci	int err;
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	padlock_reset_key(&ctx->cword.encrypt);
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	err = skcipher_walk_virt(&walk, req, false);
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	while ((nbytes = walk.nbytes) != 0) {
4228c2ecf20Sopenharmony_ci		u8 *iv = padlock_xcrypt_cbc(walk.src.virt.addr,
4238c2ecf20Sopenharmony_ci					    walk.dst.virt.addr, ctx->E,
4248c2ecf20Sopenharmony_ci					    walk.iv, &ctx->cword.encrypt,
4258c2ecf20Sopenharmony_ci					    nbytes / AES_BLOCK_SIZE);
4268c2ecf20Sopenharmony_ci		memcpy(walk.iv, iv, AES_BLOCK_SIZE);
4278c2ecf20Sopenharmony_ci		nbytes &= AES_BLOCK_SIZE - 1;
4288c2ecf20Sopenharmony_ci		err = skcipher_walk_done(&walk, nbytes);
4298c2ecf20Sopenharmony_ci	}
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	padlock_store_cword(&ctx->cword.decrypt);
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci	return err;
4348c2ecf20Sopenharmony_ci}
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_cistatic int cbc_aes_decrypt(struct skcipher_request *req)
4378c2ecf20Sopenharmony_ci{
4388c2ecf20Sopenharmony_ci	struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req);
4398c2ecf20Sopenharmony_ci	struct aes_ctx *ctx = skcipher_aes_ctx(tfm);
4408c2ecf20Sopenharmony_ci	struct skcipher_walk walk;
4418c2ecf20Sopenharmony_ci	unsigned int nbytes;
4428c2ecf20Sopenharmony_ci	int err;
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	padlock_reset_key(&ctx->cword.encrypt);
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	err = skcipher_walk_virt(&walk, req, false);
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	while ((nbytes = walk.nbytes) != 0) {
4498c2ecf20Sopenharmony_ci		padlock_xcrypt_cbc(walk.src.virt.addr, walk.dst.virt.addr,
4508c2ecf20Sopenharmony_ci				   ctx->D, walk.iv, &ctx->cword.decrypt,
4518c2ecf20Sopenharmony_ci				   nbytes / AES_BLOCK_SIZE);
4528c2ecf20Sopenharmony_ci		nbytes &= AES_BLOCK_SIZE - 1;
4538c2ecf20Sopenharmony_ci		err = skcipher_walk_done(&walk, nbytes);
4548c2ecf20Sopenharmony_ci	}
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci	padlock_store_cword(&ctx->cword.encrypt);
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	return err;
4598c2ecf20Sopenharmony_ci}
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_cistatic struct skcipher_alg cbc_aes_alg = {
4628c2ecf20Sopenharmony_ci	.base.cra_name		=	"cbc(aes)",
4638c2ecf20Sopenharmony_ci	.base.cra_driver_name	=	"cbc-aes-padlock",
4648c2ecf20Sopenharmony_ci	.base.cra_priority	=	PADLOCK_COMPOSITE_PRIORITY,
4658c2ecf20Sopenharmony_ci	.base.cra_blocksize	=	AES_BLOCK_SIZE,
4668c2ecf20Sopenharmony_ci	.base.cra_ctxsize	=	sizeof(struct aes_ctx),
4678c2ecf20Sopenharmony_ci	.base.cra_alignmask	=	PADLOCK_ALIGNMENT - 1,
4688c2ecf20Sopenharmony_ci	.base.cra_module	=	THIS_MODULE,
4698c2ecf20Sopenharmony_ci	.min_keysize		=	AES_MIN_KEY_SIZE,
4708c2ecf20Sopenharmony_ci	.max_keysize		=	AES_MAX_KEY_SIZE,
4718c2ecf20Sopenharmony_ci	.ivsize			=	AES_BLOCK_SIZE,
4728c2ecf20Sopenharmony_ci	.setkey			=	aes_set_key_skcipher,
4738c2ecf20Sopenharmony_ci	.encrypt		=	cbc_aes_encrypt,
4748c2ecf20Sopenharmony_ci	.decrypt		=	cbc_aes_decrypt,
4758c2ecf20Sopenharmony_ci};
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_cistatic const struct x86_cpu_id padlock_cpu_id[] = {
4788c2ecf20Sopenharmony_ci	X86_MATCH_FEATURE(X86_FEATURE_XCRYPT, NULL),
4798c2ecf20Sopenharmony_ci	{}
4808c2ecf20Sopenharmony_ci};
4818c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(x86cpu, padlock_cpu_id);
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_cistatic int __init padlock_init(void)
4848c2ecf20Sopenharmony_ci{
4858c2ecf20Sopenharmony_ci	int ret;
4868c2ecf20Sopenharmony_ci	struct cpuinfo_x86 *c = &cpu_data(0);
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci	if (!x86_match_cpu(padlock_cpu_id))
4898c2ecf20Sopenharmony_ci		return -ENODEV;
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	if (!boot_cpu_has(X86_FEATURE_XCRYPT_EN)) {
4928c2ecf20Sopenharmony_ci		printk(KERN_NOTICE PFX "VIA PadLock detected, but not enabled. Hmm, strange...\n");
4938c2ecf20Sopenharmony_ci		return -ENODEV;
4948c2ecf20Sopenharmony_ci	}
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci	if ((ret = crypto_register_alg(&aes_alg)) != 0)
4978c2ecf20Sopenharmony_ci		goto aes_err;
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	if ((ret = crypto_register_skcipher(&ecb_aes_alg)) != 0)
5008c2ecf20Sopenharmony_ci		goto ecb_aes_err;
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci	if ((ret = crypto_register_skcipher(&cbc_aes_alg)) != 0)
5038c2ecf20Sopenharmony_ci		goto cbc_aes_err;
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci	printk(KERN_NOTICE PFX "Using VIA PadLock ACE for AES algorithm.\n");
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci	if (c->x86 == 6 && c->x86_model == 15 && c->x86_stepping == 2) {
5088c2ecf20Sopenharmony_ci		ecb_fetch_blocks = MAX_ECB_FETCH_BLOCKS;
5098c2ecf20Sopenharmony_ci		cbc_fetch_blocks = MAX_CBC_FETCH_BLOCKS;
5108c2ecf20Sopenharmony_ci		printk(KERN_NOTICE PFX "VIA Nano stepping 2 detected: enabling workaround.\n");
5118c2ecf20Sopenharmony_ci	}
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ciout:
5148c2ecf20Sopenharmony_ci	return ret;
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_cicbc_aes_err:
5178c2ecf20Sopenharmony_ci	crypto_unregister_skcipher(&ecb_aes_alg);
5188c2ecf20Sopenharmony_ciecb_aes_err:
5198c2ecf20Sopenharmony_ci	crypto_unregister_alg(&aes_alg);
5208c2ecf20Sopenharmony_ciaes_err:
5218c2ecf20Sopenharmony_ci	printk(KERN_ERR PFX "VIA PadLock AES initialization failed.\n");
5228c2ecf20Sopenharmony_ci	goto out;
5238c2ecf20Sopenharmony_ci}
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_cistatic void __exit padlock_fini(void)
5268c2ecf20Sopenharmony_ci{
5278c2ecf20Sopenharmony_ci	crypto_unregister_skcipher(&cbc_aes_alg);
5288c2ecf20Sopenharmony_ci	crypto_unregister_skcipher(&ecb_aes_alg);
5298c2ecf20Sopenharmony_ci	crypto_unregister_alg(&aes_alg);
5308c2ecf20Sopenharmony_ci}
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_cimodule_init(padlock_init);
5338c2ecf20Sopenharmony_cimodule_exit(padlock_fini);
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("VIA PadLock AES algorithm support");
5368c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
5378c2ecf20Sopenharmony_ciMODULE_AUTHOR("Michal Ludvig");
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ciMODULE_ALIAS_CRYPTO("aes");
540