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