mirror of
https://github.com/hardkernel/kernel_common_drivers.git
synced 2026-06-25 12:03:48 +09:00
e283f523e1
PD#SWPL-124337 Problem: the size passed to ioremap func is incorrect. cma pool conflict with iotrace. Solution: fix the errors. Verify: t5w. Change-Id: Ic54a47313c5839be17a07ae1a2b60b3092d23343 Signed-off-by: Jianxiong Pan <jianxiong.pan@amlogic.com>
486 lines
12 KiB
C
486 lines
12 KiB
C
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
|
|
/*
|
|
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
|
|
*/
|
|
|
|
#include <linux/cdev.h>
|
|
#include <linux/types.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/device.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/module.h>
|
|
#include <linux/uaccess.h>
|
|
#include <linux/of.h>
|
|
#include <linux/memblock.h>
|
|
#include <linux/random.h>
|
|
#include <linux/compat.h>
|
|
#include <linux/amlogic/gki_module.h>
|
|
#include <linux/of_reserved_mem.h>
|
|
#include <linux/io.h>
|
|
#include "defendkey.h"
|
|
|
|
#define DEFENDKEY_DEVICE_NAME "defendkey"
|
|
#define DEFENDKEY_CLASS_NAME "defendkey"
|
|
|
|
static unsigned long mem_size;
|
|
static struct defendkey_mem defendkey_rmem;
|
|
static struct aml_defendkey_type defendkey_type = {
|
|
.decrypt_dtb = 0,
|
|
.status = 0,
|
|
};
|
|
|
|
static int defendkey_open(struct inode *inode, struct file *file)
|
|
{
|
|
struct aml_defendkey_dev *devp;
|
|
|
|
devp = container_of(inode->i_cdev, struct aml_defendkey_dev, cdev);
|
|
file->private_data = devp;
|
|
return 0;
|
|
}
|
|
|
|
static int defendkey_release(struct inode *inode, struct file *file)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static loff_t defendkey_llseek(struct file *filp, loff_t off, int whence)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static long defendkey_unlocked_ioctl(struct file *file,
|
|
unsigned int cmd, unsigned long arg)
|
|
{
|
|
unsigned long ret = 0;
|
|
struct aml_defendkey_dev *defendkey_dev;
|
|
|
|
defendkey_dev = file->private_data;
|
|
|
|
switch (cmd) {
|
|
case CMD_SECURE_CHECK:
|
|
ret = aml_is_secure_set(defendkey_dev->reg_base, defendkey_dev->sb_mask);
|
|
break;
|
|
case CMD_DECRYPT_DTB:
|
|
if (arg == 1) {
|
|
defendkey_type.decrypt_dtb = E_DECRYPT_DTB;
|
|
} else if (arg == 0) {
|
|
defendkey_type.decrypt_dtb = E_UPGRADE_CHECK;
|
|
} else {
|
|
return -EINVAL;
|
|
pr_info("set defendkey type fail, invalid value\n");
|
|
}
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
#ifdef CONFIG_COMPAT
|
|
static long defendkey_compat_ioctl(struct file *filp,
|
|
unsigned int cmd, unsigned long args)
|
|
{
|
|
unsigned long ret;
|
|
|
|
args = (unsigned long)compat_ptr(args);
|
|
ret = defendkey_unlocked_ioctl(filp, cmd, args);
|
|
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
static ssize_t defendkey_read(struct file *file,
|
|
char __user *buf, size_t count, loff_t *ppos)
|
|
{
|
|
ssize_t ret_value = RET_ERROR;
|
|
int ret = -EINVAL;
|
|
unsigned long mem_base_phy, check_offset;
|
|
void __iomem *mem_base_virt;
|
|
struct cpumask task_cpumask;
|
|
|
|
cpumask_copy(&task_cpumask, current->cpus_ptr);
|
|
set_cpus_allowed_ptr(current, cpumask_of(0));
|
|
|
|
if (defendkey_type.status == 1) {
|
|
defendkey_type.status = 0;
|
|
|
|
mem_base_phy = defendkey_rmem.base;
|
|
mem_base_virt = phys_to_virt(mem_base_phy);
|
|
|
|
check_offset = aml_sec_boot_check(AML_D_Q_IMG_SIG_HDR_SIZE,
|
|
mem_base_phy, mem_size, 0);
|
|
if (AML_D_Q_IMG_SIG_HDR_SIZE == (check_offset & 0xFFFF))
|
|
check_offset = (check_offset >> 16) & 0xFFFF;
|
|
else
|
|
check_offset = 0;
|
|
|
|
if (mem_size < count) {
|
|
pr_err("%s: data size overflow\n", __func__);
|
|
ret_value = RET_FAIL;
|
|
goto exit;
|
|
}
|
|
|
|
ret = copy_to_user((void __user *)buf,
|
|
(const void *)(mem_base_virt + check_offset),
|
|
count);
|
|
if (ret) {
|
|
ret_value = RET_FAIL;
|
|
pr_err("%s: copy_to_user fail! ret:%d\n",
|
|
__func__, ret);
|
|
goto exit;
|
|
}
|
|
} else {
|
|
ret_value = RET_ERROR;
|
|
pr_err("%s: need to decrypt dtb first\n", __func__);
|
|
goto exit;
|
|
}
|
|
|
|
ret_value = RET_SUCCESS;
|
|
pr_info("%s: read decrypted dtb OK\n", __func__);
|
|
|
|
exit:
|
|
set_cpus_allowed_ptr(current, &task_cpumask);
|
|
return ret_value;
|
|
}
|
|
|
|
static ssize_t defendkey_write(struct file *file,
|
|
const char __user *buf,
|
|
size_t count, loff_t *ppos)
|
|
{
|
|
ssize_t ret_value = RET_ERROR;
|
|
int ret = -EINVAL;
|
|
unsigned long mem_base_phy, copy_base, copy_size;
|
|
void __iomem *mem_base_virt;
|
|
u64 option = 0, random = 0, option_random = 0;
|
|
int i;
|
|
struct cpumask task_cpumask;
|
|
|
|
cpumask_copy(&task_cpumask, current->cpus_ptr);
|
|
set_cpus_allowed_ptr(current, cpumask_of(0));
|
|
|
|
defendkey_type.status = 0;
|
|
|
|
mem_base_phy = defendkey_rmem.base;
|
|
mem_base_virt = phys_to_virt(mem_base_phy);
|
|
|
|
pr_info("defendkey: mem_base_phy:%lx mem_size:%lx mem_base_virt:%p count:%lx\n",
|
|
mem_base_phy, mem_size, mem_base_virt, (unsigned long)count);
|
|
|
|
random = get_random_long();
|
|
|
|
option_random = (random << 8);
|
|
option_random |= (random << 32);
|
|
|
|
for (i = 0; i <= count / mem_size; i++) {
|
|
copy_base = (unsigned long)buf + mem_size * i;
|
|
|
|
if (mem_size * (i + 1) > count)
|
|
copy_size = count - mem_size * i;
|
|
else
|
|
copy_size = mem_size;
|
|
|
|
ret = copy_from_user(mem_base_virt,
|
|
(const void __user *)copy_base, copy_size);
|
|
if (ret) {
|
|
pr_err("%s: copy_from_user fail!!\n", __func__);
|
|
ret = -EFAULT;
|
|
goto exit;
|
|
}
|
|
|
|
if (i == 0) {
|
|
if (count <= mem_size)
|
|
option = NSTATE_ALL | option_random;
|
|
else
|
|
option = NSTATE_INIT | option_random;
|
|
} else if (i == count / mem_size) {
|
|
if (count % mem_size != 0)
|
|
option = NSTATE_FINAL | option_random;
|
|
else
|
|
break;
|
|
} else {
|
|
option = NSTATE_UPDATE | option_random;
|
|
|
|
if (count % mem_size == 0 && i == count / mem_size - 1)
|
|
option = NSTATE_FINAL | option_random;
|
|
}
|
|
|
|
pr_info("defendkey: copy_size:0x%lx, option:0x%llx\n",
|
|
copy_size, option);
|
|
|
|
if (defendkey_type.decrypt_dtb == E_DECRYPT_DTB)
|
|
ret = aml_sec_boot_check(AML_D_P_IMG_DECRYPT,
|
|
mem_base_phy, copy_size, 0);
|
|
else
|
|
ret = aml_sec_boot_check(AML_D_P_UPGRADE_CHECK,
|
|
mem_base_phy,
|
|
copy_size, option);
|
|
|
|
if (ret) {
|
|
ret_value = RET_FAIL;
|
|
pr_err("%s: %s fail! ret %d\n", __func__,
|
|
defendkey_type.decrypt_dtb
|
|
? "decrypt dtb" : "upgrade check", ret);
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
if (defendkey_type.decrypt_dtb)
|
|
defendkey_type.status = 1;
|
|
|
|
ret_value = RET_SUCCESS;
|
|
pr_info("%s: %s OK\n", __func__,
|
|
defendkey_type.decrypt_dtb ? "decrypt dtb" : "upgrade check");
|
|
|
|
exit:
|
|
set_cpus_allowed_ptr(current, &task_cpumask);
|
|
return ret_value;
|
|
}
|
|
|
|
static ssize_t version_show(struct class *cla,
|
|
struct class_attribute *attr, char *buf)
|
|
{
|
|
return sprintf(buf, "version:2.00\n");
|
|
}
|
|
|
|
static ssize_t secure_check_show(struct class *cla,
|
|
struct class_attribute *attr, char *buf)
|
|
{
|
|
ssize_t n = 0;
|
|
int ret;
|
|
|
|
struct aml_defendkey_dev *defendkey_dev;
|
|
|
|
defendkey_dev = container_of(cla, struct aml_defendkey_dev, cls);
|
|
|
|
ret = aml_is_secure_set(defendkey_dev->reg_base, defendkey_dev->sb_mask);
|
|
if (ret < 0)
|
|
n = sprintf(buf, "fail");
|
|
else if (ret == 0)
|
|
n = sprintf(buf, "raw");
|
|
else
|
|
n = sprintf(buf, "encrypt");
|
|
|
|
return n;
|
|
}
|
|
|
|
static ssize_t secure_verify_show(struct class *cla,
|
|
struct class_attribute *attr, char *buf)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static ssize_t decrypt_dtb_show(struct class *cla,
|
|
struct class_attribute *attr, char *buf)
|
|
{
|
|
return sprintf(buf, "%d\n", defendkey_type.decrypt_dtb);
|
|
}
|
|
|
|
static ssize_t decrypt_dtb_store(struct class *cla,
|
|
struct class_attribute *attr,
|
|
const char *buf, size_t count)
|
|
{
|
|
unsigned int len;
|
|
|
|
if (buf[count - 1] == '\n')
|
|
len = count - 1;
|
|
else
|
|
len = count;
|
|
|
|
if (!strncmp(buf, "1", len))
|
|
defendkey_type.decrypt_dtb = E_DECRYPT_DTB;
|
|
else if (!strncmp(buf, "0", len))
|
|
defendkey_type.decrypt_dtb = E_UPGRADE_CHECK;
|
|
else
|
|
pr_info("set defendkey type fail, invalid value\n");
|
|
|
|
return count;
|
|
}
|
|
|
|
static const struct file_operations defendkey_fops = {
|
|
.owner = THIS_MODULE,
|
|
.llseek = defendkey_llseek,
|
|
.open = defendkey_open,
|
|
.release = defendkey_release,
|
|
.read = defendkey_read,
|
|
.write = defendkey_write,
|
|
.unlocked_ioctl = defendkey_unlocked_ioctl,
|
|
#ifdef CONFIG_COMPAT
|
|
.compat_ioctl = defendkey_compat_ioctl,
|
|
#endif
|
|
};
|
|
|
|
static CLASS_ATTR_RO(version);
|
|
static CLASS_ATTR_RO(secure_check);
|
|
static CLASS_ATTR_RO(secure_verify);
|
|
static CLASS_ATTR_RW(decrypt_dtb);
|
|
|
|
static struct attribute *defendkey_class_attrs[] = {
|
|
&class_attr_version.attr,
|
|
&class_attr_secure_check.attr,
|
|
&class_attr_secure_verify.attr,
|
|
&class_attr_decrypt_dtb.attr,
|
|
NULL,
|
|
};
|
|
|
|
ATTRIBUTE_GROUPS(defendkey_class);
|
|
|
|
static int defendkey_probe(struct platform_device *pdev)
|
|
{
|
|
int ret;
|
|
struct device *devp;
|
|
struct resource *reg_mem = NULL;
|
|
struct device_node *mem_region;
|
|
struct reserved_mem *rmem = NULL;
|
|
void __iomem *reg_base = NULL;
|
|
struct aml_defendkey_dev *defendkey_dev;
|
|
unsigned int secureboot_mask = 0;
|
|
|
|
reg_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
if (!IS_ERR_OR_NULL(reg_mem)) {
|
|
reg_base = ioremap(reg_mem->start, reg_mem->end - reg_mem->start);
|
|
if (IS_ERR(reg_base)) {
|
|
dev_err(&pdev->dev, "reg0: cannot obtain I/O memory region.\n");
|
|
return PTR_ERR(reg_base);
|
|
}
|
|
} else {
|
|
dev_err(&pdev->dev, "get IORESOURCE_MEM error.\n");
|
|
return PTR_ERR(reg_base);
|
|
}
|
|
|
|
defendkey_dev = devm_kzalloc(&pdev->dev,
|
|
sizeof(*defendkey_dev), GFP_KERNEL);
|
|
if (!defendkey_dev) {
|
|
ret = -ENOMEM;
|
|
dev_err(&pdev->dev, "failed to allocate mem for defendkey_dev\n");
|
|
goto out;
|
|
}
|
|
|
|
defendkey_dev->pdev = pdev;
|
|
platform_set_drvdata(pdev, defendkey_dev);
|
|
|
|
ret = of_property_read_u32(pdev->dev.of_node,
|
|
"secureboot_mask", &secureboot_mask);
|
|
if (ret)
|
|
dev_err(&pdev->dev, "don't config secureboot_mask\n");
|
|
|
|
defendkey_dev->sb_mask = secureboot_mask;
|
|
|
|
ret = of_property_read_u32(pdev->dev.of_node,
|
|
"mem_size", (unsigned int *)&mem_size);
|
|
if (ret) {
|
|
dev_err(&pdev->dev, "failed to get mem_size\n");
|
|
goto error1;
|
|
}
|
|
|
|
mem_region = of_parse_phandle(pdev->dev.of_node, "memory-region", 0);
|
|
if (!mem_region) {
|
|
ret = -EINVAL;
|
|
dev_err(&pdev->dev, "no memory-region\n");
|
|
goto error1;
|
|
}
|
|
|
|
rmem = of_reserved_mem_lookup(mem_region);
|
|
if (rmem) {
|
|
defendkey_rmem.base = rmem->base;
|
|
defendkey_rmem.size = rmem->size;
|
|
}
|
|
|
|
if (mem_size > defendkey_rmem.size) {
|
|
ret = -EINVAL;
|
|
dev_err(&pdev->dev, "reserved memory is not enough\n");
|
|
goto error1;
|
|
}
|
|
|
|
ret = alloc_chrdev_region(&defendkey_dev->devno,
|
|
0, 1, DEFENDKEY_DEVICE_NAME);
|
|
if (ret < 0) {
|
|
dev_err(&pdev->dev, "failed to allocate major number\n");
|
|
goto error1;
|
|
}
|
|
|
|
defendkey_dev->reg_base = reg_base;
|
|
defendkey_dev->cls.name = DEFENDKEY_CLASS_NAME;
|
|
defendkey_dev->cls.owner = THIS_MODULE;
|
|
defendkey_dev->cls.class_groups = defendkey_class_groups;
|
|
|
|
ret = class_register(&defendkey_dev->cls);
|
|
if (ret) {
|
|
dev_err(&pdev->dev, "failed to register class\n");
|
|
goto error2;
|
|
}
|
|
|
|
cdev_init(&defendkey_dev->cdev, &defendkey_fops);
|
|
defendkey_dev->cdev.owner = THIS_MODULE;
|
|
|
|
ret = cdev_add(&defendkey_dev->cdev, defendkey_dev->devno, 1);
|
|
if (ret) {
|
|
dev_err(&pdev->dev, "failed to add device\n");
|
|
goto error3;
|
|
}
|
|
|
|
devp = device_create(&defendkey_dev->cls, NULL,
|
|
defendkey_dev->devno, defendkey_dev, DEFENDKEY_DEVICE_NAME);
|
|
if (IS_ERR(devp)) {
|
|
dev_err(&pdev->dev, "failed to create device node\n");
|
|
ret = PTR_ERR(devp);
|
|
goto error4;
|
|
}
|
|
|
|
dev_info(&pdev->dev, "device %s created OK\n", DEFENDKEY_DEVICE_NAME);
|
|
return 0;
|
|
|
|
error4:
|
|
cdev_del(&defendkey_dev->cdev);
|
|
error3:
|
|
class_unregister(&defendkey_dev->cls);
|
|
error2:
|
|
unregister_chrdev_region(defendkey_dev->devno, 1);
|
|
error1:
|
|
devm_kfree(&pdev->dev, defendkey_dev);
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
static int defendkey_remove(struct platform_device *pdev)
|
|
{
|
|
struct aml_defendkey_dev *defendkey_dev = platform_get_drvdata(pdev);
|
|
|
|
unregister_chrdev_region(defendkey_dev->devno, 1);
|
|
device_destroy(&defendkey_dev->cls, defendkey_dev->devno);
|
|
cdev_del(&defendkey_dev->cdev);
|
|
class_unregister(&defendkey_dev->cls);
|
|
platform_set_drvdata(pdev, NULL);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct of_device_id defendkey_dt_match[] = {
|
|
{ .compatible = "amlogic, defendkey",
|
|
},
|
|
{},
|
|
};
|
|
|
|
static struct platform_driver defendkey_driver = {
|
|
.probe = defendkey_probe,
|
|
.remove = defendkey_remove,
|
|
.driver = {
|
|
.name = DEFENDKEY_DEVICE_NAME,
|
|
.owner = THIS_MODULE,
|
|
.of_match_table = defendkey_dt_match,
|
|
.owner = THIS_MODULE,
|
|
},
|
|
};
|
|
|
|
int __init aml_defendkey_init(void)
|
|
{
|
|
return platform_driver_register(&defendkey_driver);
|
|
}
|
|
|
|
void aml_defendkey_exit(void)
|
|
{
|
|
platform_driver_unregister(&defendkey_driver);
|
|
}
|
|
|