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