mirror of
https://github.com/reactos/reactos.git
synced 2024-06-25 23:41:35 +00:00
![Hermès Bélusca-Maïto](/assets/img/avatar_default.png)
CORE-9023 - During loading and initialization of the list of operating systems available in freeldr.ini, convert any legacy operating system entry encountered -- they are like those in NTLDR's boot.ini file, i.e.: ArcOsLoadPartition="LoadIdentifier" /List /of /Options into a new operating system INI entry, like those used by default in FreeLoader. This allows us to avoid treating this corner-case later in different parts of the code. Also, the "BootType" value is now determined there, only once. - Convert the OS loaders entry-points to ARC-compatible ones, following the "Advanced RISC Computing Specification, Version 1.2" specification https://www.netbsd.org/docs/Hardware/Machines/ARC/riscspec.pdf - Introduce helpers for retrieving options values from the argument vector in a simple way. - Simplify LoadOperatingSystem(), since now the "BootType" value has been determined once while loading the list of OSes (see above) and is well-defined there. Use the BuildArgvForOsLoader() helper to build the ARC-compatible argument vector from the corresponding INI settings for the selected operating system entry, and use it when calling the corresponding OS loader. - In the OS loaders, since we can now directly read the settings from the argument vector (instead of using INI settings), we can avoid using a bunch of fixed-size string buffers, and avoid potentially failing IniOpenSection() calls as well. - Simplify code in the Linux loader (and the RemoveQuotes() function). - Add UiShowMessageBoxesInArgv() that acts on the "MessageBox=" settings passed through the argument vector (equivalent to UiShowMessageBoxesInSection() ). - Use string-safe functions where needed (copy/concatenation/printf on fixed-size buffers).
469 lines
14 KiB
C
469 lines
14 KiB
C
/*
|
|
* FreeLoader
|
|
* Copyright (C) 1998-2003 Brian Palmer <brianp@sginet.com>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*/
|
|
|
|
#ifndef _M_ARM
|
|
|
|
#ifdef _M_IX86
|
|
|
|
/* INCLUDES *******************************************************************/
|
|
|
|
#include <freeldr.h>
|
|
#include <debug.h>
|
|
|
|
DBG_DEFAULT_CHANNEL(LINUX);
|
|
|
|
/* GLOBALS ********************************************************************/
|
|
|
|
#define LINUX_READ_CHUNK_SIZE 0x20000 // Read 128k at a time
|
|
|
|
PLINUX_BOOTSECTOR LinuxBootSector = NULL;
|
|
PLINUX_SETUPSECTOR LinuxSetupSector = NULL;
|
|
ULONG SetupSectorSize = 0;
|
|
BOOLEAN NewStyleLinuxKernel = FALSE;
|
|
ULONG LinuxKernelSize = 0;
|
|
ULONG LinuxInitrdSize = 0;
|
|
PCSTR LinuxKernelName = NULL;
|
|
PCSTR LinuxInitrdName = NULL;
|
|
PSTR LinuxCommandLine = NULL;
|
|
ULONG LinuxCommandLineSize = 0;
|
|
PVOID LinuxKernelLoadAddress = NULL;
|
|
PVOID LinuxInitrdLoadAddress = NULL;
|
|
CHAR LinuxBootDescription[80];
|
|
PCSTR LinuxBootPath = NULL;
|
|
|
|
/* FUNCTIONS ******************************************************************/
|
|
|
|
VOID
|
|
RemoveQuotes(
|
|
IN OUT PSTR QuotedString)
|
|
{
|
|
PCHAR p;
|
|
PSTR Start;
|
|
SIZE_T Size;
|
|
|
|
/* Skip spaces up to " */
|
|
p = QuotedString;
|
|
while (*p == ' ' || *p == '\t' || *p == '"')
|
|
++p;
|
|
Start = p;
|
|
|
|
/* Go up to next " */
|
|
while (*p != ANSI_NULL && *p != '"')
|
|
++p;
|
|
/* NULL-terminate */
|
|
*p = ANSI_NULL;
|
|
|
|
/* Move the NULL-terminated string back into 'QuotedString' in place */
|
|
Size = (strlen(Start) + 1) * sizeof(CHAR);
|
|
memmove(QuotedString, Start, Size);
|
|
}
|
|
|
|
ARC_STATUS
|
|
LoadAndBootLinux(
|
|
IN ULONG Argc,
|
|
IN PCHAR Argv[],
|
|
IN PCHAR Envp[])
|
|
{
|
|
PCSTR Description;
|
|
PFILE LinuxKernel = 0;
|
|
PFILE LinuxInitrdFile = 0;
|
|
|
|
Description = GetArgumentValue(Argc, Argv, "LoadIdentifier");
|
|
if (Description)
|
|
RtlStringCbPrintfA(LinuxBootDescription, sizeof(LinuxBootDescription), "Loading %s...", Description);
|
|
else
|
|
strcpy(LinuxBootDescription, "Loading Linux...");
|
|
|
|
UiDrawBackdrop();
|
|
UiDrawStatusText(LinuxBootDescription);
|
|
UiDrawProgressBarCenter(0, 100, LinuxBootDescription);
|
|
|
|
/* Find all the message box settings and run them */
|
|
UiShowMessageBoxesInArgv(Argc, Argv);
|
|
|
|
/* Parse the .ini file section */
|
|
if (!LinuxParseIniSection(Argc, Argv))
|
|
goto LinuxBootFailed;
|
|
|
|
/* Open the kernel */
|
|
LinuxKernel = FsOpenFile(LinuxKernelName);
|
|
if (!LinuxKernel)
|
|
{
|
|
UiMessageBox("Linux kernel \'%s\' not found.", LinuxKernelName);
|
|
goto LinuxBootFailed;
|
|
}
|
|
|
|
/* Open the initrd file image (if necessary) */
|
|
if (LinuxInitrdName)
|
|
{
|
|
LinuxInitrdFile = FsOpenFile(LinuxInitrdName);
|
|
if (!LinuxInitrdFile)
|
|
{
|
|
UiMessageBox("Linux initrd image \'%s\' not found.", LinuxInitrdName);
|
|
goto LinuxBootFailed;
|
|
}
|
|
}
|
|
|
|
/* Read the boot sector */
|
|
if (!LinuxReadBootSector(LinuxKernel))
|
|
goto LinuxBootFailed;
|
|
|
|
/* Read the setup sector */
|
|
if (!LinuxReadSetupSector(LinuxKernel))
|
|
goto LinuxBootFailed;
|
|
|
|
/* Calc kernel size */
|
|
LinuxKernelSize = FsGetFileSize(LinuxKernel) - (512 + SetupSectorSize);
|
|
|
|
/* Get the file size */
|
|
LinuxInitrdSize = FsGetFileSize(LinuxInitrdFile);
|
|
|
|
/* Read the kernel */
|
|
if (!LinuxReadKernel(LinuxKernel))
|
|
goto LinuxBootFailed;
|
|
|
|
/* Read the initrd (if necessary) */
|
|
if (LinuxInitrdName)
|
|
{
|
|
if (!LinuxReadInitrd(LinuxInitrdFile))
|
|
goto LinuxBootFailed;
|
|
}
|
|
|
|
// If the default root device is set to FLOPPY (0000h), change to /dev/fd0 (0200h)
|
|
if (LinuxBootSector->RootDevice == 0x0000)
|
|
LinuxBootSector->RootDevice = 0x0200;
|
|
|
|
if (LinuxSetupSector->Version >= 0x0202)
|
|
{
|
|
LinuxSetupSector->CommandLinePointer = 0x99000;
|
|
}
|
|
else
|
|
{
|
|
LinuxBootSector->CommandLineMagic = LINUX_COMMAND_LINE_MAGIC;
|
|
LinuxBootSector->CommandLineOffset = 0x9000;
|
|
}
|
|
|
|
if (NewStyleLinuxKernel)
|
|
LinuxSetupSector->TypeOfLoader = LINUX_LOADER_TYPE_FREELOADER;
|
|
else
|
|
LinuxSetupSector->LoadFlags = 0;
|
|
|
|
RtlCopyMemory((PVOID)0x90000, LinuxBootSector, 512);
|
|
RtlCopyMemory((PVOID)0x90200, LinuxSetupSector, SetupSectorSize);
|
|
RtlCopyMemory((PVOID)0x99000,
|
|
LinuxCommandLine ? LinuxCommandLine : "",
|
|
LinuxCommandLine ? LinuxCommandLineSize : sizeof(ANSI_NULL));
|
|
|
|
UiUnInitialize("Booting Linux...");
|
|
IniCleanup();
|
|
|
|
DiskStopFloppyMotor();
|
|
|
|
if (LinuxSetupSector->LoadFlags & LINUX_FLAG_LOAD_HIGH)
|
|
BootNewLinuxKernel();
|
|
else
|
|
BootOldLinuxKernel(LinuxKernelSize);
|
|
|
|
|
|
LinuxBootFailed:
|
|
|
|
if (LinuxKernel)
|
|
FsCloseFile(LinuxKernel);
|
|
|
|
if (LinuxInitrdFile)
|
|
FsCloseFile(LinuxInitrdFile);
|
|
|
|
if (LinuxBootSector != NULL)
|
|
MmFreeMemory(LinuxBootSector);
|
|
|
|
if (LinuxSetupSector != NULL)
|
|
MmFreeMemory(LinuxSetupSector);
|
|
|
|
if (LinuxKernelLoadAddress != NULL)
|
|
MmFreeMemory(LinuxKernelLoadAddress);
|
|
|
|
if (LinuxInitrdLoadAddress != NULL)
|
|
MmFreeMemory(LinuxInitrdLoadAddress);
|
|
|
|
LinuxBootSector = NULL;
|
|
LinuxSetupSector = NULL;
|
|
SetupSectorSize = 0;
|
|
NewStyleLinuxKernel = FALSE;
|
|
LinuxKernelSize = 0;
|
|
LinuxInitrdSize = 0;
|
|
LinuxKernelName = NULL;
|
|
LinuxInitrdName = NULL;
|
|
LinuxCommandLine = NULL;
|
|
LinuxCommandLineSize = 0;
|
|
LinuxKernelLoadAddress = NULL;
|
|
LinuxInitrdLoadAddress = NULL;
|
|
*LinuxBootDescription = ANSI_NULL;
|
|
LinuxBootPath = NULL;
|
|
|
|
return ENOEXEC;
|
|
}
|
|
|
|
BOOLEAN
|
|
LinuxParseIniSection(
|
|
IN ULONG Argc,
|
|
IN PCHAR Argv[])
|
|
{
|
|
LinuxBootPath = GetArgumentValue(Argc, Argv, "BootPath");
|
|
if (!LinuxBootPath)
|
|
{
|
|
UiMessageBox("Boot path not specified for selected OS!");
|
|
return FALSE;
|
|
}
|
|
|
|
/* Get the kernel name */
|
|
LinuxKernelName = GetArgumentValue(Argc, Argv, "Kernel");
|
|
if (!LinuxKernelName)
|
|
{
|
|
UiMessageBox("Linux kernel filename not specified for selected OS!");
|
|
return FALSE;
|
|
}
|
|
|
|
/* Get the initrd name (optional) */
|
|
LinuxInitrdName = GetArgumentValue(Argc, Argv, "Initrd");
|
|
|
|
/* Get the command line (optional) */
|
|
LinuxCommandLineSize = 0;
|
|
LinuxCommandLine = GetArgumentValue(Argc, Argv, "CommandLine");
|
|
if (LinuxCommandLine)
|
|
{
|
|
RemoveQuotes(LinuxCommandLine);
|
|
LinuxCommandLineSize = min(strlen(LinuxCommandLine) + 1, 260);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOLEAN LinuxReadBootSector(PFILE LinuxKernelFile)
|
|
{
|
|
/* Allocate memory for boot sector */
|
|
LinuxBootSector = MmAllocateMemoryWithType(512, LoaderSystemCode);
|
|
if (LinuxBootSector == NULL)
|
|
return FALSE;
|
|
|
|
/* Read linux boot sector */
|
|
FsSetFilePointer(LinuxKernelFile, 0);
|
|
if (!FsReadFile(LinuxKernelFile, 512, NULL, LinuxBootSector))
|
|
return FALSE;
|
|
|
|
/* Check for validity */
|
|
if (LinuxBootSector->BootFlag != LINUX_BOOT_SECTOR_MAGIC)
|
|
{
|
|
UiMessageBox("Invalid boot sector magic (0xaa55)");
|
|
return FALSE;
|
|
}
|
|
|
|
// DbgDumpBuffer(DPRINT_LINUX, LinuxBootSector, 512);
|
|
|
|
TRACE("SetupSectors: %d\n" , LinuxBootSector->SetupSectors);
|
|
TRACE("RootFlags: 0x%x\n", LinuxBootSector->RootFlags);
|
|
TRACE("SystemSize: 0x%x\n", LinuxBootSector->SystemSize);
|
|
TRACE("SwapDevice: 0x%x\n", LinuxBootSector->SwapDevice);
|
|
TRACE("RamSize: 0x%x\n", LinuxBootSector->RamSize);
|
|
TRACE("VideoMode: 0x%x\n", LinuxBootSector->VideoMode);
|
|
TRACE("RootDevice: 0x%x\n", LinuxBootSector->RootDevice);
|
|
TRACE("BootFlag: 0x%x\n", LinuxBootSector->BootFlag);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOLEAN LinuxReadSetupSector(PFILE LinuxKernelFile)
|
|
{
|
|
UCHAR TempLinuxSetupSector[512];
|
|
|
|
/* Read first linux setup sector */
|
|
FsSetFilePointer(LinuxKernelFile, 512);
|
|
if (!FsReadFile(LinuxKernelFile, 512, NULL, TempLinuxSetupSector))
|
|
return FALSE;
|
|
|
|
/* Check the kernel version */
|
|
LinuxSetupSector = (PLINUX_SETUPSECTOR)TempLinuxSetupSector;
|
|
if (!LinuxCheckKernelVersion())
|
|
return FALSE;
|
|
|
|
if (NewStyleLinuxKernel)
|
|
SetupSectorSize = 512 * LinuxBootSector->SetupSectors;
|
|
else
|
|
SetupSectorSize = 512 * 4; // Always 4 setup sectors
|
|
|
|
/* Allocate memory for setup sectors */
|
|
LinuxSetupSector = MmAllocateMemoryWithType(SetupSectorSize, LoaderSystemCode);
|
|
if (LinuxSetupSector == NULL)
|
|
return FALSE;
|
|
|
|
/* Copy over first setup sector */
|
|
RtlCopyMemory(LinuxSetupSector, TempLinuxSetupSector, 512);
|
|
|
|
/* Read in the rest of the linux setup sectors */
|
|
FsSetFilePointer(LinuxKernelFile, 1024);
|
|
if (!FsReadFile(LinuxKernelFile, SetupSectorSize - 512, NULL, (PVOID)((ULONG_PTR)LinuxSetupSector + 512)))
|
|
return FALSE;
|
|
|
|
// DbgDumpBuffer(DPRINT_LINUX, LinuxSetupSector, SetupSectorSize);
|
|
|
|
TRACE("SetupHeaderSignature: 0x%x (HdrS)\n", LinuxSetupSector->SetupHeaderSignature);
|
|
TRACE("Version: 0x%x\n", LinuxSetupSector->Version);
|
|
TRACE("RealModeSwitch: 0x%x\n", LinuxSetupSector->RealModeSwitch);
|
|
TRACE("SetupSeg: 0x%x\n", LinuxSetupSector->SetupSeg);
|
|
TRACE("StartSystemSeg: 0x%x\n", LinuxSetupSector->StartSystemSeg);
|
|
TRACE("KernelVersion: 0x%x\n", LinuxSetupSector->KernelVersion);
|
|
TRACE("TypeOfLoader: 0x%x\n", LinuxSetupSector->TypeOfLoader);
|
|
TRACE("LoadFlags: 0x%x\n", LinuxSetupSector->LoadFlags);
|
|
TRACE("SetupMoveSize: 0x%x\n", LinuxSetupSector->SetupMoveSize);
|
|
TRACE("Code32Start: 0x%x\n", LinuxSetupSector->Code32Start);
|
|
TRACE("RamdiskAddress: 0x%x\n", LinuxSetupSector->RamdiskAddress);
|
|
TRACE("RamdiskSize: 0x%x\n", LinuxSetupSector->RamdiskSize);
|
|
TRACE("BootSectKludgeOffset: 0x%x\n", LinuxSetupSector->BootSectKludgeOffset);
|
|
TRACE("BootSectKludgeSegment: 0x%x\n", LinuxSetupSector->BootSectKludgeSegment);
|
|
TRACE("HeapEnd: 0x%x\n", LinuxSetupSector->HeapEnd);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOLEAN LinuxReadKernel(PFILE LinuxKernelFile)
|
|
{
|
|
ULONG BytesLoaded;
|
|
CHAR StatusText[260];
|
|
PVOID LoadAddress;
|
|
|
|
RtlStringCbPrintfA(StatusText, sizeof(StatusText), "Loading %s", LinuxKernelName);
|
|
UiDrawStatusText(StatusText);
|
|
|
|
/* Allocate memory for Linux kernel */
|
|
LinuxKernelLoadAddress = MmAllocateMemoryAtAddress(LinuxKernelSize, (PVOID)LINUX_KERNEL_LOAD_ADDRESS, LoaderSystemCode);
|
|
if (LinuxKernelLoadAddress != (PVOID)LINUX_KERNEL_LOAD_ADDRESS)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
LoadAddress = LinuxKernelLoadAddress;
|
|
|
|
/* Read linux kernel to 0x100000 (1mb) */
|
|
FsSetFilePointer(LinuxKernelFile, 512 + SetupSectorSize);
|
|
for (BytesLoaded=0; BytesLoaded<LinuxKernelSize; )
|
|
{
|
|
if (!FsReadFile(LinuxKernelFile, LINUX_READ_CHUNK_SIZE, NULL, LoadAddress))
|
|
return FALSE;
|
|
|
|
BytesLoaded += LINUX_READ_CHUNK_SIZE;
|
|
LoadAddress = (PVOID)((ULONG_PTR)LoadAddress + LINUX_READ_CHUNK_SIZE);
|
|
|
|
UiDrawProgressBarCenter(BytesLoaded, LinuxKernelSize + LinuxInitrdSize, LinuxBootDescription);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOLEAN LinuxCheckKernelVersion(VOID)
|
|
{
|
|
/* Just assume old kernel until we find otherwise */
|
|
NewStyleLinuxKernel = FALSE;
|
|
|
|
/* Check for new style setup header */
|
|
if (LinuxSetupSector->SetupHeaderSignature != LINUX_SETUP_HEADER_ID)
|
|
{
|
|
NewStyleLinuxKernel = FALSE;
|
|
}
|
|
/* Check for version below 2.0 */
|
|
else if (LinuxSetupSector->Version < 0x0200)
|
|
{
|
|
NewStyleLinuxKernel = FALSE;
|
|
}
|
|
/* Check for version 2.0 */
|
|
else if (LinuxSetupSector->Version == 0x0200)
|
|
{
|
|
NewStyleLinuxKernel = TRUE;
|
|
}
|
|
/* Check for version 2.01+ */
|
|
else if (LinuxSetupSector->Version >= 0x0201)
|
|
{
|
|
NewStyleLinuxKernel = TRUE;
|
|
LinuxSetupSector->HeapEnd = 0x9000;
|
|
LinuxSetupSector->LoadFlags |= LINUX_FLAG_CAN_USE_HEAP;
|
|
}
|
|
|
|
if ((NewStyleLinuxKernel == FALSE) && (LinuxInitrdName))
|
|
{
|
|
UiMessageBox("Error: Cannot load a ramdisk (initrd) with an old kernel image.");
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOLEAN LinuxReadInitrd(PFILE LinuxInitrdFile)
|
|
{
|
|
ULONG BytesLoaded;
|
|
CHAR StatusText[260];
|
|
|
|
RtlStringCbPrintfA(StatusText, sizeof(StatusText), "Loading %s", LinuxInitrdName);
|
|
UiDrawStatusText(StatusText);
|
|
|
|
// Allocate memory for the ramdisk
|
|
//LinuxInitrdLoadAddress = MmAllocateMemory(LinuxInitrdSize);
|
|
// Try to align it at the next MB boundary after the kernel
|
|
//LinuxInitrdLoadAddress = MmAllocateMemoryAtAddress(LinuxInitrdSize, (PVOID)ROUND_UP((LINUX_KERNEL_LOAD_ADDRESS + LinuxKernelSize), 0x100000));
|
|
if (LinuxSetupSector->Version <= 0x0202)
|
|
{
|
|
LinuxInitrdLoadAddress = MmAllocateHighestMemoryBelowAddress(LinuxInitrdSize, (PVOID)LINUX_MAX_INITRD_ADDRESS, LoaderSystemCode);
|
|
}
|
|
else
|
|
{
|
|
LinuxInitrdLoadAddress = MmAllocateHighestMemoryBelowAddress(LinuxInitrdSize, (PVOID)LinuxSetupSector->InitrdAddressMax, LoaderSystemCode);
|
|
}
|
|
if (LinuxInitrdLoadAddress == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
/* Set the information in the setup struct */
|
|
LinuxSetupSector->RamdiskAddress = (ULONG)LinuxInitrdLoadAddress;
|
|
LinuxSetupSector->RamdiskSize = LinuxInitrdSize;
|
|
|
|
TRACE("RamdiskAddress: 0x%x\n", LinuxSetupSector->RamdiskAddress);
|
|
TRACE("RamdiskSize: 0x%x\n", LinuxSetupSector->RamdiskSize);
|
|
|
|
if (LinuxSetupSector->Version >= 0x0203)
|
|
{
|
|
TRACE("InitrdAddressMax: 0x%x\n", LinuxSetupSector->InitrdAddressMax);
|
|
}
|
|
|
|
/* Read in the ramdisk */
|
|
for (BytesLoaded=0; BytesLoaded<LinuxInitrdSize; )
|
|
{
|
|
if (!FsReadFile(LinuxInitrdFile, LINUX_READ_CHUNK_SIZE, NULL, (PVOID)LinuxInitrdLoadAddress))
|
|
return FALSE;
|
|
|
|
BytesLoaded += LINUX_READ_CHUNK_SIZE;
|
|
LinuxInitrdLoadAddress = (PVOID)((ULONG_PTR)LinuxInitrdLoadAddress + LINUX_READ_CHUNK_SIZE);
|
|
|
|
UiDrawProgressBarCenter(BytesLoaded + LinuxKernelSize, LinuxInitrdSize + LinuxKernelSize, LinuxBootDescription);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
#endif // _M_IX86
|
|
|
|
#endif
|