// SPDX-License-Identifier: (GPL-2.0+ OR MIT) /* * Copyright (c) 2019 Amlogic, Inc. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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) { 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(const struct class *class, const struct class_attribute *attr, char *buf) { return sprintf(buf, "version:2.00\n"); } static ssize_t secure_check_show(const struct class *class, const struct class_attribute *attr, char *buf) { ssize_t n = 0; int ret; struct aml_defendkey_dev *defendkey_dev; defendkey_dev = container_of(class, 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(const struct class *class, const struct class_attribute *attr, char *buf) { return 0; } static ssize_t decrypt_dtb_show(const struct class *class, const struct class_attribute *attr, char *buf) { return sprintf(buf, "%d\n", defendkey_type.decrypt_dtb); } static ssize_t decrypt_dtb_store(const struct class *class, const 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; u32 mem_size_tmp = 0; reg_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!IS_ERR_OR_NULL(reg_mem)) { reg_base = ioremap(reg_mem->start, resource_size(reg_mem)); 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", &mem_size_tmp); if (ret) { dev_err(&pdev->dev, "failed to get mem_size\n"); mem_size = 0; goto error1; } else { mem_size = mem_size_tmp; } 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.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 void 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); } 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); }