Implement SvcWaitForAddress 0x34

Currently needed by Sonic Mania Plus
This commit is contained in:
Ac_K 2018-07-19 05:41:27 +02:00
parent 6a69001aa2
commit 2f811eff99
6 changed files with 161 additions and 5 deletions

View file

@ -9,10 +9,12 @@ namespace Ryujinx.HLE.OsHle.Handles
public int CoreMask { get; set; }
public long MutexAddress { get; set; }
public long CondVarAddress { get; set; }
public long MutexAddress { get; set; }
public long CondVarAddress { get; set; }
public long ArbiterWaitAddress { get; set; }
public bool CondVarSignaled { get; set; }
public bool ArbiterSignaled { get; set; }
private Process Process;

View file

@ -0,0 +1,94 @@
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)
{
int CurrentValue = Memory.ReadInt32(Address);
if (CurrentValue < Value)
{
if (ShouldDecrement)
{
Memory.WriteUInt32(Address, (uint)CurrentValue - 1);
}
}
else
{
return MakeError(ErrorModule.Kernel, KernelErr.InvalidState);
}
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,
}
}

View file

@ -12,7 +12,7 @@ namespace Ryujinx.HLE.OsHle.Kernel
public const int Timeout = 117;
public const int Canceled = 118;
public const int CountOutOfRange = 119;
public const int InvalidInfo = 120;
public const int InvalidEnumValue = 120;
public const int InvalidThread = 122;
public const int InvalidState = 125;
}

View file

@ -73,7 +73,8 @@ namespace Ryujinx.HLE.OsHle.Kernel
{ 0x2c, SvcMapPhysicalMemory },
{ 0x2d, SvcUnmapPhysicalMemory },
{ 0x32, SvcSetThreadActivity },
{ 0x33, SvcGetThreadContext3 }
{ 0x33, SvcGetThreadContext3 },
{ 0x34, SvcWaitForAddress }
};
this.Ns = Ns;

View file

@ -294,7 +294,7 @@ namespace Ryujinx.HLE.OsHle.Kernel
InfoType == 19 ||
InfoType == 20)
{
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidInfo);
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidEnumValue);
return;
}

View file

@ -197,6 +197,65 @@ namespace Ryujinx.HLE.OsHle.Kernel
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)
{
lock (Process.ThreadSyncLock)