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