]> mj.ucw.cz Git - pciutils.git/blob - lib/physmem-windows.c
lspci: add VirtIO SharedMemory capability support
[pciutils.git] / lib / physmem-windows.c
1 /*
2  *      The PCI Library -- Physical memory mapping for Windows systems
3  *
4  *      Copyright (c) 2023 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 "internal.h"
12
13 #include <windows.h>
14 #include <errno.h>
15 #include <stdlib.h>
16
17 #include "physmem.h"
18 #include "win32-helpers.h"
19
20 #ifndef NTSTATUS
21 #define NTSTATUS LONG
22 #endif
23 #ifndef STATUS_INVALID_HANDLE
24 #define STATUS_INVALID_HANDLE ((NTSTATUS)0xC0000008)
25 #endif
26 #ifndef STATUS_INVALID_PARAMETER
27 #define STATUS_INVALID_PARAMETER ((NTSTATUS)0xC000000D)
28 #endif
29 #ifndef STATUS_CONFLICTING_ADDRESSES
30 #define STATUS_CONFLICTING_ADDRESSES ((NTSTATUS)0xC0000018)
31 #endif
32 #ifndef STATUS_NOT_MAPPED_VIEW
33 #define STATUS_NOT_MAPPED_VIEW ((NTSTATUS)0xC0000019)
34 #endif
35 #ifndef STATUS_INVALID_VIEW_SIZE
36 #define STATUS_INVALID_VIEW_SIZE ((NTSTATUS)0xC000001F)
37 #endif
38 #ifndef STATUS_ACCESS_DENIED
39 #define STATUS_ACCESS_DENIED ((NTSTATUS)0xC0000022)
40 #endif
41 #ifndef STATUS_OBJECT_NAME_NOT_FOUND
42 #define STATUS_OBJECT_NAME_NOT_FOUND ((NTSTATUS)0xC0000034)
43 #endif
44 #ifndef STATUS_INVALID_PAGE_PROTECTION
45 #define STATUS_INVALID_PAGE_PROTECTION ((NTSTATUS)0xC0000045)
46 #endif
47 #ifndef STATUS_SECTION_PROTECTION
48 #define STATUS_SECTION_PROTECTION ((NTSTATUS)0xC000004E)
49 #endif
50 #ifndef STATUS_INSUFFICIENT_RESOURCES
51 #define STATUS_INSUFFICIENT_RESOURCES ((NTSTATUS)0xC000009A)
52 #endif
53 #ifndef STATUS_INVALID_PARAMETER_3
54 #define STATUS_INVALID_PARAMETER_3 ((NTSTATUS)0xC00000F1)
55 #endif
56 #ifndef STATUS_INVALID_PARAMETER_4
57 #define STATUS_INVALID_PARAMETER_4 ((NTSTATUS)0xC00000F2)
58 #endif
59 #ifndef STATUS_INVALID_PARAMETER_5
60 #define STATUS_INVALID_PARAMETER_5 ((NTSTATUS)0xC00000F3)
61 #endif
62 #ifndef STATUS_INVALID_PARAMETER_8
63 #define STATUS_INVALID_PARAMETER_8 ((NTSTATUS)0xC00000F6)
64 #endif
65 #ifndef STATUS_INVALID_PARAMETER_9
66 #define STATUS_INVALID_PARAMETER_9 ((NTSTATUS)0xC00000F7)
67 #endif
68 #ifndef STATUS_MAPPED_ALIGNMENT
69 #define STATUS_MAPPED_ALIGNMENT ((NTSTATUS)0xC0000220)
70 #endif
71
72 #ifndef OBJ_CASE_INSENSITIVE
73 #define OBJ_CASE_INSENSITIVE 0x00000040L
74 #endif
75
76 #ifndef SECTION_INHERIT
77 #define SECTION_INHERIT ULONG
78 #endif
79 #ifndef ViewUnmap
80 #define ViewUnmap ((SECTION_INHERIT)2)
81 #endif
82
83 #ifndef IMAGE_NT_OPTIONAL_HDR_MAGIC
84 #ifdef _WIN64
85 #define IMAGE_NT_OPTIONAL_HDR_MAGIC 0x20b
86 #else
87 #define IMAGE_NT_OPTIONAL_HDR_MAGIC 0x10b
88 #endif
89 #endif
90
91 #ifndef EOVERFLOW
92 #define EOVERFLOW 132
93 #endif
94
95 #if _WIN32_WINNT < 0x0500
96 typedef ULONG ULONG_PTR;
97 typedef ULONG_PTR SIZE_T, *PSIZE_T;
98 #endif
99
100 #ifndef __UNICODE_STRING_DEFINED
101 #define __UNICODE_STRING_DEFINED
102 typedef struct _UNICODE_STRING {
103   USHORT Length;
104   USHORT MaximumLength;
105   PWSTR  Buffer;
106 } UNICODE_STRING, *PUNICODE_STRING;
107 #endif
108
109 #ifndef __OBJECT_ATTRIBUTES_DEFINED
110 #define __OBJECT_ATTRIBUTES_DEFINED
111 typedef struct _OBJECT_ATTRIBUTES {
112   ULONG Length;
113   HANDLE RootDirectory;
114   PUNICODE_STRING ObjectName;
115   ULONG Attributes;
116   PVOID SecurityDescriptor;
117   PVOID SecurityQualityOfService;
118 } OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES;
119 #endif
120
121 #ifndef InitializeObjectAttributes
122 #define InitializeObjectAttributes(p, n, a, r, s) \
123 { \
124   (p)->Length = sizeof(OBJECT_ATTRIBUTES); \
125   (p)->RootDirectory = (r); \
126   (p)->Attributes = (a); \
127   (p)->ObjectName = (n); \
128   (p)->SecurityDescriptor = (s); \
129   (p)->SecurityQualityOfService = NULL; \
130 }
131 #endif
132
133 #ifndef RtlInitUnicodeString
134 #define RtlInitUnicodeString(d, s) \
135 { \
136   (d)->Length = wcslen(s) * sizeof(WCHAR); \
137   (d)->MaximumLength = (d)->Length + sizeof(WCHAR); \
138   (d)->Buffer = (PWCHAR)(s); \
139 }
140 #endif
141
142 #define VWIN32_DEVICE_ID 0x0002A /* from vmm.h */
143 #define WIN32_SERVICE_ID(device, function) (((device) << 16) | (function))
144 #define VWIN32_Int31Dispatch WIN32_SERVICE_ID(VWIN32_DEVICE_ID, 0x29)
145 #define DPMI_PHYSICAL_ADDRESS_MAPPING 0x0800
146
147 struct physmem {
148   HMODULE ntdll;
149   HANDLE section_handle;
150   NTSTATUS (NTAPI *NtOpenSection)(PHANDLE SectionHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes);
151   NTSTATUS (NTAPI *NtMapViewOfSection)(HANDLE SectionHandle, HANDLE ProcessHandle, PVOID *BaseAddress, ULONG_PTR ZeroBits, SIZE_T CommitSize, PLARGE_INTEGER SectionOffset, PSIZE_T ViewSize, SECTION_INHERIT InheritDisposition, ULONG AllocationType, ULONG Win32Protect);
152   NTSTATUS (NTAPI *NtUnmapViewOfSection)(HANDLE ProcessHandle, PVOID BaseAddress);
153   ULONG (NTAPI *RtlNtStatusToDosError)(NTSTATUS Status);
154 #if defined(__i386__) || defined(_M_IX86)
155   DWORD (WINAPI *VxDCall2)(DWORD Service, DWORD Arg1, DWORD Arg2);
156   LPVOID w32skrnl_dpmi_lcall_ptr;
157   DWORD base_addr_offset;
158 #endif
159 };
160
161 #if defined(__i386__) || defined(_M_IX86)
162
163 static BOOL
164 w32skrnl_physical_address_mapping(struct physmem *physmem, DWORD phys_addr, DWORD size, DWORD *virt_addr)
165 {
166   DWORD address_hi = phys_addr >> 16;
167   DWORD address_lo = phys_addr & 0xffff;
168   DWORD size_hi = size >> 16;
169   DWORD size_lo = size & 0xffff;
170   BYTE failed;
171
172   /*
173    * Physical address mapping via w32skrnl.dll on Windows maps physical memory
174    * and translates it to the virtual space of the current process memory.
175    * Works only for aligned address / length and first 1 MB cannot be mapped
176    * by this method. Expect that first 1 MB is already 1:1 mapped by the OS.
177    * So accept request for physical memory range which is whole below 1 MB
178    * without error and return virtual address same as the physical one.
179    */
180   if (phys_addr < 1*1024*1024UL)
181     {
182       if ((u64)phys_addr + size > 1*1024*1024UL)
183         {
184           errno = ENXIO;
185           return FALSE;
186         }
187
188       *virt_addr = phys_addr;
189       return TRUE;
190     }
191
192   /*
193    * Unfortunately w32skrnl.dll provides only 48-bit fword pointer to physical
194    * address mapping function and such pointer type is not supported by GCC,
195    * nor by MSVC and therefore it is not possible call this function in C code.
196    * So call this function with all parameters passed via inline assembly.
197    */
198 #if defined(__GNUC__)
199   asm volatile (
200     "stc\n\t"
201     "lcall *(%3)\n\t"
202     "setc %0\n\t"
203       : "=qm" (failed), "+b" (address_hi), "+c" (address_lo)
204       : "r" (physmem->w32skrnl_dpmi_lcall_ptr), "a" (DPMI_PHYSICAL_ADDRESS_MAPPING), "S" (size_hi), "D" (size_lo)
205       : "cc", "memory"
206   );
207 #elif defined(_MSC_VER)
208   __asm {
209     mov esi, size_hi
210     mov edi, size_lo
211     mov ebx, address_hi
212     mov ecx, address_lo
213     mov eax, DPMI_PHYSICAL_ADDRESS_MAPPING
214     stc
215     mov edx, physmem
216     mov edx, [edx]physmem.w32skrnl_dpmi_lcall_ptr
217     call fword ptr [edx]
218     setc failed
219     mov address_hi, ebx
220     mov address_lo, ecx
221   }
222 #else
223 #error "Unsupported compiler"
224 #endif
225
226   /*
227    * Windows does not provide any error code when this function call fails.
228    * So set errno just to the generic EACCES value.
229    */
230   if (failed)
231     {
232       errno = EACCES;
233       return FALSE;
234     }
235
236   *virt_addr = ((address_hi & 0xffff) << 16) | (address_lo & 0xffff);
237   return TRUE;
238 }
239
240 #if defined(__GNUC__) && ((__GNUC__ == 4 && __GNUC_MINOR__ >= 8) || (__GNUC__ > 4)) && (__GNUC__ <= 11)
241 /*
242  * GCC versions 4.8 - 11 are buggy and throw error "'asm' operand has impossible
243  * constraints" for inline assembly when optimizations (O1+) are enabled. So for
244  * these GCC versions disable buggy optimizations by enforcing O0 optimize flag
245  * affecting just this one function.
246  */
247 __attribute__((optimize("O0")))
248 #endif
249 static BOOL
250 vdxcall_physical_address_mapping(struct physmem *physmem, DWORD phys_addr, DWORD size, DWORD *virt_addr)
251 {
252   DWORD address_hi = phys_addr >> 16;
253   DWORD address_lo = phys_addr & 0xffff;
254   DWORD size_hi = size >> 16;
255   DWORD size_lo = size & 0xffff;
256   BYTE failed;
257
258   /*
259    * Physical address mapping via VxDCall2() on Windows maps physical memory
260    * and translates it to the virtual space of the current process memory.
261    * There are no restrictions for aligning or physical address ranges.
262    * Works with any (unaligned) address or length, including low 1MB range.
263    */
264
265   /*
266    * Function VxDCall2() has strange calling convention. First 3 arguments are
267    * passed on stack, which callee pops (same as stdcall convention) but rest
268    * of the function arguments are passed in ESI, EDI and EBX registers. And
269    * return value is in carry flag (CF) and AX, BX and CX registers. GCC and
270    * neither MSVC do not support this strange calling convention, so call this
271    * function via inline assembly.
272    *
273    * Pseudocode with stdcall calling convention of that function looks like:
274    * ESI = size_hi
275    * EDI = size_lo
276    * EBX = address_hi
277    * VxDCall2(VWIN32_Int31Dispatch, DPMI_PHYSICAL_ADDRESS_MAPPING, address_lo)
278    * failed = CF
279    * address_hi = BX (if not failed)
280    * address_lo = CX (if not failed)
281    */
282
283 #if defined(__GNUC__)
284   asm volatile (
285     "pushl %6\n\t"
286     "pushl %5\n\t"
287     "pushl %4\n\t"
288     "stc\n\t"
289     "call *%P3\n\t"
290     "setc %0\n\t"
291       : "=qm" (failed), "+b" (address_hi), "=c" (address_lo)
292       : "rmi" (physmem->VxDCall2), "rmi" (VWIN32_Int31Dispatch), "rmi" (DPMI_PHYSICAL_ADDRESS_MAPPING),
293         "rmi" (address_lo), "S" (size_hi), "D" (size_lo)
294 /* Specify all call clobbered scratch registers for stdcall calling convention. */
295       : "eax", "edx",
296 /*
297  * Since GCC version 4.9.0 it is possible to specify x87 registers as clobbering
298  * if they are not disabled by -mno-80387 switch (which defines _SOFT_FLOAT).
299  */
300 #if ((__GNUC__ == 4 && __GNUC_MINOR__ >= 9) || (__GNUC__ > 4)) && !defined(_SOFT_FLOAT)
301         "st", "st(1)", "st(2)", "st(3)", "st(4)", "st(5)", "st(6)", "st(7)",
302 #endif
303 #ifdef __MMX__
304         "mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm6",
305 #endif
306 #ifdef __SSE__
307         "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7",
308 #endif
309         "cc", "memory"
310   );
311 #elif defined(_MSC_VER)
312   __asm {
313     mov esi, size_hi
314     mov edi, size_lo
315     mov ebx, address_hi
316     push address_lo
317     push DPMI_PHYSICAL_ADDRESS_MAPPING
318     push VWIN32_Int31Dispatch
319     stc
320     mov eax, physmem
321     call [eax]physmem.VxDCall2
322     setc failed
323     mov address_hi, ebx
324     mov address_lo, ecx
325   }
326 #else
327 #error "Unsupported compiler"
328 #endif
329
330   /*
331    * Windows does not provide any error code when this function call fails.
332    * So set errno just to the generic EACCES value.
333    */
334   if (failed)
335     {
336       errno = EACCES;
337       return FALSE;
338     }
339
340   *virt_addr = ((address_hi & 0xffff) << 16) | (address_lo & 0xffff);
341   return TRUE;
342 }
343
344 static BOOL
345 win32_get_physmem_offset(DWORD *offset)
346 {
347   WORD DSsel;
348   LDT_ENTRY DSentry;
349
350   /*
351    * Read DS selector. For this purpose there is WinAPI function and when called
352    * as GetThreadContext(GetCurrentThread(), ...) with CONTEXT_SEGMENTS param,
353    * it fills SegDs value. But on some Windows versions, GetThreadContext() can
354    * be called only for threads attached to debugger. Hence we cannot use it for
355    * our current thread. So instead read DS selector directly from ds register
356    * via inline assembly code.
357    */
358 #if defined(__GNUC__)
359   asm ("movw %%ds, %w0" : "=rm" (DSsel));
360 #elif defined(_MSC_VER)
361   __asm { mov DSsel, ds }
362 #else
363 #error "Unsupported compiler"
364 #endif
365
366   if (!GetThreadSelectorEntry(GetCurrentThread(), DSsel, &DSentry))
367     return FALSE;
368
369   *offset = DSentry.BaseLow | (DSentry.HighWord.Bytes.BaseMid << 0x10) | (DSentry.HighWord.Bytes.BaseHi << 0x18);
370   return TRUE;
371 }
372
373 static BYTE *
374 win32_get_baseaddr_from_hmodule(HMODULE module)
375 {
376   WORD (WINAPI *ImteFromHModule)(HMODULE);
377   BYTE *(WINAPI *BaseAddrFromImte)(WORD);
378   HMODULE w32skrnl;
379   WORD imte;
380
381   if ((GetVersion() & 0xC0000000) != 0x80000000)
382     return (BYTE *)module;
383
384   w32skrnl = GetModuleHandleA("w32skrnl.dll");
385   if (!w32skrnl)
386     return NULL;
387
388   ImteFromHModule = (LPVOID)GetProcAddress(w32skrnl, "_ImteFromHModule@4");
389   BaseAddrFromImte = (LPVOID)GetProcAddress(w32skrnl, "_BaseAddrFromImte@4");
390   if (!ImteFromHModule || !BaseAddrFromImte)
391     return NULL;
392
393   imte = ImteFromHModule(module);
394   if (imte == 0xffff)
395     return NULL;
396
397   return BaseAddrFromImte(imte);
398 }
399
400 static FARPROC
401 win32_get_proc_address_by_ordinal(HMODULE module, DWORD ordinal, BOOL must_be_without_name)
402 {
403   BYTE *baseaddr;
404   IMAGE_DOS_HEADER *dos_header;
405   IMAGE_NT_HEADERS *nt_header;
406   DWORD export_dir_offset, export_dir_size;
407   IMAGE_EXPORT_DIRECTORY *export_dir;
408   DWORD base_ordinal, func_count;
409   DWORD *func_addrs;
410   FARPROC func_ptr;
411   DWORD names_count, i;
412   USHORT *names_idxs;
413   UINT prev_error_mode;
414   char module_name[MAX_PATH];
415   DWORD module_name_len;
416   char *export_name;
417   char *endptr;
418   long num;
419
420   baseaddr = win32_get_baseaddr_from_hmodule(module);
421   if (!baseaddr)
422     return NULL;
423
424   dos_header = (IMAGE_DOS_HEADER *)baseaddr;
425   if (dos_header->e_magic != IMAGE_DOS_SIGNATURE)
426     return NULL;
427
428   nt_header = (IMAGE_NT_HEADERS *)((BYTE *)dos_header + dos_header->e_lfanew);
429   if (nt_header->Signature != IMAGE_NT_SIGNATURE)
430     return NULL;
431
432   if (nt_header->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC)
433     return NULL;
434
435   if (nt_header->OptionalHeader.NumberOfRvaAndSizes <= IMAGE_DIRECTORY_ENTRY_EXPORT)
436     return NULL;
437
438   export_dir_offset = nt_header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
439   export_dir_size = nt_header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size;
440
441   if (!export_dir_offset || !export_dir_size)
442     return NULL;
443
444   export_dir = (IMAGE_EXPORT_DIRECTORY *)(baseaddr + export_dir_offset);
445   base_ordinal = export_dir->Base;
446   func_count = export_dir->NumberOfFunctions;
447   func_addrs = (DWORD *)(baseaddr + (DWORD)export_dir->AddressOfFunctions);
448
449   if (ordinal < base_ordinal || ordinal - base_ordinal > func_count)
450     return NULL;
451
452   if (must_be_without_name)
453     {
454       /* Check that function with ordinal number does not have any name. */
455       names_count = export_dir->NumberOfNames;
456       names_idxs = (USHORT *)(baseaddr + (DWORD)export_dir->AddressOfNameOrdinals);
457       for (i = 0; i < names_count; i++)
458         {
459           if (names_idxs[i] == ordinal - base_ordinal)
460             return NULL;
461         }
462     }
463
464   func_ptr = (FARPROC)(baseaddr + func_addrs[ordinal - base_ordinal]);
465   if ((BYTE *)func_ptr >= (BYTE *)export_dir && (BYTE *)func_ptr < (BYTE *)export_dir + export_dir_size)
466     {
467       /*
468        * We need to locate the _last_ dot character (separator of library name
469        * and export symbol name) because library name may contain dot character
470        * (used when specifying file name with explicit extension). For example
471        * wine is using this kind of strange symbol redirection to different
472        * library with non-standard file name extension (different than .dll).
473        */
474       export_name = strrchr((char *)func_ptr, '.');
475       if (!export_name)
476         return NULL;
477       export_name++;
478
479       module_name_len = export_name - 1 - (char *)func_ptr;
480       if (module_name_len >= sizeof(module_name))
481         return NULL;
482
483       memcpy(module_name, func_ptr, module_name_len);
484       module_name[module_name_len] = 0;
485
486       prev_error_mode = win32_change_error_mode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
487       module = LoadLibraryA(module_name);
488       win32_change_error_mode(prev_error_mode);
489       if (!module)
490         {
491           FreeLibrary(module);
492           return NULL;
493         }
494
495       if (*export_name == '#')
496         {
497           export_name++;
498           errno = 0;
499           num = strtol(export_name, &endptr, 10);
500           if (*export_name < '0' || *export_name > '9' || errno || *endptr || num < 0 || (unsigned long)num >= ((DWORD)-1)/2)
501             {
502               FreeLibrary(module);
503               return NULL;
504             }
505           ordinal = num;
506           func_ptr = win32_get_proc_address_by_ordinal(module, ordinal, FALSE);
507         }
508       else
509         {
510           func_ptr = GetProcAddress(module, export_name);
511         }
512
513       if (!func_ptr)
514         FreeLibrary(module);
515     }
516
517   return func_ptr;
518 }
519
520 static int
521 init_physmem_w32skrnl(struct physmem *physmem, struct pci_access *a)
522 {
523   HMODULE w32skrnl;
524   LPVOID (WINAPI *GetThunkBuff)(VOID);
525   OSVERSIONINFOA version;
526   LPVOID buf_ptr;
527
528   a->debug("resolving DPMI function via GetThunkBuff() function from w32skrnl.dll...");
529   w32skrnl = GetModuleHandleA("w32skrnl.dll");
530   if (!w32skrnl)
531     {
532       a->debug("failed: library not present.");
533       errno = ENOENT;
534       return 0;
535     }
536
537   GetThunkBuff = (LPVOID)GetProcAddress(w32skrnl, "_GetThunkBuff@0");
538   if (!GetThunkBuff)
539     {
540       a->debug("failed: symbol not found.");
541       errno = ENOENT;
542       return 0;
543     }
544
545   version.dwOSVersionInfoSize = sizeof(version);
546   if (!GetVersionExA(&version))
547     {
548       a->debug("failed: cannot detect version.");
549       errno = EINVAL;
550       return 0;
551     }
552
553   /* Versions before 1.1 (1.1.88) are not supported. */
554   if (version.dwMajorVersion < 1 || (version.dwMajorVersion == 1 && version.dwMinorVersion < 1))
555     {
556       a->debug("failed: found old incompatible version.");
557       errno = ENOENT;
558       return 0;
559     }
560
561   if (!win32_get_physmem_offset(&physmem->base_addr_offset))
562     {
563       a->debug("failed: cannot retrieve physical address offset: %s.", win32_strerror(GetLastError()));
564       errno = EINVAL;
565       return 0;
566     }
567
568   buf_ptr = GetThunkBuff();
569   if (!buf_ptr)
570     {
571       a->debug("failed: cannot retrieve DPMI function pointer.");
572       errno = EINVAL;
573       return 0;
574     }
575
576   /*
577    * Versions 1.1 (1.1.88) - 1.15 (1.15.103) have DPMI function at offset 0xa0.
578    * Versions 1.15a (1.15.111) - 1.30c (1.30.172) have DPMI function at offset 0xa4.
579    */
580   if (version.dwMajorVersion > 1 ||
581       (version.dwMajorVersion == 1 && version.dwMinorVersion > 15) ||
582       (version.dwMajorVersion == 1 && version.dwMinorVersion == 15 && version.dwBuildNumber >= 111))
583     physmem->w32skrnl_dpmi_lcall_ptr = (LPVOID)((BYTE *)buf_ptr + 0xa4);
584   else
585     physmem->w32skrnl_dpmi_lcall_ptr = (LPVOID)((BYTE *)buf_ptr + 0xa0);
586
587   a->debug("success.");
588   return 1;
589 }
590
591 static int
592 init_physmem_vxdcall(struct physmem *physmem, struct pci_access *a)
593 {
594   HMODULE kernel32;
595   BOOL success;
596   DWORD addr;
597
598   a->debug("resolving VxDCall2() function from kernel32.dll...");
599   kernel32 = GetModuleHandleA("kernel32.dll");
600   if (!kernel32)
601     {
602       a->debug("failed: library not present.");
603       errno = ENOENT;
604       return 0;
605     }
606
607   /*
608    * New Windows versions do not export VxDCall2 symbol by name anymore,
609    * so try also locating this symbol by its ordinal number, which is 3.
610    * Old Windows versions prevents using GetProcAddress() for locating
611    * kernel32.dll symbol by ordinal number, so use our own custom function.
612    * When locating via ordinal number, check that this ordinal number does
613    * not have any name assigned (to ensure that it is really VxDCall2).
614    */
615   physmem->VxDCall2 = (LPVOID)GetProcAddress(kernel32, "VxDCall2");
616   if (!physmem->VxDCall2)
617     physmem->VxDCall2 = (LPVOID)win32_get_proc_address_by_ordinal(kernel32, 3, TRUE);
618
619   if (!physmem->VxDCall2)
620     {
621       a->debug("failed: symbol not found.");
622       errno = ENOENT;
623       return 0;
624     }
625
626   /*
627    * Wine implementation of VxDCall2() does not support physical address
628    * mapping but returns success with virtual address same as passed physical
629    * address. Detect this broken wine behavior by trying to map zero address
630    * of zero range. Broken wine implementation returns NULL pointer. This
631    * prevents accessing unmapped memory or dereferencing NULL pointer.
632    */
633   success = vdxcall_physical_address_mapping(physmem, 0x0, 0x0, &addr);
634   if (success && addr == 0x0)
635     {
636       a->debug("failed: physical address mapping via VxDCall2() is broken.");
637       physmem->VxDCall2 = NULL;
638       errno = EINVAL;
639       return 0;
640     }
641   else if (!success)
642     {
643       a->debug("failed: physical address mapping via VxDCall2() is unsupported.");
644       physmem->VxDCall2 = NULL;
645       errno = ENOENT;
646       return 0;
647     }
648
649   /* Retrieve base address - offset for all addresses returned by VxDCall2(). */
650   if (!win32_get_physmem_offset(&physmem->base_addr_offset))
651     {
652       a->debug("failed: cannot retrieve physical address offset: %s.", win32_strerror(GetLastError()));
653       physmem->VxDCall2 = NULL;
654       errno = EINVAL;
655       return 0;
656     }
657
658   a->debug("success.");
659   return 1;
660 }
661
662 #endif
663
664 static int
665 init_physmem_ntdll(struct physmem *physmem, struct pci_access *a, const char *filename, int w)
666 {
667   wchar_t *wide_filename;
668   UNICODE_STRING unicode_filename;
669   OBJECT_ATTRIBUTES attributes;
670   UINT prev_error_mode;
671   NTSTATUS status;
672   int len;
673
674   a->debug("resolving section functions from ntdll.dll...");
675   prev_error_mode = win32_change_error_mode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
676   physmem->ntdll = LoadLibrary(TEXT("ntdll.dll"));
677   win32_change_error_mode(prev_error_mode);
678   if (!physmem->ntdll)
679     {
680       a->debug("failed: cannot open ntdll.dll library: %s.", win32_strerror(GetLastError()));
681       errno = ENOENT;
682       return 0;
683     }
684
685   physmem->RtlNtStatusToDosError = (LPVOID)GetProcAddress(physmem->ntdll, "RtlNtStatusToDosError");
686
687   physmem->NtOpenSection = (LPVOID)GetProcAddress(physmem->ntdll, "NtOpenSection");
688   if (!physmem->NtOpenSection)
689     {
690       a->debug("failed: function NtOpenSection() not found.");
691       FreeLibrary(physmem->ntdll);
692       physmem->ntdll = NULL;
693       errno = ENOENT;
694       return 0;
695     }
696
697   physmem->NtMapViewOfSection = (LPVOID)GetProcAddress(physmem->ntdll, "NtMapViewOfSection");
698   if (!physmem->NtMapViewOfSection)
699     {
700       a->debug("failed: function NtMapViewOfSection() not found.");
701       FreeLibrary(physmem->ntdll);
702       physmem->ntdll = NULL;
703       errno = ENOENT;
704       return 0;
705     }
706
707   physmem->NtUnmapViewOfSection = (LPVOID)GetProcAddress(physmem->ntdll, "NtUnmapViewOfSection");
708   if (!physmem->NtUnmapViewOfSection)
709     {
710       a->debug("failed: function NtUnmapViewOfSection() not found.");
711       FreeLibrary(physmem->ntdll);
712       physmem->ntdll = NULL;
713       errno = ENOENT;
714       return 0;
715     }
716
717   a->debug("success.");
718
719   /*
720    * Note: It is not possible to use WinAPI function OpenFileMappingA() because
721    * it takes path relative to the NT base path \\Sessions\\X\\BaseNamedObjects\\
722    * and so it does not support to open sections outside that NT directory.
723    * NtOpenSection() does not have this restriction and supports specifying any
724    * path, including path in absolute format. Unfortunately NtOpenSection()
725    * takes path in UNICODE_STRING structure, unlike OpenFileMappingA() which
726    * takes path in 8-bit char*. So first it is needed to do conversion from
727    * char* string to wchar_t* string via function MultiByteToWideChar() and
728    * then fill UNICODE_STRING structure from that wchar_t* string via function
729    * RtlInitUnicodeString().
730    */
731
732   len = MultiByteToWideChar(CP_ACP, 0, filename, -1, NULL, 0);
733   if (len <= 0)
734     {
735       a->debug("Option devmem.path '%s' is invalid multibyte string.", filename);
736       FreeLibrary(physmem->ntdll);
737       physmem->ntdll = NULL;
738       errno = EINVAL;
739       return 0;
740     }
741
742   wide_filename = pci_malloc(a, len * sizeof(wchar_t));
743   len = MultiByteToWideChar(CP_ACP, 0, filename, -1, wide_filename, len);
744   if (len <= 0)
745     {
746       a->debug("Option devmem.path '%s' is invalid multibyte string.", filename);
747       pci_mfree(wide_filename);
748       FreeLibrary(physmem->ntdll);
749       physmem->ntdll = NULL;
750       errno = EINVAL;
751       return 0;
752     }
753
754   RtlInitUnicodeString(&unicode_filename, wide_filename);
755   InitializeObjectAttributes(&attributes, &unicode_filename, OBJ_CASE_INSENSITIVE, NULL, NULL);
756
757   a->debug("trying to open NT Section %s in %s mode...", filename, w ? "read/write" : "read-only");
758   physmem->section_handle = INVALID_HANDLE_VALUE;
759   status = physmem->NtOpenSection(&physmem->section_handle, SECTION_MAP_READ | (w ? SECTION_MAP_WRITE : 0), &attributes);
760
761   pci_mfree(wide_filename);
762
763   if (status < 0 || physmem->section_handle == INVALID_HANDLE_VALUE)
764     {
765       FreeLibrary(physmem->ntdll);
766       physmem->ntdll = NULL;
767       physmem->section_handle = INVALID_HANDLE_VALUE;
768       if (status == 0)
769         a->debug("failed.");
770       else if (physmem->RtlNtStatusToDosError)
771         a->debug("failed: %s (0x%lx).", win32_strerror(physmem->RtlNtStatusToDosError(status)), status);
772       else
773         a->debug("failed: 0x%lx.", status);
774       switch (status)
775         {
776         case STATUS_INVALID_PARAMETER: /* SectionHandle or ObjectAttributes parameter is invalid */
777           errno = EINVAL;
778           break;
779         case STATUS_OBJECT_NAME_NOT_FOUND: /* Section name in ObjectAttributes.ObjectName does not exist */
780           errno = ENOENT;
781           break;
782         case STATUS_ACCESS_DENIED: /* No permission to access Section name in ObjectAttributes.ObjectName */
783           errno = EACCES;
784           break;
785         default: /* Other unspecified error */
786           errno = EINVAL;
787           break;
788         }
789       return 0;
790     }
791
792   a->debug("success.");
793   return 1;
794 }
795
796 void
797 physmem_init_config(struct pci_access *a)
798 {
799   pci_define_param(a, "devmem.path", PCI_PATH_DEVMEM_DEVICE, "NT path to the PhysicalMemory NT Section"
800 #if defined(__i386__) || defined(_M_IX86)
801     " or \"vxdcall\" or \"w32skrnl\""
802 #endif
803   );
804 }
805
806 int
807 physmem_access(struct pci_access *a, int w)
808 {
809   struct physmem *physmem = physmem_open(a, w);
810   if (!physmem)
811     return -1;
812   physmem_close(physmem);
813   return 0;
814 }
815
816 struct physmem *
817 physmem_open(struct pci_access *a, int w)
818 {
819   const char *devmem = pci_get_param(a, "devmem.path");
820 #if defined(__i386__) || defined(_M_IX86)
821   int force_vxdcall = strcmp(devmem, "vxdcall") == 0;
822   int force_w32skrnl = strcmp(devmem, "w32skrnl") == 0;
823 #endif
824   struct physmem *physmem = pci_malloc(a, sizeof(*physmem));
825
826   memset(physmem, 0, sizeof(*physmem));
827   physmem->section_handle = INVALID_HANDLE_VALUE;
828
829   errno = ENOENT;
830
831   if (
832 #if defined(__i386__) || defined(_M_IX86)
833       !force_vxdcall && !force_w32skrnl &&
834 #endif
835       init_physmem_ntdll(physmem, a, devmem, w))
836     return physmem;
837
838 #if defined(__i386__) || defined(_M_IX86)
839   if (!force_w32skrnl && init_physmem_vxdcall(physmem, a))
840     return physmem;
841
842   if (!force_vxdcall && init_physmem_w32skrnl(physmem, a))
843     return physmem;
844 #endif
845
846   a->debug("no windows method for physical memory access.");
847   pci_mfree(physmem);
848   return NULL;
849 }
850
851 void
852 physmem_close(struct physmem *physmem)
853 {
854   if (physmem->section_handle != INVALID_HANDLE_VALUE)
855     CloseHandle(physmem->section_handle);
856   if (physmem->ntdll)
857     FreeLibrary(physmem->ntdll);
858   pci_mfree(physmem);
859 }
860
861 long
862 physmem_get_pagesize(struct physmem *physmem UNUSED)
863 {
864   SYSTEM_INFO system_info;
865   system_info.dwPageSize = 0;
866   GetSystemInfo(&system_info);
867   return system_info.dwPageSize;
868 }
869
870 void *
871 physmem_map(struct physmem *physmem, u64 addr, size_t length, int w)
872 {
873   LARGE_INTEGER section_offset;
874   NTSTATUS status;
875   SIZE_T view_size;
876   VOID *ptr;
877
878   if (physmem->section_handle != INVALID_HANDLE_VALUE)
879     {
880       /*
881        * Note: Do not use WinAPI function MapViewOfFile() because it makes memory
882        * mapping available also for all child processes that are spawned in the
883        * future. NtMapViewOfSection() allows to specify ViewUnmap parameter which
884        * creates mapping just for this process and not for future child processes.
885        * For security reasons we do not want this physical address mapping to be
886        * present also in future spawned processes.
887        */
888       ptr = NULL;
889       section_offset.QuadPart = addr;
890       view_size = length;
891       status = physmem->NtMapViewOfSection(physmem->section_handle, GetCurrentProcess(), &ptr, 0, 0, &section_offset, &view_size, ViewUnmap, 0, w ? PAGE_READWRITE : PAGE_READONLY);
892       if (status < 0)
893         {
894           switch (status)
895             {
896             case STATUS_INVALID_HANDLE: /* Invalid SectionHandle (physmem->section_handle) */
897             case STATUS_INVALID_PARAMETER_3: /* Invalid BaseAddress parameter (&ptr) */
898             case STATUS_CONFLICTING_ADDRESSES: /* Invalid value of BaseAddress pointer (ptr) */
899             case STATUS_MAPPED_ALIGNMENT: /* Invalid value of BaseAddress pointer (ptr) or SectionOffset (section_offset) */
900             case STATUS_INVALID_PARAMETER_4: /* Invalid ZeroBits parameter (0) */
901             case STATUS_INVALID_PARAMETER_5: /* Invalid CommitSize parameter (0) */
902             case STATUS_INVALID_PARAMETER_8: /* Invalid InheritDisposition parameter (ViewUnmap) */
903             case STATUS_INVALID_PARAMETER_9: /* Invalid AllocationType parameter (0) */
904             case STATUS_SECTION_PROTECTION: /* Invalid Protect parameter (based on w) */
905             case STATUS_INVALID_PAGE_PROTECTION: /* Invalid Protect parameter (based on w) */
906               errno = EINVAL;
907               break;
908             case STATUS_INVALID_VIEW_SIZE: /* Invalid SectionOffset / ViewSize range (section_offset, view_size) */
909               errno = ENXIO;
910               break;
911             case STATUS_INSUFFICIENT_RESOURCES: /* Quota limit exceeded */
912             case STATUS_NO_MEMORY: /* Memory limit exceeded */
913               errno = ENOMEM;
914               break;
915             case STATUS_ACCESS_DENIED: /* No permission to create mapping */
916               errno = EPERM;
917               break;
918             default: /* Other unspecified error */
919               errno = EACCES;
920               break;
921             }
922           return (void *)-1;
923         }
924
925       return ptr;
926     }
927 #if defined(__i386__) || defined(_M_IX86)
928   else if (physmem->VxDCall2 || physmem->w32skrnl_dpmi_lcall_ptr)
929     {
930       BOOL success;
931       DWORD virt;
932
933       /*
934        * These two methods support mapping only the first 4 GB of physical memory
935        * and mapped memory is always read/write. There is no way to create
936        * read-only mapping, so argument "w" is ignored.
937        */
938       if (addr >= 0xffffffffUL || addr + length > 0xffffffffUL)
939         {
940           errno = EOVERFLOW;
941           return (void *)-1;
942         }
943
944       if (physmem->VxDCall2)
945         success = vdxcall_physical_address_mapping(physmem, (DWORD)addr, length, &virt);
946       else
947         success = w32skrnl_physical_address_mapping(physmem, (DWORD)addr, length, &virt);
948
949       /* Both above functions set errno on failure. */
950       if (!success)
951         return (void *)-1;
952
953       /* Virtual address from our view is calculated from the base offset. */
954       ptr = (VOID *)(virt - physmem->base_addr_offset);
955       return ptr;
956     }
957 #endif
958
959
960   /* invalid physmem parameter */
961   errno = EBADF;
962   return (void *)-1;
963 }
964
965 int
966 physmem_unmap(struct physmem *physmem, void *ptr, size_t length)
967 {
968   long pagesize = physmem_get_pagesize(physmem);
969   MEMORY_BASIC_INFORMATION info;
970   NTSTATUS status;
971
972   if (physmem->section_handle != INVALID_HANDLE_VALUE)
973     {
974       /*
975        * NtUnmapViewOfSection() unmaps entire memory range previously mapped by
976        * NtMapViewOfSection(). The specified ptr (BaseAddress) does not have to
977        * point to the beginning of the mapped memory range.
978        *
979        * So verify that the ptr argument is the beginning of the mapped range
980        * and length argument is the length of mapped range.
981        */
982
983       if (VirtualQuery(ptr, &info, sizeof(info)) != sizeof(info))
984         {
985           errno = EINVAL;
986           return -1;
987         }
988
989       /* RegionSize is already page aligned, but length does not have to be. */
990       if (info.AllocationBase != ptr || info.RegionSize != ((length + pagesize-1) & ~(pagesize-1)))
991         {
992           errno = EINVAL;
993           return -1;
994         }
995
996       status = physmem->NtUnmapViewOfSection(GetCurrentProcess(), ptr);
997       if (status < 0)
998         {
999           switch (status)
1000             {
1001             case STATUS_NOT_MAPPED_VIEW: /* BaseAddress parameter (ptr) not mapped */
1002               errno = EINVAL;
1003               break;
1004             case STATUS_ACCESS_DENIED: /* No permission to unmap BaseAddress (ptr) */
1005               errno = EPERM;
1006               break;
1007             default: /* Other unspecified error */
1008               errno = EACCES;
1009               break;
1010             }
1011           return -1;
1012         }
1013
1014       return 0;
1015     }
1016 #if defined(__i386__) || defined(_M_IX86)
1017   else if (physmem->VxDCall2 || physmem->w32skrnl_dpmi_lcall_ptr)
1018     {
1019       /* There is no way to unmap physical memory mapped by these methods. */
1020       errno = ENOSYS;
1021       return -1;
1022     }
1023 #endif
1024
1025   /* invalid physmem parameter */
1026   errno = EBADF;
1027   return -1;
1028 }