reactos/boot/freeldr/freeldr/miscboot.c
Hermès Bélusca-Maïto bd451f240f
[FREELDR] Code fixes and enhancements.
CORE-9023

FIXES:
======

- Fix parsing of the multiboot options string.
  NOTE: They are not yet treated in a case-insensitive manner!

- Fix a bug in ArcOpen() so that it correctly skips the first path
  separator (after the adapter-controller-peripheral ARC descriptors).
  The path separator can be either a backslash or a slash (both are
  allowed according to the specs); they were also already handled
  correctly in other parts of the code.

- Fix DissectArcPath() so as to:
  * **OPTIONALLY** (and not mandatorily!) return the path part that follows
    the ARC adapter-controller-peripheral elements in the ARC path;

  * make it correctly handle the (yes, optional!!) partition() part in the
    ARC path, for the multi(x)disk(y)rdisk(z) cases.

ENHANCEMENTS:
=============

- Directly retrieve the default OS entry as we enumerate them and
  build their list (i.e. merge the GetDefaultOperatingSystem() helper
  within InitOperatingSystemList()).

- Directly use the opened 'FreeLoader' INI section via its ID in the
  different functions that need it.

- Make the custom-boot and linux loaders honour the boot options they are
  supposed to support (see FREELDR.INI documentation / template).
  This includes the 'BootDrive' and 'BootPartition' (alternatively the ARC
  'BootPath').
  This also allows them to take into account the user-specified choices in the
  FreeLdr custom-boot editors.

- Modify the FreeLdr custom-boot editors so as to correctly honour
  the  priorities of the boot options as specified in the FREELDR.INI
  documentation / template.

- Use stack trick (union of structs) to reduce stack usage in the
  FreeLdr custom-boot editors, because there are strings buffers that are
  used in an alternate manner.

- Extract out from the editors the LoadOperatingSystem() calls, and
  move it back into OptionMenuCustomBoot(), so that when this latter
  function is called there is no risk of having a stack almost full.

- When building the ARC-compatible argument vector for the loaders, add
  the mandatory "SystemPartition" path. This allows the loaders to NOT
  call the machine-specific MachDiskGetBootPath() later on (this data is
  indeed passed to them by the boot manager part of FreeLdr).

- Improve the FsOpenFile() helper so as to make it:
  * return an adequate ARC_STATUS instead of a mere uninformative BOOLEAN;
  * take open options, as well as a default path (optional) that would be
    prepended to the file name in case the latter is a relative one.

- Make RamDiskLoadVirtualFile() return an actual descriptive ARC_STATUS
  value, and make it take an optional default path (same usage as the one
  in FsOpenFile() ).
  + Remove useless NTAPI .

- UiInitialize() and TuiTextToColor(), TuiTextToFillStyle(): load or
  convert named settings into corresponding values using setting table and
  a tight for-loop, instead of duplicating 10x the same parameter reading
  logic.

- UiInitialize(): Open the "Display" INI section just once. Remove usage
  of DisplayModeText[] buffer.

- UiShowMessageBoxesInSection() and UiShowMessageBoxesInArgv(): reduce
  code indentation level.

ENHANCEMENTS for NT OS loader:
==============================

- Don't use MachDiskGetBootPath() but use instead the "SystemPartition"
  value passed via the ARC argument vector by the boot manager
  (+ validation checks). Use it as the "default path" when calling
  FsOpenFile() or loading the ramdisk.

- Honour the FreeLdr-specific "Hal=" and "Kernel=" options by converting
  them into NT standard "/HAL=" and "/KERNEL=" options in the boot
  command line.

  Note that if the latter ones are already present on the standard "Options="
  option line, they would take precedence over those passed via the separate
  "Hal=" and "Kernel=" FreeLdr-specific options.

  Also add some documentation links to Geoff Chappell's website about
  how the default HAL and KERNEL names are chosen depending on the
  detected underlying platform on which the NT OS loader is running.
2019-08-31 01:42:46 +02:00

310 lines
9.1 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.
*/
#ifdef _M_IX86
/* INCLUDES *******************************************************************/
#include <freeldr.h>
/* FUNCTIONS ******************************************************************/
ARC_STATUS
LoadAndBootBootSector(
IN ULONG Argc,
IN PCHAR Argv[],
IN PCHAR Envp[])
{
ARC_STATUS Status;
PCSTR ArgValue;
PCSTR BootPath;
PCSTR FileName;
UCHAR DriveNumber = 0;
ULONG PartitionNumber = 0;
ULONG FileId;
ULONG BytesRead;
CHAR ArcPath[MAX_PATH];
/* Find all the message box settings and run them */
UiShowMessageBoxesInArgv(Argc, Argv);
/*
* Check whether we have a "BootPath" value (takes precedence
* over both "BootDrive" and "BootPartition").
*/
BootPath = GetArgumentValue(Argc, Argv, "BootPath");
if (!BootPath || !*BootPath)
{
/* We don't have one, check whether we use "BootDrive" and "BootPartition" */
/* Retrieve the boot drive (optional, fall back to using default path otherwise) */
ArgValue = GetArgumentValue(Argc, Argv, "BootDrive");
if (ArgValue && *ArgValue)
{
DriveNumber = DriveMapGetBiosDriveNumber(ArgValue);
/* Retrieve the boot partition (not optional and cannot be zero) */
PartitionNumber = 0;
ArgValue = GetArgumentValue(Argc, Argv, "BootPartition");
if (ArgValue && *ArgValue)
PartitionNumber = atoi(ArgValue);
if (PartitionNumber == 0)
{
UiMessageBox("Boot partition cannot be 0!");
return EINVAL;
}
/* Construct the corresponding ARC path */
ConstructArcPath(ArcPath, "", DriveNumber, PartitionNumber);
*strrchr(ArcPath, '\\') = ANSI_NULL; // Trim the trailing path separator.
BootPath = ArcPath;
}
else
{
/* Fall back to using the system partition as default path */
BootPath = GetArgumentValue(Argc, Argv, "SystemPartition");
}
}
/* Retrieve the file name */
FileName = GetArgumentValue(Argc, Argv, "BootSectorFile");
if (!FileName || !*FileName)
{
UiMessageBox("Boot sector file not specified for selected OS!");
return EINVAL;
}
/* Open the boot sector file */
Status = FsOpenFile(FileName, BootPath, OpenReadOnly, &FileId);
if (Status != ESUCCESS)
{
UiMessageBox("Unable to open %s", FileName);
return Status;
}
/* Now try to load the boot sector. If this fails then abort. */
Status = ArcRead(FileId, (PVOID)0x7c00, 512, &BytesRead);
ArcClose(FileId);
if ((Status != ESUCCESS) || (BytesRead != 512))
{
UiMessageBox("Unable to load boot sector.");
return EIO;
}
/* Check for validity */
if (*((USHORT*)(0x7c00 + 0x1fe)) != 0xaa55)
{
UiMessageBox("Invalid boot sector magic (0xaa55)");
return ENOEXEC;
}
UiUnInitialize("Booting...");
IniCleanup();
/*
* Don't stop the floppy drive motor when we
* are just booting a bootsector, or drive, or partition.
* If we were to stop the floppy motor then
* the BIOS wouldn't be informed and if the
* next read is to a floppy then the BIOS will
* still think the motor is on and this will
* result in a read error.
*/
// DiskStopFloppyMotor();
/* NOTE: Don't touch FrldrBootDrive */
ChainLoadBiosBootSectorCode();
Reboot(); /* Must not return! */
return ESUCCESS;
}
static ARC_STATUS
LoadAndBootPartitionOrDrive(
IN UCHAR DriveNumber,
IN ULONG PartitionNumber OPTIONAL,
IN PCSTR BootPath OPTIONAL)
{
ARC_STATUS Status;
ULONG FileId;
ULONG BytesRead;
CHAR ArcPath[MAX_PATH];
/*
* If the user specifies an ARC "BootPath" value, it takes precedence
* over both the DriveNumber and PartitionNumber options.
*/
if (BootPath && *BootPath)
{
PCSTR FileName = NULL;
/*
* Retrieve the BIOS drive and partition numbers; verify also that the
* path is "valid" in the sense that it must not contain any file name.
*/
if (!DissectArcPath(BootPath, &FileName, &DriveNumber, &PartitionNumber) ||
(FileName && *FileName))
{
return EINVAL;
}
}
else
{
/* We don't have one, so construct the corresponding ARC path */
ConstructArcPath(ArcPath, "", DriveNumber, PartitionNumber);
*strrchr(ArcPath, '\\') = ANSI_NULL; // Trim the trailing path separator.
BootPath = ArcPath;
}
/* Open the volume */
Status = ArcOpen((PSTR)BootPath, OpenReadOnly, &FileId);
if (Status != ESUCCESS)
{
UiMessageBox("Unable to open %s", BootPath);
return Status;
}
/*
* Now try to load the partition boot sector or the MBR (when PartitionNumber == 0).
* If this fails then abort.
*/
Status = ArcRead(FileId, (PVOID)0x7c00, 512, &BytesRead);
ArcClose(FileId);
if ((Status != ESUCCESS) || (BytesRead != 512))
{
if (PartitionNumber != 0)
UiMessageBox("Unable to load partition's boot sector.");
else
UiMessageBox("Unable to load MBR boot sector.");
return EIO;
}
/* Check for validity */
if (*((USHORT*)(0x7c00 + 0x1fe)) != 0xaa55)
{
UiMessageBox("Invalid boot sector magic (0xaa55)");
return ENOEXEC;
}
UiUnInitialize("Booting...");
IniCleanup();
/*
* Don't stop the floppy drive motor when we
* are just booting a bootsector, or drive, or partition.
* If we were to stop the floppy motor then
* the BIOS wouldn't be informed and if the
* next read is to a floppy then the BIOS will
* still think the motor is on and this will
* result in a read error.
*/
// DiskStopFloppyMotor();
FrldrBootDrive = DriveNumber;
FrldrBootPartition = PartitionNumber;
ChainLoadBiosBootSectorCode();
Reboot(); /* Must not return! */
return ESUCCESS;
}
ARC_STATUS
LoadAndBootPartition(
IN ULONG Argc,
IN PCHAR Argv[],
IN PCHAR Envp[])
{
PCSTR ArgValue;
PCSTR BootPath;
UCHAR DriveNumber = 0;
ULONG PartitionNumber = 0;
/* Find all the message box settings and run them */
UiShowMessageBoxesInArgv(Argc, Argv);
/*
* Check whether we have a "BootPath" value (takes precedence
* over both "BootDrive" and "BootPartition").
*/
BootPath = GetArgumentValue(Argc, Argv, "BootPath");
if (!BootPath || !*BootPath)
{
/* We don't have one */
/* Retrieve the boot drive */
ArgValue = GetArgumentValue(Argc, Argv, "BootDrive");
if (!ArgValue || !*ArgValue)
{
UiMessageBox("Boot drive not specified for selected OS!");
return EINVAL;
}
DriveNumber = DriveMapGetBiosDriveNumber(ArgValue);
/* Retrieve the boot partition (optional, fall back to zero otherwise) */
PartitionNumber = 0;
ArgValue = GetArgumentValue(Argc, Argv, "BootPartition");
if (ArgValue && *ArgValue)
PartitionNumber = atoi(ArgValue);
}
return LoadAndBootPartitionOrDrive(DriveNumber, PartitionNumber, BootPath);
}
ARC_STATUS
LoadAndBootDrive(
IN ULONG Argc,
IN PCHAR Argv[],
IN PCHAR Envp[])
{
PCSTR ArgValue;
PCSTR BootPath;
UCHAR DriveNumber = 0;
/* Find all the message box settings and run them */
UiShowMessageBoxesInArgv(Argc, Argv);
/* Check whether we have a "BootPath" value (takes precedence over "BootDrive") */
BootPath = GetArgumentValue(Argc, Argv, "BootPath");
if (BootPath && *BootPath)
{
/*
* We have one, check that it does not contain any
* "partition()" specification, and fail if so.
*/
if (strstr(BootPath, ")partition("))
{
UiMessageBox("Invalid 'BootPath' value!");
return EINVAL;
}
}
else
{
/* We don't, retrieve the boot drive value instead */
ArgValue = GetArgumentValue(Argc, Argv, "BootDrive");
if (!ArgValue || !*ArgValue)
{
UiMessageBox("Boot drive not specified for selected OS!");
return EINVAL;
}
DriveNumber = DriveMapGetBiosDriveNumber(ArgValue);
}
return LoadAndBootPartitionOrDrive(DriveNumber, 0, BootPath);
}
#endif // _M_IX86