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