From abc18728f7736823a28540346129bbeafb3b5866 Mon Sep 17 00:00:00 2001 From: Srikant Patnaik Date: Sun, 11 Jan 2015 19:41:45 +0530 Subject: This commit is dedicacted to MALI, UMP and MALI_DRM. This includes MALI-r3p2 from other source --- .../ump/linux/license/gpl/ump_kernel_license.h | 30 ++ drivers/gpu/mali/ump/linux/ump_ioctl.h | 53 +++ drivers/gpu/mali/ump/linux/ump_kernel_linux.c | 447 +++++++++++++++++++++ drivers/gpu/mali/ump/linux/ump_kernel_linux.h | 18 + .../linux/ump_kernel_memory_backend_dedicated.c | 271 +++++++++++++ .../linux/ump_kernel_memory_backend_dedicated.h | 23 ++ .../mali/ump/linux/ump_kernel_memory_backend_os.c | 235 +++++++++++ .../mali/ump/linux/ump_kernel_memory_backend_os.h | 23 ++ .../gpu/mali/ump/linux/ump_kernel_random_mapping.c | 207 ++++++++++ .../gpu/mali/ump/linux/ump_kernel_random_mapping.h | 84 ++++ drivers/gpu/mali/ump/linux/ump_memory_backend.c | 65 +++ drivers/gpu/mali/ump/linux/ump_osk_atomics.c | 27 ++ drivers/gpu/mali/ump/linux/ump_osk_low_level_mem.c | 314 +++++++++++++++ drivers/gpu/mali/ump/linux/ump_osk_misc.c | 36 ++ drivers/gpu/mali/ump/linux/ump_ukk_ref_wrappers.c | 71 ++++ drivers/gpu/mali/ump/linux/ump_ukk_ref_wrappers.h | 34 ++ drivers/gpu/mali/ump/linux/ump_ukk_wrappers.c | 280 +++++++++++++ drivers/gpu/mali/ump/linux/ump_ukk_wrappers.h | 46 +++ 18 files changed, 2264 insertions(+) create mode 100644 drivers/gpu/mali/ump/linux/license/gpl/ump_kernel_license.h create mode 100644 drivers/gpu/mali/ump/linux/ump_ioctl.h create mode 100644 drivers/gpu/mali/ump/linux/ump_kernel_linux.c create mode 100644 drivers/gpu/mali/ump/linux/ump_kernel_linux.h create mode 100644 drivers/gpu/mali/ump/linux/ump_kernel_memory_backend_dedicated.c create mode 100644 drivers/gpu/mali/ump/linux/ump_kernel_memory_backend_dedicated.h create mode 100644 drivers/gpu/mali/ump/linux/ump_kernel_memory_backend_os.c create mode 100644 drivers/gpu/mali/ump/linux/ump_kernel_memory_backend_os.h create mode 100644 drivers/gpu/mali/ump/linux/ump_kernel_random_mapping.c create mode 100644 drivers/gpu/mali/ump/linux/ump_kernel_random_mapping.h create mode 100644 drivers/gpu/mali/ump/linux/ump_memory_backend.c create mode 100644 drivers/gpu/mali/ump/linux/ump_osk_atomics.c create mode 100644 drivers/gpu/mali/ump/linux/ump_osk_low_level_mem.c create mode 100644 drivers/gpu/mali/ump/linux/ump_osk_misc.c create mode 100644 drivers/gpu/mali/ump/linux/ump_ukk_ref_wrappers.c create mode 100644 drivers/gpu/mali/ump/linux/ump_ukk_ref_wrappers.h create mode 100644 drivers/gpu/mali/ump/linux/ump_ukk_wrappers.c create mode 100644 drivers/gpu/mali/ump/linux/ump_ukk_wrappers.h (limited to 'drivers/gpu/mali/ump/linux') diff --git a/drivers/gpu/mali/ump/linux/license/gpl/ump_kernel_license.h b/drivers/gpu/mali/ump/linux/license/gpl/ump_kernel_license.h new file mode 100644 index 00000000..567d803b --- /dev/null +++ b/drivers/gpu/mali/ump/linux/license/gpl/ump_kernel_license.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2010, 2013-2014 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_kernel_license.h + * Defines for the macro MODULE_LICENSE. + */ + +#ifndef __UMP_KERNEL_LICENSE_H__ +#define __UMP_KERNEL_LICENSE_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#define UMP_KERNEL_LINUX_LICENSE "GPL" +#define UMP_LICENSE_IS_GPL 1 + +#ifdef __cplusplus +} +#endif + +#endif /* __UMP_KERNEL_LICENSE_H__ */ diff --git a/drivers/gpu/mali/ump/linux/ump_ioctl.h b/drivers/gpu/mali/ump/linux/ump_ioctl.h new file mode 100644 index 00000000..239e8ab3 --- /dev/null +++ b/drivers/gpu/mali/ump/linux/ump_ioctl.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2010-2014 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __UMP_IOCTL_H__ +#define __UMP_IOCTL_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#include + +#ifndef __user +#define __user +#endif + + +/** + * @file UMP_ioctl.h + * This file describes the interface needed to use the Linux device driver. + * The interface is used by the userpace UMP driver. + */ + +#define UMP_IOCTL_NR 0x90 + + +#define UMP_IOC_QUERY_API_VERSION _IOR(UMP_IOCTL_NR, _UMP_IOC_QUERY_API_VERSION, _ump_uk_api_version_s) +#define UMP_IOC_ALLOCATE _IOWR(UMP_IOCTL_NR, _UMP_IOC_ALLOCATE, _ump_uk_allocate_s) +#define UMP_IOC_RELEASE _IOR(UMP_IOCTL_NR, _UMP_IOC_RELEASE, _ump_uk_release_s) +#define UMP_IOC_SIZE_GET _IOWR(UMP_IOCTL_NR, _UMP_IOC_SIZE_GET, _ump_uk_size_get_s) +#define UMP_IOC_MSYNC _IOW(UMP_IOCTL_NR, _UMP_IOC_MSYNC, _ump_uk_msync_s) + +#define UMP_IOC_CACHE_OPERATIONS_CONTROL _IOW(UMP_IOCTL_NR, _UMP_IOC_CACHE_OPERATIONS_CONTROL, _ump_uk_cache_operations_control_s) +#define UMP_IOC_SWITCH_HW_USAGE _IOW(UMP_IOCTL_NR, _UMP_IOC_SWITCH_HW_USAGE, _ump_uk_switch_hw_usage_s) +#define UMP_IOC_LOCK _IOW(UMP_IOCTL_NR, _UMP_IOC_LOCK, _ump_uk_lock_s) +#define UMP_IOC_UNLOCK _IOW(UMP_IOCTL_NR, _UMP_IOC_UNLOCK, _ump_uk_unlock_s) + + +#ifdef __cplusplus +} +#endif + +#endif /* __UMP_IOCTL_H__ */ diff --git a/drivers/gpu/mali/ump/linux/ump_kernel_linux.c b/drivers/gpu/mali/ump/linux/ump_kernel_linux.c new file mode 100644 index 00000000..c99caa6d --- /dev/null +++ b/drivers/gpu/mali/ump/linux/ump_kernel_linux.c @@ -0,0 +1,447 @@ +/* + * Copyright (C) 2010-2014 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include /* kernel module definitions */ +#include /* file system operations */ +#include /* character device definitions */ +#include /* request_mem_region */ +#include /* memory management functions and types */ +#include /* user space access */ +#include +#include +#include + +#include "arch/config.h" /* Configuration for current platform. The symlinc for arch is set by Makefile */ +#include "ump_ioctl.h" +#include "ump_kernel_common.h" +#include "ump_kernel_interface.h" +#include "ump_kernel_interface_ref_drv.h" +#include "ump_kernel_descriptor_mapping.h" +#include "ump_kernel_memory_backend.h" +#include "ump_kernel_memory_backend_os.h" +#include "ump_kernel_memory_backend_dedicated.h" +#include "ump_kernel_license.h" + +#include "ump_osk.h" +#include "ump_ukk.h" +#include "ump_uk_types.h" +#include "ump_ukk_wrappers.h" +#include "ump_ukk_ref_wrappers.h" + + +/* Module parameter to control log level */ +int ump_debug_level = 2; +module_param(ump_debug_level, int, S_IRUSR | S_IWUSR | S_IWGRP | S_IRGRP | S_IROTH); /* rw-rw-r-- */ +MODULE_PARM_DESC(ump_debug_level, "Higher number, more dmesg output"); + +/* By default the module uses any available major, but it's possible to set it at load time to a specific number */ +int ump_major = 0; +module_param(ump_major, int, S_IRUGO); /* r--r--r-- */ +MODULE_PARM_DESC(ump_major, "Device major number"); + +/* Name of the UMP device driver */ +static char ump_dev_name[] = "ump"; /* should be const, but the functions we call requires non-cost */ + + +#if UMP_LICENSE_IS_GPL +static struct dentry *ump_debugfs_dir = NULL; +#endif + +/* + * The data which we attached to each virtual memory mapping request we get. + * Each memory mapping has a reference to the UMP memory it maps. + * We release this reference when the last memory mapping is unmapped. + */ +typedef struct ump_vma_usage_tracker { + int references; + ump_dd_handle handle; +} ump_vma_usage_tracker; + +struct ump_device { + struct cdev cdev; +#if UMP_LICENSE_IS_GPL + struct class *ump_class; +#endif +}; + +/* The global variable containing the global device data */ +static struct ump_device ump_device; + + +/* Forward declare static functions */ +static int ump_file_open(struct inode *inode, struct file *filp); +static int ump_file_release(struct inode *inode, struct file *filp); +#ifdef HAVE_UNLOCKED_IOCTL +static long ump_file_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); +#else +static int ump_file_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); +#endif +static int ump_file_mmap(struct file *filp, struct vm_area_struct *vma); + + +/* This variable defines the file operations this UMP device driver offer */ +static struct file_operations ump_fops = { + .owner = THIS_MODULE, + .open = ump_file_open, + .release = ump_file_release, +#ifdef HAVE_UNLOCKED_IOCTL + .unlocked_ioctl = ump_file_ioctl, +#else + .ioctl = ump_file_ioctl, +#endif + .mmap = ump_file_mmap +}; + + +/* This function is called by Linux to initialize this module. + * All we do is initialize the UMP device driver. + */ +static int ump_initialize_module(void) +{ + _mali_osk_errcode_t err; + + DBG_MSG(2, ("Inserting UMP device driver. Compiled: %s, time: %s\n", __DATE__, __TIME__)); + + err = ump_kernel_constructor(); + if (_MALI_OSK_ERR_OK != err) { + MSG_ERR(("UMP device driver init failed\n")); + return map_errcode(err); + } + + MSG(("UMP device driver %s loaded\n", SVN_REV_STRING)); + return 0; +} + + + +/* + * This function is called by Linux to unload/terminate/exit/cleanup this module. + * All we do is terminate the UMP device driver. + */ +static void ump_cleanup_module(void) +{ + DBG_MSG(2, ("Unloading UMP device driver\n")); + ump_kernel_destructor(); + DBG_MSG(2, ("Module unloaded\n")); +} + + + +static ssize_t ump_memory_used_read(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos) +{ + char buf[64]; + size_t r; + u32 mem = _ump_ukk_report_memory_usage(); + + r = snprintf(buf, 64, "%u\n", mem); + return simple_read_from_buffer(ubuf, cnt, ppos, buf, r); +} + +static const struct file_operations ump_memory_usage_fops = { + .owner = THIS_MODULE, + .read = ump_memory_used_read, +}; + +/* + * Initialize the UMP device driver. + */ +int ump_kernel_device_initialize(void) +{ + int err; + dev_t dev = 0; +#if UMP_LICENSE_IS_GPL + ump_debugfs_dir = debugfs_create_dir(ump_dev_name, NULL); + if (ERR_PTR(-ENODEV) == ump_debugfs_dir) { + ump_debugfs_dir = NULL; + } else { + debugfs_create_file("memory_usage", 0400, ump_debugfs_dir, NULL, &ump_memory_usage_fops); + } +#endif + + if (0 == ump_major) { + /* auto select a major */ + err = alloc_chrdev_region(&dev, 0, 1, ump_dev_name); + ump_major = MAJOR(dev); + } else { + /* use load time defined major number */ + dev = MKDEV(ump_major, 0); + err = register_chrdev_region(dev, 1, ump_dev_name); + } + + if (0 == err) { + memset(&ump_device, 0, sizeof(ump_device)); + + /* initialize our char dev data */ + cdev_init(&ump_device.cdev, &ump_fops); + ump_device.cdev.owner = THIS_MODULE; + ump_device.cdev.ops = &ump_fops; + + /* register char dev with the kernel */ + err = cdev_add(&ump_device.cdev, dev, 1/*count*/); + if (0 == err) { + +#if UMP_LICENSE_IS_GPL + ump_device.ump_class = class_create(THIS_MODULE, ump_dev_name); + if (IS_ERR(ump_device.ump_class)) { + err = PTR_ERR(ump_device.ump_class); + } else { + struct device *mdev; + mdev = device_create(ump_device.ump_class, NULL, dev, NULL, ump_dev_name); + if (!IS_ERR(mdev)) { + return 0; + } + + err = PTR_ERR(mdev); + } + cdev_del(&ump_device.cdev); +#else + return 0; +#endif + } + + unregister_chrdev_region(dev, 1); + } + + return err; +} + + + +/* + * Terminate the UMP device driver + */ +void ump_kernel_device_terminate(void) +{ + dev_t dev = MKDEV(ump_major, 0); + +#if UMP_LICENSE_IS_GPL + device_destroy(ump_device.ump_class, dev); + class_destroy(ump_device.ump_class); +#endif + + /* unregister char device */ + cdev_del(&ump_device.cdev); + + /* free major */ + unregister_chrdev_region(dev, 1); + +#if UMP_LICENSE_IS_GPL + if (ump_debugfs_dir) + debugfs_remove_recursive(ump_debugfs_dir); +#endif +} + +/* + * Open a new session. User space has called open() on us. + */ +static int ump_file_open(struct inode *inode, struct file *filp) +{ + struct ump_session_data *session_data; + _mali_osk_errcode_t err; + + /* input validation */ + if (0 != MINOR(inode->i_rdev)) { + MSG_ERR(("Minor not zero in ump_file_open()\n")); + return -ENODEV; + } + + /* Call the OS-Independent UMP Open function */ + err = _ump_ukk_open((void **) &session_data); + if (_MALI_OSK_ERR_OK != err) { + MSG_ERR(("Ump failed to open a new session\n")); + return map_errcode(err); + } + + filp->private_data = (void *)session_data; + filp->f_pos = 0; + + return 0; /* success */ +} + + + +/* + * Close a session. User space has called close() or crashed/terminated. + */ +static int ump_file_release(struct inode *inode, struct file *filp) +{ + _mali_osk_errcode_t err; + + err = _ump_ukk_close((void **) &filp->private_data); + if (_MALI_OSK_ERR_OK != err) { + return map_errcode(err); + } + + return 0; /* success */ +} + + + +/* + * Handle IOCTL requests. + */ +#ifdef HAVE_UNLOCKED_IOCTL +static long ump_file_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +#else +static int ump_file_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) +#endif +{ + int err = -ENOTTY; + void __user *argument; + struct ump_session_data *session_data; + +#ifndef HAVE_UNLOCKED_IOCTL + (void)inode; /* inode not used */ +#endif + + session_data = (struct ump_session_data *)filp->private_data; + if (NULL == session_data) { + MSG_ERR(("No session data attached to file object\n")); + return -ENOTTY; + } + + /* interpret the argument as a user pointer to something */ + argument = (void __user *)arg; + + switch (cmd) { + case UMP_IOC_QUERY_API_VERSION: + err = ump_get_api_version_wrapper((u32 __user *)argument, session_data); + break; + + case UMP_IOC_ALLOCATE : + err = ump_allocate_wrapper((u32 __user *)argument, session_data); + break; + + case UMP_IOC_RELEASE: + err = ump_release_wrapper((u32 __user *)argument, session_data); + break; + + case UMP_IOC_SIZE_GET: + err = ump_size_get_wrapper((u32 __user *)argument, session_data); + break; + + case UMP_IOC_MSYNC: + err = ump_msync_wrapper((u32 __user *)argument, session_data); + break; + + case UMP_IOC_CACHE_OPERATIONS_CONTROL: + err = ump_cache_operations_control_wrapper((u32 __user *)argument, session_data); + break; + + case UMP_IOC_SWITCH_HW_USAGE: + err = ump_switch_hw_usage_wrapper((u32 __user *)argument, session_data); + break; + + case UMP_IOC_LOCK: + err = ump_lock_wrapper((u32 __user *)argument, session_data); + break; + + case UMP_IOC_UNLOCK: + err = ump_unlock_wrapper((u32 __user *)argument, session_data); + break; + + default: + DBG_MSG(1, ("No handler for IOCTL. cmd: 0x%08x, arg: 0x%08lx\n", cmd, arg)); + err = -EFAULT; + break; + } + + return err; +} + +int map_errcode(_mali_osk_errcode_t err) +{ + switch (err) { + case _MALI_OSK_ERR_OK : + return 0; + case _MALI_OSK_ERR_FAULT: + return -EFAULT; + case _MALI_OSK_ERR_INVALID_FUNC: + return -ENOTTY; + case _MALI_OSK_ERR_INVALID_ARGS: + return -EINVAL; + case _MALI_OSK_ERR_NOMEM: + return -ENOMEM; + case _MALI_OSK_ERR_TIMEOUT: + return -ETIMEDOUT; + case _MALI_OSK_ERR_RESTARTSYSCALL: + return -ERESTARTSYS; + case _MALI_OSK_ERR_ITEM_NOT_FOUND: + return -ENOENT; + default: + return -EFAULT; + } +} + +/* + * Handle from OS to map specified virtual memory to specified UMP memory. + */ +static int ump_file_mmap(struct file *filp, struct vm_area_struct *vma) +{ + _ump_uk_map_mem_s args; + _mali_osk_errcode_t err; + struct ump_session_data *session_data; + + /* Validate the session data */ + session_data = (struct ump_session_data *)filp->private_data; + if (NULL == session_data) { + MSG_ERR(("mmap() called without any session data available\n")); + return -EFAULT; + } + + /* Re-pack the arguments that mmap() packed for us */ + args.ctx = session_data; + args.phys_addr = 0; + args.size = vma->vm_end - vma->vm_start; + args._ukk_private = vma; + args.secure_id = vma->vm_pgoff; + args.is_cached = 0; + + if (!(vma->vm_flags & VM_SHARED)) { + args.is_cached = 1; + vma->vm_flags = vma->vm_flags | VM_SHARED | VM_MAYSHARE ; + DBG_MSG(3, ("UMP Map function: Forcing the CPU to use cache\n")); + } + /* By setting this flag, during a process fork; the child process will not have the parent UMP mappings */ + vma->vm_flags |= VM_DONTCOPY; + + DBG_MSG(4, ("UMP vma->flags: %x\n", vma->vm_flags)); + + /* Call the common mmap handler */ + err = _ump_ukk_map_mem(&args); + if (_MALI_OSK_ERR_OK != err) { + MSG_ERR(("_ump_ukk_map_mem() failed in function ump_file_mmap()")); + return map_errcode(err); + } + + return 0; /* success */ +} + +/* Export UMP kernel space API functions */ +EXPORT_SYMBOL(ump_dd_secure_id_get); +EXPORT_SYMBOL(ump_dd_handle_create_from_secure_id); +EXPORT_SYMBOL(ump_dd_phys_block_count_get); +EXPORT_SYMBOL(ump_dd_phys_block_get); +EXPORT_SYMBOL(ump_dd_phys_blocks_get); +EXPORT_SYMBOL(ump_dd_size_get); +EXPORT_SYMBOL(ump_dd_reference_add); +EXPORT_SYMBOL(ump_dd_reference_release); + +/* Export our own extended kernel space allocator */ +EXPORT_SYMBOL(ump_dd_handle_create_from_phys_blocks); + +/* Setup init and exit functions for this module */ +module_init(ump_initialize_module); +module_exit(ump_cleanup_module); + +/* And some module informatio */ +MODULE_LICENSE(UMP_KERNEL_LINUX_LICENSE); +MODULE_AUTHOR("ARM Ltd."); +MODULE_VERSION(SVN_REV_STRING); diff --git a/drivers/gpu/mali/ump/linux/ump_kernel_linux.h b/drivers/gpu/mali/ump/linux/ump_kernel_linux.h new file mode 100644 index 00000000..c071b77b --- /dev/null +++ b/drivers/gpu/mali/ump/linux/ump_kernel_linux.h @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2010-2014 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __UMP_KERNEL_LINUX_H__ +#define __UMP_KERNEL_LINUX_H__ + +int ump_kernel_device_initialize(void); +void ump_kernel_device_terminate(void); + + +#endif /* __UMP_KERNEL_H__ */ diff --git a/drivers/gpu/mali/ump/linux/ump_kernel_memory_backend_dedicated.c b/drivers/gpu/mali/ump/linux/ump_kernel_memory_backend_dedicated.c new file mode 100644 index 00000000..32ea8b0b --- /dev/null +++ b/drivers/gpu/mali/ump/linux/ump_kernel_memory_backend_dedicated.c @@ -0,0 +1,271 @@ +/* + * Copyright (C) 2010-2011, 2013-2014 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/* needed to detect kernel version specific code */ +#include + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) +#include +#else /* pre 2.6.26 the file was in the arch specific location */ +#include +#endif + +#include +#include +#include +#include +#include "ump_kernel_common.h" +#include "ump_kernel_memory_backend.h" + + + +#define UMP_BLOCK_SIZE (256UL * 1024UL) /* 256kB, remember to keep the ()s */ + + + +typedef struct block_info { + struct block_info *next; +} block_info; + + + +typedef struct block_allocator { + struct semaphore mutex; + block_info *all_blocks; + block_info *first_free; + u32 base; + u32 num_blocks; + u32 num_free; +} block_allocator; + + +static void block_allocator_shutdown(ump_memory_backend *backend); +static int block_allocator_allocate(void *ctx, ump_dd_mem *mem); +static void block_allocator_release(void *ctx, ump_dd_mem *handle); +static inline u32 get_phys(block_allocator *allocator, block_info *block); +static u32 block_allocator_stat(struct ump_memory_backend *backend); + + + +/* + * Create dedicated memory backend + */ +ump_memory_backend *ump_block_allocator_create(u32 base_address, u32 size) +{ + ump_memory_backend *backend; + block_allocator *allocator; + u32 usable_size; + u32 num_blocks; + + usable_size = (size + UMP_BLOCK_SIZE - 1) & ~(UMP_BLOCK_SIZE - 1); + num_blocks = usable_size / UMP_BLOCK_SIZE; + + if (0 == usable_size) { + DBG_MSG(1, ("Memory block of size %u is unusable\n", size)); + return NULL; + } + + DBG_MSG(5, ("Creating dedicated UMP memory backend. Base address: 0x%08x, size: 0x%08x\n", base_address, size)); + DBG_MSG(6, ("%u usable bytes which becomes %u blocks\n", usable_size, num_blocks)); + + backend = kzalloc(sizeof(ump_memory_backend), GFP_KERNEL); + if (NULL != backend) { + allocator = kmalloc(sizeof(block_allocator), GFP_KERNEL); + if (NULL != allocator) { + allocator->all_blocks = kmalloc(sizeof(block_info) * num_blocks, GFP_KERNEL); + if (NULL != allocator->all_blocks) { + int i; + + allocator->first_free = NULL; + allocator->num_blocks = num_blocks; + allocator->num_free = num_blocks; + allocator->base = base_address; + sema_init(&allocator->mutex, 1); + + for (i = 0; i < num_blocks; i++) { + allocator->all_blocks[i].next = allocator->first_free; + allocator->first_free = &allocator->all_blocks[i]; + } + + backend->ctx = allocator; + backend->allocate = block_allocator_allocate; + backend->release = block_allocator_release; + backend->shutdown = block_allocator_shutdown; + backend->stat = block_allocator_stat; + backend->pre_allocate_physical_check = NULL; + backend->adjust_to_mali_phys = NULL; + + return backend; + } + kfree(allocator); + } + kfree(backend); + } + + return NULL; +} + + + +/* + * Destroy specified dedicated memory backend + */ +static void block_allocator_shutdown(ump_memory_backend *backend) +{ + block_allocator *allocator; + + BUG_ON(!backend); + BUG_ON(!backend->ctx); + + allocator = (block_allocator *)backend->ctx; + + DBG_MSG_IF(1, allocator->num_free != allocator->num_blocks, ("%u blocks still in use during shutdown\n", allocator->num_blocks - allocator->num_free)); + + kfree(allocator->all_blocks); + kfree(allocator); + kfree(backend); +} + + + +static int block_allocator_allocate(void *ctx, ump_dd_mem *mem) +{ + block_allocator *allocator; + u32 left; + block_info *last_allocated = NULL; + int i = 0; + + BUG_ON(!ctx); + BUG_ON(!mem); + + allocator = (block_allocator *)ctx; + left = mem->size_bytes; + + BUG_ON(!left); + BUG_ON(!&allocator->mutex); + + mem->nr_blocks = ((left + UMP_BLOCK_SIZE - 1) & ~(UMP_BLOCK_SIZE - 1)) / UMP_BLOCK_SIZE; + mem->block_array = (ump_dd_physical_block *)vmalloc(sizeof(ump_dd_physical_block) * mem->nr_blocks); + if (NULL == mem->block_array) { + MSG_ERR(("Failed to allocate block array\n")); + return 0; + } + + if (down_interruptible(&allocator->mutex)) { + MSG_ERR(("Could not get mutex to do block_allocate\n")); + return 0; + } + + mem->size_bytes = 0; + + while ((left > 0) && (allocator->first_free)) { + block_info *block; + + block = allocator->first_free; + allocator->first_free = allocator->first_free->next; + block->next = last_allocated; + last_allocated = block; + allocator->num_free--; + + mem->block_array[i].addr = get_phys(allocator, block); + mem->block_array[i].size = UMP_BLOCK_SIZE; + mem->size_bytes += UMP_BLOCK_SIZE; + + i++; + + if (left < UMP_BLOCK_SIZE) left = 0; + else left -= UMP_BLOCK_SIZE; + } + + if (left) { + block_info *block; + /* release all memory back to the pool */ + while (last_allocated) { + block = last_allocated->next; + last_allocated->next = allocator->first_free; + allocator->first_free = last_allocated; + last_allocated = block; + allocator->num_free++; + } + + vfree(mem->block_array); + mem->backend_info = NULL; + mem->block_array = NULL; + + DBG_MSG(4, ("Could not find a mem-block for the allocation.\n")); + up(&allocator->mutex); + + return 0; + } + + mem->backend_info = last_allocated; + + up(&allocator->mutex); + mem->is_cached = 0; + + return 1; +} + + + +static void block_allocator_release(void *ctx, ump_dd_mem *handle) +{ + block_allocator *allocator; + block_info *block, * next; + + BUG_ON(!ctx); + BUG_ON(!handle); + + allocator = (block_allocator *)ctx; + block = (block_info *)handle->backend_info; + BUG_ON(!block); + + if (down_interruptible(&allocator->mutex)) { + MSG_ERR(("Allocator release: Failed to get mutex - memory leak\n")); + return; + } + + while (block) { + next = block->next; + + BUG_ON((block < allocator->all_blocks) || (block > (allocator->all_blocks + allocator->num_blocks))); + + block->next = allocator->first_free; + allocator->first_free = block; + allocator->num_free++; + + block = next; + } + DBG_MSG(3, ("%d blocks free after release call\n", allocator->num_free)); + up(&allocator->mutex); + + vfree(handle->block_array); + handle->block_array = NULL; +} + + + +/* + * Helper function for calculating the physical base adderss of a memory block + */ +static inline u32 get_phys(block_allocator *allocator, block_info *block) +{ + return allocator->base + ((block - allocator->all_blocks) * UMP_BLOCK_SIZE); +} + +static u32 block_allocator_stat(struct ump_memory_backend *backend) +{ + block_allocator *allocator; + BUG_ON(!backend); + allocator = (block_allocator *)backend->ctx; + BUG_ON(!allocator); + + return (allocator->num_blocks - allocator->num_free) * UMP_BLOCK_SIZE; +} diff --git a/drivers/gpu/mali/ump/linux/ump_kernel_memory_backend_dedicated.h b/drivers/gpu/mali/ump/linux/ump_kernel_memory_backend_dedicated.h new file mode 100644 index 00000000..5a5a4a31 --- /dev/null +++ b/drivers/gpu/mali/ump/linux/ump_kernel_memory_backend_dedicated.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2010, 2014 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_kernel_memory_backend_dedicated.h + */ + +#ifndef __UMP_KERNEL_MEMORY_BACKEND_DEDICATED_H__ +#define __UMP_KERNEL_MEMORY_BACKEND_DEDICATED_H__ + +#include "ump_kernel_memory_backend.h" + +ump_memory_backend *ump_block_allocator_create(u32 base_address, u32 size); + +#endif /* __UMP_KERNEL_MEMORY_BACKEND_DEDICATED_H__ */ + diff --git a/drivers/gpu/mali/ump/linux/ump_kernel_memory_backend_os.c b/drivers/gpu/mali/ump/linux/ump_kernel_memory_backend_os.c new file mode 100644 index 00000000..86a8132e --- /dev/null +++ b/drivers/gpu/mali/ump/linux/ump_kernel_memory_backend_os.c @@ -0,0 +1,235 @@ +/* + * Copyright (C) 2010-2011, 2013-2014 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/* needed to detect kernel version specific code */ +#include + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) +#include +#else /* pre 2.6.26 the file was in the arch specific location */ +#include +#endif + +#include +#include +#include +#include +#include +#include +#include "ump_kernel_common.h" +#include "ump_kernel_memory_backend.h" + + + +typedef struct os_allocator { + struct semaphore mutex; + u32 num_pages_max; /**< Maximum number of pages to allocate from the OS */ + u32 num_pages_allocated; /**< Number of pages allocated from the OS */ +} os_allocator; + + + +static void os_free(void *ctx, ump_dd_mem *descriptor); +static int os_allocate(void *ctx, ump_dd_mem *descriptor); +static void os_memory_backend_destroy(ump_memory_backend *backend); +static u32 os_stat(struct ump_memory_backend *backend); + + + +/* + * Create OS memory backend + */ +ump_memory_backend *ump_os_memory_backend_create(const int max_allocation) +{ + ump_memory_backend *backend; + os_allocator *info; + + info = kmalloc(sizeof(os_allocator), GFP_KERNEL); + if (NULL == info) { + return NULL; + } + + info->num_pages_max = max_allocation >> PAGE_SHIFT; + info->num_pages_allocated = 0; + + sema_init(&info->mutex, 1); + + backend = kmalloc(sizeof(ump_memory_backend), GFP_KERNEL); + if (NULL == backend) { + kfree(info); + return NULL; + } + + backend->ctx = info; + backend->allocate = os_allocate; + backend->release = os_free; + backend->shutdown = os_memory_backend_destroy; + backend->stat = os_stat; + backend->pre_allocate_physical_check = NULL; + backend->adjust_to_mali_phys = NULL; + + return backend; +} + + + +/* + * Destroy specified OS memory backend + */ +static void os_memory_backend_destroy(ump_memory_backend *backend) +{ + os_allocator *info = (os_allocator *)backend->ctx; + + DBG_MSG_IF(1, 0 != info->num_pages_allocated, ("%d pages still in use during shutdown\n", info->num_pages_allocated)); + + kfree(info); + kfree(backend); +} + + + +/* + * Allocate UMP memory + */ +static int os_allocate(void *ctx, ump_dd_mem *descriptor) +{ + u32 left; + os_allocator *info; + int pages_allocated = 0; + int is_cached; + + BUG_ON(!descriptor); + BUG_ON(!ctx); + + info = (os_allocator *)ctx; + left = descriptor->size_bytes; + is_cached = descriptor->is_cached; + + if (down_interruptible(&info->mutex)) { + DBG_MSG(1, ("Failed to get mutex in os_free\n")); + return 0; /* failure */ + } + + descriptor->backend_info = NULL; + descriptor->nr_blocks = ((left + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1)) >> PAGE_SHIFT; + + DBG_MSG(5, ("Allocating page array. Size: %lu\n", descriptor->nr_blocks * sizeof(ump_dd_physical_block))); + + descriptor->block_array = (ump_dd_physical_block *)vmalloc(sizeof(ump_dd_physical_block) * descriptor->nr_blocks); + if (NULL == descriptor->block_array) { + up(&info->mutex); + DBG_MSG(1, ("Block array could not be allocated\n")); + return 0; /* failure */ + } + + while (left > 0 && ((info->num_pages_allocated + pages_allocated) < info->num_pages_max)) { + struct page *new_page; + + if (is_cached) { + new_page = alloc_page(GFP_HIGHUSER | __GFP_ZERO | __GFP_REPEAT | __GFP_NOWARN); + } else { + new_page = alloc_page(GFP_HIGHUSER | __GFP_ZERO | __GFP_REPEAT | __GFP_NOWARN | __GFP_COLD); + } + if (NULL == new_page) { + break; + } + + /* Ensure page caches are flushed. */ + if (is_cached) { + descriptor->block_array[pages_allocated].addr = page_to_phys(new_page); + descriptor->block_array[pages_allocated].size = PAGE_SIZE; + } else { + descriptor->block_array[pages_allocated].addr = dma_map_page(NULL, new_page, 0, PAGE_SIZE, DMA_BIDIRECTIONAL); + descriptor->block_array[pages_allocated].size = PAGE_SIZE; + } + + DBG_MSG(5, ("Allocated page 0x%08lx cached: %d\n", descriptor->block_array[pages_allocated].addr, is_cached)); + + if (left < PAGE_SIZE) { + left = 0; + } else { + left -= PAGE_SIZE; + } + + pages_allocated++; + } + + DBG_MSG(5, ("Alloce for ID:%2d got %d pages, cached: %d\n", descriptor->secure_id, pages_allocated)); + + if (left) { + DBG_MSG(1, ("Failed to allocate needed pages\n")); + + while (pages_allocated) { + pages_allocated--; + if (!is_cached) { + dma_unmap_page(NULL, descriptor->block_array[pages_allocated].addr, PAGE_SIZE, DMA_BIDIRECTIONAL); + } + __free_page(pfn_to_page(descriptor->block_array[pages_allocated].addr >> PAGE_SHIFT)); + } + + up(&info->mutex); + + return 0; /* failure */ + } + + info->num_pages_allocated += pages_allocated; + + DBG_MSG(6, ("%d out of %d pages now allocated\n", info->num_pages_allocated, info->num_pages_max)); + + up(&info->mutex); + + return 1; /* success*/ +} + + +/* + * Free specified UMP memory + */ +static void os_free(void *ctx, ump_dd_mem *descriptor) +{ + os_allocator *info; + int i; + + BUG_ON(!ctx); + BUG_ON(!descriptor); + + info = (os_allocator *)ctx; + + BUG_ON(descriptor->nr_blocks > info->num_pages_allocated); + + if (down_interruptible(&info->mutex)) { + DBG_MSG(1, ("Failed to get mutex in os_free\n")); + return; + } + + DBG_MSG(5, ("Releasing %lu OS pages\n", descriptor->nr_blocks)); + + info->num_pages_allocated -= descriptor->nr_blocks; + + up(&info->mutex); + + for (i = 0; i < descriptor->nr_blocks; i++) { + DBG_MSG(6, ("Freeing physical page. Address: 0x%08lx\n", descriptor->block_array[i].addr)); + if (! descriptor->is_cached) { + dma_unmap_page(NULL, descriptor->block_array[i].addr, PAGE_SIZE, DMA_BIDIRECTIONAL); + } + __free_page(pfn_to_page(descriptor->block_array[i].addr >> PAGE_SHIFT)); + } + + vfree(descriptor->block_array); +} + + +static u32 os_stat(struct ump_memory_backend *backend) +{ + os_allocator *info; + info = (os_allocator *)backend->ctx; + return info->num_pages_allocated * _MALI_OSK_MALI_PAGE_SIZE; +} diff --git a/drivers/gpu/mali/ump/linux/ump_kernel_memory_backend_os.h b/drivers/gpu/mali/ump/linux/ump_kernel_memory_backend_os.h new file mode 100644 index 00000000..9ac3cb7a --- /dev/null +++ b/drivers/gpu/mali/ump/linux/ump_kernel_memory_backend_os.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2010, 2014 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_kernel_memory_backend_os.h + */ + +#ifndef __UMP_KERNEL_MEMORY_BACKEND_OS_H__ +#define __UMP_KERNEL_MEMORY_BACKEND_OS_H__ + +#include "ump_kernel_memory_backend.h" + +ump_memory_backend *ump_os_memory_backend_create(const int max_allocation); + +#endif /* __UMP_KERNEL_MEMORY_BACKEND_OS_H__ */ + diff --git a/drivers/gpu/mali/ump/linux/ump_kernel_random_mapping.c b/drivers/gpu/mali/ump/linux/ump_kernel_random_mapping.c new file mode 100644 index 00000000..95bcb956 --- /dev/null +++ b/drivers/gpu/mali/ump/linux/ump_kernel_random_mapping.c @@ -0,0 +1,207 @@ +/* + * Copyright (C) 2010-2011, 2013-2014 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mali_kernel_common.h" +#include "mali_osk.h" +#include "ump_osk.h" +#include "ump_kernel_common.h" +#include "ump_kernel_types.h" +#include "ump_kernel_random_mapping.h" + +#include +#include +#include +#include + + +static ump_dd_mem *search(struct rb_root *root, int id) +{ + struct rb_node *node = root->rb_node; + + while (node) { + ump_dd_mem *e = container_of(node, ump_dd_mem, node); + + if (id < e->secure_id) { + node = node->rb_left; + } else if (id > e->secure_id) { + node = node->rb_right; + } else { + return e; + } + } + + return NULL; +} + +static mali_bool insert(struct rb_root *root, int id, ump_dd_mem *mem) +{ + struct rb_node **new = &(root->rb_node); + struct rb_node *parent = NULL; + + while (*new) { + ump_dd_mem *this = container_of(*new, ump_dd_mem, node); + + parent = *new; + if (id < this->secure_id) { + new = &((*new)->rb_left); + } else if (id > this->secure_id) { + new = &((*new)->rb_right); + } else { + printk(KERN_ERR "UMP: ID already used %x\n", id); + return MALI_FALSE; + } + } + + rb_link_node(&mem->node, parent, new); + rb_insert_color(&mem->node, root); + + return MALI_TRUE; +} + + +ump_random_mapping *ump_random_mapping_create(void) +{ + ump_random_mapping *map = _mali_osk_calloc(1, sizeof(ump_random_mapping)); + + if (NULL == map) + return NULL; + + map->lock = _mali_osk_mutex_rw_init(_MALI_OSK_LOCKFLAG_ORDERED, + _MALI_OSK_LOCK_ORDER_DESCRIPTOR_MAP); + if (NULL != map->lock) { + map->root = RB_ROOT; +#if UMP_RANDOM_MAP_DELAY + map->failed.count = 0; + map->failed.timestamp = jiffies; +#endif + return map; + } + return NULL; +} + +void ump_random_mapping_destroy(ump_random_mapping *map) +{ + _mali_osk_mutex_rw_term(map->lock); + _mali_osk_free(map); +} + +int ump_random_mapping_insert(ump_random_mapping *map, ump_dd_mem *mem) +{ + _mali_osk_mutex_rw_wait(map->lock, _MALI_OSK_LOCKMODE_RW); + + while (1) { + u32 id; + + get_random_bytes(&id, sizeof(id)); + + /* Try a new random number if id happened to be the invalid + * secure ID (-1). */ + if (unlikely(id == UMP_INVALID_SECURE_ID)) + continue; + + /* Insert into the tree. If the id was already in use, get a + * new random id and try again. */ + if (insert(&map->root, id, mem)) { + mem->secure_id = id; + break; + } + } + _mali_osk_mutex_rw_signal(map->lock, _MALI_OSK_LOCKMODE_RW); + + return 0; +} + +ump_dd_mem *ump_random_mapping_get(ump_random_mapping *map, int id) +{ + ump_dd_mem *mem = NULL; +#if UMP_RANDOM_MAP_DELAY + int do_delay = 0; +#endif + + DEBUG_ASSERT(map); + + _mali_osk_mutex_rw_wait(map->lock, _MALI_OSK_LOCKMODE_RO); + mem = search(&map->root, id); + + if (unlikely(NULL == mem)) { +#if UMP_RANDOM_MAP_DELAY + map->failed.count++; + + if (time_is_before_jiffies(map->failed.timestamp + + UMP_FAILED_LOOKUP_DELAY * HZ)) { + /* If it is a long time since last failure, reset + * the counter and skip the delay this time. */ + map->failed.count = 0; + } else if (map->failed.count > UMP_FAILED_LOOKUPS_ALLOWED) { + do_delay = 1; + } + + map->failed.timestamp = jiffies; +#endif /* UMP_RANDOM_MAP_DELAY */ + } else { + ump_dd_reference_add(mem); + } + _mali_osk_mutex_rw_signal(map->lock, _MALI_OSK_LOCKMODE_RO); + +#if UMP_RANDOM_MAP_DELAY + if (do_delay) { + /* Apply delay */ + schedule_timeout_killable(UMP_FAILED_LOOKUP_DELAY); + } +#endif /* UMP_RANDOM_MAP_DELAY */ + + return mem; +} + +static ump_dd_mem *ump_random_mapping_remove_internal(ump_random_mapping *map, int id) +{ + ump_dd_mem *mem = NULL; + + mem = search(&map->root, id); + + if (mem) { + rb_erase(&mem->node, &map->root); + } + + return mem; +} + +void ump_random_mapping_put(ump_dd_mem *mem) +{ + int new_ref; + + _mali_osk_mutex_rw_wait(device.secure_id_map->lock, _MALI_OSK_LOCKMODE_RW); + + new_ref = _ump_osk_atomic_dec_and_read(&mem->ref_count); + DBG_MSG(5, ("Memory reference decremented. ID: %u, new value: %d\n", + mem->secure_id, new_ref)); + + if (0 == new_ref) { + DBG_MSG(3, ("Final release of memory. ID: %u\n", mem->secure_id)); + + ump_random_mapping_remove_internal(device.secure_id_map, mem->secure_id); + + mem->release_func(mem->ctx, mem); + _mali_osk_free(mem); + } + + _mali_osk_mutex_rw_signal(device.secure_id_map->lock, _MALI_OSK_LOCKMODE_RW); +} + +ump_dd_mem *ump_random_mapping_remove(ump_random_mapping *map, int descriptor) +{ + ump_dd_mem *mem; + + _mali_osk_mutex_rw_wait(map->lock, _MALI_OSK_LOCKMODE_RW); + mem = ump_random_mapping_remove_internal(map, descriptor); + _mali_osk_mutex_rw_signal(map->lock, _MALI_OSK_LOCKMODE_RW); + + return mem; +} diff --git a/drivers/gpu/mali/ump/linux/ump_kernel_random_mapping.h b/drivers/gpu/mali/ump/linux/ump_kernel_random_mapping.h new file mode 100644 index 00000000..7f89ef82 --- /dev/null +++ b/drivers/gpu/mali/ump/linux/ump_kernel_random_mapping.h @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2010-2011, 2013-2014 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_kernel_random_mapping.h + */ + +#ifndef __UMP_KERNEL_RANDOM_MAPPING_H__ +#define __UMP_KERNEL_RANDOM_MAPPING_H__ + +#include "mali_osk.h" +#include + +#define UMP_RANDOM_MAP_DELAY 1 +#define UMP_FAILED_LOOKUP_DELAY 10 /* ms */ +#define UMP_FAILED_LOOKUPS_ALLOWED 10 /* number of allowed failed lookups */ + +/** + * The random mapping object + * Provides a separate namespace where we can map an integer to a pointer + */ +typedef struct ump_random_mapping { + _mali_osk_mutex_rw_t *lock; /**< Lock protecting access to the mapping object */ + struct rb_root root; +#if UMP_RANDOM_MAP_DELAY + struct { + unsigned long count; + unsigned long timestamp; + } failed; +#endif +} ump_random_mapping; + +/** + * Create a random mapping object + * Create a random mapping capable of holding 2^20 entries + * @return Pointer to a random mapping object, NULL on failure + */ +ump_random_mapping *ump_random_mapping_create(void); + +/** + * Destroy a random mapping object + * @param map The map to free + */ +void ump_random_mapping_destroy(ump_random_mapping *map); + +/** + * Allocate a new mapping entry (random ID) + * Allocates a new entry in the map. + * @param map The map to allocate a new entry in + * @param target The value to map to + * @return The random allocated, a negative value on error + */ +int ump_random_mapping_insert(ump_random_mapping *map, ump_dd_mem *mem); + +/** + * Get the value mapped to by a random ID + * + * If the lookup fails, punish the calling thread by applying a delay. + * + * @param map The map to lookup the random id in + * @param id The ID to lookup + * @param target Pointer to a pointer which will receive the stored value + * @return ump_dd_mem pointer on successful lookup, NULL on error + */ +ump_dd_mem *ump_random_mapping_get(ump_random_mapping *map, int id); + +void ump_random_mapping_put(ump_dd_mem *mem); + +/** + * Free the random ID + * For the random to be reused it has to be freed + * @param map The map to free the random from + * @param id The ID to free + */ +ump_dd_mem *ump_random_mapping_remove(ump_random_mapping *map, int id); + +#endif /* __UMP_KERNEL_RANDOM_MAPPING_H__ */ diff --git a/drivers/gpu/mali/ump/linux/ump_memory_backend.c b/drivers/gpu/mali/ump/linux/ump_memory_backend.c new file mode 100644 index 00000000..b0915435 --- /dev/null +++ b/drivers/gpu/mali/ump/linux/ump_memory_backend.c @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2010, 2013-2014 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include /* kernel module definitions */ +#include /* request_mem_region */ + +#include "arch/config.h" /* Configuration for current platform. The symlink for arch is set by Makefile */ + +#include "ump_osk.h" +#include "ump_kernel_common.h" +#include "ump_kernel_memory_backend_os.h" +#include "ump_kernel_memory_backend_dedicated.h" + +/* Configure which dynamic memory allocator to use */ +int ump_backend = ARCH_UMP_BACKEND_DEFAULT; +module_param(ump_backend, int, S_IRUGO); /* r--r--r-- */ +MODULE_PARM_DESC(ump_backend, "0 = dedicated memory backend (default), 1 = OS memory backend"); + +/* The base address of the memory block for the dedicated memory backend */ +unsigned int ump_memory_address = ARCH_UMP_MEMORY_ADDRESS_DEFAULT; +module_param(ump_memory_address, uint, S_IRUGO); /* r--r--r-- */ +MODULE_PARM_DESC(ump_memory_address, "The physical address to map for the dedicated memory backend"); + +/* The size of the memory block for the dedicated memory backend */ +unsigned int ump_memory_size = ARCH_UMP_MEMORY_SIZE_DEFAULT; +module_param(ump_memory_size, uint, S_IRUGO); /* r--r--r-- */ +MODULE_PARM_DESC(ump_memory_size, "The size of fixed memory to map in the dedicated memory backend"); + +ump_memory_backend *ump_memory_backend_create(void) +{ + ump_memory_backend *backend = NULL; + + /* Create the dynamic memory allocator backend */ + if (0 == ump_backend) { + DBG_MSG(2, ("Using dedicated memory backend\n")); + + DBG_MSG(2, ("Requesting dedicated memory: 0x%08x, size: %u\n", ump_memory_address, ump_memory_size)); + /* Ask the OS if we can use the specified physical memory */ + if (NULL == request_mem_region(ump_memory_address, ump_memory_size, "UMP Memory")) { + MSG_ERR(("Failed to request memory region (0x%08X - 0x%08X). Is Mali DD already loaded?\n", ump_memory_address, ump_memory_address + ump_memory_size - 1)); + return NULL; + } + backend = ump_block_allocator_create(ump_memory_address, ump_memory_size); + } else if (1 == ump_backend) { + DBG_MSG(2, ("Using OS memory backend, allocation limit: %d\n", ump_memory_size)); + backend = ump_os_memory_backend_create(ump_memory_size); + } + + return backend; +} + +void ump_memory_backend_destroy(void) +{ + if (0 == ump_backend) { + DBG_MSG(2, ("Releasing dedicated memory: 0x%08x\n", ump_memory_address)); + release_mem_region(ump_memory_address, ump_memory_size); + } +} diff --git a/drivers/gpu/mali/ump/linux/ump_osk_atomics.c b/drivers/gpu/mali/ump/linux/ump_osk_atomics.c new file mode 100644 index 00000000..04934234 --- /dev/null +++ b/drivers/gpu/mali/ump/linux/ump_osk_atomics.c @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2010, 2014 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_osk_atomics.c + * Implementation of the OS abstraction layer for the UMP kernel device driver + */ + +#include "ump_osk.h" +#include + +int _ump_osk_atomic_dec_and_read(_mali_osk_atomic_t *atom) +{ + return atomic_dec_return((atomic_t *)&atom->u.val); +} + +int _ump_osk_atomic_inc_and_read(_mali_osk_atomic_t *atom) +{ + return atomic_inc_return((atomic_t *)&atom->u.val); +} diff --git a/drivers/gpu/mali/ump/linux/ump_osk_low_level_mem.c b/drivers/gpu/mali/ump/linux/ump_osk_low_level_mem.c new file mode 100644 index 00000000..3cd429ad --- /dev/null +++ b/drivers/gpu/mali/ump/linux/ump_osk_low_level_mem.c @@ -0,0 +1,314 @@ +/* + * Copyright (C) 2010-2014 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_osk_memory.c + * Implementation of the OS abstraction layer for the kernel device driver + */ + +/* needed to detect kernel version specific code */ +#include + +#include "ump_osk.h" +#include "ump_uk_types.h" +#include "ump_ukk.h" +#include "ump_kernel_common.h" +#include /* kernel module definitions */ +#include +#include +#include + +#include +#include /* to verify pointers from user space */ +#include +#include + +typedef struct ump_vma_usage_tracker { + atomic_t references; + ump_memory_allocation *descriptor; +} ump_vma_usage_tracker; + +static void ump_vma_open(struct vm_area_struct *vma); +static void ump_vma_close(struct vm_area_struct *vma); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) +static int ump_cpu_page_fault_handler(struct vm_area_struct *vma, struct vm_fault *vmf); +#else +static unsigned long ump_cpu_page_fault_handler(struct vm_area_struct *vma, unsigned long address); +#endif + +static struct vm_operations_struct ump_vm_ops = { + .open = ump_vma_open, + .close = ump_vma_close, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) + .fault = ump_cpu_page_fault_handler +#else + .nopfn = ump_cpu_page_fault_handler +#endif +}; + +/* + * Page fault for VMA region + * This should never happen since we always map in the entire virtual memory range. + */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) +static int ump_cpu_page_fault_handler(struct vm_area_struct *vma, struct vm_fault *vmf) +#else +static unsigned long ump_cpu_page_fault_handler(struct vm_area_struct *vma, unsigned long address) +#endif +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) + void __user *address; + address = vmf->virtual_address; +#endif + MSG_ERR(("Page-fault in UMP memory region caused by the CPU\n")); + MSG_ERR(("VMA: 0x%08lx, virtual address: 0x%08lx\n", (unsigned long)vma, address)); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) + return VM_FAULT_SIGBUS; +#else + return NOPFN_SIGBUS; +#endif +} + +static void ump_vma_open(struct vm_area_struct *vma) +{ + ump_vma_usage_tracker *vma_usage_tracker; + int new_val; + + vma_usage_tracker = (ump_vma_usage_tracker *)vma->vm_private_data; + BUG_ON(NULL == vma_usage_tracker); + + new_val = atomic_inc_return(&vma_usage_tracker->references); + + DBG_MSG(4, ("VMA open, VMA reference count incremented. VMA: 0x%08lx, reference count: %d\n", (unsigned long)vma, new_val)); +} + +static void ump_vma_close(struct vm_area_struct *vma) +{ + ump_vma_usage_tracker *vma_usage_tracker; + _ump_uk_unmap_mem_s args; + int new_val; + + vma_usage_tracker = (ump_vma_usage_tracker *)vma->vm_private_data; + BUG_ON(NULL == vma_usage_tracker); + + new_val = atomic_dec_return(&vma_usage_tracker->references); + + DBG_MSG(4, ("VMA close, VMA reference count decremented. VMA: 0x%08lx, reference count: %d\n", (unsigned long)vma, new_val)); + + if (0 == new_val) { + ump_memory_allocation *descriptor; + + descriptor = vma_usage_tracker->descriptor; + + args.ctx = descriptor->ump_session; + args.cookie = descriptor->cookie; + args.mapping = descriptor->mapping; + args.size = descriptor->size; + + args._ukk_private = NULL; /** @note unused */ + + DBG_MSG(4, ("No more VMA references left, releasing UMP memory\n")); + _ump_ukk_unmap_mem(& args); + + /* vma_usage_tracker is free()d by _ump_osk_mem_mapregion_term() */ + } +} + +_mali_osk_errcode_t _ump_osk_mem_mapregion_init(ump_memory_allocation *descriptor) +{ + ump_vma_usage_tracker *vma_usage_tracker; + struct vm_area_struct *vma; + + if (NULL == descriptor) return _MALI_OSK_ERR_FAULT; + + vma_usage_tracker = kmalloc(sizeof(ump_vma_usage_tracker), GFP_KERNEL); + if (NULL == vma_usage_tracker) { + DBG_MSG(1, ("Failed to allocate memory for ump_vma_usage_tracker in _mali_osk_mem_mapregion_init\n")); + return -_MALI_OSK_ERR_FAULT; + } + + vma = (struct vm_area_struct *)descriptor->process_mapping_info; + if (NULL == vma) { + kfree(vma_usage_tracker); + return _MALI_OSK_ERR_FAULT; + } + + vma->vm_private_data = vma_usage_tracker; + vma->vm_flags |= VM_IO; +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0) + vma->vm_flags |= VM_RESERVED; +#else + vma->vm_flags |= VM_DONTDUMP; + vma->vm_flags |= VM_DONTEXPAND; + vma->vm_flags |= VM_PFNMAP; +#endif + + + if (0 == descriptor->is_cached) { + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); + } + DBG_MSG(3, ("Mapping with page_prot: 0x%x\n", vma->vm_page_prot)); + + /* Setup the functions which handle further VMA handling */ + vma->vm_ops = &ump_vm_ops; + + /* Do the va range allocation - in this case, it was done earlier, so we copy in that information */ + descriptor->mapping = (void __user *)vma->vm_start; + + atomic_set(&vma_usage_tracker->references, 1); /*this can later be increased if process is forked, see ump_vma_open() */ + vma_usage_tracker->descriptor = descriptor; + + return _MALI_OSK_ERR_OK; +} + +void _ump_osk_mem_mapregion_term(ump_memory_allocation *descriptor) +{ + struct vm_area_struct *vma; + ump_vma_usage_tracker *vma_usage_tracker; + + if (NULL == descriptor) return; + + /* Linux does the right thing as part of munmap to remove the mapping + * All that remains is that we remove the vma_usage_tracker setup in init() */ + vma = (struct vm_area_struct *)descriptor->process_mapping_info; + + vma_usage_tracker = vma->vm_private_data; + + /* We only get called if mem_mapregion_init succeeded */ + kfree(vma_usage_tracker); + return; +} + +_mali_osk_errcode_t _ump_osk_mem_mapregion_map(ump_memory_allocation *descriptor, u32 offset, u32 *phys_addr, unsigned long size) +{ + struct vm_area_struct *vma; + _mali_osk_errcode_t retval; + + if (NULL == descriptor) return _MALI_OSK_ERR_FAULT; + + vma = (struct vm_area_struct *)descriptor->process_mapping_info; + + if (NULL == vma) return _MALI_OSK_ERR_FAULT; + + retval = remap_pfn_range(vma, ((u32)descriptor->mapping) + offset, (*phys_addr) >> PAGE_SHIFT, size, vma->vm_page_prot) ? _MALI_OSK_ERR_FAULT : _MALI_OSK_ERR_OK;; + + DBG_MSG(4, ("Mapping virtual to physical memory. ID: %u, vma: 0x%08lx, virtual addr:0x%08lx, physical addr: 0x%08lx, size:%lu, prot:0x%x, vm_flags:0x%x RETVAL: 0x%x\n", + ump_dd_secure_id_get(descriptor->handle), + (unsigned long)vma, + (unsigned long)(vma->vm_start + offset), + (unsigned long)*phys_addr, + size, + (unsigned int)vma->vm_page_prot, vma->vm_flags, retval)); + + return retval; +} + +static void level1_cache_flush_all(void) +{ + DBG_MSG(4, ("UMP[xx] Flushing complete L1 cache\n")); + __cpuc_flush_kern_all(); +} + +void _ump_osk_msync(ump_dd_mem *mem, void *virt, u32 offset, u32 size, ump_uk_msync_op op, ump_session_data *session_data) +{ + int i; + + /* Flush L1 using virtual address, the entire range in one go. + * Only flush if user space process has a valid write mapping on given address. */ + if ((mem) && (virt != NULL) && (access_ok(VERIFY_WRITE, virt, size))) { + __cpuc_flush_dcache_area(virt, size); + DBG_MSG(3, ("UMP[%02u] Flushing CPU L1 Cache. CPU address: %x, size: %x\n", mem->secure_id, virt, size)); + } else { + if (session_data) { + if (op == _UMP_UK_MSYNC_FLUSH_L1) { + DBG_MSG(4, ("UMP Pending L1 cache flushes: %d\n", session_data->has_pending_level1_cache_flush)); + session_data->has_pending_level1_cache_flush = 0; + level1_cache_flush_all(); + return; + } else { + if (session_data->cache_operations_ongoing) { + session_data->has_pending_level1_cache_flush++; + DBG_MSG(4, ("UMP[%02u] Defering the L1 flush. Nr pending:%d\n", mem->secure_id, session_data->has_pending_level1_cache_flush)); + } else { + /* Flushing the L1 cache for each switch_user() if ump_cache_operations_control(START) is not called */ + level1_cache_flush_all(); + } + } + } else { + DBG_MSG(4, ("Unkown state %s %d\n", __FUNCTION__, __LINE__)); + level1_cache_flush_all(); + } + } + + if (NULL == mem) return; + + if (mem->size_bytes == size) { + DBG_MSG(3, ("UMP[%02u] Flushing CPU L2 Cache\n", mem->secure_id)); + } else { + DBG_MSG(3, ("UMP[%02u] Flushing CPU L2 Cache. Blocks:%u, TotalSize:%u. FlushSize:%u Offset:0x%x FirstPaddr:0x%08x\n", + mem->secure_id, mem->nr_blocks, mem->size_bytes, size, offset, mem->block_array[0].addr)); + } + + + /* Flush L2 using physical addresses, block for block. */ + for (i = 0 ; i < mem->nr_blocks; i++) { + u32 start_p, end_p; + ump_dd_physical_block *block; + block = &mem->block_array[i]; + + if (offset >= block->size) { + offset -= block->size; + continue; + } + + if (offset) { + start_p = (u32)block->addr + offset; + /* We'll zero the offset later, after using it to calculate end_p. */ + } else { + start_p = (u32)block->addr; + } + + if (size < block->size - offset) { + end_p = start_p + size - 1; + size = 0; + } else { + if (offset) { + end_p = start_p + (block->size - offset - 1); + size -= block->size - offset; + offset = 0; + } else { + end_p = start_p + block->size - 1; + size -= block->size; + } + } + + switch (op) { + case _UMP_UK_MSYNC_CLEAN: + outer_clean_range(start_p, end_p); + break; + case _UMP_UK_MSYNC_CLEAN_AND_INVALIDATE: + outer_flush_range(start_p, end_p); + break; + case _UMP_UK_MSYNC_INVALIDATE: + outer_inv_range(start_p, end_p); + break; + default: + break; + } + + if (0 == size) { + /* Nothing left to flush. */ + break; + } + } + + return; +} diff --git a/drivers/gpu/mali/ump/linux/ump_osk_misc.c b/drivers/gpu/mali/ump/linux/ump_osk_misc.c new file mode 100644 index 00000000..0f6829df --- /dev/null +++ b/drivers/gpu/mali/ump/linux/ump_osk_misc.c @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2010, 2013-2014 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_osk_misc.c + * Implementation of the OS abstraction layer for the UMP kernel device driver + */ + + +#include "ump_osk.h" + +#include +#include "ump_kernel_linux.h" + +/* is called from ump_kernel_constructor in common code */ +_mali_osk_errcode_t _ump_osk_init(void) +{ + if (0 != ump_kernel_device_initialize()) { + return _MALI_OSK_ERR_FAULT; + } + + return _MALI_OSK_ERR_OK; +} + +_mali_osk_errcode_t _ump_osk_term(void) +{ + ump_kernel_device_terminate(); + return _MALI_OSK_ERR_OK; +} diff --git a/drivers/gpu/mali/ump/linux/ump_ukk_ref_wrappers.c b/drivers/gpu/mali/ump/linux/ump_ukk_ref_wrappers.c new file mode 100644 index 00000000..ed14987c --- /dev/null +++ b/drivers/gpu/mali/ump/linux/ump_ukk_ref_wrappers.c @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2010, 2013-2014 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_ukk_wrappers.c + * Defines the wrapper functions which turn Linux IOCTL calls into _ukk_ calls for the reference implementation + */ + + +#include /* user space access */ + +#include "ump_osk.h" +#include "ump_uk_types.h" +#include "ump_ukk.h" +#include "ump_kernel_common.h" + +/* + * IOCTL operation; Allocate UMP memory + */ +int ump_allocate_wrapper(u32 __user *argument, struct ump_session_data *session_data) +{ + _ump_uk_allocate_s user_interaction; + _mali_osk_errcode_t err; + + /* Sanity check input parameters */ + if (NULL == argument || NULL == session_data) { + MSG_ERR(("NULL parameter in ump_ioctl_allocate()\n")); + return -ENOTTY; + } + + /* Copy the user space memory to kernel space (so we safely can read it) */ + if (0 != copy_from_user(&user_interaction, argument, sizeof(user_interaction))) { + MSG_ERR(("copy_from_user() in ump_ioctl_allocate()\n")); + return -EFAULT; + } + + user_interaction.ctx = (void *) session_data; + + err = _ump_ukk_allocate(&user_interaction); + if (_MALI_OSK_ERR_OK != err) { + DBG_MSG(1, ("_ump_ukk_allocate() failed in ump_ioctl_allocate()\n")); + return map_errcode(err); + } + user_interaction.ctx = NULL; + + if (0 != copy_to_user(argument, &user_interaction, sizeof(user_interaction))) { + /* If the copy fails then we should release the memory. We can use the IOCTL release to accomplish this */ + _ump_uk_release_s release_args; + + MSG_ERR(("copy_to_user() failed in ump_ioctl_allocate()\n")); + + release_args.ctx = (void *) session_data; + release_args.secure_id = user_interaction.secure_id; + + err = _ump_ukk_release(&release_args); + if (_MALI_OSK_ERR_OK != err) { + MSG_ERR(("_ump_ukk_release() also failed when trying to release newly allocated memory in ump_ioctl_allocate()\n")); + } + + return -EFAULT; + } + + return 0; /* success */ +} diff --git a/drivers/gpu/mali/ump/linux/ump_ukk_ref_wrappers.h b/drivers/gpu/mali/ump/linux/ump_ukk_ref_wrappers.h new file mode 100644 index 00000000..c88b6665 --- /dev/null +++ b/drivers/gpu/mali/ump/linux/ump_ukk_ref_wrappers.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2010, 2013-2014 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_ukk_wrappers.h + * Defines the wrapper functions which turn Linux IOCTL calls into _ukk_ calls for the reference implementation + */ + +#ifndef __UMP_UKK_REF_WRAPPERS_H__ +#define __UMP_UKK_REF_WRAPPERS_H__ + +#include +#include "ump_kernel_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +int ump_allocate_wrapper(u32 __user *argument, struct ump_session_data *session_data); + + +#ifdef __cplusplus +} +#endif + +#endif /* __UMP_UKK_REF_WRAPPERS_H__ */ diff --git a/drivers/gpu/mali/ump/linux/ump_ukk_wrappers.c b/drivers/gpu/mali/ump/linux/ump_ukk_wrappers.c new file mode 100644 index 00000000..49d58d79 --- /dev/null +++ b/drivers/gpu/mali/ump/linux/ump_ukk_wrappers.c @@ -0,0 +1,280 @@ +/* + * Copyright (C) 2010-2014 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_ukk_wrappers.c + * Defines the wrapper functions which turn Linux IOCTL calls into _ukk_ calls + */ + +#include /* user space access */ + +#include "ump_osk.h" +#include "ump_uk_types.h" +#include "ump_ukk.h" +#include "ump_kernel_common.h" + +/* + * IOCTL operation; Negotiate version of IOCTL API + */ +int ump_get_api_version_wrapper(u32 __user *argument, struct ump_session_data *session_data) +{ + _ump_uk_api_version_s version_info; + _mali_osk_errcode_t err; + + /* Sanity check input parameters */ + if (NULL == argument || NULL == session_data) { + MSG_ERR(("NULL parameter in ump_ioctl_get_api_version()\n")); + return -ENOTTY; + } + + /* Copy the user space memory to kernel space (so we safely can read it) */ + if (0 != copy_from_user(&version_info, argument, sizeof(version_info))) { + MSG_ERR(("copy_from_user() in ump_ioctl_get_api_version()\n")); + return -EFAULT; + } + + version_info.ctx = (void *) session_data; + err = _ump_uku_get_api_version(&version_info); + if (_MALI_OSK_ERR_OK != err) { + MSG_ERR(("_ump_uku_get_api_version() failed in ump_ioctl_get_api_version()\n")); + return map_errcode(err); + } + + version_info.ctx = NULL; + + /* Copy ouput data back to user space */ + if (0 != copy_to_user(argument, &version_info, sizeof(version_info))) { + MSG_ERR(("copy_to_user() failed in ump_ioctl_get_api_version()\n")); + return -EFAULT; + } + + return 0; /* success */ +} + + +/* + * IOCTL operation; Release reference to specified UMP memory. + */ +int ump_release_wrapper(u32 __user *argument, struct ump_session_data *session_data) +{ + _ump_uk_release_s release_args; + _mali_osk_errcode_t err; + + /* Sanity check input parameters */ + if (NULL == session_data) { + MSG_ERR(("NULL parameter in ump_ioctl_release()\n")); + return -ENOTTY; + } + + /* Copy the user space memory to kernel space (so we safely can read it) */ + if (0 != copy_from_user(&release_args, argument, sizeof(release_args))) { + MSG_ERR(("copy_from_user() in ump_ioctl_get_api_version()\n")); + return -EFAULT; + } + + release_args.ctx = (void *) session_data; + err = _ump_ukk_release(&release_args); + if (_MALI_OSK_ERR_OK != err) { + MSG_ERR(("_ump_ukk_release() failed in ump_ioctl_release()\n")); + return map_errcode(err); + } + + + return 0; /* success */ +} + +/* + * IOCTL operation; Return size for specified UMP memory. + */ +int ump_size_get_wrapper(u32 __user *argument, struct ump_session_data *session_data) +{ + _ump_uk_size_get_s user_interaction; + _mali_osk_errcode_t err; + + /* Sanity check input parameters */ + if (NULL == argument || NULL == session_data) { + MSG_ERR(("NULL parameter in ump_ioctl_size_get()\n")); + return -ENOTTY; + } + + if (0 != copy_from_user(&user_interaction, argument, sizeof(user_interaction))) { + MSG_ERR(("copy_from_user() in ump_ioctl_size_get()\n")); + return -EFAULT; + } + + user_interaction.ctx = (void *) session_data; + err = _ump_ukk_size_get(&user_interaction); + if (_MALI_OSK_ERR_OK != err) { + MSG_ERR(("_ump_ukk_size_get() failed in ump_ioctl_size_get()\n")); + return map_errcode(err); + } + + user_interaction.ctx = NULL; + + if (0 != copy_to_user(argument, &user_interaction, sizeof(user_interaction))) { + MSG_ERR(("copy_to_user() failed in ump_ioctl_size_get()\n")); + return -EFAULT; + } + + return 0; /* success */ +} + +/* + * IOCTL operation; Do cache maintenance on specified UMP memory. + */ +int ump_msync_wrapper(u32 __user *argument, struct ump_session_data *session_data) +{ + _ump_uk_msync_s user_interaction; + + /* Sanity check input parameters */ + if (NULL == argument || NULL == session_data) { + MSG_ERR(("NULL parameter in ump_ioctl_size_get()\n")); + return -ENOTTY; + } + + if (0 != copy_from_user(&user_interaction, argument, sizeof(user_interaction))) { + MSG_ERR(("copy_from_user() in ump_ioctl_msync()\n")); + return -EFAULT; + } + + user_interaction.ctx = (void *) session_data; + + _ump_ukk_msync(&user_interaction); + + user_interaction.ctx = NULL; + + if (0 != copy_to_user(argument, &user_interaction, sizeof(user_interaction))) { + MSG_ERR(("copy_to_user() failed in ump_ioctl_msync()\n")); + return -EFAULT; + } + + return 0; /* success */ +} +int ump_cache_operations_control_wrapper(u32 __user *argument, struct ump_session_data *session_data) +{ + _ump_uk_cache_operations_control_s user_interaction; + + /* Sanity check input parameters */ + if (NULL == argument || NULL == session_data) { + MSG_ERR(("NULL parameter in ump_ioctl_size_get()\n")); + return -ENOTTY; + } + + if (0 != copy_from_user(&user_interaction, argument, sizeof(user_interaction))) { + MSG_ERR(("copy_from_user() in ump_ioctl_cache_operations_control()\n")); + return -EFAULT; + } + + user_interaction.ctx = (void *) session_data; + + _ump_ukk_cache_operations_control((_ump_uk_cache_operations_control_s *) &user_interaction); + + user_interaction.ctx = NULL; + +#if 0 /* No data to copy back */ + if (0 != copy_to_user(argument, &user_interaction, sizeof(user_interaction))) { + MSG_ERR(("copy_to_user() failed in ump_ioctl_cache_operations_control()\n")); + return -EFAULT; + } +#endif + return 0; /* success */ +} + +int ump_switch_hw_usage_wrapper(u32 __user *argument, struct ump_session_data *session_data) +{ + _ump_uk_switch_hw_usage_s user_interaction; + + /* Sanity check input parameters */ + if (NULL == argument || NULL == session_data) { + MSG_ERR(("NULL parameter in ump_ioctl_size_get()\n")); + return -ENOTTY; + } + + if (0 != copy_from_user(&user_interaction, argument, sizeof(user_interaction))) { + MSG_ERR(("copy_from_user() in ump_ioctl_switch_hw_usage()\n")); + return -EFAULT; + } + + user_interaction.ctx = (void *) session_data; + + _ump_ukk_switch_hw_usage(&user_interaction); + + user_interaction.ctx = NULL; + +#if 0 /* No data to copy back */ + if (0 != copy_to_user(argument, &user_interaction, sizeof(user_interaction))) { + MSG_ERR(("copy_to_user() failed in ump_ioctl_switch_hw_usage()\n")); + return -EFAULT; + } +#endif + return 0; /* success */ +} + +int ump_lock_wrapper(u32 __user *argument, struct ump_session_data *session_data) +{ + _ump_uk_lock_s user_interaction; + + /* Sanity check input parameters */ + if (NULL == argument || NULL == session_data) { + MSG_ERR(("NULL parameter in ump_ioctl_size_get()\n")); + return -ENOTTY; + } + + if (0 != copy_from_user(&user_interaction, argument, sizeof(user_interaction))) { + MSG_ERR(("copy_from_user() in ump_ioctl_switch_hw_usage()\n")); + return -EFAULT; + } + + user_interaction.ctx = (void *) session_data; + + _ump_ukk_lock(&user_interaction); + + user_interaction.ctx = NULL; + +#if 0 /* No data to copy back */ + if (0 != copy_to_user(argument, &user_interaction, sizeof(user_interaction))) { + MSG_ERR(("copy_to_user() failed in ump_ioctl_switch_hw_usage()\n")); + return -EFAULT; + } +#endif + + return 0; /* success */ +} + +int ump_unlock_wrapper(u32 __user *argument, struct ump_session_data *session_data) +{ + _ump_uk_unlock_s user_interaction; + + /* Sanity check input parameters */ + if (NULL == argument || NULL == session_data) { + MSG_ERR(("NULL parameter in ump_ioctl_size_get()\n")); + return -ENOTTY; + } + + if (0 != copy_from_user(&user_interaction, argument, sizeof(user_interaction))) { + MSG_ERR(("copy_from_user() in ump_ioctl_switch_hw_usage()\n")); + return -EFAULT; + } + + user_interaction.ctx = (void *) session_data; + + _ump_ukk_unlock(&user_interaction); + + user_interaction.ctx = NULL; + +#if 0 /* No data to copy back */ + if (0 != copy_to_user(argument, &user_interaction, sizeof(user_interaction))) { + MSG_ERR(("copy_to_user() failed in ump_ioctl_switch_hw_usage()\n")); + return -EFAULT; + } +#endif + + return 0; /* success */ +} diff --git a/drivers/gpu/mali/ump/linux/ump_ukk_wrappers.h b/drivers/gpu/mali/ump/linux/ump_ukk_wrappers.h new file mode 100644 index 00000000..e9110b7b --- /dev/null +++ b/drivers/gpu/mali/ump/linux/ump_ukk_wrappers.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2010, 2012-2014 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_ukk_wrappers.h + * Defines the wrapper functions which turn Linux IOCTL calls into _ukk_ calls + */ + +#ifndef __UMP_UKK_WRAPPERS_H__ +#define __UMP_UKK_WRAPPERS_H__ + +#include +#include "ump_kernel_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + + + +int ump_get_api_version_wrapper(u32 __user *argument, struct ump_session_data *session_data); +int ump_release_wrapper(u32 __user *argument, struct ump_session_data *session_data); +int ump_size_get_wrapper(u32 __user *argument, struct ump_session_data *session_data); +int ump_msync_wrapper(u32 __user *argument, struct ump_session_data *session_data); +int ump_cache_operations_control_wrapper(u32 __user *argument, struct ump_session_data *session_data); +int ump_switch_hw_usage_wrapper(u32 __user *argument, struct ump_session_data *session_data); +int ump_lock_wrapper(u32 __user *argument, struct ump_session_data *session_data); +int ump_unlock_wrapper(u32 __user *argument, struct ump_session_data *session_data); + + + + +#ifdef __cplusplus +} +#endif + + + +#endif /* __UMP_UKK_WRAPPERS_H__ */ -- cgit