039046394ef82a3bf84c3f7aa6a1491dc4e38406
[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
1215 /*
1216 * There is a window between taking the page fault and locking the
1217 * address space when another thread could load the page so we check
1218 * that.
1219 */
1220 if (MmIsPagePresent(Process, Address))
1221 {
1222 return(STATUS_SUCCESS);
1223 }
1224
1225 /*
1226 * Check for the virtual memory area being deleted.
1227 */
1228 if (MemoryArea->DeleteInProgress)
1229 {
1230 return(STATUS_UNSUCCESSFUL);
1231 }
1232
1233 PAddress = MM_ROUND_DOWN(Address, PAGE_SIZE);
1234 Offset.QuadPart = (ULONG_PTR)PAddress - (ULONG_PTR)MemoryArea->StartingAddress
1235 + MemoryArea->Data.SectionData.ViewOffset.QuadPart;
1236
1237 Segment = MemoryArea->Data.SectionData.Segment;
1238 Section = MemoryArea->Data.SectionData.Section;
1239 Region = MmFindRegion(MemoryArea->StartingAddress,
1240 &MemoryArea->Data.SectionData.RegionListHead,
1241 Address, NULL);
1242 ASSERT(Region != NULL);
1243 /*
1244 * Lock the segment
1245 */
1246 MmLockSectionSegment(Segment);
1247 Entry = MmGetPageEntrySectionSegment(Segment, &Offset);
1248 /*
1249 * Check if this page needs to be mapped COW
1250 */
1251 if ((Segment->WriteCopy) &&
1252 (Region->Protect == PAGE_READWRITE ||
1253 Region->Protect == PAGE_EXECUTE_READWRITE))
1254 {
1255 Attributes = Region->Protect == PAGE_READWRITE ? PAGE_READONLY : PAGE_EXECUTE_READ;
1256 }
1257 else
1258 {
1259 Attributes = Region->Protect;
1260 }
1261
1262 /*
1263 * Check if someone else is already handling this fault, if so wait
1264 * for them
1265 */
1266 if (Entry && IS_SWAP_FROM_SSE(Entry) && SWAPENTRY_FROM_SSE(Entry) == MM_WAIT_ENTRY)
1267 {
1268 MmUnlockSectionSegment(Segment);
1269 MmUnlockAddressSpace(AddressSpace);
1270 MiWaitForPageEvent(NULL, NULL);
1271 MmLockAddressSpace(AddressSpace);
1272 DPRINT("Address 0x%.8X\n", Address);
1273 return(STATUS_MM_RESTART_OPERATION);
1274 }
1275
1276 HasSwapEntry = MmIsPageSwapEntry(Process, Address);
1277
1278 if (HasSwapEntry)
1279 {
1280 SWAPENTRY SwapEntry;
1281 /*
1282 * Is it a wait entry?
1283 */
1284 MmGetPageFileMapping(Process, Address, &SwapEntry);
1285
1286 if (SwapEntry == MM_WAIT_ENTRY)
1287 {
1288 MmUnlockSectionSegment(Segment);
1289 MmUnlockAddressSpace(AddressSpace);
1290 MiWaitForPageEvent(NULL, NULL);
1291 MmLockAddressSpace(AddressSpace);
1292 return STATUS_MM_RESTART_OPERATION;
1293 }
1294
1295 /*
1296 * Must be private page we have swapped out.
1297 */
1298
1299 /*
1300 * Sanity check
1301 */
1302 if (Segment->Flags & MM_PAGEFILE_SEGMENT)
1303 {
1304 DPRINT1("Found a swaped out private page in a pagefile section.\n");
1305 KeBugCheck(MEMORY_MANAGEMENT);
1306 }
1307
1308 MmUnlockSectionSegment(Segment);
1309 MmDeletePageFileMapping(Process, Address, &SwapEntry);
1310
1311 MmUnlockAddressSpace(AddressSpace);
1312 MI_SET_USAGE(MI_USAGE_SECTION);
1313 if (Process) MI_SET_PROCESS2(Process->ImageFileName);
1314 if (!Process) MI_SET_PROCESS2("Kernel Section");
1315 Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &Page);
1316 if (!NT_SUCCESS(Status))
1317 {
1318 KeBugCheck(MEMORY_MANAGEMENT);
1319 }
1320
1321 Status = MmReadFromSwapPage(SwapEntry, Page);
1322 if (!NT_SUCCESS(Status))
1323 {
1324 DPRINT1("MmReadFromSwapPage failed, status = %x\n", Status);
1325 KeBugCheck(MEMORY_MANAGEMENT);
1326 }
1327 MmLockAddressSpace(AddressSpace);
1328 Status = MmCreateVirtualMapping(Process,
1329 PAddress,
1330 Region->Protect,
1331 &Page,
1332 1);
1333 if (!NT_SUCCESS(Status))
1334 {
1335 DPRINT("MmCreateVirtualMapping failed, not out of memory\n");
1336 KeBugCheck(MEMORY_MANAGEMENT);
1337 return(Status);
1338 }
1339
1340 /*
1341 * Store the swap entry for later use.
1342 */
1343 MmSetSavedSwapEntryPage(Page, SwapEntry);
1344
1345 /*
1346 * Add the page to the process's working set
1347 */
1348 MmInsertRmap(Page, Process, Address);
1349 /*
1350 * Finish the operation
1351 */
1352 MiSetPageEvent(Process, Address);
1353 DPRINT("Address 0x%.8X\n", Address);
1354 return(STATUS_SUCCESS);
1355 }
1356
1357 /*
1358 * Satisfying a page fault on a map of /Device/PhysicalMemory is easy
1359 */
1360 if (Section->AllocationAttributes & SEC_PHYSICALMEMORY)
1361 {
1362 MmUnlockSectionSegment(Segment);
1363 /*
1364 * Just map the desired physical page
1365 */
1366 Page = (PFN_NUMBER)(Offset.QuadPart >> PAGE_SHIFT);
1367 Status = MmCreateVirtualMappingUnsafe(Process,
1368 PAddress,
1369 Region->Protect,
1370 &Page,
1371 1);
1372 if (!NT_SUCCESS(Status))
1373 {
1374 DPRINT("MmCreateVirtualMappingUnsafe failed, not out of memory\n");
1375 KeBugCheck(MEMORY_MANAGEMENT);
1376 return(Status);
1377 }
1378
1379 /*
1380 * Cleanup and release locks
1381 */
1382 MiSetPageEvent(Process, Address);
1383 DPRINT("Address 0x%.8X\n", Address);
1384 return(STATUS_SUCCESS);
1385 }
1386
1387 /*
1388 * Map anonymous memory for BSS sections
1389 */
1390 if (Segment->Image.Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA)
1391 {
1392 MmUnlockSectionSegment(Segment);
1393 MI_SET_USAGE(MI_USAGE_SECTION);
1394 if (Process) MI_SET_PROCESS2(Process->ImageFileName);
1395 if (!Process) MI_SET_PROCESS2("Kernel Section");
1396 Status = MmRequestPageMemoryConsumer(MC_USER, FALSE, &Page);
1397 if (!NT_SUCCESS(Status))
1398 {
1399 MmUnlockAddressSpace(AddressSpace);
1400 Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &Page);
1401 MmLockAddressSpace(AddressSpace);
1402 }
1403 if (!NT_SUCCESS(Status))
1404 {
1405 KeBugCheck(MEMORY_MANAGEMENT);
1406 }
1407 Status = MmCreateVirtualMapping(Process,
1408 PAddress,
1409 Region->Protect,
1410 &Page,
1411 1);
1412 if (!NT_SUCCESS(Status))
1413 {
1414 DPRINT("MmCreateVirtualMapping failed, not out of memory\n");
1415 KeBugCheck(MEMORY_MANAGEMENT);
1416 return(Status);
1417 }
1418 MmInsertRmap(Page, Process, Address);
1419
1420 /*
1421 * Cleanup and release locks
1422 */
1423 MiSetPageEvent(Process, Address);
1424 DPRINT("Address 0x%.8X\n", Address);
1425 return(STATUS_SUCCESS);
1426 }
1427
1428 /*
1429 * Get the entry corresponding to the offset within the section
1430 */
1431 Entry = MmGetPageEntrySectionSegment(Segment, &Offset);
1432
1433 if (Entry == 0)
1434 {
1435 /*
1436 * If the entry is zero (and it can't change because we have
1437 * locked the segment) then we need to load the page.
1438 */
1439
1440 /*
1441 * Release all our locks and read in the page from disk
1442 */
1443 MmSetPageEntrySectionSegment(Segment, &Offset, MAKE_SWAP_SSE(MM_WAIT_ENTRY));
1444 MmUnlockSectionSegment(Segment);
1445 MmUnlockAddressSpace(AddressSpace);
1446
1447 if ((Segment->Flags & MM_PAGEFILE_SEGMENT) ||
1448 ((Offset.QuadPart >= (LONGLONG)PAGE_ROUND_UP(Segment->RawLength.QuadPart) &&
1449 (Section->AllocationAttributes & SEC_IMAGE))))
1450 {
1451 MI_SET_USAGE(MI_USAGE_SECTION);
1452 if (Process) MI_SET_PROCESS2(Process->ImageFileName);
1453 if (!Process) MI_SET_PROCESS2("Kernel Section");
1454 Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &Page);
1455 if (!NT_SUCCESS(Status))
1456 {
1457 DPRINT1("MmRequestPageMemoryConsumer failed (Status %x)\n", Status);
1458 }
1459
1460 }
1461 else
1462 {
1463 Status = MiReadPage(MemoryArea, (ULONG_PTR)Offset.QuadPart, &Page);
1464 if (!NT_SUCCESS(Status))
1465 {
1466 DPRINT1("MiReadPage failed (Status %x)\n", Status);
1467 }
1468 }
1469 if (!NT_SUCCESS(Status))
1470 {
1471 /*
1472 * FIXME: What do we know in this case?
1473 */
1474 /*
1475 * Cleanup and release locks
1476 */
1477 MmLockAddressSpace(AddressSpace);
1478 MiSetPageEvent(Process, Address);
1479 DPRINT("Address 0x%.8X\n", Address);
1480 return(Status);
1481 }
1482
1483 /*
1484 * Mark the offset within the section as having valid, in-memory
1485 * data
1486 */
1487 MmLockAddressSpace(AddressSpace);
1488 MmLockSectionSegment(Segment);
1489 Entry = MAKE_SSE(Page << PAGE_SHIFT, 1);
1490 MmSetPageEntrySectionSegment(Segment, &Offset, Entry);
1491 MmUnlockSectionSegment(Segment);
1492
1493 Status = MmCreateVirtualMapping(Process,
1494 PAddress,
1495 Attributes,
1496 &Page,
1497 1);
1498 if (!NT_SUCCESS(Status))
1499 {
1500 DPRINT1("Unable to create virtual mapping\n");
1501 KeBugCheck(MEMORY_MANAGEMENT);
1502 }
1503 MmInsertRmap(Page, Process, Address);
1504
1505 MiSetPageEvent(Process, Address);
1506 DPRINT("Address 0x%.8X\n", Address);
1507 return(STATUS_SUCCESS);
1508 }
1509 else if (IS_SWAP_FROM_SSE(Entry))
1510 {
1511 SWAPENTRY SwapEntry;
1512
1513 SwapEntry = SWAPENTRY_FROM_SSE(Entry);
1514
1515 /*
1516 * Release all our locks and read in the page from disk
1517 */
1518 MmUnlockSectionSegment(Segment);
1519
1520 MmUnlockAddressSpace(AddressSpace);
1521 MI_SET_USAGE(MI_USAGE_SECTION);
1522 if (Process) MI_SET_PROCESS2(Process->ImageFileName);
1523 if (!Process) MI_SET_PROCESS2("Kernel Section");
1524 Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &Page);
1525 if (!NT_SUCCESS(Status))
1526 {
1527 KeBugCheck(MEMORY_MANAGEMENT);
1528 }
1529
1530 Status = MmReadFromSwapPage(SwapEntry, Page);
1531 if (!NT_SUCCESS(Status))
1532 {
1533 KeBugCheck(MEMORY_MANAGEMENT);
1534 }
1535
1536 /*
1537 * Relock the address space and segment
1538 */
1539 MmLockAddressSpace(AddressSpace);
1540 MmLockSectionSegment(Segment);
1541
1542 /*
1543 * Check the entry. No one should change the status of a page
1544 * that has a pending page-in.
1545 */
1546 Entry1 = MmGetPageEntrySectionSegment(Segment, &Offset);
1547 if (Entry != Entry1)
1548 {
1549 DPRINT1("Someone changed ppte entry while we slept (%x vs %x)\n", Entry, Entry1);
1550 KeBugCheck(MEMORY_MANAGEMENT);
1551 }
1552
1553 /*
1554 * Mark the offset within the section as having valid, in-memory
1555 * data
1556 */
1557 Entry = MAKE_SSE(Page << PAGE_SHIFT, 1);
1558 MmSetPageEntrySectionSegment(Segment, &Offset, Entry);
1559 MmUnlockSectionSegment(Segment);
1560
1561 /*
1562 * Save the swap entry.
1563 */
1564 MmSetSavedSwapEntryPage(Page, SwapEntry);
1565 Status = MmCreateVirtualMapping(Process,
1566 PAddress,
1567 Region->Protect,
1568 &Page,
1569 1);
1570 if (!NT_SUCCESS(Status))
1571 {
1572 DPRINT1("Unable to create virtual mapping\n");
1573 KeBugCheck(MEMORY_MANAGEMENT);
1574 }
1575 MmInsertRmap(Page, Process, Address);
1576 MiSetPageEvent(Process, Address);
1577 DPRINT("Address 0x%.8X\n", Address);
1578 return(STATUS_SUCCESS);
1579 }
1580 else
1581 {
1582 /*
1583 * If the section offset is already in-memory and valid then just
1584 * take another reference to the page
1585 */
1586
1587 Page = PFN_FROM_SSE(Entry);
1588
1589 MmSharePageEntrySectionSegment(Segment, &Offset);
1590 MmUnlockSectionSegment(Segment);
1591
1592 Status = MmCreateVirtualMapping(Process,
1593 PAddress,
1594 Attributes,
1595 &Page,
1596 1);
1597 if (!NT_SUCCESS(Status))
1598 {
1599 DPRINT1("Unable to create virtual mapping\n");
1600 KeBugCheck(MEMORY_MANAGEMENT);
1601 }
1602 MmInsertRmap(Page, Process, Address);
1603 MiSetPageEvent(Process, Address);
1604 DPRINT("Address 0x%.8X\n", Address);
1605 return(STATUS_SUCCESS);
1606 }
1607 }
1608
1609 NTSTATUS
1610 NTAPI
1611 MmAccessFaultSectionView(PMMSUPPORT AddressSpace,
1612 MEMORY_AREA* MemoryArea,
1613 PVOID Address)
1614 {
1615 PMM_SECTION_SEGMENT Segment;
1616 PROS_SECTION_OBJECT Section;
1617 PFN_NUMBER OldPage;
1618 PFN_NUMBER NewPage;
1619 NTSTATUS Status;
1620 PVOID PAddress;
1621 LARGE_INTEGER Offset;
1622 PMM_REGION Region;
1623 ULONG_PTR Entry;
1624 PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
1625 SWAPENTRY SwapEntry;
1626
1627 DPRINT("MmAccessFaultSectionView(%x, %x, %x, %x)\n", AddressSpace, MemoryArea, Address);
1628
1629 /*
1630 * Check if the page has already been set readwrite
1631 */
1632 if (MmGetPageProtect(Process, Address) & PAGE_READWRITE)
1633 {
1634 DPRINT("Address 0x%.8X\n", Address);
1635 return(STATUS_SUCCESS);
1636 }
1637
1638 /*
1639 * Find the offset of the page
1640 */
1641 PAddress = MM_ROUND_DOWN(Address, PAGE_SIZE);
1642 Offset.QuadPart = (ULONG_PTR)PAddress - (ULONG_PTR)MemoryArea->StartingAddress
1643 + MemoryArea->Data.SectionData.ViewOffset.QuadPart;
1644
1645 Segment = MemoryArea->Data.SectionData.Segment;
1646 Section = MemoryArea->Data.SectionData.Section;
1647 Region = MmFindRegion(MemoryArea->StartingAddress,
1648 &MemoryArea->Data.SectionData.RegionListHead,
1649 Address, NULL);
1650 ASSERT(Region != NULL);
1651 /*
1652 * Lock the segment
1653 */
1654 MmLockSectionSegment(Segment);
1655
1656 OldPage = MmGetPfnForProcess(Process, Address);
1657 Entry = MmGetPageEntrySectionSegment(Segment, &Offset);
1658
1659 MmUnlockSectionSegment(Segment);
1660
1661 /*
1662 * Check if we are doing COW
1663 */
1664 if (!((Segment->WriteCopy) &&
1665 (Region->Protect == PAGE_READWRITE ||
1666 Region->Protect == PAGE_EXECUTE_READWRITE)))
1667 {
1668 DPRINT("Address 0x%.8X\n", Address);
1669 return(STATUS_ACCESS_VIOLATION);
1670 }
1671
1672 if (IS_SWAP_FROM_SSE(Entry) ||
1673 PFN_FROM_SSE(Entry) != OldPage)
1674 {
1675 /* This is a private page. We must only change the page protection. */
1676 MmSetPageProtect(Process, Address, Region->Protect);
1677 return(STATUS_SUCCESS);
1678 }
1679
1680 if(OldPage == 0)
1681 DPRINT("OldPage == 0!\n");
1682
1683 /*
1684 * Get or create a pageop
1685 */
1686 MmLockSectionSegment(Segment);
1687 Entry = MmGetPageEntrySectionSegment(Segment, &Offset);
1688
1689 /*
1690 * Wait for any other operations to complete
1691 */
1692 if (Entry == SWAPENTRY_FROM_SSE(MM_WAIT_ENTRY))
1693 {
1694 MmUnlockSectionSegment(Segment);
1695 MmUnlockAddressSpace(AddressSpace);
1696 MiWaitForPageEvent(NULL, NULL);
1697 /*
1698 * Restart the operation
1699 */
1700 MmLockAddressSpace(AddressSpace);
1701 DPRINT("Address 0x%.8X\n", Address);
1702 return(STATUS_MM_RESTART_OPERATION);
1703 }
1704
1705 MmDeleteRmap(OldPage, Process, PAddress);
1706 MmDeleteVirtualMapping(Process, PAddress, FALSE, NULL, NULL);
1707 MmCreatePageFileMapping(Process, PAddress, MM_WAIT_ENTRY);
1708
1709 /*
1710 * Release locks now we have the pageop
1711 */
1712 MmUnlockSectionSegment(Segment);
1713 MmUnlockAddressSpace(AddressSpace);
1714
1715 /*
1716 * Allocate a page
1717 */
1718 MI_SET_USAGE(MI_USAGE_SECTION);
1719 if (Process) MI_SET_PROCESS2(Process->ImageFileName);
1720 if (!Process) MI_SET_PROCESS2("Kernel Section");
1721 Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &NewPage);
1722 if (!NT_SUCCESS(Status))
1723 {
1724 KeBugCheck(MEMORY_MANAGEMENT);
1725 }
1726
1727 /*
1728 * Copy the old page
1729 */
1730 MiCopyFromUserPage(NewPage, OldPage);
1731
1732 MmLockAddressSpace(AddressSpace);
1733
1734 /*
1735 * Set the PTE to point to the new page
1736 */
1737 MmDeletePageFileMapping(Process, PAddress, &SwapEntry);
1738 Status = MmCreateVirtualMapping(Process,
1739 PAddress,
1740 Region->Protect,
1741 &NewPage,
1742 1);
1743 if (!NT_SUCCESS(Status))
1744 {
1745 DPRINT("MmCreateVirtualMapping failed, not out of memory\n");
1746 KeBugCheck(MEMORY_MANAGEMENT);
1747 return(Status);
1748 }
1749 if (!NT_SUCCESS(Status))
1750 {
1751 DPRINT1("Unable to create virtual mapping\n");
1752 KeBugCheck(MEMORY_MANAGEMENT);
1753 }
1754
1755 /*
1756 * Unshare the old page.
1757 */
1758 DPRINT("Swapping page (Old %x New %x)\n", OldPage, NewPage);
1759 MmInsertRmap(NewPage, Process, PAddress);
1760 MmLockSectionSegment(Segment);
1761 MmUnsharePageEntrySectionSegment(Section, Segment, &Offset, FALSE, FALSE, NULL);
1762 MmUnlockSectionSegment(Segment);
1763
1764 MiSetPageEvent(Process, Address);
1765 DPRINT("Address 0x%.8X\n", Address);
1766 return(STATUS_SUCCESS);
1767 }
1768
1769 VOID
1770 MmPageOutDeleteMapping(PVOID Context, PEPROCESS Process, PVOID Address)
1771 {
1772 MM_SECTION_PAGEOUT_CONTEXT* PageOutContext;
1773 BOOLEAN WasDirty;
1774 PFN_NUMBER Page = 0;
1775
1776 PageOutContext = (MM_SECTION_PAGEOUT_CONTEXT*)Context;
1777 if (Process)
1778 {
1779 MmLockAddressSpace(&Process->Vm);
1780 }
1781
1782 MmDeleteVirtualMapping(Process,
1783 Address,
1784 FALSE,
1785 &WasDirty,
1786 &Page);
1787 if (WasDirty)
1788 {
1789 PageOutContext->WasDirty = TRUE;
1790 }
1791 if (!PageOutContext->Private)
1792 {
1793 MmLockSectionSegment(PageOutContext->Segment);
1794 MmUnsharePageEntrySectionSegment((PROS_SECTION_OBJECT)PageOutContext->Section,
1795 PageOutContext->Segment,
1796 &PageOutContext->Offset,
1797 PageOutContext->WasDirty,
1798 TRUE,
1799 &PageOutContext->SectionEntry);
1800 MmUnlockSectionSegment(PageOutContext->Segment);
1801 }
1802 if (Process)
1803 {
1804 MmUnlockAddressSpace(&Process->Vm);
1805 }
1806
1807 if (PageOutContext->Private)
1808 {
1809 MmReleasePageMemoryConsumer(MC_USER, Page);
1810 }
1811 }
1812
1813 NTSTATUS
1814 NTAPI
1815 MmPageOutSectionView(PMMSUPPORT AddressSpace,
1816 MEMORY_AREA* MemoryArea,
1817 PVOID Address, ULONG_PTR Entry)
1818 {
1819 PFN_NUMBER Page;
1820 MM_SECTION_PAGEOUT_CONTEXT Context;
1821 SWAPENTRY SwapEntry;
1822 ULONGLONG FileOffset;
1823 NTSTATUS Status;
1824 PFILE_OBJECT FileObject;
1825 #ifndef NEWCC
1826 PBCB Bcb = NULL;
1827 #endif
1828 BOOLEAN DirectMapped;
1829 BOOLEAN IsImageSection;
1830 PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
1831 KIRQL OldIrql;
1832
1833 Address = (PVOID)PAGE_ROUND_DOWN(Address);
1834
1835 /*
1836 * Get the segment and section.
1837 */
1838 Context.Segment = MemoryArea->Data.SectionData.Segment;
1839 Context.Section = MemoryArea->Data.SectionData.Section;
1840 Context.SectionEntry = Entry;
1841 Context.CallingProcess = Process;
1842
1843 Context.Offset.QuadPart = (ULONG_PTR)Address - (ULONG_PTR)MemoryArea->StartingAddress
1844 + MemoryArea->Data.SectionData.ViewOffset.QuadPart;
1845 FileOffset = Context.Offset.QuadPart + Context.Segment->Image.FileOffset;
1846
1847 IsImageSection = Context.Section->AllocationAttributes & SEC_IMAGE ? TRUE : FALSE;
1848
1849 FileObject = Context.Section->FileObject;
1850 DirectMapped = FALSE;
1851
1852 MmLockSectionSegment(Context.Segment);
1853
1854 #ifndef NEWCC
1855 if (FileObject != NULL &&
1856 !(Context.Segment->Image.Characteristics & IMAGE_SCN_MEM_SHARED))
1857 {
1858 Bcb = FileObject->SectionObjectPointer->SharedCacheMap;
1859
1860 /*
1861 * If the file system is letting us go directly to the cache and the
1862 * memory area was mapped at an offset in the file which is page aligned
1863 * then note this is a direct mapped page.
1864 */
1865 if ((FileOffset % PAGE_SIZE) == 0 &&
1866 (Context.Offset.QuadPart + PAGE_SIZE <= Context.Segment->RawLength.QuadPart || !IsImageSection))
1867 {
1868 DirectMapped = TRUE;
1869 }
1870 }
1871 #endif
1872
1873
1874 /*
1875 * This should never happen since mappings of physical memory are never
1876 * placed in the rmap lists.
1877 */
1878 if (Context.Section->AllocationAttributes & SEC_PHYSICALMEMORY)
1879 {
1880 DPRINT1("Trying to page out from physical memory section address 0x%X "
1881 "process %d\n", Address,
1882 Process ? Process->UniqueProcessId : 0);
1883 KeBugCheck(MEMORY_MANAGEMENT);
1884 }
1885
1886 /*
1887 * Get the section segment entry and the physical address.
1888 */
1889 if (!MmIsPagePresent(Process, Address))
1890 {
1891 DPRINT1("Trying to page out not-present page at (%d,0x%.8X).\n",
1892 Process ? Process->UniqueProcessId : 0, Address);
1893 KeBugCheck(MEMORY_MANAGEMENT);
1894 }
1895 Page = MmGetPfnForProcess(Process, Address);
1896 SwapEntry = MmGetSavedSwapEntryPage(Page);
1897
1898 /*
1899 * Check the reference count to ensure this page can be paged out
1900 */
1901 if (MmGetReferenceCountPage(Page) != 1)
1902 {
1903 DPRINT("Cannot page out locked section page: 0x%p (RefCount: %d)\n",
1904 Page, MmGetReferenceCountPage(Page));
1905 MmSetPageEntrySectionSegment(Context.Segment, &Context.Offset, Entry);
1906 MmUnlockSectionSegment(Context.Segment);
1907 return STATUS_UNSUCCESSFUL;
1908 }
1909
1910 /*
1911 * Prepare the context structure for the rmap delete call.
1912 */
1913 MmUnlockSectionSegment(Context.Segment);
1914 Context.WasDirty = FALSE;
1915 if (Context.Segment->Image.Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA ||
1916 IS_SWAP_FROM_SSE(Entry) ||
1917 PFN_FROM_SSE(Entry) != Page)
1918 {
1919 Context.Private = TRUE;
1920 }
1921 else
1922 {
1923 Context.Private = FALSE;
1924 }
1925
1926 /*
1927 * Take an additional reference to the page or the cache segment.
1928 */
1929 if (DirectMapped && !Context.Private)
1930 {
1931 if(!MiIsPageFromCache(MemoryArea, Context.Offset.LowPart))
1932 {
1933 DPRINT1("Direct mapped non private page is not associated with the cache.\n");
1934 KeBugCheck(MEMORY_MANAGEMENT);
1935 }
1936 }
1937 else
1938 {
1939 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
1940 MmReferencePage(Page);
1941 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
1942 }
1943
1944 MmDeleteAllRmaps(Page, (PVOID)&Context, MmPageOutDeleteMapping);
1945
1946 /* Since we passed in a surrogate, we'll get back the page entry
1947 * state in our context. This is intended to make intermediate
1948 * decrements of share count not release the wait entry.
1949 */
1950 Entry = Context.SectionEntry;
1951
1952 /*
1953 * If this wasn't a private page then we should have reduced the entry to
1954 * zero by deleting all the rmaps.
1955 */
1956 if (!Context.Private && Entry != 0)
1957 {
1958 if (!(Context.Segment->Flags & MM_PAGEFILE_SEGMENT) &&
1959 !(Context.Segment->Image.Characteristics & IMAGE_SCN_MEM_SHARED))
1960 {
1961 KeBugCheckEx(MEMORY_MANAGEMENT, Entry, (ULONG_PTR)Process, (ULONG_PTR)Address, 0);
1962 }
1963 }
1964
1965 /*
1966 * If the page wasn't dirty then we can just free it as for a readonly page.
1967 * Since we unmapped all the mappings above we know it will not suddenly
1968 * become dirty.
1969 * If the page is from a pagefile section and has no swap entry,
1970 * we can't free the page at this point.
1971 */
1972 SwapEntry = MmGetSavedSwapEntryPage(Page);
1973 if (Context.Segment->Flags & MM_PAGEFILE_SEGMENT)
1974 {
1975 if (Context.Private)
1976 {
1977 DPRINT1("Found a %s private page (address %x) in a pagefile segment.\n",
1978 Context.WasDirty ? "dirty" : "clean", Address);
1979 KeBugCheckEx(MEMORY_MANAGEMENT, SwapEntry, (ULONG_PTR)Process, (ULONG_PTR)Address, 0);
1980 }
1981 if (!Context.WasDirty && SwapEntry != 0)
1982 {
1983 MmSetSavedSwapEntryPage(Page, 0);
1984 MmLockSectionSegment(Context.Segment);
1985 MmSetPageEntrySectionSegment(Context.Segment, &Context.Offset, MAKE_SWAP_SSE(SwapEntry));
1986 MmUnlockSectionSegment(Context.Segment);
1987 MmReleasePageMemoryConsumer(MC_USER, Page);
1988 MiSetPageEvent(NULL, NULL);
1989 return(STATUS_SUCCESS);
1990 }
1991 }
1992 else if (Context.Segment->Image.Characteristics & IMAGE_SCN_MEM_SHARED)
1993 {
1994 if (Context.Private)
1995 {
1996 DPRINT1("Found a %s private page (address %x) in a shared section segment.\n",
1997 Context.WasDirty ? "dirty" : "clean", Address);
1998 KeBugCheckEx(MEMORY_MANAGEMENT, Page, (ULONG_PTR)Process, (ULONG_PTR)Address, 0);
1999 }
2000 if (!Context.WasDirty || SwapEntry != 0)
2001 {
2002 MmSetSavedSwapEntryPage(Page, 0);
2003 if (SwapEntry != 0)
2004 {
2005 MmLockSectionSegment(Context.Segment);
2006 MmSetPageEntrySectionSegment(Context.Segment, &Context.Offset, MAKE_SWAP_SSE(SwapEntry));
2007 MmUnlockSectionSegment(Context.Segment);
2008 }
2009 MmReleasePageMemoryConsumer(MC_USER, Page);
2010 MiSetPageEvent(NULL, NULL);
2011 return(STATUS_SUCCESS);
2012 }
2013 }
2014 else if (!Context.Private && DirectMapped)
2015 {
2016 if (SwapEntry != 0)
2017 {
2018 DPRINT1("Found a swapentry for a non private and direct mapped page (address %x)\n",
2019 Address);
2020 KeBugCheckEx(MEMORY_MANAGEMENT, STATUS_UNSUCCESSFUL, SwapEntry, (ULONG_PTR)Process, (ULONG_PTR)Address);
2021 }
2022 #ifndef NEWCC
2023 Status = CcRosUnmapCacheSegment(Bcb, (ULONG)FileOffset, FALSE);
2024 #else
2025 Status = STATUS_SUCCESS;
2026 #endif
2027 #ifndef NEWCC
2028 if (!NT_SUCCESS(Status))
2029 {
2030 DPRINT1("CCRosUnmapCacheSegment failed, status = %x\n", Status);
2031 KeBugCheckEx(MEMORY_MANAGEMENT, Status, (ULONG_PTR)Bcb, (ULONG_PTR)FileOffset, (ULONG_PTR)Address);
2032 }
2033 #endif
2034 MiSetPageEvent(NULL, NULL);
2035 return(STATUS_SUCCESS);
2036 }
2037 else if (!Context.WasDirty && !DirectMapped && !Context.Private)
2038 {
2039 if (SwapEntry != 0)
2040 {
2041 DPRINT1("Found a swap entry for a non dirty, non private and not direct mapped page (address %x)\n",
2042 Address);
2043 KeBugCheckEx(MEMORY_MANAGEMENT, SwapEntry, Page, (ULONG_PTR)Process, (ULONG_PTR)Address);
2044 }
2045 MmReleasePageMemoryConsumer(MC_USER, Page);
2046 MiSetPageEvent(NULL, NULL);
2047 return(STATUS_SUCCESS);
2048 }
2049 else if (!Context.WasDirty && Context.Private && SwapEntry != 0)
2050 {
2051 DPRINT("Not dirty and private and not swapped (%p:%p)\n", Process, Address);
2052 MmSetSavedSwapEntryPage(Page, 0);
2053 MmLockAddressSpace(AddressSpace);
2054 Status = MmCreatePageFileMapping(Process,
2055 Address,
2056 SwapEntry);
2057 MmUnlockAddressSpace(AddressSpace);
2058 if (!NT_SUCCESS(Status))
2059 {
2060 DPRINT1("Status %x Swapping out %p:%p\n", Status, Process, Address);
2061 KeBugCheckEx(MEMORY_MANAGEMENT, Status, (ULONG_PTR)Process, (ULONG_PTR)Address, SwapEntry);
2062 }
2063 MmReleasePageMemoryConsumer(MC_USER, Page);
2064 MiSetPageEvent(NULL, NULL);
2065 return(STATUS_SUCCESS);
2066 }
2067
2068 /*
2069 * If necessary, allocate an entry in the paging file for this page
2070 */
2071 if (SwapEntry == 0)
2072 {
2073 SwapEntry = MmAllocSwapPage();
2074 if (SwapEntry == 0)
2075 {
2076 MmShowOutOfSpaceMessagePagingFile();
2077 MmLockAddressSpace(AddressSpace);
2078 /*
2079 * For private pages restore the old mappings.
2080 */
2081 if (Context.Private)
2082 {
2083 Status = MmCreateVirtualMapping(Process,
2084 Address,
2085 MemoryArea->Protect,
2086 &Page,
2087 1);
2088 MmSetDirtyPage(Process, Address);
2089 MmInsertRmap(Page,
2090 Process,
2091 Address);
2092 }
2093 else
2094 {
2095 ULONG_PTR OldEntry;
2096 /*
2097 * For non-private pages if the page wasn't direct mapped then
2098 * set it back into the section segment entry so we don't loose
2099 * our copy. Otherwise it will be handled by the cache manager.
2100 */
2101 Status = MmCreateVirtualMapping(Process,
2102 Address,
2103 MemoryArea->Protect,
2104 &Page,
2105 1);
2106 MmSetDirtyPage(Process, Address);
2107 MmInsertRmap(Page,
2108 Process,
2109 Address);
2110 // If we got here, the previous entry should have been a wait
2111 Entry = MAKE_SSE(Page << PAGE_SHIFT, 1);
2112 MmLockSectionSegment(Context.Segment);
2113 OldEntry = MmGetPageEntrySectionSegment(Context.Segment, &Context.Offset);
2114 ASSERT(OldEntry == 0 || OldEntry == MAKE_SWAP_SSE(MM_WAIT_ENTRY));
2115 MmSetPageEntrySectionSegment(Context.Segment, &Context.Offset, Entry);
2116 MmUnlockSectionSegment(Context.Segment);
2117 }
2118 MmUnlockAddressSpace(AddressSpace);
2119 MiSetPageEvent(NULL, NULL);
2120 return(STATUS_PAGEFILE_QUOTA);
2121 }
2122 }
2123
2124 /*
2125 * Write the page to the pagefile
2126 */
2127 Status = MmWriteToSwapPage(SwapEntry, Page);
2128 if (!NT_SUCCESS(Status))
2129 {
2130 DPRINT1("MM: Failed to write to swap page (Status was 0x%.8X)\n",
2131 Status);
2132 /*
2133 * As above: undo our actions.
2134 * FIXME: Also free the swap page.
2135 */
2136 MmLockAddressSpace(AddressSpace);
2137 if (Context.Private)
2138 {
2139 Status = MmCreateVirtualMapping(Process,
2140 Address,
2141 MemoryArea->Protect,
2142 &Page,
2143 1);
2144 MmSetDirtyPage(Process, Address);
2145 MmInsertRmap(Page,
2146 Process,
2147 Address);
2148 }
2149 else
2150 {
2151 Status = MmCreateVirtualMapping(Process,
2152 Address,
2153 MemoryArea->Protect,
2154 &Page,
2155 1);
2156 MmSetDirtyPage(Process, Address);
2157 MmInsertRmap(Page,
2158 Process,
2159 Address);
2160 Entry = MAKE_SSE(Page << PAGE_SHIFT, 1);
2161 MmSetPageEntrySectionSegment(Context.Segment, &Context.Offset, Entry);
2162 }
2163 MmUnlockAddressSpace(AddressSpace);
2164 MiSetPageEvent(NULL, NULL);
2165 return(STATUS_UNSUCCESSFUL);
2166 }
2167
2168 /*
2169 * Otherwise we have succeeded.
2170 */
2171 DPRINT("MM: Wrote section page 0x%.8X to swap!\n", Page << PAGE_SHIFT);
2172 MmSetSavedSwapEntryPage(Page, 0);
2173 if (Context.Segment->Flags & MM_PAGEFILE_SEGMENT ||
2174 Context.Segment->Image.Characteristics & IMAGE_SCN_MEM_SHARED)
2175 {
2176 MmLockSectionSegment(Context.Segment);
2177 MmSetPageEntrySectionSegment(Context.Segment, &Context.Offset, MAKE_SWAP_SSE(SwapEntry));
2178 MmUnlockSectionSegment(Context.Segment);
2179 }
2180 else
2181 {
2182 MmReleasePageMemoryConsumer(MC_USER, Page);
2183 }
2184
2185 if (Context.Private)
2186 {
2187 MmLockAddressSpace(AddressSpace);
2188 MmLockSectionSegment(Context.Segment);
2189 Status = MmCreatePageFileMapping(Process,
2190 Address,
2191 SwapEntry);
2192 MmUnlockSectionSegment(Context.Segment);
2193 MmUnlockAddressSpace(AddressSpace);
2194 if (!NT_SUCCESS(Status))
2195 {
2196 DPRINT1("Status %x Creating page file mapping for %p:%p\n", Status, Process, Address);
2197 KeBugCheckEx(MEMORY_MANAGEMENT, Status, (ULONG_PTR)Process, (ULONG_PTR)Address, SwapEntry);
2198 }
2199 }
2200 else
2201 {
2202 MmLockAddressSpace(AddressSpace);
2203 MmLockSectionSegment(Context.Segment);
2204 Entry = MAKE_SWAP_SSE(SwapEntry);
2205 MmSetPageEntrySectionSegment(Context.Segment, &Context.Offset, Entry);
2206 MmUnlockSectionSegment(Context.Segment);
2207 MmUnlockAddressSpace(AddressSpace);
2208 }
2209
2210 MiSetPageEvent(NULL, NULL);
2211 return(STATUS_SUCCESS);
2212 }
2213
2214 NTSTATUS
2215 NTAPI
2216 MmWritePageSectionView(PMMSUPPORT AddressSpace,
2217 PMEMORY_AREA MemoryArea,
2218 PVOID Address,
2219 ULONG PageEntry)
2220 {
2221 LARGE_INTEGER Offset;
2222 PROS_SECTION_OBJECT Section;
2223 PMM_SECTION_SEGMENT Segment;
2224 PFN_NUMBER Page;
2225 SWAPENTRY SwapEntry;
2226 ULONG_PTR Entry;
2227 BOOLEAN Private;
2228 NTSTATUS Status;
2229 PFILE_OBJECT FileObject;
2230 PBCB Bcb = NULL;
2231 BOOLEAN DirectMapped;
2232 BOOLEAN IsImageSection;
2233 PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
2234
2235 Address = (PVOID)PAGE_ROUND_DOWN(Address);
2236
2237 Offset.QuadPart = (ULONG_PTR)Address - (ULONG_PTR)MemoryArea->StartingAddress
2238 + MemoryArea->Data.SectionData.ViewOffset.QuadPart;
2239
2240 /*
2241 * Get the segment and section.
2242 */
2243 Segment = MemoryArea->Data.SectionData.Segment;
2244 Section = MemoryArea->Data.SectionData.Section;
2245 IsImageSection = Section->AllocationAttributes & SEC_IMAGE ? TRUE : FALSE;
2246
2247 FileObject = Section->FileObject;
2248 DirectMapped = FALSE;
2249 if (FileObject != NULL &&
2250 !(Segment->Image.Characteristics & IMAGE_SCN_MEM_SHARED))
2251 {
2252 Bcb = FileObject->SectionObjectPointer->SharedCacheMap;
2253
2254 /*
2255 * If the file system is letting us go directly to the cache and the
2256 * memory area was mapped at an offset in the file which is page aligned
2257 * then note this is a direct mapped page.
2258 */
2259 if (((Offset.QuadPart + Segment->Image.FileOffset) % PAGE_SIZE) == 0 &&
2260 (Offset.QuadPart + PAGE_SIZE <= Segment->RawLength.QuadPart || !IsImageSection))
2261 {
2262 DirectMapped = TRUE;
2263 }
2264 }
2265
2266 /*
2267 * This should never happen since mappings of physical memory are never
2268 * placed in the rmap lists.
2269 */
2270 if (Section->AllocationAttributes & SEC_PHYSICALMEMORY)
2271 {
2272 DPRINT1("Trying to write back page from physical memory mapped at %X "
2273 "process %d\n", Address,
2274 Process ? Process->UniqueProcessId : 0);
2275 KeBugCheck(MEMORY_MANAGEMENT);
2276 }
2277
2278 /*
2279 * Get the section segment entry and the physical address.
2280 */
2281 Entry = MmGetPageEntrySectionSegment(Segment, &Offset);
2282 if (!MmIsPagePresent(Process, Address))
2283 {
2284 DPRINT1("Trying to page out not-present page at (%d,0x%.8X).\n",
2285 Process ? Process->UniqueProcessId : 0, Address);
2286 KeBugCheck(MEMORY_MANAGEMENT);
2287 }
2288 Page = MmGetPfnForProcess(Process, Address);
2289 SwapEntry = MmGetSavedSwapEntryPage(Page);
2290
2291 /*
2292 * Check for a private (COWed) page.
2293 */
2294 if (Segment->Image.Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA ||
2295 IS_SWAP_FROM_SSE(Entry) ||
2296 PFN_FROM_SSE(Entry) != Page)
2297 {
2298 Private = TRUE;
2299 }
2300 else
2301 {
2302 Private = FALSE;
2303 }
2304
2305 /*
2306 * Speculatively set all mappings of the page to clean.
2307 */
2308 MmSetCleanAllRmaps(Page);
2309
2310 /*
2311 * If this page was direct mapped from the cache then the cache manager
2312 * will take care of writing it back to disk.
2313 */
2314 if (DirectMapped && !Private)
2315 {
2316 LARGE_INTEGER SOffset;
2317 ASSERT(SwapEntry == 0);
2318 SOffset.QuadPart = Offset.QuadPart + Segment->Image.FileOffset;
2319 #ifndef NEWCC
2320 CcRosMarkDirtyCacheSegment(Bcb, Offset.LowPart);
2321 #endif
2322 MmLockSectionSegment(Segment);
2323 MmSetPageEntrySectionSegment(Segment, &Offset, PageEntry);
2324 MmUnlockSectionSegment(Segment);
2325 MiSetPageEvent(NULL, NULL);
2326 return(STATUS_SUCCESS);
2327 }
2328
2329 /*
2330 * If necessary, allocate an entry in the paging file for this page
2331 */
2332 if (SwapEntry == 0)
2333 {
2334 SwapEntry = MmAllocSwapPage();
2335 if (SwapEntry == 0)
2336 {
2337 MmSetDirtyAllRmaps(Page);
2338 MiSetPageEvent(NULL, NULL);
2339 return(STATUS_PAGEFILE_QUOTA);
2340 }
2341 MmSetSavedSwapEntryPage(Page, SwapEntry);
2342 }
2343
2344 /*
2345 * Write the page to the pagefile
2346 */
2347 Status = MmWriteToSwapPage(SwapEntry, Page);
2348 if (!NT_SUCCESS(Status))
2349 {
2350 DPRINT1("MM: Failed to write to swap page (Status was 0x%.8X)\n",
2351 Status);
2352 MmSetDirtyAllRmaps(Page);
2353 MiSetPageEvent(NULL, NULL);
2354 return(STATUS_UNSUCCESSFUL);
2355 }
2356
2357 /*
2358 * Otherwise we have succeeded.
2359 */
2360 DPRINT("MM: Wrote section page 0x%.8X to swap!\n", Page << PAGE_SHIFT);
2361 MiSetPageEvent(NULL, NULL);
2362 return(STATUS_SUCCESS);
2363 }
2364
2365 static VOID
2366 MmAlterViewAttributes(PMMSUPPORT AddressSpace,
2367 PVOID BaseAddress,
2368 SIZE_T RegionSize,
2369 ULONG OldType,
2370 ULONG OldProtect,
2371 ULONG NewType,
2372 ULONG NewProtect)
2373 {
2374 PMEMORY_AREA MemoryArea;
2375 PMM_SECTION_SEGMENT Segment;
2376 BOOLEAN DoCOW = FALSE;
2377 ULONG i;
2378 PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
2379
2380 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, BaseAddress);
2381 ASSERT(MemoryArea != NULL);
2382 Segment = MemoryArea->Data.SectionData.Segment;
2383 MmLockSectionSegment(Segment);
2384
2385 if ((Segment->WriteCopy) &&
2386 (NewProtect == PAGE_READWRITE || NewProtect == PAGE_EXECUTE_READWRITE))
2387 {
2388 DoCOW = TRUE;
2389 }
2390
2391 if (OldProtect != NewProtect)
2392 {
2393 for (i = 0; i < PAGE_ROUND_UP(RegionSize) / PAGE_SIZE; i++)
2394 {
2395 SWAPENTRY SwapEntry;
2396 PVOID Address = (char*)BaseAddress + (i * PAGE_SIZE);
2397 ULONG Protect = NewProtect;
2398
2399 /* Wait for a wait entry to disappear */
2400 do {
2401 MmGetPageFileMapping(Process, Address, &SwapEntry);
2402 if (SwapEntry != MM_WAIT_ENTRY)
2403 break;
2404 MiWaitForPageEvent(Process, Address);
2405 } while (TRUE);
2406
2407 /*
2408 * If we doing COW for this segment then check if the page is
2409 * already private.
2410 */
2411 if (DoCOW && MmIsPagePresent(Process, Address))
2412 {
2413 LARGE_INTEGER Offset;
2414 ULONG_PTR Entry;
2415 PFN_NUMBER Page;
2416
2417 Offset.QuadPart = (ULONG_PTR)Address - (ULONG_PTR)MemoryArea->StartingAddress
2418 + MemoryArea->Data.SectionData.ViewOffset.QuadPart;
2419 Entry = MmGetPageEntrySectionSegment(Segment, &Offset);
2420 /*
2421 * An MM_WAIT_ENTRY is ok in this case... It'll just count as
2422 * IS_SWAP_FROM_SSE and we'll do the right thing.
2423 */
2424 Page = MmGetPfnForProcess(Process, Address);
2425
2426 Protect = PAGE_READONLY;
2427 if (Segment->Image.Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA ||
2428 IS_SWAP_FROM_SSE(Entry) ||
2429 PFN_FROM_SSE(Entry) != Page)
2430 {
2431 Protect = NewProtect;
2432 }
2433 }
2434
2435 if (MmIsPagePresent(Process, Address))
2436 {
2437 MmSetPageProtect(Process, Address,
2438 Protect);
2439 }
2440 }
2441 }
2442
2443 MmUnlockSectionSegment(Segment);
2444 }
2445
2446 NTSTATUS
2447 NTAPI
2448 MmProtectSectionView(PMMSUPPORT AddressSpace,
2449 PMEMORY_AREA MemoryArea,
2450 PVOID BaseAddress,
2451 SIZE_T Length,
2452 ULONG Protect,
2453 PULONG OldProtect)
2454 {
2455 PMM_REGION Region;
2456 NTSTATUS Status;
2457 ULONG_PTR MaxLength;
2458
2459 MaxLength = (ULONG_PTR)MemoryArea->EndingAddress - (ULONG_PTR)BaseAddress;
2460 if (Length > MaxLength)
2461 Length = (ULONG)MaxLength;
2462
2463 Region = MmFindRegion(MemoryArea->StartingAddress,
2464 &MemoryArea->Data.SectionData.RegionListHead,
2465 BaseAddress, NULL);
2466 ASSERT(Region != NULL);
2467
2468 if ((MemoryArea->Flags & SEC_NO_CHANGE) &&
2469 Region->Protect != Protect)
2470 {
2471 return STATUS_INVALID_PAGE_PROTECTION;
2472 }
2473
2474 *OldProtect = Region->Protect;
2475 Status = MmAlterRegion(AddressSpace, MemoryArea->StartingAddress,
2476 &MemoryArea->Data.SectionData.RegionListHead,
2477 BaseAddress, Length, Region->Type, Protect,
2478 MmAlterViewAttributes);
2479
2480 return(Status);
2481 }
2482
2483 NTSTATUS NTAPI
2484 MmQuerySectionView(PMEMORY_AREA MemoryArea,
2485 PVOID Address,
2486 PMEMORY_BASIC_INFORMATION Info,
2487 PSIZE_T ResultLength)
2488 {
2489 PMM_REGION Region;
2490 PVOID RegionBaseAddress;
2491 PROS_SECTION_OBJECT Section;
2492 PMM_SECTION_SEGMENT Segment;
2493
2494 Region = MmFindRegion((PVOID)MemoryArea->StartingAddress,
2495 &MemoryArea->Data.SectionData.RegionListHead,
2496 Address, &RegionBaseAddress);
2497 if (Region == NULL)
2498 {
2499 return STATUS_UNSUCCESSFUL;
2500 }
2501
2502 Section = MemoryArea->Data.SectionData.Section;
2503 if (Section->AllocationAttributes & SEC_IMAGE)
2504 {
2505 Segment = MemoryArea->Data.SectionData.Segment;
2506 Info->AllocationBase = (PUCHAR)MemoryArea->StartingAddress - Segment->Image.VirtualAddress;
2507 Info->Type = MEM_IMAGE;
2508 }
2509 else
2510 {
2511 Info->AllocationBase = MemoryArea->StartingAddress;
2512 Info->Type = MEM_MAPPED;
2513 }
2514 Info->BaseAddress = RegionBaseAddress;
2515 Info->AllocationProtect = MemoryArea->Protect;
2516 Info->RegionSize = Region->Length;
2517 Info->State = MEM_COMMIT;
2518 Info->Protect = Region->Protect;
2519
2520 *ResultLength = sizeof(MEMORY_BASIC_INFORMATION);
2521 return(STATUS_SUCCESS);
2522 }
2523
2524 VOID
2525 NTAPI
2526 MmpFreePageFileSegment(PMM_SECTION_SEGMENT Segment)
2527 {
2528 ULONG Length;
2529 LARGE_INTEGER Offset;
2530 ULONG_PTR Entry;
2531 SWAPENTRY SavedSwapEntry;
2532 PFN_NUMBER Page;
2533
2534 Page = 0;
2535
2536 MmLockSectionSegment(Segment);
2537
2538 Length = PAGE_ROUND_UP(Segment->Length.QuadPart);
2539 for (Offset.QuadPart = 0; Offset.QuadPart < Length; Offset.QuadPart += PAGE_SIZE)
2540 {
2541 Entry = MmGetPageEntrySectionSegment(Segment, &Offset);
2542 if (Entry)
2543 {
2544 MmSetPageEntrySectionSegment(Segment, &Offset, 0);
2545 if (IS_SWAP_FROM_SSE(Entry))
2546 {
2547 MmFreeSwapPage(SWAPENTRY_FROM_SSE(Entry));
2548 }
2549 else
2550 {
2551 Page = PFN_FROM_SSE(Entry);
2552 SavedSwapEntry = MmGetSavedSwapEntryPage(Page);
2553 if (SavedSwapEntry != 0)
2554 {
2555 MmSetSavedSwapEntryPage(Page, 0);
2556 MmFreeSwapPage(SavedSwapEntry);
2557 }
2558 MmReleasePageMemoryConsumer(MC_USER, Page);
2559 }
2560 }
2561 }
2562
2563 MmUnlockSectionSegment(Segment);
2564 }
2565
2566 VOID NTAPI
2567 MmpDeleteSection(PVOID ObjectBody)
2568 {
2569 PROS_SECTION_OBJECT Section = (PROS_SECTION_OBJECT)ObjectBody;
2570
2571 DPRINT("MmpDeleteSection(ObjectBody %p)\n", ObjectBody);
2572 if (Section->AllocationAttributes & SEC_IMAGE)
2573 {
2574 ULONG i;
2575 ULONG NrSegments;
2576 ULONG RefCount;
2577 PMM_SECTION_SEGMENT SectionSegments;
2578
2579 /*
2580 * NOTE: Section->ImageSection can be NULL for short time
2581 * during the section creating. If we fail for some reason
2582 * until the image section is properly initialized we shouldn't
2583 * process further here.
2584 */
2585 if (Section->ImageSection == NULL)
2586 return;
2587
2588 SectionSegments = Section->ImageSection->Segments;
2589 NrSegments = Section->ImageSection->NrSegments;
2590
2591 for (i = 0; i < NrSegments; i++)
2592 {
2593 if (SectionSegments[i].Image.Characteristics & IMAGE_SCN_MEM_SHARED)
2594 {
2595 MmLockSectionSegment(&SectionSegments[i]);
2596 }
2597 RefCount = InterlockedDecrementUL(&SectionSegments[i].ReferenceCount);
2598 if (SectionSegments[i].Image.Characteristics & IMAGE_SCN_MEM_SHARED)
2599 {
2600 MmUnlockSectionSegment(&SectionSegments[i]);
2601 if (RefCount == 0)
2602 {
2603 MmpFreePageFileSegment(&SectionSegments[i]);
2604 }
2605 }
2606 }
2607 }
2608 #ifdef NEWCC
2609 else if (Section->Segment && Section->Segment->Flags & MM_DATAFILE_SEGMENT)
2610 {
2611 ULONG RefCount = 0;
2612 PMM_SECTION_SEGMENT Segment = Section->Segment;
2613
2614 if (Segment &&
2615 (RefCount = InterlockedDecrementUL(&Segment->ReferenceCount)) == 0)
2616 {
2617 DPRINT("Freeing section segment\n");
2618 Section->Segment = NULL;
2619 MmFinalizeSegment(Segment);
2620 }
2621 else
2622 {
2623 DPRINT("RefCount %d\n", RefCount);
2624 }
2625 }
2626 #endif
2627 else
2628 {
2629 /*
2630 * NOTE: Section->Segment can be NULL for short time
2631 * during the section creating.
2632 */
2633 if (Section->Segment == NULL)
2634 return;
2635
2636 if (Section->Segment->Flags & MM_PAGEFILE_SEGMENT)
2637 {
2638 MmpFreePageFileSegment(Section->Segment);
2639 MmFreePageTablesSectionSegment(Section->Segment, NULL);
2640 ExFreePool(Section->Segment);
2641 Section->Segment = NULL;
2642 }
2643 else
2644 {
2645 (void)InterlockedDecrementUL(&Section->Segment->ReferenceCount);
2646 }
2647 }
2648 if (Section->FileObject != NULL)
2649 {
2650 #ifndef NEWCC
2651 CcRosDereferenceCache(Section->FileObject);
2652 #endif
2653 ObDereferenceObject(Section->FileObject);
2654 Section->FileObject = NULL;
2655 }
2656 }
2657
2658 VOID NTAPI
2659 MmpCloseSection(IN PEPROCESS Process OPTIONAL,
2660 IN PVOID Object,
2661 IN ACCESS_MASK GrantedAccess,
2662 IN ULONG ProcessHandleCount,
2663 IN ULONG SystemHandleCount)
2664 {
2665 DPRINT("MmpCloseSection(OB %x, HC %d)\n",
2666 Object, ProcessHandleCount);
2667 }
2668
2669 NTSTATUS
2670 INIT_FUNCTION
2671 NTAPI
2672 MmCreatePhysicalMemorySection(VOID)
2673 {
2674 PROS_SECTION_OBJECT PhysSection;
2675 NTSTATUS Status;
2676 OBJECT_ATTRIBUTES Obj;
2677 UNICODE_STRING Name = RTL_CONSTANT_STRING(L"\\Device\\PhysicalMemory");
2678 LARGE_INTEGER SectionSize;
2679 HANDLE Handle;
2680
2681 /*
2682 * Create the section mapping physical memory
2683 */
2684 SectionSize.QuadPart = 0xFFFFFFFF;
2685 InitializeObjectAttributes(&Obj,
2686 &Name,
2687 OBJ_PERMANENT,
2688 NULL,
2689 NULL);
2690 Status = MmCreateSection((PVOID)&PhysSection,
2691 SECTION_ALL_ACCESS,
2692 &Obj,
2693 &SectionSize,
2694 PAGE_EXECUTE_READWRITE,
2695 0,
2696 NULL,
2697 NULL);
2698 if (!NT_SUCCESS(Status))
2699 {
2700 DPRINT1("Failed to create PhysicalMemory section\n");
2701 KeBugCheck(MEMORY_MANAGEMENT);
2702 }
2703 Status = ObInsertObject(PhysSection,
2704 NULL,
2705 SECTION_ALL_ACCESS,
2706 0,
2707 NULL,
2708 &Handle);
2709 if (!NT_SUCCESS(Status))
2710 {
2711 ObDereferenceObject(PhysSection);
2712 }
2713 ObCloseHandle(Handle, KernelMode);
2714 PhysSection->AllocationAttributes |= SEC_PHYSICALMEMORY;
2715 PhysSection->Segment->Flags &= ~MM_PAGEFILE_SEGMENT;
2716
2717 return(STATUS_SUCCESS);
2718 }
2719
2720 NTSTATUS
2721 INIT_FUNCTION
2722 NTAPI
2723 MmInitSectionImplementation(VOID)
2724 {
2725 OBJECT_TYPE_INITIALIZER ObjectTypeInitializer;
2726 UNICODE_STRING Name;
2727
2728 DPRINT("Creating Section Object Type\n");
2729
2730 /* Initialize the section based root */
2731 ASSERT(MmSectionBasedRoot.NumberGenericTableElements == 0);
2732 MmSectionBasedRoot.BalancedRoot.u1.Parent = &MmSectionBasedRoot.BalancedRoot;
2733
2734 /* Initialize the Section object type */
2735 RtlZeroMemory(&ObjectTypeInitializer, sizeof(ObjectTypeInitializer));
2736 RtlInitUnicodeString(&Name, L"Section");
2737 ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer);
2738 ObjectTypeInitializer.DefaultPagedPoolCharge = sizeof(ROS_SECTION_OBJECT);
2739 ObjectTypeInitializer.PoolType = PagedPool;
2740 ObjectTypeInitializer.UseDefaultObject = TRUE;
2741 ObjectTypeInitializer.GenericMapping = MmpSectionMapping;
2742 ObjectTypeInitializer.DeleteProcedure = MmpDeleteSection;
2743 ObjectTypeInitializer.CloseProcedure = MmpCloseSection;
2744 ObjectTypeInitializer.ValidAccessMask = SECTION_ALL_ACCESS;
2745 ObCreateObjectType(&Name, &ObjectTypeInitializer, NULL, &MmSectionObjectType);
2746
2747 MmCreatePhysicalMemorySection();
2748
2749 return(STATUS_SUCCESS);
2750 }
2751
2752 NTSTATUS
2753 NTAPI
2754 MmCreatePageFileSection(PROS_SECTION_OBJECT *SectionObject,
2755 ACCESS_MASK DesiredAccess,
2756 POBJECT_ATTRIBUTES ObjectAttributes,
2757 PLARGE_INTEGER UMaximumSize,
2758 ULONG SectionPageProtection,
2759 ULONG AllocationAttributes)
2760 /*
2761 * Create a section which is backed by the pagefile
2762 */
2763 {
2764 LARGE_INTEGER MaximumSize;
2765 PROS_SECTION_OBJECT Section;
2766 PMM_SECTION_SEGMENT Segment;
2767 NTSTATUS Status;
2768
2769 if (UMaximumSize == NULL)
2770 {
2771 return(STATUS_UNSUCCESSFUL);
2772 }
2773 MaximumSize = *UMaximumSize;
2774
2775 /*
2776 * Create the section
2777 */
2778 Status = ObCreateObject(ExGetPreviousMode(),
2779 MmSectionObjectType,
2780 ObjectAttributes,
2781 ExGetPreviousMode(),
2782 NULL,
2783 sizeof(ROS_SECTION_OBJECT),
2784 0,
2785 0,
2786 (PVOID*)(PVOID)&Section);
2787 if (!NT_SUCCESS(Status))
2788 {
2789 return(Status);
2790 }
2791
2792 /*
2793 * Initialize it
2794 */
2795 RtlZeroMemory(Section, sizeof(ROS_SECTION_OBJECT));
2796 Section->Type = 'SC';
2797 Section->Size = 'TN';
2798 Section->SectionPageProtection = SectionPageProtection;
2799 Section->AllocationAttributes = AllocationAttributes;
2800 Section->MaximumSize = MaximumSize;
2801 Segment = ExAllocatePoolWithTag(NonPagedPool, sizeof(MM_SECTION_SEGMENT),
2802 TAG_MM_SECTION_SEGMENT);
2803 if (Segment == NULL)
2804 {
2805 ObDereferenceObject(Section);
2806 return(STATUS_NO_MEMORY);
2807 }
2808 RtlZeroMemory(Segment, sizeof(MM_SECTION_SEGMENT));
2809 Section->Segment = Segment;
2810 Segment->ReferenceCount = 1;
2811 ExInitializeFastMutex(&Segment->Lock);
2812 Segment->Image.FileOffset = 0;
2813 Segment->Protection = SectionPageProtection;
2814 Segment->RawLength.QuadPart = MaximumSize.u.LowPart;
2815 Segment->Length.QuadPart = PAGE_ROUND_UP(MaximumSize.u.LowPart);
2816 Segment->Flags = MM_PAGEFILE_SEGMENT;
2817 Segment->WriteCopy = FALSE;
2818 Segment->Image.VirtualAddress = 0;
2819 Segment->Image.Characteristics = 0;
2820 *SectionObject = Section;
2821 MiInitializeSectionPageTable(Segment);
2822 return(STATUS_SUCCESS);
2823 }
2824
2825 NTSTATUS
2826 NTAPI
2827 MmCreateDataFileSection(PROS_SECTION_OBJECT *SectionObject,
2828 ACCESS_MASK DesiredAccess,
2829 POBJECT_ATTRIBUTES ObjectAttributes,
2830 PLARGE_INTEGER UMaximumSize,
2831 ULONG SectionPageProtection,
2832 ULONG AllocationAttributes,
2833 HANDLE FileHandle)
2834 /*
2835 * Create a section backed by a data file
2836 */
2837 {
2838 PROS_SECTION_OBJECT Section;
2839 NTSTATUS Status;
2840 LARGE_INTEGER MaximumSize;
2841 PFILE_OBJECT FileObject;
2842 PMM_SECTION_SEGMENT Segment;
2843 ULONG FileAccess;
2844 IO_STATUS_BLOCK Iosb;
2845 LARGE_INTEGER Offset;
2846 CHAR Buffer;
2847 FILE_STANDARD_INFORMATION FileInfo;
2848 ULONG Length;
2849
2850 /*
2851 * Create the section
2852 */
2853 Status = ObCreateObject(ExGetPreviousMode(),
2854 MmSectionObjectType,
2855 ObjectAttributes,
2856 ExGetPreviousMode(),
2857 NULL,
2858 sizeof(ROS_SECTION_OBJECT),
2859 0,
2860 0,
2861 (PVOID*)&Section);
2862 if (!NT_SUCCESS(Status))
2863 {
2864 return(Status);
2865 }
2866 /*
2867 * Initialize it
2868 */
2869 RtlZeroMemory(Section, sizeof(ROS_SECTION_OBJECT));
2870 Section->Type = 'SC';
2871 Section->Size = 'TN';
2872 Section->SectionPageProtection = SectionPageProtection;
2873 Section->AllocationAttributes = AllocationAttributes;
2874
2875 /*
2876 * Reference the file handle
2877 */
2878 FileAccess = MiArm3GetCorrectFileAccessMask(SectionPageProtection);
2879 Status = ObReferenceObjectByHandle(FileHandle,
2880 FileAccess,
2881 IoFileObjectType,
2882 ExGetPreviousMode(),
2883 (PVOID*)(PVOID)&FileObject,
2884 NULL);
2885 if (!NT_SUCCESS(Status))
2886 {
2887 ObDereferenceObject(Section);
2888 return(Status);
2889 }
2890
2891 /*
2892 * FIXME: This is propably not entirely correct. We can't look into
2893 * the standard FCB header because it might not be initialized yet
2894 * (as in case of the EXT2FS driver by Manoj Paul Joseph where the
2895 * standard file information is filled on first request).
2896 */
2897 Status = IoQueryFileInformation(FileObject,
2898 FileStandardInformation,
2899 sizeof(FILE_STANDARD_INFORMATION),
2900 &FileInfo,
2901 &Length);
2902 Iosb.Information = Length;
2903 if (!NT_SUCCESS(Status))
2904 {
2905 ObDereferenceObject(Section);
2906 ObDereferenceObject(FileObject);
2907 return Status;
2908 }
2909
2910 /*
2911 * FIXME: Revise this once a locking order for file size changes is
2912 * decided
2913 */
2914 if ((UMaximumSize != NULL) && (UMaximumSize->QuadPart != 0))
2915 {
2916 MaximumSize = *UMaximumSize;
2917 }
2918 else
2919 {
2920 MaximumSize = FileInfo.EndOfFile;
2921 /* Mapping zero-sized files isn't allowed. */
2922 if (MaximumSize.QuadPart == 0)
2923 {
2924 ObDereferenceObject(Section);
2925 ObDereferenceObject(FileObject);
2926 return STATUS_FILE_INVALID;
2927 }
2928 }
2929
2930 if (MaximumSize.QuadPart > FileInfo.EndOfFile.QuadPart)
2931 {
2932 Status = IoSetInformation(FileObject,
2933 FileAllocationInformation,
2934 sizeof(LARGE_INTEGER),
2935 &MaximumSize);
2936 if (!NT_SUCCESS(Status))
2937 {
2938 ObDereferenceObject(Section);
2939 ObDereferenceObject(FileObject);
2940 return(STATUS_SECTION_NOT_EXTENDED);
2941 }
2942 }
2943
2944 if (FileObject->SectionObjectPointer == NULL ||
2945 FileObject->SectionObjectPointer->SharedCacheMap == NULL)
2946 {
2947 /*
2948 * Read a bit so caching is initiated for the file object.
2949 * This is only needed because MiReadPage currently cannot
2950 * handle non-cached streams.
2951 */
2952 Offset.QuadPart = 0;
2953 Status = ZwReadFile(FileHandle,
2954 NULL,
2955 NULL,
2956 NULL,
2957 &Iosb,
2958 &Buffer,
2959 sizeof (Buffer),
2960 &Offset,
2961 0);
2962 if (!NT_SUCCESS(Status) && (Status != STATUS_END_OF_FILE))
2963 {
2964 ObDereferenceObject(Section);
2965 ObDereferenceObject(FileObject);
2966 return(Status);
2967 }
2968 if (FileObject->SectionObjectPointer == NULL ||
2969 FileObject->SectionObjectPointer->SharedCacheMap == NULL)
2970 {
2971 /* FIXME: handle this situation */
2972 ObDereferenceObject(Section);
2973 ObDereferenceObject(FileObject);
2974 return STATUS_INVALID_PARAMETER;
2975 }
2976 }
2977
2978 /*
2979 * Lock the file
2980 */
2981 Status = MmspWaitForFileLock(FileObject);
2982 if (Status != STATUS_SUCCESS)
2983 {
2984 ObDereferenceObject(Section);
2985 ObDereferenceObject(FileObject);
2986 return(Status);
2987 }
2988
2989 /*
2990 * If this file hasn't been mapped as a data file before then allocate a
2991 * section segment to describe the data file mapping
2992 */
2993 if (FileObject->SectionObjectPointer->DataSectionObject == NULL)
2994 {
2995 Segment = ExAllocatePoolWithTag(NonPagedPool, sizeof(MM_SECTION_SEGMENT),
2996 TAG_MM_SECTION_SEGMENT);
2997 if (Segment == NULL)
2998 {
2999 //KeSetEvent((PVOID)&FileObject->Lock, IO_NO_INCREMENT, FALSE);
3000 ObDereferenceObject(Section);
3001 ObDereferenceObject(FileObject);
3002 return(STATUS_NO_MEMORY);
3003 }
3004 Section->Segment = Segment;
3005 Segment->ReferenceCount = 1;
3006 ExInitializeFastMutex(&Segment->Lock);
3007 /*
3008 * Set the lock before assigning the segment to the file object
3009 */
3010 ExAcquireFastMutex(&Segment->Lock);
3011 FileObject->SectionObjectPointer->DataSectionObject = (PVOID)Segment;
3012
3013 Segment->Image.FileOffset = 0;
3014 Segment->Protection = SectionPageProtection;
3015 Segment->Flags = MM_DATAFILE_SEGMENT;
3016 Segment->Image.Characteristics = 0;
3017 Segment->WriteCopy = (SectionPageProtection & (PAGE_WRITECOPY | PAGE_EXECUTE_WRITECOPY));
3018 if (AllocationAttributes & SEC_RESERVE)
3019 {
3020 Segment->Length.QuadPart = Segment->RawLength.QuadPart = 0;
3021 }
3022 else
3023 {
3024 Segment->RawLength.QuadPart = MaximumSize.QuadPart;
3025 Segment->Length.QuadPart = PAGE_ROUND_UP(Segment->RawLength.QuadPart);
3026 }
3027 Segment->Image.VirtualAddress = 0;
3028 Segment->Locked = TRUE;
3029 MiInitializeSectionPageTable(Segment);
3030 }
3031 else
3032 {
3033 /*
3034 * If the file is already mapped as a data file then we may need
3035 * to extend it
3036 */
3037 Segment =
3038 (PMM_SECTION_SEGMENT)FileObject->SectionObjectPointer->
3039 DataSectionObject;
3040 Section->Segment = Segment;
3041 (void)InterlockedIncrementUL(&Segment->ReferenceCount);
3042 MmLockSectionSegment(Segment);
3043
3044 if (MaximumSize.QuadPart > Segment->RawLength.QuadPart &&
3045 !(AllocationAttributes & SEC_RESERVE))
3046 {
3047 Segment->RawLength.QuadPart = MaximumSize.QuadPart;
3048 Segment->Length.QuadPart = PAGE_ROUND_UP(Segment->RawLength.QuadPart);
3049 }
3050 }
3051 MmUnlockSectionSegment(Segment);
3052 Section->FileObject = FileObject;
3053 Section->MaximumSize = MaximumSize;
3054 #ifndef NEWCC
3055 CcRosReferenceCache(FileObject);
3056 #endif
3057 //KeSetEvent((PVOID)&FileObject->Lock, IO_NO_INCREMENT, FALSE);
3058 *SectionObject = Section;
3059 return(STATUS_SUCCESS);
3060 }
3061
3062 /*
3063 TODO: not that great (declaring loaders statically, having to declare all of
3064 them, having to keep them extern, etc.), will fix in the future
3065 */
3066 extern NTSTATUS NTAPI PeFmtCreateSection
3067 (
3068 IN CONST VOID * FileHeader,
3069 IN SIZE_T FileHeaderSize,
3070 IN PVOID File,
3071 OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
3072 OUT PULONG Flags,
3073 IN PEXEFMT_CB_READ_FILE ReadFileCb,
3074 IN PEXEFMT_CB_ALLOCATE_SEGMENTS AllocateSegmentsCb
3075 );
3076
3077 extern NTSTATUS NTAPI ElfFmtCreateSection
3078 (
3079 IN CONST VOID * FileHeader,
3080 IN SIZE_T FileHeaderSize,
3081 IN PVOID File,
3082 OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
3083 OUT PULONG Flags,
3084 IN PEXEFMT_CB_READ_FILE ReadFileCb,
3085 IN PEXEFMT_CB_ALLOCATE_SEGMENTS AllocateSegmentsCb
3086 );
3087
3088 /* TODO: this is a standard DDK/PSDK macro */
3089 #ifndef RTL_NUMBER_OF
3090 #define RTL_NUMBER_OF(ARR_) (sizeof(ARR_) / sizeof((ARR_)[0]))
3091 #endif
3092
3093 static PEXEFMT_LOADER ExeFmtpLoaders[] =
3094 {
3095 PeFmtCreateSection,
3096 #ifdef __ELF
3097 ElfFmtCreateSection
3098 #endif
3099 };
3100
3101 static
3102 PMM_SECTION_SEGMENT
3103 NTAPI
3104 ExeFmtpAllocateSegments(IN ULONG NrSegments)
3105 {
3106 SIZE_T SizeOfSegments;
3107 PMM_SECTION_SEGMENT Segments;
3108
3109 /* TODO: check for integer overflow */
3110 SizeOfSegments = sizeof(MM_SECTION_SEGMENT) * NrSegments;
3111
3112 Segments = ExAllocatePoolWithTag(NonPagedPool,
3113 SizeOfSegments,
3114 TAG_MM_SECTION_SEGMENT);
3115
3116 if(Segments)
3117 RtlZeroMemory(Segments, SizeOfSegments);
3118
3119 return Segments;
3120 }
3121
3122 static
3123 NTSTATUS
3124 NTAPI
3125 ExeFmtpReadFile(IN PVOID File,
3126 IN PLARGE_INTEGER Offset,
3127 IN ULONG Length,
3128 OUT PVOID * Data,
3129 OUT PVOID * AllocBase,
3130 OUT PULONG ReadSize)
3131 {
3132 NTSTATUS Status;
3133 LARGE_INTEGER FileOffset;
3134 ULONG AdjustOffset;
3135 ULONG OffsetAdjustment;
3136 ULONG BufferSize;
3137 ULONG UsedSize;
3138 PVOID Buffer;
3139 PFILE_OBJECT FileObject = File;
3140 IO_STATUS_BLOCK Iosb;
3141
3142 ASSERT_IRQL_LESS(DISPATCH_LEVEL);
3143
3144 if(Length == 0)
3145 {
3146 KeBugCheck(MEMORY_MANAGEMENT);
3147 }
3148
3149 FileOffset = *Offset;
3150
3151 /* Negative/special offset: it cannot be used in this context */
3152 if(FileOffset.u.HighPart < 0)
3153 {
3154 KeBugCheck(MEMORY_MANAGEMENT);
3155 }
3156
3157 AdjustOffset = PAGE_ROUND_DOWN(FileOffset.u.LowPart);
3158 OffsetAdjustment = FileOffset.u.LowPart - AdjustOffset;
3159 FileOffset.u.LowPart = AdjustOffset;
3160
3161 BufferSize = Length + OffsetAdjustment;
3162 BufferSize = PAGE_ROUND_UP(BufferSize);
3163
3164 /*
3165 * It's ok to use paged pool, because this is a temporary buffer only used in
3166 * the loading of executables. The assumption is that MmCreateSection is
3167 * always called at low IRQLs and that these buffers don't survive a brief
3168 * initialization phase
3169 */
3170 Buffer = ExAllocatePoolWithTag(PagedPool,
3171 BufferSize,
3172 'rXmM');
3173 if (!Buffer)
3174 {
3175 KeBugCheck(MEMORY_MANAGEMENT);
3176 }
3177
3178 UsedSize = 0;
3179
3180 Status = MiSimpleRead(FileObject, &FileOffset, Buffer, BufferSize, TRUE, &Iosb);
3181
3182 UsedSize = (ULONG)Iosb.Information;
3183
3184 if(NT_SUCCESS(Status) && UsedSize < OffsetAdjustment)
3185 {
3186 Status = STATUS_IN_PAGE_ERROR;
3187 ASSERT(!NT_SUCCESS(Status));
3188 }
3189
3190 if(NT_SUCCESS(Status))
3191 {
3192 *Data = (PVOID)((ULONG_PTR)Buffer + OffsetAdjustment);
3193 *AllocBase = Buffer;
3194 *ReadSize = UsedSize - OffsetAdjustment;
3195 }
3196 else
3197 {
3198 ExFreePoolWithTag(Buffer, 'rXmM');
3199 }
3200
3201 return Status;
3202 }
3203
3204 #ifdef NASSERT
3205 # define MmspAssertSegmentsSorted(OBJ_) ((void)0)
3206 # define MmspAssertSegmentsNoOverlap(OBJ_) ((void)0)
3207 # define MmspAssertSegmentsPageAligned(OBJ_) ((void)0)
3208 #else
3209 static
3210 VOID
3211 NTAPI
3212 MmspAssertSegmentsSorted(IN PMM_IMAGE_SECTION_OBJECT ImageSectionObject)
3213 {
3214 ULONG i;
3215
3216 for( i = 1; i < ImageSectionObject->NrSegments; ++ i )
3217 {
3218 ASSERT(ImageSectionObject->Segments[i].Image.VirtualAddress >=
3219 ImageSectionObject->Segments[i - 1].Image.VirtualAddress);
3220 }
3221 }
3222
3223 static
3224 VOID
3225 NTAPI
3226 MmspAssertSegmentsNoOverlap(IN PMM_IMAGE_SECTION_OBJECT ImageSectionObject)
3227 {
3228 ULONG i;
3229
3230 MmspAssertSegmentsSorted(ImageSectionObject);
3231
3232 for( i = 0; i < ImageSectionObject->NrSegments; ++ i )
3233 {
3234 ASSERT(ImageSectionObject->Segments[i].Length.QuadPart > 0);
3235
3236 if(i > 0)
3237 {
3238 ASSERT(ImageSectionObject->Segments[i].Image.VirtualAddress >=
3239 (ImageSectionObject->Segments[i - 1].Image.VirtualAddress +
3240 ImageSectionObject->Segments[i - 1].Length.QuadPart));
3241 }
3242 }
3243 }
3244
3245 static
3246 VOID
3247 NTAPI
3248 MmspAssertSegmentsPageAligned(IN PMM_IMAGE_SECTION_OBJECT ImageSectionObject)
3249 {
3250 ULONG i;
3251
3252 for( i = 0; i < ImageSectionObject->NrSegments; ++ i )
3253 {
3254 ASSERT((ImageSectionObject->Segments[i].Image.VirtualAddress % PAGE_SIZE) == 0);
3255 ASSERT((ImageSectionObject->Segments[i].Length.QuadPart % PAGE_SIZE) == 0);
3256 }
3257 }
3258 #endif
3259
3260 static
3261 int
3262 __cdecl
3263 MmspCompareSegments(const void * x,
3264 const void * y)
3265 {
3266 const MM_SECTION_SEGMENT *Segment1 = (const MM_SECTION_SEGMENT *)x;
3267 const MM_SECTION_SEGMENT *Segment2 = (const MM_SECTION_SEGMENT *)y;
3268
3269 return
3270 (Segment1->Image.VirtualAddress - Segment2->Image.VirtualAddress) >>
3271 ((sizeof(ULONG_PTR) - sizeof(int)) * 8);
3272 }
3273
3274 /*
3275 * Ensures an image section's segments are sorted in memory
3276 */
3277 static
3278 VOID
3279 NTAPI
3280 MmspSortSegments(IN OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
3281 IN ULONG Flags)
3282 {
3283 if (Flags & EXEFMT_LOAD_ASSUME_SEGMENTS_SORTED)
3284 {
3285 MmspAssertSegmentsSorted(ImageSectionObject);
3286 }
3287 else
3288 {
3289 qsort(ImageSectionObject->Segments,
3290 ImageSectionObject->NrSegments,
3291 sizeof(ImageSectionObject->Segments[0]),
3292 MmspCompareSegments);
3293 }
3294 }
3295
3296
3297 /*
3298 * Ensures an image section's segments don't overlap in memory and don't have
3299 * gaps and don't have a null size. We let them map to overlapping file regions,
3300 * though - that's not necessarily an error
3301 */
3302 static
3303 BOOLEAN
3304 NTAPI
3305 MmspCheckSegmentBounds
3306 (
3307 IN OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
3308 IN ULONG Flags
3309 )
3310 {
3311 ULONG i;
3312
3313 if (Flags & EXEFMT_LOAD_ASSUME_SEGMENTS_NO_OVERLAP)
3314 {
3315 MmspAssertSegmentsNoOverlap(ImageSectionObject);
3316 return TRUE;
3317 }
3318
3319 ASSERT(ImageSectionObject->NrSegments >= 1);
3320
3321 for ( i = 0; i < ImageSectionObject->NrSegments; ++ i )
3322 {
3323 if(ImageSectionObject->Segments[i].Length.QuadPart == 0)
3324 {
3325 return FALSE;
3326 }
3327
3328 if(i > 0)
3329 {
3330 /*
3331 * TODO: relax the limitation on gaps. For example, gaps smaller than a
3332 * page could be OK (Windows seems to be OK with them), and larger gaps
3333 * could lead to image sections spanning several discontiguous regions
3334 * (NtMapViewOfSection could then refuse to map them, and they could
3335 * e.g. only be allowed as parameters to NtCreateProcess, like on UNIX)
3336 */
3337 if ((ImageSectionObject->Segments[i - 1].Image.VirtualAddress +
3338 ImageSectionObject->Segments[i - 1].Length.QuadPart) !=
3339 ImageSectionObject->Segments[i].Image.VirtualAddress)
3340 {
3341 return FALSE;
3342 }
3343 }
3344 }
3345
3346 return TRUE;
3347 }
3348
3349 /*
3350 * Merges and pads an image section's segments until they all are page-aligned
3351 * and have a size that is a multiple of the page size
3352 */
3353 static
3354 BOOLEAN
3355 NTAPI
3356 MmspPageAlignSegments
3357 (
3358 IN OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
3359 IN ULONG Flags
3360 )
3361 {
3362 ULONG i;
3363 ULONG LastSegment;
3364 PMM_SECTION_SEGMENT EffectiveSegment;
3365
3366 if (Flags & EXEFMT_LOAD_ASSUME_SEGMENTS_PAGE_ALIGNED)
3367 {
3368 MmspAssertSegmentsPageAligned(ImageSectionObject);
3369 return TRUE;
3370 }
3371
3372 LastSegment = 0;
3373 EffectiveSegment = &ImageSectionObject->Segments[LastSegment];
3374
3375 for ( i = 0; i < ImageSectionObject->NrSegments; ++ i )
3376 {
3377 /*
3378 * The first segment requires special handling
3379 */
3380 if (i == 0)
3381 {
3382 ULONG_PTR VirtualAddress;
3383 ULONG_PTR VirtualOffset;
3384
3385 VirtualAddress = EffectiveSegment->Image.VirtualAddress;
3386
3387 /* Round down the virtual address to the nearest page */
3388 EffectiveSegment->Image.VirtualAddress = PAGE_ROUND_DOWN(VirtualAddress);
3389
3390 /* Round up the virtual size to the nearest page */
3391 EffectiveSegment->Length.QuadPart = PAGE_ROUND_UP(VirtualAddress + EffectiveSegment->Length.QuadPart) -
3392 EffectiveSegment->Image.VirtualAddress;
3393
3394 /* Adjust the raw address and size */
3395 VirtualOffset = VirtualAddress - EffectiveSegment->Image.VirtualAddress;
3396
3397 if (EffectiveSegment->Image.FileOffset < VirtualOffset)
3398 {
3399 return FALSE;
3400 }
3401
3402 /*
3403 * Garbage in, garbage out: unaligned base addresses make the file
3404 * offset point in curious and odd places, but that's what we were
3405 * asked for
3406 */
3407 EffectiveSegment->Image.FileOffset -= VirtualOffset;
3408 EffectiveSegment->RawLength.QuadPart += VirtualOffset;
3409 }
3410 else
3411 {
3412 PMM_SECTION_SEGMENT Segment = &ImageSectionObject->Segments[i];
3413 ULONG_PTR EndOfEffectiveSegment;
3414
3415 EndOfEffectiveSegment = (ULONG_PTR)(EffectiveSegment->Image.VirtualAddress + EffectiveSegment->Length.QuadPart);
3416 ASSERT((EndOfEffectiveSegment % PAGE_SIZE) == 0);
3417
3418 /*
3419 * The current segment begins exactly where the current effective
3420 * segment ended, therefore beginning a new effective segment
3421 */
3422 if (EndOfEffectiveSegment == Segment->Image.VirtualAddress)
3423 {
3424 LastSegment ++;
3425 ASSERT(LastSegment <= i);
3426 ASSERT(LastSegment < ImageSectionObject->NrSegments);
3427
3428 EffectiveSegment = &ImageSectionObject->Segments[LastSegment];
3429
3430 if (LastSegment != i)
3431 {
3432 /*
3433 * Copy the current segment. If necessary, the effective segment
3434 * will be expanded later
3435 */
3436 *EffectiveSegment = *Segment;
3437 }
3438
3439 /*
3440 * Page-align the virtual size. We know for sure the virtual address
3441 * already is
3442 */
3443 ASSERT((EffectiveSegment->Image.VirtualAddress % PAGE_SIZE) == 0);
3444 EffectiveSegment->Length.QuadPart = PAGE_ROUND_UP(EffectiveSegment->Length.QuadPart);
3445 }
3446 /*
3447 * The current segment is still part of the current effective segment:
3448 * extend the effective segment to reflect this
3449 */
3450 else if (EndOfEffectiveSegment > Segment->Image.VirtualAddress)
3451 {
3452 static const ULONG FlagsToProtection[16] =
3453 {
3454 PAGE_NOACCESS,
3455 PAGE_READONLY,
3456 PAGE_READWRITE,
3457 PAGE_READWRITE,
3458 PAGE_EXECUTE_READ,
3459 PAGE_EXECUTE_READ,
3460 PAGE_EXECUTE_READWRITE,
3461 PAGE_EXECUTE_READWRITE,
3462 PAGE_WRITECOPY,
3463 PAGE_WRITECOPY,
3464 PAGE_WRITECOPY,
3465 PAGE_WRITECOPY,
3466 PAGE_EXECUTE_WRITECOPY,
3467 PAGE_EXECUTE_WRITECOPY,
3468 PAGE_EXECUTE_WRITECOPY,
3469 PAGE_EXECUTE_WRITECOPY
3470 };
3471
3472 unsigned ProtectionFlags;
3473
3474 /*
3475 * Extend the file size
3476 */
3477
3478 /* Unaligned segments must be contiguous within the file */
3479 if (Segment->Image.FileOffset != (EffectiveSegment->Image.FileOffset +
3480 EffectiveSegment->RawLength.QuadPart))
3481 {
3482 return FALSE;
3483 }
3484
3485 EffectiveSegment->RawLength.QuadPart += Segment->RawLength.QuadPart;
3486
3487 /*
3488 * Extend the virtual size
3489 */
3490 ASSERT(PAGE_ROUND_UP(Segment->Image.VirtualAddress + Segment->Length.QuadPart) >= EndOfEffectiveSegment);
3491
3492 EffectiveSegment->Length.QuadPart = PAGE_ROUND_UP(Segment->Image.VirtualAddress + Segment->Length.QuadPart) -
3493 EffectiveSegment->Image.VirtualAddress;
3494
3495 /*
3496 * Merge the protection
3497 */
3498 EffectiveSegment->Protection |= Segment->Protection;
3499
3500 /* Clean up redundance */
3501 ProtectionFlags = 0;
3502
3503 if(EffectiveSegment->Protection & PAGE_IS_READABLE)
3504 ProtectionFlags |= 1 << 0;
3505
3506 if(EffectiveSegment->Protection & PAGE_IS_WRITABLE)
3507 ProtectionFlags |= 1 << 1;
3508
3509 if(EffectiveSegment->Protection & PAGE_IS_EXECUTABLE)
3510 ProtectionFlags |= 1 << 2;
3511
3512 if(EffectiveSegment->Protection & PAGE_IS_WRITECOPY)
3513 ProtectionFlags |= 1 << 3;
3514
3515 ASSERT(ProtectionFlags < 16);
3516 EffectiveSegment->Protection = FlagsToProtection[ProtectionFlags];
3517
3518 /* If a segment was required to be shared and cannot, fail */
3519 if(!(Segment->Protection & PAGE_IS_WRITECOPY) &&
3520 EffectiveSegment->Protection & PAGE_IS_WRITECOPY)
3521 {
3522 return FALSE;
3523 }
3524 }
3525 /*
3526 * We assume no holes between segments at this point
3527 */
3528 else
3529 {
3530 KeBugCheck(MEMORY_MANAGEMENT);
3531 }
3532 }
3533 }
3534 ImageSectionObject->NrSegments = LastSegment + 1;
3535
3536 return TRUE;
3537 }
3538
3539 NTSTATUS
3540 ExeFmtpCreateImageSection(HANDLE FileHandle,
3541 PMM_IMAGE_SECTION_OBJECT ImageSectionObject)
3542 {
3543 LARGE_INTEGER Offset;
3544 PVOID FileHeader;
3545 PVOID FileHeaderBuffer;
3546 ULONG FileHeaderSize;
3547 ULONG Flags;
3548 ULONG OldNrSegments;
3549 NTSTATUS Status;
3550 ULONG i;
3551
3552 /*
3553 * Read the beginning of the file (2 pages). Should be enough to contain
3554 * all (or most) of the headers
3555 */
3556 Offset.QuadPart = 0;
3557
3558 /* FIXME: use FileObject instead of FileHandle */
3559 Status = ExeFmtpReadFile (FileHandle,
3560 &Offset,
3561 PAGE_SIZE * 2,
3562 &FileHeader,
3563 &FileHeaderBuffer,
3564 &FileHeaderSize);
3565
3566 if (!NT_SUCCESS(Status))
3567 return Status;
3568
3569 if (FileHeaderSize == 0)
3570 {
3571 ExFreePool(FileHeaderBuffer);
3572 return STATUS_UNSUCCESSFUL;
3573 }
3574
3575 /*
3576 * Look for a loader that can handle this executable
3577 */
3578 for (i = 0; i < RTL_NUMBER_OF(ExeFmtpLoaders); ++ i)
3579 {
3580 RtlZeroMemory(ImageSectionObject, sizeof(*ImageSectionObject));
3581 Flags = 0;
3582
3583 /* FIXME: use FileObject instead of FileHandle */
3584 Status = ExeFmtpLoaders[i](FileHeader,
3585 FileHeaderSize,
3586 FileHandle,
3587 ImageSectionObject,
3588 &Flags,
3589 ExeFmtpReadFile,
3590 ExeFmtpAllocateSegments);
3591
3592 if (!NT_SUCCESS(Status))
3593 {
3594 if (ImageSectionObject->Segments)
3595 {
3596 ExFreePool(ImageSectionObject->Segments);
3597 ImageSectionObject->Segments = NULL;
3598 }
3599 }
3600
3601 if (Status != STATUS_ROS_EXEFMT_UNKNOWN_FORMAT)
3602 break;
3603 }
3604
3605 ExFreePoolWithTag(FileHeaderBuffer, 'rXmM');
3606
3607 /*
3608 * No loader handled the format
3609 */
3610 if (Status == STATUS_ROS_EXEFMT_UNKNOWN_FORMAT)
3611 {
3612 Status = STATUS_INVALID_IMAGE_NOT_MZ;
3613 ASSERT(!NT_SUCCESS(Status));
3614 }
3615
3616 if (!NT_SUCCESS(Status))
3617 return Status;
3618
3619 ASSERT(ImageSectionObject->Segments != NULL);
3620
3621 /*
3622 * Some defaults
3623 */
3624 /* FIXME? are these values platform-dependent? */
3625 if(ImageSectionObject->StackReserve == 0)
3626 ImageSectionObject->StackReserve = 0x40000;
3627
3628 if(ImageSectionObject->StackCommit == 0)
3629 ImageSectionObject->StackCommit = 0x1000;
3630
3631 if(ImageSectionObject->ImageBase == 0)
3632 {
3633 if(ImageSectionObject->ImageCharacteristics & IMAGE_FILE_DLL)
3634 ImageSectionObject->ImageBase = 0x10000000;
3635 else
3636 ImageSectionObject->ImageBase = 0x00400000;
3637 }
3638
3639 /*
3640 * And now the fun part: fixing the segments
3641 */
3642
3643 /* Sort them by virtual address */
3644 MmspSortSegments(ImageSectionObject, Flags);
3645
3646 /* Ensure they don't overlap in memory */
3647 if (!MmspCheckSegmentBounds(ImageSectionObject, Flags))
3648 return STATUS_INVALID_IMAGE_FORMAT;
3649
3650 /* Ensure they are aligned */
3651 OldNrSegments = ImageSectionObject->NrSegments;
3652
3653 if (!MmspPageAlignSegments(ImageSectionObject, Flags))
3654 return STATUS_INVALID_IMAGE_FORMAT;
3655
3656 /* Trim them if the alignment phase merged some of them */
3657 if (ImageSectionObject->NrSegments < OldNrSegments)
3658 {
3659 PMM_SECTION_SEGMENT Segments;
3660 SIZE_T SizeOfSegments;
3661
3662 SizeOfSegments = sizeof(MM_SECTION_SEGMENT) * ImageSectionObject->NrSegments;
3663
3664 Segments = ExAllocatePoolWithTag(PagedPool,
3665 SizeOfSegments,
3666 TAG_MM_SECTION_SEGMENT);
3667
3668 if (Segments == NULL)
3669 return STATUS_INSUFFICIENT_RESOURCES;
3670
3671 RtlCopyMemory(Segments, ImageSectionObject->Segments, SizeOfSegments);
3672 ExFreePool(ImageSectionObject->Segments);
3673 ImageSectionObject->Segments = Segments;
3674 }
3675
3676 /* And finish their initialization */
3677 for ( i = 0; i < ImageSectionObject->NrSegments; ++ i )
3678 {
3679 ExInitializeFastMutex(&ImageSectionObject->Segments[i].Lock);
3680 ImageSectionObject->Segments[i].ReferenceCount = 1;
3681 MiInitializeSectionPageTable(&ImageSectionObject->Segments[i]);
3682 }
3683
3684 ASSERT(NT_SUCCESS(Status));
3685 return Status;
3686 }
3687
3688 NTSTATUS
3689 MmCreateImageSection(PROS_SECTION_OBJECT *SectionObject,
3690 ACCESS_MASK DesiredAccess,
3691 POBJECT_ATTRIBUTES ObjectAttributes,
3692 PLARGE_INTEGER UMaximumSize,
3693 ULONG SectionPageProtection,
3694 ULONG AllocationAttributes,
3695 PFILE_OBJECT FileObject)
3696 {
3697 PROS_SECTION_OBJECT Section;
3698 NTSTATUS Status;
3699 PMM_SECTION_SEGMENT SectionSegments;
3700 PMM_IMAGE_SECTION_OBJECT ImageSectionObject;
3701 ULONG i;
3702
3703 if (FileObject == NULL)
3704 return STATUS_INVALID_FILE_FOR_SECTION;
3705
3706 /*
3707 * Create the section
3708 */
3709 Status = ObCreateObject (ExGetPreviousMode(),
3710 MmSectionObjectType,
3711 ObjectAttributes,
3712 ExGetPreviousMode(),
3713 NULL,
3714 sizeof(ROS_SECTION_OBJECT),
3715 0,
3716 0,
3717 (PVOID*)(PVOID)&Section);
3718 if (!NT_SUCCESS(Status))
3719 {
3720 ObDereferenceObject(FileObject);
3721 return(Status);
3722 }
3723
3724 /*
3725 * Initialize it
3726 */
3727 RtlZeroMemory(Section, sizeof(ROS_SECTION_OBJECT));
3728 Section->Type = 'SC';
3729 Section->Size = 'TN';
3730 Section->SectionPageProtection = SectionPageProtection;
3731 Section->AllocationAttributes = AllocationAttributes;
3732
3733 #ifndef NEWCC
3734 /*
3735 * Initialized caching for this file object if previously caching
3736 * was initialized for the same on disk file
3737 */
3738 Status = CcTryToInitializeFileCache(FileObject);
3739 #else
3740 Status = STATUS_SUCCESS;
3741 #endif
3742
3743 if (!NT_SUCCESS(Status) || FileObject->SectionObjectPointer->ImageSectionObject == NULL)
3744 {
3745 NTSTATUS StatusExeFmt;
3746
3747 ImageSectionObject = ExAllocatePoolWithTag(PagedPool, sizeof(MM_IMAGE_SECTION_OBJECT), TAG_MM_SECTION_SEGMENT);
3748 if (ImageSectionObject == NULL)
3749 {
3750 ObDereferenceObject(FileObject);
3751 ObDereferenceObject(Section);
3752 return(STATUS_NO_MEMORY);
3753 }
3754
3755 RtlZeroMemory(ImageSectionObject, sizeof(MM_IMAGE_SECTION_OBJECT));
3756
3757 StatusExeFmt = ExeFmtpCreateImageSection(FileObject, ImageSectionObject);
3758
3759 if (!NT_SUCCESS(StatusExeFmt))
3760 {
3761 if(ImageSectionObject->Segments != NULL)
3762 ExFreePool(ImageSectionObject->Segments);
3763
3764 ExFreePool(ImageSectionObject);
3765 ObDereferenceObject(Section);
3766 ObDereferenceObject(FileObject);
3767 return(StatusExeFmt);
3768 }
3769
3770 Section->ImageSection = ImageSectionObject;
3771 ASSERT(ImageSectionObject->Segments);
3772
3773 /*
3774 * Lock the file
3775 */
3776 Status = MmspWaitForFileLock(FileObject);
3777 if (!NT_SUCCESS(Status))
3778 {
3779 ExFreePool(ImageSectionObject->Segments);
3780 ExFreePool(ImageSectionObject);
3781 ObDereferenceObject(Section);
3782 ObDereferenceObject(FileObject);
3783 return(Status);
3784 }
3785
3786 if (NULL != InterlockedCompareExchangePointer(&FileObject->SectionObjectPointer->ImageSectionObject,
3787 ImageSectionObject, NULL))
3788 {
3789 /*
3790 * An other thread has initialized the same image in the background
3791 */
3792 ExFreePool(ImageSectionObject->Segments);
3793 ExFreePool(ImageSectionObject);
3794 ImageSectionObject = FileObject->SectionObjectPointer->ImageSectionObject;
3795 Section->ImageSection = ImageSectionObject;
3796 SectionSegments = ImageSectionObject->Segments;
3797
3798 for (i = 0; i < ImageSectionObject->NrSegments; i++)
3799 {
3800 (void)InterlockedIncrementUL(&SectionSegments[i].ReferenceCount);
3801 }
3802 }
3803
3804 Status = StatusExeFmt;
3805 }
3806 else
3807 {
3808 /*
3809 * Lock the file
3810 */
3811 Status = MmspWaitForFileLock(FileObject);
3812 if (Status != STATUS_SUCCESS)
3813 {
3814 ObDereferenceObject(Section);
3815 ObDereferenceObject(FileObject);
3816 return(Status);
3817 }
3818
3819 ImageSectionObject = FileObject->SectionObjectPointer->ImageSectionObject;
3820 Section->ImageSection = ImageSectionObject;
3821 SectionSegments = ImageSectionObject->Segments;
3822
3823 /*
3824 * Otherwise just reference all the section segments
3825 */
3826 for (i = 0; i < ImageSectionObject->NrSegments; i++)
3827 {
3828 (void)InterlockedIncrementUL(&SectionSegments[i].ReferenceCount);
3829 }
3830
3831 Status = STATUS_SUCCESS;
3832 }
3833 Section->FileObject = FileObject;
3834 #ifndef NEWCC
3835 CcRosReferenceCache(FileObject);
3836 #endif
3837 //KeSetEvent((PVOID)&FileObject->Lock, IO_NO_INCREMENT, FALSE);
3838 *SectionObject = Section;
3839 return(Status);
3840 }
3841
3842
3843
3844 static NTSTATUS
3845 MmMapViewOfSegment(PMMSUPPORT AddressSpace,
3846 PROS_SECTION_OBJECT Section,
3847 PMM_SECTION_SEGMENT Segment,
3848 PVOID* BaseAddress,
3849 SIZE_T ViewSize,
3850 ULONG Protect,
3851 ULONG ViewOffset,
3852 ULONG AllocationType)
3853 {
3854 PMEMORY_AREA MArea;
3855 NTSTATUS Status;
3856 PHYSICAL_ADDRESS BoundaryAddressMultiple;
3857
3858 if (Segment->WriteCopy)
3859 {
3860 /* We have to do this because the not present fault
3861 * and access fault handlers depend on the protection
3862 * that should be granted AFTER the COW fault takes
3863 * place to be in Region->Protect. The not present fault
3864 * handler changes this to the correct protection for COW when
3865 * mapping the pages into the process's address space. If a COW