d7c987e6994106f28419f4e9ab33f0a99127da6d
[reactos.git] / reactos / ntoskrnl / mm / process.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/mm/process.c
5 * PURPOSE: Memory functions related to Processes
6 *
7 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
8 */
9
10 /* INCLUDES *****************************************************************/
11
12 #include <ntoskrnl.h>
13 #define NDEBUG
14 #include <internal/debug.h>
15
16 extern ULONG NtMajorVersion;
17 extern ULONG NtMinorVersion;
18 extern ULONG NtOSCSDVersion;
19 extern ULONG NtGlobalFlag;
20
21 /* FUNCTIONS *****************************************************************/
22
23 PVOID
24 STDCALL
25 MiCreatePebOrTeb(PEPROCESS Process,
26 PVOID BaseAddress)
27 {
28 NTSTATUS Status;
29 PMADDRESS_SPACE ProcessAddressSpace = &Process->AddressSpace;
30 PMEMORY_AREA MemoryArea;
31 PHYSICAL_ADDRESS BoundaryAddressMultiple;
32 PVOID AllocatedBase = BaseAddress;
33 BoundaryAddressMultiple.QuadPart = 0;
34
35 /* Acquire the Lock */
36 MmLockAddressSpace(ProcessAddressSpace);
37
38 /*
39 * Create a Peb or Teb.
40 * Loop until it works, decreasing by PAGE_SIZE each time. The logic here
41 * is that a PEB allocation should never fail since the address is free,
42 * while TEB allocation can fail, and we should simply try the address
43 * below. Is there a nicer way of doing this automagically? (ie: findning)
44 * a gap region? -- Alex
45 */
46 do {
47 DPRINT("Trying to allocate: %x\n", AllocatedBase);
48 Status = MmCreateMemoryArea(Process,
49 ProcessAddressSpace,
50 MEMORY_AREA_PEB_OR_TEB,
51 &AllocatedBase,
52 PAGE_SIZE,
53 PAGE_READWRITE,
54 &MemoryArea,
55 TRUE,
56 FALSE,
57 BoundaryAddressMultiple);
58 AllocatedBase = RVA(AllocatedBase, -PAGE_SIZE);
59 } while (Status != STATUS_SUCCESS);
60
61 /* Initialize the Region */
62 MmInitialiseRegion(&MemoryArea->Data.VirtualMemoryData.RegionListHead,
63 PAGE_SIZE,
64 MEM_COMMIT,
65 PAGE_READWRITE);
66
67 /* Reserve the pages */
68 MmReserveSwapPages(PAGE_SIZE);
69
70 /* Unlock Address Space */
71 DPRINT("Returning\n");
72 MmUnlockAddressSpace(ProcessAddressSpace);
73 return RVA(AllocatedBase, PAGE_SIZE);
74 }
75
76 VOID
77 MiFreeStackPage(PVOID Context,
78 MEMORY_AREA* MemoryArea,
79 PVOID Address,
80 PFN_TYPE Page,
81 SWAPENTRY SwapEntry,
82 BOOLEAN Dirty)
83 {
84 ASSERT(SwapEntry == 0);
85 if (Page) MmReleasePageMemoryConsumer(MC_NPPOOL, Page);
86 }
87
88 VOID
89 STDCALL
90 MmDeleteKernelStack(PVOID Stack,
91 BOOLEAN GuiStack)
92 {
93 /* Lock the Address Space */
94 MmLockAddressSpace(MmGetKernelAddressSpace());
95
96 /* Delete the Stack */
97 MmFreeMemoryAreaByPtr(MmGetKernelAddressSpace(),
98 Stack,
99 MiFreeStackPage,
100 NULL);
101
102 /* Unlock the Address Space */
103 MmUnlockAddressSpace(MmGetKernelAddressSpace());
104 }
105
106 VOID
107 MiFreePebPage(PVOID Context,
108 MEMORY_AREA* MemoryArea,
109 PVOID Address,
110 PFN_TYPE Page,
111 SWAPENTRY SwapEntry,
112 BOOLEAN Dirty)
113 {
114 PEPROCESS Process = (PEPROCESS)Context;
115
116 if (Page != 0)
117 {
118 SWAPENTRY SavedSwapEntry;
119 SavedSwapEntry = MmGetSavedSwapEntryPage(Page);
120 if (SavedSwapEntry != 0)
121 {
122 MmFreeSwapPage(SavedSwapEntry);
123 MmSetSavedSwapEntryPage(Page, 0);
124 }
125 MmDeleteRmap(Page, Process, Address);
126 MmReleasePageMemoryConsumer(MC_USER, Page);
127 }
128 else if (SwapEntry != 0)
129 {
130 MmFreeSwapPage(SwapEntry);
131 }
132 }
133
134 VOID
135 STDCALL
136 MmDeleteTeb(PEPROCESS Process,
137 PTEB Teb)
138 {
139 PMADDRESS_SPACE ProcessAddressSpace = &Process->AddressSpace;
140
141 /* Lock the Address Space */
142 MmLockAddressSpace(ProcessAddressSpace);
143
144 /* Delete the Stack */
145 MmFreeMemoryAreaByPtr(ProcessAddressSpace,
146 Teb,
147 MiFreePebPage,
148 Process);
149
150 /* Unlock the Address Space */
151 MmUnlockAddressSpace(ProcessAddressSpace);
152 }
153
154 PVOID
155 STDCALL
156 MmCreateKernelStack(BOOLEAN GuiStack)
157 {
158 PMEMORY_AREA StackArea;
159 ULONG i;
160 PHYSICAL_ADDRESS BoundaryAddressMultiple;
161 PFN_TYPE Page[MM_STACK_SIZE / PAGE_SIZE];
162 PVOID KernelStack = NULL;
163 NTSTATUS Status;
164
165 /* Initialize the Boundary Address */
166 BoundaryAddressMultiple.QuadPart = 0;
167
168 /* Lock the Kernel Address Space */
169 MmLockAddressSpace(MmGetKernelAddressSpace());
170
171 /* Create a MAREA for the Kernel Stack */
172 Status = MmCreateMemoryArea(NULL,
173 MmGetKernelAddressSpace(),
174 MEMORY_AREA_KERNEL_STACK,
175 &KernelStack,
176 MM_STACK_SIZE,
177 0,
178 &StackArea,
179 FALSE,
180 FALSE,
181 BoundaryAddressMultiple);
182
183 /* Unlock the Address Space */
184 MmUnlockAddressSpace(MmGetKernelAddressSpace());
185
186 /* Check for Success */
187 if (!NT_SUCCESS(Status))
188 {
189 DPRINT1("Failed to create thread stack\n");
190 KEBUGCHECK(0);
191 }
192
193 /* Mark the Stack in use */
194 for (i = 0; i < (MM_STACK_SIZE / PAGE_SIZE); i++)
195 {
196 Status = MmRequestPageMemoryConsumer(MC_NPPOOL, TRUE, &Page[i]);
197 }
198
199 /* Create a Virtual Mapping for it */
200 Status = MmCreateVirtualMapping(NULL,
201 KernelStack,
202 PAGE_READWRITE,
203 Page,
204 MM_STACK_SIZE / PAGE_SIZE);
205
206 /* Check for success */
207 if (!NT_SUCCESS(Status))
208 {
209 DPRINT1("Could not create Virtual Mapping for Kernel Stack\n");
210 KEBUGCHECK(0);
211 }
212
213 return KernelStack;
214 }
215
216 NTSTATUS
217 STDCALL
218 MmCreatePeb(PEPROCESS Process)
219 {
220 PPEB Peb = NULL;
221 LARGE_INTEGER SectionOffset;
222 ULONG ViewSize = 0;
223 PVOID TableBase = NULL;
224 PIMAGE_NT_HEADERS NtHeaders;
225 PIMAGE_LOAD_CONFIG_DIRECTORY ImageConfigData;
226 NTSTATUS Status;
227 KAFFINITY ProcessAffinityMask = 0;
228 SectionOffset.QuadPart = (ULONGLONG)0;
229
230 DPRINT("MmCreatePeb\n");
231
232 /* Map NLS Tables */
233 DPRINT("Mapping NLS\n");
234 Status = MmMapViewOfSection(NlsSectionObject,
235 Process,
236 &TableBase,
237 0,
238 0,
239 &SectionOffset,
240 &ViewSize,
241 ViewShare,
242 MEM_TOP_DOWN,
243 PAGE_READONLY);
244 if (!NT_SUCCESS(Status))
245 {
246 DPRINT1("MmMapViewOfSection() failed (Status %lx)\n", Status);
247 return(Status);
248 }
249 DPRINT("TableBase %p ViewSize %lx\n", TableBase, ViewSize);
250
251 /* Attach to Process */
252 KeAttachProcess(&Process->Pcb);
253
254 /* Allocate the PEB */
255 Peb = MiCreatePebOrTeb(Process, (PVOID)PEB_BASE);
256
257 /* Initialize the PEB */
258 DPRINT("Allocated: %x\n", Peb);
259 RtlZeroMemory(Peb, sizeof(PEB));
260
261 /* Set up data */
262 DPRINT("Setting up PEB\n");
263 Peb->ImageBaseAddress = Process->SectionBaseAddress;
264 Peb->InheritedAddressSpace = 0;
265 Peb->Mutant = NULL;
266
267 /* NLS */
268 Peb->AnsiCodePageData = (char*)TableBase + NlsAnsiTableOffset;
269 Peb->OemCodePageData = (char*)TableBase + NlsOemTableOffset;
270 Peb->UnicodeCaseTableData = (char*)TableBase + NlsUnicodeTableOffset;
271
272 /* Default Version Data (could get changed below) */
273 Peb->OSMajorVersion = NtMajorVersion;
274 Peb->OSMinorVersion = NtMinorVersion;
275 Peb->OSBuildNumber = 2195;
276 Peb->OSPlatformId = 2; /* VER_PLATFORM_WIN32_NT */
277 Peb->OSCSDVersion = NtOSCSDVersion;
278
279 /* Heap and Debug Data */
280 Peb->NumberOfProcessors = KeNumberProcessors;
281 Peb->BeingDebugged = (BOOLEAN)(Process->DebugPort != NULL ? TRUE : FALSE);
282 Peb->NtGlobalFlag = NtGlobalFlag;
283 /*Peb->HeapSegmentReserve = MmHeapSegmentReserve;
284 Peb->HeapSegmentCommit = MmHeapSegmentCommit;
285 Peb->HeapDeCommitTotalFreeThreshold = MmHeapDeCommitTotalFreeThreshold;
286 Peb->HeapDeCommitFreeBlockThreshold = MmHeapDeCommitFreeBlockThreshold;*/
287 Peb->NumberOfHeaps = 0;
288 Peb->MaximumNumberOfHeaps = (PAGE_SIZE - sizeof(PEB)) / sizeof(PVOID);
289 Peb->ProcessHeaps = (PVOID*)Peb + 1;
290
291 /* Image Data */
292 if ((NtHeaders = RtlImageNtHeader(Peb->ImageBaseAddress)))
293 {
294 /* Get the Image Config Data too */
295 ImageConfigData = RtlImageDirectoryEntryToData(Peb->ImageBaseAddress,
296 TRUE,
297 IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG,
298 &ViewSize);
299
300 /* Write subsystem data */
301 Peb->ImageSubSystem = NtHeaders->OptionalHeader.Subsystem;
302 Peb->ImageSubSystemMajorVersion = NtHeaders->OptionalHeader.MajorSubsystemVersion;
303 Peb->ImageSubSystemMinorVersion = NtHeaders->OptionalHeader.MinorSubsystemVersion;
304
305 /* Write Version Data */
306 if (NtHeaders->OptionalHeader.Win32VersionValue)
307 {
308 Peb->OSMajorVersion = NtHeaders->OptionalHeader.Win32VersionValue & 0xFF;
309 Peb->OSMinorVersion = (NtHeaders->OptionalHeader.Win32VersionValue >> 8) & 0xFF;
310 Peb->OSBuildNumber = (NtHeaders->OptionalHeader.Win32VersionValue >> 16) & 0x3FFF;
311
312 /* Lie about the version if requested */
313 if (ImageConfigData && ImageConfigData->CSDVersion)
314 {
315 Peb->OSCSDVersion = ImageConfigData->CSDVersion;
316 }
317
318 /* Set the Platform ID */
319 Peb->OSPlatformId = (NtHeaders->OptionalHeader.Win32VersionValue >> 30) ^ 2;
320 }
321
322 /* Check for affinity override */
323 if (ImageConfigData && ImageConfigData->ProcessAffinityMask)
324 {
325 ProcessAffinityMask = ImageConfigData->ProcessAffinityMask;
326 }
327
328 /* Check if the image is not safe for SMP */
329 if (NtHeaders->FileHeader.Characteristics & IMAGE_FILE_UP_SYSTEM_ONLY)
330 {
331 /* FIXME: Choose one randomly */
332 Peb->ImageProcessAffinityMask = 1;
333 }
334 else
335 {
336 /* Use affinity from Image Header */
337 Peb->ImageProcessAffinityMask = ProcessAffinityMask;
338 }
339 }
340
341 /* Misc data */
342 Peb->SessionId = Process->Session;
343 Process->Peb = Peb;
344
345 /* Detach from the Process */
346 KeDetachProcess();
347
348 DPRINT("MmCreatePeb: Peb created at %p\n", Peb);
349 return STATUS_SUCCESS;
350 }
351
352 PTEB
353 STDCALL
354 MmCreateTeb(PEPROCESS Process,
355 PCLIENT_ID ClientId,
356 PINITIAL_TEB InitialTeb)
357 {
358 PTEB Teb;
359 BOOLEAN Attached = FALSE;
360
361 /* Attach to the process */
362 DPRINT("MmCreateTeb\n");
363 if (Process != PsGetCurrentProcess())
364 {
365 /* Attach to Target */
366 KeAttachProcess(&Process->Pcb);
367 Attached = TRUE;
368 }
369
370 /* Allocate the TEB */
371 Teb = MiCreatePebOrTeb(Process, (PVOID)TEB_BASE);
372
373 /* Initialize the PEB */
374 RtlZeroMemory(Teb, sizeof(TEB));
375
376 /* Set TIB Data */
377 Teb->Tib.ExceptionList = (PVOID)0xFFFFFFFF;
378 Teb->Tib.Version = 1;
379 Teb->Tib.Self = (PNT_TIB)Teb;
380
381 /* Set TEB Data */
382 Teb->Cid = *ClientId;
383 Teb->RealClientId = *ClientId;
384 Teb->ProcessEnvironmentBlock = Process->Peb;
385 Teb->CurrentLocale = PsDefaultThreadLocaleId;
386
387 /* Store stack information from InitialTeb */
388 if(InitialTeb != NULL)
389 {
390 Teb->Tib.StackBase = InitialTeb->StackBase;
391 Teb->Tib.StackLimit = InitialTeb->StackLimit;
392 Teb->DeallocationStack = InitialTeb->AllocatedStackBase;
393 }
394
395 /* Return TEB Address */
396 DPRINT("Allocated: %x\n", Teb);
397 if (Attached) KeDetachProcess();
398 return Teb;
399 }
400
401 NTSTATUS
402 STDCALL
403 MmCreateProcessAddressSpace(IN PEPROCESS Process,
404 IN PSECTION_OBJECT Section OPTIONAL)
405 {
406 NTSTATUS Status;
407 PMADDRESS_SPACE ProcessAddressSpace = &Process->AddressSpace;
408 PVOID BaseAddress;
409 PMEMORY_AREA MemoryArea;
410 PHYSICAL_ADDRESS BoundaryAddressMultiple;
411 ULONG ViewSize = 0;
412 PVOID ImageBase = 0;
413 BoundaryAddressMultiple.QuadPart = 0;
414
415 /* Initialize the Addresss Space */
416 MmInitializeAddressSpace(Process, ProcessAddressSpace);
417
418 /* Acquire the Lock */
419 MmLockAddressSpace(ProcessAddressSpace);
420
421 /* Protect the highest 64KB of the process address space */
422 BaseAddress = (PVOID)MmUserProbeAddress;
423 Status = MmCreateMemoryArea(Process,
424 ProcessAddressSpace,
425 MEMORY_AREA_NO_ACCESS,
426 &BaseAddress,
427 0x10000,
428 PAGE_NOACCESS,
429 &MemoryArea,
430 FALSE,
431 FALSE,
432 BoundaryAddressMultiple);
433 if (!NT_SUCCESS(Status))
434 {
435 DPRINT1("Failed to protect last 64KB\n");
436 goto exit;
437 }
438
439 /* Protect the 60KB above the shared user page */
440 BaseAddress = (char*)USER_SHARED_DATA + PAGE_SIZE;
441 Status = MmCreateMemoryArea(Process,
442 ProcessAddressSpace,
443 MEMORY_AREA_NO_ACCESS,
444 &BaseAddress,
445 0x10000 - PAGE_SIZE,
446 PAGE_NOACCESS,
447 &MemoryArea,
448 FALSE,
449 FALSE,
450 BoundaryAddressMultiple);
451 if (!NT_SUCCESS(Status))
452 {
453 DPRINT1("Failed to protect the memory above the shared user page\n");
454 goto exit;
455 }
456
457 /* Create the shared data page */
458 BaseAddress = (PVOID)USER_SHARED_DATA;
459 Status = MmCreateMemoryArea(Process,
460 ProcessAddressSpace,
461 MEMORY_AREA_SHARED_DATA,
462 &BaseAddress,
463 PAGE_SIZE,
464 PAGE_READONLY,
465 &MemoryArea,
466 FALSE,
467 FALSE,
468 BoundaryAddressMultiple);
469 if (!NT_SUCCESS(Status))
470 {
471 DPRINT1("Failed to create Shared User Data\n");
472 goto exit;
473 }
474
475 /* Check if there's a Section Object */
476 if (Section)
477 {
478 UNICODE_STRING FileName;
479 PWCHAR szSrc;
480 PCHAR szDest;
481 USHORT lnFName = 0;
482
483 /* Unlock the Address Space */
484 DPRINT("Unlocking\n");
485 MmUnlockAddressSpace(ProcessAddressSpace);
486
487 DPRINT("Mapping process image. Section: %p, Process: %p, ImageBase: %p\n",
488 Section, Process, &ImageBase);
489 Status = MmMapViewOfSection(Section,
490 Process,
491 (PVOID*)&ImageBase,
492 0,
493 0,
494 NULL,
495 &ViewSize,
496 0,
497 MEM_COMMIT,
498 PAGE_READWRITE);
499 if (!NT_SUCCESS(Status))
500 {
501 DPRINT1("Failed to map process Image\n");
502 ObDereferenceObject(Section);
503 goto exit;
504 }
505 ObDereferenceObject(Section);
506
507 /* Save the pointer */
508 Process->SectionBaseAddress = ImageBase;
509
510 /* Determine the image file name and save it to EPROCESS */
511 DPRINT("Getting Image name\n");
512 FileName = Section->FileObject->FileName;
513 szSrc = (PWCHAR)(FileName.Buffer + (FileName.Length / sizeof(WCHAR)) - 1);
514
515 while(szSrc >= FileName.Buffer)
516 {
517 if(*szSrc == L'\\')
518 {
519 szSrc++;
520 break;
521 }
522 else
523 {
524 szSrc--;
525 lnFName++;
526 }
527 }
528
529 /* Copy the to the process and truncate it to 15 characters if necessary */
530 DPRINT("Copying and truncating\n");
531 szDest = Process->ImageFileName;
532 lnFName = min(lnFName, sizeof(Process->ImageFileName) - 1);
533 while(lnFName-- > 0) *(szDest++) = (UCHAR)*(szSrc++);
534
535 /* Return status to caller */
536 return Status;
537 }
538
539 exit:
540 /* Unlock the Address Space */
541 DPRINT("Unlocking\n");
542 MmUnlockAddressSpace(ProcessAddressSpace);
543
544 /* Return status to caller */
545 return Status;
546 }