Files
PluralTechnology/service/ptos/ex/pool.c
T
2026-07-13 23:40:24 +00:00

266 lines
5.3 KiB
C

/*
* Copyright (c) 2026, Chloe M.
* Provided under the BSD-3 clause.
*
* Description: Pool allocator
* Author: Chloe M.
*/
#include <ex/pool.h>
#include <ke/bugcheck.h>
#include <hal/kpcr.h>
#include <mm/vm.h>
#include <ptapi/status.h>
#include <string.h>
/*
* Compute the segment level index from the number of
* bytes requested to be allocated. We use the following
* formula to do so:
*
* ((log2(count) - POOL_MIN_LOG2) - 1) % POOL_LEVEL_MAX
*/
static UCHAR
PoolComputeLevelIndex(USIZE ByteCount)
{
USIZE TmpCount;
USIZE Log2Sum = 0;
/* Compute the base 2 logarithm */
TmpCount = ByteCount;
while (TmpCount != 0) {
TmpCount >>= 1;
++Log2Sum;
}
/* Subtract the right side */
TmpCount -= POOL_MIN_LOG2 + 1;
TmpCount %= POOL_LEVEL_MAX;
return TmpCount;
}
/*
* Obtain a reference to the current pool region
*/
static POOL_REGION *
PoolGetCurrentRegion(VOID)
{
KPCR *Kpcr;
Kpcr = HalKpcrCurrent();
return &Kpcr->PoolRegion;
}
/*
* Allocate a page descriptor
*
* @GranBytes: Granularity in bytes of level
*/
static POOL_PAGE *
PoolAllocatePage(USIZE GranBytes)
{
PT_STATUS Status;
POOL_PAGE *PageDesc;
MM_RANGE Range;
ULONG PageSizeBitCount;
ULONG BitmapReserveMask;
Status = MmAllocatePages(1, &Range);
if (Status != STATUS_SUCCESS) {
return NULL;
}
PageDesc = RANGE_PTR(&Range);
PageSizeBitCount = sizeof(*PageDesc) * 8;
PageSizeBitCount /= GranBytes;
BitmapReserveMask = (1 << PageSizeBitCount) - 1;
PageDesc->Bitmap |= BitmapReserveMask;
return PageDesc;
}
/*
* Initialize a pool segment
*
* @Segment: Pool segment to initialize
* @Level: Segment level
*/
static PT_STATUS
PoolSegmentInit(POOL_SEGMENT *Segment, UCHAR Level)
{
USIZE Gran;
if (Segment == NULL) {
return STATUS_INVALID_PARAM;
}
Gran = POOL_LEVEL_GRAN(Level);
Segment->PageCount = 1;
Segment->FirstPage = PoolAllocatePage(Gran);
return STATUS_SUCCESS;
}
/*
* Allocate memory from a page descriptor
*
* @Gran: Granularity in bytes of level
* @Page: Page to allocate from
* @ByteCount: Number of bytes to allocate
*/
static VOID *
PoolAllocateFromPage(USIZE Gran, POOL_PAGE *Page, USIZE ByteCount)
{
USIZE ChunksNeeded, ChunksFound;
USIZE SetIdx, Idx;
SSIZE IdxBase = -1;
if (Page == NULL) {
return NULL;
}
ChunksNeeded = ByteCount / Gran;
/* Scan the page for free chunks */
for (Idx = 0; Idx < sizeof(Page->Bitmap) * 8; ++Idx) {
if (TESTBIT(&Page->Bitmap, Idx)) {
ChunksFound = 0;
IdxBase = -1;
continue;
}
if (IdxBase < 0) {
IdxBase = Idx;
}
++ChunksFound;
if (ChunksFound >= ChunksNeeded) {
break;
}
}
if (ChunksFound < ChunksNeeded) {
return NULL;
}
/* Mark all as allocated */
for (SetIdx = IdxBase; SetIdx < Idx + 1; ++SetIdx) {
SETBIT(&Page->Bitmap, SetIdx);
}
return PTR_OFFSET(Page, IdxBase * Gran);
}
/*
* Allocate memory from a pool segment
*
* @ByteCount: Number of bytes to allocate
* @LevelIndex: Segment level index
* @Segment: Segment to allocate from
*/
static VOID *
PoolAllocateFromSegment(USIZE ByteCount, UCHAR LevelIndex, POOL_SEGMENT *Segment)
{
USIZE ChunkCount, Idx;
ULONG Gran;
POOL_PAGE *Page;
VOID *Ptr;
if (ByteCount == 0 || Segment == NULL) {
return NULL;
}
Gran = POOL_LEVEL_GRAN(LevelIndex);
Page = Segment->FirstPage;
if (Page == NULL) {
return NULL;
}
while (Page != NULL) {
Ptr = PoolAllocateFromPage(Gran, Page, ByteCount);
if (Ptr != NULL) {
return Ptr;
}
if (Page->Next == NULL) {
Page->Next = PoolAllocatePage(Gran);
if (Page->Next == NULL)
return NULL;
}
Page = Page->Next;
}
return NULL;
}
/*
* TODO: We need per-region locking
*/
VOID *
ExAllocatePoolWithTag(POOL_TYPE Type, USIZE ByteCount, ULONG Tag)
{
UCHAR SegmentIndex;
POOL_REGION *Region;
POOL_SEGMENT *Segment;
VOID *Base;
if (ByteCount == 0) {
return NULL;
}
ByteCount = ALIGN_UP(ByteCount, POOL_LEVEL_GRAN(0));
/* Verify that the pool type is valid */
switch (Type) {
case POOL_NON_PAGED:
break;
default:
return NULL;
}
Region = PoolGetCurrentRegion();
SegmentIndex = PoolComputeLevelIndex(ByteCount);
Segment = &Region->Level[SegmentIndex];
Base = PoolAllocateFromSegment(
ByteCount,
SegmentIndex,
Segment
);
if (Base == NULL) {
return NULL;
}
RtlMemSet(Base, 0, ByteCount);
return Base;
}
VOID
ExPoolRegionInit(POOL_REGION *Region)
{
UCHAR Level;
POOL_SEGMENT *Segment;
PT_STATUS Status;
if (Region == NULL) {
KeBugCheck(
BUGCHECK_UNBOUND_RESRC,
"unbound region in ExPoolRegionInit()\n"
);
}
/* Initialize each segment level */
for (Level = 0; Level < POOL_LEVEL_MAX; ++Level) {
Segment = &Region->Level[Level];
Status = PoolSegmentInit(Segment, Level);
if (PT_ERROR(Status)) {
KeBugCheck(
BUGCHECK_MISC,
"failed to initialize segment level %d\n",
Level
);
}
}
}