| // SPDX-License-Identifier: (GPL-2.0+ OR MIT) |
| /* |
| * Copyright (c) 2019 Amlogic, Inc. All rights reserved. |
| */ |
| |
| #include <linux/amlogic/aml_mkl.h> |
| |
| #include <linux/kernel.h> |
| #include <linux/init.h> |
| #include <linux/errno.h> |
| #include <linux/module.h> |
| #include <linux/debugfs.h> |
| #include <linux/device.h> |
| #include <linux/cdev.h> |
| #include <linux/platform_device.h> |
| #include <linux/of.h> |
| #include <linux/of_device.h> |
| #include <linux/interrupt.h> |
| #include <linux/of_device.h> |
| #include <linux/of_address.h> |
| #include <linux/types.h> |
| #include <linux/io.h> |
| #include <linux/printk.h> |
| #include <linux/mutex.h> |
| #include <linux/fs.h> |
| #include <linux/uaccess.h> |
| #include <linux/amlogic/iomap.h> |
| #include <linux/amlogic/aml_kt.h> |
| |
| #include "aml_seckey_log.h" |
| |
| #define AML_MKL_DEVICE_NAME "aml_mkl" |
| #define DEVICE_INSTANCES 1 |
| |
| /* kl type */ |
| #define KL_TYPE_OLD (0) |
| #define KL_TYPE_NEW (1) |
| |
| /* kl offset */ |
| #define KL_PENDING_OFFSET (31U) |
| #define KL_PENDING_MASK (1) |
| #define KL_STATUS_OFFSET (29) |
| #define KL_STATUS_MASK (3) |
| #define KL_ALGO_OFFSET (28) |
| #define KL_ALGO_MASK (1) |
| #define KL_MODE_OFFSET (26) |
| #define KL_MODE_MASK (3) |
| #define KL_FLAG_OFFSET (24) |
| #define KL_FLAG_MASK (3) |
| #define KL_KEYALGO_OFFSET (20) |
| #define KL_KEYALGO_MASK (0xf) |
| #define KL_USERID_OFFSET (16) |
| #define KL_USERID_MASK (0xf) |
| #define KL_KTE_OFFSET (8) |
| #define KL_KTE_MASK (0xff) |
| #define KL_MRK_OFFSET (4) |
| #define KL_MRK_MASK (0xf) |
| #define KL_FUNC_ID_OFFSET (0) |
| #define KL_FUNC_ID_MASK (0xf) |
| #define KL_MID_OFFSET (24) |
| #define KL_MID_MASK (0xff) |
| #define KL_VID_OFFSET (8) |
| #define KL_MID_EXTRA_OFFSET (8) |
| #define KL_TEE_PRIV_OFFSET (7) |
| #define KL_TEE_PRIV_MASK (1) |
| #define KL_TEE_SEP_OFFSET (2) |
| #define KL_TEE_SEP_MASK (3) |
| #define KL_LEVEL_OFFSET (4) |
| #define KL_LEVEL_MASK (7) |
| #define KL_STAGE_OFFSET (0) |
| #define KL_STAGE_MASK (3) |
| |
| #define KL_MSR_FUNCID_OFFSET (0) |
| #define KL_MSR_KEYALGO_OFFSET (12) |
| #define KL_MSR_PAYLOAD_OFFSET (16) |
| #define KL_MSR_BUFLEVEL_OFFSET (4) |
| |
| /* kl etc */ |
| #define KL_PENDING_WAIT_TIMEOUT (20000) |
| #define KL_ETSI_MID_MSG (0) |
| |
| /* kl status */ |
| #define KL_STATUS_OK (0) |
| #define KL_STATUS_ERROR_PERMISSION_DINED (1) |
| #define KL_STATUS_ERROR_OTP (2) |
| #define KL_STATUS_ERROR_DEPOSIT (3) |
| #define KL_STATUS_ERROR_TIMEOUT (4) |
| #define KL_STATUS_ERROR_BAD_PARAM (5) |
| #define KL_STATUS_ERROR_BAD_STATE (6) |
| |
| struct aml_mkl_dev { |
| struct cdev cdev; |
| struct mutex lock; /*define mutex*/ |
| void __iomem *base_addr; |
| union { |
| struct reg { |
| u32 rdy_offset; |
| u32 cfg_offset; |
| u32 cmd_offset; |
| u32 ek_offset; |
| } reg; |
| struct old_reg { |
| u32 start0_offset; |
| u32 key1_offset; |
| u32 nonce_offset; |
| u32 key2_offset; |
| u32 key3_offset; |
| u32 key4_offset; |
| u32 key5_offset; |
| u32 key6_offset; |
| u32 key7_offset; |
| } old_reg; |
| }; |
| u32 kl_type; |
| u32 kl_vid_type; |
| }; |
| |
| static dev_t aml_mkl_devt; |
| static struct aml_mkl_dev aml_mkl_dev; |
| static struct class *aml_mkl_class; |
| static struct dentry *aml_mkl_debug_dent; |
| u32 kl_log_level = 3; |
| |
| static int aml_mkl_init_dbgfs(void) |
| { |
| if (!aml_mkl_debug_dent) { |
| aml_mkl_debug_dent = debugfs_create_dir("aml_mkl", NULL); |
| if (!aml_mkl_debug_dent) { |
| KL_LOGE("can not create debugfs directory\n"); |
| return -ENOMEM; |
| } |
| |
| debugfs_create_u32("log_level", 0644, aml_mkl_debug_dent, &kl_log_level); |
| } |
| return 0; |
| } |
| |
| static int aml_mkl_open(struct inode *inode, struct file *file) |
| { |
| struct aml_mkl_dev *dev; |
| |
| dev = container_of(inode->i_cdev, struct aml_mkl_dev, cdev); |
| file->private_data = dev; |
| |
| return 0; |
| } |
| |
| static int aml_mkl_release(struct inode *inode, struct file *file) |
| { |
| if (!file->private_data) |
| return 0; |
| |
| file->private_data = NULL; |
| return 0; |
| } |
| |
| static int aml_mkl_program_key(void __iomem *base_addr, u32 offset, const u8 *data) |
| { |
| int i = 0; |
| const u32 *data32 = (const u32 *)data; |
| |
| if (!data32) |
| return -1; |
| |
| for (i = 0; i < 4; i++) |
| iowrite32(data32[i], (char *)base_addr + offset + i * sizeof(u32)); |
| |
| return 0; |
| } |
| |
| static int aml_mkl_lock(struct aml_mkl_dev *dev) |
| { |
| int cnt = 0; |
| int ret = KL_STATUS_OK; |
| |
| if (dev->kl_type == KL_TYPE_OLD) { |
| while ((ioread32((char *)dev->base_addr + dev->old_reg.start0_offset) >> 31) & 1) { |
| if (cnt++ > KL_PENDING_WAIT_TIMEOUT) { |
| KL_LOGE("Error: wait KL ready timeout\n"); |
| ret = KL_STATUS_ERROR_TIMEOUT; |
| } |
| } |
| } else { |
| while (ioread32((char *)dev->base_addr + dev->reg.rdy_offset) != 1) { |
| if (cnt++ > KL_PENDING_WAIT_TIMEOUT) { |
| KL_LOGE("Error: wait KL ready timeout\n"); |
| ret = KL_STATUS_ERROR_TIMEOUT; |
| } |
| } |
| } |
| |
| return ret; |
| } |
| |
| static void aml_mkl_unlock(struct aml_mkl_dev *dev) |
| { |
| iowrite32(1, (char *)dev->base_addr + dev->reg.rdy_offset); |
| } |
| |
| static int aml_mkl_read_pending(struct aml_mkl_dev *dev) |
| { |
| int ret = -1; |
| int cnt = 0; |
| u32 reg_ret = 0; |
| |
| do { |
| reg_ret = ioread32((char *)dev->base_addr + dev->reg.cfg_offset); |
| if (cnt++ > KL_PENDING_WAIT_TIMEOUT) { |
| KL_LOGE("Error: wait KL pending done timeout\n"); |
| ret = KL_STATUS_ERROR_TIMEOUT; |
| return ret; |
| } |
| } while (reg_ret & (1 << KL_PENDING_OFFSET)); |
| |
| reg_ret = (reg_ret >> KL_STATUS_OFFSET) & KL_STATUS_MASK; |
| switch (reg_ret) { |
| case 0: |
| ret = KL_STATUS_OK; |
| break; |
| case 1: |
| KL_LOGE("Permission Denied Error code: %d\n", reg_ret); |
| ret = KL_STATUS_ERROR_BAD_STATE; |
| break; |
| case 2: |
| KL_LOGE("OTP Error code: %d\n", reg_ret); |
| ret = KL_STATUS_ERROR_OTP; |
| break; |
| case 3: |
| KL_LOGE("KL Deposit Error code: %d\n", reg_ret); |
| ret = KL_STATUS_ERROR_DEPOSIT; |
| break; |
| } |
| |
| return ret; |
| } |
| |
| static int aml_mkl_etsi_run(struct file *filp, struct amlkl_params *param) |
| { |
| int ret = KL_STATUS_OK; |
| int i; |
| int tee_priv = 0; |
| u32 reg_val = 0; |
| u32 reg_offset = 0; |
| struct amlkl_usage *pu; |
| struct aml_mkl_dev *dev = filp->private_data; |
| |
| if (!param) { |
| KL_LOGE("Error: param data has Null\n"); |
| return KL_STATUS_ERROR_BAD_PARAM; |
| } |
| |
| pu = ¶m->usage; |
| KL_LOGD("kte:%d, levels:%d, mid:%#x, kl_algo:%d, mrk:%d\n", |
| param->kt_handle, param->levels, param->module_id, |
| param->kl_algo, param->mrk_cfg_index); |
| KL_LOGD("kt usage, crypto:%d, algo:%d, uid:%d\n", pu->crypto, pu->algo, |
| pu->uid); |
| |
| if (pu->uid & ~KL_USERID_MASK || |
| (pu->uid > AML_KT_USER_M2M_5 && pu->uid < AML_KT_USER_TSD) || |
| (pu->uid > AML_KT_USER_TSE && pu->uid < KL_USERID_MASK) || |
| pu->algo & ~KL_KEYALGO_MASK || |
| (pu->algo > AML_KT_ALGO_DES && pu->algo < AML_KT_ALGO_NDL) || |
| (pu->algo > AML_KT_ALGO_CSA2 && pu->algo < AML_KT_ALGO_HMAC) || |
| (pu->algo > AML_KT_ALGO_HMAC && pu->algo < KL_KEYALGO_MASK) || |
| pu->crypto & ~KL_FLAG_MASK || |
| param->levels < AML_KL_LEVEL_3 || param->levels > AML_KL_LEVEL_6 || |
| param->kl_algo > AML_KL_ALGO_AES || |
| param->mrk_cfg_index > AML_KL_MRK_ETSI_3) { |
| return KL_STATUS_ERROR_BAD_PARAM; |
| } |
| |
| /* 1. Read KL_REE_RDY to lock KL */ |
| mutex_lock(&dev->lock); |
| if (aml_mkl_lock(dev) != KL_STATUS_OK) { |
| KL_LOGE("key ladder not ready\n"); |
| ret = KL_STATUS_ERROR_BAD_STATE; |
| goto unlock_mutex; |
| } |
| |
| /* 2. Program Eks */ |
| for (i = 0; i < param->levels; i++) { |
| ret = aml_mkl_program_key(dev->base_addr, dev->reg.ek_offset + i * 16, |
| param->eks[param->levels - 1 - i]); |
| if (ret != 0) { |
| KL_LOGE("Error: Ek data has bad parameter\n"); |
| ret = KL_STATUS_ERROR_BAD_PARAM; |
| goto unlock_mkl; |
| } |
| } |
| |
| /* 3. Program KL_REE_CMD */ |
| reg_val = 0; |
| reg_offset = dev->reg.cmd_offset; |
| if (dev->kl_vid_type != 0) { |
| /* This part is applicable when ETSI_SW_VID is set in OTP */ |
| reg_val = (param->module_id << KL_MID_OFFSET | |
| param->vid << KL_VID_OFFSET | |
| tee_priv << KL_TEE_PRIV_OFFSET); |
| } else { |
| /* aka MKL_REE_MID for SC2 or before */ |
| reg_val = (param->module_id << KL_MID_OFFSET | |
| KL_ETSI_MID_MSG << KL_MID_EXTRA_OFFSET | |
| tee_priv << KL_TEE_PRIV_OFFSET); |
| } |
| iowrite32(reg_val, (char *)dev->base_addr + reg_offset); |
| |
| /* 4. Program KL_REE_CFG with KL_pending to 1 */ |
| reg_val = 0; |
| reg_offset = dev->reg.cfg_offset; |
| reg_val = (1 << KL_PENDING_OFFSET | param->kl_algo << KL_ALGO_OFFSET | |
| param->kl_mode << KL_MODE_OFFSET | |
| param->usage.crypto << KL_FLAG_OFFSET | |
| param->usage.algo << KL_KEYALGO_OFFSET | |
| param->usage.uid << KL_USERID_OFFSET | |
| param->kt_handle << KL_KTE_OFFSET | |
| param->mrk_cfg_index << KL_MRK_OFFSET | param->func_id); |
| iowrite32(reg_val, (char *)dev->base_addr + reg_offset); |
| |
| /* 5. Poll KL_REE_CFG till KL_pending is 0 */ |
| ret = aml_mkl_read_pending(dev); |
| if (ret == KL_STATUS_OK) |
| KL_LOGI("ETSI Key Ladder run success\n"); |
| |
| unlock_mkl: |
| aml_mkl_unlock(dev); |
| unlock_mutex: |
| mutex_unlock(&dev->lock); |
| |
| return ret; |
| } |
| |
| static int aml_mkl_etsi_old_run(struct file *filp, struct amlkl_params *param) |
| { |
| int ret = KL_STATUS_OK; |
| int i; |
| u32 reg_val = 0; |
| u32 reg_offset = 0; |
| u32 key_addrs[7] = {0}; |
| u8 zero[16] = {0}; |
| struct aml_mkl_dev *dev = filp->private_data; |
| |
| if (!param) { |
| KL_LOGE("Error: param data has Null\n"); |
| return KL_STATUS_ERROR_BAD_PARAM; |
| } |
| |
| KL_LOGD("kte:%d, levels:%d, kl_num:%d\n", param->kt_handle, param->levels, param->kl_num); |
| |
| if (param->levels < AML_KL_LEVEL_3 || |
| param->levels > AML_KL_LEVEL_6 || |
| param->kl_num > 10) { |
| KL_LOGE("Error: param data has bad parameter\n"); |
| return KL_STATUS_ERROR_BAD_PARAM; |
| } |
| |
| key_addrs[0] = dev->old_reg.key1_offset; |
| key_addrs[1] = dev->old_reg.key2_offset; |
| key_addrs[2] = dev->old_reg.key3_offset; |
| key_addrs[3] = dev->old_reg.key4_offset; |
| key_addrs[4] = dev->old_reg.key5_offset; |
| key_addrs[5] = dev->old_reg.key6_offset; |
| key_addrs[6] = dev->old_reg.key7_offset; |
| |
| /* 1. Program Eks */ |
| ret = aml_mkl_program_key(dev->base_addr, dev->old_reg.nonce_offset, zero); |
| for (i = 0; i < param->levels; i++) { |
| ret = aml_mkl_program_key(dev->base_addr, key_addrs[i], |
| param->eks[param->levels - 1 - i]); |
| } |
| for (i = param->levels; i < 7; i++) |
| ret = aml_mkl_program_key(dev->base_addr, key_addrs[i], zero); |
| if (ret != 0) { |
| KL_LOGE("Error: Ek data has bad parameter\n"); |
| return KL_STATUS_ERROR_BAD_PARAM; |
| } |
| |
| /* 2. Program KL_REE_CFG */ |
| reg_val = 0; |
| reg_offset = dev->old_reg.start0_offset; |
| reg_val = (param->kl_num << 24 | 0 << 22 | |
| param->kt_handle << 16 | param->vid); |
| iowrite32(reg_val, (char *)dev->base_addr + reg_offset); |
| |
| /* 3. Wait Busy Done */ |
| mutex_lock(&dev->lock); |
| if (aml_mkl_lock(dev) != KL_STATUS_OK) { |
| KL_LOGE("key ladder is busy\n"); |
| ret = KL_STATUS_ERROR_BAD_STATE; |
| goto unlock_mutex; |
| } |
| |
| aml_mkl_unlock(dev); |
| KL_LOGI("ETSI Key Ladder run success\n"); |
| |
| unlock_mutex: |
| mutex_unlock(&dev->lock); |
| |
| return ret; |
| } |
| |
| static int aml_mkl_run(struct file *filp, struct amlkl_params *param) |
| { |
| int ret = KL_STATUS_OK; |
| int i; |
| int tee_priv = 0; |
| u32 reg_val = 0; |
| u32 reg_offset = 0; |
| struct amlkl_usage *pu; |
| struct aml_mkl_dev *dev = filp->private_data; |
| |
| if (!param) { |
| KL_LOGE("Error: param data has Null\n"); |
| return KL_STATUS_ERROR_BAD_PARAM; |
| } |
| |
| pu = ¶m->usage; |
| KL_LOGD("kte:%d, levels:%d, kl_algo:%d, func_id:%d, mrk:%d\n", |
| param->kt_handle, param->levels, param->kl_algo, param->func_id, |
| param->mrk_cfg_index); |
| KL_LOGD("kt usage, crypto:%d, algo:%d, uid:%d\n", pu->crypto, pu->algo, |
| pu->uid); |
| |
| if (pu->uid & ~KL_USERID_MASK || |
| (pu->uid > AML_KT_USER_M2M_5 && pu->uid < AML_KT_USER_TSD) || |
| (pu->uid > AML_KT_USER_TSE && pu->uid < KL_USERID_MASK) || |
| pu->algo & ~KL_KEYALGO_MASK || |
| (pu->algo > AML_KT_ALGO_DES && pu->algo < AML_KT_ALGO_NDL) || |
| (pu->algo > AML_KT_ALGO_CSA2 && pu->algo < AML_KT_ALGO_HMAC) || |
| (pu->algo > AML_KT_ALGO_HMAC && pu->algo < KL_KEYALGO_MASK) || |
| pu->crypto & ~KL_FLAG_MASK || param->kl_algo > AML_KL_ALGO_AES) { |
| KL_LOGE("Error: param data has bad parameter\n"); |
| return KL_STATUS_ERROR_BAD_PARAM; |
| } |
| |
| /* 1. Read KL_REE_RDY to lock KL */ |
| mutex_lock(&dev->lock); |
| if (aml_mkl_lock(dev) != KL_STATUS_OK) { |
| KL_LOGE("key ladder not ready\n"); |
| ret = KL_STATUS_ERROR_BAD_STATE; |
| goto unlock_mutex; |
| } |
| |
| /* 2. Program Eks, fixed level to 3 */ |
| param->levels = AML_KL_LEVEL_3; |
| for (i = 0; i < param->levels; i++) { |
| ret = aml_mkl_program_key(dev->base_addr, dev->reg.ek_offset + i * 16, |
| param->eks[param->levels - 1 - i]); |
| if (ret != 0) { |
| KL_LOGE("Error: Ek data has bad parameter\n"); |
| ret = KL_STATUS_ERROR_BAD_PARAM; |
| goto unlock_mkl; |
| } |
| } |
| |
| /* 3. Program KL_REE_CMD */ |
| reg_val = 0; |
| reg_offset = dev->reg.cmd_offset; |
| reg_val = (tee_priv << KL_TEE_PRIV_OFFSET | 0 << KL_LEVEL_OFFSET | |
| 0 << KL_STAGE_OFFSET | 0 << KL_TEE_SEP_OFFSET); |
| iowrite32(reg_val, (char *)dev->base_addr + reg_offset); |
| |
| /* 4. Program KL_REE_CFG with KL_pending to 1 */ |
| reg_val = 0; |
| reg_offset = dev->reg.cfg_offset; |
| reg_val = (1 << KL_PENDING_OFFSET | param->kl_algo << KL_ALGO_OFFSET | |
| param->kl_mode << KL_MODE_OFFSET | |
| param->usage.crypto << KL_FLAG_OFFSET | |
| param->usage.algo << KL_KEYALGO_OFFSET | |
| param->usage.uid << KL_USERID_OFFSET | |
| param->kt_handle << KL_KTE_OFFSET | |
| param->mrk_cfg_index << KL_MRK_OFFSET | |
| (param->func_id << KL_FUNC_ID_OFFSET)); |
| iowrite32(reg_val, (char *)dev->base_addr + reg_offset); |
| |
| /* 5. Poll KL_REE_CFG till KL_pending is 0 */ |
| ret = aml_mkl_read_pending(dev); |
| if (ret == KL_STATUS_OK) |
| KL_LOGI("AML Key Ladder run success\n"); |
| |
| unlock_mkl: |
| aml_mkl_unlock(dev); |
| unlock_mutex: |
| mutex_unlock(&dev->lock); |
| |
| return ret; |
| } |
| |
| static int aml_mkl_msr_run(struct file *filp, struct amlkl_params *param) |
| { |
| int ret = KL_STATUS_OK; |
| int i; |
| int tee_priv = 0; |
| u32 reg_val = 0; |
| u32 reg_offset = 0; |
| struct amlkl_usage *pu; |
| struct aml_mkl_dev *dev = filp->private_data; |
| |
| if (!param) { |
| KL_LOGE("Error: param data has Null\n"); |
| return KL_STATUS_ERROR_BAD_PARAM; |
| } |
| |
| pu = ¶m->usage; |
| KL_LOGD("kte:%d, kl_algo:%d, func_id:%d\n", |
| param->kt_handle, param->kl_algo, param->func_id); |
| KL_LOGD("kt usage, crypto:%d, algo:%d, uid:%d\n", pu->crypto, pu->algo, |
| pu->uid); |
| |
| if (pu->uid & ~KL_USERID_MASK || |
| (pu->uid > AML_KT_USER_M2M_5 && pu->uid < AML_KT_USER_TSD) || |
| (pu->uid > AML_KT_USER_TSE && pu->uid < KL_USERID_MASK) || |
| pu->algo & ~KL_KEYALGO_MASK || |
| (pu->algo > AML_KT_ALGO_DES && pu->algo < AML_KT_ALGO_NDL) || |
| (pu->algo > AML_KT_ALGO_CSA2 && pu->algo < AML_KT_ALGO_HMAC) || |
| (pu->algo > AML_KT_ALGO_HMAC && pu->algo < KL_KEYALGO_MASK) || |
| pu->crypto & ~KL_FLAG_MASK || |
| param->kl_algo > AML_KL_ALGO_AES) { |
| KL_LOGE("Error: param data has bad parameter\n"); |
| return KL_STATUS_ERROR_BAD_PARAM; |
| } |
| |
| if (param->func_id == MSR_KL_FUNC_ID_CWUK || |
| param->func_id == MSR_KL_FUNC_ID_SSUK || |
| param->func_id == MSR_KL_FUNC_ID_CAUK) { |
| param->levels = MSR_KL_LEVEL_2; |
| } else if (param->func_id == MSR_KL_FUNC_ID_CPUK || |
| param->func_id == MSR_KL_FUNC_ID_CCCK) { |
| param->levels = MSR_KL_LEVEL_3; |
| } else if (param->func_id == MSR_KL_FUNC_ID_TAUK) { |
| param->levels = MSR_KL_LEVEL_1; |
| } else { |
| KL_LOGE("Error: func_id data has bad parameter\n"); |
| return KL_STATUS_ERROR_BAD_PARAM; |
| } |
| |
| /* 1. Read KL_REE_RDY to lock KL */ |
| mutex_lock(&dev->lock); |
| if (aml_mkl_lock(dev) != KL_STATUS_OK) { |
| KL_LOGE("key ladder not ready\n"); |
| ret = KL_STATUS_ERROR_BAD_STATE; |
| goto unlock_mutex; |
| } |
| |
| /* 2. Program Eks */ |
| for (i = 0; i < param->levels; i++) { |
| ret = aml_mkl_program_key(dev->base_addr, dev->reg.ek_offset + i * 16, |
| param->eks[param->levels - 1 - i]); |
| if (ret != 0) { |
| KL_LOGE("Error: Ek data has bad parameter\n"); |
| ret = KL_STATUS_ERROR_BAD_PARAM; |
| goto unlock_mkl; |
| } |
| } |
| |
| /* 3. Program KL_REE_CMD */ |
| reg_val = 0; |
| reg_offset = dev->reg.cmd_offset; |
| reg_val = (param->func_id << KL_MSR_FUNCID_OFFSET | |
| param->kl_algo << KL_MSR_KEYALGO_OFFSET | |
| param->levels << KL_MSR_PAYLOAD_OFFSET); |
| iowrite32(reg_val, (char *)dev->base_addr + reg_offset); |
| |
| /* 4. Program KL_REE_CFG with KL_pending to 1 */ |
| reg_val = 0; |
| reg_offset = dev->reg.cfg_offset; |
| reg_val = (1 << KL_PENDING_OFFSET | |
| param->kl_mode << KL_MODE_OFFSET | |
| param->usage.crypto << KL_FLAG_OFFSET | |
| param->usage.algo << KL_KEYALGO_OFFSET | |
| param->usage.uid << KL_USERID_OFFSET | |
| param->kt_handle << KL_KTE_OFFSET | |
| (tee_priv << KL_TEE_PRIV_OFFSET) | |
| 0 << KL_MSR_BUFLEVEL_OFFSET); |
| iowrite32(reg_val, (char *)dev->base_addr + reg_offset); |
| |
| /* 5. Poll KL_REE_CFG till KL_pending is 0 */ |
| ret = aml_mkl_read_pending(dev); |
| if (ret == KL_STATUS_OK) |
| KL_LOGI("AML MSR Key Ladder run success\n"); |
| |
| unlock_mkl: |
| aml_mkl_unlock(dev); |
| unlock_mutex: |
| mutex_unlock(&dev->lock); |
| |
| return ret; |
| } |
| |
| static long aml_mkl_ioctl(struct file *filp, unsigned int cmd, |
| unsigned long arg) |
| { |
| int ret = -ENOTTY; |
| struct amlkl_params kl_param; |
| struct aml_mkl_dev *dev = filp->private_data; |
| |
| if (!dev->base_addr) { |
| KL_LOGE("ERROR: MKL iobase is zero\n"); |
| return -EFAULT; |
| } |
| |
| switch (cmd) { |
| case AML_MKL_IOCTL_RUN: |
| memset(&kl_param, 0, sizeof(kl_param)); |
| if (copy_from_user(&kl_param, (uint32_t *)arg, |
| sizeof(kl_param))) { |
| return -EFAULT; |
| } |
| |
| if (kl_param.kl_mode == AML_KL_MODE_ETSI) { |
| if (dev->kl_type == KL_TYPE_OLD) |
| ret = aml_mkl_etsi_old_run(filp, &kl_param); |
| else |
| ret = aml_mkl_etsi_run(filp, &kl_param); |
| if (ret != 0) { |
| KL_LOGE("MKL: aml_mkl_etsi_run failed retval=0x%08x\n", |
| ret); |
| return -EFAULT; |
| } |
| } else if (kl_param.kl_mode == AML_KL_MODE_AML) { |
| if (dev->kl_type == KL_TYPE_OLD) { |
| KL_LOGE("MKL: aml_mkl_run failed. not support.\n"); |
| return -EFAULT; |
| } |
| |
| ret = aml_mkl_run(filp, &kl_param); |
| if (ret != 0) { |
| KL_LOGE("MKL: aml_mkl_run failed retval=0x%08x\n", |
| ret); |
| return -EFAULT; |
| } |
| } else if (kl_param.kl_mode == AML_KL_MODE_MSR) { |
| if (dev->kl_type == KL_TYPE_OLD) { |
| KL_LOGE("MKL: aml_mkl_msr_run failed. not support.\n"); |
| return -EFAULT; |
| } |
| |
| ret = aml_mkl_msr_run(filp, &kl_param); |
| if (ret != 0) { |
| KL_LOGE("MKL: aml_mkl_msr_run failed retval=0x%08x\n", |
| ret); |
| return -EFAULT; |
| } |
| } |
| break; |
| default: |
| KL_LOGE("No appropriate IOCTL found\n"); |
| } |
| |
| return ret; |
| } |
| |
| static const struct file_operations aml_mkl_fops = { |
| .owner = THIS_MODULE, |
| .open = aml_mkl_open, |
| .release = aml_mkl_release, |
| .unlocked_ioctl = aml_mkl_ioctl, |
| .compat_ioctl = aml_mkl_ioctl, |
| }; |
| |
| static int aml_mkl_get_dts_info(struct aml_mkl_dev *dev, struct platform_device *pdev) |
| { |
| int ret = 0; |
| u32 old_offset[2]; |
| u32 offset[4]; |
| |
| if (unlikely(!dev)) { |
| KL_LOGE("Empty aml_mkl_dev\n"); |
| return -1; |
| } |
| |
| ret = of_property_read_u32(pdev->dev.of_node, "kl_type", &dev->kl_type); |
| if (ret) { |
| KL_LOGE("%s: not found 0x%x\n", "kl_type", dev->kl_type); |
| return -1; |
| } |
| |
| ret = of_property_read_u32(pdev->dev.of_node, "kl_vid_type", &dev->kl_vid_type); |
| if (ret) { |
| KL_LOGE("%s: not found 0x%x\n", "kl_vid_type", dev->kl_vid_type); |
| return -1; |
| } |
| |
| /* kl register offset */ |
| if (dev->kl_type == KL_TYPE_OLD) { |
| if (of_property_read_u32_array(pdev->dev.of_node, "kl_offset", old_offset, |
| ARRAY_SIZE(old_offset)) == 0) { |
| dev->old_reg.start0_offset = old_offset[0]; |
| dev->old_reg.key1_offset = old_offset[1]; |
| dev->old_reg.nonce_offset = dev->old_reg.key1_offset + 16; |
| dev->old_reg.key2_offset = dev->old_reg.nonce_offset + 16; |
| dev->old_reg.key3_offset = dev->old_reg.key2_offset + 16; |
| dev->old_reg.key4_offset = dev->old_reg.key3_offset + 16; |
| dev->old_reg.key5_offset = dev->old_reg.key4_offset + 16; |
| dev->old_reg.key6_offset = dev->old_reg.key5_offset + 16; |
| dev->old_reg.key7_offset = dev->old_reg.key6_offset + 16; |
| } else { |
| KL_LOGE("%s not found\n", "kl_offset"); |
| return -1; |
| } |
| } else { |
| if (of_property_read_u32_array(pdev->dev.of_node, "kl_offset", offset, |
| ARRAY_SIZE(offset)) == 0) { |
| dev->reg.rdy_offset = offset[0]; |
| dev->reg.cfg_offset = offset[1]; |
| dev->reg.cmd_offset = offset[2]; |
| dev->reg.ek_offset = offset[3]; |
| } else { |
| KL_LOGE("%s: not found\n", "kl_offset"); |
| return -1; |
| } |
| } |
| |
| return ret; |
| } |
| |
| int aml_mkl_init(struct class *aml_mkl_class, struct platform_device *pdev) |
| { |
| int ret = -1; |
| struct device *device; |
| struct resource *res; |
| |
| if (alloc_chrdev_region(&aml_mkl_devt, 0, DEVICE_INSTANCES, |
| AML_MKL_DEVICE_NAME) < 0) { |
| KL_LOGE("%s device can't be allocated.\n", AML_MKL_DEVICE_NAME); |
| return -ENXIO; |
| } |
| |
| cdev_init(&aml_mkl_dev.cdev, &aml_mkl_fops); |
| aml_mkl_dev.cdev.owner = THIS_MODULE; |
| ret = cdev_add(&aml_mkl_dev.cdev, |
| MKDEV(MAJOR(aml_mkl_devt), MINOR(aml_mkl_devt)), 1); |
| aml_mkl_dev.base_addr = NULL; |
| if (unlikely(ret < 0)) |
| goto unregister_chrdev; |
| |
| device = device_create(aml_mkl_class, NULL, aml_mkl_devt, NULL, |
| AML_MKL_DEVICE_NAME); |
| if (IS_ERR(device)) { |
| KL_LOGE("device_create failed\n"); |
| ret = PTR_ERR(device); |
| goto delete_cdev; |
| } |
| |
| res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
| if (!res) { |
| KL_LOGE("%s: platform_get_resource is failed\n", __func__); |
| ret = -ENOMEM; |
| goto destroy_device; |
| } |
| |
| aml_mkl_dev.base_addr = devm_ioremap_resource(&pdev->dev, res); |
| if (!aml_mkl_dev.base_addr) { |
| KL_LOGE("%s base addr error\n", __func__); |
| ret = -ENOMEM; |
| goto destroy_device; |
| } |
| |
| ret = aml_mkl_get_dts_info(&aml_mkl_dev, pdev); |
| if (ret != 0) { |
| KL_LOGE("%s: cannot find match dts info\n", __func__); |
| ret = -EINVAL; |
| goto destroy_device; |
| } |
| |
| mutex_init(&aml_mkl_dev.lock); |
| return ret; |
| |
| destroy_device: |
| device_destroy(aml_mkl_class, aml_mkl_devt); |
| delete_cdev: |
| cdev_del(&aml_mkl_dev.cdev); |
| unregister_chrdev: |
| unregister_chrdev_region(aml_mkl_devt, DEVICE_INSTANCES); |
| |
| return ret; |
| } |
| |
| void aml_mkl_exit(struct class *aml_mkl_class, struct platform_device *pdev) |
| { |
| device_destroy(aml_mkl_class, aml_mkl_devt); |
| cdev_del(&aml_mkl_dev.cdev); |
| unregister_chrdev_region(MKDEV(MAJOR(aml_mkl_devt), |
| MINOR(aml_mkl_devt)), |
| DEVICE_INSTANCES); |
| mutex_destroy(&aml_mkl_dev.lock); |
| } |
| |
| static int aml_mkl_probe(struct platform_device *pdev) |
| { |
| int ret = 0; |
| |
| aml_mkl_class = class_create(THIS_MODULE, AML_MKL_DEVICE_NAME); |
| if (IS_ERR(aml_mkl_class)) { |
| KL_LOGE("class_create failed\n"); |
| ret = PTR_ERR(aml_mkl_class); |
| return ret; |
| } |
| |
| ret = aml_mkl_init(aml_mkl_class, pdev); |
| if (unlikely(ret < 0)) { |
| KL_LOGE("aml_mkl_core_init failed\n"); |
| goto device_create_error; |
| } |
| |
| ret = aml_mkl_init_dbgfs(); |
| if (ret) { |
| KL_LOGE("aml_mkl_init_dbgfs failed\n"); |
| goto device_create_error; |
| } |
| |
| return 0; |
| |
| device_create_error: |
| class_destroy(aml_mkl_class); |
| return ret; |
| } |
| |
| static int aml_mkl_remove(struct platform_device *pdev) |
| { |
| aml_mkl_exit(aml_mkl_class, pdev); |
| class_destroy(aml_mkl_class); |
| debugfs_remove_recursive(aml_mkl_debug_dent); |
| return 0; |
| } |
| |
| #ifdef CONFIG_OF |
| static const struct of_device_id aml_mkl_dt_match[] = { |
| { |
| .compatible = "amlogic,aml_mkl", |
| }, |
| {} |
| }; |
| MODULE_DEVICE_TABLE(of, aml_mkl_dt_match); |
| #else |
| #define aml_mkl_dt_match NULL |
| #endif |
| |
| static struct platform_driver aml_mkl_drv = { |
| .probe = aml_mkl_probe, |
| .remove = aml_mkl_remove, |
| .driver = { |
| .name = AML_MKL_DEVICE_NAME, |
| .of_match_table = aml_mkl_dt_match, |
| .owner = THIS_MODULE, |
| }, |
| }; |
| |
| int __init aml_seckey_kl_init(void) |
| { |
| int err = 0; |
| |
| err = platform_driver_register(&aml_mkl_drv); |
| if (err) { |
| pr_err("%s: failed to register driver, err %d.\n", |
| __func__, err); |
| return err; |
| } |
| |
| return 0; |
| } |
| |
| void __exit aml_seckey_kl_exit(void) |
| { |
| platform_driver_unregister(&aml_mkl_drv); |
| } |