[FREELDR] Add support for loading Linux in x64 FreeLdr. Part 2/2: C code.
[reactos.git] / boot / freeldr / freeldr / bootmgr.c
1 /*
2 * FreeLoader
3 * Copyright (C) 1998-2003 Brian Palmer <brianp@sginet.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 /* INCLUDES *******************************************************************/
21
22 #include <freeldr.h>
23
24 #include <debug.h>
25 DBG_DEFAULT_CHANNEL(WARNING);
26
27 /* GLOBALS ********************************************************************/
28
29 typedef
30 VOID
31 (*EDIT_OS_ENTRY_PROC)(
32 IN OUT OperatingSystemItem* OperatingSystem);
33
34 static VOID
35 EditCustomBootReactOSSetup(
36 IN OUT OperatingSystemItem* OperatingSystem)
37 {
38 EditCustomBootReactOS(OperatingSystem, TRUE);
39 }
40
41 static VOID
42 EditCustomBootNTOS(
43 IN OUT OperatingSystemItem* OperatingSystem)
44 {
45 EditCustomBootReactOS(OperatingSystem, FALSE);
46 }
47
48 static const struct
49 {
50 PCSTR BootType;
51 EDIT_OS_ENTRY_PROC EditOsEntry;
52 ARC_ENTRY_POINT OsLoader;
53 } OSLoadingMethods[] =
54 {
55 {"ReactOSSetup", EditCustomBootReactOSSetup, LoadReactOSSetup},
56
57 #if defined(_M_IX86) || defined(_M_AMD64)
58 {"Drive" , EditCustomBootDisk , LoadAndBootDevice},
59 {"Partition" , EditCustomBootPartition , LoadAndBootDevice},
60 {"BootSector" , EditCustomBootSectorFile, LoadAndBootDevice},
61 {"Linux" , EditCustomBootLinux, LoadAndBootLinux },
62 #endif
63 #ifdef _M_IX86
64 {"WindowsNT40" , EditCustomBootNTOS , LoadAndBootWindows},
65 #endif
66 {"Windows" , EditCustomBootNTOS , LoadAndBootWindows},
67 {"Windows2003" , EditCustomBootNTOS , LoadAndBootWindows},
68 };
69
70 /* FUNCTIONS ******************************************************************/
71
72 /*
73 * This function converts the list of key=value options in the given operating
74 * system section into an ARC-compatible argument vector, providing in addition
75 * the extra mandatory Software Loading Environment Variables, following the
76 * ARC specification.
77 */
78 PCHAR*
79 BuildArgvForOsLoader(
80 IN PCSTR LoadIdentifier,
81 IN ULONG_PTR SectionId,
82 OUT PULONG pArgc)
83 {
84 SIZE_T Size;
85 ULONG Count;
86 ULONG i;
87 ULONG Argc;
88 PCHAR* Argv;
89 PCHAR* Args;
90 PCHAR SettingName, SettingValue;
91
92 *pArgc = 0;
93
94 ASSERT(SectionId != 0);
95
96 /* Validate the LoadIdentifier (to make tests simpler later) */
97 if (LoadIdentifier && !*LoadIdentifier)
98 LoadIdentifier = NULL;
99
100 /* Count the number of operating systems in the section */
101 Count = IniGetNumSectionItems(SectionId);
102
103 /*
104 * The argument vector contains the program name, the SystemPartition,
105 * the LoadIdentifier (optional), and the items in the OS section.
106 */
107 Argc = 2 + (LoadIdentifier ? 1 : 0) + Count;
108
109 /* Calculate the total size needed for the string buffer of the argument vector */
110 Size = 0;
111 /* i == 0: Program name */
112 /* i == 1: SystemPartition : from where FreeLdr has been started */
113 Size += (strlen("SystemPartition=") + strlen(FrLdrBootPath) + 1) * sizeof(CHAR);
114 /* i == 2: LoadIdentifier : ASCII string that may be used to associate an identifier with a set of load parameters */
115 if (LoadIdentifier)
116 {
117 Size += (strlen("LoadIdentifier=") + strlen(LoadIdentifier) + 1) * sizeof(CHAR);
118 }
119 for (i = 0; i < Count; ++i)
120 {
121 Size += IniGetSectionSettingNameSize(SectionId, i); // Counts also the NULL-terminator, that we transform into the '=' sign separator.
122 Size += IniGetSectionSettingValueSize(SectionId, i); // Counts also the NULL-terminator.
123 }
124 Size += sizeof(ANSI_NULL); // Final NULL-terminator.
125
126 /* Allocate memory to hold the argument vector: pointers and string buffer */
127 Argv = FrLdrHeapAlloc(Argc * sizeof(PCHAR) + Size, TAG_STRING);
128 if (!Argv)
129 return NULL;
130
131 /* Initialize the argument vector: loop through the section and copy the key=value options */
132 SettingName = (PCHAR)((ULONG_PTR)Argv + (Argc * sizeof(PCHAR)));
133 Args = Argv;
134 /* i == 0: Program name */
135 *Args++ = NULL;
136 /* i == 1: SystemPartition */
137 {
138 strcpy(SettingName, "SystemPartition=");
139 strcat(SettingName, FrLdrBootPath);
140
141 *Args++ = SettingName;
142 SettingName += (strlen(SettingName) + 1);
143 }
144 /* i == 2: LoadIdentifier */
145 if (LoadIdentifier)
146 {
147 strcpy(SettingName, "LoadIdentifier=");
148 strcat(SettingName, LoadIdentifier);
149
150 *Args++ = SettingName;
151 SettingName += (strlen(SettingName) + 1);
152 }
153 for (i = 0; i < Count; ++i)
154 {
155 Size = IniGetSectionSettingNameSize(SectionId, i);
156 SettingValue = SettingName + Size;
157 IniReadSettingByNumber(SectionId, i,
158 SettingName, Size,
159 SettingValue, IniGetSectionSettingValueSize(SectionId, i));
160 SettingName[Size - 1] = '=';
161
162 *Args++ = SettingName;
163 SettingName += (strlen(SettingName) + 1);
164 }
165
166 #if DBG
167 /* Dump the argument vector for debugging */
168 for (i = 0; i < Argc; ++i)
169 {
170 TRACE("Argv[%lu]: '%s'\n", i, Argv[i]);
171 }
172 #endif
173
174 *pArgc = Argc;
175 return Argv;
176 }
177
178 VOID LoadOperatingSystem(IN OperatingSystemItem* OperatingSystem)
179 {
180 ULONG_PTR SectionId = OperatingSystem->SectionId;
181 ULONG i;
182 ULONG Argc;
183 PCHAR* Argv;
184 CHAR BootType[80];
185
186 /* The operating system section has been opened by InitOperatingSystemList() */
187 ASSERT(SectionId != 0);
188
189 /* Try to read the boot type */
190 *BootType = ANSI_NULL;
191 IniReadSettingByName(SectionId, "BootType", BootType, sizeof(BootType));
192
193 /* We must have the "BootType" value (it has been possibly added by InitOperatingSystemList()) */
194 ASSERT(*BootType);
195
196 #ifdef _M_IX86
197 /* Install the drive mapper according to this section drive mappings */
198 DriveMapMapDrivesInSection(SectionId);
199 #endif
200
201 /* Find the suitable OS loader to start */
202 for (i = 0; ; ++i)
203 {
204 if (i >= RTL_NUMBER_OF(OSLoadingMethods))
205 return;
206 if (_stricmp(BootType, OSLoadingMethods[i].BootType) == 0)
207 break;
208 }
209
210 /* Build the ARC-compatible argument vector */
211 Argv = BuildArgvForOsLoader(OperatingSystem->LoadIdentifier, SectionId, &Argc);
212 if (!Argv)
213 return; // Unexpected failure.
214
215 /* Start the OS loader */
216 OSLoadingMethods[i].OsLoader(Argc, Argv, NULL);
217 FrLdrHeapFree(Argv, TAG_STRING);
218 }
219
220 #ifdef HAS_OPTION_MENU_EDIT_CMDLINE
221
222 VOID EditOperatingSystemEntry(IN OperatingSystemItem* OperatingSystem)
223 {
224 ULONG_PTR SectionId = OperatingSystem->SectionId;
225 ULONG i;
226 CHAR BootType[80];
227
228 /* The operating system section has been opened by InitOperatingSystemList() */
229 ASSERT(SectionId != 0);
230
231 /* Try to read the boot type */
232 *BootType = ANSI_NULL;
233 IniReadSettingByName(SectionId, "BootType", BootType, sizeof(BootType));
234
235 /* We must have the "BootType" value (it has been possibly added by InitOperatingSystemList()) */
236 ASSERT(*BootType);
237
238 /* Find the suitable OS entry editor */
239 for (i = 0; ; ++i)
240 {
241 if (i >= RTL_NUMBER_OF(OSLoadingMethods))
242 return;
243 if (_stricmp(BootType, OSLoadingMethods[i].BootType) == 0)
244 break;
245 }
246
247 /* Run it */
248 OSLoadingMethods[i].EditOsEntry(OperatingSystem);
249 }
250
251 #endif // HAS_OPTION_MENU_EDIT_CMDLINE
252
253 static LONG
254 GetTimeOut(
255 IN ULONG_PTR FrLdrSectionId)
256 {
257 LONG TimeOut = -1;
258 CHAR TimeOutText[20];
259
260 TimeOut = CmdLineGetTimeOut();
261 if (TimeOut >= 0)
262 return TimeOut;
263
264 TimeOut = -1;
265
266 if ((FrLdrSectionId != 0) &&
267 IniReadSettingByName(FrLdrSectionId, "TimeOut", TimeOutText, sizeof(TimeOutText)))
268 {
269 TimeOut = atoi(TimeOutText);
270 }
271
272 return TimeOut;
273 }
274
275 BOOLEAN
276 MainBootMenuKeyPressFilter(
277 IN ULONG KeyPress,
278 IN ULONG SelectedMenuItem,
279 IN PVOID Context OPTIONAL)
280 {
281 switch (KeyPress)
282 {
283 case KEY_F8:
284 DoOptionsMenu(&((OperatingSystemItem*)Context)[SelectedMenuItem]);
285 return TRUE;
286
287 #ifdef HAS_OPTION_MENU_EDIT_CMDLINE
288 case KEY_F10:
289 EditOperatingSystemEntry(&((OperatingSystemItem*)Context)[SelectedMenuItem]);
290 return TRUE;
291 #endif
292
293 default:
294 /* We didn't handle the key */
295 return FALSE;
296 }
297 }
298
299 VOID RunLoader(VOID)
300 {
301 ULONG_PTR SectionId;
302 LONG TimeOut;
303 ULONG OperatingSystemCount;
304 OperatingSystemItem* OperatingSystemList;
305 PCSTR* OperatingSystemDisplayNames;
306 ULONG DefaultOperatingSystem;
307 ULONG SelectedOperatingSystem;
308 ULONG i;
309
310 if (!MachInitializeBootDevices())
311 {
312 UiMessageBoxCritical("Error when detecting hardware.");
313 return;
314 }
315
316 #ifdef _M_IX86
317 /* Load additional SCSI driver (if any) */
318 if (LoadBootDeviceDriver() != ESUCCESS)
319 {
320 UiMessageBoxCritical("Unable to load additional boot device drivers.");
321 }
322 #endif
323
324 if (!IniFileInitialize())
325 {
326 UiMessageBoxCritical("Error initializing .ini file.");
327 return;
328 }
329
330 /* Open the [FreeLoader] section */
331 if (!IniOpenSection("FreeLoader", &SectionId))
332 {
333 UiMessageBoxCritical("Section [FreeLoader] not found in freeldr.ini.");
334 return;
335 }
336
337 /* Debugger main initialization */
338 DebugInit(SectionId);
339
340 /* Retrieve the default timeout */
341 TimeOut = GetTimeOut(SectionId);
342
343 /* UI main initialization */
344 if (!UiInitialize(TRUE))
345 {
346 UiMessageBoxCritical("Unable to initialize UI.");
347 return;
348 }
349
350 OperatingSystemList = InitOperatingSystemList(SectionId,
351 &OperatingSystemCount,
352 &DefaultOperatingSystem);
353 if (!OperatingSystemList)
354 {
355 UiMessageBox("Unable to read operating systems section in freeldr.ini.\nPress ENTER to reboot.");
356 goto Reboot;
357 }
358 if (OperatingSystemCount == 0)
359 {
360 UiMessageBox("There were no operating systems listed in freeldr.ini.\nPress ENTER to reboot.");
361 goto Reboot;
362 }
363
364 /* Create list of display names */
365 OperatingSystemDisplayNames = FrLdrTempAlloc(sizeof(PCSTR) * OperatingSystemCount, 'mNSO');
366 if (!OperatingSystemDisplayNames)
367 goto Reboot;
368
369 for (i = 0; i < OperatingSystemCount; i++)
370 {
371 OperatingSystemDisplayNames[i] = OperatingSystemList[i].LoadIdentifier;
372 }
373
374 /* Find all the message box settings and run them */
375 UiShowMessageBoxesInSection(SectionId);
376
377 for (;;)
378 {
379 /* Redraw the backdrop */
380 UiDrawBackdrop();
381
382 /* Show the operating system list menu */
383 if (!UiDisplayMenu("Please select the operating system to start:",
384 "For troubleshooting and advanced startup options for "
385 "ReactOS, press F8.",
386 TRUE,
387 OperatingSystemDisplayNames,
388 OperatingSystemCount,
389 DefaultOperatingSystem,
390 TimeOut,
391 &SelectedOperatingSystem,
392 FALSE,
393 MainBootMenuKeyPressFilter,
394 OperatingSystemList))
395 {
396 UiMessageBox("Press ENTER to reboot.");
397 goto Reboot;
398 }
399
400 TimeOut = -1;
401
402 /* Load the chosen operating system */
403 LoadOperatingSystem(&OperatingSystemList[SelectedOperatingSystem]);
404 }
405
406 Reboot:
407 UiUnInitialize("Rebooting...");
408 IniCleanup();
409 return;
410 }