mirror of
https://github.com/par274/sharpemu.git
synced 2026-07-22 19:06:15 +08:00
core: unify clock dispatch logic, add precise clocks, and enforce coalesced time writes (#71)
Comprehensive refactoring of the system time subsystem to unify clock dispatching, support precise clock extensions, and secure memory boundaries against partial state corruption.
Centralized Clock Dispatch Engine:
- Extracted shared elapsed-tick calculation and clock-routing math into a unified internal static bool ResolveClockTime() dispatch engine under KernelRuntimeCompatExports.cs.
- Moved all clock identifiers from KernelMemoryCompatExports to KernelRuntimeCompatExports as internal const int constants to eliminate cross-file duplication while preserving raw compiler switch-case layout optimizations.
- Added native alias mapping support for CLOCK_REALTIME_PRECISE (9) and CLOCK_MONOTONIC_PRECISE (11).
- Hardened the Orbis sceKernelClockGettime path by routing it through the new dispatcher, resolving a pre-existing logic flaw where any non-zero clock_id incorrectly fell back to monotonic time. Invalid IDs now properly fail with ORBIS_GEN2_ERROR_INVALID_ARGUMENT.
Coalesced Single-Transaction Memory Writes:
- Replaced consecutive isolated 8-byte scalar writes across POSIX clock_gettime, gettimeofday, and Orbis sceKernelClockGettime/sceKernelGettimeofday with safe single-transaction 16-byte stackalloc byte buffer writes via BinaryPrimitives and ctx.Memory.TryWrite. This entirely prevents partial memory state corruption on virtual page boundaries.
- Implemented a single 8-byte coalesced zero-fill transaction for the deprecated/legacy timezone buffer (timezoneAddress != 0), aligning it with standard FreeBSD stub behavior.
- Standardized POSIX failure path routines. Write faults cleanly issue TrySetErrno(ctx, Efault) while safely omitting explicit manual Rax writes, letting the import dispatcher natively sign-extend the return -1 value to 0xFFFFFFFFFFFFFFFF.
Zero-Alloc Host RDTSC Execution Stub:
- Patched CreateRdtscReader() to stream native architecture opcodes out of stack-allocated spans directly into host executable memory zones (VirtualAlloc) via unsafe { Buffer.MemoryCopy(...) }, completely removing the high-frequency .ToArray() runtime allocation overhead on the hot path.
Files: KernelRuntimeCompatExports.cs, KernelMemoryCompatExports.cs
This commit is contained in:
@@ -67,13 +67,6 @@ public static class KernelMemoryCompatExports
|
|||||||
private const int Einval = 22;
|
private const int Einval = 22;
|
||||||
private const int Erange = 34;
|
private const int Erange = 34;
|
||||||
private const int Struncate = 80;
|
private const int Struncate = 80;
|
||||||
private const int ClockRealtime = 0;
|
|
||||||
private const int ClockVirtual = 1;
|
|
||||||
private const int ClockProf = 2;
|
|
||||||
private const int ClockMonotonic = 4;
|
|
||||||
private const int ClockUptime = 5;
|
|
||||||
private const int ClockRealtimeFast = 10;
|
|
||||||
private const int ClockMonotonicFast = 12;
|
|
||||||
private const nuint DefaultLibcHeapAlignment = 16;
|
private const nuint DefaultLibcHeapAlignment = 16;
|
||||||
private const ushort KernelStatModeDirectory = 0x41FF;
|
private const ushort KernelStatModeDirectory = 0x41FF;
|
||||||
private const ushort KernelStatModeRegular = 0x81FF;
|
private const ushort KernelStatModeRegular = 0x81FF;
|
||||||
@@ -2011,29 +2004,11 @@ public static class KernelMemoryCompatExports
|
|||||||
|
|
||||||
long seconds;
|
long seconds;
|
||||||
long nanoseconds;
|
long nanoseconds;
|
||||||
switch (clockId)
|
if (!KernelRuntimeCompatExports.ResolveClockTime(clockId, out seconds, out nanoseconds))
|
||||||
{
|
{
|
||||||
case ClockRealtime:
|
KernelRuntimeCompatExports.TrySetErrno(ctx, Einval);
|
||||||
case ClockRealtimeFast:
|
ctx[CpuRegister.Rax] = unchecked((ulong)-1);
|
||||||
case ClockVirtual:
|
return -1;
|
||||||
case ClockProf:
|
|
||||||
{
|
|
||||||
var now = DateTimeOffset.UtcNow;
|
|
||||||
seconds = now.ToUnixTimeSeconds();
|
|
||||||
nanoseconds = (now.Ticks % TimeSpan.TicksPerSecond) * 100;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case ClockMonotonic:
|
|
||||||
case ClockMonotonicFast:
|
|
||||||
case ClockUptime:
|
|
||||||
KernelRuntimeCompatExports.GetProcessMonotonicTime(out seconds, out nanoseconds);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
KernelRuntimeCompatExports.TrySetErrno(ctx, Einval);
|
|
||||||
ctx[CpuRegister.Rax] = unchecked((ulong)-1);
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Span<byte> timespecBuffer = stackalloc byte[16];
|
Span<byte> timespecBuffer = stackalloc byte[16];
|
||||||
|
|||||||
@@ -14,6 +14,15 @@ namespace SharpEmu.Libs.Kernel;
|
|||||||
|
|
||||||
public static class KernelRuntimeCompatExports
|
public static class KernelRuntimeCompatExports
|
||||||
{
|
{
|
||||||
|
internal const int ClockRealtime = 0;
|
||||||
|
internal const int ClockVirtual = 1;
|
||||||
|
internal const int ClockProf = 2;
|
||||||
|
internal const int ClockMonotonic = 4;
|
||||||
|
internal const int ClockUptime = 5;
|
||||||
|
internal const int ClockRealtimePrecise = 9;
|
||||||
|
internal const int ClockMonotonicPrecise = 11;
|
||||||
|
internal const int ClockRealtimeFast = 10;
|
||||||
|
internal const int ClockMonotonicFast = 12;
|
||||||
private const int Efault = 14;
|
private const int Efault = 14;
|
||||||
private const ulong TlsErrnoOffset = 0x40;
|
private const ulong TlsErrnoOffset = 0x40;
|
||||||
private const ulong TlsStackChkGuardBaseOffset = 0x800;
|
private const ulong TlsStackChkGuardBaseOffset = 0x800;
|
||||||
@@ -187,15 +196,9 @@ public static class KernelRuntimeCompatExports
|
|||||||
|
|
||||||
long seconds;
|
long seconds;
|
||||||
long nanoseconds;
|
long nanoseconds;
|
||||||
if (clockId == 0)
|
if (!ResolveClockTime(clockId, out seconds, out nanoseconds))
|
||||||
{
|
{
|
||||||
var now = DateTimeOffset.UtcNow;
|
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT;
|
||||||
seconds = now.ToUnixTimeSeconds();
|
|
||||||
nanoseconds = (now.Ticks % TimeSpan.TicksPerSecond) * 100;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
GetProcessMonotonicTime(out seconds, out nanoseconds);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Span<byte> timespecBuffer = stackalloc byte[16];
|
Span<byte> timespecBuffer = stackalloc byte[16];
|
||||||
@@ -632,6 +635,36 @@ public static class KernelRuntimeCompatExports
|
|||||||
return address != 0 && ctx.TryWriteInt32(address, value);
|
return address != 0 && ctx.TryWriteInt32(address, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal static bool ResolveClockTime(int clockId, out long seconds, out long nanoseconds)
|
||||||
|
{
|
||||||
|
switch (clockId)
|
||||||
|
{
|
||||||
|
case ClockRealtime:
|
||||||
|
case ClockRealtimePrecise:
|
||||||
|
case ClockRealtimeFast:
|
||||||
|
case ClockVirtual:
|
||||||
|
case ClockProf:
|
||||||
|
{
|
||||||
|
var now = DateTimeOffset.UtcNow;
|
||||||
|
seconds = now.ToUnixTimeSeconds();
|
||||||
|
nanoseconds = (now.Ticks % TimeSpan.TicksPerSecond) * 100;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ClockMonotonic:
|
||||||
|
case ClockMonotonicPrecise:
|
||||||
|
case ClockMonotonicFast:
|
||||||
|
case ClockUptime:
|
||||||
|
GetProcessMonotonicTime(out seconds, out nanoseconds);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default:
|
||||||
|
seconds = 0;
|
||||||
|
nanoseconds = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
internal static void GetProcessMonotonicTime(out long seconds, out long nanoseconds)
|
internal static void GetProcessMonotonicTime(out long seconds, out long nanoseconds)
|
||||||
{
|
{
|
||||||
var elapsedTicks = Stopwatch.GetTimestamp() - _processStartCounter;
|
var elapsedTicks = Stopwatch.GetTimestamp() - _processStartCounter;
|
||||||
|
|||||||
Reference in New Issue
Block a user