diff -r 000000000000 -r dfb7c4ff071f commsfwutils/commsbufs/src/commsbufskern.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/commsfwutils/commsbufs/src/commsbufskern.cpp Thu Dec 17 09:22:25 2009 +0200 @@ -0,0 +1,384 @@ +// Copyright (c) 1999-2009 Nokia Corporation and/or its subsidiary(-ies). +// All rights reserved. +// This component and the accompanying materials are made available +// under the terms of "Eclipse Public License v1.0" +// which accompanies this distribution, and is available +// at the URL "http://www.eclipse.org/legal/epl-v10.html". +// +// Initial Contributors: +// Nokia Corporation - initial contribution. +// +// Contributors: +// +// Description: +// + +#include +#include "commsbufpond_internal.h" +#include + +DECLARE_STANDARD_LDD() + { + return NULL; + } + + +/** +@purpose Fetch a user side handle to underlying shared buffer for a given client and populate commsbuf +metadata ready for transfer to the client. Closes kernel reference to buffer. +*/ +EXPORT_C TInt DCommsBuf::TransferToClient(DThread* aClient) + { + // Construct the DCommsBuf metadata in the prescribed spot in the buffer + DCommsBuf* metadataPtr = (DCommsBuf*)iCommsBufPtr; + *metadataPtr = *this; + + // Initialise the fields that were being used for other purposes kernel side + metadataPtr->iShBuf = NULL; + metadataPtr->iCommsBufPtr = NULL; + + // Fetch a handle for the given client we will hand it to and also put the handle in the metadata too + NKern::ThreadEnterCS(); + TInt result = Kern::ShBufMakeHandleAndOpen(iShBuf, aClient); + NKern::ThreadLeaveCS(); + + // Doesn't matter if result is an error code for failed MakeHandle as we close it now anyway + metadataPtr->iHandle = result; + + // Close _our_ handle to the buffer + Kern::ShBufClose(iShBuf); + + // We return the handle so the the caller can pass it to the user side client independently + return result; + } + +/** +Sanitise a buffer arriving from user side and open a reference to it. This involves sanitising the metadata +and copying out a validated version to a kernel private location (aSanitised). That is, check that the fields +that will be accessed kernel side can not cause illegal operations. +@param aClient Client thread that the buffer is being received from. +@param aHandle User side handle to the buffer. Although opaque this is in effect a shbuf handle. +aSanitised Pointer to kernel private memory to take the validated DCommsBuf +@return KErrCorrupt if metadata is unsafe +*/ +EXPORT_C TInt DCommsBuf::AcceptFromClient(DThread* aClient, TInt aHandle, DCommsBuf* aSanitised) + { + // Open the underlying shbuf buffer + TShBuf* shbuf; + NKern::ThreadEnterCS(); + TInt result = Kern::ShBufOpen(shbuf, aClient, aHandle); + NKern::ThreadLeaveCS(); + if(result != KErrNone) + { + // Close the user handle + NKern::ThreadEnterCS(); + Kern::CloseHandle(aClient, aHandle); + NKern::ThreadLeaveCS(); + return result; + } + + TUint8* shBufPtr = Kern::ShBufPtr(shbuf); + TInt shBufSize = Kern::ShBufSize(shbuf); + + // Copy out first (after all it could be corrupted between checking and copying) + // The first 8 words are the only ones of interest to us + TUint8* inPlaceMetadata = (shBufPtr + shBufSize - KCommsBufMetadataSize); + typedef struct + { + TInt iCompulsoryFields[8]; + } TCompulsory; + *(TCompulsory*)aSanitised = *(TCompulsory*)inPlaceMetadata; + + // Set kernel relevant fields + aSanitised->iShBuf = shbuf; + aSanitised->iCommsBufPtr = inPlaceMetadata; + + // Now verify the metadata that matters to us + TBool valid = EFalse; +#ifndef _DEBUG + // Speedy UREL checks + if(((inPlaceMetadata + aSanitised->iOffset) >= shBufPtr) + && (aSanitised->iOffset <= 0) + && ((aSanitised->iOffset + aSanitised->iLength) <= 0) + && (aSanitised->iLength >= 0)) + { + // Don't bother checking these ones as we can cheaply write them from know values + aSanitised->iRawSize = (shBufSize - KCommsBufMetadataSize); + aSanitised->iRawDataOffset = (shBufPtr - inPlaceMetadata); + valid = ETrue; + } +#else + // More prosaic UDEB checks + if((aSanitised->iFrontCanary == KCanaryDefault) + && ((inPlaceMetadata + aSanitised->iOffset) >= shBufPtr) + && (aSanitised->iOffset <= 0) + && ((aSanitised->iOffset + aSanitised->iLength) <= 0) + && (aSanitised->iLength >= 0) + && (aSanitised->iRawSize == (shBufSize - KCommsBufMetadataSize)) + && (aSanitised->iRawDataOffset == (shBufPtr - inPlaceMetadata))) + { + valid = ETrue; + } +#endif + + __ASSERT_DEBUG(valid, KernCommsBuf::Fault(ECommsBufMetadataCorrupt)); + if(!valid) + { + // Close the user handle and our kernel reference + NKern::ThreadEnterCS(); + Kern::CloseHandle(aClient, aHandle); + NKern::ThreadLeaveCS(); + Kern::ShBufClose(shbuf); + return KErrCorrupt; + } + + // Can now close the user side handle + NKern::ThreadEnterCS(); + Kern::CloseHandle(aClient, aHandle); + NKern::ThreadLeaveCS(); + + return KErrNone; + } + +/** +Closes reference to commsbuf +*/ +EXPORT_C void DCommsBuf::Free() + { + // Close _our_ handle to the buffer + Kern::ShBufClose(iShBuf); + } + +/** +Constructs DCommsbuf based upon underlying shared buffer. +Should be constructed in kernel private memory not in place in the buffer. +Offset defaults to zero and length to the maximum length +@param aShBuf Underlying shared buffer +@param aCommsPoolHandle Opaque user side handle (as discovered through driver interface) +*/ +DCommsBuf::DCommsBuf(TShBuf* aShBuf, TInt aCommsPoolHandle, TInt aBufSize) + { + __ASSERT_COMPILE(sizeof(TCommsBuf) == TCommsBuf::KCommsBufMetadataSize); + __ASSERT_COMPILE(sizeof(DCommsBuf) == sizeof(TCommsBuf)); + + __ASSERT_DEBUG(aCommsPoolHandle, KernCommsBuf::Fault(ECommsBufMetadataCorrupt)); + + iFrontCanary = KCanaryDefault; + TUint8* shBufPtr = Kern::ShBufPtr(aShBuf); + iShBuf = aShBuf; + iCommsBufPtr = shBufPtr + aBufSize; + + iRawDataOffset = -aBufSize; + iOffset = iRawDataOffset; + iRawSize = aBufSize; + iLength = aBufSize; + iNext = NULL; + iPool = aCommsPoolHandle; + iHandle = 0; + + iReservedWords[0] = 0; + iReservedWords[1] = 0; + iReservedWords[2] = 0; + iReservedWords[3] = 0; + iReservedWords[4] = 0; + iBackCanary = KCanaryDefault; + } + +/** +@purpose Static constructor for DCommsPond +*/ +EXPORT_C DCommsPond* DCommsPond::New() + { + DCommsPond* self = new DCommsPond(); + if(!self) + { + return NULL; + } + + self->iPond = new TCommsPond(); + if(!(self->iPond)) + { + delete self; + return NULL; + } + else + { + return self; + } + } + +/** +Allocates a commsbuf from the kernel pond. Allocation is from the default shared buffer pool. +The kernel side commsbuf metadata (DCommsBuf) is constructed in-place within the buffer. +This metadata defaults to zero length and zero offset. The user side opaque handle is also set accordingly. +@return KErrNoMemory if no buffers available +*/ +EXPORT_C TInt DCommsPond::Alloc(DCommsBuf& aBuf) + { + // Pond should have been initialised from comms already + __ASSERT_DEBUG(iInitialised && iDefaultAllocPool, KernCommsBuf::Fault(EPondNotReady)); + if(!(iInitialised && iDefaultAllocPool)) + { + return KErrNoMemory; + } + + // Allocate a shbuf from our default pool + TShBuf* shbuf; + NKern::ThreadEnterCS(); + TInt result = Kern::ShPoolAlloc(iDefaultAllocPool->iShPool, shbuf, 0); + NKern::ThreadLeaveCS(); + if(result != KErrNone) + { + return KErrNoMemory; + } + + // Construct the requisite comms metadata in place + new((TUint8*)(&aBuf)) DCommsBuf(shbuf, iDefaultAllocPool->iCommsOpaqueHandle, iDefaultAllocPool->iCommsBufSize); + return KErrNone; + } + +/** +@purpose Frees a buffer back to the pond +@param aBuf Commsbuf to be freed +*/ +EXPORT_C void DCommsPond::Free(DCommsBuf& aBuf) + { + aBuf.Free(); + } + +/** +@purpose Loads the pond from a flattened version of it received from comms +@param aPondStore Descriptor containing flattened pond structure +@param aClient The user side thread whose context the pools are already open in +*/ +EXPORT_C TInt DCommsPond::Load(TPondTransferBuf& aPondStore, DThread* aClient) + { + __ASSERT_DEBUG(!iInitialised, KernCommsBuf::PanicClient(aClient, EPondAlreadyLoaded)); + if(iInitialised) + { + return KErrGeneral; + } + + // Read the pond in from the client + TPtr8 pondPtr((TUint8*)iPond, sizeof(*iPond)); + TInt r = Kern::ThreadDesRead(aClient, (TAny*)(&aPondStore), pondPtr, 0); + if(r != KErrNone) + { + KernCommsBuf::PanicClient(aClient, EPondCorrupt); + return r; + } + + // Sanity check of the pond length + if((iPond->NumPools() <= 0) || (iPond->NumPools() > TCommsPond::KMaxPoolsPerPond)) + { +#ifdef _DEBUG + KernCommsBuf::PanicClient(aClient, EPondCorrupt); +#endif + return KErrCorrupt; + } + + // Open each of the pools - if any can't be opened for any reason then close any opened so far and out of here + // todo_cdg need complete validation of the pool records + for(TInt i = 0; i < iPond->NumPools(); i++) + { + TPoolRecord* poolRecord = &(iPond->iPoolRecords[i]); + + NKern::ThreadEnterCS(); + TShPool* pool; + TInt result = Kern::ShPoolOpen(pool, aClient, poolRecord->iCommsShPoolHandle, ETrue, 0); + NKern::ThreadLeaveCS(); + if(result == KErrNone) + { + iPond->iPoolRecords[i].iShPool = pool; + } + else + { + // Close all pools opened so far and out of here + for(TInt j = 0; j < i; j++) + { + TShPool* poolToClose = iPond->iPoolRecords[j].iShPool; + Kern::ShPoolClose(poolToClose); + } + return result; + } + } + + iInitialised = ETrue; + return KErrNone; + } + +/** +@purpose Unloads the pond. This involves closing all pools and setting the pond to an uninitialised state. +*/ +EXPORT_C void DCommsPond::Unload() + { + // Close each of the pools and set uninitialised + TInt numPools = iPond->NumPools(); + for(TInt i = 0; i < numPools; i++) + { + TShPool* poolToClose = iPond->iPoolRecords[i].iShPool; + TInt result = Kern::ShPoolClose(poolToClose); + } + + iDefaultAllocPool = NULL; + iInitialised = EFalse; + } + +/** +@purpose Identifies the pool with the smallest buffers just large enough to accomodate a payload of the size given. +The pool selected is then the pool from which default allocations will be performed. +@param aRequiredSize Smallest buffer size acceptable for default allocations from the pond. +*/ +EXPORT_C TInt DCommsPond::SetDefaultAllocPool(TInt aRequiredSize) + { + __ASSERT_DEBUG(iInitialised, KernCommsBuf::Fault(EPondNotReady)); + __ASSERT_ALWAYS(aRequiredSize >= 0, KernCommsBuf::Fault(EPondNotReady)); + if(!iInitialised) + { + return KErrNotReady; + } + + // Walk our complete list of pools (don't assume they are in any specific order) + // Find the pool for which the given size will just fit + TPoolRecord* bestFitPool(NULL); + TInt bestFit(KMaxTInt32); + for(TInt i = 0; i < iPond->NumPools(); i++) + { + TPoolRecord& currentPool = iPond->iPoolRecords[i]; + TInt currentBufSize = currentPool.iCommsBufSize; + if(currentBufSize >= aRequiredSize) + { + if(currentBufSize < bestFit) + { + bestFit = currentBufSize; + bestFitPool = ¤tPool; + } + } + } + + // Set the best fit pool as the default + if(bestFit != KMaxTInt32) + { + iDefaultAllocPool = bestFitPool; + return KErrNone; + } + + return KErrNotFound; + } + +EXPORT_C DCommsPond::~DCommsPond() + { + delete iPond; + } + +EXPORT_C void KernCommsBuf::PanicClient(DThread* aClient, TInt aReason) + { + _LIT(KZCPan, "kernel commsbufs"); + Kern::ThreadKill(aClient, EExitPanic, aReason, KZCPan); + } + +EXPORT_C void KernCommsBuf::Fault(TInt aReason) + { + Kern::Fault("kernel commsbufs fault", aReason); + } +