Added support for ARM/NEON based bit sliced AES in XTS mode
[openssl.git] / crypto / aes / asm / bsaes-armv7.pl
index d9748f4b103e1a7e9d70b2795ab70577b92337d5..9be80e5a51b863aa067b395f38528881299d6365 100644 (file)
 # only low-level primitives and unsupported entry points, just enough
 # to collect performance results, which for Cortex-A8 core are:
 #
-# encrypt      20.9 cycles per byte processed with 128-bit key
-# decrypt      25.6 cycles per byte processed with 128-bit key
-# key conv.    900  cycles per 128-bit key/0.34 of 8x block
+# encrypt      19.5 cycles per byte processed with 128-bit key
+# decrypt      24.0 cycles per byte processed with 128-bit key
+# key conv.    440  cycles per 128-bit key/0.18 of 8x block
+#
+# Snapdragon S4 encrypts byte in 17.6 cycles and decrypts in 22.6,
+# which is [much] worse than anticipated (for further details see
+# http://www.openssl.org/~appro/Snapdragon-S4.html).
+#
+# Cortex-A15 manages in 14.2/19.6 cycles [when integer-only code
+# manages in 20.0 cycles].
 #
 # When comparing to x86_64 results keep in mind that NEON unit is
-# [mostly] single-issue and thus can't benefit from parallelism. And
-# when comparing to aes-armv4 results keep in mind key schedule
-# conversion overhead (see bsaes-x86_64.pl for details)...
+# [mostly] single-issue and thus can't [fully] benefit from
+# instruction-level parallelism. And when comparing to aes-armv4
+# results keep in mind key schedule conversion overhead (see
+# bsaes-x86_64.pl for further details)...
 #
 #                                              <appro@openssl.org>
 
@@ -57,8 +65,8 @@ sub InBasisChange {
 # output in lsb > [b6, b5, b0, b3, b7, b1, b4, b2] < msb 
 my @b=@_[0..7];
 $code.=<<___;
-       veor    @b[5], @b[5], @b[6]
        veor    @b[2], @b[2], @b[1]
+       veor    @b[5], @b[5], @b[6]
        veor    @b[3], @b[3], @b[0]
        veor    @b[6], @b[6], @b[2]
        veor    @b[5], @b[5], @b[0]
@@ -68,9 +76,9 @@ $code.=<<___;
        veor    @b[7], @b[7], @b[5]
        veor    @b[3], @b[3], @b[4]
        veor    @b[4], @b[4], @b[5]
-       veor    @b[3], @b[3], @b[1]
 
        veor    @b[2], @b[2], @b[7]
+       veor    @b[3], @b[3], @b[1]
        veor    @b[1], @b[1], @b[5]
 ___
 }
@@ -82,8 +90,8 @@ my @b=@_[0..7];
 $code.=<<___;
        veor    @b[0], @b[0], @b[6]
        veor    @b[1], @b[1], @b[4]
-       veor    @b[2], @b[2], @b[0]
        veor    @b[4], @b[4], @b[6]
+       veor    @b[2], @b[2], @b[0]
        veor    @b[6], @b[6], @b[1]
 
        veor    @b[1], @b[1], @b[5]
@@ -107,19 +115,20 @@ my @s=@_[12..15];
        &InvOutBasisChange      (@b[3,7,0,4,5,1,2,6]);
 }
 
-sub InvInBasisChange {         # OutBasisChange in reverse
+sub InvInBasisChange {         # OutBasisChange in reverse (with twist)
 my @b=@_[5,1,2,6,3,7,0,4];
 $code.=<<___
+        veor   @b[1], @b[1], @b[7]
        veor    @b[4], @b[4], @b[7]
 
        veor    @b[7], @b[7], @b[5]
+        veor   @b[1], @b[1], @b[3]
        veor    @b[2], @b[2], @b[5]
        veor    @b[3], @b[3], @b[7]
-       veor    @b[5], @b[5], @b[3]
-       veor    @b[1], @b[1], @b[5]
 
        veor    @b[6], @b[6], @b[1]
        veor    @b[2], @b[2], @b[0]
+        veor   @b[5], @b[5], @b[3]
        veor    @b[4], @b[4], @b[6]
        veor    @b[0], @b[0], @b[6]
        veor    @b[1], @b[1], @b[4]
@@ -151,15 +160,15 @@ sub Mul_GF4 {
 #;*************************************************************
 #;* Mul_GF4: Input x0-x1,y0-y1 Output x0-x1 Temp t0 (8) *
 #;*************************************************************
-my ($x0,$x1,$y0,$y1,$t0)=@_;
+my ($x0,$x1,$y0,$y1,$t0,$t1)=@_;
 $code.=<<___;
        veor    $t0, $y0, $y1
        vand    $t0, $t0, $x0
        veor    $x0, $x0, $x1
-       vand    $x1, $x1, $y0
+       vand    $t1, $x1, $y0
        vand    $x0, $x0, $y1
-       veor    $x0, $x0, $x1
-       veor    $x1, $x1, $t0
+       veor    $x1, $t1, $t0
+       veor    $x0, $x0, $t1
 ___
 }
 
@@ -203,13 +212,11 @@ my @x=@_[0..7];
 my @y=@_[8..11];
 my @t=@_[12..15];
 $code.=<<___;
-       vmov    @t[0], @x[0]
-       vmov    @t[1], @x[1]
+       veor    @t[0], @x[0], @x[2]
+       veor    @t[1], @x[1], @x[3]
 ___
-       &Mul_GF4        (@x[0], @x[1], @y[0], @y[1], @t[2]);
+       &Mul_GF4        (@x[0], @x[1], @y[0], @y[1], @t[2..3]);
 $code.=<<___;
-       veor    @t[0], @t[0], @x[2]
-       veor    @t[1], @t[1], @x[3]
        veor    @y[0], @y[0], @y[2]
        veor    @y[1], @y[1], @y[3]
 ___
@@ -230,7 +237,7 @@ $code.=<<___;
        veor    @y[0], @y[0], @y[2]
        veor    @y[1], @y[1], @y[3]
 ___
-       &Mul_GF4        (@x[4], @x[5], @y[0], @y[1], @t[3]);
+       &Mul_GF4        (@x[4], @x[5], @y[0], @y[1], @t[2..3]);
 $code.=<<___;
        veor    @x[4], @x[4], @t[0]
        veor    @x[6], @x[6], @t[0]
@@ -260,60 +267,52 @@ $code.=<<___;
        vorr    @t[3], @t[3], @s[0]
        veor    @s[0], @s[0], @t[1]
        vand    @t[0], @t[0], @t[1]
+       veor    @t[1], @x[3], @x[2]
        vand    @s[3], @s[3], @s[0]
-       veor    @s[0], @x[3], @x[2]
-       vand    @s[1], @s[1], @s[0]
+       vand    @s[1], @s[1], @t[1]
+       veor    @t[1], @x[4], @x[5]
+       veor    @s[0], @x[1], @x[0]
        veor    @t[3], @t[3], @s[1]
        veor    @t[2], @t[2], @s[1]
-       veor    @s[1], @x[4], @x[5]
-       veor    @s[0], @x[1], @x[0]
-       vorr    @t[1], @s[1], @s[0]
-       vand    @s[1], @s[1], @s[0]
-       veor    @t[0], @t[0], @s[1]
+       vand    @s[1], @t[1], @s[0]
+       vorr    @t[1], @t[1], @s[0]
        veor    @t[3], @t[3], @s[3]
+       veor    @t[0], @t[0], @s[1]
        veor    @t[2], @t[2], @s[2]
        veor    @t[1], @t[1], @s[3]
        veor    @t[0], @t[0], @s[2]
-       veor    @t[1], @t[1], @s[2]
        vand    @s[0], @x[7], @x[3]
+       veor    @t[1], @t[1], @s[2]
        vand    @s[1], @x[6], @x[2]
        vand    @s[2], @x[5], @x[1]
        vorr    @s[3], @x[4], @x[0]
        veor    @t[3], @t[3], @s[0]
-       veor    @t[2], @t[2], @s[1]
        veor    @t[1], @t[1], @s[2]
        veor    @t[0], @t[0], @s[3]
+       veor    @t[2], @t[2], @s[1]
 
        @ Inv_GF16 \t0, \t1, \t2, \t3, \s0, \s1, \s2, \s3
 
        @ new smaller inversion
 
-       veor    @s[0], @t[3], @t[2]
-       vand    @t[3], @t[3], @t[1]
+       vand    @s[2], @t[3], @t[1]
+       vmov    @s[0], @t[0]
 
-       veor    @s[2], @t[0], @t[3]
-       vand    @s[3], @s[0], @s[2]
-
-       veor    @s[3], @s[3], @t[2]
-       veor    @s[1], @t[1], @t[0]
+       veor    @s[1], @t[2], @s[2]
+       veor    @s[3], @t[0], @s[2]
+       veor    @s[2], @t[0], @s[2]     @ @s[2]=@s[3]
 
+       vbsl    @s[1], @t[1], @t[0]
+       vbsl    @s[3], @t[3], @t[2]
        veor    @t[3], @t[3], @t[2]
 
-       vand    @s[1], @s[1], @t[3]
-
-       veor    @s[1], @s[1], @t[0]
-
-       veor    @t[2], @s[2], @s[1]
-       veor    @t[1], @t[1], @s[1]
-
-       vand    @t[2], @t[2], @t[0]
+       vbsl    @s[0], @s[1], @s[2]
+       vbsl    @t[0], @s[2], @s[1]
 
-       veor    @s[2], @s[2], @t[2]
-       veor    @t[1], @t[1], @t[2]
+       vand    @s[2], @s[0], @s[3]
+       veor    @t[1], @t[1], @t[0]
 
-       vand    @s[2], @s[2], @s[3]
-
-       veor    @s[2], @s[2], @s[0]
+       veor    @s[2], @s[2], @t[3]
 ___
 # output in s3, s2, s1, t1
 
@@ -384,13 +383,13 @@ $code.=<<___;
         veor   @x[5], @x[5], @t[5]
        vext.8  @t[7], @x[7], @x[7], #12
         veor   @x[6], @x[6], @t[6]
-        veor   @x[7], @x[7], @t[7]
 
        veor    @t[1], @t[1], @x[0]
+        veor   @x[7], @x[7], @t[7]
         vext.8 @x[0], @x[0], @x[0], #8         @ (x0 ^ (x0 <<< 32)) <<< 64)
+       veor    @t[2], @t[2], @x[1]
        veor    @t[0], @t[0], @x[7]
        veor    @t[1], @t[1], @x[7]
-       veor    @t[2], @t[2], @x[1]
         vext.8 @x[1], @x[1], @x[1], #8
        veor    @t[5], @t[5], @x[4]
         veor   @x[0], @x[0], @t[0]
@@ -403,12 +402,12 @@ $code.=<<___;
         vext.8 @x[4], @x[3], @x[3], #8
        veor    @t[3], @t[3], @x[2]
         vext.8 @x[5], @x[7], @x[7], #8
-       veor    @t[3], @t[3], @x[7]
-        vext.8 @x[3], @x[6], @x[6], #8
        veor    @t[4], @t[4], @x[7]
+        vext.8 @x[3], @x[6], @x[6], #8
+       veor    @t[3], @t[3], @x[7]
         vext.8 @x[6], @x[2], @x[2], #8
-       veor    @x[2], @t[0], @t[4]
        veor    @x[7], @t[1], @t[5]
+       veor    @x[2], @t[0], @t[4]
 
        veor    @x[4], @x[4], @t[3]
        veor    @x[5], @x[5], @t[7]
@@ -427,8 +426,8 @@ $code.=<<___;
        @ multiplication by 0x0e
        vext.8  @t[7], @x[7], @x[7], #12
        vmov    @t[2], @x[2]
-       veor    @x[7], @x[7], @x[5]             @ 7 5
        veor    @x[2], @x[2], @x[5]             @ 2 5
+       veor    @x[7], @x[7], @x[5]             @ 7 5
        vext.8  @t[0], @x[0], @x[0], #12
        vmov    @t[5], @x[5]
        veor    @x[5], @x[5], @x[0]             @ 5 0           [1]
@@ -436,8 +435,8 @@ $code.=<<___;
        vext.8  @t[1], @x[1], @x[1], #12
        veor    @x[1], @x[1], @x[2]             @ 1 25
        veor    @x[0], @x[0], @x[6]             @ 01 6          [2]
-       veor    @x[1], @x[1], @x[3]             @ 125 3         [4]
        vext.8  @t[3], @x[3], @x[3], #12
+       veor    @x[1], @x[1], @x[3]             @ 125 3         [4]
        veor    @x[2], @x[2], @x[0]             @ 25 016        [3]
        veor    @x[3], @x[3], @x[7]             @ 3 75
        veor    @x[7], @x[7], @x[6]             @ 75 6          [0]
@@ -456,21 +455,21 @@ $code.=<<___;
        @ multiplication by 0x0b
        veor    @y[1], @y[1], @y[0]
        veor    @y[0], @y[0], @t[0]
-       veor    @y[1], @y[1], @t[1]
        vext.8  @t[2], @t[2], @t[2], #12
+       veor    @y[1], @y[1], @t[1]
        veor    @y[0], @y[0], @t[5]
+       vext.8  @t[4], @t[4], @t[4], #12
        veor    @y[1], @y[1], @t[6]
        veor    @y[0], @y[0], @t[7]
-       vext.8  @t[4], @t[4], @t[4], #12
        veor    @t[7], @t[7], @t[6]             @ clobber t[7]
-       veor    @y[1], @y[1], @y[0]
 
        veor    @y[3], @y[3], @t[0]
+        veor   @y[1], @y[1], @y[0]
        vext.8  @t[0], @t[0], @t[0], #12
        veor    @y[2], @y[2], @t[1]
        veor    @y[4], @y[4], @t[1]
-       veor    @y[2], @y[2], @t[2]
        vext.8  @t[1], @t[1], @t[1], #12
+       veor    @y[2], @y[2], @t[2]
        veor    @y[3], @y[3], @t[2]
        veor    @y[5], @y[5], @t[2]
        veor    @y[2], @y[2], @t[7]
@@ -478,13 +477,13 @@ $code.=<<___;
        veor    @y[3], @y[3], @t[3]
        veor    @y[6], @y[6], @t[3]
        veor    @y[4], @y[4], @t[3]
-       vext.8  @t[3], @t[3], @t[3], #12
        veor    @y[7], @y[7], @t[4]
+       vext.8  @t[3], @t[3], @t[3], #12
        veor    @y[5], @y[5], @t[4]
        veor    @y[7], @y[7], @t[7]
+       veor    @t[7], @t[7], @t[5]             @ clobber t[7] even more
        veor    @y[3], @y[3], @t[5]
        veor    @y[4], @y[4], @t[4]
-       veor    @t[7], @t[7], @t[5]             @ clobber t[7] even more
 
        veor    @y[5], @y[5], @t[7]
        vext.8  @t[4], @t[4], @t[4], #12
@@ -493,16 +492,16 @@ $code.=<<___;
 
        veor    @t[7], @t[7], @t[5]
        vext.8  @t[5], @t[5], @t[5], #12
-       veor    @t[7], @t[7], @t[6]             @ restore t[7]
 
        @ multiplication by 0x0d
        veor    @y[4], @y[4], @y[7]
+        veor   @t[7], @t[7], @t[6]             @ restore t[7]
        veor    @y[7], @y[7], @t[4]
        vext.8  @t[6], @t[6], @t[6], #12
        veor    @y[2], @y[2], @t[0]
        veor    @y[7], @y[7], @t[5]
-       veor    @y[2], @y[2], @t[2]
        vext.8  @t[7], @t[7], @t[7], #12
+       veor    @y[2], @y[2], @t[2]
 
        veor    @y[3], @y[3], @y[1]
        veor    @y[1], @y[1], @t[1]
@@ -510,8 +509,8 @@ $code.=<<___;
        veor    @y[3], @y[3], @t[0]
        veor    @y[1], @y[1], @t[5]
        veor    @y[0], @y[0], @t[5]
-       veor    @y[1], @y[1], @t[7]
        vext.8  @t[0], @t[0], @t[0], #12
+       veor    @y[1], @y[1], @t[7]
        veor    @y[0], @y[0], @t[6]
        veor    @y[3], @y[3], @y[1]
        veor    @y[4], @y[4], @t[1]
@@ -520,9 +519,9 @@ $code.=<<___;
        veor    @y[7], @y[7], @t[7]
        veor    @y[4], @y[4], @t[2]
        veor    @y[5], @y[5], @t[2]
-       vext.8  @t[2], @t[2], @t[2], #12
        veor    @y[2], @y[2], @t[6]
        veor    @t[6], @t[6], @t[3]             @ clobber t[6]
+       vext.8  @t[2], @t[2], @t[2], #12
        veor    @y[4], @y[4], @y[7]
        veor    @y[3], @y[3], @t[6]
 
@@ -549,13 +548,13 @@ $code.=<<___;
        veor    @t[6], @t[6], @t[7]             @ clobber t[6]
        veor    @y[4], @y[4], @t[1]
        veor    @y[7], @y[7], @t[4]
-       veor    @t[4], @t[4], @y[4]             @ t[4]=y[4]
        veor    @y[6], @y[6], @t[3]
-       veor    @t[3], @t[3], @y[3]             @ t[3]=y[3]
        veor    @y[5], @y[5], @t[2]
+       veor    @t[4], @t[4], @y[4]             @ t[4]=y[4]
+       veor    @t[3], @t[3], @y[3]             @ t[3]=y[3]
+       veor    @t[5], @t[5], @y[5]             @ t[5]=y[5]
        veor    @t[2], @t[2], @y[2]             @ t[2]=y[2]
        veor    @t[3], @t[3], @t[7]
-       veor    @t[5], @t[5], @y[5]             @ t[5]=y[5]
        veor    @XMM[5], @t[5], @t[6]
        veor    @XMM[6], @t[6], @y[6]           @ t[6]=y[6]
        veor    @XMM[2], @t[2], @t[6]
@@ -621,6 +620,9 @@ ___
 }
 
 $code.=<<___;
+#include "arm_arch.h"
+
+#if __ARM_ARCH__>=7
 .text
 .code  32
 .fpu   neon
@@ -713,6 +715,10 @@ _bsaes_const:
        .quad   0x0304090e00050a0f, 0x01060b0c0207080d
 .LM0:
        .quad   0x02060a0e03070b0f, 0x0004080c0105090d
+.LREVM0SR:
+       .quad   0x090d02060c030708, 0x00040b0f050a0e01
+.Lxts_magic:
+       .long   1, 0, 0x87, 0
 .asciz "Bit-sliced AES for NEON, CRYPTOGAMS by <appro\@openssl.org>"
 .align 6
 .size  _bsaes_const,.-_bsaes_const
@@ -725,6 +731,7 @@ _bsaes_encrypt8:
        sub     $const,$const,#_bsaes_encrypt8-.LM0SR
 
        vldmia  $const!, {@XMM[8]}              @ .LM0SR
+_bsaes_encrypt8_alt:
        veor    @XMM[10], @XMM[0], @XMM[9]      @ xor with round0 key
        veor    @XMM[11], @XMM[1], @XMM[9]
         vtbl.8 `&Dlo(@XMM[0])`, {@XMM[10]}, `&Dlo(@XMM[8])`
@@ -827,10 +834,13 @@ _bsaes_key_convert:
        sub     $const,$const,#_bsaes_key_convert-.LM0
        vld1.8  {@XMM[15]}, [$inp]!             @ load round 1 key
 
-       vmov.i8 @XMM[8], #0x55                  @ compose .LBS0
-       vmov.i8 @XMM[9], #0x33                  @ compose .LBS1
-       vmov.i8 @XMM[10],#0x0f                  @ compose .LBS2
-       vldmia  $const, {@XMM[13]}              @ .LM0
+       vmov.i8 @XMM[8],  #0x01                 @ bit masks
+       vmov.i8 @XMM[9],  #0x02
+       vmov.i8 @XMM[10], #0x04
+       vmov.i8 @XMM[11], #0x08
+       vmov.i8 @XMM[12], #0x10
+       vmov.i8 @XMM[13], #0x20
+       vldmia  $const, {@XMM[14]}              @ .LM0
 
 #ifdef __ARMEL__
        vrev32.8        @XMM[7],  @XMM[7]
@@ -842,17 +852,24 @@ _bsaes_key_convert:
 
 .align 4
 .Lkey_loop:
-       vtbl.8  `&Dlo(@XMM[6])`,{@XMM[15]},`&Dlo(@XMM[13])`
-       vtbl.8  `&Dhi(@XMM[6])`,{@XMM[15]},`&Dhi(@XMM[13])`
-       vmov    @XMM[7], @XMM[6]
-___
-       &bitslice_key   (@XMM[0..7, 8..12]);
-$code.=<<___;
+       vtbl.8  `&Dlo(@XMM[7])`,{@XMM[15]},`&Dlo(@XMM[14])`
+       vtbl.8  `&Dhi(@XMM[7])`,{@XMM[15]},`&Dhi(@XMM[14])`
+       vmov.i8 @XMM[6],  #0x40
+       vmov.i8 @XMM[15], #0x80
+
+       vtst.8  @XMM[0], @XMM[7], @XMM[8]
+       vtst.8  @XMM[1], @XMM[7], @XMM[9]
+       vtst.8  @XMM[2], @XMM[7], @XMM[10]
+       vtst.8  @XMM[3], @XMM[7], @XMM[11]
+       vtst.8  @XMM[4], @XMM[7], @XMM[12]
+       vtst.8  @XMM[5], @XMM[7], @XMM[13]
+       vtst.8  @XMM[6], @XMM[7], @XMM[6]
+       vtst.8  @XMM[7], @XMM[7], @XMM[15]
        vld1.8  {@XMM[15]}, [$inp]!             @ load next round key
-       vmvn    @XMM[5], @XMM[5]                @ "pnot"
-       vmvn    @XMM[6], @XMM[6]
-       vmvn    @XMM[0], @XMM[0]
+       vmvn    @XMM[0], @XMM[0]                @ "pnot"
        vmvn    @XMM[1], @XMM[1]
+       vmvn    @XMM[5], @XMM[5]
+       vmvn    @XMM[6], @XMM[6]
 #ifdef __ARMEL__
        vrev32.8        @XMM[15], @XMM[15]
 #endif
@@ -867,7 +884,7 @@ $code.=<<___;
 ___
 }
 
-if (1) {               # following four functions are unsupported interface
+if (0) {               # following four functions are unsupported interface
                        # used for benchmarking...
 $code.=<<___;
 .globl bsaes_enc_key_convert
@@ -895,21 +912,16 @@ bsaes_encrypt_128:
        stmdb   sp!,{r4-r6,lr}
        vstmdb  sp!,{d8-d15}            @ ABI specification says so
 .Lenc128_loop:
-       vld1.8  {@XMM[0]}, [$inp]!              @ load input
-       vld1.8  {@XMM[1]}, [$inp]!
-       vld1.8  {@XMM[2]}, [$inp]!
-       vld1.8  {@XMM[3]}, [$inp]!
-       vld1.8  {@XMM[4]}, [$inp]!
-       vld1.8  {@XMM[5]}, [$inp]!
+       vld1.8  {@XMM[0]-@XMM[1]}, [$inp]!      @ load input
+       vld1.8  {@XMM[2]-@XMM[3]}, [$inp]!
        mov     r4,$key                         @ pass the key
-       vld1.8  {@XMM[6]}, [$inp]!
+       vld1.8  {@XMM[4]-@XMM[5]}, [$inp]!
        mov     r5,#10                          @ pass rounds
-       vld1.8  {@XMM[7]}, [$inp]!
+       vld1.8  {@XMM[6]-@XMM[7]}, [$inp]!
 
        bl      _bsaes_encrypt8
 
-       vst1.8  {@XMM[0]}, [$out]!              @ write output
-       vst1.8  {@XMM[1]}, [$out]!
+       vst1.8  {@XMM[0]-@XMM[1]}, [$out]!      @ write output
        vst1.8  {@XMM[4]}, [$out]!
        vst1.8  {@XMM[6]}, [$out]!
        vst1.8  {@XMM[3]}, [$out]!
@@ -950,21 +962,16 @@ bsaes_decrypt_128:
        stmdb   sp!,{r4-r6,lr}
        vstmdb  sp!,{d8-d15}            @ ABI specification says so
 .Ldec128_loop:
-       vld1.8  {@XMM[0]}, [$inp]!              @ load input
-       vld1.8  {@XMM[1]}, [$inp]!
-       vld1.8  {@XMM[2]}, [$inp]!
-       vld1.8  {@XMM[3]}, [$inp]!
-       vld1.8  {@XMM[4]}, [$inp]!
-       vld1.8  {@XMM[5]}, [$inp]!
+       vld1.8  {@XMM[0]-@XMM[1]}, [$inp]!      @ load input
+       vld1.8  {@XMM[2]-@XMM[3]}, [$inp]!
        mov     r4,$key                         @ pass the key
-       vld1.8  {@XMM[6]}, [$inp]!
+       vld1.8  {@XMM[4]-@XMM[5]}, [$inp]!
        mov     r5,#10                          @ pass rounds
-       vld1.8  {@XMM[7]}, [$inp]!
+       vld1.8  {@XMM[6]-@XMM[7]}, [$inp]!
 
        bl      _bsaes_decrypt8
 
-       vst1.8  {@XMM[0]}, [$out]!              @ write output
-       vst1.8  {@XMM[1]}, [$out]!
+       vst1.8  {@XMM[0]-@XMM[1]}, [$out]!      @ write output
        vst1.8  {@XMM[6]}, [$out]!
        vst1.8  {@XMM[4]}, [$out]!
        vst1.8  {@XMM[2]}, [$out]!
@@ -979,6 +986,1153 @@ bsaes_decrypt_128:
 .size  bsaes_decrypt_128,.-bsaes_decrypt_128
 ___
 }
+{
+my ($inp,$out,$len,$key, $ivp,$fp,$rounds)=map("r$_",(0..3,8..10));
+my ($keysched)=("sp");
+
+$code.=<<___;
+.extern AES_cbc_encrypt
+.extern AES_decrypt
+
+.global        bsaes_cbc_encrypt
+.type  bsaes_cbc_encrypt,%function
+.align 5
+bsaes_cbc_encrypt:
+       cmp     $len, #128
+       blo     AES_cbc_encrypt
+
+       @ it is up to the caller to make sure we are called with enc == 0
+
+       stmdb   sp!, {r4-r10, lr}
+       vstmdb  sp!, {d8-d15}                   @ ABI specification says so
+       ldr     $ivp, [sp, #0x60]               @ IV is 1st arg on the stack
+       mov     $len, $len, lsr#4               @ len in 16 byte blocks
+       sub     sp, #0x10                       @ scratch space to carry over the IV
+       mov     $fp, sp                         @ save sp
+
+       @ allocate the key schedule on the stack
+       ldr     $rounds, [$key, #240]           @ get # of rounds
+       sub     sp, sp, $rounds, lsl#7          @ 128 bytes per inner round key
+       add     sp, sp, #`128-32`               @ size of bit-sliced key schedule
+
+       @ populate the key schedule
+       mov     r4, $key                        @ pass key
+       mov     r5, $rounds                     @ pass # of rounds
+       mov     r12, $keysched                  @ pass key schedule
+       bl      _bsaes_key_convert
+       vldmia  $keysched, {@XMM[6]}
+       vstmia  r12,  {@XMM[15]}                @ save last round key
+       veor    @XMM[7], @XMM[7], @XMM[6]       @ fix up round 0 key
+       vstmia  $keysched, {@XMM[7]}
+
+       vld1.8  {@XMM[15]}, [$ivp]              @ load IV
+       b       .Lcbc_dec_loop
+
+.align 4
+.Lcbc_dec_loop:
+       subs    $len, $len, #0x8
+       bmi     .Lcbc_dec_loop_finish
+
+       vld1.8  {@XMM[0]-@XMM[1]}, [$inp]!      @ load input
+       vld1.8  {@XMM[2]-@XMM[3]}, [$inp]!
+       mov     r4, $keysched                   @ pass the key
+       vld1.8  {@XMM[4]-@XMM[5]}, [$inp]!
+       mov     r5, $rounds
+       vld1.8  {@XMM[6]-@XMM[7]}, [$inp]
+       sub     $inp, $inp, #0x60
+       vstmia  $fp, {@XMM[15]}                 @ put aside IV
+
+       bl      _bsaes_decrypt8
+
+       vldmia  $fp, {@XMM[14]}                 @ reload IV
+       vld1.8  {@XMM[8]-@XMM[9]}, [$inp]!      @ reload input
+       veor    @XMM[0], @XMM[0], @XMM[14]      @ ^= IV
+       vld1.8  {@XMM[10]-@XMM[11]}, [$inp]!
+       veor    @XMM[1], @XMM[1], @XMM[8]
+       veor    @XMM[6], @XMM[6], @XMM[9]
+       vld1.8  {@XMM[12]-@XMM[13]}, [$inp]!
+       veor    @XMM[4], @XMM[4], @XMM[10]
+       veor    @XMM[2], @XMM[2], @XMM[11]
+       vld1.8  {@XMM[14]-@XMM[15]}, [$inp]!
+       veor    @XMM[7], @XMM[7], @XMM[12]
+       vst1.8  {@XMM[0]-@XMM[1]}, [$out]!      @ write output
+       veor    @XMM[3], @XMM[3], @XMM[13]
+       vst1.8  {@XMM[6]}, [$out]!
+       veor    @XMM[5], @XMM[5], @XMM[14]
+       vst1.8  {@XMM[4]}, [$out]!
+       vst1.8  {@XMM[2]}, [$out]!
+       vst1.8  {@XMM[7]}, [$out]!
+       vst1.8  {@XMM[3]}, [$out]!
+       vst1.8  {@XMM[5]}, [$out]!
+
+       b       .Lcbc_dec_loop
+
+.Lcbc_dec_loop_finish:
+       adds    $len, $len, #8
+       beq     .Lcbc_dec_done
+
+       vld1.8  {@XMM[0]}, [$inp]!              @ load input
+       cmp     $len, #2
+       blo     .Lcbc_dec_one
+       vld1.8  {@XMM[1]}, [$inp]!
+       mov     r4, $keysched                   @ pass the key
+       mov     r5, $rounds
+       vstmia  $fp, {@XMM[15]}                 @ put aside IV
+       beq     .Lcbc_dec_two
+       vld1.8  {@XMM[2]}, [$inp]!
+       cmp     $len, #4
+       blo     .Lcbc_dec_three
+       vld1.8  {@XMM[3]}, [$inp]!
+       beq     .Lcbc_dec_four
+       vld1.8  {@XMM[4]}, [$inp]!
+       cmp     $len, #6
+       blo     .Lcbc_dec_five
+       vld1.8  {@XMM[5]}, [$inp]!
+       beq     .Lcbc_dec_six
+       vld1.8  {@XMM[6]}, [$inp]!
+       sub     $inp, $inp, #0x70
+
+       bl      _bsaes_decrypt8
+
+       vldmia  $fp, {@XMM[14]}                 @ reload IV
+       vld1.8  {@XMM[8]-@XMM[9]}, [$inp]!      @ reload input
+       veor    @XMM[0], @XMM[0], @XMM[14]      @ ^= IV
+       vld1.8  {@XMM[10]-@XMM[11]}, [$inp]!
+       veor    @XMM[1], @XMM[1], @XMM[8]
+       veor    @XMM[6], @XMM[6], @XMM[9]
+       vld1.8  {@XMM[12]-@XMM[13]}, [$inp]!
+       veor    @XMM[4], @XMM[4], @XMM[10]
+       veor    @XMM[2], @XMM[2], @XMM[11]
+       vld1.8  {@XMM[15]}, [$inp]!
+       veor    @XMM[7], @XMM[7], @XMM[12]
+       vst1.8  {@XMM[0]-@XMM[1]}, [$out]!      @ write output
+       veor    @XMM[3], @XMM[3], @XMM[13]
+       vst1.8  {@XMM[6]}, [$out]!
+       vst1.8  {@XMM[4]}, [$out]!
+       vst1.8  {@XMM[2]}, [$out]!
+       vst1.8  {@XMM[7]}, [$out]!
+       vst1.8  {@XMM[3]}, [$out]!
+       b       .Lcbc_dec_done
+.align 4
+.Lcbc_dec_six:
+       sub     $inp, $inp, #0x60
+       bl      _bsaes_decrypt8
+       vldmia  $fp,{@XMM[14]}                  @ reload IV
+       vld1.8  {@XMM[8]-@XMM[9]}, [$inp]!      @ reload input
+       veor    @XMM[0], @XMM[0], @XMM[14]      @ ^= IV
+       vld1.8  {@XMM[10]-@XMM[11]}, [$inp]!
+       veor    @XMM[1], @XMM[1], @XMM[8]
+       veor    @XMM[6], @XMM[6], @XMM[9]
+       vld1.8  {@XMM[12]}, [$inp]!
+       veor    @XMM[4], @XMM[4], @XMM[10]
+       veor    @XMM[2], @XMM[2], @XMM[11]
+       vld1.8  {@XMM[15]}, [$inp]!
+       veor    @XMM[7], @XMM[7], @XMM[12]
+       vst1.8  {@XMM[0]-@XMM[1]}, [$out]!      @ write output
+       vst1.8  {@XMM[6]}, [$out]!
+       vst1.8  {@XMM[4]}, [$out]!
+       vst1.8  {@XMM[2]}, [$out]!
+       vst1.8  {@XMM[7]}, [$out]!
+       b       .Lcbc_dec_done
+.align 4
+.Lcbc_dec_five:
+       sub     $inp, $inp, #0x50
+       bl      _bsaes_decrypt8
+       vldmia  $fp, {@XMM[14]}                 @ reload IV
+       vld1.8  {@XMM[8]-@XMM[9]}, [$inp]!      @ reload input
+       veor    @XMM[0], @XMM[0], @XMM[14]      @ ^= IV
+       vld1.8  {@XMM[10]-@XMM[11]}, [$inp]!
+       veor    @XMM[1], @XMM[1], @XMM[8]
+       veor    @XMM[6], @XMM[6], @XMM[9]
+       vld1.8  {@XMM[15]}, [$inp]!
+       veor    @XMM[4], @XMM[4], @XMM[10]
+       vst1.8  {@XMM[0]-@XMM[1]}, [$out]!      @ write output
+       veor    @XMM[2], @XMM[2], @XMM[11]
+       vst1.8  {@XMM[6]}, [$out]!
+       vst1.8  {@XMM[4]}, [$out]!
+       vst1.8  {@XMM[2]}, [$out]!
+       b       .Lcbc_dec_done
+.align 4
+.Lcbc_dec_four:
+       sub     $inp, $inp, #0x40
+       bl      _bsaes_decrypt8
+       vldmia  $fp, {@XMM[14]}                 @ reload IV
+       vld1.8  {@XMM[8]-@XMM[9]}, [$inp]!      @ reload input
+       veor    @XMM[0], @XMM[0], @XMM[14]      @ ^= IV
+       vld1.8  {@XMM[10]}, [$inp]!
+       veor    @XMM[1], @XMM[1], @XMM[8]
+       veor    @XMM[6], @XMM[6], @XMM[9]
+       vld1.8  {@XMM[15]}, [$inp]!
+       veor    @XMM[4], @XMM[4], @XMM[10]
+       vst1.8  {@XMM[0]-@XMM[1]}, [$out]!      @ write output
+       vst1.8  {@XMM[6]}, [$out]!
+       vst1.8  {@XMM[4]}, [$out]!
+       b       .Lcbc_dec_done
+.align 4
+.Lcbc_dec_three:
+       sub     $inp, $inp, #0x30
+       bl      _bsaes_decrypt8
+       vldmia  $fp, {@XMM[14]}                 @ reload IV
+       vld1.8  {@XMM[8]-@XMM[9]}, [$inp]!      @ reload input
+       veor    @XMM[0], @XMM[0], @XMM[14]      @ ^= IV
+       vld1.8  {@XMM[15]}, [$inp]!
+       veor    @XMM[1], @XMM[1], @XMM[8]
+       veor    @XMM[6], @XMM[6], @XMM[9]
+       vst1.8  {@XMM[0]-@XMM[1]}, [$out]!      @ write output
+       vst1.8  {@XMM[6]}, [$out]!
+       b       .Lcbc_dec_done
+.align 4
+.Lcbc_dec_two:
+       sub     $inp, $inp, #0x20
+       bl      _bsaes_decrypt8
+       vldmia  $fp, {@XMM[14]}                 @ reload IV
+       vld1.8  {@XMM[8]}, [$inp]!              @ reload input
+       veor    @XMM[0], @XMM[0], @XMM[14]      @ ^= IV
+       vld1.8  {@XMM[15]}, [$inp]!             @ reload input
+       veor    @XMM[1], @XMM[1], @XMM[8]
+       vst1.8  {@XMM[0]-@XMM[1]}, [$out]!      @ write output
+       b       .Lcbc_dec_done
+.align 4
+.Lcbc_dec_one:
+       sub     $inp, $inp, #0x10
+       mov     $rounds, $out                   @ save original out pointer
+       mov     $out, $fp                       @ use the iv scratch space as out buffer
+       mov     r2, $key
+       vmov    @XMM[4],@XMM[15]                @ just in case ensure that IV
+       vmov    @XMM[5],@XMM[0]                 @ and input are preserved
+       bl      AES_decrypt
+       vld1.8  {@XMM[0]}, [$fp,:64]            @ load result
+       veor    @XMM[0], @XMM[0], @XMM[4]       @ ^= IV
+       vmov    @XMM[15], @XMM[5]               @ @XMM[5] holds input
+       vst1.8  {@XMM[0]}, [$rounds]            @ write output
+
+.Lcbc_dec_done:
+       vmov.i32        q0, #0
+       vmov.i32        q1, #0
+.Lcbc_dec_bzero:                               @ wipe key schedule [if any]
+       vstmia          $keysched!, {q0-q1}
+       teq             $keysched, $fp
+       bne             .Lcbc_dec_bzero
+
+       add     sp, $fp, #0x10
+       vst1.8  {@XMM[15]}, [$ivp]              @ return IV
+       vldmia  sp!, {d8-d15}
+       ldmia   sp!, {r4-r10, pc}
+.size  bsaes_cbc_encrypt,.-bsaes_cbc_encrypt
+___
+}
+{
+my ($inp,$out,$len,$key, $ctr,$fp,$rounds)=(map("r$_",(0..3,8..10)));
+my $const = "r6";      # shared with _bsaes_encrypt8_alt
+my $keysched = "sp";
+
+$code.=<<___;
+.extern        AES_encrypt
+.global        bsaes_ctr32_encrypt_blocks
+.type  bsaes_ctr32_encrypt_blocks,%function
+.align 5
+bsaes_ctr32_encrypt_blocks:
+       cmp     $len, #8                        @ use plain AES for
+       blo     .Lctr_enc_short                 @ small sizes
+
+       stmdb   sp!, {r4-r10, lr}
+       vstmdb  sp!, {d8-d15}                   @ ABI specification says so
+       ldr     $ctr, [sp, #0x60]               @ ctr is 1st arg on the stack
+       sub     sp, sp, #0x10                   @ scratch space to carry over the ctr
+       mov     $fp, sp                         @ save sp
+
+       @ allocate the key schedule on the stack
+       ldr     $rounds, [$key, #240]           @ get # of rounds
+       sub     sp, sp, $rounds, lsl#7          @ 128 bytes per inner round key
+       add     sp, sp, #`128-32`               @ size of bit-sliced key schedule
+
+       @ populate the key schedule
+       mov     r4, $key                        @ pass key
+       mov     r5, $rounds                     @ pass # of rounds
+       mov     r12, $keysched                  @ pass key schedule
+       bl      _bsaes_key_convert
+       veor    @XMM[7],@XMM[7],@XMM[15]        @ fix up last round key
+       vstmia  r12, {@XMM[7]}                  @ save last round key
+
+       vld1.8  {@XMM[0]}, [$ctr]               @ load counter
+       add     $ctr, $const, #.LREVM0SR-.LM0   @ borrow $ctr
+       vldmia  $keysched, {@XMM[4]}            @ load round0 key
+
+       vmov.i32        `&Dhi("@XMM[8]")`,#1    @ compose 1<<96
+       vmov.i32        `&Dlo("@XMM[8]")`,#0
+       vrev32.8        `&Dhi("@XMM[0]")`,`&Dhi("@XMM[0]")`
+       vshl.u64        `&Dhi("@XMM[8]")`,#32
+       vrev32.8        `&Dhi("@XMM[4]")`,`&Dhi("@XMM[4]")`
+       vadd.u32        @XMM[9],@XMM[8],@XMM[8] @ compose 2<<96
+       vstmia  $keysched, {@XMM[4]}            @ save adjusted round0 key
+       b       .Lctr_enc_loop
+
+.align 4
+.Lctr_enc_loop:
+       vadd.u32        @XMM[10], @XMM[8], @XMM[9]      @ compose 3<<96
+       vadd.u32        @XMM[1], @XMM[0], @XMM[8]       @ +1
+       vadd.u32        @XMM[2], @XMM[0], @XMM[9]       @ +2
+       vadd.u32        @XMM[3], @XMM[0], @XMM[10]      @ +3
+       vadd.u32        @XMM[4], @XMM[1], @XMM[10]
+       vadd.u32        @XMM[5], @XMM[2], @XMM[10]
+       vadd.u32        @XMM[6], @XMM[3], @XMM[10]
+       vadd.u32        @XMM[7], @XMM[4], @XMM[10]
+       vadd.u32        @XMM[10], @XMM[5], @XMM[10]     @ next counter
+
+       @ Borrow prologue from _bsaes_encrypt8 to use the opportunity
+       @ to flip byte order in 32-bit counter
+
+       vldmia          $keysched, {@XMM[9]}            @ load round0 key
+       add             r4, $keysched, #0x10            @ pass next round key
+       vldmia          $ctr, {@XMM[8]}                 @ .LREVM0SR
+       mov             r5, $rounds                     @ pass rounds
+       vstmia          $fp, {@XMM[10]}                 @ save next counter
+       sub             $const, $ctr, #.LREVM0SR-.LSR   @ pass constants
+
+       bl              _bsaes_encrypt8_alt
+
+       subs            $len, $len, #8
+       blo             .Lctr_enc_loop_done
+
+       vld1.8          {@XMM[8]-@XMM[9]}, [$inp]!      @ load input
+       vld1.8          {@XMM[10]-@XMM[11]}, [$inp]!
+       veor            @XMM[0], @XMM[8]
+       veor            @XMM[1], @XMM[9]
+       vld1.8          {@XMM[12]-@XMM[13]}, [$inp]!
+       veor            @XMM[4], @XMM[10]
+       veor            @XMM[6], @XMM[11]
+       vld1.8          {@XMM[14]-@XMM[15]}, [$inp]!
+       veor            @XMM[3], @XMM[12]
+       vst1.8          {@XMM[0]-@XMM[1]}, [$out]!      @ write output
+       veor            @XMM[7], @XMM[13]
+       veor            @XMM[2], @XMM[14]
+       vst1.8          {@XMM[4]}, [$out]!
+       veor            @XMM[5], @XMM[15]
+       vst1.8          {@XMM[6]}, [$out]!
+       vmov.i32        `&Dhi("@XMM[8]")`,#1            @ compose 1<<96
+       vst1.8          {@XMM[3]}, [$out]!
+       vmov.i32        `&Dlo("@XMM[8]")`,#0
+       vst1.8          {@XMM[7]}, [$out]!
+       vshl.u64        `&Dhi("@XMM[8]")`,#32
+       vst1.8          {@XMM[2]}, [$out]!
+       vadd.u32        @XMM[9],@XMM[8],@XMM[8]         @ compose 2<<96
+       vst1.8          {@XMM[5]}, [$out]!
+       vldmia          $fp, {@XMM[0]}                  @ load counter
+
+       bne             .Lctr_enc_loop
+       b               .Lctr_enc_done
+
+.align 4
+.Lctr_enc_loop_done:
+       add             $len, $len, #8
+       vld1.8          {@XMM[8]}, [$inp]!      @ load input
+       veor            @XMM[0], @XMM[8]
+       vst1.8          {@XMM[0]}, [$out]!      @ write output
+       cmp             $len, #2
+       blo             .Lctr_enc_done
+       vld1.8          {@XMM[9]}, [$inp]!
+       veor            @XMM[1], @XMM[9]
+       vst1.8          {@XMM[1]}, [$out]!
+       beq             .Lctr_enc_done
+       vld1.8          {@XMM[10]}, [$inp]!
+       veor            @XMM[4], @XMM[10]
+       vst1.8          {@XMM[4]}, [$out]!
+       cmp             $len, #4
+       blo             .Lctr_enc_done
+       vld1.8          {@XMM[11]}, [$inp]!
+       veor            @XMM[6], @XMM[11]
+       vst1.8          {@XMM[6]}, [$out]!
+       beq             .Lctr_enc_done
+       vld1.8          {@XMM[12]}, [$inp]!
+       veor            @XMM[3], @XMM[12]
+       vst1.8          {@XMM[3]}, [$out]!
+       cmp             $len, #6
+       blo             .Lctr_enc_done
+       vld1.8          {@XMM[13]}, [$inp]!
+       veor            @XMM[7], @XMM[13]
+       vst1.8          {@XMM[7]}, [$out]!
+       beq             .Lctr_enc_done
+       vld1.8          {@XMM[14]}, [$inp]
+       veor            @XMM[2], @XMM[14]
+       vst1.8          {@XMM[2]}, [$out]!
+
+.Lctr_enc_done:
+       vmov.i32        q0, #0
+       vmov.i32        q1, #0
+.Lctr_enc_bzero:                       @ wipe key schedule [if any]
+       vstmia          $keysched!, {q0-q1}
+       teq             $keysched, $fp
+       bne             .Lctr_enc_bzero
+
+       add     sp, $fp, #0x10
+       vldmia  sp!, {d8-d15}
+       ldmia   sp!, {r4-r10, pc}       @ return
+
+.align 4
+.Lctr_enc_short:
+       ldr     ip, [sp]                @ ctr pointer is passed on stack
+       stmdb   sp!, {r4-r8, lr}
+
+       mov     r4, $inp                @ copy arguments
+       mov     r5, $out
+       mov     r6, $len
+       mov     r7, $key
+       ldr     r8, [ip, #12]           @ load counter LSW
+       vld1.8  {@XMM[1]}, [ip]         @ load whole counter value
+#ifdef __ARMEL__
+       rev     r8, r8
+#endif
+       sub     sp, sp, #0x10
+       vst1.8  {@XMM[1]}, [sp,:64]     @ copy counter value
+       sub     sp, sp, #0x10
+
+.Lctr_enc_short_loop:
+       add     r0, sp, #0x10           @ input counter value
+       mov     r1, sp                  @ output on the stack
+       mov     r2, r7                  @ key
+
+       bl      AES_encrypt
+
+       vld1.8  {@XMM[0]}, [r4]!        @ load input
+       vld1.8  {@XMM[1]}, [sp,:64]     @ load encrypted counter
+       add     r8, r8, #1
+#ifdef __ARMEL__
+       rev     r0, r8
+       str     r0, [sp, #0x1c]         @ next counter value
+#else
+       str     r8, [sp, #0x1c]         @ next counter value
+#endif
+       veor    @XMM[0],@XMM[0],@XMM[1]
+       vst1.8  {@XMM[0]}, [r5]!        @ store output
+       subs    r6, r6, #1
+       bne     .Lctr_enc_short_loop
+
+       add     sp, sp, #0x20
+       ldmia   sp!, {r4-r8, pc}
+.size  bsaes_ctr32_encrypt_blocks,.-bsaes_ctr32_encrypt_blocks
+___
+}
+{
+######################################################################
+# void bsaes_xts_[en|de]crypt(const char *inp,char *out,size_t len,
+#      const AES_KEY *key1, const AES_KEY *key2,
+#      const unsigned char iv[16]);
+#
+my ($inp,$out,$len,$key,$rounds,$keysched,$magic,$fp)=(map("r$_",(7..11,1..3)));
+my $twmask=@XMM[5];
+my @T=@XMM[6..7];
+
+$code.=<<___;
+.globl bsaes_xts_encrypt
+.type  bsaes_xts_encrypt,%function
+.align 4
+bsaes_xts_encrypt:
+       stmdb   sp!, {r4-r11, lr}               @ 0x24
+       vstmdb  sp!, {d8-d15}                   @ 0x40
+       sub     sp, #0x14                       @ 0x14
+
+       mov     $inp, r0
+       mov     $out, r1
+       mov     $len, r2
+       mov     $key, r3
+
+       @ generate initial tweak
+       ldr     r0, [sp, #0x7c]                 @ iv[]
+       mov     r1, sp
+       ldr     r2, [sp, #0x78]                 @ key2
+       bl      AES_encrypt
+
+       @ allocate the key schedule on the stack
+       ldr     $rounds, [$key, #240]           @ get # of rounds
+       mov     $fp, sp
+       sub     sp, sp, $rounds, lsl#7          @ 128 bytes per inner round key
+       add     sp, sp, #`128-32`               @ size of bit-sliced key schedule
+       mov     $keysched, sp
+
+       @ populate the key schedule
+       mov     r4, $key                        @ pass key
+       mov     r5, $rounds                     @ pass # of rounds
+       mov     r12, $keysched                  @ pass key schedule
+       bl      _bsaes_key_convert
+       veor    @XMM[7], @XMM[7], @XMM[15]      @ fix up last round key
+       vstmia  r12, {@XMM[7]}                  @ save last round key
+
+       sub     sp, #0x80                       @ place for tweak[8]
+       bic     sp, #0x8                        @ align at 16 bytes
+
+       vld1.8  {@XMM[8]}, [$fp]                @ initial tweak
+       adrl    $magic, .Lxts_magic
+
+       subs    $len, #0x80
+       blo     .Lxts_enc_short
+       b       .Lxts_enc_loop
+
+.align 4
+.Lxts_enc_loop:
+       vld1.8          {$twmask}, [$magic]     @ load XTS magic
+       vshr.s64        @T[0], @XMM[8], #63
+       mov             r0, sp
+       vand            @T[0], @T[0], $twmask
+___
+for($i=9;$i<16;$i++) {
+$code.=<<___;
+       vadd.u64        @XMM[$i], @XMM[$i-1], @XMM[$i-1]
+       vst1.8          {@XMM[$i-1]}, [r0,:128]!
+       vswp            `&Dhi("@T[0]")`,`&Dlo("@T[0]")`
+       vshr.s64        @T[1], @XMM[$i], #63
+       veor            @XMM[$i], @XMM[$i], @T[0]
+       vand            @T[1], @T[1], $twmask
+___
+       @T=reverse(@T);
+
+$code.=<<___ if ($i>=10);
+       vld1.8          {@XMM[$i-10]}, [$inp]!
+___
+$code.=<<___ if ($i>=11);
+       veor            @XMM[$i-11], @XMM[$i-11], @XMM[$i-3]
+___
+}
+$code.=<<___;
+       vadd.u64        @XMM[8], @XMM[15], @XMM[15]
+       vst1.8          {@XMM[15]}, [r0,:128]
+       vswp            `&Dhi("@T[0]")`,`&Dlo("@T[0]")`
+       veor            @XMM[8], @XMM[8], @T[0]
+       vst1.8          {@XMM[8]}, [$fp]                @ next round tweak
+
+       vld1.8          {@XMM[6]-@XMM[7]}, [$inp]!
+       veor            @XMM[5], @XMM[5], @XMM[13]
+       mov             r4, $keysched
+       veor            @XMM[6], @XMM[6], @XMM[14]
+       mov             r5, $rounds                     @ pass rounds
+       veor            @XMM[7], @XMM[7], @XMM[15]
+       mov             r0, sp
+
+       bl              _bsaes_encrypt8
+
+       vld1.8          {@XMM[ 8]-@XMM[ 9]}, [r0,:128]!
+       vld1.8          {@XMM[10]-@XMM[11]}, [r0,:128]!
+       veor            @XMM[0], @XMM[0], @XMM[ 8]
+       vld1.8          {@XMM[12]-@XMM[13]}, [r0,:128]!
+       veor            @XMM[1], @XMM[1], @XMM[ 9]
+       veor            @XMM[8], @XMM[4], @XMM[10]
+       vst1.8          {@XMM[0]-@XMM[1]}, [$out]!
+       veor            @XMM[9], @XMM[6], @XMM[11]
+       vld1.8          {@XMM[14]-@XMM[15]}, [r0,:128]!
+       veor            @XMM[10], @XMM[3], @XMM[12]
+       vst1.8          {@XMM[8]-@XMM[9]}, [$out]!
+       veor            @XMM[11], @XMM[7], @XMM[13]
+       veor            @XMM[12], @XMM[2], @XMM[14]
+       vst1.8          {@XMM[10]-@XMM[11]}, [$out]!
+       veor            @XMM[13], @XMM[5], @XMM[15]
+       vst1.8          {@XMM[12]-@XMM[13]}, [$out]!
+
+       vld1.8          {@XMM[8]}, [$fp]                @ next round tweak
+
+       subs            $len, #0x80
+       bpl             .Lxts_enc_loop
+
+.Lxts_enc_short:
+       adds            $len, #0x70
+       bmi             .Lxts_enc_done
+
+       vld1.8          {$twmask}, [$magic]     @ load XTS magic
+       vshr.s64        @T[0], @XMM[8], #63
+       mov             r0, sp
+       vand            @T[0], @T[0], $twmask
+___
+for($i=9;$i<16;$i++) {
+$code.=<<___;
+       vadd.u64        @XMM[$i], @XMM[$i-1], @XMM[$i-1]
+       vst1.8          {@XMM[$i-1]}, [r0,:128]!
+       vswp            `&Dhi("@T[0]")`,`&Dlo("@T[0]")`
+       vshr.s64        @T[1], @XMM[$i], #63
+       veor            @XMM[$i], @XMM[$i], @T[0]
+       vand            @T[1], @T[1], $twmask
+___
+       @T=reverse(@T);
+
+$code.=<<___ if ($i>=10);
+       vld1.8          {@XMM[$i-10]}, [$inp]!
+       subs            $len, #0x10
+       bmi             .Lxts_enc_`$i-9`
+___
+$code.=<<___ if ($i>=11);
+       veor            @XMM[$i-11], @XMM[$i-11], @XMM[$i-3]
+___
+}
+$code.=<<___;
+       sub             $len, #0x10
+       vst1.8          {@XMM[15]}, [$fp]               @ next round tweak
+
+       vld1.8          {@XMM[6]}, [$inp]!
+       veor            @XMM[5], @XMM[5], @XMM[13]
+       mov             r4, $keysched
+       veor            @XMM[6], @XMM[6], @XMM[14]
+       mov             r5, $rounds                     @ pass rounds
+       mov             r0, sp
+
+       bl              _bsaes_encrypt8
+
+       vld1.8          {@XMM[ 8]-@XMM[ 9]}, [r0,:128]!
+       vld1.8          {@XMM[10]-@XMM[11]}, [r0,:128]!
+       veor            @XMM[0], @XMM[0], @XMM[ 8]
+       vld1.8          {@XMM[12]-@XMM[13]}, [r0,:128]!
+       veor            @XMM[1], @XMM[1], @XMM[ 9]
+       veor            @XMM[8], @XMM[4], @XMM[10]
+       vst1.8          {@XMM[0]-@XMM[1]}, [$out]!
+       veor            @XMM[9], @XMM[6], @XMM[11]
+       vld1.8          {@XMM[14]}, [r0,:128]!
+       veor            @XMM[10], @XMM[3], @XMM[12]
+       vst1.8          {@XMM[8]-@XMM[9]}, [$out]!
+       veor            @XMM[11], @XMM[7], @XMM[13]
+       veor            @XMM[12], @XMM[2], @XMM[14]
+       vst1.8          {@XMM[10]-@XMM[11]}, [$out]!
+       vst1.8          {@XMM[12]}, [$out]!
+
+       vld1.8          {@XMM[8]}, [$fp]                @ next round tweak
+       b               .Lxts_enc_done
+.Lxts_enc_6:
+       vst1.8          {@XMM[14]}, [$fp]               @ next round tweak
+
+       veor            @XMM[4], @XMM[4], @XMM[12]
+       mov             r4, $keysched
+       veor            @XMM[5], @XMM[5], @XMM[13]
+       mov             r5, $rounds                     @ pass rounds
+       mov             r0, sp
+
+       bl              _bsaes_encrypt8
+
+       vld1.8          {@XMM[ 8]-@XMM[ 9]}, [r0,:128]!
+       vld1.8          {@XMM[10]-@XMM[11]}, [r0,:128]!
+       veor            @XMM[0], @XMM[0], @XMM[ 8]
+       vld1.8          {@XMM[12]-@XMM[13]}, [r0,:128]!
+       veor            @XMM[1], @XMM[1], @XMM[ 9]
+       veor            @XMM[8], @XMM[4], @XMM[10]
+       vst1.8          {@XMM[0]-@XMM[1]}, [$out]!
+       veor            @XMM[9], @XMM[6], @XMM[11]
+       veor            @XMM[10], @XMM[3], @XMM[12]
+       vst1.8          {@XMM[8]-@XMM[9]}, [$out]!
+       veor            @XMM[11], @XMM[7], @XMM[13]
+       vst1.8          {@XMM[10]-@XMM[11]}, [$out]!
+
+       vld1.8          {@XMM[8]}, [$fp]                @ next round tweak
+       b               .Lxts_enc_done
+.Lxts_enc_5:
+       vst1.8          {@XMM[13]}, [$fp]               @ next round tweak
+
+       veor            @XMM[3], @XMM[3], @XMM[11]
+       mov             r4, $keysched
+       veor            @XMM[4], @XMM[4], @XMM[12]
+       mov             r5, $rounds                     @ pass rounds
+       mov             r0, sp
+
+       bl              _bsaes_encrypt8
+
+       vld1.8          {@XMM[ 8]-@XMM[ 9]}, [r0,:128]!
+       vld1.8          {@XMM[10]-@XMM[11]}, [r0,:128]!
+       veor            @XMM[0], @XMM[0], @XMM[ 8]
+       vld1.8          {@XMM[12]}, [r0,:128]!
+       veor            @XMM[1], @XMM[1], @XMM[ 9]
+       veor            @XMM[8], @XMM[4], @XMM[10]
+       vst1.8          {@XMM[0]-@XMM[1]}, [$out]!
+       veor            @XMM[9], @XMM[6], @XMM[11]
+       veor            @XMM[10], @XMM[3], @XMM[12]
+       vst1.8          {@XMM[8]-@XMM[9]}, [$out]!
+       vst1.8          {@XMM[10]}, [$out]!
+
+       vld1.8          {@XMM[8]}, [$fp]                @ next round tweak
+       b               .Lxts_enc_done
+.Lxts_enc_4:
+       vst1.8          {@XMM[12]}, [$fp]               @ next round tweak
+
+       veor            @XMM[2], @XMM[2], @XMM[10]
+       mov             r4, $keysched
+       veor            @XMM[3], @XMM[3], @XMM[11]
+       mov             r5, $rounds                     @ pass rounds
+       mov             r0, sp
+
+       bl              _bsaes_encrypt8
+
+       vld1.8          {@XMM[ 8]-@XMM[ 9]}, [r0,:128]!
+       vld1.8          {@XMM[10]-@XMM[11]}, [r0,:128]!
+       veor            @XMM[0], @XMM[0], @XMM[ 8]
+       veor            @XMM[1], @XMM[1], @XMM[ 9]
+       veor            @XMM[8], @XMM[4], @XMM[10]
+       vst1.8          {@XMM[0]-@XMM[1]}, [$out]!
+       veor            @XMM[9], @XMM[6], @XMM[11]
+       vst1.8          {@XMM[8]-@XMM[9]}, [$out]!
+
+       vld1.8          {@XMM[8]}, [$fp]                @ next round tweak
+       b               .Lxts_enc_done
+.Lxts_enc_3:
+       vst1.8          {@XMM[11]}, [$fp]               @ next round tweak
+
+       veor            @XMM[1], @XMM[1], @XMM[9]
+       mov             r4, $keysched
+       veor            @XMM[2], @XMM[2], @XMM[10]
+       mov             r5, $rounds                     @ pass rounds
+       mov             r0, sp
+
+       bl              _bsaes_encrypt8
+
+       vld1.8          {@XMM[8]-@XMM[9]}, [r0,:128]!
+       vld1.8          {@XMM[10]}, [r0,:128]!
+       veor            @XMM[0], @XMM[0], @XMM[ 8]
+       veor            @XMM[1], @XMM[1], @XMM[ 9]
+       veor            @XMM[8], @XMM[4], @XMM[10]
+       vst1.8          {@XMM[0]-@XMM[1]}, [$out]!
+       vst1.8          {@XMM[8]}, [$out]!
+
+       vld1.8          {@XMM[8]}, [$fp]                @ next round tweak
+       b               .Lxts_enc_done
+.Lxts_enc_2:
+       vst1.8          {@XMM[10]}, [$fp]               @ next round tweak
+
+       veor            @XMM[0], @XMM[0], @XMM[8]
+       mov             r4, $keysched
+       veor            @XMM[1], @XMM[1], @XMM[9]
+       mov             r5, $rounds                     @ pass rounds
+       mov             r0, sp
+
+       bl              _bsaes_encrypt8
+
+       vld1.8          {@XMM[8]-@XMM[9]}, [r0,:128]!
+       veor            @XMM[0], @XMM[0], @XMM[ 8]
+       veor            @XMM[1], @XMM[1], @XMM[ 9]
+       vst1.8          {@XMM[0]-@XMM[1]}, [$out]!
+
+       vld1.8          {@XMM[8]}, [$fp]                @ next round tweak
+       b               .Lxts_enc_done
+.Lxts_enc_1:
+       mov             r0, sp
+       veor            @XMM[0], @XMM[8]
+       mov             r1, sp
+       vst1.8          {@XMM[0]}, [sp,:128]
+       mov             r2, $key
+       mov             r4, $fp                         @ preserve fp
+
+       bl              AES_encrypt
+
+       vld1.8          {@XMM[0]}, [sp,:128]
+       veor            @XMM[0], @XMM[0], @XMM[8]
+       vst1.8          {@XMM[0]}, [$out]!
+       mov             $fp, r4
+
+       vmov            @XMM[8], @XMM[9]                @ next round tweak
+
+.Lxts_enc_done:
+       adds            $len, #0x10
+       beq             .Lxts_enc_ret
+       sub             r6, $out, #0x10
+
+.Lxts_enc_steal:
+       ldrb            r0, [$inp], #1
+       ldrb            r1, [$out, #-0x10]
+       strb            r0, [$out, #-0x10]
+       strb            r1, [$out], #1
+
+       subs            $len, #1
+       bhi             .Lxts_enc_steal
+
+       vld1.8          {@XMM[0]}, [r6]
+       mov             r0, sp
+       veor            @XMM[0], @XMM[0], @XMM[8]
+       mov             r1, sp
+       vst1.8          {@XMM[0]}, [sp,:128]
+       mov             r2, $key
+       mov             r4, $fp                 @ preserve fp
+
+       bl              AES_encrypt
+
+       vld1.8          {@XMM[0]}, [sp,:128]
+       veor            @XMM[0], @XMM[0], @XMM[8]
+       vst1.8          {@XMM[0]}, [r6]
+       mov             $fp, r4
+
+.Lxts_enc_ret:
+       vmov.i32        d0, #0
+.Lxts_enc_bzero:                               @ wipe key schedule [if any]
+       vstmia          sp!, {d0}
+       teq             sp, $fp
+       bne             .Lxts_enc_bzero
+
+       add             sp, $fp, #0x14
+       vldmia          sp!, {d8-d15}
+       ldmia           sp!, {r4-r11, pc}       @ return
+
+.size  bsaes_xts_encrypt,.-bsaes_xts_encrypt
+
+.globl bsaes_xts_decrypt
+.type  bsaes_xts_decrypt,%function
+.align 4
+bsaes_xts_decrypt:
+       stmdb   sp!, {r4-r11, lr}               @ 0x24
+       vstmdb  sp!, {d8-d15}                   @ 0x40
+       sub     sp, #0x14                       @ 0x14
+
+       mov     $inp, r0
+       mov     $out, r1
+       mov     $len, r2
+       mov     $key, r3
+
+       @ generate initial tweak
+       ldr     r0, [sp, #0x7c]                 @ iv[]
+       mov     r1, sp
+       ldr     r2, [sp, #0x78]                 @ key2
+       bl      AES_encrypt
+
+       @ allocate the key schedule on the stack
+       ldr     $rounds, [$key, #240]           @ get # of rounds
+       mov     $fp, sp
+       sub     sp, sp, $rounds, lsl#7          @ 128 bytes per inner round key
+       add     sp, sp, #`128-32`               @ size of bit-sliced key schedule
+       mov     $keysched, sp
+
+       @ populate the key schedule
+       mov     r4, $key                        @ pass key
+       mov     r5, $rounds                     @ pass # of rounds
+       mov     r12, $keysched                  @ pass key schedule
+       bl      _bsaes_key_convert
+       vldmia  $keysched, {@XMM[6]}
+       vstmia  r12,  {@XMM[15]}                @ save last round key
+       veor    @XMM[7], @XMM[7], @XMM[6]       @ fix up round 0 key
+       vstmia  $keysched, {@XMM[7]}
+
+       sub     sp, #0x80                       @ place for tweak[8]
+       bic     sp, #0x8                        @ align at 16 bytes
+
+       vld1.8  {@XMM[8]}, [$fp]                @ initial tweak
+       adrl    $magic, .Lxts_magic
+
+       tst     $len, #0xf                      @ if not multiple of 16
+       subne   $len, #0x10                     @     subtract another 16 bytes
+       subs    $len, #0x80
+
+       blo     .Lxts_dec_short
+       b       .Lxts_dec_loop
+
+.align 4
+.Lxts_dec_loop:
+       vld1.8          {$twmask}, [$magic]     @ load XTS magic
+       vshr.s64        @T[0], @XMM[8], #63
+       mov             r0, sp
+       vand            @T[0], @T[0], $twmask
+___
+for($i=9;$i<16;$i++) {
+$code.=<<___;
+       vadd.u64        @XMM[$i], @XMM[$i-1], @XMM[$i-1]
+       vst1.8          {@XMM[$i-1]}, [r0,:128]!
+       vswp            `&Dhi("@T[0]")`,`&Dlo("@T[0]")`
+       vshr.s64        @T[1], @XMM[$i], #63
+       veor            @XMM[$i], @XMM[$i], @T[0]
+       vand            @T[1], @T[1], $twmask
+___
+       @T=reverse(@T);
+
+$code.=<<___ if ($i>=10);
+       vld1.8          {@XMM[$i-10]}, [$inp]!
+___
+$code.=<<___ if ($i>=11);
+       veor            @XMM[$i-11], @XMM[$i-11], @XMM[$i-3]
+___
+}
+$code.=<<___;
+       vadd.u64        @XMM[8], @XMM[15], @XMM[15]
+       vst1.8          {@XMM[15]}, [r0,:128]
+       vswp            `&Dhi("@T[0]")`,`&Dlo("@T[0]")`
+       veor            @XMM[8], @XMM[8], @T[0]
+       vst1.8          {@XMM[8]}, [$fp]                @ next round tweak
+
+       vld1.8          {@XMM[6]-@XMM[7]}, [$inp]!
+       veor            @XMM[5], @XMM[5], @XMM[13]
+       mov             r4, $keysched
+       veor            @XMM[6], @XMM[6], @XMM[14]
+       mov             r5, $rounds                     @ pass rounds
+       veor            @XMM[7], @XMM[7], @XMM[15]
+       mov             r0, sp
+
+       bl              _bsaes_decrypt8
+
+       vld1.8          {@XMM[ 8]-@XMM[ 9]}, [r0,:128]!
+       vld1.8          {@XMM[10]-@XMM[11]}, [r0,:128]!
+       veor            @XMM[0], @XMM[0], @XMM[ 8]
+       vld1.8          {@XMM[12]-@XMM[13]}, [r0,:128]!
+       veor            @XMM[1], @XMM[1], @XMM[ 9]
+       veor            @XMM[8], @XMM[6], @XMM[10]
+       vst1.8          {@XMM[0]-@XMM[1]}, [$out]!
+       veor            @XMM[9], @XMM[4], @XMM[11]
+       vld1.8          {@XMM[14]-@XMM[15]}, [r0,:128]!
+       veor            @XMM[10], @XMM[2], @XMM[12]
+       vst1.8          {@XMM[8]-@XMM[9]}, [$out]!
+       veor            @XMM[11], @XMM[7], @XMM[13]
+       veor            @XMM[12], @XMM[3], @XMM[14]
+       vst1.8          {@XMM[10]-@XMM[11]}, [$out]!
+       veor            @XMM[13], @XMM[5], @XMM[15]
+       vst1.8          {@XMM[12]-@XMM[13]}, [$out]!
+
+       vld1.8          {@XMM[8]}, [$fp]                @ next round tweak
+
+       subs            $len, #0x80
+       bpl             .Lxts_dec_loop
+
+.Lxts_dec_short:
+       adds            $len, #0x70
+       bmi             .Lxts_dec_done
+
+       vld1.8          {$twmask}, [$magic]     @ load XTS magic
+       vshr.s64        @T[0], @XMM[8], #63
+       mov             r0, sp
+       vand            @T[0], @T[0], $twmask
+___
+for($i=9;$i<16;$i++) {
+$code.=<<___;
+       vadd.u64        @XMM[$i], @XMM[$i-1], @XMM[$i-1]
+       vst1.8          {@XMM[$i-1]}, [r0,:128]!
+       vswp            `&Dhi("@T[0]")`,`&Dlo("@T[0]")`
+       vshr.s64        @T[1], @XMM[$i], #63
+       veor            @XMM[$i], @XMM[$i], @T[0]
+       vand            @T[1], @T[1], $twmask
+___
+       @T=reverse(@T);
+
+$code.=<<___ if ($i>=10);
+       vld1.8          {@XMM[$i-10]}, [$inp]!
+       subs            $len, #0x10
+       bmi             .Lxts_dec_`$i-9`
+___
+$code.=<<___ if ($i>=11);
+       veor            @XMM[$i-11], @XMM[$i-11], @XMM[$i-3]
+___
+}
+$code.=<<___;
+       sub             $len, #0x10
+       vst1.8          {@XMM[15]}, [$fp]               @ next round tweak
+
+       vld1.8          {@XMM[6]}, [$inp]!
+       veor            @XMM[5], @XMM[5], @XMM[13]
+       mov             r4, $keysched
+       veor            @XMM[6], @XMM[6], @XMM[14]
+       mov             r5, $rounds                     @ pass rounds
+       mov             r0, sp
+
+       bl              _bsaes_decrypt8
+
+       vld1.8          {@XMM[ 8]-@XMM[ 9]}, [r0,:128]!
+       vld1.8          {@XMM[10]-@XMM[11]}, [r0,:128]!
+       veor            @XMM[0], @XMM[0], @XMM[ 8]
+       vld1.8          {@XMM[12]-@XMM[13]}, [r0,:128]!
+       veor            @XMM[1], @XMM[1], @XMM[ 9]
+       veor            @XMM[8], @XMM[6], @XMM[10]
+       vst1.8          {@XMM[0]-@XMM[1]}, [$out]!
+       veor            @XMM[9], @XMM[4], @XMM[11]
+       vld1.8          {@XMM[14]}, [r0,:128]!
+       veor            @XMM[10], @XMM[2], @XMM[12]
+       vst1.8          {@XMM[8]-@XMM[9]}, [$out]!
+       veor            @XMM[11], @XMM[7], @XMM[13]
+       veor            @XMM[12], @XMM[3], @XMM[14]
+       vst1.8          {@XMM[10]-@XMM[11]}, [$out]!
+       vst1.8          {@XMM[12]}, [$out]!
+
+       vld1.8          {@XMM[8]}, [$fp]                @ next round tweak
+       b               .Lxts_dec_done
+.Lxts_dec_6:
+       vst1.8          {@XMM[14]}, [$fp]               @ next round tweak
+
+       veor            @XMM[4], @XMM[4], @XMM[12]
+       mov             r4, $keysched
+       veor            @XMM[5], @XMM[5], @XMM[13]
+       mov             r5, $rounds                     @ pass rounds
+       mov             r0, sp
+
+       bl              _bsaes_decrypt8
+
+       vld1.8          {@XMM[ 8]-@XMM[ 9]}, [r0,:128]!
+       vld1.8          {@XMM[10]-@XMM[11]}, [r0,:128]!
+       veor            @XMM[0], @XMM[0], @XMM[ 8]
+       vld1.8          {@XMM[12]-@XMM[13]}, [r0,:128]!
+       veor            @XMM[1], @XMM[1], @XMM[ 9]
+       veor            @XMM[8], @XMM[6], @XMM[10]
+       vst1.8          {@XMM[0]-@XMM[1]}, [$out]!
+       veor            @XMM[9], @XMM[4], @XMM[11]
+       veor            @XMM[10], @XMM[2], @XMM[12]
+       vst1.8          {@XMM[8]-@XMM[9]}, [$out]!
+       veor            @XMM[11], @XMM[7], @XMM[13]
+       vst1.8          {@XMM[10]-@XMM[11]}, [$out]!
+
+       vld1.8          {@XMM[8]}, [$fp]                @ next round tweak
+       b               .Lxts_dec_done
+.Lxts_dec_5:
+       vst1.8          {@XMM[13]}, [$fp]               @ next round tweak
+
+       veor            @XMM[3], @XMM[3], @XMM[11]
+       mov             r4, $keysched
+       veor            @XMM[4], @XMM[4], @XMM[12]
+       mov             r5, $rounds                     @ pass rounds
+       mov             r0, sp
+
+       bl              _bsaes_decrypt8
+
+       vld1.8          {@XMM[ 8]-@XMM[ 9]}, [r0,:128]!
+       vld1.8          {@XMM[10]-@XMM[11]}, [r0,:128]!
+       veor            @XMM[0], @XMM[0], @XMM[ 8]
+       vld1.8          {@XMM[12]}, [r0,:128]!
+       veor            @XMM[1], @XMM[1], @XMM[ 9]
+       veor            @XMM[8], @XMM[6], @XMM[10]
+       vst1.8          {@XMM[0]-@XMM[1]}, [$out]!
+       veor            @XMM[9], @XMM[4], @XMM[11]
+       veor            @XMM[10], @XMM[2], @XMM[12]
+       vst1.8          {@XMM[8]-@XMM[9]}, [$out]!
+       vst1.8          {@XMM[10]}, [$out]!
+
+       vld1.8          {@XMM[8]}, [$fp]                @ next round tweak
+       b               .Lxts_dec_done
+.Lxts_dec_4:
+       vst1.8          {@XMM[12]}, [$fp]               @ next round tweak
+
+       veor            @XMM[2], @XMM[2], @XMM[10]
+       mov             r4, $keysched
+       veor            @XMM[3], @XMM[3], @XMM[11]
+       mov             r5, $rounds                     @ pass rounds
+       mov             r0, sp
+
+       bl              _bsaes_decrypt8
+
+       vld1.8          {@XMM[ 8]-@XMM[ 9]}, [r0,:128]!
+       vld1.8          {@XMM[10]-@XMM[11]}, [r0,:128]!
+       veor            @XMM[0], @XMM[0], @XMM[ 8]
+       veor            @XMM[1], @XMM[1], @XMM[ 9]
+       veor            @XMM[8], @XMM[6], @XMM[10]
+       vst1.8          {@XMM[0]-@XMM[1]}, [$out]!
+       veor            @XMM[9], @XMM[4], @XMM[11]
+       vst1.8          {@XMM[8]-@XMM[9]}, [$out]!
+
+       vld1.8          {@XMM[8]}, [$fp]                @ next round tweak
+       b               .Lxts_dec_done
+.Lxts_dec_3:
+       vst1.8          {@XMM[11]}, [$fp]               @ next round tweak
+
+       veor            @XMM[1], @XMM[1], @XMM[9]
+       mov             r4, $keysched
+       veor            @XMM[2], @XMM[2], @XMM[10]
+       mov             r5, $rounds                     @ pass rounds
+       mov             r0, sp
+
+       bl              _bsaes_decrypt8
+
+       vld1.8          {@XMM[8]-@XMM[9]}, [r0,:128]!
+       vld1.8          {@XMM[10]}, [r0,:128]!
+       veor            @XMM[0], @XMM[0], @XMM[ 8]
+       veor            @XMM[1], @XMM[1], @XMM[ 9]
+       veor            @XMM[8], @XMM[6], @XMM[10]
+       vst1.8          {@XMM[0]-@XMM[1]}, [$out]!
+       vst1.8          {@XMM[8]}, [$out]!
+
+       vld1.8          {@XMM[8]}, [$fp]                @ next round tweak
+       b               .Lxts_dec_done
+.Lxts_dec_2:
+       vst1.8          {@XMM[10]}, [$fp]               @ next round tweak
+
+       veor            @XMM[0], @XMM[0], @XMM[8]
+       mov             r4, $keysched
+       veor            @XMM[1], @XMM[1], @XMM[9]
+       mov             r5, $rounds                     @ pass rounds
+       mov             r0, sp
+
+       bl              _bsaes_decrypt8
+
+       vld1.8          {@XMM[8]-@XMM[9]}, [r0,:128]!
+       veor            @XMM[0], @XMM[0], @XMM[ 8]
+       veor            @XMM[1], @XMM[1], @XMM[ 9]
+       vst1.8          {@XMM[0]-@XMM[1]}, [$out]!
+
+       vld1.8          {@XMM[8]}, [$fp]                @ next round tweak
+       b               .Lxts_dec_done
+.Lxts_dec_1:
+       mov             r0, sp
+       veor            @XMM[0], @XMM[8]
+       mov             r1, sp
+       vst1.8          {@XMM[0]}, [sp,:128]
+       mov             r2, $key
+       mov             r4, $fp                         @ preserve fp
+
+       bl              AES_decrypt
+
+       vld1.8          {@XMM[0]}, [sp,:128]
+       veor            @XMM[0], @XMM[0], @XMM[8]
+       vst1.8          {@XMM[0]}, [$out]!
+       mov             $fp, r4
+
+       vmov            @XMM[8], @XMM[9]                @ next round tweak
+
+.Lxts_dec_done:
+       adds            $len, #0x10
+       beq             .Lxts_dec_ret
+
+       @ calculate one round of extra tweak for the stolen ciphertext
+       adrl            $magic, .Lxts_magic
+       vld1.8          {$twmask}, [$magic]
+       vshr.s64        @XMM[6], @XMM[8], #63
+       vand            @XMM[6], @XMM[6], $twmask
+       vadd.u64        @XMM[9], @XMM[8], @XMM[8]
+       vswp            `&Dhi("@XMM[6]")`,`&Dlo("@XMM[6]")`
+       veor            @XMM[9], @XMM[9], @XMM[6]
+
+       @ perform the final decryption with the last tweak value
+       vld1.8          {@XMM[0]}, [$inp]!
+       mov             r0, sp
+       veor            @XMM[0], @XMM[0], @XMM[9]
+       mov             r1, sp
+       vst1.8          {@XMM[0]}, [sp,:128]
+       mov             r2, $key
+       mov             r4, $fp                 @ preserve fp
+
+       bl              AES_decrypt
+
+       vld1.8          {@XMM[0]}, [sp,:128]
+       veor            @XMM[0], @XMM[0], @XMM[9]
+       vst1.8          {@XMM[0]}, [$out]
+
+       mov             r6, $out
+.Lxts_dec_steal:
+       ldrb            r1, [$out]
+       ldrb            r0, [$inp], #1
+       strb            r1, [$out, #0x10]
+       strb            r0, [$out], #1
+
+       subs            $len, #1
+       bhi             .Lxts_dec_steal
+
+       vld1.8          {@XMM[0]}, [r6]
+       mov             r0, sp
+       veor            @XMM[0], @XMM[8]
+       mov             r1, sp
+       vst1.8          {@XMM[0]}, [sp,:128]
+       mov             r2, $key
+
+       bl              AES_decrypt
+
+       vld1.8          {@XMM[0]}, [sp,:128]
+       veor            @XMM[0], @XMM[0], @XMM[8]
+       vst1.8          {@XMM[0]}, [r6]
+       mov             $fp, r4
+
+.Lxts_dec_ret:
+       vmov.i32        d0, #0
+.Lxts_dec_bzero:                               @ wipe key schedule [if any]
+       vstmia          sp!, {d0}
+       teq             sp, $fp
+       bne             .Lxts_dec_bzero
+
+       add             sp, $fp, #0x14
+       vldmia          sp!, {d8-d15}
+       ldmia           sp!, {r4-r11, pc}       @ return
+
+.size  bsaes_xts_decrypt,.-bsaes_xts_decrypt
+___
+}
+$code.=<<___;
+#endif
+___
 
 $code =~ s/\`([^\`]*)\`/eval($1)/gem;