]> mj.ucw.cz Git - pciutils.git/blob - lib/i386-io-windows.h
windows: Do not show unwanted file-not-found GUI message box
[pciutils.git] / lib / i386-io-windows.h
1 /*
2  *      The PCI Library -- Access to i386 I/O ports on Windows
3  *
4  *      Copyright (c) 2004 Alexander Stock <stock.alexander@gmx.de>
5  *      Copyright (c) 2006 Martin Mares <mj@ucw.cz>
6  *      Copyright (c) 2021 Pali Rohár <pali@kernel.org>
7  *
8  *      Can be freely distributed and used under the terms of the GNU GPL v2+
9  *
10  *      SPDX-License-Identifier: GPL-2.0-or-later
11  */
12
13 #include <windows.h>
14 #include "win32-helpers.h"
15
16 #include "i386-io-access.h"
17
18 /*
19  * Define __readeflags() for MSVC and GCC compilers.
20  * MSVC since version 14.00 included in WDK 6001 and since version 15.00
21  * included in VS 2008 provides __readeflags() intrinsic for both 32 and 64-bit
22  * modes. WDK 6001 defines macro __BUILDMACHINE__ to value WinDDK. VS 2008 does
23  * not define this macro at all. MSVC throws error if name of user defined
24  * function conflicts with some MSVC intrinsic.
25  * MSVC supports inline assembly via __asm keyword in 32-bit mode only.
26  * GCC version 4.9.0 and higher provides __builtin_ia32_readeflags_uXX()
27  * builtin for XX-mode. This builtin is also available as __readeflags()
28  * function indirectly via <x86intrin.h> header file.
29  *
30  * CAVEAT: Semicolon in MSVC __asm block means start of the comment, and not
31  * end of the __asm statement, like it is for all other C statements. Also
32  * function which uses MSVC inline assembly cannot be inlined to another function
33  * (compiler reports a warning about it, not a fatal error). So we add explicit
34  * curly brackets for __asm blocks, remove misleading semicolons and do not
35  * declare functions as inline.
36  */
37 #if defined(_MSC_VER) && (_MSC_VER >= 1500 || (_MSC_VER >= 1400 && defined(__BUILDMACHINE__)))
38 #pragma intrinsic(__readeflags)
39 #elif defined(__GNUC__) && ((__GNUC__ == 4 && __GNUC_MINOR__ >= 9) || (__GNUC__ > 4))
40 #include <x86intrin.h>
41 #elif defined(_MSC_VER) && defined(_M_IX86)
42 static unsigned int
43 __readeflags(void)
44 {
45   __asm {
46     pushfd
47     pop eax
48   }
49 }
50 #elif defined(__GNUC__)
51 static inline unsigned
52 #ifdef __x86_64__
53 long long
54 #endif
55 int
56 __readeflags(void)
57 {
58   unsigned
59 #ifdef __x86_64__
60   long long
61 #endif
62   int eflags;
63   asm volatile ("pushf\n\tpop %0\n" : "=r" (eflags));
64   return eflags;
65 }
66 #else
67 #error "Unsupported compiler"
68 #endif
69
70 /* Read IOPL of the current process, IOPL is stored in eflag bits [13:12]. */
71 #define read_iopl() ((__readeflags() >> 12) & 0x3)
72
73 /*
74  * Unfortunately NtSetInformationProcess() function, ProcessUserModeIOPL
75  * constant and all other helpers for its usage are not specified in any
76  * standard WinAPI header file. So define all of required constants and types.
77  * Function NtSetInformationProcess() is available in ntdll.dll library on all
78  * Windows systems but marked as it can be removed in some future version.
79  */
80 #ifndef NTSTATUS
81 #define NTSTATUS LONG
82 #endif
83 #ifndef STATUS_NOT_IMPLEMENTED
84 #define STATUS_NOT_IMPLEMENTED (NTSTATUS)0xC0000002
85 #endif
86 #ifndef STATUS_PRIVILEGE_NOT_HELD
87 #define STATUS_PRIVILEGE_NOT_HELD (NTSTATUS)0xC0000061
88 #endif
89 #ifndef PROCESSINFOCLASS
90 #define PROCESSINFOCLASS DWORD
91 #endif
92 #ifndef ProcessUserModeIOPL
93 #define ProcessUserModeIOPL 16
94 #endif
95 typedef NTSTATUS (NTAPI *NtSetInformationProcessProt)(HANDLE ProcessHandle, PROCESSINFOCLASS ProcessInformationClass, PVOID ProcessInformation, ULONG ProcessInformationLength);
96 typedef ULONG (NTAPI *RtlNtStatusToDosErrorProt)(NTSTATUS Status);
97
98 /*
99  * ProcessUserModeIOPL is syscall for NT kernel to change x86 IOPL
100  * of the current running process to 3.
101  *
102  * Process handle argument for ProcessUserModeIOPL is ignored and
103  * IOPL is always changed for the current running process. So pass
104  * GetCurrentProcess() handle for documentation purpose. Process
105  * information buffer and length are unused for ProcessUserModeIOPL.
106  *
107  * ProcessUserModeIOPL may success (return value >= 0) or may fail
108  * because it is not implemented or because of missing privilege.
109  * Other errors are not defined, so handle them as unknown.
110  */
111 static BOOL
112 SetProcessUserModeIOPLFunc(LPVOID Arg)
113 {
114   RtlNtStatusToDosErrorProt RtlNtStatusToDosErrorPtr = (RtlNtStatusToDosErrorProt)(((LPVOID *)Arg)[1]);
115   NtSetInformationProcessProt NtSetInformationProcessPtr = (NtSetInformationProcessProt)(((LPVOID *)Arg)[0]);
116   NTSTATUS nt_status = NtSetInformationProcessPtr(GetCurrentProcess(), ProcessUserModeIOPL, NULL, 0);
117   if (nt_status >= 0)
118     return TRUE;
119
120   /*
121    * If we have optional RtlNtStatusToDosError() function then use it for
122    * translating NT status to Win32 error. If we do not have it then translate
123    * two important status codes which we use later STATUS_NOT_IMPLEMENTED and
124    * STATUS_PRIVILEGE_NOT_HELD.
125    */
126   if (RtlNtStatusToDosErrorPtr)
127     SetLastError(RtlNtStatusToDosErrorPtr(nt_status));
128   else if (nt_status == STATUS_NOT_IMPLEMENTED)
129     SetLastError(ERROR_INVALID_FUNCTION);
130   else if (nt_status == STATUS_PRIVILEGE_NOT_HELD)
131     SetLastError(ERROR_PRIVILEGE_NOT_HELD);
132   else
133     SetLastError(ERROR_GEN_FAILURE);
134
135   return FALSE;
136 }
137
138 /*
139  * Set x86 I/O Privilege Level to 3 for the whole current NT process. Do it via
140  * NtSetInformationProcess() call with ProcessUserModeIOPL information class,
141  * which is supported by 32-bit Windows NT kernel versions and requires Tcb
142  * privilege.
143  */
144 static BOOL
145 SetProcessUserModeIOPL(VOID)
146 {
147   LPVOID Arg[2];
148   UINT prev_error_mode;
149   HMODULE ntdll;
150   BOOL ret;
151
152   /*
153    * Load ntdll.dll library with disabled critical-error-handler and
154    * file-not-found message box.
155    * It means that NT kernel does not show unwanted GUI message box to user
156    * when LoadLibrary() function fails.
157    */
158   prev_error_mode = win32_change_error_mode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
159   ntdll = LoadLibrary(TEXT("ntdll.dll"));
160   win32_change_error_mode(prev_error_mode);
161   if (!ntdll)
162     {
163       SetLastError(ERROR_INVALID_FUNCTION);
164       return FALSE;
165     }
166
167   /* Retrieve pointer to NtSetInformationProcess() function. */
168   Arg[0] = (LPVOID)GetProcAddress(ntdll, "NtSetInformationProcess");
169   if (!Arg[0])
170     {
171       FreeLibrary(ntdll);
172       SetLastError(ERROR_INVALID_FUNCTION);
173       return FALSE;
174     }
175
176   /* Retrieve pointer to optional RtlNtStatusToDosError() function, it may be NULL. */
177   Arg[1] = (LPVOID)GetProcAddress(ntdll, "RtlNtStatusToDosError");
178
179   /* Call ProcessUserModeIOPL with Tcb privilege. */
180   ret = win32_call_func_with_tcb_privilege(SetProcessUserModeIOPLFunc, (LPVOID)&Arg);
181
182   FreeLibrary(ntdll);
183
184   if (!ret)
185     return FALSE;
186
187   /*
188    * Some Windows NT kernel versions (e.g. Windows 2003 x64) do not
189    * implement ProcessUserModeIOPL syscall at all but incorrectly
190    * returns success when it is called by user process. So always
191    * after this call verify that IOPL is set to 3.
192    */
193   if (read_iopl() != 3)
194     {
195       SetLastError(ERROR_INVALID_FUNCTION);
196       return FALSE;
197     }
198
199   return TRUE;
200 }
201
202 static int
203 intel_setup_io(struct pci_access *a)
204 {
205 #ifndef _WIN64
206   /* 16/32-bit non-NT systems allow applications to access PCI I/O ports without any special setup. */
207   if (win32_is_non_nt_system())
208     {
209       a->debug("Detected 16/32-bit non-NT system, skipping NT setup...");
210       return 1;
211     }
212 #endif
213
214   /* Check if we have I/O permission */
215   if (read_iopl() == 3)
216     {
217       a->debug("IOPL is already set to 3, skipping NT setup...");
218       return 1;
219     }
220
221   /* On NT-based systems issue ProcessUserModeIOPL syscall which changes IOPL to 3. */
222   if (!SetProcessUserModeIOPL())
223     {
224       DWORD error = GetLastError();
225       a->debug("NT ProcessUserModeIOPL call failed: %s.", error == ERROR_INVALID_FUNCTION ? "Call is not supported" : win32_strerror(error));
226       return 0;
227     }
228
229   a->debug("NT ProcessUserModeIOPL call succeeded...");
230   return 1;
231 }
232
233 static inline void
234 intel_cleanup_io(struct pci_access *a UNUSED)
235 {
236   /*
237    * 16/32-bit non-NT systems do not use any special setup and on NT-based
238    * systems ProcessUserModeIOPL permanently changes IOPL to 3 for the current
239    * NT process, no revert for current process is possible.
240    */
241 }
242
243 static inline void intel_io_lock(void)
244 {
245 }
246
247 static inline void intel_io_unlock(void)
248 {
249 }