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