| /* |
| * Copyright (C) 2010-2014, 2016 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_ref_drv.h" |
| #include "ump_kernel_common.h" |
| #include "ump_kernel_descriptor_mapping.h" |
| |
| #define UMP_MINIMUM_SIZE 4096 |
| #define UMP_MINIMUM_SIZE_MASK (~(UMP_MINIMUM_SIZE-1)) |
| #define UMP_SIZE_ALIGN(x) (((x)+UMP_MINIMUM_SIZE-1)&UMP_MINIMUM_SIZE_MASK) |
| #define UMP_ADDR_ALIGN_OFFSET(x) ((x)&(UMP_MINIMUM_SIZE-1)) |
| static void phys_blocks_release(void *ctx, struct ump_dd_mem *descriptor); |
| |
| UMP_KERNEL_API_EXPORT ump_dd_handle ump_dd_handle_create_from_phys_blocks(ump_dd_physical_block *blocks, unsigned long num_blocks) |
| { |
| ump_dd_mem *mem; |
| unsigned long size_total = 0; |
| int ret; |
| u32 i; |
| |
| /* Go through the input blocks and verify that they are sane */ |
| for (i = 0; i < num_blocks; i++) { |
| unsigned long addr = blocks[i].addr; |
| unsigned long size = blocks[i].size; |
| |
| DBG_MSG(5, ("Adding physical memory to new handle. Address: 0x%08lx, size: %lu\n", addr, size)); |
| size_total += blocks[i].size; |
| |
| if (0 != UMP_ADDR_ALIGN_OFFSET(addr)) { |
| MSG_ERR(("Trying to create UMP memory from unaligned physical address. Address: 0x%08lx\n", addr)); |
| return UMP_DD_HANDLE_INVALID; |
| } |
| |
| if (0 != UMP_ADDR_ALIGN_OFFSET(size)) { |
| MSG_ERR(("Trying to create UMP memory with unaligned size. Size: %lu\n", size)); |
| return UMP_DD_HANDLE_INVALID; |
| } |
| } |
| |
| /* Allocate the ump_dd_mem struct for this allocation */ |
| mem = _mali_osk_malloc(sizeof(*mem)); |
| if (NULL == mem) { |
| DBG_MSG(1, ("Could not allocate ump_dd_mem in ump_dd_handle_create_from_phys_blocks()\n")); |
| return UMP_DD_HANDLE_INVALID; |
| } |
| |
| /* Now, make a copy of the block information supplied by the user */ |
| mem->block_array = _mali_osk_malloc(sizeof(ump_dd_physical_block) * num_blocks); |
| if (NULL == mem->block_array) { |
| _mali_osk_free(mem); |
| DBG_MSG(1, ("Could not allocate a mem handle for function ump_dd_handle_create_from_phys_blocks().\n")); |
| return UMP_DD_HANDLE_INVALID; |
| } |
| |
| _mali_osk_memcpy(mem->block_array, blocks, sizeof(ump_dd_physical_block) * num_blocks); |
| |
| /* And setup the rest of the ump_dd_mem struct */ |
| _mali_osk_atomic_init(&mem->ref_count, 1); |
| mem->size_bytes = size_total; |
| mem->nr_blocks = num_blocks; |
| mem->backend_info = NULL; |
| mem->ctx = NULL; |
| mem->release_func = phys_blocks_release; |
| /* For now UMP handles created by ump_dd_handle_create_from_phys_blocks() is forced to be Uncached */ |
| mem->is_cached = 0; |
| mem->hw_device = _UMP_UK_USED_BY_CPU; |
| mem->lock_usage = UMP_NOT_LOCKED; |
| |
| /* Find a secure ID for this allocation */ |
| ret = ump_random_mapping_insert(device.secure_id_map, mem); |
| if (unlikely(ret)) { |
| _mali_osk_free(mem->block_array); |
| _mali_osk_free(mem); |
| DBG_MSG(1, ("Failed to allocate secure ID in ump_dd_handle_create_from_phys_blocks()\n")); |
| return UMP_DD_HANDLE_INVALID; |
| } |
| |
| DBG_MSG(3, ("UMP memory created. ID: %u, size: %lu\n", mem->secure_id, mem->size_bytes)); |
| |
| return (ump_dd_handle)mem; |
| } |
| |
| static void phys_blocks_release(void *ctx, struct ump_dd_mem *descriptor) |
| { |
| _mali_osk_free(descriptor->block_array); |
| descriptor->block_array = NULL; |
| } |
| |
| _mali_osk_errcode_t _ump_ukk_allocate(_ump_uk_allocate_s *user_interaction) |
| { |
| ump_session_data *session_data = NULL; |
| ump_dd_mem *new_allocation = NULL; |
| ump_session_memory_list_element *session_memory_element = NULL; |
| int ret; |
| |
| DEBUG_ASSERT_POINTER(user_interaction); |
| DEBUG_ASSERT_POINTER(user_interaction->ctx); |
| |
| session_data = (ump_session_data *) user_interaction->ctx; |
| |
| session_memory_element = _mali_osk_calloc(1, sizeof(ump_session_memory_list_element)); |
| if (NULL == session_memory_element) { |
| DBG_MSG(1, ("Failed to allocate ump_session_memory_list_element in ump_ioctl_allocate()\n")); |
| return _MALI_OSK_ERR_NOMEM; |
| } |
| |
| |
| new_allocation = _mali_osk_calloc(1, sizeof(ump_dd_mem)); |
| if (NULL == new_allocation) { |
| _mali_osk_free(session_memory_element); |
| DBG_MSG(1, ("Failed to allocate ump_dd_mem in _ump_ukk_allocate()\n")); |
| return _MALI_OSK_ERR_NOMEM; |
| } |
| |
| /* Initialize the part of the new_allocation that we know so for */ |
| _mali_osk_atomic_init(&new_allocation->ref_count, 1); |
| if (0 == (UMP_REF_DRV_UK_CONSTRAINT_USE_CACHE & user_interaction->constraints)) |
| new_allocation->is_cached = 0; |
| else new_allocation->is_cached = 1; |
| |
| /* Special case a size of 0, we should try to emulate what malloc does |
| * in this case, which is to return a valid pointer that must be freed, |
| * but can't be dereferenced */ |
| if (0 == user_interaction->size) { |
| /* Emulate by actually allocating the minimum block size */ |
| user_interaction->size = 1; |
| } |
| |
| /* Page align the size */ |
| new_allocation->size_bytes = UMP_SIZE_ALIGN(user_interaction->size); |
| new_allocation->lock_usage = UMP_NOT_LOCKED; |
| |
| /* Now, ask the active memory backend to do the actual memory allocation */ |
| if (!device.backend->allocate(device.backend->ctx, new_allocation)) { |
| DBG_MSG(3, ("OOM: No more UMP memory left. Failed to allocate memory in ump_ioctl_allocate(). Size: %lu, requested size: %lu\n", |
| new_allocation->size_bytes, |
| (unsigned long)user_interaction->size)); |
| _mali_osk_free(new_allocation); |
| _mali_osk_free(session_memory_element); |
| return _MALI_OSK_ERR_INVALID_FUNC; |
| } |
| new_allocation->hw_device = _UMP_UK_USED_BY_CPU; |
| new_allocation->ctx = device.backend->ctx; |
| new_allocation->release_func = device.backend->release; |
| |
| /* Initialize the session_memory_element, and add it to the session object */ |
| session_memory_element->mem = new_allocation; |
| _mali_osk_mutex_wait(session_data->lock); |
| _mali_osk_list_add(&(session_memory_element->list), &(session_data->list_head_session_memory_list)); |
| _mali_osk_mutex_signal(session_data->lock); |
| |
| /* Create a secure ID for this allocation */ |
| ret = ump_random_mapping_insert(device.secure_id_map, new_allocation); |
| if (unlikely(ret)) { |
| new_allocation->release_func(new_allocation->ctx, new_allocation); |
| _mali_osk_free(session_memory_element); |
| _mali_osk_free(new_allocation); |
| DBG_MSG(1, ("Failed to allocate secure ID in ump_ioctl_allocate()\n")); |
| return _MALI_OSK_ERR_INVALID_FUNC; |
| } |
| |
| user_interaction->secure_id = new_allocation->secure_id; |
| user_interaction->size = new_allocation->size_bytes; |
| DBG_MSG(3, ("UMP memory allocated. ID: %u, size: %lu\n", |
| new_allocation->secure_id, |
| new_allocation->size_bytes)); |
| |
| return _MALI_OSK_ERR_OK; |
| } |