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 static HMODULE ntdll;
89
90 static int win32_sysdbg_initialized;
91
92 static NTSTATUS
win32_sysdbg_pci_bus_data(BOOL WriteBusData,BYTE BusNumber,BYTE DeviceNumber,BYTE FunctionNumber,BYTE Address,PVOID Buffer,BYTE BufferSize,PULONG Length)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
win32_sysdbg_setup(struct pci_access * a)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 = win32_change_error_mode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
129 ntdll = LoadLibrary(TEXT("ntdll.dll"));
130 win32_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 (!win32_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 win32_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
win32_sysdbg_detect(struct pci_access * a)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
win32_sysdbg_init(struct pci_access * a)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
win32_sysdbg_cleanup(struct pci_access * a UNUSED)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 win32_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
win32_sysdbg_read(struct pci_dev * d,int pos,byte * buf,int len)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
win32_sysdbg_write(struct pci_dev * d,int pos,byte * buf,int len)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 .name = "win32-sysdbg",
294 .help = "Win32 PCI config space access using NT SysDbg Bus Data interface",
295 .detect = win32_sysdbg_detect,
296 .init = win32_sysdbg_init,
297 .cleanup = win32_sysdbg_cleanup,
298 .scan = pci_generic_scan,
299 .fill_info = pci_generic_fill_info,
300 .read = win32_sysdbg_read,
301 .write = win32_sysdbg_write,
302 };
303