| // SPDX-License-Identifier: (GPL-2.0+ OR MIT) |
| /* |
| * Copyright (c) 2021 Amlogic, Inc. All rights reserved. |
| */ |
| |
| #include <linux/amlogic/aml_key.h> |
| #include <linux/amlogic/aml_kt.h> |
| |
| #include <linux/version.h> |
| #include <linux/kernel.h> |
| #include <linux/module.h> |
| #include <linux/debugfs.h> |
| #include <linux/mutex.h> |
| #include <linux/wait.h> |
| #include <linux/string.h> |
| #include <linux/fs.h> |
| #include <linux/cdev.h> |
| #include <linux/device.h> |
| #include <linux/fcntl.h> |
| #include <linux/uaccess.h> |
| #include <linux/delay.h> |
| #include <linux/platform_device.h> |
| #include <linux/of_platform.h> |
| #include <linux/of.h> |
| #include <linux/slab.h> |
| #include <linux/vmalloc.h> |
| #include <linux/amlogic/iomap.h> |
| #include "aml_seckey_log.h" |
| #include "aml_kt_dev.h" |
| |
| #define AML_KEY_DEVICE_NAME "key" |
| #define DEVICE_INSTANCES 1 |
| |
| #define KT_SUCCESS (0) |
| #define KT_ERROR (-1) |
| |
| struct aml_key_dev { |
| struct cdev cdev; |
| }; |
| |
| static struct aml_key_dev aml_key_dev; |
| static dev_t aml_key_devt; |
| static struct class *aml_key_class; |
| static struct dentry *aml_key_debug_dent; |
| u32 old_kt_log_level = 3; |
| |
| static int aml_key_init_dbgfs(void) |
| { |
| if (!aml_key_debug_dent) { |
| aml_key_debug_dent = debugfs_create_dir("aml_key", NULL); |
| if (!aml_key_debug_dent) { |
| LOGE("can not create debugfs directory\n"); |
| return -ENOMEM; |
| } |
| |
| debugfs_create_u32("log_level", 0644, aml_key_debug_dent, &old_kt_log_level); |
| } |
| return 0; |
| } |
| |
| static int aml_key_config(struct aml_kt_dev *dev, struct key_config key_cfg) |
| { |
| int ret = KT_SUCCESS; |
| struct amlkt_cfg_param kt_cfg; |
| |
| switch (key_cfg.key_userid) { |
| case DSC_LOC_DEC: |
| key_cfg.key_userid = AML_KT_USER_TSD; |
| break; |
| case DSC_NETWORK: |
| key_cfg.key_userid = AML_KT_USER_TSN; |
| break; |
| case DSC_LOC_ENC: |
| key_cfg.key_userid = AML_KT_USER_TSE; |
| break; |
| case CRYPTO_T0: |
| key_cfg.key_userid = AML_KT_USER_M2M_0; |
| break; |
| case CRYPTO_T1: |
| key_cfg.key_userid = AML_KT_USER_M2M_1; |
| break; |
| case CRYPTO_T2: |
| key_cfg.key_userid = AML_KT_USER_M2M_2; |
| break; |
| case CRYPTO_T3: |
| key_cfg.key_userid = AML_KT_USER_M2M_3; |
| break; |
| case CRYPTO_T4: |
| key_cfg.key_userid = AML_KT_USER_M2M_4; |
| break; |
| case CRYPTO_T5: |
| key_cfg.key_userid = AML_KT_USER_M2M_5; |
| break; |
| case CRYPTO_ANY: |
| key_cfg.key_userid = AML_KT_USER_M2M_ANY; |
| break; |
| default: |
| LOGE("%s, %d invalid user id\n", __func__, __LINE__); |
| ret = KT_ERROR; |
| return ret; |
| } |
| |
| switch (key_cfg.key_algo) { |
| case KEY_ALGO_AES: |
| key_cfg.key_algo = AML_KT_ALGO_AES; |
| break; |
| case KEY_ALGO_TDES: |
| key_cfg.key_algo = AML_KT_ALGO_TDES; |
| break; |
| case KEY_ALGO_DES: |
| key_cfg.key_algo = AML_KT_ALGO_DES; |
| break; |
| case KEY_ALGO_CSA2: |
| key_cfg.key_algo = AML_KT_ALGO_CSA2; |
| break; |
| case KEY_ALGO_CSA3: |
| key_cfg.key_algo = AML_KT_ALGO_CSA3; |
| break; |
| case KEY_ALGO_NDL: |
| key_cfg.key_algo = AML_KT_ALGO_NDL; |
| break; |
| case KEY_ALGO_ND: |
| key_cfg.key_algo = AML_KT_ALGO_ND; |
| break; |
| case KEY_ALGO_S17: |
| key_cfg.key_algo = AML_KT_ALGO_S17; |
| break; |
| case KEY_ALGO_SM4: |
| key_cfg.key_algo = AML_KT_ALGO_SM4; |
| break; |
| default: |
| LOGE("%s, %d invalid algo\n", __func__, __LINE__); |
| ret = KT_ERROR; |
| return ret; |
| } |
| |
| memset(&kt_cfg, 0, sizeof(kt_cfg)); |
| kt_cfg.handle = key_cfg.key_index; |
| kt_cfg.key_userid = key_cfg.key_userid; |
| kt_cfg.key_algo = key_cfg.key_algo; |
| kt_cfg.key_flag = AML_KT_FLAG_NONE; |
| kt_cfg.key_source = AML_KT_SRC_REE_HOST; //fixed |
| kt_cfg.ext_value = key_cfg.ext_value; |
| |
| ret = aml_kt_config(dev, kt_cfg); |
| |
| return ret; |
| } |
| |
| static int aml_key_set_host_key(struct aml_kt_dev *dev, struct key_descr *key_param) |
| { |
| int ret = KT_SUCCESS; |
| u32 index = 0; |
| u32 key_userid = 0; |
| u32 key_source = 0; |
| u32 key_flag = 0; |
| u8 is_iv = 0; |
| struct amlkt_set_key_param kt_param; |
| |
| ret = aml_kt_handle_to_kte(dev, key_param->key_index, &index); |
| if (ret != KT_SUCCESS) { |
| KT_LOGE("index get error\n"); |
| return ret; |
| } |
| |
| is_iv = key_param->key_index >> KT_IV_FLAG_OFFSET; |
| if (is_iv) { |
| if (!dev->kt_iv_slot[index]) |
| return KT_ERROR; |
| key_userid = dev->kt_iv_slot[index]->key_userid; |
| } else { |
| if (!dev->kt_slot[index]) |
| return KT_ERROR; |
| key_userid = dev->kt_slot[index]->key_userid; |
| } |
| |
| if (key_param->key_len == 1 || key_param->key_len == 2) { |
| /* key_len 1:CAS_A decrypt, 2:CAS_A encrypt */ |
| KT_LOGI("key_len=%d, mode=1 CAS_A\n", key_param->key_len); |
| key_source = AML_KT_SRC_REE_CERT; |
| } |
| |
| if (key_userid <= AML_KT_USER_M2M_ANY) { |
| if (key_param->key_len == 1) |
| key_flag = AML_KT_FLAG_ENC_ONLY; |
| else if (key_param->key_len == 2) |
| key_flag = AML_KT_FLAG_DEC_ONLY; |
| else |
| key_flag = AML_KT_FLAG_ENC_DEC; |
| } else if (key_userid == AML_KT_USER_TSD || |
| key_userid == AML_KT_USER_TSN) { |
| key_flag = AML_KT_FLAG_DEC_ONLY; |
| } else { |
| key_flag = AML_KT_FLAG_ENC_ONLY; |
| } |
| |
| if (is_iv) { |
| dev->kt_iv_slot[index]->key_flag = key_flag; |
| dev->kt_iv_slot[index]->key_source = key_source; |
| } else { |
| dev->kt_slot[index]->key_flag = key_flag; |
| dev->kt_slot[index]->key_source = key_source; |
| } |
| |
| if (key_source == AML_KT_SRC_REE_CERT) { |
| ret = aml_kt_set_hw_key(dev, key_param->key_index); |
| } else { |
| memset(&kt_param, 0, sizeof(kt_param)); |
| kt_param.handle = key_param->key_index; |
| kt_param.key_len = key_param->key_len; |
| memcpy(kt_param.key, key_param->key, key_param->key_len); |
| ret = aml_kt_set_host_key(dev, &kt_param); |
| } |
| |
| return ret; |
| } |
| |
| int aml_key_open(struct inode *inode, struct file *filp) |
| { |
| struct aml_key_dev *dev; |
| |
| dev = container_of(inode->i_cdev, struct aml_key_dev, cdev); |
| filp->private_data = dev; |
| |
| return 0; |
| } |
| |
| int aml_key_release(struct inode *inode, struct file *filp) |
| { |
| if (!filp->private_data) |
| return 0; |
| |
| filp->private_data = NULL; |
| |
| return 0; |
| } |
| |
| static long aml_key_ioctl(struct file *filp, unsigned int cmd, |
| unsigned long arg) |
| { |
| struct key_alloc alloc_param; |
| struct key_config cfg_param; |
| struct key_descr key_param; |
| u32 handle = 0; |
| u32 flag = 0; |
| u32 key_sts = 0; |
| int ret = 0; |
| |
| switch (cmd) { |
| case KEY_ALLOC: |
| memset(&alloc_param, 0, sizeof(alloc_param)); |
| if (copy_from_user(&alloc_param, (void __user *)arg, |
| sizeof(alloc_param))) { |
| LOGE("aml_key_alloc copy_from_user error\n"); |
| return -EFAULT; |
| } |
| |
| if (alloc_param.is_iv) |
| flag = AML_KT_ALLOC_FLAG_IV; |
| ret = aml_kt_alloc(&aml_kt_dev, flag, &alloc_param.key_index); |
| if (ret != 0) { |
| LOGE("aml_key_alloc failed retval=0x%08x\n", ret); |
| return -EFAULT; |
| } |
| |
| ret = copy_to_user((void __user *)arg, &alloc_param, |
| sizeof(alloc_param)); |
| if (unlikely(ret)) { |
| LOGE("aml_key_alloc copy_to_user error\n"); |
| return -EFAULT; |
| } |
| break; |
| case KEY_CONFIG: |
| memset(&cfg_param, 0, sizeof(cfg_param)); |
| if (copy_from_user(&cfg_param, (void __user *)arg, |
| sizeof(cfg_param))) { |
| LOGE("aml_key_config copy_from_user error\n"); |
| return -EFAULT; |
| } |
| |
| ret = aml_key_config(&aml_kt_dev, cfg_param); |
| if (ret != 0) { |
| LOGE("aml_key_config failed retval=0x%08x\n", ret); |
| return -EFAULT; |
| } |
| break; |
| case KEY_SET: |
| memset(&key_param, 0, sizeof(key_param)); |
| if (copy_from_user(&key_param, (void __user *)arg, |
| sizeof(key_param))) { |
| LOGE("aml_key_set_host_key copy_from_user error\n"); |
| return -EFAULT; |
| } |
| |
| if (key_param.key_len > 32) { |
| LOGE("key len[%d] error\n", key_param.key_len); |
| return -EFAULT; |
| } |
| |
| ret = aml_key_set_host_key(&aml_kt_dev, &key_param); |
| if (ret != 0) { |
| LOGE("aml_key_set_host_key failed retval=0x%08x\n", ret); |
| return -EFAULT; |
| } |
| break; |
| case KEY_FREE: |
| handle = (u32)arg; |
| ret = aml_kt_free(&aml_kt_dev, handle); |
| if (ret != 0) { |
| LOGE("aml_key_free failed retval=0x%08x\n", ret); |
| return -EFAULT; |
| } |
| break; |
| case KEY_GET_FLAG: |
| memset(&key_param, 0, sizeof(key_param)); |
| if (copy_from_user(&key_param, (void __user *)arg, |
| sizeof(key_param))) { |
| LOGE("aml_kt_get_status copy_from_user error\n"); |
| return -EFAULT; |
| } |
| |
| ret = aml_kt_get_status(&aml_kt_dev, key_param.key_index, |
| &key_sts); |
| if (ret != 0) { |
| LOGE("aml_kt_get_status failed retval=0x%08x\n", ret); |
| return -EFAULT; |
| } |
| memcpy(key_param.key, &key_sts, sizeof(key_sts)); |
| |
| ret = copy_to_user((void __user *)arg, &key_param, |
| sizeof(key_param)); |
| if (unlikely(ret)) { |
| LOGE("aml_kt_get_status copy_to_user error\n"); |
| return -EFAULT; |
| } |
| break; |
| default: |
| LOGE("Unknown cmd: %d\n", cmd); |
| return -EFAULT; |
| } |
| |
| return 0; |
| } |
| |
| static const struct file_operations aml_key_fops = { |
| .owner = THIS_MODULE, |
| .open = aml_key_open, |
| .release = aml_key_release, |
| .unlocked_ioctl = aml_key_ioctl, |
| .compat_ioctl = aml_key_ioctl, |
| }; |
| |
| int aml_key_init(struct class *aml_key_class) |
| { |
| int ret = -1; |
| struct device *device; |
| |
| if (alloc_chrdev_region(&aml_key_devt, 0, DEVICE_INSTANCES, |
| AML_KEY_DEVICE_NAME) < 0) { |
| LOGE("%s device can't be allocated.\n", AML_KEY_DEVICE_NAME); |
| return -ENXIO; |
| } |
| |
| cdev_init(&aml_key_dev.cdev, &aml_key_fops); |
| aml_key_dev.cdev.owner = THIS_MODULE; |
| ret = cdev_add(&aml_key_dev.cdev, |
| MKDEV(MAJOR(aml_key_devt), MINOR(aml_key_devt)), 1); |
| if (unlikely(ret < 0)) |
| goto unregister_chrdev; |
| |
| device = device_create(aml_key_class, NULL, aml_key_devt, NULL, |
| AML_KEY_DEVICE_NAME); |
| if (IS_ERR(device)) { |
| LOGE("device_create failed\n"); |
| ret = PTR_ERR(device); |
| goto delete_cdev; |
| } |
| |
| return ret; |
| |
| delete_cdev: |
| cdev_del(&aml_key_dev.cdev); |
| unregister_chrdev: |
| unregister_chrdev_region(aml_key_devt, DEVICE_INSTANCES); |
| |
| return ret; |
| } |
| |
| void aml_key_exit(struct class *aml_key_class) |
| { |
| device_destroy(aml_key_class, aml_key_devt); |
| cdev_del(&aml_key_dev.cdev); |
| unregister_chrdev_region(MKDEV(MAJOR(aml_key_devt), |
| MINOR(aml_key_devt)), |
| DEVICE_INSTANCES); |
| } |
| |
| int __init aml_key_driver_init(void) |
| { |
| int ret = 0; |
| |
| aml_key_class = class_create(THIS_MODULE, AML_KEY_DEVICE_NAME); |
| if (IS_ERR(aml_key_class)) { |
| LOGE("class_create failed\n"); |
| ret = PTR_ERR(aml_key_class); |
| return ret; |
| } |
| |
| ret = aml_key_init(aml_key_class); |
| if (unlikely(ret < 0)) { |
| LOGE("aml_key_init failed\n"); |
| class_destroy(aml_key_class); |
| return ret; |
| } |
| |
| ret = aml_key_init_dbgfs(); |
| if (ret) { |
| LOGE("aml_key_init_dbgfs failed\n"); |
| return ret; |
| } |
| |
| return ret; |
| } |
| |
| void __exit aml_key_driver_exit(void) |
| { |
| aml_key_exit(aml_key_class); |
| class_destroy(aml_key_class); |
| debugfs_remove_recursive(aml_key_debug_dent); |
| } |