]> mj.ucw.cz Git - pciutils.git/blob - lib/i386-io-windows.h
ca8b3bb5b901263dfbfcad4b746f9de39f6dc852
[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.
9  */
10
11 #include <io.h>
12 #include <windows.h>
13 #include <aclapi.h>
14
15 #ifdef _MSC_VER
16 /* MSVC compiler provides I/O port intrinsics for both 32 and 64-bit modes. */
17 #pragma intrinsic(_outp)
18 #pragma intrinsic(_outpw)
19 #pragma intrinsic(_outpd)
20 #pragma intrinsic(_inp)
21 #pragma intrinsic(_inpw)
22 #pragma intrinsic(_inpd)
23 #elif defined(_WIN64) || defined(_UCRT)
24 /*
25  * For other compilers I/O port intrinsics are available in <intrin.h> header
26  * file either as inline/external functions or macros. Beware that <intrin.h>
27  * names are different than MSVC intrinsics names and glibc function names.
28  * Usage of <intrin.h> is also the prefered way for 64-bit mode or when using
29  * new UCRT library.
30  */
31 #include <intrin.h>
32 #define _outp(x,y) __outbyte(x,y)
33 #define _outpw(x,y) __outword(x,y)
34 #define _outpd(x,y) __outdword(x,y)
35 #define _inp(x) __inbyte(x)
36 #define _inpw(x) __inword(x)
37 #define _inpd(x) __indword(x)
38 #elif defined(__CRTDLL__)
39 /*
40  * Old CRTDLL library does not provide I/O port functions. Even it is the oldest
41  * CRT library it exists also in 64-bit variant. Implement I/O port functions
42  * via inline assembly just for 32-bit mode as 64-bit mode uses above <intrin.h>
43  * header.
44  */
45 static inline int _outp(unsigned short port, int databyte)
46 {
47   asm volatile ("outb %b0, %w1" : : "a" (databyte), "Nd" (port));
48   return databyte;
49 }
50 static inline unsigned short _outpw(unsigned short port, unsigned short dataword)
51 {
52   asm volatile ("outw %w0, %w1" : : "a" (dataword), "Nd" (port));
53   return dataword;
54 }
55 static inline unsigned long _outpd(unsigned short port, unsigned long dataword)
56 {
57   asm volatile ("outl %0, %w1" : : "a" (dataword), "Nd" (port));
58   return dataword;
59 }
60 static inline int _inp(unsigned short port)
61 {
62   unsigned char ret;
63   asm volatile ("inb %w1, %0" : "=a" (ret) : "Nd" (port));
64   return ret;
65 }
66 static inline unsigned short _inpw(unsigned short port)
67 {
68   unsigned short ret;
69   asm volatile ("inw %w1, %0" : "=a" (ret) : "Nd" (port));
70   return ret;
71 }
72 static inline unsigned long _inpd(unsigned short port)
73 {
74   unsigned long ret;
75   asm volatile ("inl %w1, %0" : "=a" (ret) : "Nd" (port));
76   return ret;
77 }
78 #elif !defined(__GNUC__)
79 /*
80  * Old 32-bit MSVCRT (non-UCRT) library provides I/O port functions. Function
81  * prototypes are defined in <conio.h> header file but they are missing in
82  * some MinGW toolchains. So for GCC compiler define them manually.
83  */
84 #include <conio.h>
85 #else
86 int _outp(unsigned short port, int databyte);
87 unsigned short _outpw(unsigned short port, unsigned short dataword);
88 unsigned long _outpd(unsigned short port, unsigned long dataword);
89 int _inp(unsigned short port);
90 unsigned short _inpw(unsigned short port);
91 unsigned long _inpd(unsigned short port);
92 #endif
93
94 #define outb(x,y) _outp(y,x)
95 #define outw(x,y) _outpw(y,x)
96 #define outl(x,y) _outpd(y,x)
97
98 #define inb(x) _inp(x)
99 #define inw(x) _inpw(x)
100 #define inl(x) _inpd(x)
101
102 /*
103  * Define __readeflags() for MSVC and GCC compilers.
104  * MSVC since version 14.00 included in WDK 6001 and since version 15.00
105  * included in VS 2008 provides __readeflags() intrinsic for both 32 and 64-bit
106  * modes. WDK 6001 defines macro __BUILDMACHINE__ to value WinDDK. VS 2008 does
107  * not define this macro at all. MSVC throws error if name of user defined
108  * function conflicts with some MSVC intrinsic.
109  * MSVC supports inline assembly via __asm keyword in 32-bit mode only.
110  * GCC version 4.9.0 and higher provides __builtin_ia32_readeflags_uXX()
111  * builtin for XX-mode.
112  */
113 #if defined(_MSC_VER) && (_MSC_VER >= 1500 || (_MSC_VER >= 1400 && defined(__BUILDMACHINE__)))
114 #pragma intrinsic(__readeflags)
115 #elif defined(__GNUC__) && ((__GNUC__ == 4 && __GNUC_MINOR__ >= 9) || (__GNUC__ > 4))
116 #ifdef __x86_64__
117 #define __readeflags() __builtin_ia32_readeflags_u64()
118 #else
119 #define __readeflags() __builtin_ia32_readeflags_u32()
120 #endif
121 #elif defined(_MSC_VER) && defined(_M_IX86)
122 static inline unsigned int
123 __readeflags(void)
124 {
125   __asm pushfd;
126   __asm pop eax;
127 }
128 #elif defined(__GNUC__)
129 static inline unsigned
130 #ifdef __x86_64__
131 long long
132 #endif
133 int
134 __readeflags(void)
135 {
136   unsigned
137 #ifdef __x86_64__
138   long long
139 #endif
140   int eflags;
141   asm volatile ("pushf\n\tpop %0\n" : "=r" (eflags));
142   return eflags;
143 }
144 #else
145 #error "Unsupported compiler"
146 #endif
147
148 /* Read IOPL of the current process, IOPL is stored in eflag bits [13:12]. */
149 #define read_iopl() ((__readeflags() >> 12) & 0x3)
150
151 /* Unfortunately i586-mingw32msvc toolchain does not provide this constant. */
152 #ifndef PROCESS_QUERY_LIMITED_INFORMATION
153 #define PROCESS_QUERY_LIMITED_INFORMATION 0x1000
154 #endif
155
156 /* Unfortunately some toolchains do not provide this constant. */
157 #ifndef SE_IMPERSONATE_NAME
158 #define SE_IMPERSONATE_NAME TEXT("SeImpersonatePrivilege")
159 #endif
160
161 /*
162  * These psapi functions are available in kernel32.dll library with K32 prefix
163  * on Windows 7 and higher systems. On older Windows systems these functions are
164  * available in psapi.dll libary without K32 prefix. So resolve pointers to
165  * these functions dynamically at runtime from the available system library.
166  * Function GetProcessImageFileNameW() is not available on Windows 2000 and
167  * older systems.
168  */
169 typedef BOOL (WINAPI *EnumProcessesProt)(DWORD *lpidProcess, DWORD cb, DWORD *cbNeeded);
170 typedef DWORD (WINAPI *GetProcessImageFileNameWProt)(HANDLE hProcess, LPWSTR lpImageFileName, DWORD nSize);
171 typedef DWORD (WINAPI *GetModuleFileNameExWProt)(HANDLE hProcess, HMODULE hModule, LPWSTR lpImageFileName, DWORD nSize);
172
173 /*
174  * These aclapi functions are available in advapi.dll library on Windows NT 4.0
175  * and higher systems.
176  */
177 typedef DWORD (WINAPI *GetSecurityInfoProt)(HANDLE handle, SE_OBJECT_TYPE ObjectType, SECURITY_INFORMATION SecurityInfo, PSID *ppsidOwner, PSID *ppsidGroup, PACL *ppDacl, PACL *ppSacl, PSECURITY_DESCRIPTOR *ppSecurityDescriptor);
178 typedef DWORD (WINAPI *SetSecurityInfoProt)(HANDLE handle, SE_OBJECT_TYPE ObjectType, SECURITY_INFORMATION SecurityInfo, PSID psidOwner, PSID psidGroup, PACL pDacl, PACL pSacl);
179 typedef DWORD (WINAPI *SetEntriesInAclProt)(ULONG cCountOfExplicitEntries, PEXPLICIT_ACCESS pListOfExplicitEntries, PACL OldAcl, PACL *NewAcl);
180
181 /*
182  * This errhandlingapi function is available in kernel32.dll library on
183  * Windows 7 and higher systems.
184  */
185 typedef BOOL (WINAPI *SetThreadErrorModeProt)(DWORD dwNewMode, LPDWORD lpOldMode);
186
187 /*
188  * Unfortunately NtSetInformationProcess() function, ProcessUserModeIOPL
189  * constant and all other helpers for its usage are not specified in any
190  * standard WinAPI header file. So define all of required constants and types.
191  * Function NtSetInformationProcess() is available in ntdll.dll library on all
192  * Windows systems but marked as it can be removed in some future version.
193  */
194 #ifndef NTSTATUS
195 #define NTSTATUS LONG
196 #endif
197 #ifndef STATUS_NOT_IMPLEMENTED
198 #define STATUS_NOT_IMPLEMENTED (NTSTATUS)0xC0000002
199 #endif
200 #ifndef STATUS_PRIVILEGE_NOT_HELD
201 #define STATUS_PRIVILEGE_NOT_HELD (NTSTATUS)0xC0000061
202 #endif
203 #ifndef PROCESSINFOCLASS
204 #define PROCESSINFOCLASS DWORD
205 #endif
206 #ifndef ProcessUserModeIOPL
207 #define ProcessUserModeIOPL 16
208 #endif
209 typedef NTSTATUS (NTAPI *NtSetInformationProcessProt)(HANDLE ProcessHandle, PROCESSINFOCLASS ProcessInformationClass, PVOID ProcessInformation, ULONG ProcessInformationLength);
210
211 /*
212  * Check if the current thread has particular privilege in current active access
213  * token. Case when it not possible to determinate it (e.g. current thread does
214  * not have permission to open its own current active access token) is evaluated
215  * as thread does not have that privilege.
216  */
217 static BOOL
218 have_privilege(LUID luid_privilege)
219 {
220   PRIVILEGE_SET priv;
221   HANDLE token;
222   BOOL ret;
223
224   /*
225    * If the current thread does not have active access token then thread
226    * uses primary process access token for all permission checks.
227    */
228   if (!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &token) &&
229       (GetLastError() != ERROR_NO_TOKEN ||
230        !OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token)))
231     return FALSE;
232
233   priv.PrivilegeCount = 1;
234   priv.Control = PRIVILEGE_SET_ALL_NECESSARY;
235   priv.Privilege[0].Luid = luid_privilege;
236   priv.Privilege[0].Attributes = SE_PRIVILEGE_ENABLED;
237
238   if (!PrivilegeCheck(token, &priv, &ret))
239     return FALSE;
240
241   return ret;
242 }
243
244 /*
245  * Enable or disable particular privilege in specified access token.
246  *
247  * Note that it is not possible to disable privilege in access token with
248  * SE_PRIVILEGE_ENABLED_BY_DEFAULT attribute. This function does not check
249  * this case and incorrectly returns no error even when disabling failed.
250  * Rationale for this decision: Simplification of this function as WinAPI
251  * call AdjustTokenPrivileges() does not signal error in this case too.
252  */
253 static BOOL
254 set_privilege(HANDLE token, LUID luid_privilege, BOOL enable)
255 {
256   TOKEN_PRIVILEGES token_privileges;
257
258   token_privileges.PrivilegeCount = 1;
259   token_privileges.Privileges[0].Luid = luid_privilege;
260   token_privileges.Privileges[0].Attributes = enable ? SE_PRIVILEGE_ENABLED : 0;
261
262   /*
263    * WinAPI function AdjustTokenPrivileges() success also when not all
264    * privileges were enabled. It is always required to check for failure
265    * via GetLastError() call. AdjustTokenPrivileges() always sets error
266    * also when it success, as opposite to other WinAPI functions.
267    */
268   if (!AdjustTokenPrivileges(token, FALSE, &token_privileges, sizeof(token_privileges), NULL, NULL) ||
269       GetLastError() != ERROR_SUCCESS)
270     return FALSE;
271
272   return TRUE;
273 }
274
275 /*
276  * Change access token for the current thread to new specified access token.
277  * Previously active access token is stored in old_token variable and can be
278  * used for reverting to this access token. It is set to NULL if the current
279  * thread previously used primary process access token.
280  */
281 static BOOL
282 change_token(HANDLE new_token, HANDLE *old_token)
283 {
284   HANDLE token;
285
286   if (!OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE, TRUE, &token))
287     {
288       if (GetLastError() != ERROR_NO_TOKEN)
289         return FALSE;
290       token = NULL;
291     }
292
293   if (!ImpersonateLoggedOnUser(new_token))
294     {
295       if (token)
296         CloseHandle(token);
297       return FALSE;
298     }
299
300   *old_token = token;
301   return TRUE;
302 }
303
304 /*
305  * Change access token for the current thread to the primary process access
306  * token. This function fails also when the current thread already uses primary
307  * process access token.
308  */
309 static BOOL
310 change_token_to_primary(HANDLE *old_token)
311 {
312   HANDLE token;
313
314   if (!OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE, TRUE, &token))
315     return FALSE;
316
317   RevertToSelf();
318
319   *old_token = token;
320   return TRUE;
321 }
322
323 /*
324  * Revert to the specified access token for the current thread. When access
325  * token is specified as NULL then revert to the primary process access token.
326  * Use to revert after change_token() or change_token_to_primary() call.
327  */
328 static VOID
329 revert_to_token(HANDLE token)
330 {
331   /*
332    * If SetThreadToken() call fails then there is no option to revert to
333    * the specified previous thread access token. So in this case revert to
334    * the primary process access token.
335    */
336   if (!token || !SetThreadToken(NULL, token))
337     RevertToSelf();
338   if (token)
339     CloseHandle(token);
340 }
341
342 /*
343  * Enable particular privilege for the current thread. And set method how to
344  * revert this privilege (if to revert whole token or only privilege).
345  */
346 static BOOL
347 enable_privilege(LUID luid_privilege, HANDLE *revert_token, BOOL *revert_only_privilege)
348 {
349   HANDLE thread_token;
350   HANDLE new_token;
351
352   if (OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES, TRUE, &thread_token))
353     {
354       if (set_privilege(thread_token, luid_privilege, TRUE))
355         {
356           /*
357            * Indicate that correct revert method is just to
358            * disable privilege in access token.
359            */
360           if (revert_token && revert_only_privilege)
361             {
362               *revert_token = thread_token;
363               *revert_only_privilege = TRUE;
364             }
365           else
366             {
367               CloseHandle(thread_token);
368             }
369           return TRUE;
370         }
371       CloseHandle(thread_token);
372       /*
373        * If enabling privilege failed then try to enable it via
374        * primary process access token.
375        */
376     }
377
378   /*
379    * If the current thread has already active thread access token then
380    * open it with just impersonate right as it would be used only for
381    * future revert.
382    */
383   if (revert_token && revert_only_privilege)
384     {
385       if (!OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE, TRUE, &thread_token))
386         {
387           if (GetLastError() != ERROR_NO_TOKEN)
388             return FALSE;
389           thread_token = NULL;
390         }
391
392       /*
393        * If current thread has no access token (and uses primary
394        * process access token) or it does not have permission to
395        * adjust privileges or it does not have specified privilege
396        * then create a copy of the primary process access token,
397        * assign it for the current thread (= impersonate self)
398        * and then try adjusting privilege again.
399        */
400       if (!ImpersonateSelf(SecurityImpersonation))
401         {
402           if (thread_token)
403             CloseHandle(thread_token);
404           return FALSE;
405         }
406     }
407
408   if (!OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES, TRUE, &new_token))
409     {
410       /* thread_token is set only when we were asked for revert method. */
411       if (revert_token && revert_only_privilege)
412         revert_to_token(thread_token);
413       return FALSE;
414     }
415
416   if (!set_privilege(new_token, luid_privilege, TRUE))
417     {
418       CloseHandle(new_token);
419       /* thread_token is set only when we were asked for revert method. */
420       if (revert_token && revert_only_privilege)
421         revert_to_token(thread_token);
422       return FALSE;
423     }
424
425   /*
426    * Indicate that correct revert method is to change to the previous
427    * access token. Either to the primary process access token or to the
428    * previous thread access token.
429    */
430   if (revert_token && revert_only_privilege)
431     {
432       *revert_token = thread_token;
433       *revert_only_privilege = FALSE;
434     }
435   return TRUE;
436 }
437
438 /*
439  * Revert particular privilege for the current thread was previously enabled by
440  * enable_privilege() call. Either disable privilege in specified access token
441  * or revert to previous access token.
442  */
443 static VOID
444 revert_privilege(LUID luid_privilege, HANDLE revert_token, BOOL revert_only_privilege)
445 {
446   if (revert_only_privilege)
447     {
448       set_privilege(revert_token, luid_privilege, FALSE);
449       CloseHandle(revert_token);
450     }
451   else
452     {
453       revert_to_token(revert_token);
454     }
455 }
456
457 /*
458  * Return owner of the access token used by the current thread. Buffer for
459  * returned owner needs to be released by LocalFree() call.
460  */
461 static TOKEN_OWNER *
462 get_current_token_owner(VOID)
463 {
464   HANDLE token;
465   DWORD length;
466   TOKEN_OWNER *owner;
467
468   /*
469    * If the current thread does not have active access token then thread
470    * uses primary process access token for all permission checks.
471    */
472   if (!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &token) &&
473       (GetLastError() != ERROR_NO_TOKEN ||
474        !OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token)))
475     return NULL;
476
477   if (!GetTokenInformation(token, TokenOwner, NULL, 0, &length) &&
478       GetLastError() != ERROR_INSUFFICIENT_BUFFER)
479     {
480       CloseHandle(token);
481       return NULL;
482     }
483
484 retry:
485   owner = (TOKEN_OWNER *)LocalAlloc(LPTR, length);
486   if (!owner)
487     {
488       CloseHandle(token);
489       return NULL;
490     }
491
492   if (!GetTokenInformation(token, TokenOwner, owner, length, &length))
493     {
494       /*
495        * Length of token owner (SID) buffer between two get calls may
496        * changes (e.g. by another thread of process), so retry.
497        */
498       if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
499         {
500           LocalFree(owner);
501           goto retry;
502         }
503       LocalFree(owner);
504       CloseHandle(token);
505       return NULL;
506     }
507
508   CloseHandle(token);
509   return owner;
510 }
511
512 /*
513  * Grant particular permissions in the primary access token of the specified
514  * process for the owner of current thread token and set old DACL of the
515  * process access token for reverting permissions. Security descriptor is
516  * just memory buffer for old DACL.
517  */
518 static BOOL
519 grant_process_token_dacl_permissions(HANDLE process, DWORD permissions, HANDLE *token, PACL *old_dacl, PSECURITY_DESCRIPTOR *security_descriptor)
520 {
521   GetSecurityInfoProt MyGetSecurityInfo;
522   SetSecurityInfoProt MySetSecurityInfo;
523   SetEntriesInAclProt MySetEntriesInAcl;
524   EXPLICIT_ACCESS explicit_access;
525   TOKEN_OWNER *owner;
526   HMODULE advapi32;
527   PACL new_dacl;
528
529   /*
530    * This source file already uses advapi32.dll library, so it is
531    * linked to executable and automatically loaded when starting
532    * current running process.
533    */
534   advapi32 = GetModuleHandle(TEXT("advapi32.dll"));
535   if (!advapi32)
536     return FALSE;
537
538   /*
539    * It does not matter if SetEntriesInAclA() or SetEntriesInAclW() is
540    * called as no string is passed to SetEntriesInAcl function.
541    */
542   MyGetSecurityInfo = (GetSecurityInfoProt)(LPVOID)GetProcAddress(advapi32, "GetSecurityInfo");
543   MySetSecurityInfo = (SetSecurityInfoProt)(LPVOID)GetProcAddress(advapi32, "SetSecurityInfo");
544   MySetEntriesInAcl = (SetEntriesInAclProt)(LPVOID)GetProcAddress(advapi32, "SetEntriesInAclA");
545   if (!MyGetSecurityInfo || !MySetSecurityInfo || !MySetEntriesInAcl)
546     return FALSE;
547
548   owner = get_current_token_owner();
549   if (!owner)
550     return FALSE;
551
552   /*
553    * READ_CONTROL is required for GetSecurityInfo(DACL_SECURITY_INFORMATION)
554    * and WRITE_DAC is required for SetSecurityInfo(DACL_SECURITY_INFORMATION).
555    */
556   if (!OpenProcessToken(process, READ_CONTROL | WRITE_DAC, token))
557     {
558       LocalFree(owner);
559       return FALSE;
560     }
561
562   if (MyGetSecurityInfo(*token, SE_KERNEL_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL, old_dacl, NULL, security_descriptor) != ERROR_SUCCESS)
563     {
564       LocalFree(owner);
565       CloseHandle(*token);
566       return FALSE;
567     }
568
569   /*
570    * Set new explicit access for the owner of the current thread access
571    * token with non-inherited granting access to specified permissions.
572    */
573   explicit_access.grfAccessPermissions = permissions;
574   explicit_access.grfAccessMode = GRANT_ACCESS;
575   explicit_access.grfInheritance = NO_PROPAGATE_INHERIT_ACE;
576   explicit_access.Trustee.pMultipleTrustee = NULL;
577   explicit_access.Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
578   explicit_access.Trustee.TrusteeForm = TRUSTEE_IS_SID;
579   explicit_access.Trustee.TrusteeType = TRUSTEE_IS_USER;
580   /*
581    * Unfortunately i586-mingw32msvc toolchain does not have pSid pointer
582    * member in Trustee union. So assign owner SID to ptstrName pointer
583    * member which aliases with pSid pointer member in the same union.
584    */
585   explicit_access.Trustee.ptstrName = (PVOID)owner->Owner;
586
587   if (MySetEntriesInAcl(1, &explicit_access, *old_dacl, &new_dacl) != ERROR_SUCCESS)
588     {
589       LocalFree(*security_descriptor);
590       LocalFree(owner);
591       CloseHandle(*token);
592       return FALSE;
593     }
594
595   if (MySetSecurityInfo(*token, SE_KERNEL_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL, new_dacl, NULL) != ERROR_SUCCESS)
596     {
597       LocalFree(*security_descriptor);
598       LocalFree(owner);
599       CloseHandle(*token);
600       return FALSE;
601     }
602
603   LocalFree(owner);
604   return TRUE;
605 }
606
607 /*
608  * Revert particular granted permissions in specified access token done by
609  * grant_process_token_dacl_permissions() call.
610  */
611 static VOID
612 revert_token_dacl_permissions(HANDLE token, PACL old_dacl, PSECURITY_DESCRIPTOR security_descriptor)
613 {
614   SetSecurityInfoProt MySetSecurityInfo;
615   HMODULE advapi32;
616
617   /*
618    * This source file already uses advapi32.dll library, so it is
619    * linked to executable and automatically loaded when starting
620    * current running process.
621    */
622   advapi32 = GetModuleHandle(TEXT("advapi32.dll"));
623   if (advapi32)
624     {
625       MySetSecurityInfo = (SetSecurityInfoProt)(LPVOID)GetProcAddress(advapi32, "SetSecurityInfo");
626       MySetSecurityInfo(token, SE_KERNEL_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL, old_dacl, NULL);
627     }
628
629   LocalFree(security_descriptor);
630   CloseHandle(token);
631 }
632
633 /*
634  * Change error mode of the current thread. If it is not possible then change
635  * error mode of the whole process. Always returns previous error mode.
636  */
637 static UINT
638 change_error_mode(UINT new_mode)
639 {
640   SetThreadErrorModeProt MySetThreadErrorMode = NULL;
641   HMODULE kernel32;
642   DWORD old_mode;
643
644   /*
645    * Function SetThreadErrorMode() was introduced in Windows 7, so use
646    * GetProcAddress() for compatibility with older systems.
647    */
648   kernel32 = GetModuleHandle(TEXT("kernel32.dll"));
649   if (kernel32)
650     MySetThreadErrorMode = (SetThreadErrorModeProt)(LPVOID)GetProcAddress(kernel32, "SetThreadErrorMode");
651
652   if (MySetThreadErrorMode &&
653       MySetThreadErrorMode(new_mode, &old_mode))
654     return old_mode;
655
656   /*
657    * Fallback to function SetErrorMode() which modifies error mode of the
658    * whole process and returns old mode.
659    */
660   return SetErrorMode(new_mode);
661 }
662
663 /*
664  * Open process handle specified by the process id with the query right and
665  * optionally also with vm read right.
666  */
667 static HANDLE
668 open_process_for_query(DWORD pid, BOOL with_vm_read)
669 {
670   BOOL revert_only_privilege;
671   LUID luid_debug_privilege;
672   OSVERSIONINFO version;
673   DWORD process_right;
674   HANDLE revert_token;
675   HANDLE process;
676
677   /*
678    * Some processes on Windows Vista and higher systems can be opened only
679    * with PROCESS_QUERY_LIMITED_INFORMATION right. This right is enough
680    * for accessing primary process token. But this right is not supported
681    * on older pre-Vista systems. When the current thread on these older
682    * systems does not have Debug privilege then OpenProcess() fails with
683    * ERROR_ACCESS_DENIED. If the current thread has Debug privilege then
684    * OpenProcess() success and returns handle to requested process.
685    * Problem is that this handle does not have PROCESS_QUERY_INFORMATION
686    * right and so cannot be used for accessing primary process token
687    * on those older systems. Moreover it has zero rights and therefore
688    * such handle is fully useless. So never try to use open process with
689    * PROCESS_QUERY_LIMITED_INFORMATION right on older systems than
690    * Windows Vista (NT 6.0).
691    */
692   version.dwOSVersionInfoSize = sizeof(version);
693   if (GetVersionEx(&version) &&
694       version.dwPlatformId == VER_PLATFORM_WIN32_NT &&
695       version.dwMajorVersion >= 6)
696     process_right = PROCESS_QUERY_LIMITED_INFORMATION;
697   else
698     process_right = PROCESS_QUERY_INFORMATION;
699
700   if (with_vm_read)
701     process_right |= PROCESS_VM_READ;
702
703   process = OpenProcess(process_right, FALSE, pid);
704   if (process)
705     return process;
706
707   /*
708    * It is possible to open only processes to which owner of the current
709    * thread access token has permissions. For opening other processing it
710    * is required to have Debug privilege enabled. By default local
711    * administrators have this privilege, but it is disabled. So try to
712    * enable it and then try to open process again.
713    */
714
715   if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid_debug_privilege))
716     return NULL;
717
718   if (!enable_privilege(luid_debug_privilege, &revert_token, &revert_only_privilege))
719     return NULL;
720
721   process = OpenProcess(process_right, FALSE, pid);
722
723   revert_privilege(luid_debug_privilege, revert_token, revert_only_privilege);
724
725   return process;
726 }
727
728 /*
729  * Check if process image path name (wide string) matches exe file name
730  * (7-bit ASCII string). Do case-insensitive string comparison. Process
731  * image path name can be in any namespace format (DOS, Win32, UNC, ...).
732  */
733 static BOOL
734 check_process_name(LPCWSTR path, DWORD path_length, LPCSTR exe_file)
735 {
736   DWORD exe_file_length;
737   WCHAR c1;
738   UCHAR c2;
739   DWORD i;
740
741   exe_file_length = 0;
742   while (exe_file[exe_file_length] != '\0')
743     exe_file_length++;
744
745   /* Path must have backslash before exe file name. */
746   if (exe_file_length >= path_length ||
747       path[path_length-exe_file_length-1] != L'\\')
748     return FALSE;
749
750   for (i = 0; i < exe_file_length; i++)
751     {
752       c1 = path[path_length-exe_file_length+i];
753       c2 = exe_file[i];
754       /*
755        * Input string for comparison is 7-bit ASCII and file name part
756        * of path must not contain backslash as it is path separator.
757        */
758       if (c1 >= 0x80 || c2 >= 0x80 || c1 == L'\\')
759         return FALSE;
760       if (c1 >= L'a' && c1 <= L'z')
761         c1 -= L'a' - L'A';
762       if (c2 >= 'a' && c2 <= 'z')
763         c2 -= 'a' - 'A';
764       if (c1 != c2)
765         return FALSE;
766     }
767
768   return TRUE;
769 }
770
771 /* Open process handle with the query right specified by process exe file. */
772 static HANDLE
773 find_and_open_process_for_query(LPCSTR exe_file)
774 {
775   GetProcessImageFileNameWProt MyGetProcessImageFileNameW;
776   GetModuleFileNameExWProt MyGetModuleFileNameExW;
777   EnumProcessesProt MyEnumProcesses;
778   HMODULE kernel32, psapi;
779   UINT prev_error_mode;
780   WCHAR path[MAX_PATH];
781   DWORD size, length;
782   DWORD *processes;
783   HANDLE process;
784   DWORD count;
785   DWORD i;
786
787   psapi = NULL;
788   kernel32 = GetModuleHandle(TEXT("kernel32.dll"));
789   if (!kernel32)
790     return NULL;
791
792   /*
793    * On Windows 7 and higher systems these functions are available in
794    * kernel32.dll library with K32 prefix.
795    */
796   MyGetModuleFileNameExW = NULL;
797   MyGetProcessImageFileNameW = (GetProcessImageFileNameWProt)(LPVOID)GetProcAddress(kernel32, "K32GetProcessImageFileNameW");
798   MyEnumProcesses = (EnumProcessesProt)(LPVOID)GetProcAddress(kernel32, "K32EnumProcesses");
799   if (!MyGetProcessImageFileNameW || !MyEnumProcesses)
800     {
801       /*
802        * On older NT-based systems these functions are available in
803        * psapi.dll library without K32 prefix.
804        */
805       prev_error_mode = change_error_mode(SEM_FAILCRITICALERRORS);
806       psapi = LoadLibrary(TEXT("psapi.dll"));
807       change_error_mode(prev_error_mode);
808
809       if (!psapi)
810         return NULL;
811
812       /*
813        * Function GetProcessImageFileNameW() is available in
814        * Windows XP and higher systems. On older versions is
815        * available function GetModuleFileNameExW().
816        */
817       MyGetProcessImageFileNameW = (GetProcessImageFileNameWProt)(LPVOID)GetProcAddress(psapi, "GetProcessImageFileNameW");
818       MyGetModuleFileNameExW = (GetModuleFileNameExWProt)(LPVOID)GetProcAddress(psapi, "GetModuleFileNameExW");
819       MyEnumProcesses = (EnumProcessesProt)(LPVOID)GetProcAddress(psapi, "EnumProcesses");
820       if ((!MyGetProcessImageFileNameW && !MyGetModuleFileNameExW) || !MyEnumProcesses)
821         {
822           FreeLibrary(psapi);
823           return NULL;
824         }
825     }
826
827   /* Make initial buffer size for 1024 processes. */
828   size = 1024 * sizeof(*processes);
829
830 retry:
831   processes = (DWORD *)LocalAlloc(LPTR, size);
832   if (!processes)
833     {
834       if (psapi)
835         FreeLibrary(psapi);
836       return NULL;
837     }
838
839   if (!MyEnumProcesses(processes, size, &length))
840     {
841       LocalFree(processes);
842       if (psapi)
843         FreeLibrary(psapi);
844       return NULL;
845     }
846   else if (size == length)
847     {
848       /*
849        * There is no indication given when the buffer is too small to
850        * store all process identifiers. Therefore if returned length
851        * is same as buffer size there can be more processes. Call
852        * again with larger buffer.
853        */
854       LocalFree(processes);
855       size *= 2;
856       goto retry;
857     }
858
859   process = NULL;
860   count = length / sizeof(*processes);
861
862   for (i = 0; i < count; i++)
863     {
864       /* Skip System Idle Process. */
865       if (processes[i] == 0)
866         continue;
867
868       /*
869        * Function GetModuleFileNameExW() requires additional
870        * PROCESS_VM_READ right as opposite to function
871        * GetProcessImageFileNameW() which does not need it.
872        */
873       process = open_process_for_query(processes[i], MyGetProcessImageFileNameW ? FALSE : TRUE);
874       if (!process)
875         continue;
876
877       if (MyGetProcessImageFileNameW)
878         length = MyGetProcessImageFileNameW(process, path, sizeof(path)/sizeof(*path));
879       else
880         length = MyGetModuleFileNameExW(process, NULL, path, sizeof(path)/sizeof(*path));
881       if (length && check_process_name(path, length, exe_file))
882         break;
883
884       CloseHandle(process);
885       process = NULL;
886     }
887
888   LocalFree(processes);
889
890   if (psapi)
891     FreeLibrary(psapi);
892
893   return process;
894 }
895
896 /*
897  * Try to open primary access token of the particular process with specified
898  * rights. Before opening access token try to adjust DACL permissions of the
899  * primary process access token, so following open does not fail on error
900  * related to no open permissions. Revert DACL permissions after open attempt.
901  * As following steps are not atomic, try to execute them more times in case
902  * of possible race conditions caused by other threads or processes.
903  */
904 static HANDLE
905 try_grant_permissions_and_open_process_token(HANDLE process, DWORD rights)
906 {
907   PSECURITY_DESCRIPTOR security_descriptor;
908   HANDLE grant_token;
909   PACL old_dacl;
910   HANDLE token;
911   DWORD retry;
912   DWORD error;
913
914   /*
915    * This code is not atomic. Between grant and open calls can other
916    * thread or process change or revert permissions. So try to execute
917    * it more times.
918    */
919   for (retry = 0; retry < 10; retry++)
920     {
921       if (!grant_process_token_dacl_permissions(process, rights, &grant_token, &old_dacl, &security_descriptor))
922         return NULL;
923       if (!OpenProcessToken(process, rights, &token))
924         {
925           token = NULL;
926           error = GetLastError();
927         }
928       revert_token_dacl_permissions(grant_token, old_dacl, security_descriptor);
929       if (token)
930         return token;
931       else if (error != ERROR_ACCESS_DENIED)
932         return NULL;
933     }
934
935   return NULL;
936 }
937
938 /*
939  * Open primary access token of particular process handle with specified rights.
940  * If permissions for specified rights are missing then try to grant them.
941  */
942 static HANDLE
943 open_process_token_with_rights(HANDLE process, DWORD rights)
944 {
945   HANDLE old_token;
946   HANDLE token;
947
948   /* First try to open primary access token of process handle directly. */
949   if (OpenProcessToken(process, rights, &token))
950     return token;
951
952   /*
953    * If opening failed then it means that owner of the current thread
954    * access token does not have permission for it. Try it again with
955    * primary process access token.
956    */
957   if (change_token_to_primary(&old_token))
958     {
959       if (!OpenProcessToken(process, rights, &token))
960         token = NULL;
961       revert_to_token(old_token);
962       if (token)
963         return token;
964     }
965
966   /*
967    * If opening is still failing then try to grant specified permissions
968    * for the current thread and try to open it again.
969    */
970   token = try_grant_permissions_and_open_process_token(process, rights);
971   if (token)
972     return token;
973
974   /*
975    * And if it is still failing then try it again with granting
976    * permissions for the primary process token of the current process.
977    */
978   if (change_token_to_primary(&old_token))
979     {
980       token = try_grant_permissions_and_open_process_token(process, rights);
981       revert_to_token(old_token);
982       if (token)
983         return token;
984     }
985
986   /*
987    * TODO: Sorry, no other option for now...
988    * It could be possible to use Take Ownership Name privilege to
989    * temporary change token owner of specified process to the owner of
990    * the current thread token, grant permissions for current thread in
991    * that process token, change ownership back to original one, open
992    * that process token and revert granted permissions. But this is
993    * not implemented yet.
994    */
995   return NULL;
996 }
997
998 /*
999  * Set x86 I/O Privilege Level to 3 for the whole current NT process. Do it via
1000  * NtSetInformationProcess() call with ProcessUserModeIOPL information class,
1001  * which is supported by 32-bit Windows NT kernel versions and requires Tcb
1002  * privilege.
1003  */
1004 static BOOL
1005 SetProcessUserModeIOPL(VOID)
1006 {
1007   NtSetInformationProcessProt MyNtSetInformationProcess;
1008
1009   LUID luid_tcb_privilege;
1010   LUID luid_impersonate_privilege;
1011
1012   HANDLE revert_token_tcb_privilege;
1013   BOOL revert_only_tcb_privilege;
1014
1015   HANDLE revert_token_impersonate_privilege;
1016   BOOL revert_only_impersonate_privilege;
1017
1018   BOOL impersonate_privilege_enabled;
1019
1020   BOOL revert_to_old_token;
1021   HANDLE old_token;
1022
1023   HANDLE lsass_process;
1024   HANDLE lsass_token;
1025
1026   UINT prev_error_mode;
1027   NTSTATUS nt_status;
1028   HMODULE ntdll;
1029   BOOL ret;
1030
1031   impersonate_privilege_enabled = FALSE;
1032   revert_to_old_token = FALSE;
1033   lsass_token = NULL;
1034
1035   /* Fast path when ProcessUserModeIOPL was already called. */
1036   if (read_iopl() == 3)
1037     return TRUE;
1038
1039   /*
1040    * Load ntdll.dll library with disabled critical-error-handler message box.
1041    * It means that NT kernel does not show unwanted GUI message box to user
1042    * when LoadLibrary() function fails.
1043    */
1044   prev_error_mode = change_error_mode(SEM_FAILCRITICALERRORS);
1045   ntdll = LoadLibrary(TEXT("ntdll.dll"));
1046   change_error_mode(prev_error_mode);
1047   if (!ntdll)
1048     goto err_not_implemented;
1049
1050   /* Retrieve pointer to NtSetInformationProcess() function. */
1051   MyNtSetInformationProcess = (NtSetInformationProcessProt)(LPVOID)GetProcAddress(ntdll, "NtSetInformationProcess");
1052   if (!MyNtSetInformationProcess)
1053     goto err_not_implemented;
1054
1055   /*
1056    * ProcessUserModeIOPL is syscall for NT kernel to change x86 IOPL
1057    * of the current running process to 3.
1058    *
1059    * Process handle argument for ProcessUserModeIOPL is ignored and
1060    * IOPL is always changed for the current running process. So pass
1061    * GetCurrentProcess() handle for documentation purpose. Process
1062    * information buffer and length are unused for ProcessUserModeIOPL.
1063    *
1064    * ProcessUserModeIOPL may success (return value >= 0) or may fail
1065    * because it is not implemented or because of missing privilege.
1066    * Other errors are not defined, so handle them as unknown.
1067    */
1068   nt_status = MyNtSetInformationProcess(GetCurrentProcess(), ProcessUserModeIOPL, NULL, 0);
1069   if (nt_status >= 0)
1070     goto verify;
1071   else if (nt_status == STATUS_NOT_IMPLEMENTED)
1072     goto err_not_implemented;
1073   else if (nt_status != STATUS_PRIVILEGE_NOT_HELD)
1074     goto err_unknown;
1075
1076   /*
1077    * If ProcessUserModeIOPL call failed with STATUS_PRIVILEGE_NOT_HELD
1078    * error then it means that the current thread token does not have
1079    * Tcb privilege enabled. Try to enable it.
1080    */
1081
1082   if (!LookupPrivilegeValue(NULL, SE_TCB_NAME, &luid_tcb_privilege))
1083     goto err_not_implemented;
1084
1085   /*
1086    * If the current thread has already Tcb privilege enabled then there
1087    * is some additional unhanded restriction.
1088    */
1089   if (have_privilege(luid_tcb_privilege))
1090     goto err_privilege_not_held;
1091
1092   /* Try to enable Tcb privilege and try ProcessUserModeIOPL call again. */
1093   if (enable_privilege(luid_tcb_privilege, &revert_token_tcb_privilege, &revert_only_tcb_privilege))
1094     {
1095       nt_status = MyNtSetInformationProcess(GetCurrentProcess(), ProcessUserModeIOPL, NULL, 0);
1096       revert_privilege(luid_tcb_privilege, revert_token_tcb_privilege, revert_only_tcb_privilege);
1097       if (nt_status >= 0)
1098         goto verify;
1099       else if (nt_status == STATUS_NOT_IMPLEMENTED)
1100         goto err_not_implemented;
1101       else if (nt_status == STATUS_PRIVILEGE_NOT_HELD)
1102         goto err_privilege_not_held;
1103       else
1104         goto err_unknown;
1105     }
1106
1107   /*
1108    * If enabling of Tcb privilege failed then it means that current thread
1109    * does not this privilege. But current process may have it. So try it
1110    * again with primary process access token.
1111    */
1112
1113   /*
1114    * If system supports Impersonate privilege (Windows 2000 SP4 or higher) then
1115    * all future actions in this function require this Impersonate privilege.
1116    * So try to enable it in case it is currently disabled.
1117    */
1118   if (LookupPrivilegeValue(NULL, SE_IMPERSONATE_NAME, &luid_impersonate_privilege) &&
1119       !have_privilege(luid_impersonate_privilege))
1120     {
1121       /*
1122        * If current thread does not have Impersonate privilege enabled
1123        * then first try to enable it just for the current thread. If
1124        * it is not possible to enable it just for the current thread
1125        * then try it to enable globally for whole process (which
1126        * affects all process threads). Both actions will be reverted
1127        * at the end of this function.
1128        */
1129       if (enable_privilege(luid_impersonate_privilege, &revert_token_impersonate_privilege, &revert_only_impersonate_privilege))
1130         {
1131           impersonate_privilege_enabled = TRUE;
1132         }
1133       else if (enable_privilege(luid_impersonate_privilege, NULL, NULL))
1134         {
1135           impersonate_privilege_enabled = TRUE;
1136           revert_token_impersonate_privilege = NULL;
1137           revert_only_impersonate_privilege = TRUE;
1138         }
1139       else
1140         {
1141           goto err_privilege_not_held;
1142         }
1143
1144       /*
1145        * Now when Impersonate privilege is enabled, try to enable Tcb
1146        * privilege again. Enabling other privileges for the current
1147        * thread requires Impersonate privilege, so enabling Tcb again
1148        * could now pass.
1149        */
1150       if (enable_privilege(luid_tcb_privilege, &revert_token_tcb_privilege, &revert_only_tcb_privilege))
1151         {
1152           nt_status = MyNtSetInformationProcess(GetCurrentProcess(), ProcessUserModeIOPL, NULL, 0);
1153           revert_privilege(luid_tcb_privilege, revert_token_tcb_privilege, revert_only_tcb_privilege);
1154           if (nt_status >= 0)
1155             goto verify;
1156           else if (nt_status == STATUS_NOT_IMPLEMENTED)
1157             goto err_not_implemented;
1158           else if (nt_status == STATUS_PRIVILEGE_NOT_HELD)
1159             goto err_privilege_not_held;
1160           else
1161             goto err_unknown;
1162         }
1163     }
1164
1165   /*
1166    * If enabling Tcb privilege failed then it means that the current
1167    * thread access token does not have this privilege or does not
1168    * have permission to adjust privileges.
1169    *
1170    * Try to use more privileged token from Local Security Authority
1171    * Subsystem Service process (lsass.exe) which has Tcb privilege.
1172    * Retrieving this more privileged token is possible for local
1173    * administrators (unless it was disabled by local administrators).
1174    */
1175
1176   lsass_process = find_and_open_process_for_query("lsass.exe");
1177   if (!lsass_process)
1178     goto err_privilege_not_held;
1179
1180   /*
1181    * Open primary lsass.exe process access token with query and duplicate
1182    * rights. Just these two rights are required for impersonating other
1183    * primary process token (impersonate right is really not required!).
1184    */
1185   lsass_token = open_process_token_with_rights(lsass_process, TOKEN_QUERY | TOKEN_DUPLICATE);
1186
1187   CloseHandle(lsass_process);
1188
1189   if (!lsass_token)
1190     goto err_privilege_not_held;
1191
1192   /*
1193    * After successful open of the primary lsass.exe process access token,
1194    * assign its copy for the current thread.
1195    */
1196   if (!change_token(lsass_token, &old_token))
1197     goto err_privilege_not_held;
1198
1199   revert_to_old_token = TRUE;
1200
1201   nt_status = MyNtSetInformationProcess(GetCurrentProcess(), ProcessUserModeIOPL, NULL, 0);
1202   if (nt_status == STATUS_PRIVILEGE_NOT_HELD)
1203     {
1204       /*
1205        * Now current thread is not using primary process token anymore
1206        * but is using custom access token. There is no need to revert
1207        * enabled Tcb privilege as the whole custom access token would
1208        * be reverted. So there is no need to setup revert method for
1209        * enabling privilege.
1210        */
1211       if (have_privilege(luid_tcb_privilege) ||
1212           !enable_privilege(luid_tcb_privilege, NULL, NULL))
1213         goto err_privilege_not_held;
1214       nt_status = MyNtSetInformationProcess(GetCurrentProcess(), ProcessUserModeIOPL, NULL, 0);
1215     }
1216   if (nt_status >= 0)
1217     goto verify;
1218   else if (nt_status == STATUS_NOT_IMPLEMENTED)
1219     goto err_not_implemented;
1220   else if (nt_status == STATUS_PRIVILEGE_NOT_HELD)
1221     goto err_privilege_not_held;
1222   else
1223     goto err_unknown;
1224
1225 verify:
1226   /*
1227    * Some Windows NT kernel versions (e.g. Windows 2003 x64) do not
1228    * implement ProcessUserModeIOPL syscall at all but incorrectly
1229    * returns success when it is called by user process. So always
1230    * after this call verify that IOPL is set to 3.
1231    */
1232   if (read_iopl() != 3)
1233     goto err_not_implemented;
1234   ret = TRUE;
1235   goto ret;
1236
1237 err_not_implemented:
1238   SetLastError(ERROR_INVALID_FUNCTION);
1239   ret = FALSE;
1240   goto ret;
1241
1242 err_privilege_not_held:
1243   SetLastError(ERROR_PRIVILEGE_NOT_HELD);
1244   ret = FALSE;
1245   goto ret;
1246
1247 err_unknown:
1248   SetLastError(ERROR_GEN_FAILURE);
1249   ret = FALSE;
1250   goto ret;
1251
1252 ret:
1253   if (revert_to_old_token)
1254     revert_to_token(old_token);
1255
1256   if (impersonate_privilege_enabled)
1257     revert_privilege(luid_impersonate_privilege, revert_token_impersonate_privilege, revert_only_impersonate_privilege);
1258
1259   if (lsass_token)
1260     CloseHandle(lsass_token);
1261
1262   if (ntdll)
1263     FreeLibrary(ntdll);
1264
1265   return ret;
1266 }
1267
1268 static int
1269 intel_setup_io(struct pci_access *a)
1270 {
1271 #ifndef _WIN64
1272   /* 16/32-bit non-NT systems allow applications to access PCI I/O ports without any special setup. */
1273   OSVERSIONINFOA version;
1274   version.dwOSVersionInfoSize = sizeof(version);
1275   if (GetVersionExA(&version) && version.dwPlatformId < VER_PLATFORM_WIN32_NT)
1276     {
1277       a->debug("Detected 16/32-bit non-NT system, skipping NT setup...");
1278       return 1;
1279     }
1280 #endif
1281
1282   /* On NT-based systems issue ProcessUserModeIOPL syscall which changes IOPL to 3. */
1283   if (!SetProcessUserModeIOPL())
1284     {
1285       a->warning("NT ProcessUserModeIOPL call failed with error: %lu.", (unsigned long int)GetLastError());
1286       return 0;
1287     }
1288
1289   a->debug("NT ProcessUserModeIOPL call succeeded...");
1290   return 1;
1291 }
1292
1293 static inline int
1294 intel_cleanup_io(struct pci_access *a UNUSED)
1295 {
1296   /*
1297    * 16/32-bit non-NT systems do not use any special setup and on NT-based
1298    * systems ProcessUserModeIOPL permanently changes IOPL to 3 for the current
1299    * NT process, no revert for current process is possible.
1300    */
1301   return 1;
1302 }
1303
1304 static inline void intel_io_lock(void)
1305 {
1306 }
1307
1308 static inline void intel_io_unlock(void)
1309 {
1310 }