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