- Implement (although non-optimally) MmGrowKernelStack for future use.
[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 /*
170 * Mark the Stack in use.
171 * Note: Currently we mark all 60KB in use for a GUI Thread.
172 * We should only do this inside MmGrowKernelStack. TODO!
173 */
174 for (i = 0; i < (StackSize / PAGE_SIZE); i++)
175 {
176 Status = MmRequestPageMemoryConsumer(MC_NPPOOL, TRUE, &Page[i]);
177 }
178
179 /* Create a Virtual Mapping for it */
180 Status = MmCreateVirtualMapping(NULL,
181 KernelStack,
182 PAGE_READWRITE,
183 Page,
184 StackSize / PAGE_SIZE);
185
186 /* Check for success */
187 if (!NT_SUCCESS(Status))
188 {
189 DPRINT1("Could not create Virtual Mapping for Kernel Stack\n");
190 KEBUGCHECK(0);
191 }
192
193 /* Return the stack */
194 return KernelStack;
195 }
196
197 /*
198 * @implemented
199 */
200 NTSTATUS
201 STDCALL
202 MmGrowKernelStack(PVOID StackPointer)
203 {
204 PETHREAD Thread = PsGetCurrentThread();
205
206 /* Make sure we have reserved space for our grow */
207 ASSERT(((PCHAR)Thread->Tcb.StackBase - (PCHAR)Thread->Tcb.StackLimit) <=
208 (KERNEL_LARGE_STACK_SIZE + PAGE_SIZE));
209
210 /*
211 * We'll give you three more pages.
212 * NOTE: See note in MmCreateKernelStack. These pages are already being reserved.
213 * It would be more efficient to only grow them (commit them) here.
214 */
215 Thread->Tcb.StackLimit -= KERNEL_STACK_SIZE;
216
217 /* Return success */
218 DPRINT1("Thread, Thread Limit, Stack %p %p %p\n", KeGetCurrentThread(),
219 KeGetCurrentThread()->StackLimit,
220 StackPointer);
221 return STATUS_SUCCESS;
222 }
223
224 NTSTATUS
225 STDCALL
226 MmCreatePeb(PEPROCESS Process)
227 {
228 PPEB Peb = NULL;
229 LARGE_INTEGER SectionOffset;
230 SIZE_T ViewSize = 0;
231 PVOID TableBase = NULL;
232 PIMAGE_NT_HEADERS NtHeaders;
233 PIMAGE_LOAD_CONFIG_DIRECTORY ImageConfigData;
234 NTSTATUS Status;
235 KAFFINITY ProcessAffinityMask = 0;
236 SectionOffset.QuadPart = (ULONGLONG)0;
237 DPRINT("MmCreatePeb\n");
238
239 /* Allocate the PEB */
240 Peb = MiCreatePebOrTeb(Process,
241 (PVOID)((ULONG_PTR)MM_HIGHEST_VAD_ADDRESS + 1));
242 ASSERT(Peb == (PVOID)0x7FFDF000);
243
244 /* Map NLS Tables */
245 DPRINT("Mapping NLS\n");
246 Status = MmMapViewOfSection(NlsSectionObject,
247 Process,
248 &TableBase,
249 0,
250 0,
251 &SectionOffset,
252 &ViewSize,
253 ViewShare,
254 MEM_TOP_DOWN,
255 PAGE_READONLY);
256 if (!NT_SUCCESS(Status))
257 {
258 DPRINT1("MmMapViewOfSection() failed (Status %lx)\n", Status);
259 return(Status);
260 }
261 DPRINT("TableBase %p ViewSize %lx\n", TableBase, ViewSize);
262
263 /* Attach to Process */
264 KeAttachProcess(&Process->Pcb);
265
266 /* Initialize the PEB */
267 DPRINT("Allocated: %x\n", Peb);
268 RtlZeroMemory(Peb, sizeof(PEB));
269
270 /* Set up data */
271 DPRINT("Setting up PEB\n");
272 Peb->ImageBaseAddress = Process->SectionBaseAddress;
273 Peb->InheritedAddressSpace = 0;
274 Peb->Mutant = NULL;
275
276 /* NLS */
277 Peb->AnsiCodePageData = (char*)TableBase + NlsAnsiTableOffset;
278 Peb->OemCodePageData = (char*)TableBase + NlsOemTableOffset;
279 Peb->UnicodeCaseTableData = (char*)TableBase + NlsUnicodeTableOffset;
280
281 /* Default Version Data (could get changed below) */
282 Peb->OSMajorVersion = NtMajorVersion;
283 Peb->OSMinorVersion = NtMinorVersion;
284 Peb->OSBuildNumber = 2195;
285 Peb->OSPlatformId = 2; /* VER_PLATFORM_WIN32_NT */
286 Peb->OSCSDVersion = NtOSCSDVersion;
287
288 /* Heap and Debug Data */
289 Peb->NumberOfProcessors = KeNumberProcessors;
290 Peb->BeingDebugged = (BOOLEAN)(Process->DebugPort != NULL ? TRUE : FALSE);
291 Peb->NtGlobalFlag = NtGlobalFlag;
292 /*Peb->HeapSegmentReserve = MmHeapSegmentReserve;
293 Peb->HeapSegmentCommit = MmHeapSegmentCommit;
294 Peb->HeapDeCommitTotalFreeThreshold = MmHeapDeCommitTotalFreeThreshold;
295 Peb->HeapDeCommitFreeBlockThreshold = MmHeapDeCommitFreeBlockThreshold;*/
296 Peb->NumberOfHeaps = 0;
297 Peb->MaximumNumberOfHeaps = (PAGE_SIZE - sizeof(PEB)) / sizeof(PVOID);
298 Peb->ProcessHeaps = (PVOID*)Peb + 1;
299
300 /* Image Data */
301 if ((NtHeaders = RtlImageNtHeader(Peb->ImageBaseAddress)))
302 {
303 /* Get the Image Config Data too */
304 ImageConfigData = RtlImageDirectoryEntryToData(Peb->ImageBaseAddress,
305 TRUE,
306 IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG,
307 &ViewSize);
308
309 /* Write subsystem data */
310 Peb->ImageSubSystem = NtHeaders->OptionalHeader.Subsystem;
311 Peb->ImageSubSystemMajorVersion = NtHeaders->OptionalHeader.MajorSubsystemVersion;
312 Peb->ImageSubSystemMinorVersion = NtHeaders->OptionalHeader.MinorSubsystemVersion;
313
314 /* Write Version Data */
315 if (NtHeaders->OptionalHeader.Win32VersionValue)
316 {
317 Peb->OSMajorVersion = NtHeaders->OptionalHeader.Win32VersionValue & 0xFF;
318 Peb->OSMinorVersion = (NtHeaders->OptionalHeader.Win32VersionValue >> 8) & 0xFF;
319 Peb->OSBuildNumber = (NtHeaders->OptionalHeader.Win32VersionValue >> 16) & 0x3FFF;
320
321 /* Lie about the version if requested */
322 if (ImageConfigData && ImageConfigData->CSDVersion)
323 {
324 Peb->OSCSDVersion = ImageConfigData->CSDVersion;
325 }
326
327 /* Set the Platform ID */
328 Peb->OSPlatformId = (NtHeaders->OptionalHeader.Win32VersionValue >> 30) ^ 2;
329 }
330
331 /* Check for affinity override */
332 if (ImageConfigData && ImageConfigData->ProcessAffinityMask)
333 {
334 ProcessAffinityMask = ImageConfigData->ProcessAffinityMask;
335 }
336
337 /* Check if the image is not safe for SMP */
338 if (NtHeaders->FileHeader.Characteristics & IMAGE_FILE_UP_SYSTEM_ONLY)
339 {
340 /* FIXME: Choose one randomly */
341 Peb->ImageProcessAffinityMask = 1;
342 }
343 else
344 {
345 /* Use affinity from Image Header */
346 Peb->ImageProcessAffinityMask = ProcessAffinityMask;
347 }
348 }
349
350 /* Misc data */
351 Peb->SessionId = Process->Session;
352 Process->Peb = Peb;
353
354 /* Detach from the Process */
355 KeDetachProcess();
356
357 DPRINT("MmCreatePeb: Peb created at %p\n", Peb);
358 return STATUS_SUCCESS;
359 }
360
361 PTEB
362 STDCALL
363 MmCreateTeb(PEPROCESS Process,
364 PCLIENT_ID ClientId,
365 PINITIAL_TEB InitialTeb)
366 {
367 PTEB Teb;
368 BOOLEAN Attached = FALSE;
369
370 /* Attach to the process */
371 DPRINT("MmCreateTeb\n");
372 if (Process != PsGetCurrentProcess())
373 {
374 /* Attach to Target */
375 KeAttachProcess(&Process->Pcb);
376 Attached = TRUE;
377 }
378
379 /* Allocate the TEB */
380 Teb = MiCreatePebOrTeb(Process,
381 (PVOID)((ULONG_PTR)MM_HIGHEST_VAD_ADDRESS + 1));
382
383 /* Initialize the PEB */
384 RtlZeroMemory(Teb, sizeof(TEB));
385
386 /* Set TIB Data */
387 Teb->Tib.ExceptionList = (PVOID)0xFFFFFFFF;
388 Teb->Tib.Version = 1;
389 Teb->Tib.Self = (PNT_TIB)Teb;
390
391 /* Set TEB Data */
392 Teb->Cid = *ClientId;
393 Teb->RealClientId = *ClientId;
394 Teb->ProcessEnvironmentBlock = Process->Peb;
395 Teb->CurrentLocale = PsDefaultThreadLocaleId;
396
397 /* Store stack information from InitialTeb */
398 if(InitialTeb != NULL)
399 {
400 Teb->Tib.StackBase = InitialTeb->StackBase;
401 Teb->Tib.StackLimit = InitialTeb->StackLimit;
402 Teb->DeallocationStack = InitialTeb->AllocatedStackBase;
403 }
404
405 /* Return TEB Address */
406 DPRINT("Allocated: %x\n", Teb);
407 if (Attached) KeDetachProcess();
408 return Teb;
409 }
410
411 NTSTATUS
412 STDCALL
413 MmCreateProcessAddressSpace(IN PEPROCESS Process,
414 IN PSECTION_OBJECT Section OPTIONAL)
415 {
416 NTSTATUS Status;
417 PMADDRESS_SPACE ProcessAddressSpace = &Process->AddressSpace;
418 PVOID BaseAddress;
419 PMEMORY_AREA MemoryArea;
420 PHYSICAL_ADDRESS BoundaryAddressMultiple;
421 SIZE_T ViewSize = 0;
422 PVOID ImageBase = 0;
423 BoundaryAddressMultiple.QuadPart = 0;
424
425 /* Initialize the Addresss Space */
426 MmInitializeAddressSpace(Process, ProcessAddressSpace);
427
428 /* Acquire the Lock */
429 MmLockAddressSpace(ProcessAddressSpace);
430
431 /* Protect the highest 64KB of the process address space */
432 BaseAddress = (PVOID)MmUserProbeAddress;
433 Status = MmCreateMemoryArea(ProcessAddressSpace,
434 MEMORY_AREA_NO_ACCESS,
435 &BaseAddress,
436 0x10000,
437 PAGE_NOACCESS,
438 &MemoryArea,
439 FALSE,
440 0,
441 BoundaryAddressMultiple);
442 if (!NT_SUCCESS(Status))
443 {
444 DPRINT1("Failed to protect last 64KB\n");
445 goto exit;
446 }
447
448 /* Protect the 60KB above the shared user page */
449 BaseAddress = (char*)USER_SHARED_DATA + PAGE_SIZE;
450 Status = MmCreateMemoryArea(ProcessAddressSpace,
451 MEMORY_AREA_NO_ACCESS,
452 &BaseAddress,
453 0x10000 - PAGE_SIZE,
454 PAGE_NOACCESS,
455 &MemoryArea,
456 FALSE,
457 0,
458 BoundaryAddressMultiple);
459 if (!NT_SUCCESS(Status))
460 {
461 DPRINT1("Failed to protect the memory above the shared user page\n");
462 goto exit;
463 }
464
465 /* Create the shared data page */
466 BaseAddress = (PVOID)USER_SHARED_DATA;
467 Status = MmCreateMemoryArea(ProcessAddressSpace,
468 MEMORY_AREA_SHARED_DATA,
469 &BaseAddress,
470 PAGE_SIZE,
471 PAGE_EXECUTE_READ,
472 &MemoryArea,
473 FALSE,
474 0,
475 BoundaryAddressMultiple);
476 if (!NT_SUCCESS(Status))
477 {
478 DPRINT1("Failed to create Shared User Data\n");
479 goto exit;
480 }
481
482 /* Check if there's a Section Object */
483 if (Section)
484 {
485 UNICODE_STRING FileName;
486 PWCHAR szSrc;
487 PCHAR szDest;
488 USHORT lnFName = 0;
489
490 /* Unlock the Address Space */
491 DPRINT("Unlocking\n");
492 MmUnlockAddressSpace(ProcessAddressSpace);
493
494 DPRINT("Mapping process image. Section: %p, Process: %p, ImageBase: %p\n",
495 Section, Process, &ImageBase);
496 Status = MmMapViewOfSection(Section,
497 Process,
498 (PVOID*)&ImageBase,
499 0,
500 0,
501 NULL,
502 &ViewSize,
503 0,
504 MEM_COMMIT,
505 PAGE_READWRITE);
506 if (!NT_SUCCESS(Status))
507 {
508 DPRINT1("Failed to map process Image\n");
509 return Status;
510 }
511
512 /* Save the pointer */
513 Process->SectionBaseAddress = ImageBase;
514
515 /* Determine the image file name and save it to EPROCESS */
516 DPRINT("Getting Image name\n");
517 FileName = Section->FileObject->FileName;
518 szSrc = (PWCHAR)(FileName.Buffer + (FileName.Length / sizeof(WCHAR)) - 1);
519
520 while(szSrc >= FileName.Buffer)
521 {
522 if(*szSrc == L'\\')
523 {
524 szSrc++;
525 break;
526 }
527 else
528 {
529 szSrc--;
530 lnFName++;
531 }
532 }
533
534 /* Copy the to the process and truncate it to 15 characters if necessary */
535 DPRINT("Copying and truncating\n");
536 szDest = Process->ImageFileName;
537 lnFName = min(lnFName, sizeof(Process->ImageFileName) - 1);
538 while(lnFName-- > 0) *(szDest++) = (UCHAR)*(szSrc++);
539
540 /* Return status to caller */
541 return Status;
542 }
543
544 exit:
545 /* Unlock the Address Space */
546 DPRINT("Unlocking\n");
547 MmUnlockAddressSpace(ProcessAddressSpace);
548
549 /* Return status to caller */
550 return Status;
551 }