/* * This is a SIMD SHA-1 implementation. It requires the Intel(R) Supplemental * SSE3 instruction set extensions introduced in Intel Core Microarchitecture * processors. CPUs supporting Intel(R) AVX extensions will get an additional * boost. * * This work was inspired by the vectorized implementation of Dean Gaudet. * Additional information on it can be found at: * http://www.arctic.org/~dean/crypto/sha1.html * * It was improved upon with more efficient vectorization of the message * scheduling. This implementation has also been optimized for all current and * several future generations of Intel CPUs. * * See this article for more information about the implementation details: * http://software.intel.com/en-us/articles/improving-the-performance-of-the-secure-hash-algorithm-1/ * * Copyright (C) 2010, Intel Corp. * Authors: Maxim Locktyukhin <maxim.locktyukhin@intel.com> * Ronen Zohar <ronen.zohar@intel.com> * * Converted to AT&T syntax and adapted for inclusion in the Linux kernel: * Author: Mathias Krause <minipli@googlemail.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. */ #include <linux/linkage.h> #define CTX %rdi // arg1 #define BUF %rsi // arg2 #define CNT %rdx // arg3 #define REG_A %ecx #define REG_B %esi #define REG_C %edi #define REG_D %ebp #define REG_E %edx #define REG_T1 %eax #define REG_T2 %ebx #define K_BASE %r8 #define HASH_PTR %r9 #define BUFFER_PTR %r10 #define BUFFER_END %r11 #define W_TMP1 %xmm0 #define W_TMP2 %xmm9 #define W0 %xmm1 #define W4 %xmm2 #define W8 %xmm3 #define W12 %xmm4 #define W16 %xmm5 #define W20 %xmm6 #define W24 %xmm7 #define W28 %xmm8 #define XMM_SHUFB_BSWAP %xmm10 /* we keep window of 64 w[i]+K pre-calculated values in a circular buffer */ #define WK(t) (((t) & 15) * 4)(%rsp) #define W_PRECALC_AHEAD 16 /* * This macro implements the SHA-1 function's body for single 64-byte block * param: function's name */ .macro SHA1_VECTOR_ASM name ENTRY(\name) push %rbx push %rbp push %r12 mov %rsp, %r12 sub $64, %rsp # allocate workspace and $~15, %rsp # align stack mov CTX, HASH_PTR mov BUF, BUFFER_PTR shl $6, CNT # multiply by 64 add BUF, CNT mov CNT, BUFFER_END lea K_XMM_AR(%rip), K_BASE xmm_mov BSWAP_SHUFB_CTL(%rip), XMM_SHUFB_BSWAP SHA1_PIPELINED_MAIN_BODY # cleanup workspace mov $8, %ecx mov %rsp, %rdi xor %rax, %rax rep stosq mov %r12, %rsp # deallocate workspace pop %r12 pop %rbp pop %rbx ret ENDPROC(\name) .endm /* * This macro implements 80 rounds of SHA-1 for one 64-byte block */ .macro SHA1_PIPELINED_MAIN_BODY INIT_REGALLOC mov (HASH_PTR), A mov 4(HASH_PTR), B mov 8(HASH_PTR), C mov 12(HASH_PTR), D mov 16(HASH_PTR), E .set i, 0 .rept W_PRECALC_AHEAD W_PRECALC i .set i, (i+1) .endr .align 4 1: RR F1,A,B,C,D,E,0 RR F1,D,E,A,B,C,2 RR F1,B,C,D,E,A,4 RR F1,E,A,B,C,D,6 RR F1,C,D,E,A,B,8 RR F1,A,B,C,D,E,10 RR F1,D,E,A,B,C,12 RR F1,B,C,D,E,A,14 RR F1,E,A,B,C,D,16 RR F1,C,D,E,A,B,18 RR F2,A,B,C,D,E,20 RR F2,D,E,A,B,C,22 RR F2,B,C,D,E,A,24 RR F2,E,A,B,C,D,26 RR F2,C,D,E,A,B,28 RR F2,A,B,C,D,E,30 RR F2,D,E,A,B,C,32 RR F2,B,C,D,E,A,34 RR F2,E,A,B,C,D,36 RR F2,C,D,E,A,B,38 RR F3,A,B,C,D,E,40 RR F3,D,E,A,B,C,42 RR F3,B,C,D,E,A,44 RR F3,E,A,B,C,D,46 RR F3,C,D,E,A,B,48 RR F3,A,B,C,D,E,50 RR F3,D,E,A,B,C,52 RR F3,B,C,D,E,A,54 RR F3,E,A,B,C,D,56 RR F3,C,D,E,A,B,58 add $64, BUFFER_PTR # move to the next 64-byte block cmp BUFFER_END, BUFFER_PTR # if the current is the last one use cmovae K_BASE, BUFFER_PTR # dummy source to avoid buffer overrun RR F4,A,B,C,D,E,60 RR F4,D,E,A,B,C,62 RR F4,B,C,D,E,A,64 RR F4,E,A,B,C,D,66 RR F4,C,D,E,A,B,68 RR F4,A,B,C,D,E,70 RR F4,D,E,A,B,C,72 RR F4,B,C,D,E,A,74 RR F4,E,A,B,C,D,76 RR F4,C,D,E,A,B,78 UPDATE_HASH (HASH_PTR), A UPDATE_HASH 4(HASH_PTR), B UPDATE_HASH 8(HASH_PTR), C UPDATE_HASH 12(HASH_PTR), D UPDATE_HASH 16(HASH_PTR), E RESTORE_RENAMED_REGS cmp K_BASE, BUFFER_PTR # K_BASE means, we reached the end jne 1b .endm .macro INIT_REGALLOC .set A, REG_A .set B, REG_B .set C, REG_C .set D, REG_D .set E, REG_E .set T1, REG_T1 .set T2, REG_T2 .endm .macro RESTORE_RENAMED_REGS # order is important (REG_C is where it should be) mov B, REG_B mov D, REG_D mov A, REG_A mov E, REG_E .endm .macro SWAP_REG_NAMES a, b .set _T, \a .set \a, \b .set \b, _T .endm .macro F1 b, c, d mov \c, T1 SWAP_REG_NAMES \c, T1 xor \d, T1 and \b, T1 xor \d, T1 .endm .macro F2 b, c, d mov \d, T1 SWAP_REG_NAMES \d, T1 xor \c, T1 xor \b, T1 .endm .macro F3 b, c ,d mov \c, T1 SWAP_REG_NAMES \c, T1 mov \b, T2 or \b, T1 and \c, T2 and \d, T1 or T2, T1 .endm .macro F4 b, c, d F2 \b, \c, \d .endm .macro UPDATE_HASH hash, val add \hash, \val mov \val, \hash .endm /* * RR does two rounds of SHA-1 back to back with W[] pre-calc * t1 = F(b, c, d); e += w(i) * e += t1; b <<= 30; d += w(i+1); * t1 = F(a, b, c); * d += t1; a <<= 5; * e += a; * t1 = e; a >>= 7; * t1 <<= 5; * d += t1; */ .macro RR F, a, b, c, d, e, round add WK(\round), \e \F \b, \c, \d # t1 = F(b, c, d); W_PRECALC (\round + W_PRECALC_AHEAD) rol $30, \b add T1, \e add WK(\round + 1), \d \F \a, \b, \c W_PRECALC (\round + W_PRECALC_AHEAD + 1) rol $5, \a add \a, \e add T1, \d ror $7, \a # (a <<r 5) >>r 7) => a <<r 30) mov \e, T1 SWAP_REG_NAMES \e, T1 rol $5, T1 add T1, \d # write: \a, \b # rotate: \a<=\d, \b<=\e, \c<=\a, \d<=\b, \e<=\c .endm .macro W_PRECALC r .set i, \r .if (i < 20) .set K_XMM, 0 .elseif (i < 40) .set K_XMM, 16 .elseif (i < 60) .set K_XMM, 32 .elseif (i < 80) .set K_XMM, 48 .endif .if ((i < 16) || ((i >= 80) && (i < (80 + W_PRECALC_AHEAD)))) .set i, ((\r) % 80) # pre-compute for the next iteration .if (i == 0) W_PRECALC_RESET .endif W_PRECALC_00_15 .elseif (i<32) W_PRECALC_16_31 .elseif (i < 80) // rounds 32-79 W_PRECALC_32_79 .endif .endm .macro W_PRECALC_RESET .set W, W0 .set W_minus_04, W4 .set W_minus_08, W8 .set W_minus_12, W12 .set W_minus_16, W16 .set W_minus_20, W20 .set W_minus_24, W24 .set W_minus_28, W28 .set W_minus_32, W .endm .macro W_PRECALC_ROTATE .set W_minus_32, W_minus_28 .set W_minus_28, W_minus_24 .set W_minus_24, W_minus_20 .set W_minus_20, W_minus_16 .set W_minus_16, W_minus_12 .set W_minus_12, W_minus_08 .set W_minus_08, W_minus_04 .set W_minus_04, W .set W, W_minus_32 .endm .macro W_PRECALC_SSSE3 .macro W_PRECALC_00_15 W_PRECALC_00_15_SSSE3 .endm .macro W_PRECALC_16_31 W_PRECALC_16_31_SSSE3 .endm .macro W_PRECALC_32_79 W_PRECALC_32_79_SSSE3 .endm /* message scheduling pre-compute for rounds 0-15 */ .macro W_PRECALC_00_15_SSSE3 .if ((i & 3) == 0) movdqu (i*4)(BUFFER_PTR), W_TMP1 .elseif ((i & 3) == 1) pshufb XMM_SHUFB_BSWAP, W_TMP1 movdqa W_TMP1, W .elseif ((i & 3) == 2) paddd (K_BASE), W_TMP1 .elseif ((i & 3) == 3) movdqa W_TMP1, WK(i&~3) W_PRECALC_ROTATE .endif .endm /* message scheduling pre-compute for rounds 16-31 * * - calculating last 32 w[i] values in 8 XMM registers * - pre-calculate K+w[i] values and store to mem, for later load by ALU add * instruction * * some "heavy-lifting" vectorization for rounds 16-31 due to w[i]->w[i-3] * dependency, but improves for 32-79 */ .macro W_PRECALC_16_31_SSSE3 # blended scheduling of vector and scalar instruction streams, one 4-wide # vector iteration / 4 scalar rounds .if ((i & 3) == 0) movdqa W_minus_12, W palignr $8, W_minus_16, W # w[i-14] movdqa W_minus_04, W_TMP1 psrldq $4, W_TMP1 # w[i-3] pxor W_minus_08, W .elseif ((i & 3) == 1) pxor W_minus_16, W_TMP1 pxor W_TMP1, W movdqa W, W_TMP2 movdqa W, W_TMP1 pslldq $12, W_TMP2 .elseif ((i & 3) == 2) psrld $31, W pslld $1, W_TMP1 por W, W_TMP1 movdqa W_TMP2, W psrld $30, W_TMP2 pslld $2, W .elseif ((i & 3) == 3) pxor W, W_TMP1 pxor W_TMP2, W_TMP1 movdqa W_TMP1, W paddd K_XMM(K_BASE), W_TMP1 movdqa W_TMP1, WK(i&~3) W_PRECALC_ROTATE .endif .endm /* message scheduling pre-compute for rounds 32-79 * * in SHA-1 specification: w[i] = (w[i-3] ^ w[i-8] ^ w[i-14] ^ w[i-16]) rol 1 * instead we do equal: w[i] = (w[i-6] ^ w[i-16] ^ w[i-28] ^ w[i-32]) rol 2 * allows more efficient vectorization since w[i]=>w[i-3] dependency is broken */ .macro W_PRECALC_32_79_SSSE3 .if ((i & 3) == 0) movdqa W_minus_04, W_TMP1 pxor W_minus_28, W # W is W_minus_32 before xor palignr $8, W_minus_08, W_TMP1 .elseif ((i & 3) == 1) pxor W_minus_16, W pxor W_TMP1, W movdqa W, W_TMP1 .elseif ((i & 3) == 2) psrld $30, W pslld $2, W_TMP1 por W, W_TMP1 .elseif ((i & 3) == 3) movdqa W_TMP1, W paddd K_XMM(K_BASE), W_TMP1 movdqa W_TMP1, WK(i&~3) W_PRECALC_ROTATE .endif .endm .endm // W_PRECALC_SSSE3 #define K1 0x5a827999 #define K2 0x6ed9eba1 #define K3 0x8f1bbcdc #define K4 0xca62c1d6 .section .rodata .align 16 K_XMM_AR: .long K1, K1, K1, K1 .long K2, K2, K2, K2 .long K3, K3, K3, K3 .long K4, K4, K4, K4 BSWAP_SHUFB_CTL: .long 0x00010203 .long 0x04050607 .long 0x08090a0b .long 0x0c0d0e0f .section .text W_PRECALC_SSSE3 .macro xmm_mov a, b movdqu \a,\b .endm /* SSSE3 optimized implementation: * extern "C" void sha1_transform_ssse3(u32 *digest, const char *data, u32 *ws, * unsigned int rounds); */ SHA1_VECTOR_ASM sha1_transform_ssse3 #ifdef CONFIG_AS_AVX .macro W_PRECALC_AVX .purgem W_PRECALC_00_15 .macro W_PRECALC_00_15 W_PRECALC_00_15_AVX .endm .purgem W_PRECALC_16_31 .macro W_PRECALC_16_31 W_PRECALC_16_31_AVX .endm .purgem W_PRECALC_32_79 .macro W_PRECALC_32_79 W_PRECALC_32_79_AVX .endm .macro W_PRECALC_00_15_AVX .if ((i & 3) == 0) vmovdqu (i*4)(BUFFER_PTR), W_TMP1 .elseif ((i & 3) == 1) vpshufb XMM_SHUFB_BSWAP, W_TMP1, W .elseif ((i & 3) == 2) vpaddd (K_BASE), W, W_TMP1 .elseif ((i & 3) == 3) vmovdqa W_TMP1, WK(i&~3) W_PRECALC_ROTATE .endif .endm .macro W_PRECALC_16_31_AVX .if ((i & 3) == 0) vpalignr $8, W_minus_16, W_minus_12, W # w[i-14] vpsrldq $4, W_minus_04, W_TMP1 # w[i-3] vpxor W_minus_08, W, W vpxor W_minus_16, W_TMP1, W_TMP1 .elseif ((i & 3) == 1) vpxor W_TMP1, W, W vpslldq $12, W, W_TMP2 vpslld $1, W, W_TMP1 .elseif ((i & 3) == 2) vpsrld $31, W, W vpor W, W_TMP1, W_TMP1 vpslld $2, W_TMP2, W vpsrld $30, W_TMP2, W_TMP2 .elseif ((i & 3) == 3) vpxor W, W_TMP1, W_TMP1 vpxor W_TMP2, W_TMP1, W vpaddd K_XMM(K_BASE), W, W_TMP1 vmovdqu W_TMP1, WK(i&~3) W_PRECALC_ROTATE .endif .endm .macro W_PRECALC_32_79_AVX .if ((i & 3) == 0) vpalignr $8, W_minus_08, W_minus_04, W_TMP1 vpxor W_minus_28, W, W # W is W_minus_32 before xor .elseif ((i & 3) == 1) vpxor W_minus_16, W_TMP1, W_TMP1 vpxor W_TMP1, W, W .elseif ((i & 3) == 2) vpslld $2, W, W_TMP1 vpsrld $30, W, W vpor W, W_TMP1, W .elseif ((i & 3) == 3) vpaddd K_XMM(K_BASE), W, W_TMP1 vmovdqu W_TMP1, WK(i&~3) W_PRECALC_ROTATE .endif .endm .endm // W_PRECALC_AVX W_PRECALC_AVX .purgem xmm_mov .macro xmm_mov a, b vmovdqu \a,\b .endm /* AVX optimized implementation: * extern "C" void sha1_transform_avx(u32 *digest, const char *data, u32 *ws, * unsigned int rounds); */ SHA1_VECTOR_ASM sha1_transform_avx #endif