forked from Mirror/Ryujinx
Implement SvcWaitForAddress 0x34 (#289)
* Implement SvcWaitForAddress 0x34 Currently needed by Sonic Mania Plus * Fix mistake * read-decrement-write locked
This commit is contained in:
parent
6a69001aa2
commit
8b685b12f0
6 changed files with 171 additions and 5 deletions
|
@ -9,10 +9,12 @@ namespace Ryujinx.HLE.OsHle.Handles
|
||||||
|
|
||||||
public int CoreMask { get; set; }
|
public int CoreMask { get; set; }
|
||||||
|
|
||||||
public long MutexAddress { get; set; }
|
public long MutexAddress { get; set; }
|
||||||
public long CondVarAddress { get; set; }
|
public long CondVarAddress { get; set; }
|
||||||
|
public long ArbiterWaitAddress { get; set; }
|
||||||
|
|
||||||
public bool CondVarSignaled { get; set; }
|
public bool CondVarSignaled { get; set; }
|
||||||
|
public bool ArbiterSignaled { get; set; }
|
||||||
|
|
||||||
private Process Process;
|
private Process Process;
|
||||||
|
|
||||||
|
|
112
Ryujinx.HLE/OsHle/Kernel/AddressArbiter.cs
Normal file
112
Ryujinx.HLE/OsHle/Kernel/AddressArbiter.cs
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
using ChocolArm64.Memory;
|
||||||
|
using ChocolArm64.State;
|
||||||
|
using Ryujinx.HLE.OsHle.Handles;
|
||||||
|
|
||||||
|
using static Ryujinx.HLE.OsHle.ErrorCode;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.OsHle.Kernel
|
||||||
|
{
|
||||||
|
static class AddressArbiter
|
||||||
|
{
|
||||||
|
static ulong WaitForAddress(Process Process, AThreadState ThreadState, long Address, ulong Timeout)
|
||||||
|
{
|
||||||
|
KThread CurrentThread = Process.GetThread(ThreadState.Tpidr);
|
||||||
|
|
||||||
|
Process.Scheduler.SetReschedule(CurrentThread.ProcessorId);
|
||||||
|
|
||||||
|
CurrentThread.ArbiterWaitAddress = Address;
|
||||||
|
CurrentThread.ArbiterSignaled = false;
|
||||||
|
|
||||||
|
Process.Scheduler.EnterWait(CurrentThread, NsTimeConverter.GetTimeMs(Timeout));
|
||||||
|
|
||||||
|
if (!CurrentThread.ArbiterSignaled)
|
||||||
|
{
|
||||||
|
return MakeError(ErrorModule.Kernel, KernelErr.Timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ulong WaitForAddressIfLessThan(Process Process,
|
||||||
|
AThreadState ThreadState,
|
||||||
|
AMemory Memory,
|
||||||
|
long Address,
|
||||||
|
int Value,
|
||||||
|
ulong Timeout,
|
||||||
|
bool ShouldDecrement)
|
||||||
|
{
|
||||||
|
Memory.SetExclusive(ThreadState, Address);
|
||||||
|
|
||||||
|
int CurrentValue = Memory.ReadInt32(Address);
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
if (Memory.TestExclusive(ThreadState, Address))
|
||||||
|
{
|
||||||
|
if (CurrentValue < Value)
|
||||||
|
{
|
||||||
|
if (ShouldDecrement)
|
||||||
|
{
|
||||||
|
Memory.WriteInt32(Address, CurrentValue - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Memory.ClearExclusiveForStore(ThreadState);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Memory.ClearExclusiveForStore(ThreadState);
|
||||||
|
|
||||||
|
return MakeError(ErrorModule.Kernel, KernelErr.InvalidState);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Memory.SetExclusive(ThreadState, Address);
|
||||||
|
|
||||||
|
CurrentValue = Memory.ReadInt32(Address);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Timeout == 0)
|
||||||
|
{
|
||||||
|
return MakeError(ErrorModule.Kernel, KernelErr.Timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
return WaitForAddress(Process, ThreadState, Address, Timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ulong WaitForAddressIfEqual(Process Process,
|
||||||
|
AThreadState ThreadState,
|
||||||
|
AMemory Memory,
|
||||||
|
long Address,
|
||||||
|
int Value,
|
||||||
|
ulong Timeout)
|
||||||
|
{
|
||||||
|
if (Memory.ReadInt32(Address) != Value)
|
||||||
|
{
|
||||||
|
return MakeError(ErrorModule.Kernel, KernelErr.InvalidState);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Timeout == 0)
|
||||||
|
{
|
||||||
|
return MakeError(ErrorModule.Kernel, KernelErr.Timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
return WaitForAddress(Process, ThreadState, Address, Timeout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ArbitrationType : int
|
||||||
|
{
|
||||||
|
WaitIfLessThan,
|
||||||
|
DecrementAndWaitIfLessThan,
|
||||||
|
WaitIfEqual
|
||||||
|
}
|
||||||
|
|
||||||
|
enum SignalType : int
|
||||||
|
{
|
||||||
|
Signal,
|
||||||
|
IncrementAndSignalIfEqual,
|
||||||
|
ModifyByWaitingCountAndSignalIfEqual
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,7 +12,7 @@ namespace Ryujinx.HLE.OsHle.Kernel
|
||||||
public const int Timeout = 117;
|
public const int Timeout = 117;
|
||||||
public const int Canceled = 118;
|
public const int Canceled = 118;
|
||||||
public const int CountOutOfRange = 119;
|
public const int CountOutOfRange = 119;
|
||||||
public const int InvalidInfo = 120;
|
public const int InvalidEnumValue = 120;
|
||||||
public const int InvalidThread = 122;
|
public const int InvalidThread = 122;
|
||||||
public const int InvalidState = 125;
|
public const int InvalidState = 125;
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,7 +73,8 @@ namespace Ryujinx.HLE.OsHle.Kernel
|
||||||
{ 0x2c, SvcMapPhysicalMemory },
|
{ 0x2c, SvcMapPhysicalMemory },
|
||||||
{ 0x2d, SvcUnmapPhysicalMemory },
|
{ 0x2d, SvcUnmapPhysicalMemory },
|
||||||
{ 0x32, SvcSetThreadActivity },
|
{ 0x32, SvcSetThreadActivity },
|
||||||
{ 0x33, SvcGetThreadContext3 }
|
{ 0x33, SvcGetThreadContext3 },
|
||||||
|
{ 0x34, SvcWaitForAddress }
|
||||||
};
|
};
|
||||||
|
|
||||||
this.Ns = Ns;
|
this.Ns = Ns;
|
||||||
|
|
|
@ -294,7 +294,7 @@ namespace Ryujinx.HLE.OsHle.Kernel
|
||||||
InfoType == 19 ||
|
InfoType == 19 ||
|
||||||
InfoType == 20)
|
InfoType == 20)
|
||||||
{
|
{
|
||||||
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidInfo);
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidEnumValue);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -197,6 +197,57 @@ namespace Ryujinx.HLE.OsHle.Kernel
|
||||||
Process.Scheduler.EnterWait(CurrThread);
|
Process.Scheduler.EnterWait(CurrThread);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void SvcWaitForAddress(AThreadState ThreadState)
|
||||||
|
{
|
||||||
|
long Address = (long)ThreadState.X0;
|
||||||
|
ArbitrationType Type = (ArbitrationType)ThreadState.X1;
|
||||||
|
int Value = (int)ThreadState.X2;
|
||||||
|
ulong Timeout = ThreadState.X3;
|
||||||
|
|
||||||
|
Ns.Log.PrintDebug(LogClass.KernelSvc,
|
||||||
|
"Address = " + Address.ToString("x16") + ", " +
|
||||||
|
"ArbitrationType = " + Type .ToString() + ", " +
|
||||||
|
"Value = " + Value .ToString("x8") + ", " +
|
||||||
|
"Timeout = " + Timeout.ToString("x16"));
|
||||||
|
|
||||||
|
if (IsPointingInsideKernel(Address))
|
||||||
|
{
|
||||||
|
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid address 0x{Address:x16}!");
|
||||||
|
|
||||||
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsWordAddressUnaligned(Address))
|
||||||
|
{
|
||||||
|
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Unaligned address 0x{Address:x16}!");
|
||||||
|
|
||||||
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAlignment);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (Type)
|
||||||
|
{
|
||||||
|
case ArbitrationType.WaitIfLessThan:
|
||||||
|
ThreadState.X0 = AddressArbiter.WaitForAddressIfLessThan(Process, ThreadState, Memory, Address, Value, Timeout, false);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ArbitrationType.DecrementAndWaitIfLessThan:
|
||||||
|
ThreadState.X0 = AddressArbiter.WaitForAddressIfLessThan(Process, ThreadState, Memory, Address, Value, Timeout, true);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ArbitrationType.WaitIfEqual:
|
||||||
|
ThreadState.X0 = AddressArbiter.WaitForAddressIfEqual(Process, ThreadState, Memory, Address, Value, Timeout);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidEnumValue);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void MutexUnlock(KThread CurrThread, long MutexAddress)
|
private void MutexUnlock(KThread CurrThread, long MutexAddress)
|
||||||
{
|
{
|
||||||
lock (Process.ThreadSyncLock)
|
lock (Process.ThreadSyncLock)
|
||||||
|
|
Loading…
Reference in a new issue