]> mj.ucw.cz Git - pciutils.git/blob - lib/win32-sysdbg.c
windows: Deduplicate code and move helper functions to new file win32-helpers.c
[pciutils.git] / lib / win32-sysdbg.c
1 /*
2  *      The PCI Library -- PCI config space access using NT SysDbg interface
3  *
4  *      Copyright (c) 2022 Pali Rohár <pali@kernel.org>
5  *
6  *      Can be freely distributed and used under the terms of the GNU GPL v2+.
7  *
8  *      SPDX-License-Identifier: GPL-2.0-or-later
9  */
10
11 #include <windows.h>
12
13 #include "internal.h"
14 #include "i386-io-windows.h"
15 #include "win32-helpers.h"
16
17 #ifndef NTSTATUS
18 #define NTSTATUS LONG
19 #endif
20 #ifndef STATUS_UNSUCCESSFUL
21 #define STATUS_UNSUCCESSFUL (NTSTATUS)0xC0000001
22 #endif
23 #ifndef STATUS_NOT_IMPLEMENTED
24 #define STATUS_NOT_IMPLEMENTED (NTSTATUS)0xC0000002
25 #endif
26 #ifndef STATUS_INVALID_INFO_CLASS
27 #define STATUS_INVALID_INFO_CLASS (NTSTATUS)0xC0000003
28 #endif
29 #ifndef STATUS_ACCESS_DENIED
30 #define STATUS_ACCESS_DENIED (NTSTATUS)0xC0000022
31 #endif
32 #ifndef STATUS_DEBUGGER_INACTIVE
33 #define STATUS_DEBUGGER_INACTIVE (NTSTATUS)0xC0000354
34 #endif
35
36 #ifndef BUS_DATA_TYPE
37 #define BUS_DATA_TYPE LONG
38 #endif
39 #ifndef PCIConfiguration
40 #define PCIConfiguration (BUS_DATA_TYPE)4
41 #endif
42
43 #ifndef SYSDBG_COMMAND
44 #define SYSDBG_COMMAND ULONG
45 #endif
46 #ifndef SysDbgReadBusData
47 #define SysDbgReadBusData (SYSDBG_COMMAND)18
48 #endif
49 #ifndef SysDbgWriteBusData
50 #define SysDbgWriteBusData (SYSDBG_COMMAND)19
51 #endif
52
53 #ifndef SYSDBG_BUS_DATA
54 typedef struct _SYSDBG_BUS_DATA {
55   ULONG Address;
56   PVOID Buffer;
57   ULONG Request;
58   BUS_DATA_TYPE BusDataType;
59   ULONG BusNumber;
60   ULONG SlotNumber;
61 } SYSDBG_BUS_DATA, *PSYSDBG_BUS_DATA;
62 #define SYSDBG_BUS_DATA SYSDBG_BUS_DATA
63 #endif
64
65 #ifndef PCI_SLOT_NUMBER
66 typedef struct _PCI_SLOT_NUMBER {
67   union {
68     struct {
69       ULONG DeviceNumber:5;
70       ULONG FunctionNumber:3;
71       ULONG Reserved:24;
72     } bits;
73     ULONG AsULONG;
74   } u;
75 } PCI_SLOT_NUMBER, *PPCI_SLOT_NUMBER;
76 #define PCI_SLOT_NUMBER PCI_SLOT_NUMBER
77 #endif
78
79 #ifdef NtSystemDebugControl
80 #undef NtSystemDebugControl
81 #endif
82 static NTSTATUS (NTAPI *MyNtSystemDebugControl)(SYSDBG_COMMAND Command, PVOID InputBuffer, ULONG InputBufferLength, PVOID OutputBuffer, ULONG OutputBufferLength, PULONG ReturnLength);
83 #define NtSystemDebugControl MyNtSystemDebugControl
84
85 static BOOL debug_privilege_enabled;
86 static LUID luid_debug_privilege;
87 static BOOL revert_only_privilege;
88 static HANDLE revert_token;
89 static HMODULE ntdll;
90
91 static int win32_sysdbg_initialized;
92
93 static NTSTATUS
94 win32_sysdbg_pci_bus_data(BOOL WriteBusData, BYTE BusNumber, BYTE DeviceNumber, BYTE FunctionNumber, BYTE Address, PVOID Buffer, BYTE BufferSize, PULONG Length)
95 {
96   SYSDBG_BUS_DATA sysdbg_cmd;
97   PCI_SLOT_NUMBER pci_slot;
98
99   if (!NtSystemDebugControl)
100     return STATUS_NOT_IMPLEMENTED;
101
102   memset(&pci_slot, 0, sizeof(pci_slot));
103   memset(&sysdbg_cmd, 0, sizeof(sysdbg_cmd));
104
105   sysdbg_cmd.Address = Address;
106   sysdbg_cmd.Buffer = Buffer;
107   sysdbg_cmd.Request = BufferSize;
108   sysdbg_cmd.BusDataType = PCIConfiguration;
109   sysdbg_cmd.BusNumber = BusNumber;
110   pci_slot.u.bits.DeviceNumber = DeviceNumber;
111   pci_slot.u.bits.FunctionNumber = FunctionNumber;
112   sysdbg_cmd.SlotNumber = pci_slot.u.AsULONG;
113
114   *Length = 0;
115   return NtSystemDebugControl(WriteBusData ? SysDbgWriteBusData : SysDbgReadBusData, &sysdbg_cmd, sizeof(sysdbg_cmd), NULL, 0, Length);
116 }
117
118 static int
119 win32_sysdbg_setup(struct pci_access *a)
120 {
121   UINT prev_error_mode;
122   NTSTATUS status;
123   ULONG ret_len;
124   DWORD id;
125
126   if (win32_sysdbg_initialized)
127     return 1;
128
129   prev_error_mode = change_error_mode(SEM_FAILCRITICALERRORS);
130   ntdll = LoadLibrary(TEXT("ntdll.dll"));
131   change_error_mode(prev_error_mode);
132   if (!ntdll)
133     {
134       a->debug("Cannot open ntdll.dll library.");
135       return 0;
136     }
137
138   NtSystemDebugControl = (LPVOID)GetProcAddress(ntdll, "NtSystemDebugControl");
139   if (!NtSystemDebugControl)
140     {
141       a->debug("Function NtSystemDebugControl() is not supported.");
142       FreeLibrary(ntdll);
143       ntdll = NULL;
144       return 0;
145     }
146
147   /*
148    * Try to read PCI id register from PCI device 00:00.0.
149    * If this device does not exist and NT SysDbg API is working then
150    * NT SysDbg returns STATUS_UNSUCCESSFUL.
151    */
152   status = win32_sysdbg_pci_bus_data(FALSE, 0, 0, 0, 0, &id, sizeof(id), &ret_len);
153   if ((status >= 0 && ret_len == sizeof(id)) || status == STATUS_UNSUCCESSFUL)
154     {
155       win32_sysdbg_initialized = 1;
156       return 1;
157     }
158   else if (status != STATUS_ACCESS_DENIED)
159     {
160       if (status == STATUS_NOT_IMPLEMENTED || status == STATUS_INVALID_INFO_CLASS)
161         a->debug("NT SysDbg is not supported.");
162       else if (status == STATUS_DEBUGGER_INACTIVE)
163         a->debug("NT SysDbg is disabled.");
164       else
165         a->debug("NT SysDbg returned error 0x%lx.", status);
166       FreeLibrary(ntdll);
167       ntdll = NULL;
168       NtSystemDebugControl = NULL;
169       return 0;
170     }
171
172   a->debug("NT SysDbg returned Access Denied, trying again with Debug privilege...");
173
174   if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid_debug_privilege))
175     {
176       a->debug("Debug privilege is not supported.");
177       FreeLibrary(ntdll);
178       ntdll = NULL;
179       NtSystemDebugControl = NULL;
180       return 0;
181     }
182
183   if (!enable_privilege(luid_debug_privilege, &revert_token, &revert_only_privilege))
184     {
185       a->debug("Cannot enable Debug privilege.");
186       FreeLibrary(ntdll);
187       ntdll = NULL;
188       NtSystemDebugControl = NULL;
189       return 0;
190     }
191
192   status = win32_sysdbg_pci_bus_data(FALSE, 0, 0, 0, 0, &id, sizeof(id), &ret_len);
193   if ((status >= 0 && ret_len == sizeof(id)) || status == STATUS_UNSUCCESSFUL)
194     {
195       a->debug("Succeeded.");
196       debug_privilege_enabled = TRUE;
197       win32_sysdbg_initialized = 1;
198       return 1;
199     }
200
201   revert_privilege(luid_debug_privilege, revert_token, revert_only_privilege);
202   revert_token = NULL;
203   revert_only_privilege = FALSE;
204
205   FreeLibrary(ntdll);
206   ntdll = NULL;
207   NtSystemDebugControl = NULL;
208
209   if (status == STATUS_NOT_IMPLEMENTED || status == STATUS_INVALID_INFO_CLASS)
210     a->debug("NT SysDbg is not supported.");
211   else if (status == STATUS_DEBUGGER_INACTIVE)
212     a->debug("NT SysDbg is disabled.");
213   else if (status == STATUS_ACCESS_DENIED)
214     a->debug("NT SysDbg returned Access Denied.");
215   else
216     a->debug("NT SysDbg returned error 0x%lx.", status);
217
218   return 0;
219 }
220
221 static int
222 win32_sysdbg_detect(struct pci_access *a)
223 {
224   if (!win32_sysdbg_setup(a))
225     return 0;
226
227   return 1;
228 }
229
230 static void
231 win32_sysdbg_init(struct pci_access *a)
232 {
233   if (!win32_sysdbg_setup(a))
234     {
235       a->debug("\n");
236       a->error("NT SysDbg PCI Bus Data interface cannot be accessed.");
237     }
238 }
239
240 static void
241 win32_sysdbg_cleanup(struct pci_access *a UNUSED)
242 {
243   if (!win32_sysdbg_initialized)
244     return;
245
246   if (debug_privilege_enabled)
247     {
248       revert_privilege(luid_debug_privilege, revert_token, revert_only_privilege);
249       revert_token = NULL;
250       revert_only_privilege = FALSE;
251       debug_privilege_enabled = FALSE;
252     }
253
254   FreeLibrary(ntdll);
255   ntdll = NULL;
256   NtSystemDebugControl = NULL;
257
258   win32_sysdbg_initialized = 0;
259 }
260
261 static int
262 win32_sysdbg_read(struct pci_dev *d, int pos, byte *buf, int len)
263 {
264   NTSTATUS status;
265   ULONG ret_len;
266
267   if ((unsigned int)d->domain > 0 || (unsigned int)pos > 255 || (unsigned int)(pos+len) > 256)
268     return 0;
269
270   status = win32_sysdbg_pci_bus_data(FALSE, d->bus, d->dev, d->func, pos, buf, len, &ret_len);
271   if (status < 0 || ret_len != (unsigned int)len)
272     return 0;
273
274   return 1;
275 }
276
277 static int
278 win32_sysdbg_write(struct pci_dev *d, int pos, byte *buf, int len)
279 {
280   NTSTATUS status;
281   ULONG ret_len;
282
283   if ((unsigned int)d->domain > 0 || (unsigned int)pos > 255 || (unsigned int)(pos+len) > 256)
284     return 0;
285
286   status = win32_sysdbg_pci_bus_data(TRUE, d->bus, d->dev, d->func, pos, buf, len, &ret_len);
287   if (status < 0 || ret_len != (unsigned int)len)
288     return 0;
289
290   return 1;
291 }
292
293 struct pci_methods pm_win32_sysdbg = {
294   "win32-sysdbg",
295   "Win32 PCI config space access using NT SysDbg Bus Data interface",
296   NULL,                                 /* config */
297   win32_sysdbg_detect,
298   win32_sysdbg_init,
299   win32_sysdbg_cleanup,
300   pci_generic_scan,
301   pci_generic_fill_info,
302   win32_sysdbg_read,
303   win32_sysdbg_write,
304   NULL,                                 /* read_vpd */
305   NULL,                                 /* init_dev */
306   NULL                                  /* cleanup_dev */
307 };