X-Git-Url: https://code.delx.au/refind/blobdiff_plain/1ecb53a60573127a4cc976980053adda3bb5e089..c63e9bd427f945b881d337b9fd5d3ff8612f103f:/refind/driver_support.c diff --git a/refind/driver_support.c b/refind/driver_support.c index a547d65..565ae0c 100644 --- a/refind/driver_support.c +++ b/refind/driver_support.c @@ -1,12 +1,7 @@ /* - * File to implement LibScanHandleDatabase(), which is used by rEFInd's - * driver-loading code (inherited from rEFIt), but which has not been - * implemented in GNU-EFI and seems to have been dropped from current - * versions of the Tianocore library. This function was taken from a git - * site with EFI code, but some of the constants it uses were taken from - * a more recent EDK2 package (see below for details). The original files - * bore the following copyright notice: - * + * Functions related to drivers. Original copyright notices below.... + */ +/* * Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.
* This program and the accompanying materials are licensed and made available under * the terms and conditions of the BSD License that accompanies this distribution. @@ -17,11 +12,77 @@ * WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. * */ +/* + * refit/main.c + * Main code for the boot menu + * + * Copyright (c) 2006-2010 Christoph Pfisterer + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Christoph Pfisterer nor the names of the + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * Modifications copyright (c) 2012-2016 Roderick W. Smith + * + * Modifications distributed under the terms of the GNU General Public + * License (GPL) version 3 (GPLv3), or (at your option) any later version. + * + */ +/* + * 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 3 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, see . + */ #include "driver_support.h" #include "lib.h" +#include "mystrings.h" +#include "screen.h" #include "../include/refit_call_wrapper.h" +#if defined (EFIX64) +#define DRIVER_DIRS L"drivers,drivers_x64" +#elif defined (EFI32) +#define DRIVER_DIRS L"drivers,drivers_ia32" +#elif defined (EFIAARCH64) +#define DRIVER_DIRS L"drivers,drivers_aa64" +#endif + #ifdef __MAKEWITH_GNUEFI // Following "global" constants are from EDK2's AutoGen.c.... EFI_GUID gEfiLoadedImageProtocolGuid = { 0x5B1B31A1, 0x9562, 0x11D2, { 0x8E, 0x3F, 0x00, 0xA0, 0xC9, 0x69, 0x72, 0x3B }}; @@ -33,22 +94,64 @@ EFI_GUID gEfiDriverDiagnostics2ProtocolGuid = { 0x4D330321, 0x025F, 0x4AAC, { 0x EFI_GUID gEfiComponentNameProtocolGuid = { 0x107A772C, 0xD5E1, 0x11D4, { 0x9A, 0x46, 0x00, 0x90, 0x27, 0x3F, 0xC1, 0x4D }}; EFI_GUID gEfiComponentName2ProtocolGuid = { 0x6A7A5CFF, 0xE8D9, 0x4F70, { 0xBA, 0xDA, 0x75, 0xAB, 0x30, 0x25, 0xCE, 0x14 }}; EFI_GUID gEfiDevicePathProtocolGuid = { 0x09576E91, 0x6D3F, 0x11D2, { 0x8E, 0x39, 0x00, 0xA0, 0xC9, 0x69, 0x72, 0x3B }}; -#endif +EFI_GUID gEfiDiskIoProtocolGuid = { 0xCE345171, 0xBA0B, 0x11D2, { 0x8E, 0x4F, 0x00, 0xA0, 0xC9, 0x69, 0x72, 0x3B }}; +EFI_GUID gEfiBlockIoProtocolGuid = { 0x964E5B21, 0x6459, 0x11D2, { 0x8E, 0x39, 0x00, 0xA0, 0xC9, 0x69, 0x72, 0x3B }}; +EFI_GUID gEfiSimpleFileSystemProtocolGuid = { 0x964E5B22, 0x6459, 0x11D2, { 0x8E, 0x39, 0x00, 0xA0, 0xC9, 0x69, 0x72, 0x3B }}; + +struct EFI_SIMPLE_FILE_SYSTEM_PROTOCOL; +struct EFI_FILE_PROTOCOL; -// Below is from http://git.etherboot.org/?p=mirror/efi/shell/.git;a=commitdiff;h=b1b0c63423cac54dc964c2930e04aebb46a946ec; -// Seems to have been replaced by ParseHandleDatabaseByRelationshipWithType(), but the latter isn't working for me.... +typedef EFI_STATUS -LibScanHandleDatabase ( - EFI_HANDLE DriverBindingHandle, OPTIONAL - UINT32 *DriverBindingHandleIndex, OPTIONAL - EFI_HANDLE ControllerHandle, OPTIONAL - UINT32 *ControllerHandleIndex, OPTIONAL - UINTN *HandleCount, - EFI_HANDLE **HandleBuffer, - UINT32 **HandleType - ) +(EFIAPI *EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_OPEN_VOLUME)( + IN struct EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *This, + OUT struct EFI_FILE_PROTOCOL **Root + ); -{ +typedef struct _EFI_SIMPLE_FILE_SYSTEM_PROTOCOL { + UINT64 Revision; + EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_OPEN_VOLUME OpenVolume; +} EFI_SIMPLE_FILE_SYSTEM_PROTOCOL; + +typedef struct _EFI_FILE_PROTOCOL { + UINT64 Revision; + EFI_FILE_OPEN Open; + EFI_FILE_CLOSE Close; + EFI_FILE_DELETE Delete; + EFI_FILE_READ Read; + EFI_FILE_WRITE Write; + EFI_FILE_GET_POSITION GetPosition; + EFI_FILE_SET_POSITION SetPosition; + EFI_FILE_GET_INFO GetInfo; + EFI_FILE_SET_INFO SetInfo; + EFI_FILE_FLUSH Flush; +} EFI_FILE_PROTOCOL; + +typedef struct _EFI_BLOCK_IO_PROTOCOL { + UINT64 Revision; + EFI_BLOCK_IO_MEDIA *Media; + EFI_BLOCK_RESET Reset; + EFI_BLOCK_READ ReadBlocks; + EFI_BLOCK_WRITE WriteBlocks; + EFI_BLOCK_FLUSH FlushBlocks; +} EFI_BLOCK_IO_PROTOCOL; +#endif + +/* LibScanHandleDatabase() is used by rEFInd's driver-loading code (inherited + * from rEFIt), but has not been implemented in GNU-EFI and seems to have been + * dropped from current versions of the Tianocore library. This function was + * taken from http://git.etherboot.org/?p=mirror/efi/shell/.git;a=commitdiff;h=b1b0c63423cac54dc964c2930e04aebb46a946ec, + * The original files are copyright 2006-2011 Intel and BSD-licensed. Minor + * modifications by Roderick Smith are GPLv3. + */ +EFI_STATUS +LibScanHandleDatabase (EFI_HANDLE DriverBindingHandle, OPTIONAL + UINT32 *DriverBindingHandleIndex, OPTIONAL + EFI_HANDLE ControllerHandle, OPTIONAL + UINT32 *ControllerHandleIndex, OPTIONAL + UINTN *HandleCount, + EFI_HANDLE **HandleBuffer, + UINT32 **HandleType) { EFI_STATUS Status; UINTN HandleIndex; EFI_GUID **ProtocolGuidArray; @@ -59,14 +162,12 @@ LibScanHandleDatabase ( UINTN OpenInfoIndex; UINTN ChildIndex; BOOLEAN DriverBindingHandleIndexValid; -// BOOLEAN ControllerHandleIndexValid; DriverBindingHandleIndexValid = FALSE; if (DriverBindingHandleIndex != NULL) { *DriverBindingHandleIndex = 0xffffffff; } -// ControllerHandleIndexValid = FALSE; if (ControllerHandleIndex != NULL) { *ControllerHandleIndex = 0xffffffff; } @@ -111,7 +212,6 @@ LibScanHandleDatabase ( if (ControllerHandle != NULL && ControllerHandleIndex != NULL && (*HandleBuffer)[HandleIndex] == ControllerHandle) { *ControllerHandleIndex = (UINT32) HandleIndex; -// ControllerHandleIndexValid = TRUE; } } @@ -249,3 +349,250 @@ Error: return Status; } /* EFI_STATUS LibScanHandleDatabase() */ + +#ifdef __MAKEWITH_GNUEFI +/* Modified from EDK2 function of a similar name; original copyright Intel & + * BSD-licensed; modifications by Roderick Smith are GPLv3. */ +EFI_STATUS ConnectAllDriversToAllControllers(VOID) +{ + EFI_STATUS Status; + UINTN AllHandleCount; + EFI_HANDLE *AllHandleBuffer; + UINTN Index; + UINTN HandleCount; + EFI_HANDLE *HandleBuffer; + UINT32 *HandleType; + UINTN HandleIndex; + BOOLEAN Parent; + BOOLEAN Device; + + Status = LibLocateHandle(AllHandles, + NULL, + NULL, + &AllHandleCount, + &AllHandleBuffer); + if (EFI_ERROR(Status)) + return Status; + + for (Index = 0; Index < AllHandleCount; Index++) { + // + // Scan the handle database + // + Status = LibScanHandleDatabase(NULL, + NULL, + AllHandleBuffer[Index], + NULL, + &HandleCount, + &HandleBuffer, + &HandleType); + if (EFI_ERROR (Status)) + goto Done; + + Device = TRUE; + if (HandleType[Index] & EFI_HANDLE_TYPE_DRIVER_BINDING_HANDLE) + Device = FALSE; + if (HandleType[Index] & EFI_HANDLE_TYPE_IMAGE_HANDLE) + Device = FALSE; + + if (Device) { + Parent = FALSE; + for (HandleIndex = 0; HandleIndex < HandleCount; HandleIndex++) { + if (HandleType[HandleIndex] & EFI_HANDLE_TYPE_PARENT_HANDLE) + Parent = TRUE; + } // for + + if (!Parent) { + if (HandleType[Index] & EFI_HANDLE_TYPE_DEVICE_HANDLE) { + Status = refit_call4_wrapper(BS->ConnectController, + AllHandleBuffer[Index], + NULL, + NULL, + TRUE); + } + } + } + + MyFreePool (HandleBuffer); + MyFreePool (HandleType); + } + +Done: + MyFreePool (AllHandleBuffer); + return Status; +} /* EFI_STATUS ConnectAllDriversToAllControllers() */ +#else +EFI_STATUS ConnectAllDriversToAllControllers(VOID) { + BdsLibConnectAllDriversToAllControllers(); + return 0; +} +#endif + +/* + * ConnectFilesystemDriver() is modified from DisconnectInvalidDiskIoChildDrivers() + * in Clover (https://sourceforge.net/projects/cloverefiboot/), which is derived + * from rEFIt. The refit/main.c file from which this function was taken continues + * to bear rEFIt's original copyright/licence notice (BSD); modifications by + * Roderick Smith (2016) are GPLv3. + */ +/** + * Some UEFI's (like HPQ EFI from HP notebooks) have DiskIo protocols + * opened BY_DRIVER (by Partition driver in HP case) even when no file system + * is produced from this DiskIo. This then blocks our FS drivers from connecting + * and producing file systems. + * To fix it: we will disconnect drivers that connected to DiskIo BY_DRIVER + * if this is partition volume and if those drivers did not produce file system, + * then try to connect every unconnected device to the driver whose handle is + * passed to us. This should have no effect on systems unaffected by this EFI + * bug/quirk. + */ +VOID ConnectFilesystemDriver(EFI_HANDLE DriverHandle) { + EFI_STATUS Status; + UINTN HandleCount = 0; + UINTN Index; + UINTN OpenInfoIndex; + EFI_HANDLE *Handles = NULL; + EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Fs; + EFI_BLOCK_IO_PROTOCOL *BlockIo; + EFI_OPEN_PROTOCOL_INFORMATION_ENTRY *OpenInfo; + UINTN OpenInfoCount; + EFI_HANDLE DriverHandleList[2]; + + // + // Get all DiskIo handles + // + Status = refit_call5_wrapper(gBS->LocateHandleBuffer, + ByProtocol, + &gEfiDiskIoProtocolGuid, + NULL, + &HandleCount, + &Handles); + if (EFI_ERROR(Status) || HandleCount == 0) + return; + + // + // Check every DiskIo handle + // + for (Index = 0; Index < HandleCount; Index++) { + // + // If this is not partition - skip it. + // This is then whole disk and DiskIo + // should be opened here BY_DRIVER by Partition driver + // to produce partition volumes. + // + Status = refit_call3_wrapper(gBS->HandleProtocol, + Handles[Index], + &gEfiBlockIoProtocolGuid, + (VOID **) &BlockIo); + if (EFI_ERROR (Status)) + continue; + if (BlockIo->Media == NULL || !BlockIo->Media->LogicalPartition) + continue; + + // + // If SimpleFileSystem is already produced - skip it, this is ok + // + Status = refit_call3_wrapper(gBS->HandleProtocol, + Handles[Index], + &gEfiSimpleFileSystemProtocolGuid, + (VOID **) &Fs); + if (Status == EFI_SUCCESS) + continue; + + // + // If no SimpleFileSystem on this handle but DiskIo is opened BY_DRIVER + // then disconnect this connection and try to connect our driver to it + // + Status = refit_call4_wrapper(gBS->OpenProtocolInformation, + Handles[Index], + &gEfiDiskIoProtocolGuid, + &OpenInfo, + &OpenInfoCount); + if (EFI_ERROR (Status)) + continue; + DriverHandleList[1] = NULL; + for (OpenInfoIndex = 0; OpenInfoIndex < OpenInfoCount; OpenInfoIndex++) { + if ((OpenInfo[OpenInfoIndex].Attributes & EFI_OPEN_PROTOCOL_BY_DRIVER) == EFI_OPEN_PROTOCOL_BY_DRIVER) { + Status = refit_call3_wrapper(gBS->DisconnectController, + Handles[Index], + OpenInfo[OpenInfoIndex].AgentHandle, + NULL); + if (!(EFI_ERROR(Status))) { + DriverHandleList[0] = DriverHandle; + refit_call4_wrapper(gBS->ConnectController, + Handles[Index], + DriverHandleList, + NULL, + FALSE); + } // if + } // if + } // for + FreePool (OpenInfo); + } + FreePool(Handles); +} // VOID ConnectFilesystemDriver() + +// Scan a directory for drivers. +// Originally from rEFIt's main.c (BSD), but modified since then (GPLv3). +static UINTN ScanDriverDir(IN CHAR16 *Path) +{ + EFI_STATUS Status; + REFIT_DIR_ITER DirIter; + UINTN NumFound = 0; + EFI_FILE_INFO *DirEntry; + CHAR16 FileName[256]; + + CleanUpPathNameSlashes(Path); + // look through contents of the directory + DirIterOpen(SelfRootDir, Path, &DirIter); + while (DirIterNext(&DirIter, 2, LOADER_MATCH_PATTERNS, &DirEntry)) { + if (DirEntry->FileName[0] == '.') + continue; // skip this + + SPrint(FileName, 255, L"%s\\%s", Path, DirEntry->FileName); + NumFound++; + Status = StartEFIImage(FileDevicePath(SelfLoadedImage->DeviceHandle, FileName), + L"", TYPE_EFI, DirEntry->FileName, 0, NULL, FALSE, TRUE); + } + Status = DirIterClose(&DirIter); + if ((Status != EFI_NOT_FOUND) && (Status != EFI_INVALID_PARAMETER)) { + SPrint(FileName, 255, L"while scanning the %s directory", Path); + CheckError(Status, FileName); + } + return (NumFound); +} // static UINTN ScanDriverDir() + + +// Load all EFI drivers from rEFInd's "drivers" subdirectory and from the +// directories specified by the user in the "scan_driver_dirs" configuration +// file line. +// Originally from rEFIt's main.c (BSD), but modified since then (GPLv3). +VOID LoadDrivers(VOID) { + CHAR16 *Directory, *SelfDirectory; + UINTN i = 0, Length, NumFound = 0; + + // load drivers from the subdirectories of rEFInd's home directory specified + // in the DRIVER_DIRS constant. + while ((Directory = FindCommaDelimited(DRIVER_DIRS, i++)) != NULL) { + SelfDirectory = SelfDirPath ? StrDuplicate(SelfDirPath) : NULL; + CleanUpPathNameSlashes(SelfDirectory); + MergeStrings(&SelfDirectory, Directory, L'\\'); + NumFound += ScanDriverDir(SelfDirectory); + MyFreePool(Directory); + MyFreePool(SelfDirectory); + } + + // Scan additional user-specified driver directories.... + i = 0; + while ((Directory = FindCommaDelimited(GlobalConfig.DriverDirs, i++)) != NULL) { + CleanUpPathNameSlashes(Directory); + Length = StrLen(Directory); + if (Length > 0) { + NumFound += ScanDriverDir(Directory); + } // if + MyFreePool(Directory); + } // while + + // connect all devices + if (NumFound > 0) + ConnectAllDriversToAllControllers(); +} /* VOID LoadDrivers() */