commsfwutils/commsbufs/src/commsbufskern.cpp
changeset 0 dfb7c4ff071f
equal deleted inserted replaced
-1:000000000000 0:dfb7c4ff071f
       
     1 // Copyright (c) 1999-2009 Nokia Corporation and/or its subsidiary(-ies).
       
     2 // All rights reserved.
       
     3 // This component and the accompanying materials are made available
       
     4 // under the terms of "Eclipse Public License v1.0"
       
     5 // which accompanies this distribution, and is available
       
     6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     7 //
       
     8 // Initial Contributors:
       
     9 // Nokia Corporation - initial contribution.
       
    10 //
       
    11 // Contributors:
       
    12 //
       
    13 // Description:
       
    14 //
       
    15 
       
    16 #include <comms-infras/commsbufskern.h>
       
    17 #include "commsbufpond_internal.h"
       
    18 #include <kernel/kernel.h>
       
    19 
       
    20 DECLARE_STANDARD_LDD()
       
    21 	{
       
    22 	return NULL;
       
    23 	}
       
    24 
       
    25 
       
    26 /**
       
    27 @purpose Fetch a user side handle to underlying shared buffer for a given client and populate commsbuf
       
    28 metadata ready for transfer to the client. Closes kernel reference to buffer.
       
    29 */
       
    30 EXPORT_C TInt DCommsBuf::TransferToClient(DThread* aClient)
       
    31 	{
       
    32 	// Construct the DCommsBuf metadata in the prescribed spot in the buffer
       
    33 	DCommsBuf* metadataPtr = (DCommsBuf*)iCommsBufPtr;
       
    34 	*metadataPtr = *this;
       
    35 
       
    36 	// Initialise the fields that were being used for other purposes kernel side
       
    37 	metadataPtr->iShBuf = NULL;
       
    38 	metadataPtr->iCommsBufPtr = NULL;
       
    39 
       
    40 	// Fetch a handle for the given client we will hand it to and also put the handle in the metadata too
       
    41 	NKern::ThreadEnterCS();
       
    42 	TInt result = Kern::ShBufMakeHandleAndOpen(iShBuf, aClient);
       
    43 	NKern::ThreadLeaveCS();
       
    44 	
       
    45 	// Doesn't matter if result is an error code for failed MakeHandle as we close it now anyway
       
    46 	metadataPtr->iHandle = result;
       
    47 
       
    48 	// Close _our_ handle to the buffer
       
    49 	Kern::ShBufClose(iShBuf);
       
    50 
       
    51 	// We return the handle so the the caller can pass it to the user side client independently
       
    52 	return result;
       
    53 	}
       
    54 
       
    55 /**
       
    56 Sanitise a buffer arriving from user side and open a reference to it. This involves sanitising the metadata
       
    57 and copying out a validated version to a kernel private location (aSanitised). That is, check that the fields
       
    58 that will be accessed kernel side can not cause illegal operations.
       
    59 @param aClient Client thread that the buffer is being received from.
       
    60 @param aHandle User side handle to the buffer. Although opaque this is in effect a shbuf handle.
       
    61 aSanitised Pointer to kernel private memory to take the validated DCommsBuf
       
    62 @return KErrCorrupt if metadata is unsafe
       
    63 */
       
    64 EXPORT_C TInt DCommsBuf::AcceptFromClient(DThread* aClient, TInt aHandle, DCommsBuf* aSanitised)
       
    65 	{
       
    66 	// Open the underlying shbuf buffer
       
    67 	TShBuf* shbuf;
       
    68 	NKern::ThreadEnterCS();
       
    69 	TInt result = Kern::ShBufOpen(shbuf, aClient, aHandle);
       
    70 	NKern::ThreadLeaveCS();
       
    71 	if(result != KErrNone)
       
    72 		{
       
    73 		// Close the user handle
       
    74 		NKern::ThreadEnterCS();
       
    75 		Kern::CloseHandle(aClient, aHandle);
       
    76 		NKern::ThreadLeaveCS();
       
    77 		return result;
       
    78 		}
       
    79 
       
    80 	TUint8* shBufPtr = Kern::ShBufPtr(shbuf);
       
    81 	TInt shBufSize = Kern::ShBufSize(shbuf);
       
    82 
       
    83 	// Copy out first (after all it could be corrupted between checking and copying)
       
    84 	// The first 8 words are the only ones of interest to us
       
    85 	TUint8* inPlaceMetadata = (shBufPtr + shBufSize - KCommsBufMetadataSize);
       
    86 	typedef struct
       
    87 		{
       
    88 		TInt iCompulsoryFields[8];
       
    89 		} TCompulsory;
       
    90 	*(TCompulsory*)aSanitised = *(TCompulsory*)inPlaceMetadata;
       
    91 
       
    92 	// Set kernel relevant fields
       
    93 	aSanitised->iShBuf = shbuf;
       
    94 	aSanitised->iCommsBufPtr = inPlaceMetadata;
       
    95 
       
    96 	// Now verify the metadata that matters to us
       
    97 	TBool valid = EFalse;
       
    98 #ifndef _DEBUG
       
    99 	// Speedy UREL checks
       
   100 	if(((inPlaceMetadata + aSanitised->iOffset) >= shBufPtr)
       
   101 		&& (aSanitised->iOffset <= 0)
       
   102 		&& ((aSanitised->iOffset + aSanitised->iLength) <= 0)
       
   103 		&& (aSanitised->iLength >= 0))
       
   104 		{
       
   105 		// Don't bother checking these ones as we can cheaply write them from know values
       
   106 		aSanitised->iRawSize = (shBufSize - KCommsBufMetadataSize);
       
   107 		aSanitised->iRawDataOffset = (shBufPtr - inPlaceMetadata);
       
   108 		valid = ETrue;
       
   109 		}
       
   110 #else
       
   111 	// More prosaic UDEB checks
       
   112 	if((aSanitised->iFrontCanary == KCanaryDefault)
       
   113 		&& ((inPlaceMetadata + aSanitised->iOffset) >= shBufPtr)
       
   114 		&& (aSanitised->iOffset <= 0)
       
   115 		&& ((aSanitised->iOffset + aSanitised->iLength) <= 0)
       
   116 		&& (aSanitised->iLength >= 0)
       
   117 		&& (aSanitised->iRawSize == (shBufSize - KCommsBufMetadataSize))
       
   118 		&& (aSanitised->iRawDataOffset == (shBufPtr - inPlaceMetadata)))
       
   119 		{
       
   120 		valid = ETrue;
       
   121 		}
       
   122 #endif
       
   123 
       
   124 	__ASSERT_DEBUG(valid, KernCommsBuf::Fault(ECommsBufMetadataCorrupt));
       
   125 	if(!valid)
       
   126 		{
       
   127 		// Close the user handle and our kernel reference
       
   128 		NKern::ThreadEnterCS();
       
   129 		Kern::CloseHandle(aClient, aHandle);
       
   130 		NKern::ThreadLeaveCS();
       
   131 		Kern::ShBufClose(shbuf);
       
   132 		return KErrCorrupt;
       
   133 		}
       
   134 
       
   135 	// Can now close the user side handle
       
   136 	NKern::ThreadEnterCS();
       
   137 	Kern::CloseHandle(aClient, aHandle);
       
   138 	NKern::ThreadLeaveCS();
       
   139 	
       
   140 	return KErrNone;
       
   141 	}
       
   142 
       
   143 /**
       
   144 Closes reference to commsbuf
       
   145 */
       
   146 EXPORT_C void DCommsBuf::Free()
       
   147 	{
       
   148 	// Close _our_ handle to the buffer
       
   149 	Kern::ShBufClose(iShBuf);
       
   150 	}
       
   151 
       
   152 /**
       
   153 Constructs DCommsbuf based upon underlying shared buffer.
       
   154 Should be constructed in kernel private memory not in place in the buffer.
       
   155 Offset defaults to zero and length to the maximum length
       
   156 @param aShBuf Underlying shared buffer
       
   157 @param aCommsPoolHandle Opaque user side handle (as discovered through driver interface)
       
   158 */
       
   159 DCommsBuf::DCommsBuf(TShBuf* aShBuf, TInt aCommsPoolHandle, TInt aBufSize)
       
   160 	{
       
   161 	__ASSERT_COMPILE(sizeof(TCommsBuf) == TCommsBuf::KCommsBufMetadataSize);
       
   162 	__ASSERT_COMPILE(sizeof(DCommsBuf) == sizeof(TCommsBuf));
       
   163 
       
   164 	__ASSERT_DEBUG(aCommsPoolHandle, KernCommsBuf::Fault(ECommsBufMetadataCorrupt));
       
   165 
       
   166 	iFrontCanary = KCanaryDefault;
       
   167 	TUint8* shBufPtr = Kern::ShBufPtr(aShBuf);
       
   168 	iShBuf = aShBuf;
       
   169 	iCommsBufPtr = shBufPtr + aBufSize;
       
   170 	
       
   171 	iRawDataOffset = -aBufSize;
       
   172 	iOffset = iRawDataOffset;
       
   173 	iRawSize = aBufSize;
       
   174 	iLength = aBufSize;
       
   175 	iNext = NULL;
       
   176 	iPool = aCommsPoolHandle;
       
   177 	iHandle = 0;
       
   178 	
       
   179 	iReservedWords[0] = 0;
       
   180 	iReservedWords[1] = 0;
       
   181 	iReservedWords[2] = 0;
       
   182 	iReservedWords[3] = 0;
       
   183 	iReservedWords[4] = 0;
       
   184 	iBackCanary = KCanaryDefault;
       
   185 	}
       
   186 
       
   187 /**
       
   188 @purpose Static constructor for DCommsPond
       
   189 */
       
   190 EXPORT_C DCommsPond* DCommsPond::New()
       
   191 	{
       
   192 	DCommsPond* self = new DCommsPond();
       
   193 	if(!self)
       
   194 		{
       
   195 		return NULL;
       
   196 		}
       
   197 	
       
   198 	self->iPond = new TCommsPond();
       
   199 	if(!(self->iPond))
       
   200 		{
       
   201 		delete self;
       
   202 		return NULL;
       
   203 		}
       
   204 	else
       
   205 		{
       
   206 		return self;
       
   207 		}
       
   208 	}
       
   209 
       
   210 /**
       
   211 Allocates a commsbuf from the kernel pond. Allocation is from the default shared buffer pool.
       
   212 The kernel side commsbuf metadata (DCommsBuf) is constructed in-place within the buffer.
       
   213 This metadata defaults to zero length and zero offset. The user side opaque handle is also set accordingly.
       
   214 @return KErrNoMemory if no buffers available
       
   215 */
       
   216 EXPORT_C TInt DCommsPond::Alloc(DCommsBuf& aBuf)
       
   217 	{
       
   218 	// Pond should have been initialised from comms already
       
   219 	__ASSERT_DEBUG(iInitialised && iDefaultAllocPool, KernCommsBuf::Fault(EPondNotReady));
       
   220 	if(!(iInitialised && iDefaultAllocPool))
       
   221 		{
       
   222 		return KErrNoMemory;
       
   223 		}
       
   224 	
       
   225 	// Allocate a shbuf from our default pool
       
   226 	TShBuf* shbuf;
       
   227 	NKern::ThreadEnterCS();
       
   228 	TInt result = Kern::ShPoolAlloc(iDefaultAllocPool->iShPool, shbuf, 0);
       
   229 	NKern::ThreadLeaveCS();
       
   230 	if(result != KErrNone)
       
   231 		{
       
   232 		return KErrNoMemory;
       
   233 		}
       
   234 
       
   235 	// Construct the requisite comms metadata in place
       
   236 	new((TUint8*)(&aBuf)) DCommsBuf(shbuf, iDefaultAllocPool->iCommsOpaqueHandle, iDefaultAllocPool->iCommsBufSize);
       
   237 	return KErrNone;
       
   238 	}
       
   239 
       
   240 /**
       
   241 @purpose Frees a buffer back to the pond
       
   242 @param aBuf Commsbuf to be freed
       
   243 */
       
   244 EXPORT_C void DCommsPond::Free(DCommsBuf& aBuf)
       
   245 	{
       
   246 	aBuf.Free();
       
   247 	}
       
   248 
       
   249 /**
       
   250 @purpose Loads the pond from a flattened version of it received from comms
       
   251 @param aPondStore Descriptor containing flattened pond structure
       
   252 @param aClient The user side thread whose context the pools are already open in
       
   253 */
       
   254 EXPORT_C TInt DCommsPond::Load(TPondTransferBuf& aPondStore, DThread* aClient)
       
   255 	{
       
   256 	__ASSERT_DEBUG(!iInitialised, KernCommsBuf::PanicClient(aClient, EPondAlreadyLoaded));
       
   257 	if(iInitialised)
       
   258 		{
       
   259 		return KErrGeneral;
       
   260 		}
       
   261 
       
   262 	// Read the pond in from the client
       
   263 	TPtr8 pondPtr((TUint8*)iPond, sizeof(*iPond));
       
   264 	TInt r = Kern::ThreadDesRead(aClient, (TAny*)(&aPondStore), pondPtr, 0);
       
   265 	if(r != KErrNone)
       
   266 		{
       
   267 		KernCommsBuf::PanicClient(aClient, EPondCorrupt);
       
   268 		return r;
       
   269 		}
       
   270 
       
   271 	// Sanity check of the pond length
       
   272 	if((iPond->NumPools() <= 0) || (iPond->NumPools() > TCommsPond::KMaxPoolsPerPond))
       
   273 		{
       
   274 #ifdef _DEBUG
       
   275 		KernCommsBuf::PanicClient(aClient, EPondCorrupt);
       
   276 #endif
       
   277 		return KErrCorrupt;
       
   278 		}
       
   279 	
       
   280 	// Open each of the pools - if any can't be opened for any reason then close any opened so far and out of here
       
   281 	// todo_cdg need complete validation of the pool records
       
   282 	for(TInt i = 0; i < iPond->NumPools(); i++)
       
   283 		{
       
   284 		TPoolRecord* poolRecord = &(iPond->iPoolRecords[i]);
       
   285 
       
   286 		NKern::ThreadEnterCS();
       
   287 		TShPool* pool;
       
   288 		TInt result = Kern::ShPoolOpen(pool, aClient, poolRecord->iCommsShPoolHandle, ETrue, 0);
       
   289 		NKern::ThreadLeaveCS();
       
   290 		if(result == KErrNone)
       
   291 			{
       
   292 			iPond->iPoolRecords[i].iShPool = pool;
       
   293 			}
       
   294 		else
       
   295 			{
       
   296 			// Close all pools opened so far and out of here
       
   297 			for(TInt j = 0; j < i; j++)
       
   298 				{
       
   299 				TShPool* poolToClose = iPond->iPoolRecords[j].iShPool;
       
   300 				Kern::ShPoolClose(poolToClose);
       
   301 				}
       
   302 			return result;
       
   303 			}
       
   304 		}
       
   305 
       
   306 	iInitialised = ETrue;
       
   307 	return KErrNone;
       
   308 	}
       
   309 
       
   310 /**
       
   311 @purpose Unloads the pond. This involves closing all pools and setting the pond to an uninitialised state.
       
   312 */
       
   313 EXPORT_C void DCommsPond::Unload()
       
   314 	{
       
   315 	// Close each of the pools and set uninitialised
       
   316 	TInt numPools = iPond->NumPools();
       
   317 	for(TInt i = 0; i < numPools; i++)
       
   318 		{
       
   319 		TShPool* poolToClose = iPond->iPoolRecords[i].iShPool;
       
   320 		TInt result = Kern::ShPoolClose(poolToClose);
       
   321 		}
       
   322 
       
   323 	iDefaultAllocPool = NULL;
       
   324 	iInitialised = EFalse;
       
   325 	}
       
   326 
       
   327 /**
       
   328 @purpose Identifies the pool with the smallest buffers just large enough to accomodate a payload of the size given.
       
   329 The pool selected is then the pool from which default allocations will be performed.
       
   330 @param aRequiredSize Smallest buffer size acceptable for default allocations from the pond.
       
   331 */
       
   332 EXPORT_C TInt DCommsPond::SetDefaultAllocPool(TInt aRequiredSize)
       
   333 	{
       
   334 	__ASSERT_DEBUG(iInitialised, KernCommsBuf::Fault(EPondNotReady));
       
   335 	__ASSERT_ALWAYS(aRequiredSize >= 0, KernCommsBuf::Fault(EPondNotReady));
       
   336 	if(!iInitialised)
       
   337 		{
       
   338 		return KErrNotReady;
       
   339 		}
       
   340 
       
   341 	// Walk our complete list of pools (don't assume they are in any specific order)
       
   342 	// Find the pool for which the given size will just fit
       
   343 	TPoolRecord* bestFitPool(NULL);
       
   344 	TInt bestFit(KMaxTInt32);
       
   345 	for(TInt i = 0; i < iPond->NumPools(); i++)
       
   346 		{
       
   347 		TPoolRecord& currentPool = iPond->iPoolRecords[i];
       
   348 		TInt currentBufSize = currentPool.iCommsBufSize;
       
   349 		if(currentBufSize >= aRequiredSize)
       
   350 			{
       
   351 			if(currentBufSize < bestFit)
       
   352 				{
       
   353 				bestFit = currentBufSize;
       
   354 				bestFitPool = &currentPool;
       
   355 				}
       
   356 			}
       
   357 		}
       
   358 
       
   359 	// Set the best fit pool as the default
       
   360 	if(bestFit != KMaxTInt32)
       
   361 		{
       
   362 		iDefaultAllocPool = bestFitPool;
       
   363 		return KErrNone;
       
   364 		}
       
   365 
       
   366 	return KErrNotFound;
       
   367 	}
       
   368 
       
   369 EXPORT_C DCommsPond::~DCommsPond()
       
   370 	{
       
   371 	delete iPond;
       
   372 	}
       
   373 
       
   374 EXPORT_C void KernCommsBuf::PanicClient(DThread* aClient, TInt aReason)
       
   375 	{
       
   376 	_LIT(KZCPan, "kernel commsbufs");
       
   377 	Kern::ThreadKill(aClient, EExitPanic, aReason, KZCPan);
       
   378 	}
       
   379 
       
   380 EXPORT_C void KernCommsBuf::Fault(TInt aReason)
       
   381 	{
       
   382 	Kern::Fault("kernel commsbufs fault", aReason);
       
   383 	}
       
   384