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