https://bugs.winehq.org/show_bug.cgi?id=48986
--- Comment #1 from Anastasius Focht focht@gmx.net --- Hello folks,
some tidbits on why this API is called. Currently Wine doesn't implement the concept of Windows kernel IRQL (priority level) by design.
IRQL is a pretty important topic for all code that executes in kernel space. Kernel drivers need to follow general rules when/what code can be called, depending on current IRQL, usage of synchronization primitives etc. I'm not going repeat common knowledge here since you can read a lot about this topic in various online resources.
Windows x86_64 stores the current IRQL in control register CR8. A read of CR8 register equals to 'KeGetCurrentIrql()'.
With Vanguard's 'vgk.sys' you'll see a lot of this:
--- snip --- ... 00c8:trace:seh:raise_exception code=c0000096 flags=0 addr=0x11233c1 ip=11233c1 tid=00c8 00c8:trace:seh:raise_exception rax=000000005fd75f32 rbx=00000000000fccc8 rcx=0000000000000000 rdx=000000000000004a 00c8:trace:seh:raise_exception rsi=0000000000cef8dc rdi=00000000000fccc8 rbp=0000000000cef660 rsp=0000000000cef560 00c8:trace:seh:raise_exception r8=0000000000e2e320 r9=0000000000ceee92 r10=0000000000000000 r11=0000000000cef7b0 00c8:trace:seh:raise_exception r12=00000000000fcb60 r13=00007fffffea4000 r14=0000000000000004 r15=0000000000000000 00c8:trace:seh:call_vectored_handlers calling handler at 0x22ce30 code=c0000096 flags=0 00c8:trace:int:emulate_instruction mov cr8,rax at 11233c1 00c8:trace:int:vectored_handler next instruction rip=11233c5 00c8:trace:int:vectored_handler rax=0000000000000000 rbx=00000000000fccc8 rcx=0000000000000000 rdx=000000000000004a 00c8:trace:int:vectored_handler rsi=0000000000cef8dc rdi=00000000000fccc8 rbp=0000000000cef660 rsp=0000000000cef560 00c8:trace:int:vectored_handler r8=0000000000e2e320 r9=0000000000ceee92 r10=0000000000000000 r11=0000000000cef7b0 00c8:trace:int:vectored_handler r12=00000000000fcb60 r13=00000000ffea4000 r14=0000000000000004 r15=0000000000000000 00c8:trace:seh:call_vectored_handlers handler at 0x22ce30 returned ffffffff ... --- snip ---
Disassembly from exception site (with obfuscation in between):
--- snip --- ... 00000000011233C1 | mov rax,cr8 | KeGetCurrentIrql 00000000011233C5 | clc | 00000000011233C6 | cmp edx,ebx | 00000000011233C8 | sub r15d,r15d | 00000000011233CB | cmp cl,EE | 00000000011233CE | test al,al | >= APC_LEVEL? 00000000011233D0 | jne vgk.E1F250 | 00000000011233D6 | mov rax,FFFFF78000000014 | PASSIVE_LEVEL 00000000011233E0 | lea rdx,qword ptr ss:[rsp+68] | 00000000011233E5 | not cx | 00000000011233E8 | movsxd rcx,r8d | 00000000011233EB | lea rcx,qword ptr ss:[rsp+60] | 00000000011233F0 | jmp vgk.11233F5 | 00000000011233F5 | mov rax,qword ptr ds:[rax] | 00000000011233F8 | mov qword ptr ss:[rsp+60],rax | 00000000011233FD | call qword ptr ds:[<&RtlSystemTime...>] | ... 0000000000E1F250 | mov rcx,qword ptr ss:[rbp+130] | 0000000000E1F257 | xor rcx,rsp | 0000000000E1F25A | call vgk.E2D670 | 0000000000E1F25F | add rsp,248 | 0000000000E1F266 | pop r15 | 0000000000E1F268 | pop r14 | 0000000000E1F26A | pop r13 | 0000000000E1F26C | pop r12 | 0000000000E1F26E | pop rdi | 0000000000E1F26F | pop rsi | 0000000000E1F270 | pop rbx | 0000000000E1F271 | pop rbp | 0000000000E1F272 | ret | ... --- snip ---
It's used in the logging sequence, when the driver wants to write encrypted data to log file. I've found a kernel driver on github that shows how it's intended to be used in this case:
https://github.com/veracrypt/VeraCrypt/blob/94d3a1919c8eee4d7bfd7280ee496447...
--- snip --- NTSTATUS SendDeviceIoControlRequest (PDEVICE_OBJECT deviceObject, ULONG ioControlCode, void *inputBuffer, int inputBufferSize, void *outputBuffer, int outputBufferSize) { IO_STATUS_BLOCK ioStatusBlock; NTSTATUS status; PIRP irp; KEVENT event;
if ((KeGetCurrentIrql() >= APC_LEVEL) || VC_KeAreAllApcsDisabled()) { SendDeviceIoControlRequestWorkItemArgs args;
PIO_WORKITEM workItem = IoAllocateWorkItem (RootDeviceObject); if (!workItem) return STATUS_INSUFFICIENT_RESOURCES;
args.deviceObject = deviceObject; args.ioControlCode = ioControlCode; args.inputBuffer = inputBuffer; args.inputBufferSize = inputBufferSize; args.outputBuffer = outputBuffer; args.outputBufferSize = outputBufferSize;
KeInitializeEvent (&args.WorkItemCompletedEvent, SynchronizationEvent, FALSE); IoQueueWorkItem (workItem, SendDeviceIoControlRequestWorkItemRoutine, DelayedWorkQueue, &args);
KeWaitForSingleObject (&args.WorkItemCompletedEvent, Executive, KernelMode, FALSE, NULL); IoFreeWorkItem (workItem);
return args.Status; }
KeInitializeEvent (&event, NotificationEvent, FALSE);
irp = IoBuildDeviceIoControlRequest (ioControlCode, deviceObject, inputBuffer, inputBufferSize, outputBuffer, outputBufferSize, FALSE, &event, &ioStatusBlock);
if (!irp) return STATUS_INSUFFICIENT_RESOURCES;
ObReferenceObject (deviceObject);
status = IoCallDriver (deviceObject, irp); if (status == STATUS_PENDING) { KeWaitForSingleObject (&event, Executive, KernelMode, FALSE, NULL); status = ioStatusBlock.Status; }
ObDereferenceObject (deviceObject); return status; } --- snip ---
Wine currently returns hard-coded PASSIVE_LEVEL = KIRQL(0).
https://source.winehq.org/git/wine.git/blob/26b26a2e0efcb776e7b0115f15580d25...
This is ok and the code seems to work as intended. There will be cases in future where we probably need a tracking of "fake" KIRQL and sync with CR8 emulation.
If we would return APC_LEVEL = KIRQL(1) or even DISPATCH_LEVEL = KIRQL(2) in CR8 emulation, the call to KeAreAllApcsDisabled() wouldn't be made and no logging functions would be executed.
Regards