📄 fault.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.
//
/*+
fault.c - iX86 fault handlers
*/
#include "kernel.h"
// disable short jump warning.
#pragma warning(disable:4414)
///#define LIGHTS(n) mov dword ptr ss:[0AA001010h], ~(n)&0xFF
extern RETADDR ServerCallReturn(PSVRRTNSTRUCT psrs);
extern RETADDR CallbackReturn (LPDWORD pLinkage);
extern RETADDR ObjectCall (POBJCALLSTRUCT pobs);
extern RETADDR MapArgs(const CINFO *pci, int iMethod, void *args);
extern BOOL HandleException(PTHREAD pth, int id, ulong addr);
extern void NextThread(void);
extern void KCNextThread(void);
extern void OEMIdle(void);
extern KTSS MainTSS;
extern void Reschedule(void);
extern void RunThread(void);
extern void DumpTctx(PTHREAD pth, int id, ulong addr, int level);
extern DWORD (*pfnOEMIntrOccurs) (DWORD dwSysIntr);
RETADDR PerformCallBackExt (POBJCALLSTRUCT pobs);
extern unsigned __int64 g_aGlobalDescriptorTable[];
extern CRITICAL_SECTION VAcs;
#ifdef NKPROF
extern void ProfilerHit(unsigned long ra);
extern void CeLogInterrupt(DWORD dwLogValue);
#endif
void CeLogThreadMigrate(HANDLE hProcess, DWORD dwReserved);
#define LOAD_SEGS 0
#define ADDR_SLOT_SIZE 0x02000000
#define PDES_PER_SLOT (ADDR_SLOT_SIZE / 1024 / PAGE_SIZE)
#define NUM_SLOTS 32
#define PID_TO_PT_INDEX(pid) ((pid+1) * PDES_PER_SLOT)
#define BLOCKS_PER_PAGE_TABLE (1024 / PAGES_PER_BLOCK)
#define BLOCK_SIZE (PAGES_PER_BLOCK * PAGE_SIZE)
#define SYSCALL_INT 0x20
#define KCALL_INT 0x22
#define PT_PTR_TO_INDEX(pp) ((pp) - g_pptbl)
extern PPAGETABLE g_pPageDir;
extern DWORD ProcessorFeatures;
FXSAVE_AREA g_InitialFPUState;
PTHREAD g_CurFPUOwner;
DWORD MD_CBRtn;
#ifdef NKPROF
PTHREAD pthFakeStruct;
#endif
//
// CR0 bit definitions for numeric coprocessor
//
#define MP_MASK 0x00000002
#define EM_MASK 0x00000004
#define TS_MASK 0x00000008
#define NE_MASK 0x00000020
#define NPX_CW_PRECISION_MASK 0x300
#define NPX_CW_PRECISION_24 0x000
#define NPX_CW_PRECISION_53 0x200
#define NPX_CW_PRECISION_64 0x300
#define VA_TO_PD_IDX(va) ((DWORD) (va) >> 22) // (va)/4M == PDE idx
#define PD_IDX_TO_VA(idx) ((DWORD) (idx) << 22) // PDE idx * 4M == va base
#define PERFORMCALLBACK -113 // MUST be -PerformCallback Win32Methods in kwin32.c
// 113 == -(APISet 0, method 113)
#define RAISEEXCEPTION -114 // MUST be -RaiseException Win32Methods in kwin32.c
#define hCurThd [KData].ahSys[SH_CURTHREAD*4]
#define PtrCurThd [KData].pCurThd
#define PtrCurProc [KData].pCurPrc
#define THREAD_CTX_ES (THREAD_CONTEXT_OFFSET+8)
ERRFALSE(8 == offsetof(CPUCONTEXT, TcxEs));
#define THREAD_CTX_EDI (THREAD_CONTEXT_OFFSET+16)
ERRFALSE(16 == offsetof(CPUCONTEXT, TcxEdi));
#define Naked void __declspec(naked)
// #define ONE_ENTRY
#pragma warning(disable:4035) // Disable warning about no return value
//
// The Physical to Virtual mapping table is supplied by OEM.
//
extern PPTE g_pOEMAddressTable;
BOOL MDValidateRomChain (ROMChain_t *pROMChain)
{
PPTE ppte;
DWORD dwEnd;
for ( ; pROMChain; pROMChain = pROMChain->pNext) {
for (ppte = g_pOEMAddressTable; ppte->dwSize; ppte ++) {
dwEnd = ppte->dwVA + ppte->dwSize;
if (IsInRange (pROMChain->pTOC->physfirst, ppte->dwVA, dwEnd)) {
if (IsInRange (pROMChain->pTOC->physlast, ppte->dwVA, dwEnd)) {
// good XIP, break inner loop and go on to the next region
break;
}
// bad
NKDbgPrintfW (L"MDValidateRomChain: XIP (%8.8lx -> %8.8lx) span accross multiple memory region\r\n",
pROMChain->pTOC->physfirst, pROMChain->pTOC->physlast);
return FALSE;
}
}
if (!ppte->dwSize) {
NKDbgPrintfW (L"MDValidateRomChain: XIP (%8.8lx -> %8.8lx) doesn't exist in OEMAddressTable \r\n",
pROMChain->pTOC->physfirst, pROMChain->pTOC->physlast);
return FALSE;
}
}
return TRUE;
}
extern CRITICAL_SECTION VAcs;
LPBYTE GrabFirstPhysPage(DWORD dwCount);
// hardware pagetable for the 2nd gig address and secure section
ulong gHwPTBL2G[32*HARDWARE_PT_PER_PROC]; // 32 == shared section + mapping address from 0x42000000 -> 0x7fffffff
DWORD gdwSlotTouched, gfObjStoreTouched;
#define MAPPER_SLOT_TO_PTBL(slot) (gHwPTBL2G + HARDWARE_PT_PER_PROC * ((slot) - 32))
MEMBLOCK *MDAllocMemBlock (DWORD dwBase, DWORD ixBlock)
{
LPDWORD pPtbls; // which hardware PT to use
DWORD ixTbl = ixBlock >> 6; // ixTbl == which 4M block
MEMBLOCK *pmb;
DEBUGMSG (ZONE_VIRTMEM, (L"MDAllocMemBlock: dwBase = %8.8lx, ixBlock = %8.8lx\r\n", dwBase, ixBlock));
DEBUGCHK (!IsKernelVa (dwBase));
DEBUGCHK (!VAcs.hCrit || (VAcs.OwnerThread == hCurThread));
switch (dwBase) {
case SECURE_VMBASE:
// secure section, use NK's page table
pPtbls = ProcArray[0].pPTBL;
break;
case MODULE_BASE_ADDRESS:
// module section (slot 1), use 1st 8 entries of gHwPTBL2G
pPtbls = gHwPTBL2G;
break;
case 0:
// current process
pPtbls = pCurProc->pPTBL;
break;
default:
// either a sloted process address or 2nd gig
if (dwBase < FIRST_MAPPER_ADDRESS) {
// process address
pPtbls = ProcArray[(dwBase >> VA_SECTION) - 1].pPTBL;
} else {
// in the 2nd gig
pPtbls = MAPPER_SLOT_TO_PTBL(dwBase >> VA_SECTION);
}
break;
}
//
// allocate a page if not allocate yet
//
if (!pPtbls[ixTbl]) {
// allocate and setup top-level page table
DWORD dwVA = (DWORD) KCall((PKFN)GrabFirstPhysPage,1);
if (!dwVA) {
// Out of memory
return NULL;
}
// we're relying on the fact that the page we just got has already been zero'd
// or we need to memset it to 0 here.
pPtbls[ixTbl] = LIN_TO_PHYS (dwVA) | ((SECURE_VMBASE == dwBase)? PG_KRN_READ_WRITE : PG_READ_WRITE);
DEBUGMSG (ZONE_VIRTMEM, (L"MDAllocMemBlock: Allocated a new page for Page Table va = %8.8lx, entry = %8.8lx\r\n",
dwVA, pPtbls[ixTbl]));
}
DEBUGMSG (ZONE_VIRTMEM, (L"MDAllocMemBlock: Allocating MemBlock\r\n"));
// allocate MEM_BLOCK
if (pmb = AllocMem (HEAP_MEMBLOCK)) {
// initialize memblock
memset (pmb, 0, sizeof(MEMBLOCK));
// pagetable access needs to be uncached.
pmb->aPages = (LPDWORD) (PHYS_TO_LIN (pPtbls[ixTbl] & -PAGE_SIZE) + ((ixBlock << 6) & (PAGE_SIZE-1)));
}
DEBUGMSG (ZONE_VIRTMEM, (L"MDAllocMemBlock: returning %8.8lx, aPages = %8.8lx\r\n", pmb, pmb->aPages));
return pmb;
}
void FreeHardwarePT (DWORD dwVMBase)
{
LPDWORD pPtbls; // which hardware PT to use
int i;
// can not free current process, shared section, or secure section
DEBUGCHK ((dwVMBase != pCurProc->dwVMBase) && ((int) dwVMBase >= (2 << VA_SECTION)));
DEBUGCHK (!VAcs.hCrit || (VAcs.OwnerThread == hCurThread));
// clear first-level page table
memset (&g_pPageDir->PTE[(dwVMBase >> 22)], 0, HARDWARE_PT_PER_PROC * sizeof (ULONG));
if (dwVMBase < FIRST_MAPPER_ADDRESS) {
pPtbls = ProcArray [(dwVMBase >> VA_SECTION) - 1].pPTBL;
// per process
} else {
// 2nd gig
DEBUGCHK ((dwVMBase >= FIRST_MAPPER_ADDRESS) && (dwVMBase < LAST_MAPPER_ADDRESS));
pPtbls = MAPPER_SLOT_TO_PTBL (dwVMBase >> VA_SECTION);
}
for (i = 0; i < HARDWARE_PT_PER_PROC; i ++) {
if (pPtbls[i]) {
FreePhysPage (pPtbls[i] & -PAGE_SIZE);
pPtbls[i] = 0;
}
}
KCall ((FARPROC) OEMCacheRangeFlush, 0, 0, CACHE_SYNC_FLUSH_TLB);
}
void MDFreeMemBlock (MEMBLOCK *pmb)
{
DEBUGCHK ((NULL_BLOCK != pmb) && (RESERVED_BLOCK != pmb));
DEBUGCHK (!VAcs.hCrit || (VAcs.OwnerThread == hCurThread));
#ifdef DEBUG
{
int i;
for (i = 0; i < PAGES_PER_BLOCK; i ++) {
DEBUGCHK (!pmb->aPages[i] || (pmb->aPages[i] == BAD_PAGE));
}
}
#endif
// need to zero it out incase there're bad pages.
memset (pmb->aPages, 0, PAGES_PER_BLOCK * sizeof (ulong));
FreeMem (pmb, HEAP_MEMBLOCK);
}
//------------------------------------------------------------------------------
// LoadPageTable: handle Page Fault exception
// addr: address that causes the fault
// dwErrCode: the error code. (reference: x86 spec)
// bit 0 (P): if page mapping is present
// bit 1 (R/W): Read or write access
// bit 2 (U/S): user or supervisor mode
//------------------------------------------------------------------------------
#define PFCODE_PRESENT 0x01 // present
#define PFCODE_WRITE 0x02 // trying to write
#define PFCODE_USER_MODE 0x04 // in user mode
BOOL LoadPageTable (DWORD addr, DWORD dwErrCode)
{
DWORD dwSlot = addr >> VA_SECTION, aky = 0;
DWORD bitClear = 0;
//DEBUGMSG (ZONE_VIRTMEM, (L"LoadPageTable: addr = %8.8lx\r\n", addr));
if (!IsKernelVa(addr) // not kernel address
&& (((dwSlot = addr >> VA_SECTION) <= 1) // current process or slot 1
|| (dwSlot > MAX_PROCESSES) // memmap area
|| ((aky = 1 << (dwSlot-1)) & CurAKey))) { // slotted process address, have access to the slot
LPDWORD pPtbls;
DWORD ix4M = (addr >> 22);
switch (dwSlot) {
case SECURE_SECTION:
// secure slot, use NK's page table
pPtbls = ProcArray[0].pPTBL;
break;
case 0:
pPtbls = pCurProc->pPTBL;
break;
case MODULE_SECTION:
// module section (slot 1), use 1st 8 entries of gHwPTBL2G
pPtbls = gHwPTBL2G;
break;
case SHARED_SECTION:
// write access requires kernel mode
if ((dwErrCode & (PFCODE_WRITE|PFCODE_USER_MODE)) == (PFCODE_WRITE|PFCODE_USER_MODE)) {
return FALSE;
}
// user mode read-only
if (dwErrCode & PFCODE_USER_MODE) {
bitClear = PG_WRITE_MASK;
} else if (dwErrCode & PFCODE_PRESENT) {
// kernel mode trying to write, while page directory still marked R/O
// just clear the page directory and we'll fill in the right access
// DEBUGCHK (dwErrCode & PFCODE_WRITE)
g_pPageDir->PTE[ix4M] = 0;
}
// fall through
default:
// either a sloted process address or 2nd gig
if (dwSlot <= MAX_PROCESSES) {
DEBUGCHK (dwSlot > 1);
// process address
pPtbls = ProcArray[dwSlot-1].pPTBL;
gdwSlotTouched |= aky;
} else {
// in the 2nd gig
pPtbls = MAPPER_SLOT_TO_PTBL(dwSlot);
// check object store
if (IsSlotInObjStore (dwSlot)) {
if (!(CurAKey & pFileSysProc->aky))
return FALSE; // don't have access
gfObjStoreTouched = TRUE;
}
}
break;
}
if (pPtbls[ix4M & 7] && !g_pPageDir->PTE[ix4M]) {
// we have an entry on the per-slot page table, but not on HW PageTable
g_pPageDir->PTE[ix4M] = pPtbls[ix4M & 7] & ~bitClear;
return TRUE;
}
}
DEBUGMSG (ZONE_VIRTMEM, (L"LoadPageTable failed, addr = %8.8lx, dwSlot = %8.8lx, aky = %8.8lx, CurAKey = %8.8lx\r\n", addr, dwSlot, aky, CurAKey));
return FALSE;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
PVOID
Phys2Virt(
DWORD pfn
)
{
PPTE ppte;
for (ppte = g_pOEMAddressTable; ppte->dwSize; ppte ++) {
if ((pfn >= ppte->dwPA) && (pfn < ppte->dwPA + ppte->dwSize))
return (LPVOID) (pfn - ppte->dwPA + ppte->dwVA);
}
DEBUGMSG(ZONE_PHYSMEM, (TEXT("Phys2Virt() : PFN (0x%08X) not found!\r\n"), pfn));
return NULL;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -