navienginebsp/naviengine_assp/pci/chunkman.cpp
changeset 0 5de814552237
equal deleted inserted replaced
-1:000000000000 0:5de814552237
       
     1 /*
       
     2 * Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies).
       
     3 * All rights reserved.
       
     4 * This component and the accompanying materials are made available
       
     5 * under the terms of "Eclipse Public License v1.0"
       
     6 * which accompanies this distribution, and is available
       
     7 * at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     8 *
       
     9 * Initial Contributors:
       
    10 * Nokia Corporation - initial contribution.
       
    11 *
       
    12 * Contributors:
       
    13 *
       
    14 * Description:  
       
    15 *
       
    16 */
       
    17 
       
    18 
       
    19 
       
    20 #include "pci-ne.h"
       
    21 #include "../naviengine_pci.h"
       
    22 #include <naviengine.h>
       
    23 #include <kernel/kern_priv.h>
       
    24 
       
    25 //
       
    26 // TChunkManager
       
    27 //
       
    28 
       
    29 TChunkManager::TChunkRecord::TChunkRecord(DPlatChunkHw* aChunk, TInt32 aSize)
       
    30 	:iChunk(aChunk), iSize(aSize)
       
    31 	{
       
    32 	}
       
    33 
       
    34 /**
       
    35 A utitlity function so that we can find pci chunks structs in iChunks array
       
    36 */
       
    37 TBool TChunkManager::TChunkRecord::ChunkComparator(const TChunkRecord& aKeyRecord, const TChunkRecord& aRecord)
       
    38 	{
       
    39 	return (aKeyRecord.iChunk==aRecord.iChunk);
       
    40 	}
       
    41 
       
    42 TChunkManager::TChunkManager(TMappingManager& aMapMan)
       
    43 	:iMapMan(aMapMan), iChunks(), iSharedChunks(), iMutex(NULL)
       
    44 	{
       
    45 	_LIT(KChunkManMutex, "PCI_Chunk_Man_Mutex");
       
    46 	TInt r = Kern::MutexCreate(iMutex, KChunkManMutex, KMutexOrdGeneral2);
       
    47 	__NK_ASSERT_ALWAYS(KErrNone==r);
       
    48 	}
       
    49 
       
    50 TChunkManager::~TChunkManager()
       
    51 	{
       
    52 	iChunks.Reset();
       
    53 	iSharedChunks.Reset();
       
    54 	iMutex->Close(NULL);
       
    55 	}
       
    56 
       
    57 
       
    58 /**
       
    59 @param aChunk a NULL pointer. Will be set to new chunk on success.
       
    60 @param aPciAddress On success will be set to address of chunk buffer in PCI address space
       
    61 @param aSize The size of the chunk to allocate
       
    62 @return An errror code.
       
    63 	- KErrNotFound All of Bridges BARS have been used up.
       
    64 */
       
    65 TInt TChunkManager::AddChunk(DPlatChunkHw*& aChunk, TInt& aSize, TUint aAttributes, TUint32& aPciAddress)
       
    66 	{
       
    67 	NKern::ThreadEnterCS();
       
    68 	Kern::MutexWait(*iMutex);
       
    69 
       
    70 	DPlatChunkHw* chunk=NULL;
       
    71 	TUint32 pciAddress=NULL;
       
    72 	TInt32 size=aSize;
       
    73 
       
    74 	TInt r=DoAddChunk(chunk, aSize, aAttributes, pciAddress);
       
    75 	if(r==KErrNone)
       
    76 		{
       
    77 		aChunk=chunk;
       
    78 		aPciAddress=pciAddress;
       
    79 		aSize=size;
       
    80 		}
       
    81 
       
    82 	Kern::MutexSignal(*iMutex);
       
    83 	NKern::ThreadLeaveCS();
       
    84 
       
    85 	return r;
       
    86 	}
       
    87 
       
    88 /**
       
    89 @param aPciAddress On success will be set to address of chunk buffer in PCI address space
       
    90 @param aSize The size of the chunk to allocate
       
    91 @return An errror code.
       
    92 	- KErrNotFound All of Bridges BARS have been used up.
       
    93 */
       
    94 TInt TChunkManager::DoAddChunk(DPlatChunkHw*& aChunk, TInt aSize, TUint aAttributes, TUint32& aPciAddress)
       
    95 	{
       
    96 	
       
    97 	#ifdef _DEBUG	
       
    98 	__ASSERT_CRITICAL;	
       
    99 	__ASSERT_MUTEX(iMutex); 
       
   100 	#endif
       
   101 
       
   102 	TInt requestedSize=Clp2(aSize);
       
   103 	//Although the client has specified aSize, the actual chunk we get may be larger as it will be rounded to a page size.
       
   104 	//CreateChunk may adjust its size parameter to be the actual size allocated.
       
   105 	TInt r = CreateChunk(aChunk, aSize, aAttributes);
       
   106 	if(r!=KErrNone)
       
   107 		return r;
       
   108 
       
   109 	const TUint32 physicalAddress=aChunk->PhysicalAddress();
       
   110 	
       
   111 	//even if physical memory block was 4K, it is ok to have a smaller
       
   112 	//pci mapping as long as its size is power of 2
       
   113 	TInt mapSize = Min(requestedSize, aSize);
       
   114 	r = iMapMan.CreateMapping(physicalAddress, mapSize, aPciAddress);
       
   115 
       
   116 	//no point having chunk if no mapping in pci space.
       
   117 	if(r!=KErrNone)
       
   118 		{
       
   119 		const TInt ret=DeleteChunk(aChunk, aSize);
       
   120 		__NK_ASSERT_ALWAYS(KErrNone==ret);
       
   121 		return r;
       
   122 		}
       
   123 
       
   124 	//remember the actual size of memory allocated to chunk
       
   125 	const TChunkRecord newRecord(aChunk, aSize);
       
   126 	r = iChunks.Append(newRecord);
       
   127 
       
   128 	// delete chunk if a record of it can't be kept.
       
   129 	if(r!=KErrNone)
       
   130 		{
       
   131 		const TInt ret=DeleteChunk(aChunk, aSize);
       
   132 		__NK_ASSERT_ALWAYS(KErrNone==ret);
       
   133 		return r;
       
   134 		}
       
   135 
       
   136 	return r;
       
   137 	}
       
   138 
       
   139 /**
       
   140 Creates a chunk of specified size, but rounded up to at least a page.
       
   141 @param aChunk On success will be set to the new chunk.
       
   142 @param aSize Size of chunk required, on return will have modfied if size was rounded up.
       
   143 @param aAttributes A bit mask of TMappingAttributes values.
       
   144 @return
       
   145 	- KErrNone
       
   146 	- KErrNoMemory
       
   147 */
       
   148 TInt TChunkManager::CreateChunk(DPlatChunkHw*& aChunk, TInt& aSize, TUint aAttributes)
       
   149 	{
       
   150 	aSize = Kern::RoundToPageSize(aSize);
       
   151 	aSize = Clp2(aSize);
       
   152 	
       
   153 	NKern::ThreadEnterCS();
       
   154 
       
   155 	TPhysAddr physicalAddress=NULL;
       
   156 	TInt r=Epoc::AllocPhysicalRam(aSize, physicalAddress, Log2(aSize));
       
   157 	if(r!=KErrNone)
       
   158 		{
       
   159 		__KTRACE_OPT(KPCI, Kern::Printf("TChunkManager::CreateChunk(): Phys memory alloc failed with error=%d, size=%d (0x%X)", r, aSize, aSize));
       
   160 		NKern::ThreadLeaveCS();
       
   161 		return r;
       
   162 		}
       
   163 
       
   164 	r = DPlatChunkHw::New(aChunk,physicalAddress, aSize, aAttributes);
       
   165 	if(r!=KErrNone)
       
   166 		{
       
   167 		__KTRACE_OPT(KPCI, Kern::Printf("TChunkManager::CreateChunk(): Chunk creation failed with status %d, phys addr=0x%08x, size=%d (0x%X)", r, physicalAddress, aSize, aSize));
       
   168 		const TInt ret=Epoc::FreePhysicalRam(physicalAddress, aSize);
       
   169 		__NK_ASSERT_ALWAYS(KErrNone==ret);
       
   170 		NKern::ThreadLeaveCS();
       
   171 		return r;
       
   172 		}
       
   173 
       
   174 	NKern::ThreadLeaveCS();
       
   175 
       
   176 	__KTRACE_OPT(KPCI, Kern::Printf("TChunkManager::CreateChunk(): Chunk created at 0x%08x, phys addr=0x%08x, size=%d (0x%X)", aChunk, physicalAddress, aSize, aSize));
       
   177 	return r;
       
   178 	}
       
   179 
       
   180 
       
   181 /**
       
   182 Delete and remove PCI mapping for a chunk previously created by this class
       
   183 
       
   184 @param aChunk pointer to a chunk to remove.
       
   185 @return
       
   186 	- KErrNone
       
   187 	- KErrNotFound aChunk was not allocated by this class
       
   188 */
       
   189 TInt TChunkManager::RemoveChunk(DPlatChunkHw* aChunk)
       
   190 	{
       
   191 	NKern::ThreadEnterCS();
       
   192 	Kern::MutexWait(*iMutex);
       
   193 
       
   194 	__KTRACE_OPT(KPCI, Kern::Printf("TChunkManager::RemoveChunk(): Removing chunk at 0x%08X", aChunk));
       
   195 	const TChunkRecord key(aChunk, 0);
       
   196 	TInt index=iChunks.Find(key, TIdentityRelation<TChunkRecord>(TChunkRecord::ChunkComparator) );
       
   197 	TInt r=KErrNone;
       
   198 	if(index!=KErrNotFound)
       
   199 		{
       
   200 		r = iMapMan.RemoveMapping(iChunks[index].iChunk->PhysicalAddress() );
       
   201 		__NK_ASSERT_ALWAYS(KErrNone==r);
       
   202 		r = DeleteChunk(iChunks[index].iChunk, iChunks[index].iSize);
       
   203 		__NK_ASSERT_ALWAYS(r==KErrNone);
       
   204 
       
   205 		iChunks.Remove(index);
       
   206 #ifdef _DEBUG
       
   207 		//free space when entry removed
       
   208 		iChunks.Compress();
       
   209 #endif
       
   210 		}
       
   211 	else
       
   212 		r=index;
       
   213 
       
   214 	Kern::MutexSignal(*iMutex);
       
   215 	NKern::ThreadLeaveCS();
       
   216 
       
   217 	return r;
       
   218 	}
       
   219 
       
   220 /**
       
   221 Delete chunk and remove aSize of physical RAM
       
   222 */
       
   223 TInt TChunkManager::DeleteChunk(DPlatChunkHw* aChunk, TInt aSize)
       
   224 	{
       
   225 	const TPhysAddr address = aChunk->PhysicalAddress();
       
   226 	NKern::ThreadEnterCS();
       
   227 	__KTRACE_OPT(KPCI, Kern::Printf("TChunkManager::DeleteChunk(): Freeing physical RAM: %d (0x%X) bytes at 0x%X ", aSize, aSize, address));
       
   228 	Kern::SafeClose(reinterpret_cast<DObject*&>(aChunk), NULL);
       
   229 	TInt r=Epoc::FreePhysicalRam(address,aSize);
       
   230 	NKern::ThreadLeaveCS();
       
   231 	return r;
       
   232 	}
       
   233 
       
   234 
       
   235 //
       
   236 // Shared Chunk methods
       
   237 //
       
   238 
       
   239 TChunkManager::TSCRecord::TSCRecord(DChunk* aChunk, TUint32 aPhysAddr)
       
   240 	:iChunk(aChunk), iPhysAddr(aPhysAddr), iMapped(EFalse), iSize(0)
       
   241 	{
       
   242 	}
       
   243 
       
   244 
       
   245 TBool TChunkManager::TSCRecord::ChunkComparator(const TSCRecord& aKeyRecord, const TSCRecord& aRecord)
       
   246 	{
       
   247 	return (aKeyRecord.iPhysAddr==aRecord.iPhysAddr);
       
   248 	}
       
   249 
       
   250 TInt TChunkManager::AddChunk(DChunk*& aChunk, TChunkCreateInfo& aAttributes, TUint aOffset, TUint& aSize, TUint32& aPciAddress)
       
   251 	{
       
   252 	NKern::ThreadEnterCS();
       
   253 	Kern::MutexWait(*iMutex);
       
   254 
       
   255 	DChunk* chunk=NULL;
       
   256 	TUint32 pciAddress;
       
   257 
       
   258 	TInt r=DoAddChunk(chunk, aAttributes, aOffset, aSize, pciAddress);
       
   259 	if(r==KErrNone)
       
   260 		{
       
   261 		aChunk=chunk;
       
   262 		aPciAddress=pciAddress;
       
   263 		}
       
   264 	
       
   265 	Kern::MutexSignal(*iMutex);
       
   266 	NKern::ThreadLeaveCS();
       
   267 
       
   268 	return r;
       
   269 	}
       
   270 
       
   271 void TChunkManager::RemoveChunk(TUint32 aPhysicalAddress)
       
   272 	{
       
   273 	NKern::ThreadEnterCS();
       
   274 	Kern::MutexWait(*iMutex);
       
   275 
       
   276 	const TSCRecord key(NULL, aPhysicalAddress);
       
   277 
       
   278 	TInt index=iSharedChunks.Find(key, TIdentityRelation<TSCRecord>(TSCRecord::ChunkComparator) );
       
   279 	__NK_ASSERT_ALWAYS(index!=KErrNotFound);
       
   280 
       
   281 	TSCRecord& record(iSharedChunks[index]);
       
   282 	__NK_ASSERT_DEBUG(record.iPhysAddr==aPhysicalAddress);
       
   283 
       
   284 	// We will always have to free RAM
       
   285 	const TInt size=record.iSize;
       
   286 	__KTRACE_OPT(KPCI, Kern::Printf("TChunkManager::RemoveChunk(TUint32) Freeing physical RAM: %d (0x%X) bytes at 0x%X ", size, size, aPhysicalAddress));
       
   287 	NKern::ThreadEnterCS();
       
   288 	TInt r = Epoc::FreePhysicalRam(aPhysicalAddress, size);
       
   289 	NKern::ThreadLeaveCS();
       
   290 	__NK_ASSERT_ALWAYS(KErrNone==r);
       
   291 
       
   292 	if(record.iMapped)
       
   293 		{
       
   294 		__KTRACE_OPT(KPCI,
       
   295 			Kern::Printf("TChunkManager::RemoveChunk(TUint32) Removing PCI mapping for RAM: %d (0x%X) bytes at 0x%X ",
       
   296 				size, size, record.iPhysAddr));
       
   297 
       
   298 		TInt r=iMapMan.RemoveMapping(record.iPhysAddr);
       
   299 		__NK_ASSERT_ALWAYS(KErrNone==r);
       
   300 		}
       
   301 	else
       
   302 		{
       
   303 		__KTRACE_OPT(KPCI, Kern::Printf("TChunkManager::RemoveChunk(TUint32) No PCI mapping for memory")); 
       
   304 		}
       
   305 
       
   306 	iSharedChunks.Remove(index);
       
   307 #ifdef _DEBUG
       
   308 	//free space when entry removed
       
   309 	iSharedChunks.Compress();
       
   310 #endif
       
   311 
       
   312 	Kern::MutexSignal(*iMutex);
       
   313 	NKern::ThreadLeaveCS();
       
   314 	}
       
   315 
       
   316 TInt TChunkManager::DoAddChunk(DChunk*& aChunk, TChunkCreateInfo& aAttributes, TUint aOffset, TUint& aSize, TUint32& aPciAddress)
       
   317 	{
       
   318 	TInt r = iSharedChunks.Append(TSCRecord(NULL, NULL));
       
   319 	if(r!=KErrNone)
       
   320 		{
       
   321 		return r;
       
   322 		}
       
   323 
       
   324 	const TInt count = iSharedChunks.Count();
       
   325 	TSCRecord& newRecord(iSharedChunks[count-1]);
       
   326 	//Although the client has specified aSize, the actual chunk we get may be larger as it will be rounded to a page size.
       
   327 	//CreateChunk may adjust its size parameter to be the actual size allocated.
       
   328 	r = CreateChunk(aChunk, aAttributes, aOffset, aSize, newRecord);
       
   329 	if(r!=KErrNone)
       
   330 		{
       
   331 		//If newRecord is not populated we must remove it.
       
   332 		//If it has been populated, the chunk record will
       
   333 		//be removed by the chunk's cleanup DFC and we must leave
       
   334 		//it alone.
       
   335 		if(newRecord.iChunk==NULL)
       
   336 			{
       
   337 			iSharedChunks.Remove(count-1);
       
   338 #ifdef _DEBUG
       
   339 			//free space when entry removed
       
   340 			iSharedChunks.Compress();
       
   341 #endif
       
   342 			}
       
   343 
       
   344 		return r;
       
   345 		}
       
   346 
       
   347 	//map all of the commited memory, which may exceed what the client requested
       
   348 	//The minumum memory allocation is 4k
       
   349 	//And memory must also be mapped in powers of 2 eg. A request for 11k would create
       
   350 	//and map 16K
       
   351 
       
   352 
       
   353 	//need CS since if we succeed in creating a mapping we
       
   354 	//must be allowed to toggle the iMapped flag in the record array.
       
   355 	NKern::ThreadEnterCS();
       
   356 	TInt size=aSize;
       
   357 	r = iMapMan.CreateMapping(newRecord.iPhysAddr, size, aPciAddress);
       
   358 
       
   359 	//no point having chunk if no mapping in pci space.
       
   360 	if(r!=KErrNone)
       
   361 		{
       
   362 		//chunk will proceed to cleanup memory only
       
   363 		TBool destructionPending=Kern::ChunkClose(aChunk);
       
   364 		NKern::ThreadLeaveCS();
       
   365 		__NK_ASSERT_ALWAYS(destructionPending);
       
   366 		return r;
       
   367 		}
       
   368 	newRecord.iMapped=ETrue;
       
   369 	NKern::ThreadLeaveCS();
       
   370 
       
   371 	__NK_ASSERT_ALWAYS(r==KErrNone); //we reserved space for this
       
   372 
       
   373 	return r;
       
   374 	}
       
   375 
       
   376 TInt TChunkManager::CreateChunk(DChunk*& aChunk, TChunkCreateInfo& aAttributes, TUint aOffset, TUint& aSize, TSCRecord& aRecord)
       
   377 	{
       
   378 	//natuarally align aSize.
       
   379 	const TInt size=Clp2(aSize);
       
   380 	const TInt align=Log2(size);	
       
   381 
       
   382 	TPhysAddr physicalAddress=NULL;
       
   383 
       
   384 	NKern::ThreadEnterCS();
       
   385 	TInt r = Epoc::AllocPhysicalRam(size, physicalAddress, align);	
       
   386     if(r != KErrNone)
       
   387 	    {
       
   388 		__KTRACE_OPT(KPCI, Kern::Printf("TChunkManager::CreateChunk(DChunk*): Phys memory alloc failed with error=%d, size=%d (0x%X)", r, size, size));
       
   389 		NKern::ThreadLeaveCS();
       
   390 		return r; 
       
   391 	    }
       
   392 	
       
   393 	//now that we know the physical address of our memory
       
   394 	//we can create the cleanup object
       
   395 	TNaviEngineChunkCleanup* cleanup= new TNaviEngineChunkCleanup(*this, physicalAddress);
       
   396 	if(cleanup == NULL)
       
   397 		{
       
   398 		//free physical ram
       
   399 		r = Epoc::FreePhysicalRam(physicalAddress, size);
       
   400 		__NK_ASSERT_ALWAYS(KErrNone==r);
       
   401 		NKern::ThreadLeaveCS();
       
   402 		return KErrNoMemory;
       
   403 		}
       
   404 
       
   405 	//Since we are mapping in memory we alloc'd
       
   406 	//the chunk is not responsible for freeing it.
       
   407 	aAttributes.iOwnsMemory=EFalse;
       
   408 
       
   409 	//ensure that max size is large enough to contain the rounded physical block plus specified guard offsets
       
   410 	//at each end
       
   411 	aAttributes.iMaxSize=Max(aAttributes.iMaxSize, size+(2*aOffset));
       
   412 
       
   413 	aAttributes.iDestroyedDfc = cleanup;
       
   414 
       
   415 	TLinAddr kernAddr;
       
   416 	TUint32 attribs;
       
   417 	r=Kern::ChunkCreate(aAttributes, aChunk, kernAddr, attribs);
       
   418     if(r != KErrNone)
       
   419 	    {
       
   420 		//free physical ram
       
   421 		TInt err = Epoc::FreePhysicalRam(physicalAddress, size);
       
   422 		__NK_ASSERT_ALWAYS(KErrNone==err);
       
   423 
       
   424 		delete cleanup;
       
   425 		NKern::ThreadLeaveCS();
       
   426 		return r;
       
   427 	    }
       
   428 	//At this point, the cleanup object will look after its own destruction
       
   429 	//when the chunk is closed
       
   430 	cleanup=NULL;
       
   431 	
       
   432 	//these will be requird in order to free physical memory if we have to close the chunk
       
   433 	aRecord.iPhysAddr=physicalAddress;
       
   434 	aRecord.iChunk=aChunk;	
       
   435 	aRecord.iSize=size;
       
   436 		
       
   437 	r=Kern::ChunkCommitPhysical(aChunk, aOffset, size, physicalAddress);  
       
   438 
       
   439 	if(r!=KErrNone)
       
   440 		{
       
   441 		TBool destructionPending=Kern::ChunkClose(aChunk);
       
   442 		__NK_ASSERT_ALWAYS(destructionPending);
       
   443 		NKern::ThreadLeaveCS();
       
   444 		return r;
       
   445 		}
       
   446 
       
   447 	//report back size commited
       
   448 	aSize=size;
       
   449 	__KTRACE_OPT(KPCI, Kern::Printf("TChunkManager::CreateChunk(DChunk*): Chunk created at 0x%08x, phys addr=0x%08x, size=%d (0x%X)",
       
   450 		aChunk, physicalAddress, aSize, aSize));
       
   451 
       
   452 	NKern::ThreadLeaveCS();
       
   453 	return r;
       
   454 	}
       
   455 
       
   456