/* * Exynos FMP driver * * Copyright (C) 2015 Samsung Electronics Co., Ltd. * Authors: Boojin Kim * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "fmp_fips_main.h" #include "fmp_test.h" #include "fmp_fips_info.h" #ifndef CONFIG_KEYS_IN_PRDT #include "ufs-vs-mmio.h" #endif #include #include #include #include #define WORD_SIZE 4 #define FMP_IV_MAX_IDX (FMP_IV_SIZE_16 / WORD_SIZE) #define byte2word(b0, b1, b2, b3) \ (((unsigned int)(b0) << 24) | \ ((unsigned int)(b1) << 16) | \ ((unsigned int)(b2) << 8) | (b3)) #define get_word(x, c) byte2word(((unsigned char *)(x) + 4 * (c))[0], \ ((unsigned char *)(x) + 4 * (c))[1], \ ((unsigned char *)(x) + 4 * (c))[2], \ ((unsigned char *)(x) + 4 * (c))[3]) /* Legacy Operation */ #define FKL BIT(26) #define DKL BIT(27) #define SET_KEYLEN(d, v) ((d)->des3 |= (uint32_t)v) #define SET_FAS(d, v) \ ((d)->des3 = ((d)->des3 & 0xcfffffff) | v << 28) #define SET_DAS(d, v) \ ((d)->des3 = ((d)->des3 & 0x3fffffff) | v << 30) #define GET_FAS(d) ((d)->des3 & 0x30000000) #define GET_DAS(d) ((d)->des3 & 0xc0000000) #define GET_LENGTH(d) \ ((d)->des3 & 0x3ffffff) static struct device *fmp_dev; struct exynos_fmp *get_fmp(void) { return dev_get_drvdata(fmp_dev); } EXPORT_SYMBOL(get_fmp); static inline void dump_ci(struct fmp_crypto_info *c) { if (c) { pr_info ("%s: algo:%d enc:%d key_size:%d\n", __func__, c->algo_mode, c->enc_mode,c->key_size); if (c->enc_mode == EXYNOS_FMP_FILE_ENC) print_hex_dump(KERN_CONT, "key:", DUMP_PREFIX_OFFSET, 16, 1, c->key, sizeof(c->key), false); } } static inline void dump_table(struct fmp_table_setting *table) { print_hex_dump(KERN_CONT, "dump:", DUMP_PREFIX_OFFSET, 16, 1, table, sizeof(struct fmp_table_setting), false); } static inline int is_supported_ivsize(u32 ivlen) { if (ivlen && (ivlen <= FMP_IV_SIZE_16)) return TRUE; else return FALSE; } static inline int check_aes_xts_size(struct fmp_table_setting *table, bool cmdq_enabled) { int size; if (cmdq_enabled) size = GET_CMDQ_LENGTH(table); else size = GET_LENGTH(table); return (size > MAX_AES_XTS_TRANSFER_SIZE) ? size : 0; } /* check for fips that no allow same keys */ static inline int check_aes_xts_key(char *key, enum fmp_crypto_key_size key_size) { char *enckey, *twkey; enckey = key; twkey = key + key_size; return (memcmp(enckey, twkey, key_size)) ? 0 : -1; } int fmplib_set_algo_mode(struct fmp_table_setting *table, struct fmp_crypto_info *crypto, bool cmdq_enabled) { int ret; enum fmp_crypto_fips_algo_mode algo_mode = crypto->algo_mode & EXYNOS_FMP_ALGO_MODE_MASK; if (algo_mode == EXYNOS_FMP_ALGO_MODE_AES_XTS) { ret = check_aes_xts_size(table, cmdq_enabled); if (ret) { pr_err("%s: Fail FMP XTS due to invalid size(%d), cmdq:%d\n", __func__, ret, cmdq_enabled); return -EINVAL; } } switch (crypto->enc_mode) { case EXYNOS_FMP_FILE_ENC: if (cmdq_enabled) SET_CMDQ_FAS(table, algo_mode); else SET_FAS(table, algo_mode); break; default: pr_err("%s: Invalid fmp enc mode %d\n", __func__, crypto->enc_mode); return -EINVAL; } return 0; } #ifdef CONFIG_KEYS_IN_PRDT static inline __be32 fmp_key_word(const u8 *key, int j) { return cpu_to_be32(get_unaligned_le32( key + AES_KEYSIZE_256 - (j + 1) * sizeof(__le32))); } static int fmplib_set_file_key(struct fmp_table_setting *table, struct fmp_crypto_info *crypto) { enum fmp_crypto_fips_algo_mode algo_mode = crypto->algo_mode & EXYNOS_FMP_ALGO_MODE_MASK; enum fmp_crypto_key_size key_size = crypto->fmp_key_size; char *key = crypto->key; int idx, max; if (!key || (crypto->enc_mode != EXYNOS_FMP_FILE_ENC) || ((key_size != EXYNOS_FMP_KEY_SIZE_16) && (key_size != EXYNOS_FMP_KEY_SIZE_32))) { pr_err("%s: Invalid key_size:%d enc_mode:%d\n", __func__, key_size, crypto->enc_mode); return -EINVAL; } if ((algo_mode == EXYNOS_FMP_ALGO_MODE_AES_XTS) && check_aes_xts_key(key, key_size)) { pr_err("%s: Fail FMP XTS due to the same enc and twkey\n", __func__); return -EINVAL; } if (algo_mode == EXYNOS_FMP_ALGO_MODE_AES_CBC) { max = key_size / WORD_SIZE; for (idx = 0; idx < max; idx++) *(&table->file_enckey0 + idx) = get_word(key, max - (idx + 1)); } else if (algo_mode == EXYNOS_FMP_ALGO_MODE_AES_XTS) { key_size *= 2; max = key_size / WORD_SIZE; for (idx = 0; idx < (max / 2); idx++) *(&table->file_enckey0 + idx) = get_word(key, (max / 2) - (idx + 1)); for (idx = 0; idx < (max / 2); idx++) *(&table->file_twkey0 + idx) = get_word(key, max - (idx + 1)); } return 0; } #else static int fmplib_set_file_key(struct fmp_handle *handle, struct fmp_crypto_info *ci) { int ret = 0; enum fmp_crypto_key_size key_size = ci->fmp_key_size; enum fmp_crypto_fips_algo_mode algo_mode = ci->algo_mode & EXYNOS_FMP_ALGO_MODE_MASK; struct exynos_fmp_key_info fmp_key_info; if (IS_ERR_OR_NULL(ci->key) || (ci->enc_mode != EXYNOS_FMP_FILE_ENC) || ((key_size != EXYNOS_FMP_KEY_SIZE_32))) { pr_err("%s: Invalid key_size:%d enc_mode:%d\n", __func__, key_size, ci->enc_mode); return -EINVAL; } if ((algo_mode == EXYNOS_FMP_ALGO_MODE_AES_XTS) && check_aes_xts_key(ci->key, key_size)) { pr_err("%s: Fail FMP XTS due to the same enc and twkey\n", __func__); return -EINVAL; } fmp_key_info.raw = ci->key; fmp_key_info.size = ci->fmp_key_size * 2; fmp_key_info.slot = EXYNOS_FMP_FIPS_KEYSLOT; ret = exynos_fmp_setkey(&fmp_key_info, handle); if (ret) { pr_err("%s: Fail to set FMP key in keyslot (ret: %d)\n", __func__, ret); return ret; } return ret; } unsigned long exynos_fmp_set_kw_mode(uint64_t kw_mode) { unsigned long ret; ret = exynos_smc(SMC_CMD_FMP_KW_MODE, 0, FMP_EMBEDDED, kw_mode); if (ret) { pr_err("%s: SMC_CMD_FMP_KW_MODE failed to set kw_mode: %ld\n", __func__, ret); return ret; } return 0; } unsigned long exynos_fmp_get_kw_mode(dma_addr_t kw_mode_addr) { unsigned long ret; ret = exynos_smc(SMC_CMD_FMP_GET_KW_MODE, 0, FMP_EMBEDDED, kw_mode_addr); if (ret) { pr_err("%s: SMC_CMD_FMP_GET_KW_MODE failed to set kw_mode: %ld\n", __func__, ret); return ret; } return 0; } #endif /* CONFIG_KEYS_IN_PRDT */ #ifdef CONFIG_KEYS_IN_PRDT static int fmplib_set_key_size(struct fmp_table_setting *table, struct fmp_crypto_info *crypto, bool cmdq_enabled) { enum fmp_crypto_key_size key_size; key_size = crypto->fmp_key_size; if ((key_size != EXYNOS_FMP_KEY_SIZE_16) && (key_size != EXYNOS_FMP_KEY_SIZE_32)) { pr_err("%s: Invalid keysize %d\n", __func__, key_size); return -EINVAL; } switch (crypto->enc_mode) { case EXYNOS_FMP_FILE_ENC: if (cmdq_enabled) SET_CMDQ_KEYLEN(table, (key_size == FMP_KEY_SIZE_32) ? FKL_CMDQ : 0); else SET_KEYLEN(table, (key_size == FMP_KEY_SIZE_32) ? FKL : 0); break; default: pr_err("%s: Invalid fmp enc mode %d\n", __func__, crypto->enc_mode); return -EINVAL; } return 0; } static int fmplib_set_iv(struct fmp_table_setting *table, struct fmp_crypto_info *crypto, u8 *iv) { int idx; switch (crypto->enc_mode) { case EXYNOS_FMP_FILE_ENC: for (idx = 0; idx < FMP_IV_MAX_IDX; idx++) *(&table->file_iv0 + idx) = get_word(iv, FMP_IV_MAX_IDX - (idx + 1)); break; default: pr_err("%s: Invalid fmp enc mode %d\n", __func__, crypto->enc_mode); return -EINVAL; } return 0; } int exynos_fmp_crypt(struct exynos_fmp_crypt_info *fmp_ci, struct fmp_table_setting *table) { struct exynos_fmp *fmp = get_fmp(); struct fmp_crypto_info *ci; struct fmp_sg_entry *ent = (struct fmp_sg_entry *)table; u8 iv[FMP_IV_SIZE_16]; u64 ret = 0; size_t j, limit; if (!fmp) { pr_err("%s: invalid fmp\n", __func__); return -EINVAL; } if (!table) { pr_err("%s: invalid table setting\n", __func__); return -EINVAL; } if (get_fmp_fips_state()) return -EINVAL; if (fmp_ci->fips) { /* check fips test data */ if (!fmp->test_data) { dev_err(fmp->dev, "%s: no test_data for test mode\n", __func__); return -EINVAL; } ci = &fmp->test_data->ci; if (!ci || !(ci->algo_mode & EXYNOS_FMP_ALGO_MODE_TEST)) { dev_err(fmp->dev, "%s: Invalid fmp crypto_info for test mode\n", __func__); return -EINVAL; } if (!(ci->algo_mode & EXYNOS_FMP_ALGO_MODE_MASK)) { dev_err(fmp->dev, "%s: no test_data for algo mode\n", __func__); return -EINVAL; } ret = fmplib_set_algo_mode(table, ci, CMDQ_DISABLED); if (ret) { dev_err(fmp->dev, "%s: Fail to set FMP encryption mode\n", __func__); ret = -EINVAL; goto out; } /* set key into table */ switch (ci->enc_mode) { case EXYNOS_FMP_FILE_ENC: ret = fmplib_set_file_key(table, ci); if (ret) { dev_err(fmp->dev, "%s: Fail to set FMP key\n", __func__); ret = -EINVAL; goto out; } break; default: dev_err(fmp->dev, "%s: Invalid fmp enc mode %d\n", __func__, ci->enc_mode); ret = -EINVAL; goto out; } /* set key size into table */ ret = fmplib_set_key_size(table, ci, CMDQ_DISABLED); if (ret) { dev_err(fmp->dev, "%s: Fail to set FMP key size\n", __func__); goto out; } /* use test manager's iv instead of host driver's iv */ dev_dbg(fmp->dev, "%s: fips crypt: ivsize:%d, key_size: %d, %d\n", __func__, sizeof(fmp->test_data->iv), ci->key_size, ci->fmp_key_size); if (!is_supported_ivsize(sizeof(fmp->test_data->iv))) { dev_err(fmp->dev, "%s: invalid (mode:%d ivsize:%d)\n", __func__, ci->algo_mode & EXYNOS_FMP_ALGO_MODE_MASK, sizeof(fmp->test_data->iv)); ret = -EINVAL; goto out; } /* set iv as intended size */ memset(iv, 0, FMP_IV_SIZE_16); memcpy(iv, fmp->test_data->iv, sizeof(fmp->test_data->iv)); ret = fmplib_set_iv(table, ci, iv); if (ret) { dev_err(fmp->dev, "%s: Fail to set FMP IV\n", __func__); ret = -EINVAL; goto out; } } else { if (!crypto_memneq(fmp_ci->enckey, fmp_ci->twkey, AES_KEYSIZE_256)) { dev_err(fmp->dev, "Can't use weak AES-XTS key\n"); ret = -EKEYREJECTED; goto out; } SET_FAS(ent, EXYNOS_FMP_ALGO_MODE_AES_XTS); SET_KEYLEN(ent, FKL); /* set the file/tweak key in table */ for (j = 0, limit = AES_KEYSIZE_256 / sizeof(u32); j < limit; j++) { ent->file_enckey[j] = fmp_key_word(fmp_ci->enckey, j); ent->file_twkey[j] = fmp_key_word(fmp_ci->twkey, j); } /* Set the IV. */ ent->file_iv[0] = cpu_to_be32(upper_32_bits(fmp_ci->dun_hi)); ent->file_iv[1] = cpu_to_be32(lower_32_bits(fmp_ci->dun_hi)); ent->file_iv[2] = cpu_to_be32(upper_32_bits(fmp_ci->dun_lo)); ent->file_iv[3] = cpu_to_be32(lower_32_bits(fmp_ci->dun_lo)); } out: if (ret) { dump_ci(ci); dump_table(table); } return ret; } EXPORT_SYMBOL(exynos_fmp_crypt); #else int exynos_fmp_setkey(struct exynos_fmp_key_info *fmp_ki, struct fmp_handle *handle) { int ret = 0; size_t i, limit; u32 count = 0; u32 kw_keyvalid; u32 slot_offset; const u8 *enckey, *twkey; union { u8 bytes[AES_256_XTS_KEY_SIZE]; u32 words[AES_256_XTS_KEY_SIZE / sizeof(u32)]; } fmp_key; /* Key length check. FMP only support 256b Key for AES-XTS */ if (fmp_ki->size != AES_256_XTS_KEY_SIZE) { pr_err("%s: Does not support %d length of AES-XTS key\n", __func__, fmp_ki->size); return -EINVAL; } if (get_fmp_fips_state()) return -EINVAL; /* In XTS mode, the blk_crypto_key's size is already doubled */ memcpy(fmp_key.bytes, fmp_ki->raw, fmp_ki->size); enckey = fmp_ki->raw; twkey = enckey + AES_KEYSIZE_256; slot_offset = FMP_KW_SECUREKEY + (fmp_ki->slot * FMP_KW_SECUREKEY_OFFSET); /* Reject weak AES-XTS keys */ if (!crypto_memneq(enckey, twkey, AES_KEYSIZE_256)) { pr_err("%s: Can't use weak AES-XTS key\n", __func__); return -EKEYREJECTED; } /* Key program in keyslot */ /* Swap File key and Tweak key */ for (i = 0, limit = fmp_ki->size / (sizeof(u32) * 2); i < limit; i++) { ufsp_writel((struct ufs_vs_handle *)handle, le32_to_cpu(fmp_key.words[i]), slot_offset + ((i + AES_256_XTS_TWK_OFFSET) * sizeof(u32))); ufsp_writel((struct ufs_vs_handle *)handle, le32_to_cpu(fmp_key.words[i + AES_256_XTS_TWK_OFFSET]), slot_offset + (i * sizeof(u32))); } /* Zeroise the key */ memzero_explicit(&fmp_key, fmp_ki->size); /* Keyslot should be valid for crypto IO */ do { kw_keyvalid = ufsp_readl((struct ufs_vs_handle *)handle, FMP_KW_KEYVALID); if (!(kw_keyvalid & (0x1 << (fmp_ki->slot)))) { pr_warn("%s: Key slot #%d is not valid yet\n", __func__, fmp_ki->slot); udelay(2); count++; continue; } else { break; } } while (count < MAX_RETRY_COUNT); if (!(kw_keyvalid & (0x1 << fmp_ki->slot))) { pr_err("%s: Key slot #%d is not valid\n", __func__, fmp_ki->slot); return -EINVAL; } pr_debug("%s: Key valid = 0x%x\n", __func__, kw_keyvalid); return ret; } EXPORT_SYMBOL(exynos_fmp_setkey); int exynos_fmp_crypt(struct exynos_fmp_crypt_info *fmp_ci, struct fmp_handle *handle) { struct exynos_fmp *fmp = get_fmp(); struct fmp_crypto_info *ci; int ret = 0; if (!fmp) { pr_err("%s: invalid fmp\n", __func__); return -EINVAL; } if (!fmp_ci || !handle) { dev_err(fmp->dev, "%s: Invalid fmp_crypt_info, fmp_handle parameter\n", __func__); return -EINVAL; } if (get_fmp_fips_state()) return -EINVAL; if (fmp_ci->fips) { /* check fips test data */ if (!fmp->test_data) { dev_err(fmp->dev, "%s: no test_data for test mode\n", __func__); return -EINVAL; } ci = &fmp->test_data->ci; if (!ci || !(ci->algo_mode & EXYNOS_FMP_ALGO_MODE_TEST)) { dev_err(fmp->dev, "%s: Invalid fmp crypto_info for test mode\n", __func__); return -EINVAL; } if (!(ci->algo_mode & EXYNOS_FMP_ALGO_MODE_MASK)) { dev_err(fmp->dev, "%s: no test_data for algo mode\n", __func__); return -EINVAL; } switch (ci->enc_mode) { case EXYNOS_FMP_FILE_ENC: ret = fmplib_set_file_key(handle, ci); if (ret) { dev_err(fmp->dev, "%s: Fail to set FMP key\n", __func__); ret = -EINVAL; goto out; } break; default: dev_err(fmp->dev, "%s: Invalid fmp enc mode %d\n", __func__, ci->enc_mode); ret = -EINVAL; goto out; } if (fmp->dun_swap == 1) { fmp_ci->data_unit_num = cpu_to_be64(fmp->test_data->DataUnitSeqNumber); fmp_ci->crypto_key_slot = EXYNOS_FMP_FIPS_KEYSLOT; } else { fmp_ci->data_unit_num = cpu_to_le64(fmp->test_data->DataUnitSeqNumber); fmp_ci->crypto_key_slot = EXYNOS_FMP_FIPS_KEYSLOT; } } out: return ret; } EXPORT_SYMBOL(exynos_fmp_crypt); #endif /* CONFIG_KEYS_IN_PRDT */ #ifdef CONFIG_KEYS_IN_PRDT static inline void fmplib_clear_file_key(struct fmp_table_setting *table) { memset(&table->file_iv0, 0, sizeof(__le32) * 24); } #else static void fmplib_clear_file_key(struct ufs_vs_handle *handle, int slot) { size_t i, limit; int count = 0; u32 kw_keyvalid; u32 slot_offset = FMP_KW_SECUREKEY + (slot * FMP_KW_SECUREKEY_OFFSET); union { u8 bytes[AES_256_XTS_KEY_SIZE]; u32 words[AES_256_XTS_KEY_SIZE / sizeof(u32)]; } fmp_key; if (slot > EXYNOS_FMP_FIPS_KEYSLOT) return; /* Make Zeroised key */ memzero_explicit(&fmp_key, AES_256_XTS_KEY_SIZE); for (i = 0, limit = AES_256_XTS_KEY_SIZE / (sizeof(u32) * 2); i < limit; i++) { ufsp_writel(handle, le32_to_cpu(fmp_key.words[i]), slot_offset + ((i + AES_256_XTS_TWK_OFFSET) * sizeof(u32))); ufsp_writel(handle, le32_to_cpu(fmp_key.words[i + AES_256_XTS_TWK_OFFSET]), slot_offset + (i * sizeof(u32))); } /* Keyslot should be valid for crypto IO */ do { kw_keyvalid = ufsp_readl(handle, FMP_KW_KEYVALID); if (!(kw_keyvalid & (0x1 << slot))) { pr_warn("%s: Key slot #%d is not zeroised yet - 0x%x\n", __func__, slot, kw_keyvalid); udelay(2); count++; continue; } else { break; } } while (count < MAX_RETRY_COUNT); if (!(kw_keyvalid & (0x1 << slot))) { pr_err("%s: Key slot #%d is not zeroised\n", __func__, slot); return; } pr_debug("%s: Key valid = 0x%x\n", __func__, kw_keyvalid); } #endif #ifndef CONFIG_KEYS_IN_PRDT int exynos_fmp_clear(struct fmp_handle *handle, int slot) { struct exynos_fmp *fmp = get_fmp(); if (slot > EXYNOS_FMP_FIPS_KEYSLOT) { dev_err(fmp->dev, "%s: unable to clear %d keyslot. Keyslot num exceeded\n", __func__, slot); return -EINVAL; } if (!handle) { dev_err(fmp->dev, "%s: vacant ufs handle\n", __func__); return -EINVAL; } fmplib_clear_file_key((struct ufs_vs_handle *)handle, slot); return 0; } EXPORT_SYMBOL(exynos_fmp_clear); #endif /* CONFIG_KEYS_IN_PRDT */ static void fmplib_bypass(void *desc, bool cmdq_enabled) { if (cmdq_enabled) { SET_CMDQ_FAS((struct fmp_table_setting *)desc, 0); SET_CMDQ_DAS((struct fmp_table_setting *)desc, 0); } else { SET_FAS((struct fmp_table_setting *)desc, 0); SET_DAS((struct fmp_table_setting *)desc, 0); } } static bool fmp_check_fips(struct bio *bio, struct exynos_fmp *fmp) { struct page *page; struct buffer_head *bh; bool find = false; struct fmp_crypto_info *ci; if (bio->bi_io_vec) { page = bio->bi_io_vec[0].bv_page; if (page && !PageAnon(page) && page_has_buffers(page)) { bh = page_buffers(page); if (bh != fmp->bh) { dev_err(fmp->dev, "%s: invalid fips bh\n", __func__); return false; } if (bh && ((void *)bh->b_private == (void *)fmp)) find = true; } } if (find) { fmp->fips_fin++; ci = &fmp->test_data->ci; dev_dbg(fmp->dev, "%s: find fips run(%d) with algo:%d, enc:%d, key_size:%d\n", __func__, fmp->fips_run, ci->algo_mode, ci->enc_mode, ci->key_size); if (ci->algo_mode == (EXYNOS_FMP_ALGO_MODE_TEST | EXYNOS_FMP_BYPASS_MODE)) { dev_dbg(fmp->dev, "%s: find fips for bypass mode\n", __func__); return 0; } } return find; } static bool fmp_check_fips_clean(struct bio *bio, struct exynos_fmp *fmp) { bool find = false; struct page *page; struct buffer_head *bh; struct fmp_crypto_info *ci; if (bio->bi_io_vec) { page = bio->bi_io_vec[0].bv_page; if (page && !PageAnon(page) && page_has_buffers(page)) { bh = page_buffers(page); if (bh != fmp->bh) { dev_err(fmp->dev, "%s: invalid fips bh\n", __func__); return false; } if (bh && ((void *)bh->b_private == (void *)fmp)) find = true; } } if (find) { fmp->fips_fin--; ci = &fmp->test_data->ci; dev_dbg(fmp->dev, "%s: find fips run(%d) fin(%d)with algo:%d, enc:%d, key_size:%d\n", __func__, fmp->fips_run, fmp->fips_fin, ci->algo_mode, ci->enc_mode, ci->key_size); if (ci->algo_mode == (EXYNOS_FMP_ALGO_MODE_TEST | EXYNOS_FMP_BYPASS_MODE)) { dev_dbg(fmp->dev, "%s: find fips for bypass mode\n", __func__); return false; } } return find; } bool is_fmp_fips_op(struct bio *bio) { struct exynos_fmp *fmp = get_fmp(); if (!fmp->fips_run) return false; if (!fmp->result.overall && fmp_check_fips(bio, fmp)) { dev_dbg(fmp->dev, "%s: find fips\n", __func__); return true; } return false; } EXPORT_SYMBOL(is_fmp_fips_op); bool is_fmp_fips_clean(struct bio *bio) { struct exynos_fmp *fmp = get_fmp(); if (fmp->fips_fin && fmp_check_fips_clean(bio, fmp)) { dev_dbg(fmp->dev, "%s: find fips\n", __func__); return true; } return false; } EXPORT_SYMBOL(is_fmp_fips_clean); int get_fmp_fips_state(void) { if (unlikely(in_fmp_fips_err())) { #if defined(CONFIG_NODE_FOR_SELFTEST_FAIL) pr_err("%s: Fail to work fmp config due to fips in error.\n", __func__); #else panic("%s: Fail to work fmp config due to fips in error\n", __func__); #endif return -EINVAL; } return 0; } int exynos_fmp_bypass(struct fmp_table_setting *table, u32 prdt_cnt, unsigned long prdt_off) { u32 i; void *prd = table; for (i = 0; i < prdt_cnt; i++) { fmplib_bypass(prd, 0); prd = (void *)prd + prdt_off; } return 0; } EXPORT_SYMBOL(exynos_fmp_bypass); #define CFG_DESCTYPE_3 0x3 static void *exynos_fmp_init(struct platform_device *pdev) { struct exynos_fmp *fmp; struct device_node *np; u8 dun_swap; int ret = 0; if (!pdev) { pr_err("%s: Invalid platform_device.\n", __func__); goto err; } fmp = devm_kzalloc(&pdev->dev, sizeof(struct exynos_fmp), GFP_KERNEL); if (!fmp) goto err; fmp->dev = &pdev->dev; if (!fmp->dev) { pr_err("%s: Invalid device.\n", __func__); goto err_dev; } atomic_set(&fmp->fips_start, 0); ret = exynos_fmp_fips_register(fmp); if (ret) { dev_err(fmp->dev, "%s: Fail to exynos_fmp_fips_register. ret(0x%x)", __func__, ret); goto err_dev; } /* Check fmp status for featuring */ np = fmp->dev->of_node; ret = of_property_read_u8(np, "dun-swap", &dun_swap); if (ret) { dev_info(fmp->dev, "read dun_swap failed = 0x%x, set to 0\n", __func__, ret); dun_swap = 0; } fmp->dun_swap = dun_swap; dev_info(fmp->dev, "Exynos FMP dun swap = %d\n", fmp->dun_swap); fmp->fips_run = 0; dev_info(fmp->dev, "Exynos FMP driver is initialized\n"); dev_info(fmp->dev, "Exynos FMP driver Version: %s\n", FMP_DRV_VERSION); return fmp; err_dev: devm_kfree(&pdev->dev, fmp); err: return NULL; } void exynos_fmp_exit(struct platform_device *pdev) { struct exynos_fmp *fmp = dev_get_drvdata(&pdev->dev); exynos_fmp_fips_deregister(fmp); devm_kfree(&pdev->dev, fmp); } static int exynos_fmp_probe(struct platform_device *pdev) { struct exynos_fmp *fmp_ctx = exynos_fmp_init(pdev); int ret; if (!fmp_ctx) { dev_err(&pdev->dev, "%s: Fail to get fmp_ctx\n", __func__); return -EINVAL; } fmp_dev = &pdev->dev; dev_set_drvdata(&pdev->dev, fmp_ctx); ret = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(36)); if (ret) { dev_warn(&pdev->dev, "%s: No suitable DMA available (%d)\n", __func__, ret); } return 0; } static int exynos_fmp_remove(struct platform_device *pdev) { exynos_fmp_exit(pdev); dev_info(&pdev->dev, "%s: complete remove fmp driver\n", __func__); return 0; } static const struct of_device_id exynos_fmp_match[] = { { .compatible = "samsung,exynos-fmp" }, {}, }; static struct platform_driver exynos_fmp_driver = { .driver = { .name = "exynos-fmp", .owner = THIS_MODULE, .pm = NULL, .of_match_table = exynos_fmp_match, }, .probe = exynos_fmp_probe, .remove = exynos_fmp_remove, }; module_platform_driver(exynos_fmp_driver); MODULE_DESCRIPTION("FMP driver"); MODULE_AUTHOR("Boojin Kim "); MODULE_LICENSE("GPL");