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