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