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
29 /* INCLUDES *******************************************************************/
34 DBG_DEFAULT_CHANNEL(LINUX
);
36 /* GLOBALS ********************************************************************/
38 #define LINUX_READ_CHUNK_SIZE 0x20000 // Read 128k at a time
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
;
55 /* FUNCTIONS ******************************************************************/
59 IN OUT PSTR QuotedString
)
65 /* Skip spaces up to " */
67 while (*p
== ' ' || *p
== '\t' || *p
== '"')
72 while (*p
!= ANSI_NULL
&& *p
!= '"')
77 /* Move the NULL-terminated string back into 'QuotedString' in place */
78 Size
= (strlen(Start
) + 1) * sizeof(CHAR
);
79 memmove(QuotedString
, Start
, Size
);
89 ULONG LinuxKernel
= 0;
90 ULONG LinuxInitrdFile
= 0;
92 FILEINFORMATION FileInfo
;
94 Description
= GetArgumentValue(Argc
, Argv
, "LoadIdentifier");
96 RtlStringCbPrintfA(LinuxBootDescription
, sizeof(LinuxBootDescription
), "Loading %s...", Description
);
98 strcpy(LinuxBootDescription
, "Loading Linux...");
101 UiDrawStatusText(LinuxBootDescription
);
102 UiDrawProgressBarCenter(0, 100, LinuxBootDescription
);
104 /* Find all the message box settings and run them */
105 UiShowMessageBoxesInArgv(Argc
, Argv
);
107 /* Parse the .ini file section */
108 if (!LinuxParseIniSection(Argc
, Argv
))
109 goto LinuxBootFailed
;
111 /* Open the kernel */
112 LinuxKernel
= FsOpenFile(LinuxKernelName
);
115 UiMessageBox("Linux kernel \'%s\' not found.", LinuxKernelName
);
116 goto LinuxBootFailed
;
119 /* Open the initrd file image (if necessary) */
122 LinuxInitrdFile
= FsOpenFile(LinuxInitrdName
);
123 if (!LinuxInitrdFile
)
125 UiMessageBox("Linux initrd image \'%s\' not found.", LinuxInitrdName
);
126 goto LinuxBootFailed
;
130 /* Read the boot sector */
131 if (!LinuxReadBootSector(LinuxKernel
))
132 goto LinuxBootFailed
;
134 /* Read the setup sector */
135 if (!LinuxReadSetupSector(LinuxKernel
))
136 goto LinuxBootFailed
;
138 /* Calc kernel size */
139 Status
= ArcGetFileInformation(LinuxKernel
, &FileInfo
);
140 if (Status
!= ESUCCESS
|| FileInfo
.EndingAddress
.HighPart
!= 0)
143 LinuxKernelSize
= FileInfo
.EndingAddress
.LowPart
- (512 + SetupSectorSize
);
145 /* Get the initrd file image (if necessary) */
149 Status
= ArcGetFileInformation(LinuxInitrdFile
, &FileInfo
);
150 if (Status
!= ESUCCESS
|| FileInfo
.EndingAddress
.HighPart
!= 0)
153 LinuxInitrdSize
= FileInfo
.EndingAddress
.LowPart
;
156 /* Read the kernel */
157 if (!LinuxReadKernel(LinuxKernel
))
158 goto LinuxBootFailed
;
160 /* Read the initrd (if necessary) */
163 if (!LinuxReadInitrd(LinuxInitrdFile
))
164 goto LinuxBootFailed
;
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;
171 if (LinuxSetupSector
->Version
>= 0x0202)
173 LinuxSetupSector
->CommandLinePointer
= 0x99000;
177 LinuxBootSector
->CommandLineMagic
= LINUX_COMMAND_LINE_MAGIC
;
178 LinuxBootSector
->CommandLineOffset
= 0x9000;
181 if (NewStyleLinuxKernel
)
182 LinuxSetupSector
->TypeOfLoader
= LINUX_LOADER_TYPE_FREELOADER
;
184 LinuxSetupSector
->LoadFlags
= 0;
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
));
192 UiUnInitialize("Booting Linux...");
195 DiskStopFloppyMotor();
197 if (LinuxSetupSector
->LoadFlags
& LINUX_FLAG_LOAD_HIGH
)
198 BootNewLinuxKernel();
200 BootOldLinuxKernel(LinuxKernelSize
);
206 ArcClose(LinuxKernel
);
209 ArcClose(LinuxInitrdFile
);
211 if (LinuxBootSector
!= NULL
)
212 MmFreeMemory(LinuxBootSector
);
214 if (LinuxSetupSector
!= NULL
)
215 MmFreeMemory(LinuxSetupSector
);
217 if (LinuxKernelLoadAddress
!= NULL
)
218 MmFreeMemory(LinuxKernelLoadAddress
);
220 if (LinuxInitrdLoadAddress
!= NULL
)
221 MmFreeMemory(LinuxInitrdLoadAddress
);
223 LinuxBootSector
= NULL
;
224 LinuxSetupSector
= NULL
;
226 NewStyleLinuxKernel
= FALSE
;
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
;
242 LinuxParseIniSection(
247 LinuxBootPath
= GetArgumentValue(Argc
, Argv
, "BootPath");
250 UiMessageBox("Boot path not specified for selected OS!");
255 /* Get the kernel name */
256 LinuxKernelName
= GetArgumentValue(Argc
, Argv
, "Kernel");
257 if (!LinuxKernelName
)
259 UiMessageBox("Linux kernel filename not specified for selected OS!");
263 /* Get the initrd name (optional) */
264 LinuxInitrdName
= GetArgumentValue(Argc
, Argv
, "Initrd");
266 /* Get the command line (optional) */
267 LinuxCommandLineSize
= 0;
268 LinuxCommandLine
= GetArgumentValue(Argc
, Argv
, "CommandLine");
269 if (LinuxCommandLine
)
271 RemoveQuotes(LinuxCommandLine
);
272 LinuxCommandLineSize
= (ULONG
)strlen(LinuxCommandLine
) + 1;
273 LinuxCommandLineSize
= min(LinuxCommandLineSize
, 260);
279 BOOLEAN
LinuxReadBootSector(ULONG LinuxKernelFile
)
281 LARGE_INTEGER Position
;
283 /* Allocate memory for boot sector */
284 LinuxBootSector
= MmAllocateMemoryWithType(512, LoaderSystemCode
);
285 if (LinuxBootSector
== NULL
)
288 /* Read linux boot sector */
289 Position
.QuadPart
= 0;
290 if (ArcSeek(LinuxKernelFile
, &Position
, SeekAbsolute
) != ESUCCESS
)
292 if (ArcRead(LinuxKernelFile
, LinuxBootSector
, 512, NULL
) != ESUCCESS
)
295 /* Check for validity */
296 if (LinuxBootSector
->BootFlag
!= LINUX_BOOT_SECTOR_MAGIC
)
298 UiMessageBox("Invalid boot sector magic (0xaa55)");
302 // DbgDumpBuffer(DPRINT_LINUX, LinuxBootSector, 512);
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
);
316 BOOLEAN
LinuxReadSetupSector(ULONG LinuxKernelFile
)
318 LARGE_INTEGER Position
;
319 UCHAR TempLinuxSetupSector
[512];
321 /* Read first linux setup sector */
322 Position
.QuadPart
= 512;
323 if (ArcSeek(LinuxKernelFile
, &Position
, SeekAbsolute
) != ESUCCESS
)
325 if (ArcRead(LinuxKernelFile
, TempLinuxSetupSector
, 512, NULL
) != ESUCCESS
)
328 /* Check the kernel version */
329 LinuxSetupSector
= (PLINUX_SETUPSECTOR
)TempLinuxSetupSector
;
330 if (!LinuxCheckKernelVersion())
333 if (NewStyleLinuxKernel
)
334 SetupSectorSize
= 512 * LinuxBootSector
->SetupSectors
;
336 SetupSectorSize
= 512 * 4; // Always 4 setup sectors
338 /* Allocate memory for setup sectors */
339 LinuxSetupSector
= MmAllocateMemoryWithType(SetupSectorSize
, LoaderSystemCode
);
340 if (LinuxSetupSector
== NULL
)
343 /* Copy over first setup sector */
344 RtlCopyMemory(LinuxSetupSector
, TempLinuxSetupSector
, 512);
346 /* Read in the rest of the linux setup sectors */
347 Position
.QuadPart
= 1024;
348 if (ArcSeek(LinuxKernelFile
, &Position
, SeekAbsolute
) != ESUCCESS
)
350 if (ArcRead(LinuxKernelFile
, (PVOID
)((ULONG_PTR
)LinuxSetupSector
+ 512), SetupSectorSize
- 512, NULL
) != ESUCCESS
)
353 // DbgDumpBuffer(DPRINT_LINUX, LinuxSetupSector, SetupSectorSize);
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
);
374 BOOLEAN
LinuxReadKernel(ULONG LinuxKernelFile
)
377 LARGE_INTEGER Position
;
379 CHAR StatusText
[260];
381 RtlStringCbPrintfA(StatusText
, sizeof(StatusText
), "Loading %s", LinuxKernelName
);
382 UiDrawStatusText(StatusText
);
384 /* Allocate memory for Linux kernel */
385 LinuxKernelLoadAddress
= MmAllocateMemoryAtAddress(LinuxKernelSize
, (PVOID
)LINUX_KERNEL_LOAD_ADDRESS
, LoaderSystemCode
);
386 if (LinuxKernelLoadAddress
!= (PVOID
)LINUX_KERNEL_LOAD_ADDRESS
)
391 LoadAddress
= LinuxKernelLoadAddress
;
393 /* Read linux kernel to 0x100000 (1mb) */
394 Position
.QuadPart
= 512 + SetupSectorSize
;
395 if (ArcSeek(LinuxKernelFile
, &Position
, SeekAbsolute
) != ESUCCESS
)
397 for (BytesLoaded
=0; BytesLoaded
<LinuxKernelSize
; )
399 if (ArcRead(LinuxKernelFile
, LoadAddress
, LINUX_READ_CHUNK_SIZE
, NULL
) != ESUCCESS
)
402 BytesLoaded
+= LINUX_READ_CHUNK_SIZE
;
403 LoadAddress
= (PVOID
)((ULONG_PTR
)LoadAddress
+ LINUX_READ_CHUNK_SIZE
);
405 UiDrawProgressBarCenter(BytesLoaded
, LinuxKernelSize
+ LinuxInitrdSize
, LinuxBootDescription
);
411 BOOLEAN
LinuxCheckKernelVersion(VOID
)
413 /* Just assume old kernel until we find otherwise */
414 NewStyleLinuxKernel
= FALSE
;
416 /* Check for new style setup header */
417 if (LinuxSetupSector
->SetupHeaderSignature
!= LINUX_SETUP_HEADER_ID
)
419 NewStyleLinuxKernel
= FALSE
;
421 /* Check for version below 2.0 */
422 else if (LinuxSetupSector
->Version
< 0x0200)
424 NewStyleLinuxKernel
= FALSE
;
426 /* Check for version 2.0 */
427 else if (LinuxSetupSector
->Version
== 0x0200)
429 NewStyleLinuxKernel
= TRUE
;
431 /* Check for version 2.01+ */
432 else if (LinuxSetupSector
->Version
>= 0x0201)
434 NewStyleLinuxKernel
= TRUE
;
435 LinuxSetupSector
->HeapEnd
= 0x9000;
436 LinuxSetupSector
->LoadFlags
|= LINUX_FLAG_CAN_USE_HEAP
;
439 if ((NewStyleLinuxKernel
== FALSE
) && (LinuxInitrdName
))
441 UiMessageBox("Error: Cannot load a ramdisk (initrd) with an old kernel image.");
448 BOOLEAN
LinuxReadInitrd(ULONG LinuxInitrdFile
)
451 CHAR StatusText
[260];
453 RtlStringCbPrintfA(StatusText
, sizeof(StatusText
), "Loading %s", LinuxInitrdName
);
454 UiDrawStatusText(StatusText
);
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)
462 LinuxInitrdLoadAddress
= MmAllocateHighestMemoryBelowAddress(LinuxInitrdSize
, (PVOID
)LINUX_MAX_INITRD_ADDRESS
, LoaderSystemCode
);
466 LinuxInitrdLoadAddress
= MmAllocateHighestMemoryBelowAddress(LinuxInitrdSize
, (PVOID
)LinuxSetupSector
->InitrdAddressMax
, LoaderSystemCode
);
468 if (LinuxInitrdLoadAddress
== NULL
)
473 /* Set the information in the setup struct */
474 LinuxSetupSector
->RamdiskAddress
= (ULONG
)LinuxInitrdLoadAddress
;
475 LinuxSetupSector
->RamdiskSize
= LinuxInitrdSize
;
477 TRACE("RamdiskAddress: 0x%x\n", LinuxSetupSector
->RamdiskAddress
);
478 TRACE("RamdiskSize: 0x%x\n", LinuxSetupSector
->RamdiskSize
);
480 if (LinuxSetupSector
->Version
>= 0x0203)
482 TRACE("InitrdAddressMax: 0x%x\n", LinuxSetupSector
->InitrdAddressMax
);
485 /* Read in the ramdisk */
486 for (BytesLoaded
=0; BytesLoaded
<LinuxInitrdSize
; )
488 if (ArcRead(LinuxInitrdFile
, (PVOID
)LinuxInitrdLoadAddress
, LINUX_READ_CHUNK_SIZE
, NULL
) != ESUCCESS
)
491 BytesLoaded
+= LINUX_READ_CHUNK_SIZE
;
492 LinuxInitrdLoadAddress
= (PVOID
)((ULONG_PTR
)LinuxInitrdLoadAddress
+ LINUX_READ_CHUNK_SIZE
);
494 UiDrawProgressBarCenter(BytesLoaded
+ LinuxKernelSize
, LinuxInitrdSize
+ LinuxKernelSize
, LinuxBootDescription
);