[FREELDR] Improvements for the RamDisk support.
[reactos.git] / boot / freeldr / freeldr / ntldr / setupldr.c
1 /*
2 * PROJECT: FreeLoader
3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4 * PURPOSE: Windows-compatible NT OS Setup Loader.
5 * COPYRIGHT: Copyright 2009-2019 Aleksey Bragin <aleksey@reactos.org>
6 */
7
8 #include <freeldr.h>
9 #include <ndk/ldrtypes.h>
10 #include <arc/setupblk.h>
11 #include "winldr.h"
12 #include "inffile.h"
13
14 #include <debug.h>
15 DBG_DEFAULT_CHANNEL(WINDOWS);
16
17 #define TAG_BOOT_OPTIONS 'pOtB'
18
19 // TODO: Move to .h
20 VOID
21 AllocateAndInitLPB(
22 IN USHORT VersionToBoot,
23 OUT PLOADER_PARAMETER_BLOCK* OutLoaderBlock);
24
25 static VOID
26 SetupLdrLoadNlsData(PLOADER_PARAMETER_BLOCK LoaderBlock, HINF InfHandle, PCSTR SearchPath)
27 {
28 INFCONTEXT InfContext;
29 PCSTR AnsiName, OemName, LangName;
30
31 /* Get ANSI codepage file */
32 if (!InfFindFirstLine(InfHandle, "NLS", "AnsiCodepage", &InfContext))
33 {
34 ERR("Failed to find 'NLS/AnsiCodepage'\n");
35 return;
36 }
37 if (!InfGetDataField(&InfContext, 1, &AnsiName))
38 {
39 ERR("Failed to get load options\n");
40 return;
41 }
42
43 /* Get OEM codepage file */
44 if (!InfFindFirstLine(InfHandle, "NLS", "OemCodepage", &InfContext))
45 {
46 ERR("Failed to find 'NLS/AnsiCodepage'\n");
47 return;
48 }
49 if (!InfGetDataField(&InfContext, 1, &OemName))
50 {
51 ERR("Failed to get load options\n");
52 return;
53 }
54
55 if (!InfFindFirstLine(InfHandle, "NLS", "UnicodeCasetable", &InfContext))
56 {
57 ERR("Failed to find 'NLS/AnsiCodepage'\n");
58 return;
59 }
60 if (!InfGetDataField(&InfContext, 1, &LangName))
61 {
62 ERR("Failed to get load options\n");
63 return;
64 }
65
66 TRACE("NLS data '%s' '%s' '%s'\n", AnsiName, OemName, LangName);
67
68 #if DBG
69 {
70 BOOLEAN Success = WinLdrLoadNLSData(LoaderBlock, SearchPath, AnsiName, OemName, LangName);
71 (VOID)Success;
72 TRACE("NLS data loading %s\n", Success ? "successful" : "failed");
73 }
74 #else
75 WinLdrLoadNLSData(LoaderBlock, SearchPath, AnsiName, OemName, LangName);
76 #endif
77
78 /* TODO: Load OEM HAL font */
79 // Value "OemHalFont"
80 }
81
82 static
83 BOOLEAN
84 SetupLdrInitErrataInf(
85 IN OUT PLOADER_PARAMETER_BLOCK LoaderBlock,
86 IN HINF InfHandle,
87 IN PCSTR SystemRoot)
88 {
89 INFCONTEXT InfContext;
90 PCSTR FileName;
91 ULONG FileSize;
92 PVOID PhysicalBase;
93 CHAR ErrataFilePath[MAX_PATH];
94
95 /* Retrieve the INF file name value */
96 if (!InfFindFirstLine(InfHandle, "BiosInfo", "InfName", &InfContext))
97 {
98 WARN("Failed to find 'BiosInfo/InfName'\n");
99 return FALSE;
100 }
101 if (!InfGetDataField(&InfContext, 1, &FileName))
102 {
103 WARN("Failed to read 'InfName' value\n");
104 return FALSE;
105 }
106
107 RtlStringCbCopyA(ErrataFilePath, sizeof(ErrataFilePath), SystemRoot);
108 RtlStringCbCatA(ErrataFilePath, sizeof(ErrataFilePath), FileName);
109
110 /* Load the INF file */
111 PhysicalBase = WinLdrLoadModule(ErrataFilePath, &FileSize, LoaderRegistryData);
112 if (!PhysicalBase)
113 {
114 WARN("Could not load '%s'\n", ErrataFilePath);
115 return FALSE;
116 }
117
118 LoaderBlock->Extension->EmInfFileImage = PaToVa(PhysicalBase);
119 LoaderBlock->Extension->EmInfFileSize = FileSize;
120
121 return TRUE;
122 }
123
124 static VOID
125 SetupLdrScanBootDrivers(PLIST_ENTRY BootDriverListHead, HINF InfHandle, PCSTR SearchPath)
126 {
127 INFCONTEXT InfContext, dirContext;
128 BOOLEAN Success;
129 PCSTR Media, DriverName, dirIndex, ImagePath;
130 WCHAR ServiceName[256];
131 WCHAR ImagePathW[256];
132
133 /* Open inf section */
134 if (!InfFindFirstLine(InfHandle, "SourceDisksFiles", NULL, &InfContext))
135 return;
136
137 /* Load all listed boot drivers */
138 do
139 {
140 if (InfGetDataField(&InfContext, 7, &Media) &&
141 InfGetDataField(&InfContext, 0, &DriverName) &&
142 InfGetDataField(&InfContext, 13, &dirIndex))
143 {
144 if ((strcmp(Media, "x") == 0) &&
145 InfFindFirstLine(InfHandle, "Directories", dirIndex, &dirContext) &&
146 InfGetDataField(&dirContext, 1, &ImagePath))
147 {
148 /* Convert name to widechar */
149 swprintf(ServiceName, L"%S", DriverName);
150
151 /* Prepare image path */
152 swprintf(ImagePathW, L"%S", ImagePath);
153 wcscat(ImagePathW, L"\\");
154 wcscat(ImagePathW, ServiceName);
155
156 /* Remove .sys extension */
157 ServiceName[wcslen(ServiceName) - 4] = 0;
158
159 /* Add it to the list */
160 Success = WinLdrAddDriverToList(BootDriverListHead,
161 L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\",
162 ImagePathW,
163 ServiceName);
164 if (!Success)
165 {
166 ERR("Could not add boot driver '%s', '%s'\n", SearchPath, DriverName);
167 return;
168 }
169 }
170 }
171 } while (InfFindNextLine(&InfContext, &InfContext));
172 }
173
174
175 /* SETUP STARTER **************************************************************/
176
177 ARC_STATUS
178 LoadReactOSSetup(
179 IN ULONG Argc,
180 IN PCHAR Argv[],
181 IN PCHAR Envp[])
182 {
183 ARC_STATUS Status;
184 PCSTR ArgValue;
185 PCSTR SystemPartition;
186 PCHAR File;
187 CHAR FileName[MAX_PATH];
188 CHAR BootPath[MAX_PATH];
189 CHAR BootOptions2[256];
190 PCSTR LoadOptions;
191 PSTR BootOptions;
192 BOOLEAN BootFromFloppy;
193 BOOLEAN Success;
194 ULONG i, ErrorLine;
195 HINF InfHandle;
196 INFCONTEXT InfContext;
197 PLOADER_PARAMETER_BLOCK LoaderBlock;
198 PSETUP_LOADER_BLOCK SetupBlock;
199 PCSTR SystemPath;
200
201 static PCSTR SourcePaths[] =
202 {
203 "", /* Only for floppy boot */
204 #if defined(_M_IX86)
205 "I386\\",
206 #elif defined(_M_MPPC)
207 "PPC\\",
208 #elif defined(_M_MRX000)
209 "MIPS\\",
210 #endif
211 "reactos\\",
212 NULL
213 };
214
215 /* Retrieve the (mandatory) system partition */
216 SystemPartition = GetArgumentValue(Argc, Argv, "SystemPartition");
217 if (!SystemPartition || !*SystemPartition)
218 {
219 ERR("No 'SystemPartition' specified, aborting!\n");
220 return EINVAL;
221 }
222
223 UiDrawStatusText("Setup is loading...");
224
225 UiDrawBackdrop();
226 UiDrawProgressBarCenter(1, 100, "Loading ReactOS Setup...");
227
228 /* Retrieve the system path */
229 *BootPath = ANSI_NULL;
230 ArgValue = GetArgumentValue(Argc, Argv, "SystemPath");
231 if (ArgValue)
232 {
233 RtlStringCbCopyA(BootPath, sizeof(BootPath), ArgValue);
234 }
235 else
236 {
237 /*
238 * IMPROVE: I don't want to use the SystemPartition here as a
239 * default choice because I can do it after (see few lines below).
240 * Instead I reset BootPath here so that we can build the full path
241 * using the general code from below.
242 */
243 // RtlStringCbCopyA(BootPath, sizeof(BootPath), SystemPartition);
244 *BootPath = ANSI_NULL;
245 }
246
247 /*
248 * Check whether BootPath is a full path
249 * and if not, create a full boot path.
250 *
251 * See FsOpenFile for the technique used.
252 */
253 if (strrchr(BootPath, ')') == NULL)
254 {
255 /* Temporarily save the boot path */
256 RtlStringCbCopyA(FileName, sizeof(FileName), BootPath);
257
258 /* This is not a full path: prepend the SystemPartition */
259 RtlStringCbCopyA(BootPath, sizeof(BootPath), SystemPartition);
260
261 /* Append a path separator if needed */
262 if (*FileName != '\\' && *FileName != '/')
263 RtlStringCbCatA(BootPath, sizeof(BootPath), "\\");
264
265 /* Append the remaining path */
266 RtlStringCbCatA(BootPath, sizeof(BootPath), FileName);
267 }
268
269 /* Append a path separator if needed */
270 if (!*BootPath || BootPath[strlen(BootPath) - 1] != '\\')
271 RtlStringCbCatA(BootPath, sizeof(BootPath), "\\");
272
273 TRACE("BootPath: '%s'\n", BootPath);
274
275 /* Retrieve the boot options */
276 *BootOptions2 = ANSI_NULL;
277 ArgValue = GetArgumentValue(Argc, Argv, "Options");
278 if (ArgValue && *ArgValue)
279 RtlStringCbCopyA(BootOptions2, sizeof(BootOptions2), ArgValue);
280
281 TRACE("BootOptions: '%s'\n", BootOptions2);
282
283 /* Check if a ramdisk file was given */
284 File = strstr(BootOptions2, "/RDPATH=");
285 if (File)
286 {
287 /* Load the ramdisk */
288 Status = RamDiskInitialize(FALSE, BootOptions2, SystemPartition);
289 if (Status != ESUCCESS)
290 {
291 File += 8;
292 UiMessageBox("Failed to load RAM disk file '%.*s'",
293 strcspn(File, " \t"), File);
294 return Status;
295 }
296 }
297
298 /* Check if we booted from floppy */
299 BootFromFloppy = strstr(BootPath, "fdisk") != NULL;
300
301 /* Open 'txtsetup.sif' from any of source paths */
302 File = BootPath + strlen(BootPath);
303 for (i = BootFromFloppy ? 0 : 1; ; i++)
304 {
305 SystemPath = SourcePaths[i];
306 if (!SystemPath)
307 {
308 UiMessageBox("Failed to open txtsetup.sif");
309 return ENOENT;
310 }
311 RtlStringCbCopyA(File, sizeof(BootPath) - (File - BootPath)*sizeof(CHAR), SystemPath);
312 RtlStringCbCopyA(FileName, sizeof(FileName), BootPath);
313 RtlStringCbCatA(FileName, sizeof(FileName), "txtsetup.sif");
314 if (InfOpenFile(&InfHandle, FileName, &ErrorLine))
315 {
316 break;
317 }
318 }
319
320 TRACE("BootPath: '%s', SystemPath: '%s'\n", BootPath, SystemPath);
321
322 /* Get load options - debug and non-debug */
323 if (!InfFindFirstLine(InfHandle, "SetupData", "OsLoadOptions", &InfContext))
324 {
325 ERR("Failed to find 'SetupData/OsLoadOptions'\n");
326 return EINVAL;
327 }
328
329 if (!InfGetDataField(&InfContext, 1, &LoadOptions))
330 {
331 ERR("Failed to get load options\n");
332 return EINVAL;
333 }
334
335 #if DBG
336 /* Get debug load options and use them */
337 if (InfFindFirstLine(InfHandle, "SetupData", "DbgOsLoadOptions", &InfContext))
338 {
339 PCSTR DbgLoadOptions;
340
341 if (InfGetDataField(&InfContext, 1, &DbgLoadOptions))
342 LoadOptions = DbgLoadOptions;
343 }
344 #endif
345
346 /* Copy LoadOptions (original string will be freed) */
347 BootOptions = FrLdrTempAlloc(strlen(LoadOptions) + 1, TAG_BOOT_OPTIONS);
348 ASSERT(BootOptions);
349 strcpy(BootOptions, LoadOptions);
350
351 TRACE("BootOptions: '%s'\n", BootOptions);
352
353 /* Allocate and minimally-initialize the Loader Parameter Block */
354 AllocateAndInitLPB(_WIN32_WINNT_WS03, &LoaderBlock);
355
356 /* Allocate and initialize setup loader block */
357 SetupBlock = &WinLdrSystemBlock->SetupBlock;
358 LoaderBlock->SetupLdrBlock = SetupBlock;
359
360 /* Set textmode setup flag */
361 SetupBlock->Flags = SETUPLDR_TEXT_MODE;
362
363 /* Load the system hive "setupreg.hiv" for setup */
364 UiDrawBackdrop();
365 UiDrawProgressBarCenter(15, 100, "Loading setup system hive...");
366 Success = WinLdrInitSystemHive(LoaderBlock, BootPath, TRUE);
367 TRACE("Setup SYSTEM hive %s\n", (Success ? "loaded" : "not loaded"));
368 /* Bail out if failure */
369 if (!Success)
370 return ENOEXEC;
371
372 /* Load NLS data, they are in the System32 directory of the installation medium */
373 RtlStringCbCopyA(FileName, sizeof(FileName), BootPath);
374 RtlStringCbCatA(FileName, sizeof(FileName), "system32\\");
375 SetupLdrLoadNlsData(LoaderBlock, InfHandle, FileName);
376
377 /* Load the Firmware Errata file from the installation medium */
378 Success = SetupLdrInitErrataInf(LoaderBlock, InfHandle, BootPath);
379 TRACE("Firmware Errata file %s\n", (Success ? "loaded" : "not loaded"));
380 /* Not necessarily fatal if not found - carry on going */
381
382 // UiDrawStatusText("Press F6 if you need to install a 3rd-party SCSI or RAID driver...");
383
384 /* Get a list of boot drivers */
385 SetupLdrScanBootDrivers(&LoaderBlock->BootDriverListHead, InfHandle, BootPath);
386
387 /* Close the inf file */
388 InfCloseFile(InfHandle);
389
390 UiDrawStatusText("The Setup program is starting...");
391
392 /* Load ReactOS Setup */
393 return LoadAndBootWindowsCommon(_WIN32_WINNT_WS03,
394 LoaderBlock,
395 BootOptions,
396 BootPath,
397 TRUE);
398 }