12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346 |
- // SPDX-License-Identifier: GPL-2.0-only
- /*
- * Copyright (c) 2016-2021, The Linux Foundation. All rights reserved.
- * Copyright (c) 2022 Qualcomm Innovation Center, Inc. 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 <linux/kernel.h> /* min() */
- #include <linux/module.h> /* MODULE_LICENSE */
- #include <linux/device.h> /* class_create() */
- #include <linux/slab.h> /* kzalloc() */
- #include <linux/fs.h> /* file_operations */
- #include <linux/cdev.h> /* cdev_add() */
- #include <linux/errno.h> /* EINVAL, ETIMEDOUT */
- #include <linux/printk.h> /* pr_err() */
- #include <linux/bitops.h> /* BIT(x) */
- #include <linux/platform_device.h> /* platform_driver_register() */
- #include <linux/of.h> /* of_property_count_strings() */
- #include <linux/of_address.h> /* of_address_to_resource() */
- #include <linux/io.h> /* ioremap() */
- #include <linux/notifier.h>
- #include <linux/sizes.h> /* SZ_4K */
- #include <linux/uaccess.h> /* copy_from_user() */
- #include <linux/completion.h> /* wait_for_completion_timeout() */
- #include <linux/reboot.h> /* kernel_restart() */
- #include <linux/remoteproc.h>
- #include <linux/remoteproc/qcom_rproc.h>
- #include <linux/remoteproc/qcom_spss.h>
- #include <soc/qcom/secure_buffer.h> /* VMID_HLOS */
- #include <uapi/linux/ioctl.h> /* ioctl() */
- #include <linux/spss_utils.h> /* 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;
- /* SP_SCSR_MBn_SP2CL_GPm(n,m) */
- static u32 spss_debug_reg_addr; /*SP_SCSR_MB0_SP2CL_GP0*/
- static u32 spss_debug_reg_addr1; /*SP_SCSR_MB1_SP2CL_GP0*/
- static u32 spss_debug_reg_addr3; /*SP_SCSR_MB3_SP2CL_GP0*/
- 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;
- static bool is_ssr_disabled;
- #define CMAC_SIZE_IN_BYTES (128/8) /* 128 bit = 16 bytes */
- #define CMAC_SIZE_IN_DWORDS (CMAC_SIZE_IN_BYTES/sizeof(u32)) /* 4 dwords */
- // MCP code size register holds size divided by a factor
- // To get the actual size, need to multiply by the same factor
- #define MCP_SIZE_MUL_FACTOR (4)
- static u32 pil_addr;
- static u32 pil_size;
- /*
- * The saved fw cmac is stored in file in IAR-DB.
- * It is provided via ioctl from user space spu service.
- */
- static u32 saved_fw_cmac[CMAC_SIZE_IN_DWORDS]; /* saved fw cmac */
- /*
- * The calculated fw cmac is calculated by SPU PBL.
- * It is read from shared memory and provided back to user space service
- * via device attribute.
- */
- static u32 calc_fw_cmac[CMAC_SIZE_IN_DWORDS]; /* calculated pbl fw cmac */
- /*
- * The saved apps cmac is stored in file in IAR-DB.
- * It is provided via ioctl from user space spu service.
- */
- static u32 saved_apps_cmac[MAX_SPU_UEFI_APPS][CMAC_SIZE_IN_DWORDS];
- /*
- * The calculated apps cmac is calculated by SPU firmware.
- * It is read from shared memory and provided back to user space service
- * via device attribute.
- */
- static u32 calc_apps_cmac[MAX_SPU_UEFI_APPS][CMAC_SIZE_IN_DWORDS];
- static void __iomem *cmac_mem;
- static phys_addr_t cmac_mem_addr;
- #define CMAC_MEM_SIZE SZ_4K /* XPU align to 4KB */
- #define SPSS_BASE_ADDR_MASK 0xFFFF0000
- #define SPSS_RMB_CODE_SIZE_REG_OFFSET 0x1008
- #define SPU_EMULATUION (BIT(0) | BIT(1))
- #define SPU_PRESENT_IN_EMULATION BIT(0)
- /* IOCTL max request size is 1KB */
- #define MAX_IOCTL_REQ_SIZE 1024
- /* Events notification */
- static struct completion spss_events[SPSS_NUM_EVENTS];
- static bool spss_events_signaled[SPSS_NUM_EVENTS];
- /* Protect from ioctl signal func called by multiple-proc at the same time */
- static struct mutex event_lock;
- /**
- * 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_saved_fw_cmac(u32 *cmac, size_t cmac_size);
- static int spss_set_saved_uefi_apps_cmac(void);
- static int spss_get_fw_calc_cmac(void);
- static int spss_get_apps_calc_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 = 0;
- void __iomem *spss_debug_reg = NULL;
- void __iomem *spss_debug_reg1 = NULL;
- void __iomem *spss_debug_reg3 = NULL;
- u32 val1, val2, val3, val4, val7, val8;
- if (!dev || !attr || !buf) {
- pr_err("invalid param.\n");
- return -EINVAL;
- }
- pr_debug("spss_debug_reg_addr [0x%x].\n", spss_debug_reg_addr);
- pr_debug("spss_debug_reg_addr1 [0x%x].\n", spss_debug_reg_addr1);
- pr_debug("spss_debug_reg_addr3 [0x%x].\n", spss_debug_reg_addr3);
- spss_debug_reg = ioremap(spss_debug_reg_addr, sizeof(u32)*2);
- spss_debug_reg1 = ioremap(spss_debug_reg_addr1, sizeof(u32)*2);
- spss_debug_reg3 = ioremap(spss_debug_reg_addr3, sizeof(u32)*2);
- if (!spss_debug_reg) {
- pr_err("can't map SPSS debug reg addr\n");
- goto unmap_reg;
- }
- if (!spss_debug_reg1) {
- pr_err("can't map SPSS debug reg addr1\n");
- goto unmap_reg1;
- }
- if (!spss_debug_reg3) {
- pr_err("can't map SPSS debug reg addr3\n");
- goto unmap_reg3;
- }
- val1 = readl_relaxed(spss_debug_reg);
- val2 = readl_relaxed(((char *) spss_debug_reg) + sizeof(u32));
- val3 = readl_relaxed(spss_debug_reg1);
- val4 = readl_relaxed(((char *) spss_debug_reg1) + sizeof(u32));
- val7 = readl_relaxed(spss_debug_reg3);
- val8 = readl_relaxed(((char *) spss_debug_reg3) + sizeof(u32));
- ret = scnprintf(buf, PAGE_SIZE,
- "MB0: val1[0x%x] val2[0x%x],\n MB1: val3[0x%x] val4[0x%x],\n MB3: val7[0x%x] val8[0x%x]\n",
- val1, val2, val3, val4, val7, val8);
- unmap_reg3:
- iounmap(spss_debug_reg3);
- unmap_reg1:
- iounmap(spss_debug_reg1);
- unmap_reg:
- iounmap(spss_debug_reg);
- return ret;
- }
- static DEVICE_ATTR_RO(spss_debug_reg);
- static ssize_t calc_fw_cmac_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
- {
- if (!dev || !attr || !buf) {
- pr_err("invalid param.\n");
- return -EINVAL;
- }
- /* first make sure the calc cmac is updated */
- spss_get_fw_calc_cmac();
- memcpy(buf, calc_fw_cmac, sizeof(calc_fw_cmac));
- return sizeof(calc_fw_cmac);
- }
- static DEVICE_ATTR_RO(calc_fw_cmac);
- static ssize_t calc_apps_cmac_show(struct device *dev,
- struct device_attribute *attr, char *buf)
- {
- if (!dev || !attr || !buf) {
- pr_err("invalid param.\n");
- return -EINVAL;
- }
- /* first make sure the calc cmac is updated */
- spss_get_apps_calc_cmac();
- memcpy(buf, calc_apps_cmac, sizeof(calc_apps_cmac));
- return sizeof(calc_apps_cmac);
- }
- static DEVICE_ATTR_RO(calc_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_calc_fw_cmac);
- if (ret < 0) {
- pr_err("failed to create sysfs file for calc_fw_cmac.\n");
- goto remove_spss_debug_reg;
- }
- ret = device_create_file(dev, &dev_attr_calc_apps_cmac);
- if (ret < 0) {
- pr_err("failed to create sysfs file for calc_apps_cmac.\n");
- goto remove_calc_fw_cmac;
- }
- return 0;
- remove_calc_fw_cmac:
- device_remove_file(dev, &dev_attr_calc_fw_cmac);
- 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;
- }
- static void spss_destroy_sysfs(struct device *dev)
- {
- device_remove_file(dev, &dev_attr_calc_apps_cmac);
- device_remove_file(dev, &dev_attr_calc_fw_cmac);
- device_remove_file(dev, &dev_attr_spss_debug_reg);
- device_remove_file(dev, &dev_attr_test_fuse_state);
- device_remove_file(dev, &dev_attr_firmware_name);
- }
- /*==========================================================================*/
- /* IOCTL */
- /*==========================================================================*/
- static int spss_wait_for_event(struct spss_ioc_wait_for_event *req)
- {
- int ret;
- uint32_t event_id;
- uint32_t timeout_sec;
- long timeleft = 1;
- event_id = req->event_id;
- timeout_sec = req->timeout_sec;
- if (event_id >= SPSS_NUM_EVENTS) {
- pr_err("event_id [%d] invalid\n", event_id);
- return -EINVAL;
- }
- pr_debug("wait for event [%d], timeout_sec [%d]\n",
- event_id, timeout_sec);
- if (timeout_sec) {
- unsigned long jiffies = 0;
- jiffies = msecs_to_jiffies(timeout_sec*1000);
- timeleft = wait_for_completion_interruptible_timeout(
- &spss_events[event_id], jiffies);
- ret = timeleft;
- } else {
- ret = wait_for_completion_interruptible(
- &spss_events[event_id]);
- }
- if (timeleft == 0) {
- pr_err("wait for event [%d] timeout [%d] sec expired\n",
- event_id, timeout_sec);
- req->status = EVENT_STATUS_TIMEOUT;
- } else if (ret < 0) {
- pr_err("wait for event [%d] interrupted. ret [%d]\n",
- event_id, ret);
- req->status = EVENT_STATUS_ABORTED;
- if (ret == -ERESTARTSYS) /* handle LPM event */
- return ret;
- } else {
- pr_debug("wait for event [%d] completed.\n", event_id);
- req->status = EVENT_STATUS_SIGNALED;
- }
- return 0;
- }
- static int spss_signal_event(struct spss_ioc_signal_event *req)
- {
- uint32_t event_id;
- mutex_lock(&event_lock);
- event_id = req->event_id;
- if (event_id >= SPSS_NUM_EVENTS) {
- pr_err("event_id [%d] invalid\n", event_id);
- mutex_unlock(&event_lock);
- return -EINVAL;
- }
- if (spss_events_signaled[event_id]) {
- pr_err("event_id [%d] already signaled\n", event_id);
- mutex_unlock(&event_lock);
- return -EINVAL;
- }
- pr_debug("signal event [%d]\n", event_id);
- complete_all(&spss_events[event_id]);
- req->status = EVENT_STATUS_SIGNALED;
- spss_events_signaled[event_id] = true;
- mutex_unlock(&event_lock);
- return 0;
- }
- static int spss_is_event_signaled(struct spss_ioc_is_signaled *req)
- {
- uint32_t event_id;
- mutex_lock(&event_lock);
- event_id = req->event_id;
- if (event_id >= SPSS_NUM_EVENTS) {
- pr_err("event_id [%d] invalid\n", event_id);
- mutex_unlock(&event_lock);
- return -EINVAL;
- }
- if (spss_events_signaled[event_id])
- req->status = EVENT_STATUS_SIGNALED;
- else
- req->status = EVENT_STATUS_NOT_SIGNALED;
- mutex_unlock(&event_lock);
- return 0;
- }
- static int spss_handle_set_fw_and_apps_cmac(struct spss_ioc_set_fw_and_apps_cmac *req)
- {
- int ret = 0;
- u32 cmac_buf_size = req->cmac_buf_size;
- void __user *cmac_buf_ptr = u64_to_user_ptr(req->cmac_buf_ptr);
- u32 num_of_cmacs = req->num_of_cmacs;
- /* Saved cmacs of spu firmware and UEFI loaded spu apps */
- u32 fw_and_apps_cmacs[1+MAX_SPU_UEFI_APPS][CMAC_SIZE_IN_DWORDS];
- pr_debug("cmac_buf_size [0x%x].\n", (int) req->cmac_buf_size);
- pr_debug("cmac_buf_ptr [0x%x].\n", (int) req->cmac_buf_ptr);
- pr_debug("num_of_cmacs [0x%x].\n", (int) req->num_of_cmacs);
- if (cmac_buf_size != sizeof(fw_and_apps_cmacs)) {
- pr_err("cmac_buf_size [0x%x] invalid.\n", cmac_buf_size);
- return -EINVAL;
- }
- if (num_of_cmacs > (u32)(MAX_SPU_UEFI_APPS+1)) {
- pr_err("num_of_cmacs [0x%x] invalid.\n", num_of_cmacs);
- return -EINVAL;
- }
- /* copy the saved cmacs from user buffer to loacl variable */
- ret = copy_from_user(fw_and_apps_cmacs, cmac_buf_ptr, cmac_buf_size);
- if (ret < 0) {
- pr_err("copy_from_user() from cmac_buf_ptr failed.\n");
- return -EFAULT;
- }
- /* store the saved fw cmac */
- memcpy(saved_fw_cmac, fw_and_apps_cmacs[0],
- sizeof(saved_fw_cmac));
- pr_debug("saved fw cmac: 0x%08x,0x%08x,0x%08x,0x%08x\n",
- saved_fw_cmac[0], saved_fw_cmac[1],
- saved_fw_cmac[2], saved_fw_cmac[3]);
- /* store the saved apps cmac */
- memcpy(saved_apps_cmac, &fw_and_apps_cmacs[1][0],
- sizeof(saved_apps_cmac));
- /*
- * SPSS is loaded now by UEFI,
- * so PIL-IAR-callback is not being called on power-up by PIL.
- * therefore get the saved spu fw cmac and apps cmac from ioctl.
- * The PIL-IAR-callback shall be called on spss SSR.
- * The saved cmacs are used on QCOM_SSR_AFTER_POWERUP event !
- */
- spss_set_saved_fw_cmac(saved_fw_cmac, sizeof(saved_fw_cmac));
- spss_set_saved_uefi_apps_cmac();
- pr_debug("completed ok\n");
- return 0;
- }
- static long spss_utils_ioctl(struct file *file,
- unsigned int cmd, unsigned long arg)
- {
- int ret;
- void *buf = (void *) arg;
- uint8_t data[MAX_IOCTL_REQ_SIZE] = {0};
- size_t size = 0;
- void *req = (void *) data;
- 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] size [0x%x]\n",
- cmd, size);
- return -EFAULT;
- }
- }
- switch (cmd) {
- case SPSS_IOC_SET_FW_AND_APPS_CMAC:
- pr_debug("ioctl [SPSS_IOC_SET_FW_AND_APPS_CMAC]\n");
- /* spdaemon uses this ioctl only when IAR is active */
- is_iar_active = true;
- if (cmac_mem == NULL) {
- cmac_mem = ioremap(cmac_mem_addr, CMAC_MEM_SIZE);
- if (!cmac_mem) {
- pr_err("can't map cmac_mem.\n");
- return -EFAULT;
- }
- }
- ret = spss_handle_set_fw_and_apps_cmac(req);
- if (ret < 0)
- return ret;
- break;
- case SPSS_IOC_WAIT_FOR_EVENT:
- /* check input params */
- if (size != sizeof(struct spss_ioc_wait_for_event)) {
- pr_err("cmd [0x%x] invalid size [0x%x]\n", cmd, size);
- return -EINVAL;
- }
- ret = spss_wait_for_event(req);
- if (ret < 0)
- return ret;
- ret = copy_to_user((void __user *)arg, data, size);
- if (ret) {
- pr_err("cmd [0x%x] copy_to_user failed - %d\n", cmd, ret);
- return ret;
- }
- break;
- case SPSS_IOC_SIGNAL_EVENT:
- /* check input params */
- if (size != sizeof(struct spss_ioc_signal_event)) {
- pr_err("cmd [0x%x] invalid size [0x%x]\n", cmd, size);
- return -EINVAL;
- }
- ret = spss_signal_event(req);
- if (ret < 0)
- return ret;
- ret = copy_to_user((void __user *)arg, data, size);
- if (ret) {
- pr_err("cmd [0x%x] copy_to_user failed - %d\n", cmd, ret);
- return ret;
- }
- break;
- case SPSS_IOC_IS_EVENT_SIGNALED:
- /* check input params */
- if (size != sizeof(struct spss_ioc_is_signaled)) {
- pr_err("cmd [0x%x] invalid size [0x%x]\n", cmd, size);
- return -EINVAL;
- }
- ret = spss_is_event_signaled(req);
- if (ret < 0)
- return ret;
- ret = copy_to_user((void __user *)arg, data, size);
- if (ret) {
- pr_err("cmd [0x%x] copy_to_user failed - %d\n", cmd, ret);
- return ret;
- }
- break;
- case SPSS_IOC_SET_SSR_STATE:
- /* check input params */
- if (size != sizeof(uint32_t)) {
- pr_err("cmd [0x%x] invalid size [0x%x]\n", cmd, size);
- return -EINVAL;
- }
- if (is_iar_active) {
- uint32_t tmp = 0;
- memcpy(&tmp, data, sizeof(tmp));
- is_ssr_disabled = (bool) tmp; /* u32 to bool */
- pr_info("SSR disabled state updated to: %d\n",
- is_ssr_disabled);
- }
- pr_info("is_iar_active [%d] is_ssr_disabled [%d].\n",
- is_iar_active, is_ssr_disabled);
- break;
- default:
- pr_err("invalid ioctl cmd [0x%x]\n", cmd);
- return -ENOIOCTLCMD;
- }
- 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;
- }
- static void spss_utils_destroy_chardev(void)
- {
- device_destroy(spss_utils_dev->driver_class, spss_utils_dev->device_no);
- class_destroy(spss_utils_dev->driver_class);
- unregister_chrdev_region(spss_utils_dev->device_no, 1);
- }
- /*==========================================================================*/
- /* Device Tree */
- /*==========================================================================*/
- /* get the ACTUAL spss PIL firmware size from spu reg */
- static int get_pil_size(phys_addr_t base_addr)
- {
- u32 spss_code_size_addr = 0;
- void __iomem *spss_code_size_reg = NULL;
- u32 pil_size = 0;
- spss_code_size_addr = base_addr + SPSS_RMB_CODE_SIZE_REG_OFFSET;
- spss_code_size_reg = ioremap(spss_code_size_addr, sizeof(u32));
- if (!spss_code_size_reg) {
- pr_err("can't map spss_code_size_addr\n");
- return -EINVAL;
- }
- pil_size = readl_relaxed(spss_code_size_reg);
- iounmap(spss_code_size_reg);
- // Multiply the value read from code size register by factor
- // to get the actual size (see MCP_SIZE_MUL_FACTOR documentation)
- pil_size *= MCP_SIZE_MUL_FACTOR;
- if (pil_size % SZ_4K) {
- pr_err("pil_size [0x%08x] is not 4K aligned.\n", pil_size);
- return -EFAULT;
- }
- return pil_size;
- }
- /**
- * 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;
- 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;
- phys_addr_t spss_regs_base_addr = 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(spss_fuse1_addr, sizeof(u32));
- if (!spss_fuse1_reg) {
- pr_err("can't map fuse1 addr\n");
- return -EINVAL;
- }
- spss_fuse2_reg = ioremap(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: prod, changed from DEV to PROD due to
- PTE configuration error in Waipio 2.1 CONFIG_MODE 11 will be used
- for production signed MCP as workaround.
- * 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_PROD;
- 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);
- pr_debug("firmware_type value [%c]\n", firmware_type);
- 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-debug-reg-addr1",
- &spss_debug_reg_addr1);
- if (ret < 0) {
- pr_err("can't get debug regs addr1\n");
- return ret;
- }
- ret = of_property_read_u32(node, "qcom,spss-debug-reg-addr3",
- &spss_debug_reg_addr3);
- if (ret < 0) {
- pr_err("can't get debug regs addr3\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(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) {
- if (spss_emul_type_val & SPU_PRESENT_IN_EMULATION) {
- firmware_type = SPSS_FW_TYPE_TEST;
- } else {
- /* for some emulation platforms SPSS is not present */
- firmware_type = SPSS_FW_TYPE_NONE;
- }
- pr_debug("remap firmware_type value [%c]\n", firmware_type);
- }
- 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;
- }
- spss_regs_base_addr =
- (spss_debug_reg_addr & SPSS_BASE_ADDR_MASK);
- ret = get_pil_size(spss_regs_base_addr);
- if (ret < 0) {
- pr_err("failed to get pil_size.\n");
- return -EFAULT;
- }
- pil_size = (u32) ret;
- 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);
- memset(saved_fw_cmac, 0xA5, sizeof(saved_fw_cmac));
- memset(saved_apps_cmac, 0xA5, sizeof(saved_apps_cmac));
- return 0;
- }
- static int spss_set_saved_fw_cmac(u32 *cmac, size_t cmac_size)
- {
- u8 __iomem *reg = NULL;
- int i;
- if (cmac_mem == NULL) {
- pr_err("invalid cmac_mem.\n");
- return -EFAULT;
- }
- reg = cmac_mem;
- for (i = 0; i < cmac_size/sizeof(u32); i++)
- writel_relaxed(cmac[i], reg + i*sizeof(u32));
- pr_debug("saved fw cmac: 0x%08x,0x%08x,0x%08x,0x%08x\n",
- cmac[0], cmac[1], cmac[2], cmac[3]);
- return 0;
- }
- static int spss_get_fw_calc_cmac(void)
- {
- u8 __iomem *reg = NULL;
- int i;
- u32 val;
- u32 cmac[CMAC_SIZE_IN_DWORDS] = {0};
- if (cmac_mem == NULL) {
- pr_err("invalid cmac_mem.\n");
- return -EFAULT;
- }
- reg = cmac_mem; /* IAR buffer base */
- reg += CMAC_SIZE_IN_BYTES; /* skip the saved cmac */
- memset(calc_fw_cmac, 0, sizeof(calc_fw_cmac));
- /* get pbl fw cmac from ddr */
- for (i = 0; i < CMAC_SIZE_IN_DWORDS; i++) {
- val = readl_relaxed(reg);
- calc_fw_cmac[i] = val;
- reg += sizeof(u32);
- }
- /* check for any pattern to mark invalid cmac */
- if (cmac[0] == cmac[1])
- return -EINVAL; /* not valid cmac */
- memcpy(calc_fw_cmac, cmac, sizeof(calc_fw_cmac));
- pr_debug("calc_fw_cmac : 0x%08x,0x%08x,0x%08x,0x%08x\n",
- calc_fw_cmac[0], calc_fw_cmac[1],
- calc_fw_cmac[2], calc_fw_cmac[3]);
- return 0;
- }
- static int spss_get_apps_calc_cmac(void)
- {
- u8 __iomem *reg = NULL;
- int i, j;
- u32 val;
- if (cmac_mem == NULL) {
- pr_err("invalid cmac_mem.\n");
- return -EFAULT;
- }
- reg = cmac_mem; /* IAR buffer base */
- reg += CMAC_SIZE_IN_BYTES; /* skip the saved fw cmac */
- reg += CMAC_SIZE_IN_BYTES; /* skip the calc fw cmac */
- reg += CMAC_SIZE_IN_BYTES; /* skip the saved 1st app cmac */
- memset(calc_apps_cmac, 0, sizeof(calc_apps_cmac));
- /* get apps cmac from ddr */
- for (j = 0; j < ARRAY_SIZE(calc_apps_cmac); j++) {
- u32 cmac[CMAC_SIZE_IN_DWORDS] = {0};
- memset(cmac, 0, sizeof(cmac));
- for (i = 0; i < ARRAY_SIZE(cmac); i++) {
- val = readl_relaxed(reg);
- cmac[i] = val;
- reg += sizeof(u32);
- }
- reg += CMAC_SIZE_IN_BYTES; /* skip the saved cmac */
- /* check for any pattern to mark end of cmacs */
- if (cmac[0] == cmac[1])
- break; /* no more valid cmacs */
- memcpy(calc_apps_cmac[j], cmac, sizeof(calc_apps_cmac[j]));
- 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_set_saved_uefi_apps_cmac(void)
- {
- u8 __iomem *reg = NULL;
- int i, j;
- u32 val;
- if (cmac_mem == NULL) {
- pr_err("invalid cmac_mem.\n");
- return -EFAULT;
- }
- reg = cmac_mem; /* IAR buffer base */
- reg += (2*CMAC_SIZE_IN_BYTES); /* skip the saved and calc fw cmac */
- /* get saved apps cmac from ddr - were written by UEFI spss driver */
- for (j = 0; j < MAX_SPU_UEFI_APPS; j++) {
- if (saved_apps_cmac[j][0] == saved_apps_cmac[j][1])
- break; /* no more cmacs */
- 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_rproc_callback(struct notifier_block *nb,
- unsigned long code,
- void *data)
- {
- int i, event_id;
- switch (code) {
- case QCOM_SSR_BEFORE_SHUTDOWN:
- pr_debug("[QCOM_SSR_BEFORE_SHUTDOWN] event.\n");
- mutex_lock(&event_lock);
- /* Reset NVM-ready and SPU-ready events */
- for (i = SPSS_EVENT_ID_NVM_READY;
- i <= SPSS_EVENT_ID_SPU_READY; i++) {
- reinit_completion(&spss_events[i]);
- spss_events_signaled[i] = false;
- }
- mutex_unlock(&event_lock);
- pr_debug("reset spss events.\n");
- break;
- case QCOM_SSR_AFTER_SHUTDOWN:
- pr_debug("[QCOM_SSR_AFTER_SHUTDOWN] event.\n");
- mutex_lock(&event_lock);
- event_id = SPSS_EVENT_ID_SPU_POWER_DOWN;
- complete_all(&spss_events[event_id]);
- spss_events_signaled[event_id] = true;
- event_id = SPSS_EVENT_ID_SPU_POWER_UP;
- reinit_completion(&spss_events[event_id]);
- spss_events_signaled[event_id] = false;
- mutex_unlock(&event_lock);
- break;
- case QCOM_SSR_BEFORE_POWERUP:
- pr_debug("[QCOM_SSR_BEFORE_POWERUP] event.\n");
- if (is_iar_active) {
- if (is_ssr_disabled) {
- pr_warn("SPSS SSR disabled, requesting reboot\n");
- kernel_restart("SPSS SSR disabled, requesting reboot");
- } else {
- /* Called on SSR as spss firmware is loaded by UEFI */
- spss_set_saved_fw_cmac(saved_fw_cmac, sizeof(saved_fw_cmac));
- spss_set_saved_uefi_apps_cmac();
- }
- }
- break;
- case QCOM_SSR_AFTER_POWERUP:
- pr_info("QCOM_SSR_AFTER_POWERUP] event.\n");
- mutex_lock(&event_lock);
- event_id = SPSS_EVENT_ID_SPU_POWER_UP;
- complete_all(&spss_events[event_id]);
- spss_events_signaled[event_id] = true;
- event_id = SPSS_EVENT_ID_SPU_POWER_DOWN;
- reinit_completion(&spss_events[event_id]);
- spss_events_signaled[event_id] = false;
- mutex_unlock(&event_lock);
- /*
- * For IAR-DB-Recovery, read cmac regadless of is_iar_active.
- * please notice that HYP unmap this area, it is a race.
- */
- if (cmac_mem == NULL) {
- cmac_mem = ioremap(cmac_mem_addr, CMAC_MEM_SIZE);
- if (!cmac_mem) {
- pr_err("can't map cmac_mem.\n");
- return -EFAULT;
- }
- }
- spss_get_fw_calc_cmac();
- spss_get_apps_calc_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;
- int i;
- struct device_node *np = NULL;
- struct device *dev = &pdev->dev;
- struct property *prop = NULL;
- struct rproc *rproc = NULL;
- np = pdev->dev.of_node;
- spss_dev = dev;
- 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;
- }
- prop = of_find_property(np, "qcom,rproc-handle", NULL);
- if (!prop)
- return -EINVAL;
- rproc = rproc_get_by_phandle(be32_to_cpup(prop->value));
- if (!rproc)
- return -EPROBE_DEFER;
- ret = qcom_spss_set_fw_name(rproc, firmware_name);
- if (ret < 0) {
- if (ret != -EINVAL)
- pr_err("fail to set firmware name for remoteproc (%d)\n", ret);
- return -EPROBE_DEFER;
- }
- rproc_put(rproc);
- spss_utils_dev = kzalloc(sizeof(*spss_utils_dev), GFP_KERNEL);
- if (spss_utils_dev == NULL)
- return -ENOMEM;
- ret = spss_utils_create_chardev(dev);
- if (ret < 0)
- return ret;
- ret = spss_create_sysfs(dev);
- if (ret < 0)
- return ret;
- iar_nb = kzalloc(sizeof(*iar_nb), GFP_KERNEL);
- if (!iar_nb)
- return -ENOMEM;
- iar_nb->notifier_call = spss_utils_rproc_callback;
- iar_notif_handle = qcom_register_ssr_notifier("spss", iar_nb);
- if (IS_ERR_OR_NULL(iar_notif_handle)) {
- pr_err("register fail for IAR notifier\n");
- kfree(iar_nb);
- iar_notif_handle = NULL;
- iar_nb = NULL;
- }
- for (i = 0 ; i < SPSS_NUM_EVENTS; i++) {
- init_completion(&spss_events[i]);
- spss_events_signaled[i] = false;
- }
- mutex_init(&event_lock);
- is_iar_active = false;
- is_ssr_disabled = false;
- pr_info("Probe completed successfully, [%s].\n", firmware_name);
- return 0;
- }
- static int spss_remove(struct platform_device *pdev)
- {
- spss_utils_destroy_chardev();
- spss_destroy_sysfs(spss_dev);
- if (!iar_notif_handle && !iar_nb)
- qcom_unregister_ssr_notifier(iar_notif_handle, iar_nb);
- kfree(iar_nb);
- iar_nb = 0;
- kfree(spss_utils_dev);
- spss_utils_dev = 0;
- if (cmac_mem != NULL) {
- iounmap(cmac_mem);
- cmac_mem = NULL;
- }
- return 0;
- }
- static const struct of_device_id spss_match_table[] = {
- { .compatible = "qcom,spss-utils", },
- { },
- };
- static struct platform_driver spss_driver = {
- .probe = spss_probe,
- .remove = spss_remove,
- .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 */
- static void __exit spss_exit(void)
- {
- platform_driver_unregister(&spss_driver);
- }
- module_exit(spss_exit)
- MODULE_SOFTDEP("pre: qcom_spss");
- MODULE_LICENSE("GPL v2");
- MODULE_DESCRIPTION("Secure Processor Utilities");
|