From a9c383ee7744c3206d9528aaaddb8bda9070a890 Mon Sep 17 00:00:00 2001 From: sippeangelo Date: Fri, 11 Nov 2016 19:30:55 +0100 Subject: [PATCH] Better platform separation along with Windows implementation (Linux refactorings still untested) --- CMakeLists.txt | 3 ++ src/CMakeLists.txt | 3 +- src/Platform.h | 5 +++ src/Platform_Linux.cpp | 26 ++++++++++++ src/Platform_Windows.cpp | 42 +++++++++++++++++++- src/UEFI.cpp | 86 ++++++++++++++++++++++++++++++++++++++++ src/UEFI.h | 10 ++++- src/UEFI_Linux.cpp | 2 +- src/UEFI_Windows.cpp | 2 - 9 files changed, 171 insertions(+), 8 deletions(-) create mode 100644 src/UEFI.cpp delete mode 100644 src/UEFI_Windows.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index be66e6b..b196f4b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,5 +40,8 @@ set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS $<$:DEBUG> ) +set(Boost_USE_STATIC_LIBS on) +add_definitions(-DBOOST_ALL_NO_LIB) + include_directories(${PROJECT_SOURCE_DIR}/src) add_subdirectory(src) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4c1b4a9..b60899d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -17,18 +17,17 @@ include_directories( set(SOURCE_FILES Platform.h UEFI.h + UEFI.cpp main.cpp ) if(WIN32) set(SOURCE_FILES ${SOURCE_FILES} Platform_Windows.cpp - UEFI_Windows.cpp ) endif() if(UNIX) set(SOURCE_FILES ${SOURCE_FILES} Platform_Linux.cpp - UEFI_Linux.cpp ) endif() source_group("" FILES ${SOURCE_FILES}) diff --git a/src/Platform.h b/src/Platform.h index e160ca2..96d0acb 100644 --- a/src/Platform.h +++ b/src/Platform.h @@ -2,10 +2,15 @@ #define Platform_h__ #include +#include +#include namespace Platform { void Initialize(); +bool UEFICheck(); +std::pair UEFIReadVariable(const std::string& name); +void UEFIWriteVariable(const std::string& name, void* buffer, std::size_t size); void Reboot(); } diff --git a/src/Platform_Linux.cpp b/src/Platform_Linux.cpp index 76c2d96..c5f3027 100644 --- a/src/Platform_Linux.cpp +++ b/src/Platform_Linux.cpp @@ -7,6 +7,32 @@ void Platform::Initialize() // TODO: root permission check here } +bool Platform::UEFICheck() +{ + return efi_variables_supported(); +} + +std::pair Platform::UEFIReadVariable(const std::string& name) +{ + std::uint8_t* data = nullptr; + std::size_t size; + std::uint32_t attributes; + if (efi_get_variable(EFI_GLOBAL_GUID, name.c_str(), &data, &size, &attributes) != 0) { + std::stringstream message; + message << "Failed to read UEFI variable \"" << name << "\""; + throw std::runtime_error(message.str()); + } + + return std::make_pair(data, size); +} + +void Platform::UEFIWriteVariable(const std::string& name, void* buffer, std::size_t size) +{ + if (efi_set_variable(EFI_GLOBAL_GUID, name.c_str(), static_cast(buffer), size, EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE, 0600) != 0) { + throw std::runtime_error("Failed to write UEFI variable: BootNext"); + } +} + void Platform::Reboot() { sync(); diff --git a/src/Platform_Windows.cpp b/src/Platform_Windows.cpp index 03f7d65..cfb614a 100644 --- a/src/Platform_Windows.cpp +++ b/src/Platform_Windows.cpp @@ -1,7 +1,8 @@ #include "Platform.h" -#include #include +const LPCWSTR EFI_GLOBAL_GUID = L"{8BE4DF61-93CA-11D2-AA0D-00E098032B8C}"; + void SetPrivilege(HANDLE hToken, LPCWSTR lpszPrivilege, bool bEnablePrivilege) { LUID luid; @@ -44,6 +45,45 @@ void Platform::Initialize() SetPrivilege(hToken, SE_SHUTDOWN_NAME, true); } +bool Platform::UEFICheck() +{ + FIRMWARE_TYPE firmwareType; + if (GetFirmwareType(&firmwareType) == 0) { + std::stringstream message; + message << "GetFirmwareType failed: " << GetLastError(); + throw std::runtime_error(message.str()); + } + + return firmwareType == FirmwareTypeUefi; +} + +std::pair Platform::UEFIReadVariable(const std::string& name) +{ + auto nameUTF16 = boost::locale::conv::utf_to_utf(name); + + const std::size_t bufferSize = 2048; + std::uint8_t* data = new std::uint8_t[bufferSize]; + std::size_t size = GetFirmwareEnvironmentVariableW(reinterpret_cast(nameUTF16.c_str()), EFI_GLOBAL_GUID, data, sizeof(std::uint8_t) * bufferSize); + if (size == 0) { + std::stringstream message; + message << "Failed to read UEFI variable \"" << name << "\": " << GetLastError(); + throw std::runtime_error(message.str()); + } + return std::make_pair(data, size); +} + +void Platform::UEFIWriteVariable(const std::string& name, void* buffer, std::size_t size) +{ + auto nameUTF16 = boost::locale::conv::utf_to_utf(name); + + bool result = SetFirmwareEnvironmentVariableW(reinterpret_cast(nameUTF16.c_str()), EFI_GLOBAL_GUID, buffer, size); + if (!result) { + std::stringstream message; + message << "Failed to write UEFI variable \"" << name << "\": " << GetLastError(); + throw std::runtime_error(message.str()); + } +} + void Platform::Reboot() { if (InitiateSystemShutdown(nullptr, nullptr, 0, false, true) == 0) { diff --git a/src/UEFI.cpp b/src/UEFI.cpp new file mode 100644 index 0000000..1f780ce --- /dev/null +++ b/src/UEFI.cpp @@ -0,0 +1,86 @@ +#include "UEFI.h" + +UEFI::UEFI() +{ + if (!Platform::UEFICheck()) { + throw std::runtime_error("System is not UEFI!"); + } +} + +UEFI::BootOption UEFI::ReadBootCurrent() +{ + auto data = Platform::UEFIReadVariable("BootCurrent"); + + UEFI::BootOption option; + option.ID = *reinterpret_cast(data.first); + option.Description = readDescription(option.ID); + + std::free(data.first); + + return option; +} + +UEFI::BootOption UEFI::ReadBootNext() +{ + auto data = Platform::UEFIReadVariable("BootNext"); + + UEFI::BootOption option; + option.ID = *reinterpret_cast(data.first); + option.Description = readDescription(option.ID); + + std::free(data.first); + + return option; +} + +void UEFI::WriteBootNext(const UEFI::BootOption& option) +{ + auto data = const_cast(&option.ID); + Platform::UEFIWriteVariable("BootNext", data, sizeof(std::uint16_t)); +} + +std::vector UEFI::ReadBootOrder() +{ + auto data = Platform::UEFIReadVariable("BootOrder"); + + int numOptions = data.second / sizeof(std::uint16_t); + std::vector bootOrder(numOptions); + for (int i = 0; i < numOptions; ++i) { + auto& option = bootOrder[i]; + option.ID = *(reinterpret_cast(data.first) + i); + option.Description = readDescription(option.ID); + } + + std::free(data.first); + + return bootOrder; +} + +void UEFI::WriteBootOrder(const std::vector& order) +{ + std::vector data(order.size()); + for (int i = 0; i < order.size(); ++i) { + data[i] = order[i].ID; + } + + Platform::UEFIWriteVariable("BootOrder", data.data(), sizeof(std::uint16_t) * data.size()); +} + +std::string UEFI::readDescription(std::uint16_t id) +{ + auto data = Platform::UEFIReadVariable(optionVarFromID(id)); + + char16_t* descUTF16 = reinterpret_cast(data.first + EFI_LOAD_OPTION_DESCRIPTION_OFFSET); + std::string descUTF8 = boost::locale::conv::utf_to_utf(descUTF16); + + std::free(data.first); + + return descUTF8; +} + +std::string UEFI::optionVarFromID(std::uint16_t id) +{ + char entry[9] = "Boot####"; + std::snprintf(entry, 9, "Boot%04X", id); + return std::string(entry); +} diff --git a/src/UEFI.h b/src/UEFI.h index a740153..c6194a4 100644 --- a/src/UEFI.h +++ b/src/UEFI.h @@ -1,9 +1,14 @@ +#ifndef UEFI_h__ +#define UEFI_h__ + #include #include #include #include #include #include +#include +#include "Platform.h" const std::size_t EFI_LOAD_OPTION_DESCRIPTION_OFFSET = sizeof(std::uint32_t) + sizeof(std::uint16_t); @@ -25,7 +30,8 @@ public: void WriteBootOrder(const std::vector& bootOrder); private: - std::string ReadDescription(std::uint16_t id); - std::string VarNameFromID(std::uint16_t id); + std::string readDescription(std::uint16_t id); + std::string optionVarFromID(std::uint16_t id); }; +#endif \ No newline at end of file diff --git a/src/UEFI_Linux.cpp b/src/UEFI_Linux.cpp index 21afac6..853046d 100644 --- a/src/UEFI_Linux.cpp +++ b/src/UEFI_Linux.cpp @@ -14,7 +14,7 @@ std::pair ReadVariable(const std::string& name) std::uint32_t attributes; if (efi_get_variable(EFI_GLOBAL_GUID, name.c_str(), &data, &size, &attributes) != 0) { std::stringstream message; - message << "Failed to read EFI variable: " << name; + message << "Failed to read EFI variable \"" << name << "\""; throw std::runtime_error(message.str()); } diff --git a/src/UEFI_Windows.cpp b/src/UEFI_Windows.cpp deleted file mode 100644 index 91f67d3..0000000 --- a/src/UEFI_Windows.cpp +++ /dev/null @@ -1,2 +0,0 @@ -#include "UEFI.h" -