diff options
Diffstat (limited to 'Application/Loader')
-rw-r--r-- | Application/Loader/Loader.c | 288 | ||||
-rw-r--r-- | Application/Loader/Loader.inf | 10 | ||||
-rw-r--r-- | Application/Loader/Loader.vcxproj | 4 |
3 files changed, 210 insertions, 92 deletions
diff --git a/Application/Loader/Loader.c b/Application/Loader/Loader.c index 665b2fe..aab4a5c 100644 --- a/Application/Loader/Loader.c +++ b/Application/Loader/Loader.c @@ -5,9 +5,11 @@ #include <Protocol/SimpleFileSystem.h> #include <Protocol/LoadedImage.h> #include <Protocol/LegacyBios.h> +#include <Library/PcdLib.h> #include <Library/UefiLib.h> #include <Library/DebugLib.h> #include <Library/MemoryAllocationLib.h> +#include <Library/ReportStatusCodeLib.h> #include <Library/DevicePathLib.h> #include <Library/UefiBootServicesTableLib.h> #include <Library/UefiBootManagerLib.h> @@ -15,16 +17,6 @@ // -// Define whether the loader should prompt for driver configuration or not. -// If this is 0, the defaults are used and Windows will be booted with no user interaction. -// This can be overridden on the command line with -D CONFIGURE_DRIVER=[0|1] -// -#ifndef CONFIGURE_DRIVER -#define CONFIGURE_DRIVER 0 -#endif - - -// // Paths to the driver to try // #ifndef EFIGUARD_DRIVER_FILENAME @@ -36,6 +28,13 @@ STATIC CHAR16* mDriverPaths[] = { L"\\" EFIGUARD_DRIVER_FILENAME }; +STATIC EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *mTextInputEx = NULL; + +VOID +EFIAPI +BmRepairAllControllers( + IN UINTN ReconnectRepairCount + ); VOID EFIAPI @@ -43,23 +42,65 @@ BmSetMemoryTypeInformationVariable( IN BOOLEAN Boot ); +BOOLEAN +EFIAPI +BmIsAutoCreateBootOption( + IN EFI_BOOT_MANAGER_LOAD_OPTION *BootOption + ); STATIC -BOOLEAN +VOID +ResetTextInput( + VOID + ) +{ + if (mTextInputEx != NULL) + mTextInputEx->Reset(mTextInputEx, FALSE); + else + gST->ConIn->Reset(gST->ConIn, FALSE); +} + +STATIC +UINT16 EFIAPI WaitForKey( VOID ) { - EFI_INPUT_KEY Key = { 0, 0 }; + EFI_KEY_DATA KeyData = { 0 }; UINTN Index = 0; - gBS->WaitForEvent(1, &gST->ConIn->WaitForKey, &Index); - gST->ConIn->ReadKeyStroke(gST->ConIn, &Key); - - return Key.ScanCode != SCAN_ESC; + if (mTextInputEx != NULL) + { + gBS->WaitForEvent(1, &mTextInputEx->WaitForKeyEx, &Index); + mTextInputEx->ReadKeyStrokeEx(mTextInputEx, &KeyData); + } + else + { + gBS->WaitForEvent(1, &gST->ConIn->WaitForKey, &Index); + gST->ConIn->ReadKeyStroke(gST->ConIn, &KeyData.Key); + } + return KeyData.Key.ScanCode; } -#if CONFIGURE_DRIVER +STATIC +UINT16 +EFIAPI +WaitForKeyWithTimeout( + IN UINTN Milliseconds + ) +{ + ResetTextInput(); + gBS->Stall(Milliseconds * 1000); + + EFI_KEY_DATA KeyData = { 0 }; + if (mTextInputEx != NULL) + mTextInputEx->ReadKeyStrokeEx(mTextInputEx, &KeyData); + else + gST->ConIn->ReadKeyStroke(gST->ConIn, &KeyData.Key); + + ResetTextInput(); + return KeyData.Key.ScanCode; +} STATIC UINT16 @@ -76,12 +117,20 @@ PromptInput( { SelectedChar = CHAR_NULL; - EFI_INPUT_KEY Key = { 0, 0 }; + EFI_KEY_DATA KeyData = { 0 }; UINTN Index = 0; - gBS->WaitForEvent(1, &gST->ConIn->WaitForKey, &Index); - gST->ConIn->ReadKeyStroke(gST->ConIn, &Key); + if (mTextInputEx != NULL) + { + gBS->WaitForEvent(1, &mTextInputEx->WaitForKeyEx, &Index); + mTextInputEx->ReadKeyStrokeEx(mTextInputEx, &KeyData); + } + else + { + gBS->WaitForEvent(1, &gST->ConIn->WaitForKey, &Index); + gST->ConIn->ReadKeyStroke(gST->ConIn, &KeyData.Key); + } - if (Key.UnicodeChar == CHAR_LINEFEED || Key.UnicodeChar == CHAR_CARRIAGE_RETURN) + if (KeyData.Key.UnicodeChar == CHAR_LINEFEED || KeyData.Key.UnicodeChar == CHAR_CARRIAGE_RETURN) { SelectedChar = DefaultSelection; break; @@ -89,9 +138,9 @@ PromptInput( for (UINTN i = 0; i < NumAcceptedChars; ++i) { - if (Key.UnicodeChar == AcceptedChars[i]) + if (KeyData.Key.UnicodeChar == AcceptedChars[i]) { - SelectedChar = Key.UnicodeChar; + SelectedChar = KeyData.Key.UnicodeChar; break; } } @@ -104,8 +153,43 @@ PromptInput( return SelectedChar; } -#endif +STATIC +CONST CHAR16* +EFIAPI +StriStr( + IN CONST CHAR16 *String1, + IN CONST CHAR16 *String2 + ) +{ + if (*String2 == L'\0') + return String1; + + while (*String1 != L'\0') + { + CONST CHAR16* FirstMatch = String1; + CONST CHAR16* String2Ptr = String2; + CHAR16 String1Char = CharToUpper(*String1); + CHAR16 String2Char = CharToUpper(*String2Ptr); + + while (String1Char == String2Char && String1Char != L'\0') + { + String1++; + String2Ptr++; + + String1Char = CharToUpper(*String1); + String2Char = CharToUpper(*String2Ptr); + } + + if (String2Char == L'\0') + return FirstMatch; + if (String1Char == L'\0') + return NULL; + + String1 = FirstMatch + 1; + } + return NULL; +} // // Try to find a file by browsing each device @@ -156,7 +240,8 @@ LocateFile( EFI_FILE_READ_ONLY); if (!EFI_ERROR(Status)) { - VolumeHandle->Close(FileHandle); + FileHandle->Close(FileHandle); + VolumeHandle->Close(VolumeHandle); *DevicePath = FileDevicePath(Handles[i], ImagePath); CHAR16 *PathString = ConvertDevicePathToText(*DevicePath, TRUE, TRUE); DEBUG((DEBUG_INFO, "[LOADER] Found file at %S.\r\n", PathString)); @@ -164,9 +249,10 @@ LocateFile( FreePool(PathString); break; } + VolumeHandle->Close(VolumeHandle); } - FreePool(Handles); + FreePool((VOID*)Handles); return Status; } @@ -219,9 +305,8 @@ SetHighestAvailableTextMode( STATIC EFI_STATUS EFIAPI -StartAndConfigureDriver( - IN EFI_HANDLE ImageHandle, - IN EFI_SYSTEM_TABLE* SystemTable +StartEfiGuard( + IN BOOLEAN InteractiveConfiguration ) { EFIGUARD_DRIVER_PROTOCOL* EfiGuardDriverProtocol; @@ -251,7 +336,7 @@ StartAndConfigureDriver( EFI_HANDLE DriverHandle = NULL; Status = gBS->LoadImage(FALSE, // Request is not from boot manager - ImageHandle, + gImageHandle, DriverDevicePath, NULL, 0, @@ -268,58 +353,65 @@ StartAndConfigureDriver( Print(L"[LOADER] StartImage failed: %llx (%r).\r\n", Status, Status); goto Exit; } - - Status = gBS->LocateProtocol(&gEfiGuardDriverProtocolGuid, - NULL, - (VOID**)&EfiGuardDriverProtocol); - if (EFI_ERROR(Status)) - { - Print(L"[LOADER] LocateProtocol failed: %llx (%r).\r\n", Status, Status); - goto Exit; - } } else { + ASSERT_EFI_ERROR(Status); Print(L"[LOADER] The driver is already loaded.\r\n"); - Status = EFI_ALREADY_STARTED; + } + + Status = gBS->LocateProtocol(&gEfiGuardDriverProtocolGuid, + NULL, + (VOID**)&EfiGuardDriverProtocol); + if (EFI_ERROR(Status)) + { + Print(L"[LOADER] LocateProtocol failed: %llx (%r).\r\n", Status, Status); goto Exit; } -#if CONFIGURE_DRIVER - // - // Interactive driver configuration - // - Print(L"\r\nChoose the type of DSE bypass to use, or press ENTER for default:\r\n" - L" [1] No DSE bypass\r\n [2] Boot time DSE bypass\r\n [3] Runtime SetVariable hook (default)\r\n "); - CONST UINT16 AcceptedDseBypasses[] = { L'1', L'2', L'3' }; - CONST UINT16 SelectedDseBypass = PromptInput(AcceptedDseBypasses, - sizeof(AcceptedDseBypasses) / sizeof(UINT16), - L'3'); - - Print(L"Wait for a keypress to continue after each patch stage? (for debugging)\n" - L" [1] Yes\r\n [2] No (default)\r\n "); - CONST UINT16 YesNo[] = { L'1', L'2' }; - CONST UINT16 SelectedWaitForKeyPress = PromptInput(YesNo, - sizeof(YesNo) / sizeof(UINT16), - L'2'); - - EFIGUARD_CONFIGURATION_DATA ConfigData; - if (SelectedDseBypass == L'1') - ConfigData.DseBypassMethod = DSE_DISABLE_NONE; - else if (SelectedDseBypass == L'2') - ConfigData.DseBypassMethod = DSE_DISABLE_AT_BOOT; - else - ConfigData.DseBypassMethod = DSE_DISABLE_SETVARIABLE_HOOK; - ConfigData.WaitForKeyPress = (BOOLEAN)(SelectedWaitForKeyPress == L'1'); + if (InteractiveConfiguration) + { + // + // Interactive driver configuration + // + Print(L"\r\nChoose the type of DSE bypass to use, or press ENTER for default:\r\n" + L" [1] Runtime SetVariable hook (default)\r\n [2] Boot time DSE bypass\r\n [3] No DSE bypass\r\n "); + CONST UINT16 AcceptedDseBypasses[] = { L'1', L'2', L'3' }; + CONST UINT16 SelectedDseBypass = PromptInput(AcceptedDseBypasses, + sizeof(AcceptedDseBypasses) / sizeof(UINT16), + L'1'); + + Print(L"Wait for a keypress to continue after each patch stage?\n" + L" [1] No (default)\r\n [2] Yes (for debugging)\r\n "); + CONST UINT16 NoYes[] = { L'1', L'2' }; + CONST UINT16 SelectedWaitForKeyPress = PromptInput(NoYes, + sizeof(NoYes) / sizeof(UINT16), + L'1'); + + EFIGUARD_CONFIGURATION_DATA ConfigData; + switch (SelectedDseBypass) + { + case L'1': + default: + ConfigData.DseBypassMethod = DSE_DISABLE_SETVARIABLE_HOOK; + break; + case L'2': + ConfigData.DseBypassMethod = DSE_DISABLE_AT_BOOT; + break; + case L'3': + ConfigData.DseBypassMethod = DSE_DISABLE_NONE; + break; + } + ConfigData.WaitForKeyPress = (BOOLEAN)(SelectedWaitForKeyPress == L'2'); - // - // Send the configuration data to the driver - // - Status = EfiGuardDriverProtocol->Configure(&ConfigData); + // + // Send the configuration data to the driver + // + Status = EfiGuardDriverProtocol->Configure(&ConfigData); - if (EFI_ERROR(Status)) - Print(L"[LOADER] Driver Configure() returned error %llx (%r).\r\n", Status, Status); -#endif + if (EFI_ERROR(Status)) + Print(L"[LOADER] Driver Configure() returned error %llx (%r).\r\n", Status, Status); + } Exit: if (DriverDevicePath != NULL) @@ -406,8 +498,7 @@ TryBootOptionsInOrder( // but for some types of boots the filename will always be bootx64.efi, so this can't be avoided. if (!MaybeWindows && ConvertedPath != NULL && - (StrStr(ConvertedPath, L"bootmgfw.efi") != NULL || StrStr(ConvertedPath, L"BOOTMGFW.EFI") != NULL || - StrStr(ConvertedPath, L"bootx64.efi") != NULL || StrStr(ConvertedPath, L"BOOTX64.EFI") != NULL)) + (StriStr(ConvertedPath, L"bootmgfw.efi") != NULL || StriStr(ConvertedPath, L"bootx64.efi") != NULL)) { MaybeWindows = TRUE; } @@ -426,7 +517,9 @@ TryBootOptionsInOrder( // Print what we're booting if (ConvertedPath != NULL) { - Print(L"Booting %Sdevice path %S...\r\n", IsLegacy ? L"legacy " : L"", ConvertedPath); + Print(L"Booting \"%S\"...\r\n -> %S = %S\r\n", + (BootOptions[Index].Description != NULL ? BootOptions[Index].Description : L"<null description>"), + IsLegacy ? L"Legacy path" : L"Path", ConvertedPath); FreePool(ConvertedPath); } @@ -449,6 +542,13 @@ TryBootOptionsInOrder( // Signal the EVT_SIGNAL_READY_TO_BOOT event EfiSignalEventReadyToBoot(); + REPORT_STATUS_CODE(EFI_PROGRESS_CODE, (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_DXE_BS_PC_READY_TO_BOOT_EVENT)); + + // Repair system through DriverHealth protocol + BmRepairAllControllers(0); + + // Save the memory map in the MemoryTypeInformation variable for resuming from ACPI S4 (hibernate) + BmSetMemoryTypeInformationVariable((BootOptions[Index].Attributes & LOAD_OPTION_CATEGORY) == LOAD_OPTION_CATEGORY_BOOT); // Handle BBS entries if (IsLegacy) @@ -470,18 +570,12 @@ TryBootOptionsInOrder( return !EFI_ERROR(BootOptions[Index].Status); } - // So again, DO NOT call this abortion: - //BmSetMemoryTypeInformationVariable((BOOLEAN)((BootOptions[Index].Attributes & LOAD_OPTION_CATEGORY) == LOAD_OPTION_CATEGORY_BOOT)); - // - // OK, maybe call it after all, but pretend this is *not* a boot entry, so that the system will not go into an infinite boot (reset) loop. - // This may or may not fix hibernation related issues (S4 entry/resume). See https://github.com/Mattiwatti/EfiGuard/issues/12 - BmSetMemoryTypeInformationVariable(FALSE); - // Ensure the image path is connected end-to-end by Dispatch()ing any required drivers through DXE services EfiBootManagerConnectDevicePath(BootOptions[Index].FilePath, NULL); // Instead of creating a ramdisk and reading the file into it (¿que?), just pass the path we saved earlier. // This is the point where the driver kicks in via its LoadImage hook. + REPORT_STATUS_CODE(EFI_PROGRESS_CODE, PcdGet32(PcdProgressCodeOsLoaderLoad)); EFI_HANDLE ImageHandle = NULL; Status = gBS->LoadImage(TRUE, gImageHandle, @@ -500,6 +594,7 @@ TryBootOptionsInOrder( gBS->UnloadImage(ImageHandle); Print(L"LoadImage error %llx (%r)\r\n", Status, Status); + BootOptions[Index].Status = Status; continue; } @@ -514,8 +609,11 @@ TryBootOptionsInOrder( ASSERT_EFI_ERROR(Status); // Set image load options from the boot option - ImageInfo->LoadOptionsSize = BootOptions[Index].OptionalDataSize; - ImageInfo->LoadOptions = BootOptions[Index].OptionalData; + if (!BmIsAutoCreateBootOption(&BootOptions[Index])) + { + ImageInfo->LoadOptionsSize = BootOptions[Index].OptionalDataSize; + ImageInfo->LoadOptions = BootOptions[Index].OptionalData; + } // "Clean to NULL because the image is loaded directly from the firmware's boot manager." (EDK2) Good call, I agree ImageInfo->ParentHandle = NULL; @@ -524,6 +622,7 @@ TryBootOptionsInOrder( gBS->SetWatchdogTimer((UINTN)(5 * 60), 0x0000, 0x00, NULL); // Start the image and set the return code in the boot option status + REPORT_STATUS_CODE(EFI_PROGRESS_CODE, PcdGet32(PcdProgressCodeOsLoaderStart)); Status = gBS->StartImage(ImageHandle, &BootOptions[Index].ExitDataSize, &BootOptions[Index].ExitData); @@ -579,19 +678,28 @@ UefiMain( gBS->SetWatchdogTimer(0, 0, 0, NULL); // - // Locate, load, start and configure the driver + // Query the console input handle for the Simple Text Input Ex protocol // - CONST EFI_STATUS DriverStatus = StartAndConfigureDriver(ImageHandle, SystemTable); - if (DriverStatus == EFI_ALREADY_STARTED) - return EFI_SUCCESS; + gBS->HandleProtocol(gST->ConsoleInHandle, &gEfiSimpleTextInputExProtocolGuid, (VOID **)&mTextInputEx); + // + // Allow user to configure the driver by pressing a hotkey + // + Print(L"Press <HOME> to configure EfiGuard...\r\n"); + CONST BOOLEAN InteractiveConfiguration = WaitForKeyWithTimeout(1500) == SCAN_HOME; + + // + // Locate, load, start and configure the driver + // + CONST EFI_STATUS DriverStatus = StartEfiGuard(InteractiveConfiguration); if (EFI_ERROR(DriverStatus)) { Print(L"\r\nERROR: driver load failed with status %llx (%r).\r\n" L"Press any key to continue, or press ESC to return to the firmware or shell.\r\n", DriverStatus, DriverStatus); - if (!WaitForKey()) + if (WaitForKey() == SCAN_ESC) { + gBS->Exit(gImageHandle, DriverStatus, 0, NULL); return DriverStatus; } } diff --git a/Application/Loader/Loader.inf b/Application/Loader/Loader.inf index e817bd5..8d98034 100644 --- a/Application/Loader/Loader.inf +++ b/Application/Loader/Loader.inf @@ -14,13 +14,13 @@ MdePkg/MdePkg.dec EfiGuardPkg/EfiGuardPkg.dec MdeModulePkg/MdeModulePkg.dec - OvmfPkg/OvmfPkg.dec [LibraryClasses] UefiApplicationEntryPoint UefiBootServicesTableLib DebugLib UefiLib + ReportStatusCodeLib DevicePathLib PrintLib UefiBootManagerLib @@ -28,6 +28,8 @@ [Guids] ## SOMETIMES_PRODUCES ## Variable:L"BootCurrent" (The boot option of current boot) gEfiGlobalVariableGuid + ## SOMETIMES_PRODUCES ## Variable:L"MemoryTypeInformation." + gEfiMemoryTypeInformationGuid ## SOMETIMES_PRODUCES gEfiEventReadyToBootGuid ## SOMETIMES_PRODUCES gEfiHobListGuid ## CONSUMES gEfiDxeServicesTableGuid ## CONSUMES @@ -52,9 +54,15 @@ gEfiUsbIoProtocolGuid ## CONSUMES gEfiFirmwareVolume2ProtocolGuid ## CONSUMES gEfiSimpleTextInProtocolGuid ## CONSUMES + gEfiSimpleTextInputExProtocolGuid ## CONSUMES gEfiSimpleTextOutProtocolGuid ## CONSUMES gEfiLegacyBiosProtocolGuid ## CONSUMES +[Pcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdResetOnMemoryTypeInformationChange ## SOMETIMES_CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdProgressCodeOsLoaderLoad ## SOMETIMES_CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdProgressCodeOsLoaderStart ## SOMETIMES_CONSUMES + [BuildOptions.Common] *:DEBUG_*_*_PP_FLAGS = -D EFI_DEBUG *:DEBUG_*_*_CC_FLAGS = -D EFI_DEBUG diff --git a/Application/Loader/Loader.vcxproj b/Application/Loader/Loader.vcxproj index 512b624..9254ab7 100644 --- a/Application/Loader/Loader.vcxproj +++ b/Application/Loader/Loader.vcxproj @@ -19,13 +19,15 @@ <WholeProgramOptimization>true</WholeProgramOptimization> <CharacterSet>Unicode</CharacterSet> <SpectreMitigation>false</SpectreMitigation> + <VcpkgEnabled>false</VcpkgEnabled> + <EnableStdModules>false</EnableStdModules> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <Import Project="$(SolutionDir)\EfiGuard.props" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ItemDefinitionGroup> <ClCompile> - <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">CONFIGURE_DRIVER=1;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">_PCD_GET_MODE_32_PcdProgressCodeOsLoaderLoad=0x3058000;_PCD_GET_MODE_32_PcdProgressCodeOsLoaderStart=0x3058001;%(PreprocessorDefinitions)</PreprocessorDefinitions> <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(SolutionDir)Include;$(EDK_PATH)\OvmfPkg\Csm\Include</AdditionalIncludeDirectories> </ClCompile> <Link> |