summaryrefslogtreecommitdiff
path: root/drivers/gpu/mali/ump/common/ump_kernel_api.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/mali/ump/common/ump_kernel_api.c')
-rw-r--r--drivers/gpu/mali/ump/common/ump_kernel_api.c455
1 files changed, 455 insertions, 0 deletions
diff --git a/drivers/gpu/mali/ump/common/ump_kernel_api.c b/drivers/gpu/mali/ump/common/ump_kernel_api.c
new file mode 100644
index 00000000..398781db
--- /dev/null
+++ b/drivers/gpu/mali/ump/common/ump_kernel_api.c
@@ -0,0 +1,455 @@
+/*
+ * 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 "mali_osk.h"
+#include "mali_osk_list.h"
+#include "ump_osk.h"
+#include "ump_uk_types.h"
+#include "ump_kernel_interface.h"
+#include "ump_kernel_common.h"
+#include "ump_kernel_random_mapping.h"
+
+
+
+/* ---------------- UMP kernel space API functions follows ---------------- */
+
+
+
+UMP_KERNEL_API_EXPORT ump_secure_id ump_dd_secure_id_get(ump_dd_handle memh)
+{
+ ump_dd_mem *mem = (ump_dd_mem *)memh;
+
+ DEBUG_ASSERT_POINTER(mem);
+
+ DBG_MSG(5, ("Returning secure ID. ID: %u\n", mem->secure_id));
+
+ return mem->secure_id;
+}
+
+
+
+UMP_KERNEL_API_EXPORT ump_dd_handle ump_dd_handle_create_from_secure_id(ump_secure_id secure_id)
+{
+ ump_dd_mem *mem;
+
+ DBG_MSG(5, ("Getting handle from secure ID. ID: %u\n", secure_id));
+ mem = ump_random_mapping_get(device.secure_id_map, (int)secure_id);
+ if (NULL == mem) {
+ DBG_MSG(1, ("Secure ID not found. ID: %u\n", secure_id));
+ return UMP_DD_HANDLE_INVALID;
+ }
+
+ /* Keep the reference taken in ump_random_mapping_get() */
+
+ return (ump_dd_handle)mem;
+}
+
+
+
+UMP_KERNEL_API_EXPORT unsigned long ump_dd_phys_block_count_get(ump_dd_handle memh)
+{
+ ump_dd_mem *mem = (ump_dd_mem *) memh;
+
+ DEBUG_ASSERT_POINTER(mem);
+
+ return mem->nr_blocks;
+}
+
+
+
+UMP_KERNEL_API_EXPORT ump_dd_status_code ump_dd_phys_blocks_get(ump_dd_handle memh, ump_dd_physical_block *blocks, unsigned long num_blocks)
+{
+ ump_dd_mem *mem = (ump_dd_mem *)memh;
+
+ DEBUG_ASSERT_POINTER(mem);
+
+ if (blocks == NULL) {
+ DBG_MSG(1, ("NULL parameter in ump_dd_phys_blocks_get()\n"));
+ return UMP_DD_INVALID;
+ }
+
+ if (mem->nr_blocks != num_blocks) {
+ DBG_MSG(1, ("Specified number of blocks do not match actual number of blocks\n"));
+ return UMP_DD_INVALID;
+ }
+
+ DBG_MSG(5, ("Returning physical block information. ID: %u\n", mem->secure_id));
+
+ _mali_osk_memcpy(blocks, mem->block_array, sizeof(ump_dd_physical_block) * mem->nr_blocks);
+
+ return UMP_DD_SUCCESS;
+}
+
+
+
+UMP_KERNEL_API_EXPORT ump_dd_status_code ump_dd_phys_block_get(ump_dd_handle memh, unsigned long index, ump_dd_physical_block *block)
+{
+ ump_dd_mem *mem = (ump_dd_mem *)memh;
+
+ DEBUG_ASSERT_POINTER(mem);
+
+ if (block == NULL) {
+ DBG_MSG(1, ("NULL parameter in ump_dd_phys_block_get()\n"));
+ return UMP_DD_INVALID;
+ }
+
+ if (index >= mem->nr_blocks) {
+ DBG_MSG(5, ("Invalid index specified in ump_dd_phys_block_get()\n"));
+ return UMP_DD_INVALID;
+ }
+
+ DBG_MSG(5, ("Returning physical block information. ID: %u, index: %lu\n", mem->secure_id, index));
+
+ *block = mem->block_array[index];
+
+ return UMP_DD_SUCCESS;
+}
+
+
+
+UMP_KERNEL_API_EXPORT unsigned long ump_dd_size_get(ump_dd_handle memh)
+{
+ ump_dd_mem *mem = (ump_dd_mem *)memh;
+
+ DEBUG_ASSERT_POINTER(mem);
+
+ DBG_MSG(5, ("Returning size. ID: %u, size: %lu\n", mem->secure_id, mem->size_bytes));
+
+ return mem->size_bytes;
+}
+
+
+
+UMP_KERNEL_API_EXPORT void ump_dd_reference_add(ump_dd_handle memh)
+{
+ ump_dd_mem *mem = (ump_dd_mem *)memh;
+ int new_ref;
+
+ DEBUG_ASSERT_POINTER(mem);
+
+ new_ref = _ump_osk_atomic_inc_and_read(&mem->ref_count);
+
+ DBG_MSG(5, ("Memory reference incremented. ID: %u, new value: %d\n", mem->secure_id, new_ref));
+}
+
+
+
+UMP_KERNEL_API_EXPORT void ump_dd_reference_release(ump_dd_handle memh)
+{
+ ump_dd_mem *mem = (ump_dd_mem *)memh;
+
+ DEBUG_ASSERT_POINTER(mem);
+
+ ump_random_mapping_put(mem);
+}
+
+
+
+/* --------------- Handling of user space requests follows --------------- */
+
+
+_mali_osk_errcode_t _ump_uku_get_api_version(_ump_uk_api_version_s *args)
+{
+ ump_session_data *session_data;
+
+ DEBUG_ASSERT_POINTER(args);
+ DEBUG_ASSERT_POINTER(args->ctx);
+
+ session_data = (ump_session_data *)args->ctx;
+
+ /* check compatability */
+ if (args->version == UMP_IOCTL_API_VERSION) {
+ DBG_MSG(3, ("API version set to newest %d (compatible)\n",
+ GET_VERSION(args->version)));
+ args->compatible = 1;
+ session_data->api_version = args->version;
+ } else {
+ DBG_MSG(2, ("API version set to %d (incompatible with client version %d)\n",
+ GET_VERSION(UMP_IOCTL_API_VERSION), GET_VERSION(args->version)));
+ args->compatible = 0;
+ args->version = UMP_IOCTL_API_VERSION; /* report our version */
+ }
+
+ return _MALI_OSK_ERR_OK;
+}
+
+
+_mali_osk_errcode_t _ump_ukk_release(_ump_uk_release_s *release_info)
+{
+ ump_session_memory_list_element *session_memory_element;
+ ump_session_memory_list_element *tmp;
+ ump_session_data *session_data;
+ _mali_osk_errcode_t ret = _MALI_OSK_ERR_INVALID_FUNC;
+ int secure_id;
+
+ DEBUG_ASSERT_POINTER(release_info);
+ DEBUG_ASSERT_POINTER(release_info->ctx);
+
+ /* Retreive the session data */
+ session_data = (ump_session_data *)release_info->ctx;
+
+ /* If there are many items in the memory session list we
+ * could be de-referencing this pointer a lot so keep a local copy
+ */
+ secure_id = release_info->secure_id;
+
+ DBG_MSG(4, ("Releasing memory with IOCTL, ID: %u\n", secure_id));
+
+ /* Iterate through the memory list looking for the requested secure ID */
+ _mali_osk_mutex_wait(session_data->lock);
+ _MALI_OSK_LIST_FOREACHENTRY(session_memory_element, tmp, &session_data->list_head_session_memory_list, ump_session_memory_list_element, list) {
+ if (session_memory_element->mem->secure_id == secure_id) {
+ ump_dd_mem *release_mem;
+
+ release_mem = session_memory_element->mem;
+ _mali_osk_list_del(&session_memory_element->list);
+ ump_dd_reference_release(release_mem);
+ _mali_osk_free(session_memory_element);
+
+ ret = _MALI_OSK_ERR_OK;
+ break;
+ }
+ }
+
+ _mali_osk_mutex_signal(session_data->lock);
+ DBG_MSG_IF(1, _MALI_OSK_ERR_OK != ret, ("UMP memory with ID %u does not belong to this session.\n", secure_id));
+
+ DBG_MSG(4, ("_ump_ukk_release() returning 0x%x\n", ret));
+ return ret;
+}
+
+_mali_osk_errcode_t _ump_ukk_size_get(_ump_uk_size_get_s *user_interaction)
+{
+ ump_dd_mem *mem;
+ _mali_osk_errcode_t ret = _MALI_OSK_ERR_FAULT;
+
+ DEBUG_ASSERT_POINTER(user_interaction);
+
+ /* We lock the mappings so things don't get removed while we are looking for the memory */
+ mem = ump_random_mapping_get(device.secure_id_map, user_interaction->secure_id);
+ if (NULL != mem) {
+ user_interaction->size = mem->size_bytes;
+ DBG_MSG(4, ("Returning size. ID: %u, size: %lu ",
+ (ump_secure_id)user_interaction->secure_id,
+ (unsigned long)user_interaction->size));
+ ump_random_mapping_put(mem);
+ ret = _MALI_OSK_ERR_OK;
+ } else {
+ user_interaction->size = 0;
+ DBG_MSG(1, ("Failed to look up mapping in ump_ioctl_size_get(). ID: %u\n",
+ (ump_secure_id)user_interaction->secure_id));
+ }
+
+ return ret;
+}
+
+
+
+void _ump_ukk_msync(_ump_uk_msync_s *args)
+{
+ ump_dd_mem *mem = NULL;
+ void *virtual = NULL;
+ u32 size = 0;
+ u32 offset = 0;
+
+ mem = ump_random_mapping_get(device.secure_id_map, (int)args->secure_id);
+ if (NULL == mem) {
+ DBG_MSG(1, ("Failed to look up mapping in _ump_ukk_msync(). ID: %u\n",
+ (ump_secure_id)args->secure_id));
+ return;
+ }
+
+ /* Returns the cache settings back to Userspace */
+ args->is_cached = mem->is_cached;
+
+ /* If this flag is the only one set, we should not do the actual flush, only the readout */
+ if (_UMP_UK_MSYNC_READOUT_CACHE_ENABLED == args->op) {
+ DBG_MSG(3, ("_ump_ukk_msync READOUT ID: %u Enabled: %d\n", (ump_secure_id)args->secure_id, mem->is_cached));
+ goto msync_release_and_return;
+ }
+
+ /* Nothing to do if the memory is not caches */
+ if (0 == mem->is_cached) {
+ DBG_MSG(3, ("_ump_ukk_msync IGNORING ID: %u Enabled: %d OP: %d\n", (ump_secure_id)args->secure_id, mem->is_cached, args->op));
+ goto msync_release_and_return;
+ }
+ DBG_MSG(3, ("UMP[%02u] _ump_ukk_msync Flush OP: %d Address: 0x%08x Mapping: 0x%08x\n",
+ (ump_secure_id)args->secure_id, args->op, args->address, args->mapping));
+
+ if (args->address) {
+ virtual = (void *)((u32)args->address);
+ offset = (u32)((args->address) - (args->mapping));
+ } else {
+ /* Flush entire mapping when no address is specified. */
+ virtual = args->mapping;
+ }
+ if (args->size) {
+ size = args->size;
+ } else {
+ /* Flush entire mapping when no size is specified. */
+ size = mem->size_bytes - offset;
+ }
+
+ if ((offset + size) > mem->size_bytes) {
+ DBG_MSG(1, ("Trying to flush more than the entire UMP allocation: offset: %u + size: %u > %u\n", offset, size, mem->size_bytes));
+ goto msync_release_and_return;
+ }
+
+ /* The actual cache flush - Implemented for each OS*/
+ _ump_osk_msync(mem, virtual, offset, size, args->op, NULL);
+
+msync_release_and_return:
+ ump_random_mapping_put(mem);
+ return;
+}
+
+void _ump_ukk_cache_operations_control(_ump_uk_cache_operations_control_s *args)
+{
+ ump_session_data *session_data;
+ ump_uk_cache_op_control op;
+
+ DEBUG_ASSERT_POINTER(args);
+ DEBUG_ASSERT_POINTER(args->ctx);
+
+ op = args->op;
+ session_data = (ump_session_data *)args->ctx;
+
+ _mali_osk_mutex_wait(session_data->lock);
+ if (op == _UMP_UK_CACHE_OP_START) {
+ session_data->cache_operations_ongoing++;
+ DBG_MSG(4, ("Cache ops start\n"));
+ if (session_data->cache_operations_ongoing != 1) {
+ DBG_MSG(2, ("UMP: Number of simultanious cache control ops: %d\n", session_data->cache_operations_ongoing));
+ }
+ } else if (op == _UMP_UK_CACHE_OP_FINISH) {
+ DBG_MSG(4, ("Cache ops finish\n"));
+ session_data->cache_operations_ongoing--;
+#if 0
+ if (session_data->has_pending_level1_cache_flush) {
+ /* This function will set has_pending_level1_cache_flush=0 */
+ _ump_osk_msync(NULL, NULL, 0, 0, _UMP_UK_MSYNC_FLUSH_L1, session_data);
+ }
+#endif
+
+ /* to be on the safe side: always flush l1 cache when cache operations are done */
+ _ump_osk_msync(NULL, NULL, 0, 0, _UMP_UK_MSYNC_FLUSH_L1, session_data);
+ DBG_MSG(4, ("Cache ops finish end\n"));
+ } else {
+ DBG_MSG(1, ("Illegal call to %s at line %d\n", __FUNCTION__, __LINE__));
+ }
+ _mali_osk_mutex_signal(session_data->lock);
+
+}
+
+void _ump_ukk_switch_hw_usage(_ump_uk_switch_hw_usage_s *args)
+{
+ ump_dd_mem *mem = NULL;
+ ump_uk_user old_user;
+ ump_uk_msync_op cache_op = _UMP_UK_MSYNC_CLEAN_AND_INVALIDATE;
+ ump_session_data *session_data;
+
+ DEBUG_ASSERT_POINTER(args);
+ DEBUG_ASSERT_POINTER(args->ctx);
+
+ session_data = (ump_session_data *)args->ctx;
+
+ mem = ump_random_mapping_get(device.secure_id_map, (int)args->secure_id);
+ if (NULL == mem) {
+ DBG_MSG(1, ("Failed to look up mapping in _ump_ukk_switch_hw_usage(). ID: %u\n",
+ (ump_secure_id)args->secure_id));
+ return;
+ }
+
+ old_user = mem->hw_device;
+ mem->hw_device = args->new_user;
+
+ DBG_MSG(3, ("UMP[%02u] Switch usage Start New: %s Prev: %s.\n",
+ (ump_secure_id)args->secure_id,
+ args->new_user ? "MALI" : "CPU",
+ old_user ? "MALI" : "CPU"));
+
+ if (!mem->is_cached) {
+ DBG_MSG(3, ("UMP[%02u] Changing owner of uncached memory. Cache flushing not needed.\n",
+ (ump_secure_id)args->secure_id));
+ goto out;
+ }
+
+ if (old_user == args->new_user) {
+ DBG_MSG(4, ("UMP[%02u] Setting the new_user equal to previous for. Cache flushing not needed.\n",
+ (ump_secure_id)args->secure_id));
+ goto out;
+ }
+ if (
+ /* Previous AND new is both different from CPU */
+ (old_user != _UMP_UK_USED_BY_CPU) && (args->new_user != _UMP_UK_USED_BY_CPU)
+ ) {
+ DBG_MSG(4, ("UMP[%02u] Previous and new user is not CPU. Cache flushing not needed.\n",
+ (ump_secure_id)args->secure_id));
+ goto out;
+ }
+
+ if ((old_user != _UMP_UK_USED_BY_CPU) && (args->new_user == _UMP_UK_USED_BY_CPU)) {
+ cache_op = _UMP_UK_MSYNC_INVALIDATE;
+ DBG_MSG(4, ("UMP[%02u] Cache invalidation needed\n", (ump_secure_id)args->secure_id));
+#ifdef UMP_SKIP_INVALIDATION
+#error
+ DBG_MSG(4, ("UMP[%02u] Performing Cache invalidation SKIPPED\n", (ump_secure_id)args->secure_id));
+ goto out;
+#endif
+ }
+
+ /* Take lock to protect: session->cache_operations_ongoing and session->has_pending_level1_cache_flush */
+ _mali_osk_mutex_wait(session_data->lock);
+ /* Actual cache flush */
+ _ump_osk_msync(mem, NULL, 0, mem->size_bytes, cache_op, session_data);
+ _mali_osk_mutex_signal(session_data->lock);
+
+out:
+ ump_random_mapping_put(mem);
+ DBG_MSG(4, ("UMP[%02u] Switch usage Finish\n", (ump_secure_id)args->secure_id));
+ return;
+}
+
+void _ump_ukk_lock(_ump_uk_lock_s *args)
+{
+ ump_dd_mem *mem = NULL;
+
+ mem = ump_random_mapping_get(device.secure_id_map, (int)args->secure_id);
+ if (NULL == mem) {
+ DBG_MSG(1, ("UMP[%02u] Failed to look up mapping in _ump_ukk_lock(). ID: %u\n",
+ (ump_secure_id)args->secure_id));
+ return;
+ }
+
+ DBG_MSG(1, ("UMP[%02u] Lock. New lock flag: %d. Old Lock flag:\n", (u32)args->secure_id, (u32)args->lock_usage, (u32) mem->lock_usage));
+
+ mem->lock_usage = (ump_lock_usage) args->lock_usage;
+
+ ump_random_mapping_put(mem);
+}
+
+void _ump_ukk_unlock(_ump_uk_unlock_s *args)
+{
+ ump_dd_mem *mem = NULL;
+
+ mem = ump_random_mapping_get(device.secure_id_map, (int)args->secure_id);
+ if (NULL == mem) {
+ DBG_MSG(1, ("Failed to look up mapping in _ump_ukk_unlock(). ID: %u\n",
+ (ump_secure_id)args->secure_id));
+ return;
+ }
+
+ DBG_MSG(1, ("UMP[%02u] Unlocking. Old Lock flag:\n",
+ (u32)args->secure_id, (u32) mem->lock_usage));
+
+ mem->lock_usage = (ump_lock_usage) UMP_NOT_LOCKED;
+
+ ump_random_mapping_put(mem);
+}