[FREELDR] Load the Firmware Errata file specified in the registry. (#1951)
[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 = PeLdrCheckForLoadedDll(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 = PeLdrLoadImage(FullPath, LoaderBootDriver, &DriverBase);
271 if (!Success)
272 return FALSE;
273
274 // Allocate a DTE for it
275 Success = PeLdrAllocateDataTableEntry(LoadOrderListHead, DllName, DllName, DriverBase, DriverDTE);
276 if (!Success)
277 {
278 ERR("PeLdrAllocateDataTableEntry() 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 = PeLdrScanImportDescriptorTable(LoadOrderListHead, FullPath, *DriverDTE);
288 if (!Success)
289 {
290 ERR("PeLdrScanImportDescriptorTable() 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 /* Retrieve its 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 ERR("Could not allocate memory for '%s'\n", ModuleName);
395 ArcClose(FileId);
396 return NULL;
397 }
398
399 /* Load the whole file */
400 Status = ArcRead(FileId, PhysicalBase, FileSize, &BytesRead);
401 ArcClose(FileId);
402 if (Status != ESUCCESS)
403 {
404 WARN("Error while reading '%s', Status: %u\n", ModuleName, Status);
405 return NULL;
406 }
407
408 TRACE("Loaded %s at 0x%x with size 0x%x\n", ModuleName, PhysicalBase, FileSize);
409
410 return PhysicalBase;
411 }
412
413 USHORT
414 WinLdrDetectVersion(VOID)
415 {
416 LONG rc;
417 HKEY hKey;
418
419 rc = RegOpenKey(NULL,
420 L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Control\\Terminal Server",
421 &hKey);
422 if (rc != ERROR_SUCCESS)
423 {
424 /* Key doesn't exist; assume NT 4.0 */
425 return _WIN32_WINNT_NT4;
426 }
427
428 /* We may here want to read the value of ProductVersion */
429 return _WIN32_WINNT_WS03;
430 }
431
432 static
433 BOOLEAN
434 LoadModule(
435 IN OUT PLOADER_PARAMETER_BLOCK LoaderBlock,
436 IN PCCH Path,
437 IN PCCH File,
438 IN PCCH ImportName, // BaseDllName
439 IN TYPE_OF_MEMORY MemoryType,
440 OUT PLDR_DATA_TABLE_ENTRY *Dte,
441 IN ULONG Percentage)
442 {
443 BOOLEAN Success;
444 CHAR FullFileName[MAX_PATH];
445 CHAR ProgressString[256];
446 PVOID BaseAddress = NULL;
447
448 UiDrawBackdrop();
449 RtlStringCbPrintfA(ProgressString, sizeof(ProgressString), "Loading %s...", File);
450 UiDrawProgressBarCenter(Percentage, 100, ProgressString);
451
452 RtlStringCbCopyA(FullFileName, sizeof(FullFileName), Path);
453 RtlStringCbCatA(FullFileName, sizeof(FullFileName), File);
454
455 Success = PeLdrLoadImage(FullFileName, MemoryType, &BaseAddress);
456 if (!Success)
457 {
458 TRACE("Loading %s failed\n", File);
459 return FALSE;
460 }
461 TRACE("%s loaded successfully at %p\n", File, BaseAddress);
462
463 /*
464 * Cheat about the base DLL name if we are loading
465 * the Kernel Debugger Transport DLL, to make the
466 * PE loader happy.
467 */
468 Success = PeLdrAllocateDataTableEntry(&LoaderBlock->LoadOrderListHead,
469 ImportName,
470 FullFileName,
471 BaseAddress,
472 Dte);
473
474 return Success;
475 }
476
477 static
478 BOOLEAN
479 LoadWindowsCore(IN USHORT OperatingSystemVersion,
480 IN OUT PLOADER_PARAMETER_BLOCK LoaderBlock,
481 IN PCSTR BootOptions,
482 IN PCSTR BootPath,
483 IN OUT PLDR_DATA_TABLE_ENTRY* KernelDTE)
484 {
485 BOOLEAN Success;
486 PCSTR Options;
487 CHAR DirPath[MAX_PATH];
488 CHAR HalFileName[MAX_PATH];
489 CHAR KernelFileName[MAX_PATH];
490 CHAR KdTransportDllName[MAX_PATH];
491 PLDR_DATA_TABLE_ENTRY HalDTE, KdComDTE = NULL;
492
493 if (!KernelDTE) return FALSE;
494
495 /* Initialize SystemRoot\System32 path */
496 RtlStringCbCopyA(DirPath, sizeof(DirPath), BootPath);
497 RtlStringCbCatA(DirPath, sizeof(DirPath), "system32\\");
498
499 /*
500 * Default HAL and KERNEL file names.
501 * See the following links to know how the file names are actually chosen:
502 * https://www.geoffchappell.com/notes/windows/boot/bcd/osloader/detecthal.htm
503 * https://www.geoffchappell.com/notes/windows/boot/bcd/osloader/hal.htm
504 * https://www.geoffchappell.com/notes/windows/boot/bcd/osloader/kernel.htm
505 */
506 RtlStringCbCopyA(HalFileName , sizeof(HalFileName) , "hal.dll");
507 RtlStringCbCopyA(KernelFileName, sizeof(KernelFileName), "ntoskrnl.exe");
508
509 /* Find any "/HAL=" or "/KERNEL=" switch in the boot options */
510 Options = BootOptions;
511 while (Options)
512 {
513 /* Skip possible initial whitespace */
514 Options += strspn(Options, " \t");
515
516 /* Check whether a new option starts and it is either HAL or KERNEL */
517 if (*Options != '/' || (++Options,
518 !(_strnicmp(Options, "HAL=", 4) == 0 ||
519 _strnicmp(Options, "KERNEL=", 7) == 0)) )
520 {
521 /* Search for another whitespace */
522 Options = strpbrk(Options, " \t");
523 continue;
524 }
525 else
526 {
527 size_t i = strcspn(Options, " \t"); /* Skip whitespace */
528 if (i == 0)
529 {
530 /* Use the default values */
531 break;
532 }
533
534 /* We have found either HAL or KERNEL options */
535 if (_strnicmp(Options, "HAL=", 4) == 0)
536 {
537 Options += 4; i -= 4;
538 RtlStringCbCopyNA(HalFileName, sizeof(HalFileName), Options, i);
539 _strupr(HalFileName);
540 }
541 else if (_strnicmp(Options, "KERNEL=", 7) == 0)
542 {
543 Options += 7; i -= 7;
544 RtlStringCbCopyNA(KernelFileName, sizeof(KernelFileName), Options, i);
545 _strupr(KernelFileName);
546 }
547 }
548 }
549
550 TRACE("HAL file = '%s' ; Kernel file = '%s'\n", HalFileName, KernelFileName);
551
552 /* Load the Kernel */
553 LoadModule(LoaderBlock, DirPath, KernelFileName, "ntoskrnl.exe", LoaderSystemCode, KernelDTE, 30);
554
555 /* Load the HAL */
556 LoadModule(LoaderBlock, DirPath, HalFileName, "hal.dll", LoaderHalCode, &HalDTE, 45);
557
558 /* Load the Kernel Debugger Transport DLL */
559 if (OperatingSystemVersion > _WIN32_WINNT_WIN2K)
560 {
561 /*
562 * According to http://www.nynaeve.net/?p=173 :
563 * "[...] Another enhancement that could be done Microsoft-side would be
564 * a better interface for replacing KD transport modules. Right now, due
565 * to the fact that ntoskrnl is static linked to KDCOM.DLL, the OS loader
566 * has a hardcoded hack that interprets the KD type in the OS loader options,
567 * loads one of the (hardcoded filenames) "kdcom.dll", "kd1394.dll", or
568 * "kdusb2.dll" modules, and inserts them into the loaded module list under
569 * the name "kdcom.dll". [...]"
570 */
571
572 /*
573 * This loop replaces a dumb call to strstr(..., "DEBUGPORT=").
574 * Indeed I want it to be case-insensitive to allow "debugport="
575 * or "DeBuGpOrT=" or... , and I don't want it to match malformed
576 * command-line options, such as:
577 *
578 * "...foo DEBUGPORT=xxx bar..."
579 * "...foo/DEBUGPORT=xxx bar..."
580 * "...foo/DEBUGPORT=bar..."
581 *
582 * i.e. the "DEBUGPORT=" switch must start with a slash and be separated
583 * from the rest by whitespace, unless it begins the command-line, e.g.:
584 *
585 * "/DEBUGPORT=COM1 foo...bar..."
586 * "...foo /DEBUGPORT=USB bar..."
587 * or:
588 * "...foo /DEBUGPORT= bar..."
589 * (in that case, we default the port to COM).
590 */
591 Options = BootOptions;
592 while (Options)
593 {
594 /* Skip possible initial whitespace */
595 Options += strspn(Options, " \t");
596
597 /* Check whether a new option starts and it is the DEBUGPORT one */
598 if (*Options != '/' || _strnicmp(++Options, "DEBUGPORT=", 10) != 0)
599 {
600 /* Search for another whitespace */
601 Options = strpbrk(Options, " \t");
602 continue;
603 }
604 else
605 {
606 /* We found the DEBUGPORT option. Move to the port name. */
607 Options += 10;
608 break;
609 }
610 }
611
612 if (Options)
613 {
614 /*
615 * We have found the DEBUGPORT option. Parse the port name.
616 * Format: /DEBUGPORT=COM1 or /DEBUGPORT=FILE:\Device\HarddiskX\PartitionY\debug.log or /DEBUGPORT=FOO
617 * If we only have /DEBUGPORT= (i.e. without any port name), defaults it to "COM".
618 */
619 RtlStringCbCopyA(KdTransportDllName, sizeof(KdTransportDllName), "KD");
620 if (_strnicmp(Options, "COM", 3) == 0 && '0' <= Options[3] && Options[3] <= '9')
621 {
622 RtlStringCbCatNA(KdTransportDllName, sizeof(KdTransportDllName), Options, 3);
623 }
624 else
625 {
626 size_t i = strcspn(Options, " \t:"); /* Skip valid separators: whitespace or colon */
627 if (i == 0)
628 RtlStringCbCatA(KdTransportDllName, sizeof(KdTransportDllName), "COM");
629 else
630 RtlStringCbCatNA(KdTransportDllName, sizeof(KdTransportDllName), Options, i);
631 }
632 RtlStringCbCatA(KdTransportDllName, sizeof(KdTransportDllName), ".DLL");
633 _strupr(KdTransportDllName);
634
635 /*
636 * Load the transport DLL. Override the base DLL name of the
637 * loaded transport DLL to the default "KDCOM.DLL" name.
638 */
639 LoadModule(LoaderBlock, DirPath, KdTransportDllName, "kdcom.dll", LoaderSystemCode, &KdComDTE, 60);
640 }
641 }
642
643 /* Load all referenced DLLs for Kernel, HAL and Kernel Debugger Transport DLL */
644 Success = PeLdrScanImportDescriptorTable(&LoaderBlock->LoadOrderListHead, DirPath, *KernelDTE);
645 Success &= PeLdrScanImportDescriptorTable(&LoaderBlock->LoadOrderListHead, DirPath, HalDTE);
646 if (KdComDTE)
647 {
648 Success &= PeLdrScanImportDescriptorTable(&LoaderBlock->LoadOrderListHead, DirPath, KdComDTE);
649 }
650
651 return Success;
652 }
653
654 static
655 BOOLEAN
656 WinLdrInitErrataInf(
657 IN USHORT OperatingSystemVersion,
658 IN PCSTR SystemRoot)
659 {
660 LONG rc;
661 HKEY hKey;
662 ULONG BufferSize;
663 ULONG FileSize;
664 PVOID PhysicalBase;
665 WCHAR szFileName[80];
666 CHAR ErrataFilePath[MAX_PATH];
667
668 /* Open either the 'BiosInfo' (Windows <= 2003) or the 'Errata' (Vista+) key */
669 if (OperatingSystemVersion >= _WIN32_WINNT_VISTA)
670 {
671 rc = RegOpenKey(NULL,
672 L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Control\\Errata",
673 &hKey);
674 }
675 else // (OperatingSystemVersion <= _WIN32_WINNT_WS03)
676 {
677 rc = RegOpenKey(NULL,
678 L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Control\\BiosInfo",
679 &hKey);
680 }
681 if (rc != ERROR_SUCCESS)
682 {
683 WARN("Could not open the BiosInfo/Errata registry key (Error %u)\n", (int)rc);
684 return FALSE;
685 }
686
687 /* Retrieve the INF file name value */
688 BufferSize = sizeof(szFileName);
689 rc = RegQueryValue(hKey, L"InfName", NULL, (PUCHAR)szFileName, &BufferSize);
690 if (rc != ERROR_SUCCESS)
691 {
692 WARN("Could not retrieve the InfName value (Error %u)\n", (int)rc);
693 return FALSE;
694 }
695
696 // TODO: "SystemBiosDate"
697
698 RtlStringCbPrintfA(ErrataFilePath, sizeof(ErrataFilePath), "%s%s%S",
699 SystemRoot, "inf\\", szFileName);
700
701 /* Load the INF file */
702 PhysicalBase = WinLdrLoadModule(ErrataFilePath, &FileSize, LoaderRegistryData);
703 if (!PhysicalBase)
704 {
705 WARN("Could not load '%s'\n", ErrataFilePath);
706 return FALSE;
707 }
708
709 WinLdrSystemBlock->Extension.EmInfFileImage = PaToVa(PhysicalBase);
710 WinLdrSystemBlock->Extension.EmInfFileSize = FileSize;
711
712 return TRUE;
713 }
714
715 ARC_STATUS
716 LoadAndBootWindows(
717 IN ULONG Argc,
718 IN PCHAR Argv[],
719 IN PCHAR Envp[])
720 {
721 ARC_STATUS Status;
722 PCSTR ArgValue;
723 PCSTR SystemPartition;
724 PCHAR File;
725 BOOLEAN Success;
726 USHORT OperatingSystemVersion;
727 PLOADER_PARAMETER_BLOCK LoaderBlock;
728 CHAR BootPath[MAX_PATH];
729 CHAR FileName[MAX_PATH];
730 CHAR BootOptions[256];
731
732 /* Retrieve the (mandatory) boot type */
733 ArgValue = GetArgumentValue(Argc, Argv, "BootType");
734 if (!ArgValue || !*ArgValue)
735 {
736 ERR("No 'BootType' value, aborting!\n");
737 return EINVAL;
738 }
739
740 /* Convert it to an OS version */
741 if (_stricmp(ArgValue, "Windows") == 0 ||
742 _stricmp(ArgValue, "Windows2003") == 0)
743 {
744 OperatingSystemVersion = _WIN32_WINNT_WS03;
745 }
746 else if (_stricmp(ArgValue, "WindowsNT40") == 0)
747 {
748 OperatingSystemVersion = _WIN32_WINNT_NT4;
749 }
750 else
751 {
752 ERR("Unknown 'BootType' value '%s', aborting!\n", ArgValue);
753 return EINVAL;
754 }
755
756 /* Retrieve the (mandatory) system partition */
757 SystemPartition = GetArgumentValue(Argc, Argv, "SystemPartition");
758 if (!SystemPartition || !*SystemPartition)
759 {
760 ERR("No 'SystemPartition' specified, aborting!\n");
761 return EINVAL;
762 }
763
764 UiDrawBackdrop();
765 UiDrawProgressBarCenter(1, 100, "Loading NT...");
766
767 /* Retrieve the system path */
768 *BootPath = ANSI_NULL;
769 ArgValue = GetArgumentValue(Argc, Argv, "SystemPath");
770 if (ArgValue)
771 RtlStringCbCopyA(BootPath, sizeof(BootPath), ArgValue);
772
773 /*
774 * Check whether BootPath is a full path
775 * and if not, create a full boot path.
776 *
777 * See FsOpenFile for the technique used.
778 */
779 if (strrchr(BootPath, ')') == NULL)
780 {
781 /* Temporarily save the boot path */
782 RtlStringCbCopyA(FileName, sizeof(FileName), BootPath);
783
784 /* This is not a full path: prepend the SystemPartition */
785 RtlStringCbCopyA(BootPath, sizeof(BootPath), SystemPartition);
786
787 /* Append a path separator if needed */
788 if (*FileName != '\\' && *FileName != '/')
789 RtlStringCbCatA(BootPath, sizeof(BootPath), "\\");
790
791 /* Append the remaining path */
792 RtlStringCbCatA(BootPath, sizeof(BootPath), FileName);
793 }
794
795 /* Append a path separator if needed */
796 if (!*BootPath || BootPath[strlen(BootPath) - 1] != '\\')
797 RtlStringCbCatA(BootPath, sizeof(BootPath), "\\");
798
799 TRACE("BootPath: '%s'\n", BootPath);
800
801 /* Retrieve the boot options */
802 *BootOptions = ANSI_NULL;
803 ArgValue = GetArgumentValue(Argc, Argv, "Options");
804 if (ArgValue && *ArgValue)
805 RtlStringCbCopyA(BootOptions, sizeof(BootOptions), ArgValue);
806
807 /* Append boot-time options */
808 AppendBootTimeOptions(BootOptions);
809
810 /*
811 * Set "/HAL=" and "/KERNEL=" options if needed.
812 * If already present on the standard "Options=" option line, they take
813 * precedence over those passed via the separate "Hal=" and "Kernel="
814 * options.
815 */
816 if (strstr(BootOptions, "/HAL=") != 0)
817 {
818 /*
819 * Not found in the options, try to retrieve the
820 * separate value and append it to the options.
821 */
822 ArgValue = GetArgumentValue(Argc, Argv, "Hal");
823 if (ArgValue && *ArgValue)
824 {
825 RtlStringCbCatA(BootOptions, sizeof(BootOptions), " /HAL=");
826 RtlStringCbCatA(BootOptions, sizeof(BootOptions), ArgValue);
827 }
828 }
829 if (strstr(BootOptions, "/KERNEL=") != 0)
830 {
831 /*
832 * Not found in the options, try to retrieve the
833 * separate value and append it to the options.
834 */
835 ArgValue = GetArgumentValue(Argc, Argv, "Kernel");
836 if (ArgValue && *ArgValue)
837 {
838 RtlStringCbCatA(BootOptions, sizeof(BootOptions), " /KERNEL=");
839 RtlStringCbCatA(BootOptions, sizeof(BootOptions), ArgValue);
840 }
841 }
842
843 TRACE("BootOptions: '%s'\n", BootOptions);
844
845 /* Check if a ramdisk file was given */
846 File = strstr(BootOptions, "/RDPATH=");
847 if (File)
848 {
849 /* Copy the file name and everything else after it */
850 RtlStringCbCopyA(FileName, sizeof(FileName), File + 8);
851
852 /* Null-terminate */
853 *strstr(FileName, " ") = ANSI_NULL;
854
855 /* Load the ramdisk */
856 Status = RamDiskLoadVirtualFile(FileName, SystemPartition);
857 if (Status != ESUCCESS)
858 {
859 UiMessageBox("Failed to load RAM disk file %s", FileName);
860 return Status;
861 }
862 }
863
864 /* Let user know we started loading */
865 //UiDrawStatusText("Loading...");
866
867 /* Allocate and minimalist-initialize LPB */
868 AllocateAndInitLPB(&LoaderBlock);
869
870 /* Load the system hive */
871 UiDrawBackdrop();
872 UiDrawProgressBarCenter(15, 100, "Loading system hive...");
873 Success = WinLdrInitSystemHive(LoaderBlock, BootPath, FALSE);
874 TRACE("SYSTEM hive %s\n", (Success ? "loaded" : "not loaded"));
875 /* Bail out if failure */
876 if (!Success)
877 return ENOEXEC;
878
879 /* Load NLS data, OEM font, and prepare boot drivers list */
880 Success = WinLdrScanSystemHive(LoaderBlock, BootPath);
881 TRACE("SYSTEM hive %s\n", (Success ? "scanned" : "not scanned"));
882 /* Bail out if failure */
883 if (!Success)
884 return ENOEXEC;
885
886 /* Load the Firmware Errata file */
887 Success = WinLdrInitErrataInf(OperatingSystemVersion, BootPath);
888 TRACE("Firmware Errata file %s\n", (Success ? "loaded" : "not loaded"));
889 /* Not necessarily fatal if not found - carry on going */
890
891 /* Finish loading */
892 return LoadAndBootWindowsCommon(OperatingSystemVersion,
893 LoaderBlock,
894 BootOptions,
895 BootPath,
896 FALSE);
897 }
898
899 ARC_STATUS
900 LoadAndBootWindowsCommon(
901 USHORT OperatingSystemVersion,
902 PLOADER_PARAMETER_BLOCK LoaderBlock,
903 PCSTR BootOptions,
904 PCSTR BootPath,
905 BOOLEAN Setup)
906 {
907 PLOADER_PARAMETER_BLOCK LoaderBlockVA;
908 BOOLEAN Success;
909 PLDR_DATA_TABLE_ENTRY KernelDTE;
910 KERNEL_ENTRY_POINT KiSystemStartup;
911 PCSTR SystemRoot;
912
913 TRACE("LoadAndBootWindowsCommon()\n");
914
915 #ifdef _M_IX86
916 /* Setup redirection support */
917 WinLdrSetupEms((PCHAR)BootOptions);
918 #endif
919
920 /* Convert BootPath to SystemRoot */
921 SystemRoot = strstr(BootPath, "\\");
922
923 /* Detect hardware */
924 UiDrawBackdrop();
925 UiDrawProgressBarCenter(20, 100, "Detecting hardware...");
926 LoaderBlock->ConfigurationRoot = MachHwDetect();
927
928 if (OperatingSystemVersion == 0)
929 OperatingSystemVersion = WinLdrDetectVersion();
930
931 /* Load the operating system core: the Kernel, the HAL and the Kernel Debugger Transport DLL */
932 Success = LoadWindowsCore(OperatingSystemVersion,
933 LoaderBlock,
934 BootOptions,
935 BootPath,
936 &KernelDTE);
937 if (!Success)
938 {
939 UiMessageBox("Error loading NTOS core.");
940 return ENOEXEC;
941 }
942
943 /* Load boot drivers */
944 UiDrawBackdrop();
945 UiDrawProgressBarCenter(100, 100, "Loading boot drivers...");
946 Success = WinLdrLoadBootDrivers(LoaderBlock, BootPath);
947 TRACE("Boot drivers loading %s\n", Success ? "successful" : "failed");
948
949 /* Cleanup ini file */
950 IniCleanup();
951
952 /* Initialize Phase 1 - no drivers loading anymore */
953 WinLdrInitializePhase1(LoaderBlock,
954 BootOptions,
955 SystemRoot,
956 BootPath,
957 OperatingSystemVersion);
958
959 /* Save entry-point pointer and Loader block VAs */
960 KiSystemStartup = (KERNEL_ENTRY_POINT)KernelDTE->EntryPoint;
961 LoaderBlockVA = PaToVa(LoaderBlock);
962
963 /* "Stop all motors", change videomode */
964 MachPrepareForReactOS();
965
966 /* Debugging... */
967 //DumpMemoryAllocMap();
968
969 /* Do the machine specific initialization */
970 WinLdrSetupMachineDependent(LoaderBlock);
971
972 /* Map pages and create memory descriptors */
973 WinLdrSetupMemoryLayout(LoaderBlock);
974
975 /* Set processor context */
976 WinLdrSetProcessorContext();
977
978 /* Save final value of LoaderPagesSpanned */
979 LoaderBlock->Extension->LoaderPagesSpanned = LoaderPagesSpanned;
980
981 TRACE("Hello from paged mode, KiSystemStartup %p, LoaderBlockVA %p!\n",
982 KiSystemStartup, LoaderBlockVA);
983
984 /* Zero KI_USER_SHARED_DATA page */
985 RtlZeroMemory((PVOID)KI_USER_SHARED_DATA, MM_PAGE_SIZE);
986
987 WinLdrpDumpMemoryDescriptors(LoaderBlockVA);
988 WinLdrpDumpBootDriver(LoaderBlockVA);
989 #ifndef _M_AMD64
990 WinLdrpDumpArcDisks(LoaderBlockVA);
991 #endif
992
993 /* Pass control */
994 (*KiSystemStartup)(LoaderBlockVA);
995 return ESUCCESS;
996 }
997
998 VOID
999 WinLdrpDumpMemoryDescriptors(PLOADER_PARAMETER_BLOCK LoaderBlock)
1000 {
1001 PLIST_ENTRY NextMd;
1002 PMEMORY_ALLOCATION_DESCRIPTOR MemoryDescriptor;
1003
1004 NextMd = LoaderBlock->MemoryDescriptorListHead.Flink;
1005
1006 while (NextMd != &LoaderBlock->MemoryDescriptorListHead)
1007 {
1008 MemoryDescriptor = CONTAINING_RECORD(NextMd, MEMORY_ALLOCATION_DESCRIPTOR, ListEntry);
1009
1010 TRACE("BP %08X PC %04X MT %d\n", MemoryDescriptor->BasePage,
1011 MemoryDescriptor->PageCount, MemoryDescriptor->MemoryType);
1012
1013 NextMd = MemoryDescriptor->ListEntry.Flink;
1014 }
1015 }
1016
1017 VOID
1018 WinLdrpDumpBootDriver(PLOADER_PARAMETER_BLOCK LoaderBlock)
1019 {
1020 PLIST_ENTRY NextBd;
1021 PBOOT_DRIVER_LIST_ENTRY BootDriver;
1022
1023 NextBd = LoaderBlock->BootDriverListHead.Flink;
1024
1025 while (NextBd != &LoaderBlock->BootDriverListHead)
1026 {
1027 BootDriver = CONTAINING_RECORD(NextBd, BOOT_DRIVER_LIST_ENTRY, Link);
1028
1029 TRACE("BootDriver %wZ DTE %08X RegPath: %wZ\n", &BootDriver->FilePath,
1030 BootDriver->LdrEntry, &BootDriver->RegistryPath);
1031
1032 NextBd = BootDriver->Link.Flink;
1033 }
1034 }
1035
1036 VOID
1037 WinLdrpDumpArcDisks(PLOADER_PARAMETER_BLOCK LoaderBlock)
1038 {
1039 PLIST_ENTRY NextBd;
1040 PARC_DISK_SIGNATURE ArcDisk;
1041
1042 NextBd = LoaderBlock->ArcDiskInformation->DiskSignatureListHead.Flink;
1043
1044 while (NextBd != &LoaderBlock->ArcDiskInformation->DiskSignatureListHead)
1045 {
1046 ArcDisk = CONTAINING_RECORD(NextBd, ARC_DISK_SIGNATURE, ListEntry);
1047
1048 TRACE("ArcDisk %s checksum: 0x%X, signature: 0x%X\n",
1049 ArcDisk->ArcName, ArcDisk->CheckSum, ArcDisk->Signature);
1050
1051 NextBd = ArcDisk->ListEntry.Flink;
1052 }
1053 }