xref: /reactos/boot/freeldr/freeldr/ntldr/winldr.c (revision c2c66aff)
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 "registry.h"
24 
25 #include <ndk/ldrtypes.h>
26 
27 #include <debug.h>
28 DBG_DEFAULT_CHANNEL(WINDOWS);
29 
30 // FIXME: Find a better way to retrieve ARC disk information
31 extern ULONG reactos_disk_count;
32 extern ARC_DISK_SIGNATURE_EX reactos_arc_disk_info[];
33 
34 extern ULONG LoaderPagesSpanned;
35 extern BOOLEAN AcpiPresent;
36 
37 extern HEADLESS_LOADER_BLOCK LoaderRedirectionInformation;
38 extern BOOLEAN WinLdrTerminalConnected;
39 extern void WinLdrSetupEms(IN PCHAR BootOptions);
40 
41 PLOADER_SYSTEM_BLOCK WinLdrSystemBlock;
42 
43 // debug stuff
44 VOID DumpMemoryAllocMap(VOID);
45 
46 // Init "phase 0"
47 VOID
48 AllocateAndInitLPB(PLOADER_PARAMETER_BLOCK *OutLoaderBlock)
49 {
50     PLOADER_PARAMETER_BLOCK LoaderBlock;
51 
52     /* Allocate and zero-init the LPB */
53     WinLdrSystemBlock = MmAllocateMemoryWithType(sizeof(LOADER_SYSTEM_BLOCK),
54                                                  LoaderSystemBlock);
55     if (WinLdrSystemBlock == NULL)
56     {
57         UiMessageBox("Failed to allocate memory for system block!");
58         return;
59     }
60 
61     RtlZeroMemory(WinLdrSystemBlock, sizeof(LOADER_SYSTEM_BLOCK));
62 
63     LoaderBlock = &WinLdrSystemBlock->LoaderBlock;
64     LoaderBlock->NlsData = &WinLdrSystemBlock->NlsDataBlock;
65 
66     /* Init three critical lists, used right away */
67     InitializeListHead(&LoaderBlock->LoadOrderListHead);
68     InitializeListHead(&LoaderBlock->MemoryDescriptorListHead);
69     InitializeListHead(&LoaderBlock->BootDriverListHead);
70 
71     *OutLoaderBlock = LoaderBlock;
72 }
73 
74 // Init "phase 1"
75 VOID
76 WinLdrInitializePhase1(PLOADER_PARAMETER_BLOCK LoaderBlock,
77                        LPCSTR Options,
78                        LPCSTR SystemRoot,
79                        LPCSTR BootPath,
80                        USHORT VersionToBoot)
81 {
82     /* Examples of correct options and paths */
83     //CHAR    Options[] = "/DEBUGPORT=COM1 /BAUDRATE=115200";
84     //CHAR    Options[] = "/NODEBUG";
85     //CHAR    SystemRoot[] = "\\WINNT\\";
86     //CHAR    ArcBoot[] = "multi(0)disk(0)rdisk(0)partition(1)";
87 
88     LPSTR LoadOptions, NewLoadOptions;
89     CHAR  HalPath[] = "\\";
90     CHAR  ArcBoot[256];
91     CHAR  MiscFiles[256];
92     ULONG i;
93     ULONG_PTR PathSeparator;
94     PLOADER_PARAMETER_EXTENSION Extension;
95 
96     /* Construct SystemRoot and ArcBoot from SystemPath */
97     PathSeparator = strstr(BootPath, "\\") - BootPath;
98     strncpy(ArcBoot, BootPath, PathSeparator);
99     ArcBoot[PathSeparator] = ANSI_NULL;
100 
101     TRACE("ArcBoot: %s\n", ArcBoot);
102     TRACE("SystemRoot: %s\n", SystemRoot);
103     TRACE("Options: %s\n", Options);
104 
105     /* Fill Arc BootDevice */
106     LoaderBlock->ArcBootDeviceName = WinLdrSystemBlock->ArcBootDeviceName;
107     strncpy(LoaderBlock->ArcBootDeviceName, ArcBoot, MAX_PATH);
108     LoaderBlock->ArcBootDeviceName = PaToVa(LoaderBlock->ArcBootDeviceName);
109 
110     /* Fill Arc HalDevice, it matches ArcBoot path */
111     LoaderBlock->ArcHalDeviceName = WinLdrSystemBlock->ArcBootDeviceName;
112     LoaderBlock->ArcHalDeviceName = PaToVa(LoaderBlock->ArcHalDeviceName);
113 
114     /* Fill SystemRoot */
115     LoaderBlock->NtBootPathName = WinLdrSystemBlock->NtBootPathName;
116     strncpy(LoaderBlock->NtBootPathName, SystemRoot, MAX_PATH);
117     LoaderBlock->NtBootPathName = PaToVa(LoaderBlock->NtBootPathName);
118 
119     /* Fill NtHalPathName */
120     LoaderBlock->NtHalPathName = WinLdrSystemBlock->NtHalPathName;
121     strncpy(LoaderBlock->NtHalPathName, HalPath, MAX_PATH);
122     LoaderBlock->NtHalPathName = PaToVa(LoaderBlock->NtHalPathName);
123 
124     /* Fill LoadOptions and strip the '/' commutator symbol in front of each option */
125     NewLoadOptions = LoadOptions = LoaderBlock->LoadOptions = WinLdrSystemBlock->LoadOptions;
126     strncpy(LoaderBlock->LoadOptions, Options, MAX_OPTIONS_LENGTH);
127 
128     do
129     {
130         while (*LoadOptions == '/')
131             ++LoadOptions;
132 
133         *NewLoadOptions++ = *LoadOptions;
134     } while (*LoadOptions++);
135 
136     LoaderBlock->LoadOptions = PaToVa(LoaderBlock->LoadOptions);
137 
138     /* Arc devices */
139     LoaderBlock->ArcDiskInformation = &WinLdrSystemBlock->ArcDiskInformation;
140     InitializeListHead(&LoaderBlock->ArcDiskInformation->DiskSignatureListHead);
141 
142     /* Convert ARC disk information from freeldr to a correct format */
143     for (i = 0; i < reactos_disk_count; i++)
144     {
145         PARC_DISK_SIGNATURE_EX ArcDiskSig;
146 
147         /* Allocate the ARC structure */
148         ArcDiskSig = FrLdrHeapAlloc(sizeof(ARC_DISK_SIGNATURE_EX), 'giSD');
149 
150         /* Copy the data over */
151         RtlCopyMemory(ArcDiskSig, &reactos_arc_disk_info[i], sizeof(ARC_DISK_SIGNATURE_EX));
152 
153         /* Set the ARC Name pointer and mark the partition table as valid */
154         ArcDiskSig->DiskSignature.ArcName = PaToVa(ArcDiskSig->ArcName);
155         ArcDiskSig->DiskSignature.ValidPartitionTable = TRUE;
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     //sprintf(ProgressString, "Loading %s...", FileName);
362     //UiDrawProgressBarCenter(1, 100, ProgressString);
363 
364     TRACE("Loading module %s\n", ModuleName);
365     *Size = 0;
366 
367     /* Open the image file */
368     Status = ArcOpen((PCHAR)ModuleName, OpenReadOnly, &FileId);
369     if (Status != ESUCCESS)
370     {
371         /* In case of errors, we just return, without complaining to the user */
372         return NULL;
373     }
374 
375     /* Get this file's size */
376     Status = ArcGetFileInformation(FileId, &FileInfo);
377     if (Status != ESUCCESS)
378     {
379         ArcClose(FileId);
380         return NULL;
381     }
382     FileSize = FileInfo.EndingAddress.LowPart;
383     *Size = FileSize;
384 
385     /* Allocate memory */
386     PhysicalBase = MmAllocateMemoryWithType(FileSize, MemoryType);
387     if (PhysicalBase == NULL)
388     {
389         ArcClose(FileId);
390         return NULL;
391     }
392 
393     /* Load whole file */
394     Status = ArcRead(FileId, PhysicalBase, FileSize, &BytesRead);
395     ArcClose(FileId);
396     if (Status != ESUCCESS)
397     {
398         return NULL;
399     }
400 
401     TRACE("Loaded %s at 0x%x with size 0x%x\n", ModuleName, PhysicalBase, FileSize);
402 
403     return PhysicalBase;
404 }
405 
406 USHORT
407 WinLdrDetectVersion(VOID)
408 {
409     LONG rc;
410     HKEY hKey;
411 
412     rc = RegOpenKey(
413         NULL,
414         L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Control\\Terminal Server",
415         &hKey);
416     if (rc != ERROR_SUCCESS)
417     {
418         // Key doesn't exist; assume NT 4.0
419         return _WIN32_WINNT_NT4;
420     }
421 
422     // We may here want to read the value of ProductVersion
423     return _WIN32_WINNT_WS03;
424 }
425 
426 static
427 BOOLEAN
428 LoadModule(
429     IN OUT PLOADER_PARAMETER_BLOCK LoaderBlock,
430     IN PCCH Path,
431     IN PCCH File,
432     IN PCCH ImportName, // BaseDllName
433     IN TYPE_OF_MEMORY MemoryType,
434     OUT PLDR_DATA_TABLE_ENTRY *Dte,
435     IN ULONG Percentage)
436 {
437     BOOLEAN Success;
438     CHAR FullFileName[MAX_PATH];
439     CHAR ProgressString[256];
440     PVOID BaseAddress = NULL;
441 
442     UiDrawBackdrop();
443     sprintf(ProgressString, "Loading %s...", File);
444     UiDrawProgressBarCenter(Percentage, 100, ProgressString);
445 
446     strcpy(FullFileName, Path);
447     strcat(FullFileName, File);
448 
449     Success = WinLdrLoadImage(FullFileName, MemoryType, &BaseAddress);
450     if (!Success)
451     {
452         TRACE("Loading %s failed\n", File);
453         return FALSE;
454     }
455     TRACE("%s loaded successfully at %p\n", File, BaseAddress);
456 
457     /*
458      * Cheat about the base DLL name if we are loading
459      * the Kernel Debugger Transport DLL, to make the
460      * PE loader happy.
461      */
462     Success = WinLdrAllocateDataTableEntry(&LoaderBlock->LoadOrderListHead,
463                                            ImportName,
464                                            FullFileName,
465                                            BaseAddress,
466                                            Dte);
467 
468     return Success;
469 }
470 
471 static
472 BOOLEAN
473 LoadWindowsCore(IN USHORT OperatingSystemVersion,
474                 IN OUT PLOADER_PARAMETER_BLOCK LoaderBlock,
475                 IN PCSTR BootOptions,
476                 IN PCSTR BootPath,
477                 IN OUT PLDR_DATA_TABLE_ENTRY* KernelDTE)
478 {
479     BOOLEAN Success;
480     PCSTR Options;
481     CHAR DirPath[MAX_PATH];
482     CHAR KernelFileName[MAX_PATH];
483     CHAR HalFileName[MAX_PATH];
484     CHAR KdTransportDllName[MAX_PATH];
485     PLDR_DATA_TABLE_ENTRY HalDTE, KdComDTE = NULL;
486 
487     if (!KernelDTE) return FALSE;
488 
489     /* Initialize SystemRoot\System32 path */
490     strcpy(DirPath, BootPath);
491     strcat(DirPath, "SYSTEM32\\");
492 
493     //
494     // TODO: Parse also the separate INI values "Kernel=" and "Hal="
495     //
496 
497     /* Default KERNEL and HAL file names */
498     strcpy(KernelFileName, "NTOSKRNL.EXE");
499     strcpy(HalFileName   , "HAL.DLL");
500 
501     /* Find any /KERNEL= or /HAL= switch in the boot options */
502     Options = BootOptions;
503     while (Options)
504     {
505         /* Skip possible initial whitespace */
506         Options += strspn(Options, " \t");
507 
508         /* Check whether a new commutator starts and it is either KERNEL or HAL */
509         if (*Options != '/' || (++Options,
510             !(_strnicmp(Options, "KERNEL=", 7) == 0 ||
511               _strnicmp(Options, "HAL=",    4) == 0)) )
512         {
513             /* Search for another whitespace */
514             Options = strpbrk(Options, " \t");
515             continue;
516         }
517         else
518         {
519             size_t i = strcspn(Options, " \t"); /* Skip whitespace */
520             if (i == 0)
521             {
522                 /* Use the default values */
523                 break;
524             }
525 
526             /* We have found either KERNEL or HAL commutator */
527             if (_strnicmp(Options, "KERNEL=", 7) == 0)
528             {
529                 Options += 7; i -= 7;
530                 strncpy(KernelFileName, Options, i);
531                 KernelFileName[i] = ANSI_NULL;
532                 _strupr(KernelFileName);
533             }
534             else if (_strnicmp(Options, "HAL=", 4) == 0)
535             {
536                 Options += 4; i -= 4;
537                 strncpy(HalFileName, Options, i);
538                 HalFileName[i] = ANSI_NULL;
539                 _strupr(HalFileName);
540             }
541         }
542     }
543 
544     TRACE("Kernel file = '%s' ; HAL file = '%s'\n", KernelFileName, HalFileName);
545 
546     /* Load the Kernel */
547     LoadModule(LoaderBlock, DirPath, KernelFileName, "NTOSKRNL.EXE", LoaderSystemCode, KernelDTE, 30);
548 
549     /* Load the HAL */
550     LoadModule(LoaderBlock, DirPath, HalFileName, "HAL.DLL", LoaderHalCode, &HalDTE, 45);
551 
552     /* Load the Kernel Debugger Transport DLL */
553     if (OperatingSystemVersion > _WIN32_WINNT_WIN2K)
554     {
555         /*
556          * According to http://www.nynaeve.net/?p=173 :
557          * "[...] Another enhancement that could be done Microsoft-side would be
558          * a better interface for replacing KD transport modules. Right now, due
559          * to the fact that ntoskrnl is static linked to KDCOM.DLL, the OS loader
560          * has a hardcoded hack that interprets the KD type in the OS loader options,
561          * loads one of the (hardcoded filenames) "kdcom.dll", "kd1394.dll", or
562          * "kdusb2.dll" modules, and inserts them into the loaded module list under
563          * the name "kdcom.dll". [...]"
564          */
565 
566         /*
567          * This loop replaces a dumb call to strstr(..., "DEBUGPORT=").
568          * Indeed I want it to be case-insensitive to allow "debugport="
569          * or "DeBuGpOrT=" or... , and I don't want it to match malformed
570          * command-line options, such as:
571          *
572          * "...foo DEBUGPORT=xxx bar..."
573          * "...foo/DEBUGPORT=xxx bar..."
574          * "...foo/DEBUGPORT=bar..."
575          *
576          * i.e. the "DEBUGPORT=" switch must start with a slash and be separated
577          * from the rest by whitespace, unless it begins the command-line, e.g.:
578          *
579          * "/DEBUGPORT=COM1 foo...bar..."
580          * "...foo /DEBUGPORT=USB bar..."
581          * or:
582          * "...foo /DEBUGPORT= bar..."
583          * (in that case, we default the port to COM).
584          */
585         Options = BootOptions;
586         while (Options)
587         {
588             /* Skip possible initial whitespace */
589             Options += strspn(Options, " \t");
590 
591             /* Check whether a new commutator starts and it is the DEBUGPORT one */
592             if (*Options != '/' || _strnicmp(++Options, "DEBUGPORT=", 10) != 0)
593             {
594                 /* Search for another whitespace */
595                 Options = strpbrk(Options, " \t");
596                 continue;
597             }
598             else
599             {
600                 /* We found the DEBUGPORT commutator. Move to the port name. */
601                 Options += 10;
602                 break;
603             }
604         }
605 
606         if (Options)
607         {
608             /*
609              * We have found the DEBUGPORT commutator. Parse the port name.
610              * Format: /DEBUGPORT=COM1 or /DEBUGPORT=FILE:\Device\HarddiskX\PartitionY\debug.log or /DEBUGPORT=FOO
611              * If we only have /DEBUGPORT= (i.e. without any port name), defaults it to "COM".
612              */
613             strcpy(KdTransportDllName, "KD");
614             if (_strnicmp(Options, "COM", 3) == 0 && '0' <= Options[3] && Options[3] <= '9')
615             {
616                 strncat(KdTransportDllName, Options, 3);
617             }
618             else
619             {
620                 size_t i = strcspn(Options, " \t:"); /* Skip valid separators: whitespace or colon */
621                 if (i == 0)
622                     strcat(KdTransportDllName, "COM");
623                 else
624                     strncat(KdTransportDllName, Options, i);
625             }
626             strcat(KdTransportDllName, ".DLL");
627             _strupr(KdTransportDllName);
628 
629             /*
630              * Load the transport DLL. Override the base DLL name of the
631              * loaded transport DLL to the default "KDCOM.DLL" name.
632              */
633             LoadModule(LoaderBlock, DirPath, KdTransportDllName, "KDCOM.DLL", LoaderSystemCode, &KdComDTE, 60);
634         }
635     }
636 
637     /* Load all referenced DLLs for Kernel, HAL and Kernel Debugger Transport DLL */
638     Success  = WinLdrScanImportDescriptorTable(&LoaderBlock->LoadOrderListHead, DirPath, *KernelDTE);
639     Success &= WinLdrScanImportDescriptorTable(&LoaderBlock->LoadOrderListHead, DirPath, HalDTE);
640     if (KdComDTE)
641     {
642         Success &= WinLdrScanImportDescriptorTable(&LoaderBlock->LoadOrderListHead, DirPath, KdComDTE);
643     }
644 
645     return Success;
646 }
647 
648 VOID
649 LoadAndBootWindows(IN OperatingSystemItem* OperatingSystem,
650                    IN USHORT OperatingSystemVersion)
651 {
652     ULONG_PTR SectionId;
653     PCSTR SectionName = OperatingSystem->SystemPartition;
654     CHAR  SettingsValue[80];
655     BOOLEAN HasSection;
656     CHAR  BootPath[MAX_PATH];
657     CHAR  FileName[MAX_PATH];
658     CHAR  BootOptions[256];
659     PCHAR File;
660     BOOLEAN Success;
661     PLOADER_PARAMETER_BLOCK LoaderBlock;
662 
663     /* Get OS setting value */
664     SettingsValue[0] = ANSI_NULL;
665     IniOpenSection("Operating Systems", &SectionId);
666     IniReadSettingByName(SectionId, SectionName, SettingsValue, sizeof(SettingsValue));
667 
668     /* Open the operating system section specified in the .ini file */
669     HasSection = IniOpenSection(SectionName, &SectionId);
670 
671     UiDrawBackdrop();
672     UiDrawProgressBarCenter(1, 100, "Loading NT...");
673 
674     /* Read the system path is set in the .ini file */
675     if (!HasSection ||
676         !IniReadSettingByName(SectionId, "SystemPath", BootPath, sizeof(BootPath)))
677     {
678         strcpy(BootPath, SectionName);
679     }
680 
681     /*
682      * Check whether BootPath is a full path
683      * and if not, create a full boot path.
684      *
685      * See FsOpenFile for the technique used.
686      */
687     if (strrchr(BootPath, ')') == NULL)
688     {
689         /* Temporarily save the boot path */
690         strcpy(FileName, BootPath);
691 
692         /* This is not a full path. Use the current (i.e. boot) device. */
693         MachDiskGetBootPath(BootPath, sizeof(BootPath));
694 
695         /* Append a path separator if needed */
696         if (FileName[0] != '\\' && FileName[0] != '/')
697             strcat(BootPath, "\\");
698 
699         /* Append the remaining path */
700         strcat(BootPath, FileName);
701     }
702 
703     /* Append a backslash if needed */
704     if ((BootPath[0] == 0) || BootPath[strlen(BootPath) - 1] != '\\')
705         strcat(BootPath, "\\");
706 
707     /* Read booting options */
708     if (!HasSection || !IniReadSettingByName(SectionId, "Options", BootOptions, sizeof(BootOptions)))
709     {
710         /* Get options after the title */
711         PCSTR p = SettingsValue;
712         while (*p == ' ' || *p == '"')
713             p++;
714         while (*p != '\0' && *p != '"')
715             p++;
716         strcpy(BootOptions, p);
717         TRACE("BootOptions: '%s'\n", BootOptions);
718     }
719 
720     /* Append boot-time options */
721     AppendBootTimeOptions(BootOptions);
722 
723     /* Check if a ramdisk file was given */
724     File = strstr(BootOptions, "/RDPATH=");
725     if (File)
726     {
727         /* Copy the file name and everything else after it */
728         strcpy(FileName, File + 8);
729 
730         /* Null-terminate */
731         *strstr(FileName, " ") = ANSI_NULL;
732 
733         /* Load the ramdisk */
734         if (!RamDiskLoadVirtualFile(FileName))
735         {
736             UiMessageBox("Failed to load RAM disk file %s", FileName);
737             return;
738         }
739     }
740 
741     /* Let user know we started loading */
742     //UiDrawStatusText("Loading...");
743 
744     TRACE("BootPath: '%s'\n", BootPath);
745 
746     /* Allocate and minimalist-initialize LPB */
747     AllocateAndInitLPB(&LoaderBlock);
748 
749     /* Load the system hive */
750     UiDrawBackdrop();
751     UiDrawProgressBarCenter(15, 100, "Loading system hive...");
752     Success = WinLdrInitSystemHive(LoaderBlock, BootPath);
753     TRACE("SYSTEM hive %s\n", (Success ? "loaded" : "not loaded"));
754     /* Bail out if failure */
755     if (!Success)
756         return;
757 
758     /* Load NLS data, OEM font, and prepare boot drivers list */
759     Success = WinLdrScanSystemHive(LoaderBlock, BootPath);
760     TRACE("SYSTEM hive %s\n", (Success ? "scanned" : "not scanned"));
761     /* Bail out if failure */
762     if (!Success)
763         return;
764 
765     /* Finish loading */
766     LoadAndBootWindowsCommon(OperatingSystemVersion,
767                              LoaderBlock,
768                              BootOptions,
769                              BootPath,
770                              FALSE);
771 }
772 
773 VOID
774 LoadAndBootWindowsCommon(
775     USHORT OperatingSystemVersion,
776     PLOADER_PARAMETER_BLOCK LoaderBlock,
777     LPCSTR BootOptions,
778     LPCSTR BootPath,
779     BOOLEAN Setup)
780 {
781     PLOADER_PARAMETER_BLOCK LoaderBlockVA;
782     BOOLEAN Success;
783     PLDR_DATA_TABLE_ENTRY KernelDTE;
784     KERNEL_ENTRY_POINT KiSystemStartup;
785     LPCSTR SystemRoot;
786     TRACE("LoadAndBootWindowsCommon()\n");
787 
788 #ifdef _M_IX86
789     /* Setup redirection support */
790     WinLdrSetupEms((PCHAR)BootOptions);
791 #endif
792 
793     /* Convert BootPath to SystemRoot */
794     SystemRoot = strstr(BootPath, "\\");
795 
796     /* Detect hardware */
797     LoaderBlock->ConfigurationRoot = MachHwDetect();
798 
799     if (OperatingSystemVersion == 0)
800         OperatingSystemVersion = WinLdrDetectVersion();
801 
802     /* Load the operating system core: the Kernel, the HAL and the Kernel Debugger Transport DLL */
803     Success = LoadWindowsCore(OperatingSystemVersion,
804                               LoaderBlock,
805                               BootOptions,
806                               BootPath,
807                               &KernelDTE);
808     if (!Success)
809     {
810         UiMessageBox("Error loading NTOS core.");
811         return;
812     }
813 
814     /* Load boot drivers */
815     UiDrawBackdrop();
816     UiDrawProgressBarCenter(100, 100, "Loading boot drivers...");
817     Success = WinLdrLoadBootDrivers(LoaderBlock, BootPath);
818     TRACE("Boot drivers loading %s\n", Success ? "successful" : "failed");
819 
820     /* Initialize Phase 1 - no drivers loading anymore */
821     WinLdrInitializePhase1(LoaderBlock,
822                            BootOptions,
823                            SystemRoot,
824                            BootPath,
825                            OperatingSystemVersion);
826 
827     /* Save entry-point pointer and Loader block VAs */
828     KiSystemStartup = (KERNEL_ENTRY_POINT)KernelDTE->EntryPoint;
829     LoaderBlockVA = PaToVa(LoaderBlock);
830 
831     /* "Stop all motors", change videomode */
832     MachPrepareForReactOS(Setup);
833 
834     /* Cleanup ini file */
835     IniCleanup();
836 
837     /* Debugging... */
838     //DumpMemoryAllocMap();
839 
840     /* Do the machine specific initialization */
841     WinLdrSetupMachineDependent(LoaderBlock);
842 
843     /* Map pages and create memory descriptors */
844     WinLdrSetupMemoryLayout(LoaderBlock);
845 
846     /* Set processor context */
847     WinLdrSetProcessorContext();
848 
849     /* Save final value of LoaderPagesSpanned */
850     LoaderBlock->Extension->LoaderPagesSpanned = LoaderPagesSpanned;
851 
852     TRACE("Hello from paged mode, KiSystemStartup %p, LoaderBlockVA %p!\n",
853           KiSystemStartup, LoaderBlockVA);
854 
855     // Zero KI_USER_SHARED_DATA page
856     memset((PVOID)KI_USER_SHARED_DATA, 0, MM_PAGE_SIZE);
857 
858     WinLdrpDumpMemoryDescriptors(LoaderBlockVA);
859     WinLdrpDumpBootDriver(LoaderBlockVA);
860 #ifndef _M_AMD64
861     WinLdrpDumpArcDisks(LoaderBlockVA);
862 #endif
863 
864     /* Pass control */
865     (*KiSystemStartup)(LoaderBlockVA);
866 }
867 
868 VOID
869 WinLdrpDumpMemoryDescriptors(PLOADER_PARAMETER_BLOCK LoaderBlock)
870 {
871     PLIST_ENTRY NextMd;
872     PMEMORY_ALLOCATION_DESCRIPTOR MemoryDescriptor;
873 
874     NextMd = LoaderBlock->MemoryDescriptorListHead.Flink;
875 
876     while (NextMd != &LoaderBlock->MemoryDescriptorListHead)
877     {
878         MemoryDescriptor = CONTAINING_RECORD(NextMd, MEMORY_ALLOCATION_DESCRIPTOR, ListEntry);
879 
880         TRACE("BP %08X PC %04X MT %d\n", MemoryDescriptor->BasePage,
881             MemoryDescriptor->PageCount, MemoryDescriptor->MemoryType);
882 
883         NextMd = MemoryDescriptor->ListEntry.Flink;
884     }
885 }
886 
887 VOID
888 WinLdrpDumpBootDriver(PLOADER_PARAMETER_BLOCK LoaderBlock)
889 {
890     PLIST_ENTRY NextBd;
891     PBOOT_DRIVER_LIST_ENTRY BootDriver;
892 
893     NextBd = LoaderBlock->BootDriverListHead.Flink;
894 
895     while (NextBd != &LoaderBlock->BootDriverListHead)
896     {
897         BootDriver = CONTAINING_RECORD(NextBd, BOOT_DRIVER_LIST_ENTRY, Link);
898 
899         TRACE("BootDriver %wZ DTE %08X RegPath: %wZ\n", &BootDriver->FilePath,
900             BootDriver->LdrEntry, &BootDriver->RegistryPath);
901 
902         NextBd = BootDriver->Link.Flink;
903     }
904 }
905 
906 VOID
907 WinLdrpDumpArcDisks(PLOADER_PARAMETER_BLOCK LoaderBlock)
908 {
909     PLIST_ENTRY NextBd;
910     PARC_DISK_SIGNATURE ArcDisk;
911 
912     NextBd = LoaderBlock->ArcDiskInformation->DiskSignatureListHead.Flink;
913 
914     while (NextBd != &LoaderBlock->ArcDiskInformation->DiskSignatureListHead)
915     {
916         ArcDisk = CONTAINING_RECORD(NextBd, ARC_DISK_SIGNATURE, ListEntry);
917 
918         TRACE("ArcDisk %s checksum: 0x%X, signature: 0x%X\n",
919             ArcDisk->ArcName, ArcDisk->CheckSum, ArcDisk->Signature);
920 
921         NextBd = ArcDisk->ListEntry.Flink;
922     }
923 }
924