Accurately implement steady & system clocks (#732)

* Improve SteadyClock implementation accuracy

* Rewrite system clocks to be accurate

* Implement IStaticService 100 & 101

* Add time:* permissions

* Address comments

* Realign TimePermissions definitions

* Address gdk's comments

* Fix after rebase
This commit is contained in:
Thomas Guillemard 2019-07-14 22:50:11 +02:00 committed by Ac_K
parent 4ad3936afd
commit 97d0c62423
13 changed files with 522 additions and 82 deletions

View file

@ -0,0 +1,40 @@
using Ryujinx.HLE.Utilities;
using System.Runtime.InteropServices;
namespace Ryujinx.HLE.HOS.Services.Time.Clock
{
[StructLayout(LayoutKind.Sequential)]
struct TimeSpanType
{
public ulong NanoSeconds;
public TimeSpanType(ulong nanoSeconds)
{
NanoSeconds = nanoSeconds;
}
public ulong ToSeconds()
{
return NanoSeconds / 1000000000;
}
public static TimeSpanType FromTicks(ulong ticks, ulong frequency)
{
return new TimeSpanType(ticks * 1000000000 / frequency);
}
}
[StructLayout(LayoutKind.Sequential)]
struct SteadyClockTimePoint
{
public ulong TimePoint;
public UInt128 ClockSourceId;
}
[StructLayout(LayoutKind.Sequential)]
struct SystemClockContext
{
public ulong Offset;
public SteadyClockTimePoint SteadyTimePoint;
}
}

View file

@ -0,0 +1,59 @@
using Ryujinx.HLE.HOS.Kernel.Threading;
namespace Ryujinx.HLE.HOS.Services.Time.Clock
{
class StandardLocalSystemClockCore : SystemClockCore
{
private SteadyClockCore _steadyClockCore;
private SystemClockContext _context;
private static StandardLocalSystemClockCore instance;
public static StandardLocalSystemClockCore Instance
{
get
{
if (instance == null)
{
instance = new StandardLocalSystemClockCore(SteadyClockCore.Instance);
}
return instance;
}
}
public StandardLocalSystemClockCore(SteadyClockCore steadyClockCore)
{
_steadyClockCore = steadyClockCore;
_context = new SystemClockContext();
_context.SteadyTimePoint.ClockSourceId = steadyClockCore.GetClockSourceId();
}
public override ResultCode Flush(SystemClockContext context)
{
// TODO: set:sys SetUserSystemClockContext
return ResultCode.Success;
}
public override SteadyClockCore GetSteadyClockCore()
{
return _steadyClockCore;
}
public override ResultCode GetSystemClockContext(KThread thread, out SystemClockContext context)
{
context = _context;
return ResultCode.Success;
}
public override ResultCode SetSystemClockContext(SystemClockContext context)
{
_context = context;
return ResultCode.Success;
}
}
}

View file

@ -0,0 +1,59 @@
using Ryujinx.HLE.HOS.Kernel.Threading;
namespace Ryujinx.HLE.HOS.Services.Time.Clock
{
class StandardNetworkSystemClockCore : SystemClockCore
{
private SteadyClockCore _steadyClockCore;
private SystemClockContext _context;
private static StandardNetworkSystemClockCore instance;
public static StandardNetworkSystemClockCore Instance
{
get
{
if (instance == null)
{
instance = new StandardNetworkSystemClockCore(SteadyClockCore.Instance);
}
return instance;
}
}
public StandardNetworkSystemClockCore(SteadyClockCore steadyClockCore)
{
_steadyClockCore = steadyClockCore;
_context = new SystemClockContext();
_context.SteadyTimePoint.ClockSourceId = steadyClockCore.GetClockSourceId();
}
public override ResultCode Flush(SystemClockContext context)
{
// TODO: set:sys SetNetworkSystemClockContext
return ResultCode.Success;
}
public override SteadyClockCore GetSteadyClockCore()
{
return _steadyClockCore;
}
public override ResultCode GetSystemClockContext(KThread thread, out SystemClockContext context)
{
context = _context;
return ResultCode.Success;
}
public override ResultCode SetSystemClockContext(SystemClockContext context)
{
_context = context;
return ResultCode.Success;
}
}
}

View file

@ -0,0 +1,96 @@
using Ryujinx.HLE.HOS.Kernel.Threading;
namespace Ryujinx.HLE.HOS.Services.Time.Clock
{
class StandardUserSystemClockCore : SystemClockCore
{
private StandardLocalSystemClockCore _localSystemClockCore;
private StandardNetworkSystemClockCore _networkSystemClockCore;
private bool _autoCorrectionEnabled;
private static StandardUserSystemClockCore instance;
public static StandardUserSystemClockCore Instance
{
get
{
if (instance == null)
{
instance = new StandardUserSystemClockCore(StandardLocalSystemClockCore.Instance, StandardNetworkSystemClockCore.Instance);
}
return instance;
}
}
public StandardUserSystemClockCore(StandardLocalSystemClockCore localSystemClockCore, StandardNetworkSystemClockCore networkSystemClockCore)
{
_localSystemClockCore = localSystemClockCore;
_networkSystemClockCore = networkSystemClockCore;
_autoCorrectionEnabled = false;
}
public override ResultCode Flush(SystemClockContext context)
{
return ResultCode.NotImplemented;
}
public override SteadyClockCore GetSteadyClockCore()
{
return _localSystemClockCore.GetSteadyClockCore();
}
public override ResultCode GetSystemClockContext(KThread thread, out SystemClockContext context)
{
ResultCode result = ApplyAutomaticCorrection(thread, false);
context = new SystemClockContext();
if (result == ResultCode.Success)
{
return _localSystemClockCore.GetSystemClockContext(thread, out context);
}
return result;
}
public override ResultCode SetSystemClockContext(SystemClockContext context)
{
return ResultCode.NotImplemented;
}
private ResultCode ApplyAutomaticCorrection(KThread thread, bool autoCorrectionEnabled)
{
ResultCode result = ResultCode.Success;
if (_autoCorrectionEnabled != autoCorrectionEnabled && _networkSystemClockCore.IsClockSetup(thread))
{
result = _networkSystemClockCore.GetSystemClockContext(thread, out SystemClockContext context);
if (result == ResultCode.Success)
{
_localSystemClockCore.SetSystemClockContext(context);
}
}
return result;
}
public ResultCode SetAutomaticCorrectionEnabled(KThread thread, bool autoCorrectionEnabled)
{
ResultCode result = ApplyAutomaticCorrection(thread, autoCorrectionEnabled);
if (result == ResultCode.Success)
{
_autoCorrectionEnabled = autoCorrectionEnabled;
}
return result;
}
public bool IsAutomaticCorrectionEnabled()
{
return _autoCorrectionEnabled;
}
}
}

View file

@ -0,0 +1,86 @@
using Ryujinx.HLE.HOS.Kernel.Threading;
using Ryujinx.HLE.Utilities;
using System;
namespace Ryujinx.HLE.HOS.Services.Time.Clock
{
class SteadyClockCore
{
private TimeSpanType _testOffset;
private TimeSpanType _internalOffset;
private UInt128 _clockSourceId;
private static SteadyClockCore instance;
public static SteadyClockCore Instance
{
get
{
if (instance == null)
{
instance = new SteadyClockCore();
}
return instance;
}
}
private SteadyClockCore()
{
_testOffset = new TimeSpanType(0);
_internalOffset = new TimeSpanType(0);
_clockSourceId = new UInt128(Guid.NewGuid().ToByteArray());
}
private SteadyClockTimePoint GetTimePoint(KThread thread)
{
SteadyClockTimePoint result = new SteadyClockTimePoint
{
TimePoint = 0,
ClockSourceId = _clockSourceId
};
TimeSpanType ticksTimeSpan = TimeSpanType.FromTicks(thread.Context.ThreadState.CntpctEl0, thread.Context.ThreadState.CntfrqEl0);
result.TimePoint = _internalOffset.ToSeconds() + ticksTimeSpan.ToSeconds();
return result;
}
public UInt128 GetClockSourceId()
{
return _clockSourceId;
}
public SteadyClockTimePoint GetCurrentTimePoint(KThread thread)
{
SteadyClockTimePoint result = GetTimePoint(thread);
result.TimePoint += _testOffset.ToSeconds();
return result;
}
public TimeSpanType GetTestOffset()
{
return _testOffset;
}
public void SetTestOffset(TimeSpanType testOffset)
{
_testOffset = testOffset;
}
// TODO: check if this is accurate
public TimeSpanType GetInternalOffset()
{
return _internalOffset;
}
// TODO: check if this is accurate
public void SetInternalOffset(TimeSpanType internalOffset)
{
_internalOffset = internalOffset;
}
}
}

View file

@ -0,0 +1,31 @@
using Ryujinx.HLE.HOS.Kernel.Threading;
namespace Ryujinx.HLE.HOS.Services.Time.Clock
{
abstract class SystemClockCore
{
public abstract SteadyClockCore GetSteadyClockCore();
public abstract ResultCode GetSystemClockContext(KThread thread, out SystemClockContext context);
public abstract ResultCode SetSystemClockContext(SystemClockContext context);
public abstract ResultCode Flush(SystemClockContext context);
public bool IsClockSetup(KThread thread)
{
ResultCode result = GetSystemClockContext(thread, out SystemClockContext context);
if (result == ResultCode.Success)
{
SteadyClockCore steadyClockCore = GetSteadyClockCore();
SteadyClockTimePoint steadyClockTimePoint = steadyClockCore.GetCurrentTimePoint(thread);
return steadyClockTimePoint.ClockSourceId == context.SteadyTimePoint.ClockSourceId;
}
return false;
}
}
}