6ea566316549b3e76198969425f824b3a449f726
[reactos.git] / boot / freeldr / freeldr / miscboot.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 #ifdef _M_IX86
21
22 /* INCLUDES *******************************************************************/
23
24 #include <freeldr.h>
25
26 /* FUNCTIONS ******************************************************************/
27
28 static ARC_STATUS
29 LoadBootSector(
30 IN ULONG Argc,
31 IN PCHAR Argv[],
32 OUT PUCHAR DriveNumber,
33 OUT PULONG PartitionNumber)
34 {
35 ARC_STATUS Status;
36 PCSTR ArgValue;
37 PCSTR BootPath;
38 PCSTR FileName;
39 ULONG FileId;
40 ULONG BytesRead;
41 CHAR ArcPath[MAX_PATH];
42
43 *DriveNumber = 0;
44 *PartitionNumber = 0;
45
46 /*
47 * Check whether we have a "BootPath" value (takes precedence
48 * over both "BootDrive" and "BootPartition").
49 */
50 BootPath = GetArgumentValue(Argc, Argv, "BootPath");
51 if (!BootPath || !*BootPath)
52 {
53 /* We don't have one, check whether we use "BootDrive" and "BootPartition" */
54
55 /* Retrieve the boot drive (optional, fall back to using default path otherwise) */
56 ArgValue = GetArgumentValue(Argc, Argv, "BootDrive");
57 if (ArgValue && *ArgValue)
58 {
59 *DriveNumber = DriveMapGetBiosDriveNumber(ArgValue);
60
61 /* Retrieve the boot partition (not optional and cannot be zero) */
62 *PartitionNumber = 0;
63 ArgValue = GetArgumentValue(Argc, Argv, "BootPartition");
64 if (ArgValue && *ArgValue)
65 *PartitionNumber = atoi(ArgValue);
66 if (*PartitionNumber == 0)
67 {
68 UiMessageBox("Boot partition cannot be 0!");
69 return EINVAL;
70 }
71
72 /* Construct the corresponding ARC path */
73 ConstructArcPath(ArcPath, "", *DriveNumber, *PartitionNumber);
74 *strrchr(ArcPath, '\\') = ANSI_NULL; // Trim the trailing path separator.
75
76 BootPath = ArcPath;
77 }
78 else
79 {
80 /* Fall back to using the system partition as default path */
81 BootPath = GetArgumentValue(Argc, Argv, "SystemPartition");
82 }
83 }
84
85 /* Retrieve the file name */
86 FileName = GetArgumentValue(Argc, Argv, "BootSectorFile");
87 if (!FileName || !*FileName)
88 {
89 UiMessageBox("Boot sector file not specified for selected OS!");
90 return EINVAL;
91 }
92
93 /* Open the boot sector file */
94 Status = FsOpenFile(FileName, BootPath, OpenReadOnly, &FileId);
95 if (Status != ESUCCESS)
96 {
97 UiMessageBox("Unable to open %s", FileName);
98 return Status;
99 }
100
101 /* Now try to load the boot sector. If this fails then abort. */
102 Status = ArcRead(FileId, (PVOID)0x7c00, 512, &BytesRead);
103 ArcClose(FileId);
104 if ((Status != ESUCCESS) || (BytesRead != 512))
105 {
106 UiMessageBox("Unable to load boot sector.");
107 return EIO;
108 }
109
110 /* Check for validity */
111 if (*((USHORT*)(0x7c00 + 0x1fe)) != 0xaa55)
112 {
113 UiMessageBox("Invalid boot sector magic (0xaa55)");
114 return ENOEXEC;
115 }
116
117 /* Reset the drive and partition numbers so as to use their default values */
118 *DriveNumber = 0;
119 *PartitionNumber = 0;
120
121 return ESUCCESS;
122 }
123
124 static ARC_STATUS
125 LoadPartitionOrDrive(
126 IN OUT PUCHAR DriveNumber,
127 IN OUT PULONG PartitionNumber,
128 IN PCSTR BootPath OPTIONAL)
129 {
130 ARC_STATUS Status;
131 ULONG FileId;
132 ULONG BytesRead;
133 CHAR ArcPath[MAX_PATH];
134
135 /*
136 * The ARC "BootPath" value takes precedence over
137 * both the DriveNumber and PartitionNumber options.
138 */
139 if (BootPath && *BootPath)
140 {
141 PCSTR FileName = NULL;
142
143 /*
144 * Retrieve the BIOS drive and partition numbers; verify also that the
145 * path is "valid" in the sense that it must not contain any file name.
146 */
147 if (!DissectArcPath(BootPath, &FileName, DriveNumber, PartitionNumber) ||
148 (FileName && *FileName))
149 {
150 return EINVAL;
151 }
152 }
153 else
154 {
155 /* We don't have one, so construct the corresponding ARC path */
156 ConstructArcPath(ArcPath, "", *DriveNumber, *PartitionNumber);
157 *strrchr(ArcPath, '\\') = ANSI_NULL; // Trim the trailing path separator.
158
159 BootPath = ArcPath;
160 }
161
162 /* Open the volume */
163 Status = ArcOpen((PSTR)BootPath, OpenReadOnly, &FileId);
164 if (Status != ESUCCESS)
165 {
166 UiMessageBox("Unable to open %s", BootPath);
167 return Status;
168 }
169
170 /*
171 * Now try to load the partition boot sector or the MBR (when PartitionNumber == 0).
172 * If this fails then abort.
173 */
174 Status = ArcRead(FileId, (PVOID)0x7c00, 512, &BytesRead);
175 ArcClose(FileId);
176 if ((Status != ESUCCESS) || (BytesRead != 512))
177 {
178 if (*PartitionNumber != 0)
179 UiMessageBox("Unable to load partition's boot sector.");
180 else
181 UiMessageBox("Unable to load MBR boot sector.");
182 return EIO;
183 }
184
185 /* Check for validity */
186 if (*((USHORT*)(0x7c00 + 0x1fe)) != 0xaa55)
187 {
188 UiMessageBox("Invalid boot sector magic (0xaa55)");
189 return ENOEXEC;
190 }
191
192 return ESUCCESS;
193 }
194
195 static ARC_STATUS
196 LoadPartition(
197 IN ULONG Argc,
198 IN PCHAR Argv[],
199 OUT PUCHAR DriveNumber,
200 OUT PULONG PartitionNumber)
201 {
202 PCSTR ArgValue;
203 PCSTR BootPath;
204
205 *DriveNumber = 0;
206 *PartitionNumber = 0;
207
208 /*
209 * Check whether we have a "BootPath" value (takes precedence
210 * over both "BootDrive" and "BootPartition").
211 */
212 BootPath = GetArgumentValue(Argc, Argv, "BootPath");
213 if (!BootPath || !*BootPath)
214 {
215 /* We don't have one */
216
217 /* Retrieve the boot drive */
218 ArgValue = GetArgumentValue(Argc, Argv, "BootDrive");
219 if (!ArgValue || !*ArgValue)
220 {
221 UiMessageBox("Boot drive not specified for selected OS!");
222 return EINVAL;
223 }
224 *DriveNumber = DriveMapGetBiosDriveNumber(ArgValue);
225
226 /* Retrieve the boot partition (optional, fall back to zero otherwise) */
227 *PartitionNumber = 0;
228 ArgValue = GetArgumentValue(Argc, Argv, "BootPartition");
229 if (ArgValue && *ArgValue)
230 *PartitionNumber = atoi(ArgValue);
231 }
232
233 return LoadPartitionOrDrive(DriveNumber, PartitionNumber, BootPath);
234 }
235
236 static ARC_STATUS
237 LoadDrive(
238 IN ULONG Argc,
239 IN PCHAR Argv[],
240 OUT PUCHAR DriveNumber,
241 OUT PULONG PartitionNumber)
242 {
243 PCSTR ArgValue;
244 PCSTR BootPath;
245
246 *DriveNumber = 0;
247 *PartitionNumber = 0;
248
249 /* Check whether we have a "BootPath" value (takes precedence over "BootDrive") */
250 BootPath = GetArgumentValue(Argc, Argv, "BootPath");
251 if (BootPath && *BootPath)
252 {
253 /*
254 * We have one, check that it does not contain any
255 * "partition()" specification, and fail if so.
256 */
257 if (strstr(BootPath, ")partition("))
258 {
259 UiMessageBox("Invalid 'BootPath' value!");
260 return EINVAL;
261 }
262 }
263 else
264 {
265 /* We don't, retrieve the boot drive value instead */
266 ArgValue = GetArgumentValue(Argc, Argv, "BootDrive");
267 if (!ArgValue || !*ArgValue)
268 {
269 UiMessageBox("Boot drive not specified for selected OS!");
270 return EINVAL;
271 }
272 *DriveNumber = DriveMapGetBiosDriveNumber(ArgValue);
273 }
274
275 return LoadPartitionOrDrive(DriveNumber, PartitionNumber, BootPath);
276 }
277
278
279 ARC_STATUS
280 LoadAndBootDevice(
281 IN ULONG Argc,
282 IN PCHAR Argv[],
283 IN PCHAR Envp[])
284 {
285 ARC_STATUS Status;
286 PCSTR ArgValue;
287 UCHAR Type;
288 UCHAR DriveNumber = 0;
289 ULONG PartitionNumber = 0;
290
291 /* Retrieve the (mandatory) boot type */
292 ArgValue = GetArgumentValue(Argc, Argv, "BootType");
293 if (!ArgValue || !*ArgValue)
294 return EINVAL;
295 if (_stricmp(ArgValue, "Drive") == 0)
296 Type = 1;
297 else if (_stricmp(ArgValue, "Partition") == 0)
298 Type = 2;
299 else if (_stricmp(ArgValue, "BootSector") == 0)
300 Type = 3;
301 else
302 return EINVAL;
303
304 /* Find all the message box settings and run them */
305 UiShowMessageBoxesInArgv(Argc, Argv);
306
307 /* Load the corresponding device */
308 switch (Type)
309 {
310 case 1:
311 Status = LoadDrive(Argc, Argv, &DriveNumber, &PartitionNumber);
312 break;
313 case 2:
314 Status = LoadPartition(Argc, Argv, &DriveNumber, &PartitionNumber);
315 break;
316 case 3:
317 Status = LoadBootSector(Argc, Argv, &DriveNumber, &PartitionNumber);
318 break;
319 default:
320 return EINVAL;
321 }
322 if (Status != ESUCCESS)
323 return Status;
324
325 UiUnInitialize("Booting...");
326 IniCleanup();
327
328 /* Boot the loaded sector code at 0x7C00 */
329 ChainLoadBiosBootSectorCode(DriveNumber, PartitionNumber);
330 /* Must not return! */
331 return ESUCCESS;
332 }
333
334 #endif // _M_IX86