📄 physmem.c
字号:
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// This source code is licensed under Microsoft Shared Source License
// Version 1.0 for Windows CE.
// For a copy of the license visit http://go.microsoft.com/fwlink/?LinkId=3223.
//
#include "kernel.h"
//#define PHYSPAGESTOLEDS 1
#ifdef PHYSPAGESTOLEDS
#define LogPhysicalPages(PageFreeCount) OEMWriteDebugLED(0,PageFreeCount);
#else
#define LogPhysicalPages(PageFreeCount) (0)
#endif
#define PAGEOUT_LOW (( 68*1024)/PAGE_SIZE) // do pageout once below this mark, reset when above high
#define PAGEOUT_HIGH ((132*1024)/PAGE_SIZE)
#define PAGE_OUT_TRIGGER (( 24*1024)/PAGE_SIZE) // page out if below highwater and this much paged in recently
void ZeroPage(void *pvPage);
BOOL ScavengeStacks(int cNeed);
HANDLE GwesOOMEvent;
long GwesLowThreshold;
long GwesCriticalThreshold;
long GwesLowBlockSize;
long GwesCriticalBlockSize;
LPBYTE pFreeKStacks;
LPBYTE pDirtyList;
// the maximum (lowest) priority to keep a thread's stack from being scavanged
// any thread of this priority or higher will never have its stack scvanaged.
// stack scavanging will never occur if set to 255
DWORD dwNKMaxPrioNoScav = 247; // default to lowest RT priority
extern void FreeSpareBytes(LPBYTE lpb, uint size);
extern CRITICAL_SECTION PhysCS;
extern CRITICAL_SECTION DirtyPageCS;
extern PTHREAD pCleanupThread;
extern DWORD dwOEMCleanPages;
// PageOutNeeded is set to 1 when the number of free pages drops below
// PageOutTrigger and the cleanup thread's event is signalled. When the
// cleanup thread starts performing page out, it sets the variable to 2.
// When the page free count rises above PageOutLevel, PageOutNeeded is
// reset to 0.
//
// The free page count thresholds are set based upon the Gwes OOM level.
long PageOutNeeded;
long PageOutTrigger; // Threshold level to start page out.
long PageOutLevel; // Threshold to stop page out.
static DWORD PFNReserveStart; // starting PFN of the reserved memory
static DWORD PFNReserveEnd; // ending PFN of the reserved memory
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
PFREEINFO GetRegionFromAddr(PHYSICAL_ADDRESS addr)
{
// don't free the reserved area
if ((addr >= PFNReserveEnd) || (addr < PFNReserveStart)) {
PFREEINFO pfi, pfiEnd;
pfi = MemoryInfo.pFi;
pfiEnd = pfi + MemoryInfo.cFi;
for ( ; pfi < pfiEnd ; pfi++) {
if (addr >= pfi->paStart && addr < pfi->paEnd)
return pfi;
}
}
return NULL;
}
void SetOOMEvt (void)
{
ACCESSKEY oldKey;
SWITCHKEY (oldKey, 0xffffffff);
SetEvent(GwesOOMEvent);
SETCURKEY (oldKey);
}
//------------------------------------------------------------------------------
// GWE calls this function to register an Out Of Memory event, which
// HoldPages sets when free physical memory drops below the given threshold.
//------------------------------------------------------------------------------
VOID
SC_SetGwesOOMEvent(
HANDLE hEvent,
DWORD cpLow,
DWORD cpCritical,
DWORD cpLowBlockSize,
DWORD cpCriticalBlockSize
)
{
TRUSTED_API_VOID (L"SC_SetGwesOOMEvent");
DEBUGMSG(ZONE_ENTRY,(L"SC_SetGwesOOMEvent entry: %8.8lx %8.8lx %8.8lx %8.8lx %8.8lx\r\n",
hEvent,cpLow,cpCritical,cpLowBlockSize,cpCriticalBlockSize));
GwesOOMEvent = hEvent;
GwesLowThreshold = cpLow;
GwesCriticalThreshold = cpCritical;
GwesLowBlockSize = cpLowBlockSize;
GwesCriticalBlockSize = cpCriticalBlockSize;
PageOutLevel = GwesLowThreshold + PAGEOUT_HIGH;
PageOutTrigger = GwesLowThreshold + PAGEOUT_LOW;
DEBUGMSG(ZONE_ENTRY,(L"SC_SetGwesOOMEvent exit\r\n"));
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void
UnlinkPhysPage(
LPBYTE pMem
)
{
KCALLPROFON(38);
if (*(DWORD *)((DWORD)pMem + 0x20000000))
*(LPBYTE *)(*(DWORD *)((DWORD)pMem + 0x20000000) + 0x20000004) =
*(LPBYTE *)((DWORD)pMem + 0x20000004);
if (*(DWORD *)((DWORD)pMem + 0x20000004))
*(LPBYTE *)(*(DWORD *)((DWORD)pMem + 0x20000004) + 0x20000000) =
*(LPBYTE *)((DWORD)pMem + 0x20000000);
else
LogPtr->pKList = *(LPBYTE *)((DWORD)pMem + 0x20000000);
*(LPDWORD)(pMem + 0x20000000) = 0;
*(LPDWORD)(pMem + 0x20000004) = 0;
KCALLPROFOFF(38);
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void
LinkPhysPageOnly(
LPBYTE pMem
)
{
KCALLPROFON(32);
*(LPBYTE *)((DWORD)pMem + 0x20000000) = LogPtr->pKList;
*(LPBYTE *)((DWORD)pMem + 0x20000004) = 0;
if (LogPtr->pKList)
*(LPBYTE *)((DWORD)LogPtr->pKList + 0x20000004) = pMem;
LogPtr->pKList = pMem;
KCALLPROFOFF(32);
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void
LinkPhysPage(
LPBYTE pMem
)
{
KCALLPROFON(32);
*(LPBYTE *)((DWORD)pMem + 0x20000000) = LogPtr->pKList;
*(LPBYTE *)((DWORD)pMem + 0x20000004) = 0;
if (LogPtr->pKList)
*(LPBYTE *)((DWORD)LogPtr->pKList + 0x20000004) = pMem;
LogPtr->pKList = pMem;
PageFreeCount++;
LogPhysicalPages(PageFreeCount);
KCALLPROFOFF(32);
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void
LinkPhysPageList(
LPBYTE pListStart,
LPBYTE pListEnd,
DWORD cPages
)
{
PFREEINFO pfi, pfiEnd;
KCALLPROFON(32);
*(LPBYTE *)((DWORD)pListEnd + 0x20000000) = LogPtr->pKList;
if (LogPtr->pKList)
*(LPBYTE *)((DWORD)LogPtr->pKList + 0x20000004) = pListEnd;
LogPtr->pKList = pListStart;
PageFreeCount += cPages;
LogPhysicalPages(PageFreeCount);
// restore end address of each memory section, loop less than MAX_MEMORY_SECTIONS times
for (pfi = &MemoryInfo.pFi[0], pfiEnd = pfi+MemoryInfo.cFi ; pfi < pfiEnd ; ++pfi) {
pfi->paEnd = pfi->paRealEnd;
}
KCALLPROFOFF(32);
}
//------------------------------------------------------------------------------
//
// GrabFirstPhysPage gets the next available physical page from the kernel's
// linked list of free pages (LogPtr->pKList).
//
//------------------------------------------------------------------------------
LPBYTE
GrabFirstPhysPage(
DWORD dwCount
)
{
PFREEINFO pfi;
uint ix;
PHYSICAL_ADDRESS paRet;
LPBYTE pMem = NULL;
KCALLPROFON(33);
// need to check PageFreecount instead of LogPtr->pKList or we might end up
// with PageFreeCount < 0, and DEBUGCHK'd
if (PageFreeCount) {
pMem = LogPtr->pKList;
paRet = GetPFN(pMem);
pfi = GetRegionFromAddr(paRet);
if (pfi) {
//
// Unlink the first page (first two DWORD's are link pointers).
// Accesses are uncached.
//
if (LogPtr->pKList = *(LPBYTE *)((DWORD)pMem + 0x20000000)) {
*(LPBYTE *)((DWORD)LogPtr->pKList + 0x20000004) = 0;
}
//
// Clear out the link info so we have a clean page.
//
*(LPDWORD)(pMem + 0x20000000) = 0;
*(LPDWORD)(pMem + 0x20000004) = 0;
ix = (paRet - pfi->paStart) / PFN_INCR;
DEBUGCHK(pfi->pUseMap[ix] == 0);
DEBUGCHK(dwCount && (dwCount <= 0xff));
pfi->pUseMap[ix] = (BYTE)dwCount;
if (-- PageFreeCount < (long) KInfoTable[KINX_MINPAGEFREE]) {
KInfoTable[KINX_MINPAGEFREE] = PageFreeCount;
}
LogPhysicalPages(PageFreeCount);
DEBUGCHK(PageFreeCount >= 0);
} else {
ERRORMSG(1, (TEXT("GrabFirstPhysPage : invalid address 0x%08x (PFN 0x%08x)\r\n"), pMem, paRet));
DEBUGCHK(0);
pMem = NULL;
}
}
DEBUGCHK(pMem);
KCALLPROFOFF(33);
return pMem;
}
//------------------------------------------------------------------------------
// called by loader.c when moving memory "slider"
//------------------------------------------------------------------------------
void
RemovePage(
DWORD dwAddr
)
{
DWORD pfn, ix;
FREEINFO *pfi;
pfn = GetPFN(dwAddr);
pfi = GetRegionFromAddr(pfn);
if (pfi) {
ix = (pfn - pfi->paStart) / PFN_INCR;
pfi->pUseMap[ix] = 0xff;
} else {
ERRORMSG(1, (TEXT("RemovePage : removing invalid address 0x%08x (PFN 0x%08x)\r\n"), dwAddr, pfn));
DEBUGCHK(0);
}
}
_inline BOOL ValidateStack (PTHREAD pth, DWORD dwCurSP)
{
DWORD lb, ub;
NKGetStackLimits (pth, &lb, &ub);
return (dwCurSP > lb) && (dwCurSP < ub);
}
//------------------------------------------------------------------------------
//
// DemandCommit is used to automatically commit stack pages. It is called
// at the start of the exception handling code since the exception handlers
// need to use the stack.
//
//------------------------------------------------------------------------------
DWORD
DemandCommit(
ulong addr,
PTHREAD pth
)
{
DWORD dwRet = DCMT_NEW;
extern void (*lpNKHaltSystem)(void);
if (pth != pCurThread) {
// this can only happen when we created a faked thread structure on KStack. Always return success.
return DCMT_OLD;
}
if (IsKernelVa (addr)) {
// only kernel thread can have a stack with kernel address
dwRet = (ProcArray == pth->pOwnerProc)? DCMT_OLD : DCMT_FAILED;
// but because IPSM changes sp to kernel address, we can't do the strict check...
// dwRet = (KERN_TRUST_FULL == pth->pProc->bTrustLevel)? DCMT_OLD : DCMT_FAILED;
// TLSPTR and stack must be in the same slot
} else if (((addr ^ (DWORD) pth->tlsPtr) & 0xFE000000) || !ValidateStack (pth, addr)) {
dwRet = DCMT_FAILED;
} else {
MEMBLOCK *pmb;
LPDWORD pPage;
PFREEINFO pfi;
uint ix;
PHYSICAL_ADDRESS paRet;
LPBYTE pMem;
PSECTION pscn = (addr & 0x80000000)? &NKSection : SectionTable[addr>>VA_SECTION];
// valid memblock?
if (!(pmb = (*pscn)[(addr>>VA_BLOCK) & BLOCK_MASK])
|| (RESERVED_BLOCK == pmb)) {
dwRet = DCMT_FAILED;
// page already commited?
} else if (*(pPage = &pmb->aPages[(addr>>VA_PAGE) & PAGE_MASK])) {
dwRet = (*pPage & PG_VALID_MASK)? DCMT_OLD : DCMT_FAILED;
} else {
// commit a new page
pMem = LogPtr->pKList;
if (GwesOOMEvent && (PageFreeCount < GwesCriticalThreshold)) {
SetOOMEvt ();
}
if (!PageFreeCount || !pMem) {
RETAILMSG(1,(L"--->>> DemandCommit: FATAL ERROR! COMPLETELY OUT OF MEMORY (%8.8lx %8.8lx)!\r\n",pMem,PageFreeCount));
lpNKHaltSystem ();
INTERRUPTS_OFF();
while (1) {
// we don't recover if there's no memory, so better off to just stop
}
}
paRet = GetPFN(pMem);
pMem = (LPBYTE)((DWORD)pMem + 0x20000000);
if (LogPtr->pKList = *(LPBYTE *)pMem)
*(LPBYTE *)((DWORD)LogPtr->pKList + 0x20000004) = 0;
*(LPDWORD)pMem = 0;
*(LPDWORD)(pMem + 4) = 0;
pfi = GetRegionFromAddr(paRet);
if (pfi) {
ix = (paRet - pfi->paStart) / PFN_INCR;
DEBUGCHK(!pfi->pUseMap[ix]);
pfi->pUseMap[ix] = 1;
#ifdef ARM
*pPage = paRet | ((pscn == &NKSection)? PG_KRN_READ_WRITE : PG_READ_WRITE);
#else
*pPage = paRet | PG_READ_WRITE;
#endif
if (-- PageFreeCount < (long) KInfoTable[KINX_MINPAGEFREE]) {
KInfoTable[KINX_MINPAGEFREE] = PageFreeCount;
}
LogPhysicalPages(PageFreeCount);
DEBUGCHK(PageFreeCount >= 0);
return DCMT_NEW;
}
ERRORMSG(1, (TEXT("DemandCommit : invalid address 0x%08x (PFN 0x%08x)\r\n"), pMem, paRet));
DEBUGCHK(0);
dwRet = DCMT_FAILED;
}
}
if (DCMT_FAILED == dwRet) {
// make sure TLS is okay
KSTKBASE(pth) = pth->dwOrigBase;
KSTKBOUND(pth) = (DWORD) pth->tlsPtr & -PAGE_SIZE;
KPROCSTKSIZE(pth) = pth->dwOrigStkSize;
KTHRDINFO(pth) = pth->tlsPtr[PRETLS_CURFIBER] = 0;
pth->tlsPtr[TLSSLOT_KERNEL] = 0;
}
return dwRet;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void
EnterPhysCS(void)
{
LPVOID pMem;
EnterCriticalSection(&PhysCS);
DEBUGCHK(PhysCS.OwnerThread == hCurThread);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -