/* * The driver of SUNXI SecuritySystem controller. * * Copyright (C) 2013 Allwinner. * * Mintow * * This file is licensed under the terms of the GNU General Public * License version 2. This program is licensed "as is" without any * warranty of any kind, whether express or implied. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "sunxi_ss.h" #include "sunxi_ss_proc.h" #include "sunxi_ss_reg.h" #ifdef CONFIG_OF #include #include #include static const struct of_device_id sunxi_ss_of_match[] = { {.compatible = "allwinner,sunxi-ce",}, {}, }; MODULE_DEVICE_TABLE(of, sunxi_ss_of_match); #endif sunxi_ss_t *ss_dev; static DEFINE_MUTEX(ss_lock); void ss_dev_lock(void) { mutex_lock(&ss_lock); } void ss_dev_unlock(void) { mutex_unlock(&ss_lock); } void __iomem *ss_membase(void) { return ss_dev->base_addr; } void ss_reset(void) { SS_ENTER(); sunxi_periph_reset_assert(ss_dev->mclk); sunxi_periph_reset_deassert(ss_dev->mclk); } #ifdef SS_RSA_CLK_ENABLE void ss_clk_set(u32 rate) { #ifdef CONFIG_EVB_PLATFORM int ret = 0; ret = clk_get_rate(ss_dev->mclk); if (ret == rate) return; SS_DBG("Change the SS clk to %d MHz.\n", rate/1000000); ret = clk_set_rate(ss_dev->mclk, rate); if (ret != 0) SS_ERR("clk_set_rate(%d) failed! return %d\n", rate, ret); #endif } #endif static int ss_aes_key_is_weak(const u8 *key, unsigned int keylen) { s32 i; u8 tmp = key[0]; for (i = 0; i < keylen; i++) if (tmp != key[i]) return 0; SS_ERR("The key is weak!\n"); return 1; } static int ss_aes_setkey(struct crypto_ablkcipher *tfm, const u8 *key, unsigned int keylen) { int ret = 0; ss_aes_ctx_t *ctx = crypto_ablkcipher_ctx(tfm); SS_DBG("keylen = %d\n", keylen); if (ctx->comm.flags & SS_FLAG_NEW_KEY) { SS_ERR("The key has already update.\n"); return -EBUSY; } ret = ss_aes_key_valid(tfm, keylen); if (ret != 0) return ret; if (ss_aes_key_is_weak(key, keylen)) { crypto_ablkcipher_tfm(tfm)->crt_flags |= CRYPTO_TFM_REQ_WEAK_KEY; /* testmgr.c need this, but we don't want to support it. */ /* return -EINVAL; */ } ctx->key_size = keylen; memcpy(ctx->key, key, keylen); if (keylen < AES_KEYSIZE_256) memset(&ctx->key[keylen], 0, AES_KEYSIZE_256 - keylen); ctx->comm.flags |= SS_FLAG_NEW_KEY; return 0; } static int ss_aes_ecb_encrypt(struct ablkcipher_request *req) { return ss_aes_crypt(req, SS_DIR_ENCRYPT, SS_METHOD_AES, SS_AES_MODE_ECB); } static int ss_aes_ecb_decrypt(struct ablkcipher_request *req) { return ss_aes_crypt(req, SS_DIR_DECRYPT, SS_METHOD_AES, SS_AES_MODE_ECB); } static int ss_aes_cbc_encrypt(struct ablkcipher_request *req) { return ss_aes_crypt(req, SS_DIR_ENCRYPT, SS_METHOD_AES, SS_AES_MODE_CBC); } static int ss_aes_cbc_decrypt(struct ablkcipher_request *req) { return ss_aes_crypt(req, SS_DIR_DECRYPT, SS_METHOD_AES, SS_AES_MODE_CBC); } #ifdef SS_CTR_MODE_ENABLE static int ss_aes_ctr_encrypt(struct ablkcipher_request *req) { return ss_aes_crypt(req, SS_DIR_ENCRYPT, SS_METHOD_AES, SS_AES_MODE_CTR); } static int ss_aes_ctr_decrypt(struct ablkcipher_request *req) { return ss_aes_crypt(req, SS_DIR_DECRYPT, SS_METHOD_AES, SS_AES_MODE_CTR); } #endif #ifdef SS_CTS_MODE_ENABLE static int ss_aes_cts_encrypt(struct ablkcipher_request *req) { return ss_aes_crypt(req, SS_DIR_ENCRYPT, SS_METHOD_AES, SS_AES_MODE_CTS); } static int ss_aes_cts_decrypt(struct ablkcipher_request *req) { return ss_aes_crypt(req, SS_DIR_DECRYPT, SS_METHOD_AES, SS_AES_MODE_CTS); } #endif #ifdef SS_XTS_MODE_ENABLE static int ss_aes_xts_encrypt(struct ablkcipher_request *req) { return ss_aes_crypt(req, SS_DIR_ENCRYPT, SS_METHOD_AES, SS_AES_MODE_XTS); } static int ss_aes_xts_decrypt(struct ablkcipher_request *req) { return ss_aes_crypt(req, SS_DIR_DECRYPT, SS_METHOD_AES, SS_AES_MODE_XTS); } #endif #ifdef SS_OFB_MODE_ENABLE static int ss_aes_ofb_encrypt(struct ablkcipher_request *req) { return ss_aes_crypt(req, SS_DIR_ENCRYPT, SS_METHOD_AES, SS_AES_MODE_OFB); } static int ss_aes_ofb_decrypt(struct ablkcipher_request *req) { return ss_aes_crypt(req, SS_DIR_DECRYPT, SS_METHOD_AES, SS_AES_MODE_OFB); } #endif #ifdef SS_CFB_MODE_ENABLE static int ss_aes_cfb1_encrypt(struct ablkcipher_request *req) { ss_aes_req_ctx_t *req_ctx = ablkcipher_request_ctx(req); req_ctx->bitwidth = 1; return ss_aes_crypt(req, SS_DIR_ENCRYPT, SS_METHOD_AES, SS_AES_MODE_CFB); } static int ss_aes_cfb1_decrypt(struct ablkcipher_request *req) { ss_aes_req_ctx_t *req_ctx = ablkcipher_request_ctx(req); req_ctx->bitwidth = 1; return ss_aes_crypt(req, SS_DIR_DECRYPT, SS_METHOD_AES, SS_AES_MODE_CFB); } static int ss_aes_cfb8_encrypt(struct ablkcipher_request *req) { ss_aes_req_ctx_t *req_ctx = ablkcipher_request_ctx(req); req_ctx->bitwidth = 8; return ss_aes_crypt(req, SS_DIR_ENCRYPT, SS_METHOD_AES, SS_AES_MODE_CFB); } static int ss_aes_cfb8_decrypt(struct ablkcipher_request *req) { ss_aes_req_ctx_t *req_ctx = ablkcipher_request_ctx(req); req_ctx->bitwidth = 8; return ss_aes_crypt(req, SS_DIR_DECRYPT, SS_METHOD_AES, SS_AES_MODE_CFB); } static int ss_aes_cfb64_encrypt(struct ablkcipher_request *req) { ss_aes_req_ctx_t *req_ctx = ablkcipher_request_ctx(req); req_ctx->bitwidth = 64; return ss_aes_crypt(req, SS_DIR_ENCRYPT, SS_METHOD_AES, SS_AES_MODE_CFB); } static int ss_aes_cfb64_decrypt(struct ablkcipher_request *req) { ss_aes_req_ctx_t *req_ctx = ablkcipher_request_ctx(req); req_ctx->bitwidth = 64; return ss_aes_crypt(req, SS_DIR_DECRYPT, SS_METHOD_AES, SS_AES_MODE_CFB); } static int ss_aes_cfb128_encrypt(struct ablkcipher_request *req) { ss_aes_req_ctx_t *req_ctx = ablkcipher_request_ctx(req); req_ctx->bitwidth = 128; return ss_aes_crypt(req, SS_DIR_ENCRYPT, SS_METHOD_AES, SS_AES_MODE_CFB); } static int ss_aes_cfb128_decrypt(struct ablkcipher_request *req) { ss_aes_req_ctx_t *req_ctx = ablkcipher_request_ctx(req); req_ctx->bitwidth = 128; return ss_aes_crypt(req, SS_DIR_DECRYPT, SS_METHOD_AES, SS_AES_MODE_CFB); } #endif static int ss_des_ecb_encrypt(struct ablkcipher_request *req) { return ss_aes_crypt(req, SS_DIR_ENCRYPT, SS_METHOD_DES, SS_AES_MODE_ECB); } static int ss_des_ecb_decrypt(struct ablkcipher_request *req) { return ss_aes_crypt(req, SS_DIR_DECRYPT, SS_METHOD_DES, SS_AES_MODE_ECB); } static int ss_des_cbc_encrypt(struct ablkcipher_request *req) { return ss_aes_crypt(req, SS_DIR_ENCRYPT, SS_METHOD_DES, SS_AES_MODE_CBC); } static int ss_des_cbc_decrypt(struct ablkcipher_request *req) { return ss_aes_crypt(req, SS_DIR_DECRYPT, SS_METHOD_DES, SS_AES_MODE_CBC); } static int ss_des3_ecb_encrypt(struct ablkcipher_request *req) { return ss_aes_crypt(req, SS_DIR_ENCRYPT, SS_METHOD_3DES, SS_AES_MODE_ECB); } static int ss_des3_ecb_decrypt(struct ablkcipher_request *req) { return ss_aes_crypt(req, SS_DIR_DECRYPT, SS_METHOD_3DES, SS_AES_MODE_ECB); } static int ss_des3_cbc_encrypt(struct ablkcipher_request *req) { return ss_aes_crypt(req, SS_DIR_ENCRYPT, SS_METHOD_3DES, SS_AES_MODE_CBC); } static int ss_des3_cbc_decrypt(struct ablkcipher_request *req) { return ss_aes_crypt(req, SS_DIR_DECRYPT, SS_METHOD_3DES, SS_AES_MODE_CBC); } #ifdef SS_RSA_ENABLE static int ss_rsa_encrypt(struct ablkcipher_request *req) { return ss_aes_crypt(req, SS_DIR_ENCRYPT, SS_METHOD_RSA, CE_RSA_OP_M_EXP); } static int ss_rsa_decrypt(struct ablkcipher_request *req) { return ss_aes_crypt(req, SS_DIR_DECRYPT, SS_METHOD_RSA, CE_RSA_OP_M_EXP); } #endif #ifdef SS_DH_ENABLE static int ss_dh_encrypt(struct ablkcipher_request *req) { return ss_aes_crypt(req, SS_DIR_ENCRYPT, SS_METHOD_DH, CE_RSA_OP_M_EXP); } static int ss_dh_decrypt(struct ablkcipher_request *req) { return ss_aes_crypt(req, SS_DIR_DECRYPT, SS_METHOD_DH, CE_RSA_OP_M_EXP); } #endif #ifdef SS_ECC_ENABLE static int ss_ecdh_encrypt(struct ablkcipher_request *req) { return ss_aes_crypt(req, SS_DIR_ENCRYPT, SS_METHOD_ECC, CE_ECC_OP_POINT_MUL); } static int ss_ecdh_decrypt(struct ablkcipher_request *req) { return ss_aes_crypt(req, SS_DIR_DECRYPT, SS_METHOD_ECC, CE_ECC_OP_POINT_MUL); } static int ss_ecc_sign_encrypt(struct ablkcipher_request *req) { return ss_aes_crypt(req, SS_DIR_ENCRYPT, SS_METHOD_ECC, CE_ECC_OP_SIGN); } static int ss_ecc_sign_decrypt(struct ablkcipher_request *req) { return ss_aes_crypt(req, SS_DIR_DECRYPT, SS_METHOD_ECC, CE_ECC_OP_SIGN); } static int ss_ecc_verify_encrypt(struct ablkcipher_request *req) { return ss_aes_crypt(req, SS_DIR_ENCRYPT, SS_METHOD_RSA, CE_RSA_OP_M_MUL); } static int ss_ecc_verify_decrypt(struct ablkcipher_request *req) { return ss_aes_crypt(req, SS_DIR_DECRYPT, SS_METHOD_RSA, CE_RSA_OP_M_MUL); } #endif #ifdef SS_HMAC_SHA1_ENABLE static int ss_hmac_sha1_encrypt(struct ablkcipher_request *req) { return ss_aes_crypt(req, SS_DIR_ENCRYPT, SS_METHOD_HMAC_SHA1, SS_AES_MODE_ECB); } #endif #ifdef SS_HMAC_SHA256_ENABLE static int ss_hmac_sha256_encrypt(struct ablkcipher_request *req) { return ss_aes_crypt(req, SS_DIR_ENCRYPT, SS_METHOD_HMAC_SHA256, SS_AES_MODE_ECB); } #endif int ss_rng_reset(struct crypto_rng *tfm, const u8 *seed, u32 slen) { int len = slen > SS_PRNG_SEED_LEN ? SS_PRNG_SEED_LEN : slen; ss_aes_ctx_t *ctx = crypto_rng_ctx(tfm); SS_DBG("Seed len: %d/%d, flags = %#x\n", len, slen, ctx->comm.flags); ctx->key_size = len; memset(ctx->key, 0, SS_PRNG_SEED_LEN); memcpy(ctx->key, seed, len); ctx->comm.flags |= SS_FLAG_NEW_KEY; return 0; } int ss_flow_request(ss_comm_ctx_t *comm) { int i; unsigned long flags = 0; spin_lock_irqsave(&ss_dev->lock, flags); for (i = 0; i < SS_FLOW_NUM; i++) { if (ss_dev->flows[i].available == SS_FLOW_AVAILABLE) { comm->flow = i; ss_dev->flows[i].available = SS_FLOW_UNAVAILABLE; SS_DBG("The flow %d is available.\n", i); break; } } spin_unlock_irqrestore(&ss_dev->lock, flags); if (i == SS_FLOW_NUM) { SS_ERR("Failed to get an available flow.\n"); i = -1; } return i; } void ss_flow_release(ss_comm_ctx_t *comm) { unsigned long flags = 0; spin_lock_irqsave(&ss_dev->lock, flags); ss_dev->flows[comm->flow].available = SS_FLOW_AVAILABLE; spin_unlock_irqrestore(&ss_dev->lock, flags); } static int sunxi_ss_cra_init(struct crypto_tfm *tfm) { if (ss_flow_request(crypto_tfm_ctx(tfm)) < 0) return -1; tfm->crt_ablkcipher.reqsize = sizeof(ss_aes_req_ctx_t); SS_DBG("reqsize = %d\n", tfm->crt_u.ablkcipher.reqsize); return 0; } static int sunxi_ss_cra_rng_init(struct crypto_tfm *tfm) { if (ss_flow_request(crypto_tfm_ctx(tfm)) < 0) return -1; return 0; } static int sunxi_ss_cra_hash_init(struct crypto_tfm *tfm) { if (ss_flow_request(crypto_tfm_ctx(tfm)) < 0) return -1; crypto_ahash_set_reqsize(__crypto_ahash_cast(tfm), sizeof(ss_aes_req_ctx_t)); SS_DBG("reqsize = %zu\n", sizeof(ss_aes_req_ctx_t)); return 0; } static void sunxi_ss_cra_exit(struct crypto_tfm *tfm) { SS_ENTER(); ss_flow_release(crypto_tfm_ctx(tfm)); /* sun8iw6 and sun9iw1 need reset SS controller after each operation. */ #ifdef SS_IDMA_ENABLE ss_reset(); #endif } static int ss_hash_init(struct ahash_request *req, int type, int size, char *iv) { ss_aes_req_ctx_t *req_ctx = ahash_request_ctx(req); ss_hash_ctx_t *ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(req)); SS_DBG("Method: %d\n", type); memset(req_ctx, 0, sizeof(ss_aes_req_ctx_t)); req_ctx->type = type; ctx->md_size = size; memcpy(ctx->md, iv, size); ctx->cnt = 0; memset(ctx->pad, 0, SS_HASH_PAD_SIZE); return 0; } static int ss_md5_init(struct ahash_request *req) { int iv[MD5_DIGEST_SIZE/4] = {SHA1_H0, SHA1_H1, SHA1_H2, SHA1_H3}; return ss_hash_init(req, SS_METHOD_MD5, MD5_DIGEST_SIZE, (char *)iv); } static int ss_sha1_init(struct ahash_request *req) { int iv[SHA1_DIGEST_SIZE/4] = { SHA1_H0, SHA1_H1, SHA1_H2, SHA1_H3, SHA1_H4}; #ifdef SS_SHA_SWAP_PRE_ENABLE #ifdef SS_SHA_NO_SWAP_IV4 ss_hash_swap((char *)iv, SHA1_DIGEST_SIZE - 4); #else ss_hash_swap((char *)iv, SHA1_DIGEST_SIZE); #endif #endif return ss_hash_init(req, SS_METHOD_SHA1, SHA1_DIGEST_SIZE, (char *)iv); } #ifdef SS_SHA224_ENABLE static int ss_sha224_init(struct ahash_request *req) { int iv[SHA256_DIGEST_SIZE/4] = { SHA224_H0, SHA224_H1, SHA224_H2, SHA224_H3, SHA224_H4, SHA224_H5, SHA224_H6, SHA224_H7}; #ifdef SS_SHA_SWAP_PRE_ENABLE ss_hash_swap((char *)iv, SHA256_DIGEST_SIZE); #endif return ss_hash_init(req, SS_METHOD_SHA224, SHA256_DIGEST_SIZE, (char *)iv); } #endif #ifdef SS_SHA256_ENABLE static int ss_sha256_init(struct ahash_request *req) { int iv[SHA256_DIGEST_SIZE/4] = { SHA256_H0, SHA256_H1, SHA256_H2, SHA256_H3, SHA256_H4, SHA256_H5, SHA256_H6, SHA256_H7}; #ifdef SS_SHA_SWAP_PRE_ENABLE ss_hash_swap((char *)iv, SHA256_DIGEST_SIZE); #endif return ss_hash_init(req, SS_METHOD_SHA256, SHA256_DIGEST_SIZE, (char *)iv); } #endif #define GET_U64_HIGH(data64) (int)(data64 >> 32) #define GET_U64_LOW(data64) (int)(data64 & 0xFFFFFFFF) #ifdef SS_SHA384_ENABLE static int ss_sha384_init(struct ahash_request *req) { int iv[SHA512_DIGEST_SIZE/4] = { GET_U64_HIGH(SHA384_H0), GET_U64_LOW(SHA384_H0), GET_U64_HIGH(SHA384_H1), GET_U64_LOW(SHA384_H1), GET_U64_HIGH(SHA384_H2), GET_U64_LOW(SHA384_H2), GET_U64_HIGH(SHA384_H3), GET_U64_LOW(SHA384_H3), GET_U64_HIGH(SHA384_H4), GET_U64_LOW(SHA384_H4), GET_U64_HIGH(SHA384_H5), GET_U64_LOW(SHA384_H5), GET_U64_HIGH(SHA384_H6), GET_U64_LOW(SHA384_H6), GET_U64_HIGH(SHA384_H7), GET_U64_LOW(SHA384_H7)}; #ifdef SS_SHA_SWAP_PRE_ENABLE ss_hash_swap((char *)iv, SHA512_DIGEST_SIZE); #endif return ss_hash_init(req, SS_METHOD_SHA384, SHA512_DIGEST_SIZE, (char *)iv); } #endif #ifdef SS_SHA512_ENABLE static int ss_sha512_init(struct ahash_request *req) { int iv[SHA512_DIGEST_SIZE/4] = { GET_U64_HIGH(SHA512_H0), GET_U64_LOW(SHA512_H0), GET_U64_HIGH(SHA512_H1), GET_U64_LOW(SHA512_H1), GET_U64_HIGH(SHA512_H2), GET_U64_LOW(SHA512_H2), GET_U64_HIGH(SHA512_H3), GET_U64_LOW(SHA512_H3), GET_U64_HIGH(SHA512_H4), GET_U64_LOW(SHA512_H4), GET_U64_HIGH(SHA512_H5), GET_U64_LOW(SHA512_H5), GET_U64_HIGH(SHA512_H6), GET_U64_LOW(SHA512_H6), GET_U64_HIGH(SHA512_H7), GET_U64_LOW(SHA512_H7)}; #ifdef SS_SHA_SWAP_PRE_ENABLE ss_hash_swap((char *)iv, SHA512_DIGEST_SIZE); #endif return ss_hash_init(req, SS_METHOD_SHA512, SHA512_DIGEST_SIZE, (char *)iv); } #endif #define DES_MIN_KEY_SIZE DES_KEY_SIZE #define DES_MAX_KEY_SIZE DES_KEY_SIZE #define DES3_MIN_KEY_SIZE DES3_EDE_KEY_SIZE #define DES3_MAX_KEY_SIZE DES3_EDE_KEY_SIZE #define DECLARE_SS_AES_ALG(utype, ltype, lmode, block_size, iv_size) \ { \ .cra_name = #lmode"("#ltype")", \ .cra_driver_name = "ss-"#lmode"-"#ltype, \ .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC, \ .cra_type = &crypto_ablkcipher_type, \ .cra_blocksize = block_size, \ .cra_alignmask = 3, \ .cra_u.ablkcipher = { \ .setkey = ss_aes_setkey, \ .encrypt = ss_##ltype##_##lmode##_encrypt, \ .decrypt = ss_##ltype##_##lmode##_decrypt, \ .min_keysize = utype##_MIN_KEY_SIZE, \ .max_keysize = utype##_MAX_KEY_SIZE, \ .ivsize = iv_size, \ } \ } #ifdef SS_XTS_MODE_ENABLE #define DECLARE_SS_AES_XTS_ALG(utype, ltype, lmode, block_size, iv_size) \ { \ .cra_name = #lmode"("#ltype")", \ .cra_driver_name = "ss-"#lmode"-"#ltype, \ .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC, \ .cra_type = &crypto_ablkcipher_type, \ .cra_blocksize = block_size, \ .cra_alignmask = 3, \ .cra_u.ablkcipher = { \ .setkey = ss_aes_setkey, \ .encrypt = ss_##ltype##_##lmode##_encrypt, \ .decrypt = ss_##ltype##_##lmode##_decrypt, \ .min_keysize = utype##_MAX_KEY_SIZE, \ .max_keysize = utype##_MAX_KEY_SIZE * 2, \ .ivsize = iv_size, \ } \ } #endif #define DECLARE_SS_ASYM_ALG(type, bitwidth, key_size, iv_size) \ { \ .cra_name = #type"("#bitwidth")", \ .cra_driver_name = "ss-"#type"-"#bitwidth, \ .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC, \ .cra_type = &crypto_ablkcipher_type, \ .cra_blocksize = key_size%AES_BLOCK_SIZE == 0 ? AES_BLOCK_SIZE : 4, \ .cra_alignmask = key_size%AES_BLOCK_SIZE == 0 ? 31 : 3, \ .cra_u.ablkcipher = { \ .setkey = ss_aes_setkey, \ .encrypt = ss_##type##_encrypt, \ .decrypt = ss_##type##_decrypt, \ .min_keysize = key_size, \ .max_keysize = key_size, \ .ivsize = iv_size, \ }, \ } #ifndef SS_SUPPORT_CE_V3_2 #define DECLARE_SS_RSA_ALG(type, bitwidth) \ DECLARE_SS_ASYM_ALG(type, bitwidth, (bitwidth/8), (bitwidth/8)) #else #define DECLARE_SS_RSA_ALG(type, bitwidth) \ DECLARE_SS_ASYM_ALG(type, bitwidth, (bitwidth/8), 0) #endif #define DECLARE_SS_DH_ALG(type, bitwidth) DECLARE_SS_RSA_ALG(type, bitwidth) static struct crypto_alg sunxi_ss_algs[] = { DECLARE_SS_AES_ALG(AES, aes, ecb, AES_BLOCK_SIZE, 0), DECLARE_SS_AES_ALG(AES, aes, cbc, AES_BLOCK_SIZE, AES_MIN_KEY_SIZE), #ifdef SS_CTR_MODE_ENABLE DECLARE_SS_AES_ALG(AES, aes, ctr, 1, AES_MIN_KEY_SIZE), #endif #ifdef SS_CTS_MODE_ENABLE DECLARE_SS_AES_ALG(AES, aes, cts, 1, AES_MIN_KEY_SIZE), #endif #ifdef SS_XTS_MODE_ENABLE DECLARE_SS_AES_XTS_ALG(AES, aes, xts, 1, AES_MIN_KEY_SIZE), #endif #ifdef SS_OFB_MODE_ENABLE DECLARE_SS_AES_ALG(AES, aes, ofb, AES_BLOCK_SIZE, AES_MIN_KEY_SIZE), #endif #ifdef SS_CFB_MODE_ENABLE DECLARE_SS_AES_ALG(AES, aes, cfb1, AES_BLOCK_SIZE, AES_MIN_KEY_SIZE), DECLARE_SS_AES_ALG(AES, aes, cfb8, AES_BLOCK_SIZE, AES_MIN_KEY_SIZE), DECLARE_SS_AES_ALG(AES, aes, cfb64, AES_BLOCK_SIZE, AES_MIN_KEY_SIZE), DECLARE_SS_AES_ALG(AES, aes, cfb128, AES_BLOCK_SIZE, AES_MIN_KEY_SIZE), #endif DECLARE_SS_AES_ALG(DES, des, ecb, DES_BLOCK_SIZE, 0), DECLARE_SS_AES_ALG(DES, des, cbc, DES_BLOCK_SIZE, DES_KEY_SIZE), DECLARE_SS_AES_ALG(DES3, des3, ecb, DES3_EDE_BLOCK_SIZE, 0), DECLARE_SS_AES_ALG(DES3, des3, cbc, DES3_EDE_BLOCK_SIZE, DES_KEY_SIZE), #ifdef SS_RSA512_ENABLE DECLARE_SS_RSA_ALG(rsa, 512), #endif #ifdef SS_RSA1024_ENABLE DECLARE_SS_RSA_ALG(rsa, 1024), #endif #ifdef SS_RSA2048_ENABLE DECLARE_SS_RSA_ALG(rsa, 2048), #endif #ifdef SS_RSA3072_ENABLE DECLARE_SS_RSA_ALG(rsa, 3072), #endif #ifdef SS_RSA4096_ENABLE DECLARE_SS_RSA_ALG(rsa, 4096), #endif #ifdef SS_DH512_ENABLE DECLARE_SS_DH_ALG(dh, 512), #endif #ifdef SS_DH1024_ENABLE DECLARE_SS_DH_ALG(dh, 1024), #endif #ifdef SS_DH2048_ENABLE DECLARE_SS_DH_ALG(dh, 2048), #endif #ifdef SS_DH3072_ENABLE DECLARE_SS_DH_ALG(dh, 3072), #endif #ifdef SS_DH4096_ENABLE DECLARE_SS_DH_ALG(dh, 4096), #endif #ifdef SS_ECC_ENABLE #ifndef SS_SUPPORT_CE_V3_2 DECLARE_SS_ASYM_ALG(ecdh, 160, 160/8, 160/8), DECLARE_SS_ASYM_ALG(ecdh, 224, 224/8, 224/8), DECLARE_SS_ASYM_ALG(ecdh, 256, 256/8, 256/8), DECLARE_SS_ASYM_ALG(ecdh, 521, ((521+31)/32)*4, ((521+31)/32)*4), DECLARE_SS_ASYM_ALG(ecc_sign, 160, 160/8, (160/8)*2), DECLARE_SS_ASYM_ALG(ecc_sign, 224, 224/8, (224/8)*2), DECLARE_SS_ASYM_ALG(ecc_sign, 256, 256/8, (256/8)*2), DECLARE_SS_ASYM_ALG(ecc_sign, 521, ((521+31)/32)*4, ((521+31)/32)*4*2), #else DECLARE_SS_ASYM_ALG(ecdh, 160, 160/8, 0), DECLARE_SS_ASYM_ALG(ecdh, 224, 224/8, 0), DECLARE_SS_ASYM_ALG(ecdh, 256, 256/8, 0), DECLARE_SS_ASYM_ALG(ecdh, 521, ((521+31)/32)*4, 0), DECLARE_SS_ASYM_ALG(ecc_sign, 160, 160/8, 0), DECLARE_SS_ASYM_ALG(ecc_sign, 224, 224/8, 0), DECLARE_SS_ASYM_ALG(ecc_sign, 256, 256/8, 0), DECLARE_SS_ASYM_ALG(ecc_sign, 521, ((521+31)/32)*4, 0), #endif DECLARE_SS_RSA_ALG(ecc_verify, 512), DECLARE_SS_RSA_ALG(ecc_verify, 1024), #endif #ifdef SS_HMAC_SHA1_ENABLE { .cra_name = "hmac-sha1", .cra_driver_name = "ss-hmac-sha1", .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC, .cra_type = &crypto_ablkcipher_type, .cra_blocksize = 4, .cra_alignmask = 3, .cra_u.ablkcipher = { .setkey = ss_aes_setkey, .encrypt = ss_hmac_sha1_encrypt, .decrypt = NULL, .min_keysize = SHA1_BLOCK_SIZE, .max_keysize = SHA1_BLOCK_SIZE, .ivsize = 0, } }, #endif #ifdef SS_HMAC_SHA256_ENABLE { .cra_name = "hmac-sha256", .cra_driver_name = "ss-hmac-sha256", .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER|CRYPTO_ALG_ASYNC, .cra_type = &crypto_ablkcipher_type, .cra_blocksize = 4, .cra_alignmask = 3, .cra_u.ablkcipher = { .setkey = ss_aes_setkey, .encrypt = ss_hmac_sha256_encrypt, .decrypt = NULL, .min_keysize = SHA256_BLOCK_SIZE, .max_keysize = SHA256_BLOCK_SIZE, .ivsize = 0, } }, #endif }; #define DECLARE_SS_RNG_ALG(ltype) \ { \ .generate = ss_##ltype##_get_random, \ .seed = ss_rng_reset, \ .seedsize = SS_SEED_SIZE, \ .base = { \ .cra_name = #ltype, \ .cra_driver_name = "ss-"#ltype, \ .cra_flags = CRYPTO_ALG_TYPE_RNG, \ .cra_priority = SS_ALG_PRIORITY, \ .cra_ctxsize = sizeof(ss_aes_ctx_t), \ .cra_module = THIS_MODULE, \ .cra_init = sunxi_ss_cra_rng_init, \ .cra_exit = sunxi_ss_cra_exit, \ } \ } static struct rng_alg sunxi_ss_algs_rng[] = { #ifdef SS_TRNG_ENABLE DECLARE_SS_RNG_ALG(trng), #endif DECLARE_SS_RNG_ALG(prng), }; #define MD5_BLOCK_SIZE MD5_HMAC_BLOCK_SIZE #define sha224_state sha256_state #define sha384_state sha512_state #define DECLARE_SS_AHASH_ALG(ltype, utype) \ { \ .init = ss_##ltype##_init, \ .update = ss_hash_update, \ .final = ss_hash_final, \ .finup = ss_hash_finup, \ .digest = ss_hash_digest, \ .halg = { \ .digestsize = utype##_DIGEST_SIZE, \ .statesize = sizeof(struct ltype##_state), \ .base = { \ .cra_name = #ltype, \ .cra_driver_name = "ss-"#ltype, \ .cra_priority = SS_ALG_PRIORITY, \ .cra_flags = CRYPTO_ALG_TYPE_AHASH|CRYPTO_ALG_ASYNC, \ .cra_blocksize = utype##_BLOCK_SIZE, \ .cra_ctxsize = sizeof(ss_hash_ctx_t), \ .cra_alignmask = 3, \ .cra_module = THIS_MODULE, \ .cra_init = sunxi_ss_cra_hash_init, \ .cra_exit = sunxi_ss_cra_exit, \ } \ } \ } static struct ahash_alg sunxi_ss_algs_hash[] = { DECLARE_SS_AHASH_ALG(md5, MD5), DECLARE_SS_AHASH_ALG(sha1, SHA1), #ifdef SS_SHA224_ENABLE DECLARE_SS_AHASH_ALG(sha224, SHA224), #endif #ifdef SS_SHA256_ENABLE DECLARE_SS_AHASH_ALG(sha256, SHA256), #endif #ifdef SS_SHA384_ENABLE DECLARE_SS_AHASH_ALG(sha384, SHA384), #endif #ifdef SS_SHA512_ENABLE DECLARE_SS_AHASH_ALG(sha512, SHA512), #endif }; /* Requeset the resource: IRQ, mem */ static int sunxi_ss_res_request(struct platform_device *pdev) { int ret = 0; struct device_node *pnode = pdev->dev.of_node; sunxi_ss_t *sss = platform_get_drvdata(pdev); #ifdef SS_IDMA_ENABLE int i; for (i = 0; i < SS_FLOW_NUM; i++) { sss->flows[i].buf_src = kmalloc(SS_DMA_BUF_SIZE, GFP_KERNEL); if (sss->flows[i].buf_src == NULL) { SS_ERR("Can not allocate DMA source buffer\n"); return -ENOMEM; } sss->flows[i].buf_src_dma = virt_to_phys(sss->flows[i].buf_src); sss->flows[i].buf_dst = kmalloc(SS_DMA_BUF_SIZE, GFP_KERNEL); if (sss->flows[i].buf_dst == NULL) { SS_ERR("Can not allocate DMA source buffer\n"); return -ENOMEM; } sss->flows[i].buf_dst_dma = virt_to_phys(sss->flows[i].buf_dst); init_completion(&sss->flows[i].done); } #endif sss->irq = irq_of_parse_and_map(pnode, SS_RES_INDEX); if (sss->irq == 0) { SS_ERR("Failed to get the SS IRQ.\n"); return -EINVAL; } ret = request_irq(sss->irq, sunxi_ss_irq_handler, 0, sss->dev_name, sss); if (ret != 0) { SS_ERR("Cannot request IRQ\n"); return ret; } #ifdef CONFIG_OF sss->base_addr = of_iomap(pnode, SS_RES_INDEX); if (sss->base_addr == NULL) { SS_ERR("Unable to remap IO\n"); return -ENXIO; } #endif return 0; } /* Release the resource: IRQ, mem */ static int sunxi_ss_res_release(sunxi_ss_t *sss) { #ifdef SS_IDMA_ENABLE int i; #endif iounmap(sss->base_addr); #ifdef SS_IDMA_ENABLE for (i = 0; i < SS_FLOW_NUM; i++) { kfree(sss->flows[i].buf_src); kfree(sss->flows[i].buf_dst); } #endif free_irq(sss->irq, sss); return 0; } static int sunxi_ss_hw_init(sunxi_ss_t *sss) { #ifdef CONFIG_EVB_PLATFORM int ret = 0; #endif struct clk *pclk = NULL; struct device_node *pnode = sss->pdev->dev.of_node; pclk = of_clk_get(pnode, 1); if (IS_ERR_OR_NULL(pclk)) { SS_ERR("Unable to get pll clock, return %x\n", PTR_RET(pclk)); return PTR_RET(pclk); } sss->mclk = of_clk_get(pnode, 0); if (IS_ERR_OR_NULL(sss->mclk)) { SS_ERR("Fail to get module clk, ret %x\n", PTR_RET(sss->mclk)); return PTR_RET(sss->mclk); } #ifdef SS_RSA_CLK_ENABLE if (of_property_read_u32_array(pnode, "clock-frequency", &sss->gen_clkrate, 2)) { #else if (of_property_read_u32(pnode, "clock-frequency", &sss->gen_clkrate)) { #endif SS_ERR("Unable to get clock-frequency.\n"); return -EINVAL; } SS_DBG("The clk freq: %d, %d\n", sss->gen_clkrate, sss->rsa_clkrate); #ifdef CONFIG_EVB_PLATFORM ret = clk_set_parent(sss->mclk, pclk); if (ret != 0) { SS_ERR("clk_set_parent() failed! return %d\n", ret); return ret; } ret = clk_set_rate(sss->mclk, sss->gen_clkrate); if (ret != 0) { SS_ERR("Set rate(%d) failed! ret %d\n", sss->gen_clkrate, ret); return ret; } #endif SS_DBG("SS mclk %luMHz, pclk %luMHz\n", clk_get_rate(sss->mclk)/1000000, clk_get_rate(pclk)/1000000); if (clk_prepare_enable(sss->mclk)) { SS_ERR("Couldn't enable module clock\n"); return -EBUSY; } clk_put(pclk); return 0; } static int sunxi_ss_hw_exit(sunxi_ss_t *sss) { clk_disable_unprepare(sss->mclk); clk_put(sss->mclk); sss->mclk = NULL; return 0; } static int sunxi_ss_alg_register(void) { int i; int ret = 0; for (i = 0; i < ARRAY_SIZE(sunxi_ss_algs); i++) { INIT_LIST_HEAD(&sunxi_ss_algs[i].cra_list); SS_DBG("Add %s...\n", sunxi_ss_algs[i].cra_name); sunxi_ss_algs[i].cra_priority = SS_ALG_PRIORITY; sunxi_ss_algs[i].cra_ctxsize = sizeof(ss_aes_ctx_t); sunxi_ss_algs[i].cra_module = THIS_MODULE; sunxi_ss_algs[i].cra_exit = sunxi_ss_cra_exit; sunxi_ss_algs[i].cra_init = sunxi_ss_cra_init; ret = crypto_register_alg(&sunxi_ss_algs[i]); if (ret != 0) { SS_ERR("crypto_register_alg(%s) failed! return %d\n", sunxi_ss_algs[i].cra_name, ret); return ret; } } for (i = 0; i < ARRAY_SIZE(sunxi_ss_algs_rng); i++) { SS_DBG("Add %s...\n", sunxi_ss_algs_rng[i].base.cra_name); ret = crypto_register_rng(&sunxi_ss_algs_rng[i]); if (ret != 0) { SS_ERR("crypto_register_rng(%s) failed! return %d\n", sunxi_ss_algs_rng[i].base.cra_name, ret); return ret; } } for (i = 0; i < ARRAY_SIZE(sunxi_ss_algs_hash); i++) { SS_DBG("Add %s...\n", sunxi_ss_algs_hash[i].halg.base.cra_name); ret = crypto_register_ahash(&sunxi_ss_algs_hash[i]); if (ret != 0) { SS_ERR("crypto_register_ahash(%s) failed! return %d\n", sunxi_ss_algs_hash[i].halg.base.cra_name, ret); return ret; } } return 0; } static void sunxi_ss_alg_unregister(void) { int i; for (i = 0; i < ARRAY_SIZE(sunxi_ss_algs); i++) crypto_unregister_alg(&sunxi_ss_algs[i]); for (i = 0; i < ARRAY_SIZE(sunxi_ss_algs_rng); i++) crypto_unregister_rng(&sunxi_ss_algs_rng[i]); for (i = 0; i < ARRAY_SIZE(sunxi_ss_algs_hash); i++) crypto_unregister_ahash(&sunxi_ss_algs_hash[i]); } static ssize_t sunxi_ss_info_show(struct device *dev, struct device_attribute *attr, char *buf) { struct platform_device *pdev = container_of(dev, struct platform_device, dev); sunxi_ss_t *sss = platform_get_drvdata(pdev); return snprintf(buf, PAGE_SIZE, "pdev->id = %d\n" "pdev->name = %s\n" "pdev->num_resources = %u\n" "pdev->resource.irq = %d\n" "SS module clk rate = %ld Mhz\n" "IO membase = 0x%p\n", pdev->id, pdev->name, pdev->num_resources, sss->irq, (clk_get_rate(sss->mclk)/1000000), sss->base_addr); } static struct device_attribute sunxi_ss_info_attr = __ATTR(info, S_IRUGO, sunxi_ss_info_show, NULL); static ssize_t sunxi_ss_status_show(struct device *dev, struct device_attribute *attr, char *buf) { int i; struct platform_device *pdev = container_of(dev, struct platform_device, dev); sunxi_ss_t *sss = platform_get_drvdata(pdev); static char const *avail[] = {"Available", "Unavailable"}; if (sss == NULL) return snprintf(buf, PAGE_SIZE, "%s\n", "sunxi_ss is NULL!"); buf[0] = 0; for (i = 0; i < SS_FLOW_NUM; i++) { snprintf(buf+strlen(buf), PAGE_SIZE-strlen(buf), "The flow %d state: %s\n" #ifdef SS_IDMA_ENABLE " Src: 0x%p / 0x%08x\n" " Dst: 0x%p / 0x%08x\n" #endif , i, avail[sss->flows[i].available] #ifdef SS_IDMA_ENABLE , sss->flows[i].buf_src, sss->flows[i].buf_src_dma , sss->flows[i].buf_dst, sss->flows[i].buf_dst_dma #endif ); } return strlen(buf) + ss_reg_print(buf + strlen(buf), PAGE_SIZE - strlen(buf)); } static struct device_attribute sunxi_ss_status_attr = __ATTR(status, S_IRUGO, sunxi_ss_status_show, NULL); static void sunxi_ss_sysfs_create(struct platform_device *_pdev) { device_create_file(&_pdev->dev, &sunxi_ss_info_attr); device_create_file(&_pdev->dev, &sunxi_ss_status_attr); } static void sunxi_ss_sysfs_remove(struct platform_device *_pdev) { device_remove_file(&_pdev->dev, &sunxi_ss_info_attr); device_remove_file(&_pdev->dev, &sunxi_ss_status_attr); } static u64 sunxi_ss_dma_mask = DMA_BIT_MASK(32); static int sunxi_ss_probe(struct platform_device *pdev) { int ret = 0; sunxi_ss_t *sss = NULL; sss = devm_kzalloc(&pdev->dev, sizeof(sunxi_ss_t), GFP_KERNEL); if (sss == NULL) { SS_ERR("Unable to allocate sunxi_ss_t\n"); return -ENOMEM; } #ifdef CONFIG_OF pdev->dev.dma_mask = &sunxi_ss_dma_mask; pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); #endif snprintf(sss->dev_name, sizeof(sss->dev_name), SUNXI_SS_DEV_NAME); platform_set_drvdata(pdev, sss); ret = sunxi_ss_res_request(pdev); if (ret != 0) goto err0; sss->pdev = pdev; ret = sunxi_ss_hw_init(sss); if (ret != 0) { SS_ERR("SS hw init failed!\n"); goto err1; } ss_dev = sss; ret = sunxi_ss_alg_register(); if (ret != 0) { SS_ERR("sunxi_ss_alg_register() failed! return %d\n", ret); goto err2; } sunxi_ss_sysfs_create(pdev); SS_DBG("SS is inited, base 0x%p, irq %d!\n", sss->base_addr, sss->irq); return 0; err2: sunxi_ss_hw_exit(sss); err1: sunxi_ss_res_release(sss); err0: platform_set_drvdata(pdev, NULL); return ret; } static int sunxi_ss_remove(struct platform_device *pdev) { sunxi_ss_t *sss = platform_get_drvdata(pdev); ss_wait_idle(); sunxi_ss_sysfs_remove(pdev); sunxi_ss_alg_unregister(); sunxi_ss_hw_exit(sss); sunxi_ss_res_release(sss); platform_set_drvdata(pdev, NULL); ss_dev = NULL; return 0; } #ifdef CONFIG_PM static int sunxi_ss_suspend(struct device *dev) { #ifdef CONFIG_EVB_PLATFORM struct platform_device *pdev = to_platform_device(dev); sunxi_ss_t *sss = platform_get_drvdata(pdev); unsigned long flags = 0; SS_ENTER(); /* Wait for the completion of SS operation. */ ss_dev_lock(); spin_lock_irqsave(&ss_dev->lock, flags); sss->suspend = 1; spin_unlock_irqrestore(&sss->lock, flags); sunxi_ss_hw_exit(sss); ss_dev_unlock(); #endif return 0; } static int sunxi_ss_resume(struct device *dev) { int ret = 0; #ifdef CONFIG_EVB_PLATFORM struct platform_device *pdev = to_platform_device(dev); sunxi_ss_t *sss = platform_get_drvdata(pdev); unsigned long flags = 0; SS_ENTER(); ret = sunxi_ss_hw_init(sss); spin_lock_irqsave(&ss_dev->lock, flags); sss->suspend = 0; spin_unlock_irqrestore(&sss->lock, flags); #endif return ret; } static const struct dev_pm_ops sunxi_ss_dev_pm_ops = { .suspend = sunxi_ss_suspend, .resume = sunxi_ss_resume, }; #define SUNXI_SS_DEV_PM_OPS (&sunxi_ss_dev_pm_ops) #else #define SUNXI_SS_DEV_PM_OPS NULL #endif /* CONFIG_PM */ static struct platform_driver sunxi_ss_driver = { .probe = sunxi_ss_probe, .remove = sunxi_ss_remove, .driver = { .name = SUNXI_SS_DEV_NAME, .owner = THIS_MODULE, .pm = SUNXI_SS_DEV_PM_OPS, .of_match_table = sunxi_ss_of_match, }, }; static int __init sunxi_ss_init(void) { int ret = 0; ret = platform_driver_register(&sunxi_ss_driver); if (ret < 0) { SS_ERR("platform_driver_register() failed, return %d\n", ret); return ret; } return ret; } static void __exit sunxi_ss_exit(void) { platform_driver_unregister(&sunxi_ss_driver); } module_init(sunxi_ss_init); module_exit(sunxi_ss_exit); MODULE_AUTHOR("mintow"); MODULE_DESCRIPTION("SUNXI SS Controller Driver"); MODULE_ALIAS("platform:"SUNXI_SS_DEV_NAME); MODULE_LICENSE("GPL");