Sync XMM, fix bugs and add more Win32k syscalls

Adds backend-level XMM0-15 transfer APIs (KVM, Unicorn, WHP) and wires them into thread context save/restore so SIMD state survives scheduling. Introduces new Win32k syscall handlers for atom lookup, window message registration, hook register/unregister, class unregister, and related WinSyscallsHelper state management. Also improves wake scheduling behavior (yield slice after alert wake), tightens token syscall semantics/status codes, and adds DXVK/VK env passthrough plus BrovVulk issue-level diagnostics.
This commit is contained in:
AdvDebug
2026-07-25 19:37:52 +03:00
parent 0c2b8cf26b
commit 826e5e3c67
23 changed files with 464 additions and 41 deletions
@@ -142,6 +142,10 @@ namespace Brovan.Core.Emulation
return true;
}
bool ReadXmmRegisters(ulong[] values) => false;
bool WriteXmmRegisters(ulong[] values) => false;
bool WriteRegisterBatch(int[] registers, ulong[] values, int count)
{
if (registers == null || values == null || count <= 0 || count > registers.Length || count > values.Length)
@@ -95,6 +95,10 @@ namespace Brovan.Core.Emulation
=> Inner.ReadRegister(register);
public ulong ReadRegister(int register)
=> Inner.ReadRegister(register);
public bool ReadXmmRegisters(ulong[] values)
=> Inner.TransferXmmRegisters(values, false);
public bool WriteXmmRegisters(ulong[] values)
=> Inner.TransferXmmRegisters(values, true);
public uint ReadRegister32(Registers register)
=> Inner.ReadRegister32(register);
public uint ReadRegister32(int register)
@@ -98,6 +98,10 @@ namespace Brovan.Core.Emulation
=> Inner.ReadRegisterBatch(registers, values, count);
public bool WriteRegisterBatch(int[] registers, ulong[] values, int count)
=> Inner.WriteRegisterBatch(registers, values, count);
public bool ReadXmmRegisters(ulong[] values)
=> Inner.TransferXmmRegisters(values, false);
public bool WriteXmmRegisters(ulong[] values)
=> Inner.TransferXmmRegisters(values, true);
public CPUFlags GetCPUFlags() => Inner.GetCPUFlags();
public bool SetCPUFlags(CPUFlags flags) => Inner.SetCPUFlags(flags);
@@ -112,6 +112,12 @@ namespace Brovan.Core.Emulation
public IntPtr AddMemoryHook(ulong begin, ulong end, BackendHookType hookType, MemoryHookCallback callback)
=> Inner.AddMemoryHook(begin, end, hookType, callback);
public bool ReadXmmRegisters(ulong[] values)
=> Inner.TransferXmmRegisters(values, false);
public bool WriteXmmRegisters(ulong[] values)
=> Inner.TransferXmmRegisters(values, true);
public IntPtr AddCodeHook(ulong begin, ulong end, CodeHookCallback callback)
=> Inner.AddCodeHook(begin, end, callback);
+18
View File
@@ -1525,6 +1525,22 @@ namespace Brovan.Core.Emulation
}
}
/// <summary>
/// Ends the running thread's slice after it made another thread runnable, leaving it Ready so it resumes
/// where it left off. A wake only unblocks the target; without giving up the rest of the quantum the waker
/// keeps running for up to a full MLFQ slice (1.6M instructions at the lowest queue) before the scheduler
/// looks at the target again, and any handoff the waker then waits on costs that whole slice. Real hardware
/// hides this by running the woken thread on another core.
/// </summary>
internal void YieldSliceAfterWake()
{
if (CurrentThread != null && CurrentThread.State == EmulatedThreadState.Running)
CurrentThread.State = EmulatedThreadState.Ready;
SchedulerRefreshRequested = true;
_emulator.StopEmulation();
}
private void StopAfterSyntheticInstruction(ulong NextIp)
{
SchedulerRefreshRequested = true;
@@ -1831,6 +1847,7 @@ namespace Brovan.Core.Emulation
{
if (t == null || t.Context == null) return;
ReadGprBatch(t.Context);
_emulator.ReadXmmRegisters(t.Context.Xmm);
}
public bool ReadGprBatch(CpuContext c)
@@ -1885,6 +1902,7 @@ namespace Brovan.Core.Emulation
}
WriteGprBatch(t.Context);
_emulator.WriteXmmRegisters(t.Context.Xmm);
Guest.OnThreadContextLoaded(this, t);
}
@@ -1,4 +1,5 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@@ -1128,6 +1129,19 @@ namespace Brovan.Core.Emulation.Guests
{ "PATH", @"C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\WindowsPowerShell\v1.0\;C:\WINDOWS\System32\OpenSSH\" }
};
// pass DXVK environment variables if available to the guest for debugging
foreach (DictionaryEntry HostVariable in Environment.GetEnvironmentVariables())
{
string Name = HostVariable.Key as string;
if (string.IsNullOrEmpty(Name))
continue;
if (!Name.StartsWith("DXVK_", StringComparison.OrdinalIgnoreCase) && !Name.StartsWith("VK_", StringComparison.OrdinalIgnoreCase))
continue;
Env[Name] = HostVariable.Value as string ?? string.Empty;
}
StringBuilder Builder = new StringBuilder(1024);
foreach (var kv in Env)
{
+35
View File
@@ -2332,6 +2332,41 @@ namespace Brovan.Core.Emulation
}
}
internal const int XmmRegisterCount = 16;
/// <summary>
/// Transfers XMM0-15 as 32 qwords, low half of each register first.
/// </summary>
public unsafe bool TransferXmmRegisters(ulong[] Values, bool Write)
{
if (Values == null || Values.Length < XmmRegisterCount * 2)
return false;
LinuxKvmFpu Fpu = default;
lock (_vcpuLock)
{
if (KvmNative.ioctl(_vcpuFd, KvmConstants.KvmIoGetFpu, (IntPtr)(&Fpu)) < 0)
return false;
if (Write)
{
for (int i = 0; i < XmmRegisterCount * 2; i++)
((ulong*)Fpu.Xmm)[i] = Values[i];
if (KvmNative.ioctl(_vcpuFd, KvmConstants.KvmIoSetFpu, (IntPtr)(&Fpu)) < 0)
return false;
}
else
{
for (int i = 0; i < XmmRegisterCount * 2; i++)
Values[i] = ((ulong*)Fpu.Xmm)[i];
}
}
return true;
}
private unsafe void SetFpu(ref LinuxKvmFpu fpu)
{
lock (_vcpuLock)
@@ -1,5 +1,6 @@
using System;
using System.Buffers.Binary;
using Brovan.Core.Helpers;
namespace Brovan.Core.Emulation.OS.Windows
{
@@ -29,6 +30,10 @@ namespace Brovan.Core.Emulation.OS.Windows
{
if (Ioctl == IOCTL_BROVVULK_GEN)
return HandleGenIoctl(ref Data, Instance);
if ((Instance.Settings.Flags & LogFlags.Issues) != 0)
Instance.TriggerEventMessage($"[BrovVulk] unknown IOCTL 0x{Ioctl:X}.", LogFlags.Issues);
return NTSTATUS.STATUS_INVALID_DEVICE_REQUEST;
}
@@ -36,12 +41,22 @@ namespace Brovan.Core.Emulation.OS.Windows
{
byte[] Input = Data.InputBuffer;
if (Input == null || Input.Length < 8)
{
if ((Instance.Settings.Flags & LogFlags.Issues) != 0)
Instance.TriggerEventMessage($"[BrovVulk] rejected IOCTL with a {(Input == null ? -1 : Input.Length)} byte input buffer.", LogFlags.Issues);
return NTSTATUS.STATUS_INVALID_PARAMETER;
}
uint Id = BinaryPrimitives.ReadUInt32LittleEndian(Input.AsSpan(0, 4));
uint PayloadLen = BinaryPrimitives.ReadUInt32LittleEndian(Input.AsSpan(4, 4));
if (PayloadLen > (uint)(Input.Length - 8) || PayloadLen > MaxGenPayload)
{
if ((Instance.Settings.Flags & LogFlags.Issues) != 0)
Instance.TriggerEventMessage($"[BrovVulk] rejected command {Id} with payload length {PayloadLen} in a {Input.Length} byte input buffer.", LogFlags.Issues);
return NTSTATUS.STATUS_INVALID_PARAMETER;
}
byte[] OutBytes;
lock (Lock)
@@ -56,6 +71,7 @@ namespace Brovan.Core.Emulation.OS.Windows
uint Count = Reader.ReadU32();
if (Count > MaxBatchCommands)
throw new InvalidOperationException($"BrovVulk generic: batch count {Count} exceeds cap.");
Result = 0;
for (uint k = 0; k < Count; k++)
{
@@ -72,8 +88,8 @@ namespace Brovan.Core.Emulation.OS.Windows
}
catch (Exception Ex)
{
if ((Instance.Settings.Flags & LogFlags.Syscall) != 0)
Instance.TriggerEventMessage($"[!] BrovVulk(gen): {Ex.Message}", LogFlags.Syscall);
if ((Instance.Settings.Flags & LogFlags.Issues) != 0)
Instance.TriggerEventMessage($"[!] BrovVulk(gen): {Ex.Message}", LogFlags.Issues);
Writer.Reset();
Result = VK_ERROR_INITIALIZATION_FAILED;
}
@@ -1,4 +1,4 @@
using System.Linq;
using System.Linq;
using static Brovan.Core.Helpers.BinaryHelpers;
namespace Brovan.Core.Emulation.OS.Windows
@@ -46,6 +46,10 @@ namespace Brovan.Core.Emulation.OS.Windows
Instance.WriteRegister(Registers.UC_X86_REG_RIP, TargetThread.Context.RIP);
Instance.WriteRegister(Registers.UC_X86_REG_RAX, TargetThread.Context.RAX);
}
else
{
Instance.YieldSliceAfterWake();
}
}
return NTSTATUS.STATUS_SUCCESS;
@@ -13,7 +13,6 @@ namespace Brovan.Core.Emulation.OS.Windows
ulong ThreadHandle = Instance.WinHelper.GetArg(0);
ulong DesiredAccess = Instance.WinHelper.GetArg(1);
ulong OpenAsSelf = Instance.WinHelper.GetArg(2);
ulong TokenHandlePtr = Instance.WinHelper.GetArg(3);
if (TokenHandlePtr == 0)
@@ -35,26 +34,7 @@ namespace Brovan.Core.Emulation.OS.Windows
WinToken Token = WinEmulatedThread.GetState(Thread).ImpersonationToken;
if (Token == null)
{
if (OpenAsSelf == 0)
return NTSTATUS.STATUS_NO_TOKEN;
WinProcess Process = Helper.WinProcesses.FirstOrDefault(Proc => Proc.PID == Helper.PID);
if (Process == null || Process.PrimaryToken == null)
return NTSTATUS.STATUS_NO_TOKEN;
Token = new WinToken
{
Type = TokenType.Impersonation,
SessionId = Process.PrimaryToken.SessionId,
IsElevated = Process.PrimaryToken.IsElevated,
IsRestricted = Process.PrimaryToken.IsRestricted,
EffectiveOnly = Process.PrimaryToken.EffectiveOnly,
ImpersonationLevel = SecurityImpersonationLevel.SecurityImpersonation,
OwningProcessId = Process.PrimaryToken.OwningProcessId,
OwningThreadId = (ulong)Thread.ThreadId
};
}
return NTSTATUS.STATUS_NO_TOKEN;
var Handle = Helper.HandleManager.AddHandle(Token, MapDesiredTokenAccess((AccessMask)(uint)DesiredAccess) | AccessMask.TokenDuplicate);
Helper.WritePointer(TokenHandlePtr, Handle.Handle);
@@ -154,7 +154,7 @@ namespace Brovan.Core.Emulation.OS.Windows
WriteReturnLength(RequiredSize);
if (TokenInformationLength < RequiredSize)
return NTSTATUS.STATUS_BUFFER_OVERFLOW;
return NTSTATUS.STATUS_BUFFER_TOO_SMALL;
if (!Instance._emulator.WriteMemory(TokenInformation, Value))
return NTSTATUS.STATUS_ACCESS_VIOLATION;
@@ -168,7 +168,7 @@ namespace Brovan.Core.Emulation.OS.Windows
WriteReturnLength(RequiredSize);
if (TokenInformationLength < RequiredSize)
return NTSTATUS.STATUS_BUFFER_OVERFLOW;
return NTSTATUS.STATUS_BUFFER_TOO_SMALL;
if (!Instance._emulator.WriteMemory(TokenInformation, 0u))
return NTSTATUS.STATUS_ACCESS_VIOLATION;
@@ -182,7 +182,7 @@ namespace Brovan.Core.Emulation.OS.Windows
WriteReturnLength(RequiredSize);
if (TokenInformationLength < RequiredSize)
return NTSTATUS.STATUS_BUFFER_OVERFLOW;
return NTSTATUS.STATUS_BUFFER_TOO_SMALL;
Span<byte> Buffer = Instance.WinHelper.Shared.GetSpan(RequiredSize);
Buffer.Clear();
@@ -201,7 +201,7 @@ namespace Brovan.Core.Emulation.OS.Windows
WriteReturnLength(RequiredSize);
if (TokenInformationLength < RequiredSize)
return NTSTATUS.STATUS_BUFFER_OVERFLOW;
return NTSTATUS.STATUS_BUFFER_TOO_SMALL;
Span<byte> Buffer = Instance.WinHelper.Shared.GetSpan(RequiredSize);
Buffer.Clear();
@@ -220,7 +220,7 @@ namespace Brovan.Core.Emulation.OS.Windows
WriteReturnLength(RequiredSize);
if (TokenInformationLength < RequiredSize)
return NTSTATUS.STATUS_BUFFER_OVERFLOW;
return NTSTATUS.STATUS_BUFFER_TOO_SMALL;
Span<byte> Buffer = Instance.WinHelper.Shared.GetSpan(RequiredSize);
Buffer.Clear();
@@ -257,7 +257,7 @@ namespace Brovan.Core.Emulation.OS.Windows
WriteReturnLength(RequiredSize);
if (TokenInformationLength < RequiredSize)
return NTSTATUS.STATUS_BUFFER_OVERFLOW;
return NTSTATUS.STATUS_BUFFER_TOO_SMALL;
Span<byte> Buffer = Instance.WinHelper.Shared.GetSpan(RequiredSize);
Buffer.Clear();
@@ -282,7 +282,7 @@ namespace Brovan.Core.Emulation.OS.Windows
WriteReturnLength(RequiredSize);
if (TokenInformationLength < RequiredSize)
return NTSTATUS.STATUS_BUFFER_OVERFLOW;
return NTSTATUS.STATUS_BUFFER_TOO_SMALL;
Span<byte> Buffer = Instance.WinHelper.Shared.GetSpan(RequiredSize);
Buffer.Clear();
@@ -326,7 +326,7 @@ namespace Brovan.Core.Emulation.OS.Windows
WriteReturnLength(RequiredSize);
if (TokenInformationLength < RequiredSize)
return NTSTATUS.STATUS_BUFFER_OVERFLOW;
return NTSTATUS.STATUS_BUFFER_TOO_SMALL;
uint Value = Token.Type == TokenType.Primary ? 1u : 2u;
@@ -342,7 +342,7 @@ namespace Brovan.Core.Emulation.OS.Windows
WriteReturnLength(RequiredSize);
if (TokenInformationLength < RequiredSize)
return NTSTATUS.STATUS_BUFFER_OVERFLOW;
return NTSTATUS.STATUS_BUFFER_TOO_SMALL;
uint Value = (uint)Token.ImpersonationLevel;
@@ -358,7 +358,7 @@ namespace Brovan.Core.Emulation.OS.Windows
WriteReturnLength(RequiredSize);
if (TokenInformationLength < RequiredSize)
return NTSTATUS.STATUS_BUFFER_OVERFLOW;
return NTSTATUS.STATUS_BUFFER_TOO_SMALL;
uint Value = 0;
@@ -374,7 +374,7 @@ namespace Brovan.Core.Emulation.OS.Windows
WriteReturnLength(RequiredSize);
if (TokenInformationLength < RequiredSize)
return NTSTATUS.STATUS_BUFFER_OVERFLOW;
return NTSTATUS.STATUS_BUFFER_TOO_SMALL;
if (!Instance._emulator.WriteMemory(TokenInformation, Token.SessionId))
return NTSTATUS.STATUS_ACCESS_VIOLATION;
@@ -388,7 +388,7 @@ namespace Brovan.Core.Emulation.OS.Windows
WriteReturnLength(RequiredSize);
if (TokenInformationLength < RequiredSize)
return NTSTATUS.STATUS_BUFFER_OVERFLOW;
return NTSTATUS.STATUS_BUFFER_TOO_SMALL;
uint Value = Token.IsElevated ? 1u : 0u;
@@ -406,7 +406,7 @@ namespace Brovan.Core.Emulation.OS.Windows
WriteReturnLength(RequiredSize);
if (TokenInformationLength < RequiredSize)
return NTSTATUS.STATUS_BUFFER_OVERFLOW;
return NTSTATUS.STATUS_BUFFER_TOO_SMALL;
Span<byte> Buffer = Instance.WinHelper.Shared.GetSpan(RequiredSize);
Buffer.Clear();
@@ -423,7 +423,7 @@ namespace Brovan.Core.Emulation.OS.Windows
WriteReturnLength(RequiredSize);
if (TokenInformationLength < RequiredSize)
return NTSTATUS.STATUS_BUFFER_OVERFLOW;
return NTSTATUS.STATUS_BUFFER_TOO_SMALL;
Span<byte> Buffer = Instance.WinHelper.Shared.GetSpan(RequiredSize);
Buffer.Clear();
@@ -455,7 +455,7 @@ namespace Brovan.Core.Emulation.OS.Windows
WriteReturnLength(RequiredSize);
if (TokenInformationLength < RequiredSize)
return NTSTATUS.STATUS_BUFFER_OVERFLOW;
return NTSTATUS.STATUS_BUFFER_TOO_SMALL;
Span<byte> BufferData = Instance.WinHelper.Shared.GetSpan(RequiredSize);
BufferData.Clear();
@@ -486,7 +486,7 @@ namespace Brovan.Core.Emulation.OS.Windows
WriteReturnLength(RequiredSize);
if (TokenInformationLength < RequiredSize)
return NTSTATUS.STATUS_BUFFER_OVERFLOW;
return NTSTATUS.STATUS_BUFFER_TOO_SMALL;
Span<byte> BufferData = Instance.WinHelper.Shared.GetSpan(RequiredSize);
BufferData.Clear();
@@ -1,4 +1,4 @@
using System;
using System;
using static Brovan.Core.Helpers.BinaryHelpers;
namespace Brovan.Core.Emulation.OS.Windows
@@ -70,6 +70,7 @@ namespace Brovan.Core.Emulation.OS.Windows
return NTSTATUS.STATUS_UNSUCCESSFUL;
WindowsThreadState State = WinEmulatedThread.GetState(Thread);
if (State.WaitCompleted)
{
NTSTATUS Status = State.WaitStatus;
@@ -0,0 +1,58 @@
using System;
using System.Text;
using static Brovan.Core.Helpers.BinaryHelpers;
namespace Brovan.Core.Emulation.OS.Windows.Win32k
{
internal class NtUserGetAtomName : IWinSyscall
{
public NTSTATUS Handle(BinaryEmulator Instance)
{
ushort Atom = (ushort)Instance.WinHelper.GetArg(0);
ulong StringPtr = Instance.WinHelper.GetArg(1);
uint BufferOffset = (uint)Instance.WinHelper.PointerSize;
uint StructSize = BufferOffset * 2;
if (StringPtr == 0 || !Instance.IsRegionMapped(StringPtr, StructSize) ||
!Instance.WinHelper.TryGetAtomName(Atom, out string Name))
{
Instance.SetRawSyscallReturn(0);
return NTSTATUS.STATUS_SUCCESS;
}
ushort MaximumLength = (ushort)(Instance.ReadMemoryUInt(StringPtr) >> 16);
ulong Buffer = Instance.WinHelper.ReadPointer(StringPtr + BufferOffset);
int MaxChars = MaximumLength / sizeof(char);
if (Buffer == 0 || MaxChars <= 0)
{
Instance.SetRawSyscallReturn(0);
return NTSTATUS.STATUS_SUCCESS;
}
if (Name.Length >= MaxChars)
Name = Name.Substring(0, MaxChars - 1);
uint NameBytes = (uint)((Name.Length + 1) * sizeof(char));
if (!Instance.IsRegionMapped(Buffer, NameBytes))
{
Instance.SetRawSyscallReturn(0);
return NTSTATUS.STATUS_SUCCESS;
}
Span<byte> Bytes = Instance.WinHelper.Shared.GetSpan(NameBytes);
Bytes.Clear();
Encoding.Unicode.GetBytes(Name, Bytes);
if (!Instance.WriteMemory(Buffer, Bytes))
{
Instance.SetRawSyscallReturn(0);
return NTSTATUS.STATUS_SUCCESS;
}
Instance.SetRawSyscallReturn((uint)Name.Length);
return NTSTATUS.STATUS_SUCCESS;
}
}
}
@@ -0,0 +1,40 @@
using System;
using System.Text;
using static Brovan.Core.Helpers.BinaryHelpers;
namespace Brovan.Core.Emulation.OS.Windows.Win32k
{
internal class NtUserGetHDevName : IWinSyscall
{
private const int CchDeviceName = 32;
private const string DeviceName = @"\\.\DISPLAY1";
public NTSTATUS Handle(BinaryEmulator Instance)
{
ulong Monitor = Instance.WinHelper.GetArg(0);
ulong NamePtr = Instance.WinHelper.GetArg(1);
uint NameBytes = CchDeviceName * sizeof(char);
if (NamePtr == 0 || !Instance.IsRegionMapped(NamePtr, NameBytes) ||
Monitor == 0 || Monitor != Instance.WinHelper.GetPrimaryMonitorHandle())
{
Instance.SetRawSyscallReturn(0);
return NTSTATUS.STATUS_SUCCESS;
}
Span<byte> Buffer = Instance.WinHelper.Shared.GetSpan(NameBytes);
Buffer.Clear();
Encoding.Unicode.GetBytes(DeviceName, Buffer);
if (!Instance.WriteMemory(NamePtr, Buffer))
{
Instance.SetRawSyscallReturn(0);
return NTSTATUS.STATUS_SUCCESS;
}
Instance.SetRawSyscallReturn(1);
return NTSTATUS.STATUS_SUCCESS;
}
}
}
@@ -0,0 +1,17 @@
using static Brovan.Core.Helpers.BinaryHelpers;
namespace Brovan.Core.Emulation.OS.Windows.Win32k
{
internal class NtUserRegisterWindowMessage : IWinSyscall
{
public NTSTATUS Handle(BinaryEmulator Instance)
{
ulong NamePtr = Instance.WinHelper.GetArg(0);
bool Read = Instance.WinHelper.TryReadUnicodeString(NamePtr, out string Name, out _);
Instance.SetRawSyscallReturn(Read ? Instance.WinHelper.RegisterWindowMessageAtom(Name) : (ushort)0);
return NTSTATUS.STATUS_SUCCESS;
}
}
}
@@ -0,0 +1,22 @@
using static Brovan.Core.Helpers.BinaryHelpers;
namespace Brovan.Core.Emulation.OS.Windows.Win32k
{
internal class NtUserSetWindowsHookEx : IWinSyscall
{
public NTSTATUS Handle(BinaryEmulator Instance)
{
int HookId = (int)Instance.WinHelper.GetArg(3);
ulong HookProc = Instance.WinHelper.GetArg(4);
if (HookProc == 0)
{
Instance.SetRawSyscallReturn(0);
return NTSTATUS.STATUS_SUCCESS;
}
Instance.SetRawSyscallReturn(Instance.WinHelper.RegisterWindowsHook(HookId, HookProc));
return NTSTATUS.STATUS_SUCCESS;
}
}
}
@@ -0,0 +1,15 @@
using static Brovan.Core.Helpers.BinaryHelpers;
namespace Brovan.Core.Emulation.OS.Windows.Win32k
{
internal class NtUserUnhookWindowsHookEx : IWinSyscall
{
public NTSTATUS Handle(BinaryEmulator Instance)
{
ulong Hook = Instance.WinHelper.GetArg(0);
Instance.SetRawSyscallReturn(Instance.WinHelper.UnregisterWindowsHook(Hook) ? 1u : 0u);
return NTSTATUS.STATUS_SUCCESS;
}
}
}
@@ -0,0 +1,18 @@
using static Brovan.Core.Helpers.BinaryHelpers;
namespace Brovan.Core.Emulation.OS.Windows.Win32k
{
internal class NtUserUnregisterClass : IWinSyscall
{
public NTSTATUS Handle(BinaryEmulator Instance)
{
ulong ClassNamePtr = Instance.WinHelper.GetArg(0);
ulong InstanceHandle = Instance.WinHelper.GetArg(1);
bool Read = Instance.WinHelper.TryReadUnicodeString(ClassNamePtr, out string ClassName, out _);
Instance.SetRawSyscallReturn(Read && Instance.WinHelper.UnregisterWindowClass(InstanceHandle, ClassName) ? 1u : 0u);
return NTSTATUS.STATUS_SUCCESS;
}
}
}
@@ -3961,6 +3961,67 @@ namespace Brovan.Core.Emulation.OS.Windows
Data[Offset + i] = (byte)(Value >> (i * 8));
}
private readonly Dictionary<string, ushort> RegisteredMessageAtoms = new(StringComparer.OrdinalIgnoreCase);
private readonly Dictionary<ushort, string> RegisteredMessageNames = new();
private readonly Dictionary<ulong, (int HookId, ulong HookProc)> WindowsHooks = new();
private ulong NextWindowsHook = 0x1F0000;
public ulong RegisterWindowsHook(int HookId, ulong HookProc)
{
ulong Hook = NextWindowsHook;
NextWindowsHook += 4;
WindowsHooks[Hook] = (HookId, HookProc);
return Hook;
}
public bool UnregisterWindowsHook(ulong Hook)
{
return WindowsHooks.Remove(Hook);
}
public bool UnregisterWindowClass(ulong InstanceHandle, string Name)
{
WinWindowClass WindowClass = GetWindowClass(InstanceHandle, Name, null);
if (WindowClass == null)
return false;
WinWindowClassesByAtom.Remove(WindowClass.Atom);
WinWindowClassAtomsByKey.Remove(BuildWindowClassKey(WindowClass.InstanceHandle, WindowClass.Name, WindowClass.Version));
return true;
}
public ushort RegisterWindowMessageAtom(string Name)
{
if (string.IsNullOrEmpty(Name))
return 0;
if (RegisteredMessageAtoms.TryGetValue(Name, out ushort Existing))
return Existing;
if (NextWindowClassAtom < 0xC000)
NextWindowClassAtom = 0xC000;
ushort Atom = NextWindowClassAtom++;
RegisteredMessageAtoms[Name] = Atom;
RegisteredMessageNames[Atom] = Name;
return Atom;
}
public bool TryGetAtomName(ushort Atom, out string Name)
{
if (RegisteredMessageNames.TryGetValue(Atom, out Name))
return true;
if (WinWindowClassesByAtom.TryGetValue(Atom, out WinWindowClass WindowClass) && !string.IsNullOrEmpty(WindowClass.Name))
{
Name = WindowClass.Name;
return true;
}
Name = null;
return false;
}
public WinWindowClass RegisterWindowClass(WinWindowClass WindowClass)
{
if (WindowClass == null || string.IsNullOrEmpty(WindowClass.Name))
+1
View File
@@ -13,6 +13,7 @@ namespace Brovan.Core.Emulation
public ulong MXCSR;
public ulong CS, DS, ES, FS, GS, SS;
public ulong DR0, DR1, DR2, DR3, DR6, DR7;
public readonly ulong[] Xmm = new ulong[32];
}
public enum EmulatedThreadState
@@ -964,6 +964,53 @@ namespace Brovan.Core.Emulation
}
}
internal const int XmmRegisterCount = 16;
private static int[] _xmmBatchRegs;
private static int[] GetXmmBatchRegs()
{
int[] Regs = _xmmBatchRegs;
if (Regs == null)
{
Regs = new int[XmmRegisterCount];
for (int i = 0; i < XmmRegisterCount; i++)
Regs[i] = (int)Registers.UC_X86_REG_XMM0 + i;
_xmmBatchRegs = Regs;
}
return Regs;
}
/// <summary>
/// Transfers XMM0-15 as 32 qwords, low half of each register first.
/// </summary>
public unsafe bool TransferXmmRegisters(ulong[] Values, bool Write)
{
if (DisposedCheck() || Values == null || Values.Length < XmmRegisterCount * 2)
return false;
lock (_registerLock)
{
if (_uc == IntPtr.Zero)
return false;
int[] Regs = GetXmmBatchRegs();
fixed (int* RegsPtr = Regs)
fixed (ulong* ValsPtr = Values)
{
void** PtrArray = stackalloc void*[XmmRegisterCount];
for (int i = 0; i < XmmRegisterCount; i++)
PtrArray[i] = &ValsPtr[i * 2];
_error = Write
? uc_reg_write_batch(_uc, RegsPtr, PtrArray, XmmRegisterCount)
: uc_reg_read_batch(_uc, RegsPtr, PtrArray, XmmRegisterCount);
return _error == UCErrors.UC_ERR_OK;
}
}
}
/// <summary>
/// Get the CPU Flags.
/// </summary>
@@ -123,6 +123,8 @@ namespace Brovan.Core.Emulation
Cr4 = 0x0000001F,
Cr8 = 0x00000020,
Xmm0 = 0x00001000,
Dr0 = 0x00000021,
Dr1 = 0x00000022,
Dr2 = 0x00000023,
+56
View File
@@ -2299,6 +2299,62 @@ namespace Brovan.Core.Emulation
return ref _regsCache;
}
internal const int XmmRegisterCount = 16;
private static readonly uint[] XmmRegNames = BuildXmmRegNames();
private static uint[] BuildXmmRegNames()
{
uint[] Names = new uint[XmmRegisterCount];
for (int i = 0; i < XmmRegisterCount; i++)
Names[i] = (uint)WhvRegisterName.Xmm0 + (uint)i;
return Names;
}
/// <summary>
/// Transfers XMM0-15 as 32 qwords, low half of each register first.
/// </summary>
public unsafe bool TransferXmmRegisters(ulong[] Values, bool Write)
{
if (Values == null || Values.Length < XmmRegisterCount * 2)
return false;
Span<WhvRegisterValue> Regs = stackalloc WhvRegisterValue[XmmRegisterCount];
if (Write)
{
for (int i = 0; i < XmmRegisterCount; i++)
{
Regs[i].Low = Values[i * 2];
Regs[i].High = Values[i * 2 + 1];
}
}
lock (_vcpuLock)
{
fixed (uint* Names = XmmRegNames)
fixed (WhvRegisterValue* Vals = Regs)
{
int Hr = Write
? WhpNative.WHvSetVirtualProcessorRegisters(_partition, VpIndex, Names, XmmRegisterCount, Vals)
: WhpNative.WHvGetVirtualProcessorRegisters(_partition, VpIndex, Names, XmmRegisterCount, Vals);
if (WhpNative.Failed(Hr))
return false;
}
}
if (!Write)
{
for (int i = 0; i < XmmRegisterCount; i++)
{
Values[i * 2] = Regs[i].Low;
Values[i * 2 + 1] = Regs[i].High;
}
}
return true;
}
private unsafe void LoadRegisters()
{
Span<WhvRegisterValue> values = stackalloc WhvRegisterValue[GpRegNames.Length];