mirror of
https://github.com/reactos/reactos.git
synced 2026-05-22 15:20:29 +08:00
[FREELDR:NTLDR] Try to auto-detect which bootvid module to load at boot time (#8720)
Detect whether we run on XBOX or PC98 (on x86 builds), or on (U)EFI or on a BIOS-based PC, or whether a generic linear framebuffer mode is used, in order to choose the correct bootvid module to load, based from a predefined file names list (ideally it should be derived from txtsetup.sif...). Supersedes PR #8701. Use `KeFindConfiguration*Entry()` to spelunk the hardware configuration tree in the NT loader module. Alternatively, allow the user to override the choice with a new `BOOTVID=` boot option. In any case, if the selected bootvid module failed to be loaded, silently fall back to the default bootvid.dll file. The fallback is done by just letting the PE loader resolve the necessary exports from hal.dll and ntoskrnl.exe.
This commit is contained in:
@@ -6,12 +6,15 @@
|
||||
*/
|
||||
|
||||
#include <freeldr.h>
|
||||
#include <ndk/kefuncs.h> // For KeFindConfiguration*Entry()
|
||||
#include <ndk/ldrtypes.h>
|
||||
#include "winldr.h"
|
||||
#include "ntldropts.h"
|
||||
#include "registry.h"
|
||||
#include <internal/cmboot.h>
|
||||
|
||||
#include <drivers/bootvid/framebuf.h> // For CM_FRAMEBUF_DEVICE_DATA
|
||||
|
||||
#include <debug.h>
|
||||
DBG_DEFAULT_CHANNEL(WINDOWS);
|
||||
|
||||
@@ -616,6 +619,112 @@ WinLdrIsPaeSupported(
|
||||
}
|
||||
#endif /* _M_IX86 */
|
||||
|
||||
static VOID
|
||||
NtLdrDetectBootVid(
|
||||
_In_ PCONFIGURATION_COMPONENT_DATA ConfigTree,
|
||||
_Out_writes_bytes_(BootVidNameSize) _Always_(_Post_z_)
|
||||
PSTR BootVidName,
|
||||
_In_ SIZE_T BootVidNameSize)
|
||||
{
|
||||
/* NOTE: The following array should instead be read from txtsetup.sif */
|
||||
static const struct
|
||||
{
|
||||
PCSTR SystemSubId;
|
||||
PCSTR ModuleName;
|
||||
} BootVidNames[] =
|
||||
{
|
||||
#if defined(_M_IX86) && !defined(UEFIBOOT)
|
||||
{"PC-98", "pc98bvid.dll"},
|
||||
{"Xbox", "xboxbvid.dll"},
|
||||
#endif
|
||||
{"EFI", "lfbbvid.dll"},
|
||||
};
|
||||
|
||||
/* Default BootVid file name */
|
||||
RtlStringCbCopyA(BootVidName, BootVidNameSize, "bootvid.dll");
|
||||
|
||||
/* Attempt auto-detection by System ID */
|
||||
PCONFIGURATION_COMPONENT_DATA Entry;
|
||||
Entry = KeFindConfigurationEntry(ConfigTree,
|
||||
SystemClass,
|
||||
ArcSystem,
|
||||
NULL);
|
||||
if (!Entry)
|
||||
{
|
||||
/* No SystemClass ArcSystem entry, retry with hack */
|
||||
Entry = KeFindConfigurationEntry(ConfigTree,
|
||||
SystemClass,
|
||||
MaximumType, // HACK for non-ARC machines
|
||||
NULL);
|
||||
}
|
||||
if (Entry)
|
||||
{
|
||||
// Entry->ComponentEntry.IdentifierLength;
|
||||
TRACE("ARC System ID: '%s'\n", Entry->ComponentEntry.Identifier);
|
||||
|
||||
/* Look up the name */
|
||||
for (ULONG i = 0; i < RTL_NUMBER_OF(BootVidNames); ++i)
|
||||
{
|
||||
if (strstr(Entry->ComponentEntry.Identifier, BootVidNames[i].SystemSubId))
|
||||
{
|
||||
RtlStringCbCopyA(BootVidName, BootVidNameSize, BootVidNames[i].ModuleName);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Special-case detection if the display controller contains
|
||||
* a DeviceSpecific CM_FRAMEBUF_DEVICE_DATA resource */
|
||||
Entry = KeFindConfigurationEntry(ConfigTree,
|
||||
ControllerClass,
|
||||
DisplayController,
|
||||
NULL);
|
||||
if (Entry)
|
||||
{
|
||||
/* Cast to PCM_PARTIAL_RESOURCE_LIST to access
|
||||
* the common Version and Revision fields */
|
||||
PCM_PARTIAL_RESOURCE_LIST ResourceList =
|
||||
(PCM_PARTIAL_RESOURCE_LIST)Entry->ConfigurationData;
|
||||
ULONG ConfigurationDataLength =
|
||||
Entry->ComponentEntry.ConfigurationDataLength;
|
||||
|
||||
// Entry->ComponentEntry.IdentifierLength;
|
||||
TRACE("Display: '%s'\n", Entry->ComponentEntry.Identifier);
|
||||
|
||||
/* The configuration data length must be valid.
|
||||
* The Version/Revision must be greater than or equal to 1.2
|
||||
* in order to describe a valid new configuration data. */
|
||||
if (Entry->ConfigurationData &&
|
||||
(ConfigurationDataLength >= sizeof(CM_PARTIAL_RESOURCE_LIST)) &&
|
||||
( (ResourceList->Version > 1) ||
|
||||
((ResourceList->Version == 1) && (ResourceList->Revision > 1)) ))
|
||||
{
|
||||
/* Try to find the DeviceSpecific resource */
|
||||
PCM_PARTIAL_RESOURCE_DESCRIPTOR DeviceSpecific = NULL;
|
||||
for (ULONG i = 0; i < ResourceList->Count; ++i)
|
||||
{
|
||||
PCM_PARTIAL_RESOURCE_DESCRIPTOR Descriptor =
|
||||
&ResourceList->PartialDescriptors[i];
|
||||
if (Descriptor->Type == CmResourceTypeDeviceSpecific)
|
||||
{
|
||||
/* NOTE: This descriptor *MUST* be the last one.
|
||||
* The actual device data follows the descriptor. */
|
||||
ASSERT(i == ResourceList->Count - 1);
|
||||
DeviceSpecific = Descriptor;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check whether the resource describes framebuffer information */
|
||||
if (DeviceSpecific &&
|
||||
(DeviceSpecific->u.DeviceSpecificData.DataSize >= sizeof(CM_FRAMEBUF_DEVICE_DATA)))
|
||||
{
|
||||
/* It does, use the Linear FrameBuffer Boot Video Driver instead */
|
||||
RtlStringCbCopyA(BootVidName, BootVidNameSize, "lfbbvid.dll");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
BOOLEAN
|
||||
LoadWindowsCore(IN USHORT OperatingSystemVersion,
|
||||
@@ -627,14 +736,14 @@ LoadWindowsCore(IN USHORT OperatingSystemVersion,
|
||||
BOOLEAN Success;
|
||||
PCSTR Option;
|
||||
ULONG OptionLength;
|
||||
PVOID KernelBase, HalBase, KdDllBase = NULL;
|
||||
PLDR_DATA_TABLE_ENTRY HalDTE, KdDllDTE = NULL;
|
||||
PVOID KernelBase, HalBase, KdDllBase = NULL, BootVidBase = NULL;
|
||||
PLDR_DATA_TABLE_ENTRY HalDTE, KdDllDTE = NULL, BootVidDTE = NULL;
|
||||
PCSTR ModuleName;
|
||||
CHAR DirPath[MAX_PATH];
|
||||
CHAR HalFileName[MAX_PATH];
|
||||
CHAR KernelFileName[MAX_PATH];
|
||||
CHAR KdDllName[MAX_PATH];
|
||||
|
||||
if (!KernelDTE) return FALSE;
|
||||
CHAR BootVidName[MAX_PATH];
|
||||
|
||||
/* Initialize SystemRoot\System32 path */
|
||||
RtlStringCbCopyA(DirPath, sizeof(DirPath), BootPath);
|
||||
@@ -880,33 +989,76 @@ LoadWindowsCore(IN USHORT OperatingSystemVersion,
|
||||
}
|
||||
}
|
||||
|
||||
/* Load all referenced DLLs for Kernel, HAL and Kernel Debugger Transport DLL */
|
||||
/* Load the correct Boot Video Driver */
|
||||
if (OperatingSystemVersion >= _WIN32_WINNT_WIN2K)
|
||||
{
|
||||
/* Check whether there is a BOOTVID option */
|
||||
Option = NtLdrGetOptionEx(BootOptions, "BOOTVID=", &OptionLength);
|
||||
if (Option && (OptionLength > 8))
|
||||
{
|
||||
/* Retrieve the BootVid file name */
|
||||
Option += 8; OptionLength -= 8;
|
||||
RtlStringCbCopyNA(BootVidName, sizeof(BootVidName), Option, OptionLength);
|
||||
_strlwr(BootVidName);
|
||||
TRACE("User-specified BootVid file: '%s'\n", BootVidName);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* No option given, attempt auto-detection */
|
||||
NtLdrDetectBootVid(LoaderBlock->ConfigurationRoot,
|
||||
BootVidName, sizeof(BootVidName));
|
||||
TRACE("Auto-detected BootVid file: '%s'\n", BootVidName);
|
||||
}
|
||||
|
||||
/* Load the BootVid */
|
||||
BootVidBase = LoadModule(&LoaderBlock->LoadOrderListHead,
|
||||
DirPath, BootVidName,
|
||||
"bootvid.dll", LoaderSystemCode,
|
||||
&BootVidDTE, 45);
|
||||
if (!BootVidBase)
|
||||
{
|
||||
/* Ignore the failure; we will fail later when scanning the
|
||||
* kernel import tables, if it really needs the BootVid. */
|
||||
ERR("LoadModule('%s') failed\n", BootVidName);
|
||||
}
|
||||
}
|
||||
|
||||
/* Load all referenced DLLs for Kernel, HAL, Kernel Debugger Transport and BootVid */
|
||||
ModuleName = KernelFileName;
|
||||
Success = PeLdrScanImportDescriptorTable(&LoaderBlock->LoadOrderListHead, DirPath, *KernelDTE);
|
||||
if (!Success)
|
||||
{
|
||||
UiMessageBox("Could not load %s", KernelFileName);
|
||||
goto Quit;
|
||||
}
|
||||
ModuleName = HalFileName;
|
||||
Success = PeLdrScanImportDescriptorTable(&LoaderBlock->LoadOrderListHead, DirPath, HalDTE);
|
||||
if (!Success)
|
||||
{
|
||||
UiMessageBox("Could not load %s", HalFileName);
|
||||
goto Quit;
|
||||
}
|
||||
if (KdDllDTE)
|
||||
{
|
||||
ModuleName = KdDllName;
|
||||
Success = PeLdrScanImportDescriptorTable(&LoaderBlock->LoadOrderListHead, DirPath, KdDllDTE);
|
||||
if (!Success)
|
||||
{
|
||||
UiMessageBox("Could not load %s", KdDllName);
|
||||
goto Quit;
|
||||
}
|
||||
}
|
||||
if (BootVidDTE)
|
||||
{
|
||||
ModuleName = BootVidName;
|
||||
Success = PeLdrScanImportDescriptorTable(&LoaderBlock->LoadOrderListHead, DirPath, BootVidDTE);
|
||||
if (!Success)
|
||||
goto Quit;
|
||||
}
|
||||
|
||||
Quit:
|
||||
if (!Success)
|
||||
{
|
||||
ERR("PeLdrScanImportDescriptorTable('%s') failed\n", ModuleName);
|
||||
UiMessageBox("Could not load %s", ModuleName);
|
||||
|
||||
/* Cleanup and bail out */
|
||||
if (BootVidDTE)
|
||||
PeLdrFreeDataTableEntry(BootVidDTE);
|
||||
if (BootVidBase) // Optional
|
||||
MmFreeMemory(BootVidBase);
|
||||
|
||||
if (KdDllDTE)
|
||||
PeLdrFreeDataTableEntry(KdDllDTE);
|
||||
if (KdDllBase) // Optional
|
||||
|
||||
@@ -17,6 +17,7 @@ list(APPEND ROSLOAD_SOURCE
|
||||
oslist.c
|
||||
lib/rtl/libsupp.c
|
||||
${REACTOS_SOURCE_DIR}/ntoskrnl/config/cmboot.c
|
||||
${REACTOS_SOURCE_DIR}/ntoskrnl/ke/config.c
|
||||
ntldr/conversion.c
|
||||
ntldr/headless.c
|
||||
ntldr/inffile.c
|
||||
|
||||
@@ -55,6 +55,7 @@ add_asm_files(uefifreeldr_common_asm ${FREELDR_COMMON_ASM_SOURCE} ${UEFILDR_COMM
|
||||
|
||||
list(APPEND FREELDR_NTLDR_SOURCE
|
||||
${REACTOS_SOURCE_DIR}/ntoskrnl/config/cmboot.c
|
||||
${REACTOS_SOURCE_DIR}/ntoskrnl/ke/config.c
|
||||
ntldr/conversion.c
|
||||
ntldr/headless.c
|
||||
ntldr/inffile.c
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
* LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
|
||||
* PURPOSE: Configuration Tree Routines
|
||||
* COPYRIGHT: Copyright 2005-2006 Alex Ionescu <alex.ionescu@reactos.org>
|
||||
*
|
||||
* NOTE: This module is shared by both the kernel and the bootloader.
|
||||
*/
|
||||
|
||||
/* INCLUDES ******************************************************************/
|
||||
|
||||
Reference in New Issue
Block a user