[NTOS]: Remove merge artifact from newcc.
[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 #ifdef NEWCC
49 #include "../cache/section/newmm.h"
50 #endif
51 #define NDEBUG
52 #include <debug.h>
53 #include <reactos/exeformat.h>
54
55 #if defined (ALLOC_PRAGMA)
56 #pragma alloc_text(INIT, MmCreatePhysicalMemorySection)
57 #pragma alloc_text(INIT, MmInitSectionImplementation)
58 #endif
59
60 NTSTATUS
61 NTAPI
62 MiMapViewInSystemSpace(IN PVOID Section,
63 IN PVOID Session,
64 OUT PVOID *MappedBase,
65 IN OUT PSIZE_T ViewSize);
66
67 NTSTATUS
68 NTAPI
69 MmCreateArm3Section(OUT PVOID *SectionObject,
70 IN ACCESS_MASK DesiredAccess,
71 IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
72 IN PLARGE_INTEGER InputMaximumSize,
73 IN ULONG SectionPageProtection,
74 IN ULONG AllocationAttributes,
75 IN HANDLE FileHandle OPTIONAL,
76 IN PFILE_OBJECT FileObject OPTIONAL);
77
78 NTSTATUS
79 NTAPI
80 MmMapViewOfArm3Section(IN PVOID SectionObject,
81 IN PEPROCESS Process,
82 IN OUT PVOID *BaseAddress,
83 IN ULONG_PTR ZeroBits,
84 IN SIZE_T CommitSize,
85 IN OUT PLARGE_INTEGER SectionOffset OPTIONAL,
86 IN OUT PSIZE_T ViewSize,
87 IN SECTION_INHERIT InheritDisposition,
88 IN ULONG AllocationType,
89 IN ULONG Protect);
90
91 //
92 // PeFmtCreateSection depends on the following:
93 //
94 C_ASSERT(EXEFMT_LOAD_HEADER_SIZE >= sizeof(IMAGE_DOS_HEADER));
95 C_ASSERT(sizeof(IMAGE_NT_HEADERS32) <= sizeof(IMAGE_NT_HEADERS64));
96
97 C_ASSERT(TYPE_ALIGNMENT(IMAGE_NT_HEADERS32) == TYPE_ALIGNMENT(IMAGE_NT_HEADERS64));
98 C_ASSERT(RTL_SIZEOF_THROUGH_FIELD(IMAGE_NT_HEADERS32, FileHeader) == RTL_SIZEOF_THROUGH_FIELD(IMAGE_NT_HEADERS64, FileHeader));
99 C_ASSERT(FIELD_OFFSET(IMAGE_NT_HEADERS32, OptionalHeader) == FIELD_OFFSET(IMAGE_NT_HEADERS64, OptionalHeader));
100
101 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, Magic));
102 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, SectionAlignment));
103 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, FileAlignment));
104 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, Subsystem));
105 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, MinorSubsystemVersion));
106 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, MajorSubsystemVersion));
107 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, AddressOfEntryPoint));
108 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, SizeOfCode));
109 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, SizeOfHeaders));
110
111 /* TYPES *********************************************************************/
112
113 typedef struct
114 {
115 PROS_SECTION_OBJECT Section;
116 PMM_SECTION_SEGMENT Segment;
117 ULONG Offset;
118 BOOLEAN WasDirty;
119 BOOLEAN Private;
120 }
121 MM_SECTION_PAGEOUT_CONTEXT;
122
123 /* GLOBALS *******************************************************************/
124
125 POBJECT_TYPE MmSectionObjectType = NULL;
126
127 SIZE_T MmAllocationFragment;
128
129 ULONG_PTR MmSubsectionBase;
130
131 static ULONG SectionCharacteristicsToProtect[16] =
132 {
133 PAGE_NOACCESS, /* 0 = NONE */
134 PAGE_NOACCESS, /* 1 = SHARED */
135 PAGE_EXECUTE, /* 2 = EXECUTABLE */
136 PAGE_EXECUTE, /* 3 = EXECUTABLE, SHARED */
137 PAGE_READONLY, /* 4 = READABLE */
138 PAGE_READONLY, /* 5 = READABLE, SHARED */
139 PAGE_EXECUTE_READ, /* 6 = READABLE, EXECUTABLE */
140 PAGE_EXECUTE_READ, /* 7 = READABLE, EXECUTABLE, SHARED */
141 /*
142 * FIXME? do we really need the WriteCopy field in segments? can't we use
143 * PAGE_WRITECOPY here?
144 */
145 PAGE_READWRITE, /* 8 = WRITABLE */
146 PAGE_READWRITE, /* 9 = WRITABLE, SHARED */
147 PAGE_EXECUTE_READWRITE, /* 10 = WRITABLE, EXECUTABLE */
148 PAGE_EXECUTE_READWRITE, /* 11 = WRITABLE, EXECUTABLE, SHARED */
149 PAGE_READWRITE, /* 12 = WRITABLE, READABLE */
150 PAGE_READWRITE, /* 13 = WRITABLE, READABLE, SHARED */
151 PAGE_EXECUTE_READWRITE, /* 14 = WRITABLE, READABLE, EXECUTABLE */
152 PAGE_EXECUTE_READWRITE, /* 15 = WRITABLE, READABLE, EXECUTABLE, SHARED */
153 };
154
155 static GENERIC_MAPPING MmpSectionMapping = {
156 STANDARD_RIGHTS_READ | SECTION_MAP_READ | SECTION_QUERY,
157 STANDARD_RIGHTS_WRITE | SECTION_MAP_WRITE,
158 STANDARD_RIGHTS_EXECUTE | SECTION_MAP_EXECUTE,
159 SECTION_ALL_ACCESS};
160
161 #define PAGE_FROM_SSE(E) ((E) & 0xFFFFF000)
162 #define PFN_FROM_SSE(E) ((E) >> PAGE_SHIFT)
163 #define SHARE_COUNT_FROM_SSE(E) (((E) & 0x00000FFE) >> 1)
164 #define IS_SWAP_FROM_SSE(E) ((E) & 0x00000001)
165 #define MAX_SHARE_COUNT 0x7FF
166 #define MAKE_SSE(P, C) ((P) | ((C) << 1))
167 #define SWAPENTRY_FROM_SSE(E) ((E) >> 1)
168 #define MAKE_SWAP_SSE(S) (((S) << 1) | 0x1)
169
170 static const INFORMATION_CLASS_INFO ExSectionInfoClass[] =
171 {
172 ICI_SQ_SAME( sizeof(SECTION_BASIC_INFORMATION), sizeof(ULONG), ICIF_QUERY ), /* SectionBasicInformation */
173 ICI_SQ_SAME( sizeof(SECTION_IMAGE_INFORMATION), sizeof(ULONG), ICIF_QUERY ), /* SectionImageInformation */
174 };
175
176 /* FUNCTIONS *****************************************************************/
177
178
179 /*
180 References:
181 [1] Microsoft Corporation, "Microsoft Portable Executable and Common Object
182 File Format Specification", revision 6.0 (February 1999)
183 */
184 NTSTATUS NTAPI PeFmtCreateSection(IN CONST VOID * FileHeader,
185 IN SIZE_T FileHeaderSize,
186 IN PVOID File,
187 OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
188 OUT PULONG Flags,
189 IN PEXEFMT_CB_READ_FILE ReadFileCb,
190 IN PEXEFMT_CB_ALLOCATE_SEGMENTS AllocateSegmentsCb)
191 {
192 NTSTATUS nStatus;
193 ULONG cbFileHeaderOffsetSize = 0;
194 ULONG cbSectionHeadersOffset = 0;
195 ULONG cbSectionHeadersSize;
196 ULONG cbSectionHeadersOffsetSize = 0;
197 ULONG cbOptHeaderSize;
198 ULONG cbHeadersSize = 0;
199 ULONG nSectionAlignment;
200 ULONG nFileAlignment;
201 const IMAGE_DOS_HEADER * pidhDosHeader;
202 const IMAGE_NT_HEADERS32 * pinhNtHeader;
203 const IMAGE_OPTIONAL_HEADER32 * piohOptHeader;
204 const IMAGE_SECTION_HEADER * pishSectionHeaders;
205 PMM_SECTION_SEGMENT pssSegments;
206 LARGE_INTEGER lnOffset;
207 PVOID pBuffer;
208 ULONG nPrevVirtualEndOfSegment = 0;
209 ULONG nFileSizeOfHeaders = 0;
210 ULONG i;
211
212 ASSERT(FileHeader);
213 ASSERT(FileHeaderSize > 0);
214 ASSERT(File);
215 ASSERT(ImageSectionObject);
216 ASSERT(ReadFileCb);
217 ASSERT(AllocateSegmentsCb);
218
219 ASSERT(Intsafe_CanOffsetPointer(FileHeader, FileHeaderSize));
220
221 ASSERT(((UINT_PTR)FileHeader % TYPE_ALIGNMENT(IMAGE_DOS_HEADER)) == 0);
222
223 #define DIE(ARGS_) { DPRINT ARGS_; goto l_Return; }
224
225 pBuffer = NULL;
226 pidhDosHeader = FileHeader;
227
228 /* DOS HEADER */
229 nStatus = STATUS_ROS_EXEFMT_UNKNOWN_FORMAT;
230
231 /* image too small to be an MZ executable */
232 if(FileHeaderSize < sizeof(IMAGE_DOS_HEADER))
233 DIE(("Too small to be an MZ executable, size is %lu\n", FileHeaderSize));
234
235 /* no MZ signature */
236 if(pidhDosHeader->e_magic != IMAGE_DOS_SIGNATURE)
237 DIE(("No MZ signature found, e_magic is %hX\n", pidhDosHeader->e_magic));
238
239 /* not a Windows executable */
240 if(pidhDosHeader->e_lfanew <= 0)
241 DIE(("Not a Windows executable, e_lfanew is %d\n", pidhDosHeader->e_lfanew));
242
243 /* NT HEADER */
244 nStatus = STATUS_INVALID_IMAGE_FORMAT;
245
246 if(!Intsafe_AddULong32(&cbFileHeaderOffsetSize, pidhDosHeader->e_lfanew, RTL_SIZEOF_THROUGH_FIELD(IMAGE_NT_HEADERS32, FileHeader)))
247 DIE(("The DOS stub is too large, e_lfanew is %X\n", pidhDosHeader->e_lfanew));
248
249 if(FileHeaderSize < cbFileHeaderOffsetSize)
250 pinhNtHeader = NULL;
251 else
252 {
253 /*
254 * we already know that Intsafe_CanOffsetPointer(FileHeader, FileHeaderSize),
255 * and FileHeaderSize >= cbFileHeaderOffsetSize, so this holds true too
256 */
257 ASSERT(Intsafe_CanOffsetPointer(FileHeader, pidhDosHeader->e_lfanew));
258 pinhNtHeader = (PVOID)((UINT_PTR)FileHeader + pidhDosHeader->e_lfanew);
259 }
260
261 /*
262 * the buffer doesn't contain the NT file header, or the alignment is wrong: we
263 * need to read the header from the file
264 */
265 if(FileHeaderSize < cbFileHeaderOffsetSize ||
266 (UINT_PTR)pinhNtHeader % TYPE_ALIGNMENT(IMAGE_NT_HEADERS32) != 0)
267 {
268 ULONG cbNtHeaderSize;
269 ULONG cbReadSize;
270 PVOID pData;
271
272 l_ReadHeaderFromFile:
273 cbNtHeaderSize = 0;
274 lnOffset.QuadPart = pidhDosHeader->e_lfanew;
275
276 /* read the header from the file */
277 nStatus = ReadFileCb(File, &lnOffset, sizeof(IMAGE_NT_HEADERS64), &pData, &pBuffer, &cbReadSize);
278
279 if(!NT_SUCCESS(nStatus))
280 DIE(("ReadFile failed, status %08X\n", nStatus));
281
282 ASSERT(pData);
283 ASSERT(pBuffer);
284 ASSERT(cbReadSize > 0);
285
286 nStatus = STATUS_INVALID_IMAGE_FORMAT;
287
288 /* the buffer doesn't contain the file header */
289 if(cbReadSize < RTL_SIZEOF_THROUGH_FIELD(IMAGE_NT_HEADERS32, FileHeader))
290 DIE(("The file doesn't contain the PE file header\n"));
291
292 pinhNtHeader = pData;
293
294 /* object still not aligned: copy it to the beginning of the buffer */
295 if((UINT_PTR)pinhNtHeader % TYPE_ALIGNMENT(IMAGE_NT_HEADERS32) != 0)
296 {
297 ASSERT((UINT_PTR)pBuffer % TYPE_ALIGNMENT(IMAGE_NT_HEADERS32) == 0);
298 RtlMoveMemory(pBuffer, pData, cbReadSize);
299 pinhNtHeader = pBuffer;
300 }
301
302 /* invalid NT header */
303 nStatus = STATUS_INVALID_IMAGE_PROTECT;
304
305 if(pinhNtHeader->Signature != IMAGE_NT_SIGNATURE)
306 DIE(("The file isn't a PE executable, Signature is %X\n", pinhNtHeader->Signature));
307
308 nStatus = STATUS_INVALID_IMAGE_FORMAT;
309
310 if(!Intsafe_AddULong32(&cbNtHeaderSize, pinhNtHeader->FileHeader.SizeOfOptionalHeader, FIELD_OFFSET(IMAGE_NT_HEADERS32, OptionalHeader)))
311 DIE(("The full NT header is too large\n"));
312
313 /* the buffer doesn't contain the whole NT header */
314 if(cbReadSize < cbNtHeaderSize)
315 DIE(("The file doesn't contain the full NT header\n"));
316 }
317 else
318 {
319 ULONG cbOptHeaderOffsetSize = 0;
320
321 nStatus = STATUS_INVALID_IMAGE_FORMAT;
322
323 /* don't trust an invalid NT header */
324 if(pinhNtHeader->Signature != IMAGE_NT_SIGNATURE)
325 DIE(("The file isn't a PE executable, Signature is %X\n", pinhNtHeader->Signature));
326
327 if(!Intsafe_AddULong32(&cbOptHeaderOffsetSize, pidhDosHeader->e_lfanew, FIELD_OFFSET(IMAGE_NT_HEADERS32, OptionalHeader)))
328 DIE(("The DOS stub is too large, e_lfanew is %X\n", pidhDosHeader->e_lfanew));
329
330 if(!Intsafe_AddULong32(&cbOptHeaderOffsetSize, cbOptHeaderOffsetSize, pinhNtHeader->FileHeader.SizeOfOptionalHeader))
331 DIE(("The NT header is too large, SizeOfOptionalHeader is %X\n", pinhNtHeader->FileHeader.SizeOfOptionalHeader));
332
333 /* the buffer doesn't contain the whole NT header: read it from the file */
334 if(cbOptHeaderOffsetSize > FileHeaderSize)
335 goto l_ReadHeaderFromFile;
336 }
337
338 /* read information from the NT header */
339 piohOptHeader = &pinhNtHeader->OptionalHeader;
340 cbOptHeaderSize = pinhNtHeader->FileHeader.SizeOfOptionalHeader;
341
342 nStatus = STATUS_INVALID_IMAGE_FORMAT;
343
344 if(!RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, Magic))
345 DIE(("The optional header doesn't contain the Magic field, SizeOfOptionalHeader is %X\n", cbOptHeaderSize));
346
347 /* ASSUME: RtlZeroMemory(ImageSectionObject, sizeof(*ImageSectionObject)); */
348
349 switch(piohOptHeader->Magic)
350 {
351 case IMAGE_NT_OPTIONAL_HDR32_MAGIC:
352 case IMAGE_NT_OPTIONAL_HDR64_MAGIC:
353 break;
354
355 default:
356 DIE(("Unrecognized optional header, Magic is %X\n", piohOptHeader->Magic));
357 }
358
359 if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SectionAlignment) &&
360 RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, FileAlignment))
361 {
362 /* See [1], section 3.4.2 */
363 if(piohOptHeader->SectionAlignment < PAGE_SIZE)
364 {
365 if(piohOptHeader->FileAlignment != piohOptHeader->SectionAlignment)
366 DIE(("Sections aren't page-aligned and the file alignment isn't the same\n"));
367 }
368 else if(piohOptHeader->SectionAlignment < piohOptHeader->FileAlignment)
369 DIE(("The section alignment is smaller than the file alignment\n"));
370
371 nSectionAlignment = piohOptHeader->SectionAlignment;
372 nFileAlignment = piohOptHeader->FileAlignment;
373
374 if(!IsPowerOf2(nSectionAlignment) || !IsPowerOf2(nFileAlignment))
375 DIE(("The section alignment (%u) and file alignment (%u) aren't both powers of 2\n", nSectionAlignment, nFileAlignment));
376 }
377 else
378 {
379 nSectionAlignment = PAGE_SIZE;
380 nFileAlignment = PAGE_SIZE;
381 }
382
383 ASSERT(IsPowerOf2(nSectionAlignment));
384 ASSERT(IsPowerOf2(nFileAlignment));
385
386 switch(piohOptHeader->Magic)
387 {
388 /* PE32 */
389 case IMAGE_NT_OPTIONAL_HDR32_MAGIC:
390 {
391 if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, ImageBase))
392 ImageSectionObject->ImageBase = piohOptHeader->ImageBase;
393
394 if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SizeOfImage))
395 ImageSectionObject->ImageSize = piohOptHeader->SizeOfImage;
396
397 if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SizeOfStackReserve))
398 ImageSectionObject->StackReserve = piohOptHeader->SizeOfStackReserve;
399
400 if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SizeOfStackCommit))
401 ImageSectionObject->StackCommit = piohOptHeader->SizeOfStackCommit;
402
403 break;
404 }
405
406 /* PE32+ */
407 case IMAGE_NT_OPTIONAL_HDR64_MAGIC:
408 {
409 const IMAGE_OPTIONAL_HEADER64 * pioh64OptHeader;
410
411 pioh64OptHeader = (const IMAGE_OPTIONAL_HEADER64 *)piohOptHeader;
412
413 if(RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, ImageBase))
414 {
415 if(pioh64OptHeader->ImageBase > MAXULONG_PTR)
416 DIE(("ImageBase exceeds the address space\n"));
417
418 ImageSectionObject->ImageBase = (ULONG_PTR)pioh64OptHeader->ImageBase;
419 }
420
421 if(RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, SizeOfImage))
422 {
423 if(pioh64OptHeader->SizeOfImage > MAXULONG_PTR)
424 DIE(("SizeOfImage exceeds the address space\n"));
425
426 ImageSectionObject->ImageSize = pioh64OptHeader->SizeOfImage;
427 }
428
429 if(RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, SizeOfStackReserve))
430 {
431 if(pioh64OptHeader->SizeOfStackReserve > MAXULONG_PTR)
432 DIE(("SizeOfStackReserve exceeds the address space\n"));
433
434 ImageSectionObject->StackReserve = pioh64OptHeader->SizeOfStackReserve;
435 }
436
437 if(RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, SizeOfStackCommit))
438 {
439 if(pioh64OptHeader->SizeOfStackCommit > MAXULONG_PTR)
440 DIE(("SizeOfStackCommit exceeds the address space\n"));
441
442 ImageSectionObject->StackCommit = pioh64OptHeader->SizeOfStackCommit;
443 }
444
445 break;
446 }
447 }
448
449 /* [1], section 3.4.2 */
450 if((ULONG_PTR)ImageSectionObject->ImageBase % 0x10000)
451 DIE(("ImageBase is not aligned on a 64KB boundary"));
452
453 if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, Subsystem))
454 {
455 ImageSectionObject->Subsystem = piohOptHeader->Subsystem;
456
457 if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, MinorSubsystemVersion) &&
458 RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, MajorSubsystemVersion))
459 {
460 ImageSectionObject->MinorSubsystemVersion = piohOptHeader->MinorSubsystemVersion;
461 ImageSectionObject->MajorSubsystemVersion = piohOptHeader->MajorSubsystemVersion;
462 }
463 }
464
465 if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, AddressOfEntryPoint))
466 {
467 ImageSectionObject->EntryPoint = piohOptHeader->ImageBase +
468 piohOptHeader->AddressOfEntryPoint;
469 }
470
471 if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SizeOfCode))
472 ImageSectionObject->Executable = piohOptHeader->SizeOfCode != 0;
473 else
474 ImageSectionObject->Executable = TRUE;
475
476 ImageSectionObject->ImageCharacteristics = pinhNtHeader->FileHeader.Characteristics;
477 ImageSectionObject->Machine = pinhNtHeader->FileHeader.Machine;
478
479 /* SECTION HEADERS */
480 nStatus = STATUS_INVALID_IMAGE_FORMAT;
481
482 /* see [1], section 3.3 */
483 if(pinhNtHeader->FileHeader.NumberOfSections > 96)
484 DIE(("Too many sections, NumberOfSections is %u\n", pinhNtHeader->FileHeader.NumberOfSections));
485
486 /*
487 * the additional segment is for the file's headers. They need to be present for
488 * the benefit of the dynamic loader (to locate exports, defaults for thread
489 * parameters, resources, etc.)
490 */
491 ImageSectionObject->NrSegments = pinhNtHeader->FileHeader.NumberOfSections + 1;
492
493 /* file offset for the section headers */
494 if(!Intsafe_AddULong32(&cbSectionHeadersOffset, pidhDosHeader->e_lfanew, FIELD_OFFSET(IMAGE_NT_HEADERS32, OptionalHeader)))
495 DIE(("Offset overflow\n"));
496
497 if(!Intsafe_AddULong32(&cbSectionHeadersOffset, cbSectionHeadersOffset, pinhNtHeader->FileHeader.SizeOfOptionalHeader))
498 DIE(("Offset overflow\n"));
499
500 /* size of the section headers */
501 ASSERT(Intsafe_CanMulULong32(pinhNtHeader->FileHeader.NumberOfSections, sizeof(IMAGE_SECTION_HEADER)));
502 cbSectionHeadersSize = pinhNtHeader->FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER);
503
504 if(!Intsafe_AddULong32(&cbSectionHeadersOffsetSize, cbSectionHeadersOffset, cbSectionHeadersSize))
505 DIE(("Section headers too large\n"));
506
507 /* size of the executable's headers */
508 if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SizeOfHeaders))
509 {
510 // if(!IsAligned(piohOptHeader->SizeOfHeaders, nFileAlignment))
511 // DIE(("SizeOfHeaders is not aligned\n"));
512
513 if(cbSectionHeadersSize > piohOptHeader->SizeOfHeaders)
514 DIE(("The section headers overflow SizeOfHeaders\n"));
515
516 cbHeadersSize = piohOptHeader->SizeOfHeaders;
517 }
518 else if(!AlignUp(&cbHeadersSize, cbSectionHeadersOffsetSize, nFileAlignment))
519 DIE(("Overflow aligning the size of headers\n"));
520
521 if(pBuffer)
522 {
523 ExFreePool(pBuffer);
524 pBuffer = NULL;
525 }
526 /* WARNING: pinhNtHeader IS NO LONGER USABLE */
527 /* WARNING: piohOptHeader IS NO LONGER USABLE */
528 /* WARNING: pioh64OptHeader IS NO LONGER USABLE */
529
530 if(FileHeaderSize < cbSectionHeadersOffsetSize)
531 pishSectionHeaders = NULL;
532 else
533 {
534 /*
535 * we already know that Intsafe_CanOffsetPointer(FileHeader, FileHeaderSize),
536 * and FileHeaderSize >= cbSectionHeadersOffsetSize, so this holds true too
537 */
538 ASSERT(Intsafe_CanOffsetPointer(FileHeader, cbSectionHeadersOffset));
539 pishSectionHeaders = (PVOID)((UINT_PTR)FileHeader + cbSectionHeadersOffset);
540 }
541
542 /*
543 * the buffer doesn't contain the section headers, or the alignment is wrong:
544 * read the headers from the file
545 */
546 if(FileHeaderSize < cbSectionHeadersOffsetSize ||
547 (UINT_PTR)pishSectionHeaders % TYPE_ALIGNMENT(IMAGE_SECTION_HEADER) != 0)
548 {
549 PVOID pData;
550 ULONG cbReadSize;
551
552 lnOffset.QuadPart = cbSectionHeadersOffset;
553
554 /* read the header from the file */
555 nStatus = ReadFileCb(File, &lnOffset, cbSectionHeadersSize, &pData, &pBuffer, &cbReadSize);
556
557 if(!NT_SUCCESS(nStatus))
558 DIE(("ReadFile failed with status %08X\n", nStatus));
559
560 ASSERT(pData);
561 ASSERT(pBuffer);
562 ASSERT(cbReadSize > 0);
563
564 nStatus = STATUS_INVALID_IMAGE_FORMAT;
565
566 /* the buffer doesn't contain all the section headers */
567 if(cbReadSize < cbSectionHeadersSize)
568 DIE(("The file doesn't contain all of the section headers\n"));
569
570 pishSectionHeaders = pData;
571
572 /* object still not aligned: copy it to the beginning of the buffer */
573 if((UINT_PTR)pishSectionHeaders % TYPE_ALIGNMENT(IMAGE_SECTION_HEADER) != 0)
574 {
575 ASSERT((UINT_PTR)pBuffer % TYPE_ALIGNMENT(IMAGE_SECTION_HEADER) == 0);
576 RtlMoveMemory(pBuffer, pData, cbReadSize);
577 pishSectionHeaders = pBuffer;
578 }
579 }
580
581 /* SEGMENTS */
582 /* allocate the segments */
583 nStatus = STATUS_INSUFFICIENT_RESOURCES;
584 ImageSectionObject->Segments = AllocateSegmentsCb(ImageSectionObject->NrSegments);
585
586 if(ImageSectionObject->Segments == NULL)
587 DIE(("AllocateSegments failed\n"));
588
589 /* initialize the headers segment */
590 pssSegments = ImageSectionObject->Segments;
591
592 // ASSERT(IsAligned(cbHeadersSize, nFileAlignment));
593
594 if(!AlignUp(&nFileSizeOfHeaders, cbHeadersSize, nFileAlignment))
595 DIE(("Cannot align the size of the section headers\n"));
596
597 if(!AlignUp(&nPrevVirtualEndOfSegment, cbHeadersSize, nSectionAlignment))
598 DIE(("Cannot align the size of the section headers\n"));
599
600 pssSegments[0].FileOffset = 0;
601 pssSegments[0].Protection = PAGE_READONLY;
602 pssSegments[0].Length = nPrevVirtualEndOfSegment;
603 pssSegments[0].RawLength = nFileSizeOfHeaders;
604 pssSegments[0].VirtualAddress = 0;
605 pssSegments[0].Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA;
606 pssSegments[0].WriteCopy = TRUE;
607
608 /* skip the headers segment */
609 ++ pssSegments;
610
611 nStatus = STATUS_INVALID_IMAGE_FORMAT;
612
613 /* convert the executable sections into segments. See also [1], section 4 */
614 for(i = 0; i < ImageSectionObject->NrSegments - 1; ++ i)
615 {
616 ULONG nCharacteristics;
617
618 /* validate the alignment */
619 if(!IsAligned(pishSectionHeaders[i].VirtualAddress, nSectionAlignment))
620 DIE(("VirtualAddress[%u] is not aligned\n", i));
621
622 /* sections must be contiguous, ordered by base address and non-overlapping */
623 if(pishSectionHeaders[i].VirtualAddress != nPrevVirtualEndOfSegment)
624 DIE(("Memory gap between section %u and the previous\n", i));
625
626 /* ignore explicit BSS sections */
627 if(pishSectionHeaders[i].SizeOfRawData != 0)
628 {
629 /* validate the alignment */
630 #if 0
631 /* Yes, this should be a multiple of FileAlignment, but there's
632 * stuff out there that isn't. We can cope with that
633 */
634 if(!IsAligned(pishSectionHeaders[i].SizeOfRawData, nFileAlignment))
635 DIE(("SizeOfRawData[%u] is not aligned\n", i));
636 #endif
637
638 // if(!IsAligned(pishSectionHeaders[i].PointerToRawData, nFileAlignment))
639 // DIE(("PointerToRawData[%u] is not aligned\n", i));
640
641 /* conversion */
642 pssSegments[i].FileOffset = pishSectionHeaders[i].PointerToRawData;
643 pssSegments[i].RawLength = pishSectionHeaders[i].SizeOfRawData;
644 }
645 else
646 {
647 ASSERT(pssSegments[i].FileOffset == 0);
648 ASSERT(pssSegments[i].RawLength == 0);
649 }
650
651 ASSERT(Intsafe_CanAddLong64(pssSegments[i].FileOffset, pssSegments[i].RawLength));
652
653 nCharacteristics = pishSectionHeaders[i].Characteristics;
654
655 /* no explicit protection */
656 if((nCharacteristics & (IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE)) == 0)
657 {
658 if(nCharacteristics & IMAGE_SCN_CNT_CODE)
659 nCharacteristics |= IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ;
660
661 if(nCharacteristics & IMAGE_SCN_CNT_INITIALIZED_DATA)
662 nCharacteristics |= IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE;
663
664 if(nCharacteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA)
665 nCharacteristics |= IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE;
666 }
667
668 /* see table above */
669 pssSegments[i].Protection = SectionCharacteristicsToProtect[nCharacteristics >> 28];
670 pssSegments[i].WriteCopy = !(nCharacteristics & IMAGE_SCN_MEM_SHARED);
671
672 if(pishSectionHeaders[i].Misc.VirtualSize == 0 || pishSectionHeaders[i].Misc.VirtualSize < pishSectionHeaders[i].SizeOfRawData)
673 pssSegments[i].Length = pishSectionHeaders[i].SizeOfRawData;
674 else
675 pssSegments[i].Length = pishSectionHeaders[i].Misc.VirtualSize;
676
677 if(!AlignUp(&pssSegments[i].Length, pssSegments[i].Length, nSectionAlignment))
678 DIE(("Cannot align the virtual size of section %u\n", i));
679
680 ASSERT(IsAligned(pssSegments[i].Length, nSectionAlignment));
681
682 if(pssSegments[i].Length == 0)
683 DIE(("Virtual size of section %u is null\n", i));
684
685 pssSegments[i].VirtualAddress = pishSectionHeaders[i].VirtualAddress;
686 pssSegments[i].Characteristics = pishSectionHeaders[i].Characteristics;
687
688 /* ensure the memory image is no larger than 4GB */
689 if(!Intsafe_AddULong32(&nPrevVirtualEndOfSegment, pssSegments[i].VirtualAddress, pssSegments[i].Length))
690 DIE(("The image is larger than 4GB\n"));
691 }
692
693 /* spare our caller some work in validating the segments */
694 *Flags = EXEFMT_LOAD_ASSUME_SEGMENTS_SORTED | EXEFMT_LOAD_ASSUME_SEGMENTS_NO_OVERLAP;
695
696 if(nSectionAlignment >= PAGE_SIZE)
697 *Flags |= EXEFMT_LOAD_ASSUME_SEGMENTS_PAGE_ALIGNED;
698
699 /* Success */
700 nStatus = STATUS_ROS_EXEFMT_LOADED_FORMAT | EXEFMT_LOADED_PE32;
701
702 l_Return:
703 if(pBuffer)
704 ExFreePool(pBuffer);
705
706 return nStatus;
707 }
708
709 /* Note: Mmsp prefix denotes "Memory Manager Section Private". */
710
711 /*
712 * FUNCTION: Waits in kernel mode up to ten seconds for an MM_PAGEOP event.
713 * ARGUMENTS: PMM_PAGEOP which event we should wait for.
714 * RETURNS: Status of the wait.
715 */
716 static NTSTATUS
717 MmspWaitForPageOpCompletionEvent(PMM_PAGEOP PageOp)
718 {
719 LARGE_INTEGER Timeout;
720 #ifdef __GNUC__ /* TODO: Use other macro to check for suffix to use? */
721
722 Timeout.QuadPart = -100000000LL; // 10 sec
723 #else
724
725 Timeout.QuadPart = -100000000; // 10 sec
726 #endif
727
728 return KeWaitForSingleObject(&PageOp->CompletionEvent, 0, KernelMode, FALSE, &Timeout);
729 }
730
731
732 /*
733 * FUNCTION: Sets the page op completion event and releases the page op.
734 * ARGUMENTS: PMM_PAGEOP.
735 * RETURNS: In shorter time than it takes you to even read this
736 * description, so don't even think about geting a mug of coffee.
737 */
738 static void
739 MmspCompleteAndReleasePageOp(PMM_PAGEOP PageOp)
740 {
741 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
742 MmReleasePageOp(PageOp);
743 }
744
745
746 /*
747 * FUNCTION: Waits in kernel mode indefinitely for a file object lock.
748 * ARGUMENTS: PFILE_OBJECT to wait for.
749 * RETURNS: Status of the wait.
750 */
751 NTSTATUS
752 MmspWaitForFileLock(PFILE_OBJECT File)
753 {
754 return STATUS_SUCCESS;
755 //return KeWaitForSingleObject(&File->Lock, 0, KernelMode, FALSE, NULL);
756 }
757
758
759 VOID
760 MmFreePageTablesSectionSegment(PMM_SECTION_SEGMENT Segment)
761 {
762 ULONG i;
763 if (Segment->Length > NR_SECTION_PAGE_TABLES * PAGE_SIZE)
764 {
765 for (i = 0; i < NR_SECTION_PAGE_TABLES; i++)
766 {
767 if (Segment->PageDirectory.PageTables[i] != NULL)
768 {
769 ExFreePool(Segment->PageDirectory.PageTables[i]);
770 }
771 }
772 }
773 }
774
775 VOID
776 NTAPI
777 MmFreeSectionSegments(PFILE_OBJECT FileObject)
778 {
779 if (FileObject->SectionObjectPointer->ImageSectionObject != NULL)
780 {
781 PMM_IMAGE_SECTION_OBJECT ImageSectionObject;
782 PMM_SECTION_SEGMENT SectionSegments;
783 ULONG NrSegments;
784 ULONG i;
785
786 ImageSectionObject = (PMM_IMAGE_SECTION_OBJECT)FileObject->SectionObjectPointer->ImageSectionObject;
787 NrSegments = ImageSectionObject->NrSegments;
788 SectionSegments = ImageSectionObject->Segments;
789 for (i = 0; i < NrSegments; i++)
790 {
791 if (SectionSegments[i].ReferenceCount != 0)
792 {
793 DPRINT1("Image segment %d still referenced (was %d)\n", i,
794 SectionSegments[i].ReferenceCount);
795 KeBugCheck(MEMORY_MANAGEMENT);
796 }
797 MmFreePageTablesSectionSegment(&SectionSegments[i]);
798 }
799 ExFreePool(ImageSectionObject->Segments);
800 ExFreePool(ImageSectionObject);
801 FileObject->SectionObjectPointer->ImageSectionObject = NULL;
802 }
803 if (FileObject->SectionObjectPointer->DataSectionObject != NULL)
804 {
805 PMM_SECTION_SEGMENT Segment;
806
807 Segment = (PMM_SECTION_SEGMENT)FileObject->SectionObjectPointer->
808 DataSectionObject;
809
810 if (Segment->ReferenceCount != 0)
811 {
812 DPRINT1("Data segment still referenced\n");
813 KeBugCheck(MEMORY_MANAGEMENT);
814 }
815 MmFreePageTablesSectionSegment(Segment);
816 ExFreePool(Segment);
817 FileObject->SectionObjectPointer->DataSectionObject = NULL;
818 }
819 }
820
821 VOID
822 NTAPI
823 MmLockSectionSegment(PMM_SECTION_SEGMENT Segment)
824 {
825 ExAcquireFastMutex(&Segment->Lock);
826 }
827
828 VOID
829 NTAPI
830 MmUnlockSectionSegment(PMM_SECTION_SEGMENT Segment)
831 {
832 ExReleaseFastMutex(&Segment->Lock);
833 }
834
835 VOID
836 NTAPI
837 MmSetPageEntrySectionSegment(PMM_SECTION_SEGMENT Segment,
838 ULONG Offset,
839 ULONG Entry)
840 {
841 PSECTION_PAGE_TABLE Table;
842 ULONG DirectoryOffset;
843 ULONG TableOffset;
844
845 if (Segment->Length <= NR_SECTION_PAGE_TABLES * PAGE_SIZE)
846 {
847 Table = (PSECTION_PAGE_TABLE)&Segment->PageDirectory;
848 }
849 else
850 {
851 DirectoryOffset = PAGE_TO_SECTION_PAGE_DIRECTORY_OFFSET(Offset);
852 Table = Segment->PageDirectory.PageTables[DirectoryOffset];
853 if (Table == NULL)
854 {
855 Table =
856 Segment->PageDirectory.PageTables[DirectoryOffset] =
857 ExAllocatePoolWithTag(NonPagedPool, sizeof(SECTION_PAGE_TABLE),
858 TAG_SECTION_PAGE_TABLE);
859 if (Table == NULL)
860 {
861 KeBugCheck(MEMORY_MANAGEMENT);
862 }
863 memset(Table, 0, sizeof(SECTION_PAGE_TABLE));
864 DPRINT("Table %x\n", Table);
865 }
866 }
867 TableOffset = PAGE_TO_SECTION_PAGE_TABLE_OFFSET(Offset);
868 Table->Entry[TableOffset] = Entry;
869 }
870
871
872 ULONG
873 NTAPI
874 MmGetPageEntrySectionSegment(PMM_SECTION_SEGMENT Segment,
875 ULONG Offset)
876 {
877 PSECTION_PAGE_TABLE Table;
878 ULONG Entry;
879 ULONG DirectoryOffset;
880 ULONG TableOffset;
881
882 DPRINT("MmGetPageEntrySection(Segment %x, Offset %x)\n", Segment, Offset);
883
884 if (Segment->Length <= NR_SECTION_PAGE_TABLES * PAGE_SIZE)
885 {
886 Table = (PSECTION_PAGE_TABLE)&Segment->PageDirectory;
887 }
888 else
889 {
890 DirectoryOffset = PAGE_TO_SECTION_PAGE_DIRECTORY_OFFSET(Offset);
891 Table = Segment->PageDirectory.PageTables[DirectoryOffset];
892 DPRINT("Table %x\n", Table);
893 if (Table == NULL)
894 {
895 return(0);
896 }
897 }
898 TableOffset = PAGE_TO_SECTION_PAGE_TABLE_OFFSET(Offset);
899 Entry = Table->Entry[TableOffset];
900 return(Entry);
901 }
902
903 VOID
904 NTAPI
905 MmSharePageEntrySectionSegment(PMM_SECTION_SEGMENT Segment,
906 ULONG Offset)
907 {
908 ULONG Entry;
909
910 Entry = MmGetPageEntrySectionSegment(Segment, Offset);
911 if (Entry == 0)
912 {
913 DPRINT1("Entry == 0 for MmSharePageEntrySectionSegment\n");
914 KeBugCheck(MEMORY_MANAGEMENT);
915 }
916 if (SHARE_COUNT_FROM_SSE(Entry) == MAX_SHARE_COUNT)
917 {
918 DPRINT1("Maximum share count reached\n");
919 KeBugCheck(MEMORY_MANAGEMENT);
920 }
921 if (IS_SWAP_FROM_SSE(Entry))
922 {
923 KeBugCheck(MEMORY_MANAGEMENT);
924 }
925 Entry = MAKE_SSE(PAGE_FROM_SSE(Entry), SHARE_COUNT_FROM_SSE(Entry) + 1);
926 MmSetPageEntrySectionSegment(Segment, Offset, Entry);
927 }
928
929 BOOLEAN
930 NTAPI
931 MmUnsharePageEntrySectionSegment(PROS_SECTION_OBJECT Section,
932 PMM_SECTION_SEGMENT Segment,
933 ULONG Offset,
934 BOOLEAN Dirty,
935 BOOLEAN PageOut)
936 {
937 ULONG Entry;
938 BOOLEAN IsDirectMapped = FALSE;
939
940 Entry = MmGetPageEntrySectionSegment(Segment, Offset);
941 if (Entry == 0)
942 {
943 DPRINT1("Entry == 0 for MmUnsharePageEntrySectionSegment\n");
944 KeBugCheck(MEMORY_MANAGEMENT);
945 }
946 if (SHARE_COUNT_FROM_SSE(Entry) == 0)
947 {
948 DPRINT1("Zero share count for unshare\n");
949 KeBugCheck(MEMORY_MANAGEMENT);
950 }
951 if (IS_SWAP_FROM_SSE(Entry))
952 {
953 KeBugCheck(MEMORY_MANAGEMENT);
954 }
955 Entry = MAKE_SSE(PAGE_FROM_SSE(Entry), SHARE_COUNT_FROM_SSE(Entry) - 1);
956 /*
957 * If we reducing the share count of this entry to zero then set the entry
958 * to zero and tell the cache the page is no longer mapped.
959 */
960 if (SHARE_COUNT_FROM_SSE(Entry) == 0)
961 {
962 PFILE_OBJECT FileObject;
963 PBCB Bcb;
964 SWAPENTRY SavedSwapEntry;
965 PFN_NUMBER Page;
966 BOOLEAN IsImageSection;
967 ULONG FileOffset;
968
969 FileOffset = Offset + Segment->FileOffset;
970
971 IsImageSection = Section->AllocationAttributes & SEC_IMAGE ? TRUE : FALSE;
972
973 Page = PFN_FROM_SSE(Entry);
974 FileObject = Section->FileObject;
975 if (FileObject != NULL &&
976 !(Segment->Characteristics & IMAGE_SCN_MEM_SHARED))
977 {
978
979 if ((FileOffset % PAGE_SIZE) == 0 &&
980 (Offset + PAGE_SIZE <= Segment->RawLength || !IsImageSection))
981 {
982 NTSTATUS Status;
983 Bcb = FileObject->SectionObjectPointer->SharedCacheMap;
984 IsDirectMapped = TRUE;
985 #ifndef NEWCC
986 Status = CcRosUnmapCacheSegment(Bcb, FileOffset, Dirty);
987 #else
988 Status = STATUS_SUCCESS;
989 #endif
990 if (!NT_SUCCESS(Status))
991 {
992 DPRINT1("CcRosUnmapCacheSegment failed, status = %x\n", Status);
993 KeBugCheck(MEMORY_MANAGEMENT);
994 }
995 }
996 }
997
998 SavedSwapEntry = MmGetSavedSwapEntryPage(Page);
999 if (SavedSwapEntry == 0)
1000 {
1001 if (!PageOut &&
1002 ((Segment->Flags & MM_PAGEFILE_SEGMENT) ||
1003 (Segment->Characteristics & IMAGE_SCN_MEM_SHARED)))
1004 {
1005 /*
1006 * FIXME:
1007 * Try to page out this page and set the swap entry
1008 * within the section segment. There exist no rmap entry
1009 * for this page. The pager thread can't page out a
1010 * page without a rmap entry.
1011 */
1012 MmSetPageEntrySectionSegment(Segment, Offset, Entry);
1013 }
1014 else
1015 {
1016 MmSetPageEntrySectionSegment(Segment, Offset, 0);
1017 if (!IsDirectMapped)
1018 {
1019 MmReleasePageMemoryConsumer(MC_USER, Page);
1020 }
1021 }
1022 }
1023 else
1024 {
1025 if ((Segment->Flags & MM_PAGEFILE_SEGMENT) ||
1026 (Segment->Characteristics & IMAGE_SCN_MEM_SHARED))
1027 {
1028 if (!PageOut)
1029 {
1030 if (Dirty)
1031 {
1032 /*
1033 * FIXME:
1034 * We hold all locks. Nobody can do something with the current
1035 * process and the current segment (also not within an other process).
1036 */
1037 NTSTATUS Status;
1038 Status = MmWriteToSwapPage(SavedSwapEntry, Page);
1039 if (!NT_SUCCESS(Status))
1040 {
1041 DPRINT1("MM: Failed to write to swap page (Status was 0x%.8X)\n", Status);
1042 KeBugCheck(MEMORY_MANAGEMENT);
1043 }
1044 }
1045 MmSetPageEntrySectionSegment(Segment, Offset, MAKE_SWAP_SSE(SavedSwapEntry));
1046 MmSetSavedSwapEntryPage(Page, 0);
1047 }
1048 MmReleasePageMemoryConsumer(MC_USER, Page);
1049 }
1050 else
1051 {
1052 DPRINT1("Found a swapentry for a non private page in an image or data file sgment\n");
1053 KeBugCheck(MEMORY_MANAGEMENT);
1054 }
1055 }
1056 }
1057 else
1058 {
1059 MmSetPageEntrySectionSegment(Segment, Offset, Entry);
1060 }
1061 return(SHARE_COUNT_FROM_SSE(Entry) > 0);
1062 }
1063
1064 BOOLEAN MiIsPageFromCache(PMEMORY_AREA MemoryArea,
1065 ULONG SegOffset)
1066 {
1067 #ifndef NEWCC
1068 if (!(MemoryArea->Data.SectionData.Segment->Characteristics & IMAGE_SCN_MEM_SHARED))
1069 {
1070 PBCB Bcb;
1071 PCACHE_SEGMENT CacheSeg;
1072 Bcb = MemoryArea->Data.SectionData.Section->FileObject->SectionObjectPointer->SharedCacheMap;
1073 CacheSeg = CcRosLookupCacheSegment(Bcb, SegOffset + MemoryArea->Data.SectionData.Segment->FileOffset);
1074 if (CacheSeg)
1075 {
1076 CcRosReleaseCacheSegment(Bcb, CacheSeg, CacheSeg->Valid, FALSE, TRUE);
1077 return TRUE;
1078 }
1079 }
1080 #endif
1081 return FALSE;
1082 }
1083
1084 NTSTATUS
1085 NTAPI
1086 MiCopyFromUserPage(PFN_NUMBER DestPage, PVOID SourceAddress)
1087 {
1088 PEPROCESS Process;
1089 KIRQL Irql;
1090 PVOID TempAddress;
1091
1092 Process = PsGetCurrentProcess();
1093 TempAddress = MiMapPageInHyperSpace(Process, DestPage, &Irql);
1094 if (TempAddress == NULL)
1095 {
1096 return(STATUS_NO_MEMORY);
1097 }
1098 memcpy(TempAddress, SourceAddress, PAGE_SIZE);
1099 MiUnmapPageInHyperSpace(Process, TempAddress, Irql);
1100 return(STATUS_SUCCESS);
1101 }
1102
1103 #ifndef NEWCC
1104 NTSTATUS
1105 NTAPI
1106 MiReadPage(PMEMORY_AREA MemoryArea,
1107 ULONG SegOffset,
1108 PPFN_NUMBER Page)
1109 /*
1110 * FUNCTION: Read a page for a section backed memory area.
1111 * PARAMETERS:
1112 * MemoryArea - Memory area to read the page for.
1113 * Offset - Offset of the page to read.
1114 * Page - Variable that receives a page contains the read data.
1115 */
1116 {
1117 ULONG BaseOffset;
1118 ULONG FileOffset;
1119 PVOID BaseAddress;
1120 BOOLEAN UptoDate;
1121 PCACHE_SEGMENT CacheSeg;
1122 PFILE_OBJECT FileObject;
1123 NTSTATUS Status;
1124 ULONG RawLength;
1125 PBCB Bcb;
1126 BOOLEAN IsImageSection;
1127 ULONG Length;
1128
1129 FileObject = MemoryArea->Data.SectionData.Section->FileObject;
1130 Bcb = FileObject->SectionObjectPointer->SharedCacheMap;
1131 RawLength = MemoryArea->Data.SectionData.Segment->RawLength;
1132 FileOffset = SegOffset + MemoryArea->Data.SectionData.Segment->FileOffset;
1133 IsImageSection = MemoryArea->Data.SectionData.Section->AllocationAttributes & SEC_IMAGE ? TRUE : FALSE;
1134
1135 ASSERT(Bcb);
1136
1137 DPRINT("%S %x\n", FileObject->FileName.Buffer, FileOffset);
1138
1139 /*
1140 * If the file system is letting us go directly to the cache and the
1141 * memory area was mapped at an offset in the file which is page aligned
1142 * then get the related cache segment.
1143 */
1144 if ((FileOffset % PAGE_SIZE) == 0 &&
1145 (SegOffset + PAGE_SIZE <= RawLength || !IsImageSection) &&
1146 !(MemoryArea->Data.SectionData.Segment->Characteristics & IMAGE_SCN_MEM_SHARED))
1147 {
1148
1149 /*
1150 * Get the related cache segment; we use a lower level interface than
1151 * filesystems do because it is safe for us to use an offset with a
1152 * alignment less than the file system block size.
1153 */
1154 Status = CcRosGetCacheSegment(Bcb,
1155 FileOffset,
1156 &BaseOffset,
1157 &BaseAddress,
1158 &UptoDate,
1159 &CacheSeg);
1160 if (!NT_SUCCESS(Status))
1161 {
1162 return(Status);
1163 }
1164 if (!UptoDate)
1165 {
1166 /*
1167 * If the cache segment isn't up to date then call the file
1168 * system to read in the data.
1169 */
1170 Status = ReadCacheSegment(CacheSeg);
1171 if (!NT_SUCCESS(Status))
1172 {
1173 CcRosReleaseCacheSegment(Bcb, CacheSeg, FALSE, FALSE, FALSE);
1174 return Status;
1175 }
1176 }
1177 /*
1178 * Retrieve the page from the cache segment that we actually want.
1179 */
1180 (*Page) = MmGetPhysicalAddress((char*)BaseAddress +
1181 FileOffset - BaseOffset).LowPart >> PAGE_SHIFT;
1182
1183 CcRosReleaseCacheSegment(Bcb, CacheSeg, TRUE, FALSE, TRUE);
1184 }
1185 else
1186 {
1187 PEPROCESS Process;
1188 KIRQL Irql;
1189 PVOID PageAddr;
1190 ULONG CacheSegOffset;
1191
1192 /*
1193 * Allocate a page, this is rather complicated by the possibility
1194 * we might have to move other things out of memory
1195 */
1196 Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, Page);
1197 if (!NT_SUCCESS(Status))
1198 {
1199 return(Status);
1200 }
1201 Status = CcRosGetCacheSegment(Bcb,
1202 FileOffset,
1203 &BaseOffset,
1204 &BaseAddress,
1205 &UptoDate,
1206 &CacheSeg);
1207 if (!NT_SUCCESS(Status))
1208 {
1209 return(Status);
1210 }
1211 if (!UptoDate)
1212 {
1213 /*
1214 * If the cache segment isn't up to date then call the file
1215 * system to read in the data.
1216 */
1217 Status = ReadCacheSegment(CacheSeg);
1218 if (!NT_SUCCESS(Status))
1219 {
1220 CcRosReleaseCacheSegment(Bcb, CacheSeg, FALSE, FALSE, FALSE);
1221 return Status;
1222 }
1223 }
1224
1225 Process = PsGetCurrentProcess();
1226 PageAddr = MiMapPageInHyperSpace(Process, *Page, &Irql);
1227 CacheSegOffset = BaseOffset + CacheSeg->Bcb->CacheSegmentSize - FileOffset;
1228 Length = RawLength - SegOffset;
1229 if (Length <= CacheSegOffset && Length <= PAGE_SIZE)
1230 {
1231 memcpy(PageAddr, (char*)BaseAddress + FileOffset - BaseOffset, Length);
1232 }
1233 else if (CacheSegOffset >= PAGE_SIZE)
1234 {
1235 memcpy(PageAddr, (char*)BaseAddress + FileOffset - BaseOffset, PAGE_SIZE);
1236 }
1237 else
1238 {
1239 memcpy(PageAddr, (char*)BaseAddress + FileOffset - BaseOffset, CacheSegOffset);
1240 MiUnmapPageInHyperSpace(Process, PageAddr, Irql);
1241 CcRosReleaseCacheSegment(Bcb, CacheSeg, TRUE, FALSE, FALSE);
1242 Status = CcRosGetCacheSegment(Bcb,
1243 FileOffset + CacheSegOffset,
1244 &BaseOffset,
1245 &BaseAddress,
1246 &UptoDate,
1247 &CacheSeg);
1248 if (!NT_SUCCESS(Status))
1249 {
1250 return(Status);
1251 }
1252 if (!UptoDate)
1253 {
1254 /*
1255 * If the cache segment isn't up to date then call the file
1256 * system to read in the data.
1257 */
1258 Status = ReadCacheSegment(CacheSeg);
1259 if (!NT_SUCCESS(Status))
1260 {
1261 CcRosReleaseCacheSegment(Bcb, CacheSeg, FALSE, FALSE, FALSE);
1262 return Status;
1263 }
1264 }
1265 PageAddr = MiMapPageInHyperSpace(Process, *Page, &Irql);
1266 if (Length < PAGE_SIZE)
1267 {
1268 memcpy((char*)PageAddr + CacheSegOffset, BaseAddress, Length - CacheSegOffset);
1269 }
1270 else
1271 {
1272 memcpy((char*)PageAddr + CacheSegOffset, BaseAddress, PAGE_SIZE - CacheSegOffset);
1273 }
1274 }
1275 MiUnmapPageInHyperSpace(Process, PageAddr, Irql);
1276 CcRosReleaseCacheSegment(Bcb, CacheSeg, TRUE, FALSE, FALSE);
1277 }
1278 return(STATUS_SUCCESS);
1279 }
1280 #else
1281 NTSTATUS
1282 NTAPI
1283 MiReadPage(PMEMORY_AREA MemoryArea,
1284 ULONG SegOffset,
1285 PPFN_NUMBER Page)
1286 /*
1287 * FUNCTION: Read a page for a section backed memory area.
1288 * PARAMETERS:
1289 * MemoryArea - Memory area to read the page for.
1290 * Offset - Offset of the page to read.
1291 * Page - Variable that receives a page contains the read data.
1292 */
1293 {
1294 MM_REQUIRED_RESOURCES Resources = { };
1295
1296 Resources.Context = MemoryArea->Data.SectionData.Section->FileObject;
1297 Resources.FileOffset.QuadPart = SegOffset +
1298 MemoryArea->Data.SectionData.Segment->FileOffset;
1299 Resources.Consumer = MC_USER;
1300 Resources.Amount = PAGE_SIZE;
1301
1302 DPRINT1("%S, offset %x, len %d, page %x\n", ((PFILE_OBJECT)Resources.Context)->FileName.Buffer, Resources.FileOffset.LowPart, Resources.Amount, Resources.Page[0]);
1303
1304 NTSTATUS Status = MiReadFilePage(NULL, NULL, &Resources);
1305 *Page = Resources.Page[0];
1306 return Status;
1307 }
1308 #endif
1309
1310 NTSTATUS
1311 NTAPI
1312 MmNotPresentFaultSectionView(PMMSUPPORT AddressSpace,
1313 MEMORY_AREA* MemoryArea,
1314 PVOID Address,
1315 BOOLEAN Locked)
1316 {
1317 ULONG Offset;
1318 PFN_NUMBER Page;
1319 NTSTATUS Status;
1320 PVOID PAddress;
1321 PROS_SECTION_OBJECT Section;
1322 PMM_SECTION_SEGMENT Segment;
1323 ULONG Entry;
1324 ULONG Entry1;
1325 ULONG Attributes;
1326 PMM_PAGEOP PageOp;
1327 PMM_REGION Region;
1328 BOOLEAN HasSwapEntry;
1329 PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
1330
1331 /*
1332 * There is a window between taking the page fault and locking the
1333 * address space when another thread could load the page so we check
1334 * that.
1335 */
1336 if (MmIsPagePresent(Process, Address))
1337 {
1338 return(STATUS_SUCCESS);
1339 }
1340
1341 PAddress = MM_ROUND_DOWN(Address, PAGE_SIZE);
1342 Offset = (ULONG_PTR)PAddress - (ULONG_PTR)MemoryArea->StartingAddress
1343 + MemoryArea->Data.SectionData.ViewOffset;
1344
1345 Segment = MemoryArea->Data.SectionData.Segment;
1346 Section = MemoryArea->Data.SectionData.Section;
1347 Region = MmFindRegion(MemoryArea->StartingAddress,
1348 &MemoryArea->Data.SectionData.RegionListHead,
1349 Address, NULL);
1350 /*
1351 * Lock the segment
1352 */
1353 MmLockSectionSegment(Segment);
1354
1355 /*
1356 * Check if this page needs to be mapped COW
1357 */
1358 if ((Segment->WriteCopy) &&
1359 (Region->Protect == PAGE_READWRITE ||
1360 Region->Protect == PAGE_EXECUTE_READWRITE))
1361 {
1362 Attributes = Region->Protect == PAGE_READWRITE ? PAGE_READONLY : PAGE_EXECUTE_READ;
1363 }
1364 else
1365 {
1366 Attributes = Region->Protect;
1367 }
1368
1369 /*
1370 * Get or create a page operation descriptor
1371 */
1372 PageOp = MmGetPageOp(MemoryArea, NULL, 0, Segment, Offset, MM_PAGEOP_PAGEIN, FALSE);
1373 if (PageOp == NULL)
1374 {
1375 DPRINT1("MmGetPageOp failed\n");
1376 KeBugCheck(MEMORY_MANAGEMENT);
1377 }
1378
1379 /*
1380 * Check if someone else is already handling this fault, if so wait
1381 * for them
1382 */
1383 if (PageOp->Thread != PsGetCurrentThread())
1384 {
1385 MmUnlockSectionSegment(Segment);
1386 MmUnlockAddressSpace(AddressSpace);
1387 Status = MmspWaitForPageOpCompletionEvent(PageOp);
1388 /*
1389 * Check for various strange conditions
1390 */
1391 if (Status != STATUS_SUCCESS)
1392 {
1393 DPRINT1("Failed to wait for page op, status = %x\n", Status);
1394 KeBugCheck(MEMORY_MANAGEMENT);
1395 }
1396 if (PageOp->Status == STATUS_PENDING)
1397 {
1398 DPRINT1("Woke for page op before completion\n");
1399 KeBugCheck(MEMORY_MANAGEMENT);
1400 }
1401 MmLockAddressSpace(AddressSpace);
1402 /*
1403 * If this wasn't a pagein then restart the operation
1404 */
1405 if (PageOp->OpType != MM_PAGEOP_PAGEIN)
1406 {
1407 MmspCompleteAndReleasePageOp(PageOp);
1408 DPRINT("Address 0x%.8X\n", Address);
1409 return(STATUS_MM_RESTART_OPERATION);
1410 }
1411
1412 /*
1413 * If the thread handling this fault has failed then we don't retry
1414 */
1415 if (!NT_SUCCESS(PageOp->Status))
1416 {
1417 Status = PageOp->Status;
1418 MmspCompleteAndReleasePageOp(PageOp);
1419 DPRINT("Address 0x%.8X\n", Address);
1420 return(Status);
1421 }
1422 MmLockSectionSegment(Segment);
1423 /*
1424 * If the completed fault was for another address space then set the
1425 * page in this one.
1426 */
1427 if (!MmIsPagePresent(Process, Address))
1428 {
1429 Entry = MmGetPageEntrySectionSegment(Segment, Offset);
1430 HasSwapEntry = MmIsPageSwapEntry(Process, (PVOID)PAddress);
1431
1432 if (PAGE_FROM_SSE(Entry) == 0 || HasSwapEntry)
1433 {
1434 /*
1435 * The page was a private page in another or in our address space
1436 */
1437 MmUnlockSectionSegment(Segment);
1438 MmspCompleteAndReleasePageOp(PageOp);
1439 return(STATUS_MM_RESTART_OPERATION);
1440 }
1441
1442 Page = PFN_FROM_SSE(Entry);
1443
1444 MmSharePageEntrySectionSegment(Segment, Offset);
1445
1446 /* FIXME: Should we call MmCreateVirtualMappingUnsafe if
1447 * (Section->AllocationAttributes & SEC_PHYSICALMEMORY) is true?
1448 */
1449 Status = MmCreateVirtualMapping(Process,
1450 Address,
1451 Attributes,
1452 &Page,
1453 1);
1454 if (!NT_SUCCESS(Status))
1455 {
1456 DPRINT1("Unable to create virtual mapping\n");
1457 KeBugCheck(MEMORY_MANAGEMENT);
1458 }
1459 MmInsertRmap(Page, Process, (PVOID)PAddress);
1460 }
1461 MmUnlockSectionSegment(Segment);
1462 PageOp->Status = STATUS_SUCCESS;
1463 MmspCompleteAndReleasePageOp(PageOp);
1464 DPRINT("Address 0x%.8X\n", Address);
1465 return(STATUS_SUCCESS);
1466 }
1467
1468 HasSwapEntry = MmIsPageSwapEntry(Process, (PVOID)PAddress);
1469 if (HasSwapEntry)
1470 {
1471 /*
1472 * Must be private page we have swapped out.
1473 */
1474 SWAPENTRY SwapEntry;
1475
1476 /*
1477 * Sanity check
1478 */
1479 if (Segment->Flags & MM_PAGEFILE_SEGMENT)
1480 {
1481 DPRINT1("Found a swaped out private page in a pagefile section.\n");
1482 KeBugCheck(MEMORY_MANAGEMENT);
1483 }
1484
1485 MmUnlockSectionSegment(Segment);
1486 MmDeletePageFileMapping(Process, (PVOID)PAddress, &SwapEntry);
1487
1488 MmUnlockAddressSpace(AddressSpace);
1489 Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &Page);
1490 if (!NT_SUCCESS(Status))
1491 {
1492 KeBugCheck(MEMORY_MANAGEMENT);
1493 }
1494
1495 Status = MmReadFromSwapPage(SwapEntry, Page);
1496 if (!NT_SUCCESS(Status))
1497 {
1498 DPRINT1("MmReadFromSwapPage failed, status = %x\n", Status);
1499 KeBugCheck(MEMORY_MANAGEMENT);
1500 }
1501 MmLockAddressSpace(AddressSpace);
1502 Status = MmCreateVirtualMapping(Process,
1503 Address,
1504 Region->Protect,
1505 &Page,
1506 1);
1507 if (!NT_SUCCESS(Status))
1508 {
1509 DPRINT("MmCreateVirtualMapping failed, not out of memory\n");
1510 KeBugCheck(MEMORY_MANAGEMENT);
1511 return(Status);
1512 }
1513
1514 /*
1515 * Store the swap entry for later use.
1516 */
1517 MmSetSavedSwapEntryPage(Page, SwapEntry);
1518
1519 /*
1520 * Add the page to the process's working set
1521 */
1522 MmInsertRmap(Page, Process, (PVOID)PAddress);
1523
1524 /*
1525 * Finish the operation
1526 */
1527 PageOp->Status = STATUS_SUCCESS;
1528 MmspCompleteAndReleasePageOp(PageOp);
1529 DPRINT("Address 0x%.8X\n", Address);
1530 return(STATUS_SUCCESS);
1531 }
1532
1533 /*
1534 * Satisfying a page fault on a map of /Device/PhysicalMemory is easy
1535 */
1536 if (Section->AllocationAttributes & SEC_PHYSICALMEMORY)
1537 {
1538 MmUnlockSectionSegment(Segment);
1539 /*
1540 * Just map the desired physical page
1541 */
1542 Page = Offset >> PAGE_SHIFT;
1543 Status = MmCreateVirtualMappingUnsafe(Process,
1544 Address,
1545 Region->Protect,
1546 &Page,
1547 1);
1548 if (!NT_SUCCESS(Status))
1549 {
1550 DPRINT("MmCreateVirtualMappingUnsafe failed, not out of memory\n");
1551 KeBugCheck(MEMORY_MANAGEMENT);
1552 return(Status);
1553 }
1554
1555 /*
1556 * Cleanup and release locks
1557 */
1558 PageOp->Status = STATUS_SUCCESS;
1559 MmspCompleteAndReleasePageOp(PageOp);
1560 DPRINT("Address 0x%.8X\n", Address);
1561 return(STATUS_SUCCESS);
1562 }
1563
1564 /*
1565 * Map anonymous memory for BSS sections
1566 */
1567 if (Segment->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA)
1568 {
1569 MmUnlockSectionSegment(Segment);
1570 Status = MmRequestPageMemoryConsumer(MC_USER, FALSE, &Page);
1571 if (!NT_SUCCESS(Status))
1572 {
1573 MmUnlockAddressSpace(AddressSpace);
1574 Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &Page);
1575 MmLockAddressSpace(AddressSpace);
1576 }
1577 if (!NT_SUCCESS(Status))
1578 {
1579 KeBugCheck(MEMORY_MANAGEMENT);
1580 }
1581 Status = MmCreateVirtualMapping(Process,
1582 Address,
1583 Region->Protect,
1584 &Page,
1585 1);
1586 if (!NT_SUCCESS(Status))
1587 {
1588 DPRINT("MmCreateVirtualMapping failed, not out of memory\n");
1589 KeBugCheck(MEMORY_MANAGEMENT);
1590 return(Status);
1591 }
1592 MmInsertRmap(Page, Process, (PVOID)PAddress);
1593
1594 /*
1595 * Cleanup and release locks
1596 */
1597 PageOp->Status = STATUS_SUCCESS;
1598 MmspCompleteAndReleasePageOp(PageOp);
1599 DPRINT("Address 0x%.8X\n", Address);
1600 return(STATUS_SUCCESS);
1601 }
1602
1603 /*
1604 * Get the entry corresponding to the offset within the section
1605 */
1606 Entry = MmGetPageEntrySectionSegment(Segment, Offset);
1607
1608 if (Entry == 0)
1609 {
1610 /*
1611 * If the entry is zero (and it can't change because we have
1612 * locked the segment) then we need to load the page.
1613 */
1614
1615 /*
1616 * Release all our locks and read in the page from disk
1617 */
1618 MmUnlockSectionSegment(Segment);
1619 MmUnlockAddressSpace(AddressSpace);
1620
1621 if ((Segment->Flags & MM_PAGEFILE_SEGMENT) ||
1622 (Offset >= PAGE_ROUND_UP(Segment->RawLength) && Section->AllocationAttributes & SEC_IMAGE))
1623 {
1624 Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &Page);
1625 if (!NT_SUCCESS(Status))
1626 {
1627 DPRINT1("MmRequestPageMemoryConsumer failed (Status %x)\n", Status);
1628 }
1629
1630 }
1631 else
1632 {
1633 Status = MiReadPage(MemoryArea, Offset, &Page);
1634 if (!NT_SUCCESS(Status))
1635 {
1636 DPRINT1("MiReadPage failed (Status %x)\n", Status);
1637 }
1638 }
1639 if (!NT_SUCCESS(Status))
1640 {
1641 /*
1642 * FIXME: What do we know in this case?
1643 */
1644 /*
1645 * Cleanup and release locks
1646 */
1647 MmLockAddressSpace(AddressSpace);
1648 PageOp->Status = Status;
1649 MmspCompleteAndReleasePageOp(PageOp);
1650 DPRINT("Address 0x%.8X\n", Address);
1651 return(Status);
1652 }
1653 /*
1654 * Relock the address space and segment
1655 */
1656 MmLockAddressSpace(AddressSpace);
1657 MmLockSectionSegment(Segment);
1658
1659 /*
1660 * Check the entry. No one should change the status of a page
1661 * that has a pending page-in.
1662 */
1663 Entry1 = MmGetPageEntrySectionSegment(Segment, Offset);
1664 if (Entry != Entry1)
1665 {
1666 DPRINT1("Someone changed ppte entry while we slept\n");
1667 KeBugCheck(MEMORY_MANAGEMENT);
1668 }
1669
1670 /*
1671 * Mark the offset within the section as having valid, in-memory
1672 * data
1673 */
1674 Entry = MAKE_SSE(Page << PAGE_SHIFT, 1);
1675 MmSetPageEntrySectionSegment(Segment, Offset, Entry);
1676 MmUnlockSectionSegment(Segment);
1677
1678 Status = MmCreateVirtualMapping(Process,
1679 Address,
1680 Attributes,
1681 &Page,
1682 1);
1683 if (!NT_SUCCESS(Status))
1684 {
1685 DPRINT1("Unable to create virtual mapping\n");
1686 KeBugCheck(MEMORY_MANAGEMENT);
1687 }
1688 MmInsertRmap(Page, Process, (PVOID)PAddress);
1689
1690 PageOp->Status = STATUS_SUCCESS;
1691 MmspCompleteAndReleasePageOp(PageOp);
1692 DPRINT("Address 0x%.8X\n", Address);
1693 return(STATUS_SUCCESS);
1694 }
1695 else if (IS_SWAP_FROM_SSE(Entry))
1696 {
1697 SWAPENTRY SwapEntry;
1698
1699 SwapEntry = SWAPENTRY_FROM_SSE(Entry);
1700
1701 /*
1702 * Release all our locks and read in the page from disk
1703 */
1704 MmUnlockSectionSegment(Segment);
1705
1706 MmUnlockAddressSpace(AddressSpace);
1707
1708 Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &Page);
1709 if (!NT_SUCCESS(Status))
1710 {
1711 KeBugCheck(MEMORY_MANAGEMENT);
1712 }
1713
1714 Status = MmReadFromSwapPage(SwapEntry, Page);
1715 if (!NT_SUCCESS(Status))
1716 {
1717 KeBugCheck(MEMORY_MANAGEMENT);
1718 }
1719
1720 /*
1721 * Relock the address space and segment
1722 */
1723 MmLockAddressSpace(AddressSpace);
1724 MmLockSectionSegment(Segment);
1725
1726 /*
1727 * Check the entry. No one should change the status of a page
1728 * that has a pending page-in.
1729 */
1730 Entry1 = MmGetPageEntrySectionSegment(Segment, Offset);
1731 if (Entry != Entry1)
1732 {
1733 DPRINT1("Someone changed ppte entry while we slept\n");
1734 KeBugCheck(MEMORY_MANAGEMENT);
1735 }
1736
1737 /*
1738 * Mark the offset within the section as having valid, in-memory
1739 * data
1740 */
1741 Entry = MAKE_SSE(Page << PAGE_SHIFT, 1);
1742 MmSetPageEntrySectionSegment(Segment, Offset, Entry);
1743 MmUnlockSectionSegment(Segment);
1744
1745 /*
1746 * Save the swap entry.
1747 */
1748 MmSetSavedSwapEntryPage(Page, SwapEntry);
1749 Status = MmCreateVirtualMapping(Process,
1750 Address,
1751 Region->Protect,
1752 &Page,
1753 1);
1754 if (!NT_SUCCESS(Status))
1755 {
1756 DPRINT1("Unable to create virtual mapping\n");
1757 KeBugCheck(MEMORY_MANAGEMENT);
1758 }
1759 MmInsertRmap(Page, Process, (PVOID)PAddress);
1760 PageOp->Status = STATUS_SUCCESS;
1761 MmspCompleteAndReleasePageOp(PageOp);
1762 DPRINT("Address 0x%.8X\n", Address);
1763 return(STATUS_SUCCESS);
1764 }
1765 else
1766 {
1767 /*
1768 * If the section offset is already in-memory and valid then just
1769 * take another reference to the page
1770 */
1771
1772 Page = PFN_FROM_SSE(Entry);
1773
1774 MmSharePageEntrySectionSegment(Segment, Offset);
1775 MmUnlockSectionSegment(Segment);
1776
1777 Status = MmCreateVirtualMapping(Process,
1778 Address,
1779 Attributes,
1780 &Page,
1781 1);
1782 if (!NT_SUCCESS(Status))
1783 {
1784 DPRINT1("Unable to create virtual mapping\n");
1785 KeBugCheck(MEMORY_MANAGEMENT);
1786 }
1787 MmInsertRmap(Page, Process, (PVOID)PAddress);
1788 PageOp->Status = STATUS_SUCCESS;
1789 MmspCompleteAndReleasePageOp(PageOp);
1790 DPRINT("Address 0x%.8X\n", Address);
1791 return(STATUS_SUCCESS);
1792 }
1793 }
1794
1795 NTSTATUS
1796 NTAPI
1797 MmAccessFaultSectionView(PMMSUPPORT AddressSpace,
1798 MEMORY_AREA* MemoryArea,
1799 PVOID Address,
1800 BOOLEAN Locked)
1801 {
1802 PMM_SECTION_SEGMENT Segment;
1803 PROS_SECTION_OBJECT Section;
1804 PFN_NUMBER OldPage;
1805 PFN_NUMBER NewPage;
1806 NTSTATUS Status;
1807 PVOID PAddress;
1808 ULONG Offset;
1809 PMM_PAGEOP PageOp;
1810 PMM_REGION Region;
1811 ULONG Entry;
1812 PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
1813
1814 DPRINT("MmAccessFaultSectionView(%x, %x, %x, %x)\n", AddressSpace, MemoryArea, Address, Locked);
1815
1816 /*
1817 * Check if the page has been paged out or has already been set readwrite
1818 */
1819 if (!MmIsPagePresent(Process, Address) ||
1820 MmGetPageProtect(Process, Address) & PAGE_READWRITE)
1821 {
1822 DPRINT("Address 0x%.8X\n", Address);
1823 return(STATUS_SUCCESS);
1824 }
1825
1826 /*
1827 * Find the offset of the page
1828 */
1829 PAddress = MM_ROUND_DOWN(Address, PAGE_SIZE);
1830 Offset = (ULONG_PTR)PAddress - (ULONG_PTR)MemoryArea->StartingAddress
1831 + MemoryArea->Data.SectionData.ViewOffset;
1832
1833 Segment = MemoryArea->Data.SectionData.Segment;
1834 Section = MemoryArea->Data.SectionData.Section;
1835 Region = MmFindRegion(MemoryArea->StartingAddress,
1836 &MemoryArea->Data.SectionData.RegionListHead,
1837 Address, NULL);
1838 /*
1839 * Lock the segment
1840 */
1841 MmLockSectionSegment(Segment);
1842
1843 OldPage = MmGetPfnForProcess(NULL, Address);
1844 Entry = MmGetPageEntrySectionSegment(Segment, Offset);
1845
1846 MmUnlockSectionSegment(Segment);
1847
1848 /*
1849 * Check if we are doing COW
1850 */
1851 if (!((Segment->WriteCopy) &&
1852 (Region->Protect == PAGE_READWRITE ||
1853 Region->Protect == PAGE_EXECUTE_READWRITE)))
1854 {
1855 DPRINT("Address 0x%.8X\n", Address);
1856 return(STATUS_ACCESS_VIOLATION);
1857 }
1858
1859 if (IS_SWAP_FROM_SSE(Entry) ||
1860 PFN_FROM_SSE(Entry) != OldPage)
1861 {
1862 /* This is a private page. We must only change the page protection. */
1863 MmSetPageProtect(Process, PAddress, Region->Protect);
1864 return(STATUS_SUCCESS);
1865 }
1866
1867 /*
1868 * Get or create a pageop
1869 */
1870 PageOp = MmGetPageOp(MemoryArea, NULL, 0, Segment, Offset,
1871 MM_PAGEOP_ACCESSFAULT, FALSE);
1872 if (PageOp == NULL)
1873 {
1874 DPRINT1("MmGetPageOp failed\n");
1875 KeBugCheck(MEMORY_MANAGEMENT);
1876 }
1877
1878 /*
1879 * Wait for any other operations to complete
1880 */
1881 if (PageOp->Thread != PsGetCurrentThread())
1882 {
1883 MmUnlockAddressSpace(AddressSpace);
1884 Status = MmspWaitForPageOpCompletionEvent(PageOp);
1885 /*
1886 * Check for various strange conditions
1887 */
1888 if (Status == STATUS_TIMEOUT)
1889 {
1890 DPRINT1("Failed to wait for page op, status = %x\n", Status);
1891 KeBugCheck(MEMORY_MANAGEMENT);
1892 }
1893 if (PageOp->Status == STATUS_PENDING)
1894 {
1895 DPRINT1("Woke for page op before completion\n");
1896 KeBugCheck(MEMORY_MANAGEMENT);
1897 }
1898 /*
1899 * Restart the operation
1900 */
1901 MmLockAddressSpace(AddressSpace);
1902 MmspCompleteAndReleasePageOp(PageOp);
1903 DPRINT("Address 0x%.8X\n", Address);
1904 return(STATUS_MM_RESTART_OPERATION);
1905 }
1906
1907 /*
1908 * Release locks now we have the pageop
1909 */
1910 MmUnlockAddressSpace(AddressSpace);
1911
1912 /*
1913 * Allocate a page
1914 */
1915 Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &NewPage);
1916 if (!NT_SUCCESS(Status))
1917 {
1918 KeBugCheck(MEMORY_MANAGEMENT);
1919 }
1920
1921 /*
1922 * Copy the old page
1923 */
1924 MiCopyFromUserPage(NewPage, PAddress);
1925
1926 MmLockAddressSpace(AddressSpace);
1927 /*
1928 * Delete the old entry.
1929 */
1930 MmDeleteVirtualMapping(Process, Address, FALSE, NULL, NULL);
1931
1932 /*
1933 * Set the PTE to point to the new page
1934 */
1935 Status = MmCreateVirtualMapping(Process,
1936 Address,
1937 Region->Protect,
1938 &NewPage,
1939 1);
1940 if (!NT_SUCCESS(Status))
1941 {
1942 DPRINT("MmCreateVirtualMapping failed, not out of memory\n");
1943 KeBugCheck(MEMORY_MANAGEMENT);
1944 return(Status);
1945 }
1946 if (!NT_SUCCESS(Status))
1947 {
1948 DPRINT1("Unable to create virtual mapping\n");
1949 KeBugCheck(MEMORY_MANAGEMENT);
1950 }
1951
1952 /*
1953 * Unshare the old page.
1954 */
1955 MmDeleteRmap(OldPage, Process, PAddress);
1956 MmInsertRmap(NewPage, Process, PAddress);
1957 MmLockSectionSegment(Segment);
1958 MmUnsharePageEntrySectionSegment(Section, Segment, Offset, FALSE, FALSE);
1959 MmUnlockSectionSegment(Segment);
1960
1961 PageOp->Status = STATUS_SUCCESS;
1962 MmspCompleteAndReleasePageOp(PageOp);
1963 DPRINT("Address 0x%.8X\n", Address);
1964 return(STATUS_SUCCESS);
1965 }
1966
1967 VOID
1968 MmPageOutDeleteMapping(PVOID Context, PEPROCESS Process, PVOID Address)
1969 {
1970 MM_SECTION_PAGEOUT_CONTEXT* PageOutContext;
1971 BOOLEAN WasDirty;
1972 PFN_NUMBER Page;
1973
1974 PageOutContext = (MM_SECTION_PAGEOUT_CONTEXT*)Context;
1975 if (Process)
1976 {
1977 MmLockAddressSpace(&Process->Vm);
1978 }
1979
1980 MmDeleteVirtualMapping(Process,
1981 Address,
1982 FALSE,
1983 &WasDirty,
1984 &Page);
1985 if (WasDirty)
1986 {
1987 PageOutContext->WasDirty = TRUE;
1988 }
1989 if (!PageOutContext->Private)
1990 {
1991 MmLockSectionSegment(PageOutContext->Segment);
1992 MmUnsharePageEntrySectionSegment((PROS_SECTION_OBJECT)PageOutContext->Section,
1993 PageOutContext->Segment,
1994 PageOutContext->Offset,
1995 PageOutContext->WasDirty,
1996 TRUE);
1997 MmUnlockSectionSegment(PageOutContext->Segment);
1998 }
1999 if (Process)
2000 {
2001 MmUnlockAddressSpace(&Process->Vm);
2002 }
2003
2004 if (PageOutContext->Private)
2005 {
2006 MmReleasePageMemoryConsumer(MC_USER, Page);
2007 }
2008
2009 DPRINT("PhysicalAddress %x, Address %x\n", Page << PAGE_SHIFT, Address);
2010 }
2011
2012 NTSTATUS
2013 NTAPI
2014 MmPageOutSectionView(PMMSUPPORT AddressSpace,
2015 MEMORY_AREA* MemoryArea,
2016 PVOID Address,
2017 PMM_PAGEOP PageOp)
2018 {
2019 PFN_NUMBER Page;
2020 MM_SECTION_PAGEOUT_CONTEXT Context;
2021 SWAPENTRY SwapEntry;
2022 ULONG Entry;
2023 ULONG FileOffset;
2024 NTSTATUS Status;
2025 PFILE_OBJECT FileObject;
2026 PBCB Bcb = NULL;
2027 BOOLEAN DirectMapped;
2028 BOOLEAN IsImageSection;
2029 PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
2030 KIRQL OldIrql;
2031
2032 Address = (PVOID)PAGE_ROUND_DOWN(Address);
2033
2034 /*
2035 * Get the segment and section.
2036 */
2037 Context.Segment = MemoryArea->Data.SectionData.Segment;
2038 Context.Section = MemoryArea->Data.SectionData.Section;
2039
2040 Context.Offset = (ULONG_PTR)Address - (ULONG_PTR)MemoryArea->StartingAddress
2041 + MemoryArea->Data.SectionData.ViewOffset;
2042 FileOffset = Context.Offset + Context.Segment->FileOffset;
2043
2044 IsImageSection = Context.Section->AllocationAttributes & SEC_IMAGE ? TRUE : FALSE;
2045
2046 FileObject = Context.Section->FileObject;
2047 DirectMapped = FALSE;
2048 if (FileObject != NULL &&
2049 !(Context.Segment->Characteristics & IMAGE_SCN_MEM_SHARED))
2050 {
2051 Bcb = FileObject->SectionObjectPointer->SharedCacheMap;
2052
2053 /*
2054 * If the file system is letting us go directly to the cache and the
2055 * memory area was mapped at an offset in the file which is page aligned
2056 * then note this is a direct mapped page.
2057 */
2058 if ((FileOffset % PAGE_SIZE) == 0 &&
2059 (Context.Offset + PAGE_SIZE <= Context.Segment->RawLength || !IsImageSection))
2060 {
2061 DirectMapped = TRUE;
2062 }
2063 }
2064
2065
2066 /*
2067 * This should never happen since mappings of physical memory are never
2068 * placed in the rmap lists.
2069 */
2070 if (Context.Section->AllocationAttributes & SEC_PHYSICALMEMORY)
2071 {
2072 DPRINT1("Trying to page out from physical memory section address 0x%X "
2073 "process %d\n", Address,
2074 Process ? Process->UniqueProcessId : 0);
2075 KeBugCheck(MEMORY_MANAGEMENT);
2076 }
2077
2078 /*
2079 * Get the section segment entry and the physical address.
2080 */
2081 Entry = MmGetPageEntrySectionSegment(Context.Segment, Context.Offset);
2082 if (!MmIsPagePresent(Process, Address))
2083 {
2084 DPRINT1("Trying to page out not-present page at (%d,0x%.8X).\n",
2085 Process ? Process->UniqueProcessId : 0, Address);
2086 KeBugCheck(MEMORY_MANAGEMENT);
2087 }
2088 Page = MmGetPfnForProcess(Process, Address);
2089 SwapEntry = MmGetSavedSwapEntryPage(Page);
2090
2091 /*
2092 * Prepare the context structure for the rmap delete call.
2093 */
2094 Context.WasDirty = FALSE;
2095 if (Context.Segment->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA ||
2096 IS_SWAP_FROM_SSE(Entry) ||
2097 PFN_FROM_SSE(Entry) != Page)
2098 {
2099 Context.Private = TRUE;
2100 }
2101 else
2102 {
2103 Context.Private = FALSE;
2104 }
2105
2106 /*
2107 * Take an additional reference to the page or the cache segment.
2108 */
2109 if (DirectMapped && !Context.Private)
2110 {
2111 if(!MiIsPageFromCache(MemoryArea, Context.Offset))
2112 {
2113 DPRINT1("Direct mapped non private page is not associated with the cache.\n");
2114 KeBugCheck(MEMORY_MANAGEMENT);
2115 }
2116 }
2117 else
2118 {
2119 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
2120 MmReferencePage(Page);
2121 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
2122 }
2123
2124 MmDeleteAllRmaps(Page, (PVOID)&Context, MmPageOutDeleteMapping);
2125
2126 /*
2127 * If this wasn't a private page then we should have reduced the entry to
2128 * zero by deleting all the rmaps.
2129 */
2130 if (!Context.Private && MmGetPageEntrySectionSegment(Context.Segment, Context.Offset) != 0)
2131 {
2132 if (!(Context.Segment->Flags & MM_PAGEFILE_SEGMENT) &&
2133 !(Context.Segment->Characteristics & IMAGE_SCN_MEM_SHARED))
2134 {
2135 KeBugCheck(MEMORY_MANAGEMENT);
2136 }
2137 }
2138
2139 /*
2140 * If the page wasn't dirty then we can just free it as for a readonly page.
2141 * Since we unmapped all the mappings above we know it will not suddenly
2142 * become dirty.
2143 * If the page is from a pagefile section and has no swap entry,
2144 * we can't free the page at this point.
2145 */
2146 SwapEntry = MmGetSavedSwapEntryPage(Page);
2147 if (Context.Segment->Flags & MM_PAGEFILE_SEGMENT)
2148 {
2149 if (Context.Private)
2150 {
2151 DPRINT1("Found a %s private page (address %x) in a pagefile segment.\n",
2152 Context.WasDirty ? "dirty" : "clean", Address);
2153 KeBugCheck(MEMORY_MANAGEMENT);
2154 }
2155 if (!Context.WasDirty && SwapEntry != 0)
2156 {
2157 MmSetSavedSwapEntryPage(Page, 0);
2158 MmSetPageEntrySectionSegment(Context.Segment, Context.Offset, MAKE_SWAP_SSE(SwapEntry));
2159 MmReleasePageMemoryConsumer(MC_USER, Page);
2160 PageOp->Status = STATUS_SUCCESS;
2161 MmspCompleteAndReleasePageOp(PageOp);
2162 return(STATUS_SUCCESS);
2163 }
2164 }
2165 else if (Context.Segment->Characteristics & IMAGE_SCN_MEM_SHARED)
2166 {
2167 if (Context.Private)
2168 {
2169 DPRINT1("Found a %s private page (address %x) in a shared section segment.\n",
2170 Context.WasDirty ? "dirty" : "clean", Address);
2171 KeBugCheck(MEMORY_MANAGEMENT);
2172 }
2173 if (!Context.WasDirty || SwapEntry != 0)
2174 {
2175 MmSetSavedSwapEntryPage(Page, 0);
2176 if (SwapEntry != 0)
2177 {
2178 MmSetPageEntrySectionSegment(Context.Segment, Context.Offset, MAKE_SWAP_SSE(SwapEntry));
2179 }
2180 MmReleasePageMemoryConsumer(MC_USER, Page);
2181 PageOp->Status = STATUS_SUCCESS;
2182 MmspCompleteAndReleasePageOp(PageOp);
2183 return(STATUS_SUCCESS);
2184 }
2185 }
2186 else if (!Context.Private && DirectMapped)
2187 {
2188 if (SwapEntry != 0)
2189 {
2190 DPRINT1("Found a swapentry for a non private and direct mapped page (address %x)\n",
2191 Address);
2192 KeBugCheck(MEMORY_MANAGEMENT);
2193 }
2194 #ifndef NEWCC
2195 Status = CcRosUnmapCacheSegment(Bcb, FileOffset, FALSE);
2196 #else
2197 Status = STATUS_SUCCESS;
2198 #endif
2199 if (!NT_SUCCESS(Status))
2200 {
2201 DPRINT1("CCRosUnmapCacheSegment failed, status = %x\n", Status);
2202 KeBugCheck(MEMORY_MANAGEMENT);
2203 }
2204 PageOp->Status = STATUS_SUCCESS;
2205 MmspCompleteAndReleasePageOp(PageOp);
2206 return(STATUS_SUCCESS);
2207 }
2208 else if (!Context.WasDirty && !DirectMapped && !Context.Private)
2209 {
2210 if (SwapEntry != 0)
2211 {
2212 DPRINT1("Found a swap entry for a non dirty, non private and not direct mapped page (address %x)\n",
2213 Address);
2214 KeBugCheck(MEMORY_MANAGEMENT);
2215 }
2216 MmReleasePageMemoryConsumer(MC_USER, Page);
2217 PageOp->Status = STATUS_SUCCESS;
2218 MmspCompleteAndReleasePageOp(PageOp);
2219 return(STATUS_SUCCESS);
2220 }
2221 else if (!Context.WasDirty && Context.Private && SwapEntry != 0)
2222 {
2223 MmSetSavedSwapEntryPage(Page, 0);
2224 MmLockAddressSpace(AddressSpace);
2225 Status = MmCreatePageFileMapping(Process,
2226 Address,
2227 SwapEntry);
2228 MmUnlockAddressSpace(AddressSpace);
2229 if (!NT_SUCCESS(Status))
2230 {
2231 KeBugCheck(MEMORY_MANAGEMENT);
2232 }
2233 MmReleasePageMemoryConsumer(MC_USER, Page);
2234 PageOp->Status = STATUS_SUCCESS;
2235 MmspCompleteAndReleasePageOp(PageOp);
2236 return(STATUS_SUCCESS);
2237 }
2238
2239 /*
2240 * If necessary, allocate an entry in the paging file for this page
2241 */
2242 if (SwapEntry == 0)
2243 {
2244 SwapEntry = MmAllocSwapPage();
2245 if (SwapEntry == 0)
2246 {
2247 MmShowOutOfSpaceMessagePagingFile();
2248 MmLockAddressSpace(AddressSpace);
2249 /*
2250 * For private pages restore the old mappings.
2251 */
2252 if (Context.Private)
2253 {
2254 Status = MmCreateVirtualMapping(Process,
2255 Address,
2256 MemoryArea->Protect,
2257 &Page,
2258 1);
2259 MmSetDirtyPage(Process, Address);
2260 MmInsertRmap(Page,
2261 Process,
2262 Address);
2263 }
2264 else
2265 {
2266 /*
2267 * For non-private pages if the page wasn't direct mapped then
2268 * set it back into the section segment entry so we don't loose
2269 * our copy. Otherwise it will be handled by the cache manager.
2270 */
2271 Status = MmCreateVirtualMapping(Process,
2272 Address,
2273 MemoryArea->Protect,
2274 &Page,
2275 1);
2276 MmSetDirtyPage(Process, Address);
2277 MmInsertRmap(Page,
2278 Process,
2279 Address);
2280 Entry = MAKE_SSE(Page << PAGE_SHIFT, 1);
2281 MmSetPageEntrySectionSegment(Context.Segment, Context.Offset, Entry);
2282 }
2283 MmUnlockAddressSpace(AddressSpace);
2284 PageOp->Status = STATUS_UNSUCCESSFUL;
2285 MmspCompleteAndReleasePageOp(PageOp);
2286 return(STATUS_PAGEFILE_QUOTA);
2287 }
2288 }
2289
2290 /*
2291 * Write the page to the pagefile
2292 */
2293 Status = MmWriteToSwapPage(SwapEntry, Page);
2294 if (!NT_SUCCESS(Status))
2295 {
2296 DPRINT1("MM: Failed to write to swap page (Status was 0x%.8X)\n",
2297 Status);
2298 /*
2299 * As above: undo our actions.
2300 * FIXME: Also free the swap page.
2301 */
2302 MmLockAddressSpace(AddressSpace);
2303 if (Context.Private)
2304 {
2305 Status = MmCreateVirtualMapping(Process,
2306 Address,
2307 MemoryArea->Protect,
2308 &Page,
2309 1);
2310 MmSetDirtyPage(Process, Address);
2311 MmInsertRmap(Page,
2312 Process,
2313 Address);
2314 }
2315 else
2316 {
2317 Status = MmCreateVirtualMapping(Process,
2318 Address,
2319 MemoryArea->Protect,
2320 &Page,
2321 1);
2322 MmSetDirtyPage(Process, Address);
2323 MmInsertRmap(Page,
2324 Process,
2325 Address);
2326 Entry = MAKE_SSE(Page << PAGE_SHIFT, 1);
2327 MmSetPageEntrySectionSegment(Context.Segment, Context.Offset, Entry);
2328 }
2329 MmUnlockAddressSpace(AddressSpace);
2330 PageOp->Status = STATUS_UNSUCCESSFUL;
2331 MmspCompleteAndReleasePageOp(PageOp);
2332 return(STATUS_UNSUCCESSFUL);
2333 }
2334
2335 /*
2336 * Otherwise we have succeeded.
2337 */
2338 DPRINT("MM: Wrote section page 0x%.8X to swap!\n", Page << PAGE_SHIFT);
2339 MmSetSavedSwapEntryPage(Page, 0);
2340 if (Context.Segment->Flags & MM_PAGEFILE_SEGMENT ||
2341 Context.Segment->Characteristics & IMAGE_SCN_MEM_SHARED)
2342 {
2343 MmSetPageEntrySectionSegment(Context.Segment, Context.Offset, MAKE_SWAP_SSE(SwapEntry));
2344 }
2345 else
2346 {
2347 MmReleasePageMemoryConsumer(MC_USER, Page);
2348 }
2349
2350 if (Context.Private)
2351 {
2352 MmLockAddressSpace(AddressSpace);
2353 Status = MmCreatePageFileMapping(Process,
2354 Address,
2355 SwapEntry);
2356 MmUnlockAddressSpace(AddressSpace);
2357 if (!NT_SUCCESS(Status))
2358 {
2359 KeBugCheck(MEMORY_MANAGEMENT);
2360 }
2361 }
2362 else
2363 {
2364 Entry = MAKE_SWAP_SSE(SwapEntry);
2365 MmSetPageEntrySectionSegment(Context.Segment, Context.Offset, Entry);
2366 }
2367
2368 PageOp->Status = STATUS_SUCCESS;
2369 MmspCompleteAndReleasePageOp(PageOp);
2370 return(STATUS_SUCCESS);
2371 }
2372
2373 NTSTATUS
2374 NTAPI
2375 MmWritePageSectionView(PMMSUPPORT AddressSpace,
2376 PMEMORY_AREA MemoryArea,
2377 PVOID Address,
2378 PMM_PAGEOP PageOp)
2379 {
2380 ULONG Offset;
2381 PROS_SECTION_OBJECT Section;
2382 PMM_SECTION_SEGMENT Segment;
2383 PFN_NUMBER Page;
2384 SWAPENTRY SwapEntry;
2385 ULONG Entry;
2386 BOOLEAN Private;
2387 NTSTATUS Status;
2388 PFILE_OBJECT FileObject;
2389 PBCB Bcb = NULL;
2390 BOOLEAN DirectMapped;
2391 BOOLEAN IsImageSection;
2392 PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
2393
2394 Address = (PVOID)PAGE_ROUND_DOWN(Address);
2395
2396 Offset = (ULONG_PTR)Address - (ULONG_PTR)MemoryArea->StartingAddress
2397 + MemoryArea->Data.SectionData.ViewOffset;
2398
2399 /*
2400 * Get the segment and section.
2401 */
2402 Segment = MemoryArea->Data.SectionData.Segment;
2403 Section = MemoryArea->Data.SectionData.Section;
2404 IsImageSection = Section->AllocationAttributes & SEC_IMAGE ? TRUE : FALSE;
2405
2406 FileObject = Section->FileObject;
2407 DirectMapped = FALSE;
2408 if (FileObject != NULL &&
2409 !(Segment->Characteristics & IMAGE_SCN_MEM_SHARED))
2410 {
2411 Bcb = FileObject->SectionObjectPointer->SharedCacheMap;
2412
2413 /*
2414 * If the file system is letting us go directly to the cache and the
2415 * memory area was mapped at an offset in the file which is page aligned
2416 * then note this is a direct mapped page.
2417 */
2418 if (((Offset + Segment->FileOffset) % PAGE_SIZE) == 0 &&
2419 (Offset + PAGE_SIZE <= Segment->RawLength || !IsImageSection))
2420 {
2421 DirectMapped = TRUE;
2422 }
2423 }
2424
2425 /*
2426 * This should never happen since mappings of physical memory are never
2427 * placed in the rmap lists.
2428 */
2429 if (Section->AllocationAttributes & SEC_PHYSICALMEMORY)
2430 {
2431 DPRINT1("Trying to write back page from physical memory mapped at %X "
2432 "process %d\n", Address,
2433 Process ? Process->UniqueProcessId : 0);
2434 KeBugCheck(MEMORY_MANAGEMENT);
2435 }
2436
2437 /*
2438 * Get the section segment entry and the physical address.
2439 */
2440 Entry = MmGetPageEntrySectionSegment(Segment, Offset);
2441 if (!MmIsPagePresent(Process, Address))
2442 {
2443 DPRINT1("Trying to page out not-present page at (%d,0x%.8X).\n",
2444 Process ? Process->UniqueProcessId : 0, Address);
2445 KeBugCheck(MEMORY_MANAGEMENT);
2446 }
2447 Page = MmGetPfnForProcess(Process, Address);
2448 SwapEntry = MmGetSavedSwapEntryPage(Page);
2449
2450 /*
2451 * Check for a private (COWed) page.
2452 */
2453 if (Segment->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA ||
2454 IS_SWAP_FROM_SSE(Entry) ||
2455 PFN_FROM_SSE(Entry) != Page)
2456 {
2457 Private = TRUE;
2458 }
2459 else
2460 {
2461 Private = FALSE;
2462 }
2463
2464 /*
2465 * Speculatively set all mappings of the page to clean.
2466 */
2467 MmSetCleanAllRmaps(Page);
2468
2469 /*
2470 * If this page was direct mapped from the cache then the cache manager
2471 * will take care of writing it back to disk.
2472 */
2473 if (DirectMapped && !Private)
2474 {
2475 ASSERT(SwapEntry == 0);
2476 #ifndef NEWCC
2477 CcRosMarkDirtyCacheSegment(Bcb, Offset + Segment->FileOffset);
2478 #endif
2479 PageOp->Status = STATUS_SUCCESS;
2480 MmspCompleteAndReleasePageOp(PageOp);
2481 return(STATUS_SUCCESS);
2482 }
2483
2484 /*
2485 * If necessary, allocate an entry in the paging file for this page
2486 */
2487 if (SwapEntry == 0)
2488 {
2489 SwapEntry = MmAllocSwapPage();
2490 if (SwapEntry == 0)
2491 {
2492 MmSetDirtyAllRmaps(Page);
2493 PageOp->Status = STATUS_UNSUCCESSFUL;
2494 MmspCompleteAndReleasePageOp(PageOp);
2495 return(STATUS_PAGEFILE_QUOTA);
2496 }
2497 MmSetSavedSwapEntryPage(Page, SwapEntry);
2498 }
2499
2500 /*
2501 * Write the page to the pagefile
2502 */
2503 Status = MmWriteToSwapPage(SwapEntry, Page);
2504 if (!NT_SUCCESS(Status))
2505 {
2506 DPRINT1("MM: Failed to write to swap page (Status was 0x%.8X)\n",
2507 Status);
2508 MmSetDirtyAllRmaps(Page);
2509 PageOp->Status = STATUS_UNSUCCESSFUL;
2510 MmspCompleteAndReleasePageOp(PageOp);
2511 return(STATUS_UNSUCCESSFUL);
2512 }
2513
2514 /*
2515 * Otherwise we have succeeded.
2516 */
2517 DPRINT("MM: Wrote section page 0x%.8X to swap!\n", Page << PAGE_SHIFT);
2518 PageOp->Status = STATUS_SUCCESS;
2519 MmspCompleteAndReleasePageOp(PageOp);
2520 return(STATUS_SUCCESS);
2521 }
2522
2523 static VOID
2524 MmAlterViewAttributes(PMMSUPPORT AddressSpace,
2525 PVOID BaseAddress,
2526 ULONG RegionSize,
2527 ULONG OldType,
2528 ULONG OldProtect,
2529 ULONG NewType,
2530 ULONG NewProtect)
2531 {
2532 PMEMORY_AREA MemoryArea;
2533 PMM_SECTION_SEGMENT Segment;
2534 BOOLEAN DoCOW = FALSE;
2535 ULONG i;
2536 PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
2537
2538 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, BaseAddress);
2539 Segment = MemoryArea->Data.SectionData.Segment;
2540
2541 if ((Segment->WriteCopy) &&
2542 (NewProtect == PAGE_READWRITE || NewProtect == PAGE_EXECUTE_READWRITE))
2543 {
2544 DoCOW = TRUE;
2545 }
2546
2547 if (OldProtect != NewProtect)
2548 {
2549 for (i = 0; i < PAGE_ROUND_UP(RegionSize) / PAGE_SIZE; i++)
2550 {
2551 PVOID Address = (char*)BaseAddress + (i * PAGE_SIZE);
2552 ULONG Protect = NewProtect;
2553
2554 /*
2555 * If we doing COW for this segment then check if the page is
2556 * already private.
2557 */
2558 if (DoCOW && MmIsPagePresent(Process, Address))
2559 {
2560 ULONG Offset;
2561 ULONG Entry;
2562 PFN_NUMBER Page;
2563
2564 Offset = (ULONG_PTR)Address - (ULONG_PTR)MemoryArea->StartingAddress
2565 + MemoryArea->Data.SectionData.ViewOffset;
2566 Entry = MmGetPageEntrySectionSegment(Segment, Offset);
2567 Page = MmGetPfnForProcess(Process, Address);
2568
2569 Protect = PAGE_READONLY;
2570 if (Segment->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA ||
2571 IS_SWAP_FROM_SSE(Entry) ||
2572 PFN_FROM_SSE(Entry) != Page)
2573 {
2574 Protect = NewProtect;
2575 }
2576 }
2577
2578 if (MmIsPagePresent(Process, Address))
2579 {
2580 MmSetPageProtect(Process, Address,
2581 Protect);
2582 }
2583 }
2584 }
2585 }
2586
2587 NTSTATUS
2588 NTAPI
2589 MmProtectSectionView(PMMSUPPORT AddressSpace,
2590 PMEMORY_AREA MemoryArea,
2591 PVOID BaseAddress,
2592 ULONG Length,
2593 ULONG Protect,
2594 PULONG OldProtect)
2595 {
2596 PMM_REGION Region;
2597 NTSTATUS Status;
2598 ULONG_PTR MaxLength;
2599
2600 MaxLength = (ULONG_PTR)MemoryArea->EndingAddress - (ULONG_PTR)BaseAddress;
2601 if (Length > MaxLength)
2602 Length = MaxLength;
2603
2604 Region = MmFindRegion(MemoryArea->StartingAddress,
2605 &MemoryArea->Data.SectionData.RegionListHead,
2606 BaseAddress, NULL);
2607 if ((MemoryArea->Flags & SEC_NO_CHANGE) &&
2608 Region->Protect != Protect)
2609 {
2610 return STATUS_INVALID_PAGE_PROTECTION;
2611 }
2612
2613 *OldProtect = Region->Protect;
2614 Status = MmAlterRegion(AddressSpace, MemoryArea->StartingAddress,
2615 &MemoryArea->Data.SectionData.RegionListHead,
2616 BaseAddress, Length, Region->Type, Protect,
2617 MmAlterViewAttributes);
2618
2619 return(Status);
2620 }
2621
2622 NTSTATUS NTAPI
2623 MmQuerySectionView(PMEMORY_AREA MemoryArea,
2624 PVOID Address,
2625 PMEMORY_BASIC_INFORMATION Info,
2626 PSIZE_T ResultLength)
2627 {
2628 PMM_REGION Region;
2629 PVOID RegionBaseAddress;
2630 PROS_SECTION_OBJECT Section;
2631 PMM_SECTION_SEGMENT Segment;
2632
2633 Region = MmFindRegion((PVOID)MemoryArea->StartingAddress,
2634 &MemoryArea->Data.SectionData.RegionListHead,
2635 Address, &RegionBaseAddress);
2636 if (Region == NULL)
2637 {
2638 return STATUS_UNSUCCESSFUL;
2639 }
2640
2641 Section = MemoryArea->Data.SectionData.Section;
2642 if (Section->AllocationAttributes & SEC_IMAGE)
2643 {
2644 Segment = MemoryArea->Data.SectionData.Segment;
2645 Info->AllocationBase = (PUCHAR)MemoryArea->StartingAddress - Segment->VirtualAddress;
2646 Info->Type = MEM_IMAGE;
2647 }
2648 else
2649 {
2650 Info->AllocationBase = MemoryArea->StartingAddress;
2651 Info->Type = MEM_MAPPED;
2652 }
2653 Info->BaseAddress = RegionBaseAddress;
2654 Info->AllocationProtect = MemoryArea->Protect;
2655 Info->RegionSize = Region->Length;
2656 Info->State = MEM_COMMIT;
2657 Info->Protect = Region->Protect;
2658
2659 *ResultLength = sizeof(MEMORY_BASIC_INFORMATION);
2660 return(STATUS_SUCCESS);
2661 }
2662
2663 VOID
2664 NTAPI
2665 MmpFreePageFileSegment(PMM_SECTION_SEGMENT Segment)
2666 {
2667 ULONG Length;
2668 ULONG Offset;
2669 ULONG Entry;
2670 ULONG SavedSwapEntry;
2671 PFN_NUMBER Page;
2672
2673 Page = 0;
2674
2675 Length = PAGE_ROUND_UP(Segment->Length);
2676 for (Offset = 0; Offset < Length; Offset += PAGE_SIZE)
2677 {
2678 Entry = MmGetPageEntrySectionSegment(Segment, Offset);
2679 if (Entry)
2680 {
2681 if (IS_SWAP_FROM_SSE(Entry))
2682 {
2683 MmFreeSwapPage(SWAPENTRY_FROM_SSE(Entry));
2684 }
2685 else
2686 {
2687 Page = PFN_FROM_SSE(Entry);
2688 SavedSwapEntry = MmGetSavedSwapEntryPage(Page);
2689 if (SavedSwapEntry != 0)
2690 {
2691 MmSetSavedSwapEntryPage(Page, 0);
2692 MmFreeSwapPage(SavedSwapEntry);
2693 }
2694 MmReleasePageMemoryConsumer(MC_USER, Page);
2695 }
2696 MmSetPageEntrySectionSegment(Segment, Offset, 0);
2697 }
2698 }
2699 }
2700
2701 VOID NTAPI
2702 MmpDeleteSection(PVOID ObjectBody)
2703 {
2704 PROS_SECTION_OBJECT Section = (PROS_SECTION_OBJECT)ObjectBody;
2705
2706 DPRINT("MmpDeleteSection(ObjectBody %x)\n", ObjectBody);
2707 if (Section->AllocationAttributes & SEC_IMAGE)
2708 {
2709 ULONG i;
2710 ULONG NrSegments;
2711 ULONG RefCount;
2712 PMM_SECTION_SEGMENT SectionSegments;
2713
2714 /*
2715 * NOTE: Section->ImageSection can be NULL for short time
2716 * during the section creating. If we fail for some reason
2717 * until the image section is properly initialized we shouldn't
2718 * process further here.
2719 */
2720 if (Section->ImageSection == NULL)
2721 return;
2722
2723 SectionSegments = Section->ImageSection->Segments;
2724 NrSegments = Section->ImageSection->NrSegments;
2725
2726 for (i = 0; i < NrSegments; i++)
2727 {
2728 if (SectionSegments[i].Characteristics & IMAGE_SCN_MEM_SHARED)
2729 {
2730 MmLockSectionSegment(&SectionSegments[i]);
2731 }
2732 RefCount = InterlockedDecrementUL(&SectionSegments[i].ReferenceCount);
2733 if (SectionSegments[i].Characteristics & IMAGE_SCN_MEM_SHARED)
2734 {
2735 if (RefCount == 0)
2736 {
2737 MmpFreePageFileSegment(&SectionSegments[i]);
2738 }
2739 MmUnlockSectionSegment(&SectionSegments[i]);
2740 }
2741 }
2742 }
2743 else
2744 {
2745 /*
2746 * NOTE: Section->Segment can be NULL for short time
2747 * during the section creating.
2748 */
2749 if (Section->Segment == NULL)
2750 return;
2751
2752 if (Section->Segment->Flags & MM_PAGEFILE_SEGMENT)
2753 {
2754 MmpFreePageFileSegment(Section->Segment);
2755 MmFreePageTablesSectionSegment(Section->Segment);
2756 ExFreePool(Section->Segment);
2757 Section->Segment = NULL;
2758 }
2759 else
2760 {
2761 (void)InterlockedDecrementUL(&Section->Segment->ReferenceCount);
2762 }
2763 }
2764 if (Section->FileObject != NULL)
2765 {
2766 #ifndef NEWCC
2767 CcRosDereferenceCache(Section->FileObject);
2768 #endif
2769 ObDereferenceObject(Section->FileObject);
2770 Section->FileObject = NULL;
2771 }
2772 }
2773
2774 VOID NTAPI
2775 MmpCloseSection(IN PEPROCESS Process OPTIONAL,
2776 IN PVOID Object,
2777 IN ACCESS_MASK GrantedAccess,
2778 IN ULONG ProcessHandleCount,
2779 IN ULONG SystemHandleCount)
2780 {
2781 DPRINT("MmpCloseSection(OB %x, HC %d)\n",
2782 Object, ProcessHandleCount);
2783 }
2784
2785 NTSTATUS
2786 INIT_FUNCTION
2787 NTAPI
2788 MmCreatePhysicalMemorySection(VOID)
2789 {
2790 PROS_SECTION_OBJECT PhysSection;
2791 NTSTATUS Status;
2792 OBJECT_ATTRIBUTES Obj;
2793 UNICODE_STRING Name = RTL_CONSTANT_STRING(L"\\Device\\PhysicalMemory");
2794 LARGE_INTEGER SectionSize;
2795 HANDLE Handle;
2796
2797 /*
2798 * Create the section mapping physical memory
2799 */
2800 SectionSize.QuadPart = 0xFFFFFFFF;
2801 InitializeObjectAttributes(&Obj,
2802 &Name,
2803 OBJ_PERMANENT,
2804 NULL,
2805 NULL);
2806 Status = MmCreateSection((PVOID)&PhysSection,
2807 SECTION_ALL_ACCESS,
2808 &Obj,
2809 &SectionSize,
2810 PAGE_EXECUTE_READWRITE,
2811 0,
2812 NULL,
2813 NULL);
2814 if (!NT_SUCCESS(Status))
2815 {
2816 DPRINT1("Failed to create PhysicalMemory section\n");
2817 KeBugCheck(MEMORY_MANAGEMENT);
2818 }
2819 Status = ObInsertObject(PhysSection,
2820 NULL,
2821 SECTION_ALL_ACCESS,
2822 0,
2823 NULL,
2824 &Handle);
2825 if (!NT_SUCCESS(Status))
2826 {
2827 ObDereferenceObject(PhysSection);
2828 }
2829 ObCloseHandle(Handle, KernelMode);
2830 PhysSection->AllocationAttributes |= SEC_PHYSICALMEMORY;
2831 PhysSection->Segment->Flags &= ~MM_PAGEFILE_SEGMENT;
2832
2833 return(STATUS_SUCCESS);
2834 }
2835
2836 NTSTATUS
2837 INIT_FUNCTION
2838 NTAPI
2839 MmInitSectionImplementation(VOID)
2840 {
2841 OBJECT_TYPE_INITIALIZER ObjectTypeInitializer;
2842 UNICODE_STRING Name;
2843
2844 DPRINT("Creating Section Object Type\n");
2845
2846 /* Initialize the Section object type */
2847 RtlZeroMemory(&ObjectTypeInitializer, sizeof(ObjectTypeInitializer));
2848 RtlInitUnicodeString(&Name, L"Section");
2849 ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer);
2850 ObjectTypeInitializer.DefaultPagedPoolCharge = sizeof(ROS_SECTION_OBJECT);
2851 ObjectTypeInitializer.PoolType = PagedPool;
2852 ObjectTypeInitializer.UseDefaultObject = TRUE;
2853 ObjectTypeInitializer.GenericMapping = MmpSectionMapping;
2854 ObjectTypeInitializer.DeleteProcedure = MmpDeleteSection;
2855 ObjectTypeInitializer.CloseProcedure = MmpCloseSection;
2856 ObjectTypeInitializer.ValidAccessMask = SECTION_ALL_ACCESS;
2857 ObCreateObjectType(&Name, &ObjectTypeInitializer, NULL, &MmSectionObjectType);
2858
2859 MmCreatePhysicalMemorySection();
2860
2861 return(STATUS_SUCCESS);
2862 }
2863
2864 NTSTATUS
2865 NTAPI
2866 MmCreatePageFileSection(PROS_SECTION_OBJECT *SectionObject,
2867 ACCESS_MASK DesiredAccess,
2868 POBJECT_ATTRIBUTES ObjectAttributes,
2869 PLARGE_INTEGER UMaximumSize,
2870 ULONG SectionPageProtection,
2871 ULONG AllocationAttributes)
2872 /*
2873 * Create a section which is backed by the pagefile
2874 */
2875 {
2876 LARGE_INTEGER MaximumSize;
2877 PROS_SECTION_OBJECT Section;
2878 PMM_SECTION_SEGMENT Segment;
2879 NTSTATUS Status;
2880
2881 if (UMaximumSize == NULL)
2882 {
2883 return(STATUS_UNSUCCESSFUL);
2884 }
2885 MaximumSize = *UMaximumSize;
2886
2887 /*
2888 * Create the section
2889 */
2890 Status = ObCreateObject(ExGetPreviousMode(),
2891 MmSectionObjectType,
2892 ObjectAttributes,
2893 ExGetPreviousMode(),
2894 NULL,
2895 sizeof(ROS_SECTION_OBJECT),
2896 0,
2897 0,
2898 (PVOID*)(PVOID)&Section);
2899 if (!NT_SUCCESS(Status))
2900 {
2901 return(Status);
2902 }
2903
2904 /*
2905 * Initialize it
2906 */
2907 RtlZeroMemory(Section, sizeof(ROS_SECTION_OBJECT));
2908 Section->SectionPageProtection = SectionPageProtection;
2909 Section->AllocationAttributes = AllocationAttributes;
2910 Section->MaximumSize = MaximumSize;
2911 Segment = ExAllocatePoolWithTag(NonPagedPool, sizeof(MM_SECTION_SEGMENT),
2912 TAG_MM_SECTION_SEGMENT);
2913 if (Segment == NULL)
2914 {
2915 ObDereferenceObject(Section);
2916 return(STATUS_NO_MEMORY);
2917 }
2918 Section->Segment = Segment;
2919 Segment->ReferenceCount = 1;
2920 ExInitializeFastMutex(&Segment->Lock);
2921 Segment->FileOffset = 0;
2922 Segment->Protection = SectionPageProtection;
2923 Segment->RawLength = MaximumSize.u.LowPart;
2924 Segment->Length = PAGE_ROUND_UP(MaximumSize.u.LowPart);
2925 Segment->Flags = MM_PAGEFILE_SEGMENT;
2926 Segment->WriteCopy = FALSE;
2927 RtlZeroMemory(&Segment->PageDirectory, sizeof(SECTION_PAGE_DIRECTORY));
2928 Segment->VirtualAddress = 0;
2929 Segment->Characteristics = 0;
2930 *SectionObject = Section;
2931 return(STATUS_SUCCESS);
2932 }
2933
2934
2935 NTSTATUS
2936 NTAPI
2937 MmCreateDataFileSection(PROS_SECTION_OBJECT *SectionObject,
2938 ACCESS_MASK DesiredAccess,
2939 POBJECT_ATTRIBUTES ObjectAttributes,
2940 PLARGE_INTEGER UMaximumSize,
2941 ULONG SectionPageProtection,
2942 ULONG AllocationAttributes,
2943 HANDLE FileHandle)
2944 /*
2945 * Create a section backed by a data file
2946 */
2947 {
2948 PROS_SECTION_OBJECT Section;
2949 NTSTATUS Status;
2950 LARGE_INTEGER MaximumSize;
2951 PFILE_OBJECT FileObject;
2952 PMM_SECTION_SEGMENT Segment;
2953 ULONG FileAccess;
2954 IO_STATUS_BLOCK Iosb;
2955 LARGE_INTEGER Offset;
2956 CHAR Buffer;
2957 FILE_STANDARD_INFORMATION FileInfo;
2958 ULONG Length;
2959
2960 /*
2961 * Create the section
2962 */
2963 Status = ObCreateObject(ExGetPreviousMode(),
2964 MmSectionObjectType,
2965 ObjectAttributes,
2966 ExGetPreviousMode(),
2967 NULL,
2968 sizeof(ROS_SECTION_OBJECT),
2969 0,
2970 0,
2971 (PVOID*)(PVOID)&Section);
2972 if (!NT_SUCCESS(Status))
2973 {
2974 return(Status);
2975 }
2976 /*
2977 * Initialize it
2978 */
2979 RtlZeroMemory(Section, sizeof(ROS_SECTION_OBJECT));
2980 Section->SectionPageProtection = SectionPageProtection;
2981 Section->AllocationAttributes = AllocationAttributes;
2982
2983 /*
2984 * Check file access required
2985 */
2986 if (SectionPageProtection & PAGE_READWRITE ||
2987 SectionPageProtection & PAGE_EXECUTE_READWRITE)
2988 {
2989 FileAccess = FILE_READ_DATA | FILE_WRITE_DATA;
2990 }
2991 else
2992 {
2993 FileAccess = FILE_READ_DATA;
2994 }
2995
2996 /*
2997 * Reference the file handle
2998 */
2999 Status = ObReferenceObjectByHandle(FileHandle,
3000 FileAccess,
3001 IoFileObjectType,
3002 ExGetPreviousMode(),
3003 (PVOID*)(PVOID)&FileObject,
3004 NULL);
3005 if (!NT_SUCCESS(Status))
3006 {
3007 ObDereferenceObject(Section);
3008 return(Status);
3009 }
3010
3011 /*
3012 * FIXME: This is propably not entirely correct. We can't look into
3013 * the standard FCB header because it might not be initialized yet
3014 * (as in case of the EXT2FS driver by Manoj Paul Joseph where the
3015 * standard file information is filled on first request).
3016 */
3017 Status = IoQueryFileInformation(FileObject,
3018 FileStandardInformation,
3019 sizeof(FILE_STANDARD_INFORMATION),
3020 &FileInfo,
3021 &Length);
3022 Iosb.Information = Length;
3023 if (!NT_SUCCESS(Status))
3024 {
3025 ObDereferenceObject(Section);
3026 ObDereferenceObject(FileObject);
3027 return Status;
3028 }
3029
3030 /*
3031 * FIXME: Revise this once a locking order for file size changes is
3032 * decided
3033 */
3034 if ((UMaximumSize != NULL) && (UMaximumSize->QuadPart != 0))
3035 {
3036 MaximumSize = *UMaximumSize;
3037 }
3038 else
3039 {
3040 MaximumSize = FileInfo.EndOfFile;
3041 /* Mapping zero-sized files isn't allowed. */
3042 if (MaximumSize.QuadPart == 0)
3043 {
3044 ObDereferenceObject(Section);
3045 ObDereferenceObject(FileObject);
3046 return STATUS_FILE_INVALID;
3047 }
3048 }
3049
3050 if (MaximumSize.QuadPart > FileInfo.EndOfFile.QuadPart)
3051 {
3052 Status = IoSetInformation(FileObject,
3053 FileAllocationInformation,
3054 sizeof(LARGE_INTEGER),
3055 &MaximumSize);
3056 if (!NT_SUCCESS(Status))
3057 {
3058 ObDereferenceObject(Section);
3059 ObDereferenceObject(FileObject);
3060 return(STATUS_SECTION_NOT_EXTENDED);
3061 }
3062 }
3063
3064 if (FileObject->SectionObjectPointer == NULL ||
3065 FileObject->SectionObjectPointer->SharedCacheMap == NULL)
3066 {
3067 /*
3068 * Read a bit so caching is initiated for the file object.
3069 * This is only needed because MiReadPage currently cannot
3070 * handle non-cached streams.
3071 */
3072 Offset.QuadPart = 0;
3073 Status = ZwReadFile(FileHandle,
3074 NULL,
3075 NULL,
3076 NULL,
3077 &Iosb,
3078 &Buffer,
3079 sizeof (Buffer),
3080 &Offset,
3081 0);
3082 if (!NT_SUCCESS(Status) && (Status != STATUS_END_OF_FILE))
3083 {
3084 ObDereferenceObject(Section);
3085 ObDereferenceObject(FileObject);
3086 return(Status);
3087 }
3088 if (FileObject->SectionObjectPointer == NULL ||
3089 FileObject->SectionObjectPointer->SharedCacheMap == NULL)
3090 {
3091 /* FIXME: handle this situation */
3092 ObDereferenceObject(Section);
3093 ObDereferenceObject(FileObject);
3094 return STATUS_INVALID_PARAMETER;
3095 }
3096 }
3097
3098 /*
3099 * Lock the file
3100 */
3101 Status = MmspWaitForFileLock(FileObject);
3102 if (Status != STATUS_SUCCESS)
3103 {
3104 ObDereferenceObject(Section);
3105 ObDereferenceObject(FileObject);
3106 return(Status);
3107 }
3108
3109 /*
3110 * If this file hasn't been mapped as a data file before then allocate a
3111 * section segment to describe the data file mapping
3112 */
3113 if (FileObject->SectionObjectPointer->DataSectionObject == NULL)
3114 {
3115 Segment = ExAllocatePoolWithTag(NonPagedPool, sizeof(MM_SECTION_SEGMENT),
3116 TAG_MM_SECTION_SEGMENT);
3117 if (Segment == NULL)
3118 {
3119 //KeSetEvent((PVOID)&FileObject->Lock, IO_NO_INCREMENT, FALSE);
3120 ObDereferenceObject(Section);
3121 ObDereferenceObject(FileObject);
3122 return(STATUS_NO_MEMORY);
3123 }
3124 Section->Segment = Segment;
3125 Segment->ReferenceCount = 1;
3126 ExInitializeFastMutex(&Segment->Lock);
3127 /*
3128 * Set the lock before assigning the segment to the file object
3129 */
3130 ExAcquireFastMutex(&Segment->Lock);
3131 FileObject->SectionObjectPointer->DataSectionObject = (PVOID)Segment;
3132
3133 Segment->FileOffset = 0;
3134 Segment->Protection = SectionPageProtection;
3135 Segment->Flags = MM_DATAFILE_SEGMENT;
3136 Segment->Characteristics = 0;
3137 Segment->WriteCopy = FALSE;
3138 if (AllocationAttributes & SEC_RESERVE)
3139 {
3140 Segment->Length = Segment->RawLength = 0;
3141 }
3142 else
3143 {
3144 Segment->RawLength = MaximumSize.u.LowPart;
3145 Segment->Length = PAGE_ROUND_UP(Segment->RawLength);
3146 }
3147 Segment->VirtualAddress = 0;
3148 RtlZeroMemory(&Segment->PageDirectory, sizeof(SECTION_PAGE_DIRECTORY));
3149 }
3150 else
3151 {
3152 /*
3153 * If the file is already mapped as a data file then we may need
3154 * to extend it
3155 */
3156 Segment =
3157 (PMM_SECTION_SEGMENT)FileObject->SectionObjectPointer->
3158 DataSectionObject;
3159 Section->Segment = Segment;
3160 (void)InterlockedIncrementUL(&Segment->ReferenceCount);
3161 MmLockSectionSegment(Segment);
3162
3163 if (MaximumSize.u.LowPart > Segment->RawLength &&
3164 !(AllocationAttributes & SEC_RESERVE))
3165 {
3166 Segment->RawLength = MaximumSize.u.LowPart;
3167 Segment->Length = PAGE_ROUND_UP(Segment->RawLength);
3168 }
3169 }
3170 MmUnlockSectionSegment(Segment);
3171 Section->FileObject = FileObject;
3172 Section->MaximumSize = MaximumSize;
3173 #ifndef NEWCC
3174 CcRosReferenceCache(FileObject);
3175 #endif
3176 //KeSetEvent((PVOID)&FileObject->Lock, IO_NO_INCREMENT, FALSE);
3177 *SectionObject = Section;
3178 return(STATUS_SUCCESS);
3179 }
3180
3181 /*
3182 TODO: not that great (declaring loaders statically, having to declare all of
3183 them, having to keep them extern, etc.), will fix in the future
3184 */
3185 extern NTSTATUS NTAPI PeFmtCreateSection
3186 (
3187 IN CONST VOID * FileHeader,
3188 IN SIZE_T FileHeaderSize,
3189 IN PVOID File,
3190 OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
3191 OUT PULONG Flags,
3192 IN PEXEFMT_CB_READ_FILE ReadFileCb,
3193 IN PEXEFMT_CB_ALLOCATE_SEGMENTS AllocateSegmentsCb
3194 );
3195
3196 extern NTSTATUS NTAPI ElfFmtCreateSection
3197 (
3198 IN CONST VOID * FileHeader,
3199 IN SIZE_T FileHeaderSize,
3200 IN PVOID File,
3201 OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
3202 OUT PULONG Flags,
3203 IN PEXEFMT_CB_READ_FILE ReadFileCb,
3204 IN PEXEFMT_CB_ALLOCATE_SEGMENTS AllocateSegmentsCb
3205 );
3206
3207 /* TODO: this is a standard DDK/PSDK macro */
3208 #ifndef RTL_NUMBER_OF
3209 #define RTL_NUMBER_OF(ARR_) (sizeof(ARR_) / sizeof((ARR_)[0]))
3210 #endif
3211
3212 static PEXEFMT_LOADER ExeFmtpLoaders[] =
3213 {
3214 PeFmtCreateSection,
3215 #ifdef __ELF
3216 ElfFmtCreateSection
3217 #endif
3218 };
3219
3220 static
3221 PMM_SECTION_SEGMENT
3222 NTAPI
3223 ExeFmtpAllocateSegments(IN ULONG NrSegments)
3224 {
3225 SIZE_T SizeOfSegments;
3226 PMM_SECTION_SEGMENT Segments;
3227
3228 /* TODO: check for integer overflow */
3229 SizeOfSegments = sizeof(MM_SECTION_SEGMENT) * NrSegments;
3230
3231 Segments = ExAllocatePoolWithTag(NonPagedPool,
3232 SizeOfSegments,
3233 TAG_MM_SECTION_SEGMENT);
3234
3235 if(Segments)
3236 RtlZeroMemory(Segments, SizeOfSegments);
3237
3238 return Segments;
3239 }
3240
3241 static
3242 NTSTATUS
3243 NTAPI
3244 ExeFmtpReadFile(IN PVOID File,
3245 IN PLARGE_INTEGER Offset,
3246 IN ULONG Length,
3247 OUT PVOID * Data,
3248 OUT PVOID * AllocBase,
3249 OUT PULONG ReadSize)
3250 {
3251 NTSTATUS Status;
3252 LARGE_INTEGER FileOffset;
3253 ULONG AdjustOffset;
3254 ULONG OffsetAdjustment;
3255 ULONG BufferSize;
3256 ULONG UsedSize;
3257 PVOID Buffer;
3258
3259 ASSERT_IRQL_LESS(DISPATCH_LEVEL);
3260
3261 if(Length == 0)
3262 {
3263 KeBugCheck(MEMORY_MANAGEMENT);
3264 }
3265
3266 FileOffset = *Offset;
3267
3268 /* Negative/special offset: it cannot be used in this context */
3269 if(FileOffset.u.HighPart < 0)
3270 {
3271 KeBugCheck(MEMORY_MANAGEMENT);
3272 }
3273
3274 AdjustOffset = PAGE_ROUND_DOWN(FileOffset.u.LowPart);
3275 OffsetAdjustment = FileOffset.u.LowPart - AdjustOffset;
3276 FileOffset.u.LowPart = AdjustOffset;
3277
3278 BufferSize = Length + OffsetAdjustment;
3279 BufferSize = PAGE_ROUND_UP(BufferSize);
3280
3281 /*
3282 * It's ok to use paged pool, because this is a temporary buffer only used in
3283 * the loading of executables. The assumption is that MmCreateSection is
3284 * always called at low IRQLs and that these buffers don't survive a brief
3285 * initialization phase
3286 */
3287 Buffer = ExAllocatePoolWithTag(PagedPool,
3288 BufferSize,
3289 'rXmM');
3290 if (!Buffer)
3291 {
3292 KeBugCheck(MEMORY_MANAGEMENT);
3293 }
3294
3295 UsedSize = 0;
3296
3297 #if 0
3298 Status = MmspPageRead(File,
3299 Buffer,
3300 BufferSize,
3301 &FileOffset,
3302 &UsedSize);
3303 #else
3304 /*
3305 * FIXME: if we don't use ZwReadFile, caching is not enabled for the file and
3306 * nothing will work. But using ZwReadFile is wrong, and using its side effects
3307 * to initialize internal state is even worse. Our cache manager is in need of
3308 * professional help
3309 */
3310 {
3311 IO_STATUS_BLOCK Iosb;
3312
3313 Status = ZwReadFile(File,
3314 NULL,
3315 NULL,
3316 NULL,
3317 &Iosb,
3318 Buffer,
3319 BufferSize,
3320 &FileOffset,
3321 NULL);
3322
3323 if(NT_SUCCESS(Status))
3324 {
3325 UsedSize = Iosb.Information;
3326 }
3327 }
3328 #endif
3329
3330 if(NT_SUCCESS(Status) && UsedSize < OffsetAdjustment)
3331 {
3332 Status = STATUS_IN_PAGE_ERROR;
3333 ASSERT(!NT_SUCCESS(Status));
3334 }
3335
3336 if(NT_SUCCESS(Status))
3337 {
3338 *Data = (PVOID)((ULONG_PTR)Buffer + OffsetAdjustment);
3339 *AllocBase = Buffer;
3340 *ReadSize = UsedSize - OffsetAdjustment;
3341 }
3342 else
3343 {
3344 ExFreePoolWithTag(Buffer, 'rXmM');
3345 }
3346
3347 return Status;
3348 }
3349
3350 #ifdef NASSERT
3351 # define MmspAssertSegmentsSorted(OBJ_) ((void)0)
3352 # define MmspAssertSegmentsNoOverlap(OBJ_) ((void)0)
3353 # define MmspAssertSegmentsPageAligned(OBJ_) ((void)0)
3354 #else
3355 static
3356 VOID
3357 NTAPI
3358 MmspAssertSegmentsSorted(IN PMM_IMAGE_SECTION_OBJECT ImageSectionObject)
3359 {
3360 ULONG i;
3361
3362 for( i = 1; i < ImageSectionObject->NrSegments; ++ i )
3363 {
3364 ASSERT(ImageSectionObject->Segments[i].VirtualAddress >=
3365 ImageSectionObject->Segments[i - 1].VirtualAddress);
3366 }
3367 }
3368
3369 static
3370 VOID
3371 NTAPI
3372 MmspAssertSegmentsNoOverlap(IN PMM_IMAGE_SECTION_OBJECT ImageSectionObject)
3373 {
3374 ULONG i;
3375
3376 MmspAssertSegmentsSorted(ImageSectionObject);
3377
3378 for( i = 0; i < ImageSectionObject->NrSegments; ++ i )
3379 {
3380 ASSERT(ImageSectionObject->Segments[i].Length > 0);
3381
3382 if(i > 0)
3383 {
3384 ASSERT(ImageSectionObject->Segments[i].VirtualAddress >=
3385 (ImageSectionObject->Segments[i - 1].VirtualAddress +
3386 ImageSectionObject->Segments[i - 1].Length));
3387 }
3388 }
3389 }
3390
3391 static
3392 VOID
3393 NTAPI
3394 MmspAssertSegmentsPageAligned(IN PMM_IMAGE_SECTION_OBJECT ImageSectionObject)
3395 {
3396 ULONG i;
3397
3398 for( i = 0; i < ImageSectionObject->NrSegments; ++ i )
3399 {
3400 ASSERT((ImageSectionObject->Segments[i].VirtualAddress % PAGE_SIZE) == 0);
3401 ASSERT((ImageSectionObject->Segments[i].Length % PAGE_SIZE) == 0);
3402 }
3403 }
3404 #endif
3405
3406 static
3407 int
3408 __cdecl
3409 MmspCompareSegments(const void * x,
3410 const void * y)
3411 {
3412 const MM_SECTION_SEGMENT *Segment1 = (const MM_SECTION_SEGMENT *)x;
3413 const MM_SECTION_SEGMENT *Segment2 = (const MM_SECTION_SEGMENT *)y;
3414
3415 return
3416 (Segment1->VirtualAddress - Segment2->VirtualAddress) >>
3417 ((sizeof(ULONG_PTR) - sizeof(int)) * 8);
3418 }
3419
3420 /*
3421 * Ensures an image section's segments are sorted in memory
3422 */
3423 static
3424 VOID
3425 NTAPI
3426 MmspSortSegments(IN OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
3427 IN ULONG Flags)
3428 {
3429 if (Flags & EXEFMT_LOAD_ASSUME_SEGMENTS_SORTED)
3430 {
3431 MmspAssertSegmentsSorted(ImageSectionObject);
3432 }
3433 else
3434 {
3435 qsort(ImageSectionObject->Segments,
3436 ImageSectionObject->NrSegments,
3437 sizeof(ImageSectionObject->Segments[0]),
3438 MmspCompareSegments);
3439 }
3440 }
3441
3442
3443 /*
3444 * Ensures an image section's segments don't overlap in memory and don't have
3445 * gaps and don't have a null size. We let them map to overlapping file regions,
3446 * though - that's not necessarily an error
3447 */
3448 static
3449 BOOLEAN
3450 NTAPI
3451 MmspCheckSegmentBounds
3452 (
3453 IN OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
3454 IN ULONG Flags
3455 )
3456 {
3457 ULONG i;
3458
3459 if (Flags & EXEFMT_LOAD_ASSUME_SEGMENTS_NO_OVERLAP)
3460 {
3461 MmspAssertSegmentsNoOverlap(ImageSectionObject);
3462 return TRUE;
3463 }
3464
3465 ASSERT(ImageSectionObject->NrSegments >= 1);
3466
3467 for ( i = 0; i < ImageSectionObject->NrSegments; ++ i )
3468 {
3469 if(ImageSectionObject->Segments[i].Length == 0)
3470 {
3471 return FALSE;
3472 }
3473
3474 if(i > 0)
3475 {
3476 /*
3477 * TODO: relax the limitation on gaps. For example, gaps smaller than a
3478 * page could be OK (Windows seems to be OK with them), and larger gaps
3479 * could lead to image sections spanning several discontiguous regions
3480 * (NtMapViewOfSection could then refuse to map them, and they could
3481 * e.g. only be allowed as parameters to NtCreateProcess, like on UNIX)
3482 */
3483 if ((ImageSectionObject->Segments[i - 1].VirtualAddress +
3484 ImageSectionObject->Segments[i - 1].Length) !=
3485 ImageSectionObject->Segments[i].VirtualAddress)
3486 {
3487 return FALSE;
3488 }
3489 }
3490 }
3491
3492 return TRUE;
3493 }
3494
3495 /*
3496 * Merges and pads an image section's segments until they all are page-aligned
3497 * and have a size that is a multiple of the page size
3498 */
3499 static
3500 BOOLEAN
3501 NTAPI
3502 MmspPageAlignSegments
3503 (
3504 IN OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
3505 IN ULONG Flags
3506 )
3507 {
3508 ULONG i;
3509 ULONG LastSegment;
3510 PMM_SECTION_SEGMENT EffectiveSegment;
3511
3512 if (Flags & EXEFMT_LOAD_ASSUME_SEGMENTS_PAGE_ALIGNED)
3513 {
3514 MmspAssertSegmentsPageAligned(ImageSectionObject);
3515 return TRUE;
3516 }
3517
3518 LastSegment = 0;
3519 EffectiveSegment = &ImageSectionObject->Segments[LastSegment];
3520
3521 for ( i = 0; i < ImageSectionObject->NrSegments; ++ i )
3522 {
3523 /*
3524 * The first segment requires special handling
3525 */
3526 if (i == 0)
3527 {
3528 ULONG_PTR VirtualAddress;
3529 ULONG_PTR VirtualOffset;
3530
3531 VirtualAddress = EffectiveSegment->VirtualAddress;
3532
3533 /* Round down the virtual address to the nearest page */
3534 EffectiveSegment->VirtualAddress = PAGE_ROUND_DOWN(VirtualAddress);
3535
3536 /* Round up the virtual size to the nearest page */
3537 EffectiveSegment->Length = PAGE_ROUND_UP(VirtualAddress + EffectiveSegment->Length) -
3538 EffectiveSegment->VirtualAddress;
3539
3540 /* Adjust the raw address and size */
3541 VirtualOffset = VirtualAddress - EffectiveSegment->VirtualAddress;
3542
3543 if (EffectiveSegment->FileOffset < VirtualOffset)
3544 {
3545 return FALSE;
3546 }
3547
3548 /*
3549 * Garbage in, garbage out: unaligned base addresses make the file
3550 * offset point in curious and odd places, but that's what we were
3551 * asked for
3552 */
3553 EffectiveSegment->FileOffset -= VirtualOffset;
3554 EffectiveSegment->RawLength += VirtualOffset;
3555 }
3556 else
3557 {
3558 PMM_SECTION_SEGMENT Segment = &ImageSectionObject->Segments[i];
3559 ULONG_PTR EndOfEffectiveSegment;
3560
3561 EndOfEffectiveSegment = EffectiveSegment->VirtualAddress + EffectiveSegment->Length;
3562 ASSERT((EndOfEffectiveSegment % PAGE_SIZE) == 0);
3563
3564 /*
3565 * The current segment begins exactly where the current effective
3566 * segment ended, therefore beginning a new effective segment
3567 */
3568 if (EndOfEffectiveSegment == Segment->VirtualAddress)
3569 {
3570 LastSegment ++;
3571 ASSERT(LastSegment <= i);
3572 ASSERT(LastSegment < ImageSectionObject->NrSegments);
3573
3574 EffectiveSegment = &ImageSectionObject->Segments[LastSegment];
3575
3576 if (LastSegment != i)
3577 {
3578 /*
3579 * Copy the current segment. If necessary, the effective segment
3580 * will be expanded later
3581 */
3582 *EffectiveSegment = *Segment;
3583 }
3584
3585 /*
3586 * Page-align the virtual size. We know for sure the virtual address
3587 * already is
3588 */
3589 ASSERT((EffectiveSegment->VirtualAddress % PAGE_SIZE) == 0);
3590 EffectiveSegment->Length = PAGE_ROUND_UP(EffectiveSegment->Length);
3591 }
3592 /*
3593 * The current segment is still part of the current effective segment:
3594 * extend the effective segment to reflect this
3595 */
3596 else if (EndOfEffectiveSegment > Segment->VirtualAddress)
3597 {
3598 static const ULONG FlagsToProtection[16] =
3599 {
3600 PAGE_NOACCESS,
3601 PAGE_READONLY,
3602 PAGE_READWRITE,
3603 PAGE_READWRITE,
3604 PAGE_EXECUTE_READ,
3605 PAGE_EXECUTE_READ,
3606 PAGE_EXECUTE_READWRITE,
3607 PAGE_EXECUTE_READWRITE,
3608 PAGE_WRITECOPY,
3609 PAGE_WRITECOPY,
3610 PAGE_WRITECOPY,
3611 PAGE_WRITECOPY,
3612 PAGE_EXECUTE_WRITECOPY,
3613 PAGE_EXECUTE_WRITECOPY,
3614 PAGE_EXECUTE_WRITECOPY,
3615 PAGE_EXECUTE_WRITECOPY
3616 };
3617
3618 unsigned ProtectionFlags;
3619
3620 /*
3621 * Extend the file size
3622 */
3623
3624 /* Unaligned segments must be contiguous within the file */
3625 if (Segment->FileOffset != (EffectiveSegment->FileOffset +
3626 EffectiveSegment->RawLength))
3627 {
3628 return FALSE;
3629 }
3630
3631 EffectiveSegment->RawLength += Segment->RawLength;
3632
3633 /*
3634 * Extend the virtual size
3635 */
3636 ASSERT(PAGE_ROUND_UP(Segment->VirtualAddress + Segment->Length) >= EndOfEffectiveSegment);
3637
3638 EffectiveSegment->Length = PAGE_ROUND_UP(Segment->VirtualAddress + Segment->Length) -
3639 EffectiveSegment->VirtualAddress;
3640
3641 /*
3642 * Merge the protection
3643 */
3644 EffectiveSegment->Protection |= Segment->Protection;
3645
3646 /* Clean up redundance */
3647 ProtectionFlags = 0;
3648
3649 if(EffectiveSegment->Protection & PAGE_IS_READABLE)
3650 ProtectionFlags |= 1 << 0;
3651
3652 if(EffectiveSegment->Protection & PAGE_IS_WRITABLE)
3653 ProtectionFlags |= 1 << 1;
3654
3655 if(EffectiveSegment->Protection & PAGE_IS_EXECUTABLE)
3656 ProtectionFlags |= 1 << 2;
3657
3658 if(EffectiveSegment->Protection & PAGE_IS_WRITECOPY)
3659 ProtectionFlags |= 1 << 3;
3660
3661 ASSERT(ProtectionFlags < 16);
3662 EffectiveSegment->Protection = FlagsToProtection[ProtectionFlags];
3663
3664 /* If a segment was required to be shared and cannot, fail */
3665 if(!(Segment->Protection & PAGE_IS_WRITECOPY) &&
3666 EffectiveSegment->Protection & PAGE_IS_WRITECOPY)
3667 {
3668 return FALSE;
3669 }
3670 }
3671 /*
3672 * We assume no holes between segments at this point
3673 */
3674 else
3675 {
3676 KeBugCheck(MEMORY_MANAGEMENT);
3677 }
3678 }
3679 }
3680 ImageSectionObject->NrSegments = LastSegment + 1;
3681
3682 return TRUE;
3683 }
3684
3685 NTSTATUS
3686 ExeFmtpCreateImageSection(HANDLE FileHandle,
3687 PMM_IMAGE_SECTION_OBJECT ImageSectionObject)
3688 {
3689 LARGE_INTEGER Offset;
3690 PVOID FileHeader;
3691 PVOID FileHeaderBuffer;
3692 ULONG FileHeaderSize;
3693 ULONG Flags;
3694 ULONG OldNrSegments;
3695 NTSTATUS Status;
3696 ULONG i;
3697
3698 /*
3699 * Read the beginning of the file (2 pages). Should be enough to contain
3700 * all (or most) of the headers
3701 */
3702 Offset.QuadPart = 0;
3703
3704 /* FIXME: use FileObject instead of FileHandle */
3705 Status = ExeFmtpReadFile (FileHandle,
3706 &Offset,
3707 PAGE_SIZE * 2,
3708 &FileHeader,
3709 &FileHeaderBuffer,
3710 &FileHeaderSize);
3711
3712 if (!NT_SUCCESS(Status))
3713 return Status;
3714
3715 if (FileHeaderSize == 0)
3716 {
3717 ExFreePool(FileHeaderBuffer);
3718 return STATUS_UNSUCCESSFUL;
3719 }
3720
3721 /*
3722 * Look for a loader that can handle this executable
3723 */
3724 for (i = 0; i < RTL_NUMBER_OF(ExeFmtpLoaders); ++ i)
3725 {
3726 RtlZeroMemory(ImageSectionObject, sizeof(*ImageSectionObject));
3727 Flags = 0;
3728
3729 /* FIXME: use FileObject instead of FileHandle */
3730 Status = ExeFmtpLoaders[i](FileHeader,
3731 FileHeaderSize,
3732 FileHandle,
3733 ImageSectionObject,
3734 &Flags,
3735 ExeFmtpReadFile,
3736 ExeFmtpAllocateSegments);
3737
3738 if (!NT_SUCCESS(Status))
3739 {
3740 if (ImageSectionObject->Segments)
3741 {
3742 ExFreePool(ImageSectionObject->Segments);
3743 ImageSectionObject->Segments = NULL;
3744 }
3745 }
3746
3747 if (Status != STATUS_ROS_EXEFMT_UNKNOWN_FORMAT)
3748 break;
3749 }
3750
3751 ExFreePoolWithTag(FileHeaderBuffer, 'rXmM');
3752
3753 /*
3754 * No loader handled the format
3755 */
3756 if (Status == STATUS_ROS_EXEFMT_UNKNOWN_FORMAT)
3757 {
3758 Status = STATUS_INVALID_IMAGE_NOT_MZ;
3759 ASSERT(!NT_SUCCESS(Status));
3760 }
3761
3762 if (!NT_SUCCESS(Status))
3763 return Status;
3764
3765 ASSERT(ImageSectionObject->Segments != NULL);
3766
3767 /*
3768 * Some defaults
3769 */
3770 /* FIXME? are these values platform-dependent? */
3771 if(ImageSectionObject->StackReserve == 0)
3772 ImageSectionObject->StackReserve = 0x40000;
3773
3774 if(ImageSectionObject->StackCommit == 0)
3775 ImageSectionObject->StackCommit = 0x1000;
3776
3777 if(ImageSectionObject->ImageBase == 0)
3778 {
3779 if(ImageSectionObject->ImageCharacteristics & IMAGE_FILE_DLL)
3780 ImageSectionObject->ImageBase = 0x10000000;
3781 else
3782 ImageSectionObject->ImageBase = 0x00400000;
3783 }
3784
3785 /*
3786 * And now the fun part: fixing the segments
3787 */
3788
3789 /* Sort them by virtual address */
3790 MmspSortSegments(ImageSectionObject, Flags);
3791
3792 /* Ensure they don't overlap in memory */
3793 if (!MmspCheckSegmentBounds(ImageSectionObject, Flags))
3794 return STATUS_INVALID_IMAGE_FORMAT;
3795
3796 /* Ensure they are aligned */
3797 OldNrSegments = ImageSectionObject->NrSegments;
3798
3799 if (!MmspPageAlignSegments(ImageSectionObject, Flags))
3800 return STATUS_INVALID_IMAGE_FORMAT;
3801
3802 /* Trim them if the alignment phase merged some of them */
3803 if (ImageSectionObject->NrSegments < OldNrSegments)
3804 {
3805 PMM_SECTION_SEGMENT Segments;
3806 SIZE_T SizeOfSegments;
3807
3808 SizeOfSegments = sizeof(MM_SECTION_SEGMENT) * ImageSectionObject->NrSegments;
3809
3810 Segments = ExAllocatePoolWithTag(PagedPool,
3811 SizeOfSegments,
3812 TAG_MM_SECTION_SEGMENT);
3813
3814 if (Segments == NULL)
3815 return STATUS_INSUFFICIENT_RESOURCES;
3816
3817 RtlCopyMemory(Segments, ImageSectionObject->Segments, SizeOfSegments);
3818 ExFreePool(ImageSectionObject->Segments);
3819 ImageSectionObject->Segments = Segments;
3820 }
3821
3822 /* And finish their initialization */
3823 for ( i = 0; i < ImageSectionObject->NrSegments; ++ i )
3824 {
3825 ExInitializeFastMutex(&ImageSectionObject->Segments[i].Lock);
3826 ImageSectionObject->Segments[i].ReferenceCount = 1;
3827
3828 RtlZeroMemory(&ImageSectionObject->Segments[i].PageDirectory,
3829 sizeof(ImageSectionObject->Segments[i].PageDirectory));
3830 }
3831
3832 ASSERT(NT_SUCCESS(Status));
3833 return Status;
3834 }
3835
3836 NTSTATUS
3837 MmCreateImageSection(PROS_SECTION_OBJECT *SectionObject,
3838 ACCESS_MASK DesiredAccess,
3839 POBJECT_ATTRIBUTES ObjectAttributes,
3840 PLARGE_INTEGER UMaximumSize,
3841 ULONG SectionPageProtection,
3842 ULONG AllocationAttributes,
3843 HANDLE FileHandle)
3844 {
3845 PROS_SECTION_OBJECT Section;
3846 NTSTATUS Status;
3847 PFILE_OBJECT FileObject;
3848 PMM_SECTION_SEGMENT SectionSegments;
3849 PMM_IMAGE_SECTION_OBJECT ImageSectionObject;
3850 ULONG i;
3851 ULONG FileAccess = 0;
3852
3853 /*
3854 * Check file access required
3855 */
3856 if (SectionPageProtection & PAGE_READWRITE ||
3857 SectionPageProtection & PAGE_EXECUTE_READWRITE)
3858 {
3859 FileAccess = FILE_READ_DATA | FILE_WRITE_DATA;
3860 }
3861 else
3862 {
3863 FileAccess = FILE_READ_DATA;
3864 }
3865
3866 /*
3867 * Reference the file handle
3868 */
3869 Status = ObReferenceObjectByHandle(FileHandle,
3870 FileAccess,
3871 IoFileObjectType,
3872 ExGetPreviousMode(),
3873 (PVOID*)(PVOID)&FileObject,
3874 NULL);
3875
3876 if (!NT_SUCCESS(Status))
3877 {
3878 return Status;
3879 }
3880
3881 /*
3882 * Create the section
3883 */
3884 Status = ObCreateObject (ExGetPreviousMode(),
3885 MmSectionObjectType,
3886 ObjectAttributes,
3887 ExGetPreviousMode(),
3888 NULL,
3889 sizeof(ROS_SECTION_OBJECT),
3890 0,
3891 0,
3892 (PVOID*)(PVOID)&Section);
3893 if (!NT_SUCCESS(Status))
3894 {
3895 ObDereferenceObject(FileObject);
3896 return(Status);
3897 }
3898
3899 /*
3900 * Initialize it
3901 */
3902 RtlZeroMemory(Section, sizeof(ROS_SECTION_OBJECT));
3903 Section->SectionPageProtection = SectionPageProtection;
3904 Section->AllocationAttributes = AllocationAttributes;
3905
3906 #ifndef NEWCC
3907 /*
3908 * Initialized caching for this file object if previously caching
3909 * was initialized for the same on disk file
3910 */
3911 Status = CcTryToInitializeFileCache(FileObject);
3912 #else
3913 Status = STATUS_SUCCESS;
3914 #endif
3915
3916 if (!NT_SUCCESS(Status) || FileObject->SectionObjectPointer->ImageSectionObject == NULL)
3917 {
3918 NTSTATUS StatusExeFmt;
3919
3920 ImageSectionObject = ExAllocatePoolWithTag(PagedPool, sizeof(MM_IMAGE_SECTION_OBJECT), TAG_MM_SECTION_SEGMENT);
3921 if (ImageSectionObject == NULL)
3922 {
3923 ObDereferenceObject(FileObject);
3924 ObDereferenceObject(Section);
3925 return(STATUS_NO_MEMORY);
3926 }
3927
3928 RtlZeroMemory(ImageSectionObject, sizeof(MM_IMAGE_SECTION_OBJECT));
3929
3930 StatusExeFmt = ExeFmtpCreateImageSection(FileHandle, ImageSectionObject);
3931
3932 if (!NT_SUCCESS(StatusExeFmt))
3933 {
3934 if(ImageSectionObject->Segments != NULL)
3935 ExFreePool(ImageSectionObject->Segments);
3936
3937 ExFreePool(ImageSectionObject);
3938 ObDereferenceObject(Section);
3939 ObDereferenceObject(FileObject);
3940 return(StatusExeFmt);
3941 }
3942
3943 Section->ImageSection = ImageSectionObject;
3944 ASSERT(ImageSectionObject->Segments);
3945
3946 /*
3947 * Lock the file
3948 */
3949 Status = MmspWaitForFileLock(FileObject);
3950 if (!NT_SUCCESS(Status))
3951 {
3952 ExFreePool(ImageSectionObject->Segments);
3953 ExFreePool(ImageSectionObject);
3954 ObDereferenceObject(Section);
3955 ObDereferenceObject(FileObject);
3956 return(Status);
3957 }
3958
3959 if (NULL != InterlockedCompareExchangePointer(&FileObject->SectionObjectPointer->ImageSectionObject,
3960 ImageSectionObject, NULL))
3961 {
3962 /*
3963 * An other thread has initialized the same image in the background
3964 */
3965 ExFreePool(ImageSectionObject->Segments);
3966 ExFreePool(ImageSectionObject);
3967 ImageSectionObject = FileObject->SectionObjectPointer->ImageSectionObject;
3968 Section->ImageSection = ImageSectionObject;
3969 SectionSegments = ImageSectionObject->Segments;
3970
3971 for (i = 0; i < ImageSectionObject->NrSegments; i++)
3972 {
3973 (void)InterlockedIncrementUL(&SectionSegments[i].ReferenceCount);
3974 }
3975 }
3976
3977 Status = StatusExeFmt;
3978 }
3979 else
3980 {
3981 /*
3982 * Lock the file
3983 */
3984 Status = MmspWaitForFileLock(FileObject);
3985 if (Status != STATUS_SUCCESS)
3986 {
3987 ObDereferenceObject(Section);
3988 ObDereferenceObject(FileObject);
3989 return(Status);
3990 }
3991
3992 ImageSectionObject = FileObject->SectionObjectPointer->ImageSectionObject;
3993 Section->ImageSection = ImageSectionObject;
3994 SectionSegments = ImageSectionObject->Segments;
3995
3996 /*
3997 * Otherwise just reference all the section segments
3998 */
3999 for (i = 0; i < ImageSectionObject->NrSegments; i++)
4000 {
4001 (void)InterlockedIncrementUL(&SectionSegments[i].ReferenceCount);
4002 }
4003
4004 Status = STATUS_SUCCESS;
4005 }
4006 Section->FileObject = FileObject;
4007 #ifndef NEWCC
4008 CcRosReferenceCache(FileObject);
4009 #endif
4010 //KeSetEvent((PVOID)&FileObject->Lock, IO_NO_INCREMENT, FALSE);
4011 *SectionObject = Section;
4012 return(Status);
4013 }
4014
4015
4016
4017 static NTSTATUS
4018 MmMapViewOfSegment(PMMSUPPORT AddressSpace,
4019 PROS_SECTION_OBJECT Section,
4020 PMM_SECTION_SEGMENT Segment,
4021 PVOID* BaseAddress,
4022 SIZE_T ViewSize,
4023 ULONG Protect,
4024 ULONG ViewOffset,
4025 ULONG AllocationType)
4026 {
4027 PMEMORY_AREA MArea;
4028 NTSTATUS Status;
4029 PHYSICAL_ADDRESS BoundaryAddressMultiple;
4030
4031 BoundaryAddressMultiple.QuadPart = 0;
4032
4033 Status = MmCreateMemoryArea(AddressSpace,
4034 MEMORY_AREA_SECTION_VIEW,
4035 BaseAddress,
4036 ViewSize,
4037 Protect,
4038 &MArea,
4039 FALSE,
4040 AllocationType,
4041 BoundaryAddressMultiple);
4042 if (!NT_SUCCESS(Status))
4043 {
4044 DPRINT1("Mapping between 0x%.8X and 0x%.8X failed (%X).\n",
4045 (*BaseAddress), (char*)(*BaseAddress) + ViewSize, Status);
4046 return(Status);
4047 }
4048
4049 ObReferenceObject((PVOID)Section);
4050
4051 MArea->Data.SectionData.Segment = Segment;
4052 MArea->Data.SectionData.Section = Section;
4053 MArea->Data.SectionData.ViewOffset = ViewOffset;
4054 MmInitializeRegion(&MArea->Data.SectionData.RegionListHead,
4055 ViewSize, 0, Protect);
4056
4057 return(STATUS_SUCCESS);
4058 }
4059
4060
4061
4062 static VOID
4063 MmFreeSectionPage(PVOID Context, MEMORY_AREA* MemoryArea, PVOID Address,
4064 PFN_NUMBER Page, SWAPENTRY SwapEntry, BOOLEAN Dirty)
4065 {
4066 ULONG Entry;
4067 PFILE_OBJECT FileObject;
4068 PBCB Bcb;
4069 ULONG Offset;
4070 SWAPENTRY SavedSwapEntry;
4071 PMM_PAGEOP PageOp;
4072 NTSTATUS Status;
4073 PROS_SECTION_OBJECT Section;
4074 PMM_SECTION_SEGMENT Segment;
4075 PMMSUPPORT AddressSpace;
4076 PEPROCESS Process;
4077
4078 AddressSpace = (PMMSUPPORT)Context;
4079 Process = MmGetAddressSpaceOwner(AddressSpace);
4080
4081 Address = (PVOID)PAGE_ROUND_DOWN(Address);
4082
4083 Offset = ((ULONG_PTR)Address - (ULONG_PTR)MemoryArea->StartingAddress) +
4084 MemoryArea->Data.SectionData.ViewOffset;
4085
4086 Section = MemoryArea->Data.SectionData.Section;
4087 Segment = MemoryArea->Data.SectionData.Segment;
4088
4089 PageOp = MmCheckForPageOp(MemoryArea, NULL, NULL, Segment, Offset);
4090
4091 while (PageOp)
4092 {
4093 MmUnlockSectionSegment(Segment);
4094 MmUnlockAddressSpace(AddressSpace);
4095
4096 Status = MmspWaitForPageOpCompletionEvent(PageOp);
4097 if (Status != STATUS_SUCCESS)
4098 {
4099 DPRINT1("Failed to wait for page op, status = %x\n", Status);
4100 KeBugCheck(MEMORY_MANAGEMENT);
4101 }
4102
4103 MmLockAddressSpace(AddressSpace);
4104 MmLockSectionSegment(Segment);
4105 MmspCompleteAndReleasePageOp(PageOp);
4106 PageOp = MmCheckForPageOp(MemoryArea, NULL, NULL, Segment, Offset);
4107 }
4108
4109 Entry = MmGetPageEntrySectionSegment(Segment, Offset);
4110
4111 /*
4112 * For a dirty, datafile, non-private page mark it as dirty in the
4113 * cache manager.
4114 */
4115 if (Segment->Flags & MM_DATAFILE_SEGMENT)
4116 {
4117 if (Page == PFN_FROM_SSE(Entry) && Dirty)
4118 {
4119 FileObject = MemoryArea->Data.SectionData.Section->FileObject;
4120 Bcb = FileObject->SectionObjectPointer->SharedCacheMap;
4121 #ifndef NEWCC
4122 CcRosMarkDirtyCacheSegment(Bcb, Offset + Segment->FileOffset);
4123 #endif
4124 ASSERT(SwapEntry == 0);
4125 }
4126 }
4127
4128 if (SwapEntry != 0)
4129 {
4130 /*
4131 * Sanity check
4132 */
4133 if (Segment->Flags & MM_PAGEFILE_SEGMENT)
4134 {
4135 DPRINT1("Found a swap entry for a page in a pagefile section.\n");
4136 KeBugCheck(MEMORY_MANAGEMENT);
4137 }
4138 MmFreeSwapPage(SwapEntry);
4139 }
4140 else if (Page != 0)
4141 {
4142 if (IS_SWAP_FROM_SSE(Entry) ||
4143 Page != PFN_FROM_SSE(Entry))
4144 {
4145 /*
4146 * Sanity check
4147 */
4148 if (Segment->Flags & MM_PAGEFILE_SEGMENT)
4149 {
4150 DPRINT1("Found a private page in a pagefile section.\n");
4151 KeBugCheck(MEMORY_MANAGEMENT);
4152 }
4153 /*
4154 * Just dereference private pages
4155 */
4156 SavedSwapEntry = MmGetSavedSwapEntryPage(Page);
4157 if (SavedSwapEntry != 0)
4158 {
4159 MmFreeSwapPage(SavedSwapEntry);
4160 MmSetSavedSwapEntryPage(Page, 0);
4161 }
4162 MmDeleteRmap(Page, Process, Address);
4163 MmReleasePageMemoryConsumer(MC_USER, Page);
4164 }
4165 else
4166 {
4167 MmDeleteRmap(Page, Process, Address);
4168 MmUnsharePageEntrySectionSegment(Section, Segment, Offset, Dirty, FALSE);
4169 }
4170 }
4171 }
4172
4173 static NTSTATUS
4174 MmUnmapViewOfSegment(PMMSUPPORT AddressSpace,
4175 PVOID BaseAddress)
4176 {
4177 NTSTATUS Status;
4178 PMEMORY_AREA MemoryArea;
4179 PROS_SECTION_OBJECT Section;
4180 PMM_SECTION_SEGMENT Segment;
4181 PLIST_ENTRY CurrentEntry;
4182 PMM_REGION CurrentRegion;
4183 PLIST_ENTRY RegionListHead;
4184
4185 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace,
4186 BaseAddress);
4187 if (MemoryArea == NULL)
4188 {
4189 return(STATUS_UNSUCCESSFUL);
4190 }
4191
4192 MemoryArea->DeleteInProgress = TRUE;
4193 Section = MemoryArea->Data.SectionData.Section;
4194 Segment = MemoryArea->Data.SectionData.Segment;
4195
4196 MmLockSectionSegment(Segment);
4197
4198 RegionListHead = &MemoryArea->Data.SectionData.RegionListHead;
4199 while (!IsListEmpty(RegionListHead))
4200 {
4201 CurrentEntry = RemoveHeadList(RegionListHead);
4202 CurrentRegion = CONTAINING_RECORD(CurrentEntry, MM_REGION, RegionListEntry);
4203 ExFreePoolWithTag(CurrentRegion, TAG_MM_REGION);
4204 }
4205
4206 if (Section->AllocationAttributes & SEC_PHYSICALMEMORY)
4207 {
4208 Status = MmFreeMemoryArea(AddressSpace,
4209 MemoryArea,
4210 NULL,
4211 NULL);
4212 }
4213 else
4214 {
4215 Status = MmFreeMemoryArea(AddressSpace,
4216 MemoryArea,
4217 MmFreeSectionPage,
4218 AddressSpace);
4219 }
4220 MmUnlockSectionSegment(Segment);
4221 ObDereferenceObject(Section);
4222 return(Status);
4223 }
4224
4225 /*
4226 * @implemented
4227 */
4228 NTSTATUS NTAPI
4229 MmUnmapViewOfSection(PEPROCESS Process,
4230 PVOID BaseAddress)
4231 {
4232 NTSTATUS Status;
4233 PMEMORY_AREA MemoryArea;
4234 PMMSUPPORT AddressSpace;
4235 PROS_SECTION_OBJECT Section;
4236 PMM_PAGEOP PageOp;
4237 ULONG_PTR Offset;
4238 PVOID ImageBaseAddress = 0;
4239
4240 DPRINT("Opening memory area Process %x BaseAddress %x\n",
4241 Process, BaseAddress);
4242
4243 ASSERT(Process);
4244
4245 AddressSpace = &Process->Vm;
4246
4247 MmLockAddressSpace(AddressSpace);
4248 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace,
4249 BaseAddress);
4250 if (MemoryArea == NULL ||
4251 MemoryArea->Type != MEMORY_AREA_SECTION_VIEW ||
4252 MemoryArea->DeleteInProgress)
4253 {
4254 MmUnlockAddressSpace(AddressSpace);
4255 return STATUS_NOT_MAPPED_VIEW;
4256 }
4257
4258 MemoryArea->DeleteInProgress = TRUE;
4259
4260 while (MemoryArea->PageOpCount)
4261 {
4262 Offset = PAGE_ROUND_UP((ULONG_PTR)MemoryArea->EndingAddress - (ULONG_PTR)MemoryArea->StartingAddress);
4263
4264 while (Offset)
4265 {
4266 Offset -= PAGE_SIZE;
4267 PageOp = MmCheckForPageOp(MemoryArea, NULL, NULL,
4268 MemoryArea->Data.SectionData.Segment,
4269 Offset + MemoryArea->Data.SectionData.ViewOffset);
4270 if (PageOp)
4271 {
4272 MmUnlockAddressSpace(AddressSpace);
4273 Status = MmspWaitForPageOpCompletionEvent(PageOp);
4274 if (Status != STATUS_SUCCESS)
4275 {
4276 DPRINT1("Failed to wait for page op, status = %x\n", Status);
4277 KeBugCheck(MEMORY_MANAGEMENT);
4278 }
4279 MmLockAddressSpace(AddressSpace);
4280 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace,
4281 BaseAddress);
4282 if (MemoryArea == NULL ||
4283 MemoryArea->Type != MEMORY_AREA_SECTION_VIEW)
4284 {
4285 MmUnlockAddressSpace(AddressSpace);
4286 return STATUS_NOT_MAPPED_VIEW;
4287 }
4288 break;
4289 }
4290 }
4291 }
4292
4293 Section = MemoryArea->Data.SectionData.Section;
4294
4295 if (Section->AllocationAttributes & SEC_IMAGE)
4296 {
4297 ULONG i;
4298 ULONG NrSegments;
4299 PMM_IMAGE_SECTION_OBJECT ImageSectionObject;
4300 PMM_SECTION_SEGMENT SectionSegments;
4301 PMM_SECTION_SEGMENT Segment;
4302
4303 Segment = MemoryArea->Data.SectionData.Segment;
4304 ImageSectionObject = Section->ImageSection;
4305 SectionSegments = ImageSectionObject->Segments;
4306 NrSegments = ImageSectionObject->NrSegments;
4307
4308 /* Search for the current segment within the section segments
4309 * and calculate the image base address */
4310 for (i = 0; i < NrSegments; i++)
4311 {
4312 if (!(SectionSegments[i].Characteristics & IMAGE_SCN_TYPE_NOLOAD))
4313 {
4314 if (Segment == &SectionSegments[i])
4315 {
4316 ImageBaseAddress = (char*)BaseAddress - (ULONG_PTR)SectionSegments[i].VirtualAddress;
4317 break;
4318 }
4319 }
4320 }
4321 if (i >= NrSegments)
4322 {
4323 KeBugCheck(MEMORY_MANAGEMENT);
4324 }
4325
4326 for (i = 0; i < NrSegments; i++)
4327 {
4328 if (!(SectionSegments[i].Characteristics & IMAGE_SCN_TYPE_NOLOAD))
4329 {
4330 PVOID SBaseAddress = (PVOID)
4331 ((char*)ImageBaseAddress + (ULONG_PTR)SectionSegments[i].VirtualAddress);
4332
4333 Status = MmUnmapViewOfSegment(AddressSpace, SBaseAddress);
4334 }
4335 }
4336 }
4337 else
4338 {
4339 Status = MmUnmapViewOfSegment(AddressSpace, BaseAddress);
4340 }
4341
4342 MmUnlockAddressSpace(AddressSpace);
4343
4344 /* Notify debugger */
4345 if (ImageBaseAddress) DbgkUnMapViewOfSection(ImageBaseAddress);
4346
4347 return(STATUS_SUCCESS);
4348 }
4349
4350
4351
4352
4353 /**
4354 * Queries the information of a section object.
4355 *
4356 * @param SectionHandle
4357 * Handle to the section object. It must be opened with SECTION_QUERY
4358 * access.
4359 * @param SectionInformationClass
4360 * Index to a certain information structure. Can be either
4361 * SectionBasicInformation or SectionImageInformation. The latter
4362 * is valid only for sections that were created with the SEC_IMAGE
4363 * flag.
4364 * @param SectionInformation
4365 * Caller supplies storage for resulting information.
4366 * @param Length
4367 * Size of the supplied storage.
4368 * @param ResultLength
4369 * Data written.
4370 *
4371 * @return Status.
4372 *
4373 * @implemented
4374 */
4375 NTSTATUS NTAPI
4376 NtQuerySection(IN HANDLE SectionHandle,
4377 IN SECTION_INFORMATION_CLASS SectionInformationClass,
4378 OUT PVOID SectionInformation,
4379 IN SIZE_T SectionInformationLength,
4380 OUT PSIZE_T ResultLength OPTIONAL)
4381 {
4382 PROS_SECTION_OBJECT Section;
4383 KPROCESSOR_MODE PreviousMode;
4384 NTSTATUS Status;
4385 PAGED_CODE();
4386
4387 PreviousMode = ExGetPreviousMode();
4388
4389 Status = DefaultQueryInfoBufferCheck(SectionInformationClass,
4390 ExSectionInfoClass,
4391 sizeof(ExSectionInfoClass) / sizeof(ExSectionInfoClass[0]),
4392 SectionInformation,
4393 SectionInformationLength,
4394 NULL,
4395 ResultLength,
4396 PreviousMode);
4397
4398 if(!NT_SUCCESS(Status))
4399 {
4400 DPRINT1("NtQuerySection() failed, Status: 0x%x\n", Status);
4401 return Status;
4402 }
4403
4404 Status = ObReferenceObjectByHandle(SectionHandle,
4405 SECTION_QUERY,
4406 MmSectionObjectType,
4407 PreviousMode,
4408 (PVOID*)(PVOID)&Section,
4409 NULL);
4410 if (NT_SUCCESS(Status))
4411 {
4412 switch (SectionInformationClass)
4413 {
4414 case SectionBasicInformation:
4415 {
4416 PSECTION_BASIC_INFORMATION Sbi = (PSECTION_BASIC_INFORMATION)SectionInformation;
4417
4418 _SEH2_TRY
4419 {
4420 Sbi->Attributes = Section->AllocationAttributes;
4421 if (Section->AllocationAttributes & SEC_IMAGE)
4422 {
4423 Sbi->BaseAddress = 0;
4424 Sbi->Size.QuadPart = 0;
4425 }
4426 else
4427 {
4428 Sbi->BaseAddress = (PVOID)Section->Segment->VirtualAddress;
4429 Sbi->Size.QuadPart = Section->Segment->Length;
4430 }
4431
4432 if (ResultLength != NULL)
4433 {
4434 *ResultLength = sizeof(SECTION_BASIC_INFORMATION);
4435 }
4436 Status = STATUS_SUCCESS;
4437 }
4438 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
4439 {
4440 Status = _SEH2_GetExceptionCode();
4441 }
4442 _SEH2_END;
4443
4444 break;
4445 }
4446
4447 case SectionImageInformation:
4448 {
4449 PSECTION_IMAGE_INFORMATION Sii = (PSECTION_IMAGE_INFORMATION)SectionInformation;
4450
4451 _SEH2_TRY
4452 {
4453 memset(Sii, 0, sizeof(SECTION_IMAGE_INFORMATION));
4454 if (Section->AllocationAttributes & SEC_IMAGE)
4455 {
4456 PMM_IMAGE_SECTION_OBJECT ImageSectionObject;
4457 ImageSectionObject = Section->ImageSection;
4458
4459 Sii->TransferAddress = (PVOID)ImageSectionObject->EntryPoint;
4460 Sii->MaximumStackSize = ImageSectionObject->StackReserve;
4461 Sii->CommittedStackSize = ImageSectionObject->StackCommit;
4462 Sii->SubSystemType = ImageSectionObject->Subsystem;
4463 Sii->SubSystemMinorVersion = ImageSectionObject->MinorSubsystemVersion;
4464 Sii->SubSystemMajorVersion = ImageSectionObject->MajorSubsystemVersion;
4465 Sii->ImageCharacteristics = ImageSectionObject->ImageCharacteristics;
4466 Sii->Machine = ImageSectionObject->Machine;
4467 Sii->ImageContainsCode = ImageSectionObject->Executable;
4468 }
4469
4470 if (ResultLength != NULL)
4471 {
4472 *ResultLength = sizeof(SECTION_IMAGE_INFORMATION);
4473 }
4474 Status = STATUS_SUCCESS;
4475 }
4476 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
4477 {
4478 Status = _SEH2_GetExceptionCode();
4479 }
4480 _SEH2_END;
4481
4482 break;
4483 }
4484 }
4485
4486 ObDereferenceObject(Section);
4487 }
4488
4489 return(Status);
4490 }
4491
4492 /**********************************************************************
4493 * NAME EXPORTED
4494 * MmMapViewOfSection
4495 *
4496 * DESCRIPTION
4497 * Maps a view of a section into the virtual address space of a
4498 * process.
4499 *
4500 * ARGUMENTS
4501 * Section
4502 * Pointer to the section object.
4503 *
4504 * ProcessHandle
4505 * Pointer to the process.
4506 *
4507 * BaseAddress
4508 * Desired base address (or NULL) on entry;
4509 * Actual base address of the view on exit.
4510 *
4511 * ZeroBits
4512 * Number of high order address bits that must be zero.
4513 *
4514 * CommitSize
4515 * Size in bytes of the initially committed section of
4516 * the view.
4517 *
4518 * SectionOffset
4519 * Offset in bytes from the beginning of the section
4520 * to the beginning of the view.
4521 *
4522 * ViewSize
4523 * Desired length of map (or zero to map all) on entry
4524 * Actual length mapped on exit.
4525 *
4526 * InheritDisposition
4527 * Specified how the view is to be shared with
4528 * child processes.
4529 *
4530 * AllocationType
4531 * Type of allocation for the pages.
4532 *
4533 * Protect
4534 * Protection for the committed region of the view.
4535 *
4536 * RETURN VALUE
4537 * Status.
4538 *
4539 * @implemented
4540 */
4541 NTSTATUS NTAPI
4542 MmMapViewOfSection(IN PVOID SectionObject,
4543 IN PEPROCESS Process,
4544 IN OUT PVOID *BaseAddress,
4545 IN ULONG_PTR ZeroBits,
4546 IN SIZE_T CommitSize,
4547 IN OUT PLARGE_INTEGER SectionOffset OPTIONAL,
4548 IN OUT PSIZE_T ViewSize,
4549 IN SECTION_INHERIT InheritDisposition,
4550 IN ULONG AllocationType,
4551 IN ULONG Protect)
4552 {
4553 PROS_SECTION_OBJECT Section;
4554 PMMSUPPORT AddressSpace;
4555 ULONG ViewOffset;
4556 NTSTATUS Status = STATUS_SUCCESS;
4557
4558 if ((ULONG_PTR)SectionObject & 1)
4559 {
4560 return MmMapViewOfArm3Section((PVOID)((ULONG_PTR)SectionObject & ~1),
4561 Process,
4562 BaseAddress,
4563 ZeroBits,
4564 CommitSize,
4565 SectionOffset,
4566 ViewSize,
4567 InheritDisposition,
4568 AllocationType,
4569 Protect);
4570 }
4571
4572 ASSERT(Process);
4573
4574 if (!Protect || Protect & ~PAGE_FLAGS_VALID_FOR_SECTION)
4575 {
4576 return STATUS_INVALID_PAGE_PROTECTION;
4577 }
4578
4579
4580 Section = (PROS_SECTION_OBJECT)SectionObject;
4581 AddressSpace = &Process->Vm;
4582
4583 AllocationType |= (Section->AllocationAttributes & SEC_NO_CHANGE);
4584
4585 MmLockAddressSpace(AddressSpace);
4586
4587 if (Section->AllocationAttributes & SEC_IMAGE)
4588 {
4589 ULONG i;
4590 ULONG NrSegments;
4591 ULONG_PTR ImageBase;
4592 ULONG ImageSize;
4593 PMM_IMAGE_SECTION_OBJECT ImageSectionObject;
4594 PMM_SECTION_SEGMENT SectionSegments;
4595
4596 ImageSectionObject = Section->ImageSection;
4597 SectionSegments = ImageSectionObject->Segments;
4598 NrSegments = ImageSectionObject->NrSegments;
4599
4600
4601 ImageBase = (ULONG_PTR)*BaseAddress;
4602 if (ImageBase == 0)
4603 {
4604 ImageBase = ImageSectionObject->ImageBase;
4605 }
4606
4607 ImageSize = 0;
4608 for (i = 0; i < NrSegments; i++)
4609 {
4610 if (!(SectionSegments[i].Characteristics & IMAGE_SCN_TYPE_NOLOAD))
4611 {
4612 ULONG_PTR MaxExtent;
4613 MaxExtent = (ULONG_PTR)SectionSegments[i].VirtualAddress +
4614 SectionSegments[i].Length;
4615 ImageSize = max(ImageSize, MaxExtent);
4616 }
4617 }
4618
4619 ImageSectionObject->ImageSize = ImageSize;
4620
4621 /* Check there is enough space to map the section at that point. */
4622 if (MmLocateMemoryAreaByRegion(AddressSpace, (PVOID)ImageBase,
4623 PAGE_ROUND_UP(ImageSize)) != NULL)
4624 {
4625 /* Fail if the user requested a fixed base address. */
4626 if ((*BaseAddress) != NULL)
4627 {
4628 MmUnlockAddressSpace(AddressSpace);
4629 return(STATUS_UNSUCCESSFUL);
4630 }
4631 /* Otherwise find a gap to map the image. */
4632 ImageBase = (ULONG_PTR)MmFindGap(AddressSpace, PAGE_ROUND_UP(ImageSize), PAGE_SIZE, FALSE);
4633 if (ImageBase == 0)
4634 {
4635 MmUnlockAddressSpace(AddressSpace);
4636 return(STATUS_UNSUCCESSFUL);
4637 }
4638 }
4639
4640 for (i = 0; i < NrSegments; i++)
4641 {
4642 if (!(SectionSegments[i].Characteristics & IMAGE_SCN_TYPE_NOLOAD))
4643 {
4644 PVOID SBaseAddress = (PVOID)
4645 ((char*)ImageBase + (ULONG_PTR)SectionSegments[i].VirtualAddress);
4646 MmLockSectionSegment(&SectionSegments[i]);
4647 Status = MmMapViewOfSegment(AddressSpace,
4648 Section,
4649 &SectionSegments[i],
4650 &SBaseAddress,
4651 SectionSegments[i].Length,
4652 SectionSegments[i].Protection,
4653 0,
4654 0);
4655 MmUnlockSectionSegment(&SectionSegments[i]);
4656 if (!NT_SUCCESS(Status))
4657 {
4658 MmUnlockAddressSpace(AddressSpace);
4659 return(Status);
4660 }
4661 }
4662 }
4663
4664 *BaseAddress = (PVOID)ImageBase;
4665 }
4666 else
4667 {
4668 /* check for write access */
4669 if ((Protect & (PAGE_READWRITE|PAGE_EXECUTE_READWRITE)) &&
4670 !(Section->SectionPageProtection & (PAGE_READWRITE|PAGE_EXECUTE_READWRITE)))
4671 {
4672 MmUnlockAddressSpace(AddressSpace);
4673 return STATUS_SECTION_PROTECTION;
4674 }
4675 /* check for read access */
4676 if ((Protect & (PAGE_READONLY|PAGE_WRITECOPY|PAGE_EXECUTE_READ|PAGE_EXECUTE_WRITECOPY)) &&
4677 !(Section->SectionPageProtection & (PAGE_READONLY|PAGE_READWRITE|PAGE_WRITECOPY|PAGE_EXECUTE_READ|PAGE_EXECUTE_READWRITE|PAGE_EXECUTE_WRITECOPY)))
4678 {
4679 MmUnlockAddressSpace(AddressSpace);
4680 return STATUS_SECTION_PROTECTION;
4681 }
4682 /* check for execute access */
4683 if ((Protect & (PAGE_EXECUTE|PAGE_EXECUTE_READ|PAGE_EXECUTE_READWRITE|PAGE_EXECUTE_WRITECOPY)) &&
4684 !(Section->SectionPageProtection & (PAGE_EXECUTE|PAGE_EXECUTE_READ|PAGE_EXECUTE_READWRITE|PAGE_EXECUTE_WRITECOPY)))
4685 {
4686 MmUnlockAddressSpace(AddressSpace);
4687 return STATUS_SECTION_PROTECTION;
4688 }
4689
4690 if (ViewSize == NULL)
4691 {
4692 /* Following this pointer would lead to us to the dark side */
4693 /* What to do? Bugcheck? Return status? Do the mambo? */
4694 KeBugCheck(MEMORY_MANAGEMENT);
4695 }
4696
4697 if (SectionOffset == NULL)
4698 {
4699 ViewOffset = 0;
4700 }
4701 else
4702 {
4703 ViewOffset = SectionOffset->u.LowPart;
4704 }
4705
4706 if ((ViewOffset % PAGE_SIZE) != 0)
4707 {
4708 MmUnlockAddressSpace(AddressSpace);
4709 return(STATUS_MAPPED_ALIGNMENT);
4710 }
4711
4712 if ((*ViewSize) == 0)
4713 {
4714 (*ViewSize) = Section->MaximumSize.u.LowPart - ViewOffset;
4715 }
4716 else if (((*ViewSize)+ViewOffset) > Section->MaximumSize.u.LowPart)
4717 {
4718 (*ViewSize) = Section->MaximumSize.u.LowPart - ViewOffset;
4719 }
4720
4721 *ViewSize = PAGE_ROUND_UP(*ViewSize);
4722
4723 MmLockSectionSegment(Section->Segment);
4724 Status = MmMapViewOfSegment(AddressSpace,
4725 Section,
4726 Section->Segment,
4727 BaseAddress,
4728 *ViewSize,
4729 Protect,
4730 ViewOffset,
4731 AllocationType & (MEM_TOP_DOWN|SEC_NO_CHANGE));
4732 MmUnlockSectionSegment(Section->Segment);
4733 if (!NT_SUCCESS(Status))
4734 {
4735 MmUnlockAddressSpace(AddressSpace);
4736 return(Status);
4737 }
4738 }
4739
4740 MmUnlockAddressSpace(AddressSpace);
4741
4742 return(STATUS_SUCCESS);
4743 }
4744
4745 /*
4746 * @unimplemented
4747 */
4748 BOOLEAN NTAPI
4749 MmCanFileBeTruncated (IN PSECTION_OBJECT_POINTERS SectionObjectPointer,
4750 IN PLARGE_INTEGER NewFileSize)
4751 {
4752 /* Check whether an ImageSectionObject exists */
4753 if (SectionObjectPointer->ImageSectionObject != NULL)
4754 {
4755 DPRINT1("ERROR: File can't be truncated because it has an image section\n");
4756 return FALSE;
4757 }
4758
4759 if (SectionObjectPointer->DataSectionObject != NULL)
4760 {
4761 PMM_SECTION_SEGMENT Segment;
4762
4763 Segment = (PMM_SECTION_SEGMENT)SectionObjectPointer->
4764 DataSectionObject;
4765
4766 if (Segment->ReferenceCount != 0)
4767 {
4768 /* Check size of file */
4769 if (SectionObjectPointer->SharedCacheMap)
4770 {
4771 PBCB Bcb = SectionObjectPointer->SharedCacheMap;
4772 if (NewFileSize->QuadPart <= Bcb->FileSize.QuadPart)
4773 {
4774 return FALSE;
4775 }
4776 }
4777 }
4778 else
4779 {
4780 /* Something must gone wrong
4781 * how can we have a Section but no
4782 * reference? */
4783 DPRINT("ERROR: DataSectionObject without reference!\n");
4784 }
4785 }
4786
4787 DPRINT("FIXME: didn't check for outstanding write probes\n");
4788
4789 return TRUE;
4790 }
4791
4792
4793
4794
4795 /*
4796 * @implemented
4797 */
4798 BOOLEAN NTAPI
4799 MmFlushImageSection (IN PSECTION_OBJECT_POINTERS SectionObjectPointer,
4800 IN MMFLUSH_TYPE FlushType)
4801 {
4802 switch(FlushType)
4803 {
4804 case MmFlushForDelete:
4805 if (SectionObjectPointer->ImageSectionObject ||
4806 SectionObjectPointer->DataSectionObject)
4807 {
4808 return FALSE;
4809 }
4810 #ifndef NEWCC
4811 CcRosSetRemoveOnClose(SectionObjectPointer);
4812 #endif
4813 return TRUE;
4814 case MmFlushForWrite:
4815 break;
4816 }
4817 return FALSE;
4818 }
4819
4820 /*
4821 * @implemented
4822 */
4823 NTSTATUS NTAPI
4824 MmMapViewInSystemSpace (IN PVOID SectionObject,
4825 OUT PVOID * MappedBase,
4826 IN OUT PSIZE_T ViewSize)
4827 {
4828 PROS_SECTION_OBJECT Section;
4829 PMMSUPPORT AddressSpace;
4830 NTSTATUS Status;
4831 PAGED_CODE();
4832
4833 if ((ULONG_PTR)SectionObject & 1)
4834 {
4835 extern PVOID MmSession;
4836 return MiMapViewInSystemSpace((PVOID)((ULONG_PTR)SectionObject & ~1),
4837 &MmSession,
4838 MappedBase,
4839 ViewSize);
4840 }
4841
4842 DPRINT("MmMapViewInSystemSpace() called\n");
4843
4844 Section = (PROS_SECTION_OBJECT)SectionObject;
4845 AddressSpace = MmGetKernelAddressSpace();
4846
4847 MmLockAddressSpace(AddressSpace);
4848
4849
4850 if ((*ViewSize) == 0)
4851 {
4852 (*ViewSize) = Section->MaximumSize.u.LowPart;
4853 }
4854 else if ((*ViewSize) > Section->MaximumSize.u.LowPart)
4855 {
4856 (*ViewSize) = Section->MaximumSize.u.LowPart;
4857 }
4858
4859 MmLockSectionSegment(Section->Segment);
4860
4861
4862 Status = MmMapViewOfSegment(AddressSpace,
4863 Section,
4864 Section->Segment,
4865 MappedBase,
4866 *ViewSize,
4867 PAGE_READWRITE,
4868 0,
4869 0);
4870
4871 MmUnlockSectionSegment(Section->Segment);
4872 MmUnlockAddressSpace(AddressSpace);
4873
4874 return Status;
4875 }
4876
4877 /*
4878 * @implemented
4879 */
4880 NTSTATUS NTAPI
4881 MmUnmapViewInSystemSpace (IN PVOID MappedBase)
4882 {
4883 PMMSUPPORT AddressSpace;
4884 NTSTATUS Status;
4885
4886 DPRINT("MmUnmapViewInSystemSpace() called\n");
4887
4888 AddressSpace = MmGetKernelAddressSpace();
4889
4890 Status = MmUnmapViewOfSegment(AddressSpace, MappedBase);
4891
4892 return Status;
4893 }
4894
4895
4896 /**********************************************************************
4897 * NAME EXPORTED
4898 * MmCreateSection@
4899 *
4900 * DESCRIPTION
4901 * Creates a section object.
4902 *
4903 * ARGUMENTS
4904 * SectionObject (OUT)
4905 * Caller supplied storage for the resulting pointer
4906 * to a SECTION_OBJECT instance;
4907 *
4908 * DesiredAccess
4909 * Specifies the desired access to the section can be a
4910 * combination of:
4911 * STANDARD_RIGHTS_REQUIRED |
4912 * SECTION_QUERY |
4913 * SECTION_MAP_WRITE |
4914 * SECTION_MAP_READ |
4915 * SECTION_MAP_EXECUTE
4916 *
4917 * ObjectAttributes [OPTIONAL]
4918 * Initialized attributes for the object can be used
4919 * to create a named section;
4920 *
4921 * MaximumSize
4922 * Maximizes the size of the memory section. Must be
4923 * non-NULL for a page-file backed section.
4924 * If value specified for a mapped file and the file is
4925 * not large enough, file will be extended.
4926 *
4927 * SectionPageProtection
4928 * Can be a combination of:
4929 * PAGE_READONLY |
4930 * PAGE_READWRITE |
4931 * PAGE_WRITEONLY |
4932 * PAGE_WRITECOPY
4933 *
4934 * AllocationAttributes
4935 * Can be a combination of:
4936 * SEC_IMAGE |
4937 * SEC_RESERVE
4938 *
4939 * FileHandle
4940 * Handle to a file to create a section mapped to a file
4941 * instead of a memory backed section;
4942 *
4943 * File
4944 * Unknown.
4945 *
4946 * RETURN VALUE
4947 * Status.
4948 *
4949 * @implemented
4950 */
4951 NTSTATUS NTAPI
4952 MmCreateSection (OUT PVOID * Section,
4953 IN ACCESS_MASK DesiredAccess,
4954 IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
4955 IN PLARGE_INTEGER MaximumSize,
4956 IN ULONG SectionPageProtection,
4957 IN ULONG AllocationAttributes,
4958 IN HANDLE FileHandle OPTIONAL,
4959 IN PFILE_OBJECT File OPTIONAL)
4960 {
4961 ULONG Protection;
4962 PROS_SECTION_OBJECT *SectionObject = (PROS_SECTION_OBJECT *)Section;
4963
4964 /* Check if an ARM3 section is being created instead */
4965 if (AllocationAttributes & 1)
4966 {
4967 DPRINT1("arm 3 path\n");
4968 return MmCreateArm3Section(Section,
4969 DesiredAccess,
4970 ObjectAttributes,
4971 MaximumSize,
4972 SectionPageProtection,
4973 AllocationAttributes &~ 1,
4974 FileHandle,
4975 File);
4976 }
4977
4978 /*
4979 * Check the protection
4980 */
4981 Protection = SectionPageProtection & ~(PAGE_GUARD|PAGE_NOCACHE);
4982 if (Protection != PAGE_READONLY &&
4983 Protection != PAGE_READWRITE &&
4984 Protection != PAGE_WRITECOPY &&
4985 Protection != PAGE_EXECUTE &&
4986 Protection != PAGE_EXECUTE_READ &&
4987 Protection != PAGE_EXECUTE_READWRITE &&
4988 Protection != PAGE_EXECUTE_WRITECOPY)
4989 {
4990 return STATUS_INVALID_PAGE_PROTECTION;
4991 }
4992
4993 if (AllocationAttributes & SEC_IMAGE)
4994 {
4995 return(MmCreateImageSection(SectionObject,
4996 DesiredAccess,
4997 ObjectAttributes,
4998 MaximumSize,
4999 SectionPageProtection,
5000 AllocationAttributes,
5001 FileHandle));
5002 }
5003
5004 if (FileHandle != NULL)
5005 {
5006 return(MmCreateDataFileSection(SectionObject,
5007 DesiredAccess,
5008 ObjectAttributes,
5009 MaximumSize,
5010 SectionPageProtection,
5011 AllocationAttributes,
5012 FileHandle));
5013 }
5014
5015 return(MmCreatePageFileSection(SectionObject,
5016 DesiredAccess,
5017 ObjectAttributes,
5018 MaximumSize,
5019 SectionPageProtection,
5020 AllocationAttributes));
5021 }
5022
5023
5024
5025 /* EOF */