2 * Copyright (C) 1998-2005 ReactOS Team (and the authors from the programmers section)
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 * PROJECT: ReactOS kernel
20 * FILE: ntoskrnl/mm/section.c
21 * PURPOSE: Section object page tables
28 This file implements the section page table. It relies on rtl generic table
29 functionality to provide access to 256-page chunks. Calls to
30 MiSetPageEntrySectionSegment and MiGetPageEntrySectionSegment must be
31 synchronized by holding the segment lock.
33 Each page table entry is a ULONG as in x86.
35 Bit 1 is used as a swap entry indication as in the main page table.
36 Bit 2 is used as a dirty indication. A dirty page will eventually be written
38 Bits 3-11 are used as a map count in the legacy mm code, Note that zero is
39 illegal, as the legacy code does not take advantage of segment rmaps.
40 Therefore, every segment page is mapped in at least one address space, and
41 MmUnsharePageEntry is quite complicated. In addition, the page may also be
42 owned by the legacy cache manager, giving an implied additional reference.
43 Upper bits are a PFN_NUMBER.
45 These functions, in addition to maintaining the segment page table also
46 automatically maintain the segment rmap by calling MmSetSectionAssociation
47 and MmDeleteSectionAssociation. Segment rmaps are discussed in rmap.c. The
48 upshot is that it is impossible to have a page properly registered in a segment
49 page table and not also found in a segment rmap that can be found from the
54 /* INCLUDES *****************************************************************/
61 #define DPRINTC DPRINT
63 /* TYPES *********************************************************************/
65 extern KSPIN_LOCK MiSectionPageTableLock
;
70 MiSectionPageTableAllocate(PRTL_GENERIC_TABLE Table
, CLONG Bytes
)
73 Result
= ExAllocatePoolWithTag(NonPagedPool
, Bytes
, 'MmPt');
74 //DPRINT("MiSectionPageTableAllocate(%d) => %p\n", Bytes, Result);
81 MiSectionPageTableFree(PRTL_GENERIC_TABLE Table
, PVOID Data
)
83 //DPRINT("MiSectionPageTableFree(%p)\n", Data);
84 ExFreePoolWithTag(Data
, 'MmPt');
88 RTL_GENERIC_COMPARE_RESULTS
90 MiSectionPageTableCompare(PRTL_GENERIC_TABLE Table
,
94 PLARGE_INTEGER A
= PtrA
, B
= PtrB
;
95 BOOLEAN Result
= (A
->QuadPart
< B
->QuadPart
) ? GenericLessThan
:
96 (A
->QuadPart
== B
->QuadPart
) ? GenericEqual
: GenericGreaterThan
;
100 ("Compare: %08x%08x vs %08x%08x => %s\n",
101 A
->u
.HighPart
, A
->u
.LowPart
,
102 B
->u
.HighPart
, B
->u
.LowPart
,
103 Result
== GenericLessThan
? "GenericLessThan" :
104 Result
== GenericGreaterThan
? "GenericGreaterThan" :
112 PCACHE_SECTION_PAGE_TABLE
114 MiSectionPageTableGet(PRTL_GENERIC_TABLE Table
,
115 PLARGE_INTEGER FileOffset
)
117 LARGE_INTEGER SearchFileOffset
;
118 PCACHE_SECTION_PAGE_TABLE PageTable
;
119 SearchFileOffset
.QuadPart
= ROUND_DOWN(FileOffset
->QuadPart
,
120 ENTRIES_PER_ELEMENT
* PAGE_SIZE
);
121 PageTable
= RtlLookupElementGenericTable(Table
, &SearchFileOffset
);
123 DPRINT("MiSectionPageTableGet(%08x,%08x%08x)\n",
125 FileOffset
->HighPart
,
126 FileOffset
->LowPart
);
132 PCACHE_SECTION_PAGE_TABLE
134 MiSectionPageTableGetOrAllocate(PRTL_GENERIC_TABLE Table
,
135 PLARGE_INTEGER FileOffset
)
137 LARGE_INTEGER SearchFileOffset
;
138 CACHE_SECTION_PAGE_TABLE SectionZeroPageTable
;
139 PCACHE_SECTION_PAGE_TABLE PageTableSlice
= MiSectionPageTableGet(Table
,
141 /* Please zero memory when taking away zero initialization. */
142 RtlZeroMemory(&SectionZeroPageTable
, sizeof(CACHE_SECTION_PAGE_TABLE
));
145 SearchFileOffset
.QuadPart
= ROUND_DOWN(FileOffset
->QuadPart
,
146 ENTRIES_PER_ELEMENT
* PAGE_SIZE
);
147 SectionZeroPageTable
.FileOffset
= SearchFileOffset
;
148 SectionZeroPageTable
.Refcount
= 1;
149 PageTableSlice
= RtlInsertElementGenericTable(Table
,
150 &SectionZeroPageTable
,
151 sizeof(SectionZeroPageTable
),
153 if (!PageTableSlice
) return NULL
;
154 DPRINT("Allocate page table %x (%08x%08x)\n",
156 PageTableSlice
->FileOffset
.u
.HighPart
,
157 PageTableSlice
->FileOffset
.u
.LowPart
);
159 return PageTableSlice
;
164 MiInitializeSectionPageTable(PMM_SECTION_SEGMENT Segment
)
166 RtlInitializeGenericTable(&Segment
->PageTable
,
167 MiSectionPageTableCompare
,
168 MiSectionPageTableAllocate
,
169 MiSectionPageTableFree
,
172 DPRINT("MiInitializeSectionPageTable(%p)\n", &Segment
->PageTable
);
177 _MmSetPageEntrySectionSegment(PMM_SECTION_SEGMENT Segment
,
178 PLARGE_INTEGER Offset
,
183 ULONG_PTR PageIndex
, OldEntry
;
184 PCACHE_SECTION_PAGE_TABLE PageTable
;
186 ASSERT(Segment
->Locked
);
187 ASSERT(!IS_SWAP_FROM_SSE(Entry
) || !IS_DIRTY_SSE(Entry
));
189 if (Entry
&& !IS_SWAP_FROM_SSE(Entry
))
190 MmGetRmapListHeadPage(PFN_FROM_SSE(Entry
));
192 PageTable
= MiSectionPageTableGetOrAllocate(&Segment
->PageTable
, Offset
);
194 if (!PageTable
) return STATUS_NO_MEMORY
;
196 ASSERT(MiSectionPageTableGet(&Segment
->PageTable
, Offset
));
198 PageTable
->Segment
= Segment
;
199 PageIndex
= (Offset
->QuadPart
- PageTable
->FileOffset
.QuadPart
) / PAGE_SIZE
;
200 OldEntry
= PageTable
->PageEntries
[PageIndex
];
202 DPRINT("MiSetPageEntrySectionSegment(%p,%08x%08x,%x=>%x)\n",
209 if (PFN_FROM_SSE(Entry
) == PFN_FROM_SSE(OldEntry
)) {
211 } else if (Entry
&& !IS_SWAP_FROM_SSE(Entry
)) {
212 ASSERT(!OldEntry
|| IS_SWAP_FROM_SSE(OldEntry
));
213 MmSetSectionAssociation(PFN_FROM_SSE(Entry
), Segment
, Offset
);
214 } else if (OldEntry
&& !IS_SWAP_FROM_SSE(OldEntry
)) {
215 ASSERT(!Entry
|| IS_SWAP_FROM_SSE(Entry
));
216 MmDeleteSectionAssociation(PFN_FROM_SSE(OldEntry
));
217 } else if (IS_SWAP_FROM_SSE(Entry
)) {
218 ASSERT(!IS_SWAP_FROM_SSE(OldEntry
));
220 MmDeleteSectionAssociation(PFN_FROM_SSE(OldEntry
));
221 } else if (IS_SWAP_FROM_SSE(OldEntry
)) {
222 ASSERT(!IS_SWAP_FROM_SSE(Entry
));
224 MmSetSectionAssociation(PFN_FROM_SSE(OldEntry
), Segment
, Offset
);
226 /* We should not be replacing a page like this */
229 PageTable
->PageEntries
[PageIndex
] = Entry
;
230 return STATUS_SUCCESS
;
235 _MmGetPageEntrySectionSegment(PMM_SECTION_SEGMENT Segment
,
236 PLARGE_INTEGER Offset
,
240 LARGE_INTEGER FileOffset
;
241 ULONG_PTR PageIndex
, Result
;
242 PCACHE_SECTION_PAGE_TABLE PageTable
;
244 ASSERT(Segment
->Locked
);
245 FileOffset
.QuadPart
= ROUND_DOWN(Offset
->QuadPart
,
246 ENTRIES_PER_ELEMENT
* PAGE_SIZE
);
247 PageTable
= MiSectionPageTableGet(&Segment
->PageTable
, &FileOffset
);
248 if (!PageTable
) return 0;
249 PageIndex
= (Offset
->QuadPart
- PageTable
->FileOffset
.QuadPart
) / PAGE_SIZE
;
250 Result
= PageTable
->PageEntries
[PageIndex
];
253 ("MiGetPageEntrySectionSegment(%p,%08x%08x) => %x %s:%d\n",
255 FileOffset
.u
.HighPart
,
256 FileOffset
.u
.LowPart
+ PageIndex
* PAGE_SIZE
,
265 Destroy the rtl generic table that serves as the section's page table. Call
266 the FreePage function for each non-zero entry in the section page table as
267 we go. Note that the page table is still techinally valid until after all
268 pages are destroyed, as we don't finally destroy the table until we've free
269 each slice. There is no order guarantee for deletion of individual elements
270 although it's in-order as written now.
276 MmFreePageTablesSectionSegment(PMM_SECTION_SEGMENT Segment
,
277 FREE_SECTION_PAGE_FUN FreePage
)
279 PCACHE_SECTION_PAGE_TABLE Element
;
280 DPRINT("MiFreePageTablesSectionSegment(%p)\n", &Segment
->PageTable
);
281 while ((Element
= RtlGetElementGenericTable(&Segment
->PageTable
, 0))) {
282 DPRINT("Delete table for <%wZ> %x -> %08x%08x\n",
283 Segment
->FileObject
? &Segment
->FileObject
->FileName
: NULL
,
285 Element
->FileOffset
.u
.HighPart
,
286 Element
->FileOffset
.u
.LowPart
);
290 for (i
= 0; i
< ENTRIES_PER_ELEMENT
; i
++)
293 LARGE_INTEGER Offset
;
294 Offset
.QuadPart
= Element
->FileOffset
.QuadPart
+ i
* PAGE_SIZE
;
295 Entry
= Element
->PageEntries
[i
];
296 if (Entry
&& !IS_SWAP_FROM_SSE(Entry
))
298 DPRINT("Freeing page %x:%x @ %x\n",
303 FreePage(Segment
, &Offset
);
307 DPRINT("Remove memory\n");
308 RtlDeleteElementGenericTable(&Segment
->PageTable
, Element
);
315 Retrieves the MM_SECTION_SEGMENT and fills in the LARGE_INTEGER Offset given
316 by the caller that corresponds to the page specified. This uses
317 MmGetSegmentRmap to find the rmap belonging to the segment itself, and uses
318 the result as a pointer to a 256-entry page table structure. The rmap also
319 includes 8 bits of offset information indication one of 256 page entries that
320 the rmap corresponds to. This information together gives us an exact offset
321 into the file, as well as the MM_SECTION_SEGMENT pointer stored in the page
324 NULL is returned is there is no segment rmap for the page.
330 MmGetSectionAssociation(PFN_NUMBER Page
,
331 PLARGE_INTEGER Offset
)
334 PMM_SECTION_SEGMENT Segment
= NULL
;
335 PCACHE_SECTION_PAGE_TABLE PageTable
;
337 PageTable
= (PCACHE_SECTION_PAGE_TABLE
)MmGetSegmentRmap(Page
,
341 Segment
= PageTable
->Segment
;
342 Offset
->QuadPart
= PageTable
->FileOffset
.QuadPart
+
343 (RawOffset
<< PAGE_SHIFT
);
351 MmSetSectionAssociation(PFN_NUMBER Page
,
352 PMM_SECTION_SEGMENT Segment
,
353 PLARGE_INTEGER Offset
)
355 PCACHE_SECTION_PAGE_TABLE PageTable
;
358 PageTable
= MiSectionPageTableGet(&Segment
->PageTable
, Offset
);
361 ActualOffset
= (ULONG
)(Offset
->QuadPart
- PageTable
->FileOffset
.QuadPart
);
363 (PEPROCESS
)PageTable
,
364 (PVOID
)(RMAP_SEGMENT_MASK
| (ActualOffset
>> PAGE_SHIFT
)));
366 return STATUS_SUCCESS
;