[FREELDR] Code fixes and enhancements.
[reactos.git] / boot / freeldr / freeldr / ntldr / winldr.c
1 /*
2 * PROJECT: FreeLoader
3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4 * PURPOSE: Windows-compatible NT OS Loader.
5 * COPYRIGHT: Copyright 2006-2019 Aleksey Bragin <aleksey@reactos.org>
6 */
7
8 #include <freeldr.h>
9 #include <ndk/ldrtypes.h>
10 #include "winldr.h"
11 #include "registry.h"
12
13 #include <debug.h>
14 DBG_DEFAULT_CHANNEL(WINDOWS);
15
16 // FIXME: Find a better way to retrieve ARC disk information
17 extern ULONG reactos_disk_count;
18 extern ARC_DISK_SIGNATURE_EX reactos_arc_disk_info[];
19
20 extern ULONG LoaderPagesSpanned;
21 extern BOOLEAN AcpiPresent;
22
23 extern HEADLESS_LOADER_BLOCK LoaderRedirectionInformation;
24 extern BOOLEAN WinLdrTerminalConnected;
25 extern void WinLdrSetupEms(IN PCHAR BootOptions);
26
27 PLOADER_SYSTEM_BLOCK WinLdrSystemBlock;
28
29 // debug stuff
30 VOID DumpMemoryAllocMap(VOID);
31
32 // Init "phase 0"
33 VOID
34 AllocateAndInitLPB(PLOADER_PARAMETER_BLOCK *OutLoaderBlock)
35 {
36 PLOADER_PARAMETER_BLOCK LoaderBlock;
37
38 /* Allocate and zero-init the LPB */
39 WinLdrSystemBlock = MmAllocateMemoryWithType(sizeof(LOADER_SYSTEM_BLOCK),
40 LoaderSystemBlock);
41 if (WinLdrSystemBlock == NULL)
42 {
43 UiMessageBox("Failed to allocate memory for system block!");
44 return;
45 }
46
47 RtlZeroMemory(WinLdrSystemBlock, sizeof(LOADER_SYSTEM_BLOCK));
48
49 LoaderBlock = &WinLdrSystemBlock->LoaderBlock;
50 LoaderBlock->NlsData = &WinLdrSystemBlock->NlsDataBlock;
51
52 /* Init three critical lists, used right away */
53 InitializeListHead(&LoaderBlock->LoadOrderListHead);
54 InitializeListHead(&LoaderBlock->MemoryDescriptorListHead);
55 InitializeListHead(&LoaderBlock->BootDriverListHead);
56
57 *OutLoaderBlock = LoaderBlock;
58 }
59
60 // Init "phase 1"
61 VOID
62 WinLdrInitializePhase1(PLOADER_PARAMETER_BLOCK LoaderBlock,
63 PCSTR Options,
64 PCSTR SystemRoot,
65 PCSTR BootPath,
66 USHORT VersionToBoot)
67 {
68 /*
69 * Examples of correct options and paths:
70 * CHAR Options[] = "/DEBUGPORT=COM1 /BAUDRATE=115200";
71 * CHAR Options[] = "/NODEBUG";
72 * CHAR SystemRoot[] = "\\WINNT\\";
73 * CHAR ArcBoot[] = "multi(0)disk(0)rdisk(0)partition(1)";
74 */
75
76 PSTR LoadOptions, NewLoadOptions;
77 CHAR HalPath[] = "\\";
78 CHAR ArcBoot[MAX_PATH+1];
79 CHAR MiscFiles[MAX_PATH+1];
80 ULONG i;
81 ULONG_PTR PathSeparator;
82 PLOADER_PARAMETER_EXTENSION Extension;
83
84 /* Construct SystemRoot and ArcBoot from SystemPath */
85 PathSeparator = strstr(BootPath, "\\") - BootPath;
86 RtlStringCbCopyNA(ArcBoot, sizeof(ArcBoot), BootPath, PathSeparator);
87
88 TRACE("ArcBoot: '%s'\n", ArcBoot);
89 TRACE("SystemRoot: '%s'\n", SystemRoot);
90 TRACE("Options: '%s'\n", Options);
91
92 /* Fill ARC BootDevice */
93 LoaderBlock->ArcBootDeviceName = WinLdrSystemBlock->ArcBootDeviceName;
94 RtlStringCbCopyA(LoaderBlock->ArcBootDeviceName, sizeof(WinLdrSystemBlock->ArcBootDeviceName), ArcBoot);
95 LoaderBlock->ArcBootDeviceName = PaToVa(LoaderBlock->ArcBootDeviceName);
96
97 //
98 // IMPROVE!!
99 // SetupBlock->ArcSetupDeviceName must be the path to the setup **SOURCE**,
100 // and not the setup boot path. Indeed they may differ!!
101 //
102 /* If we have a setup block, adjust also its ARC path */
103 if (LoaderBlock->SetupLdrBlock)
104 {
105 PSETUP_LOADER_BLOCK SetupBlock = LoaderBlock->SetupLdrBlock;
106
107 /* Matches ArcBoot path */
108 SetupBlock->ArcSetupDeviceName = WinLdrSystemBlock->ArcBootDeviceName;
109 SetupBlock->ArcSetupDeviceName = PaToVa(SetupBlock->ArcSetupDeviceName);
110
111 /* Note: LoaderBlock->SetupLdrBlock is PaToVa'ed at the end of this function */
112 }
113
114 /* Fill ARC HalDevice, it matches ArcBoot path */
115 LoaderBlock->ArcHalDeviceName = WinLdrSystemBlock->ArcBootDeviceName;
116 LoaderBlock->ArcHalDeviceName = PaToVa(LoaderBlock->ArcHalDeviceName);
117
118 /* Fill SystemRoot */
119 LoaderBlock->NtBootPathName = WinLdrSystemBlock->NtBootPathName;
120 RtlStringCbCopyA(LoaderBlock->NtBootPathName, sizeof(WinLdrSystemBlock->NtBootPathName), SystemRoot);
121 LoaderBlock->NtBootPathName = PaToVa(LoaderBlock->NtBootPathName);
122
123 /* Fill NtHalPathName */
124 LoaderBlock->NtHalPathName = WinLdrSystemBlock->NtHalPathName;
125 RtlStringCbCopyA(LoaderBlock->NtHalPathName, sizeof(WinLdrSystemBlock->NtHalPathName), HalPath);
126 LoaderBlock->NtHalPathName = PaToVa(LoaderBlock->NtHalPathName);
127
128 /* Fill LoadOptions and strip the '/' switch symbol in front of each option */
129 NewLoadOptions = LoadOptions = LoaderBlock->LoadOptions = WinLdrSystemBlock->LoadOptions;
130 RtlStringCbCopyA(LoaderBlock->LoadOptions, sizeof(WinLdrSystemBlock->LoadOptions), Options);
131
132 do
133 {
134 while (*LoadOptions == '/')
135 ++LoadOptions;
136
137 *NewLoadOptions++ = *LoadOptions;
138 } while (*LoadOptions++);
139
140 LoaderBlock->LoadOptions = PaToVa(LoaderBlock->LoadOptions);
141
142 /* ARC devices */
143 LoaderBlock->ArcDiskInformation = &WinLdrSystemBlock->ArcDiskInformation;
144 InitializeListHead(&LoaderBlock->ArcDiskInformation->DiskSignatureListHead);
145
146 /* Convert ARC disk information from freeldr to a correct format */
147 for (i = 0; i < reactos_disk_count; i++)
148 {
149 PARC_DISK_SIGNATURE_EX ArcDiskSig;
150
151 /* Allocate the ARC structure */
152 ArcDiskSig = FrLdrHeapAlloc(sizeof(ARC_DISK_SIGNATURE_EX), 'giSD');
153
154 /* Copy the data over */
155 RtlCopyMemory(ArcDiskSig, &reactos_arc_disk_info[i], sizeof(ARC_DISK_SIGNATURE_EX));
156
157 /* Set the ARC Name pointer */
158 ArcDiskSig->DiskSignature.ArcName = PaToVa(ArcDiskSig->ArcName);
159
160 /* Insert into the list */
161 InsertTailList(&LoaderBlock->ArcDiskInformation->DiskSignatureListHead,
162 &ArcDiskSig->DiskSignature.ListEntry);
163 }
164
165 /* Convert all list's to Virtual address */
166
167 /* Convert the ArcDisks list to virtual address */
168 List_PaToVa(&LoaderBlock->ArcDiskInformation->DiskSignatureListHead);
169 LoaderBlock->ArcDiskInformation = PaToVa(LoaderBlock->ArcDiskInformation);
170
171 /* Convert configuration entries to VA */
172 ConvertConfigToVA(LoaderBlock->ConfigurationRoot);
173 LoaderBlock->ConfigurationRoot = PaToVa(LoaderBlock->ConfigurationRoot);
174
175 /* Convert all DTE into virtual addresses */
176 List_PaToVa(&LoaderBlock->LoadOrderListHead);
177
178 /* This one will be converted right before switching to virtual paging mode */
179 //List_PaToVa(&LoaderBlock->MemoryDescriptorListHead);
180
181 /* Convert list of boot drivers */
182 List_PaToVa(&LoaderBlock->BootDriverListHead);
183
184 /* Initialize Extension now */
185 Extension = &WinLdrSystemBlock->Extension;
186 Extension->Size = sizeof(LOADER_PARAMETER_EXTENSION);
187 Extension->MajorVersion = (VersionToBoot & 0xFF00) >> 8;
188 Extension->MinorVersion = VersionToBoot & 0xFF;
189 Extension->Profile.Status = 2;
190
191 /* Check if FreeLdr detected a ACPI table */
192 if (AcpiPresent)
193 {
194 /* Set the pointer to something for compatibility */
195 Extension->AcpiTable = (PVOID)1;
196 // FIXME: Extension->AcpiTableSize;
197 }
198
199 #ifdef _M_IX86
200 /* Set headless block pointer */
201 if (WinLdrTerminalConnected)
202 {
203 Extension->HeadlessLoaderBlock = &WinLdrSystemBlock->HeadlessLoaderBlock;
204 RtlCopyMemory(Extension->HeadlessLoaderBlock,
205 &LoaderRedirectionInformation,
206 sizeof(HEADLESS_LOADER_BLOCK));
207 Extension->HeadlessLoaderBlock = PaToVa(Extension->HeadlessLoaderBlock);
208 }
209 #endif
210 /* Load drivers database */
211 RtlStringCbCopyA(MiscFiles, sizeof(MiscFiles), BootPath);
212 RtlStringCbCatA(MiscFiles, sizeof(MiscFiles), "AppPatch\\drvmain.sdb");
213 Extension->DrvDBImage = PaToVa(WinLdrLoadModule(MiscFiles,
214 &Extension->DrvDBSize,
215 LoaderRegistryData));
216
217 /* Convert extension and setup block pointers */
218 LoaderBlock->Extension = PaToVa(Extension);
219
220 if (LoaderBlock->SetupLdrBlock)
221 LoaderBlock->SetupLdrBlock = PaToVa(LoaderBlock->SetupLdrBlock);
222
223 TRACE("WinLdrInitializePhase1() completed\n");
224 }
225
226 static BOOLEAN
227 WinLdrLoadDeviceDriver(PLIST_ENTRY LoadOrderListHead,
228 PCSTR BootPath,
229 PUNICODE_STRING FilePath,
230 ULONG Flags,
231 PLDR_DATA_TABLE_ENTRY *DriverDTE)
232 {
233 CHAR FullPath[1024];
234 CHAR DriverPath[1024];
235 CHAR DllName[1024];
236 PCHAR DriverNamePos;
237 BOOLEAN Success;
238 PVOID DriverBase = NULL;
239
240 // Separate the path to file name and directory path
241 RtlStringCbPrintfA(DriverPath, sizeof(DriverPath), "%wZ", FilePath);
242 DriverNamePos = strrchr(DriverPath, '\\');
243 if (DriverNamePos != NULL)
244 {
245 // Copy the name
246 RtlStringCbCopyA(DllName, sizeof(DllName), DriverNamePos+1);
247
248 // Cut out the name from the path
249 *(DriverNamePos+1) = ANSI_NULL;
250 }
251 else
252 {
253 // There is no directory in the path
254 RtlStringCbCopyA(DllName, sizeof(DllName), DriverPath);
255 *DriverPath = ANSI_NULL;
256 }
257
258 TRACE("DriverPath: '%s', DllName: '%s', LPB\n", DriverPath, DllName);
259
260 // Check if driver is already loaded
261 Success = WinLdrCheckForLoadedDll(LoadOrderListHead, DllName, DriverDTE);
262 if (Success)
263 {
264 // We've got the pointer to its DTE, just return success
265 return TRUE;
266 }
267
268 // It's not loaded, we have to load it
269 RtlStringCbPrintfA(FullPath, sizeof(FullPath), "%s%wZ", BootPath, FilePath);
270 Success = WinLdrLoadImage(FullPath, LoaderBootDriver, &DriverBase);
271 if (!Success)
272 return FALSE;
273
274 // Allocate a DTE for it
275 Success = WinLdrAllocateDataTableEntry(LoadOrderListHead, DllName, DllName, DriverBase, DriverDTE);
276 if (!Success)
277 {
278 ERR("WinLdrAllocateDataTableEntry() failed\n");
279 return FALSE;
280 }
281
282 // Modify any flags, if needed
283 (*DriverDTE)->Flags |= Flags;
284
285 // Look for any dependencies it may have, and load them too
286 RtlStringCbPrintfA(FullPath, sizeof(FullPath), "%s%s", BootPath, DriverPath);
287 Success = WinLdrScanImportDescriptorTable(LoadOrderListHead, FullPath, *DriverDTE);
288 if (!Success)
289 {
290 ERR("WinLdrScanImportDescriptorTable() failed for %s\n", FullPath);
291 return FALSE;
292 }
293
294 return TRUE;
295 }
296
297 BOOLEAN
298 WinLdrLoadBootDrivers(PLOADER_PARAMETER_BLOCK LoaderBlock,
299 PCSTR BootPath)
300 {
301 PLIST_ENTRY NextBd;
302 PBOOT_DRIVER_LIST_ENTRY BootDriver;
303 BOOLEAN Success;
304 BOOLEAN ret = TRUE;
305
306 // Walk through the boot drivers list
307 NextBd = LoaderBlock->BootDriverListHead.Flink;
308
309 while (NextBd != &LoaderBlock->BootDriverListHead)
310 {
311 BootDriver = CONTAINING_RECORD(NextBd, BOOT_DRIVER_LIST_ENTRY, Link);
312
313 TRACE("BootDriver %wZ DTE %08X RegPath: %wZ\n", &BootDriver->FilePath,
314 BootDriver->LdrEntry, &BootDriver->RegistryPath);
315
316 // Paths are relative (FIXME: Are they always relative?)
317
318 // Load it
319 Success = WinLdrLoadDeviceDriver(&LoaderBlock->LoadOrderListHead,
320 BootPath,
321 &BootDriver->FilePath,
322 0,
323 &BootDriver->LdrEntry);
324
325 if (Success)
326 {
327 // Convert the RegistryPath and DTE addresses to VA since we are not going to use it anymore
328 BootDriver->RegistryPath.Buffer = PaToVa(BootDriver->RegistryPath.Buffer);
329 BootDriver->FilePath.Buffer = PaToVa(BootDriver->FilePath.Buffer);
330 BootDriver->LdrEntry = PaToVa(BootDriver->LdrEntry);
331 }
332 else
333 {
334 // Loading failed - cry loudly
335 ERR("Can't load boot driver '%wZ'!\n", &BootDriver->FilePath);
336 UiMessageBox("Can't load boot driver '%wZ'!", &BootDriver->FilePath);
337 ret = FALSE;
338
339 // Remove it from the list and try to continue
340 RemoveEntryList(NextBd);
341 }
342
343 NextBd = BootDriver->Link.Flink;
344 }
345
346 return ret;
347 }
348
349 PVOID
350 WinLdrLoadModule(PCSTR ModuleName,
351 PULONG Size,
352 TYPE_OF_MEMORY MemoryType)
353 {
354 ULONG FileId;
355 PVOID PhysicalBase;
356 FILEINFORMATION FileInfo;
357 ULONG FileSize;
358 ARC_STATUS Status;
359 ULONG BytesRead;
360
361 //CHAR ProgressString[256];
362
363 /* Inform user we are loading files */
364 //UiDrawBackdrop();
365 //RtlStringCbPrintfA(ProgressString, sizeof(ProgressString), "Loading %s...", FileName);
366 //UiDrawProgressBarCenter(1, 100, ProgressString);
367
368 TRACE("Loading module %s\n", ModuleName);
369 *Size = 0;
370
371 /* Open the image file */
372 Status = ArcOpen((PSTR)ModuleName, OpenReadOnly, &FileId);
373 if (Status != ESUCCESS)
374 {
375 /* In case of errors, we just return, without complaining to the user */
376 WARN("Error while opening '%s', Status: %u\n", ModuleName, Status);
377 return NULL;
378 }
379
380 /* Get this file's size */
381 Status = ArcGetFileInformation(FileId, &FileInfo);
382 if (Status != ESUCCESS)
383 {
384 ArcClose(FileId);
385 return NULL;
386 }
387 FileSize = FileInfo.EndingAddress.LowPart;
388 *Size = FileSize;
389
390 /* Allocate memory */
391 PhysicalBase = MmAllocateMemoryWithType(FileSize, MemoryType);
392 if (PhysicalBase == NULL)
393 {
394 ArcClose(FileId);
395 return NULL;
396 }
397
398 /* Load whole file */
399 Status = ArcRead(FileId, PhysicalBase, FileSize, &BytesRead);
400 ArcClose(FileId);
401 if (Status != ESUCCESS)
402 {
403 WARN("Error while reading '%s', Status: %u\n", ModuleName, Status);
404 return NULL;
405 }
406
407 TRACE("Loaded %s at 0x%x with size 0x%x\n", ModuleName, PhysicalBase, FileSize);
408
409 return PhysicalBase;
410 }
411
412 USHORT
413 WinLdrDetectVersion(VOID)
414 {
415 LONG rc;
416 HKEY hKey;
417
418 rc = RegOpenKey(NULL,
419 L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Control\\Terminal Server",
420 &hKey);
421 if (rc != ERROR_SUCCESS)
422 {
423 /* Key doesn't exist; assume NT 4.0 */
424 return _WIN32_WINNT_NT4;
425 }
426
427 /* We may here want to read the value of ProductVersion */
428 return _WIN32_WINNT_WS03;
429 }
430
431 static
432 BOOLEAN
433 LoadModule(
434 IN OUT PLOADER_PARAMETER_BLOCK LoaderBlock,
435 IN PCCH Path,
436 IN PCCH File,
437 IN PCCH ImportName, // BaseDllName
438 IN TYPE_OF_MEMORY MemoryType,
439 OUT PLDR_DATA_TABLE_ENTRY *Dte,
440 IN ULONG Percentage)
441 {
442 BOOLEAN Success;
443 CHAR FullFileName[MAX_PATH];
444 CHAR ProgressString[256];
445 PVOID BaseAddress = NULL;
446
447 UiDrawBackdrop();
448 RtlStringCbPrintfA(ProgressString, sizeof(ProgressString), "Loading %s...", File);
449 UiDrawProgressBarCenter(Percentage, 100, ProgressString);
450
451 RtlStringCbCopyA(FullFileName, sizeof(FullFileName), Path);
452 RtlStringCbCatA(FullFileName, sizeof(FullFileName), File);
453
454 Success = WinLdrLoadImage(FullFileName, MemoryType, &BaseAddress);
455 if (!Success)
456 {
457 TRACE("Loading %s failed\n", File);
458 return FALSE;
459 }
460 TRACE("%s loaded successfully at %p\n", File, BaseAddress);
461
462 /*
463 * Cheat about the base DLL name if we are loading
464 * the Kernel Debugger Transport DLL, to make the
465 * PE loader happy.
466 */
467 Success = WinLdrAllocateDataTableEntry(&LoaderBlock->LoadOrderListHead,
468 ImportName,
469 FullFileName,
470 BaseAddress,
471 Dte);
472
473 return Success;
474 }
475
476 static
477 BOOLEAN
478 LoadWindowsCore(IN USHORT OperatingSystemVersion,
479 IN OUT PLOADER_PARAMETER_BLOCK LoaderBlock,
480 IN PCSTR BootOptions,
481 IN PCSTR BootPath,
482 IN OUT PLDR_DATA_TABLE_ENTRY* KernelDTE)
483 {
484 BOOLEAN Success;
485 PCSTR Options;
486 CHAR DirPath[MAX_PATH];
487 CHAR HalFileName[MAX_PATH];
488 CHAR KernelFileName[MAX_PATH];
489 CHAR KdTransportDllName[MAX_PATH];
490 PLDR_DATA_TABLE_ENTRY HalDTE, KdComDTE = NULL;
491
492 if (!KernelDTE) return FALSE;
493
494 /* Initialize SystemRoot\System32 path */
495 RtlStringCbCopyA(DirPath, sizeof(DirPath), BootPath);
496 RtlStringCbCatA(DirPath, sizeof(DirPath), "system32\\");
497
498 /*
499 * Default HAL and KERNEL file names.
500 * See the following links to know how the file names are actually chosen:
501 * https://www.geoffchappell.com/notes/windows/boot/bcd/osloader/detecthal.htm
502 * https://www.geoffchappell.com/notes/windows/boot/bcd/osloader/hal.htm
503 * https://www.geoffchappell.com/notes/windows/boot/bcd/osloader/kernel.htm
504 */
505 RtlStringCbCopyA(HalFileName , sizeof(HalFileName) , "hal.dll");
506 RtlStringCbCopyA(KernelFileName, sizeof(KernelFileName), "ntoskrnl.exe");
507
508 /* Find any "/HAL=" or "/KERNEL=" switch in the boot options */
509 Options = BootOptions;
510 while (Options)
511 {
512 /* Skip possible initial whitespace */
513 Options += strspn(Options, " \t");
514
515 /* Check whether a new option starts and it is either HAL or KERNEL */
516 if (*Options != '/' || (++Options,
517 !(_strnicmp(Options, "HAL=", 4) == 0 ||
518 _strnicmp(Options, "KERNEL=", 7) == 0)) )
519 {
520 /* Search for another whitespace */
521 Options = strpbrk(Options, " \t");
522 continue;
523 }
524 else
525 {
526 size_t i = strcspn(Options, " \t"); /* Skip whitespace */
527 if (i == 0)
528 {
529 /* Use the default values */
530 break;
531 }
532
533 /* We have found either HAL or KERNEL options */
534 if (_strnicmp(Options, "HAL=", 4) == 0)
535 {
536 Options += 4; i -= 4;
537 RtlStringCbCopyNA(HalFileName, sizeof(HalFileName), Options, i);
538 _strupr(HalFileName);
539 }
540 else if (_strnicmp(Options, "KERNEL=", 7) == 0)
541 {
542 Options += 7; i -= 7;
543 RtlStringCbCopyNA(KernelFileName, sizeof(KernelFileName), Options, i);
544 _strupr(KernelFileName);
545 }
546 }
547 }
548
549 TRACE("HAL file = '%s' ; Kernel file = '%s'\n", HalFileName, KernelFileName);
550
551 /* Load the Kernel */
552 LoadModule(LoaderBlock, DirPath, KernelFileName, "ntoskrnl.exe", LoaderSystemCode, KernelDTE, 30);
553
554 /* Load the HAL */
555 LoadModule(LoaderBlock, DirPath, HalFileName, "hal.dll", LoaderHalCode, &HalDTE, 45);
556
557 /* Load the Kernel Debugger Transport DLL */
558 if (OperatingSystemVersion > _WIN32_WINNT_WIN2K)
559 {
560 /*
561 * According to http://www.nynaeve.net/?p=173 :
562 * "[...] Another enhancement that could be done Microsoft-side would be
563 * a better interface for replacing KD transport modules. Right now, due
564 * to the fact that ntoskrnl is static linked to KDCOM.DLL, the OS loader
565 * has a hardcoded hack that interprets the KD type in the OS loader options,
566 * loads one of the (hardcoded filenames) "kdcom.dll", "kd1394.dll", or
567 * "kdusb2.dll" modules, and inserts them into the loaded module list under
568 * the name "kdcom.dll". [...]"
569 */
570
571 /*
572 * This loop replaces a dumb call to strstr(..., "DEBUGPORT=").
573 * Indeed I want it to be case-insensitive to allow "debugport="
574 * or "DeBuGpOrT=" or... , and I don't want it to match malformed
575 * command-line options, such as:
576 *
577 * "...foo DEBUGPORT=xxx bar..."
578 * "...foo/DEBUGPORT=xxx bar..."
579 * "...foo/DEBUGPORT=bar..."
580 *
581 * i.e. the "DEBUGPORT=" switch must start with a slash and be separated
582 * from the rest by whitespace, unless it begins the command-line, e.g.:
583 *
584 * "/DEBUGPORT=COM1 foo...bar..."
585 * "...foo /DEBUGPORT=USB bar..."
586 * or:
587 * "...foo /DEBUGPORT= bar..."
588 * (in that case, we default the port to COM).
589 */
590 Options = BootOptions;
591 while (Options)
592 {
593 /* Skip possible initial whitespace */
594 Options += strspn(Options, " \t");
595
596 /* Check whether a new option starts and it is the DEBUGPORT one */
597 if (*Options != '/' || _strnicmp(++Options, "DEBUGPORT=", 10) != 0)
598 {
599 /* Search for another whitespace */
600 Options = strpbrk(Options, " \t");
601 continue;
602 }
603 else
604 {
605 /* We found the DEBUGPORT option. Move to the port name. */
606 Options += 10;
607 break;
608 }
609 }
610
611 if (Options)
612 {
613 /*
614 * We have found the DEBUGPORT option. Parse the port name.
615 * Format: /DEBUGPORT=COM1 or /DEBUGPORT=FILE:\Device\HarddiskX\PartitionY\debug.log or /DEBUGPORT=FOO
616 * If we only have /DEBUGPORT= (i.e. without any port name), defaults it to "COM".
617 */
618 RtlStringCbCopyA(KdTransportDllName, sizeof(KdTransportDllName), "KD");
619 if (_strnicmp(Options, "COM", 3) == 0 && '0' <= Options[3] && Options[3] <= '9')
620 {
621 RtlStringCbCatNA(KdTransportDllName, sizeof(KdTransportDllName), Options, 3);
622 }
623 else
624 {
625 size_t i = strcspn(Options, " \t:"); /* Skip valid separators: whitespace or colon */
626 if (i == 0)
627 RtlStringCbCatA(KdTransportDllName, sizeof(KdTransportDllName), "COM");
628 else
629 RtlStringCbCatNA(KdTransportDllName, sizeof(KdTransportDllName), Options, i);
630 }
631 RtlStringCbCatA(KdTransportDllName, sizeof(KdTransportDllName), ".DLL");
632 _strupr(KdTransportDllName);
633
634 /*
635 * Load the transport DLL. Override the base DLL name of the
636 * loaded transport DLL to the default "KDCOM.DLL" name.
637 */
638 LoadModule(LoaderBlock, DirPath, KdTransportDllName, "kdcom.dll", LoaderSystemCode, &KdComDTE, 60);
639 }
640 }
641
642 /* Load all referenced DLLs for Kernel, HAL and Kernel Debugger Transport DLL */
643 Success = WinLdrScanImportDescriptorTable(&LoaderBlock->LoadOrderListHead, DirPath, *KernelDTE);
644 Success &= WinLdrScanImportDescriptorTable(&LoaderBlock->LoadOrderListHead, DirPath, HalDTE);
645 if (KdComDTE)
646 {
647 Success &= WinLdrScanImportDescriptorTable(&LoaderBlock->LoadOrderListHead, DirPath, KdComDTE);
648 }
649
650 return Success;
651 }
652
653 ARC_STATUS
654 LoadAndBootWindows(
655 IN ULONG Argc,
656 IN PCHAR Argv[],
657 IN PCHAR Envp[])
658 {
659 ARC_STATUS Status;
660 PCSTR ArgValue;
661 PCSTR SystemPartition;
662 PCHAR File;
663 BOOLEAN Success;
664 USHORT OperatingSystemVersion;
665 PLOADER_PARAMETER_BLOCK LoaderBlock;
666 CHAR BootPath[MAX_PATH];
667 CHAR FileName[MAX_PATH];
668 CHAR BootOptions[256];
669
670 /* Retrieve the (mandatory) boot type */
671 ArgValue = GetArgumentValue(Argc, Argv, "BootType");
672 if (!ArgValue || !*ArgValue)
673 {
674 ERR("No 'BootType' value, aborting!\n");
675 return EINVAL;
676 }
677
678 /* Convert it to an OS version */
679 if (_stricmp(ArgValue, "Windows") == 0 ||
680 _stricmp(ArgValue, "Windows2003") == 0)
681 {
682 OperatingSystemVersion = _WIN32_WINNT_WS03;
683 }
684 else if (_stricmp(ArgValue, "WindowsNT40") == 0)
685 {
686 OperatingSystemVersion = _WIN32_WINNT_NT4;
687 }
688 else
689 {
690 ERR("Unknown 'BootType' value '%s', aborting!\n", ArgValue);
691 return EINVAL;
692 }
693
694 /* Retrieve the (mandatory) system partition */
695 SystemPartition = GetArgumentValue(Argc, Argv, "SystemPartition");
696 if (!SystemPartition || !*SystemPartition)
697 {
698 ERR("No 'SystemPartition' specified, aborting!\n");
699 return EINVAL;
700 }
701
702 UiDrawBackdrop();
703 UiDrawProgressBarCenter(1, 100, "Loading NT...");
704
705 /* Retrieve the system path */
706 *BootPath = ANSI_NULL;
707 ArgValue = GetArgumentValue(Argc, Argv, "SystemPath");
708 if (ArgValue)
709 RtlStringCbCopyA(BootPath, sizeof(BootPath), ArgValue);
710
711 /*
712 * Check whether BootPath is a full path
713 * and if not, create a full boot path.
714 *
715 * See FsOpenFile for the technique used.
716 */
717 if (strrchr(BootPath, ')') == NULL)
718 {
719 /* Temporarily save the boot path */
720 RtlStringCbCopyA(FileName, sizeof(FileName), BootPath);
721
722 /* This is not a full path: prepend the SystemPartition */
723 RtlStringCbCopyA(BootPath, sizeof(BootPath), SystemPartition);
724
725 /* Append a path separator if needed */
726 if (*FileName != '\\' && *FileName != '/')
727 RtlStringCbCatA(BootPath, sizeof(BootPath), "\\");
728
729 /* Append the remaining path */
730 RtlStringCbCatA(BootPath, sizeof(BootPath), FileName);
731 }
732
733 /* Append a path separator if needed */
734 if (!*BootPath || BootPath[strlen(BootPath) - 1] != '\\')
735 RtlStringCbCatA(BootPath, sizeof(BootPath), "\\");
736
737 TRACE("BootPath: '%s'\n", BootPath);
738
739 /* Retrieve the boot options */
740 *BootOptions = ANSI_NULL;
741 ArgValue = GetArgumentValue(Argc, Argv, "Options");
742 if (ArgValue && *ArgValue)
743 RtlStringCbCopyA(BootOptions, sizeof(BootOptions), ArgValue);
744
745 /* Append boot-time options */
746 AppendBootTimeOptions(BootOptions);
747
748 /*
749 * Set "/HAL=" and "/KERNEL=" options if needed.
750 * If already present on the standard "Options=" option line, they take
751 * precedence over those passed via the separate "Hal=" and "Kernel="
752 * options.
753 */
754 if (strstr(BootOptions, "/HAL=") != 0)
755 {
756 /*
757 * Not found in the options, try to retrieve the
758 * separate value and append it to the options.
759 */
760 ArgValue = GetArgumentValue(Argc, Argv, "Hal");
761 if (ArgValue && *ArgValue)
762 {
763 RtlStringCbCatA(BootOptions, sizeof(BootOptions), " /HAL=");
764 RtlStringCbCatA(BootOptions, sizeof(BootOptions), ArgValue);
765 }
766 }
767 if (strstr(BootOptions, "/KERNEL=") != 0)
768 {
769 /*
770 * Not found in the options, try to retrieve the
771 * separate value and append it to the options.
772 */
773 ArgValue = GetArgumentValue(Argc, Argv, "Kernel");
774 if (ArgValue && *ArgValue)
775 {
776 RtlStringCbCatA(BootOptions, sizeof(BootOptions), " /KERNEL=");
777 RtlStringCbCatA(BootOptions, sizeof(BootOptions), ArgValue);
778 }
779 }
780
781 TRACE("BootOptions: '%s'\n", BootOptions);
782
783 /* Check if a ramdisk file was given */
784 File = strstr(BootOptions, "/RDPATH=");
785 if (File)
786 {
787 /* Copy the file name and everything else after it */
788 RtlStringCbCopyA(FileName, sizeof(FileName), File + 8);
789
790 /* Null-terminate */
791 *strstr(FileName, " ") = ANSI_NULL;
792
793 /* Load the ramdisk */
794 Status = RamDiskLoadVirtualFile(FileName, SystemPartition);
795 if (Status != ESUCCESS)
796 {
797 UiMessageBox("Failed to load RAM disk file %s", FileName);
798 return Status;
799 }
800 }
801
802 /* Let user know we started loading */
803 //UiDrawStatusText("Loading...");
804
805 /* Allocate and minimalist-initialize LPB */
806 AllocateAndInitLPB(&LoaderBlock);
807
808 /* Load the system hive */
809 UiDrawBackdrop();
810 UiDrawProgressBarCenter(15, 100, "Loading system hive...");
811 Success = WinLdrInitSystemHive(LoaderBlock, BootPath, FALSE);
812 TRACE("SYSTEM hive %s\n", (Success ? "loaded" : "not loaded"));
813 /* Bail out if failure */
814 if (!Success)
815 return ENOEXEC;
816
817 /* Load NLS data, OEM font, and prepare boot drivers list */
818 Success = WinLdrScanSystemHive(LoaderBlock, BootPath);
819 TRACE("SYSTEM hive %s\n", (Success ? "scanned" : "not scanned"));
820 /* Bail out if failure */
821 if (!Success)
822 return ENOEXEC;
823
824 /* Finish loading */
825 return LoadAndBootWindowsCommon(OperatingSystemVersion,
826 LoaderBlock,
827 BootOptions,
828 BootPath,
829 FALSE);
830 }
831
832 ARC_STATUS
833 LoadAndBootWindowsCommon(
834 USHORT OperatingSystemVersion,
835 PLOADER_PARAMETER_BLOCK LoaderBlock,
836 PCSTR BootOptions,
837 PCSTR BootPath,
838 BOOLEAN Setup)
839 {
840 PLOADER_PARAMETER_BLOCK LoaderBlockVA;
841 BOOLEAN Success;
842 PLDR_DATA_TABLE_ENTRY KernelDTE;
843 KERNEL_ENTRY_POINT KiSystemStartup;
844 PCSTR SystemRoot;
845
846 TRACE("LoadAndBootWindowsCommon()\n");
847
848 #ifdef _M_IX86
849 /* Setup redirection support */
850 WinLdrSetupEms((PCHAR)BootOptions);
851 #endif
852
853 /* Convert BootPath to SystemRoot */
854 SystemRoot = strstr(BootPath, "\\");
855
856 /* Detect hardware */
857 UiDrawBackdrop();
858 UiDrawProgressBarCenter(20, 100, "Detecting hardware...");
859 LoaderBlock->ConfigurationRoot = MachHwDetect();
860
861 if (OperatingSystemVersion == 0)
862 OperatingSystemVersion = WinLdrDetectVersion();
863
864 /* Load the operating system core: the Kernel, the HAL and the Kernel Debugger Transport DLL */
865 Success = LoadWindowsCore(OperatingSystemVersion,
866 LoaderBlock,
867 BootOptions,
868 BootPath,
869 &KernelDTE);
870 if (!Success)
871 {
872 UiMessageBox("Error loading NTOS core.");
873 return ENOEXEC;
874 }
875
876 /* Load boot drivers */
877 UiDrawBackdrop();
878 UiDrawProgressBarCenter(100, 100, "Loading boot drivers...");
879 Success = WinLdrLoadBootDrivers(LoaderBlock, BootPath);
880 TRACE("Boot drivers loading %s\n", Success ? "successful" : "failed");
881
882 /* Cleanup ini file */
883 IniCleanup();
884
885 /* Initialize Phase 1 - no drivers loading anymore */
886 WinLdrInitializePhase1(LoaderBlock,
887 BootOptions,
888 SystemRoot,
889 BootPath,
890 OperatingSystemVersion);
891
892 /* Save entry-point pointer and Loader block VAs */
893 KiSystemStartup = (KERNEL_ENTRY_POINT)KernelDTE->EntryPoint;
894 LoaderBlockVA = PaToVa(LoaderBlock);
895
896 /* "Stop all motors", change videomode */
897 MachPrepareForReactOS();
898
899 /* Debugging... */
900 //DumpMemoryAllocMap();
901
902 /* Do the machine specific initialization */
903 WinLdrSetupMachineDependent(LoaderBlock);
904
905 /* Map pages and create memory descriptors */
906 WinLdrSetupMemoryLayout(LoaderBlock);
907
908 /* Set processor context */
909 WinLdrSetProcessorContext();
910
911 /* Save final value of LoaderPagesSpanned */
912 LoaderBlock->Extension->LoaderPagesSpanned = LoaderPagesSpanned;
913
914 TRACE("Hello from paged mode, KiSystemStartup %p, LoaderBlockVA %p!\n",
915 KiSystemStartup, LoaderBlockVA);
916
917 // Zero KI_USER_SHARED_DATA page
918 memset((PVOID)KI_USER_SHARED_DATA, 0, MM_PAGE_SIZE);
919
920 WinLdrpDumpMemoryDescriptors(LoaderBlockVA);
921 WinLdrpDumpBootDriver(LoaderBlockVA);
922 #ifndef _M_AMD64
923 WinLdrpDumpArcDisks(LoaderBlockVA);
924 #endif
925
926 /* Pass control */
927 (*KiSystemStartup)(LoaderBlockVA);
928 return ESUCCESS;
929 }
930
931 VOID
932 WinLdrpDumpMemoryDescriptors(PLOADER_PARAMETER_BLOCK LoaderBlock)
933 {
934 PLIST_ENTRY NextMd;
935 PMEMORY_ALLOCATION_DESCRIPTOR MemoryDescriptor;
936
937 NextMd = LoaderBlock->MemoryDescriptorListHead.Flink;
938
939 while (NextMd != &LoaderBlock->MemoryDescriptorListHead)
940 {
941 MemoryDescriptor = CONTAINING_RECORD(NextMd, MEMORY_ALLOCATION_DESCRIPTOR, ListEntry);
942
943 TRACE("BP %08X PC %04X MT %d\n", MemoryDescriptor->BasePage,
944 MemoryDescriptor->PageCount, MemoryDescriptor->MemoryType);
945
946 NextMd = MemoryDescriptor->ListEntry.Flink;
947 }
948 }
949
950 VOID
951 WinLdrpDumpBootDriver(PLOADER_PARAMETER_BLOCK LoaderBlock)
952 {
953 PLIST_ENTRY NextBd;
954 PBOOT_DRIVER_LIST_ENTRY BootDriver;
955
956 NextBd = LoaderBlock->BootDriverListHead.Flink;
957
958 while (NextBd != &LoaderBlock->BootDriverListHead)
959 {
960 BootDriver = CONTAINING_RECORD(NextBd, BOOT_DRIVER_LIST_ENTRY, Link);
961
962 TRACE("BootDriver %wZ DTE %08X RegPath: %wZ\n", &BootDriver->FilePath,
963 BootDriver->LdrEntry, &BootDriver->RegistryPath);
964
965 NextBd = BootDriver->Link.Flink;
966 }
967 }
968
969 VOID
970 WinLdrpDumpArcDisks(PLOADER_PARAMETER_BLOCK LoaderBlock)
971 {
972 PLIST_ENTRY NextBd;
973 PARC_DISK_SIGNATURE ArcDisk;
974
975 NextBd = LoaderBlock->ArcDiskInformation->DiskSignatureListHead.Flink;
976
977 while (NextBd != &LoaderBlock->ArcDiskInformation->DiskSignatureListHead)
978 {
979 ArcDisk = CONTAINING_RECORD(NextBd, ARC_DISK_SIGNATURE, ListEntry);
980
981 TRACE("ArcDisk %s checksum: 0x%X, signature: 0x%X\n",
982 ArcDisk->ArcName, ArcDisk->CheckSum, ArcDisk->Signature);
983
984 NextBd = ArcDisk->ListEntry.Flink;
985 }
986 }