2 * The PCI Library -- Physical memory mapping for Windows systems
4 * Copyright (c) 2023 Pali Rohár <pali@kernel.org>
6 * Can be freely distributed and used under the terms of the GNU GPL v2+
8 * SPDX-License-Identifier: GPL-2.0-or-later
18 #include "win32-helpers.h"
23 #ifndef STATUS_INVALID_HANDLE
24 #define STATUS_INVALID_HANDLE ((NTSTATUS)0xC0000008)
26 #ifndef STATUS_INVALID_PARAMETER
27 #define STATUS_INVALID_PARAMETER ((NTSTATUS)0xC000000D)
29 #ifndef STATUS_CONFLICTING_ADDRESSES
30 #define STATUS_CONFLICTING_ADDRESSES ((NTSTATUS)0xC0000018)
32 #ifndef STATUS_NOT_MAPPED_VIEW
33 #define STATUS_NOT_MAPPED_VIEW ((NTSTATUS)0xC0000019)
35 #ifndef STATUS_INVALID_VIEW_SIZE
36 #define STATUS_INVALID_VIEW_SIZE ((NTSTATUS)0xC000001F)
38 #ifndef STATUS_ACCESS_DENIED
39 #define STATUS_ACCESS_DENIED ((NTSTATUS)0xC0000022)
41 #ifndef STATUS_OBJECT_NAME_NOT_FOUND
42 #define STATUS_OBJECT_NAME_NOT_FOUND ((NTSTATUS)0xC0000034)
44 #ifndef STATUS_INVALID_PAGE_PROTECTION
45 #define STATUS_INVALID_PAGE_PROTECTION ((NTSTATUS)0xC0000045)
47 #ifndef STATUS_SECTION_PROTECTION
48 #define STATUS_SECTION_PROTECTION ((NTSTATUS)0xC000004E)
50 #ifndef STATUS_INSUFFICIENT_RESOURCES
51 #define STATUS_INSUFFICIENT_RESOURCES ((NTSTATUS)0xC000009A)
53 #ifndef STATUS_INVALID_PARAMETER_3
54 #define STATUS_INVALID_PARAMETER_3 ((NTSTATUS)0xC00000F1)
56 #ifndef STATUS_INVALID_PARAMETER_4
57 #define STATUS_INVALID_PARAMETER_4 ((NTSTATUS)0xC00000F2)
59 #ifndef STATUS_INVALID_PARAMETER_5
60 #define STATUS_INVALID_PARAMETER_5 ((NTSTATUS)0xC00000F3)
62 #ifndef STATUS_INVALID_PARAMETER_8
63 #define STATUS_INVALID_PARAMETER_8 ((NTSTATUS)0xC00000F6)
65 #ifndef STATUS_INVALID_PARAMETER_9
66 #define STATUS_INVALID_PARAMETER_9 ((NTSTATUS)0xC00000F7)
68 #ifndef STATUS_MAPPED_ALIGNMENT
69 #define STATUS_MAPPED_ALIGNMENT ((NTSTATUS)0xC0000220)
72 #ifndef OBJ_CASE_INSENSITIVE
73 #define OBJ_CASE_INSENSITIVE 0x00000040L
76 #ifndef SECTION_INHERIT
77 #define SECTION_INHERIT ULONG
80 #define ViewUnmap ((SECTION_INHERIT)2)
83 #ifndef IMAGE_NT_OPTIONAL_HDR_MAGIC
85 #define IMAGE_NT_OPTIONAL_HDR_MAGIC 0x20b
87 #define IMAGE_NT_OPTIONAL_HDR_MAGIC 0x10b
95 #if _WIN32_WINNT < 0x0500
96 typedef ULONG ULONG_PTR;
97 typedef ULONG_PTR SIZE_T, *PSIZE_T;
100 #ifndef __UNICODE_STRING_DEFINED
101 #define __UNICODE_STRING_DEFINED
102 typedef struct _UNICODE_STRING {
104 USHORT MaximumLength;
106 } UNICODE_STRING, *PUNICODE_STRING;
109 #ifndef __OBJECT_ATTRIBUTES_DEFINED
110 #define __OBJECT_ATTRIBUTES_DEFINED
111 typedef struct _OBJECT_ATTRIBUTES {
113 HANDLE RootDirectory;
114 PUNICODE_STRING ObjectName;
116 PVOID SecurityDescriptor;
117 PVOID SecurityQualityOfService;
118 } OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES;
121 #ifndef InitializeObjectAttributes
122 #define InitializeObjectAttributes(p, n, a, r, s) \
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; \
133 #ifndef RtlInitUnicodeString
134 #define RtlInitUnicodeString(d, s) \
136 (d)->Length = wcslen(s) * sizeof(WCHAR); \
137 (d)->MaximumLength = (d)->Length + sizeof(WCHAR); \
138 (d)->Buffer = (PWCHAR)(s); \
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
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;
161 #if defined(__i386__) || defined(_M_IX86)
164 w32skrnl_physical_address_mapping(struct physmem *physmem, DWORD phys_addr, DWORD size, DWORD *virt_addr)
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;
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.
180 if (phys_addr < 1*1024*1024UL)
182 if ((u64)phys_addr + size > 1*1024*1024UL)
188 *virt_addr = phys_addr;
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.
198 #if defined(__GNUC__)
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)
207 #elif defined(_MSC_VER)
213 mov eax, DPMI_PHYSICAL_ADDRESS_MAPPING
216 mov edx, [edx]physmem.w32skrnl_dpmi_lcall_ptr
223 #error "Unsupported compiler"
227 * Windows does not provide any error code when this function call fails.
228 * So set errno just to the generic EACCES value.
236 *virt_addr = ((address_hi & 0xffff) << 16) | (address_lo & 0xffff);
240 #if defined(__GNUC__) && ((__GNUC__ == 4 && __GNUC_MINOR__ >= 8) || (__GNUC__ > 4)) && (__GNUC__ <= 11)
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.
247 __attribute__((optimize("O0")))
250 vdxcall_physical_address_mapping(struct physmem *physmem, DWORD phys_addr, DWORD size, DWORD *virt_addr)
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;
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.
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.
273 * Pseudocode with stdcall calling convention of that function looks like:
277 * VxDCall2(VWIN32_Int31Dispatch, DPMI_PHYSICAL_ADDRESS_MAPPING, address_lo)
279 * address_hi = BX (if not failed)
280 * address_lo = CX (if not failed)
283 #if defined(__GNUC__)
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. */
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).
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)",
304 "mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm6",
307 "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7",
311 #elif defined(_MSC_VER)
317 push DPMI_PHYSICAL_ADDRESS_MAPPING
318 push VWIN32_Int31Dispatch
321 call [eax]physmem.VxDCall2
327 #error "Unsupported compiler"
331 * Windows does not provide any error code when this function call fails.
332 * So set errno just to the generic EACCES value.
340 *virt_addr = ((address_hi & 0xffff) << 16) | (address_lo & 0xffff);
345 win32_get_physmem_offset(DWORD *offset)
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.
358 #if defined(__GNUC__)
359 asm ("movw %%ds, %w0" : "=rm" (DSsel));
360 #elif defined(_MSC_VER)
361 __asm { mov DSsel, ds }
363 #error "Unsupported compiler"
366 if (!GetThreadSelectorEntry(GetCurrentThread(), DSsel, &DSentry))
369 *offset = DSentry.BaseLow | (DSentry.HighWord.Bytes.BaseMid << 0x10) | (DSentry.HighWord.Bytes.BaseHi << 0x18);
374 win32_get_baseaddr_from_hmodule(HMODULE module)
376 WORD (WINAPI *ImteFromHModule)(HMODULE);
377 BYTE *(WINAPI *BaseAddrFromImte)(WORD);
381 if ((GetVersion() & 0xC0000000) != 0x80000000)
382 return (BYTE *)module;
384 w32skrnl = GetModuleHandleA("w32skrnl.dll");
388 ImteFromHModule = (LPVOID)GetProcAddress(w32skrnl, "_ImteFromHModule@4");
389 BaseAddrFromImte = (LPVOID)GetProcAddress(w32skrnl, "_BaseAddrFromImte@4");
390 if (!ImteFromHModule || !BaseAddrFromImte)
393 imte = ImteFromHModule(module);
397 return BaseAddrFromImte(imte);
401 win32_get_proc_address_by_ordinal(HMODULE module, DWORD ordinal, BOOL must_be_without_name)
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;
411 DWORD names_count, i;
413 UINT prev_error_mode;
414 char module_name[MAX_PATH];
415 DWORD module_name_len;
420 baseaddr = win32_get_baseaddr_from_hmodule(module);
424 dos_header = (IMAGE_DOS_HEADER *)baseaddr;
425 if (dos_header->e_magic != IMAGE_DOS_SIGNATURE)
428 nt_header = (IMAGE_NT_HEADERS *)((BYTE *)dos_header + dos_header->e_lfanew);
429 if (nt_header->Signature != IMAGE_NT_SIGNATURE)
432 if (nt_header->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC)
435 if (nt_header->OptionalHeader.NumberOfRvaAndSizes <= IMAGE_DIRECTORY_ENTRY_EXPORT)
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;
441 if (!export_dir_offset || !export_dir_size)
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);
449 if (ordinal < base_ordinal || ordinal - base_ordinal > func_count)
452 if (must_be_without_name)
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++)
459 if (names_idxs[i] == ordinal - base_ordinal)
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)
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).
474 export_name = strrchr((char *)func_ptr, '.');
479 module_name_len = export_name - 1 - (char *)func_ptr;
480 if (module_name_len >= sizeof(module_name))
483 memcpy(module_name, func_ptr, module_name_len);
484 module_name[module_name_len] = 0;
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);
495 if (*export_name == '#')
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)
506 func_ptr = win32_get_proc_address_by_ordinal(module, ordinal, FALSE);
510 func_ptr = GetProcAddress(module, export_name);
521 init_physmem_w32skrnl(struct physmem *physmem, struct pci_access *a)
524 LPVOID (WINAPI *GetThunkBuff)(VOID);
525 OSVERSIONINFOA version;
528 a->debug("resolving DPMI function via GetThunkBuff() function from w32skrnl.dll...");
529 w32skrnl = GetModuleHandleA("w32skrnl.dll");
532 a->debug("failed: library not present.");
537 GetThunkBuff = (LPVOID)GetProcAddress(w32skrnl, "_GetThunkBuff@0");
540 a->debug("failed: symbol not found.");
545 version.dwOSVersionInfoSize = sizeof(version);
546 if (!GetVersionExA(&version))
548 a->debug("failed: cannot detect version.");
553 /* Versions before 1.1 (1.1.88) are not supported. */
554 if (version.dwMajorVersion < 1 || (version.dwMajorVersion == 1 && version.dwMinorVersion < 1))
556 a->debug("failed: found old incompatible version.");
561 if (!win32_get_physmem_offset(&physmem->base_addr_offset))
563 a->debug("failed: cannot retrieve physical address offset: %s.", win32_strerror(GetLastError()));
568 buf_ptr = GetThunkBuff();
571 a->debug("failed: cannot retrieve DPMI function pointer.");
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.
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);
585 physmem->w32skrnl_dpmi_lcall_ptr = (LPVOID)((BYTE *)buf_ptr + 0xa0);
587 a->debug("success.");
592 init_physmem_vxdcall(struct physmem *physmem, struct pci_access *a)
598 a->debug("resolving VxDCall2() function from kernel32.dll...");
599 kernel32 = GetModuleHandleA("kernel32.dll");
602 a->debug("failed: library not present.");
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).
615 physmem->VxDCall2 = (LPVOID)GetProcAddress(kernel32, "VxDCall2");
616 if (!physmem->VxDCall2)
617 physmem->VxDCall2 = (LPVOID)win32_get_proc_address_by_ordinal(kernel32, 3, TRUE);
619 if (!physmem->VxDCall2)
621 a->debug("failed: symbol not found.");
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.
633 success = vdxcall_physical_address_mapping(physmem, 0x0, 0x0, &addr);
634 if (success && addr == 0x0)
636 a->debug("failed: physical address mapping via VxDCall2() is broken.");
637 physmem->VxDCall2 = NULL;
643 a->debug("failed: physical address mapping via VxDCall2() is unsupported.");
644 physmem->VxDCall2 = NULL;
649 /* Retrieve base address - offset for all addresses returned by VxDCall2(). */
650 if (!win32_get_physmem_offset(&physmem->base_addr_offset))
652 a->debug("failed: cannot retrieve physical address offset: %s.", win32_strerror(GetLastError()));
653 physmem->VxDCall2 = NULL;
658 a->debug("success.");
665 init_physmem_ntdll(struct physmem *physmem, struct pci_access *a, const char *filename, int w)
667 wchar_t *wide_filename;
668 UNICODE_STRING unicode_filename;
669 OBJECT_ATTRIBUTES attributes;
670 UINT prev_error_mode;
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);
680 a->debug("failed: cannot open ntdll.dll library: %s.", win32_strerror(GetLastError()));
685 physmem->RtlNtStatusToDosError = (LPVOID)GetProcAddress(physmem->ntdll, "RtlNtStatusToDosError");
687 physmem->NtOpenSection = (LPVOID)GetProcAddress(physmem->ntdll, "NtOpenSection");
688 if (!physmem->NtOpenSection)
690 a->debug("failed: function NtOpenSection() not found.");
691 FreeLibrary(physmem->ntdll);
692 physmem->ntdll = NULL;
697 physmem->NtMapViewOfSection = (LPVOID)GetProcAddress(physmem->ntdll, "NtMapViewOfSection");
698 if (!physmem->NtMapViewOfSection)
700 a->debug("failed: function NtMapViewOfSection() not found.");
701 FreeLibrary(physmem->ntdll);
702 physmem->ntdll = NULL;
707 physmem->NtUnmapViewOfSection = (LPVOID)GetProcAddress(physmem->ntdll, "NtUnmapViewOfSection");
708 if (!physmem->NtUnmapViewOfSection)
710 a->debug("failed: function NtUnmapViewOfSection() not found.");
711 FreeLibrary(physmem->ntdll);
712 physmem->ntdll = NULL;
717 a->debug("success.");
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().
732 len = MultiByteToWideChar(CP_ACP, 0, filename, -1, NULL, 0);
735 a->debug("Option devmem.path '%s' is invalid multibyte string.", filename);
736 FreeLibrary(physmem->ntdll);
737 physmem->ntdll = NULL;
742 wide_filename = pci_malloc(a, len * sizeof(wchar_t));
743 len = MultiByteToWideChar(CP_ACP, 0, filename, -1, wide_filename, len);
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;
754 RtlInitUnicodeString(&unicode_filename, wide_filename);
755 InitializeObjectAttributes(&attributes, &unicode_filename, OBJ_CASE_INSENSITIVE, NULL, NULL);
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);
761 pci_mfree(wide_filename);
763 if (status < 0 || physmem->section_handle == INVALID_HANDLE_VALUE)
765 FreeLibrary(physmem->ntdll);
766 physmem->ntdll = NULL;
767 physmem->section_handle = INVALID_HANDLE_VALUE;
770 else if (physmem->RtlNtStatusToDosError)
771 a->debug("failed: %s (0x%lx).", win32_strerror(physmem->RtlNtStatusToDosError(status)), status);
773 a->debug("failed: 0x%lx.", status);
776 case STATUS_INVALID_PARAMETER: /* SectionHandle or ObjectAttributes parameter is invalid */
779 case STATUS_OBJECT_NAME_NOT_FOUND: /* Section name in ObjectAttributes.ObjectName does not exist */
782 case STATUS_ACCESS_DENIED: /* No permission to access Section name in ObjectAttributes.ObjectName */
785 default: /* Other unspecified error */
792 a->debug("success.");
797 physmem_init_config(struct pci_access *a)
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\""
807 physmem_access(struct pci_access *a, int w)
809 struct physmem *physmem = physmem_open(a, w);
812 physmem_close(physmem);
817 physmem_open(struct pci_access *a, int w)
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;
824 struct physmem *physmem = pci_malloc(a, sizeof(*physmem));
826 memset(physmem, 0, sizeof(*physmem));
827 physmem->section_handle = INVALID_HANDLE_VALUE;
832 #if defined(__i386__) || defined(_M_IX86)
833 !force_vxdcall && !force_w32skrnl &&
835 init_physmem_ntdll(physmem, a, devmem, w))
838 #if defined(__i386__) || defined(_M_IX86)
839 if (!force_w32skrnl && init_physmem_vxdcall(physmem, a))
842 if (!force_vxdcall && init_physmem_w32skrnl(physmem, a))
846 a->debug("no windows method for physical memory access.");
852 physmem_close(struct physmem *physmem)
854 if (physmem->section_handle != INVALID_HANDLE_VALUE)
855 CloseHandle(physmem->section_handle);
857 FreeLibrary(physmem->ntdll);
862 physmem_get_pagesize(struct physmem *physmem UNUSED)
864 SYSTEM_INFO system_info;
865 system_info.dwPageSize = 0;
866 GetSystemInfo(&system_info);
867 return system_info.dwPageSize;
871 physmem_map(struct physmem *physmem, u64 addr, size_t length, int w)
873 LARGE_INTEGER section_offset;
878 if (physmem->section_handle != INVALID_HANDLE_VALUE)
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.
889 section_offset.QuadPart = addr;
891 status = physmem->NtMapViewOfSection(physmem->section_handle, GetCurrentProcess(), &ptr, 0, 0, §ion_offset, &view_size, ViewUnmap, 0, w ? PAGE_READWRITE : PAGE_READONLY);
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) */
908 case STATUS_INVALID_VIEW_SIZE: /* Invalid SectionOffset / ViewSize range (section_offset, view_size) */
911 case STATUS_INSUFFICIENT_RESOURCES: /* Quota limit exceeded */
912 case STATUS_NO_MEMORY: /* Memory limit exceeded */
915 case STATUS_ACCESS_DENIED: /* No permission to create mapping */
918 default: /* Other unspecified error */
927 #if defined(__i386__) || defined(_M_IX86)
928 else if (physmem->VxDCall2 || physmem->w32skrnl_dpmi_lcall_ptr)
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.
938 if (addr >= 0xffffffffUL || addr + length > 0xffffffffUL)
944 if (physmem->VxDCall2)
945 success = vdxcall_physical_address_mapping(physmem, (DWORD)addr, length, &virt);
947 success = w32skrnl_physical_address_mapping(physmem, (DWORD)addr, length, &virt);
949 /* Both above functions set errno on failure. */
953 /* Virtual address from our view is calculated from the base offset. */
954 ptr = (VOID *)(virt - physmem->base_addr_offset);
960 /* invalid physmem parameter */
966 physmem_unmap(struct physmem *physmem, void *ptr, size_t length)
968 long pagesize = physmem_get_pagesize(physmem);
969 MEMORY_BASIC_INFORMATION info;
972 if (physmem->section_handle != INVALID_HANDLE_VALUE)
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.
979 * So verify that the ptr argument is the beginning of the mapped range
980 * and length argument is the length of mapped range.
983 if (VirtualQuery(ptr, &info, sizeof(info)) != sizeof(info))
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)))
996 status = physmem->NtUnmapViewOfSection(GetCurrentProcess(), ptr);
1001 case STATUS_NOT_MAPPED_VIEW: /* BaseAddress parameter (ptr) not mapped */
1004 case STATUS_ACCESS_DENIED: /* No permission to unmap BaseAddress (ptr) */
1007 default: /* Other unspecified error */
1016 #if defined(__i386__) || defined(_M_IX86)
1017 else if (physmem->VxDCall2 || physmem->w32skrnl_dpmi_lcall_ptr)
1019 /* There is no way to unmap physical memory mapped by these methods. */
1025 /* invalid physmem parameter */