Translating Virtual addresses into Physical addresses

I'm trying to understand how virtual addresses are translated to physical addresses.

I started with the simple case: x86, non PAE.
In this case things should work as follows:
- CR3 contains the physical address of the page directory base
- a Virtual Address is split in 3: highest 10 bits are the Page Directory Index, next 10 are the Page Table Index and the last 12 are the offset in the page.
- PageDirectory[PageDirectoryIndex] will give you a Page Directory Entry. A PDE is split in 2: highest 22 bits point to the Page Table, the rest are control bits. In my particular case I care about bit 0 (Present bit, must be 1) and bit 7 (Size bit, 1 if large page, 0 is small page; must be 0).
- PageTable[PageTableIndex] represents a Page Table Entry. A PTE is similar to a PDE. We care only about bit 0 (the Present bit). The upper 20 bits give us the Page Frame, representing the Physical Page in which our VA is located.
- Finally, PageFrame[PageOffset] is the Physical Address corresponding to our VA.

I tried to write a small driver that makes this translation, but it reports that the present bit is 0 for every VA it tries to translate.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
BOOLEAN
VirtualToPhysical() // normal x86
{
	DWORD_PTR virtualAddress	= g_VirtualAdr;
	DWORD_PTR pageDirectory		= 0;
	DWORD_PTR pageDirectoryIndex	= 0;
	DWORD_PTR pageDirectoryEntry    = 0;
	DWORD_PTR pageTable		= 0;
	DWORD_PTR pageTableIndex	= 0;	
	DWORD_PTR pageTableEntry	= 0;
	DWORD_PTR physicalPage		= 0;
	PHYSICAL_ADDRESS cr3;

        // THIS MIGHT BE WRONG
	cr3.QuadPart = 0;
	cr3.HighPart = 0;
	cr3.LowPart  = __readcr3();
	pageDirectory = (DWORD_PTR)MmMapIoSpace(
		cr3,
		1024,
		MmNonCached);

	// pageDirectory = 0xC0300000;

	DbgPrint("CR3 = 0x%x\n", cr3.LowPart);
	if( 0 == pageDirectory )
	{
		DbgPrint("MmMapIoSpace() failed\n");
		return FALSE;
	}
	DbgPrint("PageDirectory = 0x%x\n",
		pageDirectory);
		
	pageDirectoryIndex	= virtualAddress >> 22;
	pageTableIndex		= (virtualAddress << 10) >> 22;

	pageDirectoryEntry = pageDirectory + pageDirectoryIndex * 4;
	if( 0 == pageDirectoryEntry )
	{
		DbgPrint("PDE = 0\n");
		return FALSE;
	}
	DbgPrint("PDE = 0x%x\n",
		pageDirectoryEntry);
	
	if( pageDirectoryEntry & 0x80 ) // Large Page
	{
		DbgPrint("Large page\n");
		return FALSE;
	}
	else // small page
	{
		DbgPrint("Small page\n");
		if( pageDirectoryEntry & 0x1 ) // Present
		{
			pageTable = pageDirectoryEntry & 0x0FFFFF000; 
			if( 0 == pageTable )
			{
				DbgPrint("PT  = 0\n");
				return FALSE;
			}

			pageTableEntry = pageTable + pageTableIndex * 4;
			if( pageTableEntry & 0x01 ) // Present
			{
				physicalPage = pageTableEntry & 0x0FFFFF000;
				DbgPrint("Physical Page = 0x%x\n",
					physicalPage);
				return TRUE;
			}
			else
			{
				DbgPrint("PT.P = 0\n");
				return FALSE;
			}
		}
		else	// not present
		{
			DbgPrint("PDE.P = 0\n");
			return FALSE;
		}
	}
	return TRUE;
}


I might not use
MmMapIoSpace
correctly, but I don't know how to check that.

PhysicalToVirtual()
is called by a function that checks if PAE is active or not:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
BOOLEAN
DetectPagingMode()
{
	// __int64 msr = 0; // later
	DWORD cr4   = 0;

        /*
	  msr = __readmsr(0xC0000080); // EFER

	  if( msr & 0x0500 ) // Long Mode Enabled & Active
	  {
	  	  DbgPrint("Long Mode\n");
		  return FALSE;
	  }
        */

	cr4 = __readcr4();
	if( cr4 & 0x020 ) // PAE Active
	{
		DbgPrint("PAE is active\n");
		return FALSE; // TO DO
	}

	DbgPrint("PAE is not active\n");
	return VirtualToPhysical();
}


I plan to have it detect if Long Mode is active also, but now I'm testing only on x86, so I commented that part.

Side-note: I forgot to MmUnmapIoSpace before returning from VirtualToPhysical().

EDIT: no answers, but if anyone lands here in the future: the problem was that I forgot to map the Page Table.
In essence, you need to zero the last 12 bytes of the PDE and then use
MmMapIoSpace()
to map that physical address to a virtual address.
Last edited on
Topic archived. No new replies allowed.