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