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