forked from Minki/linux
d7d7b85356
These x86_64 vectorized implementations support AVX, AVX-2, and AVX512F. The AVX-512F implementation is disabled on Skylake, due to throttling, but it is quite fast on >= Cannonlake. On the left is cycle counts on a Core i7 6700HQ using the AVX-2 codepath, comparing this implementation ("new") to the implementation in the current crypto api ("old"). On the right are benchmarks on a Xeon Gold 5120 using the AVX-512 codepath. The new implementation is faster on all benchmarks. AVX-2 AVX-512 --------- ----------- size old new size old new ---- ---- ---- ---- ---- ---- 0 70 68 0 74 70 16 92 90 16 96 92 32 134 104 32 136 106 48 172 120 48 184 124 64 218 136 64 218 138 80 254 158 80 260 160 96 298 174 96 300 176 112 342 192 112 342 194 128 388 212 128 384 212 144 428 228 144 420 226 160 466 246 160 464 248 176 510 264 176 504 264 192 550 282 192 544 282 208 594 302 208 582 300 224 628 316 224 624 318 240 676 334 240 662 338 256 716 354 256 708 358 272 764 374 272 748 372 288 802 352 288 788 358 304 420 366 304 422 370 320 428 360 320 432 364 336 484 378 336 486 380 352 426 384 352 434 390 368 478 400 368 480 408 384 488 394 384 490 398 400 542 408 400 542 412 416 486 416 416 492 426 432 534 430 432 538 436 448 544 422 448 546 432 464 600 438 464 600 448 480 540 448 480 548 456 496 594 464 496 594 476 512 602 456 512 606 470 528 656 476 528 656 480 544 600 480 544 606 498 560 650 494 560 652 512 576 664 490 576 662 508 592 714 508 592 716 522 608 656 514 608 664 538 624 708 532 624 710 552 640 716 524 640 720 516 656 770 536 656 772 526 672 716 548 672 722 544 688 770 562 688 768 556 704 774 552 704 778 556 720 826 568 720 832 568 736 768 574 736 780 584 752 822 592 752 826 600 768 830 584 768 836 560 784 884 602 784 888 572 800 828 610 800 838 588 816 884 628 816 884 604 832 888 618 832 894 598 848 942 632 848 946 612 864 884 644 864 896 628 880 936 660 880 942 644 896 948 652 896 952 608 912 1000 664 912 1004 616 928 942 676 928 954 634 944 994 690 944 1000 646 960 1002 680 960 1008 646 976 1054 694 976 1062 658 992 1002 706 992 1012 674 1008 1052 720 1008 1058 690 This commit wires in the prior implementation from Andy, and makes the following changes to be suitable for kernel land. - Some cosmetic and structural changes, like renaming labels to .Lname, constants, and other Linux conventions, as well as making the code easy for us to maintain moving forward. - CPU feature checking is done in C by the glue code. - We avoid jumping into the middle of functions, to appease objtool, and instead parameterize shared code. - We maintain frame pointers so that stack traces make sense. - We remove the dependency on the perl xlate code, which transforms the output into things that assemblers we don't care about use. Importantly, none of our changes affect the arithmetic or core code, but just involve the differing environment of kernel space. Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com> Signed-off-by: Samuel Neves <sneves@dei.uc.pt> Co-developed-by: Samuel Neves <sneves@dei.uc.pt> Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
298 lines
9.3 KiB
C
298 lines
9.3 KiB
C
// SPDX-License-Identifier: GPL-2.0 OR MIT
|
|
/*
|
|
* Copyright (C) 2015-2019 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
|
*/
|
|
|
|
#include <crypto/algapi.h>
|
|
#include <crypto/internal/hash.h>
|
|
#include <crypto/internal/poly1305.h>
|
|
#include <crypto/internal/simd.h>
|
|
#include <linux/crypto.h>
|
|
#include <linux/jump_label.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <asm/intel-family.h>
|
|
#include <asm/simd.h>
|
|
|
|
asmlinkage void poly1305_init_x86_64(void *ctx,
|
|
const u8 key[POLY1305_KEY_SIZE]);
|
|
asmlinkage void poly1305_blocks_x86_64(void *ctx, const u8 *inp,
|
|
const size_t len, const u32 padbit);
|
|
asmlinkage void poly1305_emit_x86_64(void *ctx, u8 mac[POLY1305_DIGEST_SIZE],
|
|
const u32 nonce[4]);
|
|
asmlinkage void poly1305_emit_avx(void *ctx, u8 mac[POLY1305_DIGEST_SIZE],
|
|
const u32 nonce[4]);
|
|
asmlinkage void poly1305_blocks_avx(void *ctx, const u8 *inp, const size_t len,
|
|
const u32 padbit);
|
|
asmlinkage void poly1305_blocks_avx2(void *ctx, const u8 *inp, const size_t len,
|
|
const u32 padbit);
|
|
asmlinkage void poly1305_blocks_avx512(void *ctx, const u8 *inp,
|
|
const size_t len, const u32 padbit);
|
|
|
|
static __ro_after_init DEFINE_STATIC_KEY_FALSE(poly1305_use_avx);
|
|
static __ro_after_init DEFINE_STATIC_KEY_FALSE(poly1305_use_avx2);
|
|
static __ro_after_init DEFINE_STATIC_KEY_FALSE(poly1305_use_avx512);
|
|
|
|
struct poly1305_arch_internal {
|
|
union {
|
|
struct {
|
|
u32 h[5];
|
|
u32 is_base2_26;
|
|
};
|
|
u64 hs[3];
|
|
};
|
|
u64 r[2];
|
|
u64 pad;
|
|
struct { u32 r2, r1, r4, r3; } rn[9];
|
|
};
|
|
|
|
/* The AVX code uses base 2^26, while the scalar code uses base 2^64. If we hit
|
|
* the unfortunate situation of using AVX and then having to go back to scalar
|
|
* -- because the user is silly and has called the update function from two
|
|
* separate contexts -- then we need to convert back to the original base before
|
|
* proceeding. It is possible to reason that the initial reduction below is
|
|
* sufficient given the implementation invariants. However, for an avoidance of
|
|
* doubt and because this is not performance critical, we do the full reduction
|
|
* anyway. Z3 proof of below function: https://xn--4db.cc/ltPtHCKN/py
|
|
*/
|
|
static void convert_to_base2_64(void *ctx)
|
|
{
|
|
struct poly1305_arch_internal *state = ctx;
|
|
u32 cy;
|
|
|
|
if (!state->is_base2_26)
|
|
return;
|
|
|
|
cy = state->h[0] >> 26; state->h[0] &= 0x3ffffff; state->h[1] += cy;
|
|
cy = state->h[1] >> 26; state->h[1] &= 0x3ffffff; state->h[2] += cy;
|
|
cy = state->h[2] >> 26; state->h[2] &= 0x3ffffff; state->h[3] += cy;
|
|
cy = state->h[3] >> 26; state->h[3] &= 0x3ffffff; state->h[4] += cy;
|
|
state->hs[0] = ((u64)state->h[2] << 52) | ((u64)state->h[1] << 26) | state->h[0];
|
|
state->hs[1] = ((u64)state->h[4] << 40) | ((u64)state->h[3] << 14) | (state->h[2] >> 12);
|
|
state->hs[2] = state->h[4] >> 24;
|
|
#define ULT(a, b) ((a ^ ((a ^ b) | ((a - b) ^ b))) >> (sizeof(a) * 8 - 1))
|
|
cy = (state->hs[2] >> 2) + (state->hs[2] & ~3ULL);
|
|
state->hs[2] &= 3;
|
|
state->hs[0] += cy;
|
|
state->hs[1] += (cy = ULT(state->hs[0], cy));
|
|
state->hs[2] += ULT(state->hs[1], cy);
|
|
#undef ULT
|
|
state->is_base2_26 = 0;
|
|
}
|
|
|
|
static void poly1305_simd_init(void *ctx, const u8 key[POLY1305_KEY_SIZE])
|
|
{
|
|
poly1305_init_x86_64(ctx, key);
|
|
}
|
|
|
|
static void poly1305_simd_blocks(void *ctx, const u8 *inp, size_t len,
|
|
const u32 padbit)
|
|
{
|
|
struct poly1305_arch_internal *state = ctx;
|
|
|
|
/* SIMD disables preemption, so relax after processing each page. */
|
|
BUILD_BUG_ON(PAGE_SIZE < POLY1305_BLOCK_SIZE ||
|
|
PAGE_SIZE % POLY1305_BLOCK_SIZE);
|
|
|
|
if (!IS_ENABLED(CONFIG_AS_AVX) || !static_branch_likely(&poly1305_use_avx) ||
|
|
(len < (POLY1305_BLOCK_SIZE * 18) && !state->is_base2_26) ||
|
|
!crypto_simd_usable()) {
|
|
convert_to_base2_64(ctx);
|
|
poly1305_blocks_x86_64(ctx, inp, len, padbit);
|
|
return;
|
|
}
|
|
|
|
for (;;) {
|
|
const size_t bytes = min_t(size_t, len, PAGE_SIZE);
|
|
|
|
kernel_fpu_begin();
|
|
if (IS_ENABLED(CONFIG_AS_AVX512) && static_branch_likely(&poly1305_use_avx512))
|
|
poly1305_blocks_avx512(ctx, inp, bytes, padbit);
|
|
else if (IS_ENABLED(CONFIG_AS_AVX2) && static_branch_likely(&poly1305_use_avx2))
|
|
poly1305_blocks_avx2(ctx, inp, bytes, padbit);
|
|
else
|
|
poly1305_blocks_avx(ctx, inp, bytes, padbit);
|
|
kernel_fpu_end();
|
|
len -= bytes;
|
|
if (!len)
|
|
break;
|
|
inp += bytes;
|
|
}
|
|
}
|
|
|
|
static void poly1305_simd_emit(void *ctx, u8 mac[POLY1305_DIGEST_SIZE],
|
|
const u32 nonce[4])
|
|
{
|
|
struct poly1305_arch_internal *state = ctx;
|
|
|
|
if (!IS_ENABLED(CONFIG_AS_AVX) || !static_branch_likely(&poly1305_use_avx) ||
|
|
!state->is_base2_26 || !crypto_simd_usable()) {
|
|
convert_to_base2_64(ctx);
|
|
poly1305_emit_x86_64(ctx, mac, nonce);
|
|
} else
|
|
poly1305_emit_avx(ctx, mac, nonce);
|
|
}
|
|
|
|
void poly1305_init_arch(struct poly1305_desc_ctx *dctx, const u8 *key)
|
|
{
|
|
poly1305_simd_init(&dctx->h, key);
|
|
dctx->s[0] = get_unaligned_le32(&key[16]);
|
|
dctx->s[1] = get_unaligned_le32(&key[20]);
|
|
dctx->s[2] = get_unaligned_le32(&key[24]);
|
|
dctx->s[3] = get_unaligned_le32(&key[28]);
|
|
dctx->buflen = 0;
|
|
dctx->sset = true;
|
|
}
|
|
EXPORT_SYMBOL(poly1305_init_arch);
|
|
|
|
static unsigned int crypto_poly1305_setdctxkey(struct poly1305_desc_ctx *dctx,
|
|
const u8 *inp, unsigned int len)
|
|
{
|
|
unsigned int acc = 0;
|
|
if (unlikely(!dctx->sset)) {
|
|
if (!dctx->rset && len >= POLY1305_BLOCK_SIZE) {
|
|
poly1305_simd_init(&dctx->h, inp);
|
|
inp += POLY1305_BLOCK_SIZE;
|
|
len -= POLY1305_BLOCK_SIZE;
|
|
acc += POLY1305_BLOCK_SIZE;
|
|
dctx->rset = 1;
|
|
}
|
|
if (len >= POLY1305_BLOCK_SIZE) {
|
|
dctx->s[0] = get_unaligned_le32(&inp[0]);
|
|
dctx->s[1] = get_unaligned_le32(&inp[4]);
|
|
dctx->s[2] = get_unaligned_le32(&inp[8]);
|
|
dctx->s[3] = get_unaligned_le32(&inp[12]);
|
|
inp += POLY1305_BLOCK_SIZE;
|
|
len -= POLY1305_BLOCK_SIZE;
|
|
acc += POLY1305_BLOCK_SIZE;
|
|
dctx->sset = true;
|
|
}
|
|
}
|
|
return acc;
|
|
}
|
|
|
|
void poly1305_update_arch(struct poly1305_desc_ctx *dctx, const u8 *src,
|
|
unsigned int srclen)
|
|
{
|
|
unsigned int bytes, used;
|
|
|
|
if (unlikely(dctx->buflen)) {
|
|
bytes = min(srclen, POLY1305_BLOCK_SIZE - dctx->buflen);
|
|
memcpy(dctx->buf + dctx->buflen, src, bytes);
|
|
src += bytes;
|
|
srclen -= bytes;
|
|
dctx->buflen += bytes;
|
|
|
|
if (dctx->buflen == POLY1305_BLOCK_SIZE) {
|
|
if (likely(!crypto_poly1305_setdctxkey(dctx, dctx->buf, POLY1305_BLOCK_SIZE)))
|
|
poly1305_simd_blocks(&dctx->h, dctx->buf, POLY1305_BLOCK_SIZE, 1);
|
|
dctx->buflen = 0;
|
|
}
|
|
}
|
|
|
|
if (likely(srclen >= POLY1305_BLOCK_SIZE)) {
|
|
bytes = round_down(srclen, POLY1305_BLOCK_SIZE);
|
|
srclen -= bytes;
|
|
used = crypto_poly1305_setdctxkey(dctx, src, bytes);
|
|
if (likely(bytes - used))
|
|
poly1305_simd_blocks(&dctx->h, src + used, bytes - used, 1);
|
|
src += bytes;
|
|
}
|
|
|
|
if (unlikely(srclen)) {
|
|
dctx->buflen = srclen;
|
|
memcpy(dctx->buf, src, srclen);
|
|
}
|
|
}
|
|
EXPORT_SYMBOL(poly1305_update_arch);
|
|
|
|
void poly1305_final_arch(struct poly1305_desc_ctx *dctx, u8 *dst)
|
|
{
|
|
if (unlikely(dctx->buflen)) {
|
|
dctx->buf[dctx->buflen++] = 1;
|
|
memset(dctx->buf + dctx->buflen, 0,
|
|
POLY1305_BLOCK_SIZE - dctx->buflen);
|
|
poly1305_simd_blocks(&dctx->h, dctx->buf, POLY1305_BLOCK_SIZE, 0);
|
|
}
|
|
|
|
poly1305_simd_emit(&dctx->h, dst, dctx->s);
|
|
*dctx = (struct poly1305_desc_ctx){};
|
|
}
|
|
EXPORT_SYMBOL(poly1305_final_arch);
|
|
|
|
static int crypto_poly1305_init(struct shash_desc *desc)
|
|
{
|
|
struct poly1305_desc_ctx *dctx = shash_desc_ctx(desc);
|
|
|
|
*dctx = (struct poly1305_desc_ctx){};
|
|
return 0;
|
|
}
|
|
|
|
static int crypto_poly1305_update(struct shash_desc *desc,
|
|
const u8 *src, unsigned int srclen)
|
|
{
|
|
struct poly1305_desc_ctx *dctx = shash_desc_ctx(desc);
|
|
|
|
poly1305_update_arch(dctx, src, srclen);
|
|
return 0;
|
|
}
|
|
|
|
static int crypto_poly1305_final(struct shash_desc *desc, u8 *dst)
|
|
{
|
|
struct poly1305_desc_ctx *dctx = shash_desc_ctx(desc);
|
|
|
|
if (unlikely(!dctx->sset))
|
|
return -ENOKEY;
|
|
|
|
poly1305_final_arch(dctx, dst);
|
|
return 0;
|
|
}
|
|
|
|
static struct shash_alg alg = {
|
|
.digestsize = POLY1305_DIGEST_SIZE,
|
|
.init = crypto_poly1305_init,
|
|
.update = crypto_poly1305_update,
|
|
.final = crypto_poly1305_final,
|
|
.descsize = sizeof(struct poly1305_desc_ctx),
|
|
.base = {
|
|
.cra_name = "poly1305",
|
|
.cra_driver_name = "poly1305-simd",
|
|
.cra_priority = 300,
|
|
.cra_blocksize = POLY1305_BLOCK_SIZE,
|
|
.cra_module = THIS_MODULE,
|
|
},
|
|
};
|
|
|
|
static int __init poly1305_simd_mod_init(void)
|
|
{
|
|
if (IS_ENABLED(CONFIG_AS_AVX) && boot_cpu_has(X86_FEATURE_AVX) &&
|
|
cpu_has_xfeatures(XFEATURE_MASK_SSE | XFEATURE_MASK_YMM, NULL))
|
|
static_branch_enable(&poly1305_use_avx);
|
|
if (IS_ENABLED(CONFIG_AS_AVX2) && boot_cpu_has(X86_FEATURE_AVX) &&
|
|
boot_cpu_has(X86_FEATURE_AVX2) &&
|
|
cpu_has_xfeatures(XFEATURE_MASK_SSE | XFEATURE_MASK_YMM, NULL))
|
|
static_branch_enable(&poly1305_use_avx2);
|
|
if (IS_ENABLED(CONFIG_AS_AVX512) && boot_cpu_has(X86_FEATURE_AVX) &&
|
|
boot_cpu_has(X86_FEATURE_AVX2) && boot_cpu_has(X86_FEATURE_AVX512F) &&
|
|
cpu_has_xfeatures(XFEATURE_MASK_SSE | XFEATURE_MASK_YMM | XFEATURE_MASK_AVX512, NULL) &&
|
|
/* Skylake downclocks unacceptably much when using zmm, but later generations are fast. */
|
|
boot_cpu_data.x86_model != INTEL_FAM6_SKYLAKE_X)
|
|
static_branch_enable(&poly1305_use_avx512);
|
|
return IS_REACHABLE(CONFIG_CRYPTO_HASH) ? crypto_register_shash(&alg) : 0;
|
|
}
|
|
|
|
static void __exit poly1305_simd_mod_exit(void)
|
|
{
|
|
if (IS_REACHABLE(CONFIG_CRYPTO_HASH))
|
|
crypto_unregister_shash(&alg);
|
|
}
|
|
|
|
module_init(poly1305_simd_mod_init);
|
|
module_exit(poly1305_simd_mod_exit);
|
|
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_AUTHOR("Jason A. Donenfeld <Jason@zx2c4.com>");
|
|
MODULE_DESCRIPTION("Poly1305 authenticator");
|
|
MODULE_ALIAS_CRYPTO("poly1305");
|
|
MODULE_ALIAS_CRYPTO("poly1305-simd");
|