diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index 8d3f12656789..892acf86d31d 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -44,6 +44,17 @@ config QCOM_GLINK_SSR implements the SSR protocol for notifying the remote processor about neighboring subsystems going up or down. +config MSM_SPSS_UTILS + depends on MSM_PIL + bool "Secure Processor Utilities" + help + spss-utils driver selects Secure Processor firmware file name.name + The firmware file name for dev, test or production is selected + based on two fuses.fuses + Different file name is used for differnt SPSS HW versions,versions + because the SPSS firmware size is too small to support multiple + HW versions. + config QCOM_GSBI tristate "QCOM General Serial Bus Interface" depends on ARCH_QCOM || COMPILE_TEST diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index 5ba70c6fb2d9..145d69eeea42 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -50,5 +50,6 @@ obj-$(CONFIG_QCOM_GLINK) += glink_probe.o obj-$(CONFIG_MSM_GLINK_SSR) += msm_glink_ssr.o obj-$(CONFIG_QTI_DDR_STATS_LOG) += ddr_stats.o obj-$(CONFIG_QTI_SYSTEM_PM) += system_pm.o +obj-$(CONFIG_MSM_SPSS_UTILS) += spss_utils.o obj-$(CONFIG_MSM_IDLE_STATS) += lpm-stats.o obj-$(CONFIG_QTI_RPM_STATS_LOG) += rpmh_master_stat.o diff --git a/drivers/soc/qcom/spss_utils.c b/drivers/soc/qcom/spss_utils.c new file mode 100644 index 000000000000..d0506a7d0647 --- /dev/null +++ b/drivers/soc/qcom/spss_utils.c @@ -0,0 +1,1062 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved. + */ + +/* + * Secure-Processor-SubSystem (SPSS) utilities. + * + * This driver provides utilities for the Secure Processor (SP). + * + * The SP daemon needs to load different SPSS images based on: + * + * 1. Test/Production key used to sign the SPSS image (read fuses). + * 2. SPSS HW version (selected via Device Tree). + * + */ + +#define pr_fmt(fmt) "spss_utils [%s]: " fmt, __func__ + +#include /* min() */ +#include /* MODULE_LICENSE */ +#include /* class_create() */ +#include /* kzalloc() */ +#include /* file_operations */ +#include /* cdev_add() */ +#include /* EINVAL, ETIMEDOUT */ +#include /* pr_err() */ +#include /* BIT(x) */ +#include /* platform_driver_register() */ +#include /* of_property_count_strings() */ +#include /* of_address_to_resource() */ +#include /* ioremap_nocache() */ +#include +#include /* SZ_4K */ +#include /* copy_from_user() */ + +#include +#include +#include /* VMID_HLOS */ + +#include /* ioctl() */ +#include /* IOCTL to user space */ + +/* driver name */ +#define DEVICE_NAME "spss_utils" + +enum spss_firmware_type { + SPSS_FW_TYPE_DEV = 'd', + SPSS_FW_TYPE_TEST = 't', + SPSS_FW_TYPE_PROD = 'p', + SPSS_FW_TYPE_NONE = 'z', +}; + +static enum spss_firmware_type firmware_type = SPSS_FW_TYPE_TEST; +static const char *dev_firmware_name; +static const char *test_firmware_name; +static const char *prod_firmware_name; +static const char *none_firmware_name = "nospss"; +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; +static bool is_iar_active; + +#define CMAC_SIZE_IN_BYTES (128/8) /* 128 bit = 16 bytes */ +#define CMAC_SIZE_IN_DWORDS (CMAC_SIZE_IN_BYTES/sizeof(u32)) /* 4 dwords */ + +/* Asym , Crypt , Keym */ +#define NUM_UEFI_APPS 3 + +static u32 pil_addr; +static u32 pil_size; +static u32 cmac_buf[CMAC_SIZE_IN_DWORDS]; /* saved cmac */ +static u32 pbl_cmac_buf[CMAC_SIZE_IN_DWORDS]; /* pbl cmac */ + +static u32 calc_apps_cmac[NUM_UEFI_APPS][CMAC_SIZE_IN_DWORDS]; +static u32 saved_apps_cmac[NUM_UEFI_APPS][CMAC_SIZE_IN_DWORDS]; + +#define FW_AND_APPS_CMAC_SIZE \ + (CMAC_SIZE_IN_DWORDS + NUM_UEFI_APPS*CMAC_SIZE_IN_DWORDS) + +static u32 iar_state; +static bool is_iar_enabled; + +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; + +/* static functions declaration */ +static int spss_set_fw_cmac(u32 *cmac, size_t cmac_size); +static int spss_get_pbl_and_apps_calc_cmac(void); + +static int spss_get_saved_uefi_apps_cmac(void); +static int spss_set_saved_uefi_apps_cmac(void); + +/*==========================================================================*/ +/* Device Sysfs */ +/*==========================================================================*/ + +static ssize_t firmware_name_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + 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 + ret = scnprintf(buf, PAGE_SIZE, "%s\n", firmware_name); + + return ret; +} + +static DEVICE_ATTR_RO(firmware_name); + +static ssize_t test_fuse_state_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + 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"); + break; + case SPSS_FW_TYPE_TEST: + ret = scnprintf(buf, PAGE_SIZE, "%s", "test"); + break; + case SPSS_FW_TYPE_PROD: + ret = scnprintf(buf, PAGE_SIZE, "%s", "prod"); + break; + default: + return -EINVAL; + } + + return ret; +} + +static DEVICE_ATTR_RO(test_fuse_state); + +static ssize_t spss_debug_reg_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int ret; + void __iomem *spss_debug_reg = NULL; + u32 val1, val2; + + 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); + + if (!spss_debug_reg) { + pr_err("can't map debug reg addr\n"); + return -EINVAL; + } + + val1 = readl_relaxed(spss_debug_reg); + val2 = readl_relaxed(((char *) spss_debug_reg) + sizeof(u32)); + + ret = scnprintf(buf, PAGE_SIZE, "val1 [0x%x] val2 [0x%x]\n", + val1, val2); + + iounmap(spss_debug_reg); + + return ret; +} + +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%08x,0x%08x,0x%08x,0x%08x\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_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%08x,0x%08x,0x%08x,0x%08x\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 ssize_t apps_cmac_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + if (!dev || !attr || !buf) { + pr_err("invalid param.\n"); + return -EINVAL; + } + + memcpy(buf, calc_apps_cmac, sizeof(calc_apps_cmac)); + + return sizeof(calc_apps_cmac); +} + +static DEVICE_ATTR_RO(apps_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"); + 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"); + 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"); + 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_cmac); + if (ret < 0) { + pr_err("failed to create sysfs file for pbl_cmac.\n"); + goto remove_iar_enabled; + } + + ret = device_create_file(dev, &dev_attr_apps_cmac); + if (ret < 0) { + pr_err("failed to create sysfs file for apps_cmac.\n"); + goto remove_pbl_cmac; + } + + + return 0; + +remove_pbl_cmac: + device_remove_file(dev, &dev_attr_pbl_cmac); +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; + u32 i = 0; + /* Saved cmacs of spu firmware and UEFI loaded spu apps */ + u32 fw_and_apps_cmacs[FW_AND_APPS_CMAC_SIZE]; + + 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(fw_and_apps_cmacs)) { + pr_err("cmd [0x%x] invalid size [0x%x]\n", cmd, size); + return -EINVAL; + } + + /* spdaemon uses this ioctl only when IAR is active */ + is_iar_active = true; + + memcpy(fw_and_apps_cmacs, data, sizeof(fw_and_apps_cmacs)); + memcpy(cmac_buf, fw_and_apps_cmacs, sizeof(cmac_buf)); + + for (i = 0; i < NUM_UEFI_APPS; ++i) { + int x = (i+1)*CMAC_SIZE_IN_DWORDS; + + memcpy(saved_apps_cmac[i], + fw_and_apps_cmacs + x, + CMAC_SIZE_IN_BYTES); + } + + /* + * SPSS is loaded now by UEFI, + * so IAR callback is not being called on powerup by PIL. + * therefore read the spu pbl fw cmac and apps cmac from ioctl. + * The callback shall be called on spss SSR. + */ + pr_debug("read pbl cmac from shared memory\n"); + spss_set_fw_cmac(cmac_buf, sizeof(cmac_buf)); + spss_set_saved_uefi_apps_cmac(); + spss_get_pbl_and_apps_calc_cmac(); + spss_get_saved_uefi_apps_cmac(); + 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; +} + +/*==========================================================================*/ +/* Device Tree */ +/*==========================================================================*/ + +/** + * spss_parse_dt() - Parse Device Tree info. + */ +static int spss_parse_dt(struct device_node *node) +{ + int ret; + u32 spss_fuse1_addr = 0; + u32 spss_fuse1_bit = 0; + u32 spss_fuse1_mask = 0; + void __iomem *spss_fuse1_reg = NULL; + u32 spss_fuse2_addr = 0; + 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; + struct device_node *np; + struct resource r; + u32 val1 = 0; + u32 val2 = 0; + void __iomem *spss_emul_type_reg = NULL; + u32 spss_emul_type_val = 0; + + ret = of_property_read_string(node, "qcom,spss-dev-firmware-name", + &dev_firmware_name); + if (ret < 0) { + pr_err("can't get dev fw name\n"); + return -EINVAL; + } + + ret = of_property_read_string(node, "qcom,spss-test-firmware-name", + &test_firmware_name); + if (ret < 0) { + pr_err("can't get test fw name\n"); + return -EINVAL; + } + + ret = of_property_read_string(node, "qcom,spss-prod-firmware-name", + &prod_firmware_name); + if (ret < 0) { + pr_err("can't get prod fw name\n"); + return -EINVAL; + } + + ret = of_property_read_u32(node, "qcom,spss-fuse1-addr", + &spss_fuse1_addr); + if (ret < 0) { + pr_err("can't get fuse1 addr\n"); + return -EINVAL; + } + + ret = of_property_read_u32(node, "qcom,spss-fuse2-addr", + &spss_fuse2_addr); + if (ret < 0) { + pr_err("can't get fuse2 addr\n"); + return -EINVAL; + } + + ret = of_property_read_u32(node, "qcom,spss-fuse1-bit", + &spss_fuse1_bit); + if (ret < 0) { + pr_err("can't get fuse1 bit\n"); + return -EINVAL; + } + + ret = of_property_read_u32(node, "qcom,spss-fuse2-bit", + &spss_fuse2_bit); + if (ret < 0) { + pr_err("can't get fuse2 bit\n"); + return -EINVAL; + } + + + spss_fuse1_mask = BIT(spss_fuse1_bit); + spss_fuse2_mask = BIT(spss_fuse2_bit); + + pr_debug("spss fuse1 addr [0x%x] bit [%d]\n", + (int) spss_fuse1_addr, (int) spss_fuse1_bit); + pr_debug("spss fuse2 addr [0x%x] bit [%d]\n", + (int) spss_fuse2_addr, (int) spss_fuse2_bit); + + spss_fuse1_reg = ioremap_nocache(spss_fuse1_addr, sizeof(u32)); + + if (!spss_fuse1_reg) { + pr_err("can't map fuse1 addr\n"); + return -EINVAL; + } + + spss_fuse2_reg = ioremap_nocache(spss_fuse2_addr, sizeof(u32)); + + if (!spss_fuse2_reg) { + iounmap(spss_fuse1_reg); + pr_err("can't map fuse2 addr\n"); + return -EINVAL; + } + + val1 = readl_relaxed(spss_fuse1_reg); + val2 = readl_relaxed(spss_fuse2_reg); + + pr_debug("spss fuse1 value [0x%08x]\n", (int) val1); + pr_debug("spss fuse2 value [0x%08x]\n", (int) val2); + + pr_debug("spss fuse1 mask [0x%08x]\n", (int) spss_fuse1_mask); + pr_debug("spss fuse2 mask [0x%08x]\n", (int) spss_fuse2_mask); + + /** + * Set firmware_type based on fuses: + * SPSS_CONFIG_MODE 11: dev + * SPSS_CONFIG_MODE 01 or 10: test + * SPSS_CONFIG_MODE 00: prod + */ + if ((val1 & spss_fuse1_mask) && (val2 & spss_fuse2_mask)) + firmware_type = SPSS_FW_TYPE_DEV; + else if ((val1 & spss_fuse1_mask) || (val2 & spss_fuse2_mask)) + firmware_type = SPSS_FW_TYPE_TEST; + else + firmware_type = SPSS_FW_TYPE_PROD; + + iounmap(spss_fuse1_reg); + iounmap(spss_fuse2_reg); + + ret = of_property_read_u32(node, "qcom,spss-debug-reg-addr", + &spss_debug_reg_addr); + if (ret < 0) { + pr_err("can't get debug regs addr\n"); + return ret; + } + + ret = of_property_read_u32(node, "qcom,spss-emul-type-reg-addr", + &spss_emul_type_reg_addr); + if (ret < 0) { + pr_err("can't get spss-emulation-type-reg addr\n"); + return -EINVAL; + } + + spss_emul_type_reg = ioremap_nocache(spss_emul_type_reg_addr, + sizeof(u32)); + if (!spss_emul_type_reg) { + pr_err("can't map soc-emulation-type reg addr\n"); + return -EINVAL; + } + + spss_emul_type_val = readl_relaxed(spss_emul_type_reg); + + pr_debug("spss_emul_type value [0x%08x]\n", (int)spss_emul_type_val); + if ((spss_emul_type_val & SPU_EMULATUION) && + !(spss_emul_type_val & SPU_PRESENT_IN_EMULATION)) { + /* for some emulation platforms SPSS is not present */ + firmware_type = SPSS_FW_TYPE_NONE; + } + iounmap(spss_emul_type_reg); + + /* PIL-SPSS area */ + np = of_parse_phandle(node, "pil-mem", 0); + if (!np) { + pr_err("no pil-mem entry, check pil-addr\n"); + ret = of_property_read_u32(node, "qcom,pil-addr", + &pil_addr); + if (ret < 0) { + pr_err("can't get pil_addr\n"); + return -EFAULT; + } + } else { + ret = of_address_to_resource(np, 0, &r); + of_node_put(np); + if (ret) + return ret; + pil_addr = (u32)r.start; + } + + 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_debug("pil_addr [0x%08x].\n", pil_addr); + pr_debug("pil_size [0x%08x].\n", pil_size); + + /* cmac buffer after spss firmware end */ + cmac_mem_addr = pil_addr + pil_size; + pr_info("iar_buf_addr [0x%08x].\n", cmac_mem_addr); + + 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_fuse4_bit) & 0x07; + + iar_state = val1; + + pr_info("iar_state [%d]\n", iar_state); + + return 0; +} + +static int spss_set_fw_cmac(u32 *cmac, size_t cmac_size) +{ + 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; + } + } + + 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; + + return 0; +} + +static int spss_get_pbl_and_apps_calc_cmac(void) +{ + u8 __iomem *reg = NULL; + int i, j; + u32 val; + + if (cmac_mem == NULL) + return -EFAULT; + + reg = cmac_mem; /* IAR buffer base */ + reg += CMAC_SIZE_IN_BYTES; /* skip the saved cmac */ + pr_debug("reg [%pK]\n", reg); + + /* get pbl fw cmac from ddr */ + for (i = 0; i < CMAC_SIZE_IN_DWORDS; i++) { + val = readl_relaxed(reg); + pbl_cmac_buf[i] = val; + reg += sizeof(u32); + } + reg += CMAC_SIZE_IN_BYTES; /* skip the saved cmac */ + + pr_debug("pbl_cmac_buf : 0x%08x,0x%08x,0x%08x,0x%08x\n", + pbl_cmac_buf[0], pbl_cmac_buf[1], + pbl_cmac_buf[2], pbl_cmac_buf[3]); + + /* get apps cmac from ddr */ + for (j = 0; j < NUM_UEFI_APPS; j++) { + for (i = 0; i < CMAC_SIZE_IN_DWORDS; i++) { + val = readl_relaxed(reg); + calc_apps_cmac[j][i] = val; + reg += sizeof(u32); + } + reg += CMAC_SIZE_IN_BYTES; /* skip the saved cmac */ + + pr_debug("app [%d] cmac : 0x%08x,0x%08x,0x%08x,0x%08x\n", j, + calc_apps_cmac[j][0], calc_apps_cmac[j][1], + calc_apps_cmac[j][2], calc_apps_cmac[j][3]); + } + + return 0; +} + +static int spss_get_saved_uefi_apps_cmac(void) +{ + u8 __iomem *reg = NULL; + int i, j; + u32 val; + + if (cmac_mem == NULL) + return -EFAULT; + + reg = cmac_mem; /* IAR buffer base */ + reg += (2*CMAC_SIZE_IN_BYTES); /* skip the saved and calc fw cmac */ + pr_debug("reg [%pK]\n", reg); + + /* get saved apps cmac from ddr - were written by UEFI spss driver */ + for (j = 0; j < NUM_UEFI_APPS; j++) { + for (i = 0; i < CMAC_SIZE_IN_DWORDS; i++) { + val = readl_relaxed(reg); + saved_apps_cmac[j][i] = val; + reg += sizeof(u32); + } + reg += CMAC_SIZE_IN_BYTES; /* skip the calc cmac */ + + pr_debug("app[%d] saved cmac: 0x%08x,0x%08x,0x%08x,0x%08x\n", + j, + saved_apps_cmac[j][0], saved_apps_cmac[j][1], + saved_apps_cmac[j][2], saved_apps_cmac[j][3]); + } + + return 0; +} + +static int spss_set_saved_uefi_apps_cmac(void) +{ + u8 __iomem *reg = NULL; + int i, j; + u32 val; + + if (cmac_mem == NULL) + return -EFAULT; + + reg = cmac_mem; /* IAR buffer base */ + reg += (2*CMAC_SIZE_IN_BYTES); /* skip the saved and calc fw cmac */ + pr_debug("reg [%pK]\n", reg); + + /* get saved apps cmac from ddr - were written by UEFI spss driver */ + for (j = 0; j < NUM_UEFI_APPS; j++) { + for (i = 0; i < CMAC_SIZE_IN_DWORDS; i++) { + val = saved_apps_cmac[j][i]; + writel_relaxed(val, reg); + reg += sizeof(u32); + } + reg += CMAC_SIZE_IN_BYTES; /* skip the calc app cmac */ + + pr_debug("app[%d] saved cmac: 0x%08x,0x%08x,0x%08x,0x%08x\n", + j, + saved_apps_cmac[j][0], saved_apps_cmac[j][1], + saved_apps_cmac[j][2], saved_apps_cmac[j][3]); + } + + return 0; +} + +static int spss_utils_iar_callback(struct notifier_block *nb, + unsigned long code, + void *data) +{ + /* do nothing if IAR is not active */ + if (!is_iar_active) + return NOTIFY_OK; + + 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_and_apps_calc_cmac(); + break; + case SUBSYS_BEFORE_AUTH_AND_RESET: + pr_debug("[SUBSYS_BEFORE_AUTH_AND_RESET] event.\n"); + /* Called on SSR as spss firmware is loaded by UEFI */ + spss_set_fw_cmac(cmac_buf, sizeof(cmac_buf)); + spss_set_saved_uefi_apps_cmac(); + break; + default: + pr_err("unknown code [0x%x] .\n", (int) code); + break; + + } + + return NOTIFY_OK; +} + +/** + * spss_probe() - initialization sequence + */ +static int spss_probe(struct platform_device *pdev) +{ + int ret = 0; + 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); + if (ret < 0) + return ret; + + switch (firmware_type) { + case SPSS_FW_TYPE_DEV: + firmware_name = dev_firmware_name; + break; + case SPSS_FW_TYPE_TEST: + firmware_name = test_firmware_name; + break; + case SPSS_FW_TYPE_PROD: + firmware_name = prod_firmware_name; + break; + case SPSS_FW_TYPE_NONE: + firmware_name = none_firmware_name; + break; + default: + pr_err("invalid firmware type %d, sysfs entry not created\n", + firmware_type); + return -EINVAL; + } + + ret = subsystem_set_fwname("spss", firmware_name); + if (ret < 0) { + pr_err("fail to set fw name\n"); + 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; +} + +static const struct of_device_id spss_match_table[] = { + { .compatible = "qcom,spss-utils", }, + { }, +}; + +static struct platform_driver spss_driver = { + .probe = spss_probe, + .driver = { + .name = DEVICE_NAME, + .of_match_table = of_match_ptr(spss_match_table), + }, +}; + +/*==========================================================================*/ +/* Driver Init/Exit */ +/*==========================================================================*/ +static int __init spss_init(void) +{ + int ret = 0; + + ret = platform_driver_register(&spss_driver); + if (ret) + pr_err("register platform driver failed, ret [%d]\n", ret); + + return ret; +} +late_initcall(spss_init); /* start after PIL driver */ + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Secure Processor Utilities"); diff --git a/include/uapi/linux/spss_utils.h b/include/uapi/linux/spss_utils.h new file mode 100644 index 000000000000..a5ff6eddc909 --- /dev/null +++ b/include/uapi/linux/spss_utils.h @@ -0,0 +1,30 @@ +/* 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 /* uint32_t, bool */ +#include /* 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' +#define NUM_SPU_UEFI_APPS 3 + +struct spss_ioc_set_fw_cmac { + uint32_t cmac[4]; + uint32_t app_cmacs[NUM_SPU_UEFI_APPS][4]; +} __packed; + +#define SPSS_IOC_SET_FW_CMAC \ + _IOWR(SPSS_IOC_MAGIC, 1, struct spss_ioc_set_fw_cmac) + +#endif /* _UAPI_SPSS_UTILS_H_ */