| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * Copyright (c) 2021 MediaTek Inc. |
| */ |
| |
| #include <linux/module.h> |
| #include <linux/init.h> |
| #include <linux/types.h> |
| #include <linux/kernel.h> |
| #include <linux/proc_fs.h> |
| #include <linux/cdev.h> |
| #include <linux/cma.h> |
| #include <linux/mm.h> |
| #include <linux/slab.h> |
| #include <linux/io.h> |
| #include <linux/uaccess.h> |
| #include <linux/ioctl.h> |
| #include <linux/pagemap.h> |
| #include <linux/kthread.h> |
| #include <linux/freezer.h> |
| #include <linux/platform_device.h> |
| #include <linux/dma-mapping.h> |
| |
| #include <linux/clk.h> |
| #include <linux/irqdomain.h> |
| #include <linux/of_platform.h> |
| #include <linux/of_irq.h> |
| #include <linux/completion.h> |
| #include <mt-plat/mtk_cmo.h> |
| |
| #include "trustzone/kree/tz_mod.h" |
| #include "trustzone/kree/mem.h" |
| #include "trustzone/kree/system.h" |
| #include "trustzone/kree/tz_pm.h" |
| #include "trustzone/kree/tz_irq.h" |
| #include "trustzone/tz_cross/ta_mem.h" |
| #include "kree_int.h" |
| #include "tz_counter.h" |
| #include "tz_cross/ta_pm.h" |
| |
| #include "tz_playready.h" |
| |
| #include "tz_secure_clock.h" |
| #define MTEE_MOD_TAG "MTEE_MOD" |
| |
| #define MAX_TAG_SIZE 32 |
| |
| #define TZ_DEVNAME "mtk_tz" |
| |
| /**Used for mapping user space address to physical memory |
| */ |
| struct MTIOMMU_PIN_RANGE_T { |
| void *start; |
| void *pageArray; |
| uint32_t size; |
| uint32_t nrPages; |
| uint32_t isPage; |
| }; |
| |
| #if IS_ENABLED(CONFIG_OF) |
| /*Used for clk management*/ |
| #define CLK_NAME_LEN 16 |
| struct mtee_clk { |
| struct list_head list; |
| char clk_name[CLK_NAME_LEN]; |
| struct clk *clk; |
| }; |
| static LIST_HEAD(mtee_clk_list); |
| |
| static void mtee_clks_init(struct platform_device *pdev) |
| { |
| int clks_num; |
| int idx; |
| |
| clks_num = of_property_count_strings(pdev->dev.of_node, "clock-names"); |
| for (idx = 0; idx < clks_num; idx++) { |
| const char *clk_name; |
| struct clk *clk; |
| struct mtee_clk *mtee_clk; |
| |
| if (of_property_read_string_index(pdev->dev.of_node, |
| "clock-names", idx, &clk_name)) { |
| pr_warn("[%s] get clk_name failed, index:%d\n", |
| MODULE_NAME, |
| idx); |
| continue; |
| } |
| if (strlen(clk_name) > CLK_NAME_LEN-1) { |
| pr_warn("[%s] clk_name %s is longer than %d, trims to %d\n", |
| MODULE_NAME, |
| clk_name, CLK_NAME_LEN-1, CLK_NAME_LEN-1); |
| } |
| clk = devm_clk_get(&pdev->dev, clk_name); |
| if (IS_ERR(clk)) { |
| pr_warn("[%s] get devm_clk_get failed, clk_name:%s\n", |
| MODULE_NAME, |
| clk_name); |
| continue; |
| } |
| |
| mtee_clk = kzalloc(sizeof(struct mtee_clk), GFP_KERNEL); |
| strncpy(mtee_clk->clk_name, clk_name, CLK_NAME_LEN-1); |
| mtee_clk->clk = clk; |
| |
| list_add(&mtee_clk->list, &mtee_clk_list); |
| } |
| } |
| |
| struct clk *mtee_clk_get(const char *clk_name) |
| { |
| struct mtee_clk *cur; |
| struct mtee_clk *tmp; |
| |
| list_for_each_entry_safe(cur, tmp, &mtee_clk_list, list) { |
| if (strncmp(cur->clk_name, clk_name, |
| strlen(cur->clk_name)) == 0) |
| return cur->clk; |
| } |
| return NULL; |
| } |
| |
| /*Used for power management*/ |
| #define PM_NAME_LEN 16 |
| struct mtee_pm { |
| struct list_head list; |
| char pm_name[PM_NAME_LEN]; |
| struct device *dev; |
| }; |
| static LIST_HEAD(mtee_pm_list); |
| |
| static void mtee_pms_init(struct platform_device *pdev) |
| { |
| int pms_num; |
| int idx; |
| |
| pms_num = of_property_count_strings(pdev->dev.of_node, "pm-names"); |
| for (idx = 0; idx < pms_num; idx++) { |
| const char *pm_name; |
| struct platform_device *platdev; |
| struct mtee_pm *mtee_pm; |
| struct device_node *node; |
| |
| if (of_property_read_string_index(pdev->dev.of_node, |
| "pm-names", idx, &pm_name)) { |
| pr_warn("[%s] get pm_name failed, index:%d\n", |
| MODULE_NAME, |
| idx); |
| continue; |
| } |
| if (strlen(pm_name) > PM_NAME_LEN-1) { |
| pr_warn("[%s] pm_name %s is longer than %d, trims to %d\n", |
| MODULE_NAME, |
| pm_name, PM_NAME_LEN-1, PM_NAME_LEN-1); |
| } |
| node = of_parse_phandle(pdev->dev.of_node, "pm-devs", idx); |
| if (!node) |
| continue; |
| |
| platdev = of_find_device_by_node(node); |
| of_node_put(node); |
| if (!platdev) |
| continue; |
| |
| mtee_pm = kzalloc(sizeof(struct mtee_pm), GFP_KERNEL); |
| strncpy(mtee_pm->pm_name, pm_name, PM_NAME_LEN-1); |
| mtee_pm->dev = &platdev->dev; |
| |
| list_add(&mtee_pm->list, &mtee_pm_list); |
| } |
| } |
| |
| struct device *mtee_pmdev_get(const char *pm_name) |
| { |
| struct mtee_pm *cur; |
| struct mtee_pm *tmp; |
| |
| list_for_each_entry_safe(cur, tmp, &mtee_pm_list, list) { |
| if (strncmp(cur->pm_name, pm_name, |
| strlen(cur->pm_name)) == 0) |
| return cur->dev; |
| } |
| return NULL; |
| } |
| |
| #endif |
| |
| /***************************************************************************** |
| * FUNCTION DEFINITION |
| *****************************************************************************/ |
| static struct cdev tz_client_cdev; |
| static dev_t tz_client_dev; |
| |
| static int tz_client_open(struct inode *inode, struct file *filp); |
| static int tz_client_release(struct inode *inode, struct file *filp); |
| static long tz_client_ioctl(struct file *file, unsigned int cmd, |
| unsigned long arg); |
| #if IS_ENABLED(CONFIG_COMPAT) |
| static long tz_client_ioctl_compat(struct file *file, unsigned int cmd, |
| unsigned long arg); |
| #endif |
| |
| static int tz_client_init_client_info(struct file *file); |
| static void tz_client_free_client_info(struct file *file); |
| |
| #define TZ_CLIENT_INIT_HANDLE_SPACE 32 |
| #define TZ_CLIENT_INIT_SHAREDMEM_SPACE 32 |
| |
| |
| struct tz_sharedmem_info { |
| KREE_SHAREDMEM_HANDLE mem_handle; |
| KREE_SESSION_HANDLE session_handle; |
| uint32_t *resource; |
| }; |
| |
| struct tz_client_info { |
| int handle_num; |
| struct mutex mux; |
| KREE_SESSION_HANDLE *handles; |
| struct tz_sharedmem_info *shm_info; |
| int shm_info_num; |
| }; |
| |
| |
| static const struct file_operations tz_client_fops = { |
| .open = tz_client_open, |
| .release = tz_client_release, |
| .unlocked_ioctl = tz_client_ioctl, |
| #if IS_ENABLED(CONFIG_COMPAT) |
| .compat_ioctl = tz_client_ioctl_compat, |
| #endif |
| |
| .owner = THIS_MODULE, |
| }; |
| |
| static int tz_client_open(struct inode *inode, struct file *filp) |
| { |
| return tz_client_init_client_info(filp); |
| } |
| |
| static int tz_client_release(struct inode *inode, struct file *filp) |
| { |
| tz_client_free_client_info(filp); |
| |
| return 0; |
| } |
| |
| /* map user space pages */ |
| /* control -> 0 = write, 1 = read only memory */ |
| static long _map_user_pages(struct MTIOMMU_PIN_RANGE_T *pinRange, |
| unsigned long uaddr, uint32_t size, |
| uint32_t control) |
| { |
| int nr_pages; |
| unsigned long first, last, gup_flags; |
| struct page **pages; |
| struct vm_area_struct *vma; |
| int res, j; |
| |
| if ((uaddr == 0) || (size == 0)) |
| return -EFAULT; |
| |
| pinRange->start = (void *)uaddr; |
| pinRange->size = size; |
| |
| first = (uaddr & PAGE_MASK) >> PAGE_SHIFT; |
| last = ((uaddr + size + PAGE_SIZE - 1) & PAGE_MASK) >> PAGE_SHIFT; |
| nr_pages = last - first; |
| pages = kcalloc(nr_pages, sizeof(struct page *), GFP_KERNEL); |
| if (pages == NULL) |
| return -ENOMEM; |
| |
| pinRange->pageArray = (void *) pages; |
| gup_flags = (control == 0) ? FOLL_WRITE : 0; |
| |
| /* Try to fault in all of the necessary pages */ |
| down_read(¤t->mm->mmap_sem); |
| vma = find_vma_intersection(current->mm, uaddr, uaddr+size); |
| if (!vma) { |
| res = -EFAULT; |
| goto out; |
| } |
| if (!(vma->vm_flags & (VM_IO | VM_PFNMAP))) { |
| pinRange->isPage = 1; |
| #if IS_ENABLED(CONFIG_MTEE_CMA_SECURE_MEMORY) |
| res = get_user_pages_durable(uaddr, nr_pages, gup_flags, |
| pages, NULL); |
| #else |
| res = get_user_pages(uaddr, nr_pages, gup_flags, |
| pages, NULL); |
| #endif |
| } else { |
| /* pfn mapped memory, don't touch page struct. |
| * the buffer manager (possibly ion) should make sure |
| * it won't be used for anything else |
| */ |
| pinRange->isPage = 0; |
| res = 0; |
| do { |
| unsigned long *pfns = (void *)pages; |
| |
| while (res < nr_pages && |
| uaddr + PAGE_SIZE <= vma->vm_end) { |
| j = follow_pfn(vma, uaddr, &pfns[res]); |
| if (j) { /* error */ |
| res = j; |
| goto out; |
| } |
| uaddr += PAGE_SIZE; |
| res++; |
| } |
| if (res >= nr_pages || uaddr < vma->vm_end) |
| break; |
| vma = find_vma_intersection(current->mm, uaddr, |
| uaddr+1); |
| } while (vma && vma->vm_flags & (VM_IO | VM_PFNMAP)); |
| } |
| out: |
| up_read(¤t->mm->mmap_sem); |
| if (res < 0) { |
| pr_warn("%s error = %d\n", __func__, res); |
| goto out_free; |
| } |
| |
| pinRange->nrPages = res; |
| /* Errors and no page mapped should return here */ |
| if (res < nr_pages) |
| goto out_unmap; |
| |
| return 0; |
| |
| out_unmap: |
| pr_warn("%s fail\n", __func__); |
| if (pinRange->isPage) { |
| for (j = 0; j < res; j++) |
| put_page(pages[j]); |
| } |
| res = -EFAULT; |
| |
| out_free: |
| kfree(pages); |
| return res; |
| } |
| |
| static void _unmap_user_pages(struct MTIOMMU_PIN_RANGE_T *pinRange) |
| { |
| int res; |
| int j; |
| struct page **pages; |
| |
| pages = (struct page **)pinRange->pageArray; |
| |
| if (pinRange->isPage) { |
| res = pinRange->nrPages; |
| |
| if (res > 0) { |
| for (j = 0; j < res; j++) |
| put_page(pages[j]); |
| res = 0; |
| } |
| } |
| |
| kfree(pages); |
| } |
| |
| static struct tz_sharedmem_info *tz_get_sharedmem(struct tz_client_info *info, |
| KREE_SHAREDMEM_HANDLE handle) |
| { |
| struct tz_sharedmem_info *shm_info; |
| int i; |
| |
| /* search handle */ |
| shm_info = NULL; |
| for (i = 0; i < info->shm_info_num; i++) { |
| if (info->shm_info[i].mem_handle == handle) { |
| shm_info = &info->shm_info[i]; |
| break; |
| } |
| } |
| |
| return shm_info; |
| } |
| |
| |
| |
| /************************************************************************** |
| * DEV tz_client_info handling |
| **************************************************************************/ |
| static int tz_client_register_session(struct file *file, |
| KREE_SESSION_HANDLE handle) |
| { |
| struct tz_client_info *info; |
| int i, num, nspace, ret = -1; |
| void *ptr; |
| |
| info = (struct tz_client_info *)file->private_data; |
| |
| /* lock */ |
| mutex_lock(&info->mux); |
| |
| /* find empty space. */ |
| num = info->handle_num; |
| for (i = 0; i < num; i++) { |
| if (info->handles[i] == 0) { |
| ret = i; |
| break; |
| } |
| } |
| |
| if (ret == -1) { |
| /* Try grow the space */ |
| nspace = num * 2; |
| ptr = krealloc(info->handles, |
| nspace * sizeof(KREE_SESSION_HANDLE), |
| GFP_KERNEL); |
| if (ptr == 0) { |
| mutex_unlock(&info->mux); |
| return -ENOMEM; |
| } |
| |
| ret = num; |
| info->handle_num = nspace; |
| info->handles = (KREE_SESSION_HANDLE *) ptr; |
| memset(&info->handles[num], 0, |
| (nspace - num) * sizeof(KREE_SESSION_HANDLE)); |
| } |
| |
| if (ret >= 0) |
| info->handles[ret] = handle; |
| |
| /* unlock */ |
| mutex_unlock(&info->mux); |
| return ret + 1; |
| } |
| |
| static KREE_SESSION_HANDLE tz_client_get_session(struct file *file, int handle) |
| { |
| struct tz_client_info *info; |
| KREE_SESSION_HANDLE rhandle; |
| |
| info = (struct tz_client_info *)file->private_data; |
| |
| /* lock */ |
| mutex_lock(&info->mux); |
| |
| if (handle <= 0 || handle > info->handle_num) |
| rhandle = (KREE_SESSION_HANDLE) 0; |
| else |
| rhandle = info->handles[handle - 1]; |
| |
| /* unlock */ |
| mutex_unlock(&info->mux); |
| return rhandle; |
| } |
| |
| static int tz_client_unregister_session(struct file *file, int handle) |
| { |
| struct tz_client_info *info; |
| int ret = 0; |
| |
| info = (struct tz_client_info *)file->private_data; |
| |
| /* lock */ |
| mutex_lock(&info->mux); |
| |
| if (handle <= 0 || handle > info->handle_num || |
| info->handles[handle - 1] == 0) |
| ret = -EINVAL; |
| else |
| info->handles[handle - 1] = (KREE_SESSION_HANDLE) 0; |
| |
| /* unlock */ |
| mutex_unlock(&info->mux); |
| return ret; |
| } |
| |
| static int |
| tz_client_register_sharedmem(struct file *file, KREE_SESSION_HANDLE handle, |
| KREE_SHAREDMEM_HANDLE mem_handle, |
| uint32_t *resource) |
| { |
| struct tz_client_info *info; |
| int i, num, nspace, ret = -1; |
| void *ptr; |
| |
| info = (struct tz_client_info *)file->private_data; |
| |
| /* lock */ |
| mutex_lock(&info->mux); |
| |
| /* find empty space. */ |
| num = info->shm_info_num; |
| for (i = 0; i < num; i++) { |
| if ((int)info->shm_info[i].mem_handle == 0) { |
| ret = i; |
| break; |
| } |
| } |
| |
| if (ret == -1) { |
| /* Try grow the space */ |
| nspace = num * 2; |
| ptr = krealloc(info->shm_info, |
| nspace * sizeof(struct tz_sharedmem_info), |
| GFP_KERNEL); |
| if (ptr == 0) { |
| mutex_unlock(&info->mux); |
| return -ENOMEM; |
| } |
| |
| ret = num; |
| info->shm_info_num = nspace; |
| info->shm_info = (struct tz_sharedmem_info *)ptr; |
| memset(&info->shm_info[num], 0, |
| (nspace - num) * sizeof(struct tz_sharedmem_info)); |
| } |
| |
| if (ret >= 0) { |
| info->shm_info[ret].mem_handle = mem_handle; |
| info->shm_info[ret].session_handle = handle; |
| info->shm_info[ret].resource = resource; |
| } |
| /* unlock */ |
| mutex_unlock(&info->mux); |
| |
| return ret; |
| } |
| |
| static int tz_client_unregister_sharedmem(struct file *file, |
| KREE_SHAREDMEM_HANDLE handle) |
| { |
| struct tz_client_info *info; |
| struct tz_sharedmem_info *shm_info; |
| struct MTIOMMU_PIN_RANGE_T *pin; |
| int ret = 0; |
| |
| info = (struct tz_client_info *)file->private_data; |
| |
| /* lock */ |
| mutex_lock(&info->mux); |
| |
| shm_info = tz_get_sharedmem(info, handle); |
| |
| if ((shm_info == NULL) || (shm_info->mem_handle == 0)) |
| ret = -EINVAL; |
| else { |
| pin = (struct MTIOMMU_PIN_RANGE_T *) shm_info->resource; |
| _unmap_user_pages(pin); |
| kfree(pin); |
| memset(shm_info, 0, sizeof(struct tz_sharedmem_info)); |
| } |
| |
| /* unlock */ |
| mutex_unlock(&info->mux); |
| |
| return ret; |
| } |
| |
| |
| static int tz_client_init_client_info(struct file *file) |
| { |
| struct tz_client_info *info; |
| |
| info = (struct tz_client_info *) |
| kmalloc(sizeof(struct tz_client_info), GFP_KERNEL); |
| if (!info) |
| return -ENOMEM; |
| |
| info->handles = kcalloc(TZ_CLIENT_INIT_HANDLE_SPACE, |
| sizeof(KREE_SESSION_HANDLE), GFP_KERNEL); |
| if (!info->handles) { |
| kfree(info); |
| return -ENOMEM; |
| } |
| info->handle_num = TZ_CLIENT_INIT_HANDLE_SPACE; |
| |
| /* init shared memory */ |
| info->shm_info = kcalloc(TZ_CLIENT_INIT_SHAREDMEM_SPACE, |
| sizeof(struct tz_sharedmem_info), GFP_KERNEL); |
| if (!info->shm_info) { |
| kfree(info->handles); |
| kfree(info); |
| return -ENOMEM; |
| } |
| info->shm_info_num = TZ_CLIENT_INIT_SHAREDMEM_SPACE; |
| |
| mutex_init(&info->mux); |
| file->private_data = info; |
| return 0; |
| } |
| |
| static void tz_client_free_client_info(struct file *file) |
| { |
| struct tz_client_info *info; |
| struct tz_sharedmem_info *shm_info; |
| struct MTIOMMU_PIN_RANGE_T *pin; |
| int i, num; |
| |
| info = (struct tz_client_info *)file->private_data; |
| |
| /* lock */ |
| mutex_lock(&info->mux); |
| |
| num = info->handle_num; |
| for (i = 0; i < num; i++) { |
| if (info->handles[i] == 0) |
| continue; |
| |
| KREE_CloseSession(info->handles[i]); |
| info->handles[i] = (KREE_SESSION_HANDLE) 0; |
| } |
| |
| /* unregister shared memory */ |
| num = info->shm_info_num; |
| for (i = 0; i < num; i++) { |
| if (info->shm_info[i].mem_handle == 0) |
| continue; |
| |
| shm_info = tz_get_sharedmem(info, info->shm_info[i].mem_handle); |
| if (shm_info == NULL) |
| continue; |
| |
| pin = (struct MTIOMMU_PIN_RANGE_T *) shm_info->resource; |
| |
| _unmap_user_pages(pin); |
| kfree(pin); |
| } |
| |
| /* unlock */ |
| file->private_data = 0; |
| mutex_unlock(&info->mux); |
| kfree(info->handles); |
| kfree(info->shm_info); |
| kfree(info); |
| } |
| |
| /************************************************************************** |
| * DEV DRIVER IOCTL |
| **************************************************************************/ |
| static KREE_SESSION_HANDLE tz_client_open_session_impl(int with_tag, |
| unsigned long arg, |
| struct kree_session_tag_cmd_param *pparam) |
| { |
| size_t param_size; |
| int ret; |
| unsigned long cret; |
| char uuid[40]; |
| long len; |
| KREE_SESSION_HANDLE handle; |
| char tag[48] = { 0 }; |
| |
| if (with_tag == 0) |
| param_size = sizeof(struct kree_session_cmd_param); |
| else |
| param_size = sizeof(struct kree_session_tag_cmd_param); |
| |
| cret = copy_from_user(pparam, (void *)arg, param_size); |
| if (cret) |
| return -EFAULT; |
| |
| /* Check if can we access UUID string. 10 for min uuid len. */ |
| if (!access_ok(VERIFY_READ, pparam->data, 10)) |
| return -EFAULT; |
| |
| if (with_tag != 0 && pparam->tag != 0) { |
| /* Check if can we access tag string. */ |
| if (!access_ok(VERIFY_READ, pparam->tag, pparam->tag_size)) |
| return -EFAULT; |
| |
| len = strncpy_from_user(tag, |
| (void *)(unsigned long)pparam->tag, |
| sizeof(tag)); |
| if (len <= 0) |
| return -EFAULT; |
| |
| tag[sizeof(tag) - 1] = 0; |
| } |
| |
| len = strncpy_from_user(uuid, |
| (void *)(unsigned long)pparam->data, |
| sizeof(uuid)); |
| if (len <= 0) |
| return -EFAULT; |
| |
| uuid[sizeof(uuid) - 1] = 0; |
| if (with_tag != 0 && pparam->tag != 0) |
| ret = KREE_CreateSessionWithTag(uuid, &handle, tag); |
| else |
| ret = KREE_CreateSession(uuid, &handle); |
| pparam->ret = ret; |
| |
| if (ret != TZ_RESULT_SUCCESS) |
| return KREE_SESSION_HANDLE_FAIL; |
| |
| return handle; |
| } |
| |
| static long tz_client_open_session(struct file *file, unsigned long arg) |
| { |
| unsigned long cret; |
| KREE_SESSION_HANDLE handle; |
| struct kree_session_tag_cmd_param param; |
| |
| handle = tz_client_open_session_impl(0, arg, ¶m); |
| |
| if (handle != KREE_SESSION_HANDLE_FAIL && |
| handle != KREE_SESSION_HANDLE_NULL) { |
| param.handle = tz_client_register_session(file, handle); |
| if (param.handle < 0) |
| goto error_register; |
| } |
| |
| cret = copy_to_user((void *)arg, ¶m, |
| sizeof(struct kree_session_cmd_param)); |
| if (cret) |
| goto error_copy; |
| |
| return 0; |
| |
| error_copy: |
| tz_client_unregister_session(file, param.handle); |
| error_register: |
| KREE_CloseSession(handle); |
| return -EFAULT; |
| } |
| |
| static long tz_client_open_session_with_tag(struct file *file, |
| unsigned long arg) |
| { |
| unsigned long cret; |
| KREE_SESSION_HANDLE handle; |
| struct kree_session_tag_cmd_param param; |
| |
| handle = tz_client_open_session_impl(1, arg, ¶m); |
| |
| if (handle != KREE_SESSION_HANDLE_FAIL && |
| handle != KREE_SESSION_HANDLE_NULL) { |
| param.handle = tz_client_register_session(file, handle); |
| if (param.handle < 0) |
| goto error_register; |
| } |
| |
| cret = copy_to_user((void *)arg, ¶m, sizeof(param)); |
| if (cret) |
| goto error_copy; |
| |
| return 0; |
| |
| error_copy: |
| tz_client_unregister_session(file, param.handle); |
| error_register: |
| KREE_CloseSession(handle); |
| return -EFAULT; |
| } |
| |
| static long tz_client_close_session(struct file *file, unsigned long arg) |
| { |
| struct kree_session_cmd_param param; |
| unsigned long cret; |
| int ret; |
| KREE_SESSION_HANDLE handle; |
| |
| cret = copy_from_user(¶m, (void *)arg, sizeof(param)); |
| if (cret) |
| return -EFAULT; |
| |
| handle = tz_client_get_session(file, param.handle); |
| if (handle == 0) |
| return -EINVAL; |
| |
| tz_client_unregister_session(file, param.handle); |
| ret = KREE_CloseSession(handle); |
| param.ret = ret; |
| |
| cret = copy_to_user((void *)arg, ¶m, sizeof(param)); |
| if (cret) |
| return -EFAULT; |
| |
| return 0; |
| } |
| |
| static long tz_client_tee_service(struct file *file, unsigned long arg, |
| unsigned int compat) |
| { |
| struct kree_tee_service_cmd_param cparam; |
| unsigned long cret; |
| uint32_t tmpTypes; |
| union MTEEC_PARAM param[4], oparam[4]; |
| int i; |
| int ret; |
| KREE_SESSION_HANDLE handle; |
| void __user *ubuf; |
| uint32_t ubuf_sz; |
| |
| cret = copy_from_user(&cparam, (void *)arg, sizeof(cparam)); |
| if (cret) |
| return -EFAULT; |
| |
| if (cparam.paramTypes != TZPT_NONE || cparam.param) { |
| /* Check if can we access param */ |
| if (!access_ok(VERIFY_READ, cparam.param, sizeof(oparam))) |
| return -EFAULT; |
| |
| cret = copy_from_user(oparam, |
| (void *)(unsigned long)cparam.param, |
| sizeof(oparam)); |
| if (cret) |
| return -EFAULT; |
| } |
| /* Check handle */ |
| handle = tz_client_get_session(file, cparam.handle); |
| if (handle <= 0) |
| return -EINVAL; |
| |
| /* Parameter processing. */ |
| memset(param, 0, sizeof(param)); |
| tmpTypes = cparam.paramTypes; |
| for (i = 0; tmpTypes; i++) { |
| enum TZ_PARAM_TYPES type = tmpTypes & 0xff; |
| |
| tmpTypes >>= 8; |
| switch (type) { |
| case TZPT_VALUE_INPUT: |
| case TZPT_VALUE_INOUT: |
| param[i] = oparam[i]; |
| break; |
| |
| case TZPT_VALUE_OUTPUT: |
| case TZPT_NONE: |
| /* Nothing to do */ |
| break; |
| |
| case TZPT_MEM_INPUT: |
| case TZPT_MEM_OUTPUT: |
| case TZPT_MEM_INOUT: |
| #if IS_ENABLED(CONFIG_COMPAT) |
| if (compat) { |
| ubuf = compat_ptr(oparam[i].mem32.buffer); |
| ubuf_sz = oparam[i].mem32.size; |
| } else |
| #endif |
| { |
| ubuf = oparam[i].mem.buffer; |
| ubuf_sz = oparam[i].mem.size; |
| } |
| |
| /* Mem Access check */ |
| if (type != TZPT_MEM_OUTPUT) { |
| if (!access_ok(VERIFY_READ, ubuf, |
| ubuf_sz)) { |
| cret = -EFAULT; |
| goto error; |
| } |
| } |
| if (type != TZPT_MEM_INPUT) { |
| if (!access_ok(VERIFY_WRITE, ubuf, |
| ubuf_sz)) { |
| cret = -EFAULT; |
| goto error; |
| } |
| } |
| /* Allocate kernel space memory. Fail if > 4kb */ |
| if (ubuf_sz > TEE_PARAM_MEM_LIMIT) { |
| cret = -ENOMEM; |
| goto error; |
| } |
| |
| param[i].mem.size = ubuf_sz; |
| param[i].mem.buffer = kmalloc(param[i].mem.size, |
| GFP_KERNEL); |
| if (!param[i].mem.buffer) { |
| cret = -ENOMEM; |
| goto error; |
| } |
| |
| if (type != TZPT_MEM_OUTPUT) { |
| cret = copy_from_user(param[i].mem.buffer, |
| ubuf, |
| param[i].mem.size); |
| if (cret) { |
| cret = -EFAULT; |
| goto error; |
| } |
| } |
| break; |
| |
| case TZPT_MEMREF_INPUT: |
| case TZPT_MEMREF_OUTPUT: |
| case TZPT_MEMREF_INOUT: |
| /* Check if share memory is valid. */ |
| /* Not done yet. */ |
| param[i] = oparam[i]; |
| break; |
| |
| default: |
| /* Bad format, return. */ |
| ret = TZ_RESULT_ERROR_BAD_FORMAT; |
| goto error; |
| } |
| } |
| |
| /* Execute. */ |
| ret = KREE_TeeServiceCallNoCheck(handle, cparam.command, |
| cparam.paramTypes, param); |
| |
| /* Copy memory back. */ |
| cparam.ret = ret; |
| tmpTypes = cparam.paramTypes; |
| for (i = 0; tmpTypes; i++) { |
| enum TZ_PARAM_TYPES type = tmpTypes & 0xff; |
| |
| tmpTypes >>= 8; |
| switch (type) { |
| case TZPT_VALUE_OUTPUT: |
| case TZPT_VALUE_INOUT: |
| oparam[i] = param[i]; |
| break; |
| |
| default: |
| /* This should never happen */ |
| case TZPT_MEMREF_INPUT: |
| case TZPT_MEMREF_OUTPUT: |
| case TZPT_MEMREF_INOUT: |
| case TZPT_VALUE_INPUT: |
| case TZPT_NONE: |
| /* Nothing to do */ |
| break; |
| |
| case TZPT_MEM_INPUT: |
| case TZPT_MEM_OUTPUT: |
| case TZPT_MEM_INOUT: |
| #if IS_ENABLED(CONFIG_COMPAT) |
| if (compat) |
| ubuf = compat_ptr(oparam[i].mem32.buffer); |
| else |
| #endif |
| ubuf = oparam[i].mem.buffer; |
| |
| if (type != TZPT_MEM_INPUT) { |
| cret = copy_to_user(ubuf, |
| param[i].mem.buffer, |
| param[i].mem.size); |
| if (cret) { |
| cret = -EFAULT; |
| goto error; |
| } |
| } |
| |
| kfree(param[i].mem.buffer); |
| break; |
| } |
| } |
| |
| /* Copy data back. */ |
| if (cparam.paramTypes != TZPT_NONE) { |
| cret = copy_to_user((void *)(unsigned long)cparam.param, |
| oparam, |
| sizeof(oparam)); |
| if (cret) |
| return -EFAULT; |
| } |
| |
| cret = copy_to_user((void *)arg, &cparam, sizeof(cparam)); |
| if (cret) |
| return -EFAULT; |
| return 0; |
| |
| error: |
| tmpTypes = cparam.paramTypes; |
| for (i = 0; tmpTypes; i++) { |
| enum TZ_PARAM_TYPES type = tmpTypes & 0xff; |
| |
| tmpTypes >>= 8; |
| switch (type) { |
| case TZPT_MEM_INPUT: |
| case TZPT_MEM_OUTPUT: |
| case TZPT_MEM_INOUT: |
| kfree(param[i].mem.buffer); |
| break; |
| |
| default: |
| /* Don't care. */ |
| break; |
| } |
| } |
| |
| return cret; |
| } |
| |
| static long __tz_reg_sharedmem(struct file *file, unsigned long arg, |
| struct kree_sharedmemory_tag_cmd_param *cparam) |
| { |
| unsigned long cret; |
| KREE_SESSION_HANDLE session; |
| uint32_t mem_handle; |
| struct MTIOMMU_PIN_RANGE_T *pin; |
| uint64_t *map_p; |
| int ret; |
| struct page **page; |
| int i; |
| long errcode; |
| unsigned long *pfns; |
| char *tag = NULL; |
| |
| /* handle tag for debugging purpose */ |
| if (cparam->tag != 0 && cparam->tag_size != 0) { |
| tag = kmalloc(cparam->tag_size+1, GFP_KERNEL); |
| if (tag == NULL) |
| return -ENOMEM; |
| cret = copy_from_user(tag, |
| (void *)(unsigned long)cparam->tag, |
| cparam->tag_size); |
| if (cret) { |
| kfree(tag); |
| return -EFAULT; |
| } |
| tag[cparam->tag_size] = '\0'; |
| } |
| |
| /* session handle */ |
| session = tz_client_get_session(file, cparam->session); |
| if (session <= 0) { |
| kfree(tag); |
| return -EINVAL; |
| } |
| |
| /* map pages |
| */ |
| /* 1. get user pages */ |
| /* note: 'pin' resource need to keep for unregister. |
| * It will be freed after unregisted. |
| */ |
| pin = kzalloc(sizeof(struct MTIOMMU_PIN_RANGE_T), GFP_KERNEL); |
| if (pin == NULL) { |
| errcode = -ENOMEM; |
| goto client_regshm_mapfail; |
| } |
| cret = _map_user_pages(pin, (unsigned long)cparam->buffer, |
| cparam->size, cparam->control); |
| if (cret != 0) { |
| pr_warn("tz_client_reg_sharedmem fail: map user pages = 0x%x\n", |
| (uint32_t) cret); |
| errcode = -EFAULT; |
| goto client_regshm_mapfail_1; |
| } |
| /* 2. build PA table */ |
| map_p = kzalloc(sizeof(uint64_t) * (pin->nrPages + 1), GFP_KERNEL); |
| if (map_p == NULL) { |
| errcode = -ENOMEM; |
| goto client_regshm_mapfail_2; |
| } |
| map_p[0] = pin->nrPages; |
| if (pin->isPage) { |
| page = (struct page **)pin->pageArray; |
| for (i = 0; i < pin->nrPages; i++) { |
| map_p[1 + i] = PFN_PHYS(page_to_pfn(page[i])); /* PA */ |
| /* pr_debug("tz_client_reg_sharedmem ---> 0x%x\n", |
| * map_p[1+i]); |
| */ |
| } |
| } else { /* pfn */ |
| pfns = (unsigned long *)pin->pageArray; |
| for (i = 0; i < pin->nrPages; i++) { |
| map_p[1 + i] = PFN_PHYS(pfns[i]); /* get PA */ |
| /* pr_debug("tz_client_reg_sharedmem ---> 0x%x\n", |
| * map_p[1+i]); |
| */ |
| } |
| } |
| |
| /* register it ... |
| */ |
| ret = kree_register_sharedmem(session, &mem_handle, pin->start, |
| pin->size, (void *)map_p, tag); |
| if (ret != TZ_RESULT_SUCCESS) { |
| pr_warn("tz_client_reg_sharedmem fail: register shm 0x%x\n", |
| ret); |
| kfree(map_p); |
| kfree(tag); |
| errcode = -EFAULT; |
| goto client_regshm_free; |
| } |
| kfree(map_p); |
| kfree(tag); |
| |
| /* register to fd |
| */ |
| cret = tz_client_register_sharedmem(file, session, |
| (KREE_SHAREDMEM_HANDLE) mem_handle, |
| (uint32_t *) pin); |
| if (cret < 0) { |
| pr_warn("tz_client_reg_sharedmem fail: register fd 0x%x\n", |
| (uint32_t) cret); |
| errcode = -EFAULT; |
| goto client_regshm_free_1; |
| } |
| |
| cparam->mem_handle = mem_handle; |
| cparam->ret = ret; /* TEE service call return */ |
| cret = copy_to_user((void *)arg, cparam, |
| sizeof(struct kree_sharedmemory_cmd_param)); |
| if (cret) |
| return -EFAULT; |
| |
| return TZ_RESULT_SUCCESS; |
| |
| client_regshm_free_1: |
| kree_unregister_sharedmem(session, mem_handle); |
| client_regshm_free: |
| _unmap_user_pages(pin); |
| kfree(pin); |
| cparam->ret = ret; /* TEE service call return */ |
| cret = copy_to_user((void *)arg, cparam, |
| sizeof(struct kree_sharedmemory_cmd_param)); |
| pr_warn("tz_client_reg_sharedmem fail: shm reg\n"); |
| return errcode; |
| |
| client_regshm_mapfail_2: |
| _unmap_user_pages(pin); |
| client_regshm_mapfail_1: |
| kfree(pin); |
| client_regshm_mapfail: |
| kfree(tag); |
| pr_warn("tz_client_reg_sharedmem fail: map memory\n"); |
| return errcode; |
| } |
| |
| static long tz_client_reg_sharedmem_with_tag(struct file *file, |
| unsigned long arg) |
| { |
| unsigned long cret; |
| struct kree_sharedmemory_tag_cmd_param cparam; |
| |
| cret = copy_from_user(&cparam, (void *)arg, sizeof(cparam)); |
| if (cret) |
| return -EFAULT; |
| |
| if (cparam.tag_size > MAX_TAG_SIZE) |
| cparam.tag_size = MAX_TAG_SIZE; |
| |
| return __tz_reg_sharedmem(file, arg, &cparam); |
| } |
| |
| static long tz_client_reg_sharedmem(struct file *file, unsigned long arg) |
| { |
| unsigned long cret; |
| struct kree_sharedmemory_tag_cmd_param cparam; |
| |
| cret = copy_from_user(&cparam, (void *)arg, |
| sizeof(struct kree_sharedmemory_cmd_param)); |
| if (cret) |
| return -EFAULT; |
| |
| cparam.tag = 0; |
| cparam.tag_size = 0; |
| |
| return __tz_reg_sharedmem(file, arg, &cparam); |
| } |
| |
| static long tz_client_unreg_sharedmem(struct file *file, unsigned long arg) |
| { |
| unsigned long cret; |
| struct kree_sharedmemory_cmd_param cparam; |
| KREE_SESSION_HANDLE session; |
| int ret; |
| |
| cret = copy_from_user(&cparam, (void *)arg, sizeof(cparam)); |
| if (cret) |
| return -EFAULT; |
| |
| /* session handle */ |
| session = tz_client_get_session(file, cparam.session); |
| if (session <= 0) |
| return -EINVAL; |
| |
| /* Unregister |
| */ |
| ret = kree_unregister_sharedmem(session, (uint32_t) cparam.mem_handle); |
| if (ret != TZ_RESULT_SUCCESS) { |
| pr_warn("%s: 0x%x\n", __func__, ret); |
| cparam.ret = ret; |
| cret = copy_to_user((void *)arg, &cparam, sizeof(cparam)); |
| return -EFAULT; |
| } |
| |
| /* unmap user pages and unregister to fd |
| */ |
| ret = tz_client_unregister_sharedmem(file, cparam.mem_handle); |
| if (ret != TZ_RESULT_SUCCESS) { |
| pr_warn("%s: unregister shm = 0x%x\n", __func__, ret); |
| return -EFAULT; |
| } |
| |
| cparam.ret = ret; |
| cret = copy_to_user((void *)arg, &cparam, sizeof(cparam)); |
| if (cret) |
| return -EFAULT; |
| |
| return TZ_RESULT_SUCCESS; |
| } |
| |
| |
| |
| static long do_tz_client_ioctl(struct file *file, unsigned int cmd, |
| unsigned long arg, unsigned int compat) |
| { |
| int err = 0; |
| |
| /* ---------------------------------- */ |
| /* IOCTL */ |
| /* ---------------------------------- */ |
| if (_IOC_TYPE(cmd) != MTEE_IOC_MAGIC) |
| return -ENOTTY; |
| if (_IOC_NR(cmd) > DEV_IOC_MAXNR) |
| return -ENOTTY; |
| |
| if (_IOC_DIR(cmd) & _IOC_READ) |
| err |= !access_ok(VERIFY_WRITE, (void __user *)arg, |
| _IOC_SIZE(cmd)); |
| if (_IOC_DIR(cmd) & _IOC_WRITE) |
| err |= !access_ok(VERIFY_READ, (void __user *)arg, |
| _IOC_SIZE(cmd)); |
| |
| if (err) |
| return -EFAULT; |
| |
| switch (cmd) { |
| case MTEE_CMD_OPEN_SESSION: |
| return tz_client_open_session(file, arg); |
| |
| case MTEE_CMD_OPEN_SESSION_WITH_TAG: |
| return tz_client_open_session_with_tag(file, arg); |
| |
| case MTEE_CMD_CLOSE_SESSION: |
| return tz_client_close_session(file, arg); |
| |
| case MTEE_CMD_TEE_SERVICE: |
| return tz_client_tee_service(file, arg, compat); |
| |
| case MTEE_CMD_SHM_REG: |
| return tz_client_reg_sharedmem(file, arg); |
| |
| case MTEE_CMD_SHM_REG_WITH_TAG: |
| return tz_client_reg_sharedmem_with_tag(file, arg); |
| |
| case MTEE_CMD_SHM_UNREG: |
| return tz_client_unreg_sharedmem(file, arg); |
| |
| default: |
| return -ENOTTY; |
| } |
| |
| return 0; |
| } |
| |
| |
| static long tz_client_ioctl(struct file *file, unsigned int cmd, |
| unsigned long arg) |
| { |
| return do_tz_client_ioctl(file, cmd, arg, 0); |
| } |
| |
| #if IS_ENABLED(CONFIG_COMPAT) |
| static long tz_client_ioctl_compat(struct file *file, unsigned int cmd, |
| unsigned long arg) |
| { |
| return do_tz_client_ioctl(file, cmd, (unsigned long)compat_ptr(arg), 1); |
| } |
| #endif |
| |
| |
| /* pm op funcstions */ |
| #ifdef TZ_PLAYREADY_SECURETIME_SUPPORT |
| #if IS_ENABLED(CONFIG_HAS_EARLYSUSPEND) |
| static int securetime_savefile(void) |
| { |
| int ret = 0; |
| |
| KREE_SESSION_HANDLE securetime_session = 0; |
| |
| ret = KREE_CreateSession(TZ_TA_PLAYREADY_UUID, &securetime_session); |
| if (ret != TZ_RESULT_SUCCESS) |
| pr_warn("[securetime]CreateSession error %d\n", ret); |
| |
| TEE_update_pr_time_infile(securetime_session); |
| |
| ret = KREE_CloseSession(securetime_session); |
| if (ret != TZ_RESULT_SUCCESS) |
| pr_warn("CloseSession error %d\n", ret); |
| |
| |
| return ret; |
| } |
| #endif /* EARLYSUSPEND */ |
| |
| static void st_shutdown(struct platform_device *pdev) |
| { |
| pr_info("[securetime]%s: kickoff\n", __func__); |
| } |
| #endif |
| |
| #if IS_ENABLED(CONFIG_PM_SLEEP) |
| static int tz_suspend(struct device *pdev) |
| { |
| int tzret; |
| |
| tzret = kree_pm_device_ops(MTEE_SUSPEND); |
| return (tzret != TZ_RESULT_SUCCESS) ? (-EBUSY) : (0); |
| } |
| |
| static int tz_resume(struct device *pdev) |
| { |
| int tzret; |
| |
| tzret = kree_pm_device_ops(MTEE_RESUME); |
| return (tzret != TZ_RESULT_SUCCESS) ? (-EBUSY) : (0); |
| } |
| #endif |
| |
| static int tz_suspend_late(struct device *pdev) |
| { |
| int tzret; |
| |
| tzret = kree_pm_device_ops(MTEE_SUSPEND_LATE); |
| return (tzret != TZ_RESULT_SUCCESS) ? (-EBUSY) : (0); |
| } |
| |
| static int tz_resume_early(struct device *pdev) |
| { |
| int tzret; |
| |
| tzret = kree_pm_device_ops(MTEE_RESUME_EARLY); |
| return (tzret != TZ_RESULT_SUCCESS) ? (-EBUSY) : (0); |
| } |
| |
| static const struct dev_pm_ops tz_pm_ops = { |
| .suspend_late = tz_suspend_late, |
| .freeze_late = tz_suspend_late, |
| .resume_early = tz_resume_early, |
| .thaw_early = tz_resume_early, |
| SET_SYSTEM_SLEEP_PM_OPS(tz_suspend, tz_resume) |
| }; |
| |
| static struct class *pTzClass; |
| static struct device *pTzDevice; |
| |
| #if IS_ENABLED(CONFIG_CMA) |
| static struct cma *tz_cma; |
| #endif /* CONFIG_CMA */ |
| |
| #if IS_ENABLED(CONFIG_MTEE_CMA_SECURE_MEMORY) |
| static struct page *secure_pages; |
| static size_t secure_size; |
| |
| /* TEE chunk memory allocate by REE service |
| */ |
| int KREE_ServGetChunkmemPool(u32 op, |
| u8 uparam[REE_SERVICE_BUFFER_SIZE]) |
| { |
| struct ree_service_chunk_mem *chunkmem; |
| |
| if (!tz_cma) |
| return TZ_RESULT_ERROR_OUT_OF_MEMORY; |
| |
| /* get parameters */ |
| chunkmem = (struct ree_service_chunk_mem *)uparam; |
| |
| if (secure_pages != NULL) { |
| pr_warn("%s() already allocated!\n", __func__); |
| return TZ_RESULT_ERROR_OUT_OF_MEMORY; |
| } |
| |
| secure_size = cma_get_size(tz_cma); |
| secure_pages = cma_alloc_large(tz_cma, secure_size/SZ_4K, |
| get_order(SZ_1M)); |
| if (secure_pages == NULL) { |
| pr_warn("%s() cma_alloc() failed!\n", __func__); |
| return TZ_RESULT_ERROR_OUT_OF_MEMORY; |
| } |
| chunkmem->size = secure_size; |
| chunkmem->chunkmem_pa = (uint64_t)page_to_phys(secure_pages); |
| |
| pr_info("%s() get @%llx [0x%zx]\n", __func__, |
| chunkmem->chunkmem_pa, secure_size); |
| |
| /* flush cache to avoid writing secure memory after allocation. */ |
| smp_inner_dcache_flush_all(); |
| |
| return TZ_RESULT_SUCCESS; |
| } |
| |
| int KREE_ServReleaseChunkmemPool(u32 op, |
| u8 uparam[REE_SERVICE_BUFFER_SIZE]) |
| { |
| if (secure_pages != NULL) { |
| phys_addr_t addr = page_to_phys(secure_pages); |
| |
| cma_release(tz_cma, secure_pages, |
| cma_get_size(tz_cma)>>PAGE_SHIFT); |
| pr_info("%s() release @%pax [0x%zx]\n", __func__, |
| &addr, secure_size); |
| secure_pages = NULL; |
| secure_size = 0; |
| return TZ_RESULT_SUCCESS; |
| } |
| return TZ_RESULT_ERROR_GENERIC; |
| } |
| |
| #ifndef NO_CMA_RELEASE_THROUGH_SHRINKER_FOR_EARLY_STAGE |
| |
| int KREE_TeeReleseChunkmemPool(void) |
| { |
| int ret; |
| KREE_SESSION_HANDLE mem_session; |
| |
| ret = KREE_CreateSession(TZ_TA_MEM_UUID, &mem_session); |
| if (ret != TZ_RESULT_SUCCESS) { |
| pr_warn("Create memory session error %d\n", ret); |
| return ret; |
| } |
| |
| ret = KREE_TeeServiceCall(mem_session, TZCMD_MEM_RELEASE_SECURECM, |
| TZPT_NONE, NULL); |
| if (ret != TZ_RESULT_SUCCESS) |
| pr_warn("Release Secure CM failed, ret = %d\n", ret); |
| |
| KREE_CloseSession(mem_session); |
| |
| return ret; |
| } |
| |
| DECLARE_COMPLETION(tz_cm_shrinker_work); |
| DECLARE_COMPLETION(tz_cm_shrinker_finish_work); |
| |
| int tz_cm_shrinker_thread(void *data) |
| { |
| do { |
| if (wait_for_completion_interruptible( |
| &tz_cm_shrinker_work) != 0) |
| continue; |
| |
| /* TeeReleaseChunkmemPool will call */ |
| /* ree-service call to do cma_release() */ |
| if (KREE_TeeReleseChunkmemPool() != TZ_RESULT_SUCCESS) |
| pr_warn("Can't free tz chunk memory\n"); |
| |
| complete(&tz_cm_shrinker_finish_work); |
| } while (1); |
| } |
| |
| static int KREE_IsTeeChunkmemPoolReleasable(int *releasable) |
| { |
| int ret; |
| KREE_SESSION_HANDLE mem_session; |
| union MTEEC_PARAM param[4]; |
| |
| if (releasable == NULL) |
| return TZ_RESULT_ERROR_BAD_FORMAT; |
| |
| ret = KREE_CreateSession(TZ_TA_MEM_UUID, &mem_session); |
| if (ret != TZ_RESULT_SUCCESS) { |
| pr_warn("Create memory session error %d\n", ret); |
| return ret; |
| } |
| |
| ret = KREE_TeeServiceCall(mem_session, TZCMD_MEM_USAGE_SECURECM, |
| TZPT_VALUE_OUTPUT, param); |
| if (ret != TZ_RESULT_SUCCESS) |
| pr_warn("Is Secure CM releasable failed, ret = %d\n", ret); |
| |
| *releasable = param[0].value.a; |
| |
| KREE_CloseSession(mem_session); |
| |
| return ret; |
| } |
| |
| static unsigned long tz_cm_count(struct shrinker *s, |
| struct shrink_control *sc) |
| { |
| int releasable; |
| |
| if (secure_pages == NULL) |
| return 0; |
| |
| if (KREE_IsTeeChunkmemPoolReleasable(&releasable) != TZ_RESULT_SUCCESS) |
| return 0; |
| |
| if (releasable == 0) |
| return 0; |
| |
| return secure_size / SZ_4K; |
| } |
| static unsigned long tz_cm_scan(struct shrinker *s, |
| struct shrink_control *sc) |
| { |
| unsigned long release_objects_count = secure_size; |
| |
| if (secure_pages == NULL) |
| return SHRINK_STOP; |
| |
| complete(&tz_cm_shrinker_work); |
| wait_for_completion(&tz_cm_shrinker_finish_work); |
| |
| if (secure_pages != NULL) |
| return SHRINK_STOP; |
| |
| return release_objects_count; |
| } |
| |
| static struct shrinker tz_cm_shrinker = { |
| .scan_objects = tz_cm_scan, |
| .count_objects = tz_cm_count, |
| .seeks = DEFAULT_SEEKS |
| }; |
| |
| #else |
| |
| static int KREE_TeeTriggerChunkmemAllocation(void) |
| { |
| int ret; |
| KREE_SESSION_HANDLE mem_session; |
| KREE_SECUREMEM_HANDLE cm_handle; |
| |
| ret = KREE_CreateSession(TZ_TA_MEM_UUID, &mem_session); |
| if (ret != TZ_RESULT_SUCCESS) { |
| pr_warn("Create memory session error %d\n", ret); |
| return ret; |
| } |
| |
| ret = KREE_AllocSecurechunkmem(mem_session, &cm_handle, 1, 4096); |
| if (ret != TZ_RESULT_SUCCESS) { |
| pr_warn("Allocate securechunkmem error %d\n", ret); |
| return ret; |
| } |
| |
| ret = KREE_UnreferenceSecurechunkmem(mem_session, cm_handle); |
| if (ret != TZ_RESULT_SUCCESS) { |
| pr_warn("Unreference securechunkmem error %d\n", ret); |
| return ret; |
| } |
| |
| KREE_CloseSession(mem_session); |
| |
| return ret; |
| } |
| #endif /* NO_CMA_RELEASE_THROUGH_SHRINKER_FOR_EARLY_STAGE */ |
| |
| #endif |
| |
| static int mtee_probe(struct platform_device *pdev) |
| { |
| int ret; |
| int tzret; |
| #ifdef ENABLE_INC_ONLY_COUNTER |
| struct task_struct *thread; |
| #endif |
| #ifdef TZ_SECURETIME_SUPPORT |
| struct task_struct *thread_securetime_gb; |
| #endif |
| #ifdef TZ_PLAYREADY_SECURETIME_SUPPORT |
| struct task_struct *thread_securetime; |
| #if IS_ENABLED(CONFIG_HAS_EARLYSUSPEND) |
| register_early_suspend(&securetime_early_suspend); |
| #endif |
| #endif |
| |
| #if IS_ENABLED(CONFIG_MTEE_CMA_SECURE_MEMORY) && \ |
| !defined(NO_CMA_RELEASE_THROUGH_SHRINKER_FOR_EARLY_STAGE) |
| struct task_struct *thread_tz_cm_shrinker; |
| #endif |
| |
| #if IS_ENABLED(CONFIG_OF) |
| struct device_node *parent_node; |
| |
| if (pdev->dev.of_node) { |
| parent_node = of_irq_find_parent(pdev->dev.of_node); |
| if (parent_node) |
| kree_set_sysirq_node(parent_node); |
| else |
| pr_warn("can't find interrupt-parent device node from mtee\n"); |
| } else |
| pr_warn("No mtee device node\n"); |
| |
| mtee_clks_init(pdev); |
| mtee_pms_init(pdev); |
| #endif /* CONFIG_OF */ |
| |
| tz_client_dev = MKDEV(MAJOR_DEV_NUM, 0); |
| |
| pr_debug(" init\n"); |
| |
| ret = register_chrdev_region(tz_client_dev, 1, TZ_DEV_NAME); |
| if (ret) { |
| pr_warn("[%s] register device failed, ret:%d\n", |
| MODULE_NAME, ret); |
| return ret; |
| } |
| |
| /* initialize the device structure and register the device */ |
| cdev_init(&tz_client_cdev, &tz_client_fops); |
| tz_client_cdev.owner = THIS_MODULE; |
| |
| ret = cdev_add(&tz_client_cdev, tz_client_dev, 1); |
| if (ret < 0) { |
| pr_warn("[%s] could not allocate chrdev for the device, ret:%d\n", |
| MODULE_NAME, |
| ret); |
| return ret; |
| } |
| |
| tzret = KREE_InitTZ(); |
| if (tzret != TZ_RESULT_SUCCESS) { |
| pr_warn("tz_client_init: TZ Failed %d\n", (int)tzret); |
| WARN_ON(tzret != TZ_RESULT_SUCCESS); |
| } |
| |
| kree_irq_init(); |
| kree_pm_init(); |
| |
| #ifdef ENABLE_INC_ONLY_COUNTER |
| thread = kthread_run(update_counter_thread, NULL, "update_tz_counter"); |
| #endif |
| |
| pr_debug("tz_client_init: successfully\n"); |
| |
| /* tz_test(); */ |
| |
| /* create /dev/trustzone automatically */ |
| pTzClass = class_create(THIS_MODULE, TZ_DEV_NAME); |
| if (IS_ERR(pTzClass)) { |
| int ret = PTR_ERR(pTzClass); |
| |
| pr_warn("[%s] could not create class for the device, ret:%d\n", |
| MODULE_NAME, |
| ret); |
| return ret; |
| } |
| pTzDevice = device_create(pTzClass, NULL, tz_client_dev, NULL, |
| TZ_DEV_NAME); |
| #ifdef TZ_PLAYREADY_SECURETIME_SUPPORT |
| thread_securetime = kthread_run(update_securetime_thread, NULL, |
| "update_securetime"); |
| #endif |
| |
| #ifdef TZ_SECURETIME_SUPPORT |
| thread_securetime_gb = kthread_run(update_securetime_thread_gb, NULL, |
| "update_securetime_gb"); |
| #endif |
| |
| #if IS_ENABLED(CONFIG_MTEE_CMA_SECURE_MEMORY) |
| #ifndef NO_CMA_RELEASE_THROUGH_SHRINKER_FOR_EARLY_STAGE |
| thread_tz_cm_shrinker = kthread_run(tz_cm_shrinker_thread, NULL, |
| "tz_cm_shrinker"); |
| register_shrinker(&tz_cm_shrinker); |
| #endif /* NO_CMA_RELEASE_THROUGH_SHRINKER_FOR_EARLY_STAGE */ |
| #endif /* CONFIG_MTEE_CMA_SECURE_MEMORY */ |
| |
| return 0; |
| } |
| |
| static const struct of_device_id mtee_of_match[] = { |
| { .compatible = "mediatek,mtee", }, |
| {} |
| }; |
| MODULE_DEVICE_TABLE(of, mtee_of_match); |
| |
| /* add tz virtual driver for suspend/resume support */ |
| static struct platform_driver tz_driver = { |
| .probe = mtee_probe, |
| .driver = { |
| .name = TZ_DEVNAME, |
| .pm = &tz_pm_ops, |
| .owner = THIS_MODULE, |
| #if IS_ENABLED(CONFIG_OF) |
| .of_match_table = mtee_of_match, |
| #endif |
| }, |
| #ifdef TZ_PLAYREADY_SECURETIME_SUPPORT |
| .shutdown = st_shutdown, |
| #endif |
| }; |
| |
| /* CMA */ |
| #if IS_ENABLED(CONFIG_CMA) |
| #include <linux/of_address.h> |
| #include <linux/of_reserved_mem.h> |
| #include <linux/of_fdt.h> |
| |
| static int __init rmem_tz_sec_setup(struct reserved_mem *rmem) |
| { |
| unsigned long node = rmem->fdt_node; |
| struct cma *cma; |
| int err; |
| |
| if (!of_get_flat_dt_prop(node, "reusable", NULL) || |
| of_get_flat_dt_prop(node, "no-map", NULL)) |
| return -EINVAL; |
| |
| if (!rmem->size) { |
| pr_err("%s: size == 0\n", __func__); |
| return -EINVAL; |
| } |
| |
| err = cma_init_reserved_mem(rmem->base, rmem->size, 0, "tz_cma", &cma); |
| if (err) { |
| pr_err("Reserved memory: unable to setup CMA region\n"); |
| return err; |
| } |
| |
| tz_cma = cma; |
| pr_info("Reserved memory: created CMA memory pool at %pa, size %ld MiB\n", |
| &rmem->base, (unsigned long)rmem->size / SZ_1M); |
| |
| return 0; |
| } |
| RESERVEDMEM_OF_DECLARE(tz_sec, "shared-secure-pool", rmem_tz_sec_setup); |
| #endif /* CONFIG_CMA */ |
| |
| /****************************************************************************** |
| * register_tz_driver |
| * |
| * DESCRIPTION: |
| * register the device driver ! |
| * |
| * PARAMETERS: |
| * None |
| * |
| * RETURNS: |
| * 0 for success |
| * |
| * NOTES: |
| * None |
| * |
| ******************************************************************************/ |
| static int __init register_tz_driver(void) |
| { |
| int ret = 0; |
| |
| #ifndef CONFIG_OF |
| if (platform_device_register(&tz_device)) { |
| ret = -ENODEV; |
| pr_warn("[%s] could not register device for the device, ret:%d\n", |
| MODULE_NAME, |
| ret); |
| return ret; |
| } |
| #endif |
| |
| if (platform_driver_register(&tz_driver)) { |
| ret = -ENODEV; |
| pr_warn("[%s] could not register device for the device, ret:%d\n", |
| MODULE_NAME, |
| ret); |
| #ifndef CONFIG_OF |
| platform_device_unregister(&tz_device); |
| #endif |
| return ret; |
| } |
| |
| return ret; |
| } |
| #ifdef TZ_PLAYREADY_SECURETIME_SUPPORT |
| |
| #if IS_ENABLED(CONFIG_HAS_EARLYSUSPEND) |
| |
| static void st_early_suspend(struct early_suspend *h) |
| { |
| pr_debug("%s: start\n", __func__); |
| securetime_savefile(); |
| } |
| |
| static void st_late_resume(struct early_suspend *h) |
| { |
| int ret = 0; |
| KREE_SESSION_HANDLE securetime_session = 0; |
| |
| ret = KREE_CreateSession(TZ_TA_PLAYREADY_UUID, &securetime_session); |
| if (ret != TZ_RESULT_SUCCESS) |
| pr_warn("[securetime]CreateSession error %d\n", ret); |
| |
| TEE_update_pr_time_intee(securetime_session); |
| ret = KREE_CloseSession(securetime_session); |
| if (ret != TZ_RESULT_SUCCESS) |
| pr_warn("[securetime]CloseSession error %d\n", ret); |
| } |
| |
| static struct early_suspend securetime_early_suspend = { |
| .level = 258, |
| .suspend = st_early_suspend, |
| .resume = st_late_resume, |
| }; |
| #endif |
| #endif |
| |
| /****************************************************************************** |
| * tz_client_init |
| * |
| * DESCRIPTION: |
| * Init the device driver ! |
| * |
| * PARAMETERS: |
| * None |
| * |
| * RETURNS: |
| * 0 for success |
| * |
| * NOTES: |
| * None |
| * |
| ******************************************************************************/ |
| static int __init tz_client_init(void) |
| { |
| int ret = 0; |
| |
| ret = register_tz_driver(); |
| if (ret) { |
| pr_warn("[%s] register device/driver failed, ret:%d\n", |
| MODULE_NAME, |
| ret); |
| return ret; |
| } |
| |
| return 0; |
| } |
| #if IS_ENABLED(CONFIG_TRUSTY) |
| device_initcall(tz_client_init); |
| #else |
| arch_initcall(tz_client_init); |
| #endif |
| |
| #ifdef NO_CMA_RELEASE_THROUGH_SHRINKER_FOR_EARLY_STAGE |
| static int __init tz_client_cma_alloc_init(void) |
| { |
| /* trigger allocation chunk memory in initialization flow */ |
| KREE_TeeTriggerChunkmemAllocation(); |
| return 0; |
| } |
| /* use late_initcall to be triggered after M4U init is done. */ |
| late_initcall(tz_client_cma_alloc_init); |
| #endif |