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