| /* |
| * Copyright (C) 2016 Samsung Electronics Co., Ltd. |
| * |
| * 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. |
| */ |
| |
| #ifndef _EXYNOS_FMP_H_ |
| #define _EXYNOS_FMP_H_ |
| |
| #include <linux/platform_device.h> |
| #include <linux/miscdevice.h> |
| #include <linux/bio.h> |
| |
| #define FMP_DRV_VERSION "3.0.1" |
| |
| #define FMP_KEY_SIZE_16 16 |
| #define FMP_KEY_SIZE_32 32 |
| #define FMP_IV_SIZE_16 16 |
| |
| #define FMP_CBC_MAX_KEY_SIZE FMP_KEY_SIZE_16 |
| #define FMP_XTS_MAX_KEY_SIZE ((FMP_KEY_SIZE_32) * (2)) |
| #define FMP_MAX_KEY_SIZE FMP_XTS_MAX_KEY_SIZE |
| |
| #define FMP_HOST_TYPE_NAME_LEN 8 |
| #define FMP_BLOCK_TYPE_NAME_LEN 8 |
| |
| #define FMP_SECTOR_SIZE 0x1000 |
| #define FMP_MIN_SECTOR_SIZE 0x200 |
| #define NUM_SECTOR_UNIT ((FMP_SECTOR_SIZE)/(FMP_MIN_SECTOR_SIZE)) |
| |
| #define MAX_AES_XTS_TRANSFER_SIZE 0x1000 |
| |
| #ifndef TRUE |
| #define TRUE 1 |
| #endif |
| |
| #ifndef FALSE |
| #define FALSE 0 |
| #endif |
| |
| enum fmp_crypto_algo_mode { |
| EXYNOS_FMP_BYPASS_MODE = 0, |
| EXYNOS_FMP_ALGO_MODE_AES_CBC = 1, |
| EXYNOS_FMP_ALGO_MODE_AES_XTS = 2, |
| }; |
| |
| enum fmp_crypto_key_size { |
| EXYNOS_FMP_KEY_SIZE_16 = 16, |
| EXYNOS_FMP_KEY_SIZE_32 = 32, |
| }; |
| |
| enum fmp_crypto_enc_mode { |
| EXYNOS_FMP_FILE_ENC = 0, |
| EXYNOS_FMP_DISK_ENC = 1, /* use persistent key */ |
| EXYNOS_FMP_ENC_MAX |
| }; |
| |
| enum fmp_disk_key_status { |
| KEY_STORED = 0, |
| KEY_SET = 1, |
| KEY_CLEAR = 2, |
| KEY_ERROR = -1, |
| }; |
| |
| struct fmp_crypto_info { |
| /* This field's stongly aligned 'crypto_diskcipher->algo' */ |
| u32 use_diskc; |
| u8 key[FMP_MAX_KEY_SIZE]; |
| u32 key_size; |
| enum fmp_crypto_key_size fmp_key_size; |
| enum fmp_crypto_enc_mode enc_mode; |
| enum fmp_crypto_algo_mode algo_mode; |
| void *ctx; |
| }; |
| |
| #if defined(CONFIG_MMC_DW_EXYNOS_FMP) && defined(CONFIG_SCSI_UFS_EXYNOS_FMP) |
| #error "FMP doesn't support muti-host" |
| #elif defined(CONFIG_MMC_DW_EXYNOS_FMP) |
| struct fmp_table_setting { |
| __le32 des0; /* des0 */ |
| #define GET_CMDQ_LENGTH(d) \ |
| (((d)->des0 & 0xffff0000) >> 16) |
| __le32 des1; /* des1 */ |
| __le32 des2; /* des2 */ |
| #define FKL BIT(26) |
| #define DKL BIT(27) |
| #define SET_KEYLEN(d, v) ((d)->des2 |= (uint32_t)v) |
| #define SET_FAS(d, v) \ |
| ((d)->des2 = ((d)->des2 & 0xcfffffff) | v << 28) |
| #define SET_DAS(d, v) \ |
| ((d)->des2 = ((d)->des2 & 0x3fffffff) | v << 30) |
| #define GET_FAS(d) ((d)->des2 & 0x30000000) |
| #define GET_DAS(d) ((d)->des2 & 0xc0000000) |
| #define GET_LENGTH(d) \ |
| ((d)->des2 & 0x3ffffff) |
| __le32 des3; /* des3 */ |
| /* CMDQ Operation */ |
| #define FKL_CMDQ BIT(0) |
| #define DKL_CMDQ BIT(1) |
| #define SET_CMDQ_KEYLEN(d, v) ((d)->des3 |= (uint32_t)v) |
| #define SET_CMDQ_FAS(d, v) \ |
| ((d)->des3 = ((d)->des3 & 0xfffffff3) | v << 2) |
| #define SET_CMDQ_DAS(d, v) \ |
| ((d)->des3 = ((d)->des3 & 0xffffffcf) | v << 4) |
| #define GET_CMDQ_FAS(d) ((d)->des3 & 0x0000000c) |
| #define GET_CMDQ_DAS(d) ((d)->des3 & 0x00000030) |
| __le32 reserved0; /* des4 */ |
| __le32 reserved1; |
| __le32 reserved2; |
| __le32 reserved3; |
| __le32 file_iv0; /* des8 */ |
| __le32 file_iv1; |
| __le32 file_iv2; |
| __le32 file_iv3; |
| __le32 file_enckey0; /* des12 */ |
| __le32 file_enckey1; |
| __le32 file_enckey2; |
| __le32 file_enckey3; |
| __le32 file_enckey4; |
| __le32 file_enckey5; |
| __le32 file_enckey6; |
| __le32 file_enckey7; |
| __le32 file_twkey0; /* des20 */ |
| __le32 file_twkey1; |
| __le32 file_twkey2; |
| __le32 file_twkey3; |
| __le32 file_twkey4; |
| __le32 file_twkey5; |
| __le32 file_twkey6; |
| __le32 file_twkey7; |
| __le32 disk_iv0; /* des28 */ |
| __le32 disk_iv1; |
| __le32 disk_iv2; |
| __le32 disk_iv3; |
| }; |
| #elif defined(CONFIG_SCSI_UFS_EXYNOS_FMP) |
| struct fmp_table_setting { |
| __le32 des0; /* des0 */ |
| #define GET_CMDQ_LENGTH(d) \ |
| (((d)->des0 & 0xffff0000) >> 16) |
| __le32 des1; /* des1 */ |
| __le32 des2; /* des2 */ |
| __le32 des3; /* des3 */ |
| /* 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) |
| /* CMDQ Operation */ |
| #define FKL_CMDQ BIT(0) |
| #define DKL_CMDQ BIT(1) |
| #define SET_CMDQ_KEYLEN(d, v) ((d)->des3 |= (uint32_t)v) |
| #define SET_CMDQ_FAS(d, v) \ |
| ((d)->des3 = ((d)->des3 & 0xfffffff3) | v << 2) |
| #define SET_CMDQ_DAS(d, v) \ |
| ((d)->des3 = ((d)->des3 & 0xffffffcf) | v << 4) |
| #define GET_CMDQ_FAS(d) ((d)->des3 & 0x0000000c) |
| #define GET_CMDQ_DAS(d) ((d)->des3 & 0x00000030) |
| __le32 file_iv0; /* des4 */ |
| __le32 file_iv1; /* des5 */ |
| __le32 file_iv2; /* des6 */ |
| __le32 file_iv3; /* des7 */ |
| __le32 file_enckey0; /* des8 */ |
| __le32 file_enckey1; /* des9 */ |
| __le32 file_enckey2; /* des10 */ |
| __le32 file_enckey3; /* des11 */ |
| __le32 file_enckey4; /* des12 */ |
| __le32 file_enckey5; /* des13 */ |
| __le32 file_enckey6; /* des14 */ |
| __le32 file_enckey7; /* des15 */ |
| __le32 file_twkey0; /* des16 */ |
| __le32 file_twkey1; /* des17 */ |
| __le32 file_twkey2; /* des18 */ |
| __le32 file_twkey3; /* des19 */ |
| __le32 file_twkey4; /* des20 */ |
| __le32 file_twkey5; /* des21 */ |
| __le32 file_twkey6; /* des22 */ |
| __le32 file_twkey7; /* des23 */ |
| __le32 disk_iv0; /* des24 */ |
| __le32 disk_iv1; /* des25 */ |
| __le32 disk_iv2; /* des26 */ |
| __le32 disk_iv3; /* des27 */ |
| __le32 reserved0; /* des28 */ |
| __le32 reserved1; /* des29 */ |
| __le32 reserved2; /* des30 */ |
| __le32 reserved3; /* des31 */ |
| }; |
| #endif |
| |
| struct fmp_data_setting { |
| struct fmp_crypto_info crypt[EXYNOS_FMP_ENC_MAX]; |
| struct fmp_table_setting *table; |
| bool cmdq_enabled; |
| }; |
| |
| #ifdef CONFIG_EXYNOS_FMP_FIPS |
| struct fips_result { |
| bool overall; |
| bool aes_xts; |
| bool aes_cbc; |
| bool sha256; |
| bool hmac; |
| bool integrity; |
| }; |
| #endif |
| |
| #define EXYNOS_FMP_ALGO_MODE_MASK (0x3) |
| #define EXYNOS_FMP_ALGO_MODE_TEST_OFFSET (0xf) |
| #define EXYNOS_FMP_ALGO_MODE_TEST (1 << EXYNOS_FMP_ALGO_MODE_TEST_OFFSET) |
| |
| struct fmp_test_data { |
| char block_type[FMP_BLOCK_TYPE_NAME_LEN]; |
| struct block_device *bdev; |
| sector_t sector; |
| dev_t devt; |
| uint32_t test_block_offset; |
| /* iv to submitted */ |
| u8 iv[FMP_IV_SIZE_16]; |
| /* diskcipher for test */ |
| struct fmp_crypto_info ci; |
| }; |
| |
| struct exynos_fmp { |
| struct device *dev; |
| enum fmp_disk_key_status status_disk_key; |
| struct fmp_test_data *test_data; |
| #ifdef CONFIG_EXYNOS_FMP_FIPS |
| atomic_t fips_start; |
| struct fips_result result; |
| struct miscdevice miscdev; |
| void *test_vops; |
| int fips_run; |
| int fips_fin; |
| struct buffer_head *bh; |
| #endif |
| }; |
| |
| struct fmp_request { |
| void *table; |
| void *iv; |
| u32 ivsize; |
| u32 prdt_cnt; |
| unsigned long prdt_off; |
| bool cmdq_enabled; |
| bool fips; |
| }; |
| |
| #define ACCESS_CONTROL_ABORT 0x14 |
| |
| #ifndef SMC_CMD_FMP_SECURITY |
| /* For FMP/SMU Ctrl */ |
| #define SMC_CMD_FMP_SECURITY (0xC2001810) |
| #define SMC_CMD_FMP_DISK_KEY_STORED (0xC2001820) |
| #define SMC_CMD_FMP_DISK_KEY_SET (0xC2001830) |
| #define SMC_CMD_FMP_DISK_KEY_CLEAR (0xC2001840) |
| #define SMC_CMD_SMU (0xC2001850) |
| #define SMC_CMD_FMP_SMU_RESUME (0xC2001860) |
| #define SMC_CMD_FMP_SMU_DUMP (0xC2001870) |
| #define SMC_CMD_UFS_LOG (0xC2001880) |
| #endif |
| |
| enum smu_id { |
| SMU_EMBEDDED = 0, |
| SMU_UFSCARD = 1, |
| SMU_SDCARD = 2, |
| SMU_ID_MAX, |
| }; |
| |
| enum smu_command { |
| SMU_INIT = 0, |
| SMU_SET = 1, |
| SMU_ABORT = 2, |
| }; |
| |
| /* fmp functions */ |
| #if defined(CONFIG_MMC_DW_EXYNOS_FMP) || defined(CONFIG_SCSI_UFS_EXYNOS_FMP) |
| int exynos_fmp_fips(struct bio *bio); |
| int exynos_fmp_bypass(struct fmp_request *req, struct bio *bio); |
| int exynos_fmp_sec_cfg(int fmp_id, int smu_id, bool init); |
| int exynos_fmp_smu_abort(int id); |
| int exynos_fmp_crypt(struct fmp_crypto_info *ci, void *priv); |
| int exynos_fmp_clear(struct fmp_crypto_info *ci, void *priv); |
| int exynos_fmp_setkey(struct fmp_crypto_info *ci, |
| u8 *in_key, u32 keylen, bool persistent); |
| int exynos_fmp_clearkey(struct fmp_crypto_info *ci); |
| bool exynos_fmp_check_test(struct bio *bio, struct fmp_crypto_info *fmp_info); |
| #else |
| #define exynos_fmp_fips(a) ((void *)0) |
| #define exynos_fmp_bypass(a, b) ((void *)0) |
| #define exynos_fmp_sec_cfg(a, b, c) (0) |
| #define exynos_fmp_smu_abort(a) (0) |
| #define exynos_fmp_crypt(a, b) (0) |
| #define exynos_fmp_clear(a, b) (0) |
| #define exynos_fmp_setkey(a, b, c, d) (0) |
| #define exynos_fmp_clearkey(a) (0) |
| #endif |
| void *exynos_fmp_init(struct platform_device *pdev); |
| void exynos_fmp_exit(struct platform_device *pdev); |
| #endif /* _EXYNOS_FMP_H_ */ |