/*********************************************************************** Copyright 2002 Ben Rudiak-Gould. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA, or visit . ***********************************************************************/ #include "SharedPool.h" #include "miniport.h" #include /*******************************************************************\ \*******************************************************************/ #define max(a,b) ((a)<(b)?(b):(a)) /*******************************************************************\ \*******************************************************************/ // values for page (PageReserve) #define PR_PRIVATE 0x80000400 #define PR_SHARED 0x80060000 #define PR_SYSTEM 0x80080000 // values for flags (PageReserve) #define PR_FIXED 0x00000008 #define PR_4MEG 0x00000001 #define PR_STATIC 0x00000010 // values for hpd (PageCommit) #define PD_ZEROINIT 1 #define PD_NOINIT 2 #define PD_FIXEDZERO 3 #define PD_FIXED 4 // values for flags (PageCommit) #define PC_FIXED 0x00000008 #define PC_LOCKED 0x00000080 #define PC_LOCKEDIFDP 0x00000100 #define PC_WRITEABLE 0x00020000 #define PC_USER 0x00040000 #define PC_INCR 0x40000000 #define PC_PRESENT 0x80000000 #define PC_STATIC 0x20000000 #define PC_DIRTY 0x08000000 #define PC_CACHEDIS 0x00100000 #define PC_CACHEWT 0x00080000 #define PC_PAGEFLUSH 0x00008000 void* _PageReserve(unsigned page, unsigned npages, unsigned flags) { return Miniport::DriverCall(miniport_handle, (char*)mpdfunc__PageReserve, "iii", page, npages, flags); } unsigned _PageCommit(unsigned page, unsigned npages, unsigned hpd, unsigned pagerdata, unsigned flags) { return (unsigned)Miniport::DriverCall(miniport_handle, (char*)mpdfunc__PageCommit, "iiiii", page, npages, hpd, pagerdata, flags); } unsigned _PageFree(void* p, unsigned flags=0) { return (unsigned)Miniport::DriverCall(miniport_handle, (char*)mpdfunc__PageFree, "ii", p, flags); } inline bool PageCommit(void* p, unsigned npages, int locked) { unsigned page = unsigned(p) >> 12; return !!_PageCommit(page, npages, locked ? PD_FIXEDZERO : PD_ZEROINIT, 0, locked ? PC_USER+PC_WRITEABLE+PC_FIXED : PC_USER+PC_WRITEABLE); } /*******************************************************************\ \*******************************************************************/ class SharedPool { char* latest_block_next_free_byte; char* latest_block_end; unsigned latest_block_index; int locked; char* blocks[18]; public: static void* Create(int _locked); void* Allocate(unsigned len); void Clear(); void Delete(); }; inline void* SharedPool::Create(int _locked) { char* block = (char*)_PageReserve(PR_SHARED, 1, _locked ? PR_FIXED : 0); if (!block) { return 0; } if (!PageCommit(block, 1, _locked)) { _PageFree(block); return 0; } SharedPool* self = (SharedPool*)block; self->latest_block_next_free_byte = block + sizeof(SharedPool); self->latest_block_end = block + 4096; self->locked = _locked; self->blocks[0] = block; return self; } inline void* SharedPool::Allocate(unsigned len) { len = (len+3) & -4; char* alloc_end = latest_block_next_free_byte + len; if (alloc_end > latest_block_end) { // Current block isn't big enough; allocate a new one. The size of // block N is at least 4K * 2^N, to ensure that we'll run out of // virtual address space before we run out of array slots. unsigned pages_to_alloc = max((len + 4095U) >> 12, 2U << latest_block_index); char* new_block = (char*)_PageReserve(PR_SHARED, pages_to_alloc, locked ? PR_FIXED : 0); if (new_block == 0) { return 0; } latest_block_next_free_byte = new_block; latest_block_end = new_block + pages_to_alloc*4096; ++latest_block_index; blocks[latest_block_index] = new_block; alloc_end = new_block + len; } char* first_uncommitted_byte = (char*)(((unsigned)latest_block_next_free_byte + 4095) & -4096); if (alloc_end > first_uncommitted_byte) { unsigned pages_to_commit = (alloc_end - first_uncommitted_byte + 4095) >> 12; if (!PageCommit(first_uncommitted_byte, pages_to_commit, locked)) { return 0; } } void* rtn = latest_block_next_free_byte; latest_block_next_free_byte = alloc_end; return rtn; } inline void SharedPool::Clear() { for (unsigned i=1; i<=latest_block_index; ++i) { _PageFree(blocks[i]); } latest_block_next_free_byte = blocks[0] + sizeof(SharedPool); latest_block_end = blocks[0] + 4096; latest_block_index = 0; } inline void SharedPool::Delete() { for (unsigned i=1; i<=latest_block_index; ++i) { _PageFree(blocks[i]); } _PageFree(blocks[0]); } void* SharedPool_Create(int locked) { return SharedPool::Create(locked); } void* SharedPool_Alloc(void* self, unsigned len) { return ((SharedPool*)self)->Allocate(len); } void SharedPool_Clear(void* self) { ((SharedPool*)self)->Clear(); } void SharedPool_Delete(void* self) { ((SharedPool*)self)->Delete(); }