kernel32: Implement GetFirmwareType, GetFirmwareEnvironmentVariableExA, GetFirmwareEnvironmentVariableExW, GetFirmwareEnvironmentVariableA, GetFirmwareEnvironmentVariableW.
Example:
```C #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <windows.h> #include <winnt.h>
/* x86_64-w64-mingw32-gcc wuefi.c -o wuefi.exe -Wl,--subsystem,console */ int main(int argc, char **argv) { BOOLEAN secureboot = 0; char asus_license[2048]; FIRMWARE_TYPE type;
if(GetFirmwareType(&type)) printf("Firmware Type: %s\n", type == FirmwareTypeUefi ? "UEFI" : "BIOS");
if(GetFirmwareEnvironmentVariableA("SecureBoot", "{8be4df61-93ca-11d2-aa0d-00e098032b8c}", &secureboot, sizeof(BOOLEAN))) printf("SecureBoot: %s\n", secureboot ? "enabled" : "disabled");
if(GetFirmwareEnvironmentVariableW(L"SecureBoot", L"{8be4df61-93ca-11d2-aa0d-00e098032b8c}", &secureboot, sizeof(BOOLEAN))) printf("SecureBoot: %s\n", secureboot ? "enabled" : "disabled");
if(GetFirmwareEnvironmentVariableA("AsusOnboardToolLicense", "02076249-a52b-420e-bd53-aed044349379", asus_license, sizeof(asus_license))) printf("Asus License: \n%s\n\n", asus_license);
return 0; } ```
``` Firmware Type: UEFI SecureBoot: enabled SecureBoot: enabled Asus License: bigtext== [LicenseID]:FED7A1 [Model]:Dummy-BaseBoard-Id [Tools]:WAsusDmiS,LogoFlashS,ASUSFwConfigS,AsusEventLogS,AsusNvLockS [Customer]:DummyLic [Expire]:2022-11-24 ```
From: Grigory Vasilyev h0tc0d3@gmail.com
--- dlls/kernel32/Makefile.in | 2 + dlls/kernel32/firmware.c | 137 ++++++++++++++++++++++++++++++++++++ dlls/kernel32/kernel32.spec | 2 + dlls/kernel32/kernel_main.c | 2 + dlls/kernel32/process.c | 73 ++++++++++++++++--- dlls/kernel32/unixlib.h | 44 ++++++++++++ 6 files changed, 252 insertions(+), 8 deletions(-) create mode 100644 dlls/kernel32/firmware.c create mode 100644 dlls/kernel32/unixlib.h
diff --git a/dlls/kernel32/Makefile.in b/dlls/kernel32/Makefile.in index ca5bb437e24..feb02d23e39 100644 --- a/dlls/kernel32/Makefile.in +++ b/dlls/kernel32/Makefile.in @@ -1,5 +1,6 @@ EXTRADEFS = -D_KERNEL32_ -D_NORMALIZE_ MODULE = kernel32.dll +UNIXLIB = kernel32.so IMPORTLIB = kernel32 IMPORTS = kernelbase ntdll winecrt0
@@ -14,6 +15,7 @@ SOURCES = \ console.c \ debugger.c \ file.c \ + firmware.c \ heap.c \ kernel_main.c \ locale.c \ diff --git a/dlls/kernel32/firmware.c b/dlls/kernel32/firmware.c new file mode 100644 index 00000000000..3bdf720188d --- /dev/null +++ b/dlls/kernel32/firmware.c @@ -0,0 +1,137 @@ +/* + * Kernel32 Firmware Interface + * + * Copyright (C) 2024 Grigory Vasilyev + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#if 0 +#pragma makedep unix +#endif + +#include <errno.h> +#include <dirent.h> +#include <fcntl.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "unixlib.h" +#include "windef.h" + +/* Get Firmware type */ +static NTSTATUS kernel32_unix_get_firmware_type(void *args) +{ + FIRMWARE_TYPE *type = args; + + DIR *efi_dir = opendir("/sys/firmware/efi"); + + if (efi_dir) { + closedir(efi_dir); + *type = FirmwareTypeUefi; + } else { + *type = FirmwareTypeBios; + } + + return STATUS_SUCCESS; +} + +/* Get Firmware Variable */ +static NTSTATUS kernel32_unix_get_firmware_variable(void *args) +{ + int fd, rc; + size_t bytes, pos = 0; + ssize_t ssz; + char tguid[37]; + char filename[128]; + struct stat sb = {0}; + struct wine_get_firmware_variable_params *params = args; + + int gsize = strlen(params->guid); + + if (params->guid[0] == '{' && params->guid[(gsize - 1)] == '}') { + if (gsize - 2 < sizeof(tguid)) + bytes = gsize - 1; + else + bytes = sizeof(tguid); + memcpy(tguid, params->guid + 1, bytes); + tguid[bytes - 1] = '\0'; + snprintf(filename, sizeof(filename), "/sys/firmware/efi/efivars/%s-%s", params->name, tguid); + } else { + snprintf(filename, sizeof(filename), "/sys/firmware/efi/efivars/%s-%s", params->name, params->guid); + } + + fd = open(filename, O_RDONLY | O_NONBLOCK); + if (fd < 0) + goto done; + + rc = fstat(fd, &sb); + if (rc < 0 || sb.st_size == 0) + goto done; + + if (sb.st_size - 4 < params->size) + bytes = sb.st_size - 4; + else + bytes = params->size; + +try_read_attributes: + ssz = read(fd, params->attributes, 4); + if (ssz < 0) { + if (errno == EAGAIN || errno == EINTR) + goto try_read_attributes; + goto done; + } + + while (pos < bytes) { + ssz = read(fd, (char *) params->buffer + pos, bytes - pos); + if (ssz < 0) { + if (errno == EAGAIN || errno == EINTR) + continue; + goto done; + } + pos += ssz; + } + + close(fd); + params->status = ssz & 0xFFFFFFFF; + return STATUS_SUCCESS; + +done: + if (fd >= 0) + close(fd); + params->status = 0; + return STATUS_UNSUCCESSFUL; +} + +const unixlib_entry_t __wine_unix_call_funcs[] = { + kernel32_unix_get_firmware_type, + kernel32_unix_get_firmware_variable, +}; + +#ifdef _WIN64 + +const unixlib_entry_t __wine_unix_call_wow64_funcs[] = { + kernel32_unix_get_firmware_type, + kernel32_unix_get_firmware_variable, +}; + +#endif /* _WIN64 */ diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec index 7ed8048f5d0..5d3556a2078 100644 --- a/dlls/kernel32/kernel32.spec +++ b/dlls/kernel32/kernel32.spec @@ -695,7 +695,9 @@ @ stdcall -import GetFileType(long) @ stdcall -import GetFinalPathNameByHandleA(long ptr long long) @ stdcall -import GetFinalPathNameByHandleW(long ptr long long) +@ stdcall GetFirmwareEnvironmentVariableExA(str str ptr long ptr) @ stdcall GetFirmwareEnvironmentVariableA(str str ptr long) +@ stdcall GetFirmwareEnvironmentVariableExW(wstr wstr ptr long ptr) @ stdcall GetFirmwareEnvironmentVariableW(wstr wstr ptr long) @ stdcall GetFirmwareType(ptr) @ stdcall -import GetFullPathNameA(str long ptr ptr) diff --git a/dlls/kernel32/kernel_main.c b/dlls/kernel32/kernel_main.c index ae16195a550..70443cbce2a 100644 --- a/dlls/kernel32/kernel_main.c +++ b/dlls/kernel32/kernel_main.c @@ -31,6 +31,7 @@
#include "kernel_private.h" #include "wine/debug.h" +#include "wine/unixlib.h"
WINE_DEFAULT_DEBUG_CHANNEL(process);
@@ -161,6 +162,7 @@ BOOL WINAPI DllMain( HINSTANCE hinst, DWORD reason, LPVOID reserved ) { case DLL_PROCESS_ATTACH: DisableThreadLibraryCalls( hinst ); + if (__wine_init_unix_call()) return FALSE; return process_attach( hinst ); case DLL_PROCESS_DETACH: WritePrivateProfileSectionW( NULL, NULL, NULL ); diff --git a/dlls/kernel32/process.c b/dlls/kernel32/process.c index 2a4ddc68f02..ffa8ce25deb 100644 --- a/dlls/kernel32/process.c +++ b/dlls/kernel32/process.c @@ -37,6 +37,7 @@ #include "ddk/wdm.h" #include "wine/asm.h" #include "wine/debug.h" +#include "unixlib.h"
WINE_DEFAULT_DEBUG_CHANNEL(process);
@@ -718,14 +719,60 @@ WORD WINAPI GetMaximumProcessorGroupCount(void) return groups; }
+/*********************************************************************** + * GetFirmwareEnvironmentVariableExA (KERNEL32.@) + */ +DWORD WINAPI GetFirmwareEnvironmentVariableExA(LPCSTR name, LPCSTR guid, PVOID buffer, DWORD size, PDWORD attributes) +{ + struct wine_get_firmware_variable_params params = { 0 }; + params.name = name; + params.guid = guid; + params.buffer = buffer; + params.size = size; + params.attributes = attributes; + WINE_UNIX_CALL( kernel32_unix_func_get_firmware_variable, ¶ms ); + return params.status; +} + /*********************************************************************** * GetFirmwareEnvironmentVariableA (KERNEL32.@) */ DWORD WINAPI GetFirmwareEnvironmentVariableA(LPCSTR name, LPCSTR guid, PVOID buffer, DWORD size) { - FIXME("stub: %s %s %p %lu\n", debugstr_a(name), debugstr_a(guid), buffer, size); - SetLastError(ERROR_INVALID_FUNCTION); - return 0; + DWORD dwAttributes; + return GetFirmwareEnvironmentVariableExA( name, guid, buffer, size, &dwAttributes ); +} + +/*********************************************************************** + * GetFirmwareEnvironmentVariableW (KERNEL32.@) + */ +DWORD WINAPI GetFirmwareEnvironmentVariableExW(LPCWSTR name, LPCWSTR guid, PVOID buffer, DWORD size, PDWORD attributes) +{ + DWORD ret; + INT len; + char *aname; + char *aguid; + + len = lstrlenW( name ) + 1; + ret = WideCharToMultiByte( GetOEMCP(), 0, name, len, NULL, 0, NULL, NULL ); + if ( !( aname = RtlAllocateHeap( GetProcessHeap(), 0, ret * sizeof(aname) ) ) ) + return 0; + WideCharToMultiByte( GetOEMCP(), 0, name, len, aname, ret, NULL, NULL ); + + len = lstrlenW( guid ) + 1; + ret = WideCharToMultiByte( GetOEMCP(), 0, guid, len, NULL, 0, NULL, NULL ); + if ( !( aguid = RtlAllocateHeap( GetProcessHeap(), 0, ret * sizeof(aguid) ) ) ) { + HeapFree( GetProcessHeap(), 0, aname ); + return 0; + } + WideCharToMultiByte( GetOEMCP(), 0, guid, len, aguid, ret, NULL, NULL ); + + ret = GetFirmwareEnvironmentVariableExA( aname, aguid, buffer, size, attributes ); + + HeapFree( GetProcessHeap(), 0, aname ); + HeapFree( GetProcessHeap(), 0, aguid ); + + return ret; }
/*********************************************************************** @@ -733,9 +780,8 @@ DWORD WINAPI GetFirmwareEnvironmentVariableA(LPCSTR name, LPCSTR guid, PVOID buf */ DWORD WINAPI GetFirmwareEnvironmentVariableW(LPCWSTR name, LPCWSTR guid, PVOID buffer, DWORD size) { - FIXME("stub: %s %s %p %lu\n", debugstr_w(name), debugstr_w(guid), buffer, size); - SetLastError(ERROR_INVALID_FUNCTION); - return 0; + DWORD dwAttributes; + return GetFirmwareEnvironmentVariableExW( name, guid, buffer, size, &dwAttributes ); }
/*********************************************************************** @@ -763,10 +809,21 @@ BOOL WINAPI SetFirmwareEnvironmentVariableW(const WCHAR *name, const WCHAR *guid */ BOOL WINAPI GetFirmwareType(FIRMWARE_TYPE *type) { - if (!type) + static FIRMWARE_TYPE ftype = FirmwareTypeUnknown; + + if ( !type ) return FALSE;
- *type = FirmwareTypeUnknown; + if( ftype != FirmwareTypeUnknown ) + { + *type = ftype; + return TRUE; + } + + WINE_UNIX_CALL( kernel32_unix_func_get_firmware_type, &ftype ); + + *type = ftype; + return TRUE; }
diff --git a/dlls/kernel32/unixlib.h b/dlls/kernel32/unixlib.h new file mode 100644 index 00000000000..321c62194a6 --- /dev/null +++ b/dlls/kernel32/unixlib.h @@ -0,0 +1,44 @@ +/* + * Kernel32 Unix interface + * + * Copyright (C) 2024 Grigory Vasilyev + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef __WINE_KERNEL_UNIXLIB_H +#define __WINE_KERNEL_UNIXLIB_H + +#include "wine/unixlib.h" + +struct wine_get_firmware_variable_params +{ + LPCSTR name; + LPCSTR guid; + PVOID buffer; + DWORD size; + PDWORD attributes; + DWORD status; +}; + +enum kernel32_unix_func +{ + kernel32_unix_func_get_firmware_type, + kernel32_unix_func_get_firmware_variable, +}; + +extern unixlib_handle_t __wine_unixlib_handle; + +#endif /* __WINE_KERNEL_UNIXLIB_H */
Hi,
It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated.
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=148138
Your paranoid android.
=== debian11 (build log) ===
/home/winetest/tools/testbot/var/wine-win32/../wine/dlls/winecrt0/unix_lib.c:52: undefined reference to `_imp__LdrGetDllHandle@16' /usr/bin/i686-w64-mingw32-ld: /home/winetest/tools/testbot/var/wine-win32/../wine/dlls/winecrt0/unix_lib.c:53: undefined reference to `_imp__RtlFindExportedRoutineByName@8' /home/winetest/tools/testbot/var/wine-win32/../wine/dlls/winecrt0/unix_lib.c:94: undefined reference to `_imp__NtQueryVirtualMemory@24' collect2: error: ld returned 1 exit status Task: The win32 Wine build failed
=== debian11b (build log) ===
/home/winetest/tools/testbot/var/wine-wow64/../wine/dlls/winecrt0/unix_lib.c:52: undefined reference to `__imp_LdrGetDllHandle' /usr/bin/x86_64-w64-mingw32-ld: /home/winetest/tools/testbot/var/wine-wow64/../wine/dlls/winecrt0/unix_lib.c:53: undefined reference to `__imp_RtlFindExportedRoutineByName' /home/winetest/tools/testbot/var/wine-wow64/../wine/dlls/winecrt0/unix_lib.c:94: undefined reference to `__imp_NtQueryVirtualMemory' collect2: error: ld returned 1 exit status Task: The wow64 Wine build failed
This MR should probably be split to separate commits for GetFirmwareType() and GetFirmwareEnvironmentVariable*() implementations
Also there are no tests for any of these functions (but I'm not sure how well they would work in the TestBot VMs)
On Sun Sep 1 18:35:15 2024 +0000, Aida Jonikienė wrote:
Also there are no tests for any of these functions (but I'm not sure how well they would work in the TestBot VMs)
Tests are not possible for UEFI, the container will most likely not have access to UEFI due to security reasons. The code is too simple to cover it with tests, and we can only test SecureBoot status. I added the code above for the test, the SecureBoot check code I found in an existing project, and I additionally checked the functionality of the code for getting text fields and uefi attributes on my computer, on another computer these names and guid may not be present. get_firmware_type checks only for the presence of the efi directory, and in case of an error always returns FirmwareTypeBios. So I doubt that it is possible to do real tests that could check anything correctly.
On Sun Sep 1 18:32:07 2024 +0000, Aida Jonikienė wrote:
This MR should probably be split to separate commits for GetFirmwareType() and GetFirmwareEnvironmentVariable*() implementations
I think it's worth waiting for the reviewers' comments on this matter.
This should likely call to some nt syscall instead, maybe NtQuerySystemEnvironmentValueEx and such. I don't think we necessarily want a unixlib for kernel32 in general.
On Sun Sep 1 19:27:31 2024 +0000, Nikolay Sivov wrote:
This should likely call to some nt syscall instead, maybe NtQuerySystemEnvironmentValueEx and such. I don't think we necessarily want a unixlib for kernel32 in general.
NtQuerySystemInformation and NtQuerySystemEnvironmentValueEx