--- /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 <comms-infras/commsbufskern.h>
+#include "commsbufpond_internal.h"
+#include <kernel/kernel.h>
+
+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);
+ }
+