[CMAKE]
[reactos.git] / ntoskrnl / mm / section.c
1 /*
2 * Copyright (C) 1998-2005 ReactOS Team (and the authors from the programmers section)
3 *
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.
8 *
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.
13 *
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.
17 *
18 *
19 * PROJECT: ReactOS kernel
20 * FILE: ntoskrnl/mm/section.c
21 * PURPOSE: Implements section objects
22 *
23 * PROGRAMMERS: Rex Jolliff
24 * David Welch
25 * Eric Kohl
26 * Emanuele Aliberti
27 * Eugene Ingerman
28 * Casper Hornstrup
29 * KJK::Hyperion
30 * Guido de Jong
31 * Ge van Geldorp
32 * Royce Mitchell III
33 * Filip Navara
34 * Aleksey Bragin
35 * Jason Filby
36 * Thomas Weidenmueller
37 * Gunnar Andre' Dalsnes
38 * Mike Nordell
39 * Alex Ionescu
40 * Gregor Anich
41 * Steven Edwards
42 * Herve Poussineau
43 */
44
45 /* INCLUDES *****************************************************************/
46
47 #include <ntoskrnl.h>
48 #ifdef NEWCC
49 #include "../cache/section/newmm.h"
50 #endif
51 #define NDEBUG
52 #include <debug.h>
53 #include <reactos/exeformat.h>
54
55 #if defined (ALLOC_PRAGMA)
56 #pragma alloc_text(INIT, MmCreatePhysicalMemorySection)
57 #pragma alloc_text(INIT, MmInitSectionImplementation)
58 #endif
59
60 NTSTATUS
61 NTAPI
62 MiMapViewInSystemSpace(IN PVOID Section,
63 IN PVOID Session,
64 OUT PVOID *MappedBase,
65 IN OUT PSIZE_T ViewSize);
66
67 NTSTATUS
68 NTAPI
69 MmCreateArm3Section(OUT PVOID *SectionObject,
70 IN ACCESS_MASK DesiredAccess,
71 IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
72 IN PLARGE_INTEGER InputMaximumSize,
73 IN ULONG SectionPageProtection,
74 IN ULONG AllocationAttributes,
75 IN HANDLE FileHandle OPTIONAL,
76 IN PFILE_OBJECT FileObject OPTIONAL);
77
78 NTSTATUS
79 NTAPI
80 MmMapViewOfArm3Section(IN PVOID SectionObject,
81 IN PEPROCESS Process,
82 IN OUT PVOID *BaseAddress,
83 IN ULONG_PTR ZeroBits,
84 IN SIZE_T CommitSize,
85 IN OUT PLARGE_INTEGER SectionOffset OPTIONAL,
86 IN OUT PSIZE_T ViewSize,
87 IN SECTION_INHERIT InheritDisposition,
88 IN ULONG AllocationType,
89 IN ULONG Protect);
90
91 //
92 // PeFmtCreateSection depends on the following:
93 //
94 C_ASSERT(EXEFMT_LOAD_HEADER_SIZE >= sizeof(IMAGE_DOS_HEADER));
95 C_ASSERT(sizeof(IMAGE_NT_HEADERS32) <= sizeof(IMAGE_NT_HEADERS64));
96
97 C_ASSERT(TYPE_ALIGNMENT(IMAGE_NT_HEADERS32) == TYPE_ALIGNMENT(IMAGE_NT_HEADERS64));
98 C_ASSERT(RTL_SIZEOF_THROUGH_FIELD(IMAGE_NT_HEADERS32, FileHeader) == RTL_SIZEOF_THROUGH_FIELD(IMAGE_NT_HEADERS64, FileHeader));
99 C_ASSERT(FIELD_OFFSET(IMAGE_NT_HEADERS32, OptionalHeader) == FIELD_OFFSET(IMAGE_NT_HEADERS64, OptionalHeader));
100
101 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, Magic));
102 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, SectionAlignment));
103 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, FileAlignment));
104 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, Subsystem));
105 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, MinorSubsystemVersion));
106 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, MajorSubsystemVersion));
107 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, AddressOfEntryPoint));
108 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, SizeOfCode));
109 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, SizeOfHeaders));
110
111 /* TYPES *********************************************************************/
112
113 typedef struct
114 {
115 PROS_SECTION_OBJECT Section;
116 PMM_SECTION_SEGMENT Segment;
117 ULONG Offset;
118 BOOLEAN WasDirty;
119 BOOLEAN Private;
120 }
121 MM_SECTION_PAGEOUT_CONTEXT;
122
123 /* GLOBALS *******************************************************************/
124
125 POBJECT_TYPE MmSectionObjectType = NULL;
126
127 SIZE_T MmAllocationFragment;
128
129 ULONG_PTR MmSubsectionBase;
130
131 static ULONG SectionCharacteristicsToProtect[16] =
132 {
133 PAGE_NOACCESS, /* 0 = NONE */
134 PAGE_NOACCESS, /* 1 = SHARED */
135 PAGE_EXECUTE, /* 2 = EXECUTABLE */
136 PAGE_EXECUTE, /* 3 = EXECUTABLE, SHARED */
137 PAGE_READONLY, /* 4 = READABLE */
138 PAGE_READONLY, /* 5 = READABLE, SHARED */
139 PAGE_EXECUTE_READ, /* 6 = READABLE, EXECUTABLE */
140 PAGE_EXECUTE_READ, /* 7 = READABLE, EXECUTABLE, SHARED */
141 /*
142 * FIXME? do we really need the WriteCopy field in segments? can't we use
143 * PAGE_WRITECOPY here?
144 */
145 PAGE_READWRITE, /* 8 = WRITABLE */
146 PAGE_READWRITE, /* 9 = WRITABLE, SHARED */
147 PAGE_EXECUTE_READWRITE, /* 10 = WRITABLE, EXECUTABLE */
148 PAGE_EXECUTE_READWRITE, /* 11 = WRITABLE, EXECUTABLE, SHARED */
149 PAGE_READWRITE, /* 12 = WRITABLE, READABLE */
150 PAGE_READWRITE, /* 13 = WRITABLE, READABLE, SHARED */
151 PAGE_EXECUTE_READWRITE, /* 14 = WRITABLE, READABLE, EXECUTABLE */
152 PAGE_EXECUTE_READWRITE, /* 15 = WRITABLE, READABLE, EXECUTABLE, SHARED */
153 };
154
155 static GENERIC_MAPPING MmpSectionMapping = {
156 STANDARD_RIGHTS_READ | SECTION_MAP_READ | SECTION_QUERY,
157 STANDARD_RIGHTS_WRITE | SECTION_MAP_WRITE,
158 STANDARD_RIGHTS_EXECUTE | SECTION_MAP_EXECUTE,
159 SECTION_ALL_ACCESS};
160
161 #define PAGE_FROM_SSE(E) ((E) & 0xFFFFF000)
162 #define PFN_FROM_SSE(E) ((E) >> PAGE_SHIFT)
163 #define SHARE_COUNT_FROM_SSE(E) (((E) & 0x00000FFE) >> 1)
164 #define IS_SWAP_FROM_SSE(E) ((E) & 0x00000001)
165 #define MAX_SHARE_COUNT 0x7FF
166 #define MAKE_SSE(P, C) ((P) | ((C) << 1))
167 #define SWAPENTRY_FROM_SSE(E) ((E) >> 1)
168 #define MAKE_SWAP_SSE(S) (((S) << 1) | 0x1)
169
170 static const INFORMATION_CLASS_INFO ExSectionInfoClass[] =
171 {
172 ICI_SQ_SAME( sizeof(SECTION_BASIC_INFORMATION), sizeof(ULONG), ICIF_QUERY ), /* SectionBasicInformation */
173 ICI_SQ_SAME( sizeof(SECTION_IMAGE_INFORMATION), sizeof(ULONG), ICIF_QUERY ), /* SectionImageInformation */
174 };
175
176 /* FUNCTIONS *****************************************************************/
177
178
179 /*
180 References:
181 [1] Microsoft Corporation, "Microsoft Portable Executable and Common Object
182 File Format Specification", revision 6.0 (February 1999)
183 */
184 NTSTATUS NTAPI PeFmtCreateSection(IN CONST VOID * FileHeader,
185 IN SIZE_T FileHeaderSize,
186 IN PVOID File,
187 OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
188 OUT PULONG Flags,
189 IN PEXEFMT_CB_READ_FILE ReadFileCb,
190 IN PEXEFMT_CB_ALLOCATE_SEGMENTS AllocateSegmentsCb)
191 {
192 NTSTATUS nStatus;
193 ULONG cbFileHeaderOffsetSize = 0;
194 ULONG cbSectionHeadersOffset = 0;
195 ULONG cbSectionHeadersSize;
196 ULONG cbSectionHeadersOffsetSize = 0;
197 ULONG cbOptHeaderSize;
198 ULONG cbHeadersSize = 0;
199 ULONG nSectionAlignment;
200 ULONG nFileAlignment;
201 const IMAGE_DOS_HEADER * pidhDosHeader;
202 const IMAGE_NT_HEADERS32 * pinhNtHeader;
203 const IMAGE_OPTIONAL_HEADER32 * piohOptHeader;
204 const IMAGE_SECTION_HEADER * pishSectionHeaders;
205 PMM_SECTION_SEGMENT pssSegments;
206 LARGE_INTEGER lnOffset;
207 PVOID pBuffer;
208 ULONG nPrevVirtualEndOfSegment = 0;
209 ULONG nFileSizeOfHeaders = 0;
210 ULONG i;
211
212 ASSERT(FileHeader);
213 ASSERT(FileHeaderSize > 0);
214 ASSERT(File);
215 ASSERT(ImageSectionObject);
216 ASSERT(ReadFileCb);
217 ASSERT(AllocateSegmentsCb);
218
219 ASSERT(Intsafe_CanOffsetPointer(FileHeader, FileHeaderSize));
220
221 ASSERT(((UINT_PTR)FileHeader % TYPE_ALIGNMENT(IMAGE_DOS_HEADER)) == 0);
222
223 #define DIE(ARGS_) { DPRINT ARGS_; goto l_Return; }
224
225 pBuffer = NULL;
226 pidhDosHeader = FileHeader;
227
228 /* DOS HEADER */
229 nStatus = STATUS_ROS_EXEFMT_UNKNOWN_FORMAT;
230
231 /* image too small to be an MZ executable */
232 if(FileHeaderSize < sizeof(IMAGE_DOS_HEADER))
233 DIE(("Too small to be an MZ executable, size is %lu\n", FileHeaderSize));
234
235 /* no MZ signature */
236 if(pidhDosHeader->e_magic != IMAGE_DOS_SIGNATURE)
237 DIE(("No MZ signature found, e_magic is %hX\n", pidhDosHeader->e_magic));
238
239 /* not a Windows executable */
240 if(pidhDosHeader->e_lfanew <= 0)
241 DIE(("Not a Windows executable, e_lfanew is %d\n", pidhDosHeader->e_lfanew));
242
243 /* NT HEADER */
244 nStatus = STATUS_INVALID_IMAGE_FORMAT;
245
246 if(!Intsafe_AddULong32(&cbFileHeaderOffsetSize, pidhDosHeader->e_lfanew, RTL_SIZEOF_THROUGH_FIELD(IMAGE_NT_HEADERS32, FileHeader)))
247 DIE(("The DOS stub is too large, e_lfanew is %X\n", pidhDosHeader->e_lfanew));
248
249 if(FileHeaderSize < cbFileHeaderOffsetSize)
250 pinhNtHeader = NULL;
251 else
252 {
253 /*
254 * we already know that Intsafe_CanOffsetPointer(FileHeader, FileHeaderSize),
255 * and FileHeaderSize >= cbFileHeaderOffsetSize, so this holds true too
256 */
257 ASSERT(Intsafe_CanOffsetPointer(FileHeader, pidhDosHeader->e_lfanew));
258 pinhNtHeader = (PVOID)((UINT_PTR)FileHeader + pidhDosHeader->e_lfanew);
259 }
260
261 /*
262 * the buffer doesn't contain the NT file header, or the alignment is wrong: we
263 * need to read the header from the file
264 */
265 if(FileHeaderSize < cbFileHeaderOffsetSize ||
266 (UINT_PTR)pinhNtHeader % TYPE_ALIGNMENT(IMAGE_NT_HEADERS32) != 0)
267 {
268 ULONG cbNtHeaderSize;
269 ULONG cbReadSize;
270 PVOID pData;
271
272 l_ReadHeaderFromFile:
273 cbNtHeaderSize = 0;
274 lnOffset.QuadPart = pidhDosHeader->e_lfanew;
275
276 /* read the header from the file */
277 nStatus = ReadFileCb(File, &lnOffset, sizeof(IMAGE_NT_HEADERS64), &pData, &pBuffer, &cbReadSize);
278
279 if(!NT_SUCCESS(nStatus))
280 DIE(("ReadFile failed, status %08X\n", nStatus));
281
282 ASSERT(pData);
283 ASSERT(pBuffer);
284 ASSERT(cbReadSize > 0);
285
286 nStatus = STATUS_INVALID_IMAGE_FORMAT;
287
288 /* the buffer doesn't contain the file header */
289 if(cbReadSize < RTL_SIZEOF_THROUGH_FIELD(IMAGE_NT_HEADERS32, FileHeader))
290 DIE(("The file doesn't contain the PE file header\n"));
291
292 pinhNtHeader = pData;
293
294 /* object still not aligned: copy it to the beginning of the buffer */
295 if((UINT_PTR)pinhNtHeader % TYPE_ALIGNMENT(IMAGE_NT_HEADERS32) != 0)
296 {
297 ASSERT((UINT_PTR)pBuffer % TYPE_ALIGNMENT(IMAGE_NT_HEADERS32) == 0);
298 RtlMoveMemory(pBuffer, pData, cbReadSize);
299 pinhNtHeader = pBuffer;
300 }
301
302 /* invalid NT header */
303 nStatus = STATUS_INVALID_IMAGE_PROTECT;
304
305 if(pinhNtHeader->Signature != IMAGE_NT_SIGNATURE)
306 DIE(("The file isn't a PE executable, Signature is %X\n", pinhNtHeader->Signature));
307
308 nStatus = STATUS_INVALID_IMAGE_FORMAT;
309
310 if(!Intsafe_AddULong32(&cbNtHeaderSize, pinhNtHeader->FileHeader.SizeOfOptionalHeader, FIELD_OFFSET(IMAGE_NT_HEADERS32, OptionalHeader)))
311 DIE(("The full NT header is too large\n"));
312
313 /* the buffer doesn't contain the whole NT header */
314 if(cbReadSize < cbNtHeaderSize)
315 DIE(("The file doesn't contain the full NT header\n"));
316 }
317 else
318 {
319 ULONG cbOptHeaderOffsetSize = 0;
320
321 nStatus = STATUS_INVALID_IMAGE_FORMAT;
322
323 /* don't trust an invalid NT header */
324 if(pinhNtHeader->Signature != IMAGE_NT_SIGNATURE)
325 DIE(("The file isn't a PE executable, Signature is %X\n", pinhNtHeader->Signature));
326
327 if(!Intsafe_AddULong32(&cbOptHeaderOffsetSize, pidhDosHeader->e_lfanew, FIELD_OFFSET(IMAGE_NT_HEADERS32, OptionalHeader)))
328 DIE(("The DOS stub is too large, e_lfanew is %X\n", pidhDosHeader->e_lfanew));
329
330 if(!Intsafe_AddULong32(&cbOptHeaderOffsetSize, cbOptHeaderOffsetSize, pinhNtHeader->FileHeader.SizeOfOptionalHeader))
331 DIE(("The NT header is too large, SizeOfOptionalHeader is %X\n", pinhNtHeader->FileHeader.SizeOfOptionalHeader));
332
333 /* the buffer doesn't contain the whole NT header: read it from the file */
334 if(cbOptHeaderOffsetSize > FileHeaderSize)
335 goto l_ReadHeaderFromFile;
336 }
337
338 /* read information from the NT header */
339 piohOptHeader = &pinhNtHeader->OptionalHeader;
340 cbOptHeaderSize = pinhNtHeader->FileHeader.SizeOfOptionalHeader;
341
342 nStatus = STATUS_INVALID_IMAGE_FORMAT;
343
344 if(!RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, Magic))
345 DIE(("The optional header doesn't contain the Magic field, SizeOfOptionalHeader is %X\n", cbOptHeaderSize));
346
347 /* ASSUME: RtlZeroMemory(ImageSectionObject, sizeof(*ImageSectionObject)); */
348
349 switch(piohOptHeader->Magic)
350 {
351 case IMAGE_NT_OPTIONAL_HDR32_MAGIC:
352 case IMAGE_NT_OPTIONAL_HDR64_MAGIC:
353 break;
354
355 default:
356 DIE(("Unrecognized optional header, Magic is %X\n", piohOptHeader->Magic));
357 }
358
359 if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SectionAlignment) &&
360 RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, FileAlignment))
361 {
362 /* See [1], section 3.4.2 */
363 if(piohOptHeader->SectionAlignment < PAGE_SIZE)
364 {
365 if(piohOptHeader->FileAlignment != piohOptHeader->SectionAlignment)
366 DIE(("Sections aren't page-aligned and the file alignment isn't the same\n"));
367 }
368 else if(piohOptHeader->SectionAlignment < piohOptHeader->FileAlignment)
369 DIE(("The section alignment is smaller than the file alignment\n"));
370
371 nSectionAlignment = piohOptHeader->SectionAlignment;
372 nFileAlignment = piohOptHeader->FileAlignment;
373
374 if(!IsPowerOf2(nSectionAlignment) || !IsPowerOf2(nFileAlignment))
375 DIE(("The section alignment (%u) and file alignment (%u) aren't both powers of 2\n", nSectionAlignment, nFileAlignment));
376 }
377 else
378 {
379 nSectionAlignment = PAGE_SIZE;
380 nFileAlignment = PAGE_SIZE;
381 }
382
383 ASSERT(IsPowerOf2(nSectionAlignment));
384 ASSERT(IsPowerOf2(nFileAlignment));
385
386 switch(piohOptHeader->Magic)
387 {
388 /* PE32 */
389 case IMAGE_NT_OPTIONAL_HDR32_MAGIC:
390 {
391 if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, ImageBase))
392 ImageSectionObject->ImageBase = piohOptHeader->ImageBase;
393
394 if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SizeOfImage))
395 ImageSectionObject->ImageSize = piohOptHeader->SizeOfImage;
396
397 if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SizeOfStackReserve))
398 ImageSectionObject->StackReserve = piohOptHeader->SizeOfStackReserve;
399
400 if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SizeOfStackCommit))
401 ImageSectionObject->StackCommit = piohOptHeader->SizeOfStackCommit;
402
403 break;
404 }
405
406 /* PE32+ */
407 case IMAGE_NT_OPTIONAL_HDR64_MAGIC:
408 {
409 const IMAGE_OPTIONAL_HEADER64 * pioh64OptHeader;
410
411 pioh64OptHeader = (const IMAGE_OPTIONAL_HEADER64 *)piohOptHeader;
412
413 if(RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, ImageBase))
414 {
415 if(pioh64OptHeader->ImageBase > MAXULONG_PTR)
416 DIE(("ImageBase exceeds the address space\n"));
417
418 ImageSectionObject->ImageBase = (ULONG_PTR)pioh64OptHeader->ImageBase;
419 }
420
421 if(RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, SizeOfImage))
422 {
423 if(pioh64OptHeader->SizeOfImage > MAXULONG_PTR)
424 DIE(("SizeOfImage exceeds the address space\n"));
425
426 ImageSectionObject->ImageSize = pioh64OptHeader->SizeOfImage;
427 }
428
429 if(RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, SizeOfStackReserve))
430 {
431 if(pioh64OptHeader->SizeOfStackReserve > MAXULONG_PTR)
432 DIE(("SizeOfStackReserve exceeds the address space\n"));
433
434 ImageSectionObject->StackReserve = pioh64OptHeader->SizeOfStackReserve;
435 }
436
437 if(RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, SizeOfStackCommit))
438 {
439 if(pioh64OptHeader->SizeOfStackCommit > MAXULONG_PTR)
440 DIE(("SizeOfStackCommit exceeds the address space\n"));
441
442 ImageSectionObject->StackCommit = pioh64OptHeader->SizeOfStackCommit;
443 }
444
445 break;
446 }
447 }
448
449 /* [1], section 3.4.2 */
450 if((ULONG_PTR)ImageSectionObject->ImageBase % 0x10000)
451 DIE(("ImageBase is not aligned on a 64KB boundary"));
452
453 if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, Subsystem))
454 {
455 ImageSectionObject->Subsystem = piohOptHeader->Subsystem;
456
457 if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, MinorSubsystemVersion) &&
458 RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, MajorSubsystemVersion))
459 {
460 ImageSectionObject->MinorSubsystemVersion = piohOptHeader->MinorSubsystemVersion;
461 ImageSectionObject->MajorSubsystemVersion = piohOptHeader->MajorSubsystemVersion;
462 }
463 }
464
465 if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, AddressOfEntryPoint))
466 {
467 ImageSectionObject->EntryPoint = piohOptHeader->ImageBase +
468 piohOptHeader->AddressOfEntryPoint;
469 }
470
471 if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SizeOfCode))
472 ImageSectionObject->Executable = piohOptHeader->SizeOfCode != 0;
473 else
474 ImageSectionObject->Executable = TRUE;
475
476 ImageSectionObject->ImageCharacteristics = pinhNtHeader->FileHeader.Characteristics;
477 ImageSectionObject->Machine = pinhNtHeader->FileHeader.Machine;
478
479 /* SECTION HEADERS */
480 nStatus = STATUS_INVALID_IMAGE_FORMAT;
481
482 /* see [1], section 3.3 */
483 if(pinhNtHeader->FileHeader.NumberOfSections > 96)
484 DIE(("Too many sections, NumberOfSections is %u\n", pinhNtHeader->FileHeader.NumberOfSections));
485
486 /*
487 * the additional segment is for the file's headers. They need to be present for
488 * the benefit of the dynamic loader (to locate exports, defaults for thread
489 * parameters, resources, etc.)
490 */
491 ImageSectionObject->NrSegments = pinhNtHeader->FileHeader.NumberOfSections + 1;
492
493 /* file offset for the section headers */
494 if(!Intsafe_AddULong32(&cbSectionHeadersOffset, pidhDosHeader->e_lfanew, FIELD_OFFSET(IMAGE_NT_HEADERS32, OptionalHeader)))
495 DIE(("Offset overflow\n"));
496
497 if(!Intsafe_AddULong32(&cbSectionHeadersOffset, cbSectionHeadersOffset, pinhNtHeader->FileHeader.SizeOfOptionalHeader))
498 DIE(("Offset overflow\n"));
499
500 /* size of the section headers */
501 ASSERT(Intsafe_CanMulULong32(pinhNtHeader->FileHeader.NumberOfSections, sizeof(IMAGE_SECTION_HEADER)));
502 cbSectionHeadersSize = pinhNtHeader->FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER);
503
504 if(!Intsafe_AddULong32(&cbSectionHeadersOffsetSize, cbSectionHeadersOffset, cbSectionHeadersSize))
505 DIE(("Section headers too large\n"));
506
507 /* size of the executable's headers */
508 if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SizeOfHeaders))
509 {
510 // if(!IsAligned(piohOptHeader->SizeOfHeaders, nFileAlignment))
511 // DIE(("SizeOfHeaders is not aligned\n"));
512
513 if(cbSectionHeadersSize > piohOptHeader->SizeOfHeaders)
514 DIE(("The section headers overflow SizeOfHeaders\n"));
515
516 cbHeadersSize = piohOptHeader->SizeOfHeaders;
517 }
518 else if(!AlignUp(&cbHeadersSize, cbSectionHeadersOffsetSize, nFileAlignment))
519 DIE(("Overflow aligning the size of headers\n"));
520
521 if(pBuffer)
522 {
523 ExFreePool(pBuffer);
524 pBuffer = NULL;
525 }
526 /* WARNING: pinhNtHeader IS NO LONGER USABLE */
527 /* WARNING: piohOptHeader IS NO LONGER USABLE */
528 /* WARNING: pioh64OptHeader IS NO LONGER USABLE */
529
530 if(FileHeaderSize < cbSectionHeadersOffsetSize)
531 pishSectionHeaders = NULL;
532 else
533 {
534 /*
535 * we already know that Intsafe_CanOffsetPointer(FileHeader, FileHeaderSize),
536 * and FileHeaderSize >= cbSectionHeadersOffsetSize, so this holds true too
537 */
538 ASSERT(Intsafe_CanOffsetPointer(FileHeader, cbSectionHeadersOffset));
539 pishSectionHeaders = (PVOID)((UINT_PTR)FileHeader + cbSectionHeadersOffset);
540 }
541
542 /*
543 * the buffer doesn't contain the section headers, or the alignment is wrong:
544 * read the headers from the file
545 */
546 if(FileHeaderSize < cbSectionHeadersOffsetSize ||
547 (UINT_PTR)pishSectionHeaders % TYPE_ALIGNMENT(IMAGE_SECTION_HEADER) != 0)
548 {
549 PVOID pData;
550 ULONG cbReadSize;
551
552 lnOffset.QuadPart = cbSectionHeadersOffset;
553
554 /* read the header from the file */
555 nStatus = ReadFileCb(File, &lnOffset, cbSectionHeadersSize, &pData, &pBuffer, &cbReadSize);
556
557 if(!NT_SUCCESS(nStatus))
558 DIE(("ReadFile failed with status %08X\n", nStatus));
559
560 ASSERT(pData);
561 ASSERT(pBuffer);
562 ASSERT(cbReadSize > 0);
563
564 nStatus = STATUS_INVALID_IMAGE_FORMAT;
565
566 /* the buffer doesn't contain all the section headers */
567 if(cbReadSize < cbSectionHeadersSize)
568 DIE(("The file doesn't contain all of the section headers\n"));
569
570 pishSectionHeaders = pData;
571
572 /* object still not aligned: copy it to the beginning of the buffer */
573 if((UINT_PTR)pishSectionHeaders % TYPE_ALIGNMENT(IMAGE_SECTION_HEADER) != 0)
574 {
575 ASSERT((UINT_PTR)pBuffer % TYPE_ALIGNMENT(IMAGE_SECTION_HEADER) == 0);
576 RtlMoveMemory(pBuffer, pData, cbReadSize);
577 pishSectionHeaders = pBuffer;
578 }
579 }
580
581 /* SEGMENTS */
582 /* allocate the segments */
583 nStatus = STATUS_INSUFFICIENT_RESOURCES;
584 ImageSectionObject->Segments = AllocateSegmentsCb(ImageSectionObject->NrSegments);
585
586 if(ImageSectionObject->Segments == NULL)
587 DIE(("AllocateSegments failed\n"));
588
589 /* initialize the headers segment */
590 pssSegments = ImageSectionObject->Segments;
591
592 // ASSERT(IsAligned(cbHeadersSize, nFileAlignment));
593
594 if(!AlignUp(&nFileSizeOfHeaders, cbHeadersSize, nFileAlignment))
595 DIE(("Cannot align the size of the section headers\n"));
596
597 if(!AlignUp(&nPrevVirtualEndOfSegment, cbHeadersSize, nSectionAlignment))
598 DIE(("Cannot align the size of the section headers\n"));
599
600 pssSegments[0].FileOffset = 0;
601 pssSegments[0].Protection = PAGE_READONLY;
602 pssSegments[0].Length = nPrevVirtualEndOfSegment;
603 pssSegments[0].RawLength = nFileSizeOfHeaders;
604 pssSegments[0].VirtualAddress = 0;
605 pssSegments[0].Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA;
606 pssSegments[0].WriteCopy = TRUE;
607
608 /* skip the headers segment */
609 ++ pssSegments;
610
611 nStatus = STATUS_INVALID_IMAGE_FORMAT;
612
613 /* convert the executable sections into segments. See also [1], section 4 */
614 for(i = 0; i < ImageSectionObject->NrSegments - 1; ++ i)
615 {
616 ULONG nCharacteristics;
617
618 /* validate the alignment */
619 if(!IsAligned(pishSectionHeaders[i].VirtualAddress, nSectionAlignment))
620 DIE(("VirtualAddress[%u] is not aligned\n", i));
621
622 /* sections must be contiguous, ordered by base address and non-overlapping */
623 if(pishSectionHeaders[i].VirtualAddress != nPrevVirtualEndOfSegment)
624 DIE(("Memory gap between section %u and the previous\n", i));
625
626 /* ignore explicit BSS sections */
627 if(pishSectionHeaders[i].SizeOfRawData != 0)
628 {
629 /* validate the alignment */
630 #if 0
631 /* Yes, this should be a multiple of FileAlignment, but there's
632 * stuff out there that isn't. We can cope with that
633 */
634 if(!IsAligned(pishSectionHeaders[i].SizeOfRawData, nFileAlignment))
635 DIE(("SizeOfRawData[%u] is not aligned\n", i));
636 #endif
637
638 // if(!IsAligned(pishSectionHeaders[i].PointerToRawData, nFileAlignment))
639 // DIE(("PointerToRawData[%u] is not aligned\n", i));
640
641 /* conversion */
642 pssSegments[i].FileOffset = pishSectionHeaders[i].PointerToRawData;
643 pssSegments[i].RawLength = pishSectionHeaders[i].SizeOfRawData;
644 }
645 else
646 {
647 ASSERT(pssSegments[i].FileOffset == 0);
648 ASSERT(pssSegments[i].RawLength == 0);
649 }
650
651 ASSERT(Intsafe_CanAddLong64(pssSegments[i].FileOffset, pssSegments[i].RawLength));
652
653 nCharacteristics = pishSectionHeaders[i].Characteristics;
654
655 /* no explicit protection */
656 if((nCharacteristics & (IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE)) == 0)
657 {
658 if(nCharacteristics & IMAGE_SCN_CNT_CODE)
659 nCharacteristics |= IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ;
660
661 if(nCharacteristics & IMAGE_SCN_CNT_INITIALIZED_DATA)
662 nCharacteristics |= IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE;
663
664 if(nCharacteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA)
665 nCharacteristics |= IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE;
666 }
667
668 /* see table above */
669 pssSegments[i].Protection = SectionCharacteristicsToProtect[nCharacteristics >> 28];
670 pssSegments[i].WriteCopy = !(nCharacteristics & IMAGE_SCN_MEM_SHARED);
671
672 if(pishSectionHeaders[i].Misc.VirtualSize == 0 || pishSectionHeaders[i].Misc.VirtualSize < pishSectionHeaders[i].SizeOfRawData)
673 pssSegments[i].Length = pishSectionHeaders[i].SizeOfRawData;
674 else
675 pssSegments[i].Length = pishSectionHeaders[i].Misc.VirtualSize;
676
677 if(!AlignUp(&pssSegments[i].Length, pssSegments[i].Length, nSectionAlignment))
678 DIE(("Cannot align the virtual size of section %u\n", i));
679
680 ASSERT(IsAligned(pssSegments[i].Length, nSectionAlignment));
681
682 if(pssSegments[i].Length == 0)
683 DIE(("Virtual size of section %u is null\n", i));
684
685 pssSegments[i].VirtualAddress = pishSectionHeaders[i].VirtualAddress;
686 pssSegments[i].Characteristics = pishSectionHeaders[i].Characteristics;
687
688 /* ensure the memory image is no larger than 4GB */
689 if(!Intsafe_AddULong32(&nPrevVirtualEndOfSegment, pssSegments[i].VirtualAddress, pssSegments[i].Length))
690 DIE(("The image is larger than 4GB\n"));
691 }
692
693 /* spare our caller some work in validating the segments */
694 *Flags = EXEFMT_LOAD_ASSUME_SEGMENTS_SORTED | EXEFMT_LOAD_ASSUME_SEGMENTS_NO_OVERLAP;
695
696 if(nSectionAlignment >= PAGE_SIZE)
697 *Flags |= EXEFMT_LOAD_ASSUME_SEGMENTS_PAGE_ALIGNED;
698
699 /* Success */
700 nStatus = STATUS_ROS_EXEFMT_LOADED_FORMAT | EXEFMT_LOADED_PE32;
701
702 l_Return:
703 if(pBuffer)
704 ExFreePool(pBuffer);
705
706 return nStatus;
707 }
708
709 /* Note: Mmsp prefix denotes "Memory Manager Section Private". */
710
711 /*
712 * FUNCTION: Waits in kernel mode up to ten seconds for an MM_PAGEOP event.
713 * ARGUMENTS: PMM_PAGEOP which event we should wait for.
714 * RETURNS: Status of the wait.
715 */
716 static NTSTATUS
717 MmspWaitForPageOpCompletionEvent(PMM_PAGEOP PageOp)
718 {
719 LARGE_INTEGER Timeout;
720 #ifdef __GNUC__ /* TODO: Use other macro to check for suffix to use? */
721
722 Timeout.QuadPart = -100000000LL; // 10 sec
723 #else
724
725 Timeout.QuadPart = -100000000; // 10 sec
726 #endif
727
728 return KeWaitForSingleObject(&PageOp->CompletionEvent, 0, KernelMode, FALSE, &Timeout);
729 }
730
731
732 /*
733 * FUNCTION: Sets the page op completion event and releases the page op.
734 * ARGUMENTS: PMM_PAGEOP.
735 * RETURNS: In shorter time than it takes you to even read this
736 * description, so don't even think about geting a mug of coffee.
737 */
738 static void
739 MmspCompleteAndReleasePageOp(PMM_PAGEOP PageOp)
740 {
741 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
742 MmReleasePageOp(PageOp);
743 }
744
745
746 /*
747 * FUNCTION: Waits in kernel mode indefinitely for a file object lock.
748 * ARGUMENTS: PFILE_OBJECT to wait for.
749 * RETURNS: Status of the wait.
750 */
751 NTSTATUS
752 MmspWaitForFileLock(PFILE_OBJECT File)
753 {
754 return STATUS_SUCCESS;
755 //return KeWaitForSingleObject(&File->Lock, 0, KernelMode, FALSE, NULL);
756 }
757
758
759 VOID
760 MmFreePageTablesSectionSegment(PMM_SECTION_SEGMENT Segment)
761 {
762 ULONG i;
763 if (Segment->Length > NR_SECTION_PAGE_TABLES * PAGE_SIZE)
764 {
765 for (i = 0; i < NR_SECTION_PAGE_TABLES; i++)
766 {
767 if (Segment->PageDirectory.PageTables[i] != NULL)
768 {
769 ExFreePool(Segment->PageDirectory.PageTables[i]);
770 }
771 }
772 }
773 }
774
775 VOID
776 NTAPI
777 MmFreeSectionSegments(PFILE_OBJECT FileObject)
778 {
779 if (FileObject->SectionObjectPointer->ImageSectionObject != NULL)
780 {
781 PMM_IMAGE_SECTION_OBJECT ImageSectionObject;
782 PMM_SECTION_SEGMENT SectionSegments;
783 ULONG NrSegments;
784 ULONG i;
785
786 ImageSectionObject = (PMM_IMAGE_SECTION_OBJECT)FileObject->SectionObjectPointer->ImageSectionObject;
787 NrSegments = ImageSectionObject->NrSegments;
788 SectionSegments = ImageSectionObject->Segments;
789 for (i = 0; i < NrSegments; i++)
790 {
791 if (SectionSegments[i].ReferenceCount != 0)
792 {
793 DPRINT1("Image segment %d still referenced (was %d)\n", i,
794 SectionSegments[i].ReferenceCount);
795 KeBugCheck(MEMORY_MANAGEMENT);
796 }
797 MmFreePageTablesSectionSegment(&SectionSegments[i]);
798 }
799 ExFreePool(ImageSectionObject->Segments);
800 ExFreePool(ImageSectionObject);
801 FileObject->SectionObjectPointer->ImageSectionObject = NULL;
802 }
803 if (FileObject->SectionObjectPointer->DataSectionObject != NULL)
804 {
805 PMM_SECTION_SEGMENT Segment;
806
807 Segment = (PMM_SECTION_SEGMENT)FileObject->SectionObjectPointer->
808 DataSectionObject;
809
810 if (Segment->ReferenceCount != 0)
811 {
812 DPRINT1("Data segment still referenced\n");
813 KeBugCheck(MEMORY_MANAGEMENT);
814 }
815 MmFreePageTablesSectionSegment(Segment);
816 ExFreePool(Segment);
817 FileObject->SectionObjectPointer->DataSectionObject = NULL;
818 }
819 }
820
821 VOID
822 NTAPI
823 MmLockSectionSegment(PMM_SECTION_SEGMENT Segment)
824 {
825 ExAcquireFastMutex(&Segment->Lock);
826 }
827
828 VOID
829 NTAPI
830 MmUnlockSectionSegment(PMM_SECTION_SEGMENT Segment)
831 {
832 ExReleaseFastMutex(&Segment->Lock);
833 }
834
835 VOID
836 NTAPI
837 MmSetPageEntrySectionSegment(PMM_SECTION_SEGMENT Segment,
838 ULONG Offset,
839 ULONG Entry)
840 {
841 PSECTION_PAGE_TABLE Table;
842 ULONG DirectoryOffset;
843 ULONG TableOffset;
844
845 if (Segment->Length <= NR_SECTION_PAGE_TABLES * PAGE_SIZE)
846 {
847 Table = (PSECTION_PAGE_TABLE)&Segment->PageDirectory;
848 }
849 else
850 {
851 DirectoryOffset = PAGE_TO_SECTION_PAGE_DIRECTORY_OFFSET(Offset);
852 Table = Segment->PageDirectory.PageTables[DirectoryOffset];
853 if (Table == NULL)
854 {
855 Table =
856 Segment->PageDirectory.PageTables[DirectoryOffset] =
857 ExAllocatePoolWithTag(NonPagedPool, sizeof(SECTION_PAGE_TABLE),
858 TAG_SECTION_PAGE_TABLE);
859 if (Table == NULL)
860 {
861 KeBugCheck(MEMORY_MANAGEMENT);
862 }
863 memset(Table, 0, sizeof(SECTION_PAGE_TABLE));
864 DPRINT("Table %x\n", Table);
865 }
866 }
867 TableOffset = PAGE_TO_SECTION_PAGE_TABLE_OFFSET(Offset);
868 Table->Entry[TableOffset] = Entry;
869 }
870
871
872 ULONG
873 NTAPI
874 MmGetPageEntrySectionSegment(PMM_SECTION_SEGMENT Segment,
875 ULONG Offset)
876 {
877 PSECTION_PAGE_TABLE Table;
878 ULONG Entry;
879 ULONG DirectoryOffset;
880 ULONG TableOffset;
881
882 DPRINT("MmGetPageEntrySection(Segment %x, Offset %x)\n", Segment, Offset);
883
884 if (Segment->Length <= NR_SECTION_PAGE_TABLES * PAGE_SIZE)
885 {
886 Table = (PSECTION_PAGE_TABLE)&Segment->PageDirectory;
887 }
888 else
889 {
890 DirectoryOffset = PAGE_TO_SECTION_PAGE_DIRECTORY_OFFSET(Offset);
891 Table = Segment->PageDirectory.PageTables[DirectoryOffset];
892 DPRINT("Table %x\n", Table);
893 if (Table == NULL)
894 {
895 return(0);
896 }
897 }
898 TableOffset = PAGE_TO_SECTION_PAGE_TABLE_OFFSET(Offset);
899 Entry = Table->Entry[TableOffset];
900 return(Entry);
901 }
902
903 VOID
904 NTAPI
905 MmSharePageEntrySectionSegment(PMM_SECTION_SEGMENT Segment,
906 ULONG Offset)
907 {
908 ULONG Entry;
909
910 Entry = MmGetPageEntrySectionSegment(Segment, Offset);
911 if (Entry == 0)
912 {
913 DPRINT1("Entry == 0 for MmSharePageEntrySectionSegment\n");
914 KeBugCheck(MEMORY_MANAGEMENT);
915 }
916 if (SHARE_COUNT_FROM_SSE(Entry) == MAX_SHARE_COUNT)
917 {
918 DPRINT1("Maximum share count reached\n");
919 KeBugCheck(MEMORY_MANAGEMENT);
920 }
921 if (IS_SWAP_FROM_SSE(Entry))
922 {
923 KeBugCheck(MEMORY_MANAGEMENT);
924 }
925 Entry = MAKE_SSE(PAGE_FROM_SSE(Entry), SHARE_COUNT_FROM_SSE(Entry) + 1);
926 MmSetPageEntrySectionSegment(Segment, Offset, Entry);
927 }
928
929 BOOLEAN
930 NTAPI
931 MmUnsharePageEntrySectionSegment(PROS_SECTION_OBJECT Section,
932 PMM_SECTION_SEGMENT Segment,
933 ULONG Offset,
934 BOOLEAN Dirty,
935 BOOLEAN PageOut)
936 {
937 ULONG Entry;
938 BOOLEAN IsDirectMapped = FALSE;
939
940 Entry = MmGetPageEntrySectionSegment(Segment, Offset);
941 if (Entry == 0)
942 {
943 DPRINT1("Entry == 0 for MmUnsharePageEntrySectionSegment\n");
944 KeBugCheck(MEMORY_MANAGEMENT);
945 }
946 if (SHARE_COUNT_FROM_SSE(Entry) == 0)
947 {
948 DPRINT1("Zero share count for unshare\n");
949 KeBugCheck(MEMORY_MANAGEMENT);
950 }
951 if (IS_SWAP_FROM_SSE(Entry))
952 {
953 KeBugCheck(MEMORY_MANAGEMENT);
954 }
955 Entry = MAKE_SSE(PAGE_FROM_SSE(Entry), SHARE_COUNT_FROM_SSE(Entry) - 1);
956 /*
957 * If we reducing the share count of this entry to zero then set the entry
958 * to zero and tell the cache the page is no longer mapped.
959 */
960 if (SHARE_COUNT_FROM_SSE(Entry) == 0)
961 {
962 PFILE_OBJECT FileObject;
963 PBCB Bcb;
964 SWAPENTRY SavedSwapEntry;
965 PFN_NUMBER Page;
966 BOOLEAN IsImageSection;
967 ULONG FileOffset;
968
969 FileOffset = Offset + Segment->FileOffset;
970
971 IsImageSection = Section->AllocationAttributes & SEC_IMAGE ? TRUE : FALSE;
972
973 Page = PFN_FROM_SSE(Entry);
974 FileObject = Section->FileObject;
975 if (FileObject != NULL &&
976 !(Segment->Characteristics & IMAGE_SCN_MEM_SHARED))
977 {
978
979 if ((FileOffset % PAGE_SIZE) == 0 &&
980 (Offset + PAGE_SIZE <= Segment->RawLength || !IsImageSection))
981 {
982 NTSTATUS Status;
983 Bcb = FileObject->SectionObjectPointer->SharedCacheMap;
984 IsDirectMapped = TRUE;
985 #ifndef NEWCC
986 Status = CcRosUnmapCacheSegment(Bcb, FileOffset, Dirty);
987 #else
988 Status = STATUS_SUCCESS;
989 #endif
990 if (!NT_SUCCESS(Status))
991 {
992 DPRINT1("CcRosUnmapCacheSegment failed, status = %x\n", Status);
993 KeBugCheck(MEMORY_MANAGEMENT);
994 }
995 }
996 }
997
998 SavedSwapEntry = MmGetSavedSwapEntryPage(Page);
999 if (SavedSwapEntry == 0)
1000 {
1001 if (!PageOut &&
1002 ((Segment->Flags & MM_PAGEFILE_SEGMENT) ||
1003 (Segment->Characteristics & IMAGE_SCN_MEM_SHARED)))
1004 {
1005 /*
1006 * FIXME:
1007 * Try to page out this page and set the swap entry
1008 * within the section segment. There exist no rmap entry
1009 * for this page. The pager thread can't page out a
1010 * page without a rmap entry.
1011 */
1012 MmSetPageEntrySectionSegment(Segment, Offset, Entry);
1013 }
1014 else
1015 {
1016 MmSetPageEntrySectionSegment(Segment, Offset, 0);
1017 if (!IsDirectMapped)
1018 {
1019 MmReleasePageMemoryConsumer(MC_USER, Page);
1020 }
1021 }
1022 }
1023 else
1024 {
1025 if ((Segment->Flags & MM_PAGEFILE_SEGMENT) ||
1026 (Segment->Characteristics & IMAGE_SCN_MEM_SHARED))
1027 {
1028 if (!PageOut)
1029 {
1030 if (Dirty)
1031 {
1032 /*
1033 * FIXME:
1034 * We hold all locks. Nobody can do something with the current
1035 * process and the current segment (also not within an other process).
1036 */
1037 NTSTATUS Status;
1038 Status = MmWriteToSwapPage(SavedSwapEntry, Page);
1039 if (!NT_SUCCESS(Status))
1040 {
1041 DPRINT1("MM: Failed to write to swap page (Status was 0x%.8X)\n", Status);
1042 KeBugCheck(MEMORY_MANAGEMENT);
1043 }
1044 }
1045 MmSetPageEntrySectionSegment(Segment, Offset, MAKE_SWAP_SSE(SavedSwapEntry));
1046 MmSetSavedSwapEntryPage(Page, 0);
1047 }
1048 MmReleasePageMemoryConsumer(MC_USER, Page);
1049 }
1050 else
1051 {
1052 DPRINT1("Found a swapentry for a non private page in an image or data file sgment\n");
1053 KeBugCheck(MEMORY_MANAGEMENT);
1054 }
1055 }
1056 }
1057 else
1058 {
1059 MmSetPageEntrySectionSegment(Segment, Offset, Entry);
1060 }
1061 return(SHARE_COUNT_FROM_SSE(Entry) > 0);
1062 }
1063
1064 BOOLEAN MiIsPageFromCache(PMEMORY_AREA MemoryArea,
1065 ULONG SegOffset)
1066 {
1067 #ifndef NEWCC
1068 if (!(MemoryArea->Data.SectionData.Segment->Characteristics & IMAGE_SCN_MEM_SHARED))
1069 {
1070 PBCB Bcb;
1071 PCACHE_SEGMENT CacheSeg;
1072 Bcb = MemoryArea->Data.SectionData.Section->FileObject->SectionObjectPointer->SharedCacheMap;
1073 CacheSeg = CcRosLookupCacheSegment(Bcb, SegOffset + MemoryArea->Data.SectionData.Segment->FileOffset);
1074 if (CacheSeg)
1075 {
1076 CcRosReleaseCacheSegment(Bcb, CacheSeg, CacheSeg->Valid, FALSE, TRUE);
1077 return TRUE;
1078 }
1079 }
1080 #endif
1081 return FALSE;
1082 }
1083
1084 NTSTATUS
1085 NTAPI
1086 MiCopyFromUserPage(PFN_NUMBER DestPage, PVOID SourceAddress)
1087 {
1088 PEPROCESS Process;
1089 KIRQL Irql;
1090 PVOID TempAddress;
1091
1092 Process = PsGetCurrentProcess();
1093 TempAddress = MiMapPageInHyperSpace(Process, DestPage, &Irql);
1094 if (TempAddress == NULL)
1095 {
1096 return(STATUS_NO_MEMORY);
1097 }
1098 memcpy(TempAddress, SourceAddress, PAGE_SIZE);
1099 MiUnmapPageInHyperSpace(Process, TempAddress, Irql);
1100 return(STATUS_SUCCESS);
1101 }
1102
1103 #ifndef NEWCC
1104 NTSTATUS
1105 NTAPI
1106 MiReadPage(PMEMORY_AREA MemoryArea,
1107 ULONG SegOffset,
1108 PPFN_NUMBER Page)
1109 /*
1110 * FUNCTION: Read a page for a section backed memory area.
1111 * PARAMETERS:
1112 * MemoryArea - Memory area to read the page for.
1113 * Offset - Offset of the page to read.
1114 * Page - Variable that receives a page contains the read data.
1115 */
1116 {
1117 ULONG BaseOffset;
1118 ULONG FileOffset;
1119 PVOID BaseAddress;
1120 BOOLEAN UptoDate;
1121 PCACHE_SEGMENT CacheSeg;
1122 PFILE_OBJECT FileObject;
1123 NTSTATUS Status;
1124 ULONG RawLength;
1125 PBCB Bcb;
1126 BOOLEAN IsImageSection;
1127 ULONG Length;
1128
1129 FileObject = MemoryArea->Data.SectionData.Section->FileObject;
1130 Bcb = FileObject->SectionObjectPointer->SharedCacheMap;
1131 RawLength = MemoryArea->Data.SectionData.Segment->RawLength;
1132 FileOffset = SegOffset + MemoryArea->Data.SectionData.Segment->FileOffset;
1133 IsImageSection = MemoryArea->Data.SectionData.Section->AllocationAttributes & SEC_IMAGE ? TRUE : FALSE;
1134
1135 ASSERT(Bcb);
1136
1137 DPRINT("%S %x\n", FileObject->FileName.Buffer, FileOffset);
1138
1139 /*
1140 * If the file system is letting us go directly to the cache and the
1141 * memory area was mapped at an offset in the file which is page aligned
1142 * then get the related cache segment.
1143 */
1144 if ((FileOffset % PAGE_SIZE) == 0 &&
1145 (SegOffset + PAGE_SIZE <= RawLength || !IsImageSection) &&
1146 !(MemoryArea->Data.SectionData.Segment->Characteristics & IMAGE_SCN_MEM_SHARED))
1147 {
1148
1149 /*
1150 * Get the related cache segment; we use a lower level interface than
1151 * filesystems do because it is safe for us to use an offset with a
1152 * alignment less than the file system block size.
1153 */
1154 Status = CcRosGetCacheSegment(Bcb,
1155 FileOffset,
1156 &BaseOffset,
1157 &BaseAddress,
1158 &UptoDate,
1159 &CacheSeg);
1160 if (!NT_SUCCESS(Status))
1161 {
1162 return(Status);
1163 }
1164 if (!UptoDate)
1165 {
1166 /*
1167 * If the cache segment isn't up to date then call the file
1168 * system to read in the data.
1169 */
1170 Status = ReadCacheSegment(CacheSeg);
1171 if (!NT_SUCCESS(Status))
1172 {
1173 CcRosReleaseCacheSegment(Bcb, CacheSeg, FALSE, FALSE, FALSE);
1174 return Status;
1175 }
1176 }
1177 /*
1178 * Retrieve the page from the cache segment that we actually want.
1179 */
1180 (*Page) = MmGetPhysicalAddress((char*)BaseAddress +
1181 FileOffset - BaseOffset).LowPart >> PAGE_SHIFT;
1182
1183 CcRosReleaseCacheSegment(Bcb, CacheSeg, TRUE, FALSE, TRUE);
1184 }
1185 else
1186 {
1187 PEPROCESS Process;
1188 KIRQL Irql;
1189 PVOID PageAddr;
1190 ULONG CacheSegOffset;
1191
1192 /*
1193 * Allocate a page, this is rather complicated by the possibility
1194 * we might have to move other things out of memory
1195 */
1196 MI_SET_USAGE(MI_USAGE_SECTION);
1197 MI_SET_PROCESS2(PsGetCurrentProcess()->ImageFileName);
1198 Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, Page);
1199 if (!NT_SUCCESS(Status))
1200 {
1201 return(Status);
1202 }
1203 Status = CcRosGetCacheSegment(Bcb,
1204 FileOffset,
1205 &BaseOffset,
1206 &BaseAddress,
1207 &UptoDate,
1208 &CacheSeg);
1209 if (!NT_SUCCESS(Status))
1210 {
1211 return(Status);
1212 }
1213 if (!UptoDate)
1214 {
1215 /*
1216 * If the cache segment isn't up to date then call the file
1217 * system to read in the data.
1218 */
1219 Status = ReadCacheSegment(CacheSeg);
1220 if (!NT_SUCCESS(Status))
1221 {
1222 CcRosReleaseCacheSegment(Bcb, CacheSeg, FALSE, FALSE, FALSE);
1223 return Status;
1224 }
1225 }
1226
1227 Process = PsGetCurrentProcess();
1228 PageAddr = MiMapPageInHyperSpace(Process, *Page, &Irql);
1229 CacheSegOffset = BaseOffset + CacheSeg->Bcb->CacheSegmentSize - FileOffset;
1230 Length = RawLength - SegOffset;
1231 if (Length <= CacheSegOffset && Length <= PAGE_SIZE)
1232 {
1233 memcpy(PageAddr, (char*)BaseAddress + FileOffset - BaseOffset, Length);
1234 }
1235 else if (CacheSegOffset >= PAGE_SIZE)
1236 {
1237 memcpy(PageAddr, (char*)BaseAddress + FileOffset - BaseOffset, PAGE_SIZE);
1238 }
1239 else
1240 {
1241 memcpy(PageAddr, (char*)BaseAddress + FileOffset - BaseOffset, CacheSegOffset);
1242 MiUnmapPageInHyperSpace(Process, PageAddr, Irql);
1243 CcRosReleaseCacheSegment(Bcb, CacheSeg, TRUE, FALSE, FALSE);
1244 Status = CcRosGetCacheSegment(Bcb,
1245 FileOffset + CacheSegOffset,
1246 &BaseOffset,
1247 &BaseAddress,
1248 &UptoDate,
1249 &CacheSeg);
1250 if (!NT_SUCCESS(Status))
1251 {
1252 return(Status);
1253 }
1254 if (!UptoDate)
1255 {
1256 /*
1257 * If the cache segment isn't up to date then call the file
1258 * system to read in the data.
1259 */
1260 Status = ReadCacheSegment(CacheSeg);
1261 if (!NT_SUCCESS(Status))
1262 {
1263 CcRosReleaseCacheSegment(Bcb, CacheSeg, FALSE, FALSE, FALSE);
1264 return Status;
1265 }
1266 }
1267 PageAddr = MiMapPageInHyperSpace(Process, *Page, &Irql);
1268 if (Length < PAGE_SIZE)
1269 {
1270 memcpy((char*)PageAddr + CacheSegOffset, BaseAddress, Length - CacheSegOffset);
1271 }
1272 else
1273 {
1274 memcpy((char*)PageAddr + CacheSegOffset, BaseAddress, PAGE_SIZE - CacheSegOffset);
1275 }
1276 }
1277 MiUnmapPageInHyperSpace(Process, PageAddr, Irql);
1278 CcRosReleaseCacheSegment(Bcb, CacheSeg, TRUE, FALSE, FALSE);
1279 }
1280 return(STATUS_SUCCESS);
1281 }
1282 #else
1283 NTSTATUS
1284 NTAPI
1285 MiReadPage(PMEMORY_AREA MemoryArea,
1286 ULONG SegOffset,
1287 PPFN_NUMBER Page)
1288 /*
1289 * FUNCTION: Read a page for a section backed memory area.
1290 * PARAMETERS:
1291 * MemoryArea - Memory area to read the page for.
1292 * Offset - Offset of the page to read.
1293 * Page - Variable that receives a page contains the read data.
1294 */
1295 {
1296 MM_REQUIRED_RESOURCES Resources = { };
1297
1298 Resources.Context = MemoryArea->Data.SectionData.Section->FileObject;
1299 Resources.FileOffset.QuadPart = SegOffset +
1300 MemoryArea->Data.SectionData.Segment->FileOffset;
1301 Resources.Consumer = MC_USER;
1302 Resources.Amount = PAGE_SIZE;
1303
1304 DPRINT1("%S, offset %x, len %d, page %x\n", ((PFILE_OBJECT)Resources.Context)->FileName.Buffer, Resources.FileOffset.LowPart, Resources.Amount, Resources.Page[0]);
1305
1306 NTSTATUS Status = MiReadFilePage(NULL, NULL, &Resources);
1307 *Page = Resources.Page[0];
1308 return Status;
1309 }
1310 #endif
1311
1312 NTSTATUS
1313 NTAPI
1314 MmNotPresentFaultSectionView(PMMSUPPORT AddressSpace,
1315 MEMORY_AREA* MemoryArea,
1316 PVOID Address,
1317 BOOLEAN Locked)
1318 {
1319 ULONG Offset;
1320 PFN_NUMBER Page;
1321 NTSTATUS Status;
1322 PVOID PAddress;
1323 PROS_SECTION_OBJECT Section;
1324 PMM_SECTION_SEGMENT Segment;
1325 ULONG Entry;
1326 ULONG Entry1;
1327 ULONG Attributes;
1328 PMM_PAGEOP PageOp;
1329 PMM_REGION Region;
1330 BOOLEAN HasSwapEntry;
1331 PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
1332
1333 /*
1334 * There is a window between taking the page fault and locking the
1335 * address space when another thread could load the page so we check
1336 * that.
1337 */
1338 if (MmIsPagePresent(Process, Address))
1339 {
1340 return(STATUS_SUCCESS);
1341 }
1342
1343 PAddress = MM_ROUND_DOWN(Address, PAGE_SIZE);
1344 Offset = (ULONG_PTR)PAddress - (ULONG_PTR)MemoryArea->StartingAddress
1345 + MemoryArea->Data.SectionData.ViewOffset;
1346
1347 Segment = MemoryArea->Data.SectionData.Segment;
1348 Section = MemoryArea->Data.SectionData.Section;
1349 Region = MmFindRegion(MemoryArea->StartingAddress,
1350 &MemoryArea->Data.SectionData.RegionListHead,
1351 Address, NULL);
1352 /*
1353 * Lock the segment
1354 */
1355 MmLockSectionSegment(Segment);
1356
1357 /*
1358 * Check if this page needs to be mapped COW
1359 */
1360 if ((Segment->WriteCopy) &&
1361 (Region->Protect == PAGE_READWRITE ||
1362 Region->Protect == PAGE_EXECUTE_READWRITE))
1363 {
1364 Attributes = Region->Protect == PAGE_READWRITE ? PAGE_READONLY : PAGE_EXECUTE_READ;
1365 }
1366 else
1367 {
1368 Attributes = Region->Protect;
1369 }
1370
1371 /*
1372 * Get or create a page operation descriptor
1373 */
1374 PageOp = MmGetPageOp(MemoryArea, NULL, 0, Segment, Offset, MM_PAGEOP_PAGEIN, FALSE);
1375 if (PageOp == NULL)
1376 {
1377 DPRINT1("MmGetPageOp failed\n");
1378 KeBugCheck(MEMORY_MANAGEMENT);
1379 }
1380
1381 /*
1382 * Check if someone else is already handling this fault, if so wait
1383 * for them
1384 */
1385 if (PageOp->Thread != PsGetCurrentThread())
1386 {
1387 MmUnlockSectionSegment(Segment);
1388 MmUnlockAddressSpace(AddressSpace);
1389 Status = MmspWaitForPageOpCompletionEvent(PageOp);
1390 /*
1391 * Check for various strange conditions
1392 */
1393 if (Status != STATUS_SUCCESS)
1394 {
1395 DPRINT1("Failed to wait for page op, status = %x\n", Status);
1396 KeBugCheck(MEMORY_MANAGEMENT);
1397 }
1398 if (PageOp->Status == STATUS_PENDING)
1399 {
1400 DPRINT1("Woke for page op before completion\n");
1401 KeBugCheck(MEMORY_MANAGEMENT);
1402 }
1403 MmLockAddressSpace(AddressSpace);
1404 /*
1405 * If this wasn't a pagein then restart the operation
1406 */
1407 if (PageOp->OpType != MM_PAGEOP_PAGEIN)
1408 {
1409 MmspCompleteAndReleasePageOp(PageOp);
1410 DPRINT("Address 0x%.8X\n", Address);
1411 return(STATUS_MM_RESTART_OPERATION);
1412 }
1413
1414 /*
1415 * If the thread handling this fault has failed then we don't retry
1416 */
1417 if (!NT_SUCCESS(PageOp->Status))
1418 {
1419 Status = PageOp->Status;
1420 MmspCompleteAndReleasePageOp(PageOp);
1421 DPRINT("Address 0x%.8X\n", Address);
1422 return(Status);
1423 }
1424 MmLockSectionSegment(Segment);
1425 /*
1426 * If the completed fault was for another address space then set the
1427 * page in this one.
1428 */
1429 if (!MmIsPagePresent(Process, Address))
1430 {
1431 Entry = MmGetPageEntrySectionSegment(Segment, Offset);
1432 HasSwapEntry = MmIsPageSwapEntry(Process, (PVOID)PAddress);
1433
1434 if (PAGE_FROM_SSE(Entry) == 0 || HasSwapEntry)
1435 {
1436 /*
1437 * The page was a private page in another or in our address space
1438 */
1439 MmUnlockSectionSegment(Segment);
1440 MmspCompleteAndReleasePageOp(PageOp);
1441 return(STATUS_MM_RESTART_OPERATION);
1442 }
1443
1444 Page = PFN_FROM_SSE(Entry);
1445
1446 MmSharePageEntrySectionSegment(Segment, Offset);
1447
1448 /* FIXME: Should we call MmCreateVirtualMappingUnsafe if
1449 * (Section->AllocationAttributes & SEC_PHYSICALMEMORY) is true?
1450 */
1451 Status = MmCreateVirtualMapping(Process,
1452 Address,
1453 Attributes,
1454 &Page,
1455 1);
1456 if (!NT_SUCCESS(Status))
1457 {
1458 DPRINT1("Unable to create virtual mapping\n");
1459 KeBugCheck(MEMORY_MANAGEMENT);
1460 }
1461 MmInsertRmap(Page, Process, (PVOID)PAddress);
1462 }
1463 MmUnlockSectionSegment(Segment);
1464 PageOp->Status = STATUS_SUCCESS;
1465 MmspCompleteAndReleasePageOp(PageOp);
1466 DPRINT("Address 0x%.8X\n", Address);
1467 return(STATUS_SUCCESS);
1468 }
1469
1470 HasSwapEntry = MmIsPageSwapEntry(Process, (PVOID)PAddress);
1471 if (HasSwapEntry)
1472 {
1473 /*
1474 * Must be private page we have swapped out.
1475 */
1476 SWAPENTRY SwapEntry;
1477
1478 /*
1479 * Sanity check
1480 */
1481 if (Segment->Flags & MM_PAGEFILE_SEGMENT)
1482 {
1483 DPRINT1("Found a swaped out private page in a pagefile section.\n");
1484 KeBugCheck(MEMORY_MANAGEMENT);
1485 }
1486
1487 MmUnlockSectionSegment(Segment);
1488 MmDeletePageFileMapping(Process, (PVOID)PAddress, &SwapEntry);
1489
1490 MmUnlockAddressSpace(AddressSpace);
1491 MI_SET_USAGE(MI_USAGE_SECTION);
1492 if (Process) MI_SET_PROCESS2(Process->ImageFileName);
1493 if (!Process) MI_SET_PROCESS2("Kernel Section");
1494 Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &Page);
1495 if (!NT_SUCCESS(Status))
1496 {
1497 KeBugCheck(MEMORY_MANAGEMENT);
1498 }
1499
1500 Status = MmReadFromSwapPage(SwapEntry, Page);
1501 if (!NT_SUCCESS(Status))
1502 {
1503 DPRINT1("MmReadFromSwapPage failed, status = %x\n", Status);
1504 KeBugCheck(MEMORY_MANAGEMENT);
1505 }
1506 MmLockAddressSpace(AddressSpace);
1507 Status = MmCreateVirtualMapping(Process,
1508 Address,
1509 Region->Protect,
1510 &Page,
1511 1);
1512 if (!NT_SUCCESS(Status))
1513 {
1514 DPRINT("MmCreateVirtualMapping failed, not out of memory\n");
1515 KeBugCheck(MEMORY_MANAGEMENT);
1516 return(Status);
1517 }
1518
1519 /*
1520 * Store the swap entry for later use.
1521 */
1522 MmSetSavedSwapEntryPage(Page, SwapEntry);
1523
1524 /*
1525 * Add the page to the process's working set
1526 */
1527 MmInsertRmap(Page, Process, (PVOID)PAddress);
1528
1529 /*
1530 * Finish the operation
1531 */
1532 PageOp->Status = STATUS_SUCCESS;
1533 MmspCompleteAndReleasePageOp(PageOp);
1534 DPRINT("Address 0x%.8X\n", Address);
1535 return(STATUS_SUCCESS);
1536 }
1537
1538 /*
1539 * Satisfying a page fault on a map of /Device/PhysicalMemory is easy
1540 */
1541 if (Section->AllocationAttributes & SEC_PHYSICALMEMORY)
1542 {
1543 MmUnlockSectionSegment(Segment);
1544 /*
1545 * Just map the desired physical page
1546 */
1547 Page = Offset >> PAGE_SHIFT;
1548 Status = MmCreateVirtualMappingUnsafe(Process,
1549 Address,
1550 Region->Protect,
1551 &Page,
1552 1);
1553 if (!NT_SUCCESS(Status))
1554 {
1555 DPRINT("MmCreateVirtualMappingUnsafe failed, not out of memory\n");
1556 KeBugCheck(MEMORY_MANAGEMENT);
1557 return(Status);
1558 }
1559
1560 /*
1561 * Cleanup and release locks
1562 */
1563 PageOp->Status = STATUS_SUCCESS;
1564 MmspCompleteAndReleasePageOp(PageOp);
1565 DPRINT("Address 0x%.8X\n", Address);
1566 return(STATUS_SUCCESS);
1567 }
1568
1569 /*
1570 * Map anonymous memory for BSS sections
1571 */
1572 if (Segment->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA)
1573 {
1574 MmUnlockSectionSegment(Segment);
1575 MI_SET_USAGE(MI_USAGE_SECTION);
1576 if (Process) MI_SET_PROCESS2(Process->ImageFileName);
1577 if (!Process) MI_SET_PROCESS2("Kernel Section");
1578 Status = MmRequestPageMemoryConsumer(MC_USER, FALSE, &Page);
1579 if (!NT_SUCCESS(Status))
1580 {
1581 MmUnlockAddressSpace(AddressSpace);
1582 Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &Page);
1583 MmLockAddressSpace(AddressSpace);
1584 }
1585 if (!NT_SUCCESS(Status))
1586 {
1587 KeBugCheck(MEMORY_MANAGEMENT);
1588 }
1589 Status = MmCreateVirtualMapping(Process,
1590 Address,
1591 Region->Protect,
1592 &Page,
1593 1);
1594 if (!NT_SUCCESS(Status))
1595 {
1596 DPRINT("MmCreateVirtualMapping failed, not out of memory\n");
1597 KeBugCheck(MEMORY_MANAGEMENT);
1598 return(Status);
1599 }
1600 MmInsertRmap(Page, Process, (PVOID)PAddress);
1601
1602 /*
1603 * Cleanup and release locks
1604 */
1605 PageOp->Status = STATUS_SUCCESS;
1606 MmspCompleteAndReleasePageOp(PageOp);
1607 DPRINT("Address 0x%.8X\n", Address);
1608 return(STATUS_SUCCESS);
1609 }
1610
1611 /*
1612 * Get the entry corresponding to the offset within the section
1613 */
1614 Entry = MmGetPageEntrySectionSegment(Segment, Offset);
1615
1616 if (Entry == 0)
1617 {
1618 /*
1619 * If the entry is zero (and it can't change because we have
1620 * locked the segment) then we need to load the page.
1621 */
1622
1623 /*
1624 * Release all our locks and read in the page from disk
1625 */
1626 MmUnlockSectionSegment(Segment);
1627 MmUnlockAddressSpace(AddressSpace);
1628
1629 if ((Segment->Flags & MM_PAGEFILE_SEGMENT) ||
1630 (Offset >= PAGE_ROUND_UP(Segment->RawLength) && Section->AllocationAttributes & SEC_IMAGE))
1631 {
1632 MI_SET_USAGE(MI_USAGE_SECTION);
1633 if (Process) MI_SET_PROCESS2(Process->ImageFileName);
1634 if (!Process) MI_SET_PROCESS2("Kernel Section");
1635 Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &Page);
1636 if (!NT_SUCCESS(Status))
1637 {
1638 DPRINT1("MmRequestPageMemoryConsumer failed (Status %x)\n", Status);
1639 }
1640
1641 }
1642 else
1643 {
1644 Status = MiReadPage(MemoryArea, Offset, &Page);
1645 if (!NT_SUCCESS(Status))
1646 {
1647 DPRINT1("MiReadPage failed (Status %x)\n", Status);
1648 }
1649 }
1650 if (!NT_SUCCESS(Status))
1651 {
1652 /*
1653 * FIXME: What do we know in this case?
1654 */
1655 /*
1656 * Cleanup and release locks
1657 */
1658 MmLockAddressSpace(AddressSpace);
1659 PageOp->Status = Status;
1660 MmspCompleteAndReleasePageOp(PageOp);
1661 DPRINT("Address 0x%.8X\n", Address);
1662 return(Status);
1663 }
1664 /*
1665 * Relock the address space and segment
1666 */
1667 MmLockAddressSpace(AddressSpace);
1668 MmLockSectionSegment(Segment);
1669
1670 /*
1671 * Check the entry. No one should change the status of a page
1672 * that has a pending page-in.
1673 */
1674 Entry1 = MmGetPageEntrySectionSegment(Segment, Offset);
1675 if (Entry != Entry1)
1676 {
1677 DPRINT1("Someone changed ppte entry while we slept\n");
1678 KeBugCheck(MEMORY_MANAGEMENT);
1679 }
1680
1681 /*
1682 * Mark the offset within the section as having valid, in-memory
1683 * data
1684 */
1685 Entry = MAKE_SSE(Page << PAGE_SHIFT, 1);
1686 MmSetPageEntrySectionSegment(Segment, Offset, Entry);
1687 MmUnlockSectionSegment(Segment);
1688
1689 Status = MmCreateVirtualMapping(Process,
1690 Address,
1691 Attributes,
1692 &Page,
1693 1);
1694 if (!NT_SUCCESS(Status))
1695 {
1696 DPRINT1("Unable to create virtual mapping\n");
1697 KeBugCheck(MEMORY_MANAGEMENT);
1698 }
1699 MmInsertRmap(Page, Process, (PVOID)PAddress);
1700
1701 PageOp->Status = STATUS_SUCCESS;
1702 MmspCompleteAndReleasePageOp(PageOp);
1703 DPRINT("Address 0x%.8X\n", Address);
1704 return(STATUS_SUCCESS);
1705 }
1706 else if (IS_SWAP_FROM_SSE(Entry))
1707 {
1708 SWAPENTRY SwapEntry;
1709
1710 SwapEntry = SWAPENTRY_FROM_SSE(Entry);
1711
1712 /*
1713 * Release all our locks and read in the page from disk
1714 */
1715 MmUnlockSectionSegment(Segment);
1716
1717 MmUnlockAddressSpace(AddressSpace);
1718 MI_SET_USAGE(MI_USAGE_SECTION);
1719 if (Process) MI_SET_PROCESS2(Process->ImageFileName);
1720 if (!Process) MI_SET_PROCESS2("Kernel Section");
1721 Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &Page);
1722 if (!NT_SUCCESS(Status))
1723 {
1724 KeBugCheck(MEMORY_MANAGEMENT);
1725 }
1726
1727 Status = MmReadFromSwapPage(SwapEntry, Page);
1728 if (!NT_SUCCESS(Status))
1729 {
1730 KeBugCheck(MEMORY_MANAGEMENT);
1731 }
1732
1733 /*
1734 * Relock the address space and segment
1735 */
1736 MmLockAddressSpace(AddressSpace);
1737 MmLockSectionSegment(Segment);
1738
1739 /*
1740 * Check the entry. No one should change the status of a page
1741 * that has a pending page-in.
1742 */
1743 Entry1 = MmGetPageEntrySectionSegment(Segment, Offset);
1744 if (Entry != Entry1)
1745 {
1746 DPRINT1("Someone changed ppte entry while we slept\n");
1747 KeBugCheck(MEMORY_MANAGEMENT);
1748 }
1749
1750 /*
1751 * Mark the offset within the section as having valid, in-memory
1752 * data
1753 */
1754 Entry = MAKE_SSE(Page << PAGE_SHIFT, 1);
1755 MmSetPageEntrySectionSegment(Segment, Offset, Entry);
1756 MmUnlockSectionSegment(Segment);
1757
1758 /*
1759 * Save the swap entry.
1760 */
1761 MmSetSavedSwapEntryPage(Page, SwapEntry);
1762 Status = MmCreateVirtualMapping(Process,
1763 Address,
1764 Region->Protect,
1765 &Page,
1766 1);
1767 if (!NT_SUCCESS(Status))
1768 {
1769 DPRINT1("Unable to create virtual mapping\n");
1770 KeBugCheck(MEMORY_MANAGEMENT);
1771 }
1772 MmInsertRmap(Page, Process, (PVOID)PAddress);
1773 PageOp->Status = STATUS_SUCCESS;
1774 MmspCompleteAndReleasePageOp(PageOp);
1775 DPRINT("Address 0x%.8X\n", Address);
1776 return(STATUS_SUCCESS);
1777 }
1778 else
1779 {
1780 /*
1781 * If the section offset is already in-memory and valid then just
1782 * take another reference to the page
1783 */
1784
1785 Page = PFN_FROM_SSE(Entry);
1786
1787 MmSharePageEntrySectionSegment(Segment, Offset);
1788 MmUnlockSectionSegment(Segment);
1789
1790 Status = MmCreateVirtualMapping(Process,
1791 Address,
1792 Attributes,
1793 &Page,
1794 1);
1795 if (!NT_SUCCESS(Status))
1796 {
1797 DPRINT1("Unable to create virtual mapping\n");
1798 KeBugCheck(MEMORY_MANAGEMENT);
1799 }
1800 MmInsertRmap(Page, Process, (PVOID)PAddress);
1801 PageOp->Status = STATUS_SUCCESS;
1802 MmspCompleteAndReleasePageOp(PageOp);
1803 DPRINT("Address 0x%.8X\n", Address);
1804 return(STATUS_SUCCESS);
1805 }
1806 }
1807
1808 NTSTATUS
1809 NTAPI
1810 MmAccessFaultSectionView(PMMSUPPORT AddressSpace,
1811 MEMORY_AREA* MemoryArea,
1812 PVOID Address,
1813 BOOLEAN Locked)
1814 {
1815 PMM_SECTION_SEGMENT Segment;
1816 PROS_SECTION_OBJECT Section;
1817 PFN_NUMBER OldPage;
1818 PFN_NUMBER NewPage;
1819 NTSTATUS Status;
1820 PVOID PAddress;
1821 ULONG Offset;
1822 PMM_PAGEOP PageOp;
1823 PMM_REGION Region;
1824 ULONG Entry;
1825 PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
1826
1827 DPRINT("MmAccessFaultSectionView(%x, %x, %x, %x)\n", AddressSpace, MemoryArea, Address, Locked);
1828
1829 /*
1830 * Check if the page has been paged out or has already been set readwrite
1831 */
1832 if (!MmIsPagePresent(Process, Address) ||
1833 MmGetPageProtect(Process, Address) & PAGE_READWRITE)
1834 {
1835 DPRINT("Address 0x%.8X\n", Address);
1836 return(STATUS_SUCCESS);
1837 }
1838
1839 /*
1840 * Find the offset of the page
1841 */
1842 PAddress = MM_ROUND_DOWN(Address, PAGE_SIZE);
1843 Offset = (ULONG_PTR)PAddress - (ULONG_PTR)MemoryArea->StartingAddress
1844 + MemoryArea->Data.SectionData.ViewOffset;
1845
1846 Segment = MemoryArea->Data.SectionData.Segment;
1847 Section = MemoryArea->Data.SectionData.Section;
1848 Region = MmFindRegion(MemoryArea->StartingAddress,
1849 &MemoryArea->Data.SectionData.RegionListHead,
1850 Address, NULL);
1851 /*
1852 * Lock the segment
1853 */
1854 MmLockSectionSegment(Segment);
1855
1856 OldPage = MmGetPfnForProcess(NULL, Address);
1857 Entry = MmGetPageEntrySectionSegment(Segment, Offset);
1858
1859 MmUnlockSectionSegment(Segment);
1860
1861 /*
1862 * Check if we are doing COW
1863 */
1864 if (!((Segment->WriteCopy) &&
1865 (Region->Protect == PAGE_READWRITE ||
1866 Region->Protect == PAGE_EXECUTE_READWRITE)))
1867 {
1868 DPRINT("Address 0x%.8X\n", Address);
1869 return(STATUS_ACCESS_VIOLATION);
1870 }
1871
1872 if (IS_SWAP_FROM_SSE(Entry) ||
1873 PFN_FROM_SSE(Entry) != OldPage)
1874 {
1875 /* This is a private page. We must only change the page protection. */
1876 MmSetPageProtect(Process, PAddress, Region->Protect);
1877 return(STATUS_SUCCESS);
1878 }
1879
1880 /*
1881 * Get or create a pageop
1882 */
1883 PageOp = MmGetPageOp(MemoryArea, NULL, 0, Segment, Offset,
1884 MM_PAGEOP_ACCESSFAULT, FALSE);
1885 if (PageOp == NULL)
1886 {
1887 DPRINT1("MmGetPageOp failed\n");
1888 KeBugCheck(MEMORY_MANAGEMENT);
1889 }
1890
1891 /*
1892 * Wait for any other operations to complete
1893 */
1894 if (PageOp->Thread != PsGetCurrentThread())
1895 {
1896 MmUnlockAddressSpace(AddressSpace);
1897 Status = MmspWaitForPageOpCompletionEvent(PageOp);
1898 /*
1899 * Check for various strange conditions
1900 */
1901 if (Status == STATUS_TIMEOUT)
1902 {
1903 DPRINT1("Failed to wait for page op, status = %x\n", Status);
1904 KeBugCheck(MEMORY_MANAGEMENT);
1905 }
1906 if (PageOp->Status == STATUS_PENDING)
1907 {
1908 DPRINT1("Woke for page op before completion\n");
1909 KeBugCheck(MEMORY_MANAGEMENT);
1910 }
1911 /*
1912 * Restart the operation
1913 */
1914 MmLockAddressSpace(AddressSpace);
1915 MmspCompleteAndReleasePageOp(PageOp);
1916 DPRINT("Address 0x%.8X\n", Address);
1917 return(STATUS_MM_RESTART_OPERATION);
1918 }
1919
1920 /*
1921 * Release locks now we have the pageop
1922 */
1923 MmUnlockAddressSpace(AddressSpace);
1924
1925 /*
1926 * Allocate a page
1927 */
1928 MI_SET_USAGE(MI_USAGE_SECTION);
1929 if (Process) MI_SET_PROCESS2(Process->ImageFileName);
1930 if (!Process) MI_SET_PROCESS2("Kernel Section");
1931 Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &NewPage);
1932 if (!NT_SUCCESS(Status))
1933 {
1934 KeBugCheck(MEMORY_MANAGEMENT);
1935 }
1936
1937 /*
1938 * Copy the old page
1939 */
1940 MiCopyFromUserPage(NewPage, PAddress);
1941
1942 MmLockAddressSpace(AddressSpace);
1943 /*
1944 * Delete the old entry.
1945 */
1946 MmDeleteVirtualMapping(Process, Address, FALSE, NULL, NULL);
1947
1948 /*
1949 * Set the PTE to point to the new page
1950 */
1951 Status = MmCreateVirtualMapping(Process,
1952 Address,
1953 Region->Protect,
1954 &NewPage,
1955 1);
1956 if (!NT_SUCCESS(Status))
1957 {
1958 DPRINT("MmCreateVirtualMapping failed, not out of memory\n");
1959 KeBugCheck(MEMORY_MANAGEMENT);
1960 return(Status);
1961 }
1962 if (!NT_SUCCESS(Status))
1963 {
1964 DPRINT1("Unable to create virtual mapping\n");
1965 KeBugCheck(MEMORY_MANAGEMENT);
1966 }
1967
1968 /*
1969 * Unshare the old page.
1970 */
1971 MmDeleteRmap(OldPage, Process, PAddress);
1972 MmInsertRmap(NewPage, Process, PAddress);
1973 MmLockSectionSegment(Segment);
1974 MmUnsharePageEntrySectionSegment(Section, Segment, Offset, FALSE, FALSE);
1975 MmUnlockSectionSegment(Segment);
1976
1977 PageOp->Status = STATUS_SUCCESS;
1978 MmspCompleteAndReleasePageOp(PageOp);
1979 DPRINT("Address 0x%.8X\n", Address);
1980 return(STATUS_SUCCESS);
1981 }
1982
1983 VOID
1984 MmPageOutDeleteMapping(PVOID Context, PEPROCESS Process, PVOID Address)
1985 {
1986 MM_SECTION_PAGEOUT_CONTEXT* PageOutContext;
1987 BOOLEAN WasDirty;
1988 PFN_NUMBER Page;
1989
1990 PageOutContext = (MM_SECTION_PAGEOUT_CONTEXT*)Context;
1991 if (Process)
1992 {
1993 MmLockAddressSpace(&Process->Vm);
1994 }
1995
1996 MmDeleteVirtualMapping(Process,
1997 Address,
1998 FALSE,
1999 &WasDirty,
2000 &Page);
2001 if (WasDirty)
2002 {
2003 PageOutContext->WasDirty = TRUE;
2004 }
2005 if (!PageOutContext->Private)
2006 {
2007 MmLockSectionSegment(PageOutContext->Segment);
2008 MmUnsharePageEntrySectionSegment((PROS_SECTION_OBJECT)PageOutContext->Section,
2009 PageOutContext->Segment,
2010 PageOutContext->Offset,
2011 PageOutContext->WasDirty,
2012 TRUE);
2013 MmUnlockSectionSegment(PageOutContext->Segment);
2014 }
2015 if (Process)
2016 {
2017 MmUnlockAddressSpace(&Process->Vm);
2018 }
2019
2020 if (PageOutContext->Private)
2021 {
2022 MmReleasePageMemoryConsumer(MC_USER, Page);
2023 }
2024
2025 DPRINT("PhysicalAddress %x, Address %x\n", Page << PAGE_SHIFT, Address);
2026 }
2027
2028 NTSTATUS
2029 NTAPI
2030 MmPageOutSectionView(PMMSUPPORT AddressSpace,
2031 MEMORY_AREA* MemoryArea,
2032 PVOID Address,
2033 PMM_PAGEOP PageOp)
2034 {
2035 PFN_NUMBER Page;
2036 MM_SECTION_PAGEOUT_CONTEXT Context;
2037 SWAPENTRY SwapEntry;
2038 ULONG Entry;
2039 ULONG FileOffset;
2040 NTSTATUS Status;
2041 PFILE_OBJECT FileObject;
2042 PBCB Bcb = NULL;
2043 BOOLEAN DirectMapped;
2044 BOOLEAN IsImageSection;
2045 PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
2046 KIRQL OldIrql;
2047
2048 Address = (PVOID)PAGE_ROUND_DOWN(Address);
2049
2050 /*
2051 * Get the segment and section.
2052 */
2053 Context.Segment = MemoryArea->Data.SectionData.Segment;
2054 Context.Section = MemoryArea->Data.SectionData.Section;
2055
2056 Context.Offset = (ULONG_PTR)Address - (ULONG_PTR)MemoryArea->StartingAddress
2057 + MemoryArea->Data.SectionData.ViewOffset;
2058 FileOffset = Context.Offset + Context.Segment->FileOffset;
2059
2060 IsImageSection = Context.Section->AllocationAttributes & SEC_IMAGE ? TRUE : FALSE;
2061
2062 FileObject = Context.Section->FileObject;
2063 DirectMapped = FALSE;
2064 if (FileObject != NULL &&
2065 !(Context.Segment->Characteristics & IMAGE_SCN_MEM_SHARED))
2066 {
2067 Bcb = FileObject->SectionObjectPointer->SharedCacheMap;
2068
2069 /*
2070 * If the file system is letting us go directly to the cache and the
2071 * memory area was mapped at an offset in the file which is page aligned
2072 * then note this is a direct mapped page.
2073 */
2074 if ((FileOffset % PAGE_SIZE) == 0 &&
2075 (Context.Offset + PAGE_SIZE <= Context.Segment->RawLength || !IsImageSection))
2076 {
2077 DirectMapped = TRUE;
2078 }
2079 }
2080
2081
2082 /*
2083 * This should never happen since mappings of physical memory are never
2084 * placed in the rmap lists.
2085 */
2086 if (Context.Section->AllocationAttributes & SEC_PHYSICALMEMORY)
2087 {
2088 DPRINT1("Trying to page out from physical memory section address 0x%X "
2089 "process %d\n", Address,
2090 Process ? Process->UniqueProcessId : 0);
2091 KeBugCheck(MEMORY_MANAGEMENT);
2092 }
2093
2094 /*
2095 * Get the section segment entry and the physical address.
2096 */
2097 Entry = MmGetPageEntrySectionSegment(Context.Segment, Context.Offset);
2098 if (!MmIsPagePresent(Process, Address))
2099 {
2100 DPRINT1("Trying to page out not-present page at (%d,0x%.8X).\n",
2101 Process ? Process->UniqueProcessId : 0, Address);
2102 KeBugCheck(MEMORY_MANAGEMENT);
2103 }
2104 Page = MmGetPfnForProcess(Process, Address);
2105 SwapEntry = MmGetSavedSwapEntryPage(Page);
2106
2107 /*
2108 * Prepare the context structure for the rmap delete call.
2109 */
2110 Context.WasDirty = FALSE;
2111 if (Context.Segment->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA ||
2112 IS_SWAP_FROM_SSE(Entry) ||
2113 PFN_FROM_SSE(Entry) != Page)
2114 {
2115 Context.Private = TRUE;
2116 }
2117 else
2118 {
2119 Context.Private = FALSE;
2120 }
2121
2122 /*
2123 * Take an additional reference to the page or the cache segment.
2124 */
2125 if (DirectMapped && !Context.Private)
2126 {
2127 if(!MiIsPageFromCache(MemoryArea, Context.Offset))
2128 {
2129 DPRINT1("Direct mapped non private page is not associated with the cache.\n");
2130 KeBugCheck(MEMORY_MANAGEMENT);
2131 }
2132 }
2133 else
2134 {
2135 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
2136 MmReferencePage(Page);
2137 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
2138 }
2139
2140 MmDeleteAllRmaps(Page, (PVOID)&Context, MmPageOutDeleteMapping);
2141
2142 /*
2143 * If this wasn't a private page then we should have reduced the entry to
2144 * zero by deleting all the rmaps.
2145 */
2146 if (!Context.Private && MmGetPageEntrySectionSegment(Context.Segment, Context.Offset) != 0)
2147 {
2148 if (!(Context.Segment->Flags & MM_PAGEFILE_SEGMENT) &&
2149 !(Context.Segment->Characteristics & IMAGE_SCN_MEM_SHARED))
2150 {
2151 KeBugCheck(MEMORY_MANAGEMENT);
2152 }
2153 }
2154
2155 /*
2156 * If the page wasn't dirty then we can just free it as for a readonly page.
2157 * Since we unmapped all the mappings above we know it will not suddenly
2158 * become dirty.
2159 * If the page is from a pagefile section and has no swap entry,
2160 * we can't free the page at this point.
2161 */
2162 SwapEntry = MmGetSavedSwapEntryPage(Page);
2163 if (Context.Segment->Flags & MM_PAGEFILE_SEGMENT)
2164 {
2165 if (Context.Private)
2166 {
2167 DPRINT1("Found a %s private page (address %x) in a pagefile segment.\n",
2168 Context.WasDirty ? "dirty" : "clean", Address);
2169 KeBugCheck(MEMORY_MANAGEMENT);
2170 }
2171 if (!Context.WasDirty && SwapEntry != 0)
2172 {
2173 MmSetSavedSwapEntryPage(Page, 0);
2174 MmSetPageEntrySectionSegment(Context.Segment, Context.Offset, MAKE_SWAP_SSE(SwapEntry));
2175 MmReleasePageMemoryConsumer(MC_USER, Page);
2176 PageOp->Status = STATUS_SUCCESS;
2177 MmspCompleteAndReleasePageOp(PageOp);
2178 return(STATUS_SUCCESS);
2179 }
2180 }
2181 else if (Context.Segment->Characteristics & IMAGE_SCN_MEM_SHARED)
2182 {
2183 if (Context.Private)
2184 {
2185 DPRINT1("Found a %s private page (address %x) in a shared section segment.\n",
2186 Context.WasDirty ? "dirty" : "clean", Address);
2187 KeBugCheck(MEMORY_MANAGEMENT);
2188 }
2189 if (!Context.WasDirty || SwapEntry != 0)
2190 {
2191 MmSetSavedSwapEntryPage(Page, 0);
2192 if (SwapEntry != 0)
2193 {
2194 MmSetPageEntrySectionSegment(Context.Segment, Context.Offset, MAKE_SWAP_SSE(SwapEntry));
2195 }
2196 MmReleasePageMemoryConsumer(MC_USER, Page);
2197 PageOp->Status = STATUS_SUCCESS;
2198 MmspCompleteAndReleasePageOp(PageOp);
2199 return(STATUS_SUCCESS);
2200 }
2201 }
2202 else if (!Context.Private && DirectMapped)
2203 {
2204 if (SwapEntry != 0)
2205 {
2206 DPRINT1("Found a swapentry for a non private and direct mapped page (address %x)\n",
2207 Address);
2208 KeBugCheck(MEMORY_MANAGEMENT);
2209 }
2210 #ifndef NEWCC
2211 Status = CcRosUnmapCacheSegment(Bcb, FileOffset, FALSE);
2212 #else
2213 Status = STATUS_SUCCESS;
2214 #endif
2215 if (!NT_SUCCESS(Status))
2216 {
2217 DPRINT1("CCRosUnmapCacheSegment failed, status = %x\n", Status);
2218 KeBugCheck(MEMORY_MANAGEMENT);
2219 }
2220 PageOp->Status = STATUS_SUCCESS;
2221 MmspCompleteAndReleasePageOp(PageOp);
2222 return(STATUS_SUCCESS);
2223 }
2224 else if (!Context.WasDirty && !DirectMapped && !Context.Private)
2225 {
2226 if (SwapEntry != 0)
2227 {
2228 DPRINT1("Found a swap entry for a non dirty, non private and not direct mapped page (address %x)\n",
2229 Address);
2230 KeBugCheck(MEMORY_MANAGEMENT);
2231 }
2232 MmReleasePageMemoryConsumer(MC_USER, Page);
2233 PageOp->Status = STATUS_SUCCESS;
2234 MmspCompleteAndReleasePageOp(PageOp);
2235 return(STATUS_SUCCESS);
2236 }
2237 else if (!Context.WasDirty && Context.Private && SwapEntry != 0)
2238 {
2239 MmSetSavedSwapEntryPage(Page, 0);
2240 MmLockAddressSpace(AddressSpace);
2241 Status = MmCreatePageFileMapping(Process,
2242 Address,
2243 SwapEntry);
2244 MmUnlockAddressSpace(AddressSpace);
2245 if (!NT_SUCCESS(Status))
2246 {
2247 KeBugCheck(MEMORY_MANAGEMENT);
2248 }
2249 MmReleasePageMemoryConsumer(MC_USER, Page);
2250 PageOp->Status = STATUS_SUCCESS;
2251 MmspCompleteAndReleasePageOp(PageOp);
2252 return(STATUS_SUCCESS);
2253 }
2254
2255 /*
2256 * If necessary, allocate an entry in the paging file for this page
2257 */
2258 if (SwapEntry == 0)
2259 {
2260 SwapEntry = MmAllocSwapPage();
2261 if (SwapEntry == 0)
2262 {
2263 MmShowOutOfSpaceMessagePagingFile();
2264 MmLockAddressSpace(AddressSpace);
2265 /*
2266 * For private pages restore the old mappings.
2267 */
2268 if (Context.Private)
2269 {
2270 Status = MmCreateVirtualMapping(Process,
2271 Address,
2272 MemoryArea->Protect,
2273 &Page,
2274 1);
2275 MmSetDirtyPage(Process, Address);
2276 MmInsertRmap(Page,
2277 Process,
2278 Address);
2279 }
2280 else
2281 {
2282 /*
2283 * For non-private pages if the page wasn't direct mapped then
2284 * set it back into the section segment entry so we don't loose
2285 * our copy. Otherwise it will be handled by the cache manager.
2286 */
2287 Status = MmCreateVirtualMapping(Process,
2288 Address,
2289 MemoryArea->Protect,
2290 &Page,
2291 1);
2292 MmSetDirtyPage(Process, Address);
2293 MmInsertRmap(Page,
2294 Process,
2295 Address);
2296 Entry = MAKE_SSE(Page << PAGE_SHIFT, 1);
2297 MmSetPageEntrySectionSegment(Context.Segment, Context.Offset, Entry);
2298 }
2299 MmUnlockAddressSpace(AddressSpace);
2300 PageOp->Status = STATUS_UNSUCCESSFUL;
2301 MmspCompleteAndReleasePageOp(PageOp);
2302 return(STATUS_PAGEFILE_QUOTA);
2303 }
2304 }
2305
2306 /*
2307 * Write the page to the pagefile
2308 */
2309 Status = MmWriteToSwapPage(SwapEntry, Page);
2310 if (!NT_SUCCESS(Status))
2311 {
2312 DPRINT1("MM: Failed to write to swap page (Status was 0x%.8X)\n",
2313 Status);
2314 /*
2315 * As above: undo our actions.
2316 * FIXME: Also free the swap page.
2317 */
2318 MmLockAddressSpace(AddressSpace);
2319 if (Context.Private)
2320 {
2321 Status = MmCreateVirtualMapping(Process,
2322 Address,
2323 MemoryArea->Protect,
2324 &Page,
2325 1);
2326 MmSetDirtyPage(Process, Address);
2327 MmInsertRmap(Page,
2328 Process,
2329 Address);
2330 }
2331 else
2332 {
2333 Status = MmCreateVirtualMapping(Process,
2334 Address,
2335 MemoryArea->Protect,
2336 &Page,
2337 1);
2338 MmSetDirtyPage(Process, Address);
2339 MmInsertRmap(Page,
2340 Process,
2341 Address);
2342 Entry = MAKE_SSE(Page << PAGE_SHIFT, 1);
2343 MmSetPageEntrySectionSegment(Context.Segment, Context.Offset, Entry);
2344 }
2345 MmUnlockAddressSpace(AddressSpace);
2346 PageOp->Status = STATUS_UNSUCCESSFUL;
2347 MmspCompleteAndReleasePageOp(PageOp);
2348 return(STATUS_UNSUCCESSFUL);
2349 }
2350
2351 /*
2352 * Otherwise we have succeeded.
2353 */
2354 DPRINT("MM: Wrote section page 0x%.8X to swap!\n", Page << PAGE_SHIFT);
2355 MmSetSavedSwapEntryPage(Page, 0);
2356 if (Context.Segment->Flags & MM_PAGEFILE_SEGMENT ||
2357 Context.Segment->Characteristics & IMAGE_SCN_MEM_SHARED)
2358 {
2359 MmSetPageEntrySectionSegment(Context.Segment, Context.Offset, MAKE_SWAP_SSE(SwapEntry));
2360 }
2361 else
2362 {
2363 MmReleasePageMemoryConsumer(MC_USER, Page);
2364 }
2365
2366 if (Context.Private)
2367 {
2368 MmLockAddressSpace(AddressSpace);
2369 Status = MmCreatePageFileMapping(Process,
2370 Address,
2371 SwapEntry);
2372 MmUnlockAddressSpace(AddressSpace);
2373 if (!NT_SUCCESS(Status))
2374 {
2375 KeBugCheck(MEMORY_MANAGEMENT);
2376 }
2377 }
2378 else
2379 {
2380 Entry = MAKE_SWAP_SSE(SwapEntry);
2381 MmSetPageEntrySectionSegment(Context.Segment, Context.Offset, Entry);
2382 }
2383
2384 PageOp->Status = STATUS_SUCCESS;
2385 MmspCompleteAndReleasePageOp(PageOp);
2386 return(STATUS_SUCCESS);
2387 }
2388
2389 NTSTATUS
2390 NTAPI
2391 MmWritePageSectionView(PMMSUPPORT AddressSpace,
2392 PMEMORY_AREA MemoryArea,
2393 PVOID Address,
2394 PMM_PAGEOP PageOp)
2395 {
2396 ULONG Offset;
2397 PROS_SECTION_OBJECT Section;
2398 PMM_SECTION_SEGMENT Segment;
2399 PFN_NUMBER Page;
2400 SWAPENTRY SwapEntry;
2401 ULONG Entry;
2402 BOOLEAN Private;
2403 NTSTATUS Status;
2404 PFILE_OBJECT FileObject;
2405 PBCB Bcb = NULL;
2406 BOOLEAN DirectMapped;
2407 BOOLEAN IsImageSection;
2408 PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
2409
2410 Address = (PVOID)PAGE_ROUND_DOWN(Address);
2411
2412 Offset = (ULONG_PTR)Address - (ULONG_PTR)MemoryArea->StartingAddress
2413 + MemoryArea->Data.SectionData.ViewOffset;
2414
2415 /*
2416 * Get the segment and section.
2417 */
2418 Segment = MemoryArea->Data.SectionData.Segment;
2419 Section = MemoryArea->Data.SectionData.Section;
2420 IsImageSection = Section->AllocationAttributes & SEC_IMAGE ? TRUE : FALSE;
2421
2422 FileObject = Section->FileObject;
2423 DirectMapped = FALSE;
2424 if (FileObject != NULL &&
2425 !(Segment->Characteristics & IMAGE_SCN_MEM_SHARED))
2426 {
2427 Bcb = FileObject->SectionObjectPointer->SharedCacheMap;
2428
2429 /*
2430 * If the file system is letting us go directly to the cache and the
2431 * memory area was mapped at an offset in the file which is page aligned
2432 * then note this is a direct mapped page.
2433 */
2434 if (((Offset + Segment->FileOffset) % PAGE_SIZE) == 0 &&
2435 (Offset + PAGE_SIZE <= Segment->RawLength || !IsImageSection))
2436 {
2437 DirectMapped = TRUE;
2438 }
2439 }
2440
2441 /*
2442 * This should never happen since mappings of physical memory are never
2443 * placed in the rmap lists.
2444 */
2445 if (Section->AllocationAttributes & SEC_PHYSICALMEMORY)
2446 {
2447 DPRINT1("Trying to write back page from physical memory mapped at %X "
2448 "process %d\n", Address,
2449 Process ? Process->UniqueProcessId : 0);
2450 KeBugCheck(MEMORY_MANAGEMENT);
2451 }
2452
2453 /*
2454 * Get the section segment entry and the physical address.
2455 */
2456 Entry = MmGetPageEntrySectionSegment(Segment, Offset);
2457 if (!MmIsPagePresent(Process, Address))
2458 {
2459 DPRINT1("Trying to page out not-present page at (%d,0x%.8X).\n",
2460 Process ? Process->UniqueProcessId : 0, Address);
2461 KeBugCheck(MEMORY_MANAGEMENT);
2462 }
2463 Page = MmGetPfnForProcess(Process, Address);
2464 SwapEntry = MmGetSavedSwapEntryPage(Page);
2465
2466 /*
2467 * Check for a private (COWed) page.
2468 */
2469 if (Segment->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA ||
2470 IS_SWAP_FROM_SSE(Entry) ||
2471 PFN_FROM_SSE(Entry) != Page)
2472 {
2473 Private = TRUE;
2474 }
2475 else
2476 {
2477 Private = FALSE;
2478 }
2479
2480 /*
2481 * Speculatively set all mappings of the page to clean.
2482 */
2483 MmSetCleanAllRmaps(Page);
2484
2485 /*
2486 * If this page was direct mapped from the cache then the cache manager
2487 * will take care of writing it back to disk.
2488 */
2489 if (DirectMapped && !Private)
2490 {
2491 ASSERT(SwapEntry == 0);
2492 #ifndef NEWCC
2493 CcRosMarkDirtyCacheSegment(Bcb, Offset + Segment->FileOffset);
2494 #endif
2495 PageOp->Status = STATUS_SUCCESS;
2496 MmspCompleteAndReleasePageOp(PageOp);
2497 return(STATUS_SUCCESS);
2498 }
2499
2500 /*
2501 * If necessary, allocate an entry in the paging file for this page
2502 */
2503 if (SwapEntry == 0)
2504 {
2505 SwapEntry = MmAllocSwapPage();
2506 if (SwapEntry == 0)
2507 {
2508 MmSetDirtyAllRmaps(Page);
2509 PageOp->Status = STATUS_UNSUCCESSFUL;
2510 MmspCompleteAndReleasePageOp(PageOp);
2511 return(STATUS_PAGEFILE_QUOTA);
2512 }
2513 MmSetSavedSwapEntryPage(Page, SwapEntry);
2514 }
2515
2516 /*
2517 * Write the page to the pagefile
2518 */
2519 Status = MmWriteToSwapPage(SwapEntry, Page);
2520 if (!NT_SUCCESS(Status))
2521 {
2522 DPRINT1("MM: Failed to write to swap page (Status was 0x%.8X)\n",
2523 Status);
2524 MmSetDirtyAllRmaps(Page);
2525 PageOp->Status = STATUS_UNSUCCESSFUL;
2526 MmspCompleteAndReleasePageOp(PageOp);
2527 return(STATUS_UNSUCCESSFUL);
2528 }
2529
2530 /*
2531 * Otherwise we have succeeded.
2532 */
2533 DPRINT("MM: Wrote section page 0x%.8X to swap!\n", Page << PAGE_SHIFT);
2534 PageOp->Status = STATUS_SUCCESS;
2535 MmspCompleteAndReleasePageOp(PageOp);
2536 return(STATUS_SUCCESS);
2537 }
2538
2539 static VOID
2540 MmAlterViewAttributes(PMMSUPPORT AddressSpace,
2541 PVOID BaseAddress,
2542 ULONG RegionSize,
2543 ULONG OldType,
2544 ULONG OldProtect,
2545 ULONG NewType,
2546 ULONG NewProtect)
2547 {
2548 PMEMORY_AREA MemoryArea;
2549 PMM_SECTION_SEGMENT Segment;
2550 BOOLEAN DoCOW = FALSE;
2551 ULONG i;
2552 PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
2553
2554 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, BaseAddress);
2555 Segment = MemoryArea->Data.SectionData.Segment;
2556
2557 if ((Segment->WriteCopy) &&
2558 (NewProtect == PAGE_READWRITE || NewProtect == PAGE_EXECUTE_READWRITE))
2559 {
2560 DoCOW = TRUE;
2561 }
2562
2563 if (OldProtect != NewProtect)
2564 {
2565 for (i = 0; i < PAGE_ROUND_UP(RegionSize) / PAGE_SIZE; i++)
2566 {
2567 PVOID Address = (char*)BaseAddress + (i * PAGE_SIZE);
2568 ULONG Protect = NewProtect;
2569
2570 /*
2571 * If we doing COW for this segment then check if the page is
2572 * already private.
2573 */
2574 if (DoCOW && MmIsPagePresent(Process, Address))
2575 {
2576 ULONG Offset;
2577 ULONG Entry;
2578 PFN_NUMBER Page;
2579
2580 Offset = (ULONG_PTR)Address - (ULONG_PTR)MemoryArea->StartingAddress
2581 + MemoryArea->Data.SectionData.ViewOffset;
2582 Entry = MmGetPageEntrySectionSegment(Segment, Offset);
2583 Page = MmGetPfnForProcess(Process, Address);
2584
2585 Protect = PAGE_READONLY;
2586 if (Segment->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA ||
2587 IS_SWAP_FROM_SSE(Entry) ||
2588 PFN_FROM_SSE(Entry) != Page)
2589 {
2590 Protect = NewProtect;
2591 }
2592 }
2593
2594 if (MmIsPagePresent(Process, Address))
2595 {
2596 MmSetPageProtect(Process, Address,
2597 Protect);
2598 }
2599 }
2600 }
2601 }
2602
2603 NTSTATUS
2604 NTAPI
2605 MmProtectSectionView(PMMSUPPORT AddressSpace,
2606 PMEMORY_AREA MemoryArea,
2607 PVOID BaseAddress,
2608 ULONG Length,
2609 ULONG Protect,
2610 PULONG OldProtect)
2611 {
2612 PMM_REGION Region;
2613 NTSTATUS Status;
2614 ULONG_PTR MaxLength;
2615
2616 MaxLength = (ULONG_PTR)MemoryArea->EndingAddress - (ULONG_PTR)BaseAddress;
2617 if (Length > MaxLength)
2618 Length = MaxLength;
2619
2620 Region = MmFindRegion(MemoryArea->StartingAddress,
2621 &MemoryArea->Data.SectionData.RegionListHead,
2622 BaseAddress, NULL);
2623 if ((MemoryArea->Flags & SEC_NO_CHANGE) &&
2624 Region->Protect != Protect)
2625 {
2626 return STATUS_INVALID_PAGE_PROTECTION;
2627 }
2628
2629 *OldProtect = Region->Protect;
2630 Status = MmAlterRegion(AddressSpace, MemoryArea->StartingAddress,
2631 &MemoryArea->Data.SectionData.RegionListHead,
2632 BaseAddress, Length, Region->Type, Protect,
2633 MmAlterViewAttributes);
2634
2635 return(Status);
2636 }
2637
2638 NTSTATUS NTAPI
2639 MmQuerySectionView(PMEMORY_AREA MemoryArea,
2640 PVOID Address,
2641 PMEMORY_BASIC_INFORMATION Info,
2642 PSIZE_T ResultLength)
2643 {
2644 PMM_REGION Region;
2645 PVOID RegionBaseAddress;
2646 PROS_SECTION_OBJECT Section;
2647 PMM_SECTION_SEGMENT Segment;
2648
2649 Region = MmFindRegion((PVOID)MemoryArea->StartingAddress,
2650 &MemoryArea->Data.SectionData.RegionListHead,
2651 Address, &RegionBaseAddress);
2652 if (Region == NULL)
2653 {
2654 return STATUS_UNSUCCESSFUL;
2655 }
2656
2657 Section = MemoryArea->Data.SectionData.Section;
2658 if (Section->AllocationAttributes & SEC_IMAGE)
2659 {
2660 Segment = MemoryArea->Data.SectionData.Segment;
2661 Info->AllocationBase = (PUCHAR)MemoryArea->StartingAddress - Segment->VirtualAddress;
2662 Info->Type = MEM_IMAGE;
2663 }
2664 else
2665 {
2666 Info->AllocationBase = MemoryArea->StartingAddress;
2667 Info->Type = MEM_MAPPED;
2668 }
2669 Info->BaseAddress = RegionBaseAddress;
2670 Info->AllocationProtect = MemoryArea->Protect;
2671 Info->RegionSize = Region->Length;
2672 Info->State = MEM_COMMIT;
2673 Info->Protect = Region->Protect;
2674
2675 *ResultLength = sizeof(MEMORY_BASIC_INFORMATION);
2676 return(STATUS_SUCCESS);
2677 }
2678
2679 VOID
2680 NTAPI
2681 MmpFreePageFileSegment(PMM_SECTION_SEGMENT Segment)
2682 {
2683 ULONG Length;
2684 ULONG Offset;
2685 ULONG Entry;
2686 ULONG SavedSwapEntry;
2687 PFN_NUMBER Page;
2688
2689 Page = 0;
2690
2691 Length = PAGE_ROUND_UP(Segment->Length);
2692 for (Offset = 0; Offset < Length; Offset += PAGE_SIZE)
2693 {
2694 Entry = MmGetPageEntrySectionSegment(Segment, Offset);
2695 if (Entry)
2696 {
2697 if (IS_SWAP_FROM_SSE(Entry))
2698 {
2699 MmFreeSwapPage(SWAPENTRY_FROM_SSE(Entry));
2700 }
2701 else
2702 {
2703 Page = PFN_FROM_SSE(Entry);
2704 SavedSwapEntry = MmGetSavedSwapEntryPage(Page);
2705 if (SavedSwapEntry != 0)
2706 {
2707 MmSetSavedSwapEntryPage(Page, 0);
2708 MmFreeSwapPage(SavedSwapEntry);
2709 }
2710 MmReleasePageMemoryConsumer(MC_USER, Page);
2711 }
2712 MmSetPageEntrySectionSegment(Segment, Offset, 0);
2713 }
2714 }
2715 }
2716
2717 VOID NTAPI
2718 MmpDeleteSection(PVOID ObjectBody)
2719 {
2720 PROS_SECTION_OBJECT Section = (PROS_SECTION_OBJECT)ObjectBody;
2721
2722 DPRINT("MmpDeleteSection(ObjectBody %x)\n", ObjectBody);
2723 if (Section->AllocationAttributes & SEC_IMAGE)
2724 {
2725 ULONG i;
2726 ULONG NrSegments;
2727 ULONG RefCount;
2728 PMM_SECTION_SEGMENT SectionSegments;
2729
2730 /*
2731 * NOTE: Section->ImageSection can be NULL for short time
2732 * during the section creating. If we fail for some reason
2733 * until the image section is properly initialized we shouldn't
2734 * process further here.
2735 */
2736 if (Section->ImageSection == NULL)
2737 return;
2738
2739 SectionSegments = Section->ImageSection->Segments;
2740 NrSegments = Section->ImageSection->NrSegments;
2741
2742 for (i = 0; i < NrSegments; i++)
2743 {
2744 if (SectionSegments[i].Characteristics & IMAGE_SCN_MEM_SHARED)
2745 {
2746 MmLockSectionSegment(&SectionSegments[i]);
2747 }
2748 RefCount = InterlockedDecrementUL(&SectionSegments[i].ReferenceCount);
2749 if (SectionSegments[i].Characteristics & IMAGE_SCN_MEM_SHARED)
2750 {
2751 if (RefCount == 0)
2752 {
2753 MmpFreePageFileSegment(&SectionSegments[i]);
2754 }
2755 MmUnlockSectionSegment(&SectionSegments[i]);
2756 }
2757 }
2758 }
2759 else
2760 {
2761 /*
2762 * NOTE: Section->Segment can be NULL for short time
2763 * during the section creating.
2764 */
2765 if (Section->Segment == NULL)
2766 return;
2767
2768 if (Section->Segment->Flags & MM_PAGEFILE_SEGMENT)
2769 {
2770 MmpFreePageFileSegment(Section->Segment);
2771 MmFreePageTablesSectionSegment(Section->Segment);
2772 ExFreePool(Section->Segment);
2773 Section->Segment = NULL;
2774 }
2775 else
2776 {
2777 (void)InterlockedDecrementUL(&Section->Segment->ReferenceCount);
2778 }
2779 }
2780 if (Section->FileObject != NULL)
2781 {
2782 #ifndef NEWCC
2783 CcRosDereferenceCache(Section->FileObject);
2784 #endif
2785 ObDereferenceObject(Section->FileObject);
2786 Section->FileObject = NULL;
2787 }
2788 }
2789
2790 VOID NTAPI
2791 MmpCloseSection(IN PEPROCESS Process OPTIONAL,
2792 IN PVOID Object,
2793 IN ACCESS_MASK GrantedAccess,
2794 IN ULONG ProcessHandleCount,
2795 IN ULONG SystemHandleCount)
2796 {
2797 DPRINT("MmpCloseSection(OB %x, HC %d)\n",
2798 Object, ProcessHandleCount);
2799 }
2800
2801 NTSTATUS
2802 INIT_FUNCTION
2803 NTAPI
2804 MmCreatePhysicalMemorySection(VOID)
2805 {
2806 PROS_SECTION_OBJECT PhysSection;
2807 NTSTATUS Status;
2808 OBJECT_ATTRIBUTES Obj;
2809 UNICODE_STRING Name = RTL_CONSTANT_STRING(L"\\Device\\PhysicalMemory");
2810 LARGE_INTEGER SectionSize;
2811 HANDLE Handle;
2812
2813 /*
2814 * Create the section mapping physical memory
2815 */
2816 SectionSize.QuadPart = 0xFFFFFFFF;
2817 InitializeObjectAttributes(&Obj,
2818 &Name,
2819 OBJ_PERMANENT,
2820 NULL,
2821 NULL);
2822 Status = MmCreateSection((PVOID)&PhysSection,
2823 SECTION_ALL_ACCESS,
2824 &Obj,
2825 &SectionSize,
2826 PAGE_EXECUTE_READWRITE,
2827 0,
2828 NULL,
2829 NULL);
2830 if (!NT_SUCCESS(Status))
2831 {
2832 DPRINT1("Failed to create PhysicalMemory section\n");
2833 KeBugCheck(MEMORY_MANAGEMENT);
2834 }
2835 Status = ObInsertObject(PhysSection,
2836 NULL,
2837 SECTION_ALL_ACCESS,
2838 0,
2839 NULL,
2840 &Handle);
2841 if (!NT_SUCCESS(Status))
2842 {
2843 ObDereferenceObject(PhysSection);
2844 }
2845 ObCloseHandle(Handle, KernelMode);
2846 PhysSection->AllocationAttributes |= SEC_PHYSICALMEMORY;
2847 PhysSection->Segment->Flags &= ~MM_PAGEFILE_SEGMENT;
2848
2849 return(STATUS_SUCCESS);
2850 }
2851
2852 NTSTATUS
2853 INIT_FUNCTION
2854 NTAPI
2855 MmInitSectionImplementation(VOID)
2856 {
2857 OBJECT_TYPE_INITIALIZER ObjectTypeInitializer;
2858 UNICODE_STRING Name;
2859
2860 DPRINT("Creating Section Object Type\n");
2861
2862 /* Initialize the Section object type */
2863 RtlZeroMemory(&ObjectTypeInitializer, sizeof(ObjectTypeInitializer));
2864 RtlInitUnicodeString(&Name, L"Section");
2865 ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer);
2866 ObjectTypeInitializer.DefaultPagedPoolCharge = sizeof(ROS_SECTION_OBJECT);
2867 ObjectTypeInitializer.PoolType = PagedPool;
2868 ObjectTypeInitializer.UseDefaultObject = TRUE;
2869 ObjectTypeInitializer.GenericMapping = MmpSectionMapping;
2870 ObjectTypeInitializer.DeleteProcedure = MmpDeleteSection;
2871 ObjectTypeInitializer.CloseProcedure = MmpCloseSection;
2872 ObjectTypeInitializer.ValidAccessMask = SECTION_ALL_ACCESS;
2873 ObCreateObjectType(&Name, &ObjectTypeInitializer, NULL, &MmSectionObjectType);
2874
2875 MmCreatePhysicalMemorySection();
2876
2877 return(STATUS_SUCCESS);
2878 }
2879
2880 NTSTATUS
2881 NTAPI
2882 MmCreatePageFileSection(PROS_SECTION_OBJECT *SectionObject,
2883 ACCESS_MASK DesiredAccess,
2884 POBJECT_ATTRIBUTES ObjectAttributes,
2885 PLARGE_INTEGER UMaximumSize,
2886 ULONG SectionPageProtection,
2887 ULONG AllocationAttributes)
2888 /*
2889 * Create a section which is backed by the pagefile
2890 */
2891 {
2892 LARGE_INTEGER MaximumSize;
2893 PROS_SECTION_OBJECT Section;
2894 PMM_SECTION_SEGMENT Segment;
2895 NTSTATUS Status;
2896
2897 if (UMaximumSize == NULL)
2898 {
2899 return(STATUS_UNSUCCESSFUL);
2900 }
2901 MaximumSize = *UMaximumSize;
2902
2903 /*
2904 * Create the section
2905 */
2906 Status = ObCreateObject(ExGetPreviousMode(),
2907 MmSectionObjectType,
2908 ObjectAttributes,
2909 ExGetPreviousMode(),
2910 NULL,
2911 sizeof(ROS_SECTION_OBJECT),
2912 0,
2913 0,
2914 (PVOID*)(PVOID)&Section);
2915 if (!NT_SUCCESS(Status))
2916 {
2917 return(Status);
2918 }
2919
2920 /*
2921 * Initialize it
2922 */
2923 RtlZeroMemory(Section, sizeof(ROS_SECTION_OBJECT));
2924 Section->SectionPageProtection = SectionPageProtection;
2925 Section->AllocationAttributes = AllocationAttributes;
2926 Section->MaximumSize = MaximumSize;
2927 Segment = ExAllocatePoolWithTag(NonPagedPool, sizeof(MM_SECTION_SEGMENT),
2928 TAG_MM_SECTION_SEGMENT);
2929 if (Segment == NULL)
2930 {
2931 ObDereferenceObject(Section);
2932 return(STATUS_NO_MEMORY);
2933 }
2934 Section->Segment = Segment;
2935 Segment->ReferenceCount = 1;
2936 ExInitializeFastMutex(&Segment->Lock);
2937 Segment->FileOffset = 0;
2938 Segment->Protection = SectionPageProtection;
2939 Segment->RawLength = MaximumSize.u.LowPart;
2940 Segment->Length = PAGE_ROUND_UP(MaximumSize.u.LowPart);
2941 Segment->Flags = MM_PAGEFILE_SEGMENT;
2942 Segment->WriteCopy = FALSE;
2943 RtlZeroMemory(&Segment->PageDirectory, sizeof(SECTION_PAGE_DIRECTORY));
2944 Segment->VirtualAddress = 0;
2945 Segment->Characteristics = 0;
2946 *SectionObject = Section;
2947 return(STATUS_SUCCESS);
2948 }
2949
2950
2951 NTSTATUS
2952 NTAPI
2953 MmCreateDataFileSection(PROS_SECTION_OBJECT *SectionObject,
2954 ACCESS_MASK DesiredAccess,
2955 POBJECT_ATTRIBUTES ObjectAttributes,
2956 PLARGE_INTEGER UMaximumSize,
2957 ULONG SectionPageProtection,
2958 ULONG AllocationAttributes,
2959 HANDLE FileHandle)
2960 /*
2961 * Create a section backed by a data file
2962 */
2963 {
2964 PROS_SECTION_OBJECT Section;
2965 NTSTATUS Status;
2966 LARGE_INTEGER MaximumSize;
2967 PFILE_OBJECT FileObject;
2968 PMM_SECTION_SEGMENT Segment;
2969 ULONG FileAccess;
2970 IO_STATUS_BLOCK Iosb;
2971 LARGE_INTEGER Offset;
2972 CHAR Buffer;
2973 FILE_STANDARD_INFORMATION FileInfo;
2974 ULONG Length;
2975
2976 /*
2977 * Create the section
2978 */
2979 Status = ObCreateObject(ExGetPreviousMode(),
2980 MmSectionObjectType,
2981 ObjectAttributes,
2982 ExGetPreviousMode(),
2983 NULL,
2984 sizeof(ROS_SECTION_OBJECT),
2985 0,
2986 0,
2987 (PVOID*)(PVOID)&Section);
2988 if (!NT_SUCCESS(Status))
2989 {
2990 return(Status);
2991 }
2992 /*
2993 * Initialize it
2994 */
2995 RtlZeroMemory(Section, sizeof(ROS_SECTION_OBJECT));
2996 Section->SectionPageProtection = SectionPageProtection;
2997 Section->AllocationAttributes = AllocationAttributes;
2998
2999 /*
3000 * Check file access required
3001 */
3002 if (SectionPageProtection & PAGE_READWRITE ||
3003 SectionPageProtection & PAGE_EXECUTE_READWRITE)
3004 {
3005 FileAccess = FILE_READ_DATA | FILE_WRITE_DATA;
3006 }
3007 else
3008 {
3009 FileAccess = FILE_READ_DATA;
3010 }
3011
3012 /*
3013 * Reference the file handle
3014 */
3015 Status = ObReferenceObjectByHandle(FileHandle,
3016 FileAccess,
3017 IoFileObjectType,
3018 ExGetPreviousMode(),
3019 (PVOID*)(PVOID)&FileObject,
3020 NULL);
3021 if (!NT_SUCCESS(Status))
3022 {
3023 ObDereferenceObject(Section);
3024 return(Status);
3025 }
3026
3027 /*
3028 * FIXME: This is propably not entirely correct. We can't look into
3029 * the standard FCB header because it might not be initialized yet
3030 * (as in case of the EXT2FS driver by Manoj Paul Joseph where the
3031 * standard file information is filled on first request).
3032 */
3033 Status = IoQueryFileInformation(FileObject,
3034 FileStandardInformation,
3035 sizeof(FILE_STANDARD_INFORMATION),
3036 &FileInfo,
3037 &Length);
3038 Iosb.Information = Length;
3039 if (!NT_SUCCESS(Status))
3040 {
3041 ObDereferenceObject(Section);
3042 ObDereferenceObject(FileObject);
3043 return Status;
3044 }
3045
3046 /*
3047 * FIXME: Revise this once a locking order for file size changes is
3048 * decided
3049 */
3050 if ((UMaximumSize != NULL) && (UMaximumSize->QuadPart != 0))
3051 {
3052 MaximumSize = *UMaximumSize;
3053 }
3054 else
3055 {
3056 MaximumSize = FileInfo.EndOfFile;
3057 /* Mapping zero-sized files isn't allowed. */
3058 if (MaximumSize.QuadPart == 0)
3059 {
3060 ObDereferenceObject(Section);
3061 ObDereferenceObject(FileObject);
3062 return STATUS_FILE_INVALID;
3063 }
3064 }
3065
3066 if (MaximumSize.QuadPart > FileInfo.EndOfFile.QuadPart)
3067 {
3068 Status = IoSetInformation(FileObject,
3069 FileAllocationInformation,
3070 sizeof(LARGE_INTEGER),
3071 &MaximumSize);
3072 if (!NT_SUCCESS(Status))
3073 {
3074 ObDereferenceObject(Section);
3075 ObDereferenceObject(FileObject);
3076 return(STATUS_SECTION_NOT_EXTENDED);
3077 }
3078 }
3079
3080 if (FileObject->SectionObjectPointer == NULL ||
3081 FileObject->SectionObjectPointer->SharedCacheMap == NULL)
3082 {
3083 /*
3084 * Read a bit so caching is initiated for the file object.
3085 * This is only needed because MiReadPage currently cannot
3086 * handle non-cached streams.
3087 */
3088 Offset.QuadPart = 0;
3089 Status = ZwReadFile(FileHandle,
3090 NULL,
3091 NULL,
3092 NULL,
3093 &Iosb,
3094 &Buffer,
3095 sizeof (Buffer),
3096 &Offset,
3097 0);
3098 if (!NT_SUCCESS(Status) && (Status != STATUS_END_OF_FILE))
3099 {
3100 ObDereferenceObject(Section);
3101 ObDereferenceObject(FileObject);
3102 return(Status);
3103 }
3104 if (FileObject->SectionObjectPointer == NULL ||
3105 FileObject->SectionObjectPointer->SharedCacheMap == NULL)
3106 {
3107 /* FIXME: handle this situation */
3108 ObDereferenceObject(Section);
3109 ObDereferenceObject(FileObject);
3110 return STATUS_INVALID_PARAMETER;
3111 }
3112 }
3113
3114 /*
3115 * Lock the file
3116 */
3117 Status = MmspWaitForFileLock(FileObject);
3118 if (Status != STATUS_SUCCESS)
3119 {
3120 ObDereferenceObject(Section);
3121 ObDereferenceObject(FileObject);
3122 return(Status);
3123 }
3124
3125 /*
3126 * If this file hasn't been mapped as a data file before then allocate a
3127 * section segment to describe the data file mapping
3128 */
3129 if (FileObject->SectionObjectPointer->DataSectionObject == NULL)
3130 {
3131 Segment = ExAllocatePoolWithTag(NonPagedPool, sizeof(MM_SECTION_SEGMENT),
3132 TAG_MM_SECTION_SEGMENT);
3133 if (Segment == NULL)
3134 {
3135 //KeSetEvent((PVOID)&FileObject->Lock, IO_NO_INCREMENT, FALSE);
3136 ObDereferenceObject(Section);
3137 ObDereferenceObject(FileObject);
3138 return(STATUS_NO_MEMORY);
3139 }
3140 Section->Segment = Segment;
3141 Segment->ReferenceCount = 1;
3142 ExInitializeFastMutex(&Segment->Lock);
3143 /*
3144 * Set the lock before assigning the segment to the file object
3145 */
3146 ExAcquireFastMutex(&Segment->Lock);
3147 FileObject->SectionObjectPointer->DataSectionObject = (PVOID)Segment;
3148
3149 Segment->FileOffset = 0;
3150 Segment->Protection = SectionPageProtection;
3151 Segment->Flags = MM_DATAFILE_SEGMENT;
3152 Segment->Characteristics = 0;
3153 Segment->WriteCopy = FALSE;
3154 if (AllocationAttributes & SEC_RESERVE)
3155 {
3156 Segment->Length = Segment->RawLength = 0;
3157 }
3158 else
3159 {
3160 Segment->RawLength = MaximumSize.u.LowPart;
3161 Segment->Length = PAGE_ROUND_UP(Segment->RawLength);
3162 }
3163 Segment->VirtualAddress = 0;
3164 RtlZeroMemory(&Segment->PageDirectory, sizeof(SECTION_PAGE_DIRECTORY));
3165 }
3166 else
3167 {
3168 /*
3169 * If the file is already mapped as a data file then we may need
3170 * to extend it
3171 */
3172 Segment =
3173 (PMM_SECTION_SEGMENT)FileObject->SectionObjectPointer->
3174 DataSectionObject;
3175 Section->Segment = Segment;
3176 (void)InterlockedIncrementUL(&Segment->ReferenceCount);
3177 MmLockSectionSegment(Segment);
3178
3179 if (MaximumSize.u.LowPart > Segment->RawLength &&
3180 !(AllocationAttributes & SEC_RESERVE))
3181 {
3182 Segment->RawLength = MaximumSize.u.LowPart;
3183 Segment->Length = PAGE_ROUND_UP(Segment->RawLength);
3184 }
3185 }
3186 MmUnlockSectionSegment(Segment);
3187 Section->FileObject = FileObject;
3188 Section->MaximumSize = MaximumSize;
3189 #ifndef NEWCC
3190 CcRosReferenceCache(FileObject);
3191 #endif
3192 //KeSetEvent((PVOID)&FileObject->Lock, IO_NO_INCREMENT, FALSE);
3193 *SectionObject = Section;
3194 return(STATUS_SUCCESS);
3195 }
3196
3197 /*
3198 TODO: not that great (declaring loaders statically, having to declare all of
3199 them, having to keep them extern, etc.), will fix in the future
3200 */
3201 extern NTSTATUS NTAPI PeFmtCreateSection
3202 (
3203 IN CONST VOID * FileHeader,
3204 IN SIZE_T FileHeaderSize,
3205 IN PVOID File,
3206 OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
3207 OUT PULONG Flags,
3208 IN PEXEFMT_CB_READ_FILE ReadFileCb,
3209 IN PEXEFMT_CB_ALLOCATE_SEGMENTS AllocateSegmentsCb
3210 );
3211
3212 extern NTSTATUS NTAPI ElfFmtCreateSection
3213 (
3214 IN CONST VOID * FileHeader,
3215 IN SIZE_T FileHeaderSize,
3216 IN PVOID File,
3217 OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
3218 OUT PULONG Flags,
3219 IN PEXEFMT_CB_READ_FILE ReadFileCb,
3220 IN PEXEFMT_CB_ALLOCATE_SEGMENTS AllocateSegmentsCb
3221 );
3222
3223 /* TODO: this is a standard DDK/PSDK macro */
3224 #ifndef RTL_NUMBER_OF
3225 #define RTL_NUMBER_OF(ARR_) (sizeof(ARR_) / sizeof((ARR_)[0]))
3226 #endif
3227
3228 static PEXEFMT_LOADER ExeFmtpLoaders[] =
3229 {
3230 PeFmtCreateSection,
3231 #ifdef __ELF
3232 ElfFmtCreateSection
3233 #endif
3234 };
3235
3236 static
3237 PMM_SECTION_SEGMENT
3238 NTAPI
3239 ExeFmtpAllocateSegments(IN ULONG NrSegments)
3240 {
3241 SIZE_T SizeOfSegments;
3242 PMM_SECTION_SEGMENT Segments;
3243
3244 /* TODO: check for integer overflow */
3245 SizeOfSegments = sizeof(MM_SECTION_SEGMENT) * NrSegments;
3246
3247 Segments = ExAllocatePoolWithTag(NonPagedPool,
3248 SizeOfSegments,
3249 TAG_MM_SECTION_SEGMENT);
3250
3251 if(Segments)
3252 RtlZeroMemory(Segments, SizeOfSegments);
3253
3254 return Segments;
3255 }
3256
3257 static
3258 NTSTATUS
3259 NTAPI
3260 ExeFmtpReadFile(IN PVOID File,
3261 IN PLARGE_INTEGER Offset,
3262 IN ULONG Length,
3263 OUT PVOID * Data,
3264 OUT PVOID * AllocBase,
3265 OUT PULONG ReadSize)
3266 {
3267 NTSTATUS Status;
3268 LARGE_INTEGER FileOffset;
3269 ULONG AdjustOffset;
3270 ULONG OffsetAdjustment;
3271 ULONG BufferSize;
3272 ULONG UsedSize;
3273 PVOID Buffer;
3274
3275 ASSERT_IRQL_LESS(DISPATCH_LEVEL);
3276
3277 if(Length == 0)
3278 {
3279 KeBugCheck(MEMORY_MANAGEMENT);
3280 }
3281
3282 FileOffset = *Offset;
3283
3284 /* Negative/special offset: it cannot be used in this context */
3285 if(FileOffset.u.HighPart < 0)
3286 {
3287 KeBugCheck(MEMORY_MANAGEMENT);
3288 }
3289
3290 AdjustOffset = PAGE_ROUND_DOWN(FileOffset.u.LowPart);
3291 OffsetAdjustment = FileOffset.u.LowPart - AdjustOffset;
3292 FileOffset.u.LowPart = AdjustOffset;
3293
3294 BufferSize = Length + OffsetAdjustment;
3295 BufferSize = PAGE_ROUND_UP(BufferSize);
3296
3297 /*
3298 * It's ok to use paged pool, because this is a temporary buffer only used in
3299 * the loading of executables. The assumption is that MmCreateSection is
3300 * always called at low IRQLs and that these buffers don't survive a brief
3301 * initialization phase
3302 */
3303 Buffer = ExAllocatePoolWithTag(PagedPool,
3304 BufferSize,
3305 'rXmM');
3306 if (!Buffer)
3307 {
3308 KeBugCheck(MEMORY_MANAGEMENT);
3309 }
3310
3311 UsedSize = 0;
3312
3313 #if 0
3314 Status = MmspPageRead(File,
3315 Buffer,
3316 BufferSize,
3317 &FileOffset,
3318 &UsedSize);
3319 #else
3320 /*
3321 * FIXME: if we don't use ZwReadFile, caching is not enabled for the file and
3322 * nothing will work. But using ZwReadFile is wrong, and using its side effects
3323 * to initialize internal state is even worse. Our cache manager is in need of
3324 * professional help
3325 */
3326 {
3327 IO_STATUS_BLOCK Iosb;
3328
3329 Status = ZwReadFile(File,
3330 NULL,
3331 NULL,
3332 NULL,
3333 &Iosb,
3334 Buffer,
3335 BufferSize,
3336 &FileOffset,
3337 NULL);
3338
3339 if(NT_SUCCESS(Status))
3340 {
3341 UsedSize = Iosb.Information;
3342 }
3343 }
3344 #endif
3345
3346 if(NT_SUCCESS(Status) && UsedSize < OffsetAdjustment)
3347 {
3348 Status = STATUS_IN_PAGE_ERROR;
3349 ASSERT(!NT_SUCCESS(Status));
3350 }
3351
3352 if(NT_SUCCESS(Status))
3353 {
3354 *Data = (PVOID)((ULONG_PTR)Buffer + OffsetAdjustment);
3355 *AllocBase = Buffer;
3356 *ReadSize = UsedSize - OffsetAdjustment;
3357 }
3358 else
3359 {
3360 ExFreePoolWithTag(Buffer, 'rXmM');
3361 }
3362
3363 return Status;
3364 }
3365
3366 #ifdef NASSERT
3367 # define MmspAssertSegmentsSorted(OBJ_) ((void)0)
3368 # define MmspAssertSegmentsNoOverlap(OBJ_) ((void)0)
3369 # define MmspAssertSegmentsPageAligned(OBJ_) ((void)0)
3370 #else
3371 static
3372 VOID
3373 NTAPI
3374 MmspAssertSegmentsSorted(IN PMM_IMAGE_SECTION_OBJECT ImageSectionObject)
3375 {
3376 ULONG i;
3377
3378 for( i = 1; i < ImageSectionObject->NrSegments; ++ i )
3379 {
3380 ASSERT(ImageSectionObject->Segments[i].VirtualAddress >=
3381 ImageSectionObject->Segments[i - 1].VirtualAddress);
3382 }
3383 }
3384
3385 static
3386 VOID
3387 NTAPI
3388 MmspAssertSegmentsNoOverlap(IN PMM_IMAGE_SECTION_OBJECT ImageSectionObject)
3389 {
3390 ULONG i;
3391
3392 MmspAssertSegmentsSorted(ImageSectionObject);
3393
3394 for( i = 0; i < ImageSectionObject->NrSegments; ++ i )
3395 {
3396 ASSERT(ImageSectionObject->Segments[i].Length > 0);
3397
3398 if(i > 0)
3399 {
3400 ASSERT(ImageSectionObject->Segments[i].VirtualAddress >=
3401 (ImageSectionObject->Segments[i - 1].VirtualAddress +
3402 ImageSectionObject->Segments[i - 1].Length));
3403 }
3404 }
3405 }
3406
3407 static
3408 VOID
3409 NTAPI
3410 MmspAssertSegmentsPageAligned(IN PMM_IMAGE_SECTION_OBJECT ImageSectionObject)
3411 {
3412 ULONG i;
3413
3414 for( i = 0; i < ImageSectionObject->NrSegments; ++ i )
3415 {
3416 ASSERT((ImageSectionObject->Segments[i].VirtualAddress % PAGE_SIZE) == 0);
3417 ASSERT((ImageSectionObject->Segments[i].Length % PAGE_SIZE) == 0);
3418 }
3419 }
3420 #endif
3421
3422 static
3423 int
3424 __cdecl
3425 MmspCompareSegments(const void * x,
3426 const void * y)
3427 {
3428 const MM_SECTION_SEGMENT *Segment1 = (const MM_SECTION_SEGMENT *)x;
3429 const MM_SECTION_SEGMENT *Segment2 = (const MM_SECTION_SEGMENT *)y