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