mirror of
https://github.com/par274/sharpemu.git
synced 2026-07-25 20:28:48 +08:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| de4fc1e1a8 | |||
| 5e76554514 | |||
| 3a24db567f |
@@ -67,13 +67,6 @@ public static class KernelMemoryCompatExports
|
||||
private const int Einval = 22;
|
||||
private const int Erange = 34;
|
||||
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 ushort KernelStatModeDirectory = 0x41FF;
|
||||
private const ushort KernelStatModeRegular = 0x81FF;
|
||||
@@ -2011,29 +2004,11 @@ public static class KernelMemoryCompatExports
|
||||
|
||||
long seconds;
|
||||
long nanoseconds;
|
||||
switch (clockId)
|
||||
if (!KernelRuntimeCompatExports.ResolveClockTime(clockId, out seconds, out nanoseconds))
|
||||
{
|
||||
case ClockRealtime:
|
||||
case ClockRealtimeFast:
|
||||
case ClockVirtual:
|
||||
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;
|
||||
KernelRuntimeCompatExports.TrySetErrno(ctx, Einval);
|
||||
ctx[CpuRegister.Rax] = unchecked((ulong)-1);
|
||||
return -1;
|
||||
}
|
||||
|
||||
Span<byte> timespecBuffer = stackalloc byte[16];
|
||||
|
||||
@@ -14,6 +14,17 @@ namespace SharpEmu.Libs.Kernel;
|
||||
|
||||
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 Einval = 22;
|
||||
private const ulong TlsErrnoOffset = 0x40;
|
||||
private const ulong TlsStackChkGuardBaseOffset = 0x800;
|
||||
private const ulong StackChkGuardFieldOffset = 0x10;
|
||||
@@ -64,6 +75,77 @@ public static class KernelRuntimeCompatExports
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
private delegate ulong RdtscDelegate();
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "QvsZxomvUHs",
|
||||
ExportName = "sceKernelNanosleep",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libKernel")]
|
||||
public static int KernelNanosleep(CpuContext ctx)
|
||||
{
|
||||
var requestAddress = ctx[CpuRegister.Rdi];
|
||||
var remainAddress = ctx[CpuRegister.Rsi];
|
||||
|
||||
if (requestAddress == 0)
|
||||
{
|
||||
ctx[CpuRegister.Rax] = Einval;
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
Span<byte> timespecBuffer = stackalloc byte[16];
|
||||
if (!ctx.Memory.TryRead(requestAddress, timespecBuffer))
|
||||
{
|
||||
ctx[CpuRegister.Rax] = Efault;
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||
}
|
||||
|
||||
var tvSec = BinaryPrimitives.ReadInt64LittleEndian(timespecBuffer);
|
||||
var tvNsec = BinaryPrimitives.ReadInt64LittleEndian(timespecBuffer[sizeof(long)..]);
|
||||
|
||||
if (tvSec < 0 || tvNsec < 0 || tvNsec >= 1_000_000_000L)
|
||||
{
|
||||
ctx[CpuRegister.Rax] = Einval;
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if (tvSec == 0 && tvNsec == 0)
|
||||
{
|
||||
WriteRemainingTime(ctx, remainAddress, 0, 0);
|
||||
ctx[CpuRegister.Rax] = 0;
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||
}
|
||||
|
||||
GuestThreadExecution.Scheduler?.Pump(ctx, "sceKernelNanosleep");
|
||||
|
||||
// TimeSpan resolution is 100 ns ticks, so sub-100 ns requests round up to
|
||||
// a single tick rather than collapsing to a zero-length (no-op) sleep.
|
||||
var totalTicks = tvSec * TimeSpan.TicksPerSecond + Math.Max(tvNsec / 100L, 1L);
|
||||
try
|
||||
{
|
||||
Thread.Sleep(TimeSpan.FromTicks(totalTicks));
|
||||
}
|
||||
catch (ArgumentOutOfRangeException)
|
||||
{
|
||||
Thread.Sleep(TimeSpan.FromMilliseconds(int.MaxValue));
|
||||
}
|
||||
|
||||
WriteRemainingTime(ctx, remainAddress, 0, 0);
|
||||
ctx[CpuRegister.Rax] = 0;
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||
}
|
||||
|
||||
private static void WriteRemainingTime(CpuContext ctx, ulong remainAddress, long seconds, long nanoseconds)
|
||||
{
|
||||
if (remainAddress == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Span<byte> remainBuffer = stackalloc byte[16];
|
||||
BinaryPrimitives.WriteInt64LittleEndian(remainBuffer, seconds);
|
||||
BinaryPrimitives.WriteInt64LittleEndian(remainBuffer[sizeof(long)..], nanoseconds);
|
||||
ctx.Memory.TryWrite(remainAddress, remainBuffer);
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "1jfXLRVzisc",
|
||||
ExportName = "sceKernelUsleep",
|
||||
@@ -186,15 +268,9 @@ public static class KernelRuntimeCompatExports
|
||||
|
||||
long seconds;
|
||||
long nanoseconds;
|
||||
if (clockId == 0)
|
||||
if (!ResolveClockTime(clockId, out seconds, out nanoseconds))
|
||||
{
|
||||
var now = DateTimeOffset.UtcNow;
|
||||
seconds = now.ToUnixTimeSeconds();
|
||||
nanoseconds = (now.Ticks % TimeSpan.TicksPerSecond) * 100;
|
||||
}
|
||||
else
|
||||
{
|
||||
GetProcessMonotonicTime(out seconds, out nanoseconds);
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
Span<byte> timespecBuffer = stackalloc byte[16];
|
||||
@@ -226,8 +302,11 @@ public static class KernelRuntimeCompatExports
|
||||
var now = DateTimeOffset.UtcNow;
|
||||
var seconds = now.ToUnixTimeSeconds();
|
||||
var microseconds = (now.Ticks % TimeSpan.TicksPerSecond) / 10;
|
||||
if (!ctx.TryWriteUInt64(timeAddress, unchecked((ulong)seconds)) ||
|
||||
!ctx.TryWriteUInt64(timeAddress + sizeof(long), unchecked((ulong)microseconds)))
|
||||
|
||||
Span<byte> timevalBuffer = stackalloc byte[16];
|
||||
BinaryPrimitives.WriteInt64LittleEndian(timevalBuffer, seconds);
|
||||
BinaryPrimitives.WriteInt64LittleEndian(timevalBuffer[sizeof(long)..], microseconds);
|
||||
if (!ctx.Memory.TryWrite(timeAddress, timevalBuffer))
|
||||
{
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||
}
|
||||
@@ -249,18 +328,28 @@ public static class KernelRuntimeCompatExports
|
||||
var seconds = now.ToUnixTimeSeconds();
|
||||
var microseconds = (now.Ticks % TimeSpan.TicksPerSecond) / 10;
|
||||
|
||||
if (timeAddress != 0 &&
|
||||
(!ctx.TryWriteUInt64(timeAddress, unchecked((ulong)seconds)) ||
|
||||
!ctx.TryWriteUInt64(timeAddress + sizeof(long), unchecked((ulong)microseconds))))
|
||||
if (timeAddress != 0)
|
||||
{
|
||||
return -1;
|
||||
Span<byte> timevalBuffer = stackalloc byte[16];
|
||||
BinaryPrimitives.WriteInt64LittleEndian(timevalBuffer, seconds);
|
||||
BinaryPrimitives.WriteInt64LittleEndian(timevalBuffer[sizeof(long)..], microseconds);
|
||||
if (!ctx.Memory.TryWrite(timeAddress, timevalBuffer))
|
||||
{
|
||||
TrySetErrno(ctx, Efault);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (timezoneAddress != 0 &&
|
||||
(!ctx.TryWriteInt32(timezoneAddress, 0) ||
|
||||
!ctx.TryWriteInt32(timezoneAddress + sizeof(int), 0)))
|
||||
if (timezoneAddress != 0)
|
||||
{
|
||||
return -1;
|
||||
Span<byte> timezoneBuffer = stackalloc byte[8];
|
||||
BinaryPrimitives.WriteInt32LittleEndian(timezoneBuffer, 0);
|
||||
BinaryPrimitives.WriteInt32LittleEndian(timezoneBuffer[sizeof(int)..], 0);
|
||||
if (!ctx.Memory.TryWrite(timezoneAddress, timezoneBuffer))
|
||||
{
|
||||
TrySetErrno(ctx, Efault);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
ctx[CpuRegister.Rax] = 0;
|
||||
@@ -618,6 +707,36 @@ public static class KernelRuntimeCompatExports
|
||||
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)
|
||||
{
|
||||
var elapsedTicks = Stopwatch.GetTimestamp() - _processStartCounter;
|
||||
|
||||
Reference in New Issue
Block a user