/* -*- 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 #include #include #include #include #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); } }