;;
;; Copyright (c) 2012-2018, Intel Corporation
;;
;; Redistribution and use in source and binary forms, with or without
;; modification, are permitted provided that the following conditions are met:
;;
;;     * Redistributions of source code must retain the above copyright notice,
;;       this list of conditions and the following disclaimer.
;;     * Redistributions in binary form must reproduce the above copyright
;;       notice, this list of conditions and the following disclaimer in the
;;       documentation and/or other materials provided with the distribution.
;;     * Neither the name of Intel Corporation nor the names of its contributors
;;       may be used to endorse or promote products derived from this software
;;       without specific prior written permission.
;;
;; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
;; AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
;; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
;; DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
;; FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
;; SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
;; CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
;; OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
;; OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
;;

; routine to do AES cbc decrypt on 16n bytes doing AES by 4

; XMM registers are clobbered. Saving/restoring must be done at a higher level

; void aes_cbc_dec_128_sse(void    *in,
;                          UINT128 *IV,
;                          UINT128  keys[11],
;                          void    *out,
;                          UINT64   len_bytes);
;
; arg 1: IN:   pointer to input (cipher text)
; arg 2: IV:   pointer to IV
; arg 3: KEYS: pointer to keys
; arg 4: OUT:  pointer to output (plain text)
; arg 5: LEN:  length in bytes (multiple of 16)
;
%include "os.asm"

%define MOVDQ	movdqu

%ifdef LINUX
%define IN		rdi
%define IV		rsi
%define KEYS		rdx
%define OUT		rcx
%define LEN		r8
%else
%define IN		rcx
%define IV		rdx
%define KEYS		r8
%define OUT		r9
%define LEN		r10
%endif

%define IDX		rax
%define TMP		IDX
%define XDATA0		xmm0
%define XDATA1		xmm1
%define XDATA2		xmm2
%define XDATA3		xmm3
%define XKEY0		xmm4
%define XKEY2		xmm5
%define XKEY4		xmm6
%define XKEY6		xmm7
%define XKEY8		xmm8
%define XKEY10		xmm9
%define XIV		xmm10
%define XSAVED0		xmm11
%define XSAVED1		xmm12
%define XSAVED2		xmm13
%define XSAVED3		xmm14
%define XKEY		xmm15

%define IV_TMP		XSAVED3

section .text

MKGLOBAL(aes_cbc_dec_128_sse,function,internal)
aes_cbc_dec_128_sse:
%ifndef LINUX
	mov	LEN, [rsp + 8*5]
%endif

	mov	TMP, LEN
	and	TMP, 3*16
	jz	initial_4
	cmp	TMP, 2*16
	jb	initial_1
	ja	initial_3

initial_2:
	; load cipher text
	movdqu	XDATA0, [IN + 0*16]
	movdqu	XDATA1, [IN + 1*16]

	movdqa	XKEY0, [KEYS + 0*16]

	; save cipher text
	movdqa	XSAVED0, XDATA0
	movdqa	XIV,     XDATA1

	pxor	XDATA0, XKEY0		; 0. ARK
	pxor	XDATA1, XKEY0

	movdqa	XKEY2, [KEYS + 2*16]

	aesdec	XDATA0, [KEYS + 1*16]	; 1. DEC
	aesdec	XDATA1, [KEYS + 1*16]

	mov	IDX, 2*16

	aesdec	XDATA0, XKEY2		; 2. DEC
	aesdec	XDATA1, XKEY2

	movdqa	XKEY4, [KEYS + 4*16]

	aesdec	XDATA0, [KEYS + 3*16]	; 3. DEC
	aesdec	XDATA1, [KEYS + 3*16]

	movdqu	IV_TMP, [IV]

	aesdec	XDATA0, XKEY4		; 4. DEC
	aesdec	XDATA1, XKEY4

	movdqa	XKEY6, [KEYS + 6*16]

	aesdec	XDATA0, [KEYS + 5*16]	; 5. DEC
	aesdec	XDATA1, [KEYS + 5*16]

	aesdec	XDATA0, XKEY6		; 6. DEC
	aesdec	XDATA1, XKEY6

	movdqa	XKEY8, [KEYS + 8*16]

	aesdec	XDATA0, [KEYS + 7*16]	; 7. DEC
	aesdec	XDATA1, [KEYS + 7*16]

	aesdec	XDATA0, XKEY8		; 8. DEC
	aesdec	XDATA1, XKEY8

	movdqa	XKEY10, [KEYS + 10*16]

	aesdec	XDATA0, [KEYS + 9*16]	; 9. DEC
	aesdec	XDATA1, [KEYS + 9*16]

	aesdeclast	XDATA0, XKEY10		; 10. DEC
	aesdeclast	XDATA1, XKEY10

	pxor	XDATA0, IV_TMP
	pxor	XDATA1, XSAVED0

	movdqu	[OUT + 0*16], XDATA0
	movdqu	[OUT + 1*16], XDATA1

	cmp	LEN, 2*16
	je	done
	jmp	main_loop


	align 16
initial_1:
	; load cipher text
	movdqu	XDATA0, [IN + 0*16]

	movdqa	XKEY0, [KEYS + 0*16]

	; save cipher text
	movdqa	XIV,     XDATA0

	pxor	XDATA0, XKEY0		; 0. ARK

	movdqa	XKEY2, [KEYS + 2*16]

	aesdec	XDATA0, [KEYS + 1*16]	; 1. DEC

	mov	IDX, 1*16

	aesdec	XDATA0, XKEY2		; 2. DEC

	movdqa	XKEY4, [KEYS + 4*16]

	aesdec	XDATA0, [KEYS + 3*16]	; 3. DEC

	movdqu	IV_TMP, [IV]

	aesdec	XDATA0, XKEY4		; 4. DEC

	movdqa	XKEY6, [KEYS + 6*16]

	aesdec	XDATA0, [KEYS + 5*16]	; 5. DEC

	aesdec	XDATA0, XKEY6		; 6. DEC

	movdqa	XKEY8, [KEYS + 8*16]

	aesdec	XDATA0, [KEYS + 7*16]	; 7. DEC

	aesdec	XDATA0, XKEY8		; 8. DEC

	movdqa	XKEY10, [KEYS + 10*16]

	aesdec	XDATA0, [KEYS + 9*16]	; 9. DEC

	aesdeclast	XDATA0, XKEY10		; 10. DEC

	pxor	XDATA0, IV_TMP

	movdqu	[OUT + 0*16], XDATA0

	cmp	LEN, 1*16
	je	done
	jmp	main_loop


initial_3:
	; load cipher text
	movdqu	XDATA0, [IN + 0*16]
	movdqu	XDATA1, [IN + 1*16]
	movdqu	XDATA2, [IN + 2*16]

	movdqa	XKEY0, [KEYS + 0*16]

	; save cipher text
	movdqa	XSAVED0, XDATA0
	movdqa	XSAVED1, XDATA1
	movdqa	XIV,     XDATA2

	movdqa	XKEY, [KEYS + 1*16]

	pxor	XDATA0, XKEY0		; 0. ARK
	pxor	XDATA1, XKEY0
	pxor	XDATA2, XKEY0

	movdqa	XKEY2, [KEYS + 2*16]

	aesdec	XDATA0, XKEY		; 1. DEC
	aesdec	XDATA1, XKEY
	aesdec	XDATA2, XKEY

	movdqa	XKEY, [KEYS + 3*16]

	mov	IDX, 3*16

	aesdec	XDATA0, XKEY2		; 2. DEC
	aesdec	XDATA1, XKEY2
	aesdec	XDATA2, XKEY2

	movdqa	XKEY4, [KEYS + 4*16]

	aesdec	XDATA0, XKEY		; 3. DEC
	aesdec	XDATA1, XKEY
	aesdec	XDATA2, XKEY

	movdqa	XKEY, [KEYS + 5*16]
	movdqu	IV_TMP, [IV]

	aesdec	XDATA0, XKEY4		; 4. DEC
	aesdec	XDATA1, XKEY4
	aesdec	XDATA2, XKEY4

	movdqa	XKEY6, [KEYS + 6*16]

	aesdec	XDATA0, XKEY		; 5. DEC
	aesdec	XDATA1, XKEY
	aesdec	XDATA2, XKEY

	movdqa	XKEY, [KEYS + 7*16]

	aesdec	XDATA0, XKEY6		; 6. DEC
	aesdec	XDATA1, XKEY6
	aesdec	XDATA2, XKEY6

	movdqa	XKEY8, [KEYS + 8*16]

	aesdec	XDATA0, XKEY		; 7. DEC
	aesdec	XDATA1, XKEY
	aesdec	XDATA2, XKEY

	movdqa	XKEY, [KEYS + 9*16]

	aesdec	XDATA0, XKEY8		; 8. DEC
	aesdec	XDATA1, XKEY8
	aesdec	XDATA2, XKEY8

	movdqa	XKEY10, [KEYS + 10*16]

	aesdec	XDATA0, XKEY		; 9. DEC
	aesdec	XDATA1, XKEY
	aesdec	XDATA2, XKEY

	aesdeclast	XDATA0, XKEY10	; 10. DEC
	aesdeclast	XDATA1, XKEY10
	aesdeclast	XDATA2, XKEY10

	pxor	XDATA0, IV_TMP
	pxor	XDATA1, XSAVED0
	pxor	XDATA2, XSAVED1

	movdqu	[OUT + 0*16], XDATA0
	movdqu	[OUT + 1*16], XDATA1
	movdqu	[OUT + 2*16], XDATA2

	cmp	LEN, 3*16
	je	done
	jmp	main_loop


	align 16
initial_4:
	; load cipher text
	movdqu	XDATA0, [IN + 0*16]
	movdqu	XDATA1, [IN + 1*16]
	movdqu	XDATA2, [IN + 2*16]
	movdqu	XDATA3, [IN + 3*16]

	movdqa	XKEY0, [KEYS + 0*16]

	; save cipher text
	movdqa	XSAVED0, XDATA0
	movdqa	XSAVED1, XDATA1
	movdqa	XSAVED2, XDATA2
	movdqa	XIV,     XDATA3

	movdqa	XKEY, [KEYS + 1*16]

	pxor	XDATA0, XKEY0		; 0. ARK
	pxor	XDATA1, XKEY0
	pxor	XDATA2, XKEY0
	pxor	XDATA3, XKEY0

	movdqa	XKEY2, [KEYS + 2*16]

	aesdec	XDATA0, XKEY		; 1. DEC
	aesdec	XDATA1, XKEY
	aesdec	XDATA2, XKEY
	aesdec	XDATA3, XKEY

	movdqa	XKEY, [KEYS + 3*16]

	mov	IDX, 4*16

	aesdec	XDATA0, XKEY2		; 2. DEC
	aesdec	XDATA1, XKEY2
	aesdec	XDATA2, XKEY2
	aesdec	XDATA3, XKEY2

	movdqa	XKEY4, [KEYS + 4*16]

	aesdec	XDATA0, XKEY		; 3. DEC
	aesdec	XDATA1, XKEY
	aesdec	XDATA2, XKEY
	aesdec	XDATA3, XKEY

	movdqa	XKEY, [KEYS + 5*16]

	movdqu	IV_TMP, [IV]

	aesdec	XDATA0, XKEY4		; 4. DEC
	aesdec	XDATA1, XKEY4
	aesdec	XDATA2, XKEY4
	aesdec	XDATA3, XKEY4

	movdqa	XKEY6, [KEYS + 6*16]

	aesdec	XDATA0, XKEY		; 5. DEC
	aesdec	XDATA1, XKEY
	aesdec	XDATA2, XKEY
	aesdec	XDATA3, XKEY

	movdqa	XKEY, [KEYS + 7*16]

	aesdec	XDATA0, XKEY6		; 6. DEC
	aesdec	XDATA1, XKEY6
	aesdec	XDATA2, XKEY6
	aesdec	XDATA3, XKEY6

	movdqa	XKEY8, [KEYS + 8*16]

	aesdec	XDATA0, XKEY		; 7. DEC
	aesdec	XDATA1, XKEY
	aesdec	XDATA2, XKEY
	aesdec	XDATA3, XKEY

	movdqa	XKEY, [KEYS + 9*16]

	aesdec	XDATA0, XKEY8		; 8. DEC
	aesdec	XDATA1, XKEY8
	aesdec	XDATA2, XKEY8
	aesdec	XDATA3, XKEY8

	movdqa	XKEY10, [KEYS + 10*16]

	aesdec	XDATA0, XKEY		; 9. DEC
	aesdec	XDATA1, XKEY
	aesdec	XDATA2, XKEY
	aesdec	XDATA3, XKEY

	aesdeclast	XDATA0, XKEY10	; 10. DEC
	aesdeclast	XDATA1, XKEY10
	aesdeclast	XDATA2, XKEY10
	aesdeclast	XDATA3, XKEY10

	pxor	XDATA0, IV_TMP
	pxor	XDATA1, XSAVED0
	pxor	XDATA2, XSAVED1
	pxor	XDATA3, XSAVED2

	movdqu	[OUT + 0*16], XDATA0
	movdqu	[OUT + 1*16], XDATA1
	movdqu	[OUT + 2*16], XDATA2
	movdqu	[OUT + 3*16], XDATA3

	cmp	LEN, 4*16
	jz	done
	jmp	main_loop

	align 16
main_loop:
	; load cipher text
	movdqu	XDATA0, [IN + IDX + 0*16]
	movdqu	XDATA1, [IN + IDX + 1*16]
	movdqu	XDATA2, [IN + IDX + 2*16]
	movdqu	XDATA3, [IN + IDX + 3*16]

	; save cipher text
	movdqa	XSAVED0, XDATA0
	movdqa	XSAVED1, XDATA1
	movdqa	XSAVED2, XDATA2
	movdqa	XSAVED3, XDATA3

	movdqa	XKEY, [KEYS + 1*16]

	pxor	XDATA0, XKEY0		; 0. ARK
	pxor	XDATA1, XKEY0
	pxor	XDATA2, XKEY0
	pxor	XDATA3, XKEY0

	add	IDX, 4*16

	aesdec	XDATA0, XKEY		; 1. DEC
	aesdec	XDATA1, XKEY
	aesdec	XDATA2, XKEY
	aesdec	XDATA3, XKEY

	movdqa	XKEY, [KEYS + 3*16]

	aesdec	XDATA0, XKEY2		; 2. DEC
	aesdec	XDATA1, XKEY2
	aesdec	XDATA2, XKEY2
	aesdec	XDATA3, XKEY2

	aesdec	XDATA0, XKEY		; 3. DEC
	aesdec	XDATA1, XKEY
	aesdec	XDATA2, XKEY
	aesdec	XDATA3, XKEY

	movdqa	XKEY, [KEYS + 5*16]

	aesdec	XDATA0, XKEY4		; 4. DEC
	aesdec	XDATA1, XKEY4
	aesdec	XDATA2, XKEY4
	aesdec	XDATA3, XKEY4

	aesdec	XDATA0, XKEY		; 5. DEC
	aesdec	XDATA1, XKEY
	aesdec	XDATA2, XKEY
	aesdec	XDATA3, XKEY

	movdqa	XKEY, [KEYS + 7*16]

	aesdec	XDATA0, XKEY6		; 6. DEC
	aesdec	XDATA1, XKEY6
	aesdec	XDATA2, XKEY6
	aesdec	XDATA3, XKEY6

	aesdec	XDATA0, XKEY		; 7. DEC
	aesdec	XDATA1, XKEY
	aesdec	XDATA2, XKEY
	aesdec	XDATA3, XKEY

	movdqa	XKEY, [KEYS + 9*16]

	aesdec	XDATA0, XKEY8		; 8. DEC
	aesdec	XDATA1, XKEY8
	aesdec	XDATA2, XKEY8
	aesdec	XDATA3, XKEY8

	aesdec	XDATA0, XKEY		; 9. DEC
	aesdec	XDATA1, XKEY
	aesdec	XDATA2, XKEY
	aesdec	XDATA3, XKEY

	aesdeclast	XDATA0, XKEY10		; 10. DEC
	aesdeclast	XDATA1, XKEY10
	aesdeclast	XDATA2, XKEY10
	aesdeclast	XDATA3, XKEY10

	pxor	XDATA0, XIV
	pxor	XDATA1, XSAVED0
	pxor	XDATA2, XSAVED1
	pxor	XDATA3, XSAVED2

	movdqu	[OUT + IDX + 0*16 - 4*16], XDATA0
	movdqu	[OUT + IDX + 1*16 - 4*16], XDATA1
	movdqu	[OUT + IDX + 2*16 - 4*16], XDATA2
	movdqu	[OUT + IDX + 3*16 - 4*16], XDATA3

	movdqa	XIV, XSAVED3

	CMP	IDX, LEN
	jne	main_loop

done:
; Don't write back IV
;	movdqu	[IV], XIV

	ret

%ifdef LINUX
section .note.GNU-stack noalloc noexec nowrite progbits
%endif
