xref: /pciutils/lib/win32-sysdbg.c (revision 76c06ea6)
1 /*
2  *      The PCI Library -- PCI config space access using NT SysDbg interface
3  *
4  *      Copyright (c) 2022 Pali Rohár <[email protected]>
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 "win32-helpers.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 
89 static int win32_sysdbg_initialized;
90 
91 static NTSTATUS
win32_sysdbg_pci_bus_data(BOOL WriteBusData,BYTE BusNumber,BYTE DeviceNumber,BYTE FunctionNumber,BYTE Address,PVOID Buffer,BYTE BufferSize,PULONG Length)92 win32_sysdbg_pci_bus_data(BOOL WriteBusData, BYTE BusNumber, BYTE DeviceNumber, BYTE FunctionNumber, BYTE Address, PVOID Buffer, BYTE BufferSize, PULONG Length)
93 {
94   SYSDBG_BUS_DATA sysdbg_cmd;
95   PCI_SLOT_NUMBER pci_slot;
96 
97   if (!NtSystemDebugControl)
98     return STATUS_NOT_IMPLEMENTED;
99 
100   memset(&pci_slot, 0, sizeof(pci_slot));
101   memset(&sysdbg_cmd, 0, sizeof(sysdbg_cmd));
102 
103   sysdbg_cmd.Address = Address;
104   sysdbg_cmd.Buffer = Buffer;
105   sysdbg_cmd.Request = BufferSize;
106   sysdbg_cmd.BusDataType = PCIConfiguration;
107   sysdbg_cmd.BusNumber = BusNumber;
108   pci_slot.u.bits.DeviceNumber = DeviceNumber;
109   pci_slot.u.bits.FunctionNumber = FunctionNumber;
110   sysdbg_cmd.SlotNumber = pci_slot.u.AsULONG;
111 
112   *Length = 0;
113   return NtSystemDebugControl(WriteBusData ? SysDbgWriteBusData : SysDbgReadBusData, &sysdbg_cmd, sizeof(sysdbg_cmd), NULL, 0, Length);
114 }
115 
116 static int
win32_sysdbg_setup(struct pci_access * a)117 win32_sysdbg_setup(struct pci_access *a)
118 {
119   NTSTATUS status;
120   HMODULE ntdll;
121   ULONG ret_len;
122   DWORD id;
123 
124   if (win32_sysdbg_initialized)
125     return 1;
126 
127   ntdll = GetModuleHandle(TEXT("ntdll.dll"));
128   if (!ntdll)
129     {
130       a->debug("Library ntdll.dll is not present.");
131       return 0;
132     }
133 
134   NtSystemDebugControl = (LPVOID)GetProcAddress(ntdll, "NtSystemDebugControl");
135   if (!NtSystemDebugControl)
136     {
137       a->debug("Function NtSystemDebugControl() is not supported.");
138       return 0;
139     }
140 
141   /*
142    * Try to read PCI id register from PCI device 00:00.0.
143    * If this device does not exist and NT SysDbg API is working then
144    * NT SysDbg returns STATUS_UNSUCCESSFUL.
145    */
146   status = win32_sysdbg_pci_bus_data(FALSE, 0, 0, 0, 0, &id, sizeof(id), &ret_len);
147   if ((status >= 0 && ret_len == sizeof(id)) || status == STATUS_UNSUCCESSFUL)
148     {
149       win32_sysdbg_initialized = 1;
150       return 1;
151     }
152   else if (status != STATUS_ACCESS_DENIED)
153     {
154       if (status == STATUS_NOT_IMPLEMENTED || status == STATUS_INVALID_INFO_CLASS)
155         a->debug("NT SysDbg is not supported.");
156       else if (status == STATUS_DEBUGGER_INACTIVE)
157         a->debug("NT SysDbg is disabled.");
158       else
159         a->debug("NT SysDbg returned error 0x%lx.", status);
160       NtSystemDebugControl = NULL;
161       return 0;
162     }
163 
164   a->debug("NT SysDbg returned Access Denied, trying again with Debug privilege...");
165 
166   if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid_debug_privilege))
167     {
168       a->debug("Debug privilege is not supported.");
169       NtSystemDebugControl = NULL;
170       return 0;
171     }
172 
173   if (!win32_enable_privilege(luid_debug_privilege, &revert_token, &revert_only_privilege))
174     {
175       a->debug("Cannot enable Debug privilege.");
176       NtSystemDebugControl = NULL;
177       return 0;
178     }
179 
180   status = win32_sysdbg_pci_bus_data(FALSE, 0, 0, 0, 0, &id, sizeof(id), &ret_len);
181   if ((status >= 0 && ret_len == sizeof(id)) || status == STATUS_UNSUCCESSFUL)
182     {
183       a->debug("Succeeded.");
184       debug_privilege_enabled = TRUE;
185       win32_sysdbg_initialized = 1;
186       return 1;
187     }
188 
189   win32_revert_privilege(luid_debug_privilege, revert_token, revert_only_privilege);
190   revert_token = NULL;
191   revert_only_privilege = FALSE;
192 
193   NtSystemDebugControl = NULL;
194 
195   if (status == STATUS_NOT_IMPLEMENTED || status == STATUS_INVALID_INFO_CLASS)
196     a->debug("NT SysDbg is not supported.");
197   else if (status == STATUS_DEBUGGER_INACTIVE)
198     a->debug("NT SysDbg is disabled.");
199   else if (status == STATUS_ACCESS_DENIED)
200     a->debug("NT SysDbg returned Access Denied.");
201   else
202     a->debug("NT SysDbg returned error 0x%lx.", status);
203 
204   return 0;
205 }
206 
207 static int
win32_sysdbg_detect(struct pci_access * a)208 win32_sysdbg_detect(struct pci_access *a)
209 {
210   if (!win32_sysdbg_setup(a))
211     return 0;
212 
213   return 1;
214 }
215 
216 static void
win32_sysdbg_init(struct pci_access * a)217 win32_sysdbg_init(struct pci_access *a)
218 {
219   if (!win32_sysdbg_setup(a))
220     {
221       a->debug("\n");
222       a->error("NT SysDbg PCI Bus Data interface cannot be accessed.");
223     }
224 }
225 
226 static void
win32_sysdbg_cleanup(struct pci_access * a UNUSED)227 win32_sysdbg_cleanup(struct pci_access *a UNUSED)
228 {
229   if (!win32_sysdbg_initialized)
230     return;
231 
232   if (debug_privilege_enabled)
233     {
234       win32_revert_privilege(luid_debug_privilege, revert_token, revert_only_privilege);
235       revert_token = NULL;
236       revert_only_privilege = FALSE;
237       debug_privilege_enabled = FALSE;
238     }
239 
240   NtSystemDebugControl = NULL;
241 
242   win32_sysdbg_initialized = 0;
243 }
244 
245 static int
win32_sysdbg_read(struct pci_dev * d,int pos,byte * buf,int len)246 win32_sysdbg_read(struct pci_dev *d, int pos, byte *buf, int len)
247 {
248   NTSTATUS status;
249   ULONG ret_len;
250 
251   if ((unsigned int)d->domain > 0 || (unsigned int)pos > 255 || (unsigned int)(pos+len) > 256)
252     return 0;
253 
254   status = win32_sysdbg_pci_bus_data(FALSE, d->bus, d->dev, d->func, pos, buf, len, &ret_len);
255   if (status < 0 || ret_len != (unsigned int)len)
256     return 0;
257 
258   return 1;
259 }
260 
261 static int
win32_sysdbg_write(struct pci_dev * d,int pos,byte * buf,int len)262 win32_sysdbg_write(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(TRUE, 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 struct pci_methods pm_win32_sysdbg = {
278   .name = "win32-sysdbg",
279   .help = "Win32 PCI config space access using NT SysDbg Bus Data interface",
280   .detect = win32_sysdbg_detect,
281   .init = win32_sysdbg_init,
282   .cleanup = win32_sysdbg_cleanup,
283   .scan = pci_generic_scan,
284   .fill_info = pci_generic_fill_info,
285   .read = win32_sysdbg_read,
286   .write = win32_sysdbg_write,
287 };
288