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