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