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