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