// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

#include "textflag.h"

// func cryptBlocks(c code, key, dst, src *byte, length int)
TEXT ·cryptBlocks(SB),NOSPLIT,$0-40
	MOVD	key+8(FP), R1
	MOVD	dst+16(FP), R2
	MOVD	src+24(FP), R4
	MOVD	length+32(FP), R5
	MOVD	c+0(FP), R0
loop:
	WORD	$0xB92E0024 // cipher message (KM)
	BVS	loop        // branch back if interrupted
	XOR	R0, R0
	RET

// func cryptBlocksChain(c code, iv, key, dst, src *byte, length int)
TEXT ·cryptBlocksChain(SB),NOSPLIT,$48-48
	LA	params-48(SP), R1
	MOVD	iv+8(FP), R8
	MOVD	key+16(FP), R9
	MVC	$16, 0(R8), 0(R1)  // move iv into params
	MVC	$32, 0(R9), 16(R1) // move key into params
	MOVD	dst+24(FP), R2
	MOVD	src+32(FP), R4
	MOVD	length+40(FP), R5
	MOVD	c+0(FP), R0
loop:
	WORD	$0xB92F0024       // cipher message with chaining (KMC)
	BVS	loop              // branch back if interrupted
	XOR	R0, R0
	MVC	$16, 0(R1), 0(R8) // update iv
	RET

// func xorBytes(dst, a, b []byte) int
TEXT ·xorBytes(SB),NOSPLIT,$0-80
	MOVD	dst_base+0(FP), R1
	MOVD	a_base+24(FP), R2
	MOVD	b_base+48(FP), R3
	MOVD	a_len+32(FP), R4
	MOVD	b_len+56(FP), R5
	CMPBLE	R4, R5, skip
	MOVD	R5, R4
skip:
	MOVD	R4, ret+72(FP)
	MOVD	$0, R5
	CMPBLT	R4, $8, tail
loop:
	MOVD	0(R2)(R5*1), R7
	MOVD	0(R3)(R5*1), R8
	XOR	R7, R8
	MOVD	R8, 0(R1)(R5*1)
	LAY	8(R5), R5
	SUB	$8, R4
	CMPBGE	R4, $8, loop
tail:
	CMPBEQ	R4, $0, done
	MOVB	0(R2)(R5*1), R7
	MOVB	0(R3)(R5*1), R8
	XOR	R7, R8
	MOVB	R8, 0(R1)(R5*1)
	LAY	1(R5), R5
	SUB	$1, R4
	BR	tail
done:
	RET

// func cryptBlocksGCM(fn code, key, dst, src, buf []byte, cnt *[16]byte)
TEXT ·cryptBlocksGCM(SB),NOSPLIT,$0-112
	MOVD	src_len+64(FP), R0
	MOVD	buf_base+80(FP), R1
	MOVD	cnt+104(FP), R12
	LMG	(R12), R2, R3

	// Check that the src size is less than or equal to the buffer size.
	MOVD	buf_len+88(FP), R4
	CMP	R0, R4
	BGT	crash

	// Check that the src size is a multiple of 16-bytes.
	MOVD	R0, R4
	AND	$0xf, R4
	BLT	crash // non-zero

	// Check that the src size is less than or equal to the dst size.
	MOVD	dst_len+40(FP), R4
	CMP	R0, R4
	BGT	crash

	MOVD	R2, R4
	MOVD	R2, R6
	MOVD	R2, R8
	MOVD	R3, R5
	MOVD	R3, R7
	MOVD	R3, R9
	ADDW	$1, R5
	ADDW	$2, R7
	ADDW	$3, R9
incr:
	CMP	R0, $64
	BLT	tail
	STMG	R2, R9, (R1)
	ADDW	$4, R3
	ADDW	$4, R5
	ADDW	$4, R7
	ADDW	$4, R9
	MOVD	$64(R1), R1
	SUB	$64, R0
	BR	incr
tail:
	CMP	R0, $0
	BEQ	crypt
	STMG	R2, R3, (R1)
	ADDW	$1, R3
	MOVD	$16(R1), R1
	SUB	$16, R0
	BR	tail
crypt:
	STMG	R2, R3, (R12)       // update next counter value
	MOVD	fn+0(FP), R0        // function code (encryption)
	MOVD	key_base+8(FP), R1  // key
	MOVD	buf_base+80(FP), R2 // counter values
	MOVD	dst_base+32(FP), R4 // dst
	MOVD	src_base+56(FP), R6 // src
	MOVD	src_len+64(FP), R7  // len
loop:
	WORD	$0xB92D2046         // cipher message with counter (KMCTR)
	BVS	loop                // branch back if interrupted
	RET
crash:
	MOVD	$0, (R0)
	RET

// func ghash(key *gcmHashKey, hash *[16]byte, data []byte)
TEXT ·ghash(SB),NOSPLIT,$32-40
	MOVD    $65, R0 // GHASH function code
	MOVD	key+0(FP), R2
	LMG	(R2), R6, R7
	MOVD	hash+8(FP), R8
	LMG	(R8), R4, R5
	MOVD	$params-32(SP), R1
	STMG	R4, R7, (R1)
	LMG	data+16(FP), R2, R3 // R2=base, R3=len
loop:
	WORD    $0xB93E0002 // compute intermediate message digest (KIMD)
	BVS     loop        // branch back if interrupted
	MVC     $16, (R1), (R8)
	MOVD	$0, R0
	RET

// func supportsKMA() bool
TEXT ·supportsKMA(SB),NOSPLIT,$24-1
	MOVD	$tmp-24(SP), R1
	MOVD	$2, R0       // store 24-bytes
	XC	$24, (R1), (R1)
	WORD	$0xb2b01000  // STFLE (R1)
	MOVWZ	16(R1), R2
	ANDW	$(1<<13), R2 // test bit 146 (message-security-assist 8)
	BEQ	no

	MOVD	$0, R0       // KMA-Query
	XC	$16, (R1), (R1)
	WORD	$0xb9296024  // kma %r6,%r2,%r4
	MOVWZ	(R1), R2
	WORD	$0xa7213800  // TMLL R2, $0x3800
	BVS	yes
no:
	MOVB	$0, ret+0(FP)
	RET
yes:
	MOVB	$1, ret+0(FP)
	RET

// func kmaGCM(fn code, key, dst, src, aad []byte, tag *[16]byte, cnt *gcmCount)
TEXT ·kmaGCM(SB),NOSPLIT,$112-120
	MOVD	fn+0(FP), R0
	MOVD	$params-112(SP), R1

	// load ptr/len pairs
	LMG	dst+32(FP), R2, R3 // R2=base R3=len
	LMG	src+56(FP), R4, R5 // R4=base R5=len
	LMG	aad+80(FP), R6, R7 // R6=base R7=len

	// setup parameters
	MOVD	cnt+112(FP), R8
	XC	$12, (R1), (R1)     // reserved
	MVC	$4, 12(R8), 12(R1)  // set chain value
	MVC	$16, (R8), 64(R1)   // set initial counter value
	XC	$32, 16(R1), 16(R1) // set hash subkey and tag
	SLD	$3, R7, R12
	MOVD	R12, 48(R1)         // set total AAD length
	SLD	$3, R5, R12
	MOVD	R12, 56(R1)         // set total plaintext/ciphertext length

	LMG	key+8(FP), R8, R9   // R8=base R9=len
	MVC	$16, (R8), 80(R1)   // set key
	CMPBEQ	R9, $16, kma
	MVC	$8, 16(R8), 96(R1)
	CMPBEQ	R9, $24, kma
	MVC	$8, 24(R8), 104(R1)

kma:
	WORD	$0xb9296024 // kma %r6,%r2,%r4
	BVS	kma

	MOVD	tag+104(FP), R2
	MVC	$16, 16(R1), 0(R2) // copy tag to output
	MOVD	cnt+112(FP), R8
	MVC	$4, 12(R1), 12(R8) // update counter value

	RET