blob: 6c0135639550697f47d053bf213d23877427bd06 [file] [log] [blame] [edit]
// 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);
}