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