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