/*
|
* Copyright (C) 2010-2011, 2013-2014, 2016-2017 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 <linux/version.h>
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)
|
#include <linux/semaphore.h>
|
#else /* pre 2.6.26 the file was in the arch specific location */
|
#include <asm/semaphore.h>
|
#endif
|
|
#include <linux/dma-mapping.h>
|
#include <linux/mm.h>
|
#include <linux/slab.h>
|
#include <asm/atomic.h>
|
#include <linux/vmalloc.h>
|
#include <asm/cacheflush.h>
|
#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;
|
}
|