/* -*- Mode: C; tab-width: 4; indent-tabs-mode: 't; c-basic-offset: 4 -*-
*
* Name : $RCSfile: virtmem.c $
*
* Copyright : 2001,2002 by Imagination Technologies Limited.
* All rights reserved.
* No part of this software, either material or conceptual
* may be copied or distributed, transmitted, transcribed,
* stored in a retrieval system or translated into any
* human or computer language in any form by any means,
* electronic, mechanical, manual or other-wise, or
* disclosed to third parties without the express written
* permission of:
* Imagination Technologies Limited,
* HomePark Industrial Estate,
* Kings Langley,
* Hertfordshire,
* WD4 8LZ,
* UK
*
* Description : Defines functions providing virtually contiguous memory
* allocations under Linux.
*
* Version : $Revision: 1.12 $
*
**************************************************************************/
#include <linux/version.h>
#include <linux/mm.h>
#include <linux/vmalloc.h>
#include <linux/wrapper.h>
#include <linux/slab.h>
#include "virtmem.h"
#include "mmap.h"
#include "debug.h"
static VIRT_ALLOC_REC VMallocHead;
static PVIRT_ALLOC_REC psVMallocHead = &VMallocHead;
/*
// virtual_allocate_reserve
//
// Purpose: Allocates virtually contiguous pages and reserves them
//
// Args: nPages - number of pages to reserve
// bCached - cacheable pages?
//
// Returns: Page-aligned address of virtual allocation or zero on error.
*/
void* virtual_allocate_reserve(unsigned long nPages, unsigned long bCached)
{
void* pkvMem;
void* pkvPageAlignedMem = 0;
unsigned long pkvCurrentPage;
PVIRT_ALLOC_REC psLastRecord = psVMallocHead;
PVIRT_ALLOC_REC psCurrentRecord = psVMallocHead->pNext;
PVIRT_ALLOC_REC psNewRecord;
/* Allocate virtually contiguous pages */
if (bCached)
pkvMem = __vmalloc(nPages*PAGE_SIZE, GFP_KERNEL, PAGE_KERNEL);
else
#if defined(GCC_IA32)
pkvMem = __vmalloc(nPages*PAGE_SIZE, GFP_KERNEL,
PAGE_KERNEL_NOCACHE);
#elif defined(ARM)
pkvMem = __vmalloc(nPages*PAGE_SIZE, GFP_KERNEL,
__pgprot(_L_PTE_DEFAULT | L_PTE_DIRTY | L_PTE_WRITE));
#else
#error "virtual_allocate_reserve - don't know how to vmalloc uncached"
#endif
if (pkvMem)
{
/* Determine a page aligned pointer */
pkvPageAlignedMem = (void *)(((unsigned long)pkvMem + PAGE_SIZE - 1) & PAGE_MASK);
/* Reserve those pages to allow them to be re-mapped to user space */
for (
pkvCurrentPage = (unsigned long)pkvPageAlignedMem;
pkvCurrentPage < ((unsigned long)pkvPageAlignedMem + nPages*PAGE_SIZE);
pkvCurrentPage += PAGE_SIZE
)
{
mem_map_reserve(ConvertKVToPage(pkvCurrentPage));
}
/* DEBUGGING START */
DPF("New vmalloc - pkvMem: 0x%08X, pkvPageAlignedMem: 0x%08X, nPages: 0x%08X\n", pkvMem, pkvPageAlignedMem, nPages);
/* DEBUGGING END */
/* Register the area in mmap address space */
pvr_mmap_register_area(pkvPageAlignedMem, nPages*PAGE_SIZE, PVR_MMAP_VIRTUAL, bCached);
/* Create a new memory allocation record */
psNewRecord = kmalloc(sizeof(VIRT_ALLOC_REC), GFP_KERNEL);
if (psNewRecord)
{
/* Locate the last entry in the tracking list */
while (psCurrentRecord)
{
psLastRecord = psCurrentRecord;
psCurrentRecord = psCurrentRecord->pNext;
}
/* Record the allocation */
psNewRecord->pkvMem = (unsigned long)pkvMem;
psNewRecord->pkvPageAlignedMem = (unsigned long)pkvPageAlignedMem;
psNewRecord->nBytes = nPages * PAGE_SIZE;
/* Append the new record */
psLastRecord->pNext = psNewRecord;
psNewRecord->pNext = NULL;
}
else
{
DPF("virtmem.c - virtual_allocate_reserve: Error - Failed to allocate memory record.\n");
}
}
return pkvPageAlignedMem;
}
/*
// virtual_deallocate_unreserve
//
// Purpose: Unreserves and deallocates pages allocated by virtual_allocate_reserve
//
// Args: pkvPageAlignedMem - Page-aligned address returned by virtual_allocate_reserve
//
// Returns: None.
*/
void virtual_deallocate_unreserve(void* pkvPageAlignedMem)
{
PVIRT_ALLOC_REC psCurrentRecord = psVMallocHead->pNext;
PVIRT_ALLOC_REC psLastRecord = psVMallocHead;
void* pkvMem;
unsigned long nBytes;
unsigned long pkvCurrentPage;
/* Locate the corresponding allocation entry */
while (psCurrentRecord)
{
if (psCurrentRecord->pkvPageAlignedMem == (unsigned long)pkvPageAlignedMem)
break;
psLastRecord = psCurrentRecord;
psCurrentRecord = psCurrentRecord->pNext;
}
if (psCurrentRecord)
{
/* Retrieve the size of the allocation */
nBytes = psCurrentRecord->nBytes;
/* Retrieve the original allocation pointer */
pkvMem = (void*)(psCurrentRecord->pkvMem);
/* Unlink the allocation record */
psLastRecord->pNext = psCurrentRecord->pNext;
/* Delete the allocation record */
kfree(psCurrentRecord);
/* Unreserve pages */
for (
pkvCurrentPage = (unsigned long)pkvPageAlignedMem;
pkvCurrentPage < ((unsigned long)pkvPageAlignedMem + nBytes);
pkvCurrentPage += PAGE_SIZE
)
{
mem_map_unreserve(ConvertKVToPage(pkvCurrentPage));
}
pvr_mmap_remove_registered_area(pkvPageAlignedMem);
/* DEBUGGING START */
DPF("Deallocating vmalloc - pkvMem: 0x%08X\n", pkvMem);
/* DEBUGGING END */
/* De-allocate memory */
if (pkvMem)
{
vfree(pkvMem);
}
}
else
{
DPF("virtmem.c - kernel_deallocate_unreserve: Error - failed to find allocation record.\n");
}
}
/*
// virtual_memory_cleanup - Frees any remaining memory allocations
*/
void virtual_memory_cleanup(void)
{
PVIRT_ALLOC_REC psCurrentRecord;
while (psVMallocHead && (psCurrentRecord = psVMallocHead->pNext))
{
virtual_deallocate_unreserve((void *)psCurrentRecord->pkvPageAlignedMem);
}
}
|