Geoff Chappell - Software Analyst
The MMPFN (Memory Manager Page Frame Number) structure is the key to pretty much everything that the Memory Manager knows about a page of physical memory that is in general use. Since an array of these structures for all physical memory needs to be kept in physical memory, the MMPFN is its own substantial overhead. Presumably to keep this down, ever more gets packed in to the MMPFN ever more intricately.
However untidy the construction of the MMPFN, it has several fields that are recognisably the same through all versions despite moving around and being repackaged. All versions have an integral reference count. Early versions have it as variously 16-bit or 32-bit, but as a direct member of the MMPFN. Public symbol files starting with Windows 2000 SP3 show a u3 member that combines the 16-bit ReferenceCount with 16 bits of flags, and it is here thought that this or something very like it had already been defined for version 4.0. As a type, u3 is an unnamed union. It shifts once within the MMPFN:
MMPFN Offset (x86) | MMPFN Offset (x64) |
---|---|
0x0C (4.0 to 6.3); 0x14 |
0x18 (late 5.2 to 6.3); 0x20 |
The u3 union is almost certainly not original. The only known use that the earliest versions make of this space is for two 16-bit counts. Both versions 3.10 and 3.50 have at offset 0x0C something very like the 16-bit ReferenceCount as known from public symbol files but they have at offset 0x0E a second 16-bit count whose name may be lost to history. Version 3.51 dropped this second count in favour of widening ReferenceCount to 32 bits.
In none of these early versions is the kernel known to treat this space as having any bit flags. What they have instead are 12 bits of flags at offset 0x14 in combination with a Page Frame Number (PFN) in something like the style of a Page Table Entry (PTE). This whole dword is here thought to be the original MMPFNENTRY. Version 4.0 moved the 12 bit flags to offset 0x0C and redefined the 20-bit PteFrame as the whole integer. Concurrently, the 32-bit ReferenceCount was narrowed to 16 bits, but now at offset 0x0E. Though this might have been done as done as separate words for offsets 0x0C and 0x0E with no union in sight, keeping the bit flags in a 32-bit MMPFNENTRY is consistent with the u3 definition from the earliest public symbol files and the simpler explanation is that this definition dates from version 4.0.
Definition | Versions |
---|---|
MMPFNENTRY e1; |
4.0 to early 5.2 |
struct { /* see below: Structure with Entry 1 */ }; |
late 5.2 and higher |
struct { /* see below: Entry 2 Structure */ } e2; |
4.0 and higher |
struct { USHORT ReferenceCount; UCHAR ByteFlags; UCHAR InterlockedByteFlags; } e3; |
6.0 only |
struct { ULONG EntireField; } e4; |
1607 and higher |
Originally, the e1 and e2 branches each lay out the whole four bytes: e1 as bit fields, leaving the reference count as a 16-bit DontUse field; e2 as 16-bit integers, collecting the flags as ShortFlags to be followed by the ReferenceCount. Version 5.2 from Windows Server 2003 SP1 switched the flags and reference count, and changed many details of the construction.
When the version 5.2 from Windows Server 2003 SP1 brought the 16-bit ReferenceCount to the front of u3, the MMPFNENTRY of ULONG bit fields lost its high 16 bits that were labelled as DontUse, and was narrowed to USHORT bit fields. Version 6.0 rearranged further into two sets of UCHAR bit fields, which the 1607 release of Windows 10 formalises as separate structures MMPFNENTRY1 and MMPFNENTRY3.
Offset | Definition | Versions |
---|---|---|
0x00 |
USHORT ReferenceCount; |
late 5.2 and higher |
0x02 |
MMPFNENTRY e1; |
late 5.2 to 1511 |
MMPFNENTRY1 e1; |
1607 and higher | |
0x03 |
MMPFNENTRY3 e3; |
1607 and higher |
The e2 member is in essence a pair of 16-bit integers: the 16 bits of flags that e1 defines as bit fields; and a 16-bit reference count. The order changes and different versions put either or both of the 16-bit integers in union with another that is volatile. Starting with the 1607 release of Windows 10, the flags are all lost, leaving the e2 member holding just the 16-bit ReferenceCount.
Offset (x86) | Definition | Versions |
---|---|---|
0x00 |
USHORT ShortFlags; |
4.0 to early 5.2 |
USHORT ReferenceCount; |
late 5.2 only | |
union { USHORT ReferenceCount; SHORT volatile VolatileReferenceCount; }; |
6.0 to 6.3 | |
USHORT ReferenceCount; |
10.0 and higher | |
0x02 |
USHORT ReferenceCount; |
4.0 to early 5.2 |
USHORT ShortFlags; |
late 5.2 to 6.1 | |
union { USHORT ShortFlags; USHORT volatile VolatileShortFlags; }; |
6.2 to 1511 |