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