forked from mirrors/linux
		
	crypto: brcm - Add Broadcom SPU driver
Add Broadcom Secure Processing Unit (SPU) crypto driver for SPU hardware crypto offload. The driver supports ablkcipher, ahash, and aead symmetric crypto operations. Signed-off-by: Steve Lin <steven.lin1@broadcom.com> Signed-off-by: Rob Rice <rob.rice@broadcom.com> Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
This commit is contained in:
		
							parent
							
								
									206dc4fc27
								
							
						
					
					
						commit
						9d12ba86f8
					
				
					 12 changed files with 9516 additions and 0 deletions
				
			
		|  | @ -587,4 +587,19 @@ source "drivers/crypto/chelsio/Kconfig" | ||||||
| 
 | 
 | ||||||
| source "drivers/crypto/virtio/Kconfig" | source "drivers/crypto/virtio/Kconfig" | ||||||
| 
 | 
 | ||||||
|  | config CRYPTO_DEV_BCM_SPU | ||||||
|  | 	tristate "Broadcom symmetric crypto/hash acceleration support" | ||||||
|  | 	depends on ARCH_BCM_IPROC | ||||||
|  | 	depends on BCM_PDC_MBOX | ||||||
|  | 	default m | ||||||
|  | 	select CRYPTO_DES | ||||||
|  | 	select CRYPTO_MD5 | ||||||
|  | 	select CRYPTO_SHA1 | ||||||
|  | 	select CRYPTO_SHA256 | ||||||
|  | 	select CRYPTO_SHA512 | ||||||
|  | 	help | ||||||
|  | 	  This driver provides support for Broadcom crypto acceleration using the | ||||||
|  | 	  Secure Processing Unit (SPU). The SPU driver registers ablkcipher, | ||||||
|  | 	  ahash, and aead algorithms with the kernel cryptographic API. | ||||||
|  | 
 | ||||||
| endif # CRYPTO_HW | endif # CRYPTO_HW | ||||||
|  |  | ||||||
|  | @ -35,3 +35,4 @@ obj-$(CONFIG_CRYPTO_DEV_TALITOS) += talitos.o | ||||||
| obj-$(CONFIG_CRYPTO_DEV_UX500) += ux500/ | obj-$(CONFIG_CRYPTO_DEV_UX500) += ux500/ | ||||||
| obj-$(CONFIG_CRYPTO_DEV_VIRTIO) += virtio/ | obj-$(CONFIG_CRYPTO_DEV_VIRTIO) += virtio/ | ||||||
| obj-$(CONFIG_CRYPTO_DEV_VMX) += vmx/ | obj-$(CONFIG_CRYPTO_DEV_VMX) += vmx/ | ||||||
|  | obj-$(CONFIG_CRYPTO_DEV_BCM_SPU) += bcm/ | ||||||
|  |  | ||||||
							
								
								
									
										15
									
								
								drivers/crypto/bcm/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								drivers/crypto/bcm/Makefile
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,15 @@ | ||||||
|  | # File: drivers/crypto/bcm/Makefile
 | ||||||
|  | #
 | ||||||
|  | # Makefile for crypto acceleration files for Broadcom SPU driver
 | ||||||
|  | #
 | ||||||
|  | # Uncomment to enable debug tracing in the SPU driver.
 | ||||||
|  | # CFLAGS_util.o := -DDEBUG
 | ||||||
|  | # CFLAGS_cipher.o := -DDEBUG
 | ||||||
|  | # CFLAGS_spu.o := -DDEBUG
 | ||||||
|  | # CFLAGS_spu2.o := -DDEBUG
 | ||||||
|  | 
 | ||||||
|  | obj-$(CONFIG_CRYPTO_DEV_BCM_SPU) := bcm_crypto_spu.o | ||||||
|  | 
 | ||||||
|  | bcm_crypto_spu-objs :=  util.o spu.o spu2.o cipher.o | ||||||
|  | 
 | ||||||
|  | ccflags-y += -I. -DBCMDRIVER | ||||||
							
								
								
									
										4964
									
								
								drivers/crypto/bcm/cipher.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4964
									
								
								drivers/crypto/bcm/cipher.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										483
									
								
								drivers/crypto/bcm/cipher.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										483
									
								
								drivers/crypto/bcm/cipher.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,483 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright 2016 Broadcom | ||||||
|  |  * | ||||||
|  |  * This program is free software; you can redistribute it and/or modify | ||||||
|  |  * it under the terms of the GNU General Public License, version 2, as | ||||||
|  |  * published by the Free Software Foundation (the "GPL"). | ||||||
|  |  * | ||||||
|  |  * This program is distributed in the hope that it will be useful, but | ||||||
|  |  * WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||||
|  |  * General Public License version 2 (GPLv2) for more details. | ||||||
|  |  * | ||||||
|  |  * You should have received a copy of the GNU General Public License | ||||||
|  |  * version 2 (GPLv2) along with this source code. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #ifndef _CIPHER_H | ||||||
|  | #define _CIPHER_H | ||||||
|  | 
 | ||||||
|  | #include <linux/atomic.h> | ||||||
|  | #include <linux/mailbox/brcm-message.h> | ||||||
|  | #include <linux/mailbox_client.h> | ||||||
|  | #include <crypto/aes.h> | ||||||
|  | #include <crypto/internal/hash.h> | ||||||
|  | #include <crypto/aead.h> | ||||||
|  | #include <crypto/sha.h> | ||||||
|  | #include <crypto/sha3.h> | ||||||
|  | 
 | ||||||
|  | #include "spu.h" | ||||||
|  | #include "spum.h" | ||||||
|  | #include "spu2.h" | ||||||
|  | 
 | ||||||
|  | /* Driver supports up to MAX_SPUS SPU blocks */ | ||||||
|  | #define MAX_SPUS 16 | ||||||
|  | 
 | ||||||
|  | #define ARC4_MIN_KEY_SIZE   1 | ||||||
|  | #define ARC4_MAX_KEY_SIZE   256 | ||||||
|  | #define ARC4_BLOCK_SIZE     1 | ||||||
|  | #define ARC4_STATE_SIZE     4 | ||||||
|  | 
 | ||||||
|  | #define CCM_AES_IV_SIZE    16 | ||||||
|  | #define GCM_AES_IV_SIZE    12 | ||||||
|  | #define GCM_ESP_IV_SIZE     8 | ||||||
|  | #define CCM_ESP_IV_SIZE     8 | ||||||
|  | #define RFC4543_ICV_SIZE   16 | ||||||
|  | 
 | ||||||
|  | #define MAX_KEY_SIZE	ARC4_MAX_KEY_SIZE | ||||||
|  | #define MAX_IV_SIZE	AES_BLOCK_SIZE | ||||||
|  | #define MAX_DIGEST_SIZE	SHA3_512_DIGEST_SIZE | ||||||
|  | #define MAX_ASSOC_SIZE	512 | ||||||
|  | 
 | ||||||
|  | /* size of salt value for AES-GCM-ESP and AES-CCM-ESP */ | ||||||
|  | #define GCM_ESP_SALT_SIZE   4 | ||||||
|  | #define CCM_ESP_SALT_SIZE   3 | ||||||
|  | #define MAX_SALT_SIZE       GCM_ESP_SALT_SIZE | ||||||
|  | #define GCM_ESP_SALT_OFFSET 0 | ||||||
|  | #define CCM_ESP_SALT_OFFSET 1 | ||||||
|  | 
 | ||||||
|  | #define GCM_ESP_DIGESTSIZE 16 | ||||||
|  | 
 | ||||||
|  | #define MAX_HASH_BLOCK_SIZE SHA512_BLOCK_SIZE | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Maximum number of bytes from a non-final hash request that can be deferred | ||||||
|  |  * until more data is available. With new crypto API framework, this | ||||||
|  |  * can be no more than one block of data. | ||||||
|  |  */ | ||||||
|  | #define HASH_CARRY_MAX  MAX_HASH_BLOCK_SIZE | ||||||
|  | 
 | ||||||
|  | /* Force at least 4-byte alignment of all SPU message fields */ | ||||||
|  | #define SPU_MSG_ALIGN  4 | ||||||
|  | 
 | ||||||
|  | /* Number of times to resend mailbox message if mb queue is full */ | ||||||
|  | #define SPU_MB_RETRY_MAX  1000 | ||||||
|  | 
 | ||||||
|  | /* op_counts[] indexes */ | ||||||
|  | enum op_type { | ||||||
|  | 	SPU_OP_CIPHER, | ||||||
|  | 	SPU_OP_HASH, | ||||||
|  | 	SPU_OP_HMAC, | ||||||
|  | 	SPU_OP_AEAD, | ||||||
|  | 	SPU_OP_NUM | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | enum spu_spu_type { | ||||||
|  | 	SPU_TYPE_SPUM, | ||||||
|  | 	SPU_TYPE_SPU2, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * SPUM_NS2 and SPUM_NSP are the SPU-M block on Northstar 2 and Northstar Plus, | ||||||
|  |  * respectively. | ||||||
|  |  */ | ||||||
|  | enum spu_spu_subtype { | ||||||
|  | 	SPU_SUBTYPE_SPUM_NS2, | ||||||
|  | 	SPU_SUBTYPE_SPUM_NSP, | ||||||
|  | 	SPU_SUBTYPE_SPU2_V1, | ||||||
|  | 	SPU_SUBTYPE_SPU2_V2 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct spu_type_subtype { | ||||||
|  | 	enum spu_spu_type type; | ||||||
|  | 	enum spu_spu_subtype subtype; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct cipher_op { | ||||||
|  | 	enum spu_cipher_alg alg; | ||||||
|  | 	enum spu_cipher_mode mode; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct auth_op { | ||||||
|  | 	enum hash_alg alg; | ||||||
|  | 	enum hash_mode mode; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct iproc_alg_s { | ||||||
|  | 	u32 type; | ||||||
|  | 	union { | ||||||
|  | 		struct crypto_alg crypto; | ||||||
|  | 		struct ahash_alg hash; | ||||||
|  | 		struct aead_alg aead; | ||||||
|  | 	} alg; | ||||||
|  | 	struct cipher_op cipher_info; | ||||||
|  | 	struct auth_op auth_info; | ||||||
|  | 	bool auth_first; | ||||||
|  | 	bool registered; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Buffers for a SPU request/reply message pair. All part of one structure to | ||||||
|  |  * allow a single alloc per request. | ||||||
|  |  */ | ||||||
|  | struct spu_msg_buf { | ||||||
|  | 	/* Request message fragments */ | ||||||
|  | 
 | ||||||
|  | 	/*
 | ||||||
|  | 	 * SPU request message header. For SPU-M, holds MH, EMH, SCTX, BDESC, | ||||||
|  | 	 * and BD header. For SPU2, holds FMD, OMD. | ||||||
|  | 	 */ | ||||||
|  | 	u8 bcm_spu_req_hdr[ALIGN(SPU2_HEADER_ALLOC_LEN, SPU_MSG_ALIGN)]; | ||||||
|  | 
 | ||||||
|  | 	/* IV or counter. Size to include salt. Also used for XTS tweek. */ | ||||||
|  | 	u8 iv_ctr[ALIGN(2 * AES_BLOCK_SIZE, SPU_MSG_ALIGN)]; | ||||||
|  | 
 | ||||||
|  | 	/* Hash digest. request and response. */ | ||||||
|  | 	u8 digest[ALIGN(MAX_DIGEST_SIZE, SPU_MSG_ALIGN)]; | ||||||
|  | 
 | ||||||
|  | 	/* SPU request message padding */ | ||||||
|  | 	u8 spu_req_pad[ALIGN(SPU_PAD_LEN_MAX, SPU_MSG_ALIGN)]; | ||||||
|  | 
 | ||||||
|  | 	/* SPU-M request message STATUS field */ | ||||||
|  | 	u8 tx_stat[ALIGN(SPU_TX_STATUS_LEN, SPU_MSG_ALIGN)]; | ||||||
|  | 
 | ||||||
|  | 	/* Response message fragments */ | ||||||
|  | 
 | ||||||
|  | 	/* SPU response message header */ | ||||||
|  | 	u8 spu_resp_hdr[ALIGN(SPU2_HEADER_ALLOC_LEN, SPU_MSG_ALIGN)]; | ||||||
|  | 
 | ||||||
|  | 	/* SPU response message STATUS field padding */ | ||||||
|  | 	u8 rx_stat_pad[ALIGN(SPU_STAT_PAD_MAX, SPU_MSG_ALIGN)]; | ||||||
|  | 
 | ||||||
|  | 	/* SPU response message STATUS field */ | ||||||
|  | 	u8 rx_stat[ALIGN(SPU_RX_STATUS_LEN, SPU_MSG_ALIGN)]; | ||||||
|  | 
 | ||||||
|  | 	union { | ||||||
|  | 		/* Buffers only used for ablkcipher */ | ||||||
|  | 		struct { | ||||||
|  | 			/*
 | ||||||
|  | 			 * Field used for either SUPDT when RC4 is used | ||||||
|  | 			 * -OR- tweak value when XTS/AES is used | ||||||
|  | 			 */ | ||||||
|  | 			u8 supdt_tweak[ALIGN(SPU_SUPDT_LEN, SPU_MSG_ALIGN)]; | ||||||
|  | 		} c; | ||||||
|  | 
 | ||||||
|  | 		/* Buffers only used for aead */ | ||||||
|  | 		struct { | ||||||
|  | 			/* SPU response pad for GCM data */ | ||||||
|  | 			u8 gcmpad[ALIGN(AES_BLOCK_SIZE, SPU_MSG_ALIGN)]; | ||||||
|  | 
 | ||||||
|  | 			/* SPU request msg padding for GCM AAD */ | ||||||
|  | 			u8 req_aad_pad[ALIGN(SPU_PAD_LEN_MAX, SPU_MSG_ALIGN)]; | ||||||
|  | 
 | ||||||
|  | 			/* SPU response data to be discarded */ | ||||||
|  | 			u8 resp_aad[ALIGN(MAX_ASSOC_SIZE + MAX_IV_SIZE, | ||||||
|  | 					  SPU_MSG_ALIGN)]; | ||||||
|  | 		} a; | ||||||
|  | 	}; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct iproc_ctx_s { | ||||||
|  | 	u8 enckey[MAX_KEY_SIZE + ARC4_STATE_SIZE]; | ||||||
|  | 	unsigned int enckeylen; | ||||||
|  | 
 | ||||||
|  | 	u8 authkey[MAX_KEY_SIZE + ARC4_STATE_SIZE]; | ||||||
|  | 	unsigned int authkeylen; | ||||||
|  | 
 | ||||||
|  | 	u8 salt[MAX_SALT_SIZE]; | ||||||
|  | 	unsigned int salt_len; | ||||||
|  | 	unsigned int salt_offset; | ||||||
|  | 	u8 iv[MAX_IV_SIZE]; | ||||||
|  | 
 | ||||||
|  | 	unsigned int digestsize; | ||||||
|  | 
 | ||||||
|  | 	struct iproc_alg_s *alg; | ||||||
|  | 	bool is_esp; | ||||||
|  | 
 | ||||||
|  | 	struct cipher_op cipher; | ||||||
|  | 	enum spu_cipher_type cipher_type; | ||||||
|  | 
 | ||||||
|  | 	struct auth_op auth; | ||||||
|  | 	bool auth_first; | ||||||
|  | 
 | ||||||
|  | 	/*
 | ||||||
|  | 	 * The maximum length in bytes of the payload in a SPU message for this | ||||||
|  | 	 * context. For SPU-M, the payload is the combination of AAD and data. | ||||||
|  | 	 * For SPU2, the payload is just data. A value of SPU_MAX_PAYLOAD_INF | ||||||
|  | 	 * indicates that there is no limit to the length of the SPU message | ||||||
|  | 	 * payload. | ||||||
|  | 	 */ | ||||||
|  | 	unsigned int max_payload; | ||||||
|  | 
 | ||||||
|  | 	struct crypto_aead *fallback_cipher; | ||||||
|  | 
 | ||||||
|  | 	/* auth_type is determined during processing of request */ | ||||||
|  | 
 | ||||||
|  | 	u8 ipad[MAX_HASH_BLOCK_SIZE]; | ||||||
|  | 	u8 opad[MAX_HASH_BLOCK_SIZE]; | ||||||
|  | 
 | ||||||
|  | 	/*
 | ||||||
|  | 	 * Buffer to hold SPU message header template. Template is created at | ||||||
|  | 	 * setkey time for ablkcipher requests, since most of the fields in the | ||||||
|  | 	 * header are known at that time. At request time, just fill in a few | ||||||
|  | 	 * missing pieces related to length of data in the request and IVs, etc. | ||||||
|  | 	 */ | ||||||
|  | 	u8 bcm_spu_req_hdr[ALIGN(SPU2_HEADER_ALLOC_LEN, SPU_MSG_ALIGN)]; | ||||||
|  | 
 | ||||||
|  | 	/* Length of SPU request header */ | ||||||
|  | 	u16 spu_req_hdr_len; | ||||||
|  | 
 | ||||||
|  | 	/* Expected length of SPU response header */ | ||||||
|  | 	u16 spu_resp_hdr_len; | ||||||
|  | 
 | ||||||
|  | 	/*
 | ||||||
|  | 	 * shash descriptor - needed to perform incremental hashing in | ||||||
|  | 	 * in software, when hw doesn't support it. | ||||||
|  | 	 */ | ||||||
|  | 	struct shash_desc *shash; | ||||||
|  | 
 | ||||||
|  | 	bool is_rfc4543;	/* RFC 4543 style of GMAC */ | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /* state from iproc_reqctx_s necessary for hash state export/import */ | ||||||
|  | struct spu_hash_export_s { | ||||||
|  | 	unsigned int total_todo; | ||||||
|  | 	unsigned int total_sent; | ||||||
|  | 	u8 hash_carry[HASH_CARRY_MAX]; | ||||||
|  | 	unsigned int hash_carry_len; | ||||||
|  | 	u8 incr_hash[MAX_DIGEST_SIZE]; | ||||||
|  | 	bool is_sw_hmac; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct iproc_reqctx_s { | ||||||
|  | 	/* general context */ | ||||||
|  | 	struct crypto_async_request *parent; | ||||||
|  | 
 | ||||||
|  | 	/* only valid after enqueue() */ | ||||||
|  | 	struct iproc_ctx_s *ctx; | ||||||
|  | 
 | ||||||
|  | 	u8 chan_idx;   /* Mailbox channel to be used to submit this request */ | ||||||
|  | 
 | ||||||
|  | 	/* total todo, rx'd, and sent for this request */ | ||||||
|  | 	unsigned int total_todo; | ||||||
|  | 	unsigned int total_received;	/* only valid for ablkcipher */ | ||||||
|  | 	unsigned int total_sent; | ||||||
|  | 
 | ||||||
|  | 	/*
 | ||||||
|  | 	 * num bytes sent to hw from the src sg in this request. This can differ | ||||||
|  | 	 * from total_sent for incremental hashing. total_sent includes previous | ||||||
|  | 	 * init() and update() data. src_sent does not. | ||||||
|  | 	 */ | ||||||
|  | 	unsigned int src_sent; | ||||||
|  | 
 | ||||||
|  | 	/*
 | ||||||
|  | 	 * For AEAD requests, start of associated data. This will typically | ||||||
|  | 	 * point to the beginning of the src scatterlist from the request, | ||||||
|  | 	 * since assoc data is at the beginning of the src scatterlist rather | ||||||
|  | 	 * than in its own sg. | ||||||
|  | 	 */ | ||||||
|  | 	struct scatterlist *assoc; | ||||||
|  | 
 | ||||||
|  | 	/*
 | ||||||
|  | 	 * scatterlist entry and offset to start of data for next chunk. Crypto | ||||||
|  | 	 * API src scatterlist for AEAD starts with AAD, if present. For first | ||||||
|  | 	 * chunk, src_sg is sg entry at beginning of input data (after AAD). | ||||||
|  | 	 * src_skip begins at the offset in that sg entry where data begins. | ||||||
|  | 	 */ | ||||||
|  | 	struct scatterlist *src_sg; | ||||||
|  | 	int src_nents;		/* Number of src entries with data */ | ||||||
|  | 	u32 src_skip;		/* bytes of current sg entry already used */ | ||||||
|  | 
 | ||||||
|  | 	/*
 | ||||||
|  | 	 * Same for destination. For AEAD, if there is AAD, output data must | ||||||
|  | 	 * be written at offset following AAD. | ||||||
|  | 	 */ | ||||||
|  | 	struct scatterlist *dst_sg; | ||||||
|  | 	int dst_nents;		/* Number of dst entries with data */ | ||||||
|  | 	u32 dst_skip;		/* bytes of current sg entry already written */ | ||||||
|  | 
 | ||||||
|  | 	/* Mailbox message used to send this request to PDC driver */ | ||||||
|  | 	struct brcm_message mb_mssg; | ||||||
|  | 
 | ||||||
|  | 	bool bd_suppress;	/* suppress BD field in SPU response? */ | ||||||
|  | 
 | ||||||
|  | 	/* cipher context */ | ||||||
|  | 	bool is_encrypt; | ||||||
|  | 
 | ||||||
|  | 	/*
 | ||||||
|  | 	 * CBC mode: IV.  CTR mode: counter.  Else empty. Used as a DMA | ||||||
|  | 	 * buffer for AEAD requests. So allocate as DMAable memory. If IV | ||||||
|  | 	 * concatenated with salt, includes the salt. | ||||||
|  | 	 */ | ||||||
|  | 	u8 *iv_ctr; | ||||||
|  | 	/* Length of IV or counter, in bytes */ | ||||||
|  | 	unsigned int iv_ctr_len; | ||||||
|  | 
 | ||||||
|  | 	/*
 | ||||||
|  | 	 * Hash requests can be of any size, whether initial, update, or final. | ||||||
|  | 	 * A non-final request must be submitted to the SPU as an integral | ||||||
|  | 	 * number of blocks. This may leave data at the end of the request | ||||||
|  | 	 * that is not a full block. Since the request is non-final, it cannot | ||||||
|  | 	 * be padded. So, we write the remainder to this hash_carry buffer and | ||||||
|  | 	 * hold it until the next request arrives. The carry data is then | ||||||
|  | 	 * submitted at the beginning of the data in the next SPU msg. | ||||||
|  | 	 * hash_carry_len is the number of bytes currently in hash_carry. These | ||||||
|  | 	 * fields are only used for ahash requests. | ||||||
|  | 	 */ | ||||||
|  | 	u8 hash_carry[HASH_CARRY_MAX]; | ||||||
|  | 	unsigned int hash_carry_len; | ||||||
|  | 	unsigned int is_final;	/* is this the final for the hash op? */ | ||||||
|  | 
 | ||||||
|  | 	/*
 | ||||||
|  | 	 * Digest from incremental hash is saved here to include in next hash | ||||||
|  | 	 * operation. Cannot be stored in req->result for truncated hashes, | ||||||
|  | 	 * since result may be sized for final digest. Cannot be saved in | ||||||
|  | 	 * msg_buf because that gets deleted between incremental hash ops | ||||||
|  | 	 * and is not saved as part of export(). | ||||||
|  | 	 */ | ||||||
|  | 	u8 incr_hash[MAX_DIGEST_SIZE]; | ||||||
|  | 
 | ||||||
|  | 	/* hmac context */ | ||||||
|  | 	bool is_sw_hmac; | ||||||
|  | 
 | ||||||
|  | 	/* aead context */ | ||||||
|  | 	struct crypto_tfm *old_tfm; | ||||||
|  | 	crypto_completion_t old_complete; | ||||||
|  | 	void *old_data; | ||||||
|  | 
 | ||||||
|  | 	gfp_t gfp; | ||||||
|  | 
 | ||||||
|  | 	/* Buffers used to build SPU request and response messages */ | ||||||
|  | 	struct spu_msg_buf msg_buf; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Structure encapsulates a set of function pointers specific to the type of | ||||||
|  |  * SPU hardware running. These functions handling creation and parsing of | ||||||
|  |  * SPU request messages and SPU response messages. Includes hardware-specific | ||||||
|  |  * values read from device tree. | ||||||
|  |  */ | ||||||
|  | struct spu_hw { | ||||||
|  | 	void (*spu_dump_msg_hdr)(u8 *buf, unsigned int buf_len); | ||||||
|  | 	u32 (*spu_ctx_max_payload)(enum spu_cipher_alg cipher_alg, | ||||||
|  | 				   enum spu_cipher_mode cipher_mode, | ||||||
|  | 				   unsigned int blocksize); | ||||||
|  | 	u32 (*spu_payload_length)(u8 *spu_hdr); | ||||||
|  | 	u16 (*spu_response_hdr_len)(u16 auth_key_len, u16 enc_key_len, | ||||||
|  | 				    bool is_hash); | ||||||
|  | 	u16 (*spu_hash_pad_len)(enum hash_alg hash_alg, | ||||||
|  | 				enum hash_mode hash_mode, u32 chunksize, | ||||||
|  | 				u16 hash_block_size); | ||||||
|  | 	u32 (*spu_gcm_ccm_pad_len)(enum spu_cipher_mode cipher_mode, | ||||||
|  | 				   unsigned int data_size); | ||||||
|  | 	u32 (*spu_assoc_resp_len)(enum spu_cipher_mode cipher_mode, | ||||||
|  | 				  unsigned int assoc_len, | ||||||
|  | 				  unsigned int iv_len, bool is_encrypt); | ||||||
|  | 	u8 (*spu_aead_ivlen)(enum spu_cipher_mode cipher_mode, | ||||||
|  | 			     u16 iv_len); | ||||||
|  | 	enum hash_type (*spu_hash_type)(u32 src_sent); | ||||||
|  | 	u32 (*spu_digest_size)(u32 digest_size, enum hash_alg alg, | ||||||
|  | 			       enum hash_type); | ||||||
|  | 	u32 (*spu_create_request)(u8 *spu_hdr, | ||||||
|  | 				  struct spu_request_opts *req_opts, | ||||||
|  | 				  struct spu_cipher_parms *cipher_parms, | ||||||
|  | 				  struct spu_hash_parms *hash_parms, | ||||||
|  | 				  struct spu_aead_parms *aead_parms, | ||||||
|  | 				  unsigned int data_size); | ||||||
|  | 	u16 (*spu_cipher_req_init)(u8 *spu_hdr, | ||||||
|  | 				   struct spu_cipher_parms *cipher_parms); | ||||||
|  | 	void (*spu_cipher_req_finish)(u8 *spu_hdr, | ||||||
|  | 				      u16 spu_req_hdr_len, | ||||||
|  | 				      unsigned int is_inbound, | ||||||
|  | 				      struct spu_cipher_parms *cipher_parms, | ||||||
|  | 				      bool update_key, | ||||||
|  | 				      unsigned int data_size); | ||||||
|  | 	void (*spu_request_pad)(u8 *pad_start, u32 gcm_padding, | ||||||
|  | 				u32 hash_pad_len, enum hash_alg auth_alg, | ||||||
|  | 				enum hash_mode auth_mode, | ||||||
|  | 				unsigned int total_sent, u32 status_padding); | ||||||
|  | 	u8 (*spu_xts_tweak_in_payload)(void); | ||||||
|  | 	u8 (*spu_tx_status_len)(void); | ||||||
|  | 	u8 (*spu_rx_status_len)(void); | ||||||
|  | 	int (*spu_status_process)(u8 *statp); | ||||||
|  | 	void (*spu_ccm_update_iv)(unsigned int digestsize, | ||||||
|  | 				  struct spu_cipher_parms *cipher_parms, | ||||||
|  | 				  unsigned int assoclen, unsigned int chunksize, | ||||||
|  | 				  bool is_encrypt, bool is_esp); | ||||||
|  | 	u32 (*spu_wordalign_padlen)(u32 data_size); | ||||||
|  | 
 | ||||||
|  | 	/* The base virtual address of the SPU hw registers */ | ||||||
|  | 	void __iomem *reg_vbase[MAX_SPUS]; | ||||||
|  | 
 | ||||||
|  | 	/* Version of the SPU hardware */ | ||||||
|  | 	enum spu_spu_type spu_type; | ||||||
|  | 
 | ||||||
|  | 	/* Sub-version of the SPU hardware */ | ||||||
|  | 	enum spu_spu_subtype spu_subtype; | ||||||
|  | 
 | ||||||
|  | 	/* The number of SPUs on this platform */ | ||||||
|  | 	u32 num_spu; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct device_private { | ||||||
|  | 	struct platform_device *pdev[MAX_SPUS]; | ||||||
|  | 
 | ||||||
|  | 	struct spu_hw spu; | ||||||
|  | 
 | ||||||
|  | 	atomic_t session_count;	/* number of streams active */ | ||||||
|  | 	atomic_t stream_count;	/* monotonic counter for streamID's */ | ||||||
|  | 
 | ||||||
|  | 	/* Length of BCM header. Set to 0 when hw does not expect BCM HEADER. */ | ||||||
|  | 	u8 bcm_hdr_len; | ||||||
|  | 
 | ||||||
|  | 	/* The index of the channel to use for the next crypto request */ | ||||||
|  | 	atomic_t next_chan; | ||||||
|  | 
 | ||||||
|  | 	struct dentry *debugfs_dir; | ||||||
|  | 	struct dentry *debugfs_stats; | ||||||
|  | 
 | ||||||
|  | 	/* Number of request bytes processed and result bytes returned */ | ||||||
|  | 	atomic64_t bytes_in; | ||||||
|  | 	atomic64_t bytes_out; | ||||||
|  | 
 | ||||||
|  | 	/* Number of operations of each type */ | ||||||
|  | 	atomic_t op_counts[SPU_OP_NUM]; | ||||||
|  | 
 | ||||||
|  | 	atomic_t cipher_cnt[CIPHER_ALG_LAST][CIPHER_MODE_LAST]; | ||||||
|  | 	atomic_t hash_cnt[HASH_ALG_LAST]; | ||||||
|  | 	atomic_t hmac_cnt[HASH_ALG_LAST]; | ||||||
|  | 	atomic_t aead_cnt[AEAD_TYPE_LAST]; | ||||||
|  | 
 | ||||||
|  | 	/* Number of calls to setkey() for each operation type */ | ||||||
|  | 	atomic_t setkey_cnt[SPU_OP_NUM]; | ||||||
|  | 
 | ||||||
|  | 	/* Number of times request was resubmitted because mb was full */ | ||||||
|  | 	atomic_t mb_no_spc; | ||||||
|  | 
 | ||||||
|  | 	/* Number of mailbox send failures */ | ||||||
|  | 	atomic_t mb_send_fail; | ||||||
|  | 
 | ||||||
|  | 	/* Number of ICV check failures for AEAD messages */ | ||||||
|  | 	atomic_t bad_icv; | ||||||
|  | 
 | ||||||
|  | 	struct mbox_client mcl[MAX_SPUS]; | ||||||
|  | 	/* Array of mailbox channel pointers, one for each channel */ | ||||||
|  | 	struct mbox_chan *mbox[MAX_SPUS]; | ||||||
|  | 
 | ||||||
|  | 	/* Driver initialized */ | ||||||
|  | 	bool inited; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | extern struct device_private iproc_priv; | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
							
								
								
									
										1251
									
								
								drivers/crypto/bcm/spu.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1251
									
								
								drivers/crypto/bcm/spu.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										287
									
								
								drivers/crypto/bcm/spu.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										287
									
								
								drivers/crypto/bcm/spu.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,287 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright 2016 Broadcom | ||||||
|  |  * | ||||||
|  |  * This program is free software; you can redistribute it and/or modify | ||||||
|  |  * it under the terms of the GNU General Public License, version 2, as | ||||||
|  |  * published by the Free Software Foundation (the "GPL"). | ||||||
|  |  * | ||||||
|  |  * This program is distributed in the hope that it will be useful, but | ||||||
|  |  * WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||||
|  |  * General Public License version 2 (GPLv2) for more details. | ||||||
|  |  * | ||||||
|  |  * You should have received a copy of the GNU General Public License | ||||||
|  |  * version 2 (GPLv2) along with this source code. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * This file contains the definition of SPU messages. There are currently two | ||||||
|  |  * SPU message formats: SPU-M and SPU2. The hardware uses different values to | ||||||
|  |  * identify the same things in SPU-M vs SPU2. So this file defines values that | ||||||
|  |  * are hardware independent. Software can use these values for any version of | ||||||
|  |  * SPU hardware. These values are used in APIs in spu.c. Functions internal to | ||||||
|  |  * spu.c and spu2.c convert these to hardware-specific values. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #ifndef _SPU_H | ||||||
|  | #define _SPU_H | ||||||
|  | 
 | ||||||
|  | #include <linux/types.h> | ||||||
|  | #include <linux/scatterlist.h> | ||||||
|  | #include <crypto/sha.h> | ||||||
|  | 
 | ||||||
|  | enum spu_cipher_alg { | ||||||
|  | 	CIPHER_ALG_NONE = 0x0, | ||||||
|  | 	CIPHER_ALG_RC4 = 0x1, | ||||||
|  | 	CIPHER_ALG_DES = 0x2, | ||||||
|  | 	CIPHER_ALG_3DES = 0x3, | ||||||
|  | 	CIPHER_ALG_AES = 0x4, | ||||||
|  | 	CIPHER_ALG_LAST = 0x5 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | enum spu_cipher_mode { | ||||||
|  | 	CIPHER_MODE_NONE = 0x0, | ||||||
|  | 	CIPHER_MODE_ECB = 0x0, | ||||||
|  | 	CIPHER_MODE_CBC = 0x1, | ||||||
|  | 	CIPHER_MODE_OFB = 0x2, | ||||||
|  | 	CIPHER_MODE_CFB = 0x3, | ||||||
|  | 	CIPHER_MODE_CTR = 0x4, | ||||||
|  | 	CIPHER_MODE_CCM = 0x5, | ||||||
|  | 	CIPHER_MODE_GCM = 0x6, | ||||||
|  | 	CIPHER_MODE_XTS = 0x7, | ||||||
|  | 	CIPHER_MODE_LAST = 0x8 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | enum spu_cipher_type { | ||||||
|  | 	CIPHER_TYPE_NONE = 0x0, | ||||||
|  | 	CIPHER_TYPE_DES = 0x0, | ||||||
|  | 	CIPHER_TYPE_3DES = 0x0, | ||||||
|  | 	CIPHER_TYPE_INIT = 0x0,	/* used for ARC4 */ | ||||||
|  | 	CIPHER_TYPE_AES128 = 0x0, | ||||||
|  | 	CIPHER_TYPE_AES192 = 0x1, | ||||||
|  | 	CIPHER_TYPE_UPDT = 0x1,	/* used for ARC4 */ | ||||||
|  | 	CIPHER_TYPE_AES256 = 0x2, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | enum hash_alg { | ||||||
|  | 	HASH_ALG_NONE = 0x0, | ||||||
|  | 	HASH_ALG_MD5 = 0x1, | ||||||
|  | 	HASH_ALG_SHA1 = 0x2, | ||||||
|  | 	HASH_ALG_SHA224 = 0x3, | ||||||
|  | 	HASH_ALG_SHA256 = 0x4, | ||||||
|  | 	HASH_ALG_AES = 0x5, | ||||||
|  | 	HASH_ALG_SHA384 = 0x6, | ||||||
|  | 	HASH_ALG_SHA512 = 0x7, | ||||||
|  | 	/* Keep SHA3 algorithms at the end always */ | ||||||
|  | 	HASH_ALG_SHA3_224 = 0x8, | ||||||
|  | 	HASH_ALG_SHA3_256 = 0x9, | ||||||
|  | 	HASH_ALG_SHA3_384 = 0xa, | ||||||
|  | 	HASH_ALG_SHA3_512 = 0xb, | ||||||
|  | 	HASH_ALG_LAST | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | enum hash_mode { | ||||||
|  | 	HASH_MODE_NONE = 0x0, | ||||||
|  | 	HASH_MODE_HASH = 0x0, | ||||||
|  | 	HASH_MODE_XCBC = 0x0, | ||||||
|  | 	HASH_MODE_CMAC = 0x1, | ||||||
|  | 	HASH_MODE_CTXT = 0x1, | ||||||
|  | 	HASH_MODE_HMAC = 0x2, | ||||||
|  | 	HASH_MODE_RABIN = 0x4, | ||||||
|  | 	HASH_MODE_FHMAC = 0x6, | ||||||
|  | 	HASH_MODE_CCM = 0x5, | ||||||
|  | 	HASH_MODE_GCM = 0x6, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | enum hash_type { | ||||||
|  | 	HASH_TYPE_NONE = 0x0, | ||||||
|  | 	HASH_TYPE_FULL = 0x0, | ||||||
|  | 	HASH_TYPE_INIT = 0x1, | ||||||
|  | 	HASH_TYPE_UPDT = 0x2, | ||||||
|  | 	HASH_TYPE_FIN = 0x3, | ||||||
|  | 	HASH_TYPE_AES128 = 0x0, | ||||||
|  | 	HASH_TYPE_AES192 = 0x1, | ||||||
|  | 	HASH_TYPE_AES256 = 0x2 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | enum aead_type { | ||||||
|  | 	AES_CCM, | ||||||
|  | 	AES_GCM, | ||||||
|  | 	AUTHENC, | ||||||
|  | 	AEAD_TYPE_LAST | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | extern char *hash_alg_name[HASH_ALG_LAST]; | ||||||
|  | extern char *aead_alg_name[AEAD_TYPE_LAST]; | ||||||
|  | 
 | ||||||
|  | struct spu_request_opts { | ||||||
|  | 	bool is_inbound; | ||||||
|  | 	bool auth_first; | ||||||
|  | 	bool is_aead; | ||||||
|  | 	bool is_esp; | ||||||
|  | 	bool bd_suppress; | ||||||
|  | 	bool is_rfc4543; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct spu_cipher_parms { | ||||||
|  | 	enum spu_cipher_alg  alg; | ||||||
|  | 	enum spu_cipher_mode mode; | ||||||
|  | 	enum spu_cipher_type type; | ||||||
|  | 	u8                  *key_buf; | ||||||
|  | 	u16                  key_len; | ||||||
|  | 	/* iv_buf and iv_len include salt, if applicable */ | ||||||
|  | 	u8                  *iv_buf; | ||||||
|  | 	u16                  iv_len; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct spu_hash_parms { | ||||||
|  | 	enum hash_alg  alg; | ||||||
|  | 	enum hash_mode mode; | ||||||
|  | 	enum hash_type type; | ||||||
|  | 	u8             digestsize; | ||||||
|  | 	u8            *key_buf; | ||||||
|  | 	u16            key_len; | ||||||
|  | 	u16            prebuf_len; | ||||||
|  | 	/* length of hash pad. signed, needs to handle roll-overs */ | ||||||
|  | 	int            pad_len; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct spu_aead_parms { | ||||||
|  | 	u32 assoc_size; | ||||||
|  | 	u16 iv_len;      /* length of IV field between assoc data and data */ | ||||||
|  | 	u8  aad_pad_len; /* For AES GCM/CCM, length of padding after AAD */ | ||||||
|  | 	u8  data_pad_len;/* For AES GCM/CCM, length of padding after data */ | ||||||
|  | 	bool return_iv;  /* True if SPU should return an IV */ | ||||||
|  | 	u32 ret_iv_len;  /* Length in bytes of returned IV */ | ||||||
|  | 	u32 ret_iv_off;  /* Offset into full IV if partial IV returned */ | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /************** SPU sizes ***************/ | ||||||
|  | 
 | ||||||
|  | #define SPU_RX_STATUS_LEN  4 | ||||||
|  | 
 | ||||||
|  | /* Max length of padding for 4-byte alignment of STATUS field */ | ||||||
|  | #define SPU_STAT_PAD_MAX  4 | ||||||
|  | 
 | ||||||
|  | /* Max length of pad fragment. 4 is for 4-byte alignment of STATUS field */ | ||||||
|  | #define SPU_PAD_LEN_MAX (SPU_GCM_CCM_ALIGN + MAX_HASH_BLOCK_SIZE + \ | ||||||
|  | 			 SPU_STAT_PAD_MAX) | ||||||
|  | 
 | ||||||
|  | /* GCM and CCM require 16-byte alignment */ | ||||||
|  | #define SPU_GCM_CCM_ALIGN 16 | ||||||
|  | 
 | ||||||
|  | /* Length up SUPDT field in SPU response message for RC4 */ | ||||||
|  | #define SPU_SUPDT_LEN 260 | ||||||
|  | 
 | ||||||
|  | /* SPU status error codes. These used as common error codes across all
 | ||||||
|  |  * SPU variants. | ||||||
|  |  */ | ||||||
|  | #define SPU_INVALID_ICV  1 | ||||||
|  | 
 | ||||||
|  | /* Indicates no limit to the length of the payload in a SPU message */ | ||||||
|  | #define SPU_MAX_PAYLOAD_INF  0xFFFFFFFF | ||||||
|  | 
 | ||||||
|  | /* Size of XTS tweak ("i" parameter), in bytes */ | ||||||
|  | #define SPU_XTS_TWEAK_SIZE 16 | ||||||
|  | 
 | ||||||
|  | /* CCM B_0 field definitions, common for SPU-M and SPU2 */ | ||||||
|  | #define CCM_B0_ADATA		0x40 | ||||||
|  | #define CCM_B0_ADATA_SHIFT	   6 | ||||||
|  | #define CCM_B0_M_PRIME		0x38 | ||||||
|  | #define CCM_B0_M_PRIME_SHIFT	   3 | ||||||
|  | #define CCM_B0_L_PRIME		0x07 | ||||||
|  | #define CCM_B0_L_PRIME_SHIFT	   0 | ||||||
|  | #define CCM_ESP_L_VALUE		   4 | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * spu_req_incl_icv() - Return true if SPU request message should include the | ||||||
|  |  * ICV as a separate buffer. | ||||||
|  |  * @cipher_mode:  the cipher mode being requested | ||||||
|  |  * @is_encrypt:   true if encrypting. false if decrypting. | ||||||
|  |  * | ||||||
|  |  * Return:  true if ICV to be included as separate buffer | ||||||
|  |  */ | ||||||
|  | static __always_inline  bool spu_req_incl_icv(enum spu_cipher_mode cipher_mode, | ||||||
|  | 					      bool is_encrypt) | ||||||
|  | { | ||||||
|  | 	if ((cipher_mode == CIPHER_MODE_GCM) && !is_encrypt) | ||||||
|  | 		return true; | ||||||
|  | 	if ((cipher_mode == CIPHER_MODE_CCM) && !is_encrypt) | ||||||
|  | 		return true; | ||||||
|  | 
 | ||||||
|  | 	return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static __always_inline u32 spu_real_db_size(u32 assoc_size, | ||||||
|  | 					    u32 aead_iv_buf_len, | ||||||
|  | 					    u32 prebuf_len, | ||||||
|  | 					    u32 data_size, | ||||||
|  | 					    u32 aad_pad_len, | ||||||
|  | 					    u32 gcm_pad_len, | ||||||
|  | 					    u32 hash_pad_len) | ||||||
|  | { | ||||||
|  | 	return assoc_size + aead_iv_buf_len + prebuf_len + data_size + | ||||||
|  | 	    aad_pad_len + gcm_pad_len + hash_pad_len; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /************** SPU Functions Prototypes **************/ | ||||||
|  | 
 | ||||||
|  | void spum_dump_msg_hdr(u8 *buf, unsigned int buf_len); | ||||||
|  | 
 | ||||||
|  | u32 spum_ns2_ctx_max_payload(enum spu_cipher_alg cipher_alg, | ||||||
|  | 			     enum spu_cipher_mode cipher_mode, | ||||||
|  | 			     unsigned int blocksize); | ||||||
|  | u32 spum_nsp_ctx_max_payload(enum spu_cipher_alg cipher_alg, | ||||||
|  | 			     enum spu_cipher_mode cipher_mode, | ||||||
|  | 			     unsigned int blocksize); | ||||||
|  | u32 spum_payload_length(u8 *spu_hdr); | ||||||
|  | u16 spum_response_hdr_len(u16 auth_key_len, u16 enc_key_len, bool is_hash); | ||||||
|  | u16 spum_hash_pad_len(enum hash_alg hash_alg, enum hash_mode hash_mode, | ||||||
|  | 		      u32 chunksize, u16 hash_block_size); | ||||||
|  | u32 spum_gcm_ccm_pad_len(enum spu_cipher_mode cipher_mode, | ||||||
|  | 			 unsigned int data_size); | ||||||
|  | u32 spum_assoc_resp_len(enum spu_cipher_mode cipher_mode, | ||||||
|  | 			unsigned int assoc_len, unsigned int iv_len, | ||||||
|  | 			bool is_encrypt); | ||||||
|  | u8 spum_aead_ivlen(enum spu_cipher_mode cipher_mode, u16 iv_len); | ||||||
|  | bool spu_req_incl_icv(enum spu_cipher_mode cipher_mode, bool is_encrypt); | ||||||
|  | enum hash_type spum_hash_type(u32 src_sent); | ||||||
|  | u32 spum_digest_size(u32 alg_digest_size, enum hash_alg alg, | ||||||
|  | 		     enum hash_type htype); | ||||||
|  | 
 | ||||||
|  | u32 spum_create_request(u8 *spu_hdr, | ||||||
|  | 			struct spu_request_opts *req_opts, | ||||||
|  | 			struct spu_cipher_parms *cipher_parms, | ||||||
|  | 			struct spu_hash_parms *hash_parms, | ||||||
|  | 			struct spu_aead_parms *aead_parms, | ||||||
|  | 			unsigned int data_size); | ||||||
|  | 
 | ||||||
|  | u16 spum_cipher_req_init(u8 *spu_hdr, struct spu_cipher_parms *cipher_parms); | ||||||
|  | 
 | ||||||
|  | void spum_cipher_req_finish(u8 *spu_hdr, | ||||||
|  | 			    u16 spu_req_hdr_len, | ||||||
|  | 			    unsigned int is_inbound, | ||||||
|  | 			    struct spu_cipher_parms *cipher_parms, | ||||||
|  | 			    bool update_key, | ||||||
|  | 			    unsigned int data_size); | ||||||
|  | 
 | ||||||
|  | void spum_request_pad(u8 *pad_start, | ||||||
|  | 		      u32 gcm_padding, | ||||||
|  | 		      u32 hash_pad_len, | ||||||
|  | 		      enum hash_alg auth_alg, | ||||||
|  | 		      enum hash_mode auth_mode, | ||||||
|  | 		      unsigned int total_sent, u32 status_padding); | ||||||
|  | 
 | ||||||
|  | u8 spum_xts_tweak_in_payload(void); | ||||||
|  | u8 spum_tx_status_len(void); | ||||||
|  | u8 spum_rx_status_len(void); | ||||||
|  | int spum_status_process(u8 *statp); | ||||||
|  | 
 | ||||||
|  | void spum_ccm_update_iv(unsigned int digestsize, | ||||||
|  | 			struct spu_cipher_parms *cipher_parms, | ||||||
|  | 			unsigned int assoclen, | ||||||
|  | 			unsigned int chunksize, | ||||||
|  | 			bool is_encrypt, | ||||||
|  | 			bool is_esp); | ||||||
|  | u32 spum_wordalign_padlen(u32 data_size); | ||||||
|  | #endif | ||||||
							
								
								
									
										1401
									
								
								drivers/crypto/bcm/spu2.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1401
									
								
								drivers/crypto/bcm/spu2.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										228
									
								
								drivers/crypto/bcm/spu2.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										228
									
								
								drivers/crypto/bcm/spu2.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,228 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright 2016 Broadcom | ||||||
|  |  * | ||||||
|  |  * This program is free software; you can redistribute it and/or modify | ||||||
|  |  * it under the terms of the GNU General Public License, version 2, as | ||||||
|  |  * published by the Free Software Foundation (the "GPL"). | ||||||
|  |  * | ||||||
|  |  * This program is distributed in the hope that it will be useful, but | ||||||
|  |  * WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||||
|  |  * General Public License version 2 (GPLv2) for more details. | ||||||
|  |  * | ||||||
|  |  * You should have received a copy of the GNU General Public License | ||||||
|  |  * version 2 (GPLv2) along with this source code. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * This file contains SPU message definitions specific to SPU2. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #ifndef _SPU2_H | ||||||
|  | #define _SPU2_H | ||||||
|  | 
 | ||||||
|  | enum spu2_cipher_type { | ||||||
|  | 	SPU2_CIPHER_TYPE_NONE = 0x0, | ||||||
|  | 	SPU2_CIPHER_TYPE_AES128 = 0x1, | ||||||
|  | 	SPU2_CIPHER_TYPE_AES192 = 0x2, | ||||||
|  | 	SPU2_CIPHER_TYPE_AES256 = 0x3, | ||||||
|  | 	SPU2_CIPHER_TYPE_DES = 0x4, | ||||||
|  | 	SPU2_CIPHER_TYPE_3DES = 0x5, | ||||||
|  | 	SPU2_CIPHER_TYPE_LAST | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | enum spu2_cipher_mode { | ||||||
|  | 	SPU2_CIPHER_MODE_ECB = 0x0, | ||||||
|  | 	SPU2_CIPHER_MODE_CBC = 0x1, | ||||||
|  | 	SPU2_CIPHER_MODE_CTR = 0x2, | ||||||
|  | 	SPU2_CIPHER_MODE_CFB = 0x3, | ||||||
|  | 	SPU2_CIPHER_MODE_OFB = 0x4, | ||||||
|  | 	SPU2_CIPHER_MODE_XTS = 0x5, | ||||||
|  | 	SPU2_CIPHER_MODE_CCM = 0x6, | ||||||
|  | 	SPU2_CIPHER_MODE_GCM = 0x7, | ||||||
|  | 	SPU2_CIPHER_MODE_LAST | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | enum spu2_hash_type { | ||||||
|  | 	SPU2_HASH_TYPE_NONE = 0x0, | ||||||
|  | 	SPU2_HASH_TYPE_AES128 = 0x1, | ||||||
|  | 	SPU2_HASH_TYPE_AES192 = 0x2, | ||||||
|  | 	SPU2_HASH_TYPE_AES256 = 0x3, | ||||||
|  | 	SPU2_HASH_TYPE_MD5 = 0x6, | ||||||
|  | 	SPU2_HASH_TYPE_SHA1 = 0x7, | ||||||
|  | 	SPU2_HASH_TYPE_SHA224 = 0x8, | ||||||
|  | 	SPU2_HASH_TYPE_SHA256 = 0x9, | ||||||
|  | 	SPU2_HASH_TYPE_SHA384 = 0xa, | ||||||
|  | 	SPU2_HASH_TYPE_SHA512 = 0xb, | ||||||
|  | 	SPU2_HASH_TYPE_SHA512_224 = 0xc, | ||||||
|  | 	SPU2_HASH_TYPE_SHA512_256 = 0xd, | ||||||
|  | 	SPU2_HASH_TYPE_SHA3_224 = 0xe, | ||||||
|  | 	SPU2_HASH_TYPE_SHA3_256 = 0xf, | ||||||
|  | 	SPU2_HASH_TYPE_SHA3_384 = 0x10, | ||||||
|  | 	SPU2_HASH_TYPE_SHA3_512 = 0x11, | ||||||
|  | 	SPU2_HASH_TYPE_LAST | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | enum spu2_hash_mode { | ||||||
|  | 	SPU2_HASH_MODE_CMAC = 0x0, | ||||||
|  | 	SPU2_HASH_MODE_CBC_MAC = 0x1, | ||||||
|  | 	SPU2_HASH_MODE_XCBC_MAC = 0x2, | ||||||
|  | 	SPU2_HASH_MODE_HMAC = 0x3, | ||||||
|  | 	SPU2_HASH_MODE_RABIN = 0x4, | ||||||
|  | 	SPU2_HASH_MODE_CCM = 0x5, | ||||||
|  | 	SPU2_HASH_MODE_GCM = 0x6, | ||||||
|  | 	SPU2_HASH_MODE_RESERVED = 0x7, | ||||||
|  | 	SPU2_HASH_MODE_LAST | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | enum spu2_ret_md_opts { | ||||||
|  | 	SPU2_RET_NO_MD = 0,	/* return no metadata */ | ||||||
|  | 	SPU2_RET_FMD_OMD = 1,	/* return both FMD and OMD */ | ||||||
|  | 	SPU2_RET_FMD_ONLY = 2,	/* return only FMD */ | ||||||
|  | 	SPU2_RET_FMD_OMD_IV = 3,	/* return FMD and OMD with just IVs */ | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /* Fixed Metadata format */ | ||||||
|  | struct SPU2_FMD { | ||||||
|  | 	u64 ctrl0; | ||||||
|  | 	u64 ctrl1; | ||||||
|  | 	u64 ctrl2; | ||||||
|  | 	u64 ctrl3; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | #define FMD_SIZE  sizeof(struct SPU2_FMD) | ||||||
|  | 
 | ||||||
|  | /* Fixed part of request message header length in bytes. Just FMD. */ | ||||||
|  | #define SPU2_REQ_FIXED_LEN FMD_SIZE | ||||||
|  | #define SPU2_HEADER_ALLOC_LEN (SPU_REQ_FIXED_LEN + \ | ||||||
|  | 				2 * MAX_KEY_SIZE + 2 * MAX_IV_SIZE) | ||||||
|  | 
 | ||||||
|  | /* FMD ctrl0 field masks */ | ||||||
|  | #define SPU2_CIPH_ENCRYPT_EN            0x1 /* 0: decrypt, 1: encrypt */ | ||||||
|  | #define SPU2_CIPH_TYPE                 0xF0 /* one of spu2_cipher_type */ | ||||||
|  | #define SPU2_CIPH_TYPE_SHIFT              4 | ||||||
|  | #define SPU2_CIPH_MODE                0xF00 /* one of spu2_cipher_mode */ | ||||||
|  | #define SPU2_CIPH_MODE_SHIFT              8 | ||||||
|  | #define SPU2_CFB_MASK                0x7000 /* cipher feedback mask */ | ||||||
|  | #define SPU2_CFB_MASK_SHIFT              12 | ||||||
|  | #define SPU2_PROTO_SEL             0xF00000 /* MACsec, IPsec, TLS... */ | ||||||
|  | #define SPU2_PROTO_SEL_SHIFT             20 | ||||||
|  | #define SPU2_HASH_FIRST           0x1000000 /* 1: hash input is input pkt | ||||||
|  | 					     * data | ||||||
|  | 					     */ | ||||||
|  | #define SPU2_CHK_TAG              0x2000000 /* 1: check digest provided */ | ||||||
|  | #define SPU2_HASH_TYPE          0x1F0000000 /* one of spu2_hash_type */ | ||||||
|  | #define SPU2_HASH_TYPE_SHIFT             28 | ||||||
|  | #define SPU2_HASH_MODE         0xF000000000 /* one of spu2_hash_mode */ | ||||||
|  | #define SPU2_HASH_MODE_SHIFT             36 | ||||||
|  | #define SPU2_CIPH_PAD_EN     0x100000000000 /* 1: Add pad to end of payload for | ||||||
|  | 					     *    enc | ||||||
|  | 					     */ | ||||||
|  | #define SPU2_CIPH_PAD      0xFF000000000000 /* cipher pad value */ | ||||||
|  | #define SPU2_CIPH_PAD_SHIFT              48 | ||||||
|  | 
 | ||||||
|  | /* FMD ctrl1 field masks */ | ||||||
|  | #define SPU2_TAG_LOC                    0x1 /* 1: end of payload, 0: undef */ | ||||||
|  | #define SPU2_HAS_FR_DATA                0x2 /* 1: msg has frame data */ | ||||||
|  | #define SPU2_HAS_AAD1                   0x4 /* 1: msg has AAD1 field */ | ||||||
|  | #define SPU2_HAS_NAAD                   0x8 /* 1: msg has NAAD field */ | ||||||
|  | #define SPU2_HAS_AAD2                  0x10 /* 1: msg has AAD2 field */ | ||||||
|  | #define SPU2_HAS_ESN                   0x20 /* 1: msg has ESN field */ | ||||||
|  | #define SPU2_HASH_KEY_LEN            0xFF00 /* len of hash key in bytes. | ||||||
|  | 					     * HMAC only. | ||||||
|  | 					     */ | ||||||
|  | #define SPU2_HASH_KEY_LEN_SHIFT           8 | ||||||
|  | #define SPU2_CIPH_KEY_LEN         0xFF00000 /* len of cipher key in bytes */ | ||||||
|  | #define SPU2_CIPH_KEY_LEN_SHIFT          20 | ||||||
|  | #define SPU2_GENIV               0x10000000 /* 1: hw generates IV */ | ||||||
|  | #define SPU2_HASH_IV             0x20000000 /* 1: IV incl in hash */ | ||||||
|  | #define SPU2_RET_IV              0x40000000 /* 1: return IV in output msg | ||||||
|  | 					     *    b4 payload | ||||||
|  | 					     */ | ||||||
|  | #define SPU2_RET_IV_LEN         0xF00000000 /* length in bytes of IV returned. | ||||||
|  | 					     * 0 = 16 bytes | ||||||
|  | 					     */ | ||||||
|  | #define SPU2_RET_IV_LEN_SHIFT            32 | ||||||
|  | #define SPU2_IV_OFFSET         0xF000000000 /* gen IV offset */ | ||||||
|  | #define SPU2_IV_OFFSET_SHIFT             36 | ||||||
|  | #define SPU2_IV_LEN          0x1F0000000000 /* length of input IV in bytes */ | ||||||
|  | #define SPU2_IV_LEN_SHIFT                40 | ||||||
|  | #define SPU2_HASH_TAG_LEN  0x7F000000000000 /* hash tag length in bytes */ | ||||||
|  | #define SPU2_HASH_TAG_LEN_SHIFT          48 | ||||||
|  | #define SPU2_RETURN_MD    0x300000000000000 /* return metadata */ | ||||||
|  | #define SPU2_RETURN_MD_SHIFT             56 | ||||||
|  | #define SPU2_RETURN_FD    0x400000000000000 | ||||||
|  | #define SPU2_RETURN_AAD1  0x800000000000000 | ||||||
|  | #define SPU2_RETURN_NAAD 0x1000000000000000 | ||||||
|  | #define SPU2_RETURN_AAD2 0x2000000000000000 | ||||||
|  | #define SPU2_RETURN_PAY  0x4000000000000000 /* return payload */ | ||||||
|  | 
 | ||||||
|  | /* FMD ctrl2 field masks */ | ||||||
|  | #define SPU2_AAD1_OFFSET              0xFFF /* byte offset of AAD1 field */ | ||||||
|  | #define SPU2_AAD1_LEN               0xFF000 /* length of AAD1 in bytes */ | ||||||
|  | #define SPU2_AAD1_LEN_SHIFT              12 | ||||||
|  | #define SPU2_AAD2_OFFSET         0xFFF00000 /* byte offset of AAD2 field */ | ||||||
|  | #define SPU2_AAD2_OFFSET_SHIFT           20 | ||||||
|  | #define SPU2_PL_OFFSET   0xFFFFFFFF00000000 /* payload offset from AAD2 */ | ||||||
|  | #define SPU2_PL_OFFSET_SHIFT             32 | ||||||
|  | 
 | ||||||
|  | /* FMD ctrl3 field masks */ | ||||||
|  | #define SPU2_PL_LEN              0xFFFFFFFF /* payload length in bytes */ | ||||||
|  | #define SPU2_TLS_LEN         0xFFFF00000000 /* TLS encrypt: cipher len | ||||||
|  | 					     * TLS decrypt: compressed len | ||||||
|  | 					     */ | ||||||
|  | #define SPU2_TLS_LEN_SHIFT               32 | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Max value that can be represented in the Payload Length field of the | ||||||
|  |  * ctrl3 word of FMD. | ||||||
|  |  */ | ||||||
|  | #define SPU2_MAX_PAYLOAD  SPU2_PL_LEN | ||||||
|  | 
 | ||||||
|  | /* Error values returned in STATUS field of response messages */ | ||||||
|  | #define SPU2_INVALID_ICV  1 | ||||||
|  | 
 | ||||||
|  | void spu2_dump_msg_hdr(u8 *buf, unsigned int buf_len); | ||||||
|  | u32 spu2_ctx_max_payload(enum spu_cipher_alg cipher_alg, | ||||||
|  | 			 enum spu_cipher_mode cipher_mode, | ||||||
|  | 			 unsigned int blocksize); | ||||||
|  | u32 spu2_payload_length(u8 *spu_hdr); | ||||||
|  | u16 spu2_response_hdr_len(u16 auth_key_len, u16 enc_key_len, bool is_hash); | ||||||
|  | u16 spu2_hash_pad_len(enum hash_alg hash_alg, enum hash_mode hash_mode, | ||||||
|  | 		      u32 chunksize, u16 hash_block_size); | ||||||
|  | u32 spu2_gcm_ccm_pad_len(enum spu_cipher_mode cipher_mode, | ||||||
|  | 			 unsigned int data_size); | ||||||
|  | u32 spu2_assoc_resp_len(enum spu_cipher_mode cipher_mode, | ||||||
|  | 			unsigned int assoc_len, unsigned int iv_len, | ||||||
|  | 			bool is_encrypt); | ||||||
|  | u8 spu2_aead_ivlen(enum spu_cipher_mode cipher_mode, | ||||||
|  | 		   u16 iv_len); | ||||||
|  | enum hash_type spu2_hash_type(u32 src_sent); | ||||||
|  | u32 spu2_digest_size(u32 alg_digest_size, enum hash_alg alg, | ||||||
|  | 		     enum hash_type htype); | ||||||
|  | u32 spu2_create_request(u8 *spu_hdr, | ||||||
|  | 			struct spu_request_opts *req_opts, | ||||||
|  | 			struct spu_cipher_parms *cipher_parms, | ||||||
|  | 			struct spu_hash_parms *hash_parms, | ||||||
|  | 			struct spu_aead_parms *aead_parms, | ||||||
|  | 			unsigned int data_size); | ||||||
|  | u16 spu2_cipher_req_init(u8 *spu_hdr, struct spu_cipher_parms *cipher_parms); | ||||||
|  | void spu2_cipher_req_finish(u8 *spu_hdr, | ||||||
|  | 			    u16 spu_req_hdr_len, | ||||||
|  | 			    unsigned int is_inbound, | ||||||
|  | 			    struct spu_cipher_parms *cipher_parms, | ||||||
|  | 			    bool update_key, | ||||||
|  | 			    unsigned int data_size); | ||||||
|  | void spu2_request_pad(u8 *pad_start, u32 gcm_padding, u32 hash_pad_len, | ||||||
|  | 		      enum hash_alg auth_alg, enum hash_mode auth_mode, | ||||||
|  | 		      unsigned int total_sent, u32 status_padding); | ||||||
|  | u8 spu2_xts_tweak_in_payload(void); | ||||||
|  | u8 spu2_tx_status_len(void); | ||||||
|  | u8 spu2_rx_status_len(void); | ||||||
|  | int spu2_status_process(u8 *statp); | ||||||
|  | void spu2_ccm_update_iv(unsigned int digestsize, | ||||||
|  | 			struct spu_cipher_parms *cipher_parms, | ||||||
|  | 			unsigned int assoclen, unsigned int chunksize, | ||||||
|  | 			bool is_encrypt, bool is_esp); | ||||||
|  | u32 spu2_wordalign_padlen(u32 data_size); | ||||||
|  | #endif | ||||||
							
								
								
									
										174
									
								
								drivers/crypto/bcm/spum.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										174
									
								
								drivers/crypto/bcm/spum.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,174 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright 2016 Broadcom | ||||||
|  |  * | ||||||
|  |  * This program is free software; you can redistribute it and/or modify | ||||||
|  |  * it under the terms of the GNU General Public License, version 2, as | ||||||
|  |  * published by the Free Software Foundation (the "GPL"). | ||||||
|  |  * | ||||||
|  |  * This program is distributed in the hope that it will be useful, but | ||||||
|  |  * WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||||
|  |  * General Public License version 2 (GPLv2) for more details. | ||||||
|  |  * | ||||||
|  |  * You should have received a copy of the GNU General Public License | ||||||
|  |  * version 2 (GPLv2) along with this source code. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * This file contains SPU message definitions specific to SPU-M. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #ifndef _SPUM_H_ | ||||||
|  | #define _SPUM_H_ | ||||||
|  | 
 | ||||||
|  | #define SPU_CRYPTO_OPERATION_GENERIC	0x1 | ||||||
|  | 
 | ||||||
|  | /* Length of STATUS field in tx and rx packets */ | ||||||
|  | #define SPU_TX_STATUS_LEN  4 | ||||||
|  | 
 | ||||||
|  | /* SPU-M error codes */ | ||||||
|  | #define SPU_STATUS_MASK                 0x0000FF00 | ||||||
|  | #define SPU_STATUS_SUCCESS              0x00000000 | ||||||
|  | #define SPU_STATUS_INVALID_ICV          0x00000100 | ||||||
|  | 
 | ||||||
|  | #define SPU_STATUS_ERROR_FLAG           0x00020000 | ||||||
|  | 
 | ||||||
|  | /* Request message. MH + EMH + BDESC + BD header */ | ||||||
|  | #define SPU_REQ_FIXED_LEN 24 | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Max length of a SPU message header. Used to allocate a buffer where | ||||||
|  |  * the SPU message header is constructed. Can be used for either a SPU-M | ||||||
|  |  * header or a SPU2 header. | ||||||
|  |  * For SPU-M, sum of the following: | ||||||
|  |  *    MH - 4 bytes | ||||||
|  |  *    EMH - 4 | ||||||
|  |  *    SCTX - 3 + | ||||||
|  |  *      max auth key len - 64 | ||||||
|  |  *      max cipher key len - 264 (RC4) | ||||||
|  |  *      max IV len - 16 | ||||||
|  |  *    BDESC - 12 | ||||||
|  |  *    BD header - 4 | ||||||
|  |  * Total:  371 | ||||||
|  |  * | ||||||
|  |  * For SPU2, FMD_SIZE (32) plus lengths of hash and cipher keys, | ||||||
|  |  * hash and cipher IVs. If SPU2 does not support RC4, then | ||||||
|  |  */ | ||||||
|  | #define SPU_HEADER_ALLOC_LEN  (SPU_REQ_FIXED_LEN + MAX_KEY_SIZE + \ | ||||||
|  | 				MAX_KEY_SIZE + MAX_IV_SIZE) | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Response message header length. Normally MH, EMH, BD header, but when | ||||||
|  |  * BD_SUPPRESS is used for hash requests, there is no BD header. | ||||||
|  |  */ | ||||||
|  | #define SPU_RESP_HDR_LEN 12 | ||||||
|  | #define SPU_HASH_RESP_HDR_LEN 8 | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Max value that can be represented in the Payload Length field of the BD | ||||||
|  |  * header. This is a 16-bit field. | ||||||
|  |  */ | ||||||
|  | #define SPUM_NS2_MAX_PAYLOAD  (BIT(16) - 1) | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * NSP SPU is limited to ~9KB because of FA2 FIFO size limitations; | ||||||
|  |  * Set MAX_PAYLOAD to 8k to allow for addition of header, digest, etc. | ||||||
|  |  * and stay within limitation. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #define SPUM_NSP_MAX_PAYLOAD	8192 | ||||||
|  | 
 | ||||||
|  | /* Buffer Descriptor Header [BDESC]. SPU in big-endian mode. */ | ||||||
|  | struct BDESC_HEADER { | ||||||
|  | 	u16 offset_mac;		/* word 0 [31-16] */ | ||||||
|  | 	u16 length_mac;		/* word 0 [15-0]  */ | ||||||
|  | 	u16 offset_crypto;	/* word 1 [31-16] */ | ||||||
|  | 	u16 length_crypto;	/* word 1 [15-0]  */ | ||||||
|  | 	u16 offset_icv;		/* word 2 [31-16] */ | ||||||
|  | 	u16 offset_iv;		/* word 2 [15-0]  */ | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /* Buffer Data Header [BD]. SPU in big-endian mode. */ | ||||||
|  | struct BD_HEADER { | ||||||
|  | 	u16 size; | ||||||
|  | 	u16 prev_length; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /* Command Context Header. SPU-M in big endian mode. */ | ||||||
|  | struct MHEADER { | ||||||
|  | 	u8 flags;	/* [31:24] */ | ||||||
|  | 	u8 op_code;	/* [23:16] */ | ||||||
|  | 	u16 reserved;	/* [15:0] */ | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /* MH header flags bits */ | ||||||
|  | #define MH_SUPDT_PRES   BIT(0) | ||||||
|  | #define MH_HASH_PRES    BIT(2) | ||||||
|  | #define MH_BD_PRES      BIT(3) | ||||||
|  | #define MH_MFM_PRES     BIT(4) | ||||||
|  | #define MH_BDESC_PRES   BIT(5) | ||||||
|  | #define MH_SCTX_PRES	BIT(7) | ||||||
|  | 
 | ||||||
|  | /* SCTX word 0 bit offsets and fields masks */ | ||||||
|  | #define SCTX_SIZE               0x000000FF | ||||||
|  | 
 | ||||||
|  | /* SCTX word 1 bit shifts and field masks */ | ||||||
|  | #define  UPDT_OFST              0x000000FF   /* offset of SCTX updateable fld */ | ||||||
|  | #define  HASH_TYPE              0x00000300   /* hash alg operation type */ | ||||||
|  | #define  HASH_TYPE_SHIFT                 8 | ||||||
|  | #define  HASH_MODE              0x00001C00   /* one of spu2_hash_mode */ | ||||||
|  | #define  HASH_MODE_SHIFT                10 | ||||||
|  | #define  HASH_ALG               0x0000E000   /* hash algorithm */ | ||||||
|  | #define  HASH_ALG_SHIFT                 13 | ||||||
|  | #define  CIPHER_TYPE            0x00030000   /* encryption operation type */ | ||||||
|  | #define  CIPHER_TYPE_SHIFT              16 | ||||||
|  | #define  CIPHER_MODE            0x001C0000   /* encryption mode */ | ||||||
|  | #define  CIPHER_MODE_SHIFT              18 | ||||||
|  | #define  CIPHER_ALG             0x00E00000   /* encryption algo */ | ||||||
|  | #define  CIPHER_ALG_SHIFT               21 | ||||||
|  | #define  ICV_IS_512                BIT(27) | ||||||
|  | #define  ICV_IS_512_SHIFT		27 | ||||||
|  | #define  CIPHER_ORDER               BIT(30) | ||||||
|  | #define  CIPHER_ORDER_SHIFT             30 | ||||||
|  | #define  CIPHER_INBOUND             BIT(31) | ||||||
|  | #define  CIPHER_INBOUND_SHIFT           31 | ||||||
|  | 
 | ||||||
|  | /* SCTX word 2 bit shifts and field masks */ | ||||||
|  | #define  EXP_IV_SIZE                   0x7 | ||||||
|  | #define  IV_OFFSET                   BIT(3) | ||||||
|  | #define  IV_OFFSET_SHIFT                 3 | ||||||
|  | #define  GEN_IV                      BIT(5) | ||||||
|  | #define  GEN_IV_SHIFT                    5 | ||||||
|  | #define  EXPLICIT_IV                 BIT(6) | ||||||
|  | #define  EXPLICIT_IV_SHIFT               6 | ||||||
|  | #define  SCTX_IV                     BIT(7) | ||||||
|  | #define  SCTX_IV_SHIFT                   7 | ||||||
|  | #define  ICV_SIZE                   0x0F00 | ||||||
|  | #define  ICV_SIZE_SHIFT                  8 | ||||||
|  | #define  CHECK_ICV                  BIT(12) | ||||||
|  | #define  CHECK_ICV_SHIFT                12 | ||||||
|  | #define  INSERT_ICV                 BIT(13) | ||||||
|  | #define  INSERT_ICV_SHIFT               13 | ||||||
|  | #define  BD_SUPPRESS                BIT(19) | ||||||
|  | #define  BD_SUPPRESS_SHIFT              19 | ||||||
|  | 
 | ||||||
|  | /* Generic Mode Security Context Structure [SCTX] */ | ||||||
|  | struct SCTX { | ||||||
|  | /* word 0: protocol flags */ | ||||||
|  | 	u32 proto_flags; | ||||||
|  | 
 | ||||||
|  | /* word 1: cipher flags */ | ||||||
|  | 	u32 cipher_flags; | ||||||
|  | 
 | ||||||
|  | /* word 2: Extended cipher flags */ | ||||||
|  | 	u32 ecf; | ||||||
|  | 
 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct SPUHEADER { | ||||||
|  | 	struct MHEADER mh; | ||||||
|  | 	u32 emh; | ||||||
|  | 	struct SCTX sa; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | #endif /* _SPUM_H_ */ | ||||||
							
								
								
									
										581
									
								
								drivers/crypto/bcm/util.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										581
									
								
								drivers/crypto/bcm/util.c
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,581 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright 2016 Broadcom | ||||||
|  |  * | ||||||
|  |  * This program is free software; you can redistribute it and/or modify | ||||||
|  |  * it under the terms of the GNU General Public License, version 2, as | ||||||
|  |  * published by the Free Software Foundation (the "GPL"). | ||||||
|  |  * | ||||||
|  |  * This program is distributed in the hope that it will be useful, but | ||||||
|  |  * WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||||
|  |  * General Public License version 2 (GPLv2) for more details. | ||||||
|  |  * | ||||||
|  |  * You should have received a copy of the GNU General Public License | ||||||
|  |  * version 2 (GPLv2) along with this source code. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include <linux/debugfs.h> | ||||||
|  | 
 | ||||||
|  | #include "cipher.h" | ||||||
|  | #include "util.h" | ||||||
|  | 
 | ||||||
|  | /* offset of SPU_OFIFO_CTRL register */ | ||||||
|  | #define SPU_OFIFO_CTRL      0x40 | ||||||
|  | #define SPU_FIFO_WATERMARK  0x1FF | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * spu_sg_at_offset() - Find the scatterlist entry at a given distance from the | ||||||
|  |  * start of a scatterlist. | ||||||
|  |  * @sg:         [in]  Start of a scatterlist | ||||||
|  |  * @skip:       [in]  Distance from the start of the scatterlist, in bytes | ||||||
|  |  * @sge:        [out] Scatterlist entry at skip bytes from start | ||||||
|  |  * @sge_offset: [out] Number of bytes from start of sge buffer to get to | ||||||
|  |  *                    requested distance. | ||||||
|  |  * | ||||||
|  |  * Return: 0 if entry found at requested distance | ||||||
|  |  *         < 0 otherwise | ||||||
|  |  */ | ||||||
|  | int spu_sg_at_offset(struct scatterlist *sg, unsigned int skip, | ||||||
|  | 		     struct scatterlist **sge, unsigned int *sge_offset) | ||||||
|  | { | ||||||
|  | 	/* byte index from start of sg to the end of the previous entry */ | ||||||
|  | 	unsigned int index = 0; | ||||||
|  | 	/* byte index from start of sg to the end of the current entry */ | ||||||
|  | 	unsigned int next_index; | ||||||
|  | 
 | ||||||
|  | 	next_index = sg->length; | ||||||
|  | 	while (next_index <= skip) { | ||||||
|  | 		sg = sg_next(sg); | ||||||
|  | 		index = next_index; | ||||||
|  | 		if (!sg) | ||||||
|  | 			return -EINVAL; | ||||||
|  | 		next_index += sg->length; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	*sge_offset = skip - index; | ||||||
|  | 	*sge = sg; | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* Copy len bytes of sg data, starting at offset skip, to a dest buffer */ | ||||||
|  | void sg_copy_part_to_buf(struct scatterlist *src, u8 *dest, | ||||||
|  | 			 unsigned int len, unsigned int skip) | ||||||
|  | { | ||||||
|  | 	size_t copied; | ||||||
|  | 	unsigned int nents = sg_nents(src); | ||||||
|  | 
 | ||||||
|  | 	copied = sg_pcopy_to_buffer(src, nents, dest, len, skip); | ||||||
|  | 	if (copied != len) { | ||||||
|  | 		flow_log("%s copied %u bytes of %u requested. ", | ||||||
|  | 			 __func__, (u32)copied, len); | ||||||
|  | 		flow_log("sg with %u entries and skip %u\n", nents, skip); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Copy data into a scatterlist starting at a specified offset in the | ||||||
|  |  * scatterlist. Specifically, copy len bytes of data in the buffer src | ||||||
|  |  * into the scatterlist dest, starting skip bytes into the scatterlist. | ||||||
|  |  */ | ||||||
|  | void sg_copy_part_from_buf(struct scatterlist *dest, u8 *src, | ||||||
|  | 			   unsigned int len, unsigned int skip) | ||||||
|  | { | ||||||
|  | 	size_t copied; | ||||||
|  | 	unsigned int nents = sg_nents(dest); | ||||||
|  | 
 | ||||||
|  | 	copied = sg_pcopy_from_buffer(dest, nents, src, len, skip); | ||||||
|  | 	if (copied != len) { | ||||||
|  | 		flow_log("%s copied %u bytes of %u requested. ", | ||||||
|  | 			 __func__, (u32)copied, len); | ||||||
|  | 		flow_log("sg with %u entries and skip %u\n", nents, skip); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * spu_sg_count() - Determine number of elements in scatterlist to provide a | ||||||
|  |  * specified number of bytes. | ||||||
|  |  * @sg_list:  scatterlist to examine | ||||||
|  |  * @skip:     index of starting point | ||||||
|  |  * @nbytes:   consider elements of scatterlist until reaching this number of | ||||||
|  |  *	      bytes | ||||||
|  |  * | ||||||
|  |  * Return: the number of sg entries contributing to nbytes of data | ||||||
|  |  */ | ||||||
|  | int spu_sg_count(struct scatterlist *sg_list, unsigned int skip, int nbytes) | ||||||
|  | { | ||||||
|  | 	struct scatterlist *sg; | ||||||
|  | 	int sg_nents = 0; | ||||||
|  | 	unsigned int offset; | ||||||
|  | 
 | ||||||
|  | 	if (!sg_list) | ||||||
|  | 		return 0; | ||||||
|  | 
 | ||||||
|  | 	if (spu_sg_at_offset(sg_list, skip, &sg, &offset) < 0) | ||||||
|  | 		return 0; | ||||||
|  | 
 | ||||||
|  | 	while (sg && (nbytes > 0)) { | ||||||
|  | 		sg_nents++; | ||||||
|  | 		nbytes -= (sg->length - offset); | ||||||
|  | 		offset = 0; | ||||||
|  | 		sg = sg_next(sg); | ||||||
|  | 	} | ||||||
|  | 	return sg_nents; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * spu_msg_sg_add() - Copy scatterlist entries from one sg to another, up to a | ||||||
|  |  * given length. | ||||||
|  |  * @to_sg:       scatterlist to copy to | ||||||
|  |  * @from_sg:     scatterlist to copy from | ||||||
|  |  * @from_skip:   number of bytes to skip in from_sg. Non-zero when previous | ||||||
|  |  *		 request included part of the buffer in entry in from_sg. | ||||||
|  |  *		 Assumes from_skip < from_sg->length. | ||||||
|  |  * @from_nents   number of entries in from_sg | ||||||
|  |  * @length       number of bytes to copy. may reach this limit before exhausting | ||||||
|  |  *		 from_sg. | ||||||
|  |  * | ||||||
|  |  * Copies the entries themselves, not the data in the entries. Assumes to_sg has | ||||||
|  |  * enough entries. Does not limit the size of an individual buffer in to_sg. | ||||||
|  |  * | ||||||
|  |  * to_sg, from_sg, skip are all updated to end of copy | ||||||
|  |  * | ||||||
|  |  * Return: Number of bytes copied | ||||||
|  |  */ | ||||||
|  | u32 spu_msg_sg_add(struct scatterlist **to_sg, | ||||||
|  | 		   struct scatterlist **from_sg, u32 *from_skip, | ||||||
|  | 		   u8 from_nents, u32 length) | ||||||
|  | { | ||||||
|  | 	struct scatterlist *sg;	/* an entry in from_sg */ | ||||||
|  | 	struct scatterlist *to = *to_sg; | ||||||
|  | 	struct scatterlist *from = *from_sg; | ||||||
|  | 	u32 skip = *from_skip; | ||||||
|  | 	u32 offset; | ||||||
|  | 	int i; | ||||||
|  | 	u32 entry_len = 0; | ||||||
|  | 	u32 frag_len = 0;	/* length of entry added to to_sg */ | ||||||
|  | 	u32 copied = 0;		/* number of bytes copied so far */ | ||||||
|  | 
 | ||||||
|  | 	if (length == 0) | ||||||
|  | 		return 0; | ||||||
|  | 
 | ||||||
|  | 	for_each_sg(from, sg, from_nents, i) { | ||||||
|  | 		/* number of bytes in this from entry not yet used */ | ||||||
|  | 		entry_len = sg->length - skip; | ||||||
|  | 		frag_len = min(entry_len, length - copied); | ||||||
|  | 		offset = sg->offset + skip; | ||||||
|  | 		if (frag_len) | ||||||
|  | 			sg_set_page(to++, sg_page(sg), frag_len, offset); | ||||||
|  | 		copied += frag_len; | ||||||
|  | 		if (copied == entry_len) { | ||||||
|  | 			/* used up all of from entry */ | ||||||
|  | 			skip = 0;	/* start at beginning of next entry */ | ||||||
|  | 		} | ||||||
|  | 		if (copied == length) | ||||||
|  | 			break; | ||||||
|  | 	} | ||||||
|  | 	*to_sg = to; | ||||||
|  | 	*from_sg = sg; | ||||||
|  | 	if (frag_len < entry_len) | ||||||
|  | 		*from_skip = skip + frag_len; | ||||||
|  | 	else | ||||||
|  | 		*from_skip = 0; | ||||||
|  | 
 | ||||||
|  | 	return copied; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void add_to_ctr(u8 *ctr_pos, unsigned int increment) | ||||||
|  | { | ||||||
|  | 	__be64 *high_be = (__be64 *)ctr_pos; | ||||||
|  | 	__be64 *low_be = high_be + 1; | ||||||
|  | 	u64 orig_low = __be64_to_cpu(*low_be); | ||||||
|  | 	u64 new_low = orig_low + (u64)increment; | ||||||
|  | 
 | ||||||
|  | 	*low_be = __cpu_to_be64(new_low); | ||||||
|  | 	if (new_low < orig_low) | ||||||
|  | 		/* there was a carry from the low 8 bytes */ | ||||||
|  | 		*high_be = __cpu_to_be64(__be64_to_cpu(*high_be) + 1); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | struct sdesc { | ||||||
|  | 	struct shash_desc shash; | ||||||
|  | 	char ctx[]; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /* do a synchronous decrypt operation */ | ||||||
|  | int do_decrypt(char *alg_name, | ||||||
|  | 	       void *key_ptr, unsigned int key_len, | ||||||
|  | 	       void *iv_ptr, void *src_ptr, void *dst_ptr, | ||||||
|  | 	       unsigned int block_len) | ||||||
|  | { | ||||||
|  | 	struct scatterlist sg_in[1], sg_out[1]; | ||||||
|  | 	struct crypto_blkcipher *tfm = | ||||||
|  | 	    crypto_alloc_blkcipher(alg_name, 0, CRYPTO_ALG_ASYNC); | ||||||
|  | 	struct blkcipher_desc desc = {.tfm = tfm, .flags = 0 }; | ||||||
|  | 	int ret = 0; | ||||||
|  | 	void *iv; | ||||||
|  | 	int ivsize; | ||||||
|  | 
 | ||||||
|  | 	flow_log("%s() name:%s block_len:%u\n", __func__, alg_name, block_len); | ||||||
|  | 
 | ||||||
|  | 	if (IS_ERR(tfm)) | ||||||
|  | 		return PTR_ERR(tfm); | ||||||
|  | 
 | ||||||
|  | 	crypto_blkcipher_setkey((void *)tfm, key_ptr, key_len); | ||||||
|  | 
 | ||||||
|  | 	sg_init_table(sg_in, 1); | ||||||
|  | 	sg_set_buf(sg_in, src_ptr, block_len); | ||||||
|  | 
 | ||||||
|  | 	sg_init_table(sg_out, 1); | ||||||
|  | 	sg_set_buf(sg_out, dst_ptr, block_len); | ||||||
|  | 
 | ||||||
|  | 	iv = crypto_blkcipher_crt(tfm)->iv; | ||||||
|  | 	ivsize = crypto_blkcipher_ivsize(tfm); | ||||||
|  | 	memcpy(iv, iv_ptr, ivsize); | ||||||
|  | 
 | ||||||
|  | 	ret = crypto_blkcipher_decrypt(&desc, sg_out, sg_in, block_len); | ||||||
|  | 	crypto_free_blkcipher(tfm); | ||||||
|  | 
 | ||||||
|  | 	if (ret < 0) | ||||||
|  | 		pr_err("aes_decrypt failed %d\n", ret); | ||||||
|  | 
 | ||||||
|  | 	return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * do_shash() - Do a synchronous hash operation in software | ||||||
|  |  * @name:       The name of the hash algorithm | ||||||
|  |  * @result:     Buffer where digest is to be written | ||||||
|  |  * @data1:      First part of data to hash. May be NULL. | ||||||
|  |  * @data1_len:  Length of data1, in bytes | ||||||
|  |  * @data2:      Second part of data to hash. May be NULL. | ||||||
|  |  * @data2_len:  Length of data2, in bytes | ||||||
|  |  * @key:	Key (if keyed hash) | ||||||
|  |  * @key_len:	Length of key, in bytes (or 0 if non-keyed hash) | ||||||
|  |  * | ||||||
|  |  * Note that the crypto API will not select this driver's own transform because | ||||||
|  |  * this driver only registers asynchronous algos. | ||||||
|  |  * | ||||||
|  |  * Return: 0 if hash successfully stored in result | ||||||
|  |  *         < 0 otherwise | ||||||
|  |  */ | ||||||
|  | int do_shash(unsigned char *name, unsigned char *result, | ||||||
|  | 	     const u8 *data1, unsigned int data1_len, | ||||||
|  | 	     const u8 *data2, unsigned int data2_len, | ||||||
|  | 	     const u8 *key, unsigned int key_len) | ||||||
|  | { | ||||||
|  | 	int rc; | ||||||
|  | 	unsigned int size; | ||||||
|  | 	struct crypto_shash *hash; | ||||||
|  | 	struct sdesc *sdesc; | ||||||
|  | 
 | ||||||
|  | 	hash = crypto_alloc_shash(name, 0, 0); | ||||||
|  | 	if (IS_ERR(hash)) { | ||||||
|  | 		rc = PTR_ERR(hash); | ||||||
|  | 		pr_err("%s: Crypto %s allocation error %d", __func__, name, rc); | ||||||
|  | 		return rc; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	size = sizeof(struct shash_desc) + crypto_shash_descsize(hash); | ||||||
|  | 	sdesc = kmalloc(size, GFP_KERNEL); | ||||||
|  | 	if (!sdesc) { | ||||||
|  | 		rc = -ENOMEM; | ||||||
|  | 		pr_err("%s: Memory allocation failure", __func__); | ||||||
|  | 		goto do_shash_err; | ||||||
|  | 	} | ||||||
|  | 	sdesc->shash.tfm = hash; | ||||||
|  | 	sdesc->shash.flags = 0x0; | ||||||
|  | 
 | ||||||
|  | 	if (key_len > 0) { | ||||||
|  | 		rc = crypto_shash_setkey(hash, key, key_len); | ||||||
|  | 		if (rc) { | ||||||
|  | 			pr_err("%s: Could not setkey %s shash", __func__, name); | ||||||
|  | 			goto do_shash_err; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	rc = crypto_shash_init(&sdesc->shash); | ||||||
|  | 	if (rc) { | ||||||
|  | 		pr_err("%s: Could not init %s shash", __func__, name); | ||||||
|  | 		goto do_shash_err; | ||||||
|  | 	} | ||||||
|  | 	rc = crypto_shash_update(&sdesc->shash, data1, data1_len); | ||||||
|  | 	if (rc) { | ||||||
|  | 		pr_err("%s: Could not update1", __func__); | ||||||
|  | 		goto do_shash_err; | ||||||
|  | 	} | ||||||
|  | 	if (data2 && data2_len) { | ||||||
|  | 		rc = crypto_shash_update(&sdesc->shash, data2, data2_len); | ||||||
|  | 		if (rc) { | ||||||
|  | 			pr_err("%s: Could not update2", __func__); | ||||||
|  | 			goto do_shash_err; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	rc = crypto_shash_final(&sdesc->shash, result); | ||||||
|  | 	if (rc) | ||||||
|  | 		pr_err("%s: Could not genereate %s hash", __func__, name); | ||||||
|  | 
 | ||||||
|  | do_shash_err: | ||||||
|  | 	crypto_free_shash(hash); | ||||||
|  | 	kfree(sdesc); | ||||||
|  | 
 | ||||||
|  | 	return rc; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* Dump len bytes of a scatterlist starting at skip bytes into the sg */ | ||||||
|  | void __dump_sg(struct scatterlist *sg, unsigned int skip, unsigned int len) | ||||||
|  | { | ||||||
|  | 	u8 dbuf[16]; | ||||||
|  | 	unsigned int idx = skip; | ||||||
|  | 	unsigned int num_out = 0;	/* number of bytes dumped so far */ | ||||||
|  | 	unsigned int count; | ||||||
|  | 
 | ||||||
|  | 	if (packet_debug_logging) { | ||||||
|  | 		while (num_out < len) { | ||||||
|  | 			count = (len - num_out > 16) ? 16 : len - num_out; | ||||||
|  | 			sg_copy_part_to_buf(sg, dbuf, count, idx); | ||||||
|  | 			num_out += count; | ||||||
|  | 			print_hex_dump(KERN_ALERT, "  sg: ", DUMP_PREFIX_NONE, | ||||||
|  | 				       4, 1, dbuf, count, false); | ||||||
|  | 			idx += 16; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if (debug_logging_sleep) | ||||||
|  | 		msleep(debug_logging_sleep); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* Returns the name for a given cipher alg/mode */ | ||||||
|  | char *spu_alg_name(enum spu_cipher_alg alg, enum spu_cipher_mode mode) | ||||||
|  | { | ||||||
|  | 	switch (alg) { | ||||||
|  | 	case CIPHER_ALG_RC4: | ||||||
|  | 		return "rc4"; | ||||||
|  | 	case CIPHER_ALG_AES: | ||||||
|  | 		switch (mode) { | ||||||
|  | 		case CIPHER_MODE_CBC: | ||||||
|  | 			return "cbc(aes)"; | ||||||
|  | 		case CIPHER_MODE_ECB: | ||||||
|  | 			return "ecb(aes)"; | ||||||
|  | 		case CIPHER_MODE_OFB: | ||||||
|  | 			return "ofb(aes)"; | ||||||
|  | 		case CIPHER_MODE_CFB: | ||||||
|  | 			return "cfb(aes)"; | ||||||
|  | 		case CIPHER_MODE_CTR: | ||||||
|  | 			return "ctr(aes)"; | ||||||
|  | 		case CIPHER_MODE_XTS: | ||||||
|  | 			return "xts(aes)"; | ||||||
|  | 		case CIPHER_MODE_GCM: | ||||||
|  | 			return "gcm(aes)"; | ||||||
|  | 		default: | ||||||
|  | 			return "aes"; | ||||||
|  | 		} | ||||||
|  | 		break; | ||||||
|  | 	case CIPHER_ALG_DES: | ||||||
|  | 		switch (mode) { | ||||||
|  | 		case CIPHER_MODE_CBC: | ||||||
|  | 			return "cbc(des)"; | ||||||
|  | 		case CIPHER_MODE_ECB: | ||||||
|  | 			return "ecb(des)"; | ||||||
|  | 		case CIPHER_MODE_CTR: | ||||||
|  | 			return "ctr(des)"; | ||||||
|  | 		default: | ||||||
|  | 			return "des"; | ||||||
|  | 		} | ||||||
|  | 		break; | ||||||
|  | 	case CIPHER_ALG_3DES: | ||||||
|  | 		switch (mode) { | ||||||
|  | 		case CIPHER_MODE_CBC: | ||||||
|  | 			return "cbc(des3_ede)"; | ||||||
|  | 		case CIPHER_MODE_ECB: | ||||||
|  | 			return "ecb(des3_ede)"; | ||||||
|  | 		case CIPHER_MODE_CTR: | ||||||
|  | 			return "ctr(des3_ede)"; | ||||||
|  | 		default: | ||||||
|  | 			return "3des"; | ||||||
|  | 		} | ||||||
|  | 		break; | ||||||
|  | 	default: | ||||||
|  | 		return "other"; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static ssize_t spu_debugfs_read(struct file *filp, char __user *ubuf, | ||||||
|  | 				size_t count, loff_t *offp) | ||||||
|  | { | ||||||
|  | 	struct device_private *ipriv; | ||||||
|  | 	char *buf; | ||||||
|  | 	ssize_t ret, out_offset, out_count; | ||||||
|  | 	int i; | ||||||
|  | 	u32 fifo_len; | ||||||
|  | 	u32 spu_ofifo_ctrl; | ||||||
|  | 	u32 alg; | ||||||
|  | 	u32 mode; | ||||||
|  | 	u32 op_cnt; | ||||||
|  | 
 | ||||||
|  | 	out_count = 2048; | ||||||
|  | 
 | ||||||
|  | 	buf = kmalloc(out_count, GFP_KERNEL); | ||||||
|  | 	if (!buf) | ||||||
|  | 		return -ENOMEM; | ||||||
|  | 
 | ||||||
|  | 	ipriv = filp->private_data; | ||||||
|  | 	out_offset = 0; | ||||||
|  | 	out_offset += snprintf(buf + out_offset, out_count - out_offset, | ||||||
|  | 			       "Number of SPUs.........%u\n", | ||||||
|  | 			       ipriv->spu.num_spu); | ||||||
|  | 	out_offset += snprintf(buf + out_offset, out_count - out_offset, | ||||||
|  | 			       "Current sessions.......%u\n", | ||||||
|  | 			       atomic_read(&ipriv->session_count)); | ||||||
|  | 	out_offset += snprintf(buf + out_offset, out_count - out_offset, | ||||||
|  | 			       "Session count..........%u\n", | ||||||
|  | 			       atomic_read(&ipriv->stream_count)); | ||||||
|  | 	out_offset += snprintf(buf + out_offset, out_count - out_offset, | ||||||
|  | 			       "Cipher setkey..........%u\n", | ||||||
|  | 			       atomic_read(&ipriv->setkey_cnt[SPU_OP_CIPHER])); | ||||||
|  | 	out_offset += snprintf(buf + out_offset, out_count - out_offset, | ||||||
|  | 			       "Cipher Ops.............%u\n", | ||||||
|  | 			       atomic_read(&ipriv->op_counts[SPU_OP_CIPHER])); | ||||||
|  | 	for (alg = 0; alg < CIPHER_ALG_LAST; alg++) { | ||||||
|  | 		for (mode = 0; mode < CIPHER_MODE_LAST; mode++) { | ||||||
|  | 			op_cnt = atomic_read(&ipriv->cipher_cnt[alg][mode]); | ||||||
|  | 			if (op_cnt) { | ||||||
|  | 				out_offset += snprintf(buf + out_offset, | ||||||
|  | 						       out_count - out_offset, | ||||||
|  | 			       "  %-13s%11u\n", | ||||||
|  | 			       spu_alg_name(alg, mode), op_cnt); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	out_offset += snprintf(buf + out_offset, out_count - out_offset, | ||||||
|  | 			       "Hash Ops...............%u\n", | ||||||
|  | 			       atomic_read(&ipriv->op_counts[SPU_OP_HASH])); | ||||||
|  | 	for (alg = 0; alg < HASH_ALG_LAST; alg++) { | ||||||
|  | 		op_cnt = atomic_read(&ipriv->hash_cnt[alg]); | ||||||
|  | 		if (op_cnt) { | ||||||
|  | 			out_offset += snprintf(buf + out_offset, | ||||||
|  | 					       out_count - out_offset, | ||||||
|  | 		       "  %-13s%11u\n", | ||||||
|  | 		       hash_alg_name[alg], op_cnt); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	out_offset += snprintf(buf + out_offset, out_count - out_offset, | ||||||
|  | 			       "HMAC setkey............%u\n", | ||||||
|  | 			       atomic_read(&ipriv->setkey_cnt[SPU_OP_HMAC])); | ||||||
|  | 	out_offset += snprintf(buf + out_offset, out_count - out_offset, | ||||||
|  | 			       "HMAC Ops...............%u\n", | ||||||
|  | 			       atomic_read(&ipriv->op_counts[SPU_OP_HMAC])); | ||||||
|  | 	for (alg = 0; alg < HASH_ALG_LAST; alg++) { | ||||||
|  | 		op_cnt = atomic_read(&ipriv->hmac_cnt[alg]); | ||||||
|  | 		if (op_cnt) { | ||||||
|  | 			out_offset += snprintf(buf + out_offset, | ||||||
|  | 					       out_count - out_offset, | ||||||
|  | 		       "  %-13s%11u\n", | ||||||
|  | 		       hash_alg_name[alg], op_cnt); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	out_offset += snprintf(buf + out_offset, out_count - out_offset, | ||||||
|  | 			       "AEAD setkey............%u\n", | ||||||
|  | 			       atomic_read(&ipriv->setkey_cnt[SPU_OP_AEAD])); | ||||||
|  | 
 | ||||||
|  | 	out_offset += snprintf(buf + out_offset, out_count - out_offset, | ||||||
|  | 			       "AEAD Ops...............%u\n", | ||||||
|  | 			       atomic_read(&ipriv->op_counts[SPU_OP_AEAD])); | ||||||
|  | 	for (alg = 0; alg < AEAD_TYPE_LAST; alg++) { | ||||||
|  | 		op_cnt = atomic_read(&ipriv->aead_cnt[alg]); | ||||||
|  | 		if (op_cnt) { | ||||||
|  | 			out_offset += snprintf(buf + out_offset, | ||||||
|  | 					       out_count - out_offset, | ||||||
|  | 		       "  %-13s%11u\n", | ||||||
|  | 		       aead_alg_name[alg], op_cnt); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	out_offset += snprintf(buf + out_offset, out_count - out_offset, | ||||||
|  | 			       "Bytes of req data......%llu\n", | ||||||
|  | 			       (u64)atomic64_read(&ipriv->bytes_out)); | ||||||
|  | 	out_offset += snprintf(buf + out_offset, out_count - out_offset, | ||||||
|  | 			       "Bytes of resp data.....%llu\n", | ||||||
|  | 			       (u64)atomic64_read(&ipriv->bytes_in)); | ||||||
|  | 	out_offset += snprintf(buf + out_offset, out_count - out_offset, | ||||||
|  | 			       "Mailbox full...........%u\n", | ||||||
|  | 			       atomic_read(&ipriv->mb_no_spc)); | ||||||
|  | 	out_offset += snprintf(buf + out_offset, out_count - out_offset, | ||||||
|  | 			       "Mailbox send failures..%u\n", | ||||||
|  | 			       atomic_read(&ipriv->mb_send_fail)); | ||||||
|  | 	out_offset += snprintf(buf + out_offset, out_count - out_offset, | ||||||
|  | 			       "Check ICV errors.......%u\n", | ||||||
|  | 			       atomic_read(&ipriv->bad_icv)); | ||||||
|  | 	if (ipriv->spu.spu_type == SPU_TYPE_SPUM) | ||||||
|  | 		for (i = 0; i < ipriv->spu.num_spu; i++) { | ||||||
|  | 			spu_ofifo_ctrl = ioread32(ipriv->spu.reg_vbase[i] + | ||||||
|  | 						  SPU_OFIFO_CTRL); | ||||||
|  | 			fifo_len = spu_ofifo_ctrl & SPU_FIFO_WATERMARK; | ||||||
|  | 			out_offset += snprintf(buf + out_offset, | ||||||
|  | 					       out_count - out_offset, | ||||||
|  | 				       "SPU %d output FIFO high water.....%u\n", | ||||||
|  | 				       i, fifo_len); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 	if (out_offset > out_count) | ||||||
|  | 		out_offset = out_count; | ||||||
|  | 
 | ||||||
|  | 	ret = simple_read_from_buffer(ubuf, count, offp, buf, out_offset); | ||||||
|  | 	kfree(buf); | ||||||
|  | 	return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static const struct file_operations spu_debugfs_stats = { | ||||||
|  | 	.owner = THIS_MODULE, | ||||||
|  | 	.open = simple_open, | ||||||
|  | 	.read = spu_debugfs_read, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Create the debug FS directories. If the top-level directory has not yet | ||||||
|  |  * been created, create it now. Create a stats file in this directory for | ||||||
|  |  * a SPU. | ||||||
|  |  */ | ||||||
|  | void spu_setup_debugfs(void) | ||||||
|  | { | ||||||
|  | 	if (!debugfs_initialized()) | ||||||
|  | 		return; | ||||||
|  | 
 | ||||||
|  | 	if (!iproc_priv.debugfs_dir) | ||||||
|  | 		iproc_priv.debugfs_dir = debugfs_create_dir(KBUILD_MODNAME, | ||||||
|  | 							    NULL); | ||||||
|  | 
 | ||||||
|  | 	if (!iproc_priv.debugfs_stats) | ||||||
|  | 		/* Create file with permissions S_IRUSR */ | ||||||
|  | 		debugfs_create_file("stats", 0400, iproc_priv.debugfs_dir, | ||||||
|  | 				    &iproc_priv, &spu_debugfs_stats); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void spu_free_debugfs(void) | ||||||
|  | { | ||||||
|  | 	debugfs_remove_recursive(iproc_priv.debugfs_dir); | ||||||
|  | 	iproc_priv.debugfs_dir = NULL; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * format_value_ccm() - Format a value into a buffer, using a specified number | ||||||
|  |  *			of bytes (i.e. maybe writing value X into a 4 byte | ||||||
|  |  *			buffer, or maybe into a 12 byte buffer), as per the | ||||||
|  |  *			SPU CCM spec. | ||||||
|  |  * | ||||||
|  |  * @val:		value to write (up to max of unsigned int) | ||||||
|  |  * @buf:		(pointer to) buffer to write the value | ||||||
|  |  * @len:		number of bytes to use (0 to 255) | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  | void format_value_ccm(unsigned int val, u8 *buf, u8 len) | ||||||
|  | { | ||||||
|  | 	int i; | ||||||
|  | 
 | ||||||
|  | 	/* First clear full output buffer */ | ||||||
|  | 	memset(buf, 0, len); | ||||||
|  | 
 | ||||||
|  | 	/* Then, starting from right side, fill in with data */ | ||||||
|  | 	for (i = 0; i < len; i++) { | ||||||
|  | 		buf[len - i - 1] = (val >> (8 * i)) & 0xff; | ||||||
|  | 		if (i >= 3) | ||||||
|  | 			break;  /* Only handle up to 32 bits of 'val' */ | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										116
									
								
								drivers/crypto/bcm/util.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								drivers/crypto/bcm/util.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,116 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright 2016 Broadcom | ||||||
|  |  * | ||||||
|  |  * This program is free software; you can redistribute it and/or modify | ||||||
|  |  * it under the terms of the GNU General Public License, version 2, as | ||||||
|  |  * published by the Free Software Foundation (the "GPL"). | ||||||
|  |  * | ||||||
|  |  * This program is distributed in the hope that it will be useful, but | ||||||
|  |  * WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||||
|  |  * General Public License version 2 (GPLv2) for more details. | ||||||
|  |  * | ||||||
|  |  * You should have received a copy of the GNU General Public License | ||||||
|  |  * version 2 (GPLv2) along with this source code. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #ifndef _UTIL_H | ||||||
|  | #define _UTIL_H | ||||||
|  | 
 | ||||||
|  | #include <linux/kernel.h> | ||||||
|  | #include <linux/delay.h> | ||||||
|  | 
 | ||||||
|  | #include "spu.h" | ||||||
|  | 
 | ||||||
|  | extern int flow_debug_logging; | ||||||
|  | extern int packet_debug_logging; | ||||||
|  | extern int debug_logging_sleep; | ||||||
|  | 
 | ||||||
|  | #ifdef DEBUG | ||||||
|  | #define flow_log(...)	                \ | ||||||
|  | 	do {	                              \ | ||||||
|  | 		if (flow_debug_logging) {	        \ | ||||||
|  | 			printk(__VA_ARGS__);	          \ | ||||||
|  | 			if (debug_logging_sleep)	      \ | ||||||
|  | 				msleep(debug_logging_sleep);	\ | ||||||
|  | 		}	                                \ | ||||||
|  | 	} while (0) | ||||||
|  | #define flow_dump(msg, var, var_len)	   \ | ||||||
|  | 	do {	                                 \ | ||||||
|  | 		if (flow_debug_logging) {	           \ | ||||||
|  | 			print_hex_dump(KERN_ALERT, msg, DUMP_PREFIX_NONE,  \ | ||||||
|  | 					16, 1, var, var_len, false); \ | ||||||
|  | 				if (debug_logging_sleep)	       \ | ||||||
|  | 					msleep(debug_logging_sleep);   \ | ||||||
|  | 		}                                    \ | ||||||
|  | 	} while (0) | ||||||
|  | 
 | ||||||
|  | #define packet_log(...)               \ | ||||||
|  | 	do {                                \ | ||||||
|  | 		if (packet_debug_logging) {       \ | ||||||
|  | 			printk(__VA_ARGS__);            \ | ||||||
|  | 			if (debug_logging_sleep)        \ | ||||||
|  | 				msleep(debug_logging_sleep);  \ | ||||||
|  | 		}                                 \ | ||||||
|  | 	} while (0) | ||||||
|  | #define packet_dump(msg, var, var_len)   \ | ||||||
|  | 	do {                                   \ | ||||||
|  | 		if (packet_debug_logging) {          \ | ||||||
|  | 			print_hex_dump(KERN_ALERT, msg, DUMP_PREFIX_NONE,  \ | ||||||
|  | 					16, 1, var, var_len, false); \ | ||||||
|  | 			if (debug_logging_sleep)           \ | ||||||
|  | 				msleep(debug_logging_sleep);     \ | ||||||
|  | 		}                                    \ | ||||||
|  | 	} while (0) | ||||||
|  | 
 | ||||||
|  | void __dump_sg(struct scatterlist *sg, unsigned int skip, unsigned int len); | ||||||
|  | 
 | ||||||
|  | #define dump_sg(sg, skip, len)     __dump_sg(sg, skip, len) | ||||||
|  | 
 | ||||||
|  | #else /* !DEBUG_ON */ | ||||||
|  | 
 | ||||||
|  | #define flow_log(...) do {} while (0) | ||||||
|  | #define flow_dump(msg, var, var_len) do {} while (0) | ||||||
|  | #define packet_log(...) do {} while (0) | ||||||
|  | #define packet_dump(msg, var, var_len) do {} while (0) | ||||||
|  | 
 | ||||||
|  | #define dump_sg(sg, skip, len) do {} while (0) | ||||||
|  | 
 | ||||||
|  | #endif /* DEBUG_ON */ | ||||||
|  | 
 | ||||||
|  | int spu_sg_at_offset(struct scatterlist *sg, unsigned int skip, | ||||||
|  | 		     struct scatterlist **sge, unsigned int *sge_offset); | ||||||
|  | 
 | ||||||
|  | /* Copy sg data, from skip, length len, to dest */ | ||||||
|  | void sg_copy_part_to_buf(struct scatterlist *src, u8 *dest, | ||||||
|  | 			 unsigned int len, unsigned int skip); | ||||||
|  | /* Copy src into scatterlist from offset, length len */ | ||||||
|  | void sg_copy_part_from_buf(struct scatterlist *dest, u8 *src, | ||||||
|  | 			   unsigned int len, unsigned int skip); | ||||||
|  | 
 | ||||||
|  | int spu_sg_count(struct scatterlist *sg_list, unsigned int skip, int nbytes); | ||||||
|  | u32 spu_msg_sg_add(struct scatterlist **to_sg, | ||||||
|  | 		   struct scatterlist **from_sg, u32 *skip, | ||||||
|  | 		   u8 from_nents, u32 tot_len); | ||||||
|  | 
 | ||||||
|  | void add_to_ctr(u8 *ctr_pos, unsigned int increment); | ||||||
|  | 
 | ||||||
|  | /* do a synchronous decrypt operation */ | ||||||
|  | int do_decrypt(char *alg_name, | ||||||
|  | 	       void *key_ptr, unsigned int key_len, | ||||||
|  | 	       void *iv_ptr, void *src_ptr, void *dst_ptr, | ||||||
|  | 	       unsigned int block_len); | ||||||
|  | 
 | ||||||
|  | /* produce a message digest from data of length n bytes */ | ||||||
|  | int do_shash(unsigned char *name, unsigned char *result, | ||||||
|  | 	     const u8 *data1, unsigned int data1_len, | ||||||
|  | 	     const u8 *data2, unsigned int data2_len, | ||||||
|  | 	     const u8 *key, unsigned int key_len); | ||||||
|  | 
 | ||||||
|  | char *spu_alg_name(enum spu_cipher_alg alg, enum spu_cipher_mode mode); | ||||||
|  | 
 | ||||||
|  | void spu_setup_debugfs(void); | ||||||
|  | void spu_free_debugfs(void); | ||||||
|  | void format_value_ccm(unsigned int val, u8 *buf, u8 len); | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
		Loading…
	
		Reference in a new issue
	
	 Rob Rice
						Rob Rice