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