Loading drivers/soc/qcom/spss_utils.c +560 −8 Original line number Diff line number Diff line Loading @@ -29,11 +29,19 @@ #include <linux/platform_device.h> /* platform_driver_register() */ #include <linux/of.h> /* of_property_count_strings() */ #include <linux/io.h> /* ioremap_nocache() */ #include <linux/notifier.h> #include <linux/sizes.h> /* SZ_4K */ #include <linux/uaccess.h> /* copy_from_user() */ #include <soc/qcom/subsystem_notif.h> #include <soc/qcom/subsystem_restart.h> #include <soc/qcom/secure_buffer.h> /* VMID_HLOS */ #include <uapi/linux/ioctl.h> /* ioctl() */ #include <uapi/linux/spss_utils.h> /* IOCTL to user space */ /* driver name */ #define DEVICE_NAME "spss-utils" #define DEVICE_NAME "spss_utils" enum spss_firmware_type { SPSS_FW_TYPE_DEV = 'd', Loading @@ -51,10 +59,41 @@ static const char *firmware_name = "NA"; static struct device *spss_dev; static u32 spss_debug_reg_addr; /* SP_SCSR_MBn_SP2CL_GPm(n,m) */ static u32 spss_emul_type_reg_addr; /* TCSR_SOC_EMULATION_TYPE */ static void *iar_notif_handle; static struct notifier_block *iar_nb; #define CMAC_SIZE_IN_BYTES (128/8) /* 128 bit */ static u32 pil_addr; static u32 pil_size; static u32 cmac_buf[CMAC_SIZE_IN_BYTES/sizeof(u32)]; /* saved cmac */ static u32 pbl_cmac_buf[CMAC_SIZE_IN_BYTES/sizeof(u32)]; /* pbl cmac */ static u32 iar_state; static bool is_iar_enabled; static bool is_pbl_ce; /* Did SPU PBL performed Cryptographic Erase (CE) */ static void __iomem *cmac_mem; static size_t cmac_mem_size = SZ_4K; /* XPU align to 4KB */ static phys_addr_t cmac_mem_addr; #define SPU_EMULATUION (BIT(0) | BIT(1)) #define SPU_PRESENT_IN_EMULATION BIT(2) /** * struct device state */ struct spss_utils_device { /* char device info */ struct cdev *cdev; dev_t device_no; struct class *driver_class; struct device *class_dev; struct platform_device *pdev; }; /* Device State */ static struct spss_utils_device *spss_utils_dev; /*==========================================================================*/ /* Device Sysfs */ /*==========================================================================*/ Loading @@ -65,6 +104,11 @@ static ssize_t firmware_name_show(struct device *dev, { int ret; if (!dev || !attr || !buf) { pr_err("invalid param.\n"); return -EINVAL; } if (firmware_name == NULL) ret = scnprintf(buf, PAGE_SIZE, "%s\n", "unknown"); else Loading @@ -81,6 +125,11 @@ static ssize_t test_fuse_state_show(struct device *dev, { int ret; if (!dev || !attr || !buf) { pr_err("invalid param.\n"); return -EINVAL; } switch (firmware_type) { case SPSS_FW_TYPE_DEV: ret = scnprintf(buf, PAGE_SIZE, "%s", "dev"); Loading Loading @@ -108,7 +157,12 @@ static ssize_t spss_debug_reg_show(struct device *dev, void __iomem *spss_debug_reg = NULL; u32 val1, val2; pr_debug("spss_debug_reg_addr [0x%x]\n", spss_debug_reg_addr); if (!dev || !attr || !buf) { pr_err("invalid param.\n"); return -EINVAL; } pr_debug("spss_debug_reg_addr [0x%x].\n", spss_debug_reg_addr); spss_debug_reg = ioremap_nocache(spss_debug_reg_addr, sizeof(u32)*2); Loading @@ -130,32 +184,287 @@ static ssize_t spss_debug_reg_show(struct device *dev, static DEVICE_ATTR_RO(spss_debug_reg); static ssize_t cmac_buf_show(struct device *dev, struct device_attribute *attr, char *buf) { int ret = 0; if (!dev || !attr || !buf) { pr_err("invalid param.\n"); return -EINVAL; } ret = snprintf(buf, PAGE_SIZE, "0x%x,0x%x,0x%x,0x%x\n", cmac_buf[0], cmac_buf[1], cmac_buf[2], cmac_buf[3]); return ret; } static DEVICE_ATTR_RO(cmac_buf); static ssize_t iar_state_show(struct device *dev, struct device_attribute *attr, char *buf) { int ret = 0; if (!dev || !attr || !buf) { pr_err("invalid param.\n"); return -EINVAL; } /* show IAR-STATE from soc fuse */ ret = snprintf(buf, PAGE_SIZE, "0x%x\n", iar_state); return ret; } static DEVICE_ATTR_RO(iar_state); static ssize_t iar_enabled_show(struct device *dev, struct device_attribute *attr, char *buf) { int ret = 0; if (!dev || !attr || !buf) { pr_err("invalid param.\n"); return -EINVAL; } ret = snprintf(buf, PAGE_SIZE, "0x%x\n", is_iar_enabled); return ret; } static DEVICE_ATTR_RO(iar_enabled); static ssize_t pbl_ce_show(struct device *dev, struct device_attribute *attr, char *buf) { int ret = 0; if (!dev || !attr || !buf) { pr_err("invalid param.\n"); return -EINVAL; } ret = snprintf(buf, PAGE_SIZE, "0x%x\n", is_pbl_ce); return ret; } static DEVICE_ATTR_RO(pbl_ce); static ssize_t pbl_cmac_show(struct device *dev, struct device_attribute *attr, char *buf) { int ret = 0; if (!dev || !attr || !buf) { pr_err("invalid param.\n"); return -EINVAL; } ret = snprintf(buf, PAGE_SIZE, "0x%x,0x%x,0x%x,0x%x\n", pbl_cmac_buf[0], pbl_cmac_buf[1], pbl_cmac_buf[2], pbl_cmac_buf[3]); return ret; } static DEVICE_ATTR_RO(pbl_cmac); /*--------------------------------------------------------------------------*/ static int spss_create_sysfs(struct device *dev) { int ret; ret = device_create_file(dev, &dev_attr_firmware_name); if (ret < 0) { pr_err("failed to create sysfs file for firmware_name\n"); pr_err("failed to create sysfs file for firmware_name.\n"); return ret; } ret = device_create_file(dev, &dev_attr_test_fuse_state); if (ret < 0) { pr_err("failed to create sysfs file for test_fuse_state\n"); device_remove_file(dev, &dev_attr_firmware_name); return ret; pr_err("failed to create sysfs file for test_fuse_state.\n"); goto remove_firmware_name; } ret = device_create_file(dev, &dev_attr_spss_debug_reg); if (ret < 0) { pr_err("failed to create sysfs file for spss_debug_reg\n"); device_remove_file(dev, &dev_attr_firmware_name); pr_err("failed to create sysfs file for spss_debug_reg.\n"); goto remove_test_fuse_state; } ret = device_create_file(dev, &dev_attr_cmac_buf); if (ret < 0) { pr_err("failed to create sysfs file for cmac_buf.\n"); goto remove_spss_debug_reg; } ret = device_create_file(dev, &dev_attr_iar_state); if (ret < 0) { pr_err("failed to create sysfs file for iar_state.\n"); goto remove_cmac_buf; } ret = device_create_file(dev, &dev_attr_iar_enabled); if (ret < 0) { pr_err("failed to create sysfs file for iar_enabled.\n"); goto remove_iar_state; } ret = device_create_file(dev, &dev_attr_pbl_ce); if (ret < 0) { pr_err("failed to create sysfs file for pbl_ce.\n"); goto remove_iar_enabled; } ret = device_create_file(dev, &dev_attr_pbl_cmac); if (ret < 0) { pr_err("failed to create sysfs file for pbl_cmac.\n"); goto remove_pbl_ce; } return 0; remove_pbl_ce: device_remove_file(dev, &dev_attr_pbl_ce); remove_iar_enabled: device_remove_file(dev, &dev_attr_iar_enabled); remove_iar_state: device_remove_file(dev, &dev_attr_iar_state); remove_cmac_buf: device_remove_file(dev, &dev_attr_cmac_buf); remove_spss_debug_reg: device_remove_file(dev, &dev_attr_spss_debug_reg); remove_test_fuse_state: device_remove_file(dev, &dev_attr_test_fuse_state); remove_firmware_name: device_remove_file(dev, &dev_attr_firmware_name); return ret; } /*==========================================================================*/ /* IOCTL */ /*==========================================================================*/ static long spss_utils_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { void *buf = (void *) arg; unsigned char data[64] = {0}; size_t size = 0; if (buf == NULL) { pr_err("invalid ioctl arg\n"); return -EINVAL; } size = _IOC_SIZE(cmd); if (size && (cmd & IOC_IN)) { if (size > sizeof(data)) { pr_err("cmd [0x%x] size [0x%x] too large\n", cmd, size); return -EINVAL; } if (copy_from_user(data, (void __user *)arg, size)) { pr_err("copy_from_user() failed, cmd [0x%x]\n", cmd, size); return -EFAULT; } } switch (cmd) { case SPSS_IOC_SET_FW_CMAC: if (size != sizeof(cmac_buf)) { pr_err("cmd [0x%x] invalid size [0x%x]\n", cmd, size); return -EINVAL; } memcpy(cmac_buf, data, sizeof(cmac_buf)); pr_info("cmac_buf: 0x%x,0x%x,0x%x,0x%x\n", cmac_buf[0], cmac_buf[1], cmac_buf[2], cmac_buf[3]); break; default: pr_err("invalid ioctl cmd [0x%x]\n", cmd); return -EINVAL; } return 0; } static const struct file_operations spss_utils_fops = { .owner = THIS_MODULE, .unlocked_ioctl = spss_utils_ioctl, .compat_ioctl = spss_utils_ioctl, }; static int spss_utils_create_chardev(struct device *dev) { int ret; unsigned int baseminor = 0; unsigned int count = 1; void *priv = (void *) spss_utils_dev; spss_utils_dev->cdev = kzalloc(sizeof(*spss_utils_dev->cdev), GFP_KERNEL); if (!spss_utils_dev->cdev) return -ENOMEM; /* get device_no */ ret = alloc_chrdev_region(&spss_utils_dev->device_no, baseminor, count, DEVICE_NAME); if (ret < 0) { pr_err("alloc_chrdev_region failed %d\n", ret); return ret; } spss_utils_dev->driver_class = class_create(THIS_MODULE, DEVICE_NAME); if (IS_ERR(spss_utils_dev->driver_class)) { ret = -ENOMEM; pr_err("class_create failed %d\n", ret); goto exit_unreg_chrdev_region; } spss_utils_dev->class_dev = device_create(spss_utils_dev->driver_class, NULL, spss_utils_dev->device_no, priv, DEVICE_NAME); if (IS_ERR(spss_utils_dev->class_dev)) { pr_err("class_device_create failed %d\n", ret); ret = -ENOMEM; goto exit_destroy_class; } cdev_init(spss_utils_dev->cdev, &spss_utils_fops); spss_utils_dev->cdev->owner = THIS_MODULE; ret = cdev_add(spss_utils_dev->cdev, MKDEV(MAJOR(spss_utils_dev->device_no), 0), 1); if (ret < 0) { pr_err("cdev_add failed %d\n", ret); goto exit_destroy_device; } pr_debug("char device created.\n"); return 0; exit_destroy_device: device_destroy(spss_utils_dev->driver_class, spss_utils_dev->device_no); exit_destroy_class: class_destroy(spss_utils_dev->driver_class); exit_unreg_chrdev_region: unregister_chrdev_region(spss_utils_dev->device_no, 1); return ret; } /*==========================================================================*/ Loading @@ -176,6 +485,17 @@ static int spss_parse_dt(struct device_node *node) u32 spss_fuse2_bit = 0; u32 spss_fuse2_mask = 0; void __iomem *spss_fuse2_reg = NULL; /* IAR_FEATURE_ENABLED soc fuse */ u32 spss_fuse3_addr = 0; u32 spss_fuse3_bit = 0; u32 spss_fuse3_mask = 0; void __iomem *spss_fuse3_reg = NULL; /* IAR_STATE soc fuses */ u32 spss_fuse4_addr = 0; u32 spss_fuse4_bit = 0; u32 spss_fuse4_mask = 0; void __iomem *spss_fuse4_reg = NULL; u32 val1 = 0; u32 val2 = 0; void __iomem *spss_emul_type_reg = NULL; Loading Loading @@ -310,9 +630,202 @@ static int spss_parse_dt(struct device_node *node) } iounmap(spss_emul_type_reg); /* PIL-SPSS area */ ret = of_property_read_u32(node, "qcom,pil-addr", &pil_addr); if (ret < 0) { pr_err("can't get pil_addr\n"); return -EFAULT; } ret = of_property_read_u32(node, "qcom,pil-size", &pil_size); if (ret < 0) { pr_err("can't get pil_size\n"); return -EFAULT; } pr_info("pil_addr [0x%x].\n", pil_addr); pr_info("pil_size [0x%x].\n", pil_size); /* cmac buffer after spss firmware end */ cmac_mem_addr = pil_addr + pil_size; ret = of_property_read_u32(node, "qcom,spss-fuse3-addr", &spss_fuse3_addr); if (ret < 0) { pr_err("can't get fuse3 addr.\n"); return -EFAULT; } ret = of_property_read_u32(node, "qcom,spss-fuse3-bit", &spss_fuse3_bit); if (ret < 0) { pr_err("can't get fuse3 bit.\n"); return -EFAULT; } spss_fuse3_reg = ioremap_nocache(spss_fuse3_addr, sizeof(u32)); if (!spss_fuse3_reg) { pr_err("can't map fuse3 addr.\n"); return -EFAULT; } /* read IAR_FEATURE_ENABLED from soc fuse */ val1 = readl_relaxed(spss_fuse3_reg); spss_fuse3_mask = (1<<spss_fuse3_bit); pr_info("iar_enabled fuse, addr [0x%x] val [0x%x] mask [0x%x].\n", spss_fuse3_addr, val1, spss_fuse3_mask); if (val1 & spss_fuse3_mask) is_iar_enabled = true; else is_iar_enabled = false; memset(cmac_buf, 0xA5, sizeof(cmac_buf)); ret = of_property_read_u32(node, "qcom,spss-fuse4-addr", &spss_fuse4_addr); if (ret < 0) { pr_err("can't get fuse4 addr.\n"); return -EFAULT; } ret = of_property_read_u32(node, "qcom,spss-fuse4-bit", &spss_fuse4_bit); if (ret < 0) { pr_err("can't get fuse4 bit.\n"); return -EFAULT; } spss_fuse4_reg = ioremap_nocache(spss_fuse4_addr, sizeof(u32)); if (!spss_fuse4_reg) { pr_err("can't map fuse4 addr.\n"); return -EFAULT; } val1 = readl_relaxed(spss_fuse4_reg); spss_fuse4_mask = (0x07 << spss_fuse4_bit); /* 3 bits */ pr_info("IAR_STATE fuse, addr [0x%x] val [0x%x] mask [0x%x].\n", spss_fuse4_addr, val1, spss_fuse4_mask); val1 = ((val1 & spss_fuse4_mask) >> spss_fuse4_bit) & 0x07; iar_state = val1; pr_info("iar_state [%d]\n", iar_state); return 0; } static int spss_assign_mem_to_spss_and_hlos(phys_addr_t addr, size_t size) { int ret; int srcVM[1] = {VMID_HLOS}; int destVM[2] = {VMID_HLOS, VMID_CP_SPSS_HLOS_SHARED}; int destVMperm[2] = {PERM_READ | PERM_WRITE, PERM_READ | PERM_WRITE}; ret = hyp_assign_phys(addr, size, srcVM, 1, destVM, destVMperm, 2); if (ret) pr_err("hyp_assign_phys() failed, addr [%pa] size [%zx] ret [%d]\n", &addr, size, ret); return ret; } static int spss_set_fw_cmac(u32 *cmac, size_t cmac_size) { int ret; u8 __iomem *reg = NULL; int i; if (cmac_mem == NULL) { cmac_mem = ioremap_nocache(cmac_mem_addr, cmac_mem_size); if (!cmac_mem) { pr_err("can't map cmac_mem.\n"); return -EFAULT; } } ret = spss_assign_mem_to_spss_and_hlos(cmac_mem_addr, cmac_mem_size); if (ret) return ret; pr_debug("pil_addr [0x%x]\n", pil_addr); pr_debug("pil_size [0x%x]\n", pil_size); pr_debug("cmac_mem [%pK]\n", cmac_mem); reg = cmac_mem; pr_debug("reg [%pK]\n", reg); for (i = 0; i < cmac_size/4; i++) { writel_relaxed(cmac[i], reg + i*sizeof(u32)); pr_debug("cmac[%d] [0x%x]\n", i, cmac[i]); } reg += cmac_size; for (i = 0; i < cmac_size/4; i++) writel_relaxed(0, reg + i*sizeof(u32)); return 0; } static int spss_get_pbl_calc_cmac(u32 *cmac, size_t cmac_size) { u8 __iomem *reg = NULL; int i; u32 val; if (cmac_mem == NULL) return -EFAULT; /* PBL calculated cmac after HLOS expected cmac */ reg = cmac_mem + cmac_size; pr_debug("reg [%pK]\n", reg); for (i = 0; i < cmac_size/4; i++) { val = readl_relaxed(reg + i*sizeof(u32)); cmac[i] = val; pr_debug("cmac[%d] [0x%x]\n", (int) i, (int) val); } return 0; } static int spss_utils_iar_callback(struct notifier_block *nb, unsigned long code, void *data) { switch (code) { case SUBSYS_BEFORE_SHUTDOWN: pr_debug("[SUBSYS_BEFORE_SHUTDOWN] event.\n"); break; case SUBSYS_AFTER_SHUTDOWN: pr_debug("[SUBSYS_AFTER_SHUTDOWN] event.\n"); break; case SUBSYS_BEFORE_POWERUP: pr_debug("[SUBSYS_BEFORE_POWERUP] event.\n"); break; case SUBSYS_AFTER_POWERUP: pr_debug("[SUBSYS_AFTER_POWERUP] event.\n"); spss_get_pbl_calc_cmac(pbl_cmac_buf, sizeof(pbl_cmac_buf)); if (memcmp(cmac_buf, pbl_cmac_buf, sizeof(cmac_buf)) != 0) is_pbl_ce = true; /* cmacs not the same */ else is_pbl_ce = false; break; case SUBSYS_BEFORE_AUTH_AND_RESET: pr_debug("[SUBSYS_BEFORE_AUTH_AND_RESET] event.\n"); spss_set_fw_cmac(cmac_buf, sizeof(cmac_buf)); break; default: pr_err("unknown code [0x%x] .\n", (int) code); break; } return NOTIFY_OK; } /** * spss_probe() - initialization sequence */ Loading @@ -322,9 +835,29 @@ static int spss_probe(struct platform_device *pdev) struct device_node *np = NULL; struct device *dev = NULL; if (!pdev) { pr_err("invalid pdev.\n"); return -ENODEV; } np = pdev->dev.of_node; if (!np) { pr_err("invalid DT node.\n"); return -EINVAL; } spss_utils_dev = kzalloc(sizeof(*spss_utils_dev), GFP_KERNEL); if (spss_utils_dev == NULL) return -ENOMEM; dev = &pdev->dev; spss_dev = dev; if (dev == NULL) { pr_err("invalid dev.\n"); return -EINVAL; } platform_set_drvdata(pdev, dev); ret = spss_parse_dt(np); Loading Loading @@ -356,10 +889,29 @@ static int spss_probe(struct platform_device *pdev) return -EINVAL; } ret = spss_utils_create_chardev(dev); if (ret < 0) return ret; ret = spss_create_sysfs(dev); if (ret < 0) return ret; pr_info("Initialization completed ok, firmware_name [%s].\n", firmware_name); iar_nb = kzalloc(sizeof(*iar_nb), GFP_KERNEL); if (!iar_nb) return -ENOMEM; iar_nb->notifier_call = spss_utils_iar_callback; iar_notif_handle = subsys_notif_register_notifier("spss", iar_nb); if (IS_ERR_OR_NULL(iar_notif_handle)) { pr_err("register fail for IAR notifier\n"); kfree(iar_nb); } return 0; } Loading include/uapi/linux/spss_utils.h 0 → 100644 +28 −0 Original line number Diff line number Diff line /* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */ /* * Copyright (c) 2019, The Linux Foundation. All rights reserved. */ #ifndef _UAPI_SPSS_UTILS_H_ #define _UAPI_SPSS_UTILS_H_ #include <linux/types.h> /* uint32_t, bool */ #include <linux/ioctl.h> /* ioctl() */ /** * @brief - Secure Processor Utilities interface to user space * * The kernel spss_utils driver interface to user space via IOCTL * and SYSFS (device attributes). */ #define SPSS_IOC_MAGIC 'S' struct spss_ioc_set_fw_cmac { uint32_t cmac[4]; } __packed; #define SPSS_IOC_SET_FW_CMAC \ _IOWR(SPSS_IOC_MAGIC, 1, struct spss_ioc_set_fw_cmac) #endif /* _UAPI_SPSS_UTILS_H_ */ Loading
drivers/soc/qcom/spss_utils.c +560 −8 Original line number Diff line number Diff line Loading @@ -29,11 +29,19 @@ #include <linux/platform_device.h> /* platform_driver_register() */ #include <linux/of.h> /* of_property_count_strings() */ #include <linux/io.h> /* ioremap_nocache() */ #include <linux/notifier.h> #include <linux/sizes.h> /* SZ_4K */ #include <linux/uaccess.h> /* copy_from_user() */ #include <soc/qcom/subsystem_notif.h> #include <soc/qcom/subsystem_restart.h> #include <soc/qcom/secure_buffer.h> /* VMID_HLOS */ #include <uapi/linux/ioctl.h> /* ioctl() */ #include <uapi/linux/spss_utils.h> /* IOCTL to user space */ /* driver name */ #define DEVICE_NAME "spss-utils" #define DEVICE_NAME "spss_utils" enum spss_firmware_type { SPSS_FW_TYPE_DEV = 'd', Loading @@ -51,10 +59,41 @@ static const char *firmware_name = "NA"; static struct device *spss_dev; static u32 spss_debug_reg_addr; /* SP_SCSR_MBn_SP2CL_GPm(n,m) */ static u32 spss_emul_type_reg_addr; /* TCSR_SOC_EMULATION_TYPE */ static void *iar_notif_handle; static struct notifier_block *iar_nb; #define CMAC_SIZE_IN_BYTES (128/8) /* 128 bit */ static u32 pil_addr; static u32 pil_size; static u32 cmac_buf[CMAC_SIZE_IN_BYTES/sizeof(u32)]; /* saved cmac */ static u32 pbl_cmac_buf[CMAC_SIZE_IN_BYTES/sizeof(u32)]; /* pbl cmac */ static u32 iar_state; static bool is_iar_enabled; static bool is_pbl_ce; /* Did SPU PBL performed Cryptographic Erase (CE) */ static void __iomem *cmac_mem; static size_t cmac_mem_size = SZ_4K; /* XPU align to 4KB */ static phys_addr_t cmac_mem_addr; #define SPU_EMULATUION (BIT(0) | BIT(1)) #define SPU_PRESENT_IN_EMULATION BIT(2) /** * struct device state */ struct spss_utils_device { /* char device info */ struct cdev *cdev; dev_t device_no; struct class *driver_class; struct device *class_dev; struct platform_device *pdev; }; /* Device State */ static struct spss_utils_device *spss_utils_dev; /*==========================================================================*/ /* Device Sysfs */ /*==========================================================================*/ Loading @@ -65,6 +104,11 @@ static ssize_t firmware_name_show(struct device *dev, { int ret; if (!dev || !attr || !buf) { pr_err("invalid param.\n"); return -EINVAL; } if (firmware_name == NULL) ret = scnprintf(buf, PAGE_SIZE, "%s\n", "unknown"); else Loading @@ -81,6 +125,11 @@ static ssize_t test_fuse_state_show(struct device *dev, { int ret; if (!dev || !attr || !buf) { pr_err("invalid param.\n"); return -EINVAL; } switch (firmware_type) { case SPSS_FW_TYPE_DEV: ret = scnprintf(buf, PAGE_SIZE, "%s", "dev"); Loading Loading @@ -108,7 +157,12 @@ static ssize_t spss_debug_reg_show(struct device *dev, void __iomem *spss_debug_reg = NULL; u32 val1, val2; pr_debug("spss_debug_reg_addr [0x%x]\n", spss_debug_reg_addr); if (!dev || !attr || !buf) { pr_err("invalid param.\n"); return -EINVAL; } pr_debug("spss_debug_reg_addr [0x%x].\n", spss_debug_reg_addr); spss_debug_reg = ioremap_nocache(spss_debug_reg_addr, sizeof(u32)*2); Loading @@ -130,32 +184,287 @@ static ssize_t spss_debug_reg_show(struct device *dev, static DEVICE_ATTR_RO(spss_debug_reg); static ssize_t cmac_buf_show(struct device *dev, struct device_attribute *attr, char *buf) { int ret = 0; if (!dev || !attr || !buf) { pr_err("invalid param.\n"); return -EINVAL; } ret = snprintf(buf, PAGE_SIZE, "0x%x,0x%x,0x%x,0x%x\n", cmac_buf[0], cmac_buf[1], cmac_buf[2], cmac_buf[3]); return ret; } static DEVICE_ATTR_RO(cmac_buf); static ssize_t iar_state_show(struct device *dev, struct device_attribute *attr, char *buf) { int ret = 0; if (!dev || !attr || !buf) { pr_err("invalid param.\n"); return -EINVAL; } /* show IAR-STATE from soc fuse */ ret = snprintf(buf, PAGE_SIZE, "0x%x\n", iar_state); return ret; } static DEVICE_ATTR_RO(iar_state); static ssize_t iar_enabled_show(struct device *dev, struct device_attribute *attr, char *buf) { int ret = 0; if (!dev || !attr || !buf) { pr_err("invalid param.\n"); return -EINVAL; } ret = snprintf(buf, PAGE_SIZE, "0x%x\n", is_iar_enabled); return ret; } static DEVICE_ATTR_RO(iar_enabled); static ssize_t pbl_ce_show(struct device *dev, struct device_attribute *attr, char *buf) { int ret = 0; if (!dev || !attr || !buf) { pr_err("invalid param.\n"); return -EINVAL; } ret = snprintf(buf, PAGE_SIZE, "0x%x\n", is_pbl_ce); return ret; } static DEVICE_ATTR_RO(pbl_ce); static ssize_t pbl_cmac_show(struct device *dev, struct device_attribute *attr, char *buf) { int ret = 0; if (!dev || !attr || !buf) { pr_err("invalid param.\n"); return -EINVAL; } ret = snprintf(buf, PAGE_SIZE, "0x%x,0x%x,0x%x,0x%x\n", pbl_cmac_buf[0], pbl_cmac_buf[1], pbl_cmac_buf[2], pbl_cmac_buf[3]); return ret; } static DEVICE_ATTR_RO(pbl_cmac); /*--------------------------------------------------------------------------*/ static int spss_create_sysfs(struct device *dev) { int ret; ret = device_create_file(dev, &dev_attr_firmware_name); if (ret < 0) { pr_err("failed to create sysfs file for firmware_name\n"); pr_err("failed to create sysfs file for firmware_name.\n"); return ret; } ret = device_create_file(dev, &dev_attr_test_fuse_state); if (ret < 0) { pr_err("failed to create sysfs file for test_fuse_state\n"); device_remove_file(dev, &dev_attr_firmware_name); return ret; pr_err("failed to create sysfs file for test_fuse_state.\n"); goto remove_firmware_name; } ret = device_create_file(dev, &dev_attr_spss_debug_reg); if (ret < 0) { pr_err("failed to create sysfs file for spss_debug_reg\n"); device_remove_file(dev, &dev_attr_firmware_name); pr_err("failed to create sysfs file for spss_debug_reg.\n"); goto remove_test_fuse_state; } ret = device_create_file(dev, &dev_attr_cmac_buf); if (ret < 0) { pr_err("failed to create sysfs file for cmac_buf.\n"); goto remove_spss_debug_reg; } ret = device_create_file(dev, &dev_attr_iar_state); if (ret < 0) { pr_err("failed to create sysfs file for iar_state.\n"); goto remove_cmac_buf; } ret = device_create_file(dev, &dev_attr_iar_enabled); if (ret < 0) { pr_err("failed to create sysfs file for iar_enabled.\n"); goto remove_iar_state; } ret = device_create_file(dev, &dev_attr_pbl_ce); if (ret < 0) { pr_err("failed to create sysfs file for pbl_ce.\n"); goto remove_iar_enabled; } ret = device_create_file(dev, &dev_attr_pbl_cmac); if (ret < 0) { pr_err("failed to create sysfs file for pbl_cmac.\n"); goto remove_pbl_ce; } return 0; remove_pbl_ce: device_remove_file(dev, &dev_attr_pbl_ce); remove_iar_enabled: device_remove_file(dev, &dev_attr_iar_enabled); remove_iar_state: device_remove_file(dev, &dev_attr_iar_state); remove_cmac_buf: device_remove_file(dev, &dev_attr_cmac_buf); remove_spss_debug_reg: device_remove_file(dev, &dev_attr_spss_debug_reg); remove_test_fuse_state: device_remove_file(dev, &dev_attr_test_fuse_state); remove_firmware_name: device_remove_file(dev, &dev_attr_firmware_name); return ret; } /*==========================================================================*/ /* IOCTL */ /*==========================================================================*/ static long spss_utils_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { void *buf = (void *) arg; unsigned char data[64] = {0}; size_t size = 0; if (buf == NULL) { pr_err("invalid ioctl arg\n"); return -EINVAL; } size = _IOC_SIZE(cmd); if (size && (cmd & IOC_IN)) { if (size > sizeof(data)) { pr_err("cmd [0x%x] size [0x%x] too large\n", cmd, size); return -EINVAL; } if (copy_from_user(data, (void __user *)arg, size)) { pr_err("copy_from_user() failed, cmd [0x%x]\n", cmd, size); return -EFAULT; } } switch (cmd) { case SPSS_IOC_SET_FW_CMAC: if (size != sizeof(cmac_buf)) { pr_err("cmd [0x%x] invalid size [0x%x]\n", cmd, size); return -EINVAL; } memcpy(cmac_buf, data, sizeof(cmac_buf)); pr_info("cmac_buf: 0x%x,0x%x,0x%x,0x%x\n", cmac_buf[0], cmac_buf[1], cmac_buf[2], cmac_buf[3]); break; default: pr_err("invalid ioctl cmd [0x%x]\n", cmd); return -EINVAL; } return 0; } static const struct file_operations spss_utils_fops = { .owner = THIS_MODULE, .unlocked_ioctl = spss_utils_ioctl, .compat_ioctl = spss_utils_ioctl, }; static int spss_utils_create_chardev(struct device *dev) { int ret; unsigned int baseminor = 0; unsigned int count = 1; void *priv = (void *) spss_utils_dev; spss_utils_dev->cdev = kzalloc(sizeof(*spss_utils_dev->cdev), GFP_KERNEL); if (!spss_utils_dev->cdev) return -ENOMEM; /* get device_no */ ret = alloc_chrdev_region(&spss_utils_dev->device_no, baseminor, count, DEVICE_NAME); if (ret < 0) { pr_err("alloc_chrdev_region failed %d\n", ret); return ret; } spss_utils_dev->driver_class = class_create(THIS_MODULE, DEVICE_NAME); if (IS_ERR(spss_utils_dev->driver_class)) { ret = -ENOMEM; pr_err("class_create failed %d\n", ret); goto exit_unreg_chrdev_region; } spss_utils_dev->class_dev = device_create(spss_utils_dev->driver_class, NULL, spss_utils_dev->device_no, priv, DEVICE_NAME); if (IS_ERR(spss_utils_dev->class_dev)) { pr_err("class_device_create failed %d\n", ret); ret = -ENOMEM; goto exit_destroy_class; } cdev_init(spss_utils_dev->cdev, &spss_utils_fops); spss_utils_dev->cdev->owner = THIS_MODULE; ret = cdev_add(spss_utils_dev->cdev, MKDEV(MAJOR(spss_utils_dev->device_no), 0), 1); if (ret < 0) { pr_err("cdev_add failed %d\n", ret); goto exit_destroy_device; } pr_debug("char device created.\n"); return 0; exit_destroy_device: device_destroy(spss_utils_dev->driver_class, spss_utils_dev->device_no); exit_destroy_class: class_destroy(spss_utils_dev->driver_class); exit_unreg_chrdev_region: unregister_chrdev_region(spss_utils_dev->device_no, 1); return ret; } /*==========================================================================*/ Loading @@ -176,6 +485,17 @@ static int spss_parse_dt(struct device_node *node) u32 spss_fuse2_bit = 0; u32 spss_fuse2_mask = 0; void __iomem *spss_fuse2_reg = NULL; /* IAR_FEATURE_ENABLED soc fuse */ u32 spss_fuse3_addr = 0; u32 spss_fuse3_bit = 0; u32 spss_fuse3_mask = 0; void __iomem *spss_fuse3_reg = NULL; /* IAR_STATE soc fuses */ u32 spss_fuse4_addr = 0; u32 spss_fuse4_bit = 0; u32 spss_fuse4_mask = 0; void __iomem *spss_fuse4_reg = NULL; u32 val1 = 0; u32 val2 = 0; void __iomem *spss_emul_type_reg = NULL; Loading Loading @@ -310,9 +630,202 @@ static int spss_parse_dt(struct device_node *node) } iounmap(spss_emul_type_reg); /* PIL-SPSS area */ ret = of_property_read_u32(node, "qcom,pil-addr", &pil_addr); if (ret < 0) { pr_err("can't get pil_addr\n"); return -EFAULT; } ret = of_property_read_u32(node, "qcom,pil-size", &pil_size); if (ret < 0) { pr_err("can't get pil_size\n"); return -EFAULT; } pr_info("pil_addr [0x%x].\n", pil_addr); pr_info("pil_size [0x%x].\n", pil_size); /* cmac buffer after spss firmware end */ cmac_mem_addr = pil_addr + pil_size; ret = of_property_read_u32(node, "qcom,spss-fuse3-addr", &spss_fuse3_addr); if (ret < 0) { pr_err("can't get fuse3 addr.\n"); return -EFAULT; } ret = of_property_read_u32(node, "qcom,spss-fuse3-bit", &spss_fuse3_bit); if (ret < 0) { pr_err("can't get fuse3 bit.\n"); return -EFAULT; } spss_fuse3_reg = ioremap_nocache(spss_fuse3_addr, sizeof(u32)); if (!spss_fuse3_reg) { pr_err("can't map fuse3 addr.\n"); return -EFAULT; } /* read IAR_FEATURE_ENABLED from soc fuse */ val1 = readl_relaxed(spss_fuse3_reg); spss_fuse3_mask = (1<<spss_fuse3_bit); pr_info("iar_enabled fuse, addr [0x%x] val [0x%x] mask [0x%x].\n", spss_fuse3_addr, val1, spss_fuse3_mask); if (val1 & spss_fuse3_mask) is_iar_enabled = true; else is_iar_enabled = false; memset(cmac_buf, 0xA5, sizeof(cmac_buf)); ret = of_property_read_u32(node, "qcom,spss-fuse4-addr", &spss_fuse4_addr); if (ret < 0) { pr_err("can't get fuse4 addr.\n"); return -EFAULT; } ret = of_property_read_u32(node, "qcom,spss-fuse4-bit", &spss_fuse4_bit); if (ret < 0) { pr_err("can't get fuse4 bit.\n"); return -EFAULT; } spss_fuse4_reg = ioremap_nocache(spss_fuse4_addr, sizeof(u32)); if (!spss_fuse4_reg) { pr_err("can't map fuse4 addr.\n"); return -EFAULT; } val1 = readl_relaxed(spss_fuse4_reg); spss_fuse4_mask = (0x07 << spss_fuse4_bit); /* 3 bits */ pr_info("IAR_STATE fuse, addr [0x%x] val [0x%x] mask [0x%x].\n", spss_fuse4_addr, val1, spss_fuse4_mask); val1 = ((val1 & spss_fuse4_mask) >> spss_fuse4_bit) & 0x07; iar_state = val1; pr_info("iar_state [%d]\n", iar_state); return 0; } static int spss_assign_mem_to_spss_and_hlos(phys_addr_t addr, size_t size) { int ret; int srcVM[1] = {VMID_HLOS}; int destVM[2] = {VMID_HLOS, VMID_CP_SPSS_HLOS_SHARED}; int destVMperm[2] = {PERM_READ | PERM_WRITE, PERM_READ | PERM_WRITE}; ret = hyp_assign_phys(addr, size, srcVM, 1, destVM, destVMperm, 2); if (ret) pr_err("hyp_assign_phys() failed, addr [%pa] size [%zx] ret [%d]\n", &addr, size, ret); return ret; } static int spss_set_fw_cmac(u32 *cmac, size_t cmac_size) { int ret; u8 __iomem *reg = NULL; int i; if (cmac_mem == NULL) { cmac_mem = ioremap_nocache(cmac_mem_addr, cmac_mem_size); if (!cmac_mem) { pr_err("can't map cmac_mem.\n"); return -EFAULT; } } ret = spss_assign_mem_to_spss_and_hlos(cmac_mem_addr, cmac_mem_size); if (ret) return ret; pr_debug("pil_addr [0x%x]\n", pil_addr); pr_debug("pil_size [0x%x]\n", pil_size); pr_debug("cmac_mem [%pK]\n", cmac_mem); reg = cmac_mem; pr_debug("reg [%pK]\n", reg); for (i = 0; i < cmac_size/4; i++) { writel_relaxed(cmac[i], reg + i*sizeof(u32)); pr_debug("cmac[%d] [0x%x]\n", i, cmac[i]); } reg += cmac_size; for (i = 0; i < cmac_size/4; i++) writel_relaxed(0, reg + i*sizeof(u32)); return 0; } static int spss_get_pbl_calc_cmac(u32 *cmac, size_t cmac_size) { u8 __iomem *reg = NULL; int i; u32 val; if (cmac_mem == NULL) return -EFAULT; /* PBL calculated cmac after HLOS expected cmac */ reg = cmac_mem + cmac_size; pr_debug("reg [%pK]\n", reg); for (i = 0; i < cmac_size/4; i++) { val = readl_relaxed(reg + i*sizeof(u32)); cmac[i] = val; pr_debug("cmac[%d] [0x%x]\n", (int) i, (int) val); } return 0; } static int spss_utils_iar_callback(struct notifier_block *nb, unsigned long code, void *data) { switch (code) { case SUBSYS_BEFORE_SHUTDOWN: pr_debug("[SUBSYS_BEFORE_SHUTDOWN] event.\n"); break; case SUBSYS_AFTER_SHUTDOWN: pr_debug("[SUBSYS_AFTER_SHUTDOWN] event.\n"); break; case SUBSYS_BEFORE_POWERUP: pr_debug("[SUBSYS_BEFORE_POWERUP] event.\n"); break; case SUBSYS_AFTER_POWERUP: pr_debug("[SUBSYS_AFTER_POWERUP] event.\n"); spss_get_pbl_calc_cmac(pbl_cmac_buf, sizeof(pbl_cmac_buf)); if (memcmp(cmac_buf, pbl_cmac_buf, sizeof(cmac_buf)) != 0) is_pbl_ce = true; /* cmacs not the same */ else is_pbl_ce = false; break; case SUBSYS_BEFORE_AUTH_AND_RESET: pr_debug("[SUBSYS_BEFORE_AUTH_AND_RESET] event.\n"); spss_set_fw_cmac(cmac_buf, sizeof(cmac_buf)); break; default: pr_err("unknown code [0x%x] .\n", (int) code); break; } return NOTIFY_OK; } /** * spss_probe() - initialization sequence */ Loading @@ -322,9 +835,29 @@ static int spss_probe(struct platform_device *pdev) struct device_node *np = NULL; struct device *dev = NULL; if (!pdev) { pr_err("invalid pdev.\n"); return -ENODEV; } np = pdev->dev.of_node; if (!np) { pr_err("invalid DT node.\n"); return -EINVAL; } spss_utils_dev = kzalloc(sizeof(*spss_utils_dev), GFP_KERNEL); if (spss_utils_dev == NULL) return -ENOMEM; dev = &pdev->dev; spss_dev = dev; if (dev == NULL) { pr_err("invalid dev.\n"); return -EINVAL; } platform_set_drvdata(pdev, dev); ret = spss_parse_dt(np); Loading Loading @@ -356,10 +889,29 @@ static int spss_probe(struct platform_device *pdev) return -EINVAL; } ret = spss_utils_create_chardev(dev); if (ret < 0) return ret; ret = spss_create_sysfs(dev); if (ret < 0) return ret; pr_info("Initialization completed ok, firmware_name [%s].\n", firmware_name); iar_nb = kzalloc(sizeof(*iar_nb), GFP_KERNEL); if (!iar_nb) return -ENOMEM; iar_nb->notifier_call = spss_utils_iar_callback; iar_notif_handle = subsys_notif_register_notifier("spss", iar_nb); if (IS_ERR_OR_NULL(iar_notif_handle)) { pr_err("register fail for IAR notifier\n"); kfree(iar_nb); } return 0; } Loading
include/uapi/linux/spss_utils.h 0 → 100644 +28 −0 Original line number Diff line number Diff line /* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */ /* * Copyright (c) 2019, The Linux Foundation. All rights reserved. */ #ifndef _UAPI_SPSS_UTILS_H_ #define _UAPI_SPSS_UTILS_H_ #include <linux/types.h> /* uint32_t, bool */ #include <linux/ioctl.h> /* ioctl() */ /** * @brief - Secure Processor Utilities interface to user space * * The kernel spss_utils driver interface to user space via IOCTL * and SYSFS (device attributes). */ #define SPSS_IOC_MAGIC 'S' struct spss_ioc_set_fw_cmac { uint32_t cmac[4]; } __packed; #define SPSS_IOC_SET_FW_CMAC \ _IOWR(SPSS_IOC_MAGIC, 1, struct spss_ioc_set_fw_cmac) #endif /* _UAPI_SPSS_UTILS_H_ */