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