Replace BOOL by BOOLEAN and STDCALL by NTAPI
[reactos.git] / reactos / boot / freeldr / freeldr / linuxboot.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
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19
20
21 #include <freeldr.h>
22
23 #define NDEBUG
24 #include <debug.h>
25
26 #define LINUX_READ_CHUNK_SIZE 0x20000 // Read 128k at a time
27
28
29 PLINUX_BOOTSECTOR LinuxBootSector = NULL;
30 PLINUX_SETUPSECTOR LinuxSetupSector = NULL;
31 ULONG SetupSectorSize = 0;
32 BOOLEAN NewStyleLinuxKernel = FALSE;
33 ULONG LinuxKernelSize = 0;
34 ULONG LinuxInitrdSize = 0;
35 CHAR LinuxKernelName[260];
36 CHAR LinuxInitrdName[260];
37 BOOLEAN LinuxHasInitrd = FALSE;
38 CHAR LinuxCommandLine[260] = "";
39 ULONG LinuxCommandLineSize = 0;
40 PVOID LinuxKernelLoadAddress = NULL;
41 PVOID LinuxInitrdLoadAddress = NULL;
42 CHAR LinuxBootDescription[80];
43 CHAR LinuxBootPath[260] = "";
44
45 VOID LoadAndBootLinux(PCSTR OperatingSystemName, PCSTR Description)
46 {
47 PFILE LinuxKernel = NULL;
48 PFILE LinuxInitrdFile = NULL;
49 CHAR TempString[260];
50
51 UiDrawBackdrop();
52
53 if (Description)
54 {
55 sprintf(LinuxBootDescription, "Loading %s...", Description);
56 }
57 else
58 {
59 strcpy(LinuxBootDescription, "Loading Linux...");
60 }
61
62 UiDrawStatusText(LinuxBootDescription);
63 UiDrawProgressBarCenter(0, 100, LinuxBootDescription);
64
65 // Parse the .ini file section
66 if (!LinuxParseIniSection(OperatingSystemName))
67 {
68 goto LinuxBootFailed;
69 }
70
71 // Open the boot volume
72 if (!MachDiskNormalizeSystemPath(LinuxBootPath, sizeof(LinuxBootPath)))
73 {
74 UiMessageBox("Invalid boot path");
75 goto LinuxBootFailed;
76 }
77 if (!FsOpenSystemVolume(LinuxBootPath, NULL, NULL))
78 {
79 UiMessageBox("Failed to open boot drive.");
80 goto LinuxBootFailed;
81 }
82
83 // Open the kernel
84 LinuxKernel = FsOpenFile(LinuxKernelName);
85 if (LinuxKernel == NULL)
86 {
87 sprintf(TempString, "Linux kernel \'%s\' not found.", LinuxKernelName);
88 UiMessageBox(TempString);
89 goto LinuxBootFailed;
90 }
91
92 // Open the initrd file image (if necessary)
93 if (LinuxHasInitrd)
94 {
95 LinuxInitrdFile = FsOpenFile(LinuxInitrdName);
96 if (LinuxInitrdFile == NULL)
97 {
98 sprintf(TempString, "Linux initrd image \'%s\' not found.", LinuxInitrdName);
99 UiMessageBox(TempString);
100 goto LinuxBootFailed;
101 }
102 }
103
104 // Read the boot sector
105 if (!LinuxReadBootSector(LinuxKernel))
106 {
107 goto LinuxBootFailed;
108 }
109
110 // Read the setup sector
111 if (!LinuxReadSetupSector(LinuxKernel))
112 {
113 goto LinuxBootFailed;
114 }
115
116 // Calc kernel size
117 LinuxKernelSize = FsGetFileSize(LinuxKernel) - (512 + SetupSectorSize);
118
119 // Get the file size
120 LinuxInitrdSize = FsGetFileSize(LinuxInitrdFile);
121
122 // Read the kernel
123 if (!LinuxReadKernel(LinuxKernel))
124 {
125 goto LinuxBootFailed;
126 }
127
128 // Read the initrd (if necessary)
129 if (LinuxHasInitrd)
130 {
131 if (!LinuxReadInitrd(LinuxInitrdFile))
132 {
133 goto LinuxBootFailed;
134 }
135 }
136
137 // If the default root device is set to FLOPPY (0000h), change to /dev/fd0 (0200h)
138 if (LinuxBootSector->RootDevice == 0x0000)
139 {
140 LinuxBootSector->RootDevice = 0x0200;
141 }
142
143 if (LinuxSetupSector->Version >= 0x0202)
144 {
145 LinuxSetupSector->CommandLinePointer = 0x99000;
146 }
147 else
148 {
149 LinuxBootSector->CommandLineMagic = LINUX_COMMAND_LINE_MAGIC;
150 LinuxBootSector->CommandLineOffset = 0x9000;
151 }
152
153 if (NewStyleLinuxKernel)
154 {
155 LinuxSetupSector->TypeOfLoader = LINUX_LOADER_TYPE_FREELOADER;
156 }
157 else
158 {
159 LinuxSetupSector->LoadFlags = 0;
160 }
161
162 RtlCopyMemory((PVOID)0x90000, LinuxBootSector, 512);
163 RtlCopyMemory((PVOID)0x90200, LinuxSetupSector, SetupSectorSize);
164 RtlCopyMemory((PVOID)0x99000, LinuxCommandLine, LinuxCommandLineSize);
165
166 UiUnInitialize("Booting Linux...");
167
168 DiskStopFloppyMotor();
169
170 if (LinuxSetupSector->LoadFlags & LINUX_FLAG_LOAD_HIGH)
171 {
172 BootNewLinuxKernel();
173 }
174 else
175 {
176 BootOldLinuxKernel(LinuxKernelSize);
177 }
178
179
180 LinuxBootFailed:
181
182 if (LinuxKernel != NULL)
183 {
184 FsCloseFile(LinuxKernel);
185 }
186 if (LinuxInitrdFile != NULL)
187 {
188 FsCloseFile(LinuxInitrdFile);
189 }
190
191 if (LinuxBootSector != NULL)
192 {
193 MmFreeMemory(LinuxBootSector);
194 }
195 if (LinuxSetupSector != NULL)
196 {
197 MmFreeMemory(LinuxSetupSector);
198 }
199 if (LinuxKernelLoadAddress != NULL)
200 {
201 MmFreeMemory(LinuxKernelLoadAddress);
202 }
203 if (LinuxInitrdLoadAddress != NULL)
204 {
205 MmFreeMemory(LinuxInitrdLoadAddress);
206 }
207
208 LinuxBootSector = NULL;
209 LinuxSetupSector = NULL;
210 LinuxKernelLoadAddress = NULL;
211 LinuxInitrdLoadAddress = NULL;
212 SetupSectorSize = 0;
213 NewStyleLinuxKernel = FALSE;
214 LinuxKernelSize = 0;
215 LinuxHasInitrd = FALSE;
216 strcpy(LinuxCommandLine, "");
217 LinuxCommandLineSize = 0;
218 }
219
220 BOOLEAN LinuxParseIniSection(PCSTR OperatingSystemName)
221 {
222 CHAR SettingName[260];
223 ULONG SectionId;
224
225 // Find all the message box settings and run them
226 UiShowMessageBoxesInSection(OperatingSystemName);
227
228 // Try to open the operating system section in the .ini file
229 if (!IniOpenSection(OperatingSystemName, &SectionId))
230 {
231 sprintf(SettingName, "Section [%s] not found in freeldr.ini.\n", OperatingSystemName);
232 UiMessageBox(SettingName);
233 return FALSE;
234 }
235
236 if (!IniReadSettingByName(SectionId, "BootPath", LinuxBootPath, 260))
237 {
238 UiMessageBox("Boot path not specified for selected OS!");
239 return FALSE;
240 }
241
242 // Get the kernel name
243 if (!IniReadSettingByName(SectionId, "Kernel", LinuxKernelName, 260))
244 {
245 UiMessageBox("Linux kernel filename not specified for selected OS!");
246 return FALSE;
247 }
248
249 // Get the initrd name
250 if (IniReadSettingByName(SectionId, "Initrd", LinuxInitrdName, 260))
251 {
252 LinuxHasInitrd = TRUE;
253 }
254
255 // Get the command line
256 if (IniReadSettingByName(SectionId, "CommandLine", LinuxCommandLine, 260))
257 {
258 RemoveQuotes(LinuxCommandLine);
259 LinuxCommandLineSize = strlen(LinuxCommandLine) + 1;
260 }
261
262 return TRUE;
263 }
264
265 BOOLEAN LinuxReadBootSector(PFILE LinuxKernelFile)
266 {
267 // Allocate memory for boot sector
268 LinuxBootSector = (PLINUX_BOOTSECTOR)MmAllocateMemory(512);
269 if (LinuxBootSector == NULL)
270 {
271 return FALSE;
272 }
273
274 // Read linux boot sector
275 FsSetFilePointer(LinuxKernelFile, 0);
276 if (!FsReadFile(LinuxKernelFile, 512, NULL, LinuxBootSector))
277 {
278 return FALSE;
279 }
280
281 // Check for validity
282 if (LinuxBootSector->BootFlag != LINUX_BOOT_SECTOR_MAGIC)
283 {
284 UiMessageBox("Invalid boot sector magic (0xaa55)");
285 return FALSE;
286 }
287
288 DbgDumpBuffer(DPRINT_LINUX, LinuxBootSector, 512);
289
290 DbgPrint((DPRINT_LINUX, "SetupSectors: %d\n", LinuxBootSector->SetupSectors));
291 DbgPrint((DPRINT_LINUX, "RootFlags: 0x%x\n", LinuxBootSector->RootFlags));
292 DbgPrint((DPRINT_LINUX, "SystemSize: 0x%x\n", LinuxBootSector->SystemSize));
293 DbgPrint((DPRINT_LINUX, "SwapDevice: 0x%x\n", LinuxBootSector->SwapDevice));
294 DbgPrint((DPRINT_LINUX, "RamSize: 0x%x\n", LinuxBootSector->RamSize));
295 DbgPrint((DPRINT_LINUX, "VideoMode: 0x%x\n", LinuxBootSector->VideoMode));
296 DbgPrint((DPRINT_LINUX, "RootDevice: 0x%x\n", LinuxBootSector->RootDevice));
297 DbgPrint((DPRINT_LINUX, "BootFlag: 0x%x\n", LinuxBootSector->BootFlag));
298
299 return TRUE;
300 }
301
302 BOOLEAN LinuxReadSetupSector(PFILE LinuxKernelFile)
303 {
304 UCHAR TempLinuxSetupSector[512];
305
306 LinuxSetupSector = (PLINUX_SETUPSECTOR)TempLinuxSetupSector;
307
308 // Read first linux setup sector
309 FsSetFilePointer(LinuxKernelFile, 512);
310 if (!FsReadFile(LinuxKernelFile, 512, NULL, TempLinuxSetupSector))
311 {
312 return FALSE;
313 }
314
315 // Check the kernel version
316 if (!LinuxCheckKernelVersion())
317 {
318 return FALSE;
319 }
320
321 if (NewStyleLinuxKernel)
322 {
323 SetupSectorSize = 512 * LinuxBootSector->SetupSectors;
324 }
325 else
326 {
327 SetupSectorSize = 4 * 512; // Always 4 setup sectors
328 }
329
330 // Allocate memory for setup sectors
331 LinuxSetupSector = (PLINUX_SETUPSECTOR)MmAllocateMemory(SetupSectorSize);
332 if (LinuxSetupSector == NULL)
333 {
334 return FALSE;
335 }
336
337 // Copy over first setup sector
338 RtlCopyMemory(LinuxSetupSector, TempLinuxSetupSector, 512);
339
340 // Read in the rest of the linux setup sectors
341 FsSetFilePointer(LinuxKernelFile, 1024);
342 if (!FsReadFile(LinuxKernelFile, SetupSectorSize - 512, NULL, (PVOID)((ULONG_PTR)LinuxSetupSector + 512)))
343 {
344 return FALSE;
345 }
346
347 DbgDumpBuffer(DPRINT_LINUX, LinuxSetupSector, SetupSectorSize);
348
349 DbgPrint((DPRINT_LINUX, "SetupHeaderSignature: 0x%x (HdrS)\n", LinuxSetupSector->SetupHeaderSignature));
350 DbgPrint((DPRINT_LINUX, "Version: 0x%x\n", LinuxSetupSector->Version));
351 DbgPrint((DPRINT_LINUX, "RealModeSwitch: 0x%x\n", LinuxSetupSector->RealModeSwitch));
352 DbgPrint((DPRINT_LINUX, "SetupSeg: 0x%x\n", LinuxSetupSector->SetupSeg));
353 DbgPrint((DPRINT_LINUX, "StartSystemSeg: 0x%x\n", LinuxSetupSector->StartSystemSeg));
354 DbgPrint((DPRINT_LINUX, "KernelVersion: 0x%x\n", LinuxSetupSector->KernelVersion));
355 DbgPrint((DPRINT_LINUX, "TypeOfLoader: 0x%x\n", LinuxSetupSector->TypeOfLoader));
356 DbgPrint((DPRINT_LINUX, "LoadFlags: 0x%x\n", LinuxSetupSector->LoadFlags));
357 DbgPrint((DPRINT_LINUX, "SetupMoveSize: 0x%x\n", LinuxSetupSector->SetupMoveSize));
358 DbgPrint((DPRINT_LINUX, "Code32Start: 0x%x\n", LinuxSetupSector->Code32Start));
359 DbgPrint((DPRINT_LINUX, "RamdiskAddress: 0x%x\n", LinuxSetupSector->RamdiskAddress));
360 DbgPrint((DPRINT_LINUX, "RamdiskSize: 0x%x\n", LinuxSetupSector->RamdiskSize));
361 DbgPrint((DPRINT_LINUX, "BootSectKludgeOffset: 0x%x\n", LinuxSetupSector->BootSectKludgeOffset));
362 DbgPrint((DPRINT_LINUX, "BootSectKludgeSegment: 0x%x\n", LinuxSetupSector->BootSectKludgeSegment));
363 DbgPrint((DPRINT_LINUX, "HeapEnd: 0x%x\n", LinuxSetupSector->HeapEnd));
364
365 return TRUE;
366 }
367
368 BOOLEAN LinuxReadKernel(PFILE LinuxKernelFile)
369 {
370 ULONG BytesLoaded;
371 CHAR StatusText[260];
372 PVOID LoadAddress;
373
374 sprintf(StatusText, "Loading %s", LinuxKernelName);
375 UiDrawStatusText(StatusText);
376
377 // Allocate memory for Linux kernel
378 LinuxKernelLoadAddress = MmAllocateMemoryAtAddress(LinuxKernelSize, (PVOID)LINUX_KERNEL_LOAD_ADDRESS);
379 if (LinuxKernelLoadAddress != (PVOID)LINUX_KERNEL_LOAD_ADDRESS)
380 {
381 return FALSE;
382 }
383
384 LoadAddress = LinuxKernelLoadAddress;
385
386 // Read linux kernel to 0x100000 (1mb)
387 FsSetFilePointer(LinuxKernelFile, 512 + SetupSectorSize);
388 for (BytesLoaded=0; BytesLoaded<LinuxKernelSize; )
389 {
390 if (!FsReadFile(LinuxKernelFile, LINUX_READ_CHUNK_SIZE, NULL, LoadAddress))
391 {
392 return FALSE;
393 }
394
395 BytesLoaded += LINUX_READ_CHUNK_SIZE;
396 LoadAddress = (PVOID)((ULONG_PTR)LoadAddress + LINUX_READ_CHUNK_SIZE);
397
398 UiDrawProgressBarCenter(BytesLoaded, LinuxKernelSize + LinuxInitrdSize, LinuxBootDescription);
399 }
400
401 return TRUE;
402 }
403
404 BOOLEAN LinuxCheckKernelVersion(VOID)
405 {
406 // Just assume old kernel until we find otherwise
407 NewStyleLinuxKernel = FALSE;
408
409 // Check for new style setup header
410 if (LinuxSetupSector->SetupHeaderSignature != LINUX_SETUP_HEADER_ID)
411 {
412 NewStyleLinuxKernel = FALSE;
413 }
414 // Check for version below 2.0
415 else if (LinuxSetupSector->Version < 0x0200)
416 {
417 NewStyleLinuxKernel = FALSE;
418 }
419 // Check for version 2.0
420 else if (LinuxSetupSector->Version == 0x0200)
421 {
422 NewStyleLinuxKernel = TRUE;
423 }
424 // Check for version 2.01+
425 else if (LinuxSetupSector->Version >= 0x0201)
426 {
427 NewStyleLinuxKernel = TRUE;
428 LinuxSetupSector->HeapEnd = 0x9000;
429 LinuxSetupSector->LoadFlags |= LINUX_FLAG_CAN_USE_HEAP;
430 }
431
432 if ((NewStyleLinuxKernel == FALSE) && (LinuxHasInitrd == TRUE))
433 {
434 UiMessageBox("Error: Cannot load a ramdisk (initrd) with an old kernel image.");
435 return FALSE;
436 }
437
438 return TRUE;
439 }
440
441 BOOLEAN LinuxReadInitrd(PFILE LinuxInitrdFile)
442 {
443 ULONG BytesLoaded;
444 CHAR StatusText[260];
445
446 sprintf(StatusText, "Loading %s", LinuxInitrdName);
447 UiDrawStatusText(StatusText);
448
449 // Allocate memory for the ramdisk
450 //LinuxInitrdLoadAddress = MmAllocateMemory(LinuxInitrdSize);
451 // Try to align it at the next MB boundary after the kernel
452 //LinuxInitrdLoadAddress = MmAllocateMemoryAtAddress(LinuxInitrdSize, (PVOID)ROUND_UP((LINUX_KERNEL_LOAD_ADDRESS + LinuxKernelSize), 0x100000));
453 if (LinuxSetupSector->Version <= 0x0202)
454 {
455 LinuxInitrdLoadAddress = MmAllocateHighestMemoryBelowAddress(LinuxInitrdSize, (PVOID)LINUX_MAX_INITRD_ADDRESS);
456 }
457 else
458 {
459 LinuxInitrdLoadAddress = MmAllocateHighestMemoryBelowAddress(LinuxInitrdSize, (PVOID)LinuxSetupSector->InitrdAddressMax);
460 }
461 if (LinuxInitrdLoadAddress == NULL)
462 {
463 return FALSE;
464 }
465
466 // Set the information in the setup struct
467 LinuxSetupSector->RamdiskAddress = (ULONG)LinuxInitrdLoadAddress;
468 LinuxSetupSector->RamdiskSize = LinuxInitrdSize;
469
470 DbgPrint((DPRINT_LINUX, "RamdiskAddress: 0x%x\n", LinuxSetupSector->RamdiskAddress));
471 DbgPrint((DPRINT_LINUX, "RamdiskSize: 0x%x\n", LinuxSetupSector->RamdiskSize));
472
473 if (LinuxSetupSector->Version >= 0x0203)
474 {
475 DbgPrint((DPRINT_LINUX, "InitrdAddressMax: 0x%x\n", LinuxSetupSector->InitrdAddressMax));
476 }
477
478 // Read in the ramdisk
479 for (BytesLoaded=0; BytesLoaded<LinuxInitrdSize; )
480 {
481 if (!FsReadFile(LinuxInitrdFile, LINUX_READ_CHUNK_SIZE, NULL, (PVOID)LinuxInitrdLoadAddress))
482 {
483 return FALSE;
484 }
485
486 BytesLoaded += LINUX_READ_CHUNK_SIZE;
487 LinuxInitrdLoadAddress = (PVOID)((ULONG_PTR)LinuxInitrdLoadAddress + LINUX_READ_CHUNK_SIZE);
488
489 UiDrawProgressBarCenter(BytesLoaded + LinuxKernelSize, LinuxInitrdSize + LinuxKernelSize, LinuxBootDescription);
490 }
491
492 return TRUE;
493 }