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