3 * Copyright (C) 1998-2003 Brian Palmer <brianp@sginet.com>
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.
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.
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.
21 * The x86 Linux Boot Protocol is explained at:
22 * https://www.kernel.org/doc/Documentation/x86/boot.txt
25 #if defined(_M_IX86) || defined(_M_AMD64)
27 /* INCLUDES *******************************************************************/
32 DBG_DEFAULT_CHANNEL(LINUX
);
34 /* GLOBALS ********************************************************************/
36 #define LINUX_READ_CHUNK_SIZE 0x20000 // Read 128k at a time
38 PLINUX_BOOTSECTOR LinuxBootSector
= NULL
;
39 PLINUX_SETUPSECTOR LinuxSetupSector
= NULL
;
40 ULONG SetupSectorSize
= 0;
41 BOOLEAN NewStyleLinuxKernel
= FALSE
;
42 ULONG LinuxKernelSize
= 0;
43 ULONG LinuxInitrdSize
= 0;
44 PCSTR LinuxKernelName
= NULL
;
45 PCSTR LinuxInitrdName
= NULL
;
46 PSTR LinuxCommandLine
= NULL
;
47 ULONG LinuxCommandLineSize
= 0;
48 PVOID LinuxKernelLoadAddress
= NULL
;
49 PVOID LinuxInitrdLoadAddress
= NULL
;
50 CHAR LinuxBootDescription
[80];
52 /* FUNCTIONS ******************************************************************/
54 static BOOLEAN
LinuxReadBootSector(ULONG LinuxKernelFile
);
55 static BOOLEAN
LinuxReadSetupSector(ULONG LinuxKernelFile
);
56 static BOOLEAN
LinuxReadKernel(ULONG LinuxKernelFile
);
57 static BOOLEAN
LinuxCheckKernelVersion(VOID
);
58 static BOOLEAN
LinuxReadInitrd(ULONG LinuxInitrdFile
);
62 IN OUT PSTR QuotedString
)
68 /* Skip spaces up to " */
70 while (*p
== ' ' || *p
== '\t' || *p
== '"')
75 while (*p
!= ANSI_NULL
&& *p
!= '"')
80 /* Move the NULL-terminated string back into 'QuotedString' in place */
81 Size
= (strlen(Start
) + 1) * sizeof(CHAR
);
82 memmove(QuotedString
, Start
, Size
);
95 UCHAR DriveNumber
= 0;
96 ULONG PartitionNumber
= 0;
97 ULONG LinuxKernel
= 0;
98 ULONG LinuxInitrdFile
= 0;
99 FILEINFORMATION FileInfo
;
100 CHAR ArcPath
[MAX_PATH
];
102 Description
= GetArgumentValue(Argc
, Argv
, "LoadIdentifier");
103 if (Description
&& *Description
)
104 RtlStringCbPrintfA(LinuxBootDescription
, sizeof(LinuxBootDescription
), "Loading %s...", Description
);
106 strcpy(LinuxBootDescription
, "Loading Linux...");
109 UiDrawStatusText(LinuxBootDescription
);
110 UiDrawProgressBarCenter(0, 100, LinuxBootDescription
);
112 /* Find all the message box settings and run them */
113 UiShowMessageBoxesInArgv(Argc
, Argv
);
116 * Check whether we have a "BootPath" value (takes precedence
117 * over both "BootDrive" and "BootPartition").
119 BootPath
= GetArgumentValue(Argc
, Argv
, "BootPath");
120 if (!BootPath
|| !*BootPath
)
122 /* We don't have one, check whether we use "BootDrive" and "BootPartition" */
124 /* Retrieve the boot drive (optional, fall back to using default path otherwise) */
125 ArgValue
= GetArgumentValue(Argc
, Argv
, "BootDrive");
126 if (ArgValue
&& *ArgValue
)
128 DriveNumber
= DriveMapGetBiosDriveNumber(ArgValue
);
130 /* Retrieve the boot partition (not optional and cannot be zero) */
132 ArgValue
= GetArgumentValue(Argc
, Argv
, "BootPartition");
133 if (ArgValue
&& *ArgValue
)
134 PartitionNumber
= atoi(ArgValue
);
135 if (PartitionNumber
== 0)
137 UiMessageBox("Boot partition cannot be 0!");
138 goto LinuxBootFailed
;
142 /* Construct the corresponding ARC path */
143 ConstructArcPath(ArcPath
, "", DriveNumber
, PartitionNumber
);
144 *strrchr(ArcPath
, '\\') = ANSI_NULL
; // Trim the trailing path separator.
150 /* Fall back to using the system partition as default path */
151 BootPath
= GetArgumentValue(Argc
, Argv
, "SystemPartition");
155 /* If we haven't retrieved the BIOS drive and partition numbers above, do it now */
156 if (PartitionNumber
== 0)
158 /* Retrieve the BIOS drive and partition numbers */
159 if (!DissectArcPath(BootPath
, NULL
, &DriveNumber
, &PartitionNumber
))
161 /* This is not a fatal failure, but just an inconvenience: display a message */
162 TRACE("DissectArcPath(%s) failed to retrieve BIOS drive and partition numbers.\n", BootPath
);
166 /* Get the kernel name */
167 LinuxKernelName
= GetArgumentValue(Argc
, Argv
, "Kernel");
168 if (!LinuxKernelName
|| !*LinuxKernelName
)
170 UiMessageBox("Linux kernel filename not specified for selected OS!");
171 goto LinuxBootFailed
;
174 /* Get the initrd name (optional) */
175 LinuxInitrdName
= GetArgumentValue(Argc
, Argv
, "Initrd");
177 /* Get the command line (optional) */
178 LinuxCommandLineSize
= 0;
179 LinuxCommandLine
= GetArgumentValue(Argc
, Argv
, "CommandLine");
180 if (LinuxCommandLine
&& *LinuxCommandLine
)
182 RemoveQuotes(LinuxCommandLine
);
183 LinuxCommandLineSize
= (ULONG
)strlen(LinuxCommandLine
) + 1;
184 LinuxCommandLineSize
= min(LinuxCommandLineSize
, 260);
187 /* Open the kernel */
188 Status
= FsOpenFile(LinuxKernelName
, BootPath
, OpenReadOnly
, &LinuxKernel
);
189 if (Status
!= ESUCCESS
)
191 UiMessageBox("Linux kernel '%s' not found.", LinuxKernelName
);
192 goto LinuxBootFailed
;
195 /* Open the initrd file image (if necessary) */
198 Status
= FsOpenFile(LinuxInitrdName
, BootPath
, OpenReadOnly
, &LinuxInitrdFile
);
199 if (Status
!= ESUCCESS
)
201 UiMessageBox("Linux initrd image '%s' not found.", LinuxInitrdName
);
202 goto LinuxBootFailed
;
206 /* Load the boot sector */
207 if (!LinuxReadBootSector(LinuxKernel
))
208 goto LinuxBootFailed
;
210 /* Load the setup sector */
211 if (!LinuxReadSetupSector(LinuxKernel
))
212 goto LinuxBootFailed
;
214 /* Calc kernel size */
215 Status
= ArcGetFileInformation(LinuxKernel
, &FileInfo
);
216 if (Status
!= ESUCCESS
|| FileInfo
.EndingAddress
.HighPart
!= 0)
219 LinuxKernelSize
= FileInfo
.EndingAddress
.LowPart
- (512 + SetupSectorSize
);
221 /* Get the initrd file image (if necessary) */
225 Status
= ArcGetFileInformation(LinuxInitrdFile
, &FileInfo
);
226 if (Status
!= ESUCCESS
|| FileInfo
.EndingAddress
.HighPart
!= 0)
229 LinuxInitrdSize
= FileInfo
.EndingAddress
.LowPart
;
232 /* Load the kernel */
233 if (!LinuxReadKernel(LinuxKernel
))
234 goto LinuxBootFailed
;
236 /* Load the initrd (if necessary) */
239 if (!LinuxReadInitrd(LinuxInitrdFile
))
240 goto LinuxBootFailed
;
243 // If the default root device is set to FLOPPY (0000h), change to /dev/fd0 (0200h)
244 if (LinuxBootSector
->RootDevice
== 0x0000)
245 LinuxBootSector
->RootDevice
= 0x0200;
247 if (LinuxSetupSector
->Version
>= 0x0202)
249 LinuxSetupSector
->CommandLinePointer
= 0x99000;
253 LinuxBootSector
->CommandLineMagic
= LINUX_COMMAND_LINE_MAGIC
;
254 LinuxBootSector
->CommandLineOffset
= 0x9000;
257 if (NewStyleLinuxKernel
)
258 LinuxSetupSector
->TypeOfLoader
= LINUX_LOADER_TYPE_FREELOADER
;
260 LinuxSetupSector
->LoadFlags
= 0;
262 RtlCopyMemory((PVOID
)0x90000, LinuxBootSector
, 512);
263 RtlCopyMemory((PVOID
)0x90200, LinuxSetupSector
, SetupSectorSize
);
264 RtlCopyMemory((PVOID
)0x99000,
265 LinuxCommandLine
? LinuxCommandLine
: "",
266 LinuxCommandLine
? LinuxCommandLineSize
: sizeof(ANSI_NULL
));
268 UiUnInitialize("Booting Linux...");
271 BootLinuxKernel(LinuxKernelSize
, LinuxKernelLoadAddress
,
272 (LinuxSetupSector
->LoadFlags
& LINUX_FLAG_LOAD_HIGH
)
273 ? (PVOID
)LINUX_KERNEL_LOAD_ADDRESS
/* == 0x100000 */
275 DriveNumber
, PartitionNumber
);
276 /* Must not return! */
282 ArcClose(LinuxKernel
);
285 ArcClose(LinuxInitrdFile
);
287 if (LinuxBootSector
!= NULL
)
288 MmFreeMemory(LinuxBootSector
);
290 if (LinuxSetupSector
!= NULL
)
291 MmFreeMemory(LinuxSetupSector
);
293 if (LinuxKernelLoadAddress
!= NULL
)
294 MmFreeMemory(LinuxKernelLoadAddress
);
296 if (LinuxInitrdLoadAddress
!= NULL
)
297 MmFreeMemory(LinuxInitrdLoadAddress
);
299 LinuxBootSector
= NULL
;
300 LinuxSetupSector
= NULL
;
302 NewStyleLinuxKernel
= FALSE
;
305 LinuxKernelName
= NULL
;
306 LinuxInitrdName
= NULL
;
307 LinuxCommandLine
= NULL
;
308 LinuxCommandLineSize
= 0;
309 LinuxKernelLoadAddress
= NULL
;
310 LinuxInitrdLoadAddress
= NULL
;
311 *LinuxBootDescription
= ANSI_NULL
;
316 static BOOLEAN
LinuxReadBootSector(ULONG LinuxKernelFile
)
318 LARGE_INTEGER Position
;
320 /* Allocate memory for boot sector */
321 LinuxBootSector
= MmAllocateMemoryWithType(512, LoaderSystemCode
);
322 if (LinuxBootSector
== NULL
)
325 /* Load the linux boot sector */
326 Position
.QuadPart
= 0;
327 if (ArcSeek(LinuxKernelFile
, &Position
, SeekAbsolute
) != ESUCCESS
)
329 if (ArcRead(LinuxKernelFile
, LinuxBootSector
, 512, NULL
) != ESUCCESS
)
332 /* Check for validity */
333 if (LinuxBootSector
->BootFlag
!= LINUX_BOOT_SECTOR_MAGIC
)
335 UiMessageBox("Invalid boot sector magic (0xaa55)");
339 // DbgDumpBuffer(DPRINT_LINUX, LinuxBootSector, 512);
341 TRACE("SetupSectors: %d\n" , LinuxBootSector
->SetupSectors
);
342 TRACE("RootFlags: 0x%x\n", LinuxBootSector
->RootFlags
);
343 TRACE("SystemSize: 0x%x\n", LinuxBootSector
->SystemSize
);
344 TRACE("SwapDevice: 0x%x\n", LinuxBootSector
->SwapDevice
);
345 TRACE("RamSize: 0x%x\n", LinuxBootSector
->RamSize
);
346 TRACE("VideoMode: 0x%x\n", LinuxBootSector
->VideoMode
);
347 TRACE("RootDevice: 0x%x\n", LinuxBootSector
->RootDevice
);
348 TRACE("BootFlag: 0x%x\n", LinuxBootSector
->BootFlag
);
353 static BOOLEAN
LinuxReadSetupSector(ULONG LinuxKernelFile
)
355 LARGE_INTEGER Position
;
356 UCHAR TempLinuxSetupSector
[512];
358 /* Load the first linux setup sector */
359 Position
.QuadPart
= 512;
360 if (ArcSeek(LinuxKernelFile
, &Position
, SeekAbsolute
) != ESUCCESS
)
362 if (ArcRead(LinuxKernelFile
, TempLinuxSetupSector
, 512, NULL
) != ESUCCESS
)
365 /* Check the kernel version */
366 LinuxSetupSector
= (PLINUX_SETUPSECTOR
)TempLinuxSetupSector
;
367 if (!LinuxCheckKernelVersion())
370 if (NewStyleLinuxKernel
)
371 SetupSectorSize
= 512 * LinuxBootSector
->SetupSectors
;
373 SetupSectorSize
= 512 * 4; // Always 4 setup sectors
375 /* Allocate memory for setup sectors */
376 LinuxSetupSector
= MmAllocateMemoryWithType(SetupSectorSize
, LoaderSystemCode
);
377 if (LinuxSetupSector
== NULL
)
380 /* Copy over first setup sector */
381 RtlCopyMemory(LinuxSetupSector
, TempLinuxSetupSector
, 512);
383 /* Load the rest of the linux setup sectors */
384 Position
.QuadPart
= 1024;
385 if (ArcSeek(LinuxKernelFile
, &Position
, SeekAbsolute
) != ESUCCESS
)
387 if (ArcRead(LinuxKernelFile
, (PVOID
)((ULONG_PTR
)LinuxSetupSector
+ 512), SetupSectorSize
- 512, NULL
) != ESUCCESS
)
390 // DbgDumpBuffer(DPRINT_LINUX, LinuxSetupSector, SetupSectorSize);
392 TRACE("SetupHeaderSignature: 0x%x (HdrS)\n", LinuxSetupSector
->SetupHeaderSignature
);
393 TRACE("Version: 0x%x\n", LinuxSetupSector
->Version
);
394 TRACE("RealModeSwitch: 0x%x\n", LinuxSetupSector
->RealModeSwitch
);
395 TRACE("SetupSeg: 0x%x\n", LinuxSetupSector
->SetupSeg
);
396 TRACE("StartSystemSeg: 0x%x\n", LinuxSetupSector
->StartSystemSeg
);
397 TRACE("KernelVersion: 0x%x\n", LinuxSetupSector
->KernelVersion
);
398 TRACE("TypeOfLoader: 0x%x\n", LinuxSetupSector
->TypeOfLoader
);
399 TRACE("LoadFlags: 0x%x\n", LinuxSetupSector
->LoadFlags
);
400 TRACE("SetupMoveSize: 0x%x\n", LinuxSetupSector
->SetupMoveSize
);
401 TRACE("Code32Start: 0x%x\n", LinuxSetupSector
->Code32Start
);
402 TRACE("RamdiskAddress: 0x%x\n", LinuxSetupSector
->RamdiskAddress
);
403 TRACE("RamdiskSize: 0x%x\n", LinuxSetupSector
->RamdiskSize
);
404 TRACE("BootSectKludgeOffset: 0x%x\n", LinuxSetupSector
->BootSectKludgeOffset
);
405 TRACE("BootSectKludgeSegment: 0x%x\n", LinuxSetupSector
->BootSectKludgeSegment
);
406 TRACE("HeapEnd: 0x%x\n", LinuxSetupSector
->HeapEnd
);
411 static BOOLEAN
LinuxReadKernel(ULONG LinuxKernelFile
)
414 LARGE_INTEGER Position
;
416 CHAR StatusText
[260];
418 RtlStringCbPrintfA(StatusText
, sizeof(StatusText
), "Loading %s", LinuxKernelName
);
419 UiDrawStatusText(StatusText
);
421 /* Try to allocate memory for the Linux kernel; if it fails, allocate somewhere else */
422 LinuxKernelLoadAddress
= MmAllocateMemoryAtAddress(LinuxKernelSize
, (PVOID
)LINUX_KERNEL_LOAD_ADDRESS
, LoaderSystemCode
);
423 if (LinuxKernelLoadAddress
!= (PVOID
)LINUX_KERNEL_LOAD_ADDRESS
)
425 /* It's OK, let's allocate again somewhere else */
426 LinuxKernelLoadAddress
= MmAllocateMemoryWithType(LinuxKernelSize
, LoaderSystemCode
);
427 if (LinuxKernelLoadAddress
== NULL
)
429 TRACE("Failed to allocate 0x%lx bytes for the kernel image.\n", LinuxKernelSize
);
434 LoadAddress
= LinuxKernelLoadAddress
;
436 /* Load the linux kernel at 0x100000 (1mb) */
437 Position
.QuadPart
= 512 + SetupSectorSize
;
438 if (ArcSeek(LinuxKernelFile
, &Position
, SeekAbsolute
) != ESUCCESS
)
440 for (BytesLoaded
= 0; BytesLoaded
< LinuxKernelSize
; )
442 if (ArcRead(LinuxKernelFile
, LoadAddress
, LINUX_READ_CHUNK_SIZE
, NULL
) != ESUCCESS
)
445 BytesLoaded
+= LINUX_READ_CHUNK_SIZE
;
446 LoadAddress
= (PVOID
)((ULONG_PTR
)LoadAddress
+ LINUX_READ_CHUNK_SIZE
);
448 UiDrawProgressBarCenter(BytesLoaded
, LinuxKernelSize
+ LinuxInitrdSize
, LinuxBootDescription
);
454 static BOOLEAN
LinuxCheckKernelVersion(VOID
)
456 /* Just assume old kernel until we find otherwise */
457 NewStyleLinuxKernel
= FALSE
;
459 /* Check for new style setup header */
460 if (LinuxSetupSector
->SetupHeaderSignature
!= LINUX_SETUP_HEADER_ID
)
462 NewStyleLinuxKernel
= FALSE
;
464 /* Check for version below 2.0 */
465 else if (LinuxSetupSector
->Version
< 0x0200)
467 NewStyleLinuxKernel
= FALSE
;
469 /* Check for version 2.0 */
470 else if (LinuxSetupSector
->Version
== 0x0200)
472 NewStyleLinuxKernel
= TRUE
;
474 /* Check for version 2.01+ */
475 else if (LinuxSetupSector
->Version
>= 0x0201)
477 NewStyleLinuxKernel
= TRUE
;
478 LinuxSetupSector
->HeapEnd
= 0x9000;
479 LinuxSetupSector
->LoadFlags
|= LINUX_FLAG_CAN_USE_HEAP
;
482 if ((NewStyleLinuxKernel
== FALSE
) && (LinuxInitrdName
))
484 UiMessageBox("Error: Cannot load a ramdisk (initrd) with an old kernel image.");
491 static BOOLEAN
LinuxReadInitrd(ULONG LinuxInitrdFile
)
494 CHAR StatusText
[260];
496 RtlStringCbPrintfA(StatusText
, sizeof(StatusText
), "Loading %s", LinuxInitrdName
);
497 UiDrawStatusText(StatusText
);
499 /* Allocate memory for the ramdisk, below 4GB */
500 // LinuxInitrdLoadAddress = MmAllocateMemory(LinuxInitrdSize);
501 /* Try to align it at the next MB boundary after the kernel */
502 // LinuxInitrdLoadAddress = MmAllocateMemoryAtAddress(LinuxInitrdSize, (PVOID)ROUND_UP((LINUX_KERNEL_LOAD_ADDRESS + LinuxKernelSize), 0x100000));
503 if (LinuxSetupSector
->Version
<= 0x0202)
506 C_ASSERT(LINUX_MAX_INITRD_ADDRESS
< 0x100000000);
508 LinuxInitrdLoadAddress
= MmAllocateHighestMemoryBelowAddress(LinuxInitrdSize
, (PVOID
)LINUX_MAX_INITRD_ADDRESS
, LoaderSystemCode
);
512 LinuxInitrdLoadAddress
= MmAllocateHighestMemoryBelowAddress(LinuxInitrdSize
, UlongToPtr(LinuxSetupSector
->InitrdAddressMax
), LoaderSystemCode
);
514 if (LinuxInitrdLoadAddress
== NULL
)
519 ASSERT((ULONG_PTR
)LinuxInitrdLoadAddress
< 0x100000000);
522 /* Set the information in the setup struct */
523 LinuxSetupSector
->RamdiskAddress
= PtrToUlong(LinuxInitrdLoadAddress
);
524 LinuxSetupSector
->RamdiskSize
= LinuxInitrdSize
;
526 TRACE("RamdiskAddress: 0x%x\n", LinuxSetupSector
->RamdiskAddress
);
527 TRACE("RamdiskSize: 0x%x\n", LinuxSetupSector
->RamdiskSize
);
529 if (LinuxSetupSector
->Version
>= 0x0203)
531 TRACE("InitrdAddressMax: 0x%x\n", LinuxSetupSector
->InitrdAddressMax
);
534 /* Load the ramdisk */
535 for (BytesLoaded
= 0; BytesLoaded
< LinuxInitrdSize
; )
537 if (ArcRead(LinuxInitrdFile
, LinuxInitrdLoadAddress
, LINUX_READ_CHUNK_SIZE
, NULL
) != ESUCCESS
)
540 BytesLoaded
+= LINUX_READ_CHUNK_SIZE
;
541 LinuxInitrdLoadAddress
= (PVOID
)((ULONG_PTR
)LinuxInitrdLoadAddress
+ LINUX_READ_CHUNK_SIZE
);
543 UiDrawProgressBarCenter(BytesLoaded
+ LinuxKernelSize
, LinuxInitrdSize
+ LinuxKernelSize
, LinuxBootDescription
);
549 #endif /* _M_IX86 || _M_AMD64 */