diff --git a/drivers/firmware/qcom_scm-smc.c b/drivers/firmware/qcom_scm-smc.c index 6bee70b3c99e..20111d2a21c1 100644 --- a/drivers/firmware/qcom_scm-smc.c +++ b/drivers/firmware/qcom_scm-smc.c @@ -2478,6 +2478,27 @@ int __init scm_mem_protection_init_do(struct device *dev) } #endif +int __qcom_scm_ddrbw_profiler(struct device *dev, phys_addr_t in_buf, + size_t in_buf_size, phys_addr_t out_buf, size_t out_buf_size) +{ + int ret; + struct qcom_scm_desc desc = { + .svc = QCOM_SCM_SVC_INFO, + .cmd = TZ_SVC_BW_PROF_ID, + .owner = ARM_SMCCC_OWNER_SIP, + }; + + desc.args[0] = in_buf; + desc.args[1] = in_buf_size; + desc.args[2] = out_buf; + desc.args[3] = out_buf_size; + desc.arginfo = QCOM_SCM_ARGS(4, QCOM_SCM_RW, QCOM_SCM_VAL, QCOM_SCM_RW, + QCOM_SCM_VAL); + ret = qcom_scm_call(dev, &desc); + + return ret; +} + void __qcom_scm_init(void) { diff --git a/drivers/firmware/qcom_scm.c b/drivers/firmware/qcom_scm.c index 090df4156d0a..47930bbc03d2 100644 --- a/drivers/firmware/qcom_scm.c +++ b/drivers/firmware/qcom_scm.c @@ -1059,6 +1059,14 @@ int qcom_scm_qseecom_call_noretry(u32 cmd_id, struct scm_desc *desc) } EXPORT_SYMBOL(qcom_scm_qseecom_call_noretry); +int qcom_scm_ddrbw_profiler(phys_addr_t in_buf, + size_t in_buf_size, phys_addr_t out_buf, size_t out_buf_size) +{ + return __qcom_scm_ddrbw_profiler(__scm ? __scm->dev : NULL, in_buf, + in_buf_size, out_buf, out_buf_size); +} +EXPORT_SYMBOL(qcom_scm_ddrbw_profiler); + /** * qcom_scm_is_available() - Checks if SCM is available */ diff --git a/drivers/firmware/qcom_scm.h b/drivers/firmware/qcom_scm.h index 724355bc4863..a512dfab7d5f 100644 --- a/drivers/firmware/qcom_scm.h +++ b/drivers/firmware/qcom_scm.h @@ -288,6 +288,10 @@ extern int __init scm_mem_protection_init_do(struct device *dev); #endif +#define TZ_SVC_BW_PROF_ID 0x07 /* ddr profiler */ +extern int __qcom_scm_ddrbw_profiler(struct device *dev, phys_addr_t in_buf, + size_t in_buf_size, phys_addr_t out_buf, size_t out_buf_size); + /* common error codes */ #define QCOM_SCM_V2_EBUSY -12 #define QCOM_SCM_ENOMEM -5 diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 996b3f82b450..4ffeae16c408 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -499,6 +499,14 @@ config HDCP_QSEECOM via the QSEECOM Driver and also communicates with the Receiver via APIs exposed by the interface driver. +config PROFILER + tristate "Qualcomm Technologies, Inc. trustzone Communicator driver" + help + Provides a communication interface between userspace and + trustzone using Secure Channel Manager (SCM) interface. + It exposes APIs for userspace to get system profiling + information. + config WIGIG_SENSING_SPI tristate "Sensing over 60GHz using wil6210 / SPI bus" depends on SPI && WIL6210 diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index a2d845bea161..66777718f9dd 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -62,5 +62,6 @@ obj-$(CONFIG_UID_SYS_STATS) += uid_sys_stats.o obj-$(CONFIG_QSEECOM) += qseecom-mod.o qseecom-mod-y := qseecom.o qseecom-mod-$(CONFIG_COMPAT) += compat_qseecom.o +obj-$(CONFIG_PROFILER) += profiler.o obj-$(CONFIG_WIGIG_SENSING_SPI) += wigig_sensing.o obj-$(CONFIG_HISI_HIKEY_USB) += hisi_hikey_usb.o diff --git a/drivers/misc/profiler.c b/drivers/misc/profiler.c new file mode 100644 index 000000000000..bec3a979577f --- /dev/null +++ b/drivers/misc/profiler.c @@ -0,0 +1,352 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2017-2018, 2020-2021 The Linux Foundation. All rights reserved. + */ + +#define pr_fmt(fmt) "PROFILER: %s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define PROFILER_DEV "profiler" + +static struct class *driver_class; +static dev_t profiler_device_no; + +struct profiler_control { + struct device *pdev; + struct cdev cdev; +}; + +static struct profiler_control profiler; + +struct profiler_dev_handle { + bool released; + int abort; +}; + +static int bw_profiling_command(const void *req) +{ + int ret = 0; + uint32_t qseos_cmd_id = 0; + struct tz_bw_svc_resp *rsp = NULL; + size_t req_size = 0, rsp_size = 0; + struct qtee_shm bw_shm = {0}; + + rsp = &((struct tz_bw_svc_buf *)req)->bwresp; + rsp_size = sizeof(struct tz_bw_svc_resp); + req_size = ((struct tz_bw_svc_buf *)req)->req_size; + + if (!req || !rsp) { + pr_err("Invalid buffer pointer\n"); + return -EINVAL; + } + qseos_cmd_id = *(uint32_t *)req; + + ret = qtee_shmbridge_allocate_shm(PAGE_ALIGN(req_size + rsp_size), &bw_shm); + if (ret) { + ret = -ENOMEM; + pr_err("shmbridge alloc failed for in msg in release\n"); + goto out; + } + + memcpy(bw_shm.vaddr, req, req_size); + qtee_shmbridge_flush_shm_buf(&bw_shm); + + switch (qseos_cmd_id) { + case TZ_BW_SVC_START_ID: + case TZ_BW_SVC_GET_ID: + case TZ_BW_SVC_STOP_ID: + /* Send the command to TZ */ + ret = qcom_scm_ddrbw_profiler(bw_shm.paddr, req_size, + bw_shm.paddr + req_size, rsp_size); + break; + default: + pr_err("cmd_id %d is not supported.\n", + qseos_cmd_id); + ret = -EINVAL; + } /*end of switch (qsee_cmd_id) */ + + qtee_shmbridge_inv_shm_buf(&bw_shm); + memcpy(rsp, (char *)bw_shm.vaddr + req_size, rsp_size); +out: + qtee_shmbridge_free_shm(&bw_shm); + /* Verify cmd id and Check that request succeeded.*/ + if ((rsp->status != E_BW_SUCCESS) || + (qseos_cmd_id != rsp->cmd_id)) { + ret = -1; + pr_err("Status: %d,Cmd: %d\n", + rsp->status, + rsp->cmd_id); + } + return ret; +} + +static int bw_profiling_start(struct tz_bw_svc_buf *bwbuf) +{ + bwbuf->bwreq.start_req.cmd_id = TZ_BW_SVC_START_ID; + bwbuf->bwreq.start_req.version = TZ_BW_SVC_VERSION; + bwbuf->req_size = sizeof(struct tz_bw_svc_start_req); + return bw_profiling_command(bwbuf); +} + +static int bw_profiling_get(void __user *argp, struct tz_bw_svc_buf *bwbuf) +{ + int ret; + const int bufsize = sizeof(struct profiler_bw_cntrs_req) + - sizeof(uint32_t); + struct profiler_bw_cntrs_req cnt_buf; + struct qtee_shm buf_shm = {0}; + + memset(&cnt_buf, 0, sizeof(cnt_buf)); + /* Allocate memory for get buffer */ + ret = qtee_shmbridge_allocate_shm(PAGE_ALIGN(bufsize), &buf_shm); + if (ret) { + ret = -ENOMEM; + pr_err("shmbridge alloc buf failed\n"); + goto out; + } + /* Populate request data */ + bwbuf->bwreq.get_req.cmd_id = TZ_BW_SVC_GET_ID; + bwbuf->bwreq.get_req.buf_ptr = buf_shm.paddr; + bwbuf->bwreq.get_req.buf_size = bufsize; + bwbuf->req_size = sizeof(struct tz_bw_svc_get_req); + qtee_shmbridge_flush_shm_buf(&buf_shm); + ret = bw_profiling_command(bwbuf); + if (ret) { + pr_err("bw_profiling_command failed\n"); + goto out; + } + qtee_shmbridge_inv_shm_buf(&buf_shm); + memcpy(&cnt_buf, buf_shm.vaddr, bufsize); + if (copy_to_user(argp, &cnt_buf, sizeof(struct profiler_bw_cntrs_req))) + pr_err("copy_to_user failed\n"); +out: + /* Free memory for response */ + qtee_shmbridge_free_shm(&buf_shm); + return ret; +} + +static int bw_profiling_stop(struct tz_bw_svc_buf *bwbuf) +{ + bwbuf->bwreq.stop_req.cmd_id = TZ_BW_SVC_STOP_ID; + bwbuf->req_size = sizeof(struct tz_bw_svc_stop_req); + return bw_profiling_command(bwbuf); +} + +static int profiler_get_bw_info(void __user *argp) +{ + int ret = 0; + struct tz_bw_svc_buf *bwbuf = NULL; + struct profiler_bw_cntrs_req cnt_buf; + + ret = copy_from_user(&cnt_buf, argp, + sizeof(struct profiler_bw_cntrs_req)); + if (ret) + return ret; + /* Allocate memory for request */ + bwbuf = kzalloc(sizeof(struct tz_bw_svc_buf), GFP_KERNEL); + if (bwbuf == NULL) + return -ENOMEM; + + switch (cnt_buf.cmd) { + case TZ_BW_SVC_START_ID: + ret = bw_profiling_start(bwbuf); + if (ret) + pr_err("bw_profiling_start Failed with ret: %d\n", ret); + break; + case TZ_BW_SVC_GET_ID: + ret = bw_profiling_get(argp, bwbuf); + if (ret) + pr_err("bw_profiling_get Failed with ret: %d\n", ret); + break; + case TZ_BW_SVC_STOP_ID: + ret = bw_profiling_stop(bwbuf); + if (ret) + pr_err("bw_profiling_stop Failed with ret: %d\n", ret); + break; + default: + pr_err("Invalid IOCTL: 0x%x\n", cnt_buf.cmd); + ret = -EINVAL; + } + /* Free memory for command */ + if (bwbuf != NULL) { + kfree(bwbuf); + bwbuf = NULL; + } + return ret; +} + +static int profiler_open(struct inode *inode, struct file *file) +{ + int ret = 0; + struct profiler_dev_handle *data; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + file->private_data = data; + data->abort = 0; + data->released = false; + return ret; +} + +static int compat_get_profiler_bw_info( + struct compat_profiler_bw_cntrs_req __user *data32, + struct profiler_bw_cntrs_req __user *data) +{ + compat_uint_t val = 0; + int err = 0; + int i = 0; + + for (i = 0; i < (sizeof(struct profiler_bw_cntrs_req)) + /sizeof(uint32_t) - 1; ++i) { + err |= get_user(val, (compat_uint_t *)data32 + i); + err |= put_user(val, (uint32_t *)data + i); + } + + return err; +} + +static int compat_put_profiler_bw_info( + struct compat_profiler_bw_cntrs_req __user *data32, + struct profiler_bw_cntrs_req __user *data) +{ + compat_uint_t val = 0; + int err = 0; + int i = 0; + + for (i = 0; i < (sizeof(struct profiler_bw_cntrs_req)) + /sizeof(uint32_t) - 1; ++i) { + err |= get_user(val, (uint32_t *)data + i); + err |= put_user(val, (compat_uint_t *)data32 + i); + } + + return err; +} + +static unsigned int convert_cmd(unsigned int cmd) +{ + switch (cmd) { + case COMPAT_PROFILER_IOCTL_GET_BW_INFO: + return PROFILER_IOCTL_GET_BW_INFO; + + default: + return cmd; + } +} + + +static long profiler_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + int ret = 0; + struct profiler_dev_handle *data = file->private_data; + void __user *argp = (void __user *) arg; + + if (!data) { + pr_err("Invalid/uninitialized device handle\n"); + return -EINVAL; + } + + if (data->abort) { + pr_err("Aborting profiler driver\n"); + return -ENODEV; + } + + switch (cmd) { + case PROFILER_IOCTL_GET_BW_INFO: + ret = profiler_get_bw_info(argp); + if (ret) + pr_err("failed get system bandwidth info: %d\n", ret); + break; + default: + pr_err("Invalid IOCTL: 0x%x\n", cmd); + return -EINVAL; + } + return ret; +} + +static int profiler_release(struct inode *inode, struct file *file) +{ + pr_info("profiler release\n"); + return 0; +} + +static const struct file_operations profiler_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = profiler_ioctl, + .open = profiler_open, + .release = profiler_release +}; + +static int profiler_init(void) +{ + int rc; + struct device *class_dev; + + rc = alloc_chrdev_region(&profiler_device_no, 0, 1, PROFILER_DEV); + if (rc < 0) { + pr_err("alloc_chrdev_region failed %d\n", rc); + return rc; + } + + driver_class = class_create(THIS_MODULE, PROFILER_DEV); + if (IS_ERR(driver_class)) { + rc = -ENOMEM; + pr_err("class_create failed %d\n", rc); + goto exit_unreg_chrdev_region; + } + + class_dev = device_create(driver_class, NULL, profiler_device_no, NULL, + PROFILER_DEV); + if (IS_ERR(class_dev)) { + pr_err("class_device_create failed %d\n", rc); + rc = -ENOMEM; + goto exit_destroy_class; + } + + cdev_init(&profiler.cdev, &profiler_fops); + profiler.cdev.owner = THIS_MODULE; + + rc = cdev_add(&profiler.cdev, MKDEV(MAJOR(profiler_device_no), 0), 1); + if (rc < 0) { + pr_err("%s: cdev_add failed %d\n", __func__, rc); + goto exit_destroy_device; + } + + profiler.pdev = class_dev; + return 0; + +exit_destroy_device: + device_destroy(driver_class, profiler_device_no); +exit_destroy_class: + class_destroy(driver_class); +exit_unreg_chrdev_region: + unregister_chrdev_region(profiler_device_no, 1); + return rc; +} + +static void profiler_exit(void) +{ + pr_info("Exiting from profiler\n"); +} + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Qualcomm Technologies, Inc. trustzone Communicator"); + +module_init(profiler_init); +module_exit(profiler_exit); diff --git a/include/linux/qcom_scm.h b/include/linux/qcom_scm.h index bdd83d7fe769..5611949ae9d2 100644 --- a/include/linux/qcom_scm.h +++ b/include/linux/qcom_scm.h @@ -205,6 +205,8 @@ extern int qcom_scm_invoke_callback_response(phys_addr_t out_buf, unsigned int *data); extern bool qcom_scm_is_available(void); extern int qcom_scm_mem_protect_audio(phys_addr_t paddr, size_t size); +extern int qcom_scm_ddrbw_profiler(phys_addr_t in_buf, size_t in_buf_size, + phys_addr_t out_buf, size_t out_buf_size); #else #include @@ -390,5 +392,8 @@ static inline int qcom_scm_invoke_callback_response(phys_addr_t out_buf, static inline bool qcom_scm_is_available(void) { return false; } static inline int qcom_scm_mem_protect_audio(phys_addr_t paddr, size_t size) { return -ENODEV; } +static inline int qcom_scm_ddrbw_profiler(phys_addr_t in_buf, size_t in_buf_size, + phys_addr_t out_buf, size_t out_buf_size) + { return -ENODEV; } #endif #endif diff --git a/include/soc/qcom/profiler.h b/include/soc/qcom/profiler.h new file mode 100644 index 000000000000..3dfbf2a53d69 --- /dev/null +++ b/include/soc/qcom/profiler.h @@ -0,0 +1,96 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2017, 2020-2021 The Linux Foundation. All rights reserved. + */ + +#ifndef __PROFILER_H_ +#define __PROFILER_H_ + + +struct profiler_bw_cntrs_req { + uint32_t llcc_total; + uint32_t llcc_rd; + uint32_t llcc_wr; + uint32_t cabo_total; + uint32_t cabo_rd; + uint32_t cabo_wr; + uint32_t cmd; +}; + +struct compat_profiler_bw_cntrs_req { + compat_uint_t llcc_total; + compat_uint_t llcc_rd; + compat_uint_t llcc_wr; + compat_uint_t cabo_total; + compat_uint_t cabo_rd; + compat_uint_t cabo_wr; + compat_uint_t cmd; +}; + +/* Error types */ +enum tz_bw_svc_err { + E_BW_SUCCESS = 0, /* Operation successful */ + E_BW_FAILURE = 1, /* Operation failed due to unknown err */ + E_BW_NULL_PARAM = 2, /* Null Parameter */ + E_BW_INVALID_ARG = 3, /* Arg is not recognized */ + E_BW_BAD_ADDRESS = 4, /* Ptr arg is bad address */ + E_BW_INVALID_ARG_LEN = 5, /* Arg length is wrong */ + E_BW_NOT_SUPPORTED = 6, /* Operation not supported */ + E_BW_NOT_PERMITTED = 7, /* Operation not permitted on platform */ + E_BW_TIME_LOCKED = 8, /* Operation not permitted right now */ + E_BW_RESERVED = 0x7FFFFFFF +}; + +#define TZ_BW_SVC_VERSION (1) +#define PROFILER_IOC_MAGIC 0x98 + +#define PROFILER_IOCTL_GET_BW_INFO \ + _IOWR(PROFILER_IOC_MAGIC, 1, struct profiler_bw_cntrs_req) + +#define COMPAT_PROFILER_IOCTL_GET_BW_INFO \ + _IOWR(PROFILER_IOC_MAGIC, 1, struct compat_profiler_bw_cntrs_req) + +/* Command types */ +enum tz_bw_svc_cmd { + TZ_BW_SVC_START_ID = 0x00000001, + TZ_BW_SVC_GET_ID = 0x00000002, + TZ_BW_SVC_STOP_ID = 0x00000003, + TZ_BW_SVC_LAST_ID = 0x7FFFFFFF +}; +/* Start Request */ +struct tz_bw_svc_start_req { + enum tz_bw_svc_cmd cmd_id; + uint32_t version; +} __packed; + +/* Get Request */ +struct tz_bw_svc_get_req { + enum tz_bw_svc_cmd cmd_id; + uint64_t buf_ptr; + uint32_t buf_size; +} __packed; + +/* Stop Request */ +struct tz_bw_svc_stop_req { + enum tz_bw_svc_cmd cmd_id; +} __packed; + +struct tz_bw_svc_resp { + enum tz_bw_svc_cmd cmd_id; + enum tz_bw_svc_err status; +} __packed; + +union tz_bw_svc_req { + struct tz_bw_svc_start_req start_req; + struct tz_bw_svc_get_req get_req; + struct tz_bw_svc_stop_req stop_req; +} __packed; + +struct tz_bw_svc_buf { + union tz_bw_svc_req bwreq; + struct tz_bw_svc_resp bwresp; + uint32_t req_size; +} __packed; + + +#endif /* __PROFILER_H_ */