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